From f62af7bdac18641887aa6e66bf47d003bb65a0dc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 19 Oct 2025 09:19:50 +0000 Subject: [PATCH 01/21] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0987f00..d088835 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 4 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-ee884a4336559147aacf9a927a540f21e9760f00d2d5588af00fa8a25e2707d9.yml -openapi_spec_hash: 2ba78bd360942c63a7d08dba791f00d2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-6705b8e0baa0e4aad69a1c04e9876b352e40e0e5caf21e87e7b2c355e70c4e66.yml +openapi_spec_hash: 87d3cc80f5ddc5275e8a47d35f1a484e config_hash: a85580968a69d8d6fadf96e5e2d6870e From 00ec4d8e97a35bc2de73666db6eede82bd618236 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 26 Oct 2025 09:03:41 +0000 Subject: [PATCH 02/21] docs(sdk): specify example params --- .stats.yml | 2 +- README.md | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index d088835..e26fc30 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 4 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-6705b8e0baa0e4aad69a1c04e9876b352e40e0e5caf21e87e7b2c355e70c4e66.yml openapi_spec_hash: 87d3cc80f5ddc5275e8a47d35f1a484e -config_hash: a85580968a69d8d6fadf96e5e2d6870e +config_hash: eb6af7379e9073b3ece2803bfcf65e68 diff --git a/README.md b/README.md index 5b38b5f..2899786 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ const client = new Isaacus({ const embeddingResponse = await client.embeddings.create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], + task: 'retrieval/query', }); console.log(embeddingResponse.embeddings); @@ -49,6 +50,7 @@ const client = new Isaacus({ const params: Isaacus.EmbeddingCreateParams = { model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], + task: 'retrieval/query', }; const embeddingResponse: Isaacus.EmbeddingResponse = await client.embeddings.create(params); ``` @@ -67,6 +69,7 @@ const embeddingResponse = await client.embeddings .create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], + task: 'retrieval/query', }) .catch(async (err) => { if (err instanceof Isaacus.APIError) { @@ -108,7 +111,7 @@ const client = new Isaacus({ }); // Or, configure per-request: -await client.embeddings.create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'] }, { +await client.embeddings.create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], task: 'retrieval/query' }, { maxRetries: 5, }); ``` @@ -125,7 +128,7 @@ const client = new Isaacus({ }); // Override per-request: -await client.embeddings.create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'] }, { +await client.embeddings.create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], task: 'retrieval/query' }, { timeout: 5 * 1000, }); ``` @@ -152,6 +155,7 @@ const response = await client.embeddings .create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], + task: 'retrieval/query', }) .asResponse(); console.log(response.headers.get('X-My-Header')); @@ -161,6 +165,7 @@ const { data: embeddingResponse, response: raw } = await client.embeddings .create({ model: 'kanon-2-embedder', texts: ['Are restraints of trade enforceable under English law?', 'What is a non-compete clause?'], + task: 'retrieval/query', }) .withResponse(); console.log(raw.headers.get('X-My-Header')); From fc022b2a940c8c3161082faa8c4e5457bdd53957 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 03:51:25 +0000 Subject: [PATCH 03/21] fix(mcpb): pin @anthropic-ai/mcpb version --- packages/mcp-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index bfd570a..8d5ba73 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -47,7 +47,7 @@ "mcp-server": "dist/index.js" }, "devDependencies": { - "@anthropic-ai/mcpb": "^1.1.0", + "@anthropic-ai/mcpb": "1.1.0", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jest": "^29.4.0", From cbeea365a8107e894cdeff0d0f7ed6ecfb1e2f2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 05:33:32 +0000 Subject: [PATCH 04/21] chore(internal): grammar fix (it's -> its) --- packages/mcp-server/src/code-tool.ts | 4 ++-- packages/mcp-server/src/dynamic-tools.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 61049d9..6dac81c 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -12,7 +12,7 @@ import { WorkerInput, WorkerError, WorkerSuccess } from './code-tool-types'; /** * A tool that runs code against a copy of the SDK. * - * Instead of exposing every endpoint as it's own tool, which uses up too many tokens for LLMs to use at once, + * Instead of exposing every endpoint as its own tool, which uses up too many tokens for LLMs to use at once, * we expose a single tool that can be used to search for endpoints by name, resource, operation, or tag, and then * a generic endpoint that can be used to invoke any endpoint with the provided arguments. * @@ -23,7 +23,7 @@ export async function codeTool(): Promise { const tool: Tool = { name: 'execute', description: - 'Runs Typescript code to interact with the API.\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client, and it will be run.\nDo not initialize a client, but instead use the client that you are given as a parameter.\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', + 'Runs TypeScript code to interact with the API.\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client, and it will be run.\nDo not initialize a client, but instead use the client that you are given as a parameter.\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', inputSchema: { type: 'object', properties: { code: { type: 'string' } } }, }; diff --git a/packages/mcp-server/src/dynamic-tools.ts b/packages/mcp-server/src/dynamic-tools.ts index 63f31f6..82e70f7 100644 --- a/packages/mcp-server/src/dynamic-tools.ts +++ b/packages/mcp-server/src/dynamic-tools.ts @@ -14,7 +14,7 @@ function zodToInputSchema(schema: z.ZodSchema) { /** * A list of tools that expose all the endpoints in the API dynamically. * - * Instead of exposing every endpoint as it's own tool, which uses up too many tokens for LLMs to use at once, + * Instead of exposing every endpoint as its own tool, which uses up too many tokens for LLMs to use at once, * we expose a single tool that can be used to search for endpoints by name, resource, operation, or tag, and then * a generic endpoint that can be used to invoke any endpoint with the provided arguments. * From bb4ba8a1485229cfc60ba38361520b2a9f65b7de Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 05:37:24 +0000 Subject: [PATCH 05/21] chore: use structured error when code execution tool errors --- packages/mcp-server/src/code-tool.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 6dac81c..eff72b8 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -3,7 +3,7 @@ import { dirname } from 'node:path'; import { pathToFileURL } from 'node:url'; import Isaacus, { ClientOptions } from 'isaacus'; -import { Endpoint, ContentBlock, Metadata } from './tools/types'; +import { ContentBlock, Endpoint, Metadata, ToolCallResult } from './tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; @@ -31,7 +31,7 @@ export async function codeTool(): Promise { const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker'); const { workerPath } = await import('./code-tool-paths.cjs'); - const handler = async (client: Isaacus, args: unknown) => { + const handler = async (client: Isaacus, args: unknown): Promise => { const baseURLHostname = new URL(client.baseURL).hostname; const { code } = args as { code: string }; @@ -97,7 +97,7 @@ export async function codeTool(): Promise { } satisfies WorkerInput); req.write(body, (err) => { - if (err !== null && err !== undefined) { + if (err != null) { reject(err); } }); @@ -108,12 +108,12 @@ export async function codeTool(): Promise { if (resp.status === 200) { const { result, logLines, errLines } = (await resp.json()) as WorkerSuccess; const returnOutput: ContentBlock | null = - result === null ? null - : result === undefined ? null - : { + result == null ? null : ( + { type: 'text', - text: typeof result === 'string' ? (result as string) : JSON.stringify(result), - }; + text: typeof result === 'string' ? result : JSON.stringify(result), + } + ); const logOutput: ContentBlock | null = logLines.length === 0 ? null @@ -133,10 +133,11 @@ export async function codeTool(): Promise { }; } else { const { message } = (await resp.json()) as WorkerError; - throw new Error(message); + return { + content: message == null ? [] : [{ type: 'text', text: message }], + isError: true, + }; } - } catch (e) { - throw e; } finally { worker.terminate(); } From 45f0ec4f7cd466097f623a9dde1a0b8e084f9fdc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 05:11:58 +0000 Subject: [PATCH 06/21] chore: mcp code tool explicit error message when missing a run function --- packages/mcp-server/package.json | 4 +- packages/mcp-server/src/code-tool-worker.ts | 47 +++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 8d5ba73..e6ed7c9 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -38,6 +38,7 @@ "express": "^5.1.0", "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz", "qs": "^6.14.0", + "typescript": "5.8.3", "yargs": "^17.7.2", "zod": "^3.25.20", "zod-to-json-schema": "^3.24.5", @@ -64,8 +65,7 @@ "ts-morph": "^19.0.0", "ts-node": "^10.5.0", "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", - "tsconfig-paths": "^4.0.0", - "typescript": "5.8.3" + "tsconfig-paths": "^4.0.0" }, "imports": { "isaacus-mcp": ".", diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index 032da42..fbd75a7 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -1,11 +1,58 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import util from 'node:util'; + +import ts from 'typescript'; + import { WorkerInput, WorkerSuccess, WorkerError } from './code-tool-types'; import { Isaacus } from 'isaacus'; +function getRunFunctionNode( + code: string, +): ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | null { + const sourceFile = ts.createSourceFile('code.ts', code, ts.ScriptTarget.Latest, true); + + for (const statement of sourceFile.statements) { + // Check for top-level function declarations + if (ts.isFunctionDeclaration(statement)) { + if (statement.name?.text === 'run') { + return statement; + } + } + + // Check for variable declarations: const run = () => {} or const run = function() {} + if (ts.isVariableStatement(statement)) { + for (const declaration of statement.declarationList.declarations) { + if (ts.isIdentifier(declaration.name) && declaration.name.text === 'run') { + // Check if it's initialized with a function + if ( + declaration.initializer && + (ts.isFunctionExpression(declaration.initializer) || ts.isArrowFunction(declaration.initializer)) + ) { + return declaration.initializer; + } + } + } + } + } + + return null; +} + const fetch = async (req: Request): Promise => { const { opts, code } = (await req.json()) as WorkerInput; + + const runFunctionNode = getRunFunctionNode(code); + if (!runFunctionNode) { + return Response.json( + { + message: + 'The code is missing a top-level `run` function. Write code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```', + } satisfies WorkerError, + { status: 400, statusText: 'Code execution error' }, + ); + } + const client = new Isaacus({ ...opts, }); From 579b63d0d5a3cde717ac41654d6e708673d0191c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 05:15:08 +0000 Subject: [PATCH 07/21] feat(mcp): enable optional code execution tool on http mcp servers --- packages/mcp-server/src/options.ts | 13 ++++++++++--- packages/mcp-server/tests/options.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index 4fe3b98..b6ff597 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -284,8 +284,10 @@ const coerceArray = (zodType: T) => ); const QueryOptions = z.object({ - tools: coerceArray(z.enum(['dynamic', 'all', 'docs'])).describe('Use dynamic tools or all tools'), - no_tools: coerceArray(z.enum(['dynamic', 'all', 'docs'])).describe('Do not use dynamic tools or all tools'), + tools: coerceArray(z.enum(['dynamic', 'all', 'code', 'docs'])).describe('Specify which MCP tools to use'), + no_tools: coerceArray(z.enum(['dynamic', 'all', 'code', 'docs'])).describe( + 'Specify which MCP tools to not use.', + ), tool: coerceArray(z.string()).describe('Include tools matching the specified names'), resource: coerceArray(z.string()).describe('Include tools matching the specified resources'), operation: coerceArray(z.enum(['read', 'write'])).describe( @@ -385,11 +387,16 @@ export function parseQueryOptions(defaultOptions: McpOptions, query: unknown): M : queryOptions.tools?.includes('docs') ? true : defaultOptions.includeDocsTools; + let codeTools: boolean | undefined = + queryOptions.no_tools && queryOptions.no_tools?.includes('code') ? false + : queryOptions.tools?.includes('code') && defaultOptions.includeCodeTools ? true + : defaultOptions.includeCodeTools; + return { client: queryOptions.client ?? defaultOptions.client, includeDynamicTools: dynamicTools, includeAllTools: allTools, - includeCodeTools: undefined, + includeCodeTools: codeTools, includeDocsTools: docsTools, filters, capabilities: clientCapabilities, diff --git a/packages/mcp-server/tests/options.test.ts b/packages/mcp-server/tests/options.test.ts index a8a5b81..4d9b60c 100644 --- a/packages/mcp-server/tests/options.test.ts +++ b/packages/mcp-server/tests/options.test.ts @@ -171,6 +171,7 @@ describe('parseQueryOptions', () => { const defaultOptions = { client: undefined, includeDynamicTools: undefined, + includeCodeTools: undefined, includeAllTools: undefined, filters: [], capabilities: { @@ -383,6 +384,27 @@ describe('parseQueryOptions', () => { { type: 'tool', op: 'exclude', value: 'exclude-tool' }, ]); }); + + it('code tools are enabled on http servers with default option set', () => { + const query = 'tools=code'; + const result = parseQueryOptions({ ...defaultOptions, includeCodeTools: true }, query); + + expect(result.includeCodeTools).toBe(true); + }); + + it('code tools are prevented on http servers when no default option set', () => { + const query = 'tools=code'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.includeCodeTools).toBe(undefined); + }); + + it('code tools are prevented on http servers when default option is explicitly false', () => { + const query = 'tools=code'; + const result = parseQueryOptions({ ...defaultOptions, includeCodeTools: false }, query); + + expect(result.includeCodeTools).toBe(false); + }); }); describe('parseEmbeddedJSON', () => { From 08e1eac13c3581f7c09150de36e8948e8ee94b6e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 05:25:58 +0000 Subject: [PATCH 08/21] chore(mcp): add friendlier MCP code tool errors on incorrect method invocations --- packages/mcp-server/package.json | 1 + packages/mcp-server/src/code-tool-worker.ts | 92 ++++++++++++++++++++- packages/mcp-server/src/code-tool.ts | 2 +- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index e6ed7c9..462fbc6 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -36,6 +36,7 @@ "@valtown/deno-http-worker": "^0.0.21", "cors": "^2.8.5", "express": "^5.1.0", + "fuse.js": "^7.1.0", "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz", "qs": "^6.14.0", "typescript": "5.8.3", diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index fbd75a7..179c5c3 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -2,6 +2,7 @@ import util from 'node:util'; +import Fuse from 'fuse.js'; import ts from 'typescript'; import { WorkerInput, WorkerSuccess, WorkerError } from './code-tool-types'; @@ -39,8 +40,97 @@ function getRunFunctionNode( return null; } +const fuse = new Fuse( + [ + 'client.embeddings.create', + 'client.classifications.universal.create', + 'client.rerankings.create', + 'client.extractions.qa.create', + ], + { threshold: 1, shouldSort: true }, +); + +function getMethodSuggestions(fullyQualifiedMethodName: string): string[] { + return fuse + .search(fullyQualifiedMethodName) + .map(({ item }) => item) + .slice(0, 5); +} + +const proxyToObj = new WeakMap(); +const objToProxy = new WeakMap(); + +type ClientProxyConfig = { + path: string[]; + isBelievedBad?: boolean; +}; + +function makeSdkProxy(obj: T, { path, isBelievedBad = false }: ClientProxyConfig): T { + let proxy: T = objToProxy.get(obj); + + if (!proxy) { + proxy = new Proxy(obj, { + get(target, prop, receiver) { + const propPath = [...path, String(prop)]; + const value = Reflect.get(target, prop, receiver); + + if (isBelievedBad || (!(prop in target) && value === undefined)) { + // If we're accessing a path that doesn't exist, it will probably eventually error. + // Let's proxy it and mark it bad so that we can control the error message. + // We proxy an empty class so that an invocation or construction attempt is possible. + return makeSdkProxy(class {}, { path: propPath, isBelievedBad: true }); + } + + if (value !== null && (typeof value === 'object' || typeof value === 'function')) { + return makeSdkProxy(value, { path: propPath, isBelievedBad }); + } + + return value; + }, + + apply(target, thisArg, args) { + if (isBelievedBad || typeof target !== 'function') { + const fullyQualifiedMethodName = path.join('.'); + const suggestions = getMethodSuggestions(fullyQualifiedMethodName); + throw new Error( + `${fullyQualifiedMethodName} is not a function. Did you mean: ${suggestions.join(', ')}`, + ); + } + + return Reflect.apply(target, proxyToObj.get(thisArg) ?? thisArg, args); + }, + + construct(target, args, newTarget) { + if (isBelievedBad || typeof target !== 'function') { + const fullyQualifiedMethodName = path.join('.'); + const suggestions = getMethodSuggestions(fullyQualifiedMethodName); + throw new Error( + `${fullyQualifiedMethodName} is not a constructor. Did you mean: ${suggestions.join(', ')}`, + ); + } + + return Reflect.construct(target, args, newTarget); + }, + }); + + objToProxy.set(obj, proxy); + proxyToObj.set(proxy, obj); + } + + return proxy; +} + const fetch = async (req: Request): Promise => { const { opts, code } = (await req.json()) as WorkerInput; + if (code == null) { + return Response.json( + { + message: + 'The code param is missing. Provide one containing a top-level `run` function. Write code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```', + } satisfies WorkerError, + { status: 400, statusText: 'Code execution error' }, + ); + } const runFunctionNode = getRunFunctionNode(code); if (!runFunctionNode) { @@ -73,7 +163,7 @@ const fetch = async (req: Request): Promise => { ${code} run_ = run; `); - const result = await run_(client); + const result = await run_(makeSdkProxy(client, { path: ['client'] })); return Response.json({ result, logLines, diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index eff72b8..7f2b41d 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -23,7 +23,7 @@ export async function codeTool(): Promise { const tool: Tool = { name: 'execute', description: - 'Runs TypeScript code to interact with the API.\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client, and it will be run.\nDo not initialize a client, but instead use the client that you are given as a parameter.\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', + 'Runs TypeScript code to interact with the API.\n\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client named "client", and it will be run.\nWrite code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```\n\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', inputSchema: { type: 'object', properties: { code: { type: 'string' } } }, }; From 51fc353019df5583769e087b93153d398019a639 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 05:26:29 +0000 Subject: [PATCH 09/21] chore(mcp): add line numbers to code tool errors --- packages/mcp-server/src/code-tool-worker.ts | 27 ++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index 179c5c3..a2bdbaf 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -120,6 +120,25 @@ function makeSdkProxy(obj: T, { path, isBelievedBad = false }: return proxy; } +function parseError(code: string, error: unknown): string | undefined { + if (!(error instanceof Error)) return; + const message = error.name ? `${error.name}: ${error.message}` : error.message; + try { + // Deno uses V8; the first ":LINE:COLUMN" is the top of stack. + const lineNumber = error.stack?.match(/:([0-9]+):[0-9]+/)?.[1]; + // -1 for the zero-based indexing + const line = + lineNumber && + code + .split('\n') + .at(parseInt(lineNumber, 10) - 1) + ?.trim(); + return line ? `${message}\n at line ${lineNumber}\n ${line}` : message; + } catch { + return message; + } +} + const fetch = async (req: Request): Promise => { const { opts, code } = (await req.json()) as WorkerInput; if (code == null) { @@ -159,10 +178,7 @@ const fetch = async (req: Request): Promise => { }; try { let run_ = async (client: any) => {}; - eval(` - ${code} - run_ = run; - `); + eval(`${code}\nrun_ = run;`); const result = await run_(makeSdkProxy(client, { path: ['client'] })); return Response.json({ result, @@ -170,10 +186,9 @@ const fetch = async (req: Request): Promise => { errLines, } satisfies WorkerSuccess); } catch (e) { - const message = e instanceof Error ? e.message : undefined; return Response.json( { - message, + message: parseError(code, e), } satisfies WorkerError, { status: 400, statusText: 'Code execution error' }, ); From afbeeb5b7b662f3144ef2571b7f33b1225daf5df Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 05:27:31 +0000 Subject: [PATCH 10/21] docs(mcp): add a README button for one-click add to Cursor --- packages/mcp-server/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 9c5476e..e2e4c03 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -34,6 +34,13 @@ For clients with a configuration JSON, it might look something like this: } ``` +### Cursor + + If you use Cursor, you can install the MCP server by using the button below. You will need to set your environment variables + in Cursor's `mcp.json`, which can be found in Cursor Settings > Tools & MCP > New MCP Server. + + [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=isaacus-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImlzYWFjdXMtbWNwIl0sImVudiI6eyJJU0FBQ1VTX0FQSV9LRVkiOiJTZXQgeW91ciBJU0FBQ1VTX0FQSV9LRVkgaGVyZS4ifX0) + ## Exposing endpoints to your MCP Client There are two ways to expose endpoints as tools in the MCP server: From 47ef37027fcc8731651c47ec72db684ae0662e54 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 05:29:28 +0000 Subject: [PATCH 11/21] chore(internal): codegen related update --- packages/mcp-server/README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index e2e4c03..11525a9 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -36,17 +36,18 @@ For clients with a configuration JSON, it might look something like this: ### Cursor - If you use Cursor, you can install the MCP server by using the button below. You will need to set your environment variables - in Cursor's `mcp.json`, which can be found in Cursor Settings > Tools & MCP > New MCP Server. +If you use Cursor, you can install the MCP server by using the button below. You will need to set your environment variables +in Cursor's `mcp.json`, which can be found in Cursor Settings > Tools & MCP > New MCP Server. - [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=isaacus-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImlzYWFjdXMtbWNwIl0sImVudiI6eyJJU0FBQ1VTX0FQSV9LRVkiOiJTZXQgeW91ciBJU0FBQ1VTX0FQSV9LRVkgaGVyZS4ifX0) +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=isaacus-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImlzYWFjdXMtbWNwIl0sImVudiI6eyJJU0FBQ1VTX0FQSV9LRVkiOiJTZXQgeW91ciBJU0FBQ1VTX0FQSV9LRVkgaGVyZS4ifX0) ## Exposing endpoints to your MCP Client -There are two ways to expose endpoints as tools in the MCP server: +There are three ways to expose endpoints as tools in the MCP server: 1. Exposing one tool per endpoint, and filtering as necessary 2. Exposing a set of tools to dynamically discover and invoke endpoints from the API +3. Exposing a docs search tool and a code execution tool, allowing the client to write code to be executed against the TypeScript client ### Filtering endpoints and tools @@ -81,6 +82,18 @@ All of these command-line options can be repeated, combined together, and have c Use `--list` to see the list of available tools, or see below. +### Code execution + +If you specify `--tools=code` to the MCP server, it will expose just two tools: + +- `search_docs` - Searches the API documentation and returns a list of markdown results +- `execute` - Runs code against the TypeScript client + +This allows the LLM to implement more complex logic by chaining together many API calls without loading +intermediary results into its context window. + +The code execution itself happens in a Deno sandbox that has network access only to the base URL for the API. + ### Specifying the MCP Client Different clients have varying abilities to handle arbitrary tools and schemas. From 6042a69bb085d6a0d21b982d448e540fccc6036f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 03:48:52 +0000 Subject: [PATCH 12/21] docs(mcp): add a README link to add server to VS Code or Claude Code --- packages/mcp-server/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 11525a9..6d54f7f 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -41,6 +41,22 @@ in Cursor's `mcp.json`, which can be found in Cursor Settings > Tools & MCP > Ne [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=isaacus-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImlzYWFjdXMtbWNwIl0sImVudiI6eyJJU0FBQ1VTX0FQSV9LRVkiOiJTZXQgeW91ciBJU0FBQ1VTX0FQSV9LRVkgaGVyZS4ifX0) +### VS Code + +If you use MCP, you can install the MCP server by clicking the link below. You will need to set your environment variables +in VS Code's `mcp.json`, which can be found via Command Palette > MCP: Open User Configuration. + +[Open VS Code](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22isaacus-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22isaacus-mcp%22%5D%2C%22env%22%3A%7B%22ISAACUS_API_KEY%22%3A%22Set%20your%20ISAACUS_API_KEY%20here.%22%7D%7D) + +### Claude Code + +If you use Claude Code, you can install the MCP server by running the command below in your terminal. You will need to set your +environment variables in Claude Code's `.claude.json`, which can be found in your home directory. + +``` +claude mcp add --transport stdio isaacus_api --env ISAACUS_API_KEY="Your ISAACUS_API_KEY here." -- npx -y isaacus-mcp +``` + ## Exposing endpoints to your MCP Client There are three ways to expose endpoints as tools in the MCP server: From d58f59bc6ac418f586698a45d535a68998454cbe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:40:43 +0000 Subject: [PATCH 13/21] chore(internal): codegen related update --- packages/mcp-server/src/code-tool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 7f2b41d..8737705 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -23,7 +23,7 @@ export async function codeTool(): Promise { const tool: Tool = { name: 'execute', description: - 'Runs TypeScript code to interact with the API.\n\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client named "client", and it will be run.\nWrite code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```\n\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', + 'Runs JavaScript code to interact with the API.\n\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client named "client", and it will be run.\nWrite code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```\n\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', inputSchema: { type: 'object', properties: { code: { type: 'string' } } }, }; From 39fab03e07a9d600b627d2146fbe231377a78577 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 05:16:32 +0000 Subject: [PATCH 14/21] chore(mcp): clarify http auth error --- packages/mcp-server/src/headers.ts | 4 +++- packages/mcp-server/src/http.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/headers.ts b/packages/mcp-server/src/headers.ts index c536d7b..2ac2859 100644 --- a/packages/mcp-server/src/headers.ts +++ b/packages/mcp-server/src/headers.ts @@ -11,7 +11,9 @@ export const parseAuthHeaders = (req: IncomingMessage): Partial = case 'Bearer': return { apiKey: req.headers.authorization.slice('Bearer '.length) }; default: - throw new Error(`Unsupported authorization scheme`); + throw new Error( + 'Unsupported authorization scheme. Expected the "Authorization" header to be a supported scheme (Bearer).', + ); } } diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index ec34ab4..8451700 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -46,12 +46,12 @@ const newServer = ({ }, mcpOptions, }); - } catch { + } catch (error) { res.status(401).json({ jsonrpc: '2.0', error: { code: -32000, - message: 'Unauthorized', + message: `Unauthorized: ${error instanceof Error ? error.message : error}`, }, }); return null; From 719a2bcb3ae336919ed9f63c04c06bf1eb5ad1fd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 04:49:34 +0000 Subject: [PATCH 15/21] fix(mcp): return tool execution error on jq failure --- packages/mcp-server/src/filtering.ts | 4 ++++ .../create-classifications-universal.ts | 17 ++++++++++++----- .../src/tools/embeddings/create-embeddings.ts | 13 ++++++++++--- .../extractions/qa/create-extractions-qa.ts | 13 ++++++++++--- .../src/tools/rerankings/create-rerankings.ts | 13 ++++++++++--- packages/mcp-server/src/tools/types.ts | 12 ++++++++++++ 6 files changed, 58 insertions(+), 14 deletions(-) diff --git a/packages/mcp-server/src/filtering.ts b/packages/mcp-server/src/filtering.ts index 1aa9a40..eaae0fc 100644 --- a/packages/mcp-server/src/filtering.ts +++ b/packages/mcp-server/src/filtering.ts @@ -12,3 +12,7 @@ export async function maybeFilter(jqFilter: unknown | undefined, response: any): async function jq(json: any, jqFilter: string) { return (await initJq).json(json, jqFilter); } + +export function isJqError(error: any): error is Error { + return error instanceof Error && 'stderr' in error; +} 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 fe35998..b46fb66 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,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'isaacus-mcp/filtering'; -import { Metadata, asTextContentResult } from 'isaacus-mcp/tools/types'; +import { isJqError, maybeFilter } from 'isaacus-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'isaacus-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Isaacus from 'isaacus'; @@ -89,9 +89,16 @@ export const tool: Tool = { export const handler = async (client: Isaacus, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult( - await maybeFilter(jq_filter, await client.classifications.universal.create(body)), - ); + try { + return asTextContentResult( + await maybeFilter(jq_filter, await client.classifications.universal.create(body)), + ); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/embeddings/create-embeddings.ts b/packages/mcp-server/src/tools/embeddings/create-embeddings.ts index 0c2bcb3..fd4f334 100644 --- a/packages/mcp-server/src/tools/embeddings/create-embeddings.ts +++ b/packages/mcp-server/src/tools/embeddings/create-embeddings.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'isaacus-mcp/filtering'; -import { Metadata, asTextContentResult } from 'isaacus-mcp/tools/types'; +import { isJqError, maybeFilter } from 'isaacus-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'isaacus-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Isaacus from 'isaacus'; @@ -75,7 +75,14 @@ export const tool: Tool = { export const handler = async (client: Isaacus, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.embeddings.create(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.embeddings.create(body))); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts b/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts index 3bb9f63..a73dd46 100644 --- a/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts +++ b/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'isaacus-mcp/filtering'; -import { Metadata, asTextContentResult } from 'isaacus-mcp/tools/types'; +import { isJqError, maybeFilter } from 'isaacus-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'isaacus-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Isaacus from 'isaacus'; @@ -88,7 +88,14 @@ export const tool: Tool = { export const handler = async (client: Isaacus, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.extractions.qa.create(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.extractions.qa.create(body))); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/rerankings/create-rerankings.ts b/packages/mcp-server/src/tools/rerankings/create-rerankings.ts index 3a0ee04..153e481 100644 --- a/packages/mcp-server/src/tools/rerankings/create-rerankings.ts +++ b/packages/mcp-server/src/tools/rerankings/create-rerankings.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'isaacus-mcp/filtering'; -import { Metadata, asTextContentResult } from 'isaacus-mcp/tools/types'; +import { isJqError, maybeFilter } from 'isaacus-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'isaacus-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Isaacus from 'isaacus'; @@ -93,7 +93,14 @@ export const tool: Tool = { export const handler = async (client: Isaacus, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.rerankings.create(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.rerankings.create(body))); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/types.ts b/packages/mcp-server/src/tools/types.ts index 5b7c5ba..4c5eda4 100644 --- a/packages/mcp-server/src/tools/types.ts +++ b/packages/mcp-server/src/tools/types.ts @@ -87,6 +87,18 @@ export async function asBinaryContentResult(response: Response): Promise Date: Thu, 13 Nov 2025 04:51:07 +0000 Subject: [PATCH 16/21] chore(mcp): upgrade jq-web --- packages/mcp-server/package.json | 2 +- packages/mcp-server/yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 462fbc6..2d2fc47 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -37,7 +37,7 @@ "cors": "^2.8.5", "express": "^5.1.0", "fuse.js": "^7.1.0", - "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz", + "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz", "qs": "^6.14.0", "typescript": "5.8.3", "yargs": "^17.7.2", diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock index 966d057..2bb21c6 100644 --- a/packages/mcp-server/yarn.lock +++ b/packages/mcp-server/yarn.lock @@ -2494,9 +2494,9 @@ jest@^29.4.0: import-local "^3.0.2" jest-cli "^29.7.0" -"jq-web@https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz": - version "0.8.6" - resolved "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz#14d0e126987736e82e964d675c3838b5944faa6f" +"jq-web@https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz": + version "0.8.8" + resolved "https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz#7849ef64bdfc28f70cbfc9888f886860e96da10d" js-tokens@^4.0.0: version "4.0.0" From a4a3478307e75a7147f2bc3137677327abbeae35 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 05:24:16 +0000 Subject: [PATCH 17/21] feat(mcp): add detail field to docs search tool --- packages/mcp-server/src/docs-search-tool.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/docs-search-tool.ts b/packages/mcp-server/src/docs-search-tool.ts index e28e583..b090539 100644 --- a/packages/mcp-server/src/docs-search-tool.ts +++ b/packages/mcp-server/src/docs-search-tool.ts @@ -13,8 +13,7 @@ export const metadata: Metadata = { export const tool: Tool = { name: 'search_docs', - description: - 'Search for documentation for how to use the client to interact with the API.\nThe tool will return an array of Markdown-formatted documentation pages.', + description: 'Search for documentation for how to use the client to interact with the API.', inputSchema: { type: 'object', properties: { @@ -25,7 +24,12 @@ export const tool: Tool = { language: { type: 'string', description: 'The language for the SDK to search for.', - enum: ['http', 'python', 'go', 'typescript', 'terraform', 'ruby', 'java', 'kotlin'], + enum: ['http', 'python', 'go', 'typescript', 'javascript', 'terraform', 'ruby', 'java', 'kotlin'], + }, + detail: { + type: 'string', + description: 'The amount of detail to return.', + enum: ['default', 'verbose'], }, }, required: ['query', 'language'], From 73a6ad5b2c1a9cd42c061401c47abc51db8510d4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 06:16:24 +0000 Subject: [PATCH 18/21] chore(client): fix logger property type --- src/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index 4ac2a40..d00abf4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -117,7 +117,7 @@ export class Isaacus { baseURL: string; maxRetries: number; timeout: number; - logger: Logger | undefined; + logger: Logger; logLevel: LogLevel | undefined; fetchOptions: MergedRequestInit | undefined; From 5ae2a33f2dfe5e3f1437e48045c15be34ee9c4e5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 06:23:13 +0000 Subject: [PATCH 19/21] fix(mcp): return tool execution error on api error --- .../universal/create-classifications-universal.ts | 2 +- packages/mcp-server/src/tools/embeddings/create-embeddings.ts | 2 +- .../src/tools/extractions/qa/create-extractions-qa.ts | 2 +- packages/mcp-server/src/tools/rerankings/create-rerankings.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 b46fb66..17575ef 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 @@ -94,7 +94,7 @@ export const handler = async (client: Isaacus, args: Record | u await maybeFilter(jq_filter, await client.classifications.universal.create(body)), ); } catch (error) { - if (isJqError(error)) { + if (error instanceof Isaacus.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/embeddings/create-embeddings.ts b/packages/mcp-server/src/tools/embeddings/create-embeddings.ts index fd4f334..f29f478 100644 --- a/packages/mcp-server/src/tools/embeddings/create-embeddings.ts +++ b/packages/mcp-server/src/tools/embeddings/create-embeddings.ts @@ -78,7 +78,7 @@ export const handler = async (client: Isaacus, args: Record | u try { return asTextContentResult(await maybeFilter(jq_filter, await client.embeddings.create(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Isaacus.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts b/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts index a73dd46..ba67746 100644 --- a/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts +++ b/packages/mcp-server/src/tools/extractions/qa/create-extractions-qa.ts @@ -91,7 +91,7 @@ export const handler = async (client: Isaacus, args: Record | u try { return asTextContentResult(await maybeFilter(jq_filter, await client.extractions.qa.create(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Isaacus.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/rerankings/create-rerankings.ts b/packages/mcp-server/src/tools/rerankings/create-rerankings.ts index 153e481..703695e 100644 --- a/packages/mcp-server/src/tools/rerankings/create-rerankings.ts +++ b/packages/mcp-server/src/tools/rerankings/create-rerankings.ts @@ -96,7 +96,7 @@ export const handler = async (client: Isaacus, args: Record | u try { return asTextContentResult(await maybeFilter(jq_filter, await client.rerankings.create(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Isaacus.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; From f8cc9b0afc5365abe559f07c85883895ee2f4900 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 06:24:53 +0000 Subject: [PATCH 20/21] feat(mcp): return logs on code tool errors --- packages/mcp-server/src/code-tool-types.ts | 6 ++++- packages/mcp-server/src/code-tool-worker.ts | 6 +++++ packages/mcp-server/src/code-tool.ts | 25 +++++++++++++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/code-tool-types.ts b/packages/mcp-server/src/code-tool-types.ts index c7d8173..65ef304 100644 --- a/packages/mcp-server/src/code-tool-types.ts +++ b/packages/mcp-server/src/code-tool-types.ts @@ -11,4 +11,8 @@ export type WorkerSuccess = { logLines: string[]; errLines: string[]; }; -export type WorkerError = { message: string | undefined }; +export type WorkerError = { + message: string | undefined; + logLines: string[]; + errLines: string[]; +}; diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index a2bdbaf..a19ef41 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -146,6 +146,8 @@ const fetch = async (req: Request): Promise => { { message: 'The code param is missing. Provide one containing a top-level `run` function. Write code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```', + logLines: [], + errLines: [], } satisfies WorkerError, { status: 400, statusText: 'Code execution error' }, ); @@ -157,6 +159,8 @@ const fetch = async (req: Request): Promise => { { message: 'The code is missing a top-level `run` function. Write code within this template:\n\n```\nasync function run(client) {\n // Fill this out\n}\n```', + logLines: [], + errLines: [], } satisfies WorkerError, { status: 400, statusText: 'Code execution error' }, ); @@ -189,6 +193,8 @@ const fetch = async (req: Request): Promise => { return Response.json( { message: parseError(code, e), + logLines, + errLines, } satisfies WorkerError, { status: 400, statusText: 'Code execution error' }, ); diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 8737705..958a9ed 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -132,9 +132,30 @@ export async function codeTool(): Promise { content: [returnOutput, logOutput, errOutput].filter((block) => block !== null), }; } else { - const { message } = (await resp.json()) as WorkerError; + const { message, logLines, errLines } = (await resp.json()) as WorkerError; + const messageOutput: ContentBlock | null = + message == null ? null : ( + { + type: 'text', + text: message, + } + ); + const logOutput: ContentBlock | null = + logLines.length === 0 ? + null + : { + type: 'text', + text: logLines.join('\n'), + }; + const errOutput: ContentBlock | null = + errLines.length === 0 ? + null + : { + type: 'text', + text: 'Error output:\n' + errLines.join('\n'), + }; return { - content: message == null ? [] : [{ type: 'text', text: message }], + content: [messageOutput, logOutput, errOutput].filter((block) => block !== null), isError: true, }; } From 492b05aee49542b2cad8f26c34f2e1f2d607eaaa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 06:25:11 +0000 Subject: [PATCH 21/21] release: 0.12.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 38 +++++++++++++++++++++++++++++++ package.json | 2 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/server.ts | 2 +- src/version.ts | 2 +- 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ddfa3e3..8032c17 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.11.1" + ".": "0.12.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 03155e3..9c6c27d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## 0.12.0 (2025-12-02) + +Full Changelog: [v0.11.1...v0.12.0](https://github.com/isaacus-dev/isaacus-typescript/compare/v0.11.1...v0.12.0) + +### Features + +* **mcp:** add detail field to docs search tool ([a4a3478](https://github.com/isaacus-dev/isaacus-typescript/commit/a4a3478307e75a7147f2bc3137677327abbeae35)) +* **mcp:** enable optional code execution tool on http mcp servers ([579b63d](https://github.com/isaacus-dev/isaacus-typescript/commit/579b63d0d5a3cde717ac41654d6e708673d0191c)) +* **mcp:** return logs on code tool errors ([f8cc9b0](https://github.com/isaacus-dev/isaacus-typescript/commit/f8cc9b0afc5365abe559f07c85883895ee2f4900)) + + +### Bug Fixes + +* **mcpb:** pin @anthropic-ai/mcpb version ([fc022b2](https://github.com/isaacus-dev/isaacus-typescript/commit/fc022b2a940c8c3161082faa8c4e5457bdd53957)) +* **mcp:** return tool execution error on api error ([5ae2a33](https://github.com/isaacus-dev/isaacus-typescript/commit/5ae2a33f2dfe5e3f1437e48045c15be34ee9c4e5)) +* **mcp:** return tool execution error on jq failure ([719a2bc](https://github.com/isaacus-dev/isaacus-typescript/commit/719a2bcb3ae336919ed9f63c04c06bf1eb5ad1fd)) + + +### Chores + +* **client:** fix logger property type ([73a6ad5](https://github.com/isaacus-dev/isaacus-typescript/commit/73a6ad5b2c1a9cd42c061401c47abc51db8510d4)) +* **internal:** codegen related update ([d58f59b](https://github.com/isaacus-dev/isaacus-typescript/commit/d58f59bc6ac418f586698a45d535a68998454cbe)) +* **internal:** codegen related update ([47ef370](https://github.com/isaacus-dev/isaacus-typescript/commit/47ef37027fcc8731651c47ec72db684ae0662e54)) +* **internal:** grammar fix (it's -> its) ([cbeea36](https://github.com/isaacus-dev/isaacus-typescript/commit/cbeea365a8107e894cdeff0d0f7ed6ecfb1e2f2f)) +* mcp code tool explicit error message when missing a run function ([45f0ec4](https://github.com/isaacus-dev/isaacus-typescript/commit/45f0ec4f7cd466097f623a9dde1a0b8e084f9fdc)) +* **mcp:** add friendlier MCP code tool errors on incorrect method invocations ([08e1eac](https://github.com/isaacus-dev/isaacus-typescript/commit/08e1eac13c3581f7c09150de36e8948e8ee94b6e)) +* **mcp:** add line numbers to code tool errors ([51fc353](https://github.com/isaacus-dev/isaacus-typescript/commit/51fc353019df5583769e087b93153d398019a639)) +* **mcp:** clarify http auth error ([39fab03](https://github.com/isaacus-dev/isaacus-typescript/commit/39fab03e07a9d600b627d2146fbe231377a78577)) +* **mcp:** upgrade jq-web ([5f375e8](https://github.com/isaacus-dev/isaacus-typescript/commit/5f375e8eb4353218198c5edd82404e00066a1fff)) +* use structured error when code execution tool errors ([bb4ba8a](https://github.com/isaacus-dev/isaacus-typescript/commit/bb4ba8a1485229cfc60ba38361520b2a9f65b7de)) + + +### Documentation + +* **mcp:** add a README button for one-click add to Cursor ([afbeeb5](https://github.com/isaacus-dev/isaacus-typescript/commit/afbeeb5b7b662f3144ef2571b7f33b1225daf5df)) +* **mcp:** add a README link to add server to VS Code or Claude Code ([6042a69](https://github.com/isaacus-dev/isaacus-typescript/commit/6042a69bb085d6a0d21b982d448e540fccc6036f)) +* **sdk:** specify example params ([00ec4d8](https://github.com/isaacus-dev/isaacus-typescript/commit/00ec4d8e97a35bc2de73666db6eede82bd618236)) + ## 0.11.1 (2025-10-14) Full Changelog: [v0.11.0...v0.11.1](https://github.com/isaacus-dev/isaacus-typescript/compare/v0.11.0...v0.11.1) diff --git a/package.json b/package.json index a9d0106..b5a7768 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "isaacus", - "version": "0.11.1", + "version": "0.12.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 2d2fc47..97196ed 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "isaacus-mcp", - "version": "0.11.1", + "version": "0.12.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 bef0e7d..b073490 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -34,7 +34,7 @@ export const newMcpServer = () => new McpServer( { name: 'isaacus_api', - version: '0.11.1', + version: '0.12.0', }, { capabilities: { tools: {}, logging: {} } }, ); diff --git a/src/version.ts b/src/version.ts index 945825f..ce6b899 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.11.1'; // x-release-please-version +export const VERSION = '0.12.0'; // x-release-please-version