Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
name: Publish NPM
on:
workflow_dispatch:
inputs:
path:
description: The path to run the release in, e.g. '.' or 'packages/mcp-server'
required: true

release:
types: [published]
Expand All @@ -27,6 +31,11 @@ jobs:

- name: Publish to NPM
run: |
bash ./bin/publish-npm
if [ -n "${{ github.event.inputs.path }}" ]; then
PATHS_RELEASED='[\"${{ github.event.inputs.path }}\"]'
else
PATHS_RELEASED='[\".\", \"packages/mcp-server\"]'
fi
yarn tsn scripts/publish-packages.ts "{ \"paths_released\": \"$PATHS_RELEASED\" }"
env:
NPM_TOKEN: ${{ secrets.ISAACUS_NPM_TOKEN || secrets.NPM_TOKEN }}
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 2
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-861e8a85f0fb73cf4b7fc6c2b27722072ff33109459e90c17be24af15dfcbd0c.yml
openapi_spec_hash: 644a0383600633ee604bb1e5b9ca025d
config_hash: 2bc262108dc3b065c16da5bb85c1e282
config_hash: 1d603d50b7183a492ad6df5f728a1863
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default tseslint.config(
},
},
{
files: ['tests/**', 'examples/**'],
files: ['tests/**', 'examples/**', 'packages/**'],
rules: {
'no-restricted-imports': 'off',
},
Expand Down
32 changes: 32 additions & 0 deletions packages/mcp-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Isaacus TypeScript MCP Server

It is generated with [Stainless](https://www.stainless.com/).

## Installation

### Via Claude Desktop

See [the user guide](https://modelcontextprotocol.io/quickstart/user) for setup.

Once it's set up, add your MCP server to your `claude_desktop_config.json` file to enable it.

The configuration file should be at:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

Add the following value to your `mcpServers` section. Make sure to provide any necessary environment variables (like API keys) as well.

```json
{
"mcpServers": {
"isaacus_api": {
"command": "npx",
"args": ["-y", "isaacus-mcp"],
"env": {
"ISAACUS_API_KEY": "My API Key"
}
}
}
}
```
45 changes: 45 additions & 0 deletions packages/mcp-server/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -exuo pipefail

rm -rf dist; mkdir dist

# Copy src to dist/src and build from dist/src into dist, so that
# the source map for index.js.map will refer to ./src/index.ts etc
cp -rp src README.md dist

for file in LICENSE; do
if [ -e "../../${file}" ]; then cp "../../${file}" dist; fi
done

for file in CHANGELOG.md; do
if [ -e "${file}" ]; then cp "${file}" dist; fi
done

# this converts the export map paths for the dist directory
# and does a few other minor things
PKG_JSON_PATH=../../packages/mcp-server/package.json node ../../scripts/utils/make-dist-package-json.cjs > dist/package.json

# updates the `isaacus` dependency to point to NPM
node scripts/postprocess-dist-package-json.cjs

# build to .js/.mjs/.d.ts files
npm exec tsc-multi
# we need to add exports = module.exports = Anthropic TypeScript to index.js;
# No way to get that from index.ts because it would cause compile errors
# when building .mjs
DIST_PATH=./dist node ../../scripts/utils/fix-index-exports.cjs

# with "moduleResolution": "nodenext", if ESM resolves to index.d.ts,
# it'll have TS errors on the default import. But if it resolves to
# index.d.mts the default import will work (even though both files have
# the same export default statement)
cp dist/index.d.ts dist/index.d.mts
cp tsconfig.dist-src.json dist/src/tsconfig.json

# Add proper Node.js shebang to the top of the file
sed -i.bak '1s;^;#!/usr/bin/env node\n;' dist/index.js
rm dist/index.js.bak

chmod +x dist/index.js

DIST_PATH=./dist PKG_IMPORT_PATH=isaacus-mcp/ node ../../scripts/utils/postprocess-files.cjs
74 changes: 74 additions & 0 deletions packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"name": "isaacus-mcp",
"version": "0.3.1",
"description": "The official MCP Server for the Isaacus API",
"author": "Isaacus <support@isaacus.com>",
"types": "dist/index.d.ts",
"main": "dist/index.js",
"type": "commonjs",
"repository": "github:isaacus-dev/isaacus-typescript",
"license": "Apache-2.0",
"packageManager": "yarn@1.22.22",
"private": false,
"scripts": {
"test": "echo 'no tests defined yet' && exit 1",
"build": "bash ./build",
"prepack": "echo 'to pack, run yarn build && (cd dist; yarn pack)' && exit 1",
"prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1",
"format": "prettier --write --cache --cache-strategy metadata . !dist",
"prepare": "npm run build",
"tsn": "ts-node -r tsconfig-paths/register",
"lint": "eslint --ext ts,js .",
"fix": "eslint --fix --ext ts,js ."
},
"dependencies": {
"isaacus": "file:../../dist/",
"@modelcontextprotocol/sdk": "^1.6.1"
},
"bin": {
"mcp-server": "dist/index.js"
},
"devDependencies": {
"@types/jest": "^29.4.0",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"eslint": "^8.49.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-unused-imports": "^3.0.0",
"jest": "^29.4.0",
"prettier": "^3.0.0",
"ts-jest": "^29.1.0",
"ts-morph": "^19.0.0",
"ts-node": "^10.5.0",
"tsc-multi": "^1.1.0",
"tsconfig-paths": "^4.0.0",
"typescript": "^4.8.2"
},
"imports": {
"isaacus-mcp": ".",
"isaacus-mcp/*": "./src/*"
},
"exports": {
".": {
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"./*.mjs": {
"types": "./dist/*.d.ts",
"default": "./dist/*.mjs"
},
"./*.js": {
"types": "./dist/*.d.ts",
"default": "./dist/*.js"
},
"./*": {
"types": "./dist/*.d.ts",
"require": "./dist/*.js",
"default": "./dist/*.mjs"
}
}
}
12 changes: 12 additions & 0 deletions packages/mcp-server/scripts/postprocess-dist-package-json.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const fs = require('fs');
const pkgJson = require('../dist/package.json');
const parentPkgJson = require('../../../package.json');

for (const dep in pkgJson.dependencies) {
// ensure we point to NPM instead of a local directory
if (dep === 'isaacus') {
pkgJson.dependencies[dep] = '^' + parentPkgJson.version;
}
}

fs.writeFileSync('dist/package.json', JSON.stringify(pkgJson, null, 2));
18 changes: 18 additions & 0 deletions packages/mcp-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { server, init } from './server';

async function main() {
init({ server });
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP Server running on stdio');
}

if (require.main === module) {
main().catch((error) => {
console.error('Fatal error in main():', error);
process.exit(1);
});
}
86 changes: 86 additions & 0 deletions packages/mcp-server/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { tools, handlers, HandlerFunction } from './tools';
import { CallToolRequestSchema, ListToolsRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js';
import Isaacus from 'isaacus';
export { tools, handlers } from './tools';

// Create server instance
export const server = new McpServer(
{
name: 'isaacus_api',
version: '0.3.1',
},
{
capabilities: {
tools: {},
},
},
);

/**
* Initializes the provided MCP Server with the given tools and handlers.
* If not provided, the default client, tools and handlers will be used.
*/
export function init(params: {
server: Server | McpServer;
client?: Isaacus;
tools?: Tool[];
handlers?: Record<string, HandlerFunction>;
}) {
const server = params.server instanceof McpServer ? params.server.server : params.server;
const providedTools = params.tools || tools;
const providedHandlers = params.handlers || handlers;
const client = params.client || new Isaacus({});

server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: providedTools,
};
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;

const handler = providedHandlers[name];
if (!handler) {
throw new Error(`Unknown tool: ${name}`);
}

return executeHandler(handler, client, args);
});
}

/**
* Runs the provided handler with the given client and arguments.
*/
export async function executeHandler(
handler: HandlerFunction,
client: Isaacus,
args: Record<string, unknown> | undefined,
) {
const result = await handler(client, args || {});
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}

export const readEnv = (env: string): string => {
let envValue = undefined;
if (typeof (globalThis as any).process !== 'undefined') {
envValue = (globalThis as any).process.env?.[env]?.trim();
} else if (typeof (globalThis as any).Deno !== 'undefined') {
envValue = (globalThis as any).Deno.env?.get?.(env)?.trim();
}
if (envValue === undefined) {
throw new Error(`Environment variable ${env} is not set`);
}
return envValue;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import Isaacus from 'isaacus';

export const tool: Tool = {
name: 'create_classifications_universal',
description:
'Classify the relevance of a legal document to a query with an Isaacus universal legal AI classifier.',
inputSchema: {
type: 'object',
properties: {
model: {
type: 'string',
description:
'The ID of the [model](https://docs.isaacus.com/models#universal-classification) to use for universal classification.',
enum: ['kanon-universal-classifier', 'kanon-universal-classifier-mini'],
},
query: {
type: 'string',
description:
'The [Isaacus Query Language (IQL)](https://docs.isaacus.com/iql) query or, if IQL is disabled, the statement, to evaluate the text against.\n\nThe query must contain at least one non-whitespace character.\n\nUnlike the text being classified, the query cannot be so long that it exceeds the maximum input length of the universal classifier.',
},
text: {
type: 'string',
description: 'The text to classify.\n\nThe text must contain at least one non-whitespace character.',
},
chunking_options: {
type: 'object',
title: 'Chunking options',
description: 'Options for how to split text into smaller chunks.',
properties: {
overlap_ratio: {
type: 'number',
title: 'Unit interval (closed, open)',
description: 'A number greater than or equal to 0 and less than 1.',
},
overlap_tokens: {
type: 'integer',
title: 'Non-negative integer',
description: 'A whole number greater than -1.',
},
size: {
type: 'integer',
title: 'Positive integer',
description: 'A whole number greater than or equal to 1.',
},
},
required: [],
},
is_iql: {
type: 'boolean',
description:
'Whether the query should be interpreted as an [IQL](https://docs.isaacus.com/iql) query or else as a statement.',
},
scoring_method: {
type: 'string',
description:
"The method to use for producing an overall confidence score.\n\n`auto` is the default scoring method and is recommended for most use cases. Currently, it is equivalent to `chunk_max`. In the future, it will automatically select the best method based on the model and input.\n\n`chunk_max` uses the highest confidence score of all of the text's chunks.\n\n`chunk_avg` averages the confidence scores of all of the text's chunks.\n\n`chunk_min` uses the lowest confidence score of all of the text's chunks.",
enum: ['auto', 'chunk_max', 'chunk_avg', 'chunk_min'],
},
},
},
};

export const handler = (client: Isaacus, args: any) => {
const { ...body } = args;
return client.classifications.universal.create(body);
};

export default { tool, handler };
Loading