From e1992ce3ade04471fdd437d753af418ed80836f4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:44:07 +0000 Subject: [PATCH 1/9] chore(internal): version bump (#32) --- packages/mcp-server/src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 6ae5f55..30018b6 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -11,7 +11,7 @@ export { tools, handlers } from './tools'; export const server = new McpServer( { name: 'isaacus_api', - version: '0.3.1', + version: '0.4.0', }, { capabilities: { From 979c453293006d0f841901df8f3383f5785b00a6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 05:46:20 +0000 Subject: [PATCH 2/9] fix(client): send `X-Stainless-Timeout` in seconds (#34) --- src/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index affa94e..0f4b089 100644 --- a/src/client.ts +++ b/src/client.ts @@ -605,7 +605,7 @@ export class Isaacus { Accept: 'application/json', 'User-Agent': this.getUserAgent(), 'X-Stainless-Retry-Count': String(retryCount), - ...(options.timeout ? { 'X-Stainless-Timeout': String(options.timeout) } : {}), + ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}), ...getPlatformHeaders(), }, this.authHeaders(options), From c3c597f21cebbb6e14e9b87fa87d543697afeb7f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 06:01:17 +0000 Subject: [PATCH 3/9] feat(mcp): support end-user filtering of tools, resources, and tags (#35) --- packages/mcp-server/README.md | 38 ++- packages/mcp-server/jest.config.ts | 17 ++ packages/mcp-server/package.json | 5 +- packages/mcp-server/src/index.ts | 180 +++++++++++++- packages/mcp-server/src/server.ts | 19 +- .../create-classifications-universal.ts | 9 +- packages/mcp-server/src/tools/index.ts | 68 +++++- .../src/tools/rerankings/create-rerankings.ts | 9 +- packages/mcp-server/tests/tools.test.ts | 226 ++++++++++++++++++ 9 files changed, 546 insertions(+), 25 deletions(-) create mode 100644 packages/mcp-server/jest.config.ts create mode 100644 packages/mcp-server/tests/tools.test.ts diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 0d58172..a43bc6a 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -8,9 +8,7 @@ It is generated with [Stainless](https://www.stainless.com/). 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: +Once it's set up, find your `claude_desktop_config.json` file: - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` - Windows: `%APPDATA%\Claude\claude_desktop_config.json` @@ -30,3 +28,37 @@ Add the following value to your `mcpServers` section. Make sure to provide any n } } ``` + +## Filtering tools + +You can run the package on the command line to discover and filter the set of tools that are exposed by the +MCP Server. This can be helpful for large APIs where including all endpoints at once is too much for your AI's +context window. + +You can filter by multiple aspects: + +- `--tool` includes a specific tool by name +- `--resource` includes all tools under a specific resource, and can have wildcards, e.g. `my.resource*` +- `--operation` includes just read (get/list) or just write operations + +See more information with `--help`: + +```sh +$ npx -y isaacus-mcp --help +``` + +All of these command-line options can be repeated, combined together, and have corresponding exclusion versions (e.g. `--no-tool`). + +Use `--list` to see the list of available tools, or see below. + +## Available Tools + +The following tools are available in this MCP server. + +### Resource `classifications.universal`: + +- `create_classifications_universal` (`write`): Classify the relevance of a legal document to a query with an Isaacus universal legal AI classifier. + +### Resource `rerankings`: + +- `create_rerankings` (`write`): Rerank legal documents by their relevance to a query with an Isaacus legal AI reranker. diff --git a/packages/mcp-server/jest.config.ts b/packages/mcp-server/jest.config.ts new file mode 100644 index 0000000..971805b --- /dev/null +++ b/packages/mcp-server/jest.config.ts @@ -0,0 +1,17 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + preset: 'ts-jest/presets/default-esm', + testEnvironment: 'node', + transform: { + '^.+\\.(t|j)sx?$': ['@swc/jest', { sourceMaps: 'inline' }], + }, + moduleNameMapper: { + '^isaacus-mcp$': '/src/index.ts', + '^isaacus-mcp/(.*)$': '/src/$1', + }, + modulePathIgnorePatterns: ['/dist/'], + testPathIgnorePatterns: ['scripts'], +}; + +export default config; diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index e4315cf..1847635 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -11,7 +11,7 @@ "packageManager": "yarn@1.22.22", "private": false, "scripts": { - "test": "echo 'no tests defined yet' && exit 1", + "test": "jest", "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", @@ -23,7 +23,8 @@ }, "dependencies": { "isaacus": "file:../../dist/", - "@modelcontextprotocol/sdk": "^1.6.1" + "@modelcontextprotocol/sdk": "^1.6.1", + "yargs": "^17.7.2" }, "bin": { "mcp-server": "dist/index.js" diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 038f6a8..fd6d7f8 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -1,10 +1,116 @@ -// 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'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { init, server } from './server'; +import { endpoints, Filter, query } from './tools'; async function main() { - init({ server }); + const opts = yargs(hideBin(process.argv)) + .option('tool', { + type: 'string', + array: true, + description: 'Include tools matching the specified names', + }) + .option('resource', { + type: 'string', + array: true, + description: 'Include tools matching the specified resources', + }) + .option('operation', { + type: 'string', + array: true, + choices: ['read', 'write'], + description: 'Include tools matching the specified operations', + }) + .option('tag', { + type: 'string', + array: true, + description: 'Include tools with the specified tags', + }) + .option('no-tool', { + type: 'string', + array: true, + description: 'Exclude tools matching the specified names', + }) + .option('no-resource', { + type: 'string', + array: true, + description: 'Exclude tools matching the specified resources', + }) + .option('no-operation', { + type: 'string', + array: true, + description: 'Exclude tools matching the specified operations', + }) + .option('no-tag', { + type: 'string', + array: true, + description: 'Exclude tools with the specified tags', + }) + .option('list', { + type: 'boolean', + description: 'List all tools and exit', + }) + .help(); + + for (const [command, desc] of examples()) { + opts.example(command, desc); + } + + const argv = opts.parseSync(); + + if (argv.list) { + listAllTools(); + return; + } + const filters: Filter[] = []; + + for (const tag of argv.tag || []) { + filters.push({ type: 'tag', op: 'include', value: tag }); + } + + for (const tag of argv.noTag || []) { + filters.push({ type: 'tag', op: 'exclude', value: tag }); + } + + for (const resource of argv.resource || []) { + filters.push({ type: 'resource', op: 'include', value: resource }); + } + + for (const resource of argv.noResource || []) { + filters.push({ type: 'resource', op: 'exclude', value: resource }); + } + + for (const tool of argv.tool || []) { + filters.push({ type: 'tool', op: 'include', value: tool }); + } + + for (const tool of argv.noTool || []) { + filters.push({ type: 'tool', op: 'exclude', value: tool }); + } + + for (const operation of argv.operation || []) { + filters.push({ type: 'operation', op: 'include', value: operation }); + } + + for (const operation of argv.noOperation || []) { + filters.push({ type: 'operation', op: 'exclude', value: operation }); + } + + const filteredEndpoints = query(filters, endpoints); + + if (filteredEndpoints.length === 0) { + console.error('No tools match the provided filters.'); + process.exit(1); + } + + console.error( + `MCP Server starting with ${filteredEndpoints.length} tools:`, + filteredEndpoints.map((e) => e.tool.name), + ); + + init({ server, endpoints: filteredEndpoints }); + const transport = new StdioServerTransport(); await server.connect(transport); console.error('MCP Server running on stdio'); @@ -16,3 +122,69 @@ if (require.main === module) { process.exit(1); }); } + +function listAllTools() { + if (endpoints.length === 0) { + console.error('No tools available.'); + return; + } + console.error('Available tools:\n'); + + // Group endpoints by resource + const resourceGroups = new Map(); + + for (const endpoint of endpoints) { + const resource = endpoint.metadata.resource; + if (!resourceGroups.has(resource)) { + resourceGroups.set(resource, []); + } + resourceGroups.get(resource)!.push(endpoint); + } + + // Sort resources alphabetically + const sortedResources = Array.from(resourceGroups.keys()).sort(); + + // Display hierarchically by resource + for (const resource of sortedResources) { + console.error(`Resource: ${resource}`); + + const resourceEndpoints = resourceGroups.get(resource)!; + // Sort endpoints by tool name + resourceEndpoints.sort((a, b) => a.tool.name.localeCompare(b.tool.name)); + + for (const endpoint of resourceEndpoints) { + const { + tool, + metadata: { operation, tags }, + } = endpoint; + + console.error(` - ${tool.name} (${operation}) ${tags.length > 0 ? `tags: ${tags.join(', ')}` : ''}`); + console.error(` Description: ${tool.description}`); + } + console.error(''); + } +} + +function examples(): [string, string][] { + const firstEndpoint = endpoints[0]!; + const secondEndpoint = + endpoints.find((e) => e.metadata.resource !== firstEndpoint.metadata.resource) || endpoints[1]; + const tag = endpoints.find((e) => e.metadata.tags.length > 0)?.metadata.tags[0]; + const otherEndpoint = secondEndpoint || firstEndpoint; + + return [ + [ + `--tool="${firstEndpoint.tool.name}" ${secondEndpoint ? `--tool="${secondEndpoint.tool.name}"` : ''}`, + 'Include tools by name', + ], + [ + `--resource="${firstEndpoint.metadata.resource}" --operation="read"`, + 'Filter by resource and operation', + ], + [ + `--resource="${otherEndpoint.metadata.resource}*" --no-tool="${otherEndpoint.tool.name}"`, + 'Use resource wildcards and exclusions', + ], + ...(tag ? [[`--tag="${tag}"`, 'Filter based on tags'] as [string, string]] : []), + ]; +} diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 30018b6..124c6bc 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -2,10 +2,10 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { tools, handlers, HandlerFunction } from './tools'; +import { endpoints, HandlerFunction } from './tools'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js'; import Isaacus from 'isaacus'; -export { tools, handlers } from './tools'; +export { endpoints } from './tools'; // Create server instance export const server = new McpServer( @@ -27,24 +27,27 @@ export const server = new McpServer( export function init(params: { server: Server | McpServer; client?: Isaacus; - tools?: Tool[]; - handlers?: Record; + endpoints?: { tool: Tool; handler: HandlerFunction }[]; }) { const server = params.server instanceof McpServer ? params.server.server : params.server; - const providedTools = params.tools || tools; - const providedHandlers = params.handlers || handlers; + const providedEndpoints = params.endpoints || endpoints; + const tools = providedEndpoints.map((endpoint) => endpoint.tool); + const handlers = Object.fromEntries( + providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint.handler]), + ); + const client = params.client || new Isaacus({}); server.setRequestHandler(ListToolsRequestSchema, async () => { return { - tools: providedTools, + tools, }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; - const handler = providedHandlers[name]; + const handler = handlers[name]; if (!handler) { throw new Error(`Unknown tool: ${name}`); } diff --git a/packages/mcp-server/src/tools/classifications/universal/create-classifications-universal.ts b/packages/mcp-server/src/tools/classifications/universal/create-classifications-universal.ts index d92321d..291fa61 100644 --- a/packages/mcp-server/src/tools/classifications/universal/create-classifications-universal.ts +++ b/packages/mcp-server/src/tools/classifications/universal/create-classifications-universal.ts @@ -1,8 +1,15 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import type { Metadata } from '../../'; import Isaacus from 'isaacus'; +export const metadata: Metadata = { + resource: 'classifications.universal', + operation: 'write', + tags: [], +}; + export const tool: Tool = { name: 'create_classifications_universal', description: @@ -68,4 +75,4 @@ export const handler = (client: Isaacus, args: any) => { return client.classifications.universal.create(body); }; -export default { tool, handler }; +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/index.ts b/packages/mcp-server/src/tools/index.ts index 50510b9..d06db43 100644 --- a/packages/mcp-server/src/tools/index.ts +++ b/packages/mcp-server/src/tools/index.ts @@ -6,15 +6,71 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js'; import create_classifications_universal from './classifications/universal/create-classifications-universal'; import create_rerankings from './rerankings/create-rerankings'; -export const tools: Tool[] = []; - export type HandlerFunction = (client: Isaacus, args: any) => Promise; -export const handlers: Record = {}; -function addEndpoint(endpoint: { tool: Tool; handler: HandlerFunction }) { - tools.push(endpoint.tool); - handlers[endpoint.tool.name] = endpoint.handler; +export type Metadata = { + resource: string; + operation: 'read' | 'write'; + tags: string[]; +}; + +export type Endpoint = { + metadata: Metadata; + tool: Tool; + handler: HandlerFunction; +}; + +export const endpoints: Endpoint[] = []; + +function addEndpoint(endpoint: Endpoint) { + endpoints.push(endpoint); } addEndpoint(create_classifications_universal); addEndpoint(create_rerankings); + +export type Filter = { + type: 'resource' | 'operation' | 'tag' | 'tool'; + op: 'include' | 'exclude'; + value: string; +}; + +export function query(filters: Filter[], endpoints: Endpoint[]): Endpoint[] { + if (filters.length === 0) { + return endpoints; + } + const allExcludes = filters.every((filter) => filter.op === 'exclude'); + + return endpoints.filter((endpoint: Endpoint) => { + let included = false || allExcludes; + + for (const filter of filters) { + if (match(filter, endpoint)) { + included = filter.op === 'include'; + } + } + + return included; + }); +} + +function match({ type, value }: Filter, endpoint: Endpoint): boolean { + switch (type) { + case 'resource': { + const regexStr = '^' + normalizeResource(value).replace(/\*/g, '.*') + '$'; + const regex = new RegExp(regexStr); + console.error('regex is', regexStr); + return regex.test(normalizeResource(endpoint.metadata.resource)); + } + case 'operation': + return endpoint.metadata.operation === value; + case 'tag': + return endpoint.metadata.tags.includes(value); + case 'tool': + return endpoint.tool.name === value; + } +} + +function normalizeResource(resource: string): string { + return resource.toLowerCase().replace(/[^a-z.*\-_]*/g, ''); +} diff --git a/packages/mcp-server/src/tools/rerankings/create-rerankings.ts b/packages/mcp-server/src/tools/rerankings/create-rerankings.ts index 1ee7786..25fb08d 100644 --- a/packages/mcp-server/src/tools/rerankings/create-rerankings.ts +++ b/packages/mcp-server/src/tools/rerankings/create-rerankings.ts @@ -1,8 +1,15 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import type { Metadata } from '../'; import Isaacus from 'isaacus'; +export const metadata: Metadata = { + resource: 'rerankings', + operation: 'write', + tags: [], +}; + export const tool: Tool = { name: 'create_rerankings', description: 'Rerank legal documents by their relevance to a query with an Isaacus legal AI reranker.', @@ -77,4 +84,4 @@ export const handler = (client: Isaacus, args: any) => { return client.rerankings.create(body); }; -export default { tool, handler }; +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/tests/tools.test.ts b/packages/mcp-server/tests/tools.test.ts new file mode 100644 index 0000000..79979e3 --- /dev/null +++ b/packages/mcp-server/tests/tools.test.ts @@ -0,0 +1,226 @@ +import { Endpoint, Filter, Metadata, query } from '../src/tools'; + +describe('Endpoint filtering', () => { + const endpoints: Endpoint[] = [ + endpoint({ + resource: 'user', + operation: 'read', + tags: ['admin'], + toolName: 'retrieve_user', + }), + endpoint({ + resource: 'user.profile', + operation: 'write', + tags: [], + toolName: 'create_user_profile', + }), + endpoint({ + resource: 'user.profile', + operation: 'read', + tags: [], + toolName: 'get_user_profile', + }), + endpoint({ + resource: 'user.roles.permissions', + operation: 'write', + tags: ['admin', 'security'], + toolName: 'update_user_role_permissions', + }), + endpoint({ + resource: 'documents.metadata.tags', + operation: 'write', + tags: ['taxonomy', 'metadata'], + toolName: 'create_document_metadata_tags', + }), + endpoint({ + resource: 'organization.settings', + operation: 'read', + tags: ['admin', 'configuration'], + toolName: 'get_organization_settings', + }), + ]; + + const tests: { name: string; filters: Filter[]; expected: string[] }[] = [ + { + name: 'match all', + filters: [], + expected: endpoints.map((e) => e.tool.name), + }, + + // Resource tests + { + name: 'simple resource', + filters: [{ type: 'resource', op: 'include', value: 'user' }], + expected: ['retrieve_user'], + }, + { + name: 'exclude resource', + filters: [{ type: 'resource', op: 'exclude', value: 'user' }], + expected: [ + 'create_user_profile', + 'get_user_profile', + 'update_user_role_permissions', + 'create_document_metadata_tags', + 'get_organization_settings', + ], + }, + { + name: 'resource and subresources', + filters: [{ type: 'resource', op: 'include', value: 'user*' }], + expected: ['retrieve_user', 'create_user_profile', 'get_user_profile', 'update_user_role_permissions'], + }, + { + name: 'just subresources', + filters: [{ type: 'resource', op: 'include', value: 'user.*' }], + expected: ['create_user_profile', 'get_user_profile', 'update_user_role_permissions'], + }, + { + name: 'specific subresource', + filters: [{ type: 'resource', op: 'include', value: 'user.roles.permissions' }], + expected: ['update_user_role_permissions'], + }, + { + name: 'deep wildcard match', + filters: [{ type: 'resource', op: 'include', value: '*.*.tags' }], + expected: ['create_document_metadata_tags'], + }, + + // Operation tests + { + name: 'read operation', + filters: [{ type: 'operation', op: 'include', value: 'read' }], + expected: ['retrieve_user', 'get_user_profile', 'get_organization_settings'], + }, + { + name: 'write operation', + filters: [{ type: 'operation', op: 'include', value: 'write' }], + expected: ['create_user_profile', 'update_user_role_permissions', 'create_document_metadata_tags'], + }, + { + name: 'resource and operation combined', + filters: [ + { type: 'resource', op: 'include', value: 'user.profile' }, + { type: 'operation', op: 'exclude', value: 'write' }, + ], + expected: ['get_user_profile'], + }, + + // Tag tests + { + name: 'admin tag', + filters: [{ type: 'tag', op: 'include', value: 'admin' }], + expected: ['retrieve_user', 'update_user_role_permissions', 'get_organization_settings'], + }, + { + name: 'taxonomy tag', + filters: [{ type: 'tag', op: 'include', value: 'taxonomy' }], + expected: ['create_document_metadata_tags'], + }, + { + name: 'multiple tags (OR logic)', + filters: [ + { type: 'tag', op: 'include', value: 'admin' }, + { type: 'tag', op: 'include', value: 'security' }, + ], + expected: ['retrieve_user', 'update_user_role_permissions', 'get_organization_settings'], + }, + { + name: 'excluding a tag', + filters: [ + { type: 'tag', op: 'include', value: 'admin' }, + { type: 'tag', op: 'exclude', value: 'security' }, + ], + expected: ['retrieve_user', 'get_organization_settings'], + }, + + // Tool name tests + { + name: 'tool name match', + filters: [{ type: 'tool', op: 'include', value: 'get_organization_settings' }], + expected: ['get_organization_settings'], + }, + { + name: 'two tools match', + filters: [ + { type: 'tool', op: 'include', value: 'get_organization_settings' }, + { type: 'tool', op: 'include', value: 'create_user_profile' }, + ], + expected: ['create_user_profile', 'get_organization_settings'], + }, + { + name: 'excluding tool by name', + filters: [ + { type: 'resource', op: 'include', value: 'user*' }, + { type: 'tool', op: 'exclude', value: 'retrieve_user' }, + ], + expected: ['create_user_profile', 'get_user_profile', 'update_user_role_permissions'], + }, + + // Complex combinations + { + name: 'complex filter: read operations with admin tag', + filters: [ + { type: 'operation', op: 'include', value: 'read' }, + { type: 'tag', op: 'include', value: 'admin' }, + ], + expected: [ + 'retrieve_user', + 'get_user_profile', + 'update_user_role_permissions', + 'get_organization_settings', + ], + }, + { + name: 'complex filter: user resources with no tags', + filters: [ + { type: 'resource', op: 'include', value: 'user.profile' }, + { type: 'tag', op: 'exclude', value: 'admin' }, + ], + expected: ['create_user_profile', 'get_user_profile'], + }, + { + name: 'complex filter: user resources and tags', + filters: [ + { type: 'resource', op: 'include', value: 'user.profile' }, + { type: 'tag', op: 'include', value: 'admin' }, + ], + expected: [ + 'retrieve_user', + 'create_user_profile', + 'get_user_profile', + 'update_user_role_permissions', + 'get_organization_settings', + ], + }, + ]; + + tests.forEach((test) => { + it(`filters by ${test.name}`, () => { + console.log(`Running test: ${test.name}`); + const filtered = query(test.filters, endpoints); + expect(filtered.map((e) => e.tool.name)).toEqual(test.expected); + }); + }); +}); + +function endpoint({ + resource, + operation, + tags, + toolName, +}: { + resource: string; + operation: Metadata['operation']; + tags: string[]; + toolName: string; +}): Endpoint { + return { + metadata: { + resource, + operation, + tags, + }, + tool: { name: toolName, inputSchema: { type: 'object', properties: {} } }, + handler: jest.fn(), + }; +} From f2f08fa2f6fcec5979d6c575dbd0347b8af77123 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 06:13:11 +0000 Subject: [PATCH 4/9] chore(internal): add aliases for Record and Array (#36) --- src/internal/builtin-types.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/internal/builtin-types.ts b/src/internal/builtin-types.ts index b2e598a..c23d3bd 100644 --- a/src/internal/builtin-types.ts +++ b/src/internal/builtin-types.ts @@ -39,9 +39,23 @@ type _HeadersInit = RequestInit['headers']; */ type _BodyInit = RequestInit['body']; +/** + * An alias to the builtin `Array` type so we can + * easily alias it in import statements if there are name clashes. + */ +type _Array = Array; + +/** + * An alias to the builtin `Record` type so we can + * easily alias it in import statements if there are name clashes. + */ +type _Record = Record; + export type { + _Array as Array, _BodyInit as BodyInit, _HeadersInit as HeadersInit, + _Record as Record, _RequestInfo as RequestInfo, _RequestInit as RequestInit, _Response as Response, From db9ceca29582d073446736def120d22a2d75a9c9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 07:09:08 +0000 Subject: [PATCH 5/9] fix(api): improve type resolution when importing as a package (#37) --- packages/mcp-server/build | 11 +---------- packages/mcp-server/package.json | 19 ++++--------------- packages/mcp-server/src/tools.ts | 1 + packages/mcp-server/tsconfig.build.json | 4 ++-- 4 files changed, 8 insertions(+), 27 deletions(-) create mode 100644 packages/mcp-server/src/tools.ts diff --git a/packages/mcp-server/build b/packages/mcp-server/build index e88aeb3..c1de500 100644 --- a/packages/mcp-server/build +++ b/packages/mcp-server/build @@ -24,16 +24,7 @@ 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 diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 1847635..b6bb18c 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -41,7 +41,7 @@ "ts-jest": "^29.1.0", "ts-morph": "^19.0.0", "ts-node": "^10.5.0", - "tsc-multi": "^1.1.0", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.3/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "typescript": "^4.8.2" }, @@ -51,23 +51,12 @@ }, "exports": { ".": { - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "types": "./dist/index.d.mts", + "require": "./dist/index.js", "default": "./dist/index.mjs" }, - "./*.mjs": { - "types": "./dist/*.d.ts", - "default": "./dist/*.mjs" - }, - "./*.js": { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" - }, + "./*.mjs": "./dist/*.mjs", + "./*.js": "./dist/*.js", "./*": { - "types": "./dist/*.d.ts", "require": "./dist/*.js", "default": "./dist/*.mjs" } diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts new file mode 100644 index 0000000..7e516de --- /dev/null +++ b/packages/mcp-server/src/tools.ts @@ -0,0 +1 @@ +export * from './tools/index'; diff --git a/packages/mcp-server/tsconfig.build.json b/packages/mcp-server/tsconfig.build.json index f1828ab..9cd534a 100644 --- a/packages/mcp-server/tsconfig.build.json +++ b/packages/mcp-server/tsconfig.build.json @@ -5,8 +5,8 @@ "compilerOptions": { "rootDir": "./dist/src", "paths": { - "isaacus-mcp/*": ["src/*"], - "isaacus-mcp": ["src/index.ts"] + "isaacus-mcp/*": ["dist/src/*"], + "isaacus-mcp": ["dist/src/index.ts"] }, "noEmit": false, "declaration": true, From 2bb3f29e9a41b834f17783cd26cce1bfb3e5c5ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 07:25:46 +0000 Subject: [PATCH 6/9] docs(mcp): improve MCP readme docs (#38) --- packages/mcp-server/README.md | 58 ++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index a43bc6a..5402582 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -4,16 +4,21 @@ It is generated with [Stainless](https://www.stainless.com/). ## Installation -### Via Claude Desktop +### Direct invocation -See [the user guide](https://modelcontextprotocol.io/quickstart/user) for setup. +You can run the MCP Server directly via `npx`: -Once it's set up, find your `claude_desktop_config.json` file: +```sh +export ISAACUS_API_KEY = "My API Key" +npx -y isaacus-mcp +``` -- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` -- Windows: `%APPDATA%\Claude\claude_desktop_config.json` +### Via MCP Client -Add the following value to your `mcpServers` section. Make sure to provide any necessary environment variables (like API keys) as well. +There is a partial list of existing clients at [modelcontextprotocol.io](https://modelcontextprotocol.io/clients). If you already +have a client, consult their documentation to install the MCP server. + +For clients with a configuration JSON, it might look something like this: ```json { @@ -41,16 +46,47 @@ You can filter by multiple aspects: - `--resource` includes all tools under a specific resource, and can have wildcards, e.g. `my.resource*` - `--operation` includes just read (get/list) or just write operations -See more information with `--help`: - -```sh -$ npx -y isaacus-mcp --help -``` +See more information with `--help`. All of these command-line options can be repeated, combined together, and have corresponding exclusion versions (e.g. `--no-tool`). Use `--list` to see the list of available tools, or see below. +## Importing the tools and server individually + +```js +// Import the server, generated endpoints, or the init function +import { server, endpoints, init } from "isaacus-mcp/server"; + +// import a specific tool +import createClassificationsUniversal from "isaacus-mcp/tools/classifications/universal/create-classifications-universal"; + +// initialize the server and all endpoints +init({ server, endpoints }); + +// manually start server +const transport = new StdioServerTransport(); +await server.connect(transport); + +// or initialize your own server with specific tools +const myServer = new McpServer(...); + +// define your own endpoint +const myCustomEndpoint = { + tool: { + name: 'my_custom_tool', + description: 'My custom tool', + inputSchema: zodToJsonSchema(z.object({ a_property: z.string() })), + }, + handler: async (client: client, args: any) => { + return { myResponse: 'Hello world!' }; + }) +}; + +// initialize the server with your custom endpoints +init({ server: myServer, endpoints: [createClassificationsUniversal, myCustomEndpoint] }); +``` + ## Available Tools The following tools are available in this MCP server. From 91f4483b3fef0e215ec59dfb10f557efce6fc3fe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:11:49 +0000 Subject: [PATCH 7/9] fix(mcp): remove debug logging (#39) --- packages/mcp-server/src/tools/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mcp-server/src/tools/index.ts b/packages/mcp-server/src/tools/index.ts index d06db43..6ffe441 100644 --- a/packages/mcp-server/src/tools/index.ts +++ b/packages/mcp-server/src/tools/index.ts @@ -59,7 +59,6 @@ function match({ type, value }: Filter, endpoint: Endpoint): boolean { case 'resource': { const regexStr = '^' + normalizeResource(value).replace(/\*/g, '.*') + '$'; const regex = new RegExp(regexStr); - console.error('regex is', regexStr); return regex.test(normalizeResource(endpoint.metadata.resource)); } case 'operation': From 5cc72f48910c6721213b17eb569d075b4d584b14 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:16:36 +0000 Subject: [PATCH 8/9] docs(mcp): update env vars in README (#40) --- packages/mcp-server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 5402582..808ce48 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -9,7 +9,7 @@ It is generated with [Stainless](https://www.stainless.com/). You can run the MCP Server directly via `npx`: ```sh -export ISAACUS_API_KEY = "My API Key" +export ISAACUS_API_KEY="My API Key" npx -y isaacus-mcp ``` From 1eeda5910861ed094cf78cc5161b16018e75a5eb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:17:03 +0000 Subject: [PATCH 9/9] release: 0.5.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 27 +++++++++++++++++++++++++++ package.json | 2 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/server.ts | 2 +- src/version.ts | 2 +- 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2537c1f..f1c1e58 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.4.0" + ".": "0.5.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index f7903df..da324ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.5.0 (2025-04-05) + +Full Changelog: [v0.4.0...v0.5.0](https://github.com/isaacus-dev/isaacus-typescript/compare/v0.4.0...v0.5.0) + +### Features + +* **mcp:** support end-user filtering of tools, resources, and tags ([#35](https://github.com/isaacus-dev/isaacus-typescript/issues/35)) ([c3c597f](https://github.com/isaacus-dev/isaacus-typescript/commit/c3c597f21cebbb6e14e9b87fa87d543697afeb7f)) + + +### Bug Fixes + +* **api:** improve type resolution when importing as a package ([#37](https://github.com/isaacus-dev/isaacus-typescript/issues/37)) ([db9ceca](https://github.com/isaacus-dev/isaacus-typescript/commit/db9ceca29582d073446736def120d22a2d75a9c9)) +* **client:** send `X-Stainless-Timeout` in seconds ([#34](https://github.com/isaacus-dev/isaacus-typescript/issues/34)) ([979c453](https://github.com/isaacus-dev/isaacus-typescript/commit/979c453293006d0f841901df8f3383f5785b00a6)) +* **mcp:** remove debug logging ([#39](https://github.com/isaacus-dev/isaacus-typescript/issues/39)) ([91f4483](https://github.com/isaacus-dev/isaacus-typescript/commit/91f4483b3fef0e215ec59dfb10f557efce6fc3fe)) + + +### Chores + +* **internal:** add aliases for Record and Array ([#36](https://github.com/isaacus-dev/isaacus-typescript/issues/36)) ([f2f08fa](https://github.com/isaacus-dev/isaacus-typescript/commit/f2f08fa2f6fcec5979d6c575dbd0347b8af77123)) +* **internal:** version bump ([#32](https://github.com/isaacus-dev/isaacus-typescript/issues/32)) ([e1992ce](https://github.com/isaacus-dev/isaacus-typescript/commit/e1992ce3ade04471fdd437d753af418ed80836f4)) + + +### Documentation + +* **mcp:** improve MCP readme docs ([#38](https://github.com/isaacus-dev/isaacus-typescript/issues/38)) ([2bb3f29](https://github.com/isaacus-dev/isaacus-typescript/commit/2bb3f29e9a41b834f17783cd26cce1bfb3e5c5ed)) +* **mcp:** update env vars in README ([#40](https://github.com/isaacus-dev/isaacus-typescript/issues/40)) ([5cc72f4](https://github.com/isaacus-dev/isaacus-typescript/commit/5cc72f48910c6721213b17eb569d075b4d584b14)) + ## 0.4.0 (2025-04-01) Full Changelog: [v0.3.1...v0.4.0](https://github.com/isaacus-dev/isaacus-typescript/compare/v0.3.1...v0.4.0) diff --git a/package.json b/package.json index 11c2f53..6b58254 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "isaacus", - "version": "0.4.0", + "version": "0.5.0", "description": "The official TypeScript library for the Isaacus API", "author": "Isaacus ", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index b6bb18c..95cf575 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "isaacus-mcp", - "version": "0.4.0", + "version": "0.5.0", "description": "The official MCP Server for the Isaacus API", "author": "Isaacus ", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 124c6bc..0474fda 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -11,7 +11,7 @@ export { endpoints } from './tools'; export const server = new McpServer( { name: 'isaacus_api', - version: '0.4.0', + version: '0.5.0', }, { capabilities: { diff --git a/src/version.ts b/src/version.ts index 4e7f788..1f5d158 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.4.0'; // x-release-please-version +export const VERSION = '0.5.0'; // x-release-please-version