diff --git a/clients/cli/__tests__/error-handler.test.ts b/clients/cli/__tests__/error-handler.test.ts index fa7bac1dd..88b626214 100644 --- a/clients/cli/__tests__/error-handler.test.ts +++ b/clients/cli/__tests__/error-handler.test.ts @@ -193,8 +193,8 @@ describe("classifyError", () => { expect(envelope.status).toBe(403); }); - it("ignores a JSON-RPC McpError numeric .code (outside the HTTP range) as a status", () => { - // MCP SDK's McpError carries a numeric `.code` that is a JSON-RPC error + it("ignores a JSON-RPC ProtocolError numeric .code (outside the HTTP range) as a status", () => { + // MCP SDK's ProtocolError carries a numeric `.code` that is a JSON-RPC error // code (e.g. -32601 MethodNotFound), NOT an HTTP status. It must not leak // into `status`, nor get misclassified as AUTH_REQUIRED. const err = Object.assign(new Error("Method not found"), { code: -32601 }); diff --git a/clients/cli/package-lock.json b/clients/cli/package-lock.json index eedd0680a..b53941412 100644 --- a/clients/cli/package-lock.json +++ b/clients/cli/package-lock.json @@ -6,10 +6,12 @@ "packages": { "": { "name": "@modelcontextprotocol/inspector-cli", - "version": "2.0.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/server-legacy": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "ajv": "^8.17.1", "atomically": "^2.1.1", @@ -751,18 +753,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@hono/node-server": { - "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -868,43 +858,73 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", + "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", + "@modelcontextprotocol/core": "2.0.0-beta.4", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.4.tgz", + "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.4.tgz", + "integrity": "sha512-pjMZcNEt1dOq0aJCcY3b7w1Ayh+qmQN2xlfTELcGV9yiAjEGIczBPBLWWW0k0zzo5T8kiv15jtKPsWeHGxLvLg==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/server-legacy": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server-legacy/-/server-legacy-2.0.0-beta.4.tgz", + "integrity": "sha512-GaEa7mfjTqevGuz2taFj2fqb3FthxFvWOmoI/wbLmiK1XRnxdYsAaaJG/lgSlA+W6XWJ1+u+6oZ7610TowkjMA==", + "deprecated": "This package is a frozen copy of v1's SSE transport and OAuth Authorization Server helpers for migration purposes only. Use StreamableHTTP from @modelcontextprotocol/server and a dedicated OAuth server in production. Will not receive new features.", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "express-rate-limit": "^8.2.1", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" + "zod": "^4.2.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" + "express": "^4.18.0 || ^5.0.0" }, "peerDependenciesMeta": { - "@cfworker/json-schema": { + "express": { "optional": true - }, - "zod": { - "optional": false } } }, @@ -2267,6 +2287,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -2314,23 +2335,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2414,6 +2418,7 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", + "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -2484,6 +2489,7 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2497,6 +2503,7 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -2619,6 +2626,7 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2648,6 +2656,7 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2657,6 +2666,7 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.6.0" } @@ -2740,6 +2750,7 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2753,13 +2764,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -2769,6 +2782,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -2778,6 +2792,7 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -2794,6 +2809,7 @@ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -2847,7 +2863,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -3055,6 +3072,7 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -3095,6 +3113,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -3223,6 +3242,7 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -3294,6 +3314,7 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -3303,6 +3324,7 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -3327,6 +3349,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3336,6 +3359,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -3360,6 +3384,7 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", + "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3399,6 +3424,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3421,6 +3447,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3433,6 +3460,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", + "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -3440,15 +3468,6 @@ "node": ">= 0.4" } }, - "node_modules/hono": { - "version": "4.12.24", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.24.tgz", - "integrity": "sha512-I36D1s+HgQc55KbhEr4iybfxv/9o1zdpw+XEM6dJa91LqQD0HCoSGdxpRJCZE+aavs87j4V3Ls2OJzq8C/U4iw==", - "license": "MIT", - "engines": { - "node": ">=16.9.0" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3559,6 +3578,7 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } @@ -3590,7 +3610,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/isexe": { "version": "2.0.0", @@ -3699,12 +3720,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -4093,6 +4108,7 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -4102,6 +4118,7 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -4111,6 +4128,7 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -4123,6 +4141,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -4132,6 +4151,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -4218,6 +4238,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -4236,6 +4257,7 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -4271,6 +4293,7 @@ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", + "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -4283,6 +4306,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", + "peer": true, "dependencies": { "wrappy": "1" } @@ -4355,6 +4379,7 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -4383,6 +4408,7 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -4602,6 +4628,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", + "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -4625,6 +4652,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -4646,6 +4674,7 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -4791,6 +4820,7 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -4835,6 +4865,7 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", @@ -4861,6 +4892,7 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", + "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -4907,6 +4939,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", @@ -4926,6 +4959,7 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" @@ -4942,6 +4976,7 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -4960,6 +4995,7 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -5307,6 +5343,7 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", + "peer": true, "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", @@ -5325,6 +5362,7 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -5652,7 +5690,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/yocto-queue": { "version": "0.1.0", @@ -5675,15 +5714,6 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } } } } diff --git a/clients/cli/package.json b/clients/cli/package.json index 89671c500..e3042d708 100644 --- a/clients/cli/package.json +++ b/clients/cli/package.json @@ -31,7 +31,10 @@ "format:check": "prettier --check src __tests__" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/server-legacy": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "ajv": "^8.17.1", "atomically": "^2.1.1", diff --git a/clients/cli/src/cli.ts b/clients/cli/src/cli.ts index 2025947ea..e6b263595 100644 --- a/clients/cli/src/cli.ts +++ b/clients/cli/src/cli.ts @@ -38,12 +38,12 @@ import { writeStoreFile } from "@inspector/core/storage/store-io.js"; import { refreshAuthorization, discoverAuthorizationServerMetadata, -} from "@modelcontextprotocol/sdk/client/auth.js"; +} from "@modelcontextprotocol/client"; import type { OAuthClientInformation, OAuthMetadata, OAuthTokens, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import { CliExitCodeError, EXIT_CODES } from "./error-handler.js"; import { ConsoleNavigation, @@ -67,10 +67,8 @@ import { loadRunnerClientConfig, type RunnerClientConfigOverrides, } from "@inspector/core/client/runner.js"; -import { - LoggingLevelSchema, - type LoggingLevel, -} from "@modelcontextprotocol/sdk/types.js"; +import { type LoggingLevel } from "@modelcontextprotocol/client"; +import { LoggingLevelSchema } from "@modelcontextprotocol/core"; import { readInspectorVersion } from "@inspector/core/node/version.js"; export const validLogLevels: LoggingLevel[] = Object.values( diff --git a/clients/cli/src/error-handler.ts b/clients/cli/src/error-handler.ts index 5578076c3..84b72d65c 100644 --- a/clients/cli/src/error-handler.ts +++ b/clients/cli/src/error-handler.ts @@ -77,7 +77,7 @@ function statusOf(error: unknown): number | undefined { if (typeof e.status === "number") return e.status; if (typeof e.response?.status === "number") return e.response.status; // SDK SseError / StreamableHTTPError expose `.code` as the HTTP status. Guard - // to the HTTP range (100-599) so a JSON-RPC McpError code (e.g. -32601 + // to the HTTP range (100-599) so a JSON-RPC ProtocolError code (e.g. -32601 // MethodNotFound) is not mistaken for an HTTP status and leaked into the // envelope or misclassified as AUTH_REQUIRED. String node codes like // "ENOTFOUND" are already excluded by the numeric check. diff --git a/clients/cli/tsup.config.ts b/clients/cli/tsup.config.ts index 752ab878b..d65d37ec3 100644 --- a/clients/cli/tsup.config.ts +++ b/clients/cli/tsup.config.ts @@ -17,7 +17,13 @@ export default defineConfig({ platform: 'node', // Bundle core source; leave npm deps external. noExternal: [/^@inspector\/core/], - external: ['@napi-rs/keyring', '@modelcontextprotocol/sdk', 'commander', 'pino'], + external: [ + '@napi-rs/keyring', + '@modelcontextprotocol/client', + '@modelcontextprotocol/core', + 'commander', + 'pino', + ], esbuildOptions(options) { options.alias = { '@inspector/core': path.join(repoRoot, 'core'), diff --git a/clients/launcher/package-lock.json b/clients/launcher/package-lock.json index d23208e5b..55f0543cf 100644 --- a/clients/launcher/package-lock.json +++ b/clients/launcher/package-lock.json @@ -6,7 +6,6 @@ "packages": { "": { "name": "@modelcontextprotocol/inspector-launcher", - "version": "2.0.0", "license": "MIT", "dependencies": { "commander": "^13.1.0" @@ -76,6 +75,40 @@ "node": ">=18" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "dev": true, @@ -289,6 +322,25 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, "node_modules/@oxc-project/types": { "version": "0.133.0", "dev": true, @@ -297,6 +349,23 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.0.3", "cpu": [ @@ -312,6 +381,247 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "dev": true, @@ -322,6 +632,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "dev": true, @@ -1393,6 +1714,27 @@ "lightningcss-win32-x64-msvc": "1.32.0" } }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lightningcss-darwin-arm64": { "version": "1.32.0", "cpu": [ @@ -1412,6 +1754,207 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/locate-path": { "version": "6.0.0", "dev": true, @@ -1824,6 +2367,14 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "dev": true, diff --git a/clients/tui/__tests__/App.test.tsx b/clients/tui/__tests__/App.test.tsx index b8efdb7c5..dcf3be47f 100644 --- a/clients/tui/__tests__/App.test.tsx +++ b/clients/tui/__tests__/App.test.tsx @@ -983,9 +983,12 @@ describe("App (OAuth flows)", () => { const r = await mount(oneHttp()); await press(r, ["c"]); await waitUntil(() => h.cb.opts !== null); - await h.cb.opts!.onCallback({ code: "abc" }); + await h.cb.opts!.onCallback({ code: "abc", iss: "https://as.example" }); await tick(); - expect(h.clientSpies.completeOAuthFlow).toHaveBeenCalledWith("abc"); + expect(h.clientSpies.completeOAuthFlow).toHaveBeenCalledWith( + "abc", + "https://as.example", + ); }); it("reports an OAuth callback error during a 401 connect", async () => { diff --git a/clients/tui/__tests__/PromptTestModal.test.tsx b/clients/tui/__tests__/PromptTestModal.test.tsx index c5058257f..d79e77bea 100644 --- a/clients/tui/__tests__/PromptTestModal.test.tsx +++ b/clients/tui/__tests__/PromptTestModal.test.tsx @@ -3,7 +3,7 @@ import { describe, it, expect, vi, afterEach } from "vitest"; import { render } from "ink-testing-library"; import type { InspectorClient } from "@inspector/core/mcp/index.js"; import { AuthRecoveryRequiredError } from "@inspector/core/auth/challenge.js"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; // ScrollView: passthrough so the results JSX actually mounts (and is counted // for coverage) and the imperative ref API exists for the scroll handlers. diff --git a/clients/tui/__tests__/PromptsTab.test.tsx b/clients/tui/__tests__/PromptsTab.test.tsx index c36a8fc00..c9adee62f 100644 --- a/clients/tui/__tests__/PromptsTab.test.tsx +++ b/clients/tui/__tests__/PromptsTab.test.tsx @@ -2,7 +2,7 @@ import React from "react"; import { describe, it, expect, vi } from "vitest"; import { render } from "ink-testing-library"; import type { InspectorClient } from "@inspector/core/mcp/index.js"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; // MUST mock ink-scroll-view: the real ScrollView renders a placeholder minimap // in the non-TTY test env and never mounts its children. This passthrough diff --git a/clients/tui/__tests__/ResourcesTab.test.tsx b/clients/tui/__tests__/ResourcesTab.test.tsx index 1c2211bf9..952b21c85 100644 --- a/clients/tui/__tests__/ResourcesTab.test.tsx +++ b/clients/tui/__tests__/ResourcesTab.test.tsx @@ -2,7 +2,7 @@ import React from "react"; import { describe, it, expect, vi } from "vitest"; import { render } from "ink-testing-library"; import type { InspectorClient } from "@inspector/core/mcp/index.js"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; // MUST mock ink-scroll-view: the real ScrollView renders a placeholder minimap // in the non-TTY test env and never mounts its children. This passthrough diff --git a/clients/tui/__tests__/ToolTestModal.test.tsx b/clients/tui/__tests__/ToolTestModal.test.tsx index db1e558dc..a2c8a8384 100644 --- a/clients/tui/__tests__/ToolTestModal.test.tsx +++ b/clients/tui/__tests__/ToolTestModal.test.tsx @@ -2,7 +2,7 @@ import React from "react"; import { describe, it, expect, vi, afterEach } from "vitest"; import { render } from "ink-testing-library"; import type { InspectorClient } from "@inspector/core/mcp/index.js"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; // ScrollView passthrough so the results JSX actually mounts (and is covered). vi.mock("ink-scroll-view", () => import("./helpers/inkScrollViewMock.js")); diff --git a/clients/tui/__tests__/ToolsTab.test.tsx b/clients/tui/__tests__/ToolsTab.test.tsx index 136fef2ae..0500a6953 100644 --- a/clients/tui/__tests__/ToolsTab.test.tsx +++ b/clients/tui/__tests__/ToolsTab.test.tsx @@ -1,7 +1,7 @@ import React from "react"; import { describe, it, expect, vi } from "vitest"; import { render } from "ink-testing-library"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; // MUST mock ink-scroll-view: the real ScrollView renders a placeholder minimap // in the non-TTY test env and never mounts its children. This passthrough diff --git a/clients/tui/package-lock.json b/clients/tui/package-lock.json index 91c3e9a14..eafc93123 100644 --- a/clients/tui/package-lock.json +++ b/clients/tui/package-lock.json @@ -6,10 +6,10 @@ "packages": { "": { "name": "@modelcontextprotocol/inspector-tui", - "version": "2.0.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "ajv": "^8.17.1", "atomically": "^2.1.1", @@ -991,18 +991,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@hono/node-server": { - "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1119,44 +1107,34 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", + "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", + "@modelcontextprotocol/core": "2.0.0-beta.4", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" + "zod": "^4.2.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.4.tgz", + "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } + "engines": { + "node": ">=20" } }, "node_modules/@napi-rs/keyring": { @@ -2523,19 +2501,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -2575,23 +2540,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-escapes": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", @@ -2718,30 +2666,6 @@ "node": ">=6.0.0" } }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -2818,15 +2742,6 @@ "esbuild": ">=0.18" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2837,35 +2752,6 @@ "node": ">=8" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3043,28 +2929,6 @@ "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3081,41 +2945,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3141,6 +2970,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3201,15 +3031,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3220,26 +3041,6 @@ "node": ">=8" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, "node_modules/electron-to-chromium": { "version": "1.5.376", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz", @@ -3253,15 +3054,6 @@ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -3274,24 +3066,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/es-module-lexer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", @@ -3299,18 +3073,6 @@ "dev": true, "license": "MIT" }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/es-toolkit": { "version": "1.47.0", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.47.0.tgz", @@ -3373,12 +3135,6 @@ "node": ">=6" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", @@ -3642,15 +3398,6 @@ "node": ">=0.10.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/eventsource": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", @@ -3682,67 +3429,6 @@ "node": ">=12.0.0" } }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.2.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3825,27 +3511,6 @@ "node": ">=16.0.0" } }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3896,24 +3561,6 @@ "dev": true, "license": "ISC" }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3929,15 +3576,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3960,43 +3598,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4023,18 +3624,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4045,30 +3634,6 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -4086,15 +3651,6 @@ "hermes-estree": "0.25.1" } }, - "node_modules/hono": { - "version": "4.12.24", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.24.tgz", - "integrity": "sha512-I36D1s+HgQc55KbhEr4iybfxv/9o1zdpw+XEM6dJa91LqQD0HCoSGdxpRJCZE+aavs87j4V3Ls2OJzq8C/U4iw==", - "license": "MIT", - "engines": { - "node": ">=16.9.0" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4102,42 +3658,6 @@ "dev": true, "license": "MIT" }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4197,12 +3717,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, "node_modules/ink": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/ink/-/ink-6.8.0.tgz", @@ -4340,24 +3854,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -4444,12 +3940,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", @@ -4597,12 +4087,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -5009,61 +4493,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -5103,6 +4532,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -5143,15 +4573,6 @@ "dev": true, "license": "MIT" }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/node-releases": { "version": "2.0.48", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", @@ -5166,23 +4587,12 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/obug": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.2.tgz", @@ -5206,27 +4616,6 @@ "node": ">=14.0.0" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -5323,15 +4712,6 @@ "node": ">=6" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/patch-console": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", @@ -5360,16 +4740,6 @@ "node": ">=8" } }, - "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -5579,19 +4949,6 @@ ], "license": "MIT" }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5602,51 +4959,12 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", @@ -5808,22 +5126,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/run-applescript": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", @@ -5845,12 +5147,6 @@ "node": ">=10" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -5870,57 +5166,6 @@ "node": ">=10" } }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5942,78 +5187,6 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -6100,15 +5273,6 @@ "dev": true, "license": "MIT" }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/std-env": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", @@ -6330,15 +5494,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -6961,37 +6116,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", - "license": "MIT", - "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -7044,15 +6168,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -7094,15 +6209,6 @@ "punycode": "^2.1.0" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/vite": { "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", @@ -7411,12 +6517,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, "node_modules/ws": { "version": "8.21.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", @@ -7488,15 +6588,6 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, "node_modules/zod-validation-error": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", diff --git a/clients/tui/package.json b/clients/tui/package.json index e4b644d56..7da8e824f 100644 --- a/clients/tui/package.json +++ b/clients/tui/package.json @@ -26,7 +26,8 @@ "format:check": "prettier --check src __tests__ index.ts tui.tsx dev.ts" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "ajv": "^8.17.1", "atomically": "^2.1.1", diff --git a/clients/tui/src/App.tsx b/clients/tui/src/App.tsx index 66a2584bf..4a43f695f 100644 --- a/clients/tui/src/App.tsx +++ b/clients/tui/src/App.tsx @@ -18,7 +18,7 @@ import type { Prompt, PromptArgument, GetPromptResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { InspectorClient } from "@inspector/core/mcp/index.js"; import { ManagedToolsState, @@ -923,7 +923,7 @@ function App({ resource: | Resource | { - content: import("@modelcontextprotocol/sdk/types.js").ReadResourceResult; + content: import("@modelcontextprotocol/client").ReadResourceResult; }, ) => ( <> diff --git a/clients/tui/src/components/PromptTestModal.tsx b/clients/tui/src/components/PromptTestModal.tsx index dfd284b3d..c48fdd8a0 100644 --- a/clients/tui/src/components/PromptTestModal.tsx +++ b/clients/tui/src/components/PromptTestModal.tsx @@ -3,10 +3,7 @@ import { Box, Text, useInput, type Key } from "ink"; import { Form } from "ink-form"; import { InspectorClient } from "@inspector/core/mcp/index.js"; import { AuthRecoveryRequiredError } from "@inspector/core/auth/challenge.js"; -import type { - Prompt, - GetPromptResult, -} from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt, GetPromptResult } from "@modelcontextprotocol/client"; import { promptArgsToForm } from "../utils/promptArgsToForm.js"; import { ScrollView, type ScrollViewRef } from "ink-scroll-view"; diff --git a/clients/tui/src/components/PromptsTab.tsx b/clients/tui/src/components/PromptsTab.tsx index f2a79d05f..4f90c1a73 100644 --- a/clients/tui/src/components/PromptsTab.tsx +++ b/clients/tui/src/components/PromptsTab.tsx @@ -7,7 +7,7 @@ import type { Prompt, PromptArgument, GetPromptResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { useSelectableList } from "../hooks/useSelectableList.js"; interface PromptsTabProps { diff --git a/clients/tui/src/components/ResourceTestModal.tsx b/clients/tui/src/components/ResourceTestModal.tsx index 8d52ee127..914b4bca6 100644 --- a/clients/tui/src/components/ResourceTestModal.tsx +++ b/clients/tui/src/components/ResourceTestModal.tsx @@ -3,7 +3,7 @@ import { Box, Text, useInput, type Key } from "ink"; import { Form } from "ink-form"; import { InspectorClient } from "@inspector/core/mcp/index.js"; import { AuthRecoveryRequiredError } from "@inspector/core/auth/challenge.js"; -import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import type { ReadResourceResult } from "@modelcontextprotocol/client"; import { uriTemplateToForm } from "../utils/uriTemplateToForm.js"; import { ScrollView, type ScrollViewRef } from "ink-scroll-view"; diff --git a/clients/tui/src/components/ResourcesTab.tsx b/clients/tui/src/components/ResourcesTab.tsx index df976708f..86ba91acf 100644 --- a/clients/tui/src/components/ResourcesTab.tsx +++ b/clients/tui/src/components/ResourcesTab.tsx @@ -6,7 +6,7 @@ import { AuthRecoveryRequiredError } from "@inspector/core/auth/challenge.js"; import type { Resource, ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { useSelectableList } from "../hooks/useSelectableList.js"; interface ResourceTemplate { diff --git a/clients/tui/src/components/ToolTestModal.tsx b/clients/tui/src/components/ToolTestModal.tsx index a556c9a67..5148b93ee 100644 --- a/clients/tui/src/components/ToolTestModal.tsx +++ b/clients/tui/src/components/ToolTestModal.tsx @@ -3,7 +3,7 @@ import { Box, Text, useInput, type Key } from "ink"; import { Form } from "ink-form"; import { InspectorClient } from "@inspector/core/mcp/index.js"; import { AuthRecoveryRequiredError } from "@inspector/core/auth/challenge.js"; -import type { Tool, CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool, CallToolResult } from "@modelcontextprotocol/client"; import type { JsonValue } from "@inspector/core/mcp/index.js"; import { schemaToForm } from "../utils/schemaToForm.js"; import { ScrollView, type ScrollViewRef } from "ink-scroll-view"; diff --git a/clients/tui/src/components/ToolsTab.tsx b/clients/tui/src/components/ToolsTab.tsx index 81dd15589..92eac0f5a 100644 --- a/clients/tui/src/components/ToolsTab.tsx +++ b/clients/tui/src/components/ToolsTab.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef } from "react"; import { Box, Text, useInput, type Key } from "ink"; import { ScrollView, type ScrollViewRef } from "ink-scroll-view"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { useSelectableList } from "../hooks/useSelectableList.js"; interface ToolsTabProps { diff --git a/clients/tui/src/utils/promptArgsToForm.ts b/clients/tui/src/utils/promptArgsToForm.ts index 01da3d3e2..109b5dc13 100644 --- a/clients/tui/src/utils/promptArgsToForm.ts +++ b/clients/tui/src/utils/promptArgsToForm.ts @@ -3,7 +3,7 @@ */ import type { FormStructure, FormSection, FormField } from "ink-form"; -import type { PromptArgument } from "@modelcontextprotocol/sdk/types.js"; +import type { PromptArgument } from "@modelcontextprotocol/client"; /** * Converts prompt arguments array to ink-form structure diff --git a/clients/tui/src/utils/uriTemplateToForm.ts b/clients/tui/src/utils/uriTemplateToForm.ts index b1ec266d0..c8a027e9c 100644 --- a/clients/tui/src/utils/uriTemplateToForm.ts +++ b/clients/tui/src/utils/uriTemplateToForm.ts @@ -3,7 +3,7 @@ */ import type { FormStructure, FormSection, FormField } from "ink-form"; -import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js"; +import { UriTemplate } from "@modelcontextprotocol/client"; /** * Converts a URI Template to ink-form structure diff --git a/clients/tui/tsup.config.ts b/clients/tui/tsup.config.ts index c998221a8..7d4313076 100644 --- a/clients/tui/tsup.config.ts +++ b/clients/tui/tsup.config.ts @@ -24,7 +24,8 @@ export default defineConfig({ 'open', 'commander', 'pino', - '@modelcontextprotocol/sdk', + '@modelcontextprotocol/client', + '@modelcontextprotocol/core', '@napi-rs/keyring', ], esbuildOptions(options) { diff --git a/clients/web/package-lock.json b/clients/web/package-lock.json index c45069043..eeca14daa 100644 --- a/clients/web/package-lock.json +++ b/clients/web/package-lock.json @@ -6,7 +6,6 @@ "packages": { "": { "name": "@modelcontextprotocol/inspector-web", - "version": "2.0.0", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^8.0.0", @@ -17,8 +16,11 @@ "@mantine/form": "^8.3.17", "@mantine/hooks": "^8.3.17", "@mantine/notifications": "^8.3.17", - "@modelcontextprotocol/ext-apps": "^1.7.1", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/ext-apps": "^1.7.4", + "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/server-legacy": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "ajv": "^8.17.1", "atomically": "^2.1.1", @@ -1438,10 +1440,40 @@ "react": ">=16" } }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", + "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.4.tgz", + "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.1.tgz", - "integrity": "sha512-J3WdG1A4JSSKnSWKyU+895dBVYBV2Utgtf7fUsUK45mlkETm53a/1DR6Pm3hUGKqLLQthZLmpxOg8VPzJi/lyg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.4.tgz", + "integrity": "sha512-QQqysE549cf/Y0VabBmAACXhj92EhB3t8yVct2BHbkWiPTFA1S91EqTVjYXXcZEefXU0pmHcdObhsNMcomJIOQ==", "license": "MIT", "workspaces": [ "examples/*" @@ -1472,6 +1504,7 @@ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", + "peer": true, "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", @@ -1507,6 +1540,46 @@ } } }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.4.tgz", + "integrity": "sha512-pjMZcNEt1dOq0aJCcY3b7w1Ayh+qmQN2xlfTELcGV9yiAjEGIczBPBLWWW0k0zzo5T8kiv15jtKPsWeHGxLvLg==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/server-legacy": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server-legacy/-/server-legacy-2.0.0-beta.4.tgz", + "integrity": "sha512-GaEa7mfjTqevGuz2taFj2fqb3FthxFvWOmoI/wbLmiK1XRnxdYsAaaJG/lgSlA+W6XWJ1+u+6oZ7610TowkjMA==", + "deprecated": "This package is a frozen copy of v1's SSE transport and OAuth Authorization Server helpers for migration purposes only. Use StreamableHTTP from @modelcontextprotocol/server and a dedicated OAuth server in production. Will not receive new features.", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "express-rate-limit": "^8.2.1", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "express": "^4.18.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, "node_modules/@napi-rs/keyring": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.3.0.tgz", @@ -3733,6 +3806,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -6070,7 +6144,8 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -10341,6 +10416,7 @@ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", + "peer": true, "peerDependencies": { "zod": "^3.25.28 || ^4" } diff --git a/clients/web/package.json b/clients/web/package.json index 6b9900d9c..b76b8c361 100644 --- a/clients/web/package.json +++ b/clients/web/package.json @@ -41,8 +41,11 @@ "@mantine/form": "^8.3.17", "@mantine/hooks": "^8.3.17", "@mantine/notifications": "^8.3.17", - "@modelcontextprotocol/ext-apps": "^1.7.1", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/ext-apps": "^1.7.4", + "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/server-legacy": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "ajv": "^8.17.1", "atomically": "^2.1.1", diff --git a/clients/web/server/vite-base-config.ts b/clients/web/server/vite-base-config.ts index 75199b37e..3bfe443a5 100644 --- a/clients/web/server/vite-base-config.ts +++ b/clients/web/server/vite-base-config.ts @@ -12,7 +12,7 @@ import { rmSync } from "node:fs"; import { join } from "node:path"; const NODE_ONLY_OPTIMIZE_DEPS_EXCLUDE = [ - "@modelcontextprotocol/sdk/client/stdio.js", + "@modelcontextprotocol/client/stdio", // `atomically` is reached only through `core/storage/store-io.ts`, // which is imported by `core/mcp/remote/node/server.ts` (the Hono // app). The module never lands in the browser graph; excluding it @@ -52,7 +52,7 @@ export function getViteDevOptimizeDeps() { exclude: [...NODE_ONLY_OPTIMIZE_DEPS_EXCLUDE], force: true, ignoreOutdatedRequests: true, - include: ["ajv", "@modelcontextprotocol/sdk/validation/ajv"], + include: ["ajv", "@modelcontextprotocol/client/validators/ajv"], }; } diff --git a/clients/web/src/App.test.tsx b/clients/web/src/App.test.tsx index bf2bdef0d..b44a34de7 100644 --- a/clients/web/src/App.test.tsx +++ b/clients/web/src/App.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; +import { ProtocolErrorCode, ProtocolError } from "@modelcontextprotocol/client"; import { UrlElicitationLoopError } from "@inspector/core/mcp/urlElicitation.js"; import { ToolCallCancelledError } from "@inspector/core/mcp/toolCallCancelledError.js"; import { @@ -1107,8 +1107,8 @@ describe("App task wiring", () => { callTool: ReturnType; } ).callTool.mockRejectedValueOnce( - new McpError( - ErrorCode.UrlElicitationRequired, + new ProtocolError( + ProtocolErrorCode.UrlElicitationRequired, "This request requires browser-based authorization.", ), ); diff --git a/clients/web/src/App.tsx b/clients/web/src/App.tsx index 0dd217cf8..92ad40b24 100644 --- a/clients/web/src/App.tsx +++ b/clients/web/src/App.tsx @@ -19,7 +19,7 @@ import type { ProgressToken, Task, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { InspectorClient } from "@inspector/core/mcp/index.js"; import { getServerType } from "@inspector/core/mcp/config.js"; import type { @@ -451,7 +451,7 @@ function errorMessage(err: unknown): string { return err instanceof Error ? err.message : String(err); } -// Pretty-print a thrown error for the URL-elicitation details modal: an McpError +// Pretty-print a thrown error for the URL-elicitation details modal: a ProtocolError // carries a `code`/`data` worth showing alongside the message, so include them // when present; otherwise fall back to the plain message. function formatErrorDetails(err: unknown): string { @@ -2326,6 +2326,7 @@ function App() { connectStartRef.current = Date.now(); await client.resumeAfterOAuth(params.code, { remoteSessionId: resumeSnapshot?.remoteSessionId, + iss: params.iss, }); } catch (err) { connectStartRef.current = undefined; diff --git a/clients/web/src/components/elements/AnnotationBadge/AnnotationBadge.tsx b/clients/web/src/components/elements/AnnotationBadge/AnnotationBadge.tsx index 03e31e16e..dc51e3331 100644 --- a/clients/web/src/components/elements/AnnotationBadge/AnnotationBadge.tsx +++ b/clients/web/src/components/elements/AnnotationBadge/AnnotationBadge.tsx @@ -1,5 +1,5 @@ import { Badge } from "@mantine/core"; -import type { Role } from "@modelcontextprotocol/sdk/types.js"; +import type { Role } from "@modelcontextprotocol/client"; import { filledBadgeColor } from "../filledBadgeColor"; export type AnnotationFacet = diff --git a/clients/web/src/components/elements/AppRenderer/AppRenderer.stories.tsx b/clients/web/src/components/elements/AppRenderer/AppRenderer.stories.tsx index 2ff8c0eb9..ce398e377 100644 --- a/clients/web/src/components/elements/AppRenderer/AppRenderer.stories.tsx +++ b/clients/web/src/components/elements/AppRenderer/AppRenderer.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { Box } from "@mantine/core"; import type { ReactNode } from "react"; import type { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { AppRenderer, type BridgeFactory } from "./AppRenderer"; diff --git a/clients/web/src/components/elements/AppRenderer/AppRenderer.test.tsx b/clients/web/src/components/elements/AppRenderer/AppRenderer.test.tsx index 440886328..c309e43ae 100644 --- a/clients/web/src/components/elements/AppRenderer/AppRenderer.test.tsx +++ b/clients/web/src/components/elements/AppRenderer/AppRenderer.test.tsx @@ -2,7 +2,7 @@ import { createRef, StrictMode } from "react"; import { act } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import type { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; -import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult, Tool } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { AppRenderer, diff --git a/clients/web/src/components/elements/AppRenderer/AppRenderer.tsx b/clients/web/src/components/elements/AppRenderer/AppRenderer.tsx index 8e061c687..bddcade2b 100644 --- a/clients/web/src/components/elements/AppRenderer/AppRenderer.tsx +++ b/clients/web/src/components/elements/AppRenderer/AppRenderer.tsx @@ -17,7 +17,7 @@ import type { CallToolResult, LoggingMessageNotification, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { currentStyles, currentTheme, @@ -226,7 +226,12 @@ export function AppRenderer({ if (pendingResultRef.current !== null) { const result = pendingResultRef.current; pendingResultRef.current = null; - void bridge.sendToolResult(result); + // ext-apps' AppBridge peers on SDK v1's CallToolResult (whose + // `structuredContent` is typed narrower than v2's). Runtime-compatible; + // cast at this boundary. TODO: drop when ext-apps#702 ships a v2 peer. + void bridge.sendToolResult( + result as Parameters[0], + ); } }, []); diff --git a/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.test.ts b/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.test.ts index 45aa3cde9..13fa9f552 100644 --- a/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.test.ts +++ b/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.test.ts @@ -7,11 +7,8 @@ */ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import type { - Tool, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; -import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { Tool, ReadResourceResult } from "@modelcontextprotocol/client"; +import type { Client } from "@modelcontextprotocol/client"; // --- ext-apps mock ------------------------------------------------------- const bridgeInstances: MockBridge[] = []; diff --git a/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.ts b/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.ts index 4b59b4f61..c2a5ecee4 100644 --- a/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.ts +++ b/clients/web/src/components/elements/AppRenderer/createAppBridgeFactory.ts @@ -8,13 +8,13 @@ import type { McpUiHostCapabilities, McpUiResourceMeta, } from "@modelcontextprotocol/ext-apps/app-bridge"; -import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { Client } from "@modelcontextprotocol/client"; import type { EmbeddedResource, Implementation, ReadResourceResult, ResourceLink, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { approveCspSources, buildSandboxCspPolicy, @@ -212,9 +212,17 @@ export function createAppBridgeFactory( // never mutates the shared HOST_CAPABILITIES constant — each app may // declare its own csp/permissions. const hostCapabilities: McpUiHostCapabilities = { ...HOST_CAPABILITIES }; - const bridge = new AppBridge(client, HOST_INFO, hostCapabilities, { - hostContext: snapshotHostContext(iframe, HOST_AVAILABLE_DISPLAY_MODES), - }); + // ext-apps' `AppBridge` peers on SDK v1's `Client`/`Implementation`; both + // are runtime-compatible with v2's. Cast at this single construction + // boundary. TODO: drop when ext-apps#702 ships a v2 peer release. + const bridge = new AppBridge( + client as unknown as ConstructorParameters[0], + HOST_INFO as unknown as ConstructorParameters[1], + hostCapabilities, + { + hostContext: snapshotHostContext(iframe, HOST_AVAILABLE_DISPLAY_MODES), + }, + ); // The double-iframe proxy posts `sandboxready` once it can receive content. // Read the tool's UI resource and hand its HTML (plus any sandbox/permission @@ -226,7 +234,9 @@ export function createAppBridgeFactory( bridge.addEventListener("sandboxready", () => { void (async () => { try { - const uri = getToolUiResourceUri(tool); + const uri = getToolUiResourceUri( + tool as Parameters[0], + ); if (!uri) return; const result = await deps.readResource(uri); const { html, meta } = extractHtmlAndMeta(result); diff --git a/clients/web/src/components/elements/CapabilityItem/CapabilityItem.tsx b/clients/web/src/components/elements/CapabilityItem/CapabilityItem.tsx index e4a1ba639..34cf5b48e 100644 --- a/clients/web/src/components/elements/CapabilityItem/CapabilityItem.tsx +++ b/clients/web/src/components/elements/CapabilityItem/CapabilityItem.tsx @@ -2,7 +2,7 @@ import { Group, Text } from "@mantine/core"; import type { ClientCapabilities, ServerCapabilities, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; export type CapabilityKey = keyof ServerCapabilities | keyof ClientCapabilities; diff --git a/clients/web/src/components/elements/ContentViewer/ContentViewer.test.tsx b/clients/web/src/components/elements/ContentViewer/ContentViewer.test.tsx index 8c9ee3331..dd777da38 100644 --- a/clients/web/src/components/elements/ContentViewer/ContentViewer.test.tsx +++ b/clients/web/src/components/elements/ContentViewer/ContentViewer.test.tsx @@ -3,7 +3,7 @@ import type { BlobResourceContents, ContentBlock, TextResourceContents, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { ContentViewer } from "./ContentViewer"; diff --git a/clients/web/src/components/elements/ContentViewer/ContentViewer.tsx b/clients/web/src/components/elements/ContentViewer/ContentViewer.tsx index fe46a4894..b17ab0a18 100644 --- a/clients/web/src/components/elements/ContentViewer/ContentViewer.tsx +++ b/clients/web/src/components/elements/ContentViewer/ContentViewer.tsx @@ -4,7 +4,7 @@ import type { BlobResourceContents, ContentBlock, TextResourceContents, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import ReactMarkdown from "react-markdown"; import type { Components } from "react-markdown"; import remarkGfm from "remark-gfm"; diff --git a/clients/web/src/components/elements/LogEntry/LogEntry.tsx b/clients/web/src/components/elements/LogEntry/LogEntry.tsx index 2f1c63f49..5f431e480 100644 --- a/clients/web/src/components/elements/LogEntry/LogEntry.tsx +++ b/clients/web/src/components/elements/LogEntry/LogEntry.tsx @@ -2,7 +2,7 @@ import { Group, Stack, Text } from "@mantine/core"; import type { LoggingLevel, LoggingMessageNotification, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { LogLevelBadge } from "../LogLevelBadge/LogLevelBadge"; import { accessibleTextColor } from "../accessibleTextColor"; diff --git a/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.test.tsx b/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.test.tsx index 283ef54f4..67ab95684 100644 --- a/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.test.tsx +++ b/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { LogLevelBadge } from "./LogLevelBadge"; diff --git a/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.tsx b/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.tsx index 5c587ded9..8e3fa30ce 100644 --- a/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.tsx +++ b/clients/web/src/components/elements/LogLevelBadge/LogLevelBadge.tsx @@ -1,5 +1,5 @@ import { Badge } from "@mantine/core"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { filledBadgeColor } from "../filledBadgeColor"; export interface LogLevelBadgeProps { diff --git a/clients/web/src/components/elements/MessageBubble/MessageBubble.test.tsx b/clients/web/src/components/elements/MessageBubble/MessageBubble.test.tsx index ddabf03cd..6045a2d99 100644 --- a/clients/web/src/components/elements/MessageBubble/MessageBubble.test.tsx +++ b/clients/web/src/components/elements/MessageBubble/MessageBubble.test.tsx @@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest"; import type { PromptMessage, SamplingMessage, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { MessageBubble } from "./MessageBubble"; diff --git a/clients/web/src/components/elements/MessageBubble/MessageBubble.tsx b/clients/web/src/components/elements/MessageBubble/MessageBubble.tsx index 2c43c37d4..1055604e6 100644 --- a/clients/web/src/components/elements/MessageBubble/MessageBubble.tsx +++ b/clients/web/src/components/elements/MessageBubble/MessageBubble.tsx @@ -3,7 +3,7 @@ import type { ContentBlock, PromptMessage, SamplingMessage, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { ContentViewer } from "../ContentViewer/ContentViewer"; export interface MessageBubbleProps { diff --git a/clients/web/src/components/elements/ProgressDisplay/ProgressDisplay.tsx b/clients/web/src/components/elements/ProgressDisplay/ProgressDisplay.tsx index a2bc8fb94..db47a9c74 100644 --- a/clients/web/src/components/elements/ProgressDisplay/ProgressDisplay.tsx +++ b/clients/web/src/components/elements/ProgressDisplay/ProgressDisplay.tsx @@ -1,5 +1,5 @@ import { Group, Progress, Stack, Text } from "@mantine/core"; -import type { ProgressNotification } from "@modelcontextprotocol/sdk/types.js"; +import type { ProgressNotification } from "@modelcontextprotocol/client"; export interface ProgressDisplayProps { params: Pick< diff --git a/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.test.tsx b/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.test.tsx index cab813ebf..ff45b2ccb 100644 --- a/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.test.tsx +++ b/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import type { TaskStatus } from "@modelcontextprotocol/sdk/types.js"; +import type { TaskStatus } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { TaskStatusBadge } from "./TaskStatusBadge"; diff --git a/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.tsx b/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.tsx index 531c8cb3d..40edd25ef 100644 --- a/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.tsx +++ b/clients/web/src/components/elements/TaskStatusBadge/TaskStatusBadge.tsx @@ -1,5 +1,5 @@ import { Badge } from "@mantine/core"; -import type { TaskStatus } from "@modelcontextprotocol/sdk/types.js"; +import type { TaskStatus } from "@modelcontextprotocol/client"; import { filledBadgeColor } from "../filledBadgeColor"; export interface TaskStatusBadgeProps { diff --git a/clients/web/src/components/groups/AppControls/AppControls.stories.tsx b/clients/web/src/components/groups/AppControls/AppControls.stories.tsx index 8a076e0ce..a058b5c29 100644 --- a/clients/web/src/components/groups/AppControls/AppControls.stories.tsx +++ b/clients/web/src/components/groups/AppControls/AppControls.stories.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import type { ComponentProps } from "react"; import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { fn, userEvent, within } from "storybook/test"; import { AppControls } from "./AppControls"; import { SUN_ICON_SVG } from "../../../test/fixtures/storyIcons"; diff --git a/clients/web/src/components/groups/AppControls/AppControls.test.tsx b/clients/web/src/components/groups/AppControls/AppControls.test.tsx index dcf61c53a..ddae4ace0 100644 --- a/clients/web/src/components/groups/AppControls/AppControls.test.tsx +++ b/clients/web/src/components/groups/AppControls/AppControls.test.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { AppControls, type AppControlsProps } from "./AppControls"; diff --git a/clients/web/src/components/groups/AppControls/AppControls.tsx b/clients/web/src/components/groups/AppControls/AppControls.tsx index 2389fbdef..f157cc79d 100644 --- a/clients/web/src/components/groups/AppControls/AppControls.tsx +++ b/clients/web/src/components/groups/AppControls/AppControls.tsx @@ -7,7 +7,7 @@ import { Title, } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator"; import { AppListItem } from "../AppListItem/AppListItem"; import { useScrollMemory } from "../../../hooks/useScrollMemory"; diff --git a/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.stories.tsx b/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.stories.tsx index 9ce42995a..0999ba844 100644 --- a/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.stories.tsx +++ b/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { SUN_ICON_SVG } from "../../../test/fixtures/storyIcons"; import { AppDetailPanel } from "./AppDetailPanel"; diff --git a/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.test.tsx b/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.test.tsx index 9ba090b88..0c40d0ae1 100644 --- a/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.test.tsx +++ b/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { AppDetailPanel } from "./AppDetailPanel"; diff --git a/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.tsx b/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.tsx index 09c25a1f4..7f33fecad 100644 --- a/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.tsx +++ b/clients/web/src/components/groups/AppDetailPanel/AppDetailPanel.tsx @@ -1,9 +1,12 @@ import { Button, Divider, ScrollArea, Stack, Text } from "@mantine/core"; import { MdPlayArrow } from "react-icons/md"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { SchemaForm } from "../SchemaForm/SchemaForm"; import { hasInputFields } from "../../../utils/toolUtils"; -import { hasMissingRequiredFields } from "../../../utils/jsonUtils"; +import { + hasMissingRequiredFields, + toFormSchema, +} from "../../../utils/jsonUtils"; export interface AppDetailPanelProps { tool: Tool; @@ -43,7 +46,12 @@ export function AppDetailPanel({ onOpenApp, }: AppDetailPanelProps) { const { description, inputSchema } = tool; - const hasErrors = hasMissingRequiredFields(inputSchema, formValues); + // Narrow the SDK protocol schema to the form renderer's schema type. A Tool's + // `inputSchema` is always an object per the SDK types, so `toFormSchema` never + // returns null here — the `?? {}` is a defensive fallback that can't be hit. + /* v8 ignore next -- unreachable: Tool.inputSchema is always an object */ + const formSchema = toFormSchema(inputSchema) ?? {}; + const hasErrors = hasMissingRequiredFields(formSchema, formValues); const disabled = isOpening || hasErrors; const hasFields = hasInputFields(tool); @@ -58,7 +66,7 @@ export function AppDetailPanel({ filling required fields. The disabled-when-incomplete gate is on the Open App button below, not on the form itself. */} diff --git a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx index aef6897b9..9fa7e7751 100644 --- a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx +++ b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import type { CreateMessageRequestParams, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { InlineSamplingRequest } from "./InlineSamplingRequest"; diff --git a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.test.tsx b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.test.tsx index 9c1d9dbea..fa05dd88d 100644 --- a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.test.tsx +++ b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.test.tsx @@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event"; import type { CreateMessageRequestParams, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { InlineSamplingRequest } from "./InlineSamplingRequest"; diff --git a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx index 0c453c4a6..b1544c4db 100644 --- a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx +++ b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx @@ -2,7 +2,7 @@ import { Badge, Button, Group, Paper, Stack, Text } from "@mantine/core"; import type { CreateMessageRequestParams, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; export interface InlineSamplingRequestProps { request: CreateMessageRequestParams; diff --git a/clients/web/src/components/groups/LogControls/LogControls.stories.tsx b/clients/web/src/components/groups/LogControls/LogControls.stories.tsx index b516b6b69..90bec5a65 100644 --- a/clients/web/src/components/groups/LogControls/LogControls.stories.tsx +++ b/clients/web/src/components/groups/LogControls/LogControls.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { LogControls } from "./LogControls"; diff --git a/clients/web/src/components/groups/LogControls/LogControls.test.tsx b/clients/web/src/components/groups/LogControls/LogControls.test.tsx index 1d7debdb0..b9970fc06 100644 --- a/clients/web/src/components/groups/LogControls/LogControls.test.tsx +++ b/clients/web/src/components/groups/LogControls/LogControls.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { LogControls } from "./LogControls"; diff --git a/clients/web/src/components/groups/LogControls/LogControls.tsx b/clients/web/src/components/groups/LogControls/LogControls.tsx index f93c1f31d..d4da7a2b9 100644 --- a/clients/web/src/components/groups/LogControls/LogControls.tsx +++ b/clients/web/src/components/groups/LogControls/LogControls.tsx @@ -1,6 +1,6 @@ import { Button, Group, Select, Stack, TextInput, Title } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { FilterToggleButton } from "../../elements/FilterToggleButton/FilterToggleButton"; const LOG_LEVELS: LoggingLevel[] = [ diff --git a/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.stories.tsx b/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.stories.tsx index 3fb4d2c7e..111f54e6f 100644 --- a/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.stories.tsx +++ b/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { LogStreamPanel } from "./LogStreamPanel"; import type { LogEntryData } from "../../elements/LogEntry/LogEntry"; diff --git a/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.test.tsx b/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.test.tsx index c24b91dec..6536ef788 100644 --- a/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.test.tsx +++ b/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { LogStreamPanel } from "./LogStreamPanel"; import type { LogEntryData } from "../../elements/LogEntry/LogEntry"; diff --git a/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.tsx b/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.tsx index c94fb0c8d..b137cd06b 100644 --- a/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.tsx +++ b/clients/web/src/components/groups/LogStreamPanel/LogStreamPanel.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { Button, Group, Paper, Stack, Text, Title } from "@mantine/core"; -import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js"; +import type { LoggingLevel } from "@modelcontextprotocol/client"; import { LogEntry } from "../../elements/LogEntry/LogEntry"; import type { LogEntryData } from "../../elements/LogEntry/LogEntry"; import { diff --git a/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.stories.tsx b/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.stories.tsx index f0bceb896..d16fd5d89 100644 --- a/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.stories.tsx +++ b/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.stories.tsx @@ -4,7 +4,7 @@ import { expect, fn, userEvent, within } from "storybook/test"; import type { CreateMessageRequestParams, ElicitRequestFormParams, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { PendingClientRequestModal, type PendingClientRequestModalProps, diff --git a/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.test.tsx b/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.test.tsx index 02ddf4d71..482373999 100644 --- a/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.test.tsx +++ b/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.test.tsx @@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event"; import type { CreateMessageRequestParams, ElicitRequestFormParams, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { PendingClientRequestModal, diff --git a/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.tsx b/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.tsx index 73ee0369a..ab388181d 100644 --- a/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.tsx +++ b/clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.tsx @@ -5,13 +5,13 @@ import type { CreateMessageResult, ElicitRequestFormParams, ElicitResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { SamplingRequestPanel } from "../SamplingRequestPanel/SamplingRequestPanel"; import { ElicitationFormPanel } from "../ElicitationFormPanel/ElicitationFormPanel"; import { ElicitationUrlPanel } from "../ElicitationUrlPanel/ElicitationUrlPanel"; import { collectSchemaDefaults, - type JsonSchemaType, + type InspectorFormSchema, } from "../../../utils/jsonUtils"; /** @@ -140,7 +140,7 @@ function ElicitationFormModalBody({ // resolveValue, but onChange only writes edited fields). The modal body is // keyed by request id, so this lazy initializer re-runs per request. const [values, setValues] = useState>(() => - collectSchemaDefaults(request.requestedSchema as JsonSchemaType), + collectSchemaDefaults(request.requestedSchema as InspectorFormSchema), ); const { responded, once } = useRespondOnce(); return ( diff --git a/clients/web/src/components/groups/PendingClientRequests/PendingClientRequests.stories.tsx b/clients/web/src/components/groups/PendingClientRequests/PendingClientRequests.stories.tsx index d02cccef8..e47ffcb26 100644 --- a/clients/web/src/components/groups/PendingClientRequests/PendingClientRequests.stories.tsx +++ b/clients/web/src/components/groups/PendingClientRequests/PendingClientRequests.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { ElicitRequest } from "@modelcontextprotocol/sdk/types.js"; +import type { ElicitRequest } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { PendingClientRequests } from "./PendingClientRequests"; import { InlineSamplingRequest } from "../InlineSamplingRequest/InlineSamplingRequest"; diff --git a/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.stories.tsx b/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.stories.tsx index 3cc01bacd..2fbf1fc0e 100644 --- a/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.stories.tsx +++ b/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { PromptArgumentsForm } from "./PromptArgumentsForm"; diff --git a/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.test.tsx b/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.test.tsx index f00afc6c2..931698bb3 100644 --- a/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.test.tsx +++ b/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.test.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { renderWithMantine, screen, diff --git a/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.tsx b/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.tsx index 5d45c2aa1..8f2fe05bb 100644 --- a/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.tsx +++ b/clients/web/src/components/groups/PromptArgumentsForm/PromptArgumentsForm.tsx @@ -9,7 +9,7 @@ import { Title, } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; export interface PromptArgumentsFormProps { prompt: Prompt; diff --git a/clients/web/src/components/groups/PromptControls/PromptControls.stories.tsx b/clients/web/src/components/groups/PromptControls/PromptControls.stories.tsx index 6fb10a947..89ec8d3c8 100644 --- a/clients/web/src/components/groups/PromptControls/PromptControls.stories.tsx +++ b/clients/web/src/components/groups/PromptControls/PromptControls.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { PromptControls } from "./PromptControls"; diff --git a/clients/web/src/components/groups/PromptControls/PromptControls.test.tsx b/clients/web/src/components/groups/PromptControls/PromptControls.test.tsx index 3979c9c58..b01ccd62c 100644 --- a/clients/web/src/components/groups/PromptControls/PromptControls.test.tsx +++ b/clients/web/src/components/groups/PromptControls/PromptControls.test.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { PromptControls, type PromptControlsProps } from "./PromptControls"; diff --git a/clients/web/src/components/groups/PromptControls/PromptControls.tsx b/clients/web/src/components/groups/PromptControls/PromptControls.tsx index a2d392e63..5a8f847f4 100644 --- a/clients/web/src/components/groups/PromptControls/PromptControls.tsx +++ b/clients/web/src/components/groups/PromptControls/PromptControls.tsx @@ -1,6 +1,6 @@ import { Group, ScrollArea, Stack, TextInput, Title } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator"; import { PromptListItem } from "../PromptListItem/PromptListItem"; import { useScrollMemory } from "../../../hooks/useScrollMemory"; diff --git a/clients/web/src/components/groups/PromptListItem/PromptListItem.test.tsx b/clients/web/src/components/groups/PromptListItem/PromptListItem.test.tsx index 9a37c5294..c3df4d40c 100644 --- a/clients/web/src/components/groups/PromptListItem/PromptListItem.test.tsx +++ b/clients/web/src/components/groups/PromptListItem/PromptListItem.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { PromptListItem } from "./PromptListItem"; diff --git a/clients/web/src/components/groups/PromptListItem/PromptListItem.tsx b/clients/web/src/components/groups/PromptListItem/PromptListItem.tsx index 85babf385..20aa73384 100644 --- a/clients/web/src/components/groups/PromptListItem/PromptListItem.tsx +++ b/clients/web/src/components/groups/PromptListItem/PromptListItem.tsx @@ -1,5 +1,5 @@ import { Stack, Text, UnstyledButton } from "@mantine/core"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; export interface PromptListItemProps { prompt: Prompt; diff --git a/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.test.tsx b/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.test.tsx index 839dcd884..23364e8bf 100644 --- a/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.test.tsx +++ b/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { PromptMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { PromptMessage } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { PromptMessagesDisplay } from "./PromptMessagesDisplay"; diff --git a/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.tsx b/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.tsx index cc652b8a9..3947d6d8c 100644 --- a/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.tsx +++ b/clients/web/src/components/groups/PromptMessagesDisplay/PromptMessagesDisplay.tsx @@ -7,7 +7,7 @@ import { Text, Title, } from "@mantine/core"; -import type { PromptMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { PromptMessage } from "@modelcontextprotocol/client"; import { MessageBubble } from "../../elements/MessageBubble/MessageBubble"; export interface PromptMessagesDisplayProps { diff --git a/clients/web/src/components/groups/ResourceControls/ResourceControls.stories.tsx b/clients/web/src/components/groups/ResourceControls/ResourceControls.stories.tsx index 81a581b01..f15d23641 100644 --- a/clients/web/src/components/groups/ResourceControls/ResourceControls.stories.tsx +++ b/clients/web/src/components/groups/ResourceControls/ResourceControls.stories.tsx @@ -1,8 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import type { Resource, - ResourceTemplate, -} from "@modelcontextprotocol/sdk/types.js"; + ResourceTemplateType as ResourceTemplate, +} from "@modelcontextprotocol/client"; import type { InspectorResourceSubscription } from "../../../../../../core/mcp/types.js"; import { expect, fn, within } from "storybook/test"; import { ResourceControls } from "./ResourceControls"; diff --git a/clients/web/src/components/groups/ResourceControls/ResourceControls.test.tsx b/clients/web/src/components/groups/ResourceControls/ResourceControls.test.tsx index c56e231ae..0ee67c466 100644 --- a/clients/web/src/components/groups/ResourceControls/ResourceControls.test.tsx +++ b/clients/web/src/components/groups/ResourceControls/ResourceControls.test.tsx @@ -3,8 +3,8 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; import type { Resource, - ResourceTemplate, -} from "@modelcontextprotocol/sdk/types.js"; + ResourceTemplateType as ResourceTemplate, +} from "@modelcontextprotocol/client"; import type { InspectorResourceSubscription } from "@inspector/core/mcp/types.js"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { diff --git a/clients/web/src/components/groups/ResourceControls/ResourceControls.tsx b/clients/web/src/components/groups/ResourceControls/ResourceControls.tsx index 15f942472..31239774b 100644 --- a/clients/web/src/components/groups/ResourceControls/ResourceControls.tsx +++ b/clients/web/src/components/groups/ResourceControls/ResourceControls.tsx @@ -3,8 +3,8 @@ import { ClearButton } from "../../elements/ClearButton/ClearButton"; import { RiArrowRightSLine } from "react-icons/ri"; import type { Resource, - ResourceTemplate, -} from "@modelcontextprotocol/sdk/types.js"; + ResourceTemplateType as ResourceTemplate, +} from "@modelcontextprotocol/client"; import type { InspectorResourceSubscription } from "../../../../../../core/mcp/types.js"; import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator"; import { ListToggle } from "../../elements/ListToggle/ListToggle"; diff --git a/clients/web/src/components/groups/ResourceLink/ResourceLink.stories.tsx b/clients/web/src/components/groups/ResourceLink/ResourceLink.stories.tsx index a1dbac366..655a5e7ae 100644 --- a/clients/web/src/components/groups/ResourceLink/ResourceLink.stories.tsx +++ b/clients/web/src/components/groups/ResourceLink/ResourceLink.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import type { ReadResourceResult } from "@modelcontextprotocol/client"; import { expect, userEvent, waitFor, within } from "storybook/test"; import { ResourceLink } from "./ResourceLink"; diff --git a/clients/web/src/components/groups/ResourceLink/ResourceLink.test.tsx b/clients/web/src/components/groups/ResourceLink/ResourceLink.test.tsx index 50a4f13b8..731c4a9a9 100644 --- a/clients/web/src/components/groups/ResourceLink/ResourceLink.test.tsx +++ b/clients/web/src/components/groups/ResourceLink/ResourceLink.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import type { ReadResourceResult } from "@modelcontextprotocol/client"; import { renderWithMantine, screen, diff --git a/clients/web/src/components/groups/ResourceLink/ResourceLink.tsx b/clients/web/src/components/groups/ResourceLink/ResourceLink.tsx index 1430c46e9..d5ca53a7b 100644 --- a/clients/web/src/components/groups/ResourceLink/ResourceLink.tsx +++ b/clients/web/src/components/groups/ResourceLink/ResourceLink.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { Alert, Card, Collapse, ScrollArea, Stack, Text } from "@mantine/core"; -import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import type { ReadResourceResult } from "@modelcontextprotocol/client"; import { ContentViewer } from "../../elements/ContentViewer/ContentViewer"; import { ExpandToggle } from "../../elements/ExpandToggle/ExpandToggle"; import { ResourceLinkInfo } from "../../elements/ResourceLinkInfo/ResourceLinkInfo"; diff --git a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.test.tsx b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.test.tsx index e5b749397..faf25a11e 100644 --- a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.test.tsx +++ b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { ResourceListItem } from "./ResourceListItem"; diff --git a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx index 3dbdf99be..9c2a768e0 100644 --- a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx +++ b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx @@ -1,8 +1,8 @@ import { Text, UnstyledButton } from "@mantine/core"; import type { Resource, - ResourceTemplate, -} from "@modelcontextprotocol/sdk/types.js"; + ResourceTemplateType as ResourceTemplate, +} from "@modelcontextprotocol/client"; export interface ResourceListItemProps { resource: Resource | ResourceTemplate; diff --git a/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.test.tsx b/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.test.tsx index 23e1070ba..35cafff82 100644 --- a/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.test.tsx +++ b/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.test.tsx @@ -4,7 +4,7 @@ import type { BlobResourceContents, Resource, TextResourceContents, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { ResourcePreviewPanel } from "./ResourcePreviewPanel"; diff --git a/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.tsx b/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.tsx index 55279024e..39630e89d 100644 --- a/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.tsx +++ b/clients/web/src/components/groups/ResourcePreviewPanel/ResourcePreviewPanel.tsx @@ -13,7 +13,7 @@ import type { BlobResourceContents, Resource, TextResourceContents, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { accessibleTextColor } from "../../elements/accessibleTextColor"; import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge"; import { ContentViewer } from "../../elements/ContentViewer/ContentViewer"; diff --git a/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.test.tsx b/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.test.tsx index 0f534c1be..596a9340f 100644 --- a/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.test.tsx +++ b/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { ResourceTemplatePanel } from "./ResourceTemplatePanel"; diff --git a/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.tsx b/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.tsx index 4a15467ae..af4d94229 100644 --- a/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.tsx +++ b/clients/web/src/components/groups/ResourceTemplatePanel/ResourceTemplatePanel.tsx @@ -10,7 +10,7 @@ import { } from "@mantine/core"; import { accessibleTextColor } from "../../elements/accessibleTextColor"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge"; import { CopyButton } from "../../elements/CopyButton/CopyButton"; diff --git a/clients/web/src/components/groups/RootsTable/RootsTable.stories.tsx b/clients/web/src/components/groups/RootsTable/RootsTable.stories.tsx index 020342c32..deec828c3 100644 --- a/clients/web/src/components/groups/RootsTable/RootsTable.stories.tsx +++ b/clients/web/src/components/groups/RootsTable/RootsTable.stories.tsx @@ -1,4 +1,4 @@ -import type { Root } from "@modelcontextprotocol/sdk/types.js"; +import type { Root } from "@modelcontextprotocol/client"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { fn } from "storybook/test"; import { RootsTable } from "./RootsTable"; diff --git a/clients/web/src/components/groups/RootsTable/RootsTable.test.tsx b/clients/web/src/components/groups/RootsTable/RootsTable.test.tsx index f9a80e109..a8903eeea 100644 --- a/clients/web/src/components/groups/RootsTable/RootsTable.test.tsx +++ b/clients/web/src/components/groups/RootsTable/RootsTable.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Root } from "@modelcontextprotocol/sdk/types.js"; +import type { Root } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { RootsTable } from "./RootsTable"; diff --git a/clients/web/src/components/groups/RootsTable/RootsTable.tsx b/clients/web/src/components/groups/RootsTable/RootsTable.tsx index 9d1069bab..5976817f5 100644 --- a/clients/web/src/components/groups/RootsTable/RootsTable.tsx +++ b/clients/web/src/components/groups/RootsTable/RootsTable.tsx @@ -11,7 +11,7 @@ import { Title, } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { Root } from "@modelcontextprotocol/sdk/types.js"; +import type { Root } from "@modelcontextprotocol/client"; export type RootDraft = { name: string; uri: string }; diff --git a/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.stories.tsx b/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.stories.tsx index 8767971bc..aee46e546 100644 --- a/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.stories.tsx +++ b/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import type { CreateMessageRequestParams, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { SamplingRequestPanel } from "./SamplingRequestPanel"; diff --git a/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.test.tsx b/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.test.tsx index 03661f0bc..d05803c1b 100644 --- a/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.test.tsx +++ b/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.test.tsx @@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event"; import type { CreateMessageRequestParams, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { SamplingRequestPanel } from "./SamplingRequestPanel"; diff --git a/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.tsx b/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.tsx index e656df0ed..0ba5cc9b2 100644 --- a/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.tsx +++ b/clients/web/src/components/groups/SamplingRequestPanel/SamplingRequestPanel.tsx @@ -16,7 +16,7 @@ import { ClearButton } from "../../elements/ClearButton/ClearButton"; import type { CreateMessageRequestParams, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { MessageBubble } from "../../elements/MessageBubble/MessageBubble"; export interface SamplingRequestPanelProps { diff --git a/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx b/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx index 6437f8fac..9d3835948 100644 --- a/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx +++ b/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { JsonSchemaType } from "../../../utils/jsonUtils"; +import type { InspectorFormSchema } from "../../../utils/jsonUtils"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { SchemaForm } from "./SchemaForm"; @@ -8,7 +8,7 @@ describe("SchemaForm", () => { it("renders a string TextInput and propagates onChange", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { name: { type: "string", title: "Name" }, @@ -26,7 +26,7 @@ describe("SchemaForm", () => { it("renders a Number/Integer field and propagates a numeric value", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { count: { type: "integer", title: "Count", minimum: 0, maximum: 100 }, @@ -47,7 +47,7 @@ describe("SchemaForm", () => { it("renders a checkbox for boolean fields and toggles on click", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { enabled: { type: "boolean", title: "Enabled" }, @@ -63,7 +63,7 @@ describe("SchemaForm", () => { it("renders an enum Select with the supplied options", () => { const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { format: { @@ -87,7 +87,7 @@ describe("SchemaForm", () => { it("invokes onChange when an enum Select option is chosen", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { format: { @@ -112,7 +112,7 @@ describe("SchemaForm", () => { it("clears a string field via its Clear button", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { name: { type: "string", title: "Name" }, @@ -133,7 +133,7 @@ describe("SchemaForm", () => { it("passes undefined to onChange when a number field is cleared", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { count: { type: "integer", title: "Count" }, @@ -153,7 +153,7 @@ describe("SchemaForm", () => { it("falls back to empty/const labels for oneOf items missing const and title", () => { const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { choice: { @@ -173,7 +173,7 @@ describe("SchemaForm", () => { it("falls back to empty/const labels for anyOf items missing const and title", () => { const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { tags: { @@ -195,7 +195,7 @@ describe("SchemaForm", () => { it("renders an oneOf Select using titles for labels", () => { const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { size: { @@ -218,7 +218,7 @@ describe("SchemaForm", () => { it("invokes onChange when a oneOf Select option is chosen", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { size: { @@ -246,7 +246,7 @@ describe("SchemaForm", () => { it("renders a MultiSelect for array with anyOf items and invokes onChange when an option is selected", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { tags: { @@ -277,7 +277,7 @@ describe("SchemaForm", () => { it("renders a MultiSelect for an array of enum items and invokes onChange when an option is selected", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { instruments: { @@ -306,7 +306,7 @@ describe("SchemaForm", () => { it("uses enumNames for enum-array option labels and the raw value on change", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { sizes: { @@ -336,7 +336,7 @@ describe("SchemaForm", () => { it("renders nested object fields recursively", () => { const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { address: { @@ -360,7 +360,7 @@ describe("SchemaForm", () => { it("propagates nested object changes back to top-level onChange", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { address: { @@ -383,7 +383,7 @@ describe("SchemaForm", () => { it("falls back to a JsonInput for complex/unsupported schemas", () => { const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { config: { @@ -406,7 +406,7 @@ describe("SchemaForm", () => { it("invokes onChange via JsonInput when valid JSON is pasted", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { config: { type: "array", title: "Config" }, @@ -426,7 +426,7 @@ describe("SchemaForm", () => { it("falls back to passing raw string to onChange when JSON is invalid in JsonInput", async () => { const user = userEvent.setup(); const onChange = vi.fn(); - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { config: { type: "array", title: "Config" }, @@ -443,7 +443,7 @@ describe("SchemaForm", () => { }); it("uses default values when value is undefined", () => { - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { name: { type: "string", title: "Name", default: "Alice" }, @@ -456,7 +456,7 @@ describe("SchemaForm", () => { }); it("respects the disabled prop on inputs", () => { - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { name: { type: "string", title: "Name" }, @@ -476,7 +476,7 @@ describe("SchemaForm", () => { }); it("uses field name when title is missing", () => { - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { rawField: { type: "string" }, @@ -489,7 +489,7 @@ describe("SchemaForm", () => { }); it("renders nothing inside the form when properties are missing", () => { - const schema: JsonSchemaType = { type: "object" }; + const schema: InspectorFormSchema = { type: "object" }; const { container } = renderWithMantine( , ); diff --git a/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx b/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx index ad6e9a0be..b7f75021e 100644 --- a/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx +++ b/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx @@ -9,7 +9,7 @@ import { TextInput, } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { JsonSchemaType } from "../../../utils/jsonUtils"; +import type { InspectorFormSchema } from "../../../utils/jsonUtils"; const FieldLabel = Text.withProps({ fw: 500, @@ -26,20 +26,23 @@ function serializeJson(value: unknown): string { } export interface SchemaFormProps { - schema: JsonSchemaType; + schema: InspectorFormSchema; values: Record; onChange: (values: Record) => void; disabled?: boolean; } -function getDefaultValue(fieldSchema: JsonSchemaType): unknown { +function getDefaultValue(fieldSchema: InspectorFormSchema): unknown { if (fieldSchema.default !== undefined) { return fieldSchema.default; } return undefined; } -function resolveValue(value: unknown, fieldSchema: JsonSchemaType): unknown { +function resolveValue( + value: unknown, + fieldSchema: InspectorFormSchema, +): unknown { if (value !== undefined) { return value; } @@ -59,7 +62,7 @@ export function SchemaForm({ onChange({ ...values, [fieldName]: fieldValue }); } - function renderField(fieldName: string, fieldSchema: JsonSchemaType) { + function renderField(fieldName: string, fieldSchema: InspectorFormSchema) { const isRequired = requiredFields.includes(fieldName); const label = fieldSchema.title ?? fieldName; const description = fieldSchema.description; diff --git a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx index 1e862dcb7..a3fef83e6 100644 --- a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx +++ b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx @@ -17,7 +17,7 @@ import type { ServerType, } from "@inspector/core/mcp/types.js"; import { isOAuthCapableServerType } from "@inspector/core/mcp/config.js"; -import type { Root } from "@modelcontextprotocol/sdk/types.js"; +import type { Root } from "@modelcontextprotocol/client"; export type ServerSettingsSection = | "options" diff --git a/clients/web/src/components/groups/TaskCard/TaskCard.stories.tsx b/clients/web/src/components/groups/TaskCard/TaskCard.stories.tsx index b11bc1b77..29c334ee5 100644 --- a/clients/web/src/components/groups/TaskCard/TaskCard.stories.tsx +++ b/clients/web/src/components/groups/TaskCard/TaskCard.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { TaskCard } from "./TaskCard"; diff --git a/clients/web/src/components/groups/TaskCard/TaskCard.test.tsx b/clients/web/src/components/groups/TaskCard/TaskCard.test.tsx index de261d693..8f8d5a6c6 100644 --- a/clients/web/src/components/groups/TaskCard/TaskCard.test.tsx +++ b/clients/web/src/components/groups/TaskCard/TaskCard.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { TaskCard } from "./TaskCard"; diff --git a/clients/web/src/components/groups/TaskCard/TaskCard.tsx b/clients/web/src/components/groups/TaskCard/TaskCard.tsx index df765b976..5edb57fa9 100644 --- a/clients/web/src/components/groups/TaskCard/TaskCard.tsx +++ b/clients/web/src/components/groups/TaskCard/TaskCard.tsx @@ -8,10 +8,7 @@ import { Stack, Text, } from "@mantine/core"; -import type { - ProgressNotification, - Task, -} from "@modelcontextprotocol/sdk/types.js"; +import type { ProgressNotification, Task } from "@modelcontextprotocol/client"; import { ContentViewer } from "../../elements/ContentViewer/ContentViewer"; import { ExpandToggle } from "../../elements/ExpandToggle/ExpandToggle"; import { ProgressDisplay } from "../../elements/ProgressDisplay/ProgressDisplay"; diff --git a/clients/web/src/components/groups/TaskControls/TaskControls.tsx b/clients/web/src/components/groups/TaskControls/TaskControls.tsx index 66afb97d3..f784221c7 100644 --- a/clients/web/src/components/groups/TaskControls/TaskControls.tsx +++ b/clients/web/src/components/groups/TaskControls/TaskControls.tsx @@ -1,6 +1,6 @@ import { Button, Group, Select, Stack, TextInput, Title } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { TaskStatus } from "@modelcontextprotocol/sdk/types.js"; +import type { TaskStatus } from "@modelcontextprotocol/client"; const STATUS_OPTIONS: TaskStatus[] = [ "working", diff --git a/clients/web/src/components/groups/TaskListPanel/TaskListPanel.stories.tsx b/clients/web/src/components/groups/TaskListPanel/TaskListPanel.stories.tsx index 2a6efa56b..974c83913 100644 --- a/clients/web/src/components/groups/TaskListPanel/TaskListPanel.stories.tsx +++ b/clients/web/src/components/groups/TaskListPanel/TaskListPanel.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { fn } from "storybook/test"; import { TaskListPanel } from "./TaskListPanel"; diff --git a/clients/web/src/components/groups/TaskListPanel/TaskListPanel.test.tsx b/clients/web/src/components/groups/TaskListPanel/TaskListPanel.test.tsx index 81c4b769e..906417ed7 100644 --- a/clients/web/src/components/groups/TaskListPanel/TaskListPanel.test.tsx +++ b/clients/web/src/components/groups/TaskListPanel/TaskListPanel.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { TaskListPanel } from "./TaskListPanel"; diff --git a/clients/web/src/components/groups/TaskListPanel/TaskListPanel.tsx b/clients/web/src/components/groups/TaskListPanel/TaskListPanel.tsx index 6097dda6c..278f8e433 100644 --- a/clients/web/src/components/groups/TaskListPanel/TaskListPanel.tsx +++ b/clients/web/src/components/groups/TaskListPanel/TaskListPanel.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from "react"; import { Button, Group, Paper, Stack, Text, Title } from "@mantine/core"; -import type { Task, TaskStatus } from "@modelcontextprotocol/sdk/types.js"; +import type { Task, TaskStatus } from "@modelcontextprotocol/client"; import { TaskCard } from "../TaskCard/TaskCard"; import type { TaskProgress } from "../TaskCard/TaskCard"; import { EmbeddableScrollArea } from "../../elements/EmbeddableScrollArea/EmbeddableScrollArea"; diff --git a/clients/web/src/components/groups/ToolControls/ToolControls.test.tsx b/clients/web/src/components/groups/ToolControls/ToolControls.test.tsx index 95870109f..a5c98ee96 100644 --- a/clients/web/src/components/groups/ToolControls/ToolControls.test.tsx +++ b/clients/web/src/components/groups/ToolControls/ToolControls.test.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { ToolControls, type ToolControlsProps } from "./ToolControls"; diff --git a/clients/web/src/components/groups/ToolControls/ToolControls.tsx b/clients/web/src/components/groups/ToolControls/ToolControls.tsx index 1143d503d..1065fd7c6 100644 --- a/clients/web/src/components/groups/ToolControls/ToolControls.tsx +++ b/clients/web/src/components/groups/ToolControls/ToolControls.tsx @@ -1,6 +1,6 @@ import { Group, ScrollArea, Stack, TextInput, Title } from "@mantine/core"; import { ClearButton } from "../../elements/ClearButton/ClearButton"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator"; import { ToolListItem } from "../ToolListItem/ToolListItem"; import { useScrollMemory } from "../../../hooks/useScrollMemory"; diff --git a/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.stories.tsx b/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.stories.tsx index e1838cb13..53071818f 100644 --- a/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.stories.tsx +++ b/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { expect, fn, userEvent, within } from "storybook/test"; import { ToolDetailPanel } from "./ToolDetailPanel"; diff --git a/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.test.tsx b/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.test.tsx index 21c2b52bf..ac9f94fbe 100644 --- a/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.test.tsx +++ b/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; import userEvent from "@testing-library/user-event"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { renderWithMantine, screen } from "../../../test/renderWithMantine"; import { ToolDetailPanel } from "./ToolDetailPanel"; diff --git a/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.tsx b/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.tsx index 36a9703c1..7fe05ee54 100644 --- a/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.tsx +++ b/clients/web/src/components/groups/ToolDetailPanel/ToolDetailPanel.tsx @@ -16,8 +16,9 @@ import type { ProgressNotification, Tool, ToolAnnotations, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { resolveDisplayLabel } from "../../../utils/toolUtils"; +import { toFormSchema } from "../../../utils/jsonUtils"; import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge"; import { ProgressDisplay } from "../../elements/ProgressDisplay/ProgressDisplay"; import { SchemaForm } from "../SchemaForm/SchemaForm"; @@ -160,6 +161,8 @@ export function ToolDetailPanel({ onCancel, }: ToolDetailPanelProps) { const { name, title, description, icons, annotations, inputSchema } = tool; + // Narrow the SDK protocol schema to the form renderer's schema type. + const formSchema = toFormSchema(inputSchema) ?? {}; const iconSrc = icons?.[0]?.src; // Descriptions are shown by default (most are short); the chevron lets the @@ -240,7 +243,7 @@ export function ToolDetailPanel({ = {}; /* v8 ignore next -- unreachable: onSelectTool always names a tool in the list */ - if (tool) formValues = collectSchemaDefaults(tool.inputSchema); + if (tool) + formValues = collectSchemaDefaults( + toFormSchema(tool.inputSchema) ?? {}, + ); onUiChange({ ...ui, selectedToolName: name, diff --git a/clients/web/src/components/views/InspectorView/InspectorView.stories.tsx b/clients/web/src/components/views/InspectorView/InspectorView.stories.tsx index a75f17f90..3af887290 100644 --- a/clients/web/src/components/views/InspectorView/InspectorView.stories.tsx +++ b/clients/web/src/components/views/InspectorView/InspectorView.stories.tsx @@ -2,10 +2,10 @@ import type { InitializeResult, Prompt, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, Task, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; import type { FetchRequestEntry, diff --git a/clients/web/src/components/views/InspectorView/InspectorView.test.tsx b/clients/web/src/components/views/InspectorView/InspectorView.test.tsx index 5499d4bb6..eb770ee8f 100644 --- a/clients/web/src/components/views/InspectorView/InspectorView.test.tsx +++ b/clients/web/src/components/views/InspectorView/InspectorView.test.tsx @@ -8,7 +8,7 @@ import type { ServerCapabilities, Task, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; import type { ServerEntry } from "@inspector/core/mcp/types.js"; import { diff --git a/clients/web/src/components/views/InspectorView/InspectorView.tsx b/clients/web/src/components/views/InspectorView/InspectorView.tsx index 3a36b519d..28c643130 100644 --- a/clients/web/src/components/views/InspectorView/InspectorView.tsx +++ b/clients/web/src/components/views/InspectorView/InspectorView.tsx @@ -22,10 +22,10 @@ import type { Prompt, ReadResourceResult, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, Task, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { ConnectionStatus, FetchRequestEntry, @@ -91,7 +91,7 @@ import { MonitoringScreen } from "../../groups/MonitoringScreen/MonitoringScreen import { ResizeHandle } from "../../elements/ResizeHandle/ResizeHandle"; import { getServerType } from "@inspector/core/mcp/config.js"; import { INSPECTOR_SERVERS_TAB } from "../../../utils/inspectorTabs"; -import { collectSchemaDefaults } from "../../../utils/jsonUtils"; +import { collectSchemaDefaults, toFormSchema } from "../../../utils/jsonUtils"; import { MONITOR_COLUMN_ANIM_MS } from "./monitorColumnAnimation"; const SORT_DEFAULT: SortDirection = "newest-first"; @@ -913,7 +913,7 @@ export function InspectorView({ // fails, and Open App is silently disabled — an automated driver's click // then no-ops and the iframe-wait spins forever. const formValues = { - ...collectSchemaDefaults(target.inputSchema), + ...collectSchemaDefaults(toFormSchema(target.inputSchema) ?? {}), ...deepLink.appArgs, }; // Seed the selection directly rather than routing through diff --git a/clients/web/src/test/core/apps.test.ts b/clients/web/src/test/core/apps.test.ts index 5e518a5a2..f97d3620d 100644 --- a/clients/web/src/test/core/apps.test.ts +++ b/clients/web/src/test/core/apps.test.ts @@ -1,8 +1,5 @@ import { describe, it, expect } from "vitest"; -import type { - ReadResourceResult, - Tool, -} from "@modelcontextprotocol/sdk/types.js"; +import type { ReadResourceResult, Tool } from "@modelcontextprotocol/client"; import { extractAppInfo, getAppResourceUri, diff --git a/clients/web/src/test/core/auth/ema/emaFlow.test.ts b/clients/web/src/test/core/auth/ema/emaFlow.test.ts index 25ff3b78b..2b71f4840 100644 --- a/clients/web/src/test/core/auth/ema/emaFlow.test.ts +++ b/clients/web/src/test/core/auth/ema/emaFlow.test.ts @@ -381,6 +381,7 @@ describe("emaFlow", () => { const tokens = await completeEmaIdpAuthorizationAndMint( config, "auth-code", + IDP_ISSUER, ); expect(tokens.access_token).toBe("resource-access-token"); @@ -415,7 +416,7 @@ describe("emaFlow", () => { await startEmaIdpAuthorization(config); await expect( - completeEmaIdpAuthorizationAndMint(config, "auth-code"), + completeEmaIdpAuthorizationAndMint(config, "auth-code", IDP_ISSUER), ).rejects.toThrow(/EMA legs 2–3 \(resource token mint\)/); }); @@ -480,7 +481,7 @@ describe("emaFlow", () => { }); await expect( - completeEmaIdpAuthorizationAndMint(config, "auth-code"), + completeEmaIdpAuthorizationAndMint(config, "auth-code", IDP_ISSUER), ).rejects.toThrow( "EMA leg 1 (IdP authorization code exchange): leg1-non-error", ); diff --git a/clients/web/src/test/core/auth/ema/idpOidc.test.ts b/clients/web/src/test/core/auth/ema/idpOidc.test.ts index 1c6d73f30..1f9fec626 100644 --- a/clients/web/src/test/core/auth/ema/idpOidc.test.ts +++ b/clients/web/src/test/core/auth/ema/idpOidc.test.ts @@ -390,6 +390,7 @@ describe("completeIdpOidcAuthorization", () => { const result = await completeIdpOidcAuthorization({ idp, authorizationCode: "auth-code-xyz", + iss: IDP_ISSUER, redirectUrl: "http://127.0.0.1:3000/oauth/callback", storage, fetchFn, @@ -465,6 +466,7 @@ describe("completeIdpOidcAuthorization", () => { const result = await completeIdpOidcAuthorization({ idp, authorizationCode: "code", + iss: IDP_ISSUER, redirectUrl: "http://127.0.0.1:3000/oauth/callback", storage, fetchFn, @@ -498,10 +500,67 @@ describe("completeIdpOidcAuthorization", () => { completeIdpOidcAuthorization({ idp, authorizationCode: "code", + iss: IDP_ISSUER, redirectUrl: "http://127.0.0.1:3000/oauth/callback", storage, fetchFn, }), ).rejects.toThrow("IdP token response did not include an ID Token"); }); + + // Regression: the IdP metadata advertises + // `authorization_response_iss_parameter_supported`, so the SDK enforces + // RFC 9207 §2.4 and rejects the exchange unless the callback `iss` is + // forwarded. Dropping `iss` anywhere between the callback and + // `exchangeAuthorization` breaks EMA leg 1 against every real IdP. + it("forwards the callback iss to the token exchange (RFC 9207)", async () => { + storage = buildStorage(); + const idToken = jwtWithExp(Math.floor(Date.now() / 1000) + 3600); + const fetchFn = vi.fn( + async () => + new Response( + JSON.stringify({ + id_token: idToken, + access_token: "at", + token_type: "Bearer", + }), + ), + ); + + await expect( + completeIdpOidcAuthorization({ + idp, + authorizationCode: "code", + redirectUrl: "http://127.0.0.1:3000/oauth/callback", + storage, + fetchFn, + }), + ).rejects.toThrow(/Issuer mismatch/); + + const result = await completeIdpOidcAuthorization({ + idp, + authorizationCode: "code", + iss: IDP_ISSUER, + redirectUrl: "http://127.0.0.1:3000/oauth/callback", + storage, + fetchFn, + }); + expect(result.idToken).toBe(idToken); + }); + + it("rejects a callback iss from a different issuer (mix-up defense)", async () => { + storage = buildStorage(); + const fetchFn = vi.fn(); + await expect( + completeIdpOidcAuthorization({ + idp, + authorizationCode: "code", + iss: "https://evil.example.com", + redirectUrl: "http://127.0.0.1:3000/oauth/callback", + storage, + fetchFn, + }), + ).rejects.toThrow(/Issuer mismatch/); + expect(fetchFn).not.toHaveBeenCalled(); + }); }); diff --git a/clients/web/src/test/core/auth/ema/resourceContext.test.ts b/clients/web/src/test/core/auth/ema/resourceContext.test.ts index c89c1345d..7d218699c 100644 --- a/clients/web/src/test/core/auth/ema/resourceContext.test.ts +++ b/clients/web/src/test/core/auth/ema/resourceContext.test.ts @@ -4,7 +4,7 @@ import { discoverEmaResourceContext, discoverResourceAsMetadata, } from "@inspector/core/auth/ema/resourceContext.js"; -import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/client"; import { minimalOAuthAsMetadata } from "../../../integration/mcp/ema-mock-servers.js"; const SERVER_URL = "http://127.0.0.1:9999/mcp"; diff --git a/clients/web/src/test/core/auth/ema/tokenEndpoint.test.ts b/clients/web/src/test/core/auth/ema/tokenEndpoint.test.ts index d1be1fe6a..bc5ee4449 100644 --- a/clients/web/src/test/core/auth/ema/tokenEndpoint.test.ts +++ b/clients/web/src/test/core/auth/ema/tokenEndpoint.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi } from "vitest"; import type { OAuthClientInformation, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import { parseOAuthTokenErrorResponse, postOAuthTokenRequest, diff --git a/clients/web/src/test/core/auth/ema/transportProvider.test.ts b/clients/web/src/test/core/auth/ema/transportProvider.test.ts index 7f6b84e9c..cde4faa74 100644 --- a/clients/web/src/test/core/auth/ema/transportProvider.test.ts +++ b/clients/web/src/test/core/auth/ema/transportProvider.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthTokens } from "@modelcontextprotocol/client"; import type { BaseOAuthClientProvider } from "@inspector/core/auth/providers.js"; import type { EmaFlowConfig } from "@inspector/core/auth/ema/emaFlow.js"; import { EmaTransportOAuthProvider } from "@inspector/core/auth/ema/transportProvider.js"; diff --git a/clients/web/src/test/core/auth/mcpAuth.test.ts b/clients/web/src/test/core/auth/mcpAuth.test.ts index 79af5d79f..a1a8717c8 100644 --- a/clients/web/src/test/core/auth/mcpAuth.test.ts +++ b/clients/web/src/test/core/auth/mcpAuth.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; const { sdkAuth, @@ -17,11 +17,9 @@ const { isHttpsUrl: vi.fn(), })); -vi.mock("@modelcontextprotocol/sdk/client/auth.js", async (importOriginal) => { +vi.mock("@modelcontextprotocol/client", async (importOriginal) => { const actual = - await importOriginal< - typeof import("@modelcontextprotocol/sdk/client/auth.js") - >(); + await importOriginal(); return { ...actual, auth: sdkAuth, @@ -34,7 +32,7 @@ vi.mock("@modelcontextprotocol/sdk/client/auth.js", async (importOriginal) => { }); import { mcpAuth } from "@inspector/core/auth/mcpAuth.js"; -import { InvalidClientMetadataError } from "@modelcontextprotocol/sdk/server/auth/errors.js"; +import { InvalidClientMetadataError } from "@inspector/core/auth/mcpAuth.js"; function makeProvider( overrides: Partial = {}, diff --git a/clients/web/src/test/core/auth/runner-interactive-oauth.test.ts b/clients/web/src/test/core/auth/runner-interactive-oauth.test.ts index 1dbf740fe..d572e0bb7 100644 --- a/clients/web/src/test/core/auth/runner-interactive-oauth.test.ts +++ b/clients/web/src/test/core/auth/runner-interactive-oauth.test.ts @@ -56,15 +56,21 @@ function createMockCallbackServer(handlers: { } as unknown as OAuthCallbackServer; } -/** Drive the loopback callback the way a browser redirect would. */ +const CALLBACK_ISS = "https://as.example"; + +/** + * Drive the loopback callback the way a browser redirect would, including the + * RFC 9207 `iss` that real authorization servers send. + */ async function simulateCallback( handlers: MockCallbackHandlers, code = "auth-code-123", + iss: string | undefined = CALLBACK_ISS, ): Promise { if (!handlers.onCallback) { throw new Error("onCallback not registered"); } - await handlers.onCallback({ code }); + await handlers.onCallback({ code, iss }); } describe("runRunnerInteractiveOAuth", () => { @@ -145,7 +151,10 @@ describe("runRunnerInteractiveOAuth", () => { }); expect(result).toEqual({ kind: "success" }); - expect(client.completeOAuthFlow).toHaveBeenCalledWith("auth-code-123"); + expect(client.completeOAuthFlow).toHaveBeenCalledWith( + "auth-code-123", + CALLBACK_ISS, + ); expect(redirectUrlProvider.redirectUrl).toBe( "http://127.0.0.1:6276/oauth/callback", ); @@ -179,7 +188,10 @@ describe("runRunnerInteractiveOAuth", () => { authorizationUrl, ); expect(client.authenticate).not.toHaveBeenCalled(); - expect(client.completeOAuthFlow).toHaveBeenCalledWith("step-up-code"); + expect(client.completeOAuthFlow).toHaveBeenCalledWith( + "step-up-code", + CALLBACK_ISS, + ); }); it("returns insufficient_scope when post-step-up check fails", async () => { diff --git a/clients/web/src/test/core/auth/storage-browser.test.ts b/clients/web/src/test/core/auth/storage-browser.test.ts index bb855584e..951d20385 100644 --- a/clients/web/src/test/core/auth/storage-browser.test.ts +++ b/clients/web/src/test/core/auth/storage-browser.test.ts @@ -7,7 +7,7 @@ import type { OAuthClientInformation, OAuthTokens, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; // Mock sessionStorage for Node.js environment class MockSessionStorage implements Storage { diff --git a/clients/web/src/test/core/jsonUtils.test.ts b/clients/web/src/test/core/jsonUtils.test.ts index f15606f49..89f25f0c8 100644 --- a/clients/web/src/test/core/jsonUtils.test.ts +++ b/clients/web/src/test/core/jsonUtils.test.ts @@ -4,7 +4,7 @@ import { convertToolParameters, convertPromptArguments, } from "@inspector/core/json/jsonUtils.js"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; describe("JSON Utils", () => { describe("convertParameterValue", () => { diff --git a/clients/web/src/test/core/mcp/authResyncPropagation.test.ts b/clients/web/src/test/core/mcp/authResyncPropagation.test.ts index 0b4da48c0..96e03fc39 100644 --- a/clients/web/src/test/core/mcp/authResyncPropagation.test.ts +++ b/clients/web/src/test/core/mcp/authResyncPropagation.test.ts @@ -3,8 +3,8 @@ */ import { describe, it, expect, vi } from "vitest"; -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import { LATEST_PROTOCOL_VERSION } from "@modelcontextprotocol/sdk/types.js"; +import { Client } from "@modelcontextprotocol/client"; +import { LATEST_PROTOCOL_VERSION } from "@modelcontextprotocol/client"; import { MessageTrackingTransport } from "@inspector/core/mcp/messageTrackingTransport.js"; import { RemoteClientTransport } from "@inspector/core/mcp/remote/remoteClientTransport.js"; diff --git a/clients/web/src/test/core/mcp/elicitationCreateMessage.test.ts b/clients/web/src/test/core/mcp/elicitationCreateMessage.test.ts index 41d8a976e..752bceec9 100644 --- a/clients/web/src/test/core/mcp/elicitationCreateMessage.test.ts +++ b/clients/web/src/test/core/mcp/elicitationCreateMessage.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import type { ElicitRequest } from "@modelcontextprotocol/sdk/types.js"; +import type { ElicitRequest } from "@modelcontextprotocol/client"; import { ElicitationCreateMessage } from "@inspector/core/mcp/elicitationCreateMessage.js"; function urlRequest(): ElicitRequest { diff --git a/clients/web/src/test/core/mcp/inspectorClientUrlElicitation.test.ts b/clients/web/src/test/core/mcp/inspectorClientUrlElicitation.test.ts index c32b7000d..0a3093848 100644 --- a/clients/web/src/test/core/mcp/inspectorClientUrlElicitation.test.ts +++ b/clients/web/src/test/core/mcp/inspectorClientUrlElicitation.test.ts @@ -1,13 +1,13 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { - ErrorCode, - McpError, + ProtocolErrorCode, + ProtocolError, UrlElicitationRequiredError, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { ElicitRequestURLParams, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { InspectorClient } from "@inspector/core/mcp/inspectorClient.js"; import { UrlElicitationLoopError } from "@inspector/core/mcp/urlElicitation.js"; import type { CreateTransport } from "@inspector/core/mcp/types.js"; @@ -224,8 +224,8 @@ describe("InspectorClient URL-elicitation error path", () => { it("rethrows a -32042 error with no elicitations without queuing anything", async () => { const fake: FakeClient = { callTool: vi.fn(async () => { - throw new McpError( - ErrorCode.UrlElicitationRequired, + throw new ProtocolError( + ProtocolErrorCode.UrlElicitationRequired, "This request requires browser-based authorization.", ); }), @@ -234,7 +234,7 @@ describe("InspectorClient URL-elicitation error path", () => { const client = makeClient(fake); await expect(client.callTool(tool, {})).rejects.toMatchObject({ - code: ErrorCode.UrlElicitationRequired, + code: ProtocolErrorCode.UrlElicitationRequired, }); expect(client.getPendingElicitations()).toHaveLength(0); expect(fake.callTool).toHaveBeenCalledTimes(1); diff --git a/clients/web/src/test/core/mcp/messageTrackingTransport.test.ts b/clients/web/src/test/core/mcp/messageTrackingTransport.test.ts index 807a881b5..7d1782d0d 100644 --- a/clients/web/src/test/core/mcp/messageTrackingTransport.test.ts +++ b/clients/web/src/test/core/mcp/messageTrackingTransport.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from "vitest"; -import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; -import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { Transport } from "@modelcontextprotocol/client"; +import type { JSONRPCMessage } from "@modelcontextprotocol/client"; import { MessageTrackingTransport } from "@inspector/core/mcp/messageTrackingTransport.js"; /** Minimal in-memory Transport so we can drive send()/onmessage directly. */ diff --git a/clients/web/src/test/core/mcp/oauthManager.test.ts b/clients/web/src/test/core/mcp/oauthManager.test.ts index 064c93017..b26538dd9 100644 --- a/clients/web/src/test/core/mcp/oauthManager.test.ts +++ b/clients/web/src/test/core/mcp/oauthManager.test.ts @@ -1525,11 +1525,12 @@ describe("OAuthManager", () => { ); expect(outcome.kind).toBe("interactive"); - await manager.completeOAuthFlow("auth-code"); + await manager.completeOAuthFlow("auth-code", "https://idp.example.com"); expect(mintSpy).toHaveBeenCalledWith( expect.objectContaining({ scope: "mcp tools:read weather:read" }), "auth-code", + "https://idp.example.com", ); expect(storageOf(params).saveScope).toHaveBeenCalledWith( SERVER_URL, diff --git a/clients/web/src/test/core/mcp/state/managedPromptsState.test.ts b/clients/web/src/test/core/mcp/state/managedPromptsState.test.ts index 2a775d15e..573068206 100644 --- a/clients/web/src/test/core/mcp/state/managedPromptsState.test.ts +++ b/clients/web/src/test/core/mcp/state/managedPromptsState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; import { ManagedPromptsState } from "@inspector/core/mcp/state/managedPromptsState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/managedRequestorTasksState.test.ts b/clients/web/src/test/core/mcp/state/managedRequestorTasksState.test.ts index ca00ee9f9..7e001c11a 100644 --- a/clients/web/src/test/core/mcp/state/managedRequestorTasksState.test.ts +++ b/clients/web/src/test/core/mcp/state/managedRequestorTasksState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { ManagedRequestorTasksState } from "@inspector/core/mcp/state/managedRequestorTasksState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { waitForChangeEvent } from "./waitForChangeEvent"; diff --git a/clients/web/src/test/core/mcp/state/managedResourceTemplatesState.test.ts b/clients/web/src/test/core/mcp/state/managedResourceTemplatesState.test.ts index 980d3a9fc..8aaae5431 100644 --- a/clients/web/src/test/core/mcp/state/managedResourceTemplatesState.test.ts +++ b/clients/web/src/test/core/mcp/state/managedResourceTemplatesState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; import { ManagedResourceTemplatesState } from "@inspector/core/mcp/state/managedResourceTemplatesState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/managedResourcesState.test.ts b/clients/web/src/test/core/mcp/state/managedResourcesState.test.ts index e738f5168..d02586176 100644 --- a/clients/web/src/test/core/mcp/state/managedResourcesState.test.ts +++ b/clients/web/src/test/core/mcp/state/managedResourcesState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; import { ManagedResourcesState } from "@inspector/core/mcp/state/managedResourcesState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/managedToolsState.test.ts b/clients/web/src/test/core/mcp/state/managedToolsState.test.ts index a88659228..1ff01acff 100644 --- a/clients/web/src/test/core/mcp/state/managedToolsState.test.ts +++ b/clients/web/src/test/core/mcp/state/managedToolsState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; import { ManagedToolsState } from "@inspector/core/mcp/state/managedToolsState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/mergeTaskIntoList.test.ts b/clients/web/src/test/core/mcp/state/mergeTaskIntoList.test.ts index b2acb3d69..6b7e84e2e 100644 --- a/clients/web/src/test/core/mcp/state/mergeTaskIntoList.test.ts +++ b/clients/web/src/test/core/mcp/state/mergeTaskIntoList.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import type { TaskWithOptionalCreatedAt } from "@inspector/core/mcp/inspectorClientEventTarget"; import { mergeTaskIntoList } from "@inspector/core/mcp/state/mergeTaskIntoList"; diff --git a/clients/web/src/test/core/mcp/state/pagedPromptsState.test.ts b/clients/web/src/test/core/mcp/state/pagedPromptsState.test.ts index eaf320362..9df6895c3 100644 --- a/clients/web/src/test/core/mcp/state/pagedPromptsState.test.ts +++ b/clients/web/src/test/core/mcp/state/pagedPromptsState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { PagedPromptsState } from "@inspector/core/mcp/state/pagedPromptsState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/pagedRequestorTasksState.test.ts b/clients/web/src/test/core/mcp/state/pagedRequestorTasksState.test.ts index 716bf9936..390139c20 100644 --- a/clients/web/src/test/core/mcp/state/pagedRequestorTasksState.test.ts +++ b/clients/web/src/test/core/mcp/state/pagedRequestorTasksState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { PagedRequestorTasksState } from "@inspector/core/mcp/state/pagedRequestorTasksState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/pagedResourceTemplatesState.test.ts b/clients/web/src/test/core/mcp/state/pagedResourceTemplatesState.test.ts index 30edf9e6e..ae5c9d8f0 100644 --- a/clients/web/src/test/core/mcp/state/pagedResourceTemplatesState.test.ts +++ b/clients/web/src/test/core/mcp/state/pagedResourceTemplatesState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { PagedResourceTemplatesState } from "@inspector/core/mcp/state/pagedResourceTemplatesState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/pagedResourcesState.test.ts b/clients/web/src/test/core/mcp/state/pagedResourcesState.test.ts index 03d6661e2..5a8dbec4d 100644 --- a/clients/web/src/test/core/mcp/state/pagedResourcesState.test.ts +++ b/clients/web/src/test/core/mcp/state/pagedResourcesState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { PagedResourcesState } from "@inspector/core/mcp/state/pagedResourcesState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/pagedToolsState.test.ts b/clients/web/src/test/core/mcp/state/pagedToolsState.test.ts index 4f4b51e0e..23c31af7a 100644 --- a/clients/web/src/test/core/mcp/state/pagedToolsState.test.ts +++ b/clients/web/src/test/core/mcp/state/pagedToolsState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { PagedToolsState } from "@inspector/core/mcp/state/pagedToolsState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/state/resourceSubscriptionsState.test.ts b/clients/web/src/test/core/mcp/state/resourceSubscriptionsState.test.ts index 5dbd5659d..a328ceb0e 100644 --- a/clients/web/src/test/core/mcp/state/resourceSubscriptionsState.test.ts +++ b/clients/web/src/test/core/mcp/state/resourceSubscriptionsState.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { ResourceSubscriptionsState } from "@inspector/core/mcp/state/resourceSubscriptionsState"; import { ManagedResourcesState } from "@inspector/core/mcp/state/managedResourcesState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; diff --git a/clients/web/src/test/core/mcp/toolOutputValidation.test.ts b/clients/web/src/test/core/mcp/toolOutputValidation.test.ts index f89a86594..9eeedcee3 100644 --- a/clients/web/src/test/core/mcp/toolOutputValidation.test.ts +++ b/clients/web/src/test/core/mcp/toolOutputValidation.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "vitest"; -import { AjvJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/ajv"; -import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js"; +import { AjvJsonSchemaValidator } from "@modelcontextprotocol/client/validators/ajv"; +import type { CallToolResult, Tool } from "@modelcontextprotocol/client"; import { validateToolOutput } from "@inspector/core/mcp/toolOutputValidation"; const provider = new AjvJsonSchemaValidator(); diff --git a/clients/web/src/test/core/mcp/urlElicitation.test.ts b/clients/web/src/test/core/mcp/urlElicitation.test.ts index e92dbd55e..a3104bc3a 100644 --- a/clients/web/src/test/core/mcp/urlElicitation.test.ts +++ b/clients/web/src/test/core/mcp/urlElicitation.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from "vitest"; import { - ErrorCode, - McpError, + ProtocolErrorCode, + ProtocolError, UrlElicitationRequiredError, -} from "@modelcontextprotocol/sdk/types.js"; -import type { ElicitRequestURLParams } from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; +import type { ElicitRequestURLParams } from "@modelcontextprotocol/client"; import { getUrlElicitationsFromError } from "@inspector/core/mcp/urlElicitation.js"; const elicitation: ElicitRequestURLParams = { @@ -20,25 +20,28 @@ describe("getUrlElicitationsFromError", () => { expect(getUrlElicitationsFromError(error)).toEqual([elicitation]); }); - it("returns the elicitations from a generic McpError carrying the -32042 data", () => { - const error = new McpError( - ErrorCode.UrlElicitationRequired, + it("returns the elicitations from a generic ProtocolError carrying the -32042 data", () => { + const error = new ProtocolError( + ProtocolErrorCode.UrlElicitationRequired, "This request requires browser-based authorization.", { elicitations: [elicitation] }, ); expect(getUrlElicitationsFromError(error)).toEqual([elicitation]); }); - it("returns an empty array for a -32042 McpError with no elicitations (non-spec)", () => { - const error = new McpError( - ErrorCode.UrlElicitationRequired, + it("returns an empty array for a -32042 ProtocolError with no elicitations (non-spec)", () => { + const error = new ProtocolError( + ProtocolErrorCode.UrlElicitationRequired, "This request requires browser-based authorization.", ); expect(getUrlElicitationsFromError(error)).toEqual([]); }); - it("returns null for a non -32042 McpError", () => { - const error = new McpError(ErrorCode.InvalidParams, "bad params"); + it("returns null for a non -32042 ProtocolError", () => { + const error = new ProtocolError( + ProtocolErrorCode.InvalidParams, + "bad params", + ); expect(getUrlElicitationsFromError(error)).toBeNull(); }); diff --git a/clients/web/src/test/core/react/useInspectorClient.test.tsx b/clients/web/src/test/core/react/useInspectorClient.test.tsx index c1c0dbf58..83b53eee6 100644 --- a/clients/web/src/test/core/react/useInspectorClient.test.tsx +++ b/clients/web/src/test/core/react/useInspectorClient.test.tsx @@ -4,7 +4,7 @@ import type { ClientCapabilities, Implementation, ServerCapabilities, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { useInspectorClient } from "@inspector/core/react/useInspectorClient"; import type { InspectorClientProtocol } from "@inspector/core/mcp/inspectorClientProtocol"; diff --git a/clients/web/src/test/core/react/useManagedPrompts.test.tsx b/clients/web/src/test/core/react/useManagedPrompts.test.tsx index 815beb571..ff40e29b3 100644 --- a/clients/web/src/test/core/react/useManagedPrompts.test.tsx +++ b/clients/web/src/test/core/react/useManagedPrompts.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { ManagedPromptsState } from "@inspector/core/mcp/state/managedPromptsState"; import { useManagedPrompts } from "@inspector/core/react/useManagedPrompts"; diff --git a/clients/web/src/test/core/react/useManagedRequestorTasks.test.tsx b/clients/web/src/test/core/react/useManagedRequestorTasks.test.tsx index 6775ea673..3260b255f 100644 --- a/clients/web/src/test/core/react/useManagedRequestorTasks.test.tsx +++ b/clients/web/src/test/core/react/useManagedRequestorTasks.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { ManagedRequestorTasksState } from "@inspector/core/mcp/state/managedRequestorTasksState"; import { useManagedRequestorTasks } from "@inspector/core/react/useManagedRequestorTasks"; diff --git a/clients/web/src/test/core/react/useManagedResourceTemplates.test.tsx b/clients/web/src/test/core/react/useManagedResourceTemplates.test.tsx index 87505871a..f7d948364 100644 --- a/clients/web/src/test/core/react/useManagedResourceTemplates.test.tsx +++ b/clients/web/src/test/core/react/useManagedResourceTemplates.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { ManagedResourceTemplatesState } from "@inspector/core/mcp/state/managedResourceTemplatesState"; import { useManagedResourceTemplates } from "@inspector/core/react/useManagedResourceTemplates"; diff --git a/clients/web/src/test/core/react/useManagedResources.test.tsx b/clients/web/src/test/core/react/useManagedResources.test.tsx index 154eb15c1..ee94a5a63 100644 --- a/clients/web/src/test/core/react/useManagedResources.test.tsx +++ b/clients/web/src/test/core/react/useManagedResources.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { ManagedResourcesState } from "@inspector/core/mcp/state/managedResourcesState"; import { useManagedResources } from "@inspector/core/react/useManagedResources"; diff --git a/clients/web/src/test/core/react/useManagedTools.test.tsx b/clients/web/src/test/core/react/useManagedTools.test.tsx index 742437ebc..a4fa7d258 100644 --- a/clients/web/src/test/core/react/useManagedTools.test.tsx +++ b/clients/web/src/test/core/react/useManagedTools.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { ManagedToolsState } from "@inspector/core/mcp/state/managedToolsState"; import { useManagedTools } from "@inspector/core/react/useManagedTools"; diff --git a/clients/web/src/test/core/react/usePagedPrompts.test.tsx b/clients/web/src/test/core/react/usePagedPrompts.test.tsx index c1b6cc961..63be072bd 100644 --- a/clients/web/src/test/core/react/usePagedPrompts.test.tsx +++ b/clients/web/src/test/core/react/usePagedPrompts.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { PagedPromptsState } from "@inspector/core/mcp/state/pagedPromptsState"; import { usePagedPrompts } from "@inspector/core/react/usePagedPrompts"; diff --git a/clients/web/src/test/core/react/usePagedRequestorTasks.test.tsx b/clients/web/src/test/core/react/usePagedRequestorTasks.test.tsx index db24113e9..ae4bce667 100644 --- a/clients/web/src/test/core/react/usePagedRequestorTasks.test.tsx +++ b/clients/web/src/test/core/react/usePagedRequestorTasks.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { PagedRequestorTasksState } from "@inspector/core/mcp/state/pagedRequestorTasksState"; import { usePagedRequestorTasks } from "@inspector/core/react/usePagedRequestorTasks"; diff --git a/clients/web/src/test/core/react/usePagedResourceTemplates.test.tsx b/clients/web/src/test/core/react/usePagedResourceTemplates.test.tsx index 52910084e..a200296ba 100644 --- a/clients/web/src/test/core/react/usePagedResourceTemplates.test.tsx +++ b/clients/web/src/test/core/react/usePagedResourceTemplates.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { PagedResourceTemplatesState } from "@inspector/core/mcp/state/pagedResourceTemplatesState"; import { usePagedResourceTemplates } from "@inspector/core/react/usePagedResourceTemplates"; diff --git a/clients/web/src/test/core/react/usePagedResources.test.tsx b/clients/web/src/test/core/react/usePagedResources.test.tsx index ccb0a4842..f85424379 100644 --- a/clients/web/src/test/core/react/usePagedResources.test.tsx +++ b/clients/web/src/test/core/react/usePagedResources.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { PagedResourcesState } from "@inspector/core/mcp/state/pagedResourcesState"; import { usePagedResources } from "@inspector/core/react/usePagedResources"; diff --git a/clients/web/src/test/core/react/usePagedTools.test.tsx b/clients/web/src/test/core/react/usePagedTools.test.tsx index 26422915c..acdfa4db4 100644 --- a/clients/web/src/test/core/react/usePagedTools.test.tsx +++ b/clients/web/src/test/core/react/usePagedTools.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { act, renderHook, waitFor } from "@testing-library/react"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; import { PagedToolsState } from "@inspector/core/mcp/state/pagedToolsState"; import { usePagedTools } from "@inspector/core/react/usePagedTools"; diff --git a/clients/web/src/test/integration/auth/discovery.test.ts b/clients/web/src/test/integration/auth/discovery.test.ts index c7104add4..0a7e84642 100644 --- a/clients/web/src/test/integration/auth/discovery.test.ts +++ b/clients/web/src/test/integration/auth/discovery.test.ts @@ -3,10 +3,10 @@ import { discoverScopes, getAuthorizationServerUrl, } from "@inspector/core/auth/discovery.js"; -import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/client"; // Mock SDK functions -vi.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({ +vi.mock("@modelcontextprotocol/client", () => ({ discoverAuthorizationServerMetadata: vi.fn(), })); @@ -17,7 +17,7 @@ describe("OAuth Scope Discovery", () => { it("should return scopes from resource metadata when available", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", authorization_endpoint: "http://localhost:3000/authorize", @@ -42,7 +42,7 @@ describe("OAuth Scope Discovery", () => { it("should fall back to OAuth metadata scopes when resource metadata has no scopes", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", authorization_endpoint: "http://localhost:3000/authorize", @@ -67,7 +67,7 @@ describe("OAuth Scope Discovery", () => { it("should fall back to OAuth metadata scopes when resource metadata is not provided", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", authorization_endpoint: "http://localhost:3000/authorize", @@ -83,7 +83,7 @@ describe("OAuth Scope Discovery", () => { it("should return undefined when no scopes are available", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", authorization_endpoint: "http://localhost:3000/authorize", @@ -99,7 +99,7 @@ describe("OAuth Scope Discovery", () => { it("should return undefined when discovery fails", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockRejectedValue( new Error("Discovery failed"), ); @@ -111,7 +111,7 @@ describe("OAuth Scope Discovery", () => { it("should return undefined when metadata is undefined", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue(undefined); const scopes = await discoverScopes("http://localhost:3000"); @@ -121,7 +121,7 @@ describe("OAuth Scope Discovery", () => { it("should use OAuth metadata scopes when resource has scopes_supported undefined", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", authorization_endpoint: "http://localhost:3000/authorize", @@ -146,7 +146,7 @@ describe("OAuth Scope Discovery", () => { it("should return single scope when only one scope is supported", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", authorization_endpoint: "http://localhost:3000/authorize", @@ -162,7 +162,7 @@ describe("OAuth Scope Discovery", () => { it("should pass fetchFn to discoverAuthorizationServerMetadata when provided", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); const mockFetchFn = vi.fn(); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "http://localhost:3000", @@ -182,7 +182,7 @@ describe("OAuth Scope Discovery", () => { it("should use authorization_servers URL from resource metadata for discovery (different domain)", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "https://auth-server.com", authorization_endpoint: "https://auth-server.com/authorize", @@ -211,7 +211,7 @@ describe("OAuth Scope Discovery", () => { it("should preserve full path in authorization_servers URL", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "https://auth-server.com/realms/my-realm", authorization_endpoint: @@ -241,7 +241,7 @@ describe("OAuth Scope Discovery", () => { it("should fall back to serverUrl when authorization_servers is empty", async () => { const { discoverAuthorizationServerMetadata } = - await import("@modelcontextprotocol/sdk/client/auth.js"); + await import("@modelcontextprotocol/client"); vi.mocked(discoverAuthorizationServerMetadata).mockResolvedValue({ issuer: "https://mcp-server.com", authorization_endpoint: "https://mcp-server.com/authorize", diff --git a/clients/web/src/test/integration/auth/node/storage.test.ts b/clients/web/src/test/integration/auth/node/storage.test.ts index d024fadc8..d78d4a9f9 100644 --- a/clients/web/src/test/integration/auth/node/storage.test.ts +++ b/clients/web/src/test/integration/auth/node/storage.test.ts @@ -9,7 +9,7 @@ import type { OAuthClientInformation, OAuthTokens, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import * as fs from "node:fs/promises"; import * as path from "node:path"; import * as os from "node:os"; diff --git a/clients/web/src/test/integration/mcp/ema-mock-servers.ts b/clients/web/src/test/integration/mcp/ema-mock-servers.ts index 45ea16d0f..d502cf9c9 100644 --- a/clients/web/src/test/integration/mcp/ema-mock-servers.ts +++ b/clients/web/src/test/integration/mcp/ema-mock-servers.ts @@ -21,7 +21,15 @@ export const EMA_MOCK_IDP_CLIENT_SECRET = "ema-mock-idp-secret"; export const EMA_MOCK_RESOURCE_CLIENT_ID = "ema-mock-resource-client"; export const EMA_MOCK_RESOURCE_CLIENT_SECRET = "ema-mock-resource-secret"; -/** Fields required by SDK `OAuthMetadataSchema` for discovery in tests. */ +/** + * Fields required by SDK `OAuthMetadataSchema` for discovery in tests. + * + * `authorization_response_iss_parameter_supported` mirrors real-world IdPs + * (e.g. Okta/xaa.dev): once advertised, the SDK enforces RFC 9207 §2.4 and + * rejects a callback whose `iss` is missing. Keep it on so the mock stays at + * least as strict as production servers — with it absent the SDK silently takes + * its lenient path and a dropped `iss` goes unnoticed. + */ export function minimalOAuthAsMetadata( baseUrl: string, authorizationEndpoint = `${baseUrl}/authorize`, @@ -32,6 +40,7 @@ export function minimalOAuthAsMetadata( token_endpoint: `${baseUrl}/token`, response_types_supported: ["code"], token_endpoint_auth_methods_supported: ["client_secret_post"], + authorization_response_iss_parameter_supported: true, }; } diff --git a/clients/web/src/test/integration/mcp/inspectorClient-coverage-backfill.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-coverage-backfill.test.ts index 1bc75c5b9..faa5c19cd 100644 --- a/clients/web/src/test/integration/mcp/inspectorClient-coverage-backfill.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-coverage-backfill.test.ts @@ -23,8 +23,8 @@ import { createNumberedResources, createNumberedPrompts, } from "@modelcontextprotocol/inspector-test-server"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; -import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; +import { ProtocolError, ProtocolErrorCode } from "@modelcontextprotocol/client"; const serverCommand = getTestMcpServerCommand(); @@ -184,7 +184,10 @@ describe("InspectorClient coverage backfill", () => { }; const original = c.client.complete; c.client.complete = async () => { - throw new McpError(ErrorCode.MethodNotFound, "Method not found"); + throw new ProtocolError( + ProtocolErrorCode.MethodNotFound, + "Method not found", + ); }; try { await expect( @@ -591,11 +594,13 @@ describe("InspectorClient coverage backfill", () => { client = stdioClient(); await client.connect(); // Force the streaming API to throw synchronously so the error-path - // metadata-merge branches run for each combination. + // metadata-merge branches run for each combination. `callToolStream` + // drives tasks via the private `pollTaskToolCall` generator (SDK v2 + // removed `client.experimental.tasks`), so patch that instead. const c = client as unknown as { - client: { experimental: { tasks: { callToolStream: () => never } } }; + pollTaskToolCall: () => never; }; - c.client.experimental.tasks.callToolStream = () => { + c.pollTaskToolCall = () => { throw new Error("forced-stream-failure"); }; @@ -747,9 +752,9 @@ describe("InspectorClient coverage backfill", () => { await client.connect(); const echo = await getTool(client, "echo"); const c = client as unknown as { - client: { experimental: { tasks: { callToolStream: () => never } } }; + pollTaskToolCall: () => never; }; - c.client.experimental.tasks.callToolStream = () => { + c.pollTaskToolCall = () => { throw "string-stream-failure"; }; await expect( @@ -761,18 +766,12 @@ describe("InspectorClient coverage backfill", () => { client = stdioClient(); await client.connect(); const c = client as unknown as { - client: { - listResources: () => Promise; - listResourceTemplates: () => Promise; - listPrompts: () => Promise; - listTools: () => Promise; - }; + client: { request: () => Promise }; }; - // Return responses missing the array field → `|| []` fallback branches. - c.client.listResources = async () => ({}); - c.client.listResourceTemplates = async () => ({}); - c.client.listPrompts = async () => ({}); - c.client.listTools = async () => ({}); + // InspectorClient's list methods issue raw `client.request({method:"…/list"})` + // (SDK v2's `client.listX()` auto-aggregate pages), so mock `request` to + // return a body missing the array field → `|| []` fallback branches. + c.client.request = async () => ({}); expect((await client.listResources()).resources).toEqual([]); expect((await client.listResourceTemplates()).resourceTemplates).toEqual( @@ -800,19 +799,26 @@ describe("InspectorClient coverage backfill", () => { gen: () => AsyncGenerator, getTaskResult?: () => Promise, ): void { + // SDK v2 removed `client.experimental.tasks`; `callToolStream` now drives + // tasks via the private `pollTaskToolCall` generator, and the no-result + // fallback fetches the payload with a raw `client.request({method:"tasks/result"})`. const internal = c as unknown as { + pollTaskToolCall: () => AsyncGenerator; client: { - experimental: { - tasks: { - callToolStream: () => AsyncGenerator; - getTaskResult: (id: string) => Promise; - }; - }; + request: ( + req: { method: string }, + schema: unknown, + opts: unknown, + ) => Promise; }; }; - internal.client.experimental.tasks.callToolStream = gen; + internal.pollTaskToolCall = gen; if (getTaskResult) { - internal.client.experimental.tasks.getTaskResult = getTaskResult; + const origRequest = internal.client.request.bind(internal.client); + internal.client.request = (req, schema, opts) => + req.method === "tasks/result" + ? getTaskResult() + : origRequest(req, schema, opts); } } diff --git a/clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts index 5ed25eeef..bffb99900 100644 --- a/clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts @@ -32,9 +32,17 @@ function createTestOAuthConfig( } const mockAuth = vi.fn(); -vi.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({ - auth: (...args: unknown[]) => mockAuth(...args), -})); +// Partial mock: keep every real export (Client, transports, types, …) and +// override only `auth`. A full-replace factory would drop `Client`, which +// InspectorClient imports from the same module. +vi.mock("@modelcontextprotocol/client", async (importOriginal) => { + const actual = + await importOriginal(); + return { + ...actual, + auth: (...args: unknown[]) => mockAuth(...args), + }; +}); describe("InspectorClient OAuth fetchFn", () => { let client: InspectorClient; diff --git a/clients/web/src/test/integration/mcp/inspectorClient.test.ts b/clients/web/src/test/integration/mcp/inspectorClient.test.ts index 237f374f6..84e4225de 100644 --- a/clients/web/src/test/integration/mcp/inspectorClient.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient.test.ts @@ -63,16 +63,16 @@ import type { Task, Tool, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, Prompt, Progress, ContentBlock, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { RELATED_TASK_META_KEY, - McpError, - ErrorCode, -} from "@modelcontextprotocol/sdk/types.js"; + SdkError, + SdkErrorCode, +} from "@modelcontextprotocol/client"; /** Get all tools from the client via listTools() (paginates if needed). */ async function getAllTools(client: InspectorClient): Promise { @@ -310,7 +310,7 @@ describe("InspectorClient", () => { }; const fakeFactory = () => ({ transport: - hangingTransport as unknown as import("@modelcontextprotocol/sdk/shared/transport.js").Transport, + hangingTransport as unknown as import("@modelcontextprotocol/client").Transport, }); client = new InspectorClient( { type: "streamable-http", url: "http://localhost:1/never" }, @@ -358,7 +358,7 @@ describe("InspectorClient", () => { }; const fakeFactory = () => ({ transport: - unauthorizedTransport as unknown as import("@modelcontextprotocol/sdk/shared/transport.js").Transport, + unauthorizedTransport as unknown as import("@modelcontextprotocol/client").Transport, }); client = new InspectorClient( { type: "streamable-http", url: "http://localhost:8081/mcp" }, @@ -967,20 +967,13 @@ describe("InspectorClient", () => { }); it("should handle tool not found", async () => { - const result = await client!.callTool( - minimalTool("nonexistent-tool"), - {}, - ); - // When tool is not found, the SDK returns an error response, not an exception - expect(result.success).toBe(true); // SDK returns error in result, not as exception - expect(result.result).toHaveProperty("isError", true); - expect(result.result).toBeDefined(); - if (result.result) { - expect(result.result).toHaveProperty("content"); - const content = result.result.content as ContentBlock[]; - expect(content[0]).toHaveProperty("text"); - expect((content[0] as { text: string }).text).toContain("not found"); - } + // SDK v2 change (#1624): an unknown-tool call now *rejects* with a + // ProtocolError (-32602) instead of resolving an `isError: true` result. + // callTool records the failed call and rethrows, so the caller sees a + // rejection whose message names the missing tool. + await expect( + client!.callTool(minimalTool("nonexistent-tool"), {}), + ).rejects.toThrow(/not found|unknown tool|nonexistent-tool/i); }); it("cancelToolCall() is a no-op (returns false) when no call is in flight", () => { @@ -994,13 +987,9 @@ describe("InspectorClient", () => { ); }); - it("cancelToolCall() aborts the in-flight call: sends notifications/cancelled, rejects with ToolCallCancelledError, and records no failed call", async () => { + it("cancelToolCall() aborts the in-flight call: rejects with ToolCallCancelledError, and records no failed call", async () => { const tool = await getTool(client!, "echo"); - const messages: MessageEntry[] = []; - client!.addEventListener("message", (event) => { - messages.push(event.detail); - }); let failedCallCount = 0; client!.addEventListener("toolCallResultChange", (event) => { if (!event.detail.success) failedCallCount++; @@ -1013,14 +1002,14 @@ describe("InspectorClient", () => { await expect(promise).rejects.toBeInstanceOf(ToolCallCancelledError); - // The MCP cancellation flow: a notifications/cancelled reaches the server. - const cancelled = messages.find( - (m) => - m.direction === "notification" && - "method" in m.message && - m.message.method === "notifications/cancelled", - ); - expect(cancelled).toBeDefined(); + // SDK v2 change (spec §9.2): cancellation now surfaces as a stream/connection + // abort rather than a guaranteed `notifications/cancelled` frame. v2's + // `callTool` awaits output-validator compilation before it issues the + // `tools/call` request, so a synchronous cancel aborts the shared signal + // *before* that request registers — the SDK then takes its pre-aborted + // early-reject path and emits no cancellation frame. The user-facing + // behavior we care about is unchanged: the call rejects with + // ToolCallCancelledError and is not recorded as a failed call. // Cancelling is intentional, so it is not recorded as a failed tool call. expect(failedCallCount).toBe(0); @@ -2427,8 +2416,8 @@ describe("InspectorClient", () => { } catch (e) { err = e; } - expect(err).toBeInstanceOf(McpError); - expect((err as McpError).code).toBe(ErrorCode.RequestTimeout); + expect(err).toBeInstanceOf(SdkError); + expect((err as SdkError).code).toBe(SdkErrorCode.RequestTimeout); await client.disconnect(); await server.stop(); @@ -2858,7 +2847,7 @@ describe("InspectorClient", () => { onerror: undefined, onmessage: undefined, sessionId: undefined, - } as unknown as import("@modelcontextprotocol/sdk/shared/transport.js").Transport, + } as unknown as import("@modelcontextprotocol/client").Transport, }); client = new InspectorClient( { type: "streamable-http", url: "http://localhost:1/never" }, @@ -2896,7 +2885,7 @@ describe("InspectorClient", () => { }; const factory = () => ({ transport: - transport as unknown as import("@modelcontextprotocol/sdk/shared/transport.js").Transport, + transport as unknown as import("@modelcontextprotocol/client").Transport, }); client = new InspectorClient( { type: "streamable-http", url: "http://localhost:1/never" }, @@ -5168,7 +5157,7 @@ describe("InspectorClient", () => { }); describe("getAppRendererClient", () => { - it("returns null before connect, and a proxy after connect that forwards setNotificationHandler", async () => { + it("returns null before connect, and a cached proxy after connect", async () => { server = createTestServerHttp({ serverInfo: createTestServerInfo(), tools: [createEchoTool()], @@ -5188,12 +5177,76 @@ describe("InspectorClient", () => { expect(proxy1).not.toBeNull(); // Second call returns the cached proxy expect(c.getAppRendererClient()).toBe(proxy1); - // setNotificationHandler on the proxy delegates to the underlying client expect( typeof (proxy1 as unknown as { setNotificationHandler?: unknown }) .setNotificationHandler, ).toBe("function"); }); + + it("translates the ext-apps v1 schema-first setNotificationHandler call to v2's method-string form", async () => { + // Regression: `@modelcontextprotocol/ext-apps` (SDK v1 peer) subscribes + // with `setNotificationHandler(NotificationSchema, handler)`. On SDK v2 + // that throws "'[object Object]' is not a spec notification method", + // breaking App rendering at connect. The proxy must translate the + // schema (whose `.shape.method.value` is the method literal) to the + // method string so the handler still fires on the real notification. + server = createTestServerHttp({ + serverInfo: createTestServerInfo(), + tools: [createAddToolTool()], + listChanged: { tools: true }, + }); + await server.start(); + const c = new InspectorClient( + { type: "streamable-http", url: server.url }, + { environment: { transport: createTransportNode } }, + ); + client = c; + await c.connect(); + + const proxy = c.getAppRendererClient() as unknown as { + setNotificationHandler: ( + schema: unknown, + handler: () => void, + ) => void; + }; + // A v1-style Zod notification schema, as ext-apps passes it (NOT a + // string): its `.shape.method.value` carries the method literal. + const v1StyleSchema = { + shape: { method: { value: "notifications/tools/list_changed" } }, + }; + let handlerFired = false; + expect(() => + proxy.setNotificationHandler(v1StyleSchema, () => { + handlerFired = true; + }), + ).not.toThrow(); + + // The registration must land under the extracted method string: trigger + // a real tools/list_changed and confirm the schema-first handler runs. + const addToolTool = await getTool(c, "add_tool"); + await c.callTool(addToolTool, { + name: "added_via_schema_first", + description: "added at runtime", + }); + await vi.waitFor(() => expect(handlerFired).toBe(true), { + timeout: 5000, + }); + + // A native string-first call (ours) passes through unchanged. + expect(() => + proxy.setNotificationHandler( + "notifications/prompts/list_changed", + () => {}, + ), + ).not.toThrow(); + + // An unrecognized first arg (no `.shape.method.value`) can't be + // translated and falls through to the SDK, which rejects it clearly — + // we don't silently swallow a genuinely-malformed registration. + expect(() => + proxy.setNotificationHandler({} as unknown, () => {}), + ).toThrow(/not a spec notification method/); + }); }); describe("list_changed + resourceUpdated notifications", () => { diff --git a/clients/web/src/test/integration/mcp/mcp-app-demo.test.ts b/clients/web/src/test/integration/mcp/mcp-app-demo.test.ts index 298dd43df..ad2330704 100644 --- a/clients/web/src/test/integration/mcp/mcp-app-demo.test.ts +++ b/clients/web/src/test/integration/mcp/mcp-app-demo.test.ts @@ -9,7 +9,7 @@ import { createMcpAppDemoResource, MCP_APP_DEMO_URI, } from "@modelcontextprotocol/inspector-test-server"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; /** * Exercises the `mcp_app_demo` preset added in #1557 end-to-end: it confirms the diff --git a/clients/web/src/test/integration/mcp/node/transport.test.ts b/clients/web/src/test/integration/mcp/node/transport.test.ts index eb9872d47..f72229114 100644 --- a/clients/web/src/test/integration/mcp/node/transport.test.ts +++ b/clients/web/src/test/integration/mcp/node/transport.test.ts @@ -15,7 +15,7 @@ import { createEchoTool, createTestServerInfo, } from "@modelcontextprotocol/inspector-test-server"; -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { Client } from "@modelcontextprotocol/client"; describe("Transport", () => { describe("getServerType", () => { diff --git a/clients/web/src/test/integration/mcp/remote/remote-session.test.ts b/clients/web/src/test/integration/mcp/remote/remote-session.test.ts index bb52a33b9..5fe6f4dec 100644 --- a/clients/web/src/test/integration/mcp/remote/remote-session.test.ts +++ b/clients/web/src/test/integration/mcp/remote/remote-session.test.ts @@ -7,7 +7,7 @@ */ import { describe, it, expect, vi } from "vitest"; -import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; +import type { Transport } from "@modelcontextprotocol/client"; import { RemoteSession } from "@inspector/core/mcp/remote/node/remote-session.js"; import type { FetchRequestEntryBase } from "@inspector/core/mcp/types.js"; import { AuthChallengeError } from "@inspector/core/auth/challenge.js"; diff --git a/clients/web/src/test/integration/mcp/remote/remoteClientTransport-unit.test.ts b/clients/web/src/test/integration/mcp/remote/remoteClientTransport-unit.test.ts index 4e07b3aa3..24ecc8143 100644 --- a/clients/web/src/test/integration/mcp/remote/remoteClientTransport-unit.test.ts +++ b/clients/web/src/test/integration/mcp/remote/remoteClientTransport-unit.test.ts @@ -15,7 +15,7 @@ import { RemoteClientTransport } from "@inspector/core/mcp/remote/remoteClientTr import type { RemoteTransportOptions } from "@inspector/core/mcp/remote/remoteClientTransport.js"; import type { MCPServerConfig } from "@inspector/core/mcp/types.js"; import type { RemoteEvent } from "@inspector/core/mcp/remote/types.js"; -import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { JSONRPCMessage } from "@modelcontextprotocol/client"; import { AuthChallengeError, AuthRecoveryRequiredError, diff --git a/clients/web/src/test/integration/server/vite-base-config.test.ts b/clients/web/src/test/integration/server/vite-base-config.test.ts index 1bc464e74..fc3052233 100644 --- a/clients/web/src/test/integration/server/vite-base-config.test.ts +++ b/clients/web/src/test/integration/server/vite-base-config.test.ts @@ -19,7 +19,7 @@ describe("getViteBaseConfig", () => { const config = getViteBaseConfig(); expect(config.optimizeDeps.exclude).toEqual( expect.arrayContaining([ - "@modelcontextprotocol/sdk/client/stdio.js", + "@modelcontextprotocol/client/stdio", "atomically", "cross-spawn", "which", @@ -42,7 +42,7 @@ describe("getViteDevOptimizeDeps", () => { expect(config.ignoreOutdatedRequests).toBe(true); expect(config.include).toEqual([ "ajv", - "@modelcontextprotocol/sdk/validation/ajv", + "@modelcontextprotocol/client/validators/ajv", ]); expect(config.exclude).toEqual(getViteBaseConfig().optimizeDeps.exclude); }); diff --git a/clients/web/src/utils/jsonUtils.test.ts b/clients/web/src/utils/jsonUtils.test.ts index c127cc9ff..57a611322 100644 --- a/clients/web/src/utils/jsonUtils.test.ts +++ b/clients/web/src/utils/jsonUtils.test.ts @@ -7,7 +7,7 @@ import { collectSchemaDefaults, hasMissingRequiredFields, } from "./jsonUtils"; -import type { JsonSchemaType } from "./jsonUtils"; +import type { InspectorFormSchema } from "./jsonUtils"; describe("getDataType", () => { it("returns 'array' for arrays", () => { @@ -172,7 +172,7 @@ describe("updateValueAtPath edge case suppression", () => { describe("collectSchemaDefaults", () => { it("collects defaults across field types and omits fields without one", () => { - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { firstLine: { @@ -197,7 +197,7 @@ describe("collectSchemaDefaults", () => { }); it("recurses into nested object schemas and skips empty ones", () => { - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { db: { @@ -224,7 +224,7 @@ describe("collectSchemaDefaults", () => { }); describe("hasMissingRequiredFields", () => { - const schema: JsonSchemaType = { + const schema: InspectorFormSchema = { type: "object", properties: { name: { type: "string" }, age: { type: "integer" } }, required: ["name"], diff --git a/clients/web/src/utils/jsonUtils.ts b/clients/web/src/utils/jsonUtils.ts index 67a4bfb34..98dd905a2 100644 --- a/clients/web/src/utils/jsonUtils.ts +++ b/clients/web/src/utils/jsonUtils.ts @@ -13,7 +13,7 @@ export type JsonSchemaConst = { description?: string; }; -export type JsonSchemaType = { +export type InspectorFormSchema = { type?: | "string" | "number" @@ -35,8 +35,8 @@ export type JsonSchemaType = { description?: string; required?: string[]; default?: JsonValue; - properties?: Record; - items?: JsonSchemaType; + properties?: Record; + items?: InspectorFormSchema; // Array validation constraints minItems?: number; maxItems?: number; @@ -51,13 +51,35 @@ export type JsonSchemaType = { // Non-standard legacy support: titles for enum values enumNames?: string[]; const?: JsonValue; - oneOf?: (JsonSchemaType | JsonSchemaConst)[]; - anyOf?: (JsonSchemaType | JsonSchemaConst)[]; + oneOf?: (InspectorFormSchema | JsonSchemaConst)[]; + anyOf?: (InspectorFormSchema | JsonSchemaConst)[]; $ref?: string; }; export type JsonObject = { [key: string]: JsonValue }; +/** + * Narrow an MCP protocol schema (SDK `JsonSchemaType` — e.g. `Tool["inputSchema"]` + * / `outputSchema`, an elicitation `requestedSchema`) to the {@link + * InspectorFormSchema} subset the {@link SchemaForm} renderer understands. + * + * Under SDK v2 the protocol schema type (from `json-schema-typed`, exported as + * `JsonSchemaType` from `@modelcontextprotocol/client`) is structurally distinct + * from Inspector's form schema — same JSON on the wire, incompatible TS types. + * Rather than cast at every call site, callers pass the SDK schema through here. + * Returns `null` when there is no renderable object shape (missing schema, or a + * non-object schema the form can't build fields from); callers handle `null`. + */ +export function toFormSchema(schema: unknown): InspectorFormSchema | null { + if (schema == null || typeof schema !== "object" || Array.isArray(schema)) { + return null; + } + // Structural narrow: the SDK schema's fields are a superset of what the form + // reads (`type`, `properties`, `required`, `items`, …); the values the form + // never dereferences don't affect rendering. + return schema as InspectorFormSchema; +} + export type DataType = | "string" | "number" @@ -90,7 +112,7 @@ export function getDataType(value: JsonValue): DataType { * omits fields that have no default. */ export function collectSchemaDefaults( - schema: JsonSchemaType, + schema: InspectorFormSchema, ): Record { const properties = schema.properties ?? {}; const result: Record = {}; @@ -113,7 +135,7 @@ export function collectSchemaDefaults( * action until required fields are supplied. */ export function hasMissingRequiredFields( - schema: JsonSchemaType, + schema: InspectorFormSchema, values: Record, ): boolean { const required = schema.required ?? []; diff --git a/clients/web/src/utils/schemaUtils.test.ts b/clients/web/src/utils/schemaUtils.test.ts index 3fd0aaf93..2689f3179 100644 --- a/clients/web/src/utils/schemaUtils.test.ts +++ b/clients/web/src/utils/schemaUtils.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import type { Tool, JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool, JSONRPCMessage } from "@modelcontextprotocol/client"; import { cacheToolOutputSchemas, getToolOutputValidator, diff --git a/clients/web/src/utils/schemaUtils.ts b/clients/web/src/utils/schemaUtils.ts index 975f79b07..4e578a1c1 100644 --- a/clients/web/src/utils/schemaUtils.ts +++ b/clients/web/src/utils/schemaUtils.ts @@ -1,8 +1,8 @@ -import type { JsonValue, JsonSchemaType, JsonObject } from "./jsonUtils"; +import type { JsonValue, InspectorFormSchema, JsonObject } from "./jsonUtils"; import Ajv from "ajv"; import type { ValidateFunction } from "ajv"; -import type { Tool, JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; -import { isJSONRPCRequest } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool, JSONRPCMessage } from "@modelcontextprotocol/client"; +import { isJSONRPCRequest } from "@modelcontextprotocol/client"; const ajv = new Ajv(); @@ -87,9 +87,9 @@ export function hasOutputSchema(toolName: string): boolean { * @returns A default value matching the schema type */ export function generateDefaultValue( - schema: JsonSchemaType, + schema: InspectorFormSchema, propertyName?: string, - parentSchema?: JsonSchemaType, + parentSchema?: InspectorFormSchema, ): JsonValue { if ("default" in schema && schema.default !== undefined) { return schema.default; @@ -121,7 +121,8 @@ export function generateDefaultValue( // Include required properties OR optional properties that declare a default Object.entries(schema.properties).forEach(([key, prop]) => { const hasExplicitDefault = - "default" in prop && (prop as JsonSchemaType).default !== undefined; + "default" in prop && + (prop as InspectorFormSchema).default !== undefined; if (isPropertyRequired(key, schema) || hasExplicitDefault) { const value = generateDefaultValue(prop, key, schema); if (value !== undefined) { @@ -150,7 +151,7 @@ export function generateDefaultValue( */ export function isPropertyRequired( propertyName: string, - schema: JsonSchemaType, + schema: InspectorFormSchema, ): boolean { return schema.required?.includes(propertyName) ?? false; } @@ -162,9 +163,9 @@ export function isPropertyRequired( * @returns The resolved schema without $ref */ export function resolveRef( - schema: JsonSchemaType, - rootSchema: JsonSchemaType, -): JsonSchemaType { + schema: InspectorFormSchema, + rootSchema: InspectorFormSchema, +): InspectorFormSchema { if (!("$ref" in schema) || !schema.$ref) { return schema; } @@ -191,7 +192,7 @@ export function resolveRef( } } - return current as JsonSchemaType; + return current as InspectorFormSchema; } // For other types of references, return the original schema @@ -204,13 +205,15 @@ export function resolveRef( * @param schema The JSON schema to normalize * @returns A normalized schema or the original schema */ -export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType { +export function normalizeUnionType( + schema: InspectorFormSchema, +): InspectorFormSchema { // Handle anyOf with exactly string and null (FastMCP pattern) if ( schema.anyOf && schema.anyOf.length === 2 && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "string") && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "null") + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "string") && + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "null") ) { return { ...schema, type: "string", anyOf: undefined, nullable: true }; } @@ -219,8 +222,8 @@ export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType { if ( schema.anyOf && schema.anyOf.length === 2 && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "boolean") && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "null") + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "boolean") && + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "null") ) { return { ...schema, type: "boolean", anyOf: undefined, nullable: true }; } @@ -229,8 +232,8 @@ export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType { if ( schema.anyOf && schema.anyOf.length === 2 && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "number") && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "null") + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "number") && + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "null") ) { return { ...schema, type: "number", anyOf: undefined, nullable: true }; } @@ -239,8 +242,8 @@ export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType { if ( schema.anyOf && schema.anyOf.length === 2 && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "integer") && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "null") + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "integer") && + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "null") ) { return { ...schema, type: "integer", anyOf: undefined, nullable: true }; } @@ -249,8 +252,8 @@ export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType { if ( schema.anyOf && schema.anyOf.length === 2 && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "array") && - schema.anyOf.some((t) => (t as JsonSchemaType).type === "null") + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "array") && + schema.anyOf.some((t) => (t as InspectorFormSchema).type === "null") ) { return { ...schema, type: "array", anyOf: undefined, nullable: true }; } @@ -320,7 +323,7 @@ export function resolveRefsInMessage(message: JSONRPCMessage): JSONRPCMessage { return message; } - const requestedSchema = message.params.requestedSchema as JsonSchemaType; + const requestedSchema = message.params.requestedSchema as InspectorFormSchema; if (!requestedSchema?.properties) { return message; diff --git a/clients/web/src/utils/toolUtils.test.ts b/clients/web/src/utils/toolUtils.test.ts index 50ae679cb..c54d8a2ed 100644 --- a/clients/web/src/utils/toolUtils.test.ts +++ b/clients/web/src/utils/toolUtils.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { hasInputFields, resolveDisplayLabel } from "./toolUtils"; describe("resolveDisplayLabel", () => { diff --git a/clients/web/src/utils/toolUtils.ts b/clients/web/src/utils/toolUtils.ts index 00bb30aa1..247e31b41 100644 --- a/clients/web/src/utils/toolUtils.ts +++ b/clients/web/src/utils/toolUtils.ts @@ -1,4 +1,4 @@ -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; /** * Returns the display label for an MCP entity that follows the BaseMetadata diff --git a/clients/web/tsup.runner.config.ts b/clients/web/tsup.runner.config.ts index a0fb8a2ea..d369dfbcf 100644 --- a/clients/web/tsup.runner.config.ts +++ b/clients/web/tsup.runner.config.ts @@ -27,7 +27,8 @@ export default defineConfig({ 'atomically', 'chokidar', '@napi-rs/keyring', - '@modelcontextprotocol/sdk', + '@modelcontextprotocol/client', + '@modelcontextprotocol/core', ], esbuildOptions(options) { options.alias = { diff --git a/clients/web/vite.config.ts b/clients/web/vite.config.ts index d1acd9967..eeaee2ea0 100644 --- a/clients/web/vite.config.ts +++ b/clients/web/vite.config.ts @@ -48,7 +48,7 @@ export default defineConfig(({ command }) => { // build-time onwarn suppressions rather than re-hiding the symptom). plugins: [react(), honoMiddlewarePlugin(buildWebServerConfigFromEnv())], // Shared optimizeDeps exclusions so node-only packages - // (`@modelcontextprotocol/sdk/client/stdio.js`, `cross-spawn`, `which`) + // (`@modelcontextprotocol/client/stdio`, `cross-spawn`, `which`) // consumed by the dev backend aren't scanned for browser pre-bundling. // Browser code reaches the node-side stack via the Hono plugin only. // Dev server: force a full dep pre-bundle each launch (no stale cache). @@ -229,15 +229,14 @@ export default defineConfig(({ command }) => { // storage. 30s matches the v1.5 core/vitest.config.ts. testTimeout: 30000, hookTimeout: 30000, - // Inline the MCP SDK so vi.mock("@modelcontextprotocol/sdk/...") + // Inline the MCP SDK so vi.mock("@modelcontextprotocol/client") // hooks the same transformed copy that source files import. // Externalized node_modules are loaded via Node's loader and bypass - // Vitest's mock system. The explicit alias above pins the auth.js - // subpath to one canonical ID; inlining ensures the transformer - // pipeline owns the module rather than the Node loader. + // Vitest's mock system. Inlining ensures the transformer pipeline + // owns the module rather than the Node loader. server: { deps: { - inline: [/@modelcontextprotocol\/sdk/], + inline: [/@modelcontextprotocol\/(client|core)/], }, }, }, diff --git a/core/auth/cimd.ts b/core/auth/cimd.ts index b595b03dc..76f125cb3 100644 --- a/core/auth/cimd.ts +++ b/core/auth/cimd.ts @@ -1,8 +1,8 @@ import { discoverAuthorizationServerMetadata, discoverOAuthProtectedResourceMetadata, -} from "@modelcontextprotocol/sdk/client/auth.js"; -import type { OAuthClientInformation } from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; +import type { OAuthClientInformation } from "@modelcontextprotocol/client"; import { getAuthorizationServerUrl } from "./discovery.js"; import type { BaseOAuthClientProvider } from "./providers.js"; diff --git a/core/auth/connection-state.ts b/core/auth/connection-state.ts index f0642019a..8d20d7e29 100644 --- a/core/auth/connection-state.ts +++ b/core/auth/connection-state.ts @@ -1,13 +1,18 @@ import type { OAuthClientInformation, OAuthTokens, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import type { EnterpriseManagedAuthIdpConfig } from "../client/types.js"; import { getEmaIdpLoginState, normalizeIdpIssuer } from "./ema/idpSession.js"; import { idpOAuthStorageKey } from "./ema/storage.js"; import { isJwtExpired } from "./ema/jwt.js"; import type { OAuthStorage } from "./storage.js"; -import type { AuthProtocol, OAuthClientRegistrationKind, OAuthConnectionState, OAuthFlowState } from "./types.js"; +import type { + AuthProtocol, + OAuthClientRegistrationKind, + OAuthConnectionState, + OAuthFlowState, +} from "./types.js"; import { authProtocolFromEnterpriseManaged } from "./types.js"; export interface BuildOAuthConnectionStateParams { @@ -20,9 +25,7 @@ export interface BuildOAuthConnectionStateParams { } /** True when persisted tokens include a non-expired access token (JWT exp when parseable). */ -export function isAccessTokenUsable( - tokens: OAuthTokens | undefined, -): boolean { +export function isAccessTokenUsable(tokens: OAuthTokens | undefined): boolean { if (!tokens?.access_token) return false; return !isJwtExpired(tokens.access_token); } @@ -30,10 +33,9 @@ export function isAccessTokenUsable( function resolveClient( preregistered: OAuthClientInformation | undefined, dynamic: OAuthClientInformation | undefined, - dynamicRegistrationKind: Extract< - OAuthClientRegistrationKind, - "dcr" | "cimd" - > | undefined, + dynamicRegistrationKind: + | Extract + | undefined, ): OAuthConnectionState["client"] | undefined { if (preregistered?.client_id) { return { @@ -84,7 +86,7 @@ export async function buildOAuthConnectionState( ? flowTokens : storedTokens && isAccessTokenUsable(storedTokens) ? storedTokens - : flowTokens ?? storedTokens; + : (flowTokens ?? storedTokens); const preregistered = await storage.getClientInformation(serverUrl, true); const dynamic = await storage.getClientInformation(serverUrl); @@ -123,7 +125,9 @@ export async function buildOAuthConnectionState( const idp = params.enterpriseManagedAuth.idp; const issuer = normalizeIdpIssuer(idp.issuer); const idpSession = await getEmaIdpLoginState(storage, issuer); - const idpMetadata = await storage.getServerMetadata(idpOAuthStorageKey(issuer)); + const idpMetadata = await storage.getServerMetadata( + idpOAuthStorageKey(issuer), + ); state.ema = { idpIssuer: issuer, idpClientId: idp.clientId, diff --git a/core/auth/discovery.ts b/core/auth/discovery.ts index 1a860ab9f..5109cd231 100644 --- a/core/auth/discovery.ts +++ b/core/auth/discovery.ts @@ -1,5 +1,5 @@ -import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/sdk/client/auth.js"; -import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/sdk/shared/auth.js"; +import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/client"; +import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/client"; import { parseHttpUrl } from "./utils.js"; /** diff --git a/core/auth/ema/emaFlow.ts b/core/auth/ema/emaFlow.ts index 693749865..eb3f40f0b 100644 --- a/core/auth/ema/emaFlow.ts +++ b/core/auth/ema/emaFlow.ts @@ -1,4 +1,4 @@ -import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthTokens } from "@modelcontextprotocol/client"; import type { OAuthStorage } from "../storage.js"; import type { EnterpriseManagedAuthIdpConfig } from "../../client/types.js"; import { @@ -125,11 +125,13 @@ export async function startEmaIdpAuthorization( export async function completeEmaIdpAuthorizationAndMint( config: EmaFlowConfig, authorizationCode: string, + iss?: string, ): Promise { try { await completeIdpOidcAuthorization({ idp: config.idp, authorizationCode, + iss, redirectUrl: config.redirectUrl, storage: config.storage, fetchFn: config.fetchFn, diff --git a/core/auth/ema/idpOidc.ts b/core/auth/ema/idpOidc.ts index 0f0237c89..60f3bdbb6 100644 --- a/core/auth/ema/idpOidc.ts +++ b/core/auth/ema/idpOidc.ts @@ -2,12 +2,12 @@ import { discoverAuthorizationServerMetadata, exchangeAuthorization, startAuthorization, -} from "@modelcontextprotocol/sdk/client/auth.js"; +} from "@modelcontextprotocol/client"; import type { OAuthClientInformation, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; -import { OAuthMetadataSchema } from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; +import { OAuthMetadataSchema } from "@modelcontextprotocol/core"; import type { OAuthStorage } from "../storage.js"; import type { EnterpriseManagedAuthIdpConfig } from "../../client/types.js"; import { generateOAuthState, parseHttpUrl } from "../utils.js"; @@ -51,7 +51,9 @@ export async function discoverIdpMetadata( fetchFn, }); if (!metadata) { - throw new Error(`Failed to discover OIDC metadata for IdP issuer ${issuer}`); + throw new Error( + `Failed to discover OIDC metadata for IdP issuer ${issuer}`, + ); } return OAuthMetadataSchema.parse(metadata); } @@ -63,7 +65,11 @@ export async function startIdpOidcAuthorization(params: { fetchFn?: typeof fetch; }): Promise<{ authorizationUrl: URL }> { const issuer = normalizeIdpIssuer(params.idp.issuer); - const metadata = await resolveIdpMetadata(issuer, params.storage, params.fetchFn); + const metadata = await resolveIdpMetadata( + issuer, + params.storage, + params.fetchFn, + ); const clientInformation = idpClientInformation(params.idp); const storageKey = idpOAuthStorageKey(issuer); const state = generateOAuthState(); @@ -91,6 +97,12 @@ export async function startIdpOidcAuthorization(params: { export async function completeIdpOidcAuthorization(params: { idp: EnterpriseManagedAuthIdpConfig; authorizationCode: string; + /** + * RFC 9207 `iss` from the IdP callback. Forwarded to the SDK, which rejects + * the exchange when the IdP metadata advertises + * `authorization_response_iss_parameter_supported` but no `iss` arrives. + */ + iss?: string; redirectUrl: string; storage: OAuthStorage; fetchFn?: typeof fetch; @@ -121,6 +133,7 @@ export async function completeIdpOidcAuthorization(params: { metadata, clientInformation, authorizationCode: params.authorizationCode, + iss: params.iss, codeVerifier, redirectUri: params.redirectUrl, fetchFn: params.fetchFn, diff --git a/core/auth/ema/resourceContext.ts b/core/auth/ema/resourceContext.ts index d8345d996..0ed0e43b5 100644 --- a/core/auth/ema/resourceContext.ts +++ b/core/auth/ema/resourceContext.ts @@ -2,9 +2,9 @@ import { discoverAuthorizationServerMetadata, discoverOAuthProtectedResourceMetadata, selectResourceURL, -} from "@modelcontextprotocol/sdk/client/auth.js"; -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; -import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; +import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/client"; import { getAuthorizationServerUrl } from "../discovery.js"; export interface EmaResourceContext { diff --git a/core/auth/ema/tokenEndpoint.ts b/core/auth/ema/tokenEndpoint.ts index 3652cb7f3..bc76a59fd 100644 --- a/core/auth/ema/tokenEndpoint.ts +++ b/core/auth/ema/tokenEndpoint.ts @@ -1,8 +1,8 @@ -import { selectClientAuthMethod } from "@modelcontextprotocol/sdk/client/auth.js"; +import { selectClientAuthMethod } from "@modelcontextprotocol/client"; import type { OAuthClientInformation, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; export async function parseOAuthTokenErrorResponse( response: Response, diff --git a/core/auth/ema/transportProvider.ts b/core/auth/ema/transportProvider.ts index 5a16226b0..b130fff95 100644 --- a/core/auth/ema/transportProvider.ts +++ b/core/auth/ema/transportProvider.ts @@ -1,9 +1,9 @@ -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; import type { OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import type { BaseOAuthClientProvider } from "../providers.js"; import { refreshEmaResourceTokens, @@ -12,10 +12,7 @@ import { } from "./emaFlow.js"; import { isJwtExpired } from "./jwt.js"; -function isAccessTokenUsable( - tokens: OAuthTokens, - skewMs = 60_000, -): boolean { +function isAccessTokenUsable(tokens: OAuthTokens, skewMs = 60_000): boolean { if (!tokens.access_token) return false; return !isJwtExpired(tokens.access_token, skewMs); } @@ -32,10 +29,7 @@ export class EmaTransportOAuthProvider implements OAuthClientProvider { private readonly inner: BaseOAuthClientProvider; private readonly emaConfig: EmaFlowConfig; - constructor( - inner: BaseOAuthClientProvider, - emaConfig: EmaFlowConfig, - ) { + constructor(inner: BaseOAuthClientProvider, emaConfig: EmaFlowConfig) { this.inner = inner; this.emaConfig = emaConfig; } diff --git a/core/auth/ema/wire.ts b/core/auth/ema/wire.ts index a56456cfa..a5f0170a8 100644 --- a/core/auth/ema/wire.ts +++ b/core/auth/ema/wire.ts @@ -1,9 +1,9 @@ -import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/sdk/client/auth.js"; +import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/client"; import type { OAuthClientInformation, OAuthTokens, -} from "@modelcontextprotocol/sdk/shared/auth.js"; -import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; +import { OAuthTokensSchema } from "@modelcontextprotocol/core"; import type { EnterpriseManagedAuthIdpConfig } from "../../client/types.js"; import { GRANT_TYPE_JWT_BEARER, diff --git a/core/auth/mcpAuth.ts b/core/auth/mcpAuth.ts index 9e52a7695..4638605e5 100644 --- a/core/auth/mcpAuth.ts +++ b/core/auth/mcpAuth.ts @@ -17,10 +17,23 @@ import { registerClient, selectResourceURL, startAuthorization, -} from "@modelcontextprotocol/sdk/client/auth.js"; -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; -import type { FetchLike } from "@modelcontextprotocol/sdk/shared/transport.js"; -import { InvalidClientMetadataError } from "@modelcontextprotocol/sdk/server/auth/errors.js"; +} from "@modelcontextprotocol/client"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; +import type { FetchLike } from "@modelcontextprotocol/client"; +/** + * Thrown when the OAuth client metadata the Inspector would register is + * invalid (e.g. a non-HTTPS `clientMetadataUrl`). SDK v2 moved the original + * `InvalidClientMetadataError` into `@modelcontextprotocol/server-legacy`, + * whose `/auth` entry pulls in Node/Express server handlers that must never + * reach the browser bundle. This local class preserves the thrown type and + * message for the Inspector's client-side validation without that dependency. + */ +export class InvalidClientMetadataError extends Error { + constructor(message: string) { + super(message); + this.name = "InvalidClientMetadataError"; + } +} export type McpAuthResult = "AUTHORIZED" | "REDIRECT"; @@ -31,7 +44,7 @@ export type McpAuthResult = "AUTHORIZED" | "REDIRECT"; export interface McpAuthOptions { serverUrl: string | URL; authorizationCode?: string; - /** RFC 9207 callback `iss` — forwarded on v2 upgrade; ignored by v1 SDK `auth()`. */ + /** RFC 9207 callback `iss`, validated by the SDK against the metadata issuer. */ iss?: string; scope?: string; resourceMetadataUrl?: URL; @@ -61,6 +74,7 @@ export async function mcpAuth( return sdkAuth(provider, { serverUrl: options.serverUrl, authorizationCode: options.authorizationCode, + iss: options.iss, scope: options.scope, resourceMetadataUrl: options.resourceMetadataUrl, fetchFn: options.fetchFn, diff --git a/core/auth/node/oauth-callback-server.ts b/core/auth/node/oauth-callback-server.ts index 2b5149ec2..37a0cce6a 100644 --- a/core/auth/node/oauth-callback-server.ts +++ b/core/auth/node/oauth-callback-server.ts @@ -30,6 +30,8 @@ function escapeHtml(s: string): string { export type OAuthCallbackHandler = (params: { code: string; state?: string; + /** RFC 9207 `iss`, when the authorization server sends it. */ + iss?: string; }) => Promise; export type OAuthErrorHandler = (params: { @@ -181,7 +183,7 @@ export class OAuthCallbackServer { this.handled = true; const cb = this.onCallback; if (cb) { - cb({ code: params.code, state }) + cb({ code: params.code, state, iss: params.iss }) .then(() => { send(200, SUCCESS_HTML); void this.stop(); diff --git a/core/auth/node/runner-interactive-oauth.ts b/core/auth/node/runner-interactive-oauth.ts index f68a32802..edbb394d0 100644 --- a/core/auth/node/runner-interactive-oauth.ts +++ b/core/auth/node/runner-interactive-oauth.ts @@ -9,7 +9,7 @@ import type { RunnerOAuthCallbackConfig } from "./runner-oauth-callback.js"; export interface RunnerInteractiveOAuthClient { authenticate(): Promise; beginInteractiveAuthorization(authorizationUrl: URL): Promise; - completeOAuthFlow(authorizationCode: string): Promise; + completeOAuthFlow(authorizationCode: string, iss?: string): Promise; checkAuthChallengeSatisfied(challenge: AuthChallenge): Promise; } @@ -67,7 +67,7 @@ export async function runRunnerInteractiveOAuth( path: options.callbackListen.pathname, onCallback: async (params) => { try { - await options.client.completeOAuthFlow(params.code); + await options.client.completeOAuthFlow(params.code, params.iss); flowResolve(); } catch (err) { flowReject(err instanceof Error ? err : new Error(String(err))); diff --git a/core/auth/oauth-storage.ts b/core/auth/oauth-storage.ts index fe5a7c2ed..cef19cc38 100644 --- a/core/auth/oauth-storage.ts +++ b/core/auth/oauth-storage.ts @@ -2,11 +2,11 @@ import type { OAuthClientInformation, OAuthTokens, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import { OAuthClientInformationSchema, OAuthTokensSchema, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/core"; import type { OAuthStorage } from "./storage.js"; import { type OAuthMemoryStore, type ServerOAuthState } from "./store.js"; import type { OAuthPersistBackend } from "./oauth-persist.js"; @@ -180,7 +180,9 @@ export class OAuthStorageBase implements OAuthStorage { async clearCodeVerifier(serverUrl: string): Promise { await this.ensureLoaded(); - this.memory.getState().setServerState(serverUrl, { codeVerifier: undefined }); + this.memory + .getState() + .setServerState(serverUrl, { codeVerifier: undefined }); await this.persist(); } @@ -213,7 +215,9 @@ export class OAuthStorageBase implements OAuthStorage { metadata: OAuthMetadata, ): Promise { await this.ensureLoaded(); - this.memory.getState().setServerState(serverUrl, { serverMetadata: metadata }); + this.memory + .getState() + .setServerState(serverUrl, { serverMetadata: metadata }); await this.persist(); } diff --git a/core/auth/providers.ts b/core/auth/providers.ts index b9cbdf301..0ce422151 100644 --- a/core/auth/providers.ts +++ b/core/auth/providers.ts @@ -1,10 +1,13 @@ -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { + OAuthClientProvider, + OAuthClientInformationContext, +} from "@modelcontextprotocol/client"; import type { OAuthClientInformation, OAuthClientMetadata, OAuthTokens, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import type { OAuthStorage, SaveClientInformationOptions } from "./storage.js"; import { generateOAuthState } from "./utils.js"; @@ -201,11 +204,24 @@ export class BaseOAuthClientProvider implements OAuthClientProvider { async saveClientInformation( clientInformation: OAuthClientInformation, - options?: SaveClientInformationOptions, + // SDK v2's `OAuthClientProvider.saveClientInformation` passes an + // `OAuthClientInformationContext` ({ issuer }); our own DCR/CIMD callers + // pass `SaveClientInformationOptions` ({ registrationKind }). Accept either + // and narrow on the discriminating key. Per-issuer keying (SEP-2352) is a + // later card — for now we only read the registration kind. + options?: SaveClientInformationOptions | OAuthClientInformationContext, ): Promise { - await this.storage.saveClientInformation(this.serverUrl, clientInformation, { - registrationKind: options?.registrationKind ?? "dcr", - }); + const registrationKind = + options && "registrationKind" in options + ? options.registrationKind + : "dcr"; + await this.storage.saveClientInformation( + this.serverUrl, + clientInformation, + { + registrationKind, + }, + ); } async saveScope(scope: string | undefined): Promise { diff --git a/core/auth/storage.ts b/core/auth/storage.ts index 7ac9bf1ee..1af4d61f1 100644 --- a/core/auth/storage.ts +++ b/core/auth/storage.ts @@ -2,7 +2,7 @@ import type { OAuthClientInformation, OAuthTokens, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import type { OAuthClientRegistrationKind } from "./types.js"; /** diff --git a/core/auth/store.ts b/core/auth/store.ts index 08e2084b0..ba72ff6fc 100644 --- a/core/auth/store.ts +++ b/core/auth/store.ts @@ -7,7 +7,7 @@ import type { OAuthClientInformation, OAuthTokens, OAuthMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import type { IdpSessionState, OAuthClientRegistrationKind, diff --git a/core/auth/types.ts b/core/auth/types.ts index b555c9515..82b0bfb42 100644 --- a/core/auth/types.ts +++ b/core/auth/types.ts @@ -4,7 +4,7 @@ import type { OAuthClientInformationFull, OAuthTokens, OAuthProtectedResourceMetadata, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; // OAuth flow steps export type OAuthStep = @@ -105,6 +105,12 @@ export type CallbackParams = successful: true; // The authorization code is generated by the authorization server. code: string; + // RFC 9207 `iss`. Present only when the authorization server includes it. + // Forwarded to the SDK, which validates it against the issuer in the + // validated metadata (RFC 9207 §2.4) to detect mix-up attacks. Servers + // advertising `authorization_response_iss_parameter_supported: true` MUST + // send it, and the SDK rejects the callback when it is missing. + iss?: string; } | { successful: false; diff --git a/core/auth/utils.ts b/core/auth/utils.ts index c15bb974b..95ebccca1 100644 --- a/core/auth/utils.ts +++ b/core/auth/utils.ts @@ -89,7 +89,10 @@ export const parseOAuthCallbackParams = (location: string): CallbackParams => { const code = params.get("code"); if (code) { - return { successful: true, code }; + const iss = params.get("iss"); + return iss === null + ? { successful: true, code } + : { successful: true, code, iss }; } const error = params.get("error"); diff --git a/core/json/jsonUtils.ts b/core/json/jsonUtils.ts index cda2d0db3..d9a099c61 100644 --- a/core/json/jsonUtils.ts +++ b/core/json/jsonUtils.ts @@ -1,4 +1,4 @@ -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; /** * JSON value type used across the inspector project diff --git a/core/mcp/__tests__/fakeInspectorClient.ts b/core/mcp/__tests__/fakeInspectorClient.ts index 07ef71826..8eed2dd04 100644 --- a/core/mcp/__tests__/fakeInspectorClient.ts +++ b/core/mcp/__tests__/fakeInspectorClient.ts @@ -12,11 +12,11 @@ import type { LoggingLevel, Prompt, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, ServerCapabilities, Task, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import { InspectorClientEventTarget } from "../inspectorClientEventTarget.js"; import type { AppRendererClient, diff --git a/core/mcp/apps.ts b/core/mcp/apps.ts index 937acc736..366e5a21a 100644 --- a/core/mcp/apps.ts +++ b/core/mcp/apps.ts @@ -1,8 +1,5 @@ import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge"; -import type { - ReadResourceResult, - Tool, -} from "@modelcontextprotocol/sdk/types.js"; +import type { ReadResourceResult, Tool } from "@modelcontextprotocol/client"; /** * Returns the UI resource URI advertised by an MCP App tool, or `undefined` @@ -22,7 +19,13 @@ import type { * `@inspector/core`. */ export function getAppResourceUri(tool: Tool): string | undefined { - return getToolUiResourceUri(tool); + // `@modelcontextprotocol/ext-apps` still peers on SDK v1, so its + // `getToolUiResourceUri` param is typed as the v1 `Tool`. The runtime shape + // this reads (`_meta.ui.resourceUri`) is identical under v2, so cast at this + // single boundary. TODO: drop the cast when ext-apps#702 ships a v2 peer. + return getToolUiResourceUri( + tool as Parameters[0], + ); } /** diff --git a/core/mcp/elicitationCreateMessage.ts b/core/mcp/elicitationCreateMessage.ts index 830bcd5d2..8589a4bc3 100644 --- a/core/mcp/elicitationCreateMessage.ts +++ b/core/mcp/elicitationCreateMessage.ts @@ -1,8 +1,5 @@ -import type { - ElicitRequest, - ElicitResult, -} from "@modelcontextprotocol/sdk/types.js"; -import { RELATED_TASK_META_KEY } from "@modelcontextprotocol/sdk/types.js"; +import type { ElicitRequest, ElicitResult } from "@modelcontextprotocol/client"; +import { RELATED_TASK_META_KEY } from "@modelcontextprotocol/client"; export type { ElicitRequest, ElicitResult }; diff --git a/core/mcp/inspectorClient.ts b/core/mcp/inspectorClient.ts index 8ae4fccbd..c2452dc3e 100644 --- a/core/mcp/inspectorClient.ts +++ b/core/mcp/inspectorClient.ts @@ -1,4 +1,4 @@ -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { Client } from "@modelcontextprotocol/client"; import type { MCPServerConfig, StderrLogEntry, @@ -58,10 +58,12 @@ import type { LoggingLevel, Tool, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, Prompt, Root, + CreateMessageRequest, CreateMessageResult, + CreateTaskResult, ElicitRequest, ElicitResult, ElicitRequestURLParams, @@ -76,34 +78,42 @@ import type { ReadResourceRequest, GetPromptRequest, CompleteRequest, -} from "@modelcontextprotocol/sdk/types.js"; -import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; +} from "@modelcontextprotocol/client"; +import type { Transport } from "@modelcontextprotocol/client"; import type { RequestOptions, ProgressCallback, -} from "@modelcontextprotocol/sdk/shared/protocol.js"; + VersionNegotiationOptions, +} from "@modelcontextprotocol/client"; +import { ProtocolError, ProtocolErrorCode } from "@modelcontextprotocol/client"; import { - CreateMessageRequestSchema, - ElicitRequestSchema, EmptyResultSchema, - ListRootsRequestSchema, - ElicitationCompleteNotificationSchema, - RootsListChangedNotificationSchema, - ToolListChangedNotificationSchema, - ResourceListChangedNotificationSchema, - PromptListChangedNotificationSchema, - ResourceUpdatedNotificationSchema, CallToolResultSchema, - McpError, - ErrorCode, + // Task request schemas — used for `.shape.params` in the 3-arg custom + // `setRequestHandler` form (tasks/* are excluded from v2's spec-method set). ListTasksRequestSchema, GetTaskRequestSchema, GetTaskPayloadRequestSchema, CancelTaskRequestSchema, TaskStatusNotificationSchema, -} from "@modelcontextprotocol/sdk/types.js"; -import type { ClientResult } from "@modelcontextprotocol/sdk/types.js"; -import { AjvJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/ajv"; + // Task result schemas — explicit result schemas for the raw requestor-task + // requests that replace the removed `client.experimental.tasks.*` helpers. + CreateTaskResultSchema, + GetTaskResultSchema, + CancelTaskResultSchema, + ListTasksResultSchema, + // List result schemas — used by the single-page list methods below. SDK v2's + // high-level `client.listTools()` etc. auto-aggregate ALL pages (returning + // `nextCursor: undefined`), which defeats the Inspector's pagination-debugging + // purpose. Drop to raw `client.request` with these explicit schemas so each + // call fetches exactly one page and surfaces the server's `nextCursor`. + ListToolsResultSchema, + ListResourcesResultSchema, + ListResourceTemplatesResultSchema, + ListPromptsResultSchema, +} from "@modelcontextprotocol/core"; +import type { ClientResult } from "@modelcontextprotocol/client"; +import { AjvJsonSchemaValidator } from "@modelcontextprotocol/client/validators/ajv"; import { validateToolOutput } from "./toolOutputValidation.js"; import { TasksListChangedNotificationSchema } from "./taskNotificationSchemas.js"; import { @@ -111,7 +121,7 @@ import { convertToolParameters, convertPromptArguments, } from "../json/jsonUtils.js"; -import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js"; +import { UriTemplate } from "@modelcontextprotocol/client"; import { InspectorClientEventTarget, type TaskWithOptionalCreatedAt, @@ -138,7 +148,7 @@ import { type AuthChallengeOutcome, type HandleAuthChallengeOptions, } from "../auth/challenge.js"; -import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthTokens } from "@modelcontextprotocol/client"; import { silentLogger, type InspectorLogger } from "../logging/logger.js"; import { createFetchTracker } from "./fetchTracking.js"; import { OAuthManager, type OAuthManagerConfig } from "./oauthManager.js"; @@ -170,6 +180,31 @@ const MAX_URL_ELICITATION_RETRIES = 5; */ const TOOL_CALL_CANCELLED_REASON = "Tool call cancelled by user"; +/** + * Fallback poll cadence (ms) for {@link InspectorClient.pollTaskToolCall} when a + * task does not advertise its own `pollInterval`. Replaces the cadence the + * removed SDK `experimental.tasks.callToolStream` helper managed internally. + */ +const DEFAULT_TASK_POLL_INTERVAL_MS = 500; + +/** + * Extract the method literal from an MCP notification Zod schema (e.g. + * `ToolListChangedNotificationSchema`), or `undefined` if the shape isn't + * recognized. Used by the App-renderer client proxy to translate the SDK-v1 + * schema-first `setNotificationHandler` API — which `@modelcontextprotocol/ext-apps` + * still uses — into SDK v2's method-string form. Reads the `method` literal off + * the notification schema's `shape` (the shape both the v1 SDK and v2 core + * schemas expose). + */ +function notificationMethodFromSchema(schema: unknown): string | undefined { + if (schema !== null && typeof schema === "object") { + const literal = (schema as { shape?: { method?: { value?: unknown } } }) + .shape?.method?.value; + if (typeof literal === "string") return literal; + } + return undefined; +} + /** * The descriptor for a single tools/call, threaded through the retry loop and * each attempt. Bundled into one object so `callToolWithRetries`/`attemptToolCall` @@ -353,7 +388,15 @@ export class InspectorClient extends InspectorClientEventTarget { // Transport is created in connect() (single place for create / wrap / attach). // Build client capabilities - const clientOptions: { capabilities?: ClientCapabilities } = {}; + const clientOptions: { + capabilities?: ClientCapabilities; + versionNegotiation?: VersionNegotiationOptions; + } = { + // Pin to the legacy (2025-11-25) protocol so this migration stays + // behavior-neutral: nothing 2026-era goes on the wire. Era/version + // selection becomes per-server configuration in a later card. + versionNegotiation: { mode: "legacy" }, + }; const capabilities: ClientCapabilities = {}; if (this.sample) { capabilities.sampling = {}; @@ -603,6 +646,55 @@ export class InspectorClient extends InspectorClientEventTarget { ); } + /** + * Route a receiver (server-initiated) task-augmented `sampling/createMessage` + * or `elicitation/create` response around the v2 Client's result validation. + * + * SDK v2's `Client` wraps every spec request handler (`_wrapHandler`) to + * validate the result it returns — for sampling/elicitation it checks the + * value against `CreateMessageResult` / `ElicitResult` and rejects anything + * else with a `-32602`. The 2025-11-25 task flow answers a task-augmented + * request with a `CreateTaskResult` (`{ task }`), which that validation + * rejects — breaking server-initiated tasks that worked on the legacy client. + * + * There is no public seam to opt a handler out of result validation, so we + * swap the wrapped entry in the Protocol's private `_requestHandlers` map for + * one that dispatches the task-augmented branch straight through the raw + * handler (whose `{ task }` return then rides the legacy codec's pass-through + * `encodeResult` to the wire), while ordinary (non-task) requests keep the + * validating path. Mirrors the bypass a legacy server needs to emit `{ task }`. + * Delete once the SDK models task-augmented results natively (see #1624 stack). + */ + private installReceiverTaskResponseBypass( + method: "sampling/createMessage" | "elicitation/create", + rawHandler: ( + request: CreateMessageRequest & ElicitRequest, + ) => Promise | Promise, + ): void { + if (!this.client) return; + // SDK gap: `Client` exposes no public way to (a) read a registered request + // handler or (b) opt one out of the result validation its `_wrapHandler` + // installs, so we reach the private `_requestHandlers` map through a + // narrowed cast. A public "register a raw/unvalidated handler" API — or a + // handler-result type that includes `CreateTaskResult` — would remove both + // this cast and the ones on the sampling/elicit returns above. + const internal = this.client as unknown as { + _requestHandlers: Map< + string, + (request: unknown, ctx: unknown) => unknown + >; + }; + const validating = internal._requestHandlers.get(method); + if (!validating) return; + internal._requestHandlers.set(method, (request, ctx) => { + const task = (request as { params?: { task?: unknown } })?.params?.task; + if (this.receiverTasks && task != null) { + return rawHandler(request as CreateMessageRequest & ElicitRequest); + } + return validating(request, ctx); + }); + } + private createReceiverTask(opts: { ttl?: number; initialStatus: Task["status"]; @@ -685,7 +777,10 @@ export class InspectorClient extends InspectorClientEventTarget { private async getReceiverTaskPayload(taskId: string): Promise { const record = this.receiverTaskRecords.get(taskId); if (!record) { - throw new McpError(ErrorCode.InvalidParams, `Unknown taskId: ${taskId}`); + throw new ProtocolError( + ProtocolErrorCode.InvalidParams, + `Unknown taskId: ${taskId}`, + ); } return record.payloadPromise; } @@ -693,7 +788,10 @@ export class InspectorClient extends InspectorClientEventTarget { private cancelReceiverTask(taskId: string): Task { const record = this.receiverTaskRecords.get(taskId); if (!record) { - throw new McpError(ErrorCode.InvalidParams, `Unknown taskId: ${taskId}`); + throw new ProtocolError( + ProtocolErrorCode.InvalidParams, + `Unknown taskId: ${taskId}`, + ); } if (InspectorClient.isTerminalTaskStatus(record.task.status)) { return record.task; @@ -906,7 +1004,9 @@ export class InspectorClient extends InspectorClientEventTarget { // Set up sampling request handler if sampling capability is enabled if (this.sample && this.client) { - this.client.setRequestHandler(CreateMessageRequestSchema, (request) => { + const samplingHandler = ( + request: CreateMessageRequest, + ): Promise => { const paramsTask = (request.params as { task?: { ttl?: number } }) ?.task; if (this.receiverTasks && paramsTask != null) { @@ -946,7 +1046,24 @@ export class InspectorClient extends InspectorClientEventTarget { ); this.addPendingSample(samplingRequest); })(); - return Promise.resolve({ task: record.task }); + // Task-augmented (2025-11-25) response: the server sent a + // task-augmented `sampling/createMessage`, so we reply with a + // `CreateTaskResult` (`{ task }`) rather than a `CreateMessageResult`. + // The v2 Client validates a spec handler's result and would reject + // `{ task }` with -32602; `installReceiverTaskResponseBypass` below + // routes this task-augmented branch around that validation so the + // legacy `{ task }` response reaches the wire. `taskResult` is typed + // as `CreateTaskResult` so its shape IS checked; the unavoidable + // `as unknown as CreateMessageResult` bridges the SDK gap — the 2-arg + // `setRequestHandler` overload types a sampling handler's return as + // `CreateMessageResult` only and doesn't model the (deprecated but + // wire-valid) task-augmented `CreateTaskResult`. A handler-result + // union `CreateMessageResult | CreateTaskResult` on the SDK side + // would remove this cast. + const taskResult: CreateTaskResult = { task: record.task }; + return Promise.resolve( + taskResult as unknown as CreateMessageResult, + ); } return new Promise((resolve, reject) => { const samplingRequest = new SamplingCreateMessage( @@ -961,12 +1078,24 @@ export class InspectorClient extends InspectorClientEventTarget { ); this.addPendingSample(samplingRequest); }); - }); + }; + this.client.setRequestHandler( + "sampling/createMessage", + samplingHandler, + ); + if (this.receiverTasks) { + this.installReceiverTaskResponseBypass( + "sampling/createMessage", + samplingHandler, + ); + } } // Set up elicitation request handler if elicitation capability is enabled if (this.elicit && this.client) { - this.client.setRequestHandler(ElicitRequestSchema, (request) => { + const elicitHandler = ( + request: ElicitRequest, + ): Promise => { const paramsTask = (request.params as { task?: { ttl?: number } }) ?.task; if (this.receiverTasks && paramsTask != null) { @@ -1005,7 +1134,16 @@ export class InspectorClient extends InspectorClientEventTarget { ); this.addPendingElicitation(elicitationRequest); })(); - return Promise.resolve({ task: record.task }); + // Task-augmented (2025-11-25) response — see the sampling handler + // above. Reply with a `CreateTaskResult` (`{ task }`), routed around + // the v2 Client's result validation by + // `installReceiverTaskResponseBypass` below. `taskResult` is typed so + // its shape is checked; the `as unknown as ElicitResult` bridges the + // same SDK gap as the sampling handler — the 2-arg `setRequestHandler` + // overload types an elicitation handler's return as `ElicitResult` + // only and doesn't model the task-augmented `CreateTaskResult`. + const taskResult: CreateTaskResult = { task: record.task }; + return Promise.resolve(taskResult as unknown as ElicitResult); } return new Promise((resolve) => { const elicitationRequest = new ElicitationCreateMessage( @@ -1017,44 +1155,67 @@ export class InspectorClient extends InspectorClientEventTarget { ); this.addPendingElicitation(elicitationRequest); }); - }); + }; + this.client.setRequestHandler("elicitation/create", elicitHandler); + if (this.receiverTasks) { + this.installReceiverTaskResponseBypass( + "elicitation/create", + elicitHandler, + ); + } } // Set up roots/list request handler if roots capability is enabled if (this.roots !== undefined && this.client) { - this.client.setRequestHandler(ListRootsRequestSchema, async () => { + this.client.setRequestHandler("roots/list", async () => { return { roots: this.roots ?? [] }; }); } - // Set up receiver-task request handlers (server polls us for tasks/list, tasks/get, tasks/result, tasks/cancel) + // Set up receiver-task request handlers (server polls us for tasks/list, + // tasks/get, tasks/result, tasks/cancel). SDK v2 removed tasks from the + // spec-method set, so these register through the 3-arg custom form with an + // explicit params schema (from the deprecated-but-importable task request + // schemas). The `result` schema is intentionally omitted so the SDK does + // not validate our responder return — matching v1, where only the + // requester validated (our receiver `Task` may omit fields a strict result + // schema would require). if (this.receiverTasks && this.client) { - this.client.setRequestHandler(ListTasksRequestSchema, async () => ({ - tasks: this.listReceiverTasks(), - })); - this.client.setRequestHandler(GetTaskRequestSchema, async (req) => { - const record = this.getReceiverTask(req.params.taskId); - if (!record) { - throw new McpError( - ErrorCode.InvalidParams, - `Unknown taskId: ${req.params.taskId}`, - ); - } - return record.task; - }); this.client.setRequestHandler( - GetTaskPayloadRequestSchema, - async (req) => this.getReceiverTaskPayload(req.params.taskId), + "tasks/list", + { params: ListTasksRequestSchema.shape.params }, + async () => ({ tasks: this.listReceiverTasks() }), + ); + this.client.setRequestHandler( + "tasks/get", + { params: GetTaskRequestSchema.shape.params }, + async (params) => { + const record = this.getReceiverTask(params.taskId); + if (!record) { + throw new ProtocolError( + ProtocolErrorCode.InvalidParams, + `Unknown taskId: ${params.taskId}`, + ); + } + return record.task; + }, + ); + this.client.setRequestHandler( + "tasks/result", + { params: GetTaskPayloadRequestSchema.shape.params }, + async (params) => this.getReceiverTaskPayload(params.taskId), ); - this.client.setRequestHandler(CancelTaskRequestSchema, async (req) => - this.cancelReceiverTask(req.params.taskId), + this.client.setRequestHandler( + "tasks/cancel", + { params: CancelTaskRequestSchema.shape.params }, + async (params) => this.cancelReceiverTask(params.taskId), ); } // Set up notification handler for roots/list_changed from server if (this.client) { this.client.setNotificationHandler( - RootsListChangedNotificationSchema, + "notifications/roots/list_changed", async () => { // Dispatch event to notify UI that server's roots may have changed // Note: rootsChange is a CustomEvent with Root[] payload, not a signal event @@ -1074,7 +1235,7 @@ export class InspectorClient extends InspectorClientEventTarget { this.capabilities?.tools?.listChanged ) { this.client.setNotificationHandler( - ToolListChangedNotificationSchema, + "notifications/tools/list_changed", async () => { // Always fire notification event (for tracking) this.dispatchTypedEvent("toolsListChanged"); @@ -1091,7 +1252,7 @@ export class InspectorClient extends InspectorClientEventTarget { this.capabilities?.resources?.listChanged ) { this.client.setNotificationHandler( - ResourceListChangedNotificationSchema, + "notifications/resources/list_changed", async () => { this.dispatchTypedEvent("resourcesListChanged"); this.dispatchTypedEvent("resourceTemplatesListChanged"); @@ -1105,25 +1266,30 @@ export class InspectorClient extends InspectorClientEventTarget { this.capabilities?.prompts?.listChanged ) { this.client.setNotificationHandler( - PromptListChangedNotificationSchema, + "notifications/prompts/list_changed", async () => { this.dispatchTypedEvent("promptsListChanged"); }, ); } - // Tasks list_changed and status handlers (when server advertises tasks capability) + // Tasks list_changed and status handlers (when server advertises tasks + // capability). Both are custom (2025-11-25) notification methods absent + // from v2's spec-notification set, so they register through the 3-arg + // custom form with an explicit params schema. if (this.capabilities?.tasks) { this.client.setNotificationHandler( - TasksListChangedNotificationSchema, + "notifications/tasks/list_changed", + { params: TasksListChangedNotificationSchema.shape.params }, async () => { this.dispatchTypedEvent("tasksListChanged"); }, ); this.client.setNotificationHandler( - TaskStatusNotificationSchema, - async (notification) => { - const task = notification.params as Task; + "notifications/tasks/status", + { params: TaskStatusNotificationSchema.shape.params }, + async (params) => { + const task = params as Task; this.dispatchTypedEvent("taskStatusChange", { taskId: task.taskId, task, @@ -1135,7 +1301,7 @@ export class InspectorClient extends InspectorClientEventTarget { // Resource updated notification handler (only if server supports subscriptions) if (this.capabilities?.resources?.subscribe === true) { this.client.setNotificationHandler( - ResourceUpdatedNotificationSchema, + "notifications/resources/updated", async (notification) => { const uri = notification.params.uri; // Only process if we're subscribed to this resource @@ -1154,7 +1320,7 @@ export class InspectorClient extends InspectorClientEventTarget { this.elicit.url === true; if (urlElicitEnabled) { this.client.setNotificationHandler( - ElicitationCompleteNotificationSchema, + "notifications/elicitation/complete", async (notification) => { const { elicitationId } = notification.params; const pending = this.pendingElicitations.find( @@ -1309,9 +1475,25 @@ export class InspectorClient extends InspectorClientEventTarget { get(proxyTarget, prop, receiver) { const value = Reflect.get(proxyTarget, prop, receiver); if (prop === "setNotificationHandler" && typeof value === "function") { - return (...args: Parameters) => { - // Add behavior here (e.g. wrap handler, log, filter) - return value.apply(target, args); + return (schemaOrMethod: unknown, ...rest: unknown[]) => { + // `@modelcontextprotocol/ext-apps` still peers on SDK v1 and + // subscribes to list-changed notifications with the v1 schema-first + // API `setNotificationHandler(NotificationSchema, handler)`. SDK v2 + // requires a method STRING as the first argument and throws + // "'[object Object]' is not a spec notification method" on a schema — + // which broke App rendering during the initial connect handshake. + // Translate a schema-first call to the method-string form; native + // string-first calls (ours) pass through untouched. Remove when + // ext-apps#702 ships a v2 peer. + const method = + typeof schemaOrMethod === "string" + ? schemaOrMethod + : (notificationMethodFromSchema(schemaOrMethod) ?? + schemaOrMethod); + return (value as (...a: unknown[]) => unknown).apply(target, [ + method, + ...rest, + ]); }; } return value; @@ -1378,10 +1560,14 @@ export class InspectorClient extends InspectorClientEventTarget { if (!this.client) { throw new Error("Client is not connected"); } - const task = await this.client.experimental.tasks.getTask( - taskId, + // SDK v2 removed `client.experimental.tasks.*`; drive the 2025-11-25 + // `tasks/get` wire method directly with its explicit (deprecated-but- + // importable) result schema. `GetTaskResult` is the flattened task object. + const task = (await this.client.request( + { method: "tasks/get", params: { taskId } }, + GetTaskResultSchema, this.getRequestOptions(), - ); + )) as Task; // Dispatch client-origin event (taskStatusChange is server-only) this.dispatchTypedEvent("requestorTaskUpdated", { @@ -1400,9 +1586,11 @@ export class InspectorClient extends InspectorClientEventTarget { if (!this.client) { throw new Error("Client is not connected"); } - // Use CallToolResultSchema for validation - return await this.client.experimental.tasks.getTaskResult( - taskId, + // `tasks/result` returns the task's stored payload; for a task-augmented + // tool call that payload is a CallToolResult, so validate with + // CallToolResultSchema (replacing the removed experimental helper). + return await this.client.request( + { method: "tasks/result", params: { taskId } }, CallToolResultSchema, this.getRequestOptions(), ); @@ -1421,8 +1609,9 @@ export class InspectorClient extends InspectorClientEventTarget { // whose error message may arrive before this resolves — the stream's error // path reads this set to label the task "cancelled" rather than "failed". this.cancelledTaskIds.add(taskId); - await this.client.experimental.tasks.cancelTask( - taskId, + await this.client.request( + { method: "tasks/cancel", params: { taskId } }, + CancelTaskResultSchema, this.getRequestOptions(), ); @@ -1469,10 +1658,12 @@ export class InspectorClient extends InspectorClientEventTarget { if (!this.client) { throw new Error("Client is not connected"); } - return await this.client.experimental.tasks.listTasks( - cursor, + const result = await this.client.request( + { method: "tasks/list", params: cursor ? { cursor } : {} }, + ListTasksResultSchema, this.getRequestOptions(), ); + return { tasks: result.tasks as Task[], nextCursor: result.nextCursor }; } /** @@ -1626,8 +1817,9 @@ export class InspectorClient extends InspectorClientEventTarget { ...(cursor ? { cursor } : {}), }; const response = await this.invokeMcpClient(() => - this.client!.listTools( - params, + this.client!.request( + { method: "tools/list", params }, + ListToolsResultSchema, this.getRequestOptions(metadata?.progressToken), ), ); @@ -1880,17 +2072,32 @@ export class InspectorClient extends InspectorClientEventTarget { CallToolResultSchema, requestOptions, ) - : client.callTool(callParams, undefined, requestOptions), + : client.callTool(callParams, requestOptions), { method: "tools/call", toolName: tool.name }, ); - // On the bypass path the result was delivered without the SDK's strict - // output validation. Run that check ourselves, non-fatally, so callers can - // warn that strict clients would reject this payload (the app still - // renders, but it may not in other hosts). - const outputValidationError = options?.skipOutputValidation - ? this.validateToolOutput(tool, result as CallToolResult) - : undefined; + // Output-schema validation. SDK v2's `callTool` relaxed some checks (e.g. it + // no longer rejects a structuredContent with undeclared properties against a + // strict `additionalProperties: false` schema), so we run our own Ajv check + // to preserve the Inspector's v1 behavior: + // - default path: strict — a schema violation rejects the call (matching + // what a strict host would do), so the caller sees the error. + // - skipOutputValidation (MCP Apps passthrough): non-fatal — surface it as + // an advisory so a schema-violating-but-real result still reaches the app. + const outputValidationError = this.validateToolOutput( + tool, + result as CallToolResult, + ); + if (outputValidationError && !options?.skipOutputValidation) { + // Match the prior contract: on v1 a strict output-schema violation + // surfaced as the SDK's typed `McpError`/`ProtocolError` (code + // InvalidParams), not a bare Error — so downstream code that branches on + // `instanceof ProtocolError` / `error.code` keeps working. + throw new ProtocolError( + ProtocolErrorCode.InvalidParams, + outputValidationError, + ); + } const invocation: ToolCallInvocation = { toolName: tool.name, @@ -2002,6 +2209,126 @@ export class InspectorClient extends InspectorClientEventTarget { return validateToolOutput(this.outputValidator, tool, result); } + /** + * Poll a task-augmented tool call to completion. Replaces the removed + * `client.experimental.tasks.callToolStream` helper: it sends the + * task-augmented `tools/call` (the server responds with a task handle, i.e. a + * `CreateTaskResult`), then polls `tasks/get` until the task reaches a + * terminal status, yielding the same `taskCreated | taskStatus | result | + * error` message shapes the caller's `for await` loop consumes — so all the + * downstream event dispatch and terminal-state handling stays unchanged. + */ + private async *pollTaskToolCall( + params: CallToolRequest["params"], + requestOptions: RequestOptions, + ): AsyncGenerator< + | { type: "taskCreated"; task: Task } + | { type: "taskStatus"; task: Task } + | { type: "result"; result: CallToolResult } + | { type: "error"; error: ProtocolError } + > { + if (!this.client) { + throw new Error("Client is not connected"); + } + const client = this.client; + // The server streams `notifications/progress` for a task AFTER the + // task-augmented `tools/call` has already returned its `{ task }` handle. But + // SDK v2 deletes a request's progress subscription the moment that request + // resolves, so those later ticks would be dropped. Capture the subscription + // id the SDK registers for this request (the only new key in the private + // `_progressHandlers` map) so we can keep the caller's `onprogress` alive + // through the poll and clean it up when the task terminates. + // SDK gap: `Client` exposes no public API to keep a progress subscription + // alive across a resolved request (or to subscribe to progress by token), so + // we reach the private `_progressHandlers` map through a narrowed cast. A + // public "durable progress subscription" hook would remove this cast. + const progressHandlers = ( + client as unknown as { + _progressHandlers: Map; + } + )._progressHandlers; + const keysBeforeRequest = new Set(progressHandlers.keys()); + // Create the task-augmented tool call. A task-capable server returns a task + // handle (`CreateTaskResult` = `{ task }`), but a server that completes + // synchronously (or for which the tool forbids/ignores task augmentation) + // may return an immediate `CallToolResult` instead — accept either with a + // union schema and branch on the presence of `task`. + const requestPromise = client.request( + { method: "tools/call", params }, + CreateTaskResultSchema.or(CallToolResultSchema), + requestOptions, + ); + // The SDK registers the progress handler synchronously while constructing + // the request promise (before this await), so the new key is present now. + // ASSUMES SERIAL CONSTRUCTION: `find` takes the first key not present in the + // pre-request snapshot, which is unambiguous only because no OTHER request + // registers a progress handler between the snapshot and this request's + // synchronous registration. Tool calls are user-driven and serial, so that + // holds today; if concurrent task-augmented calls are ever constructed in + // the same microtask window, two subscription ids could cross-wire and this + // must move to an SDK-supported correlation (see the delete-when-native note + // on `installReceiverTaskResponseBypass`). + const progressSubscriptionId = requestOptions.onprogress + ? [...progressHandlers.keys()].find((k) => !keysBeforeRequest.has(k)) + : undefined; + const created = await requestPromise; + if (!("task" in created) || created.task == null) { + // Immediate result — no task was created; yield it directly. + yield { type: "result", result: created as CallToolResult }; + return; + } + let task = created.task as Task; + yield { type: "taskCreated", task }; + + // Revive the (now-deleted) progress subscription for the poll so task- + // execution progress ticks reach the caller's `onprogress`. + if (progressSubscriptionId != null && requestOptions.onprogress) { + progressHandlers.set(progressSubscriptionId, requestOptions.onprogress); + } + try { + // Poll `tasks/get` until the task reaches a terminal status. Honour the + // server-advertised `pollInterval` when present, else the default cadence. + while (!InspectorClient.isTerminalTaskStatus(task.status)) { + const pollInterval = + typeof task.pollInterval === "number" && task.pollInterval > 0 + ? task.pollInterval + : DEFAULT_TASK_POLL_INTERVAL_MS; + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + task = (await client.request( + { method: "tasks/get", params: { taskId: task.taskId } }, + GetTaskResultSchema, + this.getRequestOptions(), + )) as Task; + yield { type: "taskStatus", task }; + } + } finally { + if (progressSubscriptionId != null) { + progressHandlers.delete(progressSubscriptionId); + } + } + + if (task.status === "completed") { + const result = await client.request( + { method: "tasks/result", params: { taskId: task.taskId } }, + CallToolResultSchema, + this.getRequestOptions(), + ); + yield { type: "result", result }; + } else { + // failed | cancelled — surface as an error the caller's loop labels as + // "cancelled" (via cancelledTaskIds) or "failed". Carry a ProtocolError so + // the `error` payload matches the event map's type (the SDK helper this + // replaces also yielded a protocol-error-shaped value). + yield { + type: "error", + error: new ProtocolError( + ProtocolErrorCode.InternalError, + task.statusMessage ?? `Task ${task.status}`, + ), + }; + } + } + /** * Call a tool with task support (streaming). * Caller must provide the Tool (e.g. from a state manager). @@ -2086,9 +2413,8 @@ export class InspectorClient extends InspectorClientEventTarget { }; } - const stream = this.client.experimental.tasks.callToolStream( + const stream = this.pollTaskToolCall( streamParams as CallToolRequest["params"], - undefined, // Use default CallToolResultSchema requestOptions, ); @@ -2194,9 +2520,9 @@ export class InspectorClient extends InspectorClientEventTarget { // Try to get it from the task result endpoint if (!finalResult && taskId) { try { - finalResult = await this.client.experimental.tasks.getTaskResult( - taskId, - undefined, + finalResult = await this.client.request( + { method: "tasks/result", params: { taskId } }, + CallToolResultSchema, this.getRequestOptions(), // no metadata for fallback ); } catch (resultError) { @@ -2271,8 +2597,9 @@ export class InspectorClient extends InspectorClientEventTarget { ...(cursor ? { cursor } : {}), }; const response = await this.invokeMcpClient(() => - this.client!.listResources( - params, + this.client!.request( + { method: "resources/list", params }, + ListResourcesResultSchema, this.getRequestOptions(metadata?.progressToken), ), ); @@ -2398,8 +2725,9 @@ export class InspectorClient extends InspectorClientEventTarget { }; const response = await this.invokeMcpClient( () => - this.client!.listResourceTemplates( - params, + this.client!.request( + { method: "resources/templates/list", params }, + ListResourceTemplatesResultSchema, this.getRequestOptions(metadata?.progressToken), ), { method: "resources/templates/list" }, @@ -2429,8 +2757,9 @@ export class InspectorClient extends InspectorClientEventTarget { ...(cursor ? { cursor } : {}), }; const response = await this.invokeMcpClient(() => - this.client!.listPrompts( - params, + this.client!.request( + { method: "prompts/list", params }, + ListPromptsResultSchema, this.getRequestOptions(metadata?.progressToken), ), ); @@ -2546,8 +2875,8 @@ export class InspectorClient extends InspectorClientEventTarget { } catch (error) { // Handle MethodNotFound gracefully (server doesn't support completions) if ( - (error instanceof McpError && - error.code === ErrorCode.MethodNotFound) || + (error instanceof ProtocolError && + error.code === ProtocolErrorCode.MethodNotFound) || (error instanceof Error && (error.message.includes("Method not found") || error.message.includes("does not support completions"))) @@ -3031,9 +3360,9 @@ export class InspectorClient extends InspectorClientEventTarget { */ async resumeAfterOAuth( authorizationCode: string, - options?: { remoteSessionId?: string }, + options?: { remoteSessionId?: string; iss?: string }, ): Promise { - await this.completeOAuthFlow(authorizationCode); + await this.completeOAuthFlow(authorizationCode, options?.iss); const remoteSessionId = options?.remoteSessionId; const transport = this.baseTransport; diff --git a/core/mcp/inspectorClientEventTarget.ts b/core/mcp/inspectorClientEventTarget.ts index bc9ffa1a4..c7da5094c 100644 --- a/core/mcp/inspectorClientEventTarget.ts +++ b/core/mcp/inspectorClientEventTarget.ts @@ -33,12 +33,12 @@ import type { ProgressToken, Task, CallToolResult, - McpError, -} from "@modelcontextprotocol/sdk/types.js"; + ProtocolError, +} from "@modelcontextprotocol/client"; import type { SamplingCreateMessage } from "./samplingCreateMessage.js"; import type { ElicitationCreateMessage } from "./elicitationCreateMessage.js"; import type { JsonValue } from "../json/jsonUtils.js"; -import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthTokens } from "@modelcontextprotocol/client"; import type { AuthChallenge } from "../auth/challenge.js"; /** Task with createdAt optional so we can emit synthetic tasks (e.g. on result/error) that omit it. */ @@ -116,14 +116,14 @@ export interface InspectorClientEventMap { taskId: string; task: TaskWithOptionalCreatedAt; result?: CallToolResult; - error?: McpError; + error?: ProtocolError; }; /** Fired from getRequestorTask() and callToolStream (client-origin task updates). */ requestorTaskUpdated: { taskId: string; task: TaskWithOptionalCreatedAt; result?: CallToolResult; - error?: McpError; + error?: ProtocolError; }; taskCancelled: { taskId: string }; /** diff --git a/core/mcp/inspectorClientProtocol.ts b/core/mcp/inspectorClientProtocol.ts index 007b811b1..78d021588 100644 --- a/core/mcp/inspectorClientProtocol.ts +++ b/core/mcp/inspectorClientProtocol.ts @@ -26,11 +26,11 @@ import type { LoggingLevel, Prompt, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, ServerCapabilities, Task, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { JsonValue } from "../json/jsonUtils.js"; import type { InspectorClientEventTarget } from "./inspectorClientEventTarget.js"; import type { SamplingCreateMessage } from "./samplingCreateMessage.js"; diff --git a/core/mcp/messageTrackingTransport.ts b/core/mcp/messageTrackingTransport.ts index 80fc40cf9..d55158800 100644 --- a/core/mcp/messageTrackingTransport.ts +++ b/core/mcp/messageTrackingTransport.ts @@ -1,17 +1,17 @@ import type { Transport, TransportSendOptions, -} from "@modelcontextprotocol/sdk/shared/transport.js"; +} from "@modelcontextprotocol/client"; import type { JSONRPCMessage, MessageExtraInfo, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { JSONRPCRequest, JSONRPCNotification, JSONRPCResultResponse, JSONRPCErrorResponse, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { MessageOrigin } from "./types.js"; export interface MessageTrackingCallbacks { @@ -32,10 +32,7 @@ export class MessageTrackingTransport implements Transport { private callbacks: MessageTrackingCallbacks; private negotiatedProtocolVersion?: string; - constructor( - baseTransport: Transport, - callbacks: MessageTrackingCallbacks, - ) { + constructor(baseTransport: Transport, callbacks: MessageTrackingCallbacks) { this.baseTransport = baseTransport; this.callbacks = callbacks; } diff --git a/core/mcp/node/transport.ts b/core/mcp/node/transport.ts index 59260bc2e..971bf9e29 100644 --- a/core/mcp/node/transport.ts +++ b/core/mcp/node/transport.ts @@ -8,9 +8,9 @@ import type { CreateTransportResult, InspectorServerSettings, } from "../types.js"; -import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; -import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; -import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import { StdioClientTransport } from "@modelcontextprotocol/client/stdio"; +import { SSEClientTransport } from "@modelcontextprotocol/client"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client"; import { createFetchTracker } from "../fetchTracking.js"; import { createAuthChallengeInterceptFetch } from "./authChallengeFetch.js"; import type { Dispatcher } from "undici"; diff --git a/core/mcp/oauthManager.ts b/core/mcp/oauthManager.ts index f413e5a4b..4297f70c0 100644 --- a/core/mcp/oauthManager.ts +++ b/core/mcp/oauthManager.ts @@ -7,8 +7,8 @@ import { BaseOAuthClientProvider } from "../auth/providers.js"; import type { OAuthFlowState, OAuthStep } from "../auth/types.js"; import { EMPTY_OAUTH_FLOW_STATE } from "../auth/types.js"; -import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js"; -import type { OAuthClientInformation } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthTokens } from "@modelcontextprotocol/client"; +import type { OAuthClientInformation } from "@modelcontextprotocol/client"; import { mcpAuth } from "../auth/mcpAuth.js"; import type { OAuthStorage } from "../auth/storage.js"; import { parseOAuthState } from "../auth/utils.js"; @@ -286,14 +286,14 @@ export class OAuthManager { try { if (this.isEnterpriseManaged()) { const emaConfig = await this.getEmaFlowConfig(); - const scopeForMint = - this.pendingAuthorizationScope ?? emaConfig.scope; + const scopeForMint = this.pendingAuthorizationScope ?? emaConfig.scope; const config = scopeForMint ? { ...emaConfig, scope: scopeForMint } : emaConfig; const tokens = await completeEmaIdpAuthorizationAndMint( config, authorizationCode, + iss, ); const requestedScope = this.pendingAuthorizationScope; const scopeToPersist = resolvePersistedScopeAfterGrant( @@ -449,7 +449,9 @@ export class OAuthManager { */ async refreshEnterpriseManagedTokens(): Promise { if (!this.isEnterpriseManaged()) return false; - const tokens = await refreshEmaResourceTokens(await this.getEmaFlowConfig()); + const tokens = await refreshEmaResourceTokens( + await this.getEmaFlowConfig(), + ); return tokens !== undefined; } diff --git a/core/mcp/remote/node/remote-session.ts b/core/mcp/remote/node/remote-session.ts index 83abd5ddb..34a2db098 100644 --- a/core/mcp/remote/node/remote-session.ts +++ b/core/mcp/remote/node/remote-session.ts @@ -2,8 +2,8 @@ * Remote session - holds a transport and event queue for a remote client. */ -import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; -import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { Transport } from "@modelcontextprotocol/client"; +import type { JSONRPCMessage } from "@modelcontextprotocol/client"; import type { FetchRequestEntryBase } from "../../types.js"; import type { RemoteEvent } from "../types.js"; import type { AuthChallenge } from "../../../auth/challenge.js"; diff --git a/core/mcp/remote/node/server.ts b/core/mcp/remote/node/server.ts index bbfaf1988..719719306 100644 --- a/core/mcp/remote/node/server.ts +++ b/core/mcp/remote/node/server.ts @@ -31,7 +31,7 @@ import type { RemoteSendRequest, RemoteSetAuthStateRequest, } from "../types.js"; -import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { JSONRPCMessage } from "@modelcontextprotocol/client"; import { AuthChallengeError } from "../../../auth/challenge.js"; import { DEFAULT_MAX_FETCH_REQUESTS, diff --git a/core/mcp/remote/node/tokenAuthProvider.ts b/core/mcp/remote/node/tokenAuthProvider.ts index 3793eb721..2bf8a9dfc 100644 --- a/core/mcp/remote/node/tokenAuthProvider.ts +++ b/core/mcp/remote/node/tokenAuthProvider.ts @@ -1,9 +1,9 @@ -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; import type { OAuthClientInformation, OAuthClientMetadata, OAuthTokens, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import type { RemoteAuthState } from "../types.js"; const REMOTE_OAUTH_STUB_METADATA: OAuthClientMetadata = { diff --git a/core/mcp/remote/remoteClientTransport.ts b/core/mcp/remote/remoteClientTransport.ts index 82c86b4f0..2a80a3f95 100644 --- a/core/mcp/remote/remoteClientTransport.ts +++ b/core/mcp/remote/remoteClientTransport.ts @@ -6,11 +6,11 @@ import type { Transport, TransportSendOptions, -} from "@modelcontextprotocol/sdk/shared/transport.js"; +} from "@modelcontextprotocol/client"; import type { JSONRPCMessage, MessageExtraInfo, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; import type { InspectorServerSettings, StderrLogEntry } from "../types.js"; import type { FetchRequestEntryBase } from "../types.js"; import type { @@ -61,7 +61,7 @@ export interface RemoteTransportOptions { onFetchResponseBody?: (id: string, responseBody: string) => void; /** Optional OAuth client provider for Bearer authentication */ - authProvider?: import("@modelcontextprotocol/sdk/client/auth.js").OAuthClientProvider; + authProvider?: import("@modelcontextprotocol/client").OAuthClientProvider; /** * Optional per-server settings forwarded in the /api/mcp/connect body. diff --git a/core/mcp/remote/types.ts b/core/mcp/remote/types.ts index 7c3ffea52..2b9c0e584 100644 --- a/core/mcp/remote/types.ts +++ b/core/mcp/remote/types.ts @@ -7,9 +7,9 @@ import type { FetchRequestEntryBase, InspectorServerSettings, } from "../types.js"; -import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { JSONRPCMessage } from "@modelcontextprotocol/client"; import type { AuthChallenge } from "../../auth/challenge.js"; -import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js"; +import type { OAuthTokens } from "@modelcontextprotocol/client"; /** OAuth token set pushed to the remote backend for upstream MCP Bearer auth. */ export interface RemoteMcpOAuthTokens { diff --git a/core/mcp/samplingCreateMessage.ts b/core/mcp/samplingCreateMessage.ts index 487d77294..b03b098fb 100644 --- a/core/mcp/samplingCreateMessage.ts +++ b/core/mcp/samplingCreateMessage.ts @@ -1,8 +1,8 @@ import type { CreateMessageRequest, CreateMessageResult, -} from "@modelcontextprotocol/sdk/types.js"; -import { RELATED_TASK_META_KEY } from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; +import { RELATED_TASK_META_KEY } from "@modelcontextprotocol/client"; export type { CreateMessageRequest, CreateMessageResult }; diff --git a/core/mcp/serverList.ts b/core/mcp/serverList.ts index 03b05936f..becedb131 100644 --- a/core/mcp/serverList.ts +++ b/core/mcp/serverList.ts @@ -6,7 +6,7 @@ */ import { DEFAULT_MAX_FETCH_REQUESTS, DEFAULT_TASK_TTL_MS } from "./types.js"; -import type { Root } from "@modelcontextprotocol/sdk/types.js"; +import type { Root } from "@modelcontextprotocol/client"; import type { InspectorServerSettings, MCPConfig, @@ -106,9 +106,7 @@ type StoredInspectorFields = Pick< export function envRecordToPairs( env: Record | undefined, ): { key: string; value: string }[] { - return env - ? Object.entries(env).map(([key, value]) => ({ key, value })) - : []; + return env ? Object.entries(env).map(([key, value]) => ({ key, value })) : []; } /** @@ -232,9 +230,7 @@ export function inspectorSettingsToStoredFields( out.headers = headersRecord; } - const metadataFiltered = settings.metadata.filter( - (m) => m.key.trim() !== "", - ); + const metadataFiltered = settings.metadata.filter((m) => m.key.trim() !== ""); if (metadataFiltered.length > 0) { out.metadata = metadataFiltered; } @@ -333,9 +329,7 @@ export const INSPECTOR_FIELD_KEYS = new Set( * `satisfies` constraint above forces the map update, which propagates * here. */ -export function stripInspectorFields( - stored: StoredMCPServer, -): MCPServerConfig { +export function stripInspectorFields(stored: StoredMCPServer): MCPServerConfig { const out: Record = {}; for (const [k, v] of Object.entries( stored as unknown as Record, @@ -361,7 +355,14 @@ export function mcpConfigToServerEntries(config: MCPConfig): ServerEntry[] { // `InspectorServerSettings` only. const inspectorFields: StoredInspectorFields = {}; const sdkOnly: Record = {}; - for (const [k, v] of Object.entries(raw as unknown as Record)) { + // Widen the typed config object to a generic record to iterate its keys. + // `StoredMCPServer` has no index signature, so TS requires the `unknown` + // step (`as Record` alone is TS2352). This is a plain + // structural widening, not an SDK-shape workaround. (Pre-existing pattern, + // also at the `serverEntryToStored` / oauth-strip casts in this file.) + for (const [k, v] of Object.entries( + raw as unknown as Record, + )) { if (INSPECTOR_FIELD_KEYS.has(k as keyof StoredInspectorFields)) { (inspectorFields as Record)[k] = v; } else { diff --git a/core/mcp/state/managedListState.ts b/core/mcp/state/managedListState.ts index 99471d18d..0cf67137b 100644 --- a/core/mcp/state/managedListState.ts +++ b/core/mcp/state/managedListState.ts @@ -13,7 +13,7 @@ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; import type { InspectorClientEventMap } from "../inspectorClientEventTarget.js"; -import type { ServerCapabilities } from "@modelcontextprotocol/sdk/types.js"; +import type { ServerCapabilities } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import { TypedEventTarget } from "../typedEventTarget.js"; @@ -194,9 +194,10 @@ export abstract class ManagedListState< * concrete subclass event maps keep the call sites type-safe. */ private emit(type: keyof M, detail: unknown): void { - ( - this.dispatchTypedEvent as unknown as (t: keyof M, d: unknown) => void - )(type, detail); + (this.dispatchTypedEvent as unknown as (t: keyof M, d: unknown) => void)( + type, + detail, + ); } private setListChanged(value: boolean): void { diff --git a/core/mcp/state/managedPromptsState.ts b/core/mcp/state/managedPromptsState.ts index 75435a885..a986a195c 100644 --- a/core/mcp/state/managedPromptsState.ts +++ b/core/mcp/state/managedPromptsState.ts @@ -4,7 +4,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { ManagedListState, DEFAULT_LIST_CHANGED_DEBOUNCE_MS, diff --git a/core/mcp/state/managedRequestorTasksState.ts b/core/mcp/state/managedRequestorTasksState.ts index 42e71f00c..1bb3ec102 100644 --- a/core/mcp/state/managedRequestorTasksState.ts +++ b/core/mcp/state/managedRequestorTasksState.ts @@ -8,7 +8,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import type { InspectorClientEventMap } from "../inspectorClientEventTarget.js"; import { diff --git a/core/mcp/state/managedResourceTemplatesState.ts b/core/mcp/state/managedResourceTemplatesState.ts index fd4d22e8f..b7d0e0033 100644 --- a/core/mcp/state/managedResourceTemplatesState.ts +++ b/core/mcp/state/managedResourceTemplatesState.ts @@ -11,7 +11,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { ManagedListState, DEFAULT_LIST_CHANGED_DEBOUNCE_MS, diff --git a/core/mcp/state/managedResourcesState.ts b/core/mcp/state/managedResourcesState.ts index 5387a8c6e..f8865e818 100644 --- a/core/mcp/state/managedResourcesState.ts +++ b/core/mcp/state/managedResourcesState.ts @@ -4,7 +4,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { ManagedListState, DEFAULT_LIST_CHANGED_DEBOUNCE_MS, diff --git a/core/mcp/state/managedToolsState.ts b/core/mcp/state/managedToolsState.ts index bafb32369..a99176228 100644 --- a/core/mcp/state/managedToolsState.ts +++ b/core/mcp/state/managedToolsState.ts @@ -4,7 +4,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { ManagedListState, DEFAULT_LIST_CHANGED_DEBOUNCE_MS, diff --git a/core/mcp/state/mergeTaskIntoList.ts b/core/mcp/state/mergeTaskIntoList.ts index dfd373dfe..a811754b9 100644 --- a/core/mcp/state/mergeTaskIntoList.ts +++ b/core/mcp/state/mergeTaskIntoList.ts @@ -1,4 +1,4 @@ -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import type { TaskWithOptionalCreatedAt } from "../inspectorClientEventTarget.js"; /** diff --git a/core/mcp/state/pagedPromptsState.ts b/core/mcp/state/pagedPromptsState.ts index 0d49aed3e..fd437e2be 100644 --- a/core/mcp/state/pagedPromptsState.ts +++ b/core/mcp/state/pagedPromptsState.ts @@ -11,7 +11,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import { TypedEventTarget } from "../typedEventTarget.js"; diff --git a/core/mcp/state/pagedRequestorTasksState.ts b/core/mcp/state/pagedRequestorTasksState.ts index 9092339af..cb215e304 100644 --- a/core/mcp/state/pagedRequestorTasksState.ts +++ b/core/mcp/state/pagedRequestorTasksState.ts @@ -8,7 +8,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import type { InspectorClientEventMap } from "../inspectorClientEventTarget.js"; import { diff --git a/core/mcp/state/pagedResourceTemplatesState.ts b/core/mcp/state/pagedResourceTemplatesState.ts index 0bdff4aee..2fd2e2af1 100644 --- a/core/mcp/state/pagedResourceTemplatesState.ts +++ b/core/mcp/state/pagedResourceTemplatesState.ts @@ -12,7 +12,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import { TypedEventTarget } from "../typedEventTarget.js"; diff --git a/core/mcp/state/pagedResourcesState.ts b/core/mcp/state/pagedResourcesState.ts index c90c08cca..48cf36b00 100644 --- a/core/mcp/state/pagedResourcesState.ts +++ b/core/mcp/state/pagedResourcesState.ts @@ -11,7 +11,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import { TypedEventTarget } from "../typedEventTarget.js"; diff --git a/core/mcp/state/pagedToolsState.ts b/core/mcp/state/pagedToolsState.ts index 2bc93e2fb..ce4c5496a 100644 --- a/core/mcp/state/pagedToolsState.ts +++ b/core/mcp/state/pagedToolsState.ts @@ -12,7 +12,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { isTerminalStatus } from "../types.js"; import { TypedEventTarget } from "../typedEventTarget.js"; diff --git a/core/mcp/state/resourceSubscriptionsState.ts b/core/mcp/state/resourceSubscriptionsState.ts index 7135e326e..a9f51b5d9 100644 --- a/core/mcp/state/resourceSubscriptionsState.ts +++ b/core/mcp/state/resourceSubscriptionsState.ts @@ -23,7 +23,7 @@ import type { } from "./managedResourcesState.js"; import type { InspectorResourceSubscription } from "../types.js"; import { isTerminalStatus } from "../types.js"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import { TypedEventTarget, type TypedEventGeneric, diff --git a/core/mcp/taskNotificationSchemas.ts b/core/mcp/taskNotificationSchemas.ts index 5b877487c..68b59c171 100644 --- a/core/mcp/taskNotificationSchemas.ts +++ b/core/mcp/taskNotificationSchemas.ts @@ -14,7 +14,7 @@ * types (which is private, so we can't import it). We accept optional params only * so the notification parses; we do not use any params in the handler. */ -import { NotificationSchema } from "@modelcontextprotocol/sdk/types.js"; +import { NotificationSchema } from "@modelcontextprotocol/core"; import { z } from "zod/v4"; export const TasksListChangedNotificationSchema = NotificationSchema.extend({ diff --git a/core/mcp/toolOutputValidation.ts b/core/mcp/toolOutputValidation.ts index bf7e47527..add2c81e2 100644 --- a/core/mcp/toolOutputValidation.ts +++ b/core/mcp/toolOutputValidation.ts @@ -1,8 +1,8 @@ -import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult, Tool } from "@modelcontextprotocol/client"; import type { JsonSchemaType, jsonSchemaValidator, -} from "@modelcontextprotocol/sdk/validation"; +} from "@modelcontextprotocol/client"; /** * Validate a delivered tool result's `structuredContent` against the tool's diff --git a/core/mcp/types.ts b/core/mcp/types.ts index 424556b1f..192785417 100644 --- a/core/mcp/types.ts +++ b/core/mcp/types.ts @@ -17,10 +17,10 @@ import type { ServerNotification, ServerRequest, Tool, -} from "@modelcontextprotocol/sdk/types.js"; -import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; -import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; +} from "@modelcontextprotocol/client"; +import type { Client } from "@modelcontextprotocol/client"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; +import type { Transport } from "@modelcontextprotocol/client"; import type { InspectorLogger } from "../logging/logger.js"; import type { JsonValue } from "../json/jsonUtils.js"; import type { @@ -168,7 +168,9 @@ export type ConnectionStatus = * (see InspectorClient's `onclose` handler, #1490). A bare `=== "disconnected"` * check would therefore tear down in one ordering but not the other. */ -export function isTerminalStatus(status: ConnectionStatus | undefined): boolean { +export function isTerminalStatus( + status: ConnectionStatus | undefined, +): boolean { return status === "disconnected" || status === "error"; } diff --git a/core/mcp/urlElicitation.ts b/core/mcp/urlElicitation.ts index fdb5ebd55..5e33b11ce 100644 --- a/core/mcp/urlElicitation.ts +++ b/core/mcp/urlElicitation.ts @@ -1,9 +1,9 @@ import { - ErrorCode, - McpError, + ProtocolErrorCode, + ProtocolError, UrlElicitationRequiredError, -} from "@modelcontextprotocol/sdk/types.js"; -import type { ElicitRequestURLParams } from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; +import type { ElicitRequestURLParams } from "@modelcontextprotocol/client"; export type { ElicitRequestURLParams }; @@ -34,8 +34,8 @@ export class UrlElicitationLoopError extends Error { * * Two shapes reach us, both code `-32042`: * - the SDK's typed {@link UrlElicitationRequiredError} (created by - * `McpError.fromError` when `data.elicitations` is present), and - * - a generic {@link McpError} with code `-32042` when the server omitted + * `ProtocolError.fromError` when `data.elicitations` is present), and + * - a generic {@link ProtocolError} with code `-32042` when the server omitted * `data.elicitations` (a non-spec response — the spec requires the list). * * The empty array is meaningful: it signals the non-spec "no elicitations" @@ -50,7 +50,10 @@ export function getUrlElicitationsFromError( if (error instanceof UrlElicitationRequiredError) { return error.elicitations ?? []; } - if (error instanceof McpError && error.code === ErrorCode.UrlElicitationRequired) { + if ( + error instanceof ProtocolError && + error.code === ProtocolErrorCode.UrlElicitationRequired + ) { const data = error.data as | { elicitations?: ElicitRequestURLParams[] } | undefined; diff --git a/core/react/useInspectorClient.ts b/core/react/useInspectorClient.ts index c104d41cb..a7bc6485b 100644 --- a/core/react/useInspectorClient.ts +++ b/core/react/useInspectorClient.ts @@ -7,7 +7,7 @@ import type { ClientCapabilities, ServerCapabilities, Implementation, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; // Module-scope frozen object so the `?? EMPTY_CLIENT_CAPABILITIES` // fallback below doesn't return a fresh literal on every render — diff --git a/core/react/useManagedPrompts.ts b/core/react/useManagedPrompts.ts index 762dc5996..cacb8bd8e 100644 --- a/core/react/useManagedPrompts.ts +++ b/core/react/useManagedPrompts.ts @@ -4,7 +4,7 @@ import type { ManagedPromptsState, ManagedPromptsStateEventMap, } from "../mcp/state/managedPromptsState.js"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UseManagedPromptsResult { @@ -55,10 +55,7 @@ export function useManagedPrompts( onListChangedChange, ); return () => { - managedPromptsState.removeEventListener( - "promptsChange", - onPromptsChange, - ); + managedPromptsState.removeEventListener("promptsChange", onPromptsChange); managedPromptsState.removeEventListener( "listChangedChange", onListChangedChange, diff --git a/core/react/useManagedRequestorTasks.ts b/core/react/useManagedRequestorTasks.ts index 9084324ca..05d23ac19 100644 --- a/core/react/useManagedRequestorTasks.ts +++ b/core/react/useManagedRequestorTasks.ts @@ -4,7 +4,7 @@ import type { ManagedRequestorTasksState, ManagedRequestorTasksStateEventMap, } from "../mcp/state/managedRequestorTasksState.js"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UseManagedRequestorTasksResult { diff --git a/core/react/useManagedResourceTemplates.ts b/core/react/useManagedResourceTemplates.ts index 98236ec6e..1f09d09a7 100644 --- a/core/react/useManagedResourceTemplates.ts +++ b/core/react/useManagedResourceTemplates.ts @@ -4,7 +4,7 @@ import type { ManagedResourceTemplatesState, ManagedResourceTemplatesStateEventMap, } from "../mcp/state/managedResourceTemplatesState.js"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UseManagedResourceTemplatesResult { diff --git a/core/react/useManagedResources.ts b/core/react/useManagedResources.ts index 94ffae952..9e8d2e70c 100644 --- a/core/react/useManagedResources.ts +++ b/core/react/useManagedResources.ts @@ -4,7 +4,7 @@ import type { ManagedResourcesState, ManagedResourcesStateEventMap, } from "../mcp/state/managedResourcesState.js"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UseManagedResourcesResult { diff --git a/core/react/useManagedTools.ts b/core/react/useManagedTools.ts index 333b21e13..85eb59878 100644 --- a/core/react/useManagedTools.ts +++ b/core/react/useManagedTools.ts @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from "react"; import type { InspectorClientProtocol } from "../mcp/inspectorClientProtocol.js"; import type { ManagedToolsState } from "../mcp/state/managedToolsState.js"; import type { ManagedToolsStateEventMap } from "../mcp/state/managedToolsState.js"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UseManagedToolsResult { diff --git a/core/react/usePagedPrompts.ts b/core/react/usePagedPrompts.ts index 853da7ed2..f8463170e 100644 --- a/core/react/usePagedPrompts.ts +++ b/core/react/usePagedPrompts.ts @@ -5,7 +5,7 @@ import type { PagedPromptsStateEventMap, LoadPageResult, } from "../mcp/state/pagedPromptsState.js"; -import type { Prompt } from "@modelcontextprotocol/sdk/types.js"; +import type { Prompt } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UsePagedPromptsResult { diff --git a/core/react/usePagedRequestorTasks.ts b/core/react/usePagedRequestorTasks.ts index 87f5da18a..aefb4cc74 100644 --- a/core/react/usePagedRequestorTasks.ts +++ b/core/react/usePagedRequestorTasks.ts @@ -5,7 +5,7 @@ import type { PagedRequestorTasksStateEventMap, LoadPageResult, } from "../mcp/state/pagedRequestorTasksState.js"; -import type { Task } from "@modelcontextprotocol/sdk/types.js"; +import type { Task } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UsePagedRequestorTasksResult { @@ -39,10 +39,7 @@ export function usePagedRequestorTasks( setTasks(pagedRequestorTasksState.getTasks()); setNextCursor(pagedRequestorTasksState.getNextCursor() ?? undefined); const onTasksChange = ( - event: TypedEventGeneric< - PagedRequestorTasksStateEventMap, - "tasksChange" - >, + event: TypedEventGeneric, ) => { setTasks(event.detail); setNextCursor(pagedRequestorTasksState.getNextCursor() ?? undefined); diff --git a/core/react/usePagedResourceTemplates.ts b/core/react/usePagedResourceTemplates.ts index dc2c7e993..ab4707a46 100644 --- a/core/react/usePagedResourceTemplates.ts +++ b/core/react/usePagedResourceTemplates.ts @@ -5,7 +5,7 @@ import type { PagedResourceTemplatesStateEventMap, LoadPageResult, } from "../mcp/state/pagedResourceTemplatesState.js"; -import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js"; +import type { ResourceTemplateType as ResourceTemplate } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UsePagedResourceTemplatesResult { diff --git a/core/react/usePagedResources.ts b/core/react/usePagedResources.ts index 0a2716418..a52096cd0 100644 --- a/core/react/usePagedResources.ts +++ b/core/react/usePagedResources.ts @@ -5,7 +5,7 @@ import type { PagedResourcesStateEventMap, LoadPageResult, } from "../mcp/state/pagedResourcesState.js"; -import type { Resource } from "@modelcontextprotocol/sdk/types.js"; +import type { Resource } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UsePagedResourcesResult { diff --git a/core/react/usePagedTools.ts b/core/react/usePagedTools.ts index 7bff9f954..0f9254aa6 100644 --- a/core/react/usePagedTools.ts +++ b/core/react/usePagedTools.ts @@ -5,7 +5,7 @@ import type { PagedToolsStateEventMap, LoadPageResult, } from "../mcp/state/pagedToolsState.js"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UsePagedToolsResult { @@ -21,9 +21,7 @@ export function usePagedTools( client: InspectorClientProtocol | null, pagedToolsState: PagedToolsState | null, ): UsePagedToolsResult { - const [tools, setTools] = useState( - pagedToolsState?.getTools() ?? [], - ); + const [tools, setTools] = useState(pagedToolsState?.getTools() ?? []); useEffect(() => { if (!pagedToolsState) { diff --git a/package-lock.json b/package-lock.json index ac8a1921f..83c5a9bb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,11 @@ "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.14", - "@modelcontextprotocol/ext-apps": "^1.7.1", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/ext-apps": "^1.7.4", + "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/server-legacy": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "@vitejs/plugin-react": "^6.0.0", "ajv": "^8.17.1", @@ -94,10 +97,40 @@ "hono": "^4" } }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", + "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.4.tgz", + "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.1.tgz", - "integrity": "sha512-J3WdG1A4JSSKnSWKyU+895dBVYBV2Utgtf7fUsUK45mlkETm53a/1DR6Pm3hUGKqLLQthZLmpxOg8VPzJi/lyg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.4.tgz", + "integrity": "sha512-QQqysE549cf/Y0VabBmAACXhj92EhB3t8yVct2BHbkWiPTFA1S91EqTVjYXXcZEefXU0pmHcdObhsNMcomJIOQ==", "license": "MIT", "workspaces": [ "examples/*" @@ -128,6 +161,7 @@ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", + "peer": true, "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", @@ -163,6 +197,46 @@ } } }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.4.tgz", + "integrity": "sha512-pjMZcNEt1dOq0aJCcY3b7w1Ayh+qmQN2xlfTELcGV9yiAjEGIczBPBLWWW0k0zzo5T8kiv15jtKPsWeHGxLvLg==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/server-legacy": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server-legacy/-/server-legacy-2.0.0-beta.4.tgz", + "integrity": "sha512-GaEa7mfjTqevGuz2taFj2fqb3FthxFvWOmoI/wbLmiK1XRnxdYsAaaJG/lgSlA+W6XWJ1+u+6oZ7610TowkjMA==", + "deprecated": "This package is a frozen copy of v1's SSE transport and OAuth Authorization Server helpers for migration purposes only. Use StreamableHTTP from @modelcontextprotocol/server and a dedicated OAuth server in production. Will not receive new features.", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "express-rate-limit": "^8.2.1", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "express": "^4.18.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, "node_modules/@napi-rs/keyring": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.3.0.tgz", @@ -709,6 +783,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -738,6 +813,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -825,6 +901,7 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", + "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -873,6 +950,7 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -886,6 +964,7 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -993,6 +1072,7 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -1024,6 +1104,7 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -1033,6 +1114,7 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.6.0" } @@ -1073,6 +1155,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1148,6 +1231,7 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -1161,7 +1245,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/emoji-regex": { "version": "10.6.0", @@ -1174,6 +1259,7 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -1195,6 +1281,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -1204,6 +1291,7 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -1213,6 +1301,7 @@ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -1234,7 +1323,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/escape-string-regexp": { "version": "2.0.0", @@ -1250,6 +1340,7 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -1280,6 +1371,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -1395,6 +1487,7 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -1416,6 +1509,7 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -1425,6 +1519,7 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -1448,6 +1543,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1469,6 +1565,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -1493,6 +1590,7 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", + "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -1506,6 +1604,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -1518,6 +1617,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -1530,6 +1630,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", + "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -1733,6 +1834,7 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } @@ -1804,7 +1906,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/is-unicode-supported": { "version": "2.1.0", @@ -1858,7 +1961,8 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/lightningcss": { "version": "1.32.0", @@ -2114,6 +2218,7 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -2123,6 +2228,7 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -2132,6 +2238,7 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2144,6 +2251,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2153,6 +2261,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -2177,7 +2286,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/nanoid": { "version": "3.3.12", @@ -2202,6 +2312,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2220,6 +2331,7 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -2241,6 +2353,7 @@ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", + "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -2253,6 +2366,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", + "peer": true, "dependencies": { "wrappy": "1" } @@ -2295,6 +2409,7 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -2322,6 +2437,7 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -2440,6 +2556,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", + "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -2453,6 +2570,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -2474,6 +2592,7 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2602,6 +2721,7 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -2651,6 +2771,7 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", @@ -2677,6 +2798,7 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", + "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -2723,6 +2845,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -2742,6 +2865,7 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" @@ -2758,6 +2882,7 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -2776,6 +2901,7 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -3003,6 +3129,7 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", + "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -3190,7 +3317,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/ws": { "version": "8.21.0", @@ -3263,6 +3391,7 @@ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", + "peer": true, "peerDependencies": { "zod": "^3.25.28 || ^4" } diff --git a/package.json b/package.json index 278bc4370..4416e35fc 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,11 @@ }, "dependencies": { "@hono/node-server": "^1.19.14", - "@modelcontextprotocol/ext-apps": "^1.7.1", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/ext-apps": "^1.7.4", + "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/server-legacy": "2.0.0-beta.4", "@napi-rs/keyring": "^1.3.0", "@vitejs/plugin-react": "^6.0.0", "ajv": "^8.17.1", diff --git a/test-servers/src/composable-test-server.ts b/test-servers/src/composable-test-server.ts index 4c9440e7b..44ac2fe64 100644 --- a/test-servers/src/composable-test-server.ts +++ b/test-servers/src/composable-test-server.ts @@ -5,65 +5,42 @@ * This allows composing MCP test servers with different capabilities, tools, resources, and prompts. */ +import * as z from "zod/v4"; import { McpServer, ResourceTemplate as SdkResourceTemplate, -} from "@modelcontextprotocol/sdk/server/mcp.js"; + completable, + isCompletable, +} from "@modelcontextprotocol/server"; import type { Implementation, Tool, Resource, - ResourceTemplate, + ResourceTemplateType as ResourceTemplate, Prompt, CallToolResult, -} from "@modelcontextprotocol/sdk/types.js"; -import { - InMemoryTaskStore, - InMemoryTaskMessageQueue, -} from "@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js"; -import type { - TaskStore, - TaskMessageQueue, - ToolTaskHandler, -} from "@modelcontextprotocol/sdk/experimental/tasks/interfaces.js"; -import type { + PromptArgument, RegisteredTool, RegisteredResource, RegisteredPrompt, RegisteredResourceTemplate, -} from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; -import type { - ServerRequest, - ServerNotification, -} from "@modelcontextprotocol/sdk/types.js"; -import { - SetLevelRequestSchema, - SubscribeRequestSchema, - UnsubscribeRequestSchema, - ListToolsRequestSchema, - ListResourcesRequestSchema, - ListResourceTemplatesRequestSchema, - ListPromptsRequestSchema, - type ListToolsResult, - type ListResourcesResult, - type ListResourceTemplatesResult, - type ListPromptsResult, -} from "@modelcontextprotocol/sdk/types.js"; -import type { AnySchema } from "@modelcontextprotocol/sdk/server/zod-compat.js"; + ServerContext, + ServerCapabilities, + Task, + GetTaskResult, + ListTasksResult, + ListToolsResult, + ListResourcesResult, + ListResourceTemplatesResult, + ListPromptsResult, +} from "@modelcontextprotocol/server"; import { - type ZodRawShapeCompat, - getObjectShape, - getSchemaDescription, - isSchemaOptional, - normalizeObjectSchema, -} from "@modelcontextprotocol/sdk/server/zod-compat.js"; -import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js"; -import { - completable, - isCompletable, -} from "@modelcontextprotocol/sdk/server/completable.js"; -import type { PromptArgument } from "@modelcontextprotocol/sdk/types.js"; + GetTaskRequestSchema, + GetTaskPayloadRequestSchema, + CancelTaskRequestSchema, + ListTasksRequestSchema, + TaskStatusNotificationSchema, +} from "@modelcontextprotocol/core"; // Empty object JSON schema constant (from SDK's mcp.js) const EMPTY_OBJECT_JSON_SCHEMA = { @@ -71,8 +48,83 @@ const EMPTY_OBJECT_JSON_SCHEMA = { properties: {}, } as const; -type ToolInputSchema = ZodRawShapeCompat; -type PromptArgsSchema = ZodRawShapeCompat; +// A raw arg shape: field name → zod schema. Using the full `z.ZodType` (not +// `z.ZodRawShape`, which is a readonly `$ZodType` map) keeps it assignable to the +// SDK's `registerTool`/`registerPrompt` raw-shape overloads and mutable for the +// completable() wrapping below. +type ToolInputSchema = Record; +type PromptArgsSchema = Record; + +/** + * Back-compat handler `extra` shape. + * + * SDK v2 removed `RequestHandlerExtra`; low-level handlers now receive a + * {@link ServerContext} whose per-request data lives under `ctx.mcpReq`. This + * type is the flattened view the composable test-server tool/resource/task + * handlers consume, rebuilt from `ctx.mcpReq` by {@link toHandlerExtra}. + */ +export interface HandlerExtra { + /** `_meta` sent with the request params (e.g. `_meta.progressToken`). */ + _meta?: Record; + /** The inbound JSON-RPC request id (for `relatedRequestId` on notifications). */ + requestId?: string | number; + /** Abort signal for the in-flight request. */ + signal?: AbortSignal; + /** Send a server→client request (`sampling/createMessage`, `elicitation/create`, `tasks/*`, …). */ + sendRequest?: ( + request: { method: string; params?: Record }, + resultSchema: T, + ) => Promise>; + /** Send a server→client notification. */ + sendNotification?: (notification: { + method: string; + params?: Record; + }) => Promise; +} + +/** Minimal structural view of `ctx.mcpReq` for building {@link HandlerExtra}. */ +interface McpReqContext { + mcpReq?: { + id?: string | number; + _meta?: Record; + signal?: AbortSignal; + send?: HandlerExtra["sendRequest"]; + notify?: HandlerExtra["sendNotification"]; + }; +} + +/** Rebuild the legacy flattened `extra` object from a v2 {@link ServerContext}. */ +function toHandlerExtra(ctx: ServerContext | undefined): HandlerExtra { + const mcpReq = (ctx as McpReqContext | undefined)?.mcpReq; + return { + _meta: mcpReq?._meta, + requestId: mcpReq?.id, + signal: mcpReq?.signal, + sendRequest: mcpReq?.send, + sendNotification: mcpReq?.notify, + }; +} + +/** + * Derive `prompts/list` argument descriptors from a raw zod arg shape. + * Replaces the SDK's removed `getObjectShape` / `getSchemaDescription` / + * `isSchemaOptional` compat helpers. A field is `required` unless it accepts + * `undefined` (i.e. it is `.optional()` / `.default()` / nullish). + */ +function promptArgumentsFromRawShape( + shape: Record | undefined, +): PromptArgument[] | undefined { + if (!shape) return undefined; + return Object.entries(shape).map(([name, field]) => { + const schema = field as z.ZodType; + const acceptsUndefined = schema.safeParse(undefined).success; + return { + name, + description: schema.description, + required: !acceptsUndefined, + } as PromptArgument; + }); +} interface ServerState { registeredTools: Map; // Keyed by name @@ -96,6 +148,140 @@ export interface TestServerContext { serverControl?: { isClosing(): boolean }; } +// --------------------------------------------------------------------------- +// Legacy (2025-11-25) tasks — re-implemented by hand. +// +// SDK v2 deleted the experimental tasks runtime (`InMemoryTaskStore`, +// `registerToolTask`, `client/server.experimental.tasks.*`). The Inspector still +// exercises the 2025-11-25 task wire methods end to end, so the composable +// server keeps answering them with a small in-memory store plus low-level +// custom-method handlers wired up in `createMcpServer`. +// --------------------------------------------------------------------------- + +/** Task lifecycle status (2025-11-25 vocabulary). */ +export type TaskStatus = Task["status"]; + +/** Loosely-typed argument bag handed to a task tool's `getTask`/`getTaskResult`. */ +export type ShapeOutput<_Shape> = Record; + +/** Payload a task stores as its result (for `tasks/result`). */ +export type StoredTaskResult = CallToolResult & Record; + +/** + * In-memory task store. Replaces the deleted SDK `InMemoryTaskStore`; keeps the + * subset of methods the composable fixtures use plus list/cancel for the + * `tasks/list` and `tasks/cancel` wire handlers. + */ +export class InMemoryTaskStore { + private tasks = new Map(); + private results = new Map(); + /** Optional hook fired on every status change (wired to `notifications/tasks/status`). */ + onTaskStatusChanged?: (task: Task) => void; + + private touch(task: Task): Task { + task.lastUpdatedAt = new Date().toISOString(); + return task; + } + + async createTask(options: { ttl?: number } = {}): Promise { + const now = new Date().toISOString(); + const task: Task = { + taskId: + globalThis.crypto?.randomUUID?.() ?? + `task-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, + status: "working", + ttl: options.ttl ?? null, + createdAt: now, + lastUpdatedAt: now, + }; + this.tasks.set(task.taskId, task); + this.onTaskStatusChanged?.(task); + return task; + } + + async getTask(taskId: string): Promise { + return this.tasks.get(taskId); + } + + async updateTaskStatus( + taskId: string, + status: TaskStatus, + statusMessage?: string, + ): Promise { + const task = this.tasks.get(taskId); + if (!task) return undefined; + task.status = status; + if (statusMessage !== undefined) { + task.statusMessage = statusMessage; + } + this.touch(task); + this.onTaskStatusChanged?.(task); + return task; + } + + async storeTaskResult( + taskId: string, + status: TaskStatus, + result: StoredTaskResult, + ): Promise { + this.results.set(taskId, result); + await this.updateTaskStatus(taskId, status); + } + + async getTaskResult(taskId: string): Promise { + const result = this.results.get(taskId); + if (!result) { + throw new Error(`No result stored for task ${taskId}`); + } + return result; + } + + async listTasks(): Promise { + return [...this.tasks.values()]; + } +} + +/** `extra` handed to a task tool's `createTask` (server→client requests + the store). */ +export interface CreateTaskRequestHandlerExtra { + taskStore: InMemoryTaskStore; + /** `_meta` from the augmenting `tools/call` (e.g. `_meta.progressToken`). */ + _meta?: Record; + /** The inbound request id. */ + requestId?: string | number; + /** Send a server→client request (`elicitation/create`, `sampling/createMessage`, `tasks/*`). */ + sendRequest: ( + request: { method: string; params?: Record }, + resultSchema: T, + ) => Promise>; + /** Send a server→client notification (`notifications/progress`, …). */ + sendNotification: (notification: { + method: string; + params?: Record; + }) => Promise; +} + +/** `extra` handed to a task tool's `getTask`/`getTaskResult`. */ +export interface TaskRequestHandlerExtra { + taskId: string; + taskStore: InMemoryTaskStore; +} + +/** Handler triplet for a task-augmented tool (replaces the SDK's `ToolTaskHandler`). */ +export interface ToolTaskHandler { + createTask: ( + args: Record, + extra: CreateTaskRequestHandlerExtra, + ) => Promise<{ task: Task }>; + getTask: ( + args: Record, + extra: TaskRequestHandlerExtra, + ) => Promise; + getTaskResult: ( + args: Record, + extra: TaskRequestHandlerExtra, + ) => Promise; +} + export interface ToolDefinition { name: string; description: string; @@ -109,7 +295,7 @@ export interface ToolDefinition { handler: ( params: Record, context?: TestServerContext, - extra?: RequestHandlerExtra, + extra?: HandlerExtra, ) => Promise; } @@ -122,7 +308,7 @@ export interface TaskToolDefinition { execution?: { taskSupport: "required" | "optional" }; /** Passed through to the SDK so clients can read tool-level `_meta` (e.g. `_meta.ui.resourceUri` for an App-flavored task tool). Mirrors {@link ToolDefinition._meta}. */ _meta?: Record; - handler: ToolTaskHandler; + handler: ToolTaskHandler; } export interface ResourceDefinition { @@ -164,12 +350,12 @@ export interface ResourceTemplateDefinition { description?: string; /** OAuth scopes required to read resources from this template (enforced at HTTP layer). */ requiredScopes?: string[]; - inputSchema?: ZodRawShapeCompat; // Schema for template variables + inputSchema?: Record; // Schema for template variables handler: ( uri: URL, params: Record, context?: TestServerContext, - extra?: RequestHandlerExtra, + extra?: HandlerExtra, ) => Promise<{ contents: Array<{ uri: string; mimeType?: string; text: string }>; }>; @@ -248,15 +434,10 @@ export interface ServerConfig { cancel?: boolean; // default: true }; /** - * Task store implementation (optional, defaults to InMemoryTaskStore) - * Only used if tasks capability is enabled + * Task store implementation (optional, defaults to a fresh {@link InMemoryTaskStore}). + * Only used if tasks capability is enabled. */ - taskStore?: TaskStore; - /** - * Task message queue implementation (optional, defaults to InMemoryTaskMessageQueue) - * Only used if tasks capability is enabled - */ - taskMessageQueue?: TaskMessageQueue; + taskStore?: InMemoryTaskStore; /** * OAuth 2.1 configuration for test server. * - **combined** (default): this server is both MCP resource and authorization server. @@ -400,23 +581,31 @@ export function createMcpServer(config: ServerConfig): McpServer { } } - // Create task store and message queue if tasks are enabled + // Create the in-memory task store if tasks are enabled. SDK v2 has no built-in + // task runtime, so the store is owned here and its methods answer the wire. const taskStore = config.tasks !== undefined ? config.taskStore || new InMemoryTaskStore() : undefined; - const taskMessageQueue = - config.tasks !== undefined - ? config.taskMessageQueue || new InMemoryTaskMessageQueue() - : undefined; - // Create the server with capabilities and task stores + // Create the server with capabilities const mcpServer = new McpServer(config.serverInfo, { - capabilities, - taskStore, - taskMessageQueue, + capabilities: capabilities as ServerCapabilities, }); + // Emit `notifications/tasks/status` whenever a task changes status, mirroring + // the deleted SDK task runtime. Best-effort: notifications need a live stream, + // so a send failure (e.g. no active SSE stream) must not break task execution. + if (taskStore) { + taskStore.onTaskStatusChanged = (task: Task) => { + const notification = TaskStatusNotificationSchema.parse({ + method: "notifications/tasks/status", + params: { ...task }, + }); + void mcpServer.server.notification(notification).catch(() => {}); + }; + } + // Create state (this is really session state, which is what we'll call it if we implement sessions at some point) const state: ServerState = { registeredTools: new Map(), // Keyed by name @@ -434,25 +623,23 @@ export function createMcpServer(config: ServerConfig): McpServer { ...(config.serverControl && { serverControl: config.serverControl }), }; - // Set up logging handler if logging is enabled + // Set up logging handler if logging is enabled. `logging/setLevel` is a spec + // method, so v2's `setRequestHandler` takes the method string (2-arg form). if (config.logging === true) { - mcpServer.server.setRequestHandler( - SetLevelRequestSchema, - async (request) => { - // Call optional callback if provided (for testing) - if (config.onLogLevelSet) { - config.onLogLevelSet(request.params.level); - } - // Return empty result as per MCP spec - return {}; - }, - ); + mcpServer.server.setRequestHandler("logging/setLevel", async (request) => { + // Call optional callback if provided (for testing) + if (config.onLogLevelSet) { + config.onLogLevelSet(request.params.level); + } + // Return empty result as per MCP spec + return {}; + }); } // Set up resource subscription handlers if subscriptions are enabled if (config.subscriptions === true) { mcpServer.server.setRequestHandler( - SubscribeRequestSchema, + "resources/subscribe", async (request) => { // Track subscription in state (accessible via closure) const uri = request.params.uri; @@ -462,7 +649,7 @@ export function createMcpServer(config: ServerConfig): McpServer { ); mcpServer.server.setRequestHandler( - UnsubscribeRequestSchema, + "resources/unsubscribe", async (request) => { // Remove subscription from state (accessible via closure) const uri = request.params.uri; @@ -484,50 +671,58 @@ export function createMcpServer(config: ServerConfig): McpServer { ); } + // Task tools registered on this server, keyed by name. The `tools/call` + // override below routes calls to these through their `createTask` handler + // (returning a task handle), reproducing the deleted SDK task runtime. + const taskTools = new Map(); + // Set up tools if (config.tools && config.tools.length > 0) { for (const tool of config.tools) { if (isTaskTool(tool)) { - // Register task-based tool - // registerToolTask has two overloads: one with inputSchema (required) and one without - const registered = tool.inputSchema - ? mcpServer.experimental.tasks.registerToolTask( - tool.name, - { - description: tool.description, - inputSchema: tool.inputSchema, - execution: tool.execution, - ...(tool._meta != null && { _meta: tool._meta }), - }, - tool.handler, - ) - : mcpServer.experimental.tasks.registerToolTask( - tool.name, + // Register the task tool as an ordinary tool so it surfaces in + // `tools/list` with its input schema, `_meta`, and `execution` support. + // Its callback is a defensive placeholder: the `tools/call` override + // intercepts task tools before the SDK handler ever runs it. + const registered = mcpServer.registerTool( + tool.name, + { + description: tool.description, + ...(tool.inputSchema && { inputSchema: tool.inputSchema }), + ...(tool._meta != null && { _meta: tool._meta }), + }, + async () => ({ + content: [ { - description: tool.description, - execution: tool.execution, - ...(tool._meta != null && { _meta: tool._meta }), + type: "text" as const, + text: `Task tool ${tool.name} must be invoked as a task`, }, - tool.handler, - ); + ], + isError: true, + }), + ); + // `execution` is not a `registerTool` config field; set it directly so + // `tools/list` advertises task support (matches the old registerToolTask). + registered.execution = tool.execution ?? { taskSupport: "required" }; state.registeredTools.set(tool.name, registered); + taskTools.set(tool.name, tool); } else { // Register regular tool const registered = mcpServer.registerTool( tool.name, { + inputSchema: tool.inputSchema ?? {}, description: tool.description, - inputSchema: tool.inputSchema, ...(tool.outputSchema != null && { - outputSchema: tool.outputSchema as AnySchema, + outputSchema: tool.outputSchema as z.ZodType, }), ...(tool._meta != null && { _meta: tool._meta }), }, - async (args, extra) => { + async (args, ctx) => { const result = await tool.handler( args as Record, context, - extra, + toHandlerExtra(ctx), ); const rawStructured = result && @@ -688,8 +883,13 @@ export function createMcpServer(config: ServerConfig): McpServer { { description: template.description, }, - async (uri: URL, variables: Record, extra) => { - const result = await template.handler(uri, variables, context, extra); + async (uri: URL, variables: Record, ctx) => { + const result = await template.handler( + uri, + variables, + context, + toHandlerExtra(ctx), + ); return result; }, ); @@ -705,7 +905,7 @@ export function createMcpServer(config: ServerConfig): McpServer { // If completions callbacks are provided, wrap the corresponding schemas if (prompt.completions && argsSchema) { - const enhancedSchema: ZodRawShapeCompat = { ...argsSchema }; + const enhancedSchema: Record = { ...argsSchema }; for (const [argName, completeCallback] of Object.entries( prompt.completions, )) { @@ -774,68 +974,53 @@ export function createMcpServer(config: ServerConfig): McpServer { // Tools pagination if (capabilities.tools && maxPageSize.tools !== undefined) { - mcpServer.server.setRequestHandler( - ListToolsRequestSchema, - async (request) => { - const cursor = request.params?.cursor; - const pageSize = maxPageSize.tools!; - - // Convert registered tools to Tool format using the same logic as the SDK (mcp.js lines 67-95) - const allTools: Tool[] = []; - for (const [name, registered] of state.registeredTools.entries()) { - if (registered.enabled) { - // Match SDK's approach exactly (mcp.js lines 71-95) - const toolDefinition: Record = { - name, - title: registered.title, - description: registered.description, - inputSchema: (() => { - const obj = normalizeObjectSchema(registered.inputSchema); - return obj - ? toJsonSchemaCompat(obj, { - strictUnions: true, - pipeStrategy: "input", - }) - : EMPTY_OBJECT_JSON_SCHEMA; - })(), - annotations: registered.annotations, - execution: registered.execution, - _meta: registered._meta, - }; - - if (registered.outputSchema) { - const obj = normalizeObjectSchema(registered.outputSchema); - if (obj) { - toolDefinition.outputSchema = toJsonSchemaCompat(obj, { - strictUnions: true, - pipeStrategy: "output", - }); - } - } + mcpServer.server.setRequestHandler("tools/list", async (request) => { + const cursor = request.params?.cursor; + const pageSize = maxPageSize.tools!; + + // Convert registered tools to Tool format, mirroring the SDK's tools/list. + // The input-schema JSON comes from the SDK's memoised converter; the + // output-schema JSON is the value the SDK cached at registration. + const allTools: Tool[] = []; + for (const [name, registered] of state.registeredTools.entries()) { + if (registered.enabled) { + const toolDefinition: Record = { + name, + title: registered.title, + description: registered.description, + inputSchema: + mcpServer.toolInputSchemaJson(name) ?? EMPTY_OBJECT_JSON_SCHEMA, + annotations: registered.annotations, + execution: registered.execution, + _meta: registered._meta, + }; - allTools.push(toolDefinition as Tool); + if (registered.outputSchema && registered.outputSchemaJson) { + toolDefinition.outputSchema = registered.outputSchemaJson; } - } - const startIndex = cursor ? parseInt(cursor, 10) : 0; - const endIndex = startIndex + pageSize; - const page = allTools.slice(startIndex, endIndex); - const nextCursor = - endIndex < allTools.length ? endIndex.toString() : undefined; + allTools.push(toolDefinition as Tool); + } + } - return { - tools: page, - nextCursor, - } as ListToolsResult; - }, - ); + const startIndex = cursor ? parseInt(cursor, 10) : 0; + const endIndex = startIndex + pageSize; + const page = allTools.slice(startIndex, endIndex); + const nextCursor = + endIndex < allTools.length ? endIndex.toString() : undefined; + + return { + tools: page, + nextCursor, + } as ListToolsResult; + }); } // Resources pagination if (capabilities.resources && maxPageSize.resources !== undefined) { mcpServer.server.setRequestHandler( - ListResourcesRequestSchema, - async (request, extra) => { + "resources/list", + async (request, ctx) => { const cursor = request.params?.cursor; const pageSize = maxPageSize.resources!; @@ -860,8 +1045,7 @@ export function createMcpServer(config: ServerConfig): McpServer { for (const template of state.registeredResourceTemplates.values()) { if (template.enabled && template.resourceTemplate.listCallback) { try { - const result = - await template.resourceTemplate.listCallback(extra); + const result = await template.resourceTemplate.listCallback(ctx); for (const resource of result.resources) { allResources.push({ ...resource, @@ -896,7 +1080,7 @@ export function createMcpServer(config: ServerConfig): McpServer { // Resource templates pagination if (capabilities.resources && maxPageSize.resourceTemplates !== undefined) { mcpServer.server.setRequestHandler( - ListResourceTemplatesRequestSchema, + "resources/templates/list", async (request) => { const cursor = request.params?.cursor; const pageSize = maxPageSize.resourceTemplates!; @@ -952,54 +1136,193 @@ export function createMcpServer(config: ServerConfig): McpServer { // Prompts pagination if (capabilities.prompts && maxPageSize.prompts !== undefined) { - mcpServer.server.setRequestHandler( - ListPromptsRequestSchema, - async (request) => { - const cursor = request.params?.cursor; - const pageSize = maxPageSize.prompts!; - - // Convert registered prompts to Prompt format using the same logic as the SDK - const allPrompts: Prompt[] = []; - for (const [name, prompt] of state.registeredPrompts.entries()) { - if (prompt.enabled) { - // Use the same conversion logic the SDK uses (from mcp.js line 408-419) - const shape = prompt.argsSchema - ? getObjectShape(prompt.argsSchema) - : undefined; - const arguments_ = shape - ? Object.entries(shape).map(([argName, field]) => { - const description = getSchemaDescription(field); - const isOptional = isSchemaOptional(field); - return { - name: argName, - description, - required: !isOptional, - } as PromptArgument; - }) - : undefined; - - allPrompts.push({ - name, - title: prompt.title, - description: prompt.description, - arguments: arguments_, - } as Prompt); - } + mcpServer.server.setRequestHandler("prompts/list", async (request) => { + const cursor = request.params?.cursor; + const pageSize = maxPageSize.prompts!; + + // Convert registered prompts to Prompt format. The argument descriptors + // are derived from the config's raw arg shape (the SDK no longer exposes + // a public shape-introspection helper). + const allPrompts: Prompt[] = []; + for (const [name, prompt] of state.registeredPrompts.entries()) { + if (prompt.enabled) { + const promptDef = config.prompts?.find((p) => p.name === name); + const arguments_ = promptArgumentsFromRawShape(promptDef?.argsSchema); + + allPrompts.push({ + name, + title: prompt.title, + description: prompt.description, + arguments: arguments_, + } as Prompt); } + } - const startIndex = cursor ? parseInt(cursor, 10) : 0; - const endIndex = startIndex + pageSize; - const page = allPrompts.slice(startIndex, endIndex); - const nextCursor = - endIndex < allPrompts.length ? endIndex.toString() : undefined; + const startIndex = cursor ? parseInt(cursor, 10) : 0; + const endIndex = startIndex + pageSize; + const page = allPrompts.slice(startIndex, endIndex); + const nextCursor = + endIndex < allPrompts.length ? endIndex.toString() : undefined; + + return { + prompts: page, + nextCursor, + } as ListPromptsResult; + }); + } - return { - prompts: page, - nextCursor, - } as ListPromptsResult; - }, - ); + // --- Legacy tasks wiring (only when the tasks capability is enabled) --- + if (taskStore) { + wireTaskHandlers(mcpServer, taskStore, taskTools); } return mcpServer; } + +/** Structural view of the low-level `Server`'s request-handler registry. */ +interface RawHandlerHost { + _requestHandlers: Map< + string, + (request: unknown, ctx: unknown) => Promise + >; +} + +/** Inbound `tools/call` request shape we read for task routing. */ +interface ToolsCallRequest { + params: { + name: string; + arguments?: Record; + _meta?: Record; + }; +} + +/** + * Wire up the 2025-11-25 task methods by hand: + * - override `tools/call` so a task tool returns a `{ task }` handle + * (`CreateTaskResult`) and runs its `createTask`, while ordinary tools fall + * through to the SDK's own handler; + * - register low-level `tasks/get` / `tasks/result` / `tasks/cancel` / + * `tasks/list` handlers backed by the in-memory store. + * + * The `tools/call` override is installed directly into the handler registry + * (not via `setRequestHandler`) so the `{ task }` result skips the `Server`'s + * `tools/call` result-schema validation — a task handle is not a `CallToolResult` + * but is a valid legacy `tools/call` response. + */ +function wireTaskHandlers( + mcpServer: McpServer, + taskStore: InMemoryTaskStore, + taskTools: Map, +): void { + const lowLevel = mcpServer.server; + // SDK gap (server side): `Server` exposes no public API to override an already + // registered handler or to send a `tools/call` response that bypasses its + // result-schema validation, so we reach the private `_requestHandlers` map via + // a narrowed cast (`RawHandlerHost`). Mirrors the client-side bypass in + // `core/mcp/inspectorClient.ts`; both go away when the SDK models task-augmented + // results natively. + const registry = (lowLevel as unknown as RawHandlerHost)._requestHandlers; + const sdkToolsCall = registry.get("tools/call"); + // Map each created task back to the tool that owns it, so tasks/get and + // tasks/result route through that tool's handler. + const taskOwners = new Map(); + + registry.set("tools/call", async (request, ctx) => { + const req = request as ToolsCallRequest; + const taskTool = taskTools.get(req.params.name); + if (taskTool) { + const mcpReq = (ctx as McpReqContext).mcpReq; + // Task execution is fire-and-forget: it runs AFTER this `tools/call` + // request has already responded with the task handle. Its server→client + // requests (elicitation/sampling) and notifications (progress) must + // therefore go on the server's STANDALONE stream, not the per-request + // stream (`mcpReq.send`/`mcpReq.notify` tag `relatedRequestId` to this + // now-closed request, so the client never sees them). Route through the + // server's own `request`/`notification` instead. + const extra: CreateTaskRequestHandlerExtra = { + taskStore, + _meta: req.params._meta ?? mcpReq?._meta, + requestId: mcpReq?.id, + sendRequest: ((request, resultSchema) => + mcpServer.server.request( + request, + resultSchema, + )) as CreateTaskRequestHandlerExtra["sendRequest"], + sendNotification: (notification) => + mcpServer.server.notification(notification), + }; + const { task } = await taskTool.handler.createTask( + req.params.arguments ?? {}, + extra, + ); + taskOwners.set(task.taskId, taskTool); + // Return a result that is BOTH a valid legacy `CreateTaskResult` (carries + // `task`) AND a valid wire `CallToolResult` (carries `content`). The + // Inspector's task-augmented `tools/call` is validated on the client + // against the tools/call wire schema, whose SEP-2106 guard rejects a body + // that carries `task` but no `content`; including a placeholder `content` + // satisfies that guard while the `task` handle drives the task flow (the + // real payload is later fetched via `tasks/result`). + return { + content: [{ type: "text", text: `Task ${task.taskId} created` }], + task, + }; + } + if (!sdkToolsCall) { + throw new Error("tools/call handler is not initialized"); + } + return sdkToolsCall(request, ctx); + }); + + mcpServer.server.setRequestHandler( + "tasks/get", + { params: GetTaskRequestSchema.shape.params }, + async (params): Promise => { + const taskId = params.taskId; + const owner = taskOwners.get(taskId); + if (owner) { + return owner.handler.getTask({}, { taskId, taskStore }); + } + const task = await taskStore.getTask(taskId); + if (!task) { + throw new Error(`Unknown taskId: ${taskId}`); + } + return task as GetTaskResult; + }, + ); + + mcpServer.server.setRequestHandler( + "tasks/result", + { params: GetTaskPayloadRequestSchema.shape.params }, + async (params): Promise => { + const taskId = params.taskId; + const owner = taskOwners.get(taskId); + if (owner) { + return owner.handler.getTaskResult({}, { taskId, taskStore }); + } + return taskStore.getTaskResult(taskId); + }, + ); + + mcpServer.server.setRequestHandler( + "tasks/cancel", + { params: CancelTaskRequestSchema.shape.params }, + async (params): Promise => { + const taskId = params.taskId; + const task = await taskStore.updateTaskStatus(taskId, "cancelled"); + if (!task) { + throw new Error(`Unknown taskId: ${taskId}`); + } + return task; + }, + ); + + mcpServer.server.setRequestHandler( + "tasks/list", + { params: ListTasksRequestSchema.shape.params }, + async (): Promise => { + const tasks = await taskStore.listTasks(); + return { tasks }; + }, + ); +} diff --git a/test-servers/src/server-composable.ts b/test-servers/src/server-composable.ts index bcdc2826f..3849e11ea 100644 --- a/test-servers/src/server-composable.ts +++ b/test-servers/src/server-composable.ts @@ -5,7 +5,7 @@ * Usage: server-composable --config [--json|--yaml] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import type { ResourceDefinition } from "./composable-test-server.js"; import { createMcpServer } from "./test-server-fixtures.js"; import { loadConfig, type ConfigFormat } from "./load-config.js"; diff --git a/test-servers/src/test-server-fixtures.ts b/test-servers/src/test-server-fixtures.ts index bcaf6e1b3..356dd8635 100644 --- a/test-servers/src/test-server-fixtures.ts +++ b/test-servers/src/test-server-fixtures.ts @@ -6,13 +6,20 @@ */ import * as z from "zod/v4"; -import type { Implementation } from "@modelcontextprotocol/sdk/types.js"; +import { + RELATED_TASK_META_KEY, + type Implementation, + type ElicitRequestFormParams, + type ElicitRequestURLParams, + type GetTaskResult, + type CallToolResult, +} from "@modelcontextprotocol/server"; import { CreateMessageResultSchema, CreateTaskResultSchema, ElicitResultSchema, GetTaskResultSchema, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/core"; import type { ToolDefinition, TaskToolDefinition, @@ -21,27 +28,12 @@ import type { ResourceTemplateDefinition, ServerConfig, TestServerContext, -} from "./composable-test-server.js"; -import { getTestServerControl } from "./test-server-control.js"; -import type { - ElicitRequestFormParams, - ElicitRequestURLParams, -} from "@modelcontextprotocol/sdk/types.js"; -import type { TaskRequestHandlerExtra, CreateTaskRequestHandlerExtra, -} from "@modelcontextprotocol/sdk/experimental/tasks/interfaces.js"; -import { RELATED_TASK_META_KEY } from "@modelcontextprotocol/sdk/types.js"; -import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js"; -import type { ShapeOutput } from "@modelcontextprotocol/sdk/server/zod-compat.js"; -import type { - GetTaskResult, - CallToolResult, - ServerRequest, - ServerNotification, -} from "@modelcontextprotocol/sdk/types.js"; -import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; -import type { ZodRawShapeCompat } from "@modelcontextprotocol/sdk/server/zod-compat.js"; + HandlerExtra, + ShapeOutput, +} from "./composable-test-server.js"; +import { getTestServerControl } from "./test-server-control.js"; /** Build a CallToolResult from a text message (and optional isError). */ function toToolResult(text: string, isError?: boolean): CallToolResult { @@ -1224,7 +1216,9 @@ export function createAddToolTool(): ToolDefinition { params.name as string, { description: params.description as string, - inputSchema: params.inputSchema as ZodRawShapeCompat | undefined, + inputSchema: params.inputSchema as + | Record + | undefined, }, async () => { return { @@ -1327,7 +1321,9 @@ export function createAddPromptTool(): ToolDefinition { params.name as string, { description: params.description as string | undefined, - argsSchema: params.argsSchema as ZodRawShapeCompat | undefined, + argsSchema: params.argsSchema as + | Record + | undefined, }, async () => { return { @@ -1457,7 +1453,7 @@ export function createSendProgressTool( handler: async ( params: Record, context?: TestServerContext, - extra?: RequestHandlerExtra, + extra?: HandlerExtra, ): Promise => { if (!context) { throw new Error("Server context not available"); @@ -1470,7 +1466,10 @@ export function createSendProgressTool( const message = (params.message as string) || "Processing..."; // Extract progressToken from metadata - const progressToken = extra?._meta?.progressToken; + const progressToken = extra?._meta?.progressToken as + | string + | number + | undefined; // Send progress notifications let sent = 0; @@ -1668,7 +1667,7 @@ async function runTaskExecution(params: RunTaskExecutionParams): Promise { if (elicitationSchema) { await extra.taskStore.updateTaskStatus(task.taskId, "input_required"); try { - const jsonSchema = toJsonSchemaCompat( + const jsonSchema = z.toJSONSchema( elicitationSchema, ) as ElicitRequestFormParams["requestedSchema"]; const elicitationParams: ElicitRequestFormParams = { @@ -1908,7 +1907,10 @@ export function createTaskTool( const message = (args as Record)?.message as | string | undefined; - const progressToken = extra._meta?.progressToken; + const progressToken = extra._meta?.progressToken as + | string + | number + | undefined; const task = await extra.taskStore.createTask({}); runTaskExecution({ task, diff --git a/test-servers/src/test-server-http.ts b/test-servers/src/test-server-http.ts index 4ebb5a37d..2bcadbf0c 100644 --- a/test-servers/src/test-server-http.ts +++ b/test-servers/src/test-server-http.ts @@ -1,11 +1,15 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { + McpServer, + WebStandardStreamableHTTPServerTransport, +} from "@modelcontextprotocol/server"; +import type { JSONRPCMessage } from "@modelcontextprotocol/server"; import { createMcpServer } from "./test-server-fixtures.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; -import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +import { SSEServerTransport } from "@modelcontextprotocol/server-legacy/sse"; import type { Request, Response } from "express"; import express from "express"; import { createServer as createHttpServer, Server as HttpServer } from "http"; import { createServer as createNetServer } from "net"; +import { Readable } from "node:stream"; import * as crypto from "node:crypto"; import type { ServerConfig } from "./test-server-fixtures.js"; import { @@ -20,6 +24,53 @@ import { type ServerControl, } from "./test-server-control.js"; +/** + * Build a Web-standard {@link Request} from an Express request. + * + * SDK v2's {@link WebStandardStreamableHTTPServerTransport} speaks the Fetch API + * (`Request`/`Response`) rather than Node `req`/`res`. The JSON body is passed + * to `handleRequest` via `parsedBody` (Express already parsed it), so the Web + * request carries only method, URL, and headers. + */ +function toWebRequest(req: Request): globalThis.Request { + const headers = new Headers(); + for (const [key, value] of Object.entries(req.headers)) { + if (Array.isArray(value)) { + for (const v of value) headers.append(key, v); + } else if (value != null) { + headers.set(key, String(value)); + } + } + const url = `http://localhost${req.originalUrl || req.url}`; + return new globalThis.Request(url, { method: req.method, headers }); +} + +/** + * Stream a Web-standard {@link Response} (from `handleRequest`) back onto an + * Express response, preserving status, headers, and any SSE body stream. The + * body stream is destroyed if the client disconnects first. + */ +async function writeWebResponse( + res: Response, + webResponse: globalThis.Response, +): Promise { + res.status(webResponse.status); + webResponse.headers.forEach((value, key) => { + res.setHeader(key, value); + }); + if (!webResponse.body) { + res.end(); + return; + } + const nodeStream = Readable.fromWeb( + webResponse.body as import("node:stream/web").ReadableStream, + ); + res.on("close", () => { + nodeStream.destroy(); + }); + nodeStream.pipe(res); +} + export interface RecordedRequest { method: string; params?: Record; @@ -79,7 +130,9 @@ export class TestServerHttp { private _closing = false; private recordedRequests: RecordedRequest[] = []; private httpServer?: HttpServer; - private transport?: StreamableHTTPServerTransport | SSEServerTransport; + private transport?: + | WebStandardStreamableHTTPServerTransport + | SSEServerTransport; private baseUrl?: string; private currentRequestHeaders?: Record; private currentLogLevel: string | null = null; @@ -105,10 +158,10 @@ export class TestServerHttp { * This wraps the transport's onmessage handler to record requests/notifications */ private setupMessageInterception( - transport: StreamableHTTPServerTransport | SSEServerTransport, + transport: WebStandardStreamableHTTPServerTransport | SSEServerTransport, ): void { const originalOnMessage = transport.onmessage; - transport.onmessage = async (message) => { + transport.onmessage = async (message: JSONRPCMessage) => { const timestamp = Date.now(); const method = "method" in message && typeof message.method === "string" @@ -191,7 +244,8 @@ export class TestServerHttp { } // Store transports and one McpServer per session (SDK allows only one transport per server) - const transports: Map = new Map(); + const transports: Map = + new Map(); this.mcpServersBySession = new Map(); // Bearer token middleware for MCP routes if requireAuth @@ -224,7 +278,10 @@ export class TestServerHttp { } try { - await transport.handleRequest(req, res, req.body); + const webResponse = await transport.handleRequest(toWebRequest(req), { + parsedBody: req.body, + }); + await writeWebResponse(res, webResponse); } catch (error) { // If response already sent (e.g., by OAuth middleware), don't send another if (!res.headersSent) { @@ -236,7 +293,7 @@ export class TestServerHttp { } else { // New session - create a new transport and a new McpServer (one server per connection) const sessionMcpServer = createMcpServer(this.configWithCallback); - const newTransport = new StreamableHTTPServerTransport({ + const newTransport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID(), onsessioninitialized: (sessionId: string) => { transports.set(sessionId, newTransport); @@ -257,7 +314,11 @@ export class TestServerHttp { await sessionMcpServer.connect(newTransport); try { - await newTransport.handleRequest(req, res, req.body); + const webResponse = await newTransport.handleRequest( + toWebRequest(req), + { parsedBody: req.body }, + ); + await writeWebResponse(res, webResponse); } catch (error) { // If response already sent (e.g., by OAuth middleware), don't send another if (!res.headersSent) { @@ -292,7 +353,8 @@ export class TestServerHttp { // Let the transport handle the GET request this.currentRequestHeaders = extractHeaders(req); try { - await transport.handleRequest(req, res); + const webResponse = await transport.handleRequest(toWebRequest(req)); + await writeWebResponse(res, webResponse); } catch (error) { if (!res.headersSent) { res.status(500).json({ diff --git a/test-servers/src/test-server-oauth-jwt.ts b/test-servers/src/test-server-oauth-jwt.ts index 519e0408a..7762a860c 100644 --- a/test-servers/src/test-server-oauth-jwt.ts +++ b/test-servers/src/test-server-oauth-jwt.ts @@ -2,20 +2,13 @@ * JWT access-token validation for test MCP servers using an external authorization server. */ -import { - createRemoteJWKSet, - customFetch, - jwtVerify, -} from "jose"; -import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/sdk/client/auth.js"; +import { createRemoteJWKSet, customFetch, jwtVerify } from "jose"; +import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/client"; import type { ServerConfig } from "./composable-test-server.js"; type ExternalValidatorOAuthConfig = Pick< NonNullable, - | "authorizationServers" - | "accessTokenIssuers" - | "jwksUri" - | "resourceAudience" + "authorizationServers" | "accessTokenIssuers" | "jwksUri" | "resourceAudience" >; function normalizeIssuer(issuer: string): string { @@ -28,7 +21,9 @@ function audienceMatches(aud: unknown, expected: string): boolean { return normalizeIssuer(aud) === normalized; } if (Array.isArray(aud)) { - return aud.some((a) => typeof a === "string" && normalizeIssuer(a) === normalized); + return aud.some( + (a) => typeof a === "string" && normalizeIssuer(a) === normalized, + ); } return false; } @@ -85,9 +80,7 @@ export class ExternalAccessTokenValidator { private async init(): Promise { const configuredIssuers = - this.config.accessTokenIssuers ?? - this.config.authorizationServers ?? - []; + this.config.accessTokenIssuers ?? this.config.authorizationServers ?? []; if (configuredIssuers.length === 0) { throw new Error( "External access token validation requires authorizationServers or accessTokenIssuers", diff --git a/test-servers/src/test-server-oauth.ts b/test-servers/src/test-server-oauth.ts index 4ea0ac301..c8c9bc20e 100644 --- a/test-servers/src/test-server-oauth.ts +++ b/test-servers/src/test-server-oauth.ts @@ -152,7 +152,10 @@ function setupMetadataEndpoints( const requestBaseUrl = `${req.protocol}://${req.get("host")}`; const actualIssuerUrl = config.issuerUrl ?? new URL(requestBaseUrl); const metadata = { - issuer: actualIssuerUrl.href.replace(/\/$/, "") + "/", + // RFC 8414 §3.3: the issuer MUST be identical to the base URL the + // well-known path was appended to — i.e. no trailing slash. SDK v2's + // client enforces this exactly (IssuerMismatchError otherwise). + issuer: actualIssuerUrl.href.replace(/\/$/, ""), authorization_endpoint: new URL("/oauth/authorize", actualIssuerUrl) .href, token_endpoint: new URL("/oauth/token", actualIssuerUrl).href, @@ -160,7 +163,10 @@ function setupMetadataEndpoints( response_types_supported: ["code"], grant_types_supported: ["authorization_code", "refresh_token"], code_challenge_methods_supported: ["S256"], - token_endpoint_auth_methods_supported: ["client_secret_basic", "none"], + token_endpoint_auth_methods_supported: [ + "client_secret_basic", + "none", + ], ...(config.supportDCR && { registration_endpoint: new URL("/oauth/register", actualIssuerUrl) .href, @@ -180,12 +186,10 @@ function setupMetadataEndpoints( "/.well-known/oauth-protected-resource", (req: Request, res: Response) => { const requestBaseUrl = `${req.protocol}://${req.get("host")}`; - const resourceUrl = - config.resource ?? new URL("/", requestBaseUrl).href; - const localAsUrl = (config.issuerUrl ?? new URL(requestBaseUrl)).href.replace( - /\/$/, - "", - ); + const resourceUrl = config.resource ?? new URL("/", requestBaseUrl).href; + const localAsUrl = ( + config.issuerUrl ?? new URL(requestBaseUrl) + ).href.replace(/\/$/, ""); const authorizationServers = mode === "protected-resource" ? (config.authorizationServers ?? []) @@ -278,7 +282,11 @@ async function parseAuthorizationRequest( } if (response_type !== "code") { - return { ok: false, status: 400, body: { error: "unsupported_response_type" } }; + return { + ok: false, + status: 400, + body: { error: "unsupported_response_type" }, + }; } const client = await findClient(client_id, config); @@ -286,10 +294,7 @@ async function parseAuthorizationRequest( return { ok: false, status: 400, body: { error: "invalid_client" } }; } - if ( - client.redirectUris && - !client.redirectUris.includes(redirect_uri) - ) { + if (client.redirectUris && !client.redirectUris.includes(redirect_uri)) { return { ok: false, status: 400, @@ -874,7 +879,10 @@ export function buildScopeRequirementRegistry( } for (const template of config.resourceTemplates ?? []) { if (template.requiredScopes?.length) { - registry.resourceTemplates.set(template.uriTemplate, template.requiredScopes); + registry.resourceTemplates.set( + template.uriTemplate, + template.requiredScopes, + ); } } @@ -967,14 +975,15 @@ export function createScopeCheckMiddleware( return next(); } - const granted = - oauthReq.oauthTokenScopes ?? getAccessTokenScopes(token); + const granted = oauthReq.oauthTokenScopes ?? getAccessTokenScopes(token); if (tokenHasRequiredScopes(granted, requiredScopes)) { return next(); } const grantedSet = new Set(granted); - const missingScopes = requiredScopes.filter((scope) => !grantedSet.has(scope)); + const missingScopes = requiredScopes.filter( + (scope) => !grantedSet.has(scope), + ); const scopeHeader = missingScopes.join(" ") || requiredScopes.join(" "); res.status(403); res.setHeader("Content-Type", "application/json"); diff --git a/test-servers/src/test-server-stdio.ts b/test-servers/src/test-server-stdio.ts index cbf583da1..3d115f794 100644 --- a/test-servers/src/test-server-stdio.ts +++ b/test-servers/src/test-server-stdio.ts @@ -5,8 +5,8 @@ * Can be used programmatically or run as a standalone executable */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import { fileURLToPath } from "url"; import type { ServerConfig, diff --git a/vitest.shared.mts b/vitest.shared.mts index 0326e3c27..f0c25d724 100644 --- a/vitest.shared.mts +++ b/vitest.shared.mts @@ -19,7 +19,12 @@ export function vitestSharedPaths(clientDir: string) { ), }; - const sharedDedupe = ['react', 'react-dom', '@modelcontextprotocol/sdk']; + const sharedDedupe = [ + 'react', + 'react-dom', + '@modelcontextprotocol/client', + '@modelcontextprotocol/core', + ]; const nodeModulesAliases = [ { find: /^react$/, replacement: path.resolve(dirname, 'node_modules/react') }, @@ -64,13 +69,6 @@ export function vitestSharedPaths(clientDir: string) { }, { find: /^express$/, replacement: path.resolve(dirname, 'node_modules/express') }, { find: /^yaml$/, replacement: path.resolve(repoRoot, 'node_modules/yaml') }, - { - find: /^@modelcontextprotocol\/sdk\/client\/auth\.js$/, - replacement: path.resolve( - dirname, - 'node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js', - ), - }, ]; const projectResolve = {