From 62283aed01b3bb3bc408946faeb1f62c9ea9864d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:18:25 +0000 Subject: [PATCH 01/31] chore: update SDK settings --- .github/workflows/release-doctor.yml | 21 ++++++++ .release-please-manifest.json | 3 ++ .stats.yml | 2 +- CONTRIBUTING.md | 6 +-- README.md | 4 +- bin/check-release-environment | 18 +++++++ package.json | 2 +- packages/mcp-server/README.md | 10 ++-- packages/mcp-server/manifest.json | 4 +- packages/mcp-server/package.json | 4 +- release-please-config.json | 73 ++++++++++++++++++++++++++++ src/version.ts | 2 +- 12 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/release-doctor.yml create mode 100644 .release-please-manifest.json create mode 100644 bin/check-release-environment create mode 100644 release-please-config.json diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..5bd88f9 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,21 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'AlexK420/mcpdata' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..b985ff6 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.1" +} diff --git a/.stats.yml b/.stats.yml index 4e5bf8f..9ccc1c6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 40 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/alex-org%2Fdatanewton-9a672e96630ea01df8f98685d606644cfbb1b99058df75c70cee8f222eed7204.yml openapi_spec_hash: 6c1bfc2e1523a275c2f8246e2004d30c -config_hash: 2aea0249f23849f8c3cee4c82ad3453f +config_hash: f46f6d0fb04fa34b3c440e5fac06807d diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3181889..71b8dcc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,15 +42,15 @@ If you’d like to use the repository from source, you can either install from g To install via git: ```sh -$ npm install git+ssh://git@github.com:stainless-sdks/datanewton-typescript.git +$ npm install git+ssh://git@github.com:AlexK420/mcpdata.git ``` Alternatively, to link a local copy of the repo: ```sh # Clone -$ git clone https://www.github.com/stainless-sdks/datanewton-typescript -$ cd datanewton-typescript +$ git clone https://www.github.com/AlexK420/mcpdata +$ cd mcpdata # With yarn $ yarn link diff --git a/README.md b/README.md index 68034fc..d90c621 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It is generated with [Stainless](https://www.stainless.com/). ## Installation ```sh -npm install git+ssh://git@github.com:stainless-sdks/datanewton-typescript.git +npm install git+ssh://git@github.com:AlexK420/mcpdata.git ``` > [!NOTE] @@ -338,7 +338,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/datanewton-typescript/issues) with questions, bugs, or suggestions. +We are keen for your feedback; please open an [issue](https://www.github.com/AlexK420/mcpdata/issues) with questions, bugs, or suggestions. ## Requirements diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..6b43775 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +errors=() + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" + diff --git a/package.json b/package.json index fffcadf..3678aa8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "types": "dist/index.d.ts", "main": "dist/index.js", "type": "commonjs", - "repository": "github:stainless-sdks/datanewton-typescript", + "repository": "github:AlexK420/mcpdata", "license": "Apache-2.0", "packageManager": "yarn@1.22.22", "files": [ diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index d48b660..b32b827 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -9,8 +9,8 @@ It is generated with [Stainless](https://www.stainless.com/). Because it's not published yet, clone the repo and build it: ```sh -git clone git@github.com:stainless-sdks/datanewton-typescript.git -cd datanewton-typescript +git clone git@github.com:AlexK420/mcpdata.git +cd mcpdata ./scripts/bootstrap ./scripts/build ``` @@ -40,11 +40,7 @@ For clients with a configuration JSON, it might look something like this: "mcpServers": { "datanewton_api": { "command": "node", - "args": [ - "/path/to/local/datanewton-typescript/packages/mcp-server", - "--client=claude", - "--tools=dynamic" - ], + "args": ["/path/to/local/mcpdata/packages/mcp-server", "--client=claude", "--tools=dynamic"], "env": { "DATANEWTON_API_KEY": "My API Key" } diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json index 647c6ae..fcb0979 100644 --- a/packages/mcp-server/manifest.json +++ b/packages/mcp-server/manifest.json @@ -8,9 +8,9 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/stainless-sdks/datanewton-typescript.git" + "url": "git+https://github.com/AlexK420/mcpdata.git" }, - "homepage": "https://github.com/stainless-sdks/datanewton-typescript/tree/main/packages/mcp-server#readme", + "homepage": "https://github.com/AlexK420/mcpdata/tree/main/packages/mcp-server#readme", "server": { "type": "node", "entry_point": "index.js", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 99dea7a..738b6be 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -8,10 +8,10 @@ "type": "commonjs", "repository": { "type": "git", - "url": "git+https://github.com/stainless-sdks/datanewton-typescript.git", + "url": "git+https://github.com/AlexK420/mcpdata.git", "directory": "packages/mcp-server" }, - "homepage": "https://github.com/stainless-sdks/datanewton-typescript/tree/main/packages/mcp-server#readme", + "homepage": "https://github.com/AlexK420/mcpdata/tree/main/packages/mcp-server#readme", "license": "Apache-2.0", "packageManager": "yarn@1.22.22", "private": false, diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..b190980 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,73 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "node", + "extra-files": [ + "src/version.ts", + "README.md", + "packages/mcp-server/yarn.lock", + { + "type": "json", + "path": "packages/mcp-server/package.json", + "jsonpath": "$.version" + } + ] +} diff --git a/src/version.ts b/src/version.ts index ecebcdd..d74dce8 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.0.1'; +export const VERSION = '0.0.1'; // x-release-please-version From 990298fcc57b030ae1f78d7ba385ec5d9624f91d 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:53:33 +0000 Subject: [PATCH 02/31] 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 738b6be..907ac7a 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 4e37db84dd2fd317a15243277ebb9c90d165bb78 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:05 +0000 Subject: [PATCH 03/31] 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 e988339..652c931 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 f501d94..368deaf 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 9b50e982056247d81e175e5c2a00d14c1e4b1acd 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:40:44 +0000 Subject: [PATCH 04/31] 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 652c931..70a6c3f 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 Datanewton, { ClientOptions } from 'datanewton'; -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: Datanewton, args: unknown) => { + const handler = async (client: Datanewton, 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 f3e1d63b1e17ea6b6daca88b19afdcf08d46f803 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:30 +0000 Subject: [PATCH 05/31] 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 907ac7a..b003706 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": { "datanewton-mcp": ".", diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index bcbb447..3e88d45 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 { Datanewton } from 'datanewton'; +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 Datanewton({ ...opts, }); From 72af577677c3997c6b9deb3cbe55f0783f12dc6a 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:18:11 +0000 Subject: [PATCH 06/31] 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 5ead6bc9cca93e80715d0278e41c29a6939e6e4b 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:28:24 +0000 Subject: [PATCH 07/31] 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 | 128 +++++++++++++++++++- packages/mcp-server/src/code-tool.ts | 2 +- 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index b003706..28d8007 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 3e88d45..b0e41fd 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,133 @@ function getRunFunctionNode( return null; } +const fuse = new Fuse( + [ + 'client.taxpayerStatuses.create', + 'client.suggestions.create', + 'client.ogrnsByAddress.create', + 'client.leases.create', + 'client.filtersPreview.create', + 'client.batchChanges.create', + 'client.batchCards.create', + 'client.batchCardsByFilters.create', + 'client.arbitration.getCourts', + 'client.arbitration.getDisputeCategories', + 'client.arbitration.getDocumentTypes', + 'client.arbitration.batchCases.create', + 'client.taxInfo.retrieve', + 'client.sroMembership.retrieve', + 'client.risks.list', + 'client.paidTaxes.list', + 'client.okpdList.list', + 'client.nkoReestr.retrieve', + 'client.links.list', + 'client.governmentContracts.list', + 'client.governmentContractsStat.retrieve', + 'client.finance.retrieve', + 'client.regions.list', + 'client.dictionary.listLeaseClassifier', + 'client.dictionary.listLicenses', + 'client.dictionary.listOkveds', + 'client.dictionary.listRegions', + 'client.dictionary.procurement.listOkpd2', + 'client.dictionary.arbitration.listCourts', + 'client.dictionary.arbitration.listDisputeCategories', + 'client.dictionary.arbitration.listDocumentTypes', + 'client.procurement.retrieveOkpd2', + 'client.okveds.list', + 'client.licenses.list', + 'client.leaseClassifier.retrieve', + 'client.counterparty.retrieve', + 'client.corporateActions.list', + 'client.blockedBankAccounts.list', + 'client.bankruptcy.retrieve', + 'client.arbitrationCases.list', + ], + { 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 +199,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 70a6c3f..20093af 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 3a5a56c4595482567f3596f34e7c620ee4de0749 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:28:56 +0000 Subject: [PATCH 08/31] 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 b0e41fd..614b323 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -156,6 +156,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) { @@ -195,10 +214,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, @@ -206,10 +222,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 2bcff4090eacb07af1a4de4679726ec05020bf89 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:32:10 +0000 Subject: [PATCH 09/31] chore(internal): codegen related update --- packages/mcp-server/README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index b32b827..cbb5961 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -51,10 +51,11 @@ For clients with a configuration JSON, it might look something like this: ## 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 @@ -89,6 +90,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 35c5956900f002543fc8782f748cd0fc5aa54594 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:54:16 +0000 Subject: [PATCH 10/31] 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 20093af..9a2d7b2 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 e26147edf9215e2524946a442b5dfc6d4a41cb54 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:21:07 +0000 Subject: [PATCH 11/31] 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 c748993..4937a54 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 fe28a35e4398286ca2a262969fff5d76573e425a 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:52:52 +0000 Subject: [PATCH 12/31] fix(mcp): return tool execution error on jq failure --- packages/mcp-server/src/filtering.ts | 4 ++++ .../tools/arbitration/get-courts-arbitration.ts | 13 ++++++++++--- .../get-dispute-categories-arbitration.ts | 13 ++++++++++--- .../get-document-types-arbitration.ts | 13 ++++++++++--- .../src/tools/bankruptcy/retrieve-bankruptcy.ts | 13 ++++++++++--- .../list-blocked-bank-accounts.ts | 13 ++++++++++--- .../list-courts-dictionary-arbitration.ts | 15 ++++++++++++--- ...dispute-categories-dictionary-arbitration.ts | 17 ++++++++++++----- ...ist-document-types-dictionary-arbitration.ts | 17 ++++++++++++----- .../list-lease-classifier-dictionary.ts | 13 ++++++++++--- .../dictionary/list-licenses-dictionary.ts | 13 ++++++++++--- .../tools/dictionary/list-okveds-dictionary.ts | 13 ++++++++++--- .../tools/dictionary/list-regions-dictionary.ts | 13 ++++++++++--- .../list-okpd2-dictionary-procurement.ts | 13 ++++++++++--- .../retrieve-government-contracts-stat.ts | 17 ++++++++++++----- .../retrieve-lease-classifier.ts | 13 ++++++++++--- .../src/tools/licenses/list-licenses.ts | 13 ++++++++++--- .../mcp-server/src/tools/links/list-links.ts | 13 ++++++++++--- .../src/tools/nko-reestr/retrieve-nko-reestr.ts | 13 ++++++++++--- .../ogrns-by-address/create-ogrns-by-address.ts | 13 ++++++++++--- .../src/tools/okpd-list/list-okpd-list.ts | 13 ++++++++++--- .../mcp-server/src/tools/okveds/list-okveds.ts | 13 ++++++++++--- .../src/tools/paid-taxes/list-paid-taxes.ts | 13 ++++++++++--- .../procurement/retrieve-okpd2-procurement.ts | 13 ++++++++++--- .../src/tools/regions/list-regions.ts | 13 ++++++++++--- .../mcp-server/src/tools/risks/list-risks.ts | 13 ++++++++++--- .../sro-membership/retrieve-sro-membership.ts | 13 ++++++++++--- .../src/tools/suggestions/create-suggestions.ts | 13 ++++++++++--- .../src/tools/tax-info/retrieve-tax-info.ts | 13 ++++++++++--- .../create-taxpayer-statuses.ts | 13 ++++++++++--- packages/mcp-server/src/tools/types.ts | 12 ++++++++++++ 31 files changed, 314 insertions(+), 93 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/arbitration/get-courts-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts index 930918f..d03be5e 100644 --- a/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts +++ b/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getCourts())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getCourts())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts index bc7c5a1..b24f517 100644 --- a/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts +++ b/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDisputeCategories())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDisputeCategories())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts index 91e1ec8..ca5160f 100644 --- a/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts +++ b/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDocumentTypes())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDocumentTypes())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts b/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts index db9a2e9..9cd7668 100644 --- a/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts +++ b/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -58,7 +58,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.bankruptcy.retrieve(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.bankruptcy.retrieve(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/blocked-bank-accounts/list-blocked-bank-accounts.ts b/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts index f816ec3..0332e96 100644 --- a/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts +++ b/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -50,7 +50,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.blockedBankAccounts.list(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.blockedBankAccounts.list(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/dictionary/arbitration/list-courts-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts index bddf04d..3ba5189 100644 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts +++ b/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,16 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.arbitration.listCourts())); + try { + return asTextContentResult( + await maybeFilter(jq_filter, await client.dictionary.arbitration.listCourts()), + ); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts index 6c4dd38..4f440b3 100644 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts +++ b/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,9 +38,16 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult( - await maybeFilter(jq_filter, await client.dictionary.arbitration.listDisputeCategories()), - ); + try { + return asTextContentResult( + await maybeFilter(jq_filter, await client.dictionary.arbitration.listDisputeCategories()), + ); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts index 500abb4..bbeac02 100644 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts +++ b/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,9 +38,16 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult( - await maybeFilter(jq_filter, await client.dictionary.arbitration.listDocumentTypes()), - ); + try { + return asTextContentResult( + await maybeFilter(jq_filter, await client.dictionary.arbitration.listDocumentTypes()), + ); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts index 766ac12..9960a7e 100644 --- a/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLeaseClassifier())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLeaseClassifier())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts index 4886ca0..9cb958c 100644 --- a/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLicenses())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLicenses())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts index c492d2e..a953da5 100644 --- a/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listOkveds())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listOkveds())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts index 4229be2..54b8f64 100644 --- a/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listRegions())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listRegions())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts b/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts index 18c35c9..cf5c5bd 100644 --- a/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts +++ b/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.procurement.listOkpd2())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.procurement.listOkpd2())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts b/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts index 7664d29..467092f 100644 --- a/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts +++ b/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -66,9 +66,16 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult( - await maybeFilter(jq_filter, await client.governmentContractsStat.retrieve(body)), - ); + try { + return asTextContentResult( + await maybeFilter(jq_filter, await client.governmentContractsStat.retrieve(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/lease-classifier/retrieve-lease-classifier.ts b/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts index 3c433ef..2feaef0 100644 --- a/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts +++ b/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.leaseClassifier.retrieve())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.leaseClassifier.retrieve())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/licenses/list-licenses.ts b/packages/mcp-server/src/tools/licenses/list-licenses.ts index ab29ee8..cd3f05f 100644 --- a/packages/mcp-server/src/tools/licenses/list-licenses.ts +++ b/packages/mcp-server/src/tools/licenses/list-licenses.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.licenses.list())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.licenses.list())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/links/list-links.ts b/packages/mcp-server/src/tools/links/list-links.ts index f7ae2f6..8dcf96b 100644 --- a/packages/mcp-server/src/tools/links/list-links.ts +++ b/packages/mcp-server/src/tools/links/list-links.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -51,7 +51,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.links.list(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.links.list(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/nko-reestr/retrieve-nko-reestr.ts b/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts index 977586b..fd5bdfe 100644 --- a/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts +++ b/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -50,7 +50,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.nkoReestr.retrieve(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.nkoReestr.retrieve(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/ogrns-by-address/create-ogrns-by-address.ts b/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts index 21be931..52cc03c 100644 --- a/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts +++ b/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -52,7 +52,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.ogrnsByAddress.create(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.ogrnsByAddress.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/okpd-list/list-okpd-list.ts b/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts index 11abe54..11bcd9d 100644 --- a/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts +++ b/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -50,7 +50,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.okpdList.list(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.okpdList.list(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/okveds/list-okveds.ts b/packages/mcp-server/src/tools/okveds/list-okveds.ts index 46405f7..d3bd304 100644 --- a/packages/mcp-server/src/tools/okveds/list-okveds.ts +++ b/packages/mcp-server/src/tools/okveds/list-okveds.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.okveds.list())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.okveds.list())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts b/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts index c9599eb..074e91b 100644 --- a/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts +++ b/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -50,7 +50,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.paidTaxes.list(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.paidTaxes.list(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/procurement/retrieve-okpd2-procurement.ts b/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts index 777ecd5..a27d37f 100644 --- a/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts +++ b/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.procurement.retrieveOkpd2())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.procurement.retrieveOkpd2())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/regions/list-regions.ts b/packages/mcp-server/src/tools/regions/list-regions.ts index 0b5d4d5..ada53b4 100644 --- a/packages/mcp-server/src/tools/regions/list-regions.ts +++ b/packages/mcp-server/src/tools/regions/list-regions.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -38,7 +38,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.regions.list())); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.regions.list())); + } catch (error) { + if (isJqError(error)) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/risks/list-risks.ts b/packages/mcp-server/src/tools/risks/list-risks.ts index 2995512..a35143a 100644 --- a/packages/mcp-server/src/tools/risks/list-risks.ts +++ b/packages/mcp-server/src/tools/risks/list-risks.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -50,7 +50,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.risks.list(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.risks.list(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/sro-membership/retrieve-sro-membership.ts b/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts index 80d3be0..bb5333e 100644 --- a/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts +++ b/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -72,7 +72,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.sroMembership.retrieve(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.sroMembership.retrieve(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/suggestions/create-suggestions.ts b/packages/mcp-server/src/tools/suggestions/create-suggestions.ts index a865a13..4be6bc9 100644 --- a/packages/mcp-server/src/tools/suggestions/create-suggestions.ts +++ b/packages/mcp-server/src/tools/suggestions/create-suggestions.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -53,7 +53,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.suggestions.create(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.suggestions.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/tax-info/retrieve-tax-info.ts b/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts index 1f836bd..38eca61 100644 --- a/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts +++ b/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -50,7 +50,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.taxInfo.retrieve(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.taxInfo.retrieve(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/taxpayer-statuses/create-taxpayer-statuses.ts b/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts index 91cf0f6..89287d1 100644 --- a/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts +++ b/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -48,7 +48,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const { jq_filter, ...body } = args as any; - return asTextContentResult(await maybeFilter(jq_filter, await client.taxpayerStatuses.create(body))); + try { + return asTextContentResult(await maybeFilter(jq_filter, await client.taxpayerStatuses.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 57bf89e..c346125 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:54:34 +0000 Subject: [PATCH 13/31] 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 28d8007..6790e47 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 4b2017b55610adec8b1a46b2d37d12703df9b47f 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:33:41 +0000 Subject: [PATCH 14/31] 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 eb8d540..53ea334 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 e9faa7a2b1cc9fa6ea4bec052bfba825261c06e9 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:06 +0000 Subject: [PATCH 15/31] 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 184a38d..6c50373 100644 --- a/src/client.ts +++ b/src/client.ts @@ -205,7 +205,7 @@ export class Datanewton { baseURL: string; maxRetries: number; timeout: number; - logger: Logger | undefined; + logger: Logger; logLevel: LogLevel | undefined; fetchOptions: MergedRequestInit | undefined; From 6459934e52ba9f580b96314cfa2a3d3157e53256 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:30:10 +0000 Subject: [PATCH 16/31] fix(mcp): return tool execution error on api error --- .../tools/arbitration-cases/list-arbitration-cases.ts | 11 +++++++++-- .../batch-cases/create-arbitration-batch-cases.ts | 11 +++++++++-- .../src/tools/arbitration/get-courts-arbitration.ts | 2 +- .../arbitration/get-dispute-categories-arbitration.ts | 2 +- .../arbitration/get-document-types-arbitration.ts | 2 +- .../src/tools/bankruptcy/retrieve-bankruptcy.ts | 2 +- .../create-batch-cards-by-filters.ts | 11 +++++++++-- .../src/tools/batch-cards/create-batch-cards.ts | 11 +++++++++-- .../src/tools/batch-changes/create-batch-changes.ts | 11 +++++++++-- .../list-blocked-bank-accounts.ts | 2 +- .../tools/corporate-actions/list-corporate-actions.ts | 11 +++++++++-- .../src/tools/counterparty/retrieve-counterparty.ts | 11 +++++++++-- .../arbitration/list-courts-dictionary-arbitration.ts | 2 +- .../list-dispute-categories-dictionary-arbitration.ts | 2 +- .../list-document-types-dictionary-arbitration.ts | 2 +- .../dictionary/list-lease-classifier-dictionary.ts | 2 +- .../src/tools/dictionary/list-licenses-dictionary.ts | 2 +- .../src/tools/dictionary/list-okveds-dictionary.ts | 2 +- .../src/tools/dictionary/list-regions-dictionary.ts | 2 +- .../procurement/list-okpd2-dictionary-procurement.ts | 2 +- .../tools/filters-preview/create-filters-preview.ts | 11 +++++++++-- .../mcp-server/src/tools/finance/retrieve-finance.ts | 11 +++++++++-- .../retrieve-government-contracts-stat.ts | 2 +- .../government-contracts/list-government-contracts.ts | 11 +++++++++-- .../lease-classifier/retrieve-lease-classifier.ts | 2 +- packages/mcp-server/src/tools/leases/create-leases.ts | 11 +++++++++-- .../mcp-server/src/tools/licenses/list-licenses.ts | 2 +- packages/mcp-server/src/tools/links/list-links.ts | 2 +- .../src/tools/nko-reestr/retrieve-nko-reestr.ts | 2 +- .../tools/ogrns-by-address/create-ogrns-by-address.ts | 2 +- .../mcp-server/src/tools/okpd-list/list-okpd-list.ts | 2 +- packages/mcp-server/src/tools/okveds/list-okveds.ts | 2 +- .../src/tools/paid-taxes/list-paid-taxes.ts | 2 +- .../tools/procurement/retrieve-okpd2-procurement.ts | 2 +- packages/mcp-server/src/tools/regions/list-regions.ts | 2 +- packages/mcp-server/src/tools/risks/list-risks.ts | 2 +- .../tools/sro-membership/retrieve-sro-membership.ts | 2 +- .../src/tools/suggestions/create-suggestions.ts | 2 +- .../src/tools/tax-info/retrieve-tax-info.ts | 2 +- .../taxpayer-statuses/create-taxpayer-statuses.ts | 2 +- 40 files changed, 128 insertions(+), 51 deletions(-) diff --git a/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts b/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts index dce7396..e54bc87 100644 --- a/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts +++ b/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -112,7 +112,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.arbitrationCases.list(body)); + try { + return asTextContentResult(await client.arbitrationCases.list(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts b/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts index ab44d0e..66f123f 100644 --- a/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts +++ b/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -131,7 +131,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.arbitration.batchCases.create(body)); + try { + return asTextContentResult(await client.arbitration.batchCases.create(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts index d03be5e..e31ce53 100644 --- a/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts +++ b/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getCourts())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts index b24f517..1e4f5fd 100644 --- a/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts +++ b/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDisputeCategories())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts index ca5160f..4803e94 100644 --- a/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts +++ b/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDocumentTypes())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts b/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts index 9cd7668..b06a063 100644 --- a/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts +++ b/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts @@ -61,7 +61,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.bankruptcy.retrieve(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts b/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts index dc945ee..d48e90f 100644 --- a/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts +++ b/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -503,7 +503,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.batchCardsByFilters.create(body)); + try { + return asTextContentResult(await client.batchCardsByFilters.create(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts b/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts index dccc8e3..ebc96bf 100644 --- a/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts +++ b/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -39,7 +39,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.batchCards.create(body)); + try { + return asTextContentResult(await client.batchCards.create(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts b/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts index 1f07498..0210e1e 100644 --- a/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts +++ b/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -103,7 +103,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.batchChanges.create(body)); + try { + return asTextContentResult(await client.batchChanges.create(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts b/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts index 0332e96..9d8d92c 100644 --- a/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts +++ b/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts @@ -53,7 +53,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.blockedBankAccounts.list(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts b/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts index dd176be..05c09d0 100644 --- a/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts +++ b/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -88,7 +88,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.corporateActions.list(body)); + try { + return asTextContentResult(await client.corporateActions.list(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts b/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts index 169aa04..368aa8f 100644 --- a/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts +++ b/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -62,7 +62,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.counterparty.retrieve(body)); + try { + return asTextContentResult(await client.counterparty.retrieve(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts index 3ba5189..81f7f70 100644 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts +++ b/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts @@ -43,7 +43,7 @@ export const handler = async (client: Datanewton, args: Record await maybeFilter(jq_filter, await client.dictionary.arbitration.listCourts()), ); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts index 4f440b3..76b63e1 100644 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts +++ b/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts @@ -43,7 +43,7 @@ export const handler = async (client: Datanewton, args: Record await maybeFilter(jq_filter, await client.dictionary.arbitration.listDisputeCategories()), ); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts index bbeac02..71ac417 100644 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts +++ b/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts @@ -43,7 +43,7 @@ export const handler = async (client: Datanewton, args: Record await maybeFilter(jq_filter, await client.dictionary.arbitration.listDocumentTypes()), ); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts index 9960a7e..5e3ccdc 100644 --- a/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLeaseClassifier())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts index 9cb958c..ad4bd69 100644 --- a/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLicenses())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts index a953da5..3114a72 100644 --- a/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listOkveds())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts index 54b8f64..323df45 100644 --- a/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts +++ b/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listRegions())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts b/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts index cf5c5bd..c8c9fca 100644 --- a/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts +++ b/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.procurement.listOkpd2())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts b/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts index 485d330..5446af2 100644 --- a/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts +++ b/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -503,7 +503,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.filtersPreview.create(body)); + try { + return asTextContentResult(await client.filtersPreview.create(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/finance/retrieve-finance.ts b/packages/mcp-server/src/tools/finance/retrieve-finance.ts index c67d1f0..84dcc45 100644 --- a/packages/mcp-server/src/tools/finance/retrieve-finance.ts +++ b/packages/mcp-server/src/tools/finance/retrieve-finance.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -43,7 +43,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.finance.retrieve(body)); + try { + return asTextContentResult(await client.finance.retrieve(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts b/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts index 467092f..2740f03 100644 --- a/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts +++ b/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts @@ -71,7 +71,7 @@ export const handler = async (client: Datanewton, args: Record await maybeFilter(jq_filter, await client.governmentContractsStat.retrieve(body)), ); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts b/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts index 536bd65..ab23a64 100644 --- a/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts +++ b/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -157,7 +157,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.governmentContracts.list(body)); + try { + return asTextContentResult(await client.governmentContracts.list(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts b/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts index 2feaef0..a0fe4cb 100644 --- a/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts +++ b/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.leaseClassifier.retrieve())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/leases/create-leases.ts b/packages/mcp-server/src/tools/leases/create-leases.ts index c6df659..c413941 100644 --- a/packages/mcp-server/src/tools/leases/create-leases.ts +++ b/packages/mcp-server/src/tools/leases/create-leases.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from 'datanewton-mcp/tools/types'; +import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import Datanewton from 'datanewton'; @@ -91,7 +91,14 @@ export const tool: Tool = { export const handler = async (client: Datanewton, args: Record | undefined) => { const body = args as any; - return asTextContentResult(await client.leases.create(body)); + try { + return asTextContentResult(await client.leases.create(body)); + } catch (error) { + if (error instanceof Datanewton.APIError) { + return asErrorResult(error.message); + } + throw error; + } }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/licenses/list-licenses.ts b/packages/mcp-server/src/tools/licenses/list-licenses.ts index cd3f05f..7d2baf9 100644 --- a/packages/mcp-server/src/tools/licenses/list-licenses.ts +++ b/packages/mcp-server/src/tools/licenses/list-licenses.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.licenses.list())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/links/list-links.ts b/packages/mcp-server/src/tools/links/list-links.ts index 8dcf96b..f6b0f92 100644 --- a/packages/mcp-server/src/tools/links/list-links.ts +++ b/packages/mcp-server/src/tools/links/list-links.ts @@ -54,7 +54,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.links.list(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts b/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts index fd5bdfe..6f14f0a 100644 --- a/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts +++ b/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts @@ -53,7 +53,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.nkoReestr.retrieve(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts b/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts index 52cc03c..b298830 100644 --- a/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts +++ b/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts @@ -55,7 +55,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.ogrnsByAddress.create(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts b/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts index 11bcd9d..549ccd3 100644 --- a/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts +++ b/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts @@ -53,7 +53,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.okpdList.list(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/okveds/list-okveds.ts b/packages/mcp-server/src/tools/okveds/list-okveds.ts index d3bd304..dcc3269 100644 --- a/packages/mcp-server/src/tools/okveds/list-okveds.ts +++ b/packages/mcp-server/src/tools/okveds/list-okveds.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.okveds.list())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts b/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts index 074e91b..ef10998 100644 --- a/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts +++ b/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts @@ -53,7 +53,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.paidTaxes.list(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts b/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts index a27d37f..ddfdb15 100644 --- a/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts +++ b/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.procurement.retrieveOkpd2())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/regions/list-regions.ts b/packages/mcp-server/src/tools/regions/list-regions.ts index ada53b4..130ac69 100644 --- a/packages/mcp-server/src/tools/regions/list-regions.ts +++ b/packages/mcp-server/src/tools/regions/list-regions.ts @@ -41,7 +41,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.regions.list())); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/risks/list-risks.ts b/packages/mcp-server/src/tools/risks/list-risks.ts index a35143a..2892c4d 100644 --- a/packages/mcp-server/src/tools/risks/list-risks.ts +++ b/packages/mcp-server/src/tools/risks/list-risks.ts @@ -53,7 +53,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.risks.list(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts b/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts index bb5333e..fa547ad 100644 --- a/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts +++ b/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts @@ -75,7 +75,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.sroMembership.retrieve(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/suggestions/create-suggestions.ts b/packages/mcp-server/src/tools/suggestions/create-suggestions.ts index 4be6bc9..d04c6d7 100644 --- a/packages/mcp-server/src/tools/suggestions/create-suggestions.ts +++ b/packages/mcp-server/src/tools/suggestions/create-suggestions.ts @@ -56,7 +56,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.suggestions.create(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts b/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts index 38eca61..c58aefa 100644 --- a/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts +++ b/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts @@ -53,7 +53,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.taxInfo.retrieve(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; diff --git a/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts b/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts index 89287d1..7589ecb 100644 --- a/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts +++ b/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts @@ -51,7 +51,7 @@ export const handler = async (client: Datanewton, args: Record try { return asTextContentResult(await maybeFilter(jq_filter, await client.taxpayerStatuses.create(body))); } catch (error) { - if (isJqError(error)) { + if (error instanceof Datanewton.APIError || isJqError(error)) { return asErrorResult(error.message); } throw error; From 41d5fd29e71193adbca5ea8e9fa4c7cc2173cf33 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:32:02 +0000 Subject: [PATCH 17/31] 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 d43eea1..7b80387 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 614b323..fd3e0fa 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -182,6 +182,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' }, ); @@ -193,6 +195,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' }, ); @@ -225,6 +229,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 9a2d7b2..4545158 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 97681abb1098977c32bdb1a9ee82548d500dbd1e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 06:15:55 +0000 Subject: [PATCH 18/31] chore(internal): upgrade eslint --- package.json | 2 +- yarn.lock | 150 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 89 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index 3678aa8..bad930d 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@types/node": "^20.17.6", "@typescript-eslint/eslint-plugin": "8.31.1", "@typescript-eslint/parser": "8.31.1", - "eslint": "^9.20.1", + "eslint": "^9.39.1", "eslint-plugin-prettier": "^5.4.1", "eslint-plugin-unused-imports": "^4.1.4", "iconv-lite": "^0.6.3", diff --git a/yarn.lock b/yarn.lock index 8311caf..5f56a20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -350,45 +350,52 @@ dependencies: "@cspotcode/source-map-consumer" "0.8.0" -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": +"@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: eslint-visitor-keys "^3.3.0" +"@eslint-community/eslint-utils@^4.8.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== + dependencies: + eslint-visitor-keys "^3.4.3" + "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.19.0": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" - integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== +"@eslint/config-array@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: - "@eslint/object-schema" "^2.1.6" + "@eslint/object-schema" "^2.1.7" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/core@^0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091" - integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw== +"@eslint/config-helpers@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== dependencies: - "@types/json-schema" "^7.0.15" + "@eslint/core" "^0.17.0" -"@eslint/core@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.11.0.tgz#7a9226e850922e42cbd2ba71361eacbe74352a12" - integrity sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA== +"@eslint/core@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== dependencies: "@types/json-schema" "^7.0.15" -"@eslint/eslintrc@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" - integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== +"@eslint/eslintrc@^3.3.1": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" + integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -396,26 +403,26 @@ globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^4.1.0" + js-yaml "^4.1.1" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.20.0": - version "9.20.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.20.0.tgz#7421bcbe74889fcd65d1be59f00130c289856eb4" - integrity sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ== +"@eslint/js@9.39.1": + version "9.39.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.1.tgz#0dd59c3a9f40e3f1882975c321470969243e0164" + integrity sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw== -"@eslint/object-schema@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" - integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== -"@eslint/plugin-kit@^0.2.5": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81" - integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A== +"@eslint/plugin-kit@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== dependencies: - "@eslint/core" "^0.10.0" + "@eslint/core" "^0.17.0" levn "^0.4.1" "@humanfs/core@^0.19.1": @@ -441,10 +448,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== -"@humanwhocodes/retry@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" - integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== +"@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1057,6 +1064,11 @@ acorn@^8.14.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + acorn@^8.4.1: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" @@ -1560,15 +1572,15 @@ eslint-plugin-unused-imports@^4.1.4: resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738" integrity sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ== -eslint-scope@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" - integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== +eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -1578,31 +1590,36 @@ eslint-visitor-keys@^4.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== -eslint@^9.20.1: - version "9.20.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6" - integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g== +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^9.39.1: + version "9.39.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.1.tgz#be8bf7c6de77dcc4252b5a8dcb31c2efff74a6e5" + integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g== dependencies: - "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.19.0" - "@eslint/core" "^0.11.0" - "@eslint/eslintrc" "^3.2.0" - "@eslint/js" "9.20.0" - "@eslint/plugin-kit" "^0.2.5" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.2" + "@eslint/core" "^0.17.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.39.1" + "@eslint/plugin-kit" "^0.4.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.1" + "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.6" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.2.0" - eslint-visitor-keys "^4.2.0" - espree "^10.3.0" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -1618,7 +1635,7 @@ eslint@^9.20.1: natural-compare "^1.4.0" optionator "^0.9.3" -espree@^10.0.1, espree@^10.3.0: +espree@^10.0.1: version "10.3.0" resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== @@ -1627,6 +1644,15 @@ espree@^10.0.1, espree@^10.3.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.2.0" +espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -2440,10 +2466,10 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +js-yaml@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" From 733071a72ccfacc4edb2387d1f839f32d2b75938 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 05:05:35 +0000 Subject: [PATCH 19/31] chore: use latest @modelcontextprotocol/sdk --- 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 6790e47..fa91624 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -32,7 +32,7 @@ "dependencies": { "datanewton": "file:../../dist/", "@cloudflare/cabidela": "^0.2.4", - "@modelcontextprotocol/sdk": "^1.11.5", + "@modelcontextprotocol/sdk": "^1.24.0", "@valtown/deno-http-worker": "^0.0.21", "cors": "^2.8.5", "express": "^5.1.0", From 5066987adf78a9796d7bab03826a46f6262dbad4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 05:10:38 +0000 Subject: [PATCH 20/31] feat(mcp): add typescript check to code execution tool --- packages/mcp-server/src/code-tool-worker.ts | 107 ++++++++++++++++---- packages/mcp-server/src/code-tool.ts | 16 ++- 2 files changed, 100 insertions(+), 23 deletions(-) diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts index fd3e0fa..d9d8592 100644 --- a/packages/mcp-server/src/code-tool-worker.ts +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -1,5 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import path from 'node:path'; import util from 'node:util'; import Fuse from 'fuse.js'; @@ -8,30 +9,41 @@ import ts from 'typescript'; import { WorkerInput, WorkerSuccess, WorkerError } from './code-tool-types'; import { Datanewton } from 'datanewton'; -function getRunFunctionNode( - code: string, -): ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | null { +function getRunFunctionSource(code: string): { + type: 'declaration' | 'expression'; + client: string | undefined; + code: string; +} | null { const sourceFile = ts.createSourceFile('code.ts', code, ts.ScriptTarget.Latest, true); + const printer = ts.createPrinter(); for (const statement of sourceFile.statements) { // Check for top-level function declarations if (ts.isFunctionDeclaration(statement)) { if (statement.name?.text === 'run') { - return statement; + return { + type: 'declaration', + client: statement.parameters[0]?.name.getText(), + code: printer.printNode(ts.EmitHint.Unspecified, statement.body!, sourceFile), + }; } } // 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') { + 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; - } + declaration.initializer && + (ts.isFunctionExpression(declaration.initializer) || ts.isArrowFunction(declaration.initializer)) + ) { + return { + type: 'expression', + client: declaration.initializer.parameters[0]?.name.getText(), + code: printer.printNode(ts.EmitHint.Unspecified, declaration.initializer, sourceFile), + }; } } } @@ -40,6 +52,61 @@ function getRunFunctionNode( return null; } +function getTSDiagnostics(code: string): string[] { + const functionSource = getRunFunctionSource(code)!; + const codeWithImport = [ + 'import { Datanewton } from "datanewton";', + functionSource.type === 'declaration' ? + `async function run(${functionSource.client}: Datanewton)` + : `const run: (${functionSource.client}: Datanewton) => Promise =`, + functionSource.code, + ].join('\n'); + const sourcePath = path.resolve('code.ts'); + const ast = ts.createSourceFile(sourcePath, codeWithImport, ts.ScriptTarget.Latest, true); + const options = ts.getDefaultCompilerOptions(); + options.target = ts.ScriptTarget.Latest; + options.module = ts.ModuleKind.NodeNext; + options.moduleResolution = ts.ModuleResolutionKind.NodeNext; + const host = ts.createCompilerHost(options, true); + const newHost: typeof host = { + ...host, + getSourceFile: (...args) => { + if (path.resolve(args[0]) === sourcePath) { + return ast; + } + return host.getSourceFile(...args); + }, + readFile: (...args) => { + if (path.resolve(args[0]) === sourcePath) { + return codeWithImport; + } + return host.readFile(...args); + }, + fileExists: (...args) => { + if (path.resolve(args[0]) === sourcePath) { + return true; + } + return host.fileExists(...args); + }, + }; + const program = ts.createProgram({ + options, + rootNames: [sourcePath], + host: newHost, + }); + const diagnostics = ts.getPreEmitDiagnostics(program, ast); + return diagnostics.map((d) => { + const message = ts.flattenDiagnosticMessageText(d.messageText, '\n'); + if (!d.file || !d.start) return `- ${message}`; + const { line: tsLine } = ts.getLineAndCharacterOfPosition(d.file, d.start); + // We add two lines in the beginning, for the client import and the function declaration. + // So the actual (zero-based) line number is tsLine - 2. + const lineNumber = tsLine - 2; + const line = code.split('\n').at(lineNumber)?.trim(); + return line ? `- ${message}\n at line ${lineNumber + 1}\n ${line}` : `- ${message}`; + }); +} + const fuse = new Fuse( [ 'client.taxpayerStatuses.create', @@ -177,11 +244,16 @@ function parseError(code: string, error: unknown): string | undefined { const fetch = async (req: Request): Promise => { const { opts, code } = (await req.json()) as WorkerInput; - if (code == null) { + + const runFunctionSource = code ? getRunFunctionSource(code) : null; + if (!runFunctionSource) { + const message = + code ? + 'The code is missing a top-level `run` function.' + : 'The code argument is missing. Provide one containing a top-level `run` function.'; 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```', + message: `${message} Write code within this template:\n\n\`\`\`\nasync function run(client) {\n // Fill this out\n}\n\`\`\``, logLines: [], errLines: [], } satisfies WorkerError, @@ -189,12 +261,11 @@ const fetch = async (req: Request): Promise => { ); } - const runFunctionNode = getRunFunctionNode(code); - if (!runFunctionNode) { + const diagnostics = getTSDiagnostics(code); + if (diagnostics.length > 0) { 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```', + message: `The code contains TypeScript diagnostics:\n${diagnostics.join('\n')}`, logLines: [], errLines: [], } satisfies WorkerError, diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 4545158..b22f8df 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -1,7 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { dirname } from 'node:path'; -import { pathToFileURL } from 'node:url'; +import path from 'node:path'; +import url from 'node:url'; import Datanewton, { ClientOptions } from 'datanewton'; import { ContentBlock, Endpoint, Metadata, ToolCallResult } from './tools/types'; @@ -35,10 +35,16 @@ export async function codeTool(): Promise { const baseURLHostname = new URL(client.baseURL).hostname; const { code } = args as { code: string }; - const worker = await newDenoHTTPWorker(pathToFileURL(workerPath), { + const allowRead = [ + 'code-tool-worker.mjs', + `${workerPath.replace(/([\/\\]node_modules)[\/\\].+$/, '$1')}/`, + path.resolve(path.dirname(workerPath), '..'), + ].join(','); + + const worker = await newDenoHTTPWorker(url.pathToFileURL(workerPath), { runFlags: [ `--node-modules-dir=manual`, - `--allow-read=code-tool-worker.mjs,${workerPath.replace(/([\/\\]node_modules)[\/\\].+$/, '$1')}/`, + `--allow-read=${allowRead}`, `--allow-net=${baseURLHostname}`, // Allow environment variables because instantiating the client will try to read from them, // even though they are not set. @@ -46,7 +52,7 @@ export async function codeTool(): Promise { ], printOutput: true, spawnOptions: { - cwd: dirname(workerPath), + cwd: path.dirname(workerPath), }, }); From 839ac88fbd1df74deca3e4895e3584b39d7fb8c3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:57:45 +0000 Subject: [PATCH 21/31] feat(mcp): handle code mode calls in the Stainless API Moves the code-mode execution to an endpoint in the Stainless API. --- packages/mcp-server/src/code-tool-paths.cts | 3 - packages/mcp-server/src/code-tool-worker.ts | 311 -------------------- packages/mcp-server/src/code-tool.ts | 180 ++--------- packages/mcp-server/src/docs-search-tool.ts | 7 + 4 files changed, 39 insertions(+), 462 deletions(-) delete mode 100644 packages/mcp-server/src/code-tool-paths.cts delete mode 100644 packages/mcp-server/src/code-tool-worker.ts diff --git a/packages/mcp-server/src/code-tool-paths.cts b/packages/mcp-server/src/code-tool-paths.cts deleted file mode 100644 index 15ce7f5..0000000 --- a/packages/mcp-server/src/code-tool-paths.cts +++ /dev/null @@ -1,3 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export const workerPath = require.resolve('./code-tool-worker.mjs'); diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts deleted file mode 100644 index d9d8592..0000000 --- a/packages/mcp-server/src/code-tool-worker.ts +++ /dev/null @@ -1,311 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import path from 'node:path'; -import util from 'node:util'; - -import Fuse from 'fuse.js'; -import ts from 'typescript'; - -import { WorkerInput, WorkerSuccess, WorkerError } from './code-tool-types'; -import { Datanewton } from 'datanewton'; - -function getRunFunctionSource(code: string): { - type: 'declaration' | 'expression'; - client: string | undefined; - code: string; -} | null { - const sourceFile = ts.createSourceFile('code.ts', code, ts.ScriptTarget.Latest, true); - const printer = ts.createPrinter(); - - for (const statement of sourceFile.statements) { - // Check for top-level function declarations - if (ts.isFunctionDeclaration(statement)) { - if (statement.name?.text === 'run') { - return { - type: 'declaration', - client: statement.parameters[0]?.name.getText(), - code: printer.printNode(ts.EmitHint.Unspecified, statement.body!, sourceFile), - }; - } - } - - // 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 - declaration.initializer && - (ts.isFunctionExpression(declaration.initializer) || ts.isArrowFunction(declaration.initializer)) - ) { - return { - type: 'expression', - client: declaration.initializer.parameters[0]?.name.getText(), - code: printer.printNode(ts.EmitHint.Unspecified, declaration.initializer, sourceFile), - }; - } - } - } - } - - return null; -} - -function getTSDiagnostics(code: string): string[] { - const functionSource = getRunFunctionSource(code)!; - const codeWithImport = [ - 'import { Datanewton } from "datanewton";', - functionSource.type === 'declaration' ? - `async function run(${functionSource.client}: Datanewton)` - : `const run: (${functionSource.client}: Datanewton) => Promise =`, - functionSource.code, - ].join('\n'); - const sourcePath = path.resolve('code.ts'); - const ast = ts.createSourceFile(sourcePath, codeWithImport, ts.ScriptTarget.Latest, true); - const options = ts.getDefaultCompilerOptions(); - options.target = ts.ScriptTarget.Latest; - options.module = ts.ModuleKind.NodeNext; - options.moduleResolution = ts.ModuleResolutionKind.NodeNext; - const host = ts.createCompilerHost(options, true); - const newHost: typeof host = { - ...host, - getSourceFile: (...args) => { - if (path.resolve(args[0]) === sourcePath) { - return ast; - } - return host.getSourceFile(...args); - }, - readFile: (...args) => { - if (path.resolve(args[0]) === sourcePath) { - return codeWithImport; - } - return host.readFile(...args); - }, - fileExists: (...args) => { - if (path.resolve(args[0]) === sourcePath) { - return true; - } - return host.fileExists(...args); - }, - }; - const program = ts.createProgram({ - options, - rootNames: [sourcePath], - host: newHost, - }); - const diagnostics = ts.getPreEmitDiagnostics(program, ast); - return diagnostics.map((d) => { - const message = ts.flattenDiagnosticMessageText(d.messageText, '\n'); - if (!d.file || !d.start) return `- ${message}`; - const { line: tsLine } = ts.getLineAndCharacterOfPosition(d.file, d.start); - // We add two lines in the beginning, for the client import and the function declaration. - // So the actual (zero-based) line number is tsLine - 2. - const lineNumber = tsLine - 2; - const line = code.split('\n').at(lineNumber)?.trim(); - return line ? `- ${message}\n at line ${lineNumber + 1}\n ${line}` : `- ${message}`; - }); -} - -const fuse = new Fuse( - [ - 'client.taxpayerStatuses.create', - 'client.suggestions.create', - 'client.ogrnsByAddress.create', - 'client.leases.create', - 'client.filtersPreview.create', - 'client.batchChanges.create', - 'client.batchCards.create', - 'client.batchCardsByFilters.create', - 'client.arbitration.getCourts', - 'client.arbitration.getDisputeCategories', - 'client.arbitration.getDocumentTypes', - 'client.arbitration.batchCases.create', - 'client.taxInfo.retrieve', - 'client.sroMembership.retrieve', - 'client.risks.list', - 'client.paidTaxes.list', - 'client.okpdList.list', - 'client.nkoReestr.retrieve', - 'client.links.list', - 'client.governmentContracts.list', - 'client.governmentContractsStat.retrieve', - 'client.finance.retrieve', - 'client.regions.list', - 'client.dictionary.listLeaseClassifier', - 'client.dictionary.listLicenses', - 'client.dictionary.listOkveds', - 'client.dictionary.listRegions', - 'client.dictionary.procurement.listOkpd2', - 'client.dictionary.arbitration.listCourts', - 'client.dictionary.arbitration.listDisputeCategories', - 'client.dictionary.arbitration.listDocumentTypes', - 'client.procurement.retrieveOkpd2', - 'client.okveds.list', - 'client.licenses.list', - 'client.leaseClassifier.retrieve', - 'client.counterparty.retrieve', - 'client.corporateActions.list', - 'client.blockedBankAccounts.list', - 'client.bankruptcy.retrieve', - 'client.arbitrationCases.list', - ], - { 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; -} - -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; - - const runFunctionSource = code ? getRunFunctionSource(code) : null; - if (!runFunctionSource) { - const message = - code ? - 'The code is missing a top-level `run` function.' - : 'The code argument is missing. Provide one containing a top-level `run` function.'; - return Response.json( - { - message: `${message} 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' }, - ); - } - - const diagnostics = getTSDiagnostics(code); - if (diagnostics.length > 0) { - return Response.json( - { - message: `The code contains TypeScript diagnostics:\n${diagnostics.join('\n')}`, - logLines: [], - errLines: [], - } satisfies WorkerError, - { status: 400, statusText: 'Code execution error' }, - ); - } - - const client = new Datanewton({ - ...opts, - }); - - const logLines: string[] = []; - const errLines: string[] = []; - const console = { - log: (...args: unknown[]) => { - logLines.push(util.format(...args)); - }, - error: (...args: unknown[]) => { - errLines.push(util.format(...args)); - }, - }; - try { - let run_ = async (client: any) => {}; - eval(`${code}\nrun_ = run;`); - const result = await run_(makeSdkProxy(client, { path: ['client'] })); - return Response.json({ - result, - logLines, - errLines, - } satisfies WorkerSuccess); - } catch (e) { - return Response.json( - { - message: parseError(code, e), - logLines, - errLines, - } satisfies WorkerError, - { status: 400, statusText: 'Code execution error' }, - ); - } -}; - -export default { fetch }; diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index b22f8df..0e0e9da 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -1,14 +1,9 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import path from 'node:path'; -import url from 'node:url'; -import Datanewton, { ClientOptions } from 'datanewton'; -import { ContentBlock, Endpoint, Metadata, ToolCallResult } from './tools/types'; - +import { Metadata, ToolCallResult, asTextContentResult } from './tools/types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; - -import { WorkerInput, WorkerError, WorkerSuccess } from './code-tool-types'; - +import { readEnv } from './server'; +import { WorkerSuccess } from './code-tool-types'; /** * A tool that runs code against a copy of the SDK. * @@ -18,156 +13,45 @@ import { WorkerInput, WorkerError, WorkerSuccess } from './code-tool-types'; * * @param endpoints - The endpoints to include in the list. */ -export async function codeTool(): Promise { +export async function codeTool() { const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] }; const tool: Tool = { name: 'execute', description: - '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.', + '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 SDK 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' } } }, }; - - // Import dynamically to avoid failing at import time in cases where the environment is not well-supported. - const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker'); - const { workerPath } = await import('./code-tool-paths.cjs'); - - const handler = async (client: Datanewton, args: unknown): Promise => { - const baseURLHostname = new URL(client.baseURL).hostname; - const { code } = args as { code: string }; - - const allowRead = [ - 'code-tool-worker.mjs', - `${workerPath.replace(/([\/\\]node_modules)[\/\\].+$/, '$1')}/`, - path.resolve(path.dirname(workerPath), '..'), - ].join(','); - - const worker = await newDenoHTTPWorker(url.pathToFileURL(workerPath), { - runFlags: [ - `--node-modules-dir=manual`, - `--allow-read=${allowRead}`, - `--allow-net=${baseURLHostname}`, - // Allow environment variables because instantiating the client will try to read from them, - // even though they are not set. - '--allow-env', - ], - printOutput: true, - spawnOptions: { - cwd: path.dirname(workerPath), + const handler = async (_: unknown, args: any): Promise => { + const code = args.code as string; + + // this is not required, but passing a Stainless API key for the matching project_name + // will allow you to run code-mode queries against non-published versions of your SDK. + const stainlessAPIKey = readEnv('STAINLESS_API_KEY'); + const codeModeEndpoint = + readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool/'; + + const res = await fetch(codeModeEndpoint, { + method: 'POST', + headers: { + ...(stainlessAPIKey && { Authorization: stainlessAPIKey }), + 'Content-Type': 'application/json', + client_envs: JSON.stringify({ DATANEWTON_API_KEY: readEnv('DATANEWTON_API_KEY') }), }, + body: JSON.stringify({ + project_name: 'datanewton', + code, + }), }); - try { - const resp = await new Promise((resolve, reject) => { - worker.addEventListener('exit', (exitCode) => { - reject(new Error(`Worker exited with code ${exitCode}`)); - }); - - const opts: ClientOptions = { - baseURL: client.baseURL, - apiKey: client.apiKey, - defaultHeaders: { - 'X-Stainless-MCP': 'true', - }, - }; - - const req = worker.request( - 'http://localhost', - { - headers: { - 'content-type': 'application/json', - }, - method: 'POST', - }, - (resp) => { - const body: Uint8Array[] = []; - resp.on('error', (err) => { - reject(err); - }); - resp.on('data', (chunk) => { - body.push(chunk); - }); - resp.on('end', () => { - resolve( - new Response(Buffer.concat(body).toString(), { - status: resp.statusCode ?? 200, - headers: resp.headers as any, - }), - ); - }); - }, - ); - - const body = JSON.stringify({ - opts, - code, - } satisfies WorkerInput); - - req.write(body, (err) => { - if (err != null) { - reject(err); - } - }); - - req.end(); - }); - - if (resp.status === 200) { - const { result, logLines, errLines } = (await resp.json()) as WorkerSuccess; - const returnOutput: ContentBlock | null = - result == null ? null : ( - { - type: 'text', - text: typeof result === 'string' ? result : JSON.stringify(result), - } - ); - 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: [returnOutput, logOutput, errOutput].filter((block) => block !== null), - }; - } else { - 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: [messageOutput, logOutput, errOutput].filter((block) => block !== null), - isError: true, - }; - } - } finally { - worker.terminate(); + if (!res.ok) { + throw new Error( + `${res.status}: ${ + res.statusText + } error when trying to contact Code Tool server. Details: ${await res.text()}`, + ); } + + return asTextContentResult((await res.json()) as WorkerSuccess); }; return { metadata, tool, handler }; diff --git a/packages/mcp-server/src/docs-search-tool.ts b/packages/mcp-server/src/docs-search-tool.ts index 53ea334..c289576 100644 --- a/packages/mcp-server/src/docs-search-tool.ts +++ b/packages/mcp-server/src/docs-search-tool.ts @@ -46,6 +46,13 @@ export const handler = async (_: unknown, args: Record | undefi const body = args as any; const query = new URLSearchParams(body).toString(); const result = await fetch(`${docsSearchURL}?${query}`); + + if (!result.ok) { + throw new Error( + `${result.status}: ${result.statusText} when using doc search tool. Details: ${await result.text()}`, + ); + } + return asTextContentResult(await result.json()); }; From 986864ae236a763a5f37699eb32b3a5db4a791e8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:58:29 +0000 Subject: [PATCH 22/31] fix(mcp): return correct lines on typescript errors --- .devcontainer/Dockerfile | 23 ++ .eslintrc.js | 10 + jest.setup.ts | 0 packages/mcp-server/src/did-you-mean-proxy.ts | 356 ++++++++++++++++++ src/internal/polyfill/file.node.d.ts | 14 + src/internal/polyfill/file.node.mjs | 9 + tests/responses.test.ts | 24 ++ 7 files changed, 436 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .eslintrc.js create mode 100644 jest.setup.ts create mode 100644 packages/mcp-server/src/did-you-mean-proxy.ts create mode 100644 src/internal/polyfill/file.node.d.ts create mode 100644 src/internal/polyfill/file.node.mjs create mode 100644 tests/responses.test.ts diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..8ea34be --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 +FROM debian:bookworm-slim AS stainless + +RUN apt-get update && apt-get install -y \ + nodejs \ + npm \ + yarnpkg \ + && apt-get clean autoclean + +# Ensure UTF-8 encoding +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 + +# Yarn +RUN ln -sf /usr/bin/yarnpkg /usr/bin/yarn + +WORKDIR /workspace + +COPY package.json yarn.lock /workspace/ + +RUN yarn install + +COPY . /workspace diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..60f0e7a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'unused-imports', 'prettier'], + rules: { + 'no-unused-vars': 'off', + 'prettier/prettier': 'error', + 'unused-imports/no-unused-imports': 'error', + }, + root: true, +}; diff --git a/jest.setup.ts b/jest.setup.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/mcp-server/src/did-you-mean-proxy.ts b/packages/mcp-server/src/did-you-mean-proxy.ts new file mode 100644 index 0000000..8c1380d --- /dev/null +++ b/packages/mcp-server/src/did-you-mean-proxy.ts @@ -0,0 +1,356 @@ +import Fuse from 'fuse.js'; + +const allKinds = ['string', 'number', 'boolean', 'object', 'array', 'method', 'constructor'] as const; +type Kind = (typeof allKinds)[number]; + +const nodeInspect = Symbol.for('nodejs.util.inspect.custom'); +const denoInspect = Symbol.for('Deno.customInspect'); + +const stringMethods = new Set(Reflect.ownKeys(String.prototype).filter((k) => typeof k === 'string')); +const numberMethods = new Set(Reflect.ownKeys(Number.prototype).filter((k) => typeof k === 'string')); +const arrayMethods = new Set(Reflect.ownKeys(Array.prototype).filter((k) => typeof k === 'string')); + +function getApplyKinds(name?: string | symbol): readonly Kind[] { + if (!name || name === nodeInspect || name === denoInspect) { + return allKinds; + } + + if (name === Symbol.toPrimitive || name === 'toString' || name === 'valueOf') { + return ['string', 'number', 'boolean']; + } + + if (name === Symbol.iterator) { + return ['array']; + } + + if (typeof name !== 'string') { + return ['method']; + } + + const kinds: Kind[] = []; + if (stringMethods.has(name)) { + kinds.push('string'); + } else if (numberMethods.has(name)) { + kinds.push('number'); + } else if (arrayMethods.has(name)) { + kinds.push('array'); + } + + return kinds.length > 0 ? kinds : ['method']; +} + +type KindPaths = Record; + +function traverseKinds( + obj: object, + path: string = '', + result: KindPaths = { + string: [], + number: [], + boolean: [], + object: [], + array: [], + method: [], + constructor: [], + }, +): KindPaths { + while (obj !== null) { + for (const key of Reflect.ownKeys(obj)) { + if (typeof key !== 'string') { + continue; + } + + if (key === 'constructor') { + continue; + } + + if (!/^[a-zA-Z]/.test(key)) { + continue; + } + + const value = Reflect.get(obj, key); + let kind: Kind; + + switch (typeof value) { + case 'string': { + kind = 'string'; + break; + } + case 'number': + case 'bigint': { + kind = 'number'; + break; + } + case 'boolean': { + kind = 'boolean'; + break; + } + case 'object': { + if (value === null) { + continue; + } + kind = Array.isArray(value) ? 'array' : 'object'; + break; + } + case 'function': { + kind = + key === value.name && value.name === value.prototype?.constructor?.name ? + 'constructor' + : 'method'; + break; + } + default: { + continue; + } + } + + const fullKey = path ? `${path}.${key}` : key; + result[kind].push(fullKey); + + if (kind === 'object') { + traverseKinds(value, fullKey, result); + } else if (kind === 'array' && value.length > 0) { + traverseKinds(value[0], `${fullKey}[]`, result); + } + } + + obj = Object.getPrototypeOf(obj); + if (obj === Object.prototype || obj === Array.prototype) { + break; + } + } + + return result; +} + +export type MakeError = (props: { + expected: readonly Kind[]; + rootPath: (string | symbol)[]; + path: (string | symbol)[]; + suggestions: { item: string; score: number }[]; +}) => string; + +export type ProxyConfig = { + /** + * Whether to also proxy the return values. They will be proxied with + * the same config, except the root path will be blank. Defaults to true. + */ + proxyReturn?: boolean; + /** + * The path to the root object, prepended to path suggestions. For example, + * if this is set to ['client'], then suggestions will be 'client.repos.list', + * 'client.users.list', etc. + */ + rootPath?: (string | symbol)[]; + /** + * Customize the error message to be thrown. The root path will not be + * prepended to either the path or the suggestions. + */ + makeSuggestionError?: MakeError; +}; + +function shouldProxy(value: unknown): value is NonNullable { + return value !== null && (typeof value === 'object' || typeof value === 'function'); +} + +const emptyTargetSymbol = Symbol.for('did-you-mean-proxy.emptyTargetPath'); + +type EmptyTarget = { + [emptyTargetSymbol]: { + getError: () => string; + }; +}; +type EmptyTargetInfo = EmptyTarget[typeof emptyTargetSymbol]; + +/** + * We use a special empty target so we can catch calls and constructions. + * Also useful for de-proxying in the end; if we get an empty target, we know + * we can throw an error. + */ +function createEmptyTarget(info: EmptyTargetInfo): EmptyTarget { + const emptyTarget = function () {} as any; + emptyTarget[nodeInspect] = () => { + throw info.getError(); + }; + emptyTarget[denoInspect] = () => { + throw info.getError(); + }; + emptyTarget[emptyTargetSymbol] = info; + return emptyTarget; +} + +function isEmptyTarget(value: unknown): value is EmptyTarget { + return typeof value === 'function' && (value as any)[emptyTargetSymbol] !== undefined; +} + +export const defaultMakeError: MakeError = function ({ expected, rootPath, path, suggestions }) { + const rootPathString = + rootPath.length > 0 ? `${rootPath.filter((p) => typeof p === 'string').join('.')}.` : ''; + const pathString = `'${rootPathString}${path.filter((p) => typeof p === 'string').join('.')}'`; + + let header = `${pathString} does not exist.`; + if (expected.length === 1) { + const expectedType = + expected[0] === 'array' ? 'an array' + : expected[0] === 'object' ? 'an object' + : expected[0] === 'method' ? 'a function' + : `a ${expected[0]}`; + header = `${pathString} is not ${expectedType}.`; + } + + const suggestionStrings = suggestions + // TODO(sometime): thresholding? + .filter((suggestion) => suggestion.score < 1) + .slice(0, 5) + .map((suggestion) => `'${rootPathString}${suggestion.item}'`); + + let body = ''; + if (suggestionStrings.length === 1) { + body = `Did you mean ${suggestionStrings[0]}?`; + } else if (suggestionStrings.length > 1) { + const commas = suggestionStrings.slice(0, suggestionStrings.length - 1).join(', '); + body = `Did you mean ${commas}, or ${suggestionStrings[suggestionStrings.length - 1]}?`; + } + + return body ? `${header} ${body}` : header; +}; + +export const debugMakeError: MakeError = function ({ expected, path, suggestions }) { + return `path ${path.filter((p) => typeof p === 'string').join('.')}; expected ${expected.join(', ')} +${suggestions + .slice(0, 10) + .map((suggestion) => ` - [${suggestion.score.toFixed(2)}] ${suggestion.item}`) + .join('\n')} +`; +}; + +const proxyToObj = new WeakMap(); + +export function makeProxy(root: Root, config: ProxyConfig = {}): Root { + let kindPaths: KindPaths | null = null; + + config.proxyReturn ??= true; + config.rootPath ??= ['']; + config.makeSuggestionError ??= defaultMakeError; + + const { proxyReturn, rootPath, makeSuggestionError } = config; + const { rootPath: _, ...subconfig } = config; + + function makeError(pathWithRoot: (string | symbol)[], expected: readonly Kind[]) { + if (!kindPaths) { + kindPaths = traverseKinds(root); + } + + const fuse = new Fuse( + expected.flatMap((kind) => kindPaths![kind]), + { includeScore: true }, + ); + + const path = pathWithRoot.slice(rootPath.length); + const searchKey: string[] = []; + for (const key of path) { + // Convert array keys to []: + if (/^\d+$/.test(key.toString())) { + searchKey.push('[]'); + } else if (typeof key === 'string') { + searchKey.push('.'); + searchKey.push(key); + } + } + + const key = searchKey.join(''); + const suggestions = fuse.search(key.slice(1)) as { item: string; score: number }[]; + + return makeSuggestionError({ expected, rootPath, path, suggestions }); + } + + function subproxy(obj: T, path: (string | symbol)[]): T { + const handlers: ProxyHandler = { + get(target, prop, receiver) { + const newPath = [...path, prop]; + const value = Reflect.get(target, prop, receiver); + + if (value === undefined && !Reflect.has(target, prop)) { + // Some common special cases: + // - 'then' is called on a non-thenable. + // - 'toJSON' is called when it's not defined. + // In these cases, we actually want to return undefined, so we + // resolve to the top-level thing. + if (prop === 'then' || prop === 'toJSON') { + return undefined; + } + + return subproxy( + createEmptyTarget({ + getError: () => makeError(newPath, allKinds), + }), + newPath, + ); + } + + return shouldProxy(value) ? subproxy(value, newPath) : value; + }, + construct(target, args, newTarget) { + if (isEmptyTarget(target) || typeof target !== 'function') { + throw new Error(makeError(path, ['constructor'])); + } + + const result = Reflect.construct(target, args, newTarget); + + return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; + }, + apply(target, thisArg, args) { + if (isEmptyTarget(target) || typeof target !== 'function') { + throw new Error(makeError(path, getApplyKinds(path[path.length - 1]))); + } + + const correctThisArg = proxyToObj.get(thisArg) ?? thisArg; + const proxiedArgs = + proxyReturn ? args.map((arg) => (shouldProxy(arg) ? makeProxy(arg, subconfig) : arg)) : args; + const result = Reflect.apply(target, correctThisArg, proxiedArgs); + + return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; + }, + }; + + // All other traps demand a non-empty target: + for (const trap of [ + 'defineProperty', + 'has', + 'set', + 'deleteProperty', + 'ownKeys', + 'getPrototypeOf', + 'setPrototypeOf', + 'isExtensible', + 'preventExtensions', + 'getOwnPropertyDescriptor', + ] as const) { + handlers[trap] = function (target: any, ...args: any[]) { + if (isEmptyTarget(target)) { + throw new Error(makeError(path, allKinds)); + } + + return (Reflect[trap] as any)(target, ...args); + }; + } + + const proxy = new Proxy(obj, handlers); + proxyToObj.set(proxy, obj); + + return proxy; + } + + return subproxy(root, rootPath); +} + +export function deproxy(value: T): T { + // Primitives never get proxied, so these are safe: + if (typeof value !== 'object' && typeof value !== 'function') { + return value; + } + if (isEmptyTarget(value)) { + throw new Error(value[emptyTargetSymbol].getError()); + } + return proxyToObj.get(value) ?? value; +} diff --git a/src/internal/polyfill/file.node.d.ts b/src/internal/polyfill/file.node.d.ts new file mode 100644 index 0000000..c95276d --- /dev/null +++ b/src/internal/polyfill/file.node.d.ts @@ -0,0 +1,14 @@ +/** + * This file polyfills the global `File` object for you if it's not already defined + * when running on Node.js + * + * This is only needed on Node.js v18 & v19. Newer versions already define `File` + * as a global. + */ + +// @ts-ignore +type nodeBuffer = typeof import('node:buffer'); +declare const File: typeof globalThis extends { File: unknown } ? (typeof globalThis)['File'] +: nodeBuffer extends { File: unknown } ? nodeBuffer['File'] +: any; +export {}; diff --git a/src/internal/polyfill/file.node.mjs b/src/internal/polyfill/file.node.mjs new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/src/internal/polyfill/file.node.mjs @@ -0,0 +1,9 @@ +/** + * This file polyfills the global `File` object for you if it's not already defined + * when running on Node.js + * + * This is only needed on Node.js v18 & v19. Newer versions already define `File` + * as a global. + */ + +import './file.node.js'; diff --git a/tests/responses.test.ts b/tests/responses.test.ts new file mode 100644 index 0000000..47d9a1f --- /dev/null +++ b/tests/responses.test.ts @@ -0,0 +1,24 @@ +import { createResponseHeaders } from 'datanewton/internal/headers'; + +describe('response parsing', () => { + // TODO: test unicode characters + test('headers are case agnostic', async () => { + const headers = createResponseHeaders(new Headers({ 'Content-Type': 'foo', Accept: 'text/plain' })); + expect(headers['content-type']).toEqual('foo'); + expect(headers['Content-type']).toEqual('foo'); + expect(headers['Content-Type']).toEqual('foo'); + expect(headers['accept']).toEqual('text/plain'); + expect(headers['Accept']).toEqual('text/plain'); + expect(headers['Hello-World']).toBeUndefined(); + }); + + test('duplicate headers are concatenated', () => { + const headers = createResponseHeaders( + new Headers([ + ['Content-Type', 'text/xml'], + ['Content-Type', 'application/json'], + ]), + ); + expect(headers['content-type']).toBe('text/xml, application/json'); + }); +}); From ec870f58a106701cec6f695c485dc1a9622e421c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:59:18 +0000 Subject: [PATCH 23/31] chore(internal): codegen related update --- .devcontainer/Dockerfile | 23 -- .eslintrc.js | 10 - jest.setup.ts | 0 packages/mcp-server/src/did-you-mean-proxy.ts | 356 ------------------ src/internal/polyfill/file.node.d.ts | 14 - src/internal/polyfill/file.node.mjs | 9 - tests/responses.test.ts | 24 -- 7 files changed, 436 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .eslintrc.js delete mode 100644 jest.setup.ts delete mode 100644 packages/mcp-server/src/did-you-mean-proxy.ts delete mode 100644 src/internal/polyfill/file.node.d.ts delete mode 100644 src/internal/polyfill/file.node.mjs delete mode 100644 tests/responses.test.ts diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 8ea34be..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM debian:bookworm-slim AS stainless - -RUN apt-get update && apt-get install -y \ - nodejs \ - npm \ - yarnpkg \ - && apt-get clean autoclean - -# Ensure UTF-8 encoding -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 - -# Yarn -RUN ln -sf /usr/bin/yarnpkg /usr/bin/yarn - -WORKDIR /workspace - -COPY package.json yarn.lock /workspace/ - -RUN yarn install - -COPY . /workspace diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 60f0e7a..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint', 'unused-imports', 'prettier'], - rules: { - 'no-unused-vars': 'off', - 'prettier/prettier': 'error', - 'unused-imports/no-unused-imports': 'error', - }, - root: true, -}; diff --git a/jest.setup.ts b/jest.setup.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/mcp-server/src/did-you-mean-proxy.ts b/packages/mcp-server/src/did-you-mean-proxy.ts deleted file mode 100644 index 8c1380d..0000000 --- a/packages/mcp-server/src/did-you-mean-proxy.ts +++ /dev/null @@ -1,356 +0,0 @@ -import Fuse from 'fuse.js'; - -const allKinds = ['string', 'number', 'boolean', 'object', 'array', 'method', 'constructor'] as const; -type Kind = (typeof allKinds)[number]; - -const nodeInspect = Symbol.for('nodejs.util.inspect.custom'); -const denoInspect = Symbol.for('Deno.customInspect'); - -const stringMethods = new Set(Reflect.ownKeys(String.prototype).filter((k) => typeof k === 'string')); -const numberMethods = new Set(Reflect.ownKeys(Number.prototype).filter((k) => typeof k === 'string')); -const arrayMethods = new Set(Reflect.ownKeys(Array.prototype).filter((k) => typeof k === 'string')); - -function getApplyKinds(name?: string | symbol): readonly Kind[] { - if (!name || name === nodeInspect || name === denoInspect) { - return allKinds; - } - - if (name === Symbol.toPrimitive || name === 'toString' || name === 'valueOf') { - return ['string', 'number', 'boolean']; - } - - if (name === Symbol.iterator) { - return ['array']; - } - - if (typeof name !== 'string') { - return ['method']; - } - - const kinds: Kind[] = []; - if (stringMethods.has(name)) { - kinds.push('string'); - } else if (numberMethods.has(name)) { - kinds.push('number'); - } else if (arrayMethods.has(name)) { - kinds.push('array'); - } - - return kinds.length > 0 ? kinds : ['method']; -} - -type KindPaths = Record; - -function traverseKinds( - obj: object, - path: string = '', - result: KindPaths = { - string: [], - number: [], - boolean: [], - object: [], - array: [], - method: [], - constructor: [], - }, -): KindPaths { - while (obj !== null) { - for (const key of Reflect.ownKeys(obj)) { - if (typeof key !== 'string') { - continue; - } - - if (key === 'constructor') { - continue; - } - - if (!/^[a-zA-Z]/.test(key)) { - continue; - } - - const value = Reflect.get(obj, key); - let kind: Kind; - - switch (typeof value) { - case 'string': { - kind = 'string'; - break; - } - case 'number': - case 'bigint': { - kind = 'number'; - break; - } - case 'boolean': { - kind = 'boolean'; - break; - } - case 'object': { - if (value === null) { - continue; - } - kind = Array.isArray(value) ? 'array' : 'object'; - break; - } - case 'function': { - kind = - key === value.name && value.name === value.prototype?.constructor?.name ? - 'constructor' - : 'method'; - break; - } - default: { - continue; - } - } - - const fullKey = path ? `${path}.${key}` : key; - result[kind].push(fullKey); - - if (kind === 'object') { - traverseKinds(value, fullKey, result); - } else if (kind === 'array' && value.length > 0) { - traverseKinds(value[0], `${fullKey}[]`, result); - } - } - - obj = Object.getPrototypeOf(obj); - if (obj === Object.prototype || obj === Array.prototype) { - break; - } - } - - return result; -} - -export type MakeError = (props: { - expected: readonly Kind[]; - rootPath: (string | symbol)[]; - path: (string | symbol)[]; - suggestions: { item: string; score: number }[]; -}) => string; - -export type ProxyConfig = { - /** - * Whether to also proxy the return values. They will be proxied with - * the same config, except the root path will be blank. Defaults to true. - */ - proxyReturn?: boolean; - /** - * The path to the root object, prepended to path suggestions. For example, - * if this is set to ['client'], then suggestions will be 'client.repos.list', - * 'client.users.list', etc. - */ - rootPath?: (string | symbol)[]; - /** - * Customize the error message to be thrown. The root path will not be - * prepended to either the path or the suggestions. - */ - makeSuggestionError?: MakeError; -}; - -function shouldProxy(value: unknown): value is NonNullable { - return value !== null && (typeof value === 'object' || typeof value === 'function'); -} - -const emptyTargetSymbol = Symbol.for('did-you-mean-proxy.emptyTargetPath'); - -type EmptyTarget = { - [emptyTargetSymbol]: { - getError: () => string; - }; -}; -type EmptyTargetInfo = EmptyTarget[typeof emptyTargetSymbol]; - -/** - * We use a special empty target so we can catch calls and constructions. - * Also useful for de-proxying in the end; if we get an empty target, we know - * we can throw an error. - */ -function createEmptyTarget(info: EmptyTargetInfo): EmptyTarget { - const emptyTarget = function () {} as any; - emptyTarget[nodeInspect] = () => { - throw info.getError(); - }; - emptyTarget[denoInspect] = () => { - throw info.getError(); - }; - emptyTarget[emptyTargetSymbol] = info; - return emptyTarget; -} - -function isEmptyTarget(value: unknown): value is EmptyTarget { - return typeof value === 'function' && (value as any)[emptyTargetSymbol] !== undefined; -} - -export const defaultMakeError: MakeError = function ({ expected, rootPath, path, suggestions }) { - const rootPathString = - rootPath.length > 0 ? `${rootPath.filter((p) => typeof p === 'string').join('.')}.` : ''; - const pathString = `'${rootPathString}${path.filter((p) => typeof p === 'string').join('.')}'`; - - let header = `${pathString} does not exist.`; - if (expected.length === 1) { - const expectedType = - expected[0] === 'array' ? 'an array' - : expected[0] === 'object' ? 'an object' - : expected[0] === 'method' ? 'a function' - : `a ${expected[0]}`; - header = `${pathString} is not ${expectedType}.`; - } - - const suggestionStrings = suggestions - // TODO(sometime): thresholding? - .filter((suggestion) => suggestion.score < 1) - .slice(0, 5) - .map((suggestion) => `'${rootPathString}${suggestion.item}'`); - - let body = ''; - if (suggestionStrings.length === 1) { - body = `Did you mean ${suggestionStrings[0]}?`; - } else if (suggestionStrings.length > 1) { - const commas = suggestionStrings.slice(0, suggestionStrings.length - 1).join(', '); - body = `Did you mean ${commas}, or ${suggestionStrings[suggestionStrings.length - 1]}?`; - } - - return body ? `${header} ${body}` : header; -}; - -export const debugMakeError: MakeError = function ({ expected, path, suggestions }) { - return `path ${path.filter((p) => typeof p === 'string').join('.')}; expected ${expected.join(', ')} -${suggestions - .slice(0, 10) - .map((suggestion) => ` - [${suggestion.score.toFixed(2)}] ${suggestion.item}`) - .join('\n')} -`; -}; - -const proxyToObj = new WeakMap(); - -export function makeProxy(root: Root, config: ProxyConfig = {}): Root { - let kindPaths: KindPaths | null = null; - - config.proxyReturn ??= true; - config.rootPath ??= ['']; - config.makeSuggestionError ??= defaultMakeError; - - const { proxyReturn, rootPath, makeSuggestionError } = config; - const { rootPath: _, ...subconfig } = config; - - function makeError(pathWithRoot: (string | symbol)[], expected: readonly Kind[]) { - if (!kindPaths) { - kindPaths = traverseKinds(root); - } - - const fuse = new Fuse( - expected.flatMap((kind) => kindPaths![kind]), - { includeScore: true }, - ); - - const path = pathWithRoot.slice(rootPath.length); - const searchKey: string[] = []; - for (const key of path) { - // Convert array keys to []: - if (/^\d+$/.test(key.toString())) { - searchKey.push('[]'); - } else if (typeof key === 'string') { - searchKey.push('.'); - searchKey.push(key); - } - } - - const key = searchKey.join(''); - const suggestions = fuse.search(key.slice(1)) as { item: string; score: number }[]; - - return makeSuggestionError({ expected, rootPath, path, suggestions }); - } - - function subproxy(obj: T, path: (string | symbol)[]): T { - const handlers: ProxyHandler = { - get(target, prop, receiver) { - const newPath = [...path, prop]; - const value = Reflect.get(target, prop, receiver); - - if (value === undefined && !Reflect.has(target, prop)) { - // Some common special cases: - // - 'then' is called on a non-thenable. - // - 'toJSON' is called when it's not defined. - // In these cases, we actually want to return undefined, so we - // resolve to the top-level thing. - if (prop === 'then' || prop === 'toJSON') { - return undefined; - } - - return subproxy( - createEmptyTarget({ - getError: () => makeError(newPath, allKinds), - }), - newPath, - ); - } - - return shouldProxy(value) ? subproxy(value, newPath) : value; - }, - construct(target, args, newTarget) { - if (isEmptyTarget(target) || typeof target !== 'function') { - throw new Error(makeError(path, ['constructor'])); - } - - const result = Reflect.construct(target, args, newTarget); - - return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; - }, - apply(target, thisArg, args) { - if (isEmptyTarget(target) || typeof target !== 'function') { - throw new Error(makeError(path, getApplyKinds(path[path.length - 1]))); - } - - const correctThisArg = proxyToObj.get(thisArg) ?? thisArg; - const proxiedArgs = - proxyReturn ? args.map((arg) => (shouldProxy(arg) ? makeProxy(arg, subconfig) : arg)) : args; - const result = Reflect.apply(target, correctThisArg, proxiedArgs); - - return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; - }, - }; - - // All other traps demand a non-empty target: - for (const trap of [ - 'defineProperty', - 'has', - 'set', - 'deleteProperty', - 'ownKeys', - 'getPrototypeOf', - 'setPrototypeOf', - 'isExtensible', - 'preventExtensions', - 'getOwnPropertyDescriptor', - ] as const) { - handlers[trap] = function (target: any, ...args: any[]) { - if (isEmptyTarget(target)) { - throw new Error(makeError(path, allKinds)); - } - - return (Reflect[trap] as any)(target, ...args); - }; - } - - const proxy = new Proxy(obj, handlers); - proxyToObj.set(proxy, obj); - - return proxy; - } - - return subproxy(root, rootPath); -} - -export function deproxy(value: T): T { - // Primitives never get proxied, so these are safe: - if (typeof value !== 'object' && typeof value !== 'function') { - return value; - } - if (isEmptyTarget(value)) { - throw new Error(value[emptyTargetSymbol].getError()); - } - return proxyToObj.get(value) ?? value; -} diff --git a/src/internal/polyfill/file.node.d.ts b/src/internal/polyfill/file.node.d.ts deleted file mode 100644 index c95276d..0000000 --- a/src/internal/polyfill/file.node.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * This file polyfills the global `File` object for you if it's not already defined - * when running on Node.js - * - * This is only needed on Node.js v18 & v19. Newer versions already define `File` - * as a global. - */ - -// @ts-ignore -type nodeBuffer = typeof import('node:buffer'); -declare const File: typeof globalThis extends { File: unknown } ? (typeof globalThis)['File'] -: nodeBuffer extends { File: unknown } ? nodeBuffer['File'] -: any; -export {}; diff --git a/src/internal/polyfill/file.node.mjs b/src/internal/polyfill/file.node.mjs deleted file mode 100644 index 520dcb8..0000000 --- a/src/internal/polyfill/file.node.mjs +++ /dev/null @@ -1,9 +0,0 @@ -/** - * This file polyfills the global `File` object for you if it's not already defined - * when running on Node.js - * - * This is only needed on Node.js v18 & v19. Newer versions already define `File` - * as a global. - */ - -import './file.node.js'; diff --git a/tests/responses.test.ts b/tests/responses.test.ts deleted file mode 100644 index 47d9a1f..0000000 --- a/tests/responses.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createResponseHeaders } from 'datanewton/internal/headers'; - -describe('response parsing', () => { - // TODO: test unicode characters - test('headers are case agnostic', async () => { - const headers = createResponseHeaders(new Headers({ 'Content-Type': 'foo', Accept: 'text/plain' })); - expect(headers['content-type']).toEqual('foo'); - expect(headers['Content-type']).toEqual('foo'); - expect(headers['Content-Type']).toEqual('foo'); - expect(headers['accept']).toEqual('text/plain'); - expect(headers['Accept']).toEqual('text/plain'); - expect(headers['Hello-World']).toBeUndefined(); - }); - - test('duplicate headers are concatenated', () => { - const headers = createResponseHeaders( - new Headers([ - ['Content-Type', 'text/xml'], - ['Content-Type', 'application/json'], - ]), - ); - expect(headers['content-type']).toBe('text/xml, application/json'); - }); -}); From d7df03a0cd7a5522e3dfff87f4b07ea60e16b260 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 05:00:05 +0000 Subject: [PATCH 24/31] fix(mcp): correct code tool API endpoint --- .devcontainer/Dockerfile | 23 ++ .eslintrc.js | 10 + jest.setup.ts | 0 packages/mcp-server/src/code-tool.ts | 2 +- packages/mcp-server/src/did-you-mean-proxy.ts | 356 ++++++++++++++++++ src/internal/polyfill/file.node.d.ts | 14 + src/internal/polyfill/file.node.mjs | 9 + tests/responses.test.ts | 24 ++ 8 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .eslintrc.js create mode 100644 jest.setup.ts create mode 100644 packages/mcp-server/src/did-you-mean-proxy.ts create mode 100644 src/internal/polyfill/file.node.d.ts create mode 100644 src/internal/polyfill/file.node.mjs create mode 100644 tests/responses.test.ts diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..8ea34be --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 +FROM debian:bookworm-slim AS stainless + +RUN apt-get update && apt-get install -y \ + nodejs \ + npm \ + yarnpkg \ + && apt-get clean autoclean + +# Ensure UTF-8 encoding +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 + +# Yarn +RUN ln -sf /usr/bin/yarnpkg /usr/bin/yarn + +WORKDIR /workspace + +COPY package.json yarn.lock /workspace/ + +RUN yarn install + +COPY . /workspace diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..60f0e7a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'unused-imports', 'prettier'], + rules: { + 'no-unused-vars': 'off', + 'prettier/prettier': 'error', + 'unused-imports/no-unused-imports': 'error', + }, + root: true, +}; diff --git a/jest.setup.ts b/jest.setup.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 0e0e9da..05f0895 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -28,7 +28,7 @@ export async function codeTool() { // will allow you to run code-mode queries against non-published versions of your SDK. const stainlessAPIKey = readEnv('STAINLESS_API_KEY'); const codeModeEndpoint = - readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool/'; + readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool'; const res = await fetch(codeModeEndpoint, { method: 'POST', diff --git a/packages/mcp-server/src/did-you-mean-proxy.ts b/packages/mcp-server/src/did-you-mean-proxy.ts new file mode 100644 index 0000000..8c1380d --- /dev/null +++ b/packages/mcp-server/src/did-you-mean-proxy.ts @@ -0,0 +1,356 @@ +import Fuse from 'fuse.js'; + +const allKinds = ['string', 'number', 'boolean', 'object', 'array', 'method', 'constructor'] as const; +type Kind = (typeof allKinds)[number]; + +const nodeInspect = Symbol.for('nodejs.util.inspect.custom'); +const denoInspect = Symbol.for('Deno.customInspect'); + +const stringMethods = new Set(Reflect.ownKeys(String.prototype).filter((k) => typeof k === 'string')); +const numberMethods = new Set(Reflect.ownKeys(Number.prototype).filter((k) => typeof k === 'string')); +const arrayMethods = new Set(Reflect.ownKeys(Array.prototype).filter((k) => typeof k === 'string')); + +function getApplyKinds(name?: string | symbol): readonly Kind[] { + if (!name || name === nodeInspect || name === denoInspect) { + return allKinds; + } + + if (name === Symbol.toPrimitive || name === 'toString' || name === 'valueOf') { + return ['string', 'number', 'boolean']; + } + + if (name === Symbol.iterator) { + return ['array']; + } + + if (typeof name !== 'string') { + return ['method']; + } + + const kinds: Kind[] = []; + if (stringMethods.has(name)) { + kinds.push('string'); + } else if (numberMethods.has(name)) { + kinds.push('number'); + } else if (arrayMethods.has(name)) { + kinds.push('array'); + } + + return kinds.length > 0 ? kinds : ['method']; +} + +type KindPaths = Record; + +function traverseKinds( + obj: object, + path: string = '', + result: KindPaths = { + string: [], + number: [], + boolean: [], + object: [], + array: [], + method: [], + constructor: [], + }, +): KindPaths { + while (obj !== null) { + for (const key of Reflect.ownKeys(obj)) { + if (typeof key !== 'string') { + continue; + } + + if (key === 'constructor') { + continue; + } + + if (!/^[a-zA-Z]/.test(key)) { + continue; + } + + const value = Reflect.get(obj, key); + let kind: Kind; + + switch (typeof value) { + case 'string': { + kind = 'string'; + break; + } + case 'number': + case 'bigint': { + kind = 'number'; + break; + } + case 'boolean': { + kind = 'boolean'; + break; + } + case 'object': { + if (value === null) { + continue; + } + kind = Array.isArray(value) ? 'array' : 'object'; + break; + } + case 'function': { + kind = + key === value.name && value.name === value.prototype?.constructor?.name ? + 'constructor' + : 'method'; + break; + } + default: { + continue; + } + } + + const fullKey = path ? `${path}.${key}` : key; + result[kind].push(fullKey); + + if (kind === 'object') { + traverseKinds(value, fullKey, result); + } else if (kind === 'array' && value.length > 0) { + traverseKinds(value[0], `${fullKey}[]`, result); + } + } + + obj = Object.getPrototypeOf(obj); + if (obj === Object.prototype || obj === Array.prototype) { + break; + } + } + + return result; +} + +export type MakeError = (props: { + expected: readonly Kind[]; + rootPath: (string | symbol)[]; + path: (string | symbol)[]; + suggestions: { item: string; score: number }[]; +}) => string; + +export type ProxyConfig = { + /** + * Whether to also proxy the return values. They will be proxied with + * the same config, except the root path will be blank. Defaults to true. + */ + proxyReturn?: boolean; + /** + * The path to the root object, prepended to path suggestions. For example, + * if this is set to ['client'], then suggestions will be 'client.repos.list', + * 'client.users.list', etc. + */ + rootPath?: (string | symbol)[]; + /** + * Customize the error message to be thrown. The root path will not be + * prepended to either the path or the suggestions. + */ + makeSuggestionError?: MakeError; +}; + +function shouldProxy(value: unknown): value is NonNullable { + return value !== null && (typeof value === 'object' || typeof value === 'function'); +} + +const emptyTargetSymbol = Symbol.for('did-you-mean-proxy.emptyTargetPath'); + +type EmptyTarget = { + [emptyTargetSymbol]: { + getError: () => string; + }; +}; +type EmptyTargetInfo = EmptyTarget[typeof emptyTargetSymbol]; + +/** + * We use a special empty target so we can catch calls and constructions. + * Also useful for de-proxying in the end; if we get an empty target, we know + * we can throw an error. + */ +function createEmptyTarget(info: EmptyTargetInfo): EmptyTarget { + const emptyTarget = function () {} as any; + emptyTarget[nodeInspect] = () => { + throw info.getError(); + }; + emptyTarget[denoInspect] = () => { + throw info.getError(); + }; + emptyTarget[emptyTargetSymbol] = info; + return emptyTarget; +} + +function isEmptyTarget(value: unknown): value is EmptyTarget { + return typeof value === 'function' && (value as any)[emptyTargetSymbol] !== undefined; +} + +export const defaultMakeError: MakeError = function ({ expected, rootPath, path, suggestions }) { + const rootPathString = + rootPath.length > 0 ? `${rootPath.filter((p) => typeof p === 'string').join('.')}.` : ''; + const pathString = `'${rootPathString}${path.filter((p) => typeof p === 'string').join('.')}'`; + + let header = `${pathString} does not exist.`; + if (expected.length === 1) { + const expectedType = + expected[0] === 'array' ? 'an array' + : expected[0] === 'object' ? 'an object' + : expected[0] === 'method' ? 'a function' + : `a ${expected[0]}`; + header = `${pathString} is not ${expectedType}.`; + } + + const suggestionStrings = suggestions + // TODO(sometime): thresholding? + .filter((suggestion) => suggestion.score < 1) + .slice(0, 5) + .map((suggestion) => `'${rootPathString}${suggestion.item}'`); + + let body = ''; + if (suggestionStrings.length === 1) { + body = `Did you mean ${suggestionStrings[0]}?`; + } else if (suggestionStrings.length > 1) { + const commas = suggestionStrings.slice(0, suggestionStrings.length - 1).join(', '); + body = `Did you mean ${commas}, or ${suggestionStrings[suggestionStrings.length - 1]}?`; + } + + return body ? `${header} ${body}` : header; +}; + +export const debugMakeError: MakeError = function ({ expected, path, suggestions }) { + return `path ${path.filter((p) => typeof p === 'string').join('.')}; expected ${expected.join(', ')} +${suggestions + .slice(0, 10) + .map((suggestion) => ` - [${suggestion.score.toFixed(2)}] ${suggestion.item}`) + .join('\n')} +`; +}; + +const proxyToObj = new WeakMap(); + +export function makeProxy(root: Root, config: ProxyConfig = {}): Root { + let kindPaths: KindPaths | null = null; + + config.proxyReturn ??= true; + config.rootPath ??= ['']; + config.makeSuggestionError ??= defaultMakeError; + + const { proxyReturn, rootPath, makeSuggestionError } = config; + const { rootPath: _, ...subconfig } = config; + + function makeError(pathWithRoot: (string | symbol)[], expected: readonly Kind[]) { + if (!kindPaths) { + kindPaths = traverseKinds(root); + } + + const fuse = new Fuse( + expected.flatMap((kind) => kindPaths![kind]), + { includeScore: true }, + ); + + const path = pathWithRoot.slice(rootPath.length); + const searchKey: string[] = []; + for (const key of path) { + // Convert array keys to []: + if (/^\d+$/.test(key.toString())) { + searchKey.push('[]'); + } else if (typeof key === 'string') { + searchKey.push('.'); + searchKey.push(key); + } + } + + const key = searchKey.join(''); + const suggestions = fuse.search(key.slice(1)) as { item: string; score: number }[]; + + return makeSuggestionError({ expected, rootPath, path, suggestions }); + } + + function subproxy(obj: T, path: (string | symbol)[]): T { + const handlers: ProxyHandler = { + get(target, prop, receiver) { + const newPath = [...path, prop]; + const value = Reflect.get(target, prop, receiver); + + if (value === undefined && !Reflect.has(target, prop)) { + // Some common special cases: + // - 'then' is called on a non-thenable. + // - 'toJSON' is called when it's not defined. + // In these cases, we actually want to return undefined, so we + // resolve to the top-level thing. + if (prop === 'then' || prop === 'toJSON') { + return undefined; + } + + return subproxy( + createEmptyTarget({ + getError: () => makeError(newPath, allKinds), + }), + newPath, + ); + } + + return shouldProxy(value) ? subproxy(value, newPath) : value; + }, + construct(target, args, newTarget) { + if (isEmptyTarget(target) || typeof target !== 'function') { + throw new Error(makeError(path, ['constructor'])); + } + + const result = Reflect.construct(target, args, newTarget); + + return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; + }, + apply(target, thisArg, args) { + if (isEmptyTarget(target) || typeof target !== 'function') { + throw new Error(makeError(path, getApplyKinds(path[path.length - 1]))); + } + + const correctThisArg = proxyToObj.get(thisArg) ?? thisArg; + const proxiedArgs = + proxyReturn ? args.map((arg) => (shouldProxy(arg) ? makeProxy(arg, subconfig) : arg)) : args; + const result = Reflect.apply(target, correctThisArg, proxiedArgs); + + return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; + }, + }; + + // All other traps demand a non-empty target: + for (const trap of [ + 'defineProperty', + 'has', + 'set', + 'deleteProperty', + 'ownKeys', + 'getPrototypeOf', + 'setPrototypeOf', + 'isExtensible', + 'preventExtensions', + 'getOwnPropertyDescriptor', + ] as const) { + handlers[trap] = function (target: any, ...args: any[]) { + if (isEmptyTarget(target)) { + throw new Error(makeError(path, allKinds)); + } + + return (Reflect[trap] as any)(target, ...args); + }; + } + + const proxy = new Proxy(obj, handlers); + proxyToObj.set(proxy, obj); + + return proxy; + } + + return subproxy(root, rootPath); +} + +export function deproxy(value: T): T { + // Primitives never get proxied, so these are safe: + if (typeof value !== 'object' && typeof value !== 'function') { + return value; + } + if (isEmptyTarget(value)) { + throw new Error(value[emptyTargetSymbol].getError()); + } + return proxyToObj.get(value) ?? value; +} diff --git a/src/internal/polyfill/file.node.d.ts b/src/internal/polyfill/file.node.d.ts new file mode 100644 index 0000000..c95276d --- /dev/null +++ b/src/internal/polyfill/file.node.d.ts @@ -0,0 +1,14 @@ +/** + * This file polyfills the global `File` object for you if it's not already defined + * when running on Node.js + * + * This is only needed on Node.js v18 & v19. Newer versions already define `File` + * as a global. + */ + +// @ts-ignore +type nodeBuffer = typeof import('node:buffer'); +declare const File: typeof globalThis extends { File: unknown } ? (typeof globalThis)['File'] +: nodeBuffer extends { File: unknown } ? nodeBuffer['File'] +: any; +export {}; diff --git a/src/internal/polyfill/file.node.mjs b/src/internal/polyfill/file.node.mjs new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/src/internal/polyfill/file.node.mjs @@ -0,0 +1,9 @@ +/** + * This file polyfills the global `File` object for you if it's not already defined + * when running on Node.js + * + * This is only needed on Node.js v18 & v19. Newer versions already define `File` + * as a global. + */ + +import './file.node.js'; diff --git a/tests/responses.test.ts b/tests/responses.test.ts new file mode 100644 index 0000000..47d9a1f --- /dev/null +++ b/tests/responses.test.ts @@ -0,0 +1,24 @@ +import { createResponseHeaders } from 'datanewton/internal/headers'; + +describe('response parsing', () => { + // TODO: test unicode characters + test('headers are case agnostic', async () => { + const headers = createResponseHeaders(new Headers({ 'Content-Type': 'foo', Accept: 'text/plain' })); + expect(headers['content-type']).toEqual('foo'); + expect(headers['Content-type']).toEqual('foo'); + expect(headers['Content-Type']).toEqual('foo'); + expect(headers['accept']).toEqual('text/plain'); + expect(headers['Accept']).toEqual('text/plain'); + expect(headers['Hello-World']).toBeUndefined(); + }); + + test('duplicate headers are concatenated', () => { + const headers = createResponseHeaders( + new Headers([ + ['Content-Type', 'text/xml'], + ['Content-Type', 'application/json'], + ]), + ); + expect(headers['content-type']).toBe('text/xml, application/json'); + }); +}); From b9aa6fcd48973b44d9e719ec0d100a18379d9384 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 05:00:45 +0000 Subject: [PATCH 25/31] chore(internal): codegen related update --- .devcontainer/Dockerfile | 23 -- .eslintrc.js | 10 - jest.setup.ts | 0 packages/mcp-server/src/did-you-mean-proxy.ts | 356 ------------------ src/internal/polyfill/file.node.d.ts | 14 - src/internal/polyfill/file.node.mjs | 9 - tests/responses.test.ts | 24 -- 7 files changed, 436 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .eslintrc.js delete mode 100644 jest.setup.ts delete mode 100644 packages/mcp-server/src/did-you-mean-proxy.ts delete mode 100644 src/internal/polyfill/file.node.d.ts delete mode 100644 src/internal/polyfill/file.node.mjs delete mode 100644 tests/responses.test.ts diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 8ea34be..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM debian:bookworm-slim AS stainless - -RUN apt-get update && apt-get install -y \ - nodejs \ - npm \ - yarnpkg \ - && apt-get clean autoclean - -# Ensure UTF-8 encoding -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 - -# Yarn -RUN ln -sf /usr/bin/yarnpkg /usr/bin/yarn - -WORKDIR /workspace - -COPY package.json yarn.lock /workspace/ - -RUN yarn install - -COPY . /workspace diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 60f0e7a..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint', 'unused-imports', 'prettier'], - rules: { - 'no-unused-vars': 'off', - 'prettier/prettier': 'error', - 'unused-imports/no-unused-imports': 'error', - }, - root: true, -}; diff --git a/jest.setup.ts b/jest.setup.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/mcp-server/src/did-you-mean-proxy.ts b/packages/mcp-server/src/did-you-mean-proxy.ts deleted file mode 100644 index 8c1380d..0000000 --- a/packages/mcp-server/src/did-you-mean-proxy.ts +++ /dev/null @@ -1,356 +0,0 @@ -import Fuse from 'fuse.js'; - -const allKinds = ['string', 'number', 'boolean', 'object', 'array', 'method', 'constructor'] as const; -type Kind = (typeof allKinds)[number]; - -const nodeInspect = Symbol.for('nodejs.util.inspect.custom'); -const denoInspect = Symbol.for('Deno.customInspect'); - -const stringMethods = new Set(Reflect.ownKeys(String.prototype).filter((k) => typeof k === 'string')); -const numberMethods = new Set(Reflect.ownKeys(Number.prototype).filter((k) => typeof k === 'string')); -const arrayMethods = new Set(Reflect.ownKeys(Array.prototype).filter((k) => typeof k === 'string')); - -function getApplyKinds(name?: string | symbol): readonly Kind[] { - if (!name || name === nodeInspect || name === denoInspect) { - return allKinds; - } - - if (name === Symbol.toPrimitive || name === 'toString' || name === 'valueOf') { - return ['string', 'number', 'boolean']; - } - - if (name === Symbol.iterator) { - return ['array']; - } - - if (typeof name !== 'string') { - return ['method']; - } - - const kinds: Kind[] = []; - if (stringMethods.has(name)) { - kinds.push('string'); - } else if (numberMethods.has(name)) { - kinds.push('number'); - } else if (arrayMethods.has(name)) { - kinds.push('array'); - } - - return kinds.length > 0 ? kinds : ['method']; -} - -type KindPaths = Record; - -function traverseKinds( - obj: object, - path: string = '', - result: KindPaths = { - string: [], - number: [], - boolean: [], - object: [], - array: [], - method: [], - constructor: [], - }, -): KindPaths { - while (obj !== null) { - for (const key of Reflect.ownKeys(obj)) { - if (typeof key !== 'string') { - continue; - } - - if (key === 'constructor') { - continue; - } - - if (!/^[a-zA-Z]/.test(key)) { - continue; - } - - const value = Reflect.get(obj, key); - let kind: Kind; - - switch (typeof value) { - case 'string': { - kind = 'string'; - break; - } - case 'number': - case 'bigint': { - kind = 'number'; - break; - } - case 'boolean': { - kind = 'boolean'; - break; - } - case 'object': { - if (value === null) { - continue; - } - kind = Array.isArray(value) ? 'array' : 'object'; - break; - } - case 'function': { - kind = - key === value.name && value.name === value.prototype?.constructor?.name ? - 'constructor' - : 'method'; - break; - } - default: { - continue; - } - } - - const fullKey = path ? `${path}.${key}` : key; - result[kind].push(fullKey); - - if (kind === 'object') { - traverseKinds(value, fullKey, result); - } else if (kind === 'array' && value.length > 0) { - traverseKinds(value[0], `${fullKey}[]`, result); - } - } - - obj = Object.getPrototypeOf(obj); - if (obj === Object.prototype || obj === Array.prototype) { - break; - } - } - - return result; -} - -export type MakeError = (props: { - expected: readonly Kind[]; - rootPath: (string | symbol)[]; - path: (string | symbol)[]; - suggestions: { item: string; score: number }[]; -}) => string; - -export type ProxyConfig = { - /** - * Whether to also proxy the return values. They will be proxied with - * the same config, except the root path will be blank. Defaults to true. - */ - proxyReturn?: boolean; - /** - * The path to the root object, prepended to path suggestions. For example, - * if this is set to ['client'], then suggestions will be 'client.repos.list', - * 'client.users.list', etc. - */ - rootPath?: (string | symbol)[]; - /** - * Customize the error message to be thrown. The root path will not be - * prepended to either the path or the suggestions. - */ - makeSuggestionError?: MakeError; -}; - -function shouldProxy(value: unknown): value is NonNullable { - return value !== null && (typeof value === 'object' || typeof value === 'function'); -} - -const emptyTargetSymbol = Symbol.for('did-you-mean-proxy.emptyTargetPath'); - -type EmptyTarget = { - [emptyTargetSymbol]: { - getError: () => string; - }; -}; -type EmptyTargetInfo = EmptyTarget[typeof emptyTargetSymbol]; - -/** - * We use a special empty target so we can catch calls and constructions. - * Also useful for de-proxying in the end; if we get an empty target, we know - * we can throw an error. - */ -function createEmptyTarget(info: EmptyTargetInfo): EmptyTarget { - const emptyTarget = function () {} as any; - emptyTarget[nodeInspect] = () => { - throw info.getError(); - }; - emptyTarget[denoInspect] = () => { - throw info.getError(); - }; - emptyTarget[emptyTargetSymbol] = info; - return emptyTarget; -} - -function isEmptyTarget(value: unknown): value is EmptyTarget { - return typeof value === 'function' && (value as any)[emptyTargetSymbol] !== undefined; -} - -export const defaultMakeError: MakeError = function ({ expected, rootPath, path, suggestions }) { - const rootPathString = - rootPath.length > 0 ? `${rootPath.filter((p) => typeof p === 'string').join('.')}.` : ''; - const pathString = `'${rootPathString}${path.filter((p) => typeof p === 'string').join('.')}'`; - - let header = `${pathString} does not exist.`; - if (expected.length === 1) { - const expectedType = - expected[0] === 'array' ? 'an array' - : expected[0] === 'object' ? 'an object' - : expected[0] === 'method' ? 'a function' - : `a ${expected[0]}`; - header = `${pathString} is not ${expectedType}.`; - } - - const suggestionStrings = suggestions - // TODO(sometime): thresholding? - .filter((suggestion) => suggestion.score < 1) - .slice(0, 5) - .map((suggestion) => `'${rootPathString}${suggestion.item}'`); - - let body = ''; - if (suggestionStrings.length === 1) { - body = `Did you mean ${suggestionStrings[0]}?`; - } else if (suggestionStrings.length > 1) { - const commas = suggestionStrings.slice(0, suggestionStrings.length - 1).join(', '); - body = `Did you mean ${commas}, or ${suggestionStrings[suggestionStrings.length - 1]}?`; - } - - return body ? `${header} ${body}` : header; -}; - -export const debugMakeError: MakeError = function ({ expected, path, suggestions }) { - return `path ${path.filter((p) => typeof p === 'string').join('.')}; expected ${expected.join(', ')} -${suggestions - .slice(0, 10) - .map((suggestion) => ` - [${suggestion.score.toFixed(2)}] ${suggestion.item}`) - .join('\n')} -`; -}; - -const proxyToObj = new WeakMap(); - -export function makeProxy(root: Root, config: ProxyConfig = {}): Root { - let kindPaths: KindPaths | null = null; - - config.proxyReturn ??= true; - config.rootPath ??= ['']; - config.makeSuggestionError ??= defaultMakeError; - - const { proxyReturn, rootPath, makeSuggestionError } = config; - const { rootPath: _, ...subconfig } = config; - - function makeError(pathWithRoot: (string | symbol)[], expected: readonly Kind[]) { - if (!kindPaths) { - kindPaths = traverseKinds(root); - } - - const fuse = new Fuse( - expected.flatMap((kind) => kindPaths![kind]), - { includeScore: true }, - ); - - const path = pathWithRoot.slice(rootPath.length); - const searchKey: string[] = []; - for (const key of path) { - // Convert array keys to []: - if (/^\d+$/.test(key.toString())) { - searchKey.push('[]'); - } else if (typeof key === 'string') { - searchKey.push('.'); - searchKey.push(key); - } - } - - const key = searchKey.join(''); - const suggestions = fuse.search(key.slice(1)) as { item: string; score: number }[]; - - return makeSuggestionError({ expected, rootPath, path, suggestions }); - } - - function subproxy(obj: T, path: (string | symbol)[]): T { - const handlers: ProxyHandler = { - get(target, prop, receiver) { - const newPath = [...path, prop]; - const value = Reflect.get(target, prop, receiver); - - if (value === undefined && !Reflect.has(target, prop)) { - // Some common special cases: - // - 'then' is called on a non-thenable. - // - 'toJSON' is called when it's not defined. - // In these cases, we actually want to return undefined, so we - // resolve to the top-level thing. - if (prop === 'then' || prop === 'toJSON') { - return undefined; - } - - return subproxy( - createEmptyTarget({ - getError: () => makeError(newPath, allKinds), - }), - newPath, - ); - } - - return shouldProxy(value) ? subproxy(value, newPath) : value; - }, - construct(target, args, newTarget) { - if (isEmptyTarget(target) || typeof target !== 'function') { - throw new Error(makeError(path, ['constructor'])); - } - - const result = Reflect.construct(target, args, newTarget); - - return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; - }, - apply(target, thisArg, args) { - if (isEmptyTarget(target) || typeof target !== 'function') { - throw new Error(makeError(path, getApplyKinds(path[path.length - 1]))); - } - - const correctThisArg = proxyToObj.get(thisArg) ?? thisArg; - const proxiedArgs = - proxyReturn ? args.map((arg) => (shouldProxy(arg) ? makeProxy(arg, subconfig) : arg)) : args; - const result = Reflect.apply(target, correctThisArg, proxiedArgs); - - return proxyReturn && shouldProxy(result) ? makeProxy(result, subconfig) : result; - }, - }; - - // All other traps demand a non-empty target: - for (const trap of [ - 'defineProperty', - 'has', - 'set', - 'deleteProperty', - 'ownKeys', - 'getPrototypeOf', - 'setPrototypeOf', - 'isExtensible', - 'preventExtensions', - 'getOwnPropertyDescriptor', - ] as const) { - handlers[trap] = function (target: any, ...args: any[]) { - if (isEmptyTarget(target)) { - throw new Error(makeError(path, allKinds)); - } - - return (Reflect[trap] as any)(target, ...args); - }; - } - - const proxy = new Proxy(obj, handlers); - proxyToObj.set(proxy, obj); - - return proxy; - } - - return subproxy(root, rootPath); -} - -export function deproxy(value: T): T { - // Primitives never get proxied, so these are safe: - if (typeof value !== 'object' && typeof value !== 'function') { - return value; - } - if (isEmptyTarget(value)) { - throw new Error(value[emptyTargetSymbol].getError()); - } - return proxyToObj.get(value) ?? value; -} diff --git a/src/internal/polyfill/file.node.d.ts b/src/internal/polyfill/file.node.d.ts deleted file mode 100644 index c95276d..0000000 --- a/src/internal/polyfill/file.node.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * This file polyfills the global `File` object for you if it's not already defined - * when running on Node.js - * - * This is only needed on Node.js v18 & v19. Newer versions already define `File` - * as a global. - */ - -// @ts-ignore -type nodeBuffer = typeof import('node:buffer'); -declare const File: typeof globalThis extends { File: unknown } ? (typeof globalThis)['File'] -: nodeBuffer extends { File: unknown } ? nodeBuffer['File'] -: any; -export {}; diff --git a/src/internal/polyfill/file.node.mjs b/src/internal/polyfill/file.node.mjs deleted file mode 100644 index 520dcb8..0000000 --- a/src/internal/polyfill/file.node.mjs +++ /dev/null @@ -1,9 +0,0 @@ -/** - * This file polyfills the global `File` object for you if it's not already defined - * when running on Node.js - * - * This is only needed on Node.js v18 & v19. Newer versions already define `File` - * as a global. - */ - -import './file.node.js'; diff --git a/tests/responses.test.ts b/tests/responses.test.ts deleted file mode 100644 index 47d9a1f..0000000 --- a/tests/responses.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createResponseHeaders } from 'datanewton/internal/headers'; - -describe('response parsing', () => { - // TODO: test unicode characters - test('headers are case agnostic', async () => { - const headers = createResponseHeaders(new Headers({ 'Content-Type': 'foo', Accept: 'text/plain' })); - expect(headers['content-type']).toEqual('foo'); - expect(headers['Content-type']).toEqual('foo'); - expect(headers['Content-Type']).toEqual('foo'); - expect(headers['accept']).toEqual('text/plain'); - expect(headers['Accept']).toEqual('text/plain'); - expect(headers['Hello-World']).toBeUndefined(); - }); - - test('duplicate headers are concatenated', () => { - const headers = createResponseHeaders( - new Headers([ - ['Content-Type', 'text/xml'], - ['Content-Type', 'application/json'], - ]), - ); - expect(headers['content-type']).toBe('text/xml, application/json'); - }); -}); From 5555bbd7d126b51692f036eebe12ad0840c54ef4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 05:41:44 +0000 Subject: [PATCH 26/31] fix(mcp): add client instantiation options to code tool --- packages/mcp-server/src/code-tool.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 05f0895..7f110f4 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -39,6 +39,7 @@ export async function codeTool() { }, body: JSON.stringify({ project_name: 'datanewton', + client_opts: {}, code, }), }); From 7f2e41df2d8a8697cc6f9617b34f08e40770685e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 05:42:32 +0000 Subject: [PATCH 27/31] chore(mcp): update lockfile --- packages/mcp-server/yarn.lock | 381 ++++++++++++++++++++++++++++++++-- 1 file changed, 364 insertions(+), 17 deletions(-) diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock index 2bb21c6..38be884 100644 --- a/packages/mcp-server/yarn.lock +++ b/packages/mcp-server/yarn.lock @@ -10,6 +10,20 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@anthropic-ai/mcpb@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@anthropic-ai/mcpb/-/mcpb-1.1.0.tgz#1af18de2ab9499d321d6310d0be095f5fef5161b" + integrity sha512-nOnhG1eNpGKSIDv6lt3xsI3w2p2k0D/rPTMGXXugLovCEaJ7svh8XMfCe145vs8qo384t8wKbokWAvx9PkQMDA== + dependencies: + "@inquirer/prompts" "^6.0.1" + commander "^13.1.0" + fflate "^0.8.2" + galactus "^1.0.0" + ignore "^7.0.5" + node-forge "^1.3.1" + pretty-bytes "^5.6.0" + zod "^3.25.67" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" @@ -336,6 +350,144 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@inquirer/checkbox@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-3.0.1.tgz#0a57f704265f78c36e17f07e421b98efb4b9867b" + integrity sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + +"@inquirer/confirm@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-4.0.1.tgz#9106d6bffa0b2fdd0e4f60319b6f04f2e06e6e25" + integrity sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + +"@inquirer/core@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-9.2.1.tgz#677c49dee399c9063f31e0c93f0f37bddc67add1" + integrity sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg== + dependencies: + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + "@types/mute-stream" "^0.0.4" + "@types/node" "^22.5.5" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/editor@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-3.0.1.tgz#d109f21e050af6b960725388cb1c04214ed7c7bc" + integrity sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + external-editor "^3.1.0" + +"@inquirer/expand@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-3.0.1.tgz#aed9183cac4d12811be47a4a895ea8e82a17e22c" + integrity sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/figures@^1.0.6": + version "1.0.15" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.15.tgz#dbb49ed80df11df74268023b496ac5d9acd22b3a" + integrity sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g== + +"@inquirer/input@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-3.0.1.tgz#de63d49e516487388508d42049deb70f2cb5f28e" + integrity sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + +"@inquirer/number@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-2.0.1.tgz#b9863080d02ab7dc2e56e16433d83abea0f2a980" + integrity sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + +"@inquirer/password@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-3.0.1.tgz#2a9a9143591088336bbd573bcb05d5bf080dbf87" + integrity sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + ansi-escapes "^4.3.2" + +"@inquirer/prompts@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-6.0.1.tgz#43f5c0ed35c5ebfe52f1d43d46da2d363d950071" + integrity sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A== + dependencies: + "@inquirer/checkbox" "^3.0.1" + "@inquirer/confirm" "^4.0.1" + "@inquirer/editor" "^3.0.1" + "@inquirer/expand" "^3.0.1" + "@inquirer/input" "^3.0.1" + "@inquirer/number" "^2.0.1" + "@inquirer/password" "^3.0.1" + "@inquirer/rawlist" "^3.0.1" + "@inquirer/search" "^2.0.1" + "@inquirer/select" "^3.0.1" + +"@inquirer/rawlist@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-3.0.1.tgz#729def358419cc929045f264131878ed379e0af3" + integrity sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/search@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-2.0.1.tgz#69b774a0a826de2e27b48981d01bc5ad81e73721" + integrity sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/select@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-3.0.1.tgz#1df9ed27fb85a5f526d559ac5ce7cc4e9dc4e7ec" + integrity sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + +"@inquirer/type@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-2.0.0.tgz#08fa513dca2cb6264fe1b0a2fabade051444e3f6" + integrity sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag== + dependencies: + mute-stream "^1.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -584,12 +736,13 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@modelcontextprotocol/sdk@^1.11.5": - version "1.17.3" - resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz#cf92354220f0183d28179e96a9bf3a8f6d3211ae" - integrity sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg== +"@modelcontextprotocol/sdk@^1.24.0": + version "1.24.3" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.24.3.tgz#81a3fcc919cb4ce8630e2bcecf59759176eb331a" + integrity sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw== dependencies: - ajv "^6.12.6" + ajv "^8.17.1" + ajv-formats "^3.0.1" content-type "^1.0.5" cors "^2.8.5" cross-spawn "^7.0.5" @@ -597,10 +750,11 @@ eventsource-parser "^3.0.0" express "^5.0.1" express-rate-limit "^7.5.0" + jose "^6.1.1" pkce-challenge "^5.0.0" raw-body "^3.0.0" - zod "^3.23.8" - zod-to-json-schema "^3.24.1" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.0" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -795,6 +949,13 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== +"@types/mute-stream@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" + integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== + dependencies: + "@types/node" "*" + "@types/node@*": version "22.15.17" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.17.tgz#355ccec95f705b664e4332bb64a7f07db30b7055" @@ -802,6 +963,13 @@ dependencies: undici-types "~6.21.0" +"@types/node@^22.5.5": + version "22.19.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.2.tgz#2f0956fba46518aaf7578c84e37bddab55f85d01" + integrity sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw== + dependencies: + undici-types "~6.21.0" + "@types/qs@*", "@types/qs@^6.14.0": version "6.14.0" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" @@ -834,6 +1002,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/wrap-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" + integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -970,7 +1143,14 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.12.4, ajv@^6.12.6: +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -980,7 +1160,17 @@ ajv@^6.12.4, ajv@^6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.2.1: +ajv@^8.0.0, ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1222,6 +1412,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" @@ -1237,6 +1432,11 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -1273,6 +1473,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +commander@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" + integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1685,6 +1890,15 @@ express@^5.0.1, express@^5.1.0: type-is "^2.0.1" vary "^1.1.2" +external-editor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1716,6 +1930,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + fastq@^1.6.0: version "1.19.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" @@ -1730,6 +1949,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -1793,6 +2017,14 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== +flora-colossus@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-2.0.0.tgz#af1e85db0a8256ef05f3fb531c1235236c97220a" + integrity sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA== + dependencies: + debug "^4.3.4" + fs-extra "^10.1.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -1803,6 +2035,15 @@ fresh@^2.0.0: resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1818,6 +2059,20 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +fuse.js@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.1.0.tgz#306228b4befeee11e05b027087c2744158527d09" + integrity sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ== + +galactus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/galactus/-/galactus-1.0.0.tgz#c2615182afa0c6d0859b92e56ae36d052827db7e" + integrity sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ== + dependencies: + debug "^4.3.4" + flora-colossus "^2.0.0" + fs-extra "^10.1.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -1910,7 +2165,7 @@ gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.2.9: +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -1965,11 +2220,23 @@ iconv-lite@0.6.3, iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== +ignore@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + import-fresh@^3.2.1: version "3.3.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" @@ -2494,6 +2761,11 @@ jest@^29.4.0: import-local "^3.0.2" jest-cli "^29.7.0" +jose@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.1.3.tgz#8453d7be88af7bb7d64a0481d6a35a0145ba3ea5" + integrity sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ== + "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" @@ -2538,6 +2810,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -2548,6 +2825,15 @@ json5@^2.2.2, json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonfile@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -2721,6 +3007,11 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mute-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -2731,6 +3022,11 @@ negotiator@^1.0.0: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== +node-forge@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.3.tgz#0ad80f6333b3a0045e827ac20b7f735f93716751" + integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -2796,6 +3092,11 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + p-all@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb" @@ -2939,6 +3240,11 @@ prettier@^3.0.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -3020,6 +3326,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -3086,7 +3397,7 @@ safe-buffer@5.2.1, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -3190,6 +3501,11 @@ signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -3334,6 +3650,13 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -3474,6 +3797,11 @@ undici-types@~6.21.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -3537,6 +3865,15 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3597,22 +3934,32 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod-to-json-schema@^3.24.1, zod-to-json-schema@^3.24.5: +yoctocolors-cjs@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" + integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== + +zod-to-json-schema@^3.24.5: version "3.24.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3" integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g== +zod-to-json-schema@^3.25.0: + version "3.25.0" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz#df504c957c4fb0feff467c74d03e6aab0b013e1c" + integrity sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ== + zod-validation-error@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.1.tgz#a105723eb40299578a6a38cb86647068f6d005b1" integrity sha512-F3rdaCOHs5ViJ5YTz5zzRtfkQdMdIeKudJAoxy7yB/2ZMEHw73lmCAcQw11r7++20MyGl4WV59EVh7A9rNAyog== -zod@^3.23.8: - version "3.24.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.4.tgz#e2e2cca5faaa012d76e527d0d36622e0a90c315f" - integrity sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg== +"zod@^3.25 || ^4.0": + version "4.1.13" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.13.tgz#93699a8afe937ba96badbb0ce8be6033c0a4b6b1" + integrity sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig== -zod@^3.25.20: +zod@^3.25.20, zod@^3.25.67: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== From f1b4e0f55332e801de9ba9d1ed594890f5a5f3b7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 07:59:35 +0000 Subject: [PATCH 28/31] fix(mcp): pass base url to code tool --- packages/mcp-server/src/code-tool.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 7f110f4..5ff1e62 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -35,7 +35,10 @@ export async function codeTool() { headers: { ...(stainlessAPIKey && { Authorization: stainlessAPIKey }), 'Content-Type': 'application/json', - client_envs: JSON.stringify({ DATANEWTON_API_KEY: readEnv('DATANEWTON_API_KEY') }), + client_envs: JSON.stringify({ + DATANEWTON_API_KEY: readEnv('DATANEWTON_API_KEY'), + DATANEWTON_BASE_URL: readEnv('DATANEWTON_BASE_URL'), + }), }, body: JSON.stringify({ project_name: 'datanewton', From 5627a82b2cff281d6b1a786056c4e2bf7a154e2e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 06:44:03 +0000 Subject: [PATCH 29/31] chore(mcp)!: remove deprecated tool schemes This removes all tool schemes except for "code mode" tools. Specifically, this removes "all tools" and "dynamic tools" schemes. Additionally, this removes support for resource filtering, tags, jq filtering, and compatibility controls in MCP servers, as they are no longer necessary when using code mode. # Migration To migrate, simply modify the command used to invoke the MCP server. Currently, the only supported tool scheme is code mode. Now, starting the server with just `node /path/to/mcp/server` or `npx package-name` will invoke code tools: changing your command to one of these is likely all you will need to do. Specifically, you must remove all flags including things like --resources, --tags, --client, --tools=dynamic, etc from your invocation command. The only supported flags are now `--port`, `--transport`, `--socket`, and `--tools=docs` (specifically for "docs"). These are also the only options available for http servers. migration-effort: small --- packages/mcp-server/README.md | 409 +----- packages/mcp-server/src/code-tool.ts | 4 +- packages/mcp-server/src/compat.ts | 483 ------- packages/mcp-server/src/docs-search-tool.ts | 2 +- packages/mcp-server/src/dynamic-tools.ts | 159 --- packages/mcp-server/src/filtering.ts | 18 - packages/mcp-server/src/http.ts | 20 +- packages/mcp-server/src/index.ts | 57 +- packages/mcp-server/src/options.ts | 391 +----- packages/mcp-server/src/server.ts | 82 +- packages/mcp-server/src/stdio.ts | 5 +- packages/mcp-server/src/tools.ts | 1 - .../list-arbitration-cases.ts | 125 -- .../create-arbitration-batch-cases.ts | 144 -- .../arbitration/get-courts-arbitration.ts | 51 - .../get-dispute-categories-arbitration.ts | 51 - .../get-document-types-arbitration.ts | 51 - .../tools/bankruptcy/retrieve-bankruptcy.ts | 71 - .../create-batch-cards-by-filters.ts | 516 -------- .../tools/batch-cards/create-batch-cards.ts | 52 - .../batch-changes/create-batch-changes.ts | 116 -- .../list-blocked-bank-accounts.ts | 63 - .../list-corporate-actions.ts | 101 -- .../counterparty/retrieve-counterparty.ts | 75 -- .../list-courts-dictionary-arbitration.ts | 53 - ...spute-categories-dictionary-arbitration.ts | 53 - ...t-document-types-dictionary-arbitration.ts | 53 - .../list-lease-classifier-dictionary.ts | 51 - .../dictionary/list-licenses-dictionary.ts | 51 - .../dictionary/list-okveds-dictionary.ts | 51 - .../dictionary/list-regions-dictionary.ts | 51 - .../list-okpd2-dictionary-procurement.ts | 51 - .../filters-preview/create-filters-preview.ts | 516 -------- .../src/tools/finance/retrieve-finance.ts | 56 - .../retrieve-government-contracts-stat.ts | 81 -- .../list-government-contracts.ts | 170 --- packages/mcp-server/src/tools/index.ts | 149 --- .../retrieve-lease-classifier.ts | 51 - .../src/tools/leases/create-leases.ts | 104 -- .../src/tools/licenses/list-licenses.ts | 51 - .../mcp-server/src/tools/links/list-links.ts | 64 - .../tools/nko-reestr/retrieve-nko-reestr.ts | 63 - .../create-ogrns-by-address.ts | 65 - .../src/tools/okpd-list/list-okpd-list.ts | 63 - .../src/tools/okveds/list-okveds.ts | 51 - .../src/tools/paid-taxes/list-paid-taxes.ts | 63 - .../procurement/retrieve-okpd2-procurement.ts | 51 - .../src/tools/regions/list-regions.ts | 51 - .../mcp-server/src/tools/risks/list-risks.ts | 63 - .../sro-membership/retrieve-sro-membership.ts | 85 -- .../tools/suggestions/create-suggestions.ts | 66 - .../src/tools/tax-info/retrieve-tax-info.ts | 63 - .../create-taxpayer-statuses.ts | 61 - packages/mcp-server/src/{tools => }/types.ts | 2 +- packages/mcp-server/tests/compat.test.ts | 1166 ----------------- .../mcp-server/tests/dynamic-tools.test.ts | 185 --- packages/mcp-server/tests/options.test.ts | 510 +------ packages/mcp-server/tests/tools.test.ts | 225 ---- 58 files changed, 51 insertions(+), 7485 deletions(-) delete mode 100644 packages/mcp-server/src/compat.ts delete mode 100644 packages/mcp-server/src/dynamic-tools.ts delete mode 100644 packages/mcp-server/src/filtering.ts delete mode 100644 packages/mcp-server/src/tools.ts delete mode 100644 packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts delete mode 100644 packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts delete mode 100644 packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts delete mode 100644 packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts delete mode 100644 packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts delete mode 100644 packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts delete mode 100644 packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts delete mode 100644 packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts delete mode 100644 packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts delete mode 100644 packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts delete mode 100644 packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts delete mode 100644 packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts delete mode 100644 packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts delete mode 100644 packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts delete mode 100644 packages/mcp-server/src/tools/finance/retrieve-finance.ts delete mode 100644 packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts delete mode 100644 packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts delete mode 100644 packages/mcp-server/src/tools/index.ts delete mode 100644 packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts delete mode 100644 packages/mcp-server/src/tools/leases/create-leases.ts delete mode 100644 packages/mcp-server/src/tools/licenses/list-licenses.ts delete mode 100644 packages/mcp-server/src/tools/links/list-links.ts delete mode 100644 packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts delete mode 100644 packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts delete mode 100644 packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts delete mode 100644 packages/mcp-server/src/tools/okveds/list-okveds.ts delete mode 100644 packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts delete mode 100644 packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts delete mode 100644 packages/mcp-server/src/tools/regions/list-regions.ts delete mode 100644 packages/mcp-server/src/tools/risks/list-risks.ts delete mode 100644 packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts delete mode 100644 packages/mcp-server/src/tools/suggestions/create-suggestions.ts delete mode 100644 packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts delete mode 100644 packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts rename packages/mcp-server/src/{tools => }/types.ts (98%) delete mode 100644 packages/mcp-server/tests/compat.test.ts delete mode 100644 packages/mcp-server/tests/dynamic-tools.test.ts delete mode 100644 packages/mcp-server/tests/tools.test.ts diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index cbb5961..b305489 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -40,7 +40,7 @@ For clients with a configuration JSON, it might look something like this: "mcpServers": { "datanewton_api": { "command": "node", - "args": ["/path/to/local/mcpdata/packages/mcp-server", "--client=claude", "--tools=dynamic"], + "args": ["/path/to/local/mcpdata/packages/mcp-server"], "env": { "DATANEWTON_API_KEY": "My API Key" } @@ -49,110 +49,22 @@ For clients with a configuration JSON, it might look something like this: } ``` -## Exposing endpoints to your MCP Client +## Code Mode -There are three ways to expose endpoints as tools in the MCP server: +This MCP server is built on the "Code Mode" tool scheme. In this MCP Server, +your agent will write code against the TypeScript SDK, which will then be executed in an +isolated sandbox. To accomplish this, the server will expose two tools to your agent: -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 +- The first tool is a docs search tool, which can be used to generically query for + documentation about your API/SDK. -### Filtering endpoints and tools +- The second tool is a code tool, where the agent can write code against the TypeScript SDK. + The code will be executed in a sandbox environment without web or filesystem access. Then, + anything the code returns or prints will be returned to the agent as the result of the + tool call. -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 - -### Dynamic tools - -If you specify `--tools=dynamic` to the MCP server, instead of exposing one tool per endpoint in the API, it will -expose the following tools: - -1. `list_api_endpoints` - Discovers available endpoints, with optional filtering by search query -2. `get_api_endpoint_schema` - Gets detailed schema information for a specific endpoint -3. `invoke_api_endpoint` - Executes any endpoint with the appropriate parameters - -This allows you to have the full set of API endpoints available to your MCP Client, while not requiring that all -of their schemas be loaded into context at once. Instead, the LLM will automatically use these tools together to -search for, look up, and invoke endpoints dynamically. However, due to the indirect nature of the schemas, it -can struggle to provide the correct properties a bit more than when tools are imported explicitly. Therefore, -you can opt-in to explicit tools, the dynamic tools, or both. - -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. - -### 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. - -You can specify the client you are using with the `--client` argument, and the MCP server will automatically -serve tools and schemas that are more compatible with that client. - -- `--client=`: Set all capabilities based on a known MCP client - - - Valid values: `openai-agents`, `claude`, `claude-code`, `cursor` - - Example: `--client=cursor` - -Additionally, if you have a client not on the above list, or the client has gotten better -over time, you can manually enable or disable certain capabilities: - -- `--capability=`: Specify individual client capabilities - - Available capabilities: - - `top-level-unions`: Enable support for top-level unions in tool schemas - - `valid-json`: Enable JSON string parsing for arguments - - `refs`: Enable support for $ref pointers in schemas - - `unions`: Enable support for union types (anyOf) in schemas - - `formats`: Enable support for format validations in schemas (e.g. date-time, email) - - `tool-name-length=N`: Set maximum tool name length to N characters - - Example: `--capability=top-level-unions --capability=tool-name-length=40` - - Example: `--capability=top-level-unions,tool-name-length=40` - -### Examples - -1. Filter for read operations on cards: - -```bash ---resource=cards --operation=read -``` - -2. Exclude specific tools while including others: - -```bash ---resource=cards --no-tool=create_cards -``` - -3. Configure for Cursor client with custom max tool name length: - -```bash ---client=cursor --capability=tool-name-length=40 -``` - -4. Complex filtering with multiple criteria: - -```bash ---resource=cards,accounts --operation=read --tag=kyc --no-tool=create_cards -``` +Using this scheme, agents are capable of performing very complex tasks deterministically +and repeatably. ## Running remotely @@ -179,298 +91,3 @@ A configuration JSON for this server might look like this, assuming the server i } } ``` - -The command-line arguments for filtering tools and specifying clients can also be used as query parameters in the URL. -For example, to exclude specific tools while including others, use the URL: - -``` -http://localhost:3000?resource=cards&resource=accounts&no_tool=create_cards -``` - -Or, to configure for the Cursor client, with a custom max tool name length, use the URL: - -``` -http://localhost:3000?client=cursor&capability=tool-name-length%3D40 -``` - -## Importing the tools and server individually - -```js -// Import the server, generated endpoints, or the init function -import { server, endpoints, init } from "datanewton-mcp/server"; - -// import a specific tool -import createTaxpayerStatuses from "datanewton-mcp/tools/taxpayer-statuses/create-taxpayer-statuses"; - -// 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: [createTaxpayerStatuses, myCustomEndpoint] }); -``` - -## Available Tools - -The following tools are available in this MCP server. - -### Resource `taxpayer_statuses`: - -- `create_taxpayer_statuses` (`write`): Получить данные о статусе самозанятого налогоплательщика по ИНН - -### Resource `suggestions`: - -- `create_suggestions` (`write`): Получить список первых 10 подходящих контрагентов, соответствующих входному значению - - Поддерживаются следующие виды поиска: - - - `ОГРН` - - `ИНН` - - `Название ЮЛ/ИП, в том числе неполное наименование (например, "Датаном" вместо "Датаномика")` - - `Транслитерированное название ЮЛ` - - `Название ЮЛ, написанное в неправильной раскладке клавиатуры` - - `Неточное название ЮЛ/ИП (с опечатками)` - -### Resource `ogrns_by_address`: - -- `create_ogrns_by_address` (`write`): Получить список компаний по заданному адресу - -### Resource `leases`: - -- `create_leases` (`write`): Получить данные о договорах лизинга по заданным условиям - -### Resource `filters_preview`: - -- `create_filters_preview` (`write`): Получить краткую информацию (предпросмотр) по фильтрам/условиям: оквэд, регионы и т.д. - -### Resource `batch_changes`: - -- `create_batch_changes` (`write`): Получить изменения по списку контрагентов с определенной даты. - Можно смотреть изменения за день, за неделю, за квартал, а также за произвольный период времени. - Можно получить историю изменений выбранных параметров за произвольный период времени. - Отслеживаются: - - - ИНН, ОГРН, КПП - - Статус по ЕГРЮЛ/ЕГРИП - - Название: - - Полное - - Краткое - - Организационно-правовая форма (ОПФ) - - Код ОПФ по ЕГРЮЛ - - Адрес местонахождения - - Единоличный исполнительный орган: - - Руководитель - - Управляющая компания - - Участники - - Уставный капитал: - - Размер - - Тип - - Реестр МСП: - - Статус - - Категория отнесения - - Недостоверность: - - Учредителей - - Руководителей - - Управляющей компании - - Адреса - - Дисквалификация: - - Учредителей - - Руководителей - - Налоговые режимы - - Бухгалтерская (финансовая) отчетность - - ОКВЭД (добавление, изменение, удаление) - - Максимальное количество контрагентов в одном запросе - 500. - Для получения ключа к этой точке API [напишите нам](mailto:info@datanomica.org). - -### Resource `batch_cards`: - -- `create_batch_cards` (`write`): Получить информацию о нескольких организациях одним запросом (до 5 000 контрагентов в запросе), отправив список ИНН/ОГРН - -### Resource `batch_cards_by_filters`: - -- `create_batch_cards_by_filters` (`write`): Получить данные контрагентов по фильтрам/условиям: оквэды, регионы и т.д. - -### Resource `arbitration`: - -- `get_courts_arbitration` (`read`): Получить справочник арбитражных судов: полный список наименований арбитражных судов РФ и количество дел, рассмотренных и рассматриваемых в данных инстанциях -- `get_dispute_categories_arbitration` (`read`): Получить справочник арбитражных споров (категорий), относящихся к компетенции арбитражных судов: категории споров и количество дел в данных категориях -- `get_document_types_arbitration` (`read`): Получить справочник типов документов арбитражных дел: полный список документов (судебных актов), изданных арбитражными судами, а также их количество - -### Resource `arbitration.batch_cases`: - -- `create_arbitration_batch_cases` (`write`): Получить данные об арбитражных делах с возможностью фильтрации - -### Resource `tax_info`: - -- `retrieve_tax_info` (`read`): Получить информацию об уплаченных налогах, задолженностях и штрафах, налоговых нарушениях по ИНН/ОГРН - -### Resource `sro_membership`: - -- `retrieve_sro_membership` (`read`): Получить информацию о членстве в СРО НОСТРОЙ/НОПРИЗ - -### Resource `risks`: - -- `list_risks` (`read`): Получить список рисков по контрагенту - - **Возможные значения** - - **Негативные списки: (NEGATIVE_LISTS)** - - - `owner_inaccuracy` — Недостоверность учредителя - - `disqualified_managers` — Руководитель дисквалифицирован - - `in_sanctions_ofac` — Находится в санкционном списке OFAC - - `illegal_rewards` — Находится в реестре ЮЛ, привлеченных за незаконное вознаграждение - - `address_inaccuracy` — Недостоверность адреса - - `manager_inaccuracy` — Недостоверность руководителя - - `in_sanctions_uk` — Находится в санкционном списке UK - - `in_sanctions_eu` — Находится в санкционном списке EU - - `false_info` — Недостоверные данные по ЕГРЮЛ. Если в ЕГР есть отметка о недостоверности любого из блоков: адрес, участник, руководитель, управляющая компания. - - `disqualified_owners` — Участник дисквалифицирован - - `unscrupulous_manager` — Руководитель в РНП - - `unscrupulous_owner` — Участник в РНП - - `unscrupulous_supplier_223` — Состоит в реестре недобросовестных поставщиков по ФЗ 223 - - `unscrupulous_supplier_615` — Состоит в реестре недобросовестных поставщиков по ПП 615 - - `unscrupulous_supplier_44` — Состоит в реестре недобросовестных поставщиков по ФЗ 44 - - `disqualified_individual` — ИП дисквалифицирован - - `management_company_inaccuracy` — Недостоверность управляющей компании - - `fin_illegal` — Имеет признаки нелегальной деятельности на финансовом рынке - - **Признаки однодневок: (ONE_DAY_COMPANY)** - - - `fns_migration` — Миграция между ФНС (более 2 за 12 месяцев) - - `mass_address` — Массовый юридический адрес. Под массовым юридическим адресом понимается факт регистрации 5 и более компаний с точностью до офиса/помещения/комнаты - - `mass_okveds` — ОКВЭДов более 30 - - `company_age` — Возраст менее 1 года - - `default_capital_table` — Уставный капитал 10 000 ₽ - - `owner_change` — Учредитель сменился менее 1 года назад - - `mass_owner` — Массовый учредитель (более 5) - - `manager_change` — Руководитель сменился менее 1 года назад - - `management_company_change` — Управляющая компания сменилась менее 1 года назад - - `workers_amount` — Численность работников 0 или 1 - - `mass_manager` — Массовый руководитель (более 5) - - `tax_debts` — Имеет налоговую задолженность - - `tax_offences` — Имеет налоговые нарушения - - **Иные факты: (OTHER_FACTS)** - - - `has_licenses` — Имеет действующие лицензии - - `has_declarations` — Имеет действующие Декларации соответствия - - `owners_information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ об участнике - - `zero_intangible_assets` — Нулевая балансовая стоимость нематериальных активов, за последний год - - `zero_fixed_assets` — Нулевая балансовая стоимость основных средств, за последний год - - `has_certifications` — Имеет действующие Сертификаты соответствия - - `management_companies_information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ об управляющей компании - - `has_government_contracts` — Является поставщиком по госконтрактам - - `managers_information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ о руководителе - - `information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ - - `debt` — Сумма к взысканию по исполнительным производствам превышает 300 000 руб - - `status_egrul` — Статус в ЕГРЮЛ, требующий внимания - - `status_egrip` — Статус в ЕГРИП, требующий внимания - - **Сведения о банкротстве: (BANKRUPTCY)** - - - `has_bankruptcy_messages` — Есть сообщения о банкротстве за последний год - -### Resource `paid_taxes`: - -- `list_paid_taxes` (`read`): Получить данные об уплаченных налогах по ИНН/ОГРН - -### Resource `okpd_list`: - -- `list_okpd_list` (`read`): Получить список всех ОКПД (ОКПД2) компании, на основании данных в ЕИС Закупки - -### Resource `nko_reestr`: - -- `retrieve_nko_reestr` (`read`): Получить информацию о принадлежности контрагента к НКО - -### Resource `links`: - -- `list_links` (`read`): Получить связи контрагента (до 2-го уровня включительно): руководители, участники, ИП - -### Resource `government_contracts`: - -- `list_government_contracts` (`read`): Получить данные о госконтрактах контрагента по информации из ЕИС Закупки - -### Resource `government_contracts_stat`: - -- `retrieve_government_contracts_stat` (`read`): Получить статистику по госконтрактам контрагента по информации из ЕИС Закупки - -### Resource `finance`: - -- `retrieve_finance` (`read`): Получить данные отчетов о финансовых результатах организации - -### Resource `regions`: - -- `list_regions` (`read`): Получить справочник регионов России - -### Resource `dictionary`: - -- `list_lease_classifier_dictionary` (`read`): Получить классификатор лизинговых договоров: код лизинга, его наименование и количество заключенных договоров с данным кодом -- `list_licenses_dictionary` (`read`): Получить справочник кодов и типов лицензий -- `list_okveds_dictionary` (`read`): Получить справочник ОКВЭДов -- `list_regions_dictionary` (`read`): Получить справочник регионов России - -### Resource `dictionary.procurement`: - -- `list_okpd2_dictionary_procurement` (`read`): Получить общероссийский классификатор продукции по видам экономической деятельности (ОКПД2) - -### Resource `dictionary.arbitration`: - -- `list_courts_dictionary_arbitration` (`read`): Получить справочник арбитражных судов: полный список наименований арбитражных судов РФ и количество дел, рассмотренных и рассматриваемых в данных инстанциях -- `list_dispute_categories_dictionary_arbitration` (`read`): Получить справочник арбитражных споров (категорий), относящихся к компетенции арбитражных судов: категории споров и количество дел в данных категориях -- `list_document_types_dictionary_arbitration` (`read`): Получить справочник типов документов арбитражных дел: полный список документов (судебных актов), изданных арбитражными судами, а также их количество - -### Resource `procurement`: - -- `retrieve_okpd2_procurement` (`read`): Получить общероссийский классификатор продукции по видам экономической деятельности (ОКПД2) - -### Resource `okveds`: - -- `list_okveds` (`read`): Получить справочник ОКВЭДов - -### Resource `licenses`: - -- `list_licenses` (`read`): Получить справочник кодов и типов лицензий - -### Resource `lease_classifier`: - -- `retrieve_lease_classifier` (`read`): Получить классификатор лизинговых договоров: код лизинга, его наименование и количество заключенных договоров с данным кодом - -### Resource `counterparty`: - -- `retrieve_counterparty` (`read`): Получить общую информацию о контрагенте из ЕГРЮЛ/ЕГРИП. Конкретное наполнение зависит от переданных секций, описание которых приведено в параметрах запроса. - -### Resource `corporate_actions`: - -- `list_corporate_actions` (`read`): Получить данные о корпоративных действиях из ФедРесурса - -### Resource `blocked_bank_accounts`: - -- `list_blocked_bank_accounts` (`read`): Получить структурированные данные о решениях ФНС о приостановлении операций по счетам юридических лиц и ИП - -### Resource `bankruptcy`: - -- `retrieve_bankruptcy` (`read`): Получить признаки банкротства контрагента - -### Resource `arbitration_cases`: - -- `list_arbitration_cases` (`read`): Получить данные о судебных делах контрагента с возможностью фильтрации diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 5ff1e62..ac6af47 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, ToolCallResult, asTextContentResult } from './tools/types'; +import { McpTool, Metadata, ToolCallResult, asTextContentResult } from './types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { readEnv } from './server'; import { WorkerSuccess } from './code-tool-types'; @@ -13,7 +13,7 @@ import { WorkerSuccess } from './code-tool-types'; * * @param endpoints - The endpoints to include in the list. */ -export async function codeTool() { +export function codeTool(): McpTool { const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] }; const tool: Tool = { name: 'execute', diff --git a/packages/mcp-server/src/compat.ts b/packages/mcp-server/src/compat.ts deleted file mode 100644 index f84053c..0000000 --- a/packages/mcp-server/src/compat.ts +++ /dev/null @@ -1,483 +0,0 @@ -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { z } from 'zod'; -import { Endpoint } from './tools'; - -export interface ClientCapabilities { - topLevelUnions: boolean; - validJson: boolean; - refs: boolean; - unions: boolean; - formats: boolean; - toolNameLength: number | undefined; -} - -export const defaultClientCapabilities: ClientCapabilities = { - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, -}; - -export const ClientType = z.enum(['openai-agents', 'claude', 'claude-code', 'cursor', 'infer']); -export type ClientType = z.infer; - -// Client presets for compatibility -// Note that these could change over time as models get better, so this is -// a best effort. -export const knownClients: Record, ClientCapabilities> = { - 'openai-agents': { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - claude: { - topLevelUnions: true, - validJson: false, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - 'claude-code': { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - cursor: { - topLevelUnions: false, - validJson: true, - refs: false, - unions: false, - formats: false, - toolNameLength: 50, - }, -}; - -/** - * Attempts to parse strings into JSON objects - */ -export function parseEmbeddedJSON(args: Record, schema: Record) { - let updated = false; - const newArgs: Record = Object.assign({}, args); - - for (const [key, value] of Object.entries(newArgs)) { - if (typeof value === 'string') { - try { - const parsed = JSON.parse(value); - // Only parse if result is a plain object (not array, null, or primitive) - if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { - newArgs[key] = parsed; - updated = true; - } - } catch (e) { - // Not valid JSON, leave as is - } - } - } - - if (updated) { - return newArgs; - } - - return args; -} - -export type JSONSchema = { - type?: string; - properties?: Record; - required?: string[]; - anyOf?: JSONSchema[]; - $ref?: string; - $defs?: Record; - [key: string]: any; -}; - -/** - * Truncates tool names to the specified length while ensuring uniqueness. - * If truncation would cause duplicate names, appends a number to make them unique. - */ -export function truncateToolNames(names: string[], maxLength: number): Map { - if (maxLength <= 0) { - return new Map(); - } - - const renameMap = new Map(); - const usedNames = new Set(); - - const toTruncate = names.filter((name) => name.length > maxLength); - - if (toTruncate.length === 0) { - return renameMap; - } - - const willCollide = - new Set(toTruncate.map((name) => name.slice(0, maxLength - 1))).size < toTruncate.length; - - if (!willCollide) { - for (const name of toTruncate) { - const truncatedName = name.slice(0, maxLength); - renameMap.set(name, truncatedName); - } - } else { - const baseLength = maxLength - 1; - - for (const name of toTruncate) { - const baseName = name.slice(0, baseLength); - let counter = 1; - - while (usedNames.has(baseName + counter)) { - counter++; - } - - const finalName = baseName + counter; - renameMap.set(name, finalName); - usedNames.add(finalName); - } - } - - return renameMap; -} - -/** - * Removes top-level unions from a tool by splitting it into multiple tools, - * one for each variant in the union. - */ -export function removeTopLevelUnions(tool: Tool): Tool[] { - const inputSchema = tool.inputSchema as JSONSchema; - const variants = inputSchema.anyOf; - - if (!variants || !Array.isArray(variants) || variants.length === 0) { - return [tool]; - } - - const defs = inputSchema.$defs || {}; - - return variants.map((variant, index) => { - const variantSchema: JSONSchema = { - ...inputSchema, - ...variant, - type: 'object', - properties: { - ...(inputSchema.properties || {}), - ...(variant.properties || {}), - }, - }; - - delete variantSchema.anyOf; - - if (!variantSchema['description']) { - variantSchema['description'] = tool.description; - } - - const usedDefs = findUsedDefs(variant, defs); - if (Object.keys(usedDefs).length > 0) { - variantSchema.$defs = usedDefs; - } else { - delete variantSchema.$defs; - } - - return { - ...tool, - name: `${tool.name}_${toSnakeCase(variant['title'] || `variant${index + 1}`)}`, - description: variant['description'] || tool.description, - inputSchema: variantSchema, - } as Tool; - }); -} - -function findUsedDefs( - schema: JSONSchema, - defs: Record, - visited: Set = new Set(), -): Record { - const usedDefs: Record = {}; - - if (typeof schema !== 'object' || schema === null) { - return usedDefs; - } - - if (schema.$ref) { - const refParts = schema.$ref.split('/'); - if (refParts[0] === '#' && refParts[1] === '$defs' && refParts[2]) { - const defName = refParts[2]; - const def = defs[defName]; - if (def && !visited.has(schema.$ref)) { - usedDefs[defName] = def; - visited.add(schema.$ref); - Object.assign(usedDefs, findUsedDefs(def, defs, visited)); - visited.delete(schema.$ref); - } - } - return usedDefs; - } - - for (const key in schema) { - if (key !== '$defs' && typeof schema[key] === 'object' && schema[key] !== null) { - Object.assign(usedDefs, findUsedDefs(schema[key] as JSONSchema, defs, visited)); - } - } - - return usedDefs; -} - -// Export for testing -export { findUsedDefs }; - -/** - * Inlines all $refs in a schema, eliminating $defs. - * If a circular reference is detected, the circular property is removed. - */ -export function inlineRefs(schema: JSONSchema): JSONSchema { - if (!schema || typeof schema !== 'object') { - return schema; - } - - const clonedSchema = { ...schema }; - const defs: Record = schema.$defs || {}; - - delete clonedSchema.$defs; - - const result = inlineRefsRecursive(clonedSchema, defs, new Set()); - // The top level can never be null - return result === null ? {} : result; -} - -function inlineRefsRecursive( - schema: JSONSchema, - defs: Record, - refPath: Set, -): JSONSchema | null { - if (!schema || typeof schema !== 'object') { - return schema; - } - - if (Array.isArray(schema)) { - return schema.map((item) => { - const processed = inlineRefsRecursive(item, defs, refPath); - return processed === null ? {} : processed; - }) as JSONSchema; - } - - const result = { ...schema }; - - if ('$ref' in result && typeof result.$ref === 'string') { - if (result.$ref.startsWith('#/$defs/')) { - const refName = result.$ref.split('/').pop() as string; - const def = defs[refName]; - - // If we've already seen this ref in our path, we have a circular reference - if (refPath.has(result.$ref)) { - // For circular references, we completely remove the property - // by returning null. The parent will remove it. - return null; - } - - if (def) { - const newRefPath = new Set(refPath); - newRefPath.add(result.$ref); - - const inlinedDef = inlineRefsRecursive({ ...def }, defs, newRefPath); - - if (inlinedDef === null) { - return { ...result }; - } - - // Merge the inlined definition with the original schema's properties - // but preserve things like description, etc. - const { $ref, ...rest } = result; - return { ...inlinedDef, ...rest }; - } - } - - // Keep external refs as-is - return result; - } - - for (const key in result) { - if (result[key] && typeof result[key] === 'object') { - const processed = inlineRefsRecursive(result[key] as JSONSchema, defs, refPath); - if (processed === null) { - // Remove properties that would cause circular references - delete result[key]; - } else { - result[key] = processed; - } - } - } - - return result; -} - -/** - * Removes anyOf fields from a schema, using only the first variant. - */ -export function removeAnyOf(schema: JSONSchema): JSONSchema { - if (!schema || typeof schema !== 'object') { - return schema; - } - - if (Array.isArray(schema)) { - return schema.map((item) => removeAnyOf(item)) as JSONSchema; - } - - const result = { ...schema }; - - if ('anyOf' in result && Array.isArray(result.anyOf) && result.anyOf.length > 0) { - const firstVariant = result.anyOf[0]; - - if (firstVariant && typeof firstVariant === 'object') { - // Special handling for properties to ensure deep merge - if (firstVariant.properties && result.properties) { - result.properties = { - ...result.properties, - ...(firstVariant.properties as Record), - }; - } else if (firstVariant.properties) { - result.properties = { ...firstVariant.properties }; - } - - for (const key in firstVariant) { - if (key !== 'properties') { - result[key] = firstVariant[key]; - } - } - } - - delete result.anyOf; - } - - for (const key in result) { - if (result[key] && typeof result[key] === 'object') { - result[key] = removeAnyOf(result[key] as JSONSchema); - } - } - - return result; -} - -/** - * Removes format fields from a schema and appends them to the description. - */ -export function removeFormats(schema: JSONSchema, formatsCapability: boolean): JSONSchema { - if (formatsCapability) { - return schema; - } - - if (!schema || typeof schema !== 'object') { - return schema; - } - - if (Array.isArray(schema)) { - return schema.map((item) => removeFormats(item, formatsCapability)) as JSONSchema; - } - - const result = { ...schema }; - - if ('format' in result && typeof result['format'] === 'string') { - const formatStr = `(format: "${result['format']}")`; - - if ('description' in result && typeof result['description'] === 'string') { - result['description'] = `${result['description']} ${formatStr}`; - } else { - result['description'] = formatStr; - } - - delete result['format']; - } - - for (const key in result) { - if (result[key] && typeof result[key] === 'object') { - result[key] = removeFormats(result[key] as JSONSchema, formatsCapability); - } - } - - return result; -} - -/** - * Applies all compatibility transformations to the endpoints based on the provided capabilities. - */ -export function applyCompatibilityTransformations( - endpoints: Endpoint[], - capabilities: ClientCapabilities, -): Endpoint[] { - let transformedEndpoints = [...endpoints]; - - // Handle top-level unions first as this changes tool names - if (!capabilities.topLevelUnions) { - const newEndpoints: Endpoint[] = []; - - for (const endpoint of transformedEndpoints) { - const variantTools = removeTopLevelUnions(endpoint.tool); - - if (variantTools.length === 1) { - newEndpoints.push(endpoint); - } else { - for (const variantTool of variantTools) { - newEndpoints.push({ - ...endpoint, - tool: variantTool, - }); - } - } - } - - transformedEndpoints = newEndpoints; - } - - if (capabilities.toolNameLength) { - const toolNames = transformedEndpoints.map((endpoint) => endpoint.tool.name); - const renameMap = truncateToolNames(toolNames, capabilities.toolNameLength); - - transformedEndpoints = transformedEndpoints.map((endpoint) => ({ - ...endpoint, - tool: { - ...endpoint.tool, - name: renameMap.get(endpoint.tool.name) ?? endpoint.tool.name, - }, - })); - } - - if (!capabilities.refs || !capabilities.unions || !capabilities.formats) { - transformedEndpoints = transformedEndpoints.map((endpoint) => { - let schema = endpoint.tool.inputSchema as JSONSchema; - - if (!capabilities.refs) { - schema = inlineRefs(schema); - } - - if (!capabilities.unions) { - schema = removeAnyOf(schema); - } - - if (!capabilities.formats) { - schema = removeFormats(schema, capabilities.formats); - } - - return { - ...endpoint, - tool: { - ...endpoint.tool, - inputSchema: schema as typeof endpoint.tool.inputSchema, - }, - }; - }); - } - - return transformedEndpoints; -} - -function toSnakeCase(str: string): string { - return str - .replace(/\s+/g, '_') - .replace(/([a-z])([A-Z])/g, '$1_$2') - .toLowerCase(); -} diff --git a/packages/mcp-server/src/docs-search-tool.ts b/packages/mcp-server/src/docs-search-tool.ts index c289576..5e2bdd6 100644 --- a/packages/mcp-server/src/docs-search-tool.ts +++ b/packages/mcp-server/src/docs-search-tool.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Metadata, asTextContentResult } from './tools/types'; +import { Metadata, asTextContentResult } from './types'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; diff --git a/packages/mcp-server/src/dynamic-tools.ts b/packages/mcp-server/src/dynamic-tools.ts deleted file mode 100644 index 368deaf..0000000 --- a/packages/mcp-server/src/dynamic-tools.ts +++ /dev/null @@ -1,159 +0,0 @@ -import Datanewton from 'datanewton'; -import { Endpoint, asTextContentResult, ToolCallResult } from './tools/types'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { z } from 'zod'; -import { Cabidela } from '@cloudflare/cabidela'; - -function zodToInputSchema(schema: z.ZodSchema) { - return { - type: 'object' as const, - ...(zodToJsonSchema(schema) as any), - }; -} - -/** - * A list of tools that expose all the endpoints in the API dynamically. - * - * 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. - * - * @param endpoints - The endpoints to include in the list. - */ -export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { - const listEndpointsSchema = z.object({ - search_query: z - .string() - .optional() - .describe( - 'An optional search query to filter the endpoints by. Provide a partial name, resource, operation, or tag to filter the endpoints returned.', - ), - }); - - const listEndpointsTool = { - metadata: { - resource: 'dynamic_tools', - operation: 'read' as const, - tags: [], - }, - tool: { - name: 'list_api_endpoints', - description: 'List or search for all endpoints in the Datanewton TypeScript API', - inputSchema: zodToInputSchema(listEndpointsSchema), - }, - handler: async ( - client: Datanewton, - args: Record | undefined, - ): Promise => { - const query = args && listEndpointsSchema.parse(args).search_query?.trim(); - - const filteredEndpoints = - query && query.length > 0 ? - endpoints.filter((endpoint) => { - const fieldsToMatch = [ - endpoint.tool.name, - endpoint.tool.description, - endpoint.metadata.resource, - endpoint.metadata.operation, - ...endpoint.metadata.tags, - ]; - return fieldsToMatch.some((field) => field && field.toLowerCase().includes(query.toLowerCase())); - }) - : endpoints; - - return asTextContentResult({ - tools: filteredEndpoints.map(({ tool, metadata }) => ({ - name: tool.name, - description: tool.description, - resource: metadata.resource, - operation: metadata.operation, - tags: metadata.tags, - })), - }); - }, - }; - - const getEndpointSchema = z.object({ - endpoint: z.string().describe('The name of the endpoint to get the schema for.'), - }); - const getEndpointTool = { - metadata: { - resource: 'dynamic_tools', - operation: 'read' as const, - tags: [], - }, - tool: { - name: 'get_api_endpoint_schema', - description: - 'Get the schema for an endpoint in the Datanewton TypeScript API. You can use the schema returned by this tool to invoke an endpoint with the `invoke_api_endpoint` tool.', - inputSchema: zodToInputSchema(getEndpointSchema), - }, - handler: async (client: Datanewton, args: Record | undefined) => { - if (!args) { - throw new Error('No endpoint provided'); - } - const endpointName = getEndpointSchema.parse(args).endpoint; - - const endpoint = endpoints.find((e) => e.tool.name === endpointName); - if (!endpoint) { - throw new Error(`Endpoint ${endpointName} not found`); - } - return asTextContentResult(endpoint.tool); - }, - }; - - const invokeEndpointSchema = z.object({ - endpoint_name: z.string().describe('The name of the endpoint to invoke.'), - args: z - .record(z.string(), z.any()) - .describe( - 'The arguments to pass to the endpoint. This must match the schema returned by the `get_api_endpoint_schema` tool.', - ), - }); - - const invokeEndpointTool = { - metadata: { - resource: 'dynamic_tools', - operation: 'write' as const, - tags: [], - }, - tool: { - name: 'invoke_api_endpoint', - description: - 'Invoke an endpoint in the Datanewton TypeScript API. Note: use the `list_api_endpoints` tool to get the list of endpoints and `get_api_endpoint_schema` tool to get the schema for an endpoint.', - inputSchema: zodToInputSchema(invokeEndpointSchema), - }, - handler: async ( - client: Datanewton, - args: Record | undefined, - ): Promise => { - if (!args) { - throw new Error('No endpoint provided'); - } - const { success, data, error } = invokeEndpointSchema.safeParse(args); - if (!success) { - throw new Error(`Invalid arguments for endpoint. ${error?.format()}`); - } - const { endpoint_name, args: endpointArgs } = data; - - const endpoint = endpoints.find((e) => e.tool.name === endpoint_name); - if (!endpoint) { - throw new Error( - `Endpoint ${endpoint_name} not found. Use the \`list_api_endpoints\` tool to get the list of available endpoints.`, - ); - } - - try { - // Try to validate the arguments for a better error message - const cabidela = new Cabidela(endpoint.tool.inputSchema, { fullErrors: true }); - cabidela.validate(endpointArgs); - } catch (error) { - throw new Error(`Invalid arguments for endpoint ${endpoint_name}:\n${error}`); - } - - return await endpoint.handler(client, endpointArgs); - }, - }; - - return [getEndpointTool, listEndpointsTool, invokeEndpointTool]; -} diff --git a/packages/mcp-server/src/filtering.ts b/packages/mcp-server/src/filtering.ts deleted file mode 100644 index eaae0fc..0000000 --- a/packages/mcp-server/src/filtering.ts +++ /dev/null @@ -1,18 +0,0 @@ -// @ts-nocheck -import initJq from 'jq-web'; - -export async function maybeFilter(jqFilter: unknown | undefined, response: any): Promise { - if (jqFilter && typeof jqFilter === 'string') { - return await jq(response, jqFilter); - } else { - return response; - } -} - -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/http.ts b/packages/mcp-server/src/http.ts index 8451700..2366d8f 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -4,38 +4,21 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import express from 'express'; -import { fromError } from 'zod-validation-error/v3'; -import { McpOptions, parseQueryOptions } from './options'; +import { McpOptions } from './options'; import { ClientOptions, initMcpServer, newMcpServer } from './server'; import { parseAuthHeaders } from './headers'; const newServer = ({ clientOptions, - mcpOptions: defaultMcpOptions, req, res, }: { clientOptions: ClientOptions; - mcpOptions: McpOptions; req: express.Request; res: express.Response; }): McpServer | null => { const server = newMcpServer(); - let mcpOptions: McpOptions; - try { - mcpOptions = parseQueryOptions(defaultMcpOptions, req.query); - } catch (error) { - res.status(400).json({ - jsonrpc: '2.0', - error: { - code: -32000, - message: `Invalid request: ${fromError(error)}`, - }, - }); - return null; - } - try { const authOptions = parseAuthHeaders(req); initMcpServer({ @@ -44,7 +27,6 @@ const newServer = ({ ...clientOptions, ...authOptions, }, - mcpOptions, }); } catch (error) { res.status(401).json({ diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 4850a0e..0f6dd42 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -1,20 +1,15 @@ #!/usr/bin/env node import { selectTools } from './server'; -import { Endpoint, endpoints } from './tools'; import { McpOptions, parseCLIOptions } from './options'; import { launchStdioServer } from './stdio'; import { launchStreamableHTTPServer } from './http'; +import type { McpTool } from './types'; async function main() { const options = parseOptionsOrError(); - if (options.list) { - listAllTools(); - return; - } - - const selectedTools = await selectToolsOrError(endpoints, options); + const selectedTools = await selectToolsOrError(options); console.error( `MCP Server starting with ${selectedTools.length} tools:`, @@ -23,7 +18,7 @@ async function main() { switch (options.transport) { case 'stdio': - await launchStdioServer(options); + await launchStdioServer(); break; case 'http': await launchStreamableHTTPServer(options, options.port ?? options.socket); @@ -47,9 +42,9 @@ function parseOptionsOrError() { } } -async function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): Promise { +async function selectToolsOrError(options: McpOptions): Promise { try { - const includedTools = await selectTools(endpoints, options); + const includedTools = selectTools(options); if (includedTools.length === 0) { console.error('No tools match the provided filters.'); process.exit(1); @@ -64,45 +59,3 @@ async function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): P process.exit(1); } } - -function listAllTools() { - if (endpoints.length === 0) { - console.log('No tools available.'); - return; - } - console.log('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.log(`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.log(` - ${tool.name} (${operation}) ${tags.length > 0 ? `tags: ${tags.join(', ')}` : ''}`); - console.log(` Description: ${tool.description}`); - } - console.log(''); - } -} diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index b6ff597..6c8bb8d 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -2,140 +2,31 @@ import qs from 'qs'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import z from 'zod'; -import { endpoints, Filter } from './tools'; -import { ClientCapabilities, knownClients, ClientType } from './compat'; export type CLIOptions = McpOptions & { - list: boolean; transport: 'stdio' | 'http'; port: number | undefined; socket: string | undefined; }; export type McpOptions = { - client?: ClientType | undefined; - includeDynamicTools?: boolean | undefined; - includeAllTools?: boolean | undefined; - includeCodeTools?: boolean | undefined; includeDocsTools?: boolean | undefined; - filters?: Filter[] | undefined; - capabilities?: Partial | undefined; }; -const CAPABILITY_CHOICES = [ - 'top-level-unions', - 'valid-json', - 'refs', - 'unions', - 'formats', - 'tool-name-length', -] as const; - -type Capability = (typeof CAPABILITY_CHOICES)[number]; - -function parseCapabilityValue(cap: string): { name: Capability; value?: number } { - if (cap.startsWith('tool-name-length=')) { - const parts = cap.split('='); - if (parts.length === 2) { - const length = parseInt(parts[1]!, 10); - if (!isNaN(length)) { - return { name: 'tool-name-length', value: length }; - } - throw new Error(`Invalid tool-name-length value: ${parts[1]}. Expected a number.`); - } - throw new Error(`Invalid format for tool-name-length. Expected tool-name-length=N.`); - } - if (!CAPABILITY_CHOICES.includes(cap as Capability)) { - throw new Error(`Unknown capability: ${cap}. Valid capabilities are: ${CAPABILITY_CHOICES.join(', ')}`); - } - return { name: cap as Capability }; -} - export function parseCLIOptions(): CLIOptions { const opts = yargs(hideBin(process.argv)) .option('tools', { type: 'string', array: true, - choices: ['dynamic', 'all', 'code', 'docs'], + choices: ['code', 'docs'], description: 'Use dynamic tools or all tools', }) .option('no-tools', { type: 'string', array: true, - choices: ['dynamic', 'all', 'code', 'docs'], + choices: ['code', 'docs'], description: 'Do not use any dynamic or all tools', }) - .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', - }) - .option('client', { - type: 'string', - choices: Object.keys(knownClients), - description: 'Specify the MCP client being used', - }) - .option('capability', { - type: 'string', - array: true, - description: 'Specify client capabilities', - coerce: (values: string[]) => { - return values.flatMap((v) => v.split(',')); - }, - }) - .option('no-capability', { - type: 'string', - array: true, - description: 'Unset client capabilities', - choices: CAPABILITY_CHOICES, - coerce: (values: string[]) => { - return values.flatMap((v) => v.split(',')); - }, - }) - .option('describe-capabilities', { - type: 'boolean', - description: 'Print detailed explanation of client capabilities and exit', - }) .option('transport', { type: 'string', choices: ['stdio', 'http'], @@ -152,122 +43,19 @@ export function parseCLIOptions(): CLIOptions { }) .help(); - for (const [command, desc] of examples()) { - opts.example(command, desc); - } - const argv = opts.parseSync(); - // Handle describe-capabilities flag - if (argv.describeCapabilities) { - console.log(getCapabilitiesExplanation()); - process.exit(0); - } - - const filters: Filter[] = []; - - // Helper function to support comma-separated values - const splitValues = (values: string[] | undefined): string[] => { - if (!values) return []; - return values.flatMap((v) => v.split(',')); - }; - - for (const tag of splitValues(argv.tag)) { - filters.push({ type: 'tag', op: 'include', value: tag }); - } - - for (const tag of splitValues(argv.noTag)) { - filters.push({ type: 'tag', op: 'exclude', value: tag }); - } - - for (const resource of splitValues(argv.resource)) { - filters.push({ type: 'resource', op: 'include', value: resource }); - } - - for (const resource of splitValues(argv.noResource)) { - filters.push({ type: 'resource', op: 'exclude', value: resource }); - } - - for (const tool of splitValues(argv.tool)) { - filters.push({ type: 'tool', op: 'include', value: tool }); - } - - for (const tool of splitValues(argv.noTool)) { - filters.push({ type: 'tool', op: 'exclude', value: tool }); - } - - for (const operation of splitValues(argv.operation)) { - filters.push({ type: 'operation', op: 'include', value: operation }); - } - - for (const operation of splitValues(argv.noOperation)) { - filters.push({ type: 'operation', op: 'exclude', value: operation }); - } - - // Parse client capabilities - const clientCapabilities: Partial = {}; - - // Apply individual capability overrides - if (Array.isArray(argv.capability)) { - for (const cap of argv.capability) { - const parsedCap = parseCapabilityValue(cap); - if (parsedCap.name === 'top-level-unions') { - clientCapabilities.topLevelUnions = true; - } else if (parsedCap.name === 'valid-json') { - clientCapabilities.validJson = true; - } else if (parsedCap.name === 'refs') { - clientCapabilities.refs = true; - } else if (parsedCap.name === 'unions') { - clientCapabilities.unions = true; - } else if (parsedCap.name === 'formats') { - clientCapabilities.formats = true; - } else if (parsedCap.name === 'tool-name-length') { - clientCapabilities.toolNameLength = parsedCap.value; - } - } - } - - // Handle no-capability options to unset capabilities - if (Array.isArray(argv.noCapability)) { - for (const cap of argv.noCapability) { - if (cap === 'top-level-unions') { - clientCapabilities.topLevelUnions = false; - } else if (cap === 'valid-json') { - clientCapabilities.validJson = false; - } else if (cap === 'refs') { - clientCapabilities.refs = false; - } else if (cap === 'unions') { - clientCapabilities.unions = false; - } else if (cap === 'formats') { - clientCapabilities.formats = false; - } else if (cap === 'tool-name-length') { - clientCapabilities.toolNameLength = undefined; - } - } - } - - const shouldIncludeToolType = (toolType: 'dynamic' | 'all' | 'code' | 'docs') => + const shouldIncludeToolType = (toolType: 'code' | 'docs') => argv.noTools?.includes(toolType) ? false : argv.tools?.includes(toolType) ? true : undefined; - const includeDynamicTools = shouldIncludeToolType('dynamic'); - const includeAllTools = shouldIncludeToolType('all'); - const includeCodeTools = shouldIncludeToolType('code'); const includeDocsTools = shouldIncludeToolType('docs'); const transport = argv.transport as 'stdio' | 'http'; - const client = argv.client as ClientType; return { - client: client && client !== 'infer' && knownClients[client] ? client : undefined, - includeDynamicTools, - includeAllTools, - includeCodeTools, includeDocsTools, - filters, - capabilities: clientCapabilities, - list: argv.list || false, transport, port: argv.port, socket: argv.socket, @@ -284,190 +72,21 @@ const coerceArray = (zodType: T) => ); const QueryOptions = z.object({ - 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.', - ), + tools: coerceArray(z.enum(['code', 'docs'])).describe('Specify which MCP tools to use'), + no_tools: coerceArray(z.enum(['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( - 'Include tools matching the specified operations', - ), - tag: coerceArray(z.string()).describe('Include tools with the specified tags'), - no_tool: coerceArray(z.string()).describe('Exclude tools matching the specified names'), - no_resource: coerceArray(z.string()).describe('Exclude tools matching the specified resources'), - no_operation: coerceArray(z.enum(['read', 'write'])).describe( - 'Exclude tools matching the specified operations', - ), - no_tag: coerceArray(z.string()).describe('Exclude tools with the specified tags'), - client: ClientType.optional().describe('Specify the MCP client being used'), - capability: coerceArray(z.string()).describe('Specify client capabilities'), - no_capability: coerceArray(z.enum(CAPABILITY_CHOICES)).describe('Unset client capabilities'), }); export function parseQueryOptions(defaultOptions: McpOptions, query: unknown): McpOptions { const queryObject = typeof query === 'string' ? qs.parse(query) : query; const queryOptions = QueryOptions.parse(queryObject); - const filters: Filter[] = [...(defaultOptions.filters ?? [])]; - - for (const resource of queryOptions.resource || []) { - filters.push({ type: 'resource', op: 'include', value: resource }); - } - for (const operation of queryOptions.operation || []) { - filters.push({ type: 'operation', op: 'include', value: operation }); - } - for (const tag of queryOptions.tag || []) { - filters.push({ type: 'tag', op: 'include', value: tag }); - } - for (const tool of queryOptions.tool || []) { - filters.push({ type: 'tool', op: 'include', value: tool }); - } - for (const resource of queryOptions.no_resource || []) { - filters.push({ type: 'resource', op: 'exclude', value: resource }); - } - for (const operation of queryOptions.no_operation || []) { - filters.push({ type: 'operation', op: 'exclude', value: operation }); - } - for (const tag of queryOptions.no_tag || []) { - filters.push({ type: 'tag', op: 'exclude', value: tag }); - } - for (const tool of queryOptions.no_tool || []) { - filters.push({ type: 'tool', op: 'exclude', value: tool }); - } - - // Parse client capabilities - const clientCapabilities: Partial = { ...defaultOptions.capabilities }; - - for (const cap of queryOptions.capability || []) { - const parsed = parseCapabilityValue(cap); - if (parsed.name === 'top-level-unions') { - clientCapabilities.topLevelUnions = true; - } else if (parsed.name === 'valid-json') { - clientCapabilities.validJson = true; - } else if (parsed.name === 'refs') { - clientCapabilities.refs = true; - } else if (parsed.name === 'unions') { - clientCapabilities.unions = true; - } else if (parsed.name === 'formats') { - clientCapabilities.formats = true; - } else if (parsed.name === 'tool-name-length') { - clientCapabilities.toolNameLength = parsed.value; - } - } - - for (const cap of queryOptions.no_capability || []) { - if (cap === 'top-level-unions') { - clientCapabilities.topLevelUnions = false; - } else if (cap === 'valid-json') { - clientCapabilities.validJson = false; - } else if (cap === 'refs') { - clientCapabilities.refs = false; - } else if (cap === 'unions') { - clientCapabilities.unions = false; - } else if (cap === 'formats') { - clientCapabilities.formats = false; - } else if (cap === 'tool-name-length') { - clientCapabilities.toolNameLength = undefined; - } - } - - let dynamicTools: boolean | undefined = - queryOptions.no_tools && queryOptions.no_tools?.includes('dynamic') ? false - : queryOptions.tools?.includes('dynamic') ? true - : defaultOptions.includeDynamicTools; - - let allTools: boolean | undefined = - queryOptions.no_tools && queryOptions.no_tools?.includes('all') ? false - : queryOptions.tools?.includes('all') ? true - : defaultOptions.includeAllTools; - let docsTools: boolean | undefined = queryOptions.no_tools && queryOptions.no_tools?.includes('docs') ? false : 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: codeTools, includeDocsTools: docsTools, - filters, - capabilities: clientCapabilities, }; } - -function getCapabilitiesExplanation(): string { - return ` -Client Capabilities Explanation: - -Different Language Models (LLMs) and the MCP clients that use them have varying limitations in how they handle tool schemas. Capability flags allow you to inform the MCP server about these limitations. - -When a capability flag is set to false, the MCP server will automatically adjust the tool schemas to work around that limitation, ensuring broader compatibility. - -Available Capabilities: - -# top-level-unions -Some clients/LLMs do not support JSON schemas with a union type (anyOf) at the root level. If a client lacks this capability, the MCP server splits tools with top-level unions into multiple separate tools, one for each variant in the union. - -# refs -Some clients/LLMs do not support $ref pointers for schema reuse. If a client lacks this capability, the MCP server automatically inlines all references ($defs) directly into the schema. Properties that would cause circular references are removed during this process. - -# valid-json -Some clients/LLMs may incorrectly send arguments as a JSON-encoded string instead of a proper JSON object. If a client *has* this capability, the MCP server will attempt to parse string values as JSON if the initial validation against the schema fails. - -# unions -Some clients/LLMs do not support union types (anyOf) in JSON schemas. If a client lacks this capability, the MCP server removes all anyOf fields and uses only the first variant as the schema. - -# formats -Some clients/LLMs do not support the 'format' keyword in JSON Schema specifications. If a client lacks this capability, the MCP server removes all format fields and appends the format information to the field's description in parentheses. - -# tool-name-length=N -Some clients/LLMs impose a maximum length on tool names. If this capability is set, the MCP server will automatically truncate tool names exceeding the specified length (N), ensuring uniqueness by appending numbers if necessary. - -Client Presets (--client): -Presets like '--client=openai-agents' or '--client=cursor' automatically configure these capabilities based on current known limitations of those clients, simplifying setup. - -Current presets: -${JSON.stringify(knownClients, null, 2)} - `; -} - -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', - ], - [`--client="cursor"`, 'Adjust schemas to be more compatible with Cursor'], - [ - `--capability="top-level-unions" --capability="tool-name-length=40"`, - 'Specify individual client capabilities', - ], - [ - `--client="cursor" --no-capability="tool-name-length"`, - 'Use cursor client preset but remove tool name length limit', - ], - ...(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 0feb3c9..ce57ed3 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -2,33 +2,20 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { Endpoint, endpoints, HandlerFunction, query } from './tools'; import { CallToolRequestSchema, ListToolsRequestSchema, SetLevelRequestSchema, - Implementation, - Tool, } from '@modelcontextprotocol/sdk/types.js'; import { ClientOptions } from 'datanewton'; import Datanewton from 'datanewton'; -import { - applyCompatibilityTransformations, - ClientCapabilities, - defaultClientCapabilities, - knownClients, - parseEmbeddedJSON, -} from './compat'; -import { dynamicTools } from './dynamic-tools'; import { codeTool } from './code-tool'; import docsSearchTool from './docs-search-tool'; import { McpOptions } from './options'; +import { HandlerFunction, McpTool } from './types'; export { McpOptions } from './options'; -export { ClientType } from './compat'; -export { Filter } from './tools'; export { ClientOptions } from 'datanewton'; -export { endpoints } from './tools'; export const newMcpServer = () => new McpServer( @@ -52,25 +39,6 @@ export function initMcpServer(params: { mcpOptions?: McpOptions; }) { const server = params.server instanceof McpServer ? params.server.server : params.server; - const mcpOptions = params.mcpOptions ?? {}; - - let providedEndpoints: Endpoint[] | null = null; - let endpointMap: Record | null = null; - - const initTools = async (implementation?: Implementation) => { - if (implementation && (!mcpOptions.client || mcpOptions.client === 'infer')) { - mcpOptions.client = - implementation.name.toLowerCase().includes('claude') ? 'claude' - : implementation.name.toLowerCase().includes('cursor') ? 'cursor' - : undefined; - mcpOptions.capabilities = { - ...(mcpOptions.client && knownClients[mcpOptions.client]), - ...mcpOptions.capabilities, - }; - } - providedEndpoints ??= await selectTools(endpoints, mcpOptions); - endpointMap ??= Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint])); - }; const logAtLevel = (level: 'debug' | 'info' | 'warning' | 'error') => @@ -96,26 +64,23 @@ export function initMcpServer(params: { }, }); + const providedTools = selectTools(params.mcpOptions); + const toolMap = Object.fromEntries(providedTools.map((mcpTool) => [mcpTool.tool.name, mcpTool])); + server.setRequestHandler(ListToolsRequestSchema, async () => { - if (providedEndpoints === null) { - await initTools(server.getClientVersion()); - } return { - tools: providedEndpoints!.map((endpoint) => endpoint.tool), + tools: providedTools.map((mcpTool) => mcpTool.tool), }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { - if (endpointMap === null) { - await initTools(server.getClientVersion()); - } const { name, arguments: args } = request.params; - const endpoint = endpointMap![name]; - if (!endpoint) { + const mcpTool = toolMap[name]; + if (!mcpTool) { throw new Error(`Unknown tool: ${name}`); } - return executeHandler(endpoint.tool, endpoint.handler, client, args, mcpOptions.capabilities); + return executeHandler(mcpTool.handler, client, args); }); server.setRequestHandler(SetLevelRequestSchema, async (request) => { @@ -145,47 +110,22 @@ export function initMcpServer(params: { /** * Selects the tools to include in the MCP Server based on the provided options. */ -export async function selectTools(endpoints: Endpoint[], options?: McpOptions): Promise { - const filteredEndpoints = query(options?.filters ?? [], endpoints); - - let includedTools = filteredEndpoints.slice(); - - if (includedTools.length > 0) { - if (options?.includeDynamicTools) { - includedTools = dynamicTools(includedTools); - } - } else { - if (options?.includeAllTools) { - includedTools = endpoints.slice(); - } else if (options?.includeDynamicTools) { - includedTools = dynamicTools(endpoints); - } else if (options?.includeCodeTools) { - includedTools = [await codeTool()]; - } else { - includedTools = endpoints.slice(); - } - } +export function selectTools(options?: McpOptions): McpTool[] { + const includedTools = [codeTool()]; if (options?.includeDocsTools ?? true) { includedTools.push(docsSearchTool); } - const capabilities = { ...defaultClientCapabilities, ...options?.capabilities }; - return applyCompatibilityTransformations(includedTools, capabilities); + return includedTools; } /** * Runs the provided handler with the given client and arguments. */ export async function executeHandler( - tool: Tool, handler: HandlerFunction, client: Datanewton, args: Record | undefined, - compatibilityOptions?: Partial, ) { - const options = { ...defaultClientCapabilities, ...compatibilityOptions }; - if (!options.validJson && args) { - args = parseEmbeddedJSON(args, tool.inputSchema); - } return await handler(client, args || {}); } diff --git a/packages/mcp-server/src/stdio.ts b/packages/mcp-server/src/stdio.ts index d902a5b..f07696f 100644 --- a/packages/mcp-server/src/stdio.ts +++ b/packages/mcp-server/src/stdio.ts @@ -1,11 +1,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { initMcpServer, newMcpServer } from './server'; -import { McpOptions } from './options'; -export const launchStdioServer = async (options: McpOptions) => { +export const launchStdioServer = async () => { const server = newMcpServer(); - initMcpServer({ server, mcpOptions: options }); + initMcpServer({ server }); const transport = new StdioServerTransport(); await server.connect(transport); diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts deleted file mode 100644 index 7e516de..0000000 --- a/packages/mcp-server/src/tools.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './tools/index'; diff --git a/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts b/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts deleted file mode 100644 index e54bc87..0000000 --- a/packages/mcp-server/src/tools/arbitration-cases/list-arbitration-cases.ts +++ /dev/null @@ -1,125 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'arbitration_cases', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/arbitrationCases', - operationId: '5_getArbitrationCase_1', -}; - -export const tool: Tool = { - name: 'list_arbitration_cases', - description: 'Получить данные о судебных делах контрагента с возможностью фильтрации\n', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - company_role: { - type: 'string', - description: - 'Роль компании \n\n**Возможные значения**:\n- `RESPONDENT` - Ответчик\n- `PLAINTIFF` - Истец\n- `THIRD_PARTY` - Третье лицо\n- `INTERESTED_PERSONS` - Заинтересованное лицо\n- `CREDITOR` - Кредитор\n- `APPLICANT` - Заявитель\n- `OTHER` - Иное лицо\n- `ALL` - Все', - enum: [ - 'RESPONDENT', - 'PLAINTIFF', - 'THIRD_PARTY', - 'INTERESTED_PERSONS', - 'CREDITOR', - 'APPLICANT', - 'DEBTOR', - 'CREDITOR_CURRENT_PAYMENTS', - 'OTHER', - 'ALL', - ], - }, - dispute: { - type: 'string', - description: - 'Арбитражный спор \n\n**Возможные значения**:\n- `ABSENT` - отсутствует(0)\n- `HAS_LEGAL_SIGNIFICANCE` - об установлении фактов, имеющих юридическое значение(1)\n- `ADMINISTRATIVE_OFFENCES` - об административных правонарушениях(2)\n- `CIVIL_LAW_DISPUTES` - экономические споры по гражданским правоотношениям(3)\n- `BANKRUPTCY_ORGANIZATIONS_AND_CITIZENS` - о несостоятельности (банкротстве) организаций и граждан(4)\n- `ECONOMIC_DISPUTES_ON_PUBLIC_LEGAL_RELATIONS` - (экономические споры по административным и иным публичным правоотношениям (исключая споры об административных правонарушениях)(5)\n- `ENFORCMENT_OF_FOREIGN_JUDGMENTS` - о признании и приведении в исполнение решений иностранных судов и иностранных арбитражных решений(6)\n- `OTHER` - иное(7)\n- `ADMINISTRATIVE_DISPUTE` - административный спор(8)\n- `CHALLENGING_COURT_DECISIONS` - об оспаривании решений трет. судов и о выдаче исп. листов на принудительное исполнение решений трет. судов(9)\n- `DISCIPLINARY_DISPUTES` - дисциплинарные споры(10)\n- `ECONOMIC_DISPUTES` - экономические споры по административным правоотношениям(11)', - enum: [ - 'ABSENT', - 'HAS_LEGAL_SIGNIFICANCE', - 'ADMINISTRATIVE_OFFENCES', - 'CIVIL_LAW_DISPUTES', - 'BANKRUPTCY_ORGANIZATIONS_AND_CITIZENS', - 'ECONOMIC_DISPUTES_ON_PUBLIC_LEGAL_RELATIONS', - 'ENFORCMENT_OF_FOREIGN_JUDGMENTS', - 'OTHER', - 'ADMINISTRATIVE_DISPUTE', - 'CHALLENGING_COURT_DECISIONS', - 'DISCIPLINARY_DISPUTES', - 'ECONOMIC_DISPUTES', - ], - }, - end_date: { - type: 'string', - description: 'Дата окончания дела.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - limit: { - type: 'integer', - description: 'Количество сообщений на странице(максимум 1000).', - }, - need_document: { - type: 'boolean', - description: 'Возвращать список документов вынесенных судом или поданных сторонами.', - }, - offset: { - type: 'integer', - description: 'Отступ страницы', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - start_date: { - type: 'string', - description: 'Дата начала дела.', - }, - status: { - type: 'string', - description: - 'Статус дела \n\n**Возможные значения**:\n- `OPEN` - Дело рассматривается(0)\n- `CLOSE` - Дело завершено(1)', - enum: ['CLOSE', 'OPEN'], - }, - updated_at_from: { - type: 'string', - description: 'Дата изменения с', - }, - year: { - type: 'string', - description: 'Год дела.', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.arbitrationCases.list(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts b/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts deleted file mode 100644 index 66f123f..0000000 --- a/packages/mcp-server/src/tools/arbitration/batch-cases/create-arbitration-batch-cases.ts +++ /dev/null @@ -1,144 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'arbitration.batch_cases', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/arbitration/batchCases', - operationId: '26_getBatchCases_1', -}; - -export const tool: Tool = { - name: 'create_arbitration_batch_cases', - description: 'Получить данные об арбитражных делах с возможностью фильтрации', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - limit: { - type: 'integer', - description: - 'Лимит запрошенных арбитражных дел в запросе (значение должно быть > 0, обязательный для заполнения параметр)', - }, - offset: { - type: 'integer', - description: - 'Смещение (для пагинации, отступ в указанное количество дел) (значение должно быть >= 0, обязательный для заполнения параметр)', - }, - case_num: { - type: 'string', - description: 'Номер дела', - }, - court: { - type: 'string', - description: 'Наименование суда', - }, - dispute: { - type: 'string', - description: - 'Арбитражный спор.\n\n**Допустимые значения:**\n- `0` - Отсутствует\n- `1` - Об установлении фактов, имеющих юридическое значение\n- `2` - Об административных правонарушениях\n- `3` - Экономические споры по гражданским правоотношениям\n- `4` - О несостоятельности (банкротстве) организаций и граждан\n- `5` - Экономические споры по административным и иным публичным правоотношениям (исключая споры об административных правонарушениях)\n- `6` - О признании и приведении в исполнение решений иностранных судов и иностранных арбитражных решений\n- `7` - Иное\n- `8` - Административный спор\n- `9` - Об оспаривании решений трет. судов и о выдаче исп. листов на принудительное исполнение решений трет. судов\n- `10` - Дисциплинарные споры\n- `11` - Экономические споры по административным правоотношениям', - enum: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'], - }, - doc_type: { - type: 'string', - description: - 'Тип документа.\n\n**Допустимые значения:**\n- `Определение`\n- `Заявление`\n- `Решения и постановления`\n- `Заявление (исковое заявление)`\n- `Решение`\n- `Прочие судебные документы`\n- `Жалоба`\n- `Апелляционная жалоба`\n- `Постановление апелляционной инстанции`\n- `Информация о принятом судебном акте`\n- `Письмо`\n- `Решение в виде резолютивной части (ст. 229 АПК)`\n- `Решение (резолютивная часть)`\n- `Судебный приказ`\n- `Постановление кассационной инстанции`\n- `Кассационная жалоба`\n- `Постановление апелляционной инстанции (резолютивная часть)`\n- `Определение (резолютивная часть)`\n- `Определение (протокольное)`\n- `Ходатайства`\n- `Ходатайство (заявление)`\n- `Судебное поручение`\n- `Постановление кассационной инстанции (резолютивная часть)`\n- `Определение в виде резолютивной части (ст. 229 АПК)`\n- `Постановление апелляции`\n- `Постановление кассации`\n- `Определение в виде резолютивной части (ст. 229 АПК, ст. 60 ФЗ О несостоятельности)`\n- `Жалоба на определение суда кассационной инстанции`\n- `События`\n- `Постановление апелляционной инстанции (дополнительное)`\n- `Запрос`\n- `Исполнительный лист`\n- `Определение Верховного Суда РФ`\n- `Постановление кассационной инстанции (дополнительное)`\n- `Решение (дополнительное - резолютивная часть)`\n- `Дополнение к делу`\n- `Определение Судебной коллегии ВС РФ`\n- `Ответ заявителю`\n- `Постановление Президиума ВАС РФ`\n- `Постановление апелляционной инстанции (дополнительное - резолютивная часть)`\n- `Решение (дополнительное)`\n- `Отзыв на …`\n- `Переписка по делу`\n- `Представления`\n- `Протокол`\n- `Материалы по делу`\n- `Получение дела`\n- `Переадресация дела`\n- `Справка на возврат госпошлины`\n- `Судебный приказ (дубликат)`\n- `Отзыв`\n- `Постановление судебного пристава-исполнителя`\n- `Распоряжение председателя суда (председателя судебной коллегии, председателя судебного состава)`\n- `Дополнительное решение`\n- `Постановление Верховного Суда РФ`\n- `Взыскание госпошлины`\n- `Исполнительный лист (дубликат)`\n- `Постановление СПИ`\n- `Постановление кассационной инстанции (дополнительное - резолютивная часть)`\n- `Событие`', - }, - need_document: { - type: 'boolean', - description: 'Включение документов в ответ', - }, - order: { - type: 'string', - description: - 'Направление сортировки.\n\n**Допустимые значения:**\n- `ASC` – возрастание\n- `DESC` – убывание (по умолчанию)', - enum: ['ASC', 'DESC'], - }, - participant: { - type: 'string', - description: 'Участник дела (наименование, ОГРН, ИНН)', - }, - role: { - type: 'string', - description: - 'Роль участника в деле.\n\n**Допустимые значения:**\n- `RESPONDENT` – Ответчик\n- `PLAINTIFF` – Истец\n- `THIRD_PARTY` – Третье лицо\n- `INTERESTED_PERSONS` – Заинтересованное лицо\n- `CREDITOR` – Кредитор\n- `APPLICANT` – Заявитель\n- `DEBTOR` – Должник\n- `CREDITOR_CURRENT_PAYMENTS` – Кредитор по текущим платежам\n- `OTHER` – Иная роль в деле', - enum: [ - 'RESPONDENT', - 'PLAINTIFF', - 'THIRD_PARTY', - 'INTERESTED_PERSONS', - 'CREDITOR', - 'APPLICANT', - 'DEBTOR', - 'CREDITOR_CURRENT_PAYMENTS', - 'OTHER', - ], - }, - sort: { - type: 'string', - description: - 'Поле для сортировки арбитражных дел\n\n**Допустимые значения:**\n- `date_start` – дата начала дела (по умолчанию)\n- `sum` – цена иска', - enum: ['date_start', 'sum'], - }, - start_date_from: { - type: 'string', - description: 'Нижняя граница диапазона даты начала дела (дата начала дела с, в формате YYYY-MM-DD)', - format: 'date', - }, - start_date_to: { - type: 'string', - description: 'Верхняя граница диапазона даты начала дела (дата начала дела по, в формате YYYY-MM-DD)', - format: 'date', - }, - status: { - type: 'string', - description: - 'Статус дела.\n\n**Допустимые значения:**\n- `0` - Дело рассматривается\n- `1` - Дело завершено', - enum: ['0', '1'], - }, - sum_from: { - type: 'integer', - description: 'Нижняя граница диапазона цены иска (сумма от)', - }, - sum_to: { - type: 'integer', - description: 'Верхняя граница диапазона цены иска (сумма до)', - }, - updated_at_from: { - type: 'string', - description: 'Нижняя граница периода обновления арбитражных дел (в формате YYYY-MM-DD)', - format: 'date', - }, - updated_at_to: { - type: 'string', - description: 'Верхняя граница периода обновления арбитражных дел (в формате YYYY-MM-DD)', - format: 'date', - }, - }, - required: ['key', 'limit', 'offset'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.arbitration.batchCases.create(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts deleted file mode 100644 index e31ce53..0000000 --- a/packages/mcp-server/src/tools/arbitration/get-courts-arbitration.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'arbitration', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/arbitration/courts-dictionary', - operationId: '28_getArbitrationCasesCourtsDictionary_1', -}; - -export const tool: Tool = { - name: 'get_courts_arbitration', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник арбитражных судов: полный список наименований арбитражных судов РФ и количество дел, рассмотренных и рассматриваемых в данных инстанциях\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/arbitration_get_courts_response',\n $defs: {\n arbitration_get_courts_response: {\n type: 'object',\n description: 'Статистика по арбитражного суда',\n properties: {\n count: {\n type: 'integer',\n description: 'Количество рассмотренных и рассматриваемых арбитражных дел в данной инстанции'\n },\n name: {\n type: 'string',\n description: 'Наименование арбитражного суда (инстанции)'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getCourts())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts deleted file mode 100644 index 1e4f5fd..0000000 --- a/packages/mcp-server/src/tools/arbitration/get-dispute-categories-arbitration.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'arbitration', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/arbitration/dispute-categories-dictionary', - operationId: '30_getArbitrationCasesDisputesDictionary_1', -}; - -export const tool: Tool = { - name: 'get_dispute_categories_arbitration', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник арбитражных споров (категорий), относящихся к компетенции арбитражных судов: категории споров и количество дел в данных категориях\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/arbitration_get_dispute_categories_response',\n $defs: {\n arbitration_get_dispute_categories_response: {\n type: 'object',\n description: 'Статистика по категории спора в арбитражных делах',\n properties: {\n code: {\n type: 'string',\n description: 'Код категории спора'\n },\n count: {\n type: 'integer',\n description: 'Количество рассмотренных и рассматриваемых арбитражных дел в данной категории'\n },\n name: {\n type: 'string',\n description: 'Наименование категории спора'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDisputeCategories())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts b/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts deleted file mode 100644 index 4803e94..0000000 --- a/packages/mcp-server/src/tools/arbitration/get-document-types-arbitration.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'arbitration', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/arbitration/document-types-dictionary', - operationId: '29_getArbitrationCasesDocTypesDictionary_1', -}; - -export const tool: Tool = { - name: 'get_document_types_arbitration', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник типов документов арбитражных дел: полный список документов (судебных актов), изданных арбитражными судами, а также их количество\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/arbitration_get_document_types_response',\n $defs: {\n arbitration_get_document_types_response: {\n type: 'object',\n description: 'Статистика по типу документов в арбитражных делах',\n properties: {\n count: {\n type: 'integer',\n description: 'Количество оформленных документов данного типа'\n },\n name: {\n type: 'string',\n description: 'Наименование судебного акта (тип документа)'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.arbitration.getDocumentTypes())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts b/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts deleted file mode 100644 index b06a063..0000000 --- a/packages/mcp-server/src/tools/bankruptcy/retrieve-bankruptcy.ts +++ /dev/null @@ -1,71 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'bankruptcy', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/bankruptcy', - operationId: '17_getBankruptcy', -}; - -export const tool: Tool = { - name: 'retrieve_bankruptcy', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить признаки банкротства контрагента\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/bankruptcy_retrieve_response',\n $defs: {\n bankruptcy_retrieve_response: {\n type: 'object',\n description: 'Ответ: банкротства',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n data: {\n type: 'array',\n description: 'Данные',\n items: {\n type: 'object',\n description: 'Банкротство',\n properties: {\n content: {\n type: 'string',\n description: 'Текст сообщения'\n },\n guid: {\n type: 'string',\n description: 'GUID сообщения'\n },\n number: {\n type: 'string',\n description: 'Номер сообщения'\n },\n participants: {\n type: 'array',\n description: 'Участники сообщения, лица упомянутые в сообщении',\n items: {\n type: 'object',\n description: 'Участник',\n properties: {\n full_name: {\n type: 'string',\n description: 'Наименование/ФИО'\n },\n inn: {\n type: 'string',\n description: 'ИНН'\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН/ОГРНИП'\n },\n role: {\n type: 'string',\n description: 'Роль участника (не заполняется для публикатора)',\n enum: [ 'Mortgagor',\n 'Mortgagee',\n 'Guarantor',\n 'Principal',\n 'Beneficiary',\n 'OtherGuaranteeContractor',\n 'Lessor',\n 'Lessee',\n 'MonetaryClient',\n 'MonetaryDebtor',\n 'FinancialAgent',\n 'Seller',\n 'Buyer',\n 'Grantor',\n 'Concessionaire',\n 'EstateOwner',\n 'Debtor',\n 'CommonParticipant',\n 'ReorganizationCompany',\n 'Sro',\n 'Expert',\n 'Creditor',\n 'Appraiser',\n 'Customer',\n 'Owner',\n 'Auditee',\n 'Auditor',\n 'Renter',\n 'Landlord',\n 'Founder',\n 'ParticipantWithRightToBuyBack',\n 'ObligedParty',\n 'ParticipantInterestedInRightsRestriction',\n 'ParticipantWithRestrictedRights',\n 'ParticipantHoldingItem',\n 'DebtCollector',\n 'Organizer',\n 'ControllingPerson',\n 'ControlledNonResidentCompany',\n 'Company',\n 'TradeOrganizer'\n ]\n },\n type: {\n type: 'string',\n description: 'Тип участника',\n enum: [ 'Company',\n 'IndividualEntrepreneur',\n 'Person',\n 'Appraiser',\n 'NonResidentCompany',\n 'ForeignSystem',\n 'Notary'\n ]\n }\n }\n }\n },\n publish_date: {\n type: 'string',\n description: 'Дата публикации сообщения'\n },\n publisher: {\n type: 'object',\n description: 'Участник',\n properties: {\n full_name: {\n type: 'string',\n description: 'Наименование/ФИО'\n },\n inn: {\n type: 'string',\n description: 'ИНН'\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН/ОГРНИП'\n },\n role: {\n type: 'string',\n description: 'Роль участника (не заполняется для публикатора)',\n enum: [ 'Mortgagor',\n 'Mortgagee',\n 'Guarantor',\n 'Principal',\n 'Beneficiary',\n 'OtherGuaranteeContractor',\n 'Lessor',\n 'Lessee',\n 'MonetaryClient',\n 'MonetaryDebtor',\n 'FinancialAgent',\n 'Seller',\n 'Buyer',\n 'Grantor',\n 'Concessionaire',\n 'EstateOwner',\n 'Debtor',\n 'CommonParticipant',\n 'ReorganizationCompany',\n 'Sro',\n 'Expert',\n 'Creditor',\n 'Appraiser',\n 'Customer',\n 'Owner',\n 'Auditee',\n 'Auditor',\n 'Renter',\n 'Landlord',\n 'Founder',\n 'ParticipantWithRightToBuyBack',\n 'ObligedParty',\n 'ParticipantInterestedInRightsRestriction',\n 'ParticipantWithRestrictedRights',\n 'ParticipantHoldingItem',\n 'DebtCollector',\n 'Organizer',\n 'ControllingPerson',\n 'ControlledNonResidentCompany',\n 'Company',\n 'TradeOrganizer'\n ]\n },\n type: {\n type: 'string',\n description: 'Тип участника',\n enum: [ 'Company',\n 'IndividualEntrepreneur',\n 'Person',\n 'Appraiser',\n 'NonResidentCompany',\n 'ForeignSystem',\n 'Notary'\n ]\n }\n }\n },\n type_description: {\n type: 'string',\n description: 'Тип сообщения (описание)'\n },\n type_name: {\n type: 'string',\n description: 'Тип сообщения',\n enum: [ 'CreditorIntentionGoToCourt',\n 'DebtorIntentionGoToCourt',\n 'BankruptcyArticle8',\n 'BankruptcyArticle9',\n 'AssetImpairment',\n 'AppearanceOfBankruptcySigns',\n 'DebtorBankruptcyCourtNotification',\n 'FirmSupervisionPutIn',\n 'MoratoriumRejection'\n ]\n }\n }\n }\n },\n limit: {\n type: 'integer',\n description: 'Размер страницы'\n },\n offset: {\n type: 'integer',\n description: 'Смещение страницы'\n },\n total: {\n type: 'integer',\n description: 'Всего записей'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - limit: { - type: 'integer', - description: 'Количество договоров на странице(максимум 1000).', - }, - offset: { - type: 'integer', - description: 'Отступ страницы', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.bankruptcy.retrieve(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts b/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts deleted file mode 100644 index d48e90f..0000000 --- a/packages/mcp-server/src/tools/batch-cards-by-filters/create-batch-cards-by-filters.ts +++ /dev/null @@ -1,516 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'batch_cards_by_filters', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/batchCardsByFilters', - operationId: '18_batchCounterpartiesByFilters', -}; - -export const tool: Tool = { - name: 'create_batch_cards_by_filters', - description: 'Получить данные контрагентов по фильтрам/условиям: оквэды, регионы и т.д.', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - limit: { - type: 'integer', - description: 'Лимит.', - }, - offset: { - type: 'integer', - description: 'Оффсет.', - }, - contact_conditions_operator: { - type: 'string', - description: - 'Тип объединения условий контактной информации (AND - все условия должны выполняться, OR - хотя бы одно)', - enum: ['AND', 'OR'], - }, - contracts: { - type: 'object', - description: 'Запрос по контрактам', - properties: { - contract_date_from: { - type: 'string', - description: 'Дата заключения контракта: С (включительно)', - format: 'date', - }, - contract_date_to: { - type: 'string', - description: 'Дата заключения контракта: По (включительно)', - format: 'date', - }, - contract_type: { - type: 'string', - description: 'Тип контракта (FZ44, FZ223 и др.)', - enum: ['FZ44', 'FZ223', 'ALL'], - }, - has_contracts: { - type: 'boolean', - description: 'Наличие контрактов', - }, - max_price: { - type: 'number', - description: 'Максимальная сумма контракта', - }, - min_price: { - type: 'number', - description: 'Минимальная сумма контракта', - }, - okpd2_codes: { - type: 'array', - description: 'Коды ОКПД2 предмета контракта', - items: { - type: 'string', - description: 'Коды ОКПД2 предмета контракта', - }, - }, - only_active: { - type: 'boolean', - description: 'Только активные контракты', - }, - region_code: { - type: 'string', - description: 'Регион (код субъекта РФ)', - }, - role: { - type: 'string', - description: 'Роль контрагента', - enum: ['SUPPLIER', 'CUSTOMER'], - }, - search_terms: { - type: 'array', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ)', - items: { - type: 'string', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ)', - }, - }, - search_text: { - type: 'string', - description: 'Ключевые слова для поиска в предмете контракта', - }, - }, - }, - counterparty_type: { - type: 'string', - description: 'Возможные типы контрагентов', - enum: ['ul', 'ip', 'fl', 'all'], - }, - egr_statuses: { - type: 'array', - description: 'Статус компании в ЕГРЮЛ', - items: { - type: 'string', - description: 'Статус компании в ЕГРЮЛ', - }, - }, - establishment_date_from: { - type: 'string', - description: 'Дата создания компании: с', - format: 'date', - }, - establishment_date_to: { - type: 'string', - description: 'Дата создания компании: по', - format: 'date', - }, - exclude_okveds: { - type: 'array', - description: - 'Исключить список ОКВЭД (компаний с какими оквэдами быть не должно) для выгрузки. По умолчанию применяется только к основным ОКВЭД, для применения к дополнительным необходимо устанавливать свойство exclude_only_main_okveds = false.', - items: { - type: 'string', - description: - 'Исключить список ОКВЭД (компаний с какими оквэдами быть не должно) для выгрузки. По умолчанию применяется только к основным ОКВЭД, для применения к дополнительным необходимо устанавливать свойство exclude_only_main_okveds = false.', - }, - }, - exclude_only_main_okveds: { - type: 'boolean', - description: - 'Применять поле exclude_okveds только к основным ОКВЭД компаний. По умолчанию true - исключать из выборке только те компании, у которых значения exclude_okveds среди основных ОКВЭД', - }, - finance_report_year: { - type: 'integer', - description: - 'По какому отчетному финансовому году применяем фильтр по выручке/прибыли. Если 0, то смотрим за последний известный год (когда компания подавала отчетность)', - }, - finance_request: { - type: 'object', - description: 'Запрос по финансам', - properties: { - metrics: { - type: 'array', - description: 'Финансовые метрики для анализа', - items: { - type: 'string', - description: 'Финансовые метрики для анализа', - enum: ['Выручка', 'Чистая прибыль', 'INCOME', 'NET_INCOME'], - }, - }, - growth_from: { - type: 'number', - description: 'Динамика выбранных метрик: рост от (в %)', - }, - growth_to: { - type: 'number', - description: 'Динамика выбранных метрик: рост до (в %)', - }, - year_by_year: { - type: 'boolean', - description: - 'Применять условия по росту к каждому году (год к году), а не только к первому и последнему', - }, - years_count: { - type: 'integer', - description: 'Количество лет для анализа динамики', - }, - }, - required: ['metrics'], - }, - has_income: { - type: 'boolean', - description: 'Флаг - выгружать только те, у которых есть выручка', - }, - income_from: { - type: 'number', - description: 'Выручка за последний год (в тыс. рублей): с', - }, - income_to: { - type: 'number', - description: 'Выручка за последний год (в тыс. рублей): по', - }, - leases: { - type: 'object', - description: 'Запрос по договорам лизинга', - properties: { - role: { - type: 'string', - description: 'Роль контрагента', - enum: ['Lessor', 'Lessee'], - }, - classifier_codes: { - type: 'array', - description: 'Коды типа лизинга из классификатора (справочника)', - items: { - type: 'string', - description: 'Коды типа лизинга из классификатора (справочника)', - }, - }, - contract_date_from: { - type: 'string', - description: 'Дата заключения договора: С (включительно)', - format: 'date', - }, - contract_date_to: { - type: 'string', - description: 'Дата заключения договора: По (включительно)', - format: 'date', - }, - excluded_text: { - type: 'string', - description: 'Слова-исключения в реквизитах договора', - }, - has_leases: { - type: 'boolean', - description: 'Наличие договоров лизинга', - }, - only_active: { - type: 'boolean', - description: 'Только активные договора', - }, - search_terms: { - type: 'array', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в реквизитах договора ', - items: { - type: 'string', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в реквизитах договора ', - }, - }, - search_text: { - type: 'string', - description: 'Ключевые слова для поиска в реквизитах договора', - }, - stop_date_from: { - type: 'string', - description: 'Дата прекращения договора: С (включительно)', - format: 'date', - }, - stop_date_to: { - type: 'string', - description: 'Дата прекращения договора: По (включительно)', - format: 'date', - }, - }, - required: ['role'], - }, - licenses: { - type: 'array', - description: 'Список номеров лицензий', - items: { - type: 'integer', - description: 'Список номеров лицензий', - }, - }, - msp_categories: { - type: 'array', - description: - 'Категории МСП. Возможные варианты: 0 - не в МСП (никогда не состоял или вышел), 1 - микропредприятие, 2 - малое предприятие, 3 - среднее предприятие ', - items: { - type: 'string', - description: - 'Категории МСП. Возможные варианты: 0 - не в МСП (никогда не состоял или вышел), 1 - микропредприятие, 2 - малое предприятие, 3 - среднее предприятие ', - }, - }, - net_income_from: { - type: 'number', - description: 'Прибыль за последний год (в тыс. рублей): с', - }, - net_income_to: { - type: 'number', - description: 'Прибыль за последний год (в тыс. рублей): по', - }, - okveds: { - type: 'array', - description: 'Список ОКВЭД для выгрузки', - items: { - type: 'string', - description: 'Список ОКВЭД для выгрузки', - }, - }, - only_active: { - type: 'boolean', - description: 'Флаг - только действующие компании', - }, - only_it_companies: { - type: 'boolean', - description: 'Вернуть только аккредитованные ИТ-компании', - }, - only_main_okveds: { - type: 'boolean', - description: - 'Поиск указанных в поле "okveds" видов деятельности среди основных ОКВЭДов компаний. По умолчанию true.', - }, - only_msp_innovative: { - type: 'boolean', - description: 'Производство инновационной, высокотехнологичной продукции', - }, - only_msp_partner: { - type: 'boolean', - description: 'Является партнером', - }, - only_msp_social: { - type: 'boolean', - description: 'Социальное предприятие', - }, - only_nopriz_members: { - type: 'boolean', - description: 'Только члены СРО НОПРИЗ', - }, - only_nostroy_members: { - type: 'boolean', - description: 'Только члены СРО НОСТРОЙ', - }, - only_with_bfo: { - type: 'boolean', - description: 'Только с наличием данных БФО за выбранный год', - }, - only_with_emails: { - type: 'boolean', - description: 'Наличие у контрагента минимум одной электронной почты', - }, - only_with_phones: { - type: 'boolean', - description: 'Наличие у контрагента минимум одного номера телефона', - }, - only_with_websites: { - type: 'boolean', - description: 'Наличие у контрагента минимум одного сайта', - }, - opf_codes: { - type: 'array', - description: 'Код ОПФ или тип контрагента (ip, ul)', - items: { - type: 'string', - description: 'Код ОПФ или тип контрагента (ip, ul)', - }, - }, - region_codes: { - type: 'array', - description: 'Коды регионов', - items: { - type: 'string', - description: 'Коды регионов', - }, - }, - rosaccreditations: { - type: 'object', - description: 'Реестр деклараций/сертификатов соответствия', - properties: { - applicant_type: { - type: 'array', - description: 'Тип заявителя', - items: { - type: 'string', - description: 'Тип заявителя', - enum: [ - 'Продавец', - 'Изготовитель', - 'Иcполнитель', - 'Уполномоченное изготовителем лицо', - 'Поставщик', - ], - }, - }, - description: { - type: 'string', - description: 'Слова в описании документа Росаккредитации', - }, - search_terms: { - type: 'array', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в описании документа Росаккредитации ', - items: { - type: 'string', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в описании документа Росаккредитации ', - }, - }, - statuses: { - type: 'array', - description: 'Статус декларации или сертификата', - items: { - type: 'string', - description: 'Статус декларации или сертификата', - enum: [ - 'Архивный', - 'Возобновлён', - 'Выдано предписание', - 'Действует', - 'Недействителен', - 'Прекращён', - 'Приостановлен', - 'Продлен', - 'Направлено уведомление о прекращении', - 'Черновик', - 'Ожидает проверки оператора реестра', - ], - }, - }, - type: { - type: 'string', - description: 'Тип документа', - enum: ['Декларация', 'Сертификат', 'Декларация или сертификат'], - }, - }, - }, - search_terms: { - type: 'array', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) для свободного поиска ', - items: { - type: 'string', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) для свободного поиска ', - }, - }, - search_text: { - type: 'string', - description: 'Свободное поле поиска', - }, - ssch_from: { - type: 'integer', - description: 'Численность сотрудников: от', - }, - ssch_to: { - type: 'integer', - description: 'Численность сотрудников: по', - }, - support_forms: { - type: 'array', - description: 'Список видов/форм поддержки', - items: { - type: 'integer', - description: 'Список видов/форм поддержки', - }, - }, - vacancies: { - type: 'object', - description: 'Запрос по вакансиям', - properties: { - excluded_text: { - type: 'string', - description: 'Слово в названии или описании вакансии, которого быть не должно', - }, - has_vacancies: { - type: 'boolean', - description: 'Наличие вакансий', - }, - only_active: { - type: 'boolean', - description: 'Вакансия должна быть активной', - }, - only_name: { - type: 'boolean', - description: - 'Искать поисковые слова/термины (или слова исключения) только в названии вакансии (без описания). То есть, применять условия text, search_terms, excluded_text только к названию вакансии (значение true) или к названию и описанию (значение false, по умолчанию)', - }, - salary_max: { - type: 'number', - description: 'Зарплата: до', - }, - salary_min: { - type: 'number', - description: 'Зарплата: от', - }, - search_terms: { - type: 'array', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в названии или описании вакансии', - items: { - type: 'string', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в названии или описании вакансии', - }, - }, - source: { - type: 'string', - description: 'Источник вакансий', - enum: ['HH_VACANCIES', 'VACANCIES', 'ALL'], - }, - text: { - type: 'string', - description: 'Слово в названии или описании вакансии', - }, - }, - }, - }, - required: ['key', 'limit', 'offset'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.batchCardsByFilters.create(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts b/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts deleted file mode 100644 index ebc96bf..0000000 --- a/packages/mcp-server/src/tools/batch-cards/create-batch-cards.ts +++ /dev/null @@ -1,52 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'batch_cards', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/batchCards', - operationId: '3_getBatchCounterpatriesByInnsOrOgrns', -}; - -export const tool: Tool = { - name: 'create_batch_cards', - description: - 'Получить информацию о нескольких организациях одним запросом (до 5 000 контрагентов в запросе), отправив список ИНН/ОГРН', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - source_inns_or_ogrns: { - type: 'array', - items: { - type: 'string', - }, - }, - }, - required: ['key'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.batchCards.create(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts b/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts deleted file mode 100644 index 0210e1e..0000000 --- a/packages/mcp-server/src/tools/batch-changes/create-batch-changes.ts +++ /dev/null @@ -1,116 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'batch_changes', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/batchChanges', - operationId: '4_getBatchCompanyChanges', -}; - -export const tool: Tool = { - name: 'create_batch_changes', - description: - 'Получить изменения по списку контрагентов с определенной даты. \nМожно смотреть изменения за день, за неделю, за квартал, а также за произвольный период времени. \nМожно получить историю изменений выбранных параметров за произвольный период времени. \nОтслеживаются:\n- ИНН, ОГРН, КПП\n- Статус по ЕГРЮЛ/ЕГРИП\n- Название:\n - Полное\n - Краткое\n - Организационно-правовая форма (ОПФ)\n - Код ОПФ по ЕГРЮЛ\n- Адрес местонахождения\n- Единоличный исполнительный орган:\n - Руководитель\n - Управляющая компания\n- Участники\n- Уставный капитал:\n - Размер\n - Тип\n- Реестр МСП:\n - Статус\n - Категория отнесения\n- Недостоверность:\n - Учредителей\n - Руководителей\n - Управляющей компании\n - Адреса\n- Дисквалификация:\n - Учредителей\n - Руководителей\n- Налоговые режимы\n- Бухгалтерская (финансовая) отчетность\n- ОКВЭД (добавление, изменение, удаление)\n\nМаксимальное количество контрагентов в одном запросе - 500.\nДля получения ключа к этой точке API [напишите нам](mailto:info@datanomica.org).', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - include_history: { - type: 'boolean', - description: 'Включить историю изменений параметра', - }, - ogrns: { - type: 'array', - description: 'ОГРН компаний, по которым нужно получить изменения', - items: { - type: 'string', - description: 'ОГРН компаний, по которым нужно получить изменения', - }, - }, - param_types: { - type: 'array', - description: - 'Параметры мониторинга. \n**Возможные значения типа изменений**:\n- `REPORT` — Бух/Фин отчетность (публикация нового отчета);\n- `MSP_STATUS` — статус МСП;\n- `MSP_CATEGORY` — категория МСП;\n- `MANAGEMENT_COMPANY` — данные управляющей компании;\n- `OGRN` — ОГРН;\n- `INN` — ИНН;\n- `KPP` — КПП;\n- `FULL_NAME` — полное наименования компании;\n- `SHORT_NAME` — краткое наименования компании;\n- `OPF` — ОПФ (организационно-правовой формы) компании;\n- `OPF_CODE` — код ОПФ;\n- `ADDRESS_FALSE_INFO` — признак недостоверности адреса;\n- `OKVED` — ОКВЭД;\n- `OWNERS` — участники;\n- `MANAGERS` — руководители;\n- `EGR_STATUS` — статус организации в ЕГРЮЛ/ЕГРИП;\n- `COUNTERPARTY_CREATION` — факт создания организации;\n- `REG_ADDRESS` — адрес местонахождения организации;\n- `CAPITAL_TABLE_SIZE` — размер уставного капитала;\n- `CAPITAL_TABLE_TYPE` — тип уставного капитала;\n- `DISQUALIFIED_OWNER` — дисквалификация участника;\n- `DISQUALIFIED_MANAGER` — дисквалификация руководителей;\n- `OWNERS_FALSE_INFO` — признак недостоверности участников;\n- `MANAGERS_FALSE_INFO` — признак недостоверности руководителей;\n- `MANAGEMENT_COMPANY_FALSE_INFO` — признак недостоверности управляющей компании;\n- `TAX_MODE_ESHN_SIGN` — налоговый режим (ЕСХН);\n- `TAX_MODE_USN_SIGN` — налоговый режим (УСН);\n- `TAX_MODE_ENVD_SIGN` — налоговый режим (ЕНДВ);\n- `TAX_MODE_SRP_SIGN` — налоговый режим (СРП);\n- `TAX_MODE_AUSN_SIGN` — налоговый режим (АУСН);\n- `ALL` — все параметры', - items: { - type: 'string', - description: - 'Параметры мониторинга. \n**Возможные значения типа изменений**:\n- `REPORT` — Бух/Фин отчетность (публикация нового отчета);\n- `MSP_STATUS` — статус МСП;\n- `MSP_CATEGORY` — категория МСП;\n- `MANAGEMENT_COMPANY` — данные управляющей компании;\n- `OGRN` — ОГРН;\n- `INN` — ИНН;\n- `KPP` — КПП;\n- `FULL_NAME` — полное наименования компании;\n- `SHORT_NAME` — краткое наименования компании;\n- `OPF` — ОПФ (организационно-правовой формы) компании;\n- `OPF_CODE` — код ОПФ;\n- `ADDRESS_FALSE_INFO` — признак недостоверности адреса;\n- `OKVED` — ОКВЭД;\n- `OWNERS` — участники;\n- `MANAGERS` — руководители;\n- `EGR_STATUS` — статус организации в ЕГРЮЛ/ЕГРИП;\n- `COUNTERPARTY_CREATION` — факт создания организации;\n- `REG_ADDRESS` — адрес местонахождения организации;\n- `CAPITAL_TABLE_SIZE` — размер уставного капитала;\n- `CAPITAL_TABLE_TYPE` — тип уставного капитала;\n- `DISQUALIFIED_OWNER` — дисквалификация участника;\n- `DISQUALIFIED_MANAGER` — дисквалификация руководителей;\n- `OWNERS_FALSE_INFO` — признак недостоверности участников;\n- `MANAGERS_FALSE_INFO` — признак недостоверности руководителей;\n- `MANAGEMENT_COMPANY_FALSE_INFO` — признак недостоверности управляющей компании;\n- `TAX_MODE_ESHN_SIGN` — налоговый режим (ЕСХН);\n- `TAX_MODE_USN_SIGN` — налоговый режим (УСН);\n- `TAX_MODE_ENVD_SIGN` — налоговый режим (ЕНДВ);\n- `TAX_MODE_SRP_SIGN` — налоговый режим (СРП);\n- `TAX_MODE_AUSN_SIGN` — налоговый режим (АУСН);\n- `ALL` — все параметры', - enum: [ - 'REPORT', - 'MSP_STATUS', - 'MSP_CATEGORY', - 'MANAGEMENT_COMPANY', - 'OGRN', - 'INN', - 'KPP', - 'FULL_NAME', - 'SHORT_NAME', - 'OPF', - 'OPF_CODE', - 'ADDRESS_FALSE_INFO', - 'OKVED', - 'OWNERS', - 'MANAGERS', - 'EGR_STATUS', - 'COUNTERPARTY_CREATION', - 'REG_ADDRESS', - 'CAPITAL_TABLE_SIZE', - 'CAPITAL_TABLE_TYPE', - 'DISQUALIFIED_OWNER', - 'DISQUALIFIED_MANAGER', - 'OWNERS_FALSE_INFO', - 'MANAGERS_FALSE_INFO', - 'MANAGEMENT_COMPANY_FALSE_INFO', - 'TAX_MODE_ESHN_SIGN', - 'TAX_MODE_USN_SIGN', - 'TAX_MODE_ENVD_SIGN', - 'TAX_MODE_SRP_SIGN', - 'TAX_MODE_AUSN_SIGN', - 'BANKRUPTCY', - 'CASE_RESPONDENT', - 'CASE_PLAINTIFF', - 'CASE_THIRD_PARTY', - 'CASE_INTERESTED_PERSONS', - 'CASE_CREDITOR', - 'CASE_APPLICANT', - 'CASE_DEBTOR', - 'CASE_CREDITOR_CURRENT_PAYMENTS', - 'CASE_OTHER', - 'ALL', - ], - }, - }, - start_date: { - type: 'string', - description: 'Дата начала мониторинга изменений', - format: 'date', - }, - }, - required: ['key'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.batchChanges.create(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts b/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts deleted file mode 100644 index 9d8d92c..0000000 --- a/packages/mcp-server/src/tools/blocked-bank-accounts/list-blocked-bank-accounts.ts +++ /dev/null @@ -1,63 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'blocked_bank_accounts', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/blockedBankAccounts', - operationId: '31_getBlockedBankAccounts', -}; - -export const tool: Tool = { - name: 'list_blocked_bank_accounts', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить структурированные данные о решениях ФНС о приостановлении операций по счетам юридических лиц и ИП\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/blocked_bank_account_list_response',\n $defs: {\n blocked_bank_account_list_response: {\n type: 'object',\n description: 'Информация о решениях налоговых органов о приостановлении операций по счетам компании',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n blockages: {\n type: 'array',\n description: 'Информация о блокировках',\n items: {\n type: 'object',\n description: 'Информация о блокировке',\n properties: {\n bank_bic: {\n type: 'string',\n description: 'БИК банка'\n },\n code_tax_inspection: {\n type: 'string',\n description: 'Код налогового органа'\n },\n decision_date: {\n type: 'string',\n description: 'Дата приостановления операции по счетам',\n format: 'date'\n },\n decision_number: {\n type: 'string',\n description: 'Номер приостановления операции по счетам'\n },\n reason_code: {\n type: 'string',\n description: 'Код основания'\n }\n }\n }\n },\n check_date: {\n type: 'string',\n description: 'Дата и время выполнения live-запроса в ФНС',\n format: 'date-time'\n },\n inn: {\n type: 'string',\n description: 'ИНН, по которому получены данные'\n },\n message: {\n type: 'string',\n description: 'Сообщение о наличии блокировок'\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН, по которому получены данные'\n },\n total_blockages_count: {\n type: 'integer',\n description: 'Общее количество найденных записей'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации. Должен быть указан, если не указан ОГРН.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.blockedBankAccounts.list(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts b/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts deleted file mode 100644 index 05c09d0..0000000 --- a/packages/mcp-server/src/tools/corporate-actions/list-corporate-actions.ts +++ /dev/null @@ -1,101 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'corporate_actions', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/corporateActions', - operationId: '12_getFedResource', -}; - -export const tool: Tool = { - name: 'list_corporate_actions', - description: 'Получить данные о корпоративных действиях из ФедРесурса\n', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - date_from: { - type: 'string', - description: 'Дата публикации от', - }, - date_to: { - type: 'string', - description: 'Дата публикации до', - }, - group: { - type: 'string', - description: 'Группа типов: CapitalChange | RegisterAction | Shareholders | Other', - enum: ['CapitalChange', 'RegisterAction', 'Shareholders', 'Other'], - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - limit: { - type: 'integer', - description: 'Количество сообщений на странице(максимум 1000).', - }, - offset: { - type: 'integer', - description: 'Отступ страницы', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - type: { - type: 'string', - description: 'Тип сообщения: FirmAuthorizedCapitalDecrease | FirmAuthorizedCapitalIncrease |...', - enum: [ - 'FirmAuthorizedCapitalDecrease', - 'FirmAuthorizedCapitalIncrease', - 'FirmSharesAcquisition', - 'FirmLiquidation', - 'FirmReorganization', - 'FirmRegisterExcludeTermination', - 'FirmAutonomousInstitutionCreation', - 'FirmCreated', - 'FirmRegisterExclude', - 'StopOfBusiness', - 'FirmAuthoritiesChange', - 'UnreliableInformation', - 'IntentionExerciseTheRightsOfShareholderNonResident', - 'SatisfactionExerciseTheRightsOfShareholderNonResident', - 'FirmMembersMeeting', - 'SaleOrLeaseEnterprise', - 'AnyOther', - 'RevocationOfPowerAttorney', - 'IntentionInheritanceOfLandProperty', - ], - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.corporateActions.list(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts b/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts deleted file mode 100644 index 368aa8f..0000000 --- a/packages/mcp-server/src/tools/counterparty/retrieve-counterparty.ts +++ /dev/null @@ -1,75 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'counterparty', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/counterparty', - operationId: '1_getCompanyData', -}; - -export const tool: Tool = { - name: 'retrieve_counterparty', - description: - 'Получить общую информацию о контрагенте из ЕГРЮЛ/ЕГРИП. Конкретное наполнение зависит от переданных секций, описание которых приведено в параметрах запроса.', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - filters: { - type: 'array', - description: - 'Перечень дополнительных секций с данными, которые нужно включить в ответ, разделённые запятой.\nЕсли не указан, то возвращается общая информация об организации (названия, дата регистрации, КПП, статус, уставный капитал).\n**Возможные значения**:\n- `ROSSTAT_BLOCK` — данные о кодах Росстата (ОКПО, ОКАТО, ОКФС, ОКТМО, ОКОГУ, ОКОПФ);\n- `ADDRESS_BLOCK` — данные о юридическом адресе и отметках из ЕГРЮЛ;\n- `MANAGER_BLOCK` — данные о руководителях организации, имеющих право действовать от имени ЮЛ без доверенности;\n- `OWNER_BLOCK` — данные об учредителях и участниках организации (физических лицах, российских ЮЛ, иностранных ЮЛ, ПИФах, государственных учреждениях и субъектах РФ, инвестиционных товариществах);\n- `OKVED_BLOCK` — данные об основных и дополнительных видах экономической деятельности (кодах ОКВЭД) организации;\n- `NEGATIVE_LISTS_BLOCK` — данные о присутствии контрагента (в том числе руководителей, учредителей) в негативных списках: дисквалификация, недостоверные сведения, РНП и т.д.;\n- `WORKERS_COUNT_BLOCK` — данные о среднесписочной численности сотрудников по годам;\n- `CONTACT_BLOCK` — данные о контактах (email, web-сайт, телефон)', - items: { - type: 'string', - enum: [ - 'ROSSTAT_BLOCK', - 'ADDRESS_BLOCK', - 'MANAGER_BLOCK', - 'OWNER_BLOCK', - 'OKVED_BLOCK', - 'NEGATIVE_LISTS_BLOCK', - 'WORKERS_COUNT_BLOCK', - 'CONTACT_BLOCK', - ], - }, - }, - inn: { - type: 'string', - description: - 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП. В случае определения нескольких организаций с этим ИНН (например, при наличии филиалов) будет возвращена информация по головной организации.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.counterparty.retrieve(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts deleted file mode 100644 index 81f7f70..0000000 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-courts-dictionary-arbitration.ts +++ /dev/null @@ -1,53 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary.arbitration', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/arbitration/courts', - operationId: '28_getArbitrationCasesCourtsDictionary', -}; - -export const tool: Tool = { - name: 'list_courts_dictionary_arbitration', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник арбитражных судов: полный список наименований арбитражных судов РФ и количество дел, рассмотренных и рассматриваемых в данных инстанциях\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/arbitration_list_courts_response',\n $defs: {\n arbitration_list_courts_response: {\n type: 'object',\n description: 'Статистика по арбитражного суда',\n properties: {\n count: {\n type: 'integer',\n description: 'Количество рассмотренных и рассматриваемых арбитражных дел в данной инстанции'\n },\n name: {\n type: 'string',\n description: 'Наименование арбитражного суда (инстанции)'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult( - await maybeFilter(jq_filter, await client.dictionary.arbitration.listCourts()), - ); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts deleted file mode 100644 index 76b63e1..0000000 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-dispute-categories-dictionary-arbitration.ts +++ /dev/null @@ -1,53 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary.arbitration', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/arbitration/dispute-categories', - operationId: '30_getArbitrationCasesDisputesDictionary', -}; - -export const tool: Tool = { - name: 'list_dispute_categories_dictionary_arbitration', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник арбитражных споров (категорий), относящихся к компетенции арбитражных судов: категории споров и количество дел в данных категориях\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/arbitration_list_dispute_categories_response',\n $defs: {\n arbitration_list_dispute_categories_response: {\n type: 'object',\n description: 'Статистика по категории спора в арбитражных делах',\n properties: {\n code: {\n type: 'string',\n description: 'Код категории спора'\n },\n count: {\n type: 'integer',\n description: 'Количество рассмотренных и рассматриваемых арбитражных дел в данной категории'\n },\n name: {\n type: 'string',\n description: 'Наименование категории спора'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult( - await maybeFilter(jq_filter, await client.dictionary.arbitration.listDisputeCategories()), - ); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts b/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts deleted file mode 100644 index 71ac417..0000000 --- a/packages/mcp-server/src/tools/dictionary/arbitration/list-document-types-dictionary-arbitration.ts +++ /dev/null @@ -1,53 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary.arbitration', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/arbitration/document-types', - operationId: '29_getArbitrationCasesDocTypesDictionary', -}; - -export const tool: Tool = { - name: 'list_document_types_dictionary_arbitration', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник типов документов арбитражных дел: полный список документов (судебных актов), изданных арбитражными судами, а также их количество\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/arbitration_list_document_types_response',\n $defs: {\n arbitration_list_document_types_response: {\n type: 'object',\n description: 'Статистика по типу документов в арбитражных делах',\n properties: {\n count: {\n type: 'integer',\n description: 'Количество оформленных документов данного типа'\n },\n name: {\n type: 'string',\n description: 'Наименование судебного акта (тип документа)'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult( - await maybeFilter(jq_filter, await client.dictionary.arbitration.listDocumentTypes()), - ); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts deleted file mode 100644 index 5e3ccdc..0000000 --- a/packages/mcp-server/src/tools/dictionary/list-lease-classifier-dictionary.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/lease-classifier', - operationId: '24_getLeaseClassifierDictionary_1', -}; - -export const tool: Tool = { - name: 'list_lease_classifier_dictionary', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить классификатор лизинговых договоров: код лизинга, его наименование и количество заключенных договоров с данным кодом\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/dictionary_list_lease_classifier_response',\n $defs: {\n dictionary_list_lease_classifier_response: {\n type: 'object',\n properties: {\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Код - расшифровка - количество',\n properties: {\n code: {\n type: 'string',\n description: 'Значение кода'\n },\n count: {\n type: 'integer',\n description: 'Количество'\n },\n group: {\n type: 'string',\n description: 'Группа, к которой относится код'\n },\n group_code: {\n type: 'string',\n description: 'Код группы, к которой относится код'\n },\n name: {\n type: 'string',\n description: 'Расшифровка кода'\n },\n type: {\n type: 'string',\n description: 'Тип контрагента: ip или ul'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLeaseClassifier())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts deleted file mode 100644 index ad4bd69..0000000 --- a/packages/mcp-server/src/tools/dictionary/list-licenses-dictionary.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/licenses', - operationId: '23_getLicensesDictionary_1', -}; - -export const tool: Tool = { - name: 'list_licenses_dictionary', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник кодов и типов лицензий\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/dictionary_list_licenses_response',\n $defs: {\n dictionary_list_licenses_response: {\n type: 'object',\n properties: {\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Код - расшифровка - количество',\n properties: {\n code: {\n type: 'string',\n description: 'Значение кода'\n },\n count: {\n type: 'integer',\n description: 'Количество'\n },\n group: {\n type: 'string',\n description: 'Группа, к которой относится код'\n },\n group_code: {\n type: 'string',\n description: 'Код группы, к которой относится код'\n },\n name: {\n type: 'string',\n description: 'Расшифровка кода'\n },\n type: {\n type: 'string',\n description: 'Тип контрагента: ip или ul'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listLicenses())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts deleted file mode 100644 index 3114a72..0000000 --- a/packages/mcp-server/src/tools/dictionary/list-okveds-dictionary.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/okveds', - operationId: '22_getOkvedsDictionary', -}; - -export const tool: Tool = { - name: 'list_okveds_dictionary', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник ОКВЭДов\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/dictionary_list_okveds_response',\n $defs: {\n dictionary_list_okveds_response: {\n type: 'object',\n description: 'ОКВЭД, имеет вложенную структуру',\n properties: {\n all_children_code: {\n type: 'array',\n items: {\n type: 'string'\n }\n },\n children: {\n type: 'array',\n description: 'Дочерние записи ОКВЭД',\n items: {\n $ref: '#/$defs/okved1'\n }\n },\n code: {\n type: 'string',\n description: 'Код ОКВЭД'\n },\n count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (с учетом дочерних)'\n },\n direct_count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (без учета дочерних)'\n },\n group: {\n type: 'string',\n description: 'Группа: A-U'\n },\n level: {\n type: 'integer'\n },\n name: {\n type: 'string',\n description: 'Наименование ОКВЭД'\n }\n }\n },\n okved1: {\n type: 'object',\n description: 'ОКВЭД, имеет вложенную структуру',\n properties: {\n all_children_code: {\n type: 'array',\n items: {\n type: 'string'\n }\n },\n children: {\n type: 'array',\n description: 'Дочерние записи ОКВЭД',\n items: {\n $ref: '#/$defs/okved1'\n }\n },\n code: {\n type: 'string',\n description: 'Код ОКВЭД'\n },\n count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (с учетом дочерних)'\n },\n direct_count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (без учета дочерних)'\n },\n group: {\n type: 'string',\n description: 'Группа: A-U'\n },\n level: {\n type: 'integer'\n },\n name: {\n type: 'string',\n description: 'Наименование ОКВЭД'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listOkveds())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts b/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts deleted file mode 100644 index 323df45..0000000 --- a/packages/mcp-server/src/tools/dictionary/list-regions-dictionary.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/regions', - operationId: '16_getRegionsDictionary_1', -}; - -export const tool: Tool = { - name: 'list_regions_dictionary', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник регионов России\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/dictionary_list_regions_response',\n $defs: {\n dictionary_list_regions_response: {\n type: 'object',\n description: 'Федеральный округ с регионами',\n properties: {\n name: {\n type: 'string'\n },\n regions: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Регион',\n properties: {\n code: {\n type: 'string'\n },\n name: {\n type: 'string'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.listRegions())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts b/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts deleted file mode 100644 index c8c9fca..0000000 --- a/packages/mcp-server/src/tools/dictionary/procurement/list-okpd2-dictionary-procurement.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'dictionary.procurement', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/dictionary/procurement/okpd2', - operationId: '27_getOkpd2Dictionary_1', -}; - -export const tool: Tool = { - name: 'list_okpd2_dictionary_procurement', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить общероссийский классификатор продукции по видам экономической деятельности (ОКПД2)\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/procurement_list_okpd2_response',\n $defs: {\n procurement_list_okpd2_response: {\n type: 'object',\n properties: {\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Код - расшифровка - количество',\n properties: {\n code: {\n type: 'string',\n description: 'Значение кода'\n },\n count: {\n type: 'integer',\n description: 'Количество'\n },\n group: {\n type: 'string',\n description: 'Группа, к которой относится код'\n },\n group_code: {\n type: 'string',\n description: 'Код группы, к которой относится код'\n },\n name: {\n type: 'string',\n description: 'Расшифровка кода'\n },\n type: {\n type: 'string',\n description: 'Тип контрагента: ip или ul'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.dictionary.procurement.listOkpd2())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts b/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts deleted file mode 100644 index 5446af2..0000000 --- a/packages/mcp-server/src/tools/filters-preview/create-filters-preview.ts +++ /dev/null @@ -1,516 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'filters_preview', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/filtersPreview', - operationId: '25_filtersPreview', -}; - -export const tool: Tool = { - name: 'create_filters_preview', - description: 'Получить краткую информацию (предпросмотр) по фильтрам/условиям: оквэд, регионы и т.д.', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - limit: { - type: 'integer', - description: 'Лимит.', - }, - offset: { - type: 'integer', - description: 'Оффсет.', - }, - contact_conditions_operator: { - type: 'string', - description: - 'Тип объединения условий контактной информации (AND - все условия должны выполняться, OR - хотя бы одно)', - enum: ['AND', 'OR'], - }, - contracts: { - type: 'object', - description: 'Запрос по контрактам', - properties: { - contract_date_from: { - type: 'string', - description: 'Дата заключения контракта: С (включительно)', - format: 'date', - }, - contract_date_to: { - type: 'string', - description: 'Дата заключения контракта: По (включительно)', - format: 'date', - }, - contract_type: { - type: 'string', - description: 'Тип контракта (FZ44, FZ223 и др.)', - enum: ['FZ44', 'FZ223', 'ALL'], - }, - has_contracts: { - type: 'boolean', - description: 'Наличие контрактов', - }, - max_price: { - type: 'number', - description: 'Максимальная сумма контракта', - }, - min_price: { - type: 'number', - description: 'Минимальная сумма контракта', - }, - okpd2_codes: { - type: 'array', - description: 'Коды ОКПД2 предмета контракта', - items: { - type: 'string', - description: 'Коды ОКПД2 предмета контракта', - }, - }, - only_active: { - type: 'boolean', - description: 'Только активные контракты', - }, - region_code: { - type: 'string', - description: 'Регион (код субъекта РФ)', - }, - role: { - type: 'string', - description: 'Роль контрагента', - enum: ['SUPPLIER', 'CUSTOMER'], - }, - search_terms: { - type: 'array', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ)', - items: { - type: 'string', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ)', - }, - }, - search_text: { - type: 'string', - description: 'Ключевые слова для поиска в предмете контракта', - }, - }, - }, - counterparty_type: { - type: 'string', - description: 'Возможные типы контрагентов', - enum: ['ul', 'ip', 'fl', 'all'], - }, - egr_statuses: { - type: 'array', - description: 'Статус компании в ЕГРЮЛ', - items: { - type: 'string', - description: 'Статус компании в ЕГРЮЛ', - }, - }, - establishment_date_from: { - type: 'string', - description: 'Дата создания компании: с', - format: 'date', - }, - establishment_date_to: { - type: 'string', - description: 'Дата создания компании: по', - format: 'date', - }, - exclude_okveds: { - type: 'array', - description: - 'Исключить список ОКВЭД (компаний с какими оквэдами быть не должно) для выгрузки. По умолчанию применяется только к основным ОКВЭД, для применения к дополнительным необходимо устанавливать свойство exclude_only_main_okveds = false.', - items: { - type: 'string', - description: - 'Исключить список ОКВЭД (компаний с какими оквэдами быть не должно) для выгрузки. По умолчанию применяется только к основным ОКВЭД, для применения к дополнительным необходимо устанавливать свойство exclude_only_main_okveds = false.', - }, - }, - exclude_only_main_okveds: { - type: 'boolean', - description: - 'Применять поле exclude_okveds только к основным ОКВЭД компаний. По умолчанию true - исключать из выборке только те компании, у которых значения exclude_okveds среди основных ОКВЭД', - }, - finance_report_year: { - type: 'integer', - description: - 'По какому отчетному финансовому году применяем фильтр по выручке/прибыли. Если 0, то смотрим за последний известный год (когда компания подавала отчетность)', - }, - finance_request: { - type: 'object', - description: 'Запрос по финансам', - properties: { - metrics: { - type: 'array', - description: 'Финансовые метрики для анализа', - items: { - type: 'string', - description: 'Финансовые метрики для анализа', - enum: ['Выручка', 'Чистая прибыль', 'INCOME', 'NET_INCOME'], - }, - }, - growth_from: { - type: 'number', - description: 'Динамика выбранных метрик: рост от (в %)', - }, - growth_to: { - type: 'number', - description: 'Динамика выбранных метрик: рост до (в %)', - }, - year_by_year: { - type: 'boolean', - description: - 'Применять условия по росту к каждому году (год к году), а не только к первому и последнему', - }, - years_count: { - type: 'integer', - description: 'Количество лет для анализа динамики', - }, - }, - required: ['metrics'], - }, - has_income: { - type: 'boolean', - description: 'Флаг - выгружать только те, у которых есть выручка', - }, - income_from: { - type: 'number', - description: 'Выручка за последний год (в тыс. рублей): с', - }, - income_to: { - type: 'number', - description: 'Выручка за последний год (в тыс. рублей): по', - }, - leases: { - type: 'object', - description: 'Запрос по договорам лизинга', - properties: { - role: { - type: 'string', - description: 'Роль контрагента', - enum: ['Lessor', 'Lessee'], - }, - classifier_codes: { - type: 'array', - description: 'Коды типа лизинга из классификатора (справочника)', - items: { - type: 'string', - description: 'Коды типа лизинга из классификатора (справочника)', - }, - }, - contract_date_from: { - type: 'string', - description: 'Дата заключения договора: С (включительно)', - format: 'date', - }, - contract_date_to: { - type: 'string', - description: 'Дата заключения договора: По (включительно)', - format: 'date', - }, - excluded_text: { - type: 'string', - description: 'Слова-исключения в реквизитах договора', - }, - has_leases: { - type: 'boolean', - description: 'Наличие договоров лизинга', - }, - only_active: { - type: 'boolean', - description: 'Только активные договора', - }, - search_terms: { - type: 'array', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в реквизитах договора ', - items: { - type: 'string', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в реквизитах договора ', - }, - }, - search_text: { - type: 'string', - description: 'Ключевые слова для поиска в реквизитах договора', - }, - stop_date_from: { - type: 'string', - description: 'Дата прекращения договора: С (включительно)', - format: 'date', - }, - stop_date_to: { - type: 'string', - description: 'Дата прекращения договора: По (включительно)', - format: 'date', - }, - }, - required: ['role'], - }, - licenses: { - type: 'array', - description: 'Список номеров лицензий', - items: { - type: 'integer', - description: 'Список номеров лицензий', - }, - }, - msp_categories: { - type: 'array', - description: - 'Категории МСП. Возможные варианты: 0 - не в МСП (никогда не состоял или вышел), 1 - микропредприятие, 2 - малое предприятие, 3 - среднее предприятие ', - items: { - type: 'string', - description: - 'Категории МСП. Возможные варианты: 0 - не в МСП (никогда не состоял или вышел), 1 - микропредприятие, 2 - малое предприятие, 3 - среднее предприятие ', - }, - }, - net_income_from: { - type: 'number', - description: 'Прибыль за последний год (в тыс. рублей): с', - }, - net_income_to: { - type: 'number', - description: 'Прибыль за последний год (в тыс. рублей): по', - }, - okveds: { - type: 'array', - description: 'Список ОКВЭД для выгрузки', - items: { - type: 'string', - description: 'Список ОКВЭД для выгрузки', - }, - }, - only_active: { - type: 'boolean', - description: 'Флаг - только действующие компании', - }, - only_it_companies: { - type: 'boolean', - description: 'Вернуть только аккредитованные ИТ-компании', - }, - only_main_okveds: { - type: 'boolean', - description: - 'Поиск указанных в поле "okveds" видов деятельности среди основных ОКВЭДов компаний. По умолчанию true.', - }, - only_msp_innovative: { - type: 'boolean', - description: 'Производство инновационной, высокотехнологичной продукции', - }, - only_msp_partner: { - type: 'boolean', - description: 'Является партнером', - }, - only_msp_social: { - type: 'boolean', - description: 'Социальное предприятие', - }, - only_nopriz_members: { - type: 'boolean', - description: 'Только члены СРО НОПРИЗ', - }, - only_nostroy_members: { - type: 'boolean', - description: 'Только члены СРО НОСТРОЙ', - }, - only_with_bfo: { - type: 'boolean', - description: 'Только с наличием данных БФО за выбранный год', - }, - only_with_emails: { - type: 'boolean', - description: 'Наличие у контрагента минимум одной электронной почты', - }, - only_with_phones: { - type: 'boolean', - description: 'Наличие у контрагента минимум одного номера телефона', - }, - only_with_websites: { - type: 'boolean', - description: 'Наличие у контрагента минимум одного сайта', - }, - opf_codes: { - type: 'array', - description: 'Код ОПФ или тип контрагента (ip, ul)', - items: { - type: 'string', - description: 'Код ОПФ или тип контрагента (ip, ul)', - }, - }, - region_codes: { - type: 'array', - description: 'Коды регионов', - items: { - type: 'string', - description: 'Коды регионов', - }, - }, - rosaccreditations: { - type: 'object', - description: 'Реестр деклараций/сертификатов соответствия', - properties: { - applicant_type: { - type: 'array', - description: 'Тип заявителя', - items: { - type: 'string', - description: 'Тип заявителя', - enum: [ - 'Продавец', - 'Изготовитель', - 'Иcполнитель', - 'Уполномоченное изготовителем лицо', - 'Поставщик', - ], - }, - }, - description: { - type: 'string', - description: 'Слова в описании документа Росаккредитации', - }, - search_terms: { - type: 'array', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в описании документа Росаккредитации ', - items: { - type: 'string', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в описании документа Росаккредитации ', - }, - }, - statuses: { - type: 'array', - description: 'Статус декларации или сертификата', - items: { - type: 'string', - description: 'Статус декларации или сертификата', - enum: [ - 'Архивный', - 'Возобновлён', - 'Выдано предписание', - 'Действует', - 'Недействителен', - 'Прекращён', - 'Приостановлен', - 'Продлен', - 'Направлено уведомление о прекращении', - 'Черновик', - 'Ожидает проверки оператора реестра', - ], - }, - }, - type: { - type: 'string', - description: 'Тип документа', - enum: ['Декларация', 'Сертификат', 'Декларация или сертификат'], - }, - }, - }, - search_terms: { - type: 'array', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) для свободного поиска ', - items: { - type: 'string', - description: 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) для свободного поиска ', - }, - }, - search_text: { - type: 'string', - description: 'Свободное поле поиска', - }, - ssch_from: { - type: 'integer', - description: 'Численность сотрудников: от', - }, - ssch_to: { - type: 'integer', - description: 'Численность сотрудников: по', - }, - support_forms: { - type: 'array', - description: 'Список видов/форм поддержки', - items: { - type: 'integer', - description: 'Список видов/форм поддержки', - }, - }, - vacancies: { - type: 'object', - description: 'Запрос по вакансиям', - properties: { - excluded_text: { - type: 'string', - description: 'Слово в названии или описании вакансии, которого быть не должно', - }, - has_vacancies: { - type: 'boolean', - description: 'Наличие вакансий', - }, - only_active: { - type: 'boolean', - description: 'Вакансия должна быть активной', - }, - only_name: { - type: 'boolean', - description: - 'Искать поисковые слова/термины (или слова исключения) только в названии вакансии (без описания). То есть, применять условия text, search_terms, excluded_text только к названию вакансии (значение true) или к названию и описанию (значение false, по умолчанию)', - }, - salary_max: { - type: 'number', - description: 'Зарплата: до', - }, - salary_min: { - type: 'number', - description: 'Зарплата: от', - }, - search_terms: { - type: 'array', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в названии или описании вакансии', - items: { - type: 'string', - description: - 'Поисковые термины (каждый элемент — отдельное условие ИЛИ) в названии или описании вакансии', - }, - }, - source: { - type: 'string', - description: 'Источник вакансий', - enum: ['HH_VACANCIES', 'VACANCIES', 'ALL'], - }, - text: { - type: 'string', - description: 'Слово в названии или описании вакансии', - }, - }, - }, - }, - required: ['key', 'limit', 'offset'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.filtersPreview.create(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/finance/retrieve-finance.ts b/packages/mcp-server/src/tools/finance/retrieve-finance.ts deleted file mode 100644 index 84dcc45..0000000 --- a/packages/mcp-server/src/tools/finance/retrieve-finance.ts +++ /dev/null @@ -1,56 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'finance', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/finance', - operationId: '2_getFinanceInfo', -}; - -export const tool: Tool = { - name: 'retrieve_finance', - description: 'Получить данные отчетов о финансовых результатах организации', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: - 'ИНН организации. Должен быть указан, если не указан ОГРН. В случае определения нескольких организаций с этим ИНН (например, при наличии филиалов) будет возвращена информация по головной организации.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.finance.retrieve(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts b/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts deleted file mode 100644 index 2740f03..0000000 --- a/packages/mcp-server/src/tools/government-contracts-stat/retrieve-government-contracts-stat.ts +++ /dev/null @@ -1,81 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'government_contracts_stat', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/governmentContractsStat', - operationId: '7_getGovernmentContractsStat', -}; - -export const tool: Tool = { - name: 'retrieve_government_contracts_stat', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить статистику по госконтрактам контрагента по информации из ЕИС Закупки\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/government_contracts_stat_retrieve_response',\n $defs: {\n government_contracts_stat_retrieve_response: {\n type: 'object',\n description: 'Данные по статистике о госконтрактах',\n properties: {\n error: {\n type: 'object',\n description: 'Исключительная ситуация',\n properties: {\n code: {\n type: 'integer',\n description: 'Код ошибки. Например: 1 - контрагент по заданным параметрам не найден'\n },\n message: {\n type: 'string',\n description: 'Описание'\n },\n source: {\n type: 'object',\n description: 'Параметры - входные данные, вызвавшие исключительную ситуацию',\n additionalProperties: true\n }\n }\n },\n stat: {\n type: 'array',\n description: 'Статистика',\n items: {\n type: 'object',\n description: 'Статистика',\n properties: {\n amount: {\n type: 'integer',\n description: 'Количество контрактов'\n },\n count_with_penalty: {\n type: 'integer',\n description: 'Количество контрактов с неустойкой'\n },\n penalty_sum: {\n type: 'number',\n description: 'Сумма неустоек'\n },\n sum: {\n type: 'number',\n description: 'Сумма'\n },\n year: {\n type: 'string',\n description: 'Год'\n }\n }\n }\n },\n status_canceled: {\n type: 'integer',\n description: 'Аннулирован'\n },\n status_execution: {\n type: 'integer',\n description: 'Исполнение'\n },\n status_execution_completed: {\n type: 'integer',\n description: 'Исполнение завершено'\n },\n status_execution_terminated: {\n type: 'integer',\n description: 'Исполнение прекращено'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - type: { - type: 'string', - description: - 'Тип госконтрактов\n**Возможные значения**:\n- `FZ44` - ФЗ-44\n- `FZ223` - ФЗ-223\n- `PP615` - ПП-615\n- `ALL` - Все типы', - enum: [ - 'FZ44', - 'FZ223', - 'PP615', - 'FZ44_API', - 'FZ223_API', - 'PP615_API', - 'P_FZ44', - 'P_FZ44_API', - 'P_FZ223_API', - 'P_PP615', - 'RNP_FZ44', - 'RNP_FZ223', - 'ALL', - ], - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key', 'ogrn', 'type'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult( - await maybeFilter(jq_filter, await client.governmentContractsStat.retrieve(body)), - ); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts b/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts deleted file mode 100644 index ab23a64..0000000 --- a/packages/mcp-server/src/tools/government-contracts/list-government-contracts.ts +++ /dev/null @@ -1,170 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'government_contracts', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/governmentContracts', - operationId: '8_getGovernmentContracts', -}; - -export const tool: Tool = { - name: 'list_government_contracts', - description: 'Получить данные о госконтрактах контрагента по информации из ЕИС Закупки\n', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - types: { - type: 'array', - description: - 'Тип госконтрактов (может быть несколько)\n\n\n\n**Возможные значения**:\n- `FZ44` - ФЗ-44\n- `FZ223` - ФЗ-223\n- `PP615` - ПП-615\n- `ALL` - Все типы', - items: { - type: 'string', - enum: ['FZ44', 'FZ223', 'ALL'], - }, - }, - company_name: { - type: 'string', - description: 'Наименование участника', - }, - end_date: { - type: 'string', - description: 'Дата конца действия контракта', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - limit: { - type: 'integer', - description: 'Количество дел на странице(максимум 1000).', - }, - offset: { - type: 'integer', - description: 'Отступ страницы.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - order: { - type: 'string', - description: - 'Тип сортировки госконтрактов, если поле sort указано\n \n**Возможные значения**:\n- `ASC` - по возрастанию\n- `DESC` - по убыванию', - enum: ['ASC', 'DESC'], - }, - penalty_sum_high: { - type: 'number', - description: 'Сумма до', - }, - penalty_sum_low: { - type: 'number', - description: 'Сумма от', - }, - penalty_types: { - type: 'array', - description: 'Тип неустойки\n \n**Возможные значения**:\n\n\n- `F` - штраф\n- `I` - пени', - items: { - type: 'string', - description: 'Тип штрафа/неустойки', - enum: ['I', 'F'], - }, - }, - price_high: { - type: 'number', - description: 'Сумма до', - }, - price_low: { - type: 'number', - description: 'Сумма от', - }, - purchase_type_info_code: { - type: 'object', - properties: { - type: { - type: 'string', - description: - 'Способ закупки\n \n**Возможные значения**:\n\n\nдля FZ44: \n- `11011` - открытый конкурс\n- `11021` - конкурс с ограниченным участием\n- `11031` - двухэтапный конкурс\n- `12011` - электронный аукцион\n- `13011` - запрос котировок\n- `14011` - запрос предложений\n- `20000` - закупка у единственного поставщика (подрядчика, исполнителя)\n- `30000` - способ определения поставщика (подрядчика, исполнителя),\n установленный Правительством Российской Федерации\n в соответствии со статьей 111 Федерального закона\n- `11044` - закрытый конкурс в электронной форме\n- `11054` - закрытый конкурс с ограниченным участием в электронной форме\n- `11064` - закрытый двухэтапный конкурс в электронной форме\n- `12024` - закрытый аукцион в электронной форме\n- `40000` - закупка товара у единственного поставщика на сумму, предусмотренную частью 12 статьи 93 Закона № 44-ФЗ\n \nдля FZ223: \n- `11011` - конкурс\n- `12012` - аукцион\n- `30000` - закупка у единственного поставщика/подрядчика/исполнителя\n- `40000` - иной способ закупки, предусмотренный правовым актом заказчика, указанным в части 1 статьи 2 Федерального закона\n \nдля PP615: \n- `12011` - электронный аукцион', - enum: [ - '11011', - '11021', - '11031', - '11044', - '11054', - '11064', - '12011', - '12012', - '12024', - '13011', - '14011', - '20000', - '30000', - '40000', - ], - }, - }, - }, - reg_number: { - type: 'string', - description: 'Номер закупки или контракта', - }, - role: { - type: 'string', - description: - 'Роль компании в контракте\n\n\n\n**Возможные значения**:\n- `CUSTOMER` - Заказчик\n- `SUPPLIER` - Поставщик\n- `ALL` - Все роли', - enum: ['CUSTOMER', 'SUPPLIER', 'ALL'], - }, - sort: { - type: 'string', - description: - 'Поле для сортировки госконтрактов\n \n**Возможные значения**:\n- `DATE` - по дате\n- `PRICE` - по цене', - enum: ['DATE', 'PRICE'], - }, - start_date: { - type: 'string', - description: 'Дата начала действия контракта', - }, - statuses: { - type: 'array', - description: - 'Статусы контракта\n \n**Возможные значения**:\n- `E` - Исполнение\n- `IN` - Аннулирован\n- `EC` - Исполнение завершено\n- `ET` - Исполнение прекращено', - items: { - type: 'string', - enum: ['E', 'IN', 'EC', 'ET'], - }, - }, - subject_contract: { - type: 'string', - description: 'Предмет контракта', - }, - }, - required: ['key', 'types'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.governmentContracts.list(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/index.ts b/packages/mcp-server/src/tools/index.ts deleted file mode 100644 index d7bb33c..0000000 --- a/packages/mcp-server/src/tools/index.ts +++ /dev/null @@ -1,149 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, Endpoint, HandlerFunction } from './types'; - -export { Metadata, Endpoint, HandlerFunction }; - -import create_taxpayer_statuses from './taxpayer-statuses/create-taxpayer-statuses'; -import create_suggestions from './suggestions/create-suggestions'; -import create_ogrns_by_address from './ogrns-by-address/create-ogrns-by-address'; -import create_leases from './leases/create-leases'; -import create_filters_preview from './filters-preview/create-filters-preview'; -import create_batch_changes from './batch-changes/create-batch-changes'; -import create_batch_cards from './batch-cards/create-batch-cards'; -import create_batch_cards_by_filters from './batch-cards-by-filters/create-batch-cards-by-filters'; -import get_courts_arbitration from './arbitration/get-courts-arbitration'; -import get_dispute_categories_arbitration from './arbitration/get-dispute-categories-arbitration'; -import get_document_types_arbitration from './arbitration/get-document-types-arbitration'; -import create_arbitration_batch_cases from './arbitration/batch-cases/create-arbitration-batch-cases'; -import retrieve_tax_info from './tax-info/retrieve-tax-info'; -import retrieve_sro_membership from './sro-membership/retrieve-sro-membership'; -import list_risks from './risks/list-risks'; -import list_paid_taxes from './paid-taxes/list-paid-taxes'; -import list_okpd_list from './okpd-list/list-okpd-list'; -import retrieve_nko_reestr from './nko-reestr/retrieve-nko-reestr'; -import list_links from './links/list-links'; -import list_government_contracts from './government-contracts/list-government-contracts'; -import retrieve_government_contracts_stat from './government-contracts-stat/retrieve-government-contracts-stat'; -import retrieve_finance from './finance/retrieve-finance'; -import list_regions from './regions/list-regions'; -import list_lease_classifier_dictionary from './dictionary/list-lease-classifier-dictionary'; -import list_licenses_dictionary from './dictionary/list-licenses-dictionary'; -import list_okveds_dictionary from './dictionary/list-okveds-dictionary'; -import list_regions_dictionary from './dictionary/list-regions-dictionary'; -import list_okpd2_dictionary_procurement from './dictionary/procurement/list-okpd2-dictionary-procurement'; -import list_courts_dictionary_arbitration from './dictionary/arbitration/list-courts-dictionary-arbitration'; -import list_dispute_categories_dictionary_arbitration from './dictionary/arbitration/list-dispute-categories-dictionary-arbitration'; -import list_document_types_dictionary_arbitration from './dictionary/arbitration/list-document-types-dictionary-arbitration'; -import retrieve_okpd2_procurement from './procurement/retrieve-okpd2-procurement'; -import list_okveds from './okveds/list-okveds'; -import list_licenses from './licenses/list-licenses'; -import retrieve_lease_classifier from './lease-classifier/retrieve-lease-classifier'; -import retrieve_counterparty from './counterparty/retrieve-counterparty'; -import list_corporate_actions from './corporate-actions/list-corporate-actions'; -import list_blocked_bank_accounts from './blocked-bank-accounts/list-blocked-bank-accounts'; -import retrieve_bankruptcy from './bankruptcy/retrieve-bankruptcy'; -import list_arbitration_cases from './arbitration-cases/list-arbitration-cases'; - -export const endpoints: Endpoint[] = []; - -function addEndpoint(endpoint: Endpoint) { - endpoints.push(endpoint); -} - -addEndpoint(create_taxpayer_statuses); -addEndpoint(create_suggestions); -addEndpoint(create_ogrns_by_address); -addEndpoint(create_leases); -addEndpoint(create_filters_preview); -addEndpoint(create_batch_changes); -addEndpoint(create_batch_cards); -addEndpoint(create_batch_cards_by_filters); -addEndpoint(get_courts_arbitration); -addEndpoint(get_dispute_categories_arbitration); -addEndpoint(get_document_types_arbitration); -addEndpoint(create_arbitration_batch_cases); -addEndpoint(retrieve_tax_info); -addEndpoint(retrieve_sro_membership); -addEndpoint(list_risks); -addEndpoint(list_paid_taxes); -addEndpoint(list_okpd_list); -addEndpoint(retrieve_nko_reestr); -addEndpoint(list_links); -addEndpoint(list_government_contracts); -addEndpoint(retrieve_government_contracts_stat); -addEndpoint(retrieve_finance); -addEndpoint(list_regions); -addEndpoint(list_lease_classifier_dictionary); -addEndpoint(list_licenses_dictionary); -addEndpoint(list_okveds_dictionary); -addEndpoint(list_regions_dictionary); -addEndpoint(list_okpd2_dictionary_procurement); -addEndpoint(list_courts_dictionary_arbitration); -addEndpoint(list_dispute_categories_dictionary_arbitration); -addEndpoint(list_document_types_dictionary_arbitration); -addEndpoint(retrieve_okpd2_procurement); -addEndpoint(list_okveds); -addEndpoint(list_licenses); -addEndpoint(retrieve_lease_classifier); -addEndpoint(retrieve_counterparty); -addEndpoint(list_corporate_actions); -addEndpoint(list_blocked_bank_accounts); -addEndpoint(retrieve_bankruptcy); -addEndpoint(list_arbitration_cases); - -export type Filter = { - type: 'resource' | 'operation' | 'tag' | 'tool'; - op: 'include' | 'exclude'; - value: string; -}; - -export function query(filters: Filter[], endpoints: Endpoint[]): Endpoint[] { - const allExcludes = filters.length > 0 && filters.every((filter) => filter.op === 'exclude'); - const unmatchedFilters = new Set(filters); - - const filtered = endpoints.filter((endpoint: Endpoint) => { - let included = false || allExcludes; - - for (const filter of filters) { - if (match(filter, endpoint)) { - unmatchedFilters.delete(filter); - included = filter.op === 'include'; - } - } - - return included; - }); - - // Check if any filters didn't match - const unmatched = Array.from(unmatchedFilters).filter((f) => f.type === 'tool' || f.type === 'resource'); - if (unmatched.length > 0) { - throw new Error( - `The following filters did not match any endpoints: ${unmatched - .map((f) => `${f.type}=${f.value}`) - .join(', ')}`, - ); - } - - return filtered; -} - -function match({ type, value }: Filter, endpoint: Endpoint): boolean { - switch (type) { - case 'resource': { - const regexStr = '^' + normalizeResource(value).replace(/\*/g, '.*') + '$'; - const regex = new RegExp(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/lease-classifier/retrieve-lease-classifier.ts b/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts deleted file mode 100644 index a0fe4cb..0000000 --- a/packages/mcp-server/src/tools/lease-classifier/retrieve-lease-classifier.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'lease_classifier', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/lease_classifier', - operationId: '24_getLeaseClassifierDictionary', -}; - -export const tool: Tool = { - name: 'retrieve_lease_classifier', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить классификатор лизинговых договоров: код лизинга, его наименование и количество заключенных договоров с данным кодом\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/lease_classifier_retrieve_response',\n $defs: {\n lease_classifier_retrieve_response: {\n type: 'object',\n properties: {\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Код - расшифровка - количество',\n properties: {\n code: {\n type: 'string',\n description: 'Значение кода'\n },\n count: {\n type: 'integer',\n description: 'Количество'\n },\n group: {\n type: 'string',\n description: 'Группа, к которой относится код'\n },\n group_code: {\n type: 'string',\n description: 'Код группы, к которой относится код'\n },\n name: {\n type: 'string',\n description: 'Расшифровка кода'\n },\n type: {\n type: 'string',\n description: 'Тип контрагента: ip или ul'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.leaseClassifier.retrieve())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/leases/create-leases.ts b/packages/mcp-server/src/tools/leases/create-leases.ts deleted file mode 100644 index c413941..0000000 --- a/packages/mcp-server/src/tools/leases/create-leases.ts +++ /dev/null @@ -1,104 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'leases', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/leases', - operationId: '11_getLeases', -}; - -export const tool: Tool = { - name: 'create_leases', - description: 'Получить данные о договорах лизинга по заданным условиям\n', - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - limit: { - type: 'integer', - description: 'Лимит.', - }, - offset: { - type: 'integer', - description: 'Оффсет.', - }, - classifier_codes: { - type: 'array', - description: 'Коды типа лизинга из классификатора (справочника)', - items: { - type: 'string', - description: 'Коды типа лизинга из классификатора (справочника)', - }, - }, - lease_end_from: { - type: 'string', - description: 'Дата прекращения договора: С (включительно)', - format: 'date', - }, - lease_end_to: { - type: 'string', - description: 'Дата прекращения договора: По (включительно)', - format: 'date', - }, - lease_start_from: { - type: 'string', - description: 'Дата заключения договора: С (включительно)', - format: 'date', - }, - lease_start_to: { - type: 'string', - description: 'Дата заключения договора: По (включит ельно)', - format: 'date', - }, - only_active: { - type: 'boolean', - description: 'Только активные договора', - }, - order: { - type: 'string', - description: 'Порядок сортировки', - enum: ['ASC', 'DESC'], - }, - search_text: { - type: 'string', - description: 'Ключевые слова для поиска в реквизитах договора', - }, - sort: { - type: 'string', - description: 'Поле для сортировки результата', - enum: ['date_start', 'date_end'], - }, - updated_after: { - type: 'string', - description: 'Дата последнего изменения: С (включительно)', - format: 'date-time', - }, - }, - required: ['key', 'limit', 'offset'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const body = args as any; - try { - return asTextContentResult(await client.leases.create(body)); - } catch (error) { - if (error instanceof Datanewton.APIError) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/licenses/list-licenses.ts b/packages/mcp-server/src/tools/licenses/list-licenses.ts deleted file mode 100644 index 7d2baf9..0000000 --- a/packages/mcp-server/src/tools/licenses/list-licenses.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'licenses', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/licenses', - operationId: '23_getLicensesDictionary', -}; - -export const tool: Tool = { - name: 'list_licenses', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник кодов и типов лицензий\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/license_list_response',\n $defs: {\n license_list_response: {\n type: 'object',\n properties: {\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Код - расшифровка - количество',\n properties: {\n code: {\n type: 'string',\n description: 'Значение кода'\n },\n count: {\n type: 'integer',\n description: 'Количество'\n },\n group: {\n type: 'string',\n description: 'Группа, к которой относится код'\n },\n group_code: {\n type: 'string',\n description: 'Код группы, к которой относится код'\n },\n name: {\n type: 'string',\n description: 'Расшифровка кода'\n },\n type: {\n type: 'string',\n description: 'Тип контрагента: ip или ul'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.licenses.list())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/links/list-links.ts b/packages/mcp-server/src/tools/links/list-links.ts deleted file mode 100644 index f6b0f92..0000000 --- a/packages/mcp-server/src/tools/links/list-links.ts +++ /dev/null @@ -1,64 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'links', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/links', - operationId: '10_getLinks', -}; - -export const tool: Tool = { - name: 'list_links', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить связи контрагента (до 2-го уровня включительно): руководители, участники, ИП\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/link_list_response',\n $defs: {\n link_list_response: {\n type: 'object',\n description: 'Граф связей. Содержит список узлов и список связей между ними. Идентификация между связями и узлами производится по ИНН',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n edge_types: {\n type: 'array',\n description: 'Типы связей, по которым был построен граф',\n items: {\n type: 'string',\n description: 'Типы связей, по которым был построен граф',\n enum: [ 'OWNER',\n 'MANAGER',\n 'INDIVIDUAL',\n 'HISTORY_OWNER',\n 'HISTORY_MANAGER'\n ]\n }\n },\n edges: {\n type: 'array',\n description: 'Список связей (ребер графа). Содержит информацию об связи между вершинами графа, включая ИНН связанных узлов',\n items: {\n type: 'object',\n description: 'Связь между узлами. Содержит информацию о связанных узлах (ИНН исходного и результирующего узла), уровень связи от корня (начиная с 0), тип связи, значение (описание)',\n properties: {\n inn_source: {\n type: 'string',\n description: 'ИНН исходного узла'\n },\n inn_target: {\n type: 'string',\n description: 'ИНН результирующего узла'\n },\n level: {\n type: 'integer',\n description: 'Уровень (начиная с 0 от корня)'\n },\n type: {\n type: 'string',\n description: 'Тип связи',\n enum: [ 'OWNER',\n 'MANAGER',\n 'INDIVIDUAL',\n 'HISTORY_OWNER',\n 'HISTORY_MANAGER'\n ]\n },\n value: {\n type: 'string',\n description: 'Значение (описание) связи'\n }\n }\n }\n },\n edges_count: {\n type: 'integer',\n description: 'количество связей'\n },\n inn_root: {\n type: 'string',\n description: 'ИНН главной вершины (корня) графа, начиная с которой он был построен'\n },\n max_level: {\n type: 'integer',\n description: 'Сколько уровней связей было построено, начиная c 0 от корня'\n },\n nodes: {\n type: 'array',\n description: 'Список узлов (вершин графа). Содержит информацию об участниках связей (ЮЛ, ИП, ФЛ). Для каждого указан ИНН (для ЮЛ/ИП также указывается ОГРН), название и статус.',\n items: {\n type: 'object',\n description: 'Узел связи. Содержит информацию об участнике связи: тип (ФЛ, ЮЛ, ИП), ИНН, ОГРН (для ЮЛ/ИП), наименование, статус',\n properties: {\n activity_kind: {\n type: 'string',\n description: 'Основной ОКВЭД, код'\n },\n activity_kind_dsc: {\n type: 'string',\n description: 'Основной ОКВЭД, описание'\n },\n inn: {\n type: 'string',\n description: 'ИНН'\n },\n name: {\n type: 'string',\n description: 'Наименование'\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН'\n },\n status: {\n type: 'string',\n description: 'Статус',\n enum: [ '0',\n '1',\n '-1'\n ]\n },\n type: {\n type: 'string',\n description: 'Тип',\n enum: [ 'UL',\n 'IP',\n 'FL'\n ]\n }\n }\n }\n },\n nodes_count: {\n type: 'integer',\n description: 'количество узлов'\n },\n ogrn_root: {\n type: 'string',\n description: 'ОГРН главной вершины (корня) графа, начиная с которой он был построен'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП', - }, - level: { - type: 'integer', - description: - 'Уровень до которого ищем связи, не более 2 (если больше 2, то будет использоваться 2, если меньше 0, то будет использоваться 0). Если не задан, то считается равным 2', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key', 'ogrn'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.links.list(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts b/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts deleted file mode 100644 index 6f14f0a..0000000 --- a/packages/mcp-server/src/tools/nko-reestr/retrieve-nko-reestr.ts +++ /dev/null @@ -1,63 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'nko_reestr', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/nkoReestr', - operationId: '15_getNkoReestr', -}; - -export const tool: Tool = { - name: 'retrieve_nko_reestr', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить информацию о принадлежности контрагента к НКО\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/nko_reestr_retrieve_response',\n $defs: {\n nko_reestr_retrieve_response: {\n type: 'object',\n description: 'Реестр НКО',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n error: {\n type: 'object',\n description: 'Исключительная ситуация',\n properties: {\n code: {\n type: 'integer',\n description: 'Код ошибки. Например: 1 - контрагент по заданным параметрам не найден'\n },\n message: {\n type: 'string',\n description: 'Описание'\n },\n source: {\n type: 'object',\n description: 'Параметры - входные данные, вызвавшие исключительную ситуацию',\n additionalProperties: true\n }\n }\n },\n nko_info: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Записи из реестра НКО',\n properties: {\n address: {\n type: 'string',\n description: 'Адрес'\n },\n forma: {\n type: 'string',\n description: 'Наименование вида организационно-правовой формы организации'\n },\n name: {\n type: 'string',\n description: 'Полное наименование организации'\n },\n nko_id: {\n type: 'integer',\n description: 'Идентификатор записи'\n },\n ogrn_date: {\n type: 'string',\n description: 'Дата вступления организации в реестр'\n },\n reestr_name: {\n type: 'string',\n description: 'Наименование реестра, в который входит организация'\n },\n reg_number: {\n type: 'string',\n description: 'Номер, присваиваемый организации в системе'\n },\n region: {\n type: 'string',\n description: 'Регион, в котором осуществляет деятельность НКО'\n },\n status_nko: {\n type: 'string',\n description: 'Статус НКО',\n enum: [ 'Исключена',\n 'Зарегистрирована'\n ]\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.nkoReestr.retrieve(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts b/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts deleted file mode 100644 index b298830..0000000 --- a/packages/mcp-server/src/tools/ogrns-by-address/create-ogrns-by-address.ts +++ /dev/null @@ -1,65 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'ogrns_by_address', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/ogrnsByAddress', - operationId: '32_ogrnsByAddress', -}; - -export const tool: Tool = { - name: 'create_ogrns_by_address', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить список компаний по заданному адресу\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/ogrns_by_address_create_response',\n $defs: {\n ogrns_by_address_create_response: {\n type: 'object',\n description: 'Результат поиска компаний по адресу',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n counterparties: {\n type: 'array',\n description: 'Список компаний',\n items: {\n type: 'object',\n description: 'Список компаний',\n properties: {\n active: {\n type: 'boolean',\n description: 'Признак активности контрагента (действующий/недействующий)'\n },\n activity_kind: {\n type: 'string',\n description: 'Основной ОКВЭД'\n },\n activity_kind_dsc: {\n type: 'string',\n description: 'Описание основного вида деятельности'\n },\n activity_kind_industry_code: {\n type: 'string',\n description: 'Код основного вида деятельности'\n },\n activity_kind_mode: {\n type: 'string',\n description: 'Тип основного ОКВЭД (new/old): new - после изменений 2014 года, old - до него'\n },\n address: {\n type: 'string',\n description: 'Адрес'\n },\n charter_capital: {\n type: 'string',\n description: 'Уставный капитал'\n },\n enable_email_notification: {\n type: 'boolean',\n description: 'Присылать уведомления на почту'\n },\n establishment_date: {\n type: 'string',\n description: 'Дата регистрации'\n },\n fin_report_year: {\n type: 'integer',\n description: 'Последний год сданной отчетности БФО'\n },\n groups: {\n type: 'object',\n description: 'Блок с информацией о группах (холдинги)',\n properties: {\n holding: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n holding_by_active: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n metagroup: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n metagroup_by_active: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n network: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n network_by_active: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n }\n }\n },\n inn: {\n type: 'string',\n description: 'ИНН контрагента'\n },\n last_income: {\n type: 'number',\n description: 'Выручка за последний отчетный год (в тыс. рублей)'\n },\n manager_name: {\n type: 'string',\n description: 'Руководитель'\n },\n manager_position: {\n type: 'string',\n description: 'Должность руководителя'\n },\n match: {\n type: 'string',\n description: 'Что совпало в поиске (значение)'\n },\n match_actual: {\n type: 'boolean',\n description: 'Совпадение по актуальным данным или историческим (например, бывшим руководителям)'\n },\n match_dsc: {\n type: 'string',\n description: 'Тип/описание совпадения - что именно совпало (какое поле/домен)'\n },\n name: {\n type: 'string',\n description: 'Наименование контрагента (краткое, при его отсутствии - полное)'\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН контрагента'\n },\n region: {\n type: 'string',\n description: 'Регион'\n },\n region_code: {\n type: 'string',\n description: 'Регион (код)'\n },\n type: {\n type: 'string',\n description: 'Тип: ul - ЮЛ, ip - ИП'\n }\n }\n }\n },\n error: {\n type: 'object',\n description: 'Исключительная ситуация',\n properties: {\n code: {\n type: 'integer',\n description: 'Код ошибки. Например: 1 - контрагент по заданным параметрам не найден'\n },\n message: {\n type: 'string',\n description: 'Описание'\n },\n source: {\n type: 'object',\n description: 'Параметры - входные данные, вызвавшие исключительную ситуацию',\n additionalProperties: true\n }\n }\n },\n limit: {\n type: 'integer',\n description: 'Лимит записей на страницу (limit)'\n },\n offset: {\n type: 'integer',\n description: 'Смещение выборки (offset)'\n },\n total: {\n type: 'integer',\n description: 'Общее количество компаний по запросу'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - address: { - type: 'string', - description: 'Адрес', - }, - limit: { - type: 'integer', - description: 'Количество записей (максимум 100)', - }, - offset: { - type: 'integer', - description: 'Смещение выборки (offset)', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key', 'address'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.ogrnsByAddress.create(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts b/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts deleted file mode 100644 index 549ccd3..0000000 --- a/packages/mcp-server/src/tools/okpd-list/list-okpd-list.ts +++ /dev/null @@ -1,63 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'okpd_list', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/okpdList', - operationId: '13_getOKPDList', -}; - -export const tool: Tool = { - name: 'list_okpd_list', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить список всех ОКПД (ОКПД2) компании, на основании данных в ЕИС Закупки\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/okpd_list_list_response',\n $defs: {\n okpd_list_list_response: {\n type: 'object',\n description: 'Данные об ОКПД огранизации',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n data: {\n type: 'array',\n description: 'Список ОКПД огранизации',\n items: {\n type: 'object',\n description: 'Данные об ОКПД организации',\n properties: {\n number: {\n type: 'string',\n description: 'Номер позиции'\n },\n okpd_code: {\n type: 'string',\n description: 'Код ОКПД'\n },\n okpd_name: {\n type: 'string',\n description: 'Наименование ОКПД'\n },\n okpd2_code: {\n type: 'string',\n description: 'Код ОКПД2'\n },\n okpd2_name: {\n type: 'string',\n description: 'Наименование ОКПД2'\n }\n }\n }\n },\n error: {\n type: 'object',\n description: 'Исключительная ситуация',\n properties: {\n code: {\n type: 'integer',\n description: 'Код ошибки. Например: 1 - контрагент по заданным параметрам не найден'\n },\n message: {\n type: 'string',\n description: 'Описание'\n },\n source: {\n type: 'object',\n description: 'Параметры - входные данные, вызвавшие исключительную ситуацию',\n additionalProperties: true\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.okpdList.list(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/okveds/list-okveds.ts b/packages/mcp-server/src/tools/okveds/list-okveds.ts deleted file mode 100644 index dcc3269..0000000 --- a/packages/mcp-server/src/tools/okveds/list-okveds.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'okveds', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/okveds', - operationId: '22_getOkvedsDictionary_1', -}; - -export const tool: Tool = { - name: 'list_okveds', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник ОКВЭДов\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/okved_list_response',\n $defs: {\n okved_list_response: {\n type: 'object',\n description: 'ОКВЭД, имеет вложенную структуру',\n properties: {\n all_children_code: {\n type: 'array',\n items: {\n type: 'string'\n }\n },\n children: {\n type: 'array',\n description: 'Дочерние записи ОКВЭД',\n items: {\n $ref: '#/$defs/okved1'\n }\n },\n code: {\n type: 'string',\n description: 'Код ОКВЭД'\n },\n count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (с учетом дочерних)'\n },\n direct_count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (без учета дочерних)'\n },\n group: {\n type: 'string',\n description: 'Группа: A-U'\n },\n level: {\n type: 'integer'\n },\n name: {\n type: 'string',\n description: 'Наименование ОКВЭД'\n }\n }\n },\n okved1: {\n type: 'object',\n description: 'ОКВЭД, имеет вложенную структуру',\n properties: {\n all_children_code: {\n type: 'array',\n items: {\n type: 'string'\n }\n },\n children: {\n type: 'array',\n description: 'Дочерние записи ОКВЭД',\n items: {\n $ref: '#/$defs/okved1'\n }\n },\n code: {\n type: 'string',\n description: 'Код ОКВЭД'\n },\n count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (с учетом дочерних)'\n },\n direct_count: {\n type: 'integer',\n description: 'Количество действующих юридических лиц, у которых главный ОКВЭД равен name (без учета дочерних)'\n },\n group: {\n type: 'string',\n description: 'Группа: A-U'\n },\n level: {\n type: 'integer'\n },\n name: {\n type: 'string',\n description: 'Наименование ОКВЭД'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.okveds.list())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts b/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts deleted file mode 100644 index ef10998..0000000 --- a/packages/mcp-server/src/tools/paid-taxes/list-paid-taxes.ts +++ /dev/null @@ -1,63 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'paid_taxes', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/paidTaxes', - operationId: '9_getPaidTaxes', -}; - -export const tool: Tool = { - name: 'list_paid_taxes', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить данные об уплаченных налогах по ИНН/ОГРН\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/paid_tax_list_response',\n $defs: {\n paid_tax_list_response: {\n type: 'object',\n description: 'Данные об уплаченных налогах',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n data: {\n type: 'array',\n description: 'Данные о уплаченных налогах',\n items: {\n type: 'object',\n description: 'Данные об уплаченных налогах',\n properties: {\n doc_date: {\n type: 'string',\n description: 'Дата формирования документа'\n },\n report_date: {\n type: 'string',\n description: 'Дата, по состоянию на которую, подготовлены данные для публикации (в ней указан отчетный год)'\n },\n tax_info_list: {\n type: 'array',\n description: 'Список уплаченных налогов',\n items: {\n type: 'object',\n description: 'Информация о налоге',\n properties: {\n taxName: {\n type: 'string',\n description: 'Наименование налога'\n },\n taxValue: {\n type: 'string',\n description: ' Сумма уплаченного налога (в рублях)'\n }\n }\n }\n }\n }\n }\n },\n error: {\n type: 'object',\n description: 'Исключительная ситуация',\n properties: {\n code: {\n type: 'integer',\n description: 'Код ошибки. Например: 1 - контрагент по заданным параметрам не найден'\n },\n message: {\n type: 'string',\n description: 'Описание'\n },\n source: {\n type: 'object',\n description: 'Параметры - входные данные, вызвавшие исключительную ситуацию',\n additionalProperties: true\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.paidTaxes.list(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts b/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts deleted file mode 100644 index ddfdb15..0000000 --- a/packages/mcp-server/src/tools/procurement/retrieve-okpd2-procurement.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'procurement', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/procurement/okpd2', - operationId: '27_getOkpd2Dictionary', -}; - -export const tool: Tool = { - name: 'retrieve_okpd2_procurement', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить общероссийский классификатор продукции по видам экономической деятельности (ОКПД2)\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/procurement_retrieve_okpd2_response',\n $defs: {\n procurement_retrieve_okpd2_response: {\n type: 'object',\n properties: {\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Код - расшифровка - количество',\n properties: {\n code: {\n type: 'string',\n description: 'Значение кода'\n },\n count: {\n type: 'integer',\n description: 'Количество'\n },\n group: {\n type: 'string',\n description: 'Группа, к которой относится код'\n },\n group_code: {\n type: 'string',\n description: 'Код группы, к которой относится код'\n },\n name: {\n type: 'string',\n description: 'Расшифровка кода'\n },\n type: {\n type: 'string',\n description: 'Тип контрагента: ip или ul'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.procurement.retrieveOkpd2())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/regions/list-regions.ts b/packages/mcp-server/src/tools/regions/list-regions.ts deleted file mode 100644 index 130ac69..0000000 --- a/packages/mcp-server/src/tools/regions/list-regions.ts +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'regions', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/regions', - operationId: '16_getRegionsDictionary', -}; - -export const tool: Tool = { - name: 'list_regions', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить справочник регионов России\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/region_list_response',\n $defs: {\n region_list_response: {\n type: 'object',\n description: 'Федеральный округ с регионами',\n properties: {\n name: {\n type: 'string'\n },\n regions: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Регион',\n properties: {\n code: {\n type: 'string'\n },\n name: {\n type: 'string'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: [], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.regions.list())); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/risks/list-risks.ts b/packages/mcp-server/src/tools/risks/list-risks.ts deleted file mode 100644 index 2892c4d..0000000 --- a/packages/mcp-server/src/tools/risks/list-risks.ts +++ /dev/null @@ -1,63 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'risks', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/risks', - operationId: '20_getRisks', -}; - -export const tool: Tool = { - name: 'list_risks', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить список рисков по контрагенту\n\n**Возможные значения**\n\n**Негативные списки: (NEGATIVE_LISTS)**\n\n - `owner_inaccuracy` — Недостоверность учредителя\n - `disqualified_managers` — Руководитель дисквалифицирован\n - `in_sanctions_ofac` — Находится в санкционном списке OFAC\n - `illegal_rewards` — Находится в реестре ЮЛ, привлеченных за незаконное вознаграждение\n - `address_inaccuracy` — Недостоверность адреса\n - `manager_inaccuracy` — Недостоверность руководителя\n - `in_sanctions_uk` — Находится в санкционном списке UK\n - `in_sanctions_eu` — Находится в санкционном списке EU\n - `false_info` — Недостоверные данные по ЕГРЮЛ. Если в ЕГР есть отметка о недостоверности любого из блоков: адрес, участник, руководитель, управляющая компания.\n - `disqualified_owners` — Участник дисквалифицирован\n - `unscrupulous_manager` — Руководитель в РНП\n - `unscrupulous_owner` — Участник в РНП\n - `unscrupulous_supplier_223` — Состоит в реестре недобросовестных поставщиков по ФЗ 223\n - `unscrupulous_supplier_615` — Состоит в реестре недобросовестных поставщиков по ПП 615\n - `unscrupulous_supplier_44` — Состоит в реестре недобросовестных поставщиков по ФЗ 44\n - `disqualified_individual` — ИП дисквалифицирован\n - `management_company_inaccuracy` — Недостоверность управляющей компании\n - `fin_illegal` — Имеет признаки нелегальной деятельности на финансовом рынке\n\n**Признаки однодневок: (ONE_DAY_COMPANY)**\n\n - `fns_migration` — Миграция между ФНС (более 2 за 12 месяцев)\n - `mass_address` — Массовый юридический адрес. Под массовым юридическим адресом понимается факт регистрации 5 и более компаний с точностью до офиса/помещения/комнаты\n - `mass_okveds` — ОКВЭДов более 30\n - `company_age` — Возраст менее 1 года\n - `default_capital_table` — Уставный капитал 10 000 ₽\n - `owner_change` — Учредитель сменился менее 1 года назад\n - `mass_owner` — Массовый учредитель (более 5)\n - `manager_change` — Руководитель сменился менее 1 года назад\n - `management_company_change` — Управляющая компания сменилась менее 1 года назад\n - `workers_amount` — Численность работников 0 или 1\n - `mass_manager` — Массовый руководитель (более 5)\n - `tax_debts` — Имеет налоговую задолженность\n - `tax_offences` — Имеет налоговые нарушения\n\n**Иные факты: (OTHER_FACTS)**\n\n - `has_licenses` — Имеет действующие лицензии\n - `has_declarations` — Имеет действующие Декларации соответствия\n - `owners_information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ об участнике\n - `zero_intangible_assets` — Нулевая балансовая стоимость нематериальных активов, за последний год\n - `zero_fixed_assets` — Нулевая балансовая стоимость основных средств, за последний год\n - `has_certifications` — Имеет действующие Сертификаты соответствия\n - `management_companies_information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ об управляющей компании\n - `has_government_contracts` — Является поставщиком по госконтрактам\n - `managers_information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ о руководителе\n - `information_limited` — Ограничение доступа к сведениям в ЕГРЮЛ\n - `debt` — Сумма к взысканию по исполнительным производствам превышает 300 000 руб\n - `status_egrul` — Статус в ЕГРЮЛ, требующий внимания\n - `status_egrip` — Статус в ЕГРИП, требующий внимания\n\n**Сведения о банкротстве: (BANKRUPTCY)**\n\n - `has_bankruptcy_messages` — Есть сообщения о банкротстве за последний год\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/risk_list_response',\n $defs: {\n risk_list_response: {\n type: 'object',\n description: 'Информация о рисках контрагента',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n flags: {\n type: 'array',\n description: 'Список рисков',\n items: {\n type: 'object',\n description: 'Показатель риска',\n properties: {\n category: {\n type: 'integer',\n description: 'категория (приоритет) для признаков: 0 - высший (красный), 1 - средний (желтый), остальное - 2. 0 и 1 устанавливается только для негативных признаков.'\n },\n comment: {\n type: 'string',\n description: 'расширенный комментарий'\n },\n description: {\n type: 'string',\n description: 'описание признака'\n },\n details: {\n type: 'array',\n description: 'дополнительная информация',\n items: {\n type: 'array',\n description: 'дополнительная информация',\n items: {\n type: 'object',\n description: 'Дополнительная информация',\n properties: {\n name: {\n type: 'string',\n description: 'название'\n },\n value: {\n type: 'string',\n description: 'значение'\n },\n value_type: {\n type: 'string',\n description: 'тип значения',\n enum: [ 'string',\n 'date',\n 'boolean',\n 'url'\n ]\n }\n }\n }\n }\n },\n name: {\n type: 'string',\n description: 'наименование признака'\n },\n positive: {\n type: 'boolean',\n description: 'является ли позитивным фактором'\n },\n type: {\n type: 'string',\n description: 'тип показателя',\n enum: [ 'NEGATIVE_LISTS',\n 'ONE_DAY_COMPANY',\n 'OTHER_FACTS',\n 'BANKRUPTCY'\n ]\n },\n value: {\n type: 'boolean',\n description: 'значение true/false'\n }\n }\n }\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН компании'\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.risks.list(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts b/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts deleted file mode 100644 index fa547ad..0000000 --- a/packages/mcp-server/src/tools/sro-membership/retrieve-sro-membership.ts +++ /dev/null @@ -1,85 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'sro_membership', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/sroMembership', - operationId: '14_getSroMembership', -}; - -export const tool: Tool = { - name: 'retrieve_sro_membership', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить информацию о членстве в СРО НОСТРОЙ/НОПРИЗ\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/sro_membership_retrieve_response',\n $defs: {\n sro_membership_retrieve_response: {\n type: 'object',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n sro_infos: {\n type: 'array',\n description: 'Данные',\n items: {\n type: 'object',\n description: 'Данные',\n properties: {\n checks: {\n type: 'array',\n description: 'Проверки',\n items: {\n type: 'object',\n description: 'Проверки',\n properties: {\n date: {\n type: 'string',\n description: 'Дата окончания проверки'\n },\n facts: {\n type: 'string',\n description: 'Факты применения мер дисциплинарного воздействия'\n },\n result: {\n type: 'string',\n description: 'Результат проверки члена СРО'\n },\n type: {\n type: 'string',\n description: 'Тип проверки'\n }\n }\n }\n },\n ensures: {\n type: 'array',\n description: 'Страхование',\n items: {\n type: 'object',\n description: 'Страхование',\n properties: {\n company: {\n type: 'string',\n description: 'Наименование страховой компании'\n },\n phones: {\n type: 'string',\n description: 'Контактные телефоны'\n },\n place: {\n type: 'string',\n description: 'Место нахождения'\n },\n subject: {\n type: 'string',\n description: 'Предмет договора страхования'\n },\n sum: {\n type: 'string',\n description: 'Размер страховой суммы'\n }\n }\n }\n },\n fund: {\n type: 'object',\n description: 'Информация о КФ',\n properties: {\n fund_compensate: {\n type: 'number',\n description: 'Размер взноса в компенсационный фонд возмещения вреда'\n },\n fund_secure: {\n type: 'number',\n description: 'Размер взноса в компенсационный фонд обеспечения договорных обязательств'\n }\n }\n },\n info: {\n type: 'object',\n description: 'Общая информация',\n properties: {\n accordance: {\n type: 'string',\n description: 'Сведения о соответствии условиям членства в СРО'\n },\n address: {\n type: 'string',\n description: 'Адрес'\n },\n phones: {\n type: 'string',\n description: 'Контактные телефоны'\n },\n reg_number: {\n type: 'string',\n description: 'Регистрационный номер в реестре СРО'\n },\n sro_date: {\n type: 'string',\n description: 'Дата регистрации в реестре СРО',\n format: 'date'\n },\n sro_id: {\n type: 'string',\n description: 'Идентификатор СРО'\n },\n sro_name: {\n type: 'string',\n description: 'Наименование СРО'\n },\n sro_ogrn: {\n type: 'string',\n description: 'ОГРН СРО'\n },\n stop_date: {\n type: 'string',\n description: 'Дата прекращения членства',\n format: 'date'\n },\n stop_reason: {\n type: 'string',\n description: 'Основание прекращения членства'\n },\n type: {\n type: 'string',\n description: 'Тип(реестр) СРО',\n enum: [ 'НОСТРОЙ',\n 'НОПРИЗ'\n ]\n }\n }\n },\n right: {\n type: 'object',\n description: 'Сведения о наличии права',\n properties: {\n atomic_relation: {\n type: 'string',\n description: 'В отношении объектов использования атомной энергии'\n },\n build_relation: {\n type: 'string',\n description: 'В отношении объектов капитального строительства (кроме особо опасных, технически сложных и уникальных объектов, объектов использования атомной энергии)'\n },\n danger_relation: {\n type: 'string',\n description: 'В отношении особо опасных, технически сложных и уникальных объектов капитального строительства (кроме объектов использования атомной энергии)'\n },\n decision_num_date: {\n type: 'string',\n description: 'Основание наделения правом'\n },\n work_obligations: {\n type: 'string',\n description: 'Размер обязательств по договорам подряда с использованием конкурентных способов заключения договоров (уровень ответственности)'\n },\n work_price: {\n type: 'string',\n description: 'Стоимость работ по одному договору подряда (уровень ответственности)'\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - accordance: { - type: 'boolean', - description: - 'Сведения о соответствии:\ntrue - только соответствующие,\nfalse - только несоответствующие', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - order: { - type: 'boolean', - description: 'Порядок сортировки по дате регистрации:\ntrue - прямой,\nfalse - обратный', - }, - sro_name_like: { - type: 'string', - description: 'Фильтр наименования СРО', - }, - status: { - type: 'boolean', - description: 'Статус членства:\ntrue - только активные,\nfalse - только прекращённые', - }, - type: { - type: 'string', - description: 'Реестр СРО: НОСТРОЙ | НОПРИЗ', - enum: ['НОСТРОЙ', 'НОПРИЗ'], - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.sroMembership.retrieve(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/suggestions/create-suggestions.ts b/packages/mcp-server/src/tools/suggestions/create-suggestions.ts deleted file mode 100644 index d04c6d7..0000000 --- a/packages/mcp-server/src/tools/suggestions/create-suggestions.ts +++ /dev/null @@ -1,66 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'suggestions', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/suggestions', - operationId: '21_getSuggestions', -}; - -export const tool: Tool = { - name: 'create_suggestions', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить список первых 10 подходящих контрагентов, соответствующих входному значению \n\nПоддерживаются следующие виды поиска: \n - `ОГРН` \n - `ИНН` \n - `Название ЮЛ/ИП, в том числе неполное наименование (например, \"Датаном\" вместо \"Датаномика\")` \n - `Транслитерированное название ЮЛ` \n - `Название ЮЛ, написанное в неправильной раскладке клавиатуры` \n - `Неточное название ЮЛ/ИП (с опечатками)`\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/suggestion_create_response',\n $defs: {\n suggestion_create_response: {\n type: 'object',\n description: 'Ответ в подсказках с набором подходящих запросу контрагентов',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n count: {\n type: 'object',\n properties: {\n ip: {\n type: 'integer'\n },\n total: {\n type: 'integer'\n },\n ul: {\n type: 'integer'\n }\n }\n },\n data: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Список компаний',\n properties: {\n active: {\n type: 'boolean',\n description: 'Признак активности контрагента (действующий/недействующий)'\n },\n activity_kind: {\n type: 'string',\n description: 'Основной ОКВЭД'\n },\n activity_kind_dsc: {\n type: 'string',\n description: 'Описание основного вида деятельности'\n },\n activity_kind_industry_code: {\n type: 'string',\n description: 'Код основного вида деятельности'\n },\n activity_kind_mode: {\n type: 'string',\n description: 'Тип основного ОКВЭД (new/old): new - после изменений 2014 года, old - до него'\n },\n address: {\n type: 'string',\n description: 'Адрес'\n },\n charter_capital: {\n type: 'string',\n description: 'Уставный капитал'\n },\n enable_email_notification: {\n type: 'boolean',\n description: 'Присылать уведомления на почту'\n },\n establishment_date: {\n type: 'string',\n description: 'Дата регистрации'\n },\n fin_report_year: {\n type: 'integer',\n description: 'Последний год сданной отчетности БФО'\n },\n groups: {\n type: 'object',\n description: 'Блок с информацией о группах (холдинги)',\n properties: {\n holding: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n holding_by_active: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n metagroup: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n metagroup_by_active: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n network: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n },\n network_by_active: {\n type: 'object',\n properties: {\n ogrn: {\n type: 'string'\n },\n share: {\n type: 'number'\n },\n size: {\n type: 'integer'\n }\n }\n }\n }\n },\n inn: {\n type: 'string',\n description: 'ИНН контрагента'\n },\n last_income: {\n type: 'number',\n description: 'Выручка за последний отчетный год (в тыс. рублей)'\n },\n manager_name: {\n type: 'string',\n description: 'Руководитель'\n },\n manager_position: {\n type: 'string',\n description: 'Должность руководителя'\n },\n match: {\n type: 'string',\n description: 'Что совпало в поиске (значение)'\n },\n match_actual: {\n type: 'boolean',\n description: 'Совпадение по актуальным данным или историческим (например, бывшим руководителям)'\n },\n match_dsc: {\n type: 'string',\n description: 'Тип/описание совпадения - что именно совпало (какое поле/домен)'\n },\n name: {\n type: 'string',\n description: 'Наименование контрагента (краткое, при его отсутствии - полное)'\n },\n ogrn: {\n type: 'string',\n description: 'ОГРН контрагента'\n },\n region: {\n type: 'string',\n description: 'Регион'\n },\n region_code: {\n type: 'string',\n description: 'Регион (код)'\n },\n type: {\n type: 'string',\n description: 'Тип: ul - ЮЛ, ip - ИП'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - search_query: { - type: 'string', - description: 'Поисковое значение (ОГРН/ИНН/наименование)', - }, - is_active: { - type: 'boolean', - description: 'Признак, позволяющий искать только среди действующих контрагентов', - }, - type: { - type: 'string', - description: 'Возможные типы контрагентов', - enum: ['ul', 'ip', 'fl', 'all'], - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key', 'search_query'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.suggestions.create(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts b/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts deleted file mode 100644 index c58aefa..0000000 --- a/packages/mcp-server/src/tools/tax-info/retrieve-tax-info.ts +++ /dev/null @@ -1,63 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'tax_info', - operation: 'read', - tags: [], - httpMethod: 'get', - httpPath: '/v1/taxInfo', - operationId: '22_getTaxInfo', -}; - -export const tool: Tool = { - name: 'retrieve_tax_info', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить информацию об уплаченных налогах, задолженностях и штрафах, налоговых нарушениях по ИНН/ОГРН\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/tax_info_retrieve_response',\n $defs: {\n tax_info_retrieve_response: {\n type: 'object',\n description: 'Информация об уплаченных налогах, задолженностях и штрафах, налоговых нарушениях',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n company_name: {\n type: 'string',\n description: 'Наименование компании'\n },\n error: {\n type: 'object',\n description: 'Исключительная ситуация',\n properties: {\n code: {\n type: 'integer',\n description: 'Код ошибки. Например: 1 - контрагент по заданным параметрам не найден'\n },\n message: {\n type: 'string',\n description: 'Описание'\n },\n source: {\n type: 'object',\n description: 'Параметры - входные данные, вызвавшие исключительную ситуацию',\n additionalProperties: true\n }\n }\n },\n fines_debts: {\n type: 'array',\n description: 'Данные о задолженностях и штрафах',\n items: {\n type: 'object',\n description: 'Данные о задолженностях и штрафах',\n properties: {\n arrears_sum_infos: {\n type: 'array',\n description: 'Сведения о задолженности по налогам, пеням и штрафам',\n items: {\n type: 'object',\n description: 'Сведения о налогах',\n properties: {\n fine_sum: {\n type: 'number',\n description: 'Сумма штрафа'\n },\n penalty_sum: {\n type: 'number',\n description: 'Сумма пени'\n },\n tax_arrears_sum: {\n type: 'number',\n description: 'Сумма недоимки по налогу'\n },\n tax_name: {\n type: 'string',\n description: 'Наименование налога (сбора, страховых взносов), денежного взыскания'\n },\n total_sum: {\n type: 'number',\n description: 'Общая сумма недоимки по налогу, пени и штрафу'\n }\n }\n }\n },\n doc_creation_date: {\n type: 'string',\n description: 'Дата формирования документа',\n format: 'date-time'\n },\n doc_preparation_date: {\n type: 'string',\n description: 'Дата, по состоянию на которую, подготовлены данные для публикации',\n format: 'date-time'\n }\n }\n }\n },\n paid_taxes: {\n type: 'array',\n description: 'Данные об уплаченных налогах',\n items: {\n type: 'object',\n description: 'Данные об уплаченных налогах',\n properties: {\n doc_date: {\n type: 'string',\n description: 'Дата формирования документа'\n },\n report_date: {\n type: 'string',\n description: 'Дата, по состоянию на которую, подготовлены данные для публикации (в ней указан отчетный год)'\n },\n tax_info_list: {\n type: 'array',\n description: 'Список уплаченных налогов',\n items: {\n type: 'object',\n description: 'Информация о налоге',\n properties: {\n taxName: {\n type: 'string',\n description: 'Наименование налога'\n },\n taxValue: {\n type: 'string',\n description: ' Сумма уплаченного налога (в рублях)'\n }\n }\n }\n }\n }\n }\n },\n tax_offences: {\n type: 'array',\n description: 'Данные о налоговых нарушениях',\n items: {\n type: 'object',\n description: 'Данные о налоговых нарушениях',\n properties: {\n doc_date: {\n type: 'string',\n description: 'Дата документа',\n format: 'date-time'\n },\n status_date: {\n type: 'string',\n description: 'Дата, по состоянию на которую, подготовлены данные для публикации',\n format: 'date-time'\n },\n sum: {\n type: 'number',\n description: 'Сумма штрафа'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inn: { - type: 'string', - description: 'ИНН организации или ИП. Должен быть указан, если не указан ОГРН/ОГРНИП.', - }, - ogrn: { - type: 'string', - description: 'ОГРН или ОГРНИП. Должен быть указан, если не указан ИНН.', - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: { - readOnlyHint: true, - }, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.taxInfo.retrieve(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || isJqError(error)) { - return asErrorResult(error.message); - } - throw error; - } -}; - -export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts b/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts deleted file mode 100644 index 7589ecb..0000000 --- a/packages/mcp-server/src/tools/taxpayer-statuses/create-taxpayer-statuses.ts +++ /dev/null @@ -1,61 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isJqError, maybeFilter } from 'datanewton-mcp/filtering'; -import { Metadata, asErrorResult, asTextContentResult } from 'datanewton-mcp/tools/types'; - -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import Datanewton from 'datanewton'; - -export const metadata: Metadata = { - resource: 'taxpayer_statuses', - operation: 'write', - tags: [], - httpMethod: 'post', - httpPath: '/v1/taxpayerStatuses', - operationId: '6_getTaxpayerStatuses', -}; - -export const tool: Tool = { - name: 'create_taxpayer_statuses', - description: - "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nПолучить данные о статусе самозанятого налогоплательщика по ИНН\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/taxpayer_status_create_response',\n $defs: {\n taxpayer_status_create_response: {\n type: 'object',\n description: 'Статусы самозанятых',\n properties: {\n available_count: {\n type: 'integer',\n description: 'Количество доступных запросов'\n },\n data: {\n type: 'array',\n description: 'Статусы самозанятых',\n items: {\n type: 'object',\n description: 'Статус самозанятого налогоплательщика',\n properties: {\n inn: {\n type: 'string',\n description: 'ИНН'\n },\n status_date: {\n type: 'string',\n description: 'Дата статуса самозанятого',\n format: 'date-time'\n },\n taxpayer: {\n type: 'boolean'\n }\n }\n }\n }\n }\n }\n }\n}\n```", - inputSchema: { - type: 'object', - properties: { - key: { - type: 'string', - description: 'API-ключ.', - }, - inns: { - type: 'array', - description: 'Список ИНН', - items: { - type: 'string', - description: 'Список ИНН', - }, - }, - jq_filter: { - type: 'string', - title: 'jq Filter', - description: - 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', - }, - }, - required: ['key'], - }, - annotations: {}, -}; - -export const handler = async (client: Datanewton, args: Record | undefined) => { - const { jq_filter, ...body } = args as any; - try { - return asTextContentResult(await maybeFilter(jq_filter, await client.taxpayerStatuses.create(body))); - } catch (error) { - if (error instanceof Datanewton.APIError || 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/types.ts similarity index 98% rename from packages/mcp-server/src/tools/types.ts rename to packages/mcp-server/src/types.ts index c346125..aa37e08 100644 --- a/packages/mcp-server/src/tools/types.ts +++ b/packages/mcp-server/src/types.ts @@ -108,7 +108,7 @@ export type Metadata = { operationId?: string; }; -export type Endpoint = { +export type McpTool = { metadata: Metadata; tool: Tool; handler: HandlerFunction; diff --git a/packages/mcp-server/tests/compat.test.ts b/packages/mcp-server/tests/compat.test.ts deleted file mode 100644 index d6272f6..0000000 --- a/packages/mcp-server/tests/compat.test.ts +++ /dev/null @@ -1,1166 +0,0 @@ -import { - truncateToolNames, - removeTopLevelUnions, - removeAnyOf, - inlineRefs, - applyCompatibilityTransformations, - removeFormats, - findUsedDefs, -} from '../src/compat'; -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { JSONSchema } from '../src/compat'; -import { Endpoint } from '../src/tools'; - -describe('truncateToolNames', () => { - it('should return original names when maxLength is 0 or negative', () => { - const names = ['tool1', 'tool2', 'tool3']; - expect(truncateToolNames(names, 0)).toEqual(new Map()); - expect(truncateToolNames(names, -1)).toEqual(new Map()); - }); - - it('should return original names when all names are shorter than maxLength', () => { - const names = ['tool1', 'tool2', 'tool3']; - expect(truncateToolNames(names, 10)).toEqual(new Map()); - }); - - it('should truncate names longer than maxLength', () => { - const names = ['very-long-tool-name', 'another-long-tool-name', 'short']; - expect(truncateToolNames(names, 10)).toEqual( - new Map([ - ['very-long-tool-name', 'very-long-'], - ['another-long-tool-name', 'another-lo'], - ]), - ); - }); - - it('should handle duplicate truncated names by appending numbers', () => { - const names = ['tool-name-a', 'tool-name-b', 'tool-name-c']; - expect(truncateToolNames(names, 8)).toEqual( - new Map([ - ['tool-name-a', 'tool-na1'], - ['tool-name-b', 'tool-na2'], - ['tool-name-c', 'tool-na3'], - ]), - ); - }); -}); - -describe('removeTopLevelUnions', () => { - const createTestTool = (overrides = {}): Tool => ({ - name: 'test-tool', - description: 'Test tool', - inputSchema: { - type: 'object', - properties: {}, - }, - ...overrides, - }); - - it('should return the original tool if it has no anyOf at the top level', () => { - const tool = createTestTool({ - inputSchema: { - type: 'object', - properties: { - foo: { type: 'string' }, - }, - }, - }); - - expect(removeTopLevelUnions(tool)).toEqual([tool]); - }); - - it('should split a tool with top-level anyOf into multiple tools', () => { - const tool = createTestTool({ - name: 'union-tool', - description: 'A tool with unions', - inputSchema: { - type: 'object', - properties: { - common: { type: 'string' }, - }, - anyOf: [ - { - title: 'first variant', - description: 'Its the first variant', - properties: { - variant1: { type: 'string' }, - }, - required: ['variant1'], - }, - { - title: 'second variant', - properties: { - variant2: { type: 'number' }, - }, - required: ['variant2'], - }, - ], - }, - }); - - const result = removeTopLevelUnions(tool); - - expect(result).toEqual([ - { - name: 'union-tool_first_variant', - description: 'Its the first variant', - inputSchema: { - type: 'object', - title: 'first variant', - description: 'Its the first variant', - properties: { - common: { type: 'string' }, - variant1: { type: 'string' }, - }, - required: ['variant1'], - }, - }, - { - name: 'union-tool_second_variant', - description: 'A tool with unions', - inputSchema: { - type: 'object', - title: 'second variant', - description: 'A tool with unions', - properties: { - common: { type: 'string' }, - variant2: { type: 'number' }, - }, - required: ['variant2'], - }, - }, - ]); - }); - - it('should handle $defs and only include those used by the variant', () => { - const tool = createTestTool({ - name: 'defs-tool', - description: 'A tool with $defs', - inputSchema: { - type: 'object', - properties: { - common: { type: 'string' }, - }, - $defs: { - def1: { type: 'string', format: 'email' }, - def2: { type: 'number', minimum: 0 }, - unused: { type: 'boolean' }, - }, - anyOf: [ - { - properties: { - email: { $ref: '#/$defs/def1' }, - }, - }, - { - properties: { - count: { $ref: '#/$defs/def2' }, - }, - }, - ], - }, - }); - - const result = removeTopLevelUnions(tool); - - expect(result).toEqual([ - { - name: 'defs-tool_variant1', - description: 'A tool with $defs', - inputSchema: { - type: 'object', - description: 'A tool with $defs', - properties: { - common: { type: 'string' }, - email: { $ref: '#/$defs/def1' }, - }, - $defs: { - def1: { type: 'string', format: 'email' }, - }, - }, - }, - { - name: 'defs-tool_variant2', - description: 'A tool with $defs', - inputSchema: { - type: 'object', - description: 'A tool with $defs', - properties: { - common: { type: 'string' }, - count: { $ref: '#/$defs/def2' }, - }, - $defs: { - def2: { type: 'number', minimum: 0 }, - }, - }, - }, - ]); - }); -}); - -describe('removeAnyOf', () => { - it('should return original schema if it has no anyOf', () => { - const schema = { - type: 'object', - properties: { - foo: { type: 'string' }, - bar: { type: 'number' }, - }, - }; - - expect(removeAnyOf(schema)).toEqual(schema); - }); - - it('should remove anyOf field and use the first variant', () => { - const schema = { - type: 'object', - properties: { - common: { type: 'string' }, - }, - anyOf: [ - { - properties: { - variant1: { type: 'string' }, - }, - required: ['variant1'], - }, - { - properties: { - variant2: { type: 'number' }, - }, - required: ['variant2'], - }, - ], - }; - - const expected = { - type: 'object', - properties: { - common: { type: 'string' }, - variant1: { type: 'string' }, - }, - required: ['variant1'], - }; - - expect(removeAnyOf(schema)).toEqual(expected); - }); - - it('should recursively remove anyOf fields from nested properties', () => { - const schema = { - type: 'object', - properties: { - foo: { type: 'string' }, - nested: { - type: 'object', - properties: { - bar: { type: 'number' }, - }, - anyOf: [ - { - properties: { - option1: { type: 'boolean' }, - }, - }, - { - properties: { - option2: { type: 'array' }, - }, - }, - ], - }, - }, - }; - - const expected = { - type: 'object', - properties: { - foo: { type: 'string' }, - nested: { - type: 'object', - properties: { - bar: { type: 'number' }, - option1: { type: 'boolean' }, - }, - }, - }, - }; - - expect(removeAnyOf(schema)).toEqual(expected); - }); - - it('should handle arrays', () => { - const schema = { - type: 'object', - properties: { - items: { - type: 'array', - items: { - anyOf: [{ type: 'string' }, { type: 'number' }], - }, - }, - }, - }; - - const expected = { - type: 'object', - properties: { - items: { - type: 'array', - items: { - type: 'string', - }, - }, - }, - }; - - expect(removeAnyOf(schema)).toEqual(expected); - }); -}); - -describe('findUsedDefs', () => { - it('should handle circular references without stack overflow', () => { - const defs = { - person: { - type: 'object', - properties: { - name: { type: 'string' }, - friend: { $ref: '#/$defs/person' }, // Circular reference - }, - }, - }; - - const schema = { - type: 'object', - properties: { - user: { $ref: '#/$defs/person' }, - }, - }; - - // This should not throw a stack overflow error - expect(() => { - const result = findUsedDefs(schema, defs); - expect(result).toHaveProperty('person'); - }).not.toThrow(); - }); - - it('should handle indirect circular references without stack overflow', () => { - const defs = { - node: { - type: 'object', - properties: { - value: { type: 'string' }, - child: { $ref: '#/$defs/childNode' }, - }, - }, - childNode: { - type: 'object', - properties: { - value: { type: 'string' }, - parent: { $ref: '#/$defs/node' }, // Indirect circular reference - }, - }, - }; - - const schema = { - type: 'object', - properties: { - root: { $ref: '#/$defs/node' }, - }, - }; - - // This should not throw a stack overflow error - expect(() => { - const result = findUsedDefs(schema, defs); - expect(result).toHaveProperty('node'); - expect(result).toHaveProperty('childNode'); - }).not.toThrow(); - }); - - it('should find all used definitions in non-circular schemas', () => { - const defs = { - user: { - type: 'object', - properties: { - name: { type: 'string' }, - address: { $ref: '#/$defs/address' }, - }, - }, - address: { - type: 'object', - properties: { - street: { type: 'string' }, - city: { type: 'string' }, - }, - }, - unused: { - type: 'object', - properties: { - data: { type: 'string' }, - }, - }, - }; - - const schema = { - type: 'object', - properties: { - person: { $ref: '#/$defs/user' }, - }, - }; - - const result = findUsedDefs(schema, defs); - expect(result).toHaveProperty('user'); - expect(result).toHaveProperty('address'); - expect(result).not.toHaveProperty('unused'); - }); -}); - -describe('inlineRefs', () => { - it('should return the original schema if it does not contain $refs', () => { - const schema: JSONSchema = { - type: 'object', - properties: { - name: { type: 'string' }, - age: { type: 'number' }, - }, - }; - - expect(inlineRefs(schema)).toEqual(schema); - }); - - it('should inline simple $refs', () => { - const schema: JSONSchema = { - type: 'object', - properties: { - user: { $ref: '#/$defs/user' }, - }, - $defs: { - user: { - type: 'object', - properties: { - name: { type: 'string' }, - email: { type: 'string' }, - }, - }, - }, - }; - - const expected: JSONSchema = { - type: 'object', - properties: { - user: { - type: 'object', - properties: { - name: { type: 'string' }, - email: { type: 'string' }, - }, - }, - }, - }; - - expect(inlineRefs(schema)).toEqual(expected); - }); - - it('should inline nested $refs', () => { - const schema: JSONSchema = { - type: 'object', - properties: { - order: { $ref: '#/$defs/order' }, - }, - $defs: { - order: { - type: 'object', - properties: { - id: { type: 'string' }, - items: { type: 'array', items: { $ref: '#/$defs/item' } }, - }, - }, - item: { - type: 'object', - properties: { - product: { type: 'string' }, - quantity: { type: 'integer' }, - }, - }, - }, - }; - - const expected: JSONSchema = { - type: 'object', - properties: { - order: { - type: 'object', - properties: { - id: { type: 'string' }, - items: { - type: 'array', - items: { - type: 'object', - properties: { - product: { type: 'string' }, - quantity: { type: 'integer' }, - }, - }, - }, - }, - }, - }, - }; - - expect(inlineRefs(schema)).toEqual(expected); - }); - - it('should handle circular references by removing the circular part', () => { - const schema: JSONSchema = { - type: 'object', - properties: { - person: { $ref: '#/$defs/person' }, - }, - $defs: { - person: { - type: 'object', - properties: { - name: { type: 'string' }, - friend: { $ref: '#/$defs/person' }, // Circular reference - }, - }, - }, - }; - - const expected: JSONSchema = { - type: 'object', - properties: { - person: { - type: 'object', - properties: { - name: { type: 'string' }, - // friend property is removed to break the circular reference - }, - }, - }, - }; - - expect(inlineRefs(schema)).toEqual(expected); - }); - - it('should handle indirect circular references', () => { - const schema: JSONSchema = { - type: 'object', - properties: { - node: { $ref: '#/$defs/node' }, - }, - $defs: { - node: { - type: 'object', - properties: { - value: { type: 'string' }, - child: { $ref: '#/$defs/childNode' }, - }, - }, - childNode: { - type: 'object', - properties: { - value: { type: 'string' }, - parent: { $ref: '#/$defs/node' }, // Circular reference through childNode - }, - }, - }, - }; - - const expected: JSONSchema = { - type: 'object', - properties: { - node: { - type: 'object', - properties: { - value: { type: 'string' }, - child: { - type: 'object', - properties: { - value: { type: 'string' }, - // parent property is removed to break the circular reference - }, - }, - }, - }, - }, - }; - - expect(inlineRefs(schema)).toEqual(expected); - }); - - it('should preserve other properties when inlining references', () => { - const schema: JSONSchema = { - type: 'object', - properties: { - address: { $ref: '#/$defs/address', description: 'User address' }, - }, - $defs: { - address: { - type: 'object', - properties: { - street: { type: 'string' }, - city: { type: 'string' }, - }, - required: ['street'], - }, - }, - }; - - const expected: JSONSchema = { - type: 'object', - properties: { - address: { - type: 'object', - description: 'User address', - properties: { - street: { type: 'string' }, - city: { type: 'string' }, - }, - required: ['street'], - }, - }, - }; - - expect(inlineRefs(schema)).toEqual(expected); - }); -}); - -describe('removeFormats', () => { - it('should return original schema if formats capability is true', () => { - const schema = { - type: 'object', - properties: { - date: { type: 'string', description: 'A date field', format: 'date' }, - email: { type: 'string', description: 'An email field', format: 'email' }, - }, - }; - - expect(removeFormats(schema, true)).toEqual(schema); - }); - - it('should move format to description when formats capability is false', () => { - const schema = { - type: 'object', - properties: { - date: { type: 'string', description: 'A date field', format: 'date' }, - email: { type: 'string', description: 'An email field', format: 'email' }, - }, - }; - - const expected = { - type: 'object', - properties: { - date: { type: 'string', description: 'A date field (format: "date")' }, - email: { type: 'string', description: 'An email field (format: "email")' }, - }, - }; - - expect(removeFormats(schema, false)).toEqual(expected); - }); - - it('should handle properties without description', () => { - const schema = { - type: 'object', - properties: { - date: { type: 'string', format: 'date' }, - }, - }; - - const expected = { - type: 'object', - properties: { - date: { type: 'string', description: '(format: "date")' }, - }, - }; - - expect(removeFormats(schema, false)).toEqual(expected); - }); - - it('should handle nested properties', () => { - const schema = { - type: 'object', - properties: { - user: { - type: 'object', - properties: { - created_at: { type: 'string', description: 'Creation date', format: 'date-time' }, - }, - }, - }, - }; - - const expected = { - type: 'object', - properties: { - user: { - type: 'object', - properties: { - created_at: { type: 'string', description: 'Creation date (format: "date-time")' }, - }, - }, - }, - }; - - expect(removeFormats(schema, false)).toEqual(expected); - }); - - it('should handle arrays of objects', () => { - const schema = { - type: 'object', - properties: { - dates: { - type: 'array', - items: { - type: 'object', - properties: { - start: { type: 'string', description: 'Start date', format: 'date' }, - end: { type: 'string', description: 'End date', format: 'date' }, - }, - }, - }, - }, - }; - - const expected = { - type: 'object', - properties: { - dates: { - type: 'array', - items: { - type: 'object', - properties: { - start: { type: 'string', description: 'Start date (format: "date")' }, - end: { type: 'string', description: 'End date (format: "date")' }, - }, - }, - }, - }, - }; - - expect(removeFormats(schema, false)).toEqual(expected); - }); - - it('should handle schemas with $defs', () => { - const schema = { - type: 'object', - properties: { - date: { type: 'string', description: 'A date field', format: 'date' }, - }, - $defs: { - timestamp: { - type: 'string', - description: 'A timestamp field', - format: 'date-time', - }, - }, - }; - - const expected = { - type: 'object', - properties: { - date: { type: 'string', description: 'A date field (format: "date")' }, - }, - $defs: { - timestamp: { - type: 'string', - description: 'A timestamp field (format: "date-time")', - }, - }, - }; - - expect(removeFormats(schema, false)).toEqual(expected); - }); -}); - -describe('applyCompatibilityTransformations', () => { - const createTestTool = (name: string, overrides = {}): Tool => ({ - name, - description: 'Test tool', - inputSchema: { - type: 'object', - properties: {}, - }, - ...overrides, - }); - - const createTestEndpoint = (tool: Tool): Endpoint => ({ - tool, - handler: jest.fn(), - metadata: { - resource: 'test', - operation: 'read' as const, - tags: [], - }, - }); - - it('should not modify endpoints when all capabilities are enabled', () => { - const tool = createTestTool('test-tool'); - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - expect(transformed).toEqual(endpoints); - }); - - it('should split tools with top-level unions when topLevelUnions is disabled', () => { - const tool = createTestTool('union-tool', { - inputSchema: { - type: 'object', - properties: { - common: { type: 'string' }, - }, - anyOf: [ - { - title: 'first variant', - properties: { - variant1: { type: 'string' }, - }, - }, - { - title: 'second variant', - properties: { - variant2: { type: 'number' }, - }, - }, - ], - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - expect(transformed.length).toBe(2); - expect(transformed[0]!.tool.name).toBe('union-tool_first_variant'); - expect(transformed[1]!.tool.name).toBe('union-tool_second_variant'); - }); - - it('should handle variants without titles in removeTopLevelUnions', () => { - const tool = createTestTool('union-tool', { - inputSchema: { - type: 'object', - properties: { - common: { type: 'string' }, - }, - anyOf: [ - { - properties: { - variant1: { type: 'string' }, - }, - }, - { - properties: { - variant2: { type: 'number' }, - }, - }, - ], - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - expect(transformed.length).toBe(2); - expect(transformed[0]!.tool.name).toBe('union-tool_variant1'); - expect(transformed[1]!.tool.name).toBe('union-tool_variant2'); - }); - - it('should truncate tool names when toolNameLength is set', () => { - const tools = [ - createTestTool('very-long-tool-name-that-exceeds-limit'), - createTestTool('another-long-tool-name-to-truncate'), - createTestTool('short-name'), - ]; - - const endpoints = tools.map(createTestEndpoint); - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: 20, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - expect(transformed[0]!.tool.name).toBe('very-long-tool-name-'); - expect(transformed[1]!.tool.name).toBe('another-long-tool-na'); - expect(transformed[2]!.tool.name).toBe('short-name'); - }); - - it('should inline refs when refs capability is disabled', () => { - const tool = createTestTool('ref-tool', { - inputSchema: { - type: 'object', - properties: { - user: { $ref: '#/$defs/user' }, - }, - $defs: { - user: { - type: 'object', - properties: { - name: { type: 'string' }, - email: { type: 'string' }, - }, - }, - }, - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: false, - unions: true, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - const schema = transformed[0]!.tool.inputSchema as JSONSchema; - expect(schema.$defs).toBeUndefined(); - - if (schema.properties) { - expect(schema.properties['user']).toEqual({ - type: 'object', - properties: { - name: { type: 'string' }, - email: { type: 'string' }, - }, - }); - } - }); - - it('should preserve external refs when inlining', () => { - const tool = createTestTool('ref-tool', { - inputSchema: { - type: 'object', - properties: { - internal: { $ref: '#/$defs/internal' }, - external: { $ref: 'https://example.com/schemas/external.json' }, - }, - $defs: { - internal: { - type: 'object', - properties: { - name: { type: 'string' }, - }, - }, - }, - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: false, - unions: true, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - const schema = transformed[0]!.tool.inputSchema as JSONSchema; - - if (schema.properties) { - expect(schema.properties['internal']).toEqual({ - type: 'object', - properties: { - name: { type: 'string' }, - }, - }); - expect(schema.properties['external']).toEqual({ - $ref: 'https://example.com/schemas/external.json', - }); - } - }); - - it('should remove anyOf fields when unions capability is disabled', () => { - const tool = createTestTool('union-tool', { - inputSchema: { - type: 'object', - properties: { - field: { - anyOf: [{ type: 'string' }, { type: 'number' }], - }, - }, - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: true, - unions: false, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - const schema = transformed[0]!.tool.inputSchema as JSONSchema; - - if (schema.properties && schema.properties['field']) { - const field = schema.properties['field']; - expect(field.anyOf).toBeUndefined(); - expect(field.type).toBe('string'); - } - }); - - it('should correctly combine topLevelUnions and toolNameLength transformations', () => { - const tool = createTestTool('very-long-union-tool-name', { - inputSchema: { - type: 'object', - properties: { - common: { type: 'string' }, - }, - anyOf: [ - { - title: 'first variant', - properties: { - variant1: { type: 'string' }, - }, - }, - { - title: 'second variant', - properties: { - variant2: { type: 'number' }, - }, - }, - ], - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: 20, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - expect(transformed.length).toBe(2); - - // Both names should be truncated because they exceed 20 characters - expect(transformed[0]!.tool.name).toBe('very-long-union-too1'); - expect(transformed[1]!.tool.name).toBe('very-long-union-too2'); - }); - - it('should correctly combine refs and unions transformations', () => { - const tool = createTestTool('complex-tool', { - inputSchema: { - type: 'object', - properties: { - user: { $ref: '#/$defs/user' }, - }, - $defs: { - user: { - type: 'object', - properties: { - preference: { - anyOf: [{ type: 'string' }, { type: 'number' }], - }, - }, - }, - }, - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: false, - unions: false, - formats: true, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - const schema = transformed[0]!.tool.inputSchema as JSONSchema; - - // Refs should be inlined - expect(schema.$defs).toBeUndefined(); - - // Safely access nested properties - if (schema.properties && schema.properties['user']) { - const user = schema.properties['user']; - // User should be inlined - expect(user.type).toBe('object'); - - // AnyOf in the inlined user.preference should be removed - if (user.properties && user.properties['preference']) { - const preference = user.properties['preference']; - expect(preference.anyOf).toBeUndefined(); - expect(preference.type).toBe('string'); - } - } - }); - - it('should handle formats capability being false', () => { - const tool = createTestTool('format-tool', { - inputSchema: { - type: 'object', - properties: { - date: { type: 'string', description: 'A date', format: 'date' }, - }, - }, - }); - - const endpoints = [createTestEndpoint(tool)]; - - const capabilities = { - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: false, - toolNameLength: undefined, - }; - - const transformed = applyCompatibilityTransformations(endpoints, capabilities); - const schema = transformed[0]!.tool.inputSchema as JSONSchema; - - if (schema.properties && schema.properties['date']) { - const dateField = schema.properties['date']; - expect(dateField['format']).toBeUndefined(); - expect(dateField['description']).toBe('A date (format: "date")'); - } - }); -}); diff --git a/packages/mcp-server/tests/dynamic-tools.test.ts b/packages/mcp-server/tests/dynamic-tools.test.ts deleted file mode 100644 index 08963af..0000000 --- a/packages/mcp-server/tests/dynamic-tools.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { dynamicTools } from '../src/dynamic-tools'; -import { Endpoint } from '../src/tools'; - -describe('dynamicTools', () => { - const fakeClient = {} as any; - - const endpoints: Endpoint[] = [ - makeEndpoint('test_read_endpoint', 'test_resource', 'read', ['test']), - makeEndpoint('test_write_endpoint', 'test_resource', 'write', ['test']), - makeEndpoint('user_endpoint', 'user', 'read', ['user', 'admin']), - makeEndpoint('admin_endpoint', 'admin', 'write', ['admin']), - ]; - - const tools = dynamicTools(endpoints); - - const toolsMap = { - list_api_endpoints: toolOrError('list_api_endpoints'), - get_api_endpoint_schema: toolOrError('get_api_endpoint_schema'), - invoke_api_endpoint: toolOrError('invoke_api_endpoint'), - }; - - describe('list_api_endpoints', () => { - it('should return all endpoints when no search query is provided', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, {}); - const result = JSON.parse(content.content[0].text); - - expect(result.tools).toHaveLength(endpoints.length); - expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_read_endpoint'); - expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_write_endpoint'); - expect(result.tools.map((t: { name: string }) => t.name)).toContain('user_endpoint'); - expect(result.tools.map((t: { name: string }) => t.name)).toContain('admin_endpoint'); - }); - - it('should filter endpoints by name', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'user' }); - const result = JSON.parse(content.content[0].text); - - expect(result.tools).toHaveLength(1); - expect(result.tools[0].name).toBe('user_endpoint'); - }); - - it('should filter endpoints by resource', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' }); - const result = JSON.parse(content.content[0].text); - - expect(result.tools.some((t: { resource: string }) => t.resource === 'admin')).toBeTruthy(); - }); - - it('should filter endpoints by tag', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' }); - const result = JSON.parse(content.content[0].text); - - expect(result.tools.some((t: { tags: string[] }) => t.tags.includes('admin'))).toBeTruthy(); - }); - - it('should be case insensitive in search', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'ADMIN' }); - const result = JSON.parse(content.content[0].text); - - expect(result.tools.length).toBe(2); - result.tools.forEach((tool: { name: string; resource: string; tags: string[] }) => { - expect( - tool.name.toLowerCase().includes('admin') || - tool.resource.toLowerCase().includes('admin') || - tool.tags.some((tag: string) => tag.toLowerCase().includes('admin')), - ).toBeTruthy(); - }); - }); - - it('should filter endpoints by description', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, { - search_query: 'Test endpoint for user_endpoint', - }); - const result = JSON.parse(content.content[0].text); - - expect(result.tools).toHaveLength(1); - expect(result.tools[0].name).toBe('user_endpoint'); - expect(result.tools[0].description).toBe('Test endpoint for user_endpoint'); - }); - - it('should filter endpoints by partial description match', async () => { - const content = await toolsMap.list_api_endpoints.handler(fakeClient, { - search_query: 'endpoint for user', - }); - const result = JSON.parse(content.content[0].text); - - expect(result.tools).toHaveLength(1); - expect(result.tools[0].name).toBe('user_endpoint'); - }); - }); - - describe('get_api_endpoint_schema', () => { - it('should return schema for existing endpoint', async () => { - const content = await toolsMap.get_api_endpoint_schema.handler(fakeClient, { - endpoint: 'test_read_endpoint', - }); - const result = JSON.parse(content.content[0].text); - - expect(result).toEqual(endpoints[0]?.tool); - }); - - it('should throw error for non-existent endpoint', async () => { - await expect( - toolsMap.get_api_endpoint_schema.handler(fakeClient, { endpoint: 'non_existent_endpoint' }), - ).rejects.toThrow('Endpoint non_existent_endpoint not found'); - }); - - it('should throw error when no endpoint provided', async () => { - await expect(toolsMap.get_api_endpoint_schema.handler(fakeClient, undefined)).rejects.toThrow( - 'No endpoint provided', - ); - }); - }); - - describe('invoke_api_endpoint', () => { - it('should successfully invoke endpoint with valid arguments', async () => { - const mockHandler = endpoints[0]?.handler as jest.Mock; - mockHandler.mockClear(); - - await toolsMap.invoke_api_endpoint.handler(fakeClient, { - endpoint_name: 'test_read_endpoint', - args: { testParam: 'test value' }, - }); - - expect(mockHandler).toHaveBeenCalledWith(fakeClient, { testParam: 'test value' }); - }); - - it('should throw error for non-existent endpoint', async () => { - await expect( - toolsMap.invoke_api_endpoint.handler(fakeClient, { - endpoint_name: 'non_existent_endpoint', - args: { testParam: 'test value' }, - }), - ).rejects.toThrow(/Endpoint non_existent_endpoint not found/); - }); - - it('should throw error when no arguments provided', async () => { - await expect(toolsMap.invoke_api_endpoint.handler(fakeClient, undefined)).rejects.toThrow( - 'No endpoint provided', - ); - }); - - it('should throw error for invalid argument schema', async () => { - await expect( - toolsMap.invoke_api_endpoint.handler(fakeClient, { - endpoint_name: 'test_read_endpoint', - args: { wrongParam: 'test value' }, // Missing required testParam - }), - ).rejects.toThrow(/Invalid arguments for endpoint/); - }); - }); - - function toolOrError(name: string) { - const tool = tools.find((tool) => tool.tool.name === name); - if (!tool) throw new Error(`Tool ${name} not found`); - return tool; - } -}); - -function makeEndpoint( - name: string, - resource: string, - operation: 'read' | 'write', - tags: string[] = [], -): Endpoint { - return { - metadata: { - resource, - operation, - tags, - }, - tool: { - name, - description: `Test endpoint for ${name}`, - inputSchema: { - type: 'object', - properties: { - testParam: { type: 'string' }, - }, - required: ['testParam'], - }, - }, - handler: jest.fn().mockResolvedValue({ success: true }), - }; -} diff --git a/packages/mcp-server/tests/options.test.ts b/packages/mcp-server/tests/options.test.ts index 4d9b60c..532666a 100644 --- a/packages/mcp-server/tests/options.test.ts +++ b/packages/mcp-server/tests/options.test.ts @@ -1,6 +1,4 @@ import { parseCLIOptions, parseQueryOptions } from '../src/options'; -import { Filter } from '../src/tools'; -import { parseEmbeddedJSON } from '../src/compat'; // Mock process.argv const mockArgv = (args: string[]) => { @@ -12,338 +10,35 @@ const mockArgv = (args: string[]) => { }; describe('parseCLIOptions', () => { - it('should parse basic filter options', () => { - const cleanup = mockArgv([ - '--tool=test-tool', - '--resource=test-resource', - '--operation=read', - '--tag=test-tag', - ]); + it('default parsing should be stdio', () => { + const cleanup = mockArgv([]); const result = parseCLIOptions(); - expect(result.filters).toEqual([ - { type: 'tag', op: 'include', value: 'test-tag' }, - { type: 'resource', op: 'include', value: 'test-resource' }, - { type: 'tool', op: 'include', value: 'test-tool' }, - { type: 'operation', op: 'include', value: 'read' }, - ] as Filter[]); - - expect(result.capabilities).toEqual({}); - - expect(result.list).toBe(false); + expect(result.transport).toBe('stdio'); cleanup(); }); - it('should parse exclusion filters', () => { - const cleanup = mockArgv([ - '--no-tool=exclude-tool', - '--no-resource=exclude-resource', - '--no-operation=write', - '--no-tag=exclude-tag', - ]); + it('using http transport with a port', () => { + const cleanup = mockArgv(['--transport=http', '--port=2222']); const result = parseCLIOptions(); - expect(result.filters).toEqual([ - { type: 'tag', op: 'exclude', value: 'exclude-tag' }, - { type: 'resource', op: 'exclude', value: 'exclude-resource' }, - { type: 'tool', op: 'exclude', value: 'exclude-tool' }, - { type: 'operation', op: 'exclude', value: 'write' }, - ] as Filter[]); - - expect(result.capabilities).toEqual({}); - - cleanup(); - }); - - it('should parse client presets', () => { - const cleanup = mockArgv(['--client=openai-agents']); - - const result = parseCLIOptions(); - - expect(result.client).toEqual('openai-agents'); - - cleanup(); - }); - - it('should parse individual capabilities', () => { - const cleanup = mockArgv([ - '--capability=top-level-unions', - '--capability=valid-json', - '--capability=refs', - '--capability=unions', - '--capability=tool-name-length=40', - ]); - - const result = parseCLIOptions(); - - expect(result.capabilities).toEqual({ - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - toolNameLength: 40, - }); - - cleanup(); - }); - - it('should handle list option', () => { - const cleanup = mockArgv(['--list']); - - const result = parseCLIOptions(); - - expect(result.list).toBe(true); - - cleanup(); - }); - - it('should handle multiple filters of the same type', () => { - const cleanup = mockArgv(['--tool=tool1', '--tool=tool2', '--resource=res1', '--resource=res2']); - - const result = parseCLIOptions(); - - expect(result.filters).toEqual([ - { type: 'resource', op: 'include', value: 'res1' }, - { type: 'resource', op: 'include', value: 'res2' }, - { type: 'tool', op: 'include', value: 'tool1' }, - { type: 'tool', op: 'include', value: 'tool2' }, - ] as Filter[]); - - cleanup(); - }); - - it('should handle comma-separated values in array options', () => { - const cleanup = mockArgv([ - '--tool=tool1,tool2', - '--resource=res1,res2', - '--capability=top-level-unions,valid-json,unions', - ]); - - const result = parseCLIOptions(); - - expect(result.filters).toEqual([ - { type: 'resource', op: 'include', value: 'res1' }, - { type: 'resource', op: 'include', value: 'res2' }, - { type: 'tool', op: 'include', value: 'tool1' }, - { type: 'tool', op: 'include', value: 'tool2' }, - ] as Filter[]); - - expect(result.capabilities).toEqual({ - topLevelUnions: true, - validJson: true, - unions: true, - }); - - cleanup(); - }); - - it('should handle invalid tool-name-length format', () => { - const cleanup = mockArgv(['--capability=tool-name-length=invalid']); - - // Mock console.error to prevent output during test - const originalError = console.error; - console.error = jest.fn(); - - expect(() => parseCLIOptions()).toThrow(); - - console.error = originalError; - cleanup(); - }); - - it('should handle unknown capability', () => { - const cleanup = mockArgv(['--capability=unknown-capability']); - - // Mock console.error to prevent output during test - const originalError = console.error; - console.error = jest.fn(); - - expect(() => parseCLIOptions()).toThrow(); - - console.error = originalError; + expect(result.transport).toBe('http'); + expect(result.port).toBe('2222'); cleanup(); }); }); describe('parseQueryOptions', () => { - const defaultOptions = { - client: undefined, - includeDynamicTools: undefined, - includeCodeTools: undefined, - includeAllTools: undefined, - filters: [], - capabilities: { - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - }; - - it('should parse basic filter options from query string', () => { - const query = 'tool=test-tool&resource=test-resource&operation=read&tag=test-tag'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.filters).toEqual([ - { type: 'resource', op: 'include', value: 'test-resource' }, - { type: 'operation', op: 'include', value: 'read' }, - { type: 'tag', op: 'include', value: 'test-tag' }, - { type: 'tool', op: 'include', value: 'test-tool' }, - ]); - - expect(result.capabilities).toEqual({ - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }); - }); - - it('should parse exclusion filters from query string', () => { - const query = 'no_tool=exclude-tool&no_resource=exclude-resource&no_operation=write&no_tag=exclude-tag'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.filters).toEqual([ - { type: 'resource', op: 'exclude', value: 'exclude-resource' }, - { type: 'operation', op: 'exclude', value: 'write' }, - { type: 'tag', op: 'exclude', value: 'exclude-tag' }, - { type: 'tool', op: 'exclude', value: 'exclude-tool' }, - ]); - }); - - it('should parse client option from query string', () => { - const query = 'client=openai-agents'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.client).toBe('openai-agents'); - }); - - it('should parse client capabilities from query string', () => { - const query = 'capability=top-level-unions&capability=valid-json&capability=tool-name-length%3D40'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.capabilities).toEqual({ - topLevelUnions: true, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: 40, - }); - }); - - it('should parse no-capability options from query string', () => { - const query = 'no_capability=top-level-unions&no_capability=refs&no_capability=formats'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.capabilities).toEqual({ - topLevelUnions: false, - validJson: true, - refs: false, - unions: true, - formats: false, - toolNameLength: undefined, - }); - }); - - it('should parse tools options from query string', () => { - const query = 'tools=dynamic&tools=all'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.includeDynamicTools).toBe(true); - expect(result.includeAllTools).toBe(true); - }); - - it('should parse no-tools options from query string', () => { - const query = 'tools=dynamic&tools=all&no_tools=dynamic'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.includeDynamicTools).toBe(false); - expect(result.includeAllTools).toBe(true); - }); - - it('should handle array values in query string', () => { - const query = 'tool[]=tool1&tool[]=tool2&resource[]=res1&resource[]=res2'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.filters).toEqual([ - { type: 'resource', op: 'include', value: 'res1' }, - { type: 'resource', op: 'include', value: 'res2' }, - { type: 'tool', op: 'include', value: 'tool1' }, - { type: 'tool', op: 'include', value: 'tool2' }, - ]); - }); - - it('should merge with default options', () => { - const defaultWithFilters = { - ...defaultOptions, - filters: [{ type: 'tag' as const, op: 'include' as const, value: 'existing-tag' }], - client: 'cursor' as const, - includeDynamicTools: true, - }; - - const query = 'tool=new-tool&resource=new-resource'; - const result = parseQueryOptions(defaultWithFilters, query); - - expect(result.filters).toEqual([ - { type: 'tag', op: 'include', value: 'existing-tag' }, - { type: 'resource', op: 'include', value: 'new-resource' }, - { type: 'tool', op: 'include', value: 'new-tool' }, - ]); - - expect(result.client).toBe('cursor'); - expect(result.includeDynamicTools).toBe(true); - }); - - it('should override client from default options', () => { - const defaultWithClient = { - ...defaultOptions, - client: 'cursor' as const, - }; + const defaultOptions = {}; - const query = 'client=openai-agents'; - const result = parseQueryOptions(defaultWithClient, query); - - expect(result.client).toBe('openai-agents'); - }); - - it('should merge capabilities with default options', () => { - const defaultWithCapabilities = { - ...defaultOptions, - capabilities: { - topLevelUnions: false, - validJson: false, - refs: true, - unions: true, - formats: true, - toolNameLength: 30, - }, - }; - - const query = 'capability=top-level-unions&no_capability=refs'; - const result = parseQueryOptions(defaultWithCapabilities, query); - - expect(result.capabilities).toEqual({ - topLevelUnions: true, - validJson: false, - refs: false, - unions: true, - formats: true, - toolNameLength: 30, - }); - }); - - it('should handle empty query string', () => { + it('default parsing should be empty', () => { const query = ''; const result = parseQueryOptions(defaultOptions, query); - expect(result).toEqual(defaultOptions); + expect(result).toBe({}); }); it('should handle invalid query string gracefully', () => { @@ -352,189 +47,4 @@ describe('parseQueryOptions', () => { // Should throw due to Zod validation for invalid operation expect(() => parseQueryOptions(defaultOptions, query)).toThrow(); }); - - it('should preserve default undefined values when not specified', () => { - const defaultWithUndefined = { - ...defaultOptions, - client: undefined, - includeDynamicTools: undefined, - includeAllTools: undefined, - }; - - const query = 'tool=test-tool'; - const result = parseQueryOptions(defaultWithUndefined, query); - - expect(result.client).toBeUndefined(); - expect(result.includeDynamicTools).toBeFalsy(); - expect(result.includeAllTools).toBeFalsy(); - }); - - it('should handle complex query with mixed include and exclude filters', () => { - const query = - 'tool=include-tool&no_tool=exclude-tool&resource=include-res&no_resource=exclude-res&operation=read&tag=include-tag&no_tag=exclude-tag'; - const result = parseQueryOptions(defaultOptions, query); - - expect(result.filters).toEqual([ - { type: 'resource', op: 'include', value: 'include-res' }, - { type: 'operation', op: 'include', value: 'read' }, - { type: 'tag', op: 'include', value: 'include-tag' }, - { type: 'tool', op: 'include', value: 'include-tool' }, - { type: 'resource', op: 'exclude', value: 'exclude-res' }, - { type: 'tag', op: 'exclude', value: 'exclude-tag' }, - { 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', () => { - it('should not change non-string values', () => { - const args = { - numberProp: 42, - booleanProp: true, - objectProp: { nested: 'value' }, - arrayProp: [1, 2, 3], - nullProp: null, - undefinedProp: undefined, - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).toBe(args); // Should return original object since no changes made - expect(result['numberProp']).toBe(42); - expect(result['booleanProp']).toBe(true); - expect(result['objectProp']).toEqual({ nested: 'value' }); - expect(result['arrayProp']).toEqual([1, 2, 3]); - expect(result['nullProp']).toBe(null); - expect(result['undefinedProp']).toBe(undefined); - }); - - it('should parse valid JSON objects in string properties', () => { - const args = { - jsonObjectString: '{"key": "value", "number": 123}', - regularString: 'not json', - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).not.toBe(args); // Should return new object since changes were made - expect(result['jsonObjectString']).toEqual({ key: 'value', number: 123 }); - expect(result['regularString']).toBe('not json'); - }); - - it('should leave invalid JSON in string properties unchanged', () => { - const args = { - invalidJson1: '{"key": value}', // Missing quotes around value - invalidJson2: '{key: "value"}', // Missing quotes around key - invalidJson3: '{"key": "value",}', // Trailing comma - invalidJson4: 'just a regular string', - emptyString: '', - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).toBe(args); // Should return original object since no changes made - expect(result['invalidJson1']).toBe('{"key": value}'); - expect(result['invalidJson2']).toBe('{key: "value"}'); - expect(result['invalidJson3']).toBe('{"key": "value",}'); - expect(result['invalidJson4']).toBe('just a regular string'); - expect(result['emptyString']).toBe(''); - }); - - it('should not parse JSON primitives in string properties', () => { - const args = { - numberString: '123', - floatString: '45.67', - negativeNumberString: '-89', - booleanTrueString: 'true', - booleanFalseString: 'false', - nullString: 'null', - jsonArrayString: '[1, 2, 3, "test"]', - regularString: 'not json', - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).toBe(args); // Should return original object since no changes made - expect(result['numberString']).toBe('123'); - expect(result['floatString']).toBe('45.67'); - expect(result['negativeNumberString']).toBe('-89'); - expect(result['booleanTrueString']).toBe('true'); - expect(result['booleanFalseString']).toBe('false'); - expect(result['nullString']).toBe('null'); - expect(result['jsonArrayString']).toBe('[1, 2, 3, "test"]'); - expect(result['regularString']).toBe('not json'); - }); - - it('should handle mixed valid objects and other JSON types', () => { - const args = { - validObject: '{"success": true}', - invalidObject: '{"missing": quote}', - validNumber: '42', - validArray: '[1, 2, 3]', - keepAsString: 'hello world', - nonString: 123, - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).not.toBe(args); // Should return new object since some changes were made - expect(result['validObject']).toEqual({ success: true }); - expect(result['invalidObject']).toBe('{"missing": quote}'); - expect(result['validNumber']).toBe('42'); // Not parsed, remains string - expect(result['validArray']).toBe('[1, 2, 3]'); // Not parsed, remains string - expect(result['keepAsString']).toBe('hello world'); - expect(result['nonString']).toBe(123); - }); - - it('should return original object when no strings are present', () => { - const args = { - number: 42, - boolean: true, - object: { key: 'value' }, - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).toBe(args); // Should return original object since no changes made - }); - - it('should return original object when all strings are invalid JSON', () => { - const args = { - string1: 'hello', - string2: 'world', - string3: 'not json at all', - }; - const schema = {}; - - const result = parseEmbeddedJSON(args, schema); - - expect(result).toBe(args); // Should return original object since no changes made - }); }); diff --git a/packages/mcp-server/tests/tools.test.ts b/packages/mcp-server/tests/tools.test.ts deleted file mode 100644 index cfff24a..0000000 --- a/packages/mcp-server/tests/tools.test.ts +++ /dev/null @@ -1,225 +0,0 @@ -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 none', - filters: [], - expected: [], - }, - - // 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}`, () => { - 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 83728c61575af28520a137f8f09a30273a40d58b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 05:59:58 +0000 Subject: [PATCH 30/31] chore(internal): codegen related update --- .github/workflows/release-doctor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 5bd88f9..c13364e 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -17,5 +17,4 @@ jobs: - name: Check release environment run: | bash ./bin/check-release-environment - env: From 283686884cacc6d5e80d0ecf5e5ccac819f13838 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 06:00:20 +0000 Subject: [PATCH 31/31] release: 0.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 52 +++++++++++++++++++++++++++++++ package.json | 2 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/server.ts | 2 +- src/version.ts | 2 +- 6 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b985ff6..466df71 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1" + ".": "0.1.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0904ce3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,52 @@ +# Changelog + +## 0.1.0 (2025-12-20) + +Full Changelog: [v0.0.1...v0.1.0](https://github.com/AlexK420/mcpdata/compare/v0.0.1...v0.1.0) + +### ⚠ BREAKING CHANGES + +* **mcp:** remove deprecated tool schemes +* **mcp:** **Migration:** To migrate, simply modify the command used to invoke the MCP server. Currently, the only supported tool scheme is code mode. Now, starting the server with just `node /path/to/mcp/server` or `npx package-name` will invoke code tools: changing your command to one of these is likely all you will need to do. + +### Features + +* **mcp:** add detail field to docs search tool ([4b2017b](https://github.com/AlexK420/mcpdata/commit/4b2017b55610adec8b1a46b2d37d12703df9b47f)) +* **mcp:** add typescript check to code execution tool ([5066987](https://github.com/AlexK420/mcpdata/commit/5066987adf78a9796d7bab03826a46f6262dbad4)) +* **mcp:** enable optional code execution tool on http mcp servers ([72af577](https://github.com/AlexK420/mcpdata/commit/72af577677c3997c6b9deb3cbe55f0783f12dc6a)) +* **mcp:** handle code mode calls in the Stainless API ([839ac88](https://github.com/AlexK420/mcpdata/commit/839ac88fbd1df74deca3e4895e3584b39d7fb8c3)) +* **mcp:** return logs on code tool errors ([41d5fd2](https://github.com/AlexK420/mcpdata/commit/41d5fd29e71193adbca5ea8e9fa4c7cc2173cf33)) + + +### Bug Fixes + +* **mcp:** add client instantiation options to code tool ([5555bbd](https://github.com/AlexK420/mcpdata/commit/5555bbd7d126b51692f036eebe12ad0840c54ef4)) +* **mcpb:** pin @anthropic-ai/mcpb version ([990298f](https://github.com/AlexK420/mcpdata/commit/990298fcc57b030ae1f78d7ba385ec5d9624f91d)) +* **mcp:** correct code tool API endpoint ([d7df03a](https://github.com/AlexK420/mcpdata/commit/d7df03a0cd7a5522e3dfff87f4b07ea60e16b260)) +* **mcp:** pass base url to code tool ([f1b4e0f](https://github.com/AlexK420/mcpdata/commit/f1b4e0f55332e801de9ba9d1ed594890f5a5f3b7)) +* **mcp:** return correct lines on typescript errors ([986864a](https://github.com/AlexK420/mcpdata/commit/986864ae236a763a5f37699eb32b3a5db4a791e8)) +* **mcp:** return tool execution error on api error ([6459934](https://github.com/AlexK420/mcpdata/commit/6459934e52ba9f580b96314cfa2a3d3157e53256)) +* **mcp:** return tool execution error on jq failure ([fe28a35](https://github.com/AlexK420/mcpdata/commit/fe28a35e4398286ca2a262969fff5d76573e425a)) + + +### Chores + +* **client:** fix logger property type ([e9faa7a](https://github.com/AlexK420/mcpdata/commit/e9faa7a2b1cc9fa6ea4bec052bfba825261c06e9)) +* configure new SDK language ([a930fd0](https://github.com/AlexK420/mcpdata/commit/a930fd0832c1d8ef3fd888df75ec19b122bb2ade)) +* **internal:** codegen related update ([83728c6](https://github.com/AlexK420/mcpdata/commit/83728c61575af28520a137f8f09a30273a40d58b)) +* **internal:** codegen related update ([b9aa6fc](https://github.com/AlexK420/mcpdata/commit/b9aa6fcd48973b44d9e719ec0d100a18379d9384)) +* **internal:** codegen related update ([ec870f5](https://github.com/AlexK420/mcpdata/commit/ec870f58a106701cec6f695c485dc1a9622e421c)) +* **internal:** codegen related update ([35c5956](https://github.com/AlexK420/mcpdata/commit/35c5956900f002543fc8782f748cd0fc5aa54594)) +* **internal:** codegen related update ([2bcff40](https://github.com/AlexK420/mcpdata/commit/2bcff4090eacb07af1a4de4679726ec05020bf89)) +* **internal:** grammar fix (it's -> its) ([4e37db8](https://github.com/AlexK420/mcpdata/commit/4e37db84dd2fd317a15243277ebb9c90d165bb78)) +* **internal:** upgrade eslint ([97681ab](https://github.com/AlexK420/mcpdata/commit/97681abb1098977c32bdb1a9ee82548d500dbd1e)) +* mcp code tool explicit error message when missing a run function ([f3e1d63](https://github.com/AlexK420/mcpdata/commit/f3e1d63b1e17ea6b6daca88b19afdcf08d46f803)) +* **mcp:** add friendlier MCP code tool errors on incorrect method invocations ([5ead6bc](https://github.com/AlexK420/mcpdata/commit/5ead6bc9cca93e80715d0278e41c29a6939e6e4b)) +* **mcp:** add line numbers to code tool errors ([3a5a56c](https://github.com/AlexK420/mcpdata/commit/3a5a56c4595482567f3596f34e7c620ee4de0749)) +* **mcp:** clarify http auth error ([e26147e](https://github.com/AlexK420/mcpdata/commit/e26147edf9215e2524946a442b5dfc6d4a41cb54)) +* **mcp:** remove deprecated tool schemes ([5627a82](https://github.com/AlexK420/mcpdata/commit/5627a82b2cff281d6b1a786056c4e2bf7a154e2e)) +* **mcp:** update lockfile ([7f2e41d](https://github.com/AlexK420/mcpdata/commit/7f2e41df2d8a8697cc6f9617b34f08e40770685e)) +* **mcp:** upgrade jq-web ([977d40c](https://github.com/AlexK420/mcpdata/commit/977d40c3e6e9a19168d445200509d6364018a7ea)) +* update SDK settings ([62283ae](https://github.com/AlexK420/mcpdata/commit/62283aed01b3bb3bc408946faeb1f62c9ea9864d)) +* use latest @modelcontextprotocol/sdk ([733071a](https://github.com/AlexK420/mcpdata/commit/733071a72ccfacc4edb2387d1f839f32d2b75938)) +* use structured error when code execution tool errors ([9b50e98](https://github.com/AlexK420/mcpdata/commit/9b50e982056247d81e175e5c2a00d14c1e4b1acd)) diff --git a/package.json b/package.json index bad930d..39d1c8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datanewton", - "version": "0.0.1", + "version": "0.1.0", "description": "The official TypeScript library for the Datanewton API", "author": "Datanewton <>", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index fa91624..f38fd0c 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "datanewton-mcp", - "version": "0.0.1", + "version": "0.1.0", "description": "The official MCP Server for the Datanewton API", "author": "Datanewton <>", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index ce57ed3..b82c011 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -21,7 +21,7 @@ export const newMcpServer = () => new McpServer( { name: 'datanewton_api', - version: '0.0.1', + version: '0.1.0', }, { capabilities: { tools: {}, logging: {} } }, ); diff --git a/src/version.ts b/src/version.ts index d74dce8..1baa228 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.0.1'; // x-release-please-version +export const VERSION = '0.1.0'; // x-release-please-version