diff --git a/package-lock.json b/package-lock.json index ce84da188d..31a45ee6cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1579,8 +1579,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.0.tgz", "integrity": "sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=20" }, @@ -1726,12 +1726,43 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0.tgz", + "integrity": "sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@modelcontextprotocol/core": "2.0.0", + "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", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0.tgz", + "integrity": "sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", @@ -1767,6 +1798,20 @@ } } }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz", + "integrity": "sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", @@ -3165,6 +3210,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -3211,8 +3257,8 @@ "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3228,8 +3274,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -3316,6 +3362,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -3340,6 +3387,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -3379,6 +3427,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -3388,6 +3437,7 @@ "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==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3401,6 +3451,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3427,6 +3478,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -3440,6 +3492,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -3456,6 +3509,7 @@ "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -3465,6 +3519,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.6.0" @@ -3474,8 +3529,8 @@ "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "object-assign": "^4", "vary": "^1" @@ -3516,6 +3571,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" @@ -3540,6 +3596,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -3559,6 +3616,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3583,12 +3641,14 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -3598,6 +3658,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3607,6 +3668,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3623,6 +3685,7 @@ "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==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3677,6 +3740,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, "license": "MIT" }, "node_modules/escape-string-regexp": { @@ -4087,6 +4151,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4097,7 +4162,6 @@ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", - "peer": true, "dependencies": { "eventsource-parser": "^3.0.1" }, @@ -4128,6 +4192,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -4171,8 +4236,8 @@ "version": "8.5.2", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ip-address": "^10.2.0" }, @@ -4197,6 +4262,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -4224,6 +4290,7 @@ "version": "3.1.8", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "dev": true, "funding": [ { "type": "github", @@ -4295,6 +4362,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -4367,6 +4435,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4376,6 +4445,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -4400,6 +4470,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4439,6 +4510,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4463,6 +4535,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -4517,6 +4590,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4539,6 +4613,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4551,6 +4626,7 @@ "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==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4563,8 +4639,8 @@ "version": "4.13.8", "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.8.tgz", "integrity": "sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -4580,6 +4656,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -4630,6 +4707,7 @@ "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -4666,14 +4744,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, "license": "ISC" }, "node_modules/ip-address": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 12" } @@ -4682,6 +4761,7 @@ "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==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" @@ -4730,6 +4810,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -4805,7 +4886,6 @@ "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -4859,14 +4939,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, "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", - "peer": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5232,6 +5313,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5241,6 +5323,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -5250,6 +5333,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -5262,6 +5346,7 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -5271,6 +5356,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -5313,6 +5399,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/nanoid": { @@ -5345,6 +5432,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -5394,8 +5482,8 @@ "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", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5404,6 +5492,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5427,6 +5516,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -5439,6 +5529,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -5543,6 +5634,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -5578,6 +5670,7 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, "license": "MIT", "funding": { "type": "opencollective", @@ -5616,7 +5709,6 @@ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.20.0" } @@ -5746,6 +5838,7 @@ "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==", + "dev": true, "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -5769,6 +5862,7 @@ "version": "6.15.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -5784,6 +5878,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -5793,6 +5888,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -5808,6 +5904,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5893,6 +5990,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -5930,6 +6028,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, "license": "MIT" }, "node_modules/semver": { @@ -5949,6 +6048,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -5975,6 +6075,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -5994,6 +6095,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, "license": "ISC" }, "node_modules/shebang-command": { @@ -6021,6 +6123,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6040,6 +6143,7 @@ "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==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6056,6 +6160,7 @@ "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==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -6074,6 +6179,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -6143,6 +6249,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -6229,6 +6336,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.6" @@ -6335,6 +6443,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dev": true, "license": "MIT", "dependencies": { "content-type": "^2.0.0", @@ -6353,6 +6462,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6388,6 +6498,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -6420,6 +6531,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -6664,6 +6776,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/ws": { @@ -6721,7 +6834,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -6730,8 +6842,8 @@ "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==", + "dev": true, "license": "ISC", - "peer": true, "peerDependencies": { "zod": "^3.25.28 || ^4" } @@ -6766,6 +6878,8 @@ "@cedar-policy/mcp-schema-generator-wasm": "^0.6.0", "@eslint/js": "^10.0.1", "@google/genai": "^2.6.0", + "@modelcontextprotocol/sdk": "^1.25.2", + "@modelcontextprotocol/server": "^2.0.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.219.0", "@opentelemetry/exporter-trace-otlp-http": "^0.219.0", @@ -6806,7 +6920,7 @@ "@cedar-policy/cedar-wasm": "^4.0.0", "@cedar-policy/mcp-schema-generator-wasm": "^0.6.0", "@google/genai": "^2.6.0", - "@modelcontextprotocol/sdk": "^1.25.2", + "@modelcontextprotocol/client": "^2.0.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.219.0", "@opentelemetry/exporter-trace-otlp-http": "^0.219.0", diff --git a/strands-ts/README.md b/strands-ts/README.md index 43ffd89638..efeb2390cb 100644 --- a/strands-ts/README.md +++ b/strands-ts/README.md @@ -219,7 +219,7 @@ Seamlessly integrate Model Context Protocol (MCP) servers: ```typescript import { Agent, McpClient } from "@strands-agents/sdk"; -import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; +import { StdioClientTransport } from "@modelcontextprotocol/client/stdio"; // Create a client for a local MCP server const documentationTools = new McpClient({ diff --git a/strands-ts/examples/mcp/package.json b/strands-ts/examples/mcp/package.json index 48ca2f98c7..377e0fb472 100644 --- a/strands-ts/examples/mcp/package.json +++ b/strands-ts/examples/mcp/package.json @@ -13,6 +13,7 @@ "../../" ], "dependencies": { + "@modelcontextprotocol/client": "^2.0.0", "@strands-agents/sdk": "*" }, "devDependencies": { diff --git a/strands-ts/examples/mcp/src/index.ts b/strands-ts/examples/mcp/src/index.ts index ba2de5d73e..56c9df75df 100644 --- a/strands-ts/examples/mcp/src/index.ts +++ b/strands-ts/examples/mcp/src/index.ts @@ -1,6 +1,6 @@ import { Agent, McpClient } from '@strands-agents/sdk' -import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' +import { StdioClientTransport } from '@modelcontextprotocol/client/stdio' +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/client' async function runInvoke(title: string, agent: Agent, prompt: string) { console.log(`--- ${title} ---\nUser: ${prompt}`) diff --git a/strands-ts/package.json b/strands-ts/package.json index 6366f11bad..9d0cd6ff54 100644 --- a/strands-ts/package.json +++ b/strands-ts/package.json @@ -243,6 +243,8 @@ "@cedar-policy/mcp-schema-generator-wasm": "^0.6.0", "@eslint/js": "^10.0.1", "@google/genai": "^2.6.0", + "@modelcontextprotocol/sdk": "^1.25.2", + "@modelcontextprotocol/server": "^2.0.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.219.0", "@opentelemetry/exporter-trace-otlp-http": "^0.219.0", @@ -298,7 +300,7 @@ "@cedar-policy/cedar-wasm": "^4.0.0", "@cedar-policy/mcp-schema-generator-wasm": "^0.6.0", "@google/genai": "^2.6.0", - "@modelcontextprotocol/sdk": "^1.25.2", + "@modelcontextprotocol/client": "^2.0.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.219.0", "@opentelemetry/exporter-trace-otlp-http": "^0.219.0", diff --git a/strands-ts/src/mcp/__tests__/client-annotations.test.ts b/strands-ts/src/mcp/__tests__/client-annotations.test.ts index abb46369c4..1305096245 100644 --- a/strands-ts/src/mcp/__tests__/client-annotations.test.ts +++ b/strands-ts/src/mcp/__tests__/client-annotations.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' -import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js' -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' +import { InMemoryTransport, type ToolAnnotations } from '@modelcontextprotocol/client' +import { McpServer } from '@modelcontextprotocol/server' import { z } from 'zod' import { McpClient } from '../client.js' @@ -17,7 +17,7 @@ describe('McpClient annotations over a real transport', () => { { description: 'Echoes input', inputSchema: { value: z.string() }, - ...(annotations !== undefined && { annotations }), + ...(annotations !== undefined && { annotations: annotations as ToolAnnotations }), }, async ({ value }) => ({ content: [{ type: 'text', text: value }] }) ) diff --git a/strands-ts/src/mcp/__tests__/client.test.ts b/strands-ts/src/mcp/__tests__/client.test.ts index 62fa6c41ce..4fa6261dfa 100644 --- a/strands-ts/src/mcp/__tests__/client.test.ts +++ b/strands-ts/src/mcp/__tests__/client.test.ts @@ -1,14 +1,14 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' -import { Client } from '@modelcontextprotocol/sdk/client/index.js' -import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' import { - McpError, - ErrorCode, - ElicitRequestSchema, + Client, + ProtocolError, + ProtocolErrorCode, UrlElicitationRequiredError, -} from '@modelcontextprotocol/sdk/types.js' -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' -import { ClientCredentialsProvider } from '@modelcontextprotocol/sdk/client/auth-extensions.js' + StreamableHTTPClientTransport, + ClientCredentialsProvider, + type Transport, + type LoggingMessageNotificationParams, +} from '@modelcontextprotocol/client' import { McpClient } from '../client.js' import { McpTool } from '../../tools/mcp-tool.js' import { JsonBlock, type TextBlock, type ToolResultBlock } from '../../types/messages.js' @@ -19,31 +19,17 @@ import type { ElicitationCallback } from '../../types/elicitation.js' import { context, propagation, trace, TraceFlags } from '@opentelemetry/api' import type { SpanContext } from '@opentelemetry/api' import { logger } from '../../logging/index.js' -import type { LoggingMessageNotificationParams } from '@modelcontextprotocol/sdk/types.js' -/** - * Helper to create a mock async generator that yields a result message. - * This simulates the behavior of callToolStream returning a stream that ends with a result. - */ -function createMockCallToolStream(result: unknown) { - return async function* () { - yield { type: 'result', result } - } -} - -vi.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({ +// Only the constructible surfaces are mocked; the error classes and other exports stay real so +// instanceof checks in the code under test keep working. +vi.mock('@modelcontextprotocol/client', async (importOriginal) => ({ + ...(await importOriginal()), StreamableHTTPClientTransport: vi.fn(function () { return { start: vi.fn(), send: vi.fn(), close: vi.fn() } }), -})) - -vi.mock('@modelcontextprotocol/sdk/client/auth-extensions.js', () => ({ ClientCredentialsProvider: vi.fn(function () { return { redirectUrl: undefined, clientMetadata: { client_id: 'test' } } }), -})) - -vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ Client: vi.fn(function () { return { connect: vi.fn(), @@ -55,11 +41,6 @@ vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ getServerCapabilities: vi.fn(), getServerVersion: vi.fn(), getInstructions: vi.fn(), - experimental: { - tasks: { - callToolStream: vi.fn(), - }, - }, } }), })) @@ -151,7 +132,6 @@ describe('MCP Integration', () => { getServerCapabilities: ReturnType getServerVersion: ReturnType getInstructions: ReturnType - experimental: { tasks: { callToolStream: ReturnType } } } beforeEach(() => { @@ -166,6 +146,7 @@ describe('MCP Integration', () => { expect(Client).toHaveBeenCalledWith( { name: 'TestApp', version: '0.0.1' }, expect.objectContaining({ + versionNegotiation: { mode: 'auto' }, listChanged: expect.objectContaining({ tools: expect.objectContaining({ autoRefresh: false, debounceMs: 300 }), }), @@ -428,32 +409,7 @@ describe('MCP Integration', () => { await prefixedClient.callTool(tools[1]!, { value: 1 }) expect(tools.map((tool) => tool.name)).toEqual(['server_keep_one', 'server_keep_two']) - expect(prefixedSdkClient.callTool).toHaveBeenCalledWith( - { name: 'keep_two', arguments: { value: 1 } }, - undefined, - undefined - ) - }) - - it('uses the server-side name for task-based invocation of a prefixed tool', async () => { - const taskClient = new McpClient({ - applicationName: 'TestApp', - transport: mockTransport, - prefix: 'server', - tasksConfig: {}, - }) - const taskSdkClient = vi.mocked(Client).mock.results.at(-1)!.value - taskSdkClient.listTools.mockResolvedValue({ tools: [{ name: 'long_task', inputSchema: {} }] }) - taskSdkClient.experimental.tasks.callToolStream.mockReturnValue(createMockCallToolStream({ content: [] })()) - - const [tool] = await taskClient.listTools() - await taskClient.callTool(tool!, {}) - - expect(taskSdkClient.experimental.tasks.callToolStream).toHaveBeenCalledWith( - { name: 'long_task', arguments: {} }, - undefined, - expect.any(Object) - ) + expect(prefixedSdkClient.callTool).toHaveBeenCalledWith({ name: 'keep_two', arguments: { value: 1 } }, undefined) }) it('surfaces tool annotations in the tool spec', async () => { @@ -512,19 +468,14 @@ describe('MCP Integration', () => { expect(tools[0]!.description).toBe('Tool which performs my_tool') }) - it('uses callTool when tasksConfig is undefined (default)', async () => { + it('calls the tool with no request options by default', async () => { const tool = new McpTool({ name: 'calc', description: '', inputSchema: {}, client }) sdkClientMock.callTool.mockResolvedValue({ content: [] }) await client.callTool(tool, { op: 'add' }) expect(sdkClientMock.connect).toHaveBeenCalled() - expect(sdkClientMock.callTool).toHaveBeenCalledWith( - { name: 'calc', arguments: { op: 'add' } }, - undefined, - undefined - ) - expect(sdkClientMock.experimental.tasks.callToolStream).not.toHaveBeenCalled() + expect(sdkClientMock.callTool).toHaveBeenCalledWith({ name: 'calc', arguments: { op: 'add' } }, undefined) }) it('forwards abort signal to SDK callTool', async () => { @@ -534,33 +485,27 @@ describe('MCP Integration', () => { await client.callTool(tool, { op: 'add' }, { signal: controller.signal }) - expect(sdkClientMock.callTool).toHaveBeenCalledWith({ name: 'calc', arguments: { op: 'add' } }, undefined, { - signal: controller.signal, - }) + expect(sdkClientMock.callTool).toHaveBeenCalledWith( + { name: 'calc', arguments: { op: 'add' } }, + { signal: controller.signal } + ) }) - it('forwards abort signal to callToolStream when tasksConfig is provided', async () => { + it('throws on callTool when tasksConfig is set', async () => { const resultsLengthBefore = vi.mocked(Client).mock.results.length const taskClient = new McpClient({ applicationName: 'TestApp', transport: mockTransport, - tasksConfig: {}, + tasksConfig: { ttl: 30000, pollTimeout: 120000 }, }) const taskSdkClientMock = vi.mocked(Client).mock.results[resultsLengthBefore]!.value const tool = new McpTool({ name: 'calc', description: '', inputSchema: {}, client: taskClient }) - taskSdkClientMock.experimental.tasks.callToolStream.mockReturnValue(createMockCallToolStream({ content: [] })()) - const controller = new AbortController() - - await taskClient.callTool(tool, { op: 'add' }, { signal: controller.signal }) - expect(taskSdkClientMock.experimental.tasks.callToolStream).toHaveBeenCalledWith( - { name: 'calc', arguments: { op: 'add' } }, - undefined, - { timeout: 60000, maxTotalTimeout: 300000, resetTimeoutOnProgress: true, signal: controller.signal } - ) + await expect(taskClient.callTool(tool, { op: 'add' })).rejects.toThrow(/temporarily unavailable/) + expect(taskSdkClientMock.callTool).not.toHaveBeenCalled() }) - it('uses callToolStream when tasksConfig is provided (empty object)', async () => { + it('still lists tools when tasksConfig is set', async () => { const resultsLengthBefore = vi.mocked(Client).mock.results.length const taskClient = new McpClient({ applicationName: 'TestApp', @@ -568,38 +513,22 @@ describe('MCP Integration', () => { tasksConfig: {}, }) const taskSdkClientMock = vi.mocked(Client).mock.results[resultsLengthBefore]!.value - const tool = new McpTool({ name: 'calc', description: '', inputSchema: {}, client: taskClient }) - taskSdkClientMock.experimental.tasks.callToolStream.mockReturnValue(createMockCallToolStream({ content: [] })()) + taskSdkClientMock.listTools.mockResolvedValue({ tools: [{ name: 'calc', inputSchema: {} }] }) - await taskClient.callTool(tool, { op: 'add' }) + const tools = await taskClient.listTools() - expect(taskSdkClientMock.connect).toHaveBeenCalled() - expect(taskSdkClientMock.experimental.tasks.callToolStream).toHaveBeenCalledWith( - { name: 'calc', arguments: { op: 'add' } }, - undefined, - { timeout: 60000, maxTotalTimeout: 300000, resetTimeoutOnProgress: true } - ) - expect(taskSdkClientMock.callTool).not.toHaveBeenCalled() + expect(tools.map((tool) => tool.name)).toEqual(['calc']) }) - it('passes custom TTL and pollTimeout to callToolStream', async () => { - const resultsLengthBefore = vi.mocked(Client).mock.results.length - const taskClient = new McpClient({ - applicationName: 'TestApp', - transport: mockTransport, - tasksConfig: { ttl: 30000, pollTimeout: 120000 }, - }) - const taskSdkClientMock = vi.mocked(Client).mock.results[resultsLengthBefore]!.value - const tool = new McpTool({ name: 'calc', description: '', inputSchema: {}, client: taskClient }) - taskSdkClientMock.experimental.tasks.callToolStream.mockReturnValue(createMockCallToolStream({ content: [] })()) + it('warns at construction when tasksConfig is set and only then', () => { + const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}) - await taskClient.callTool(tool, { op: 'add' }) + new McpClient({ applicationName: 'TestApp', transport: mockTransport, tasksConfig: {} }) + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('callTool will throw')) - expect(taskSdkClientMock.experimental.tasks.callToolStream).toHaveBeenCalledWith( - { name: 'calc', arguments: { op: 'add' } }, - undefined, - { timeout: 30000, maxTotalTimeout: 120000, resetTimeoutOnProgress: true } - ) + warnSpy.mockClear() + new McpClient({ applicationName: 'TestApp', transport: mockTransport }) + expect(warnSpy).not.toHaveBeenCalled() }) it('validates tool arguments', async () => { @@ -631,7 +560,7 @@ describe('MCP Integration', () => { await elicitClient.connect() - expect(elicitSdkClientMock.setRequestHandler).toHaveBeenCalledWith(ElicitRequestSchema, expect.any(Function)) + expect(elicitSdkClientMock.setRequestHandler).toHaveBeenCalledWith('elicitation/create', expect.any(Function)) const setHandlerOrder = elicitSdkClientMock.setRequestHandler.mock.invocationCallOrder[0]! const connectOrder = elicitSdkClientMock.connect.mock.invocationCallOrder[0]! expect(setHandlerOrder).toBeLessThan(connectOrder) @@ -655,7 +584,7 @@ describe('MCP Integration', () => { expect(lastCall[1]).toEqual(expect.objectContaining({ capabilities: { elicitation: { form: {}, url: {} } } })) }) - it('elicitation handler returns accepted result with content', async () => { + it('elicitation handler returns accepted result with content and exposes the abort signal at mcpReq.signal and signal', async () => { const callbackResult = { action: 'accept' as const, content: { username: 'alice' } } const callback: ElicitationCallback = vi.fn().mockResolvedValue(callbackResult) const { handler } = await connectAndGetElicitationHandler(callback) @@ -663,11 +592,15 @@ describe('MCP Integration', () => { method: 'elicitation/create', params: { message: 'Enter username', requestedSchema: { type: 'object' } }, } - const extra = { signal: new AbortController().signal } + const abortSignal = new AbortController().signal + const extra = { mcpReq: { signal: abortSignal } } const result = await handler(request, extra) - expect(callback).toHaveBeenCalledWith(extra, request.params) + expect(callback).toHaveBeenCalledWith({ ...extra, signal: abortSignal }, request.params) + const receivedContext = vi.mocked(callback).mock.calls[0]![0] + expect(receivedContext.mcpReq.signal).toBe(abortSignal) + expect(receivedContext.signal).toBe(abortSignal) expect(result).toEqual({ action: 'accept', content: { username: 'alice' } }) }) @@ -680,11 +613,11 @@ describe('MCP Integration', () => { method: 'elicitation/create', params: { message: 'Enter username', requestedSchema: { type: 'object' } }, } - const extra = { signal: new AbortController().signal } + const extra = { mcpReq: { signal: new AbortController().signal } } const result = await handler(request, extra) - expect(callback).toHaveBeenCalledWith(extra, request.params) + expect(callback).toHaveBeenCalledWith({ ...extra, signal: extra.mcpReq.signal }, request.params) expect(result).toEqual({ action: callbackResult.action }) } ) @@ -702,11 +635,11 @@ describe('MCP Integration', () => { elicitationId: 'elicit-123', }, } - const extra = { signal: new AbortController().signal } + const extra = { mcpReq: { signal: new AbortController().signal } } const result = await handler(request, extra) - expect(callback).toHaveBeenCalledWith(extra, request.params) + expect(callback).toHaveBeenCalledWith({ ...extra, signal: extra.mcpReq.signal }, request.params) expect(result).toEqual({ action: 'accept' }) }) @@ -717,7 +650,7 @@ describe('MCP Integration', () => { method: 'elicitation/create', params: { message: 'Confirm?' }, } - const extra = { signal: new AbortController().signal } + const extra = { mcpReq: { signal: new AbortController().signal } } await expect(handler(request, extra)).rejects.toThrow('User cancelled') }) @@ -735,7 +668,6 @@ describe('MCP Integration', () => { getServerCapabilities: ReturnType getServerVersion: ReturnType getInstructions: ReturnType - experimental: { tasks: { callToolStream: ReturnType } } } beforeEach(() => { @@ -1098,7 +1030,7 @@ describe('MCP Integration', () => { expect((result.content[2] as TextBlock).text).toBe('Some notes') }) - it('surfaces elicitation data for McpError with code -32042', async () => { + it('surfaces elicitation data for ProtocolError with code -32042', async () => { const elicitations = [ { mode: 'url', @@ -1107,18 +1039,20 @@ describe('MCP Integration', () => { url: 'https://github.com/login/oauth/authorize?client_id=abc', }, ] - const mcpError = new McpError(ErrorCode.UrlElicitationRequired, 'Authorization required', { elicitations }) - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + const protocolError = new ProtocolError(ProtocolErrorCode.UrlElicitationRequired, 'Authorization required', { + elicitations, + }) + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') expect((result.content[0] as TextBlock).text).toBe( - `MCP Elicitation required: [${String(mcpError)}] with data ${JSON.stringify(elicitations)}` + `MCP Elicitation required: [${String(protocolError)}] with data ${JSON.stringify(elicitations)}` ) }) - it('surfaces multiple elicitations for McpError with code -32042', async () => { + it('surfaces multiple elicitations for ProtocolError with code -32042', async () => { const elicitations = [ { mode: 'url', @@ -1133,27 +1067,29 @@ describe('MCP Integration', () => { url: 'https://accounts.google.com/o/oauth2/auth', }, ] - const mcpError = new McpError(ErrorCode.UrlElicitationRequired, 'Authorization required', { elicitations }) - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + const protocolError = new ProtocolError(ProtocolErrorCode.UrlElicitationRequired, 'Authorization required', { + elicitations, + }) + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') expect((result.content[0] as TextBlock).text).toBe( - `MCP Elicitation required: [${String(mcpError)}] with data ${JSON.stringify(elicitations)}` + `MCP Elicitation required: [${String(protocolError)}] with data ${JSON.stringify(elicitations)}` ) }) - it('falls through to generic error for McpError -32042 with malformed data', async () => { - const mcpError = new McpError(ErrorCode.UrlElicitationRequired, 'Authorization required', { + it('falls through to generic error for ProtocolError -32042 with malformed data', async () => { + const protocolError = new ProtocolError(ProtocolErrorCode.UrlElicitationRequired, 'Authorization required', { unexpected: 'shape', }) - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') - expect((result.content[0] as TextBlock).text).toBe('MCP error -32042: Authorization required') + expect((result.content[0] as TextBlock).text).toBe('Authorization required') }) it('surfaces elicitation data for UrlElicitationRequiredError', async () => { @@ -1175,48 +1111,48 @@ describe('MCP Integration', () => { expect((result.content[0] as TextBlock).text).toContain('https://example.com/auth') }) - it('falls through to generic error for McpError -32042 with undefined data', async () => { - const mcpError = new McpError(ErrorCode.UrlElicitationRequired, 'Auth required') - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + it('falls through to generic error for ProtocolError -32042 with undefined data', async () => { + const protocolError = new ProtocolError(ProtocolErrorCode.UrlElicitationRequired, 'Auth required') + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') - expect((result.content[0] as TextBlock).text).toBe('MCP error -32042: Auth required') + expect((result.content[0] as TextBlock).text).toBe('Auth required') }) - it('falls through to generic error for McpError -32042 with non-array elicitations', async () => { - const mcpError = new McpError(ErrorCode.UrlElicitationRequired, 'Auth required', { + it('falls through to generic error for ProtocolError -32042 with non-array elicitations', async () => { + const protocolError = new ProtocolError(ProtocolErrorCode.UrlElicitationRequired, 'Auth required', { elicitations: 'not-an-array', }) - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') - expect((result.content[0] as TextBlock).text).toBe('MCP error -32042: Auth required') + expect((result.content[0] as TextBlock).text).toBe('Auth required') }) - it('falls through to generic error for McpError -32042 with empty elicitations', async () => { - const mcpError = new McpError(ErrorCode.UrlElicitationRequired, 'Auth required', { + it('falls through to generic error for ProtocolError -32042 with empty elicitations', async () => { + const protocolError = new ProtocolError(ProtocolErrorCode.UrlElicitationRequired, 'Auth required', { elicitations: [], }) - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') - expect((result.content[0] as TextBlock).text).toBe('MCP error -32042: Auth required') + expect((result.content[0] as TextBlock).text).toBe('Auth required') }) - it('falls through to generic error for McpError with a different code', async () => { - const mcpError = new McpError(ErrorCode.InvalidRequest, 'Bad request') - vi.mocked(mockClientWrapper.callTool).mockRejectedValue(mcpError) + it('falls through to generic error for ProtocolError with a different code', async () => { + const protocolError = new ProtocolError(ProtocolErrorCode.InvalidRequest, 'Bad request') + vi.mocked(mockClientWrapper.callTool).mockRejectedValue(protocolError) const result = await runTool(tool.stream(toolContext)) expect(result.status).toBe('error') - expect((result.content[0] as TextBlock).text).toBe('MCP error -32600: Bad request') + expect((result.content[0] as TextBlock).text).toBe('Bad request') }) }) }) diff --git a/strands-ts/src/mcp/__tests__/config.test.node.ts b/strands-ts/src/mcp/__tests__/config.test.node.ts index 58954bed1f..de6d9562f3 100644 --- a/strands-ts/src/mcp/__tests__/config.test.node.ts +++ b/strands-ts/src/mcp/__tests__/config.test.node.ts @@ -1,8 +1,6 @@ import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest' -import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' -import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' -import { Client } from '@modelcontextprotocol/sdk/client/index.js' +import { StdioClientTransport } from '@modelcontextprotocol/client/stdio' +import { StreamableHTTPClientTransport, SSEClientTransport, Client } from '@modelcontextprotocol/client' import { McpClient } from '../client.js' import { mcpServerLoader } from '../config.js' @@ -18,20 +16,15 @@ vi.mock('node:path', () => ({ join: (...segments: string[]) => segments.join('/'), })) -vi.mock('@modelcontextprotocol/sdk/client/stdio.js', () => ({ +vi.mock('@modelcontextprotocol/client/stdio', () => ({ StdioClientTransport: vi.fn(function () {}), getDefaultEnvironment: vi.fn(() => ({ PATH: '/usr/bin', HOME: '/home/user' })), })) -vi.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({ +vi.mock('@modelcontextprotocol/client', async (importOriginal) => ({ + ...(await importOriginal()), StreamableHTTPClientTransport: vi.fn(function () {}), -})) - -vi.mock('@modelcontextprotocol/sdk/client/sse.js', () => ({ SSEClientTransport: vi.fn(function () {}), -})) - -vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ Client: vi.fn(function (this: Record) { this.connect = vi.fn() this.close = vi.fn() @@ -42,7 +35,6 @@ vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ this.getServerCapabilities = vi.fn() this.getServerVersion = vi.fn() this.getInstructions = vi.fn() - this.experimental = { tasks: { callToolStream: vi.fn() } } }), })) diff --git a/strands-ts/src/mcp/client.ts b/strands-ts/src/mcp/client.ts index 64f7f0aebd..23d69953b0 100644 --- a/strands-ts/src/mcp/client.ts +++ b/strands-ts/src/mcp/client.ts @@ -1,16 +1,13 @@ -import { Client } from '@modelcontextprotocol/sdk/client/index.js' -import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' -import { ClientCredentialsProvider } from '@modelcontextprotocol/sdk/client/auth-extensions.js' -import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js' -import { takeResult } from '@modelcontextprotocol/sdk/shared/responseMessage.js' import { - ElicitRequestSchema, - LoggingMessageNotificationSchema, + Client, + ClientCredentialsProvider, + StreamableHTTPClientTransport, + type Transport, + type OAuthClientProvider, type ServerCapabilities, type Implementation, type LoggingMessageNotificationParams, -} from '@modelcontextprotocol/sdk/types.js' +} from '@modelcontextprotocol/client' import { context, propagation, trace } from '@opentelemetry/api' import type { JSONSchema, JSONValue } from '../types/json.js' import type { ElicitationCallback } from '../types/elicitation.js' @@ -21,10 +18,12 @@ import { type McpLoadServersOptions, type McpServerConfig, mcpServerLoader } fro /** * Widened transport type that accepts MCP transport implementations without requiring explicit casts. * - * Under `exactOptionalPropertyTypes`, `StreamableHTTPClientTransport` is not directly assignable - * to `Transport` because its `sessionId` getter returns `string | undefined`, while `Transport` - * declares `sessionId?: string` (absent or string, but not explicitly undefined). - * This type relaxes that constraint so users can pass any MCP transport without `as Transport`. + * The `sessionId` member is widened to `string | undefined` so that, under + * `exactOptionalPropertyTypes`, transport instances whose `sessionId` getter returns + * `string | undefined` — including transports constructed from the legacy + * `@modelcontextprotocol/sdk` package — are assignable without `as Transport`. The MCP `Transport` + * contract's required members (`start`, `send`, `close`) are unchanged between the legacy package + * and `@modelcontextprotocol/client`, so legacy instances keep working. */ export type McpTransport = Omit & { sessionId?: string | undefined } @@ -40,21 +39,15 @@ export interface RuntimeConfig { * WARNING: MCP Tasks is an experimental feature in both the MCP specification and this SDK. * The API may change without notice in future versions. * - * When provided to McpClient, enables task-based tool invocation which supports - * long-running tools with progress tracking. Without this config, tools are - * called directly without task management. + * Task-augmented execution is temporarily unavailable while task support is rebuilt on the + * MCP tasks extension (https://github.com/strands-agents/harness-sdk/issues/1659). A client + * constructed with `tasksConfig` throws from {@link McpClient.callTool}. */ export interface TasksConfig { - /** - * Time-to-live in milliseconds for task polling. - * Defaults to 60000 (60 seconds). - */ + /** Time-to-live in milliseconds for task polling. */ ttl?: number - /** - * Maximum time in milliseconds to wait for task completion during polling. - * Defaults to 300000 (5 minutes). - */ + /** Maximum time in milliseconds to wait for task completion during polling. */ pollTimeout?: number } @@ -113,8 +106,9 @@ export interface McpClientOptions extends RuntimeConfig { /** * Configuration for task-augmented tool execution (experimental). - * When provided (even as empty object), enables MCP task-based tool invocation. - * When undefined, tools are called directly without task management. + * + * Temporarily unavailable while task support is rebuilt on the MCP tasks extension + * (https://github.com/strands-agents/harness-sdk/issues/1659). When set, `callTool` throws. */ tasksConfig?: TasksConfig @@ -152,10 +146,18 @@ export type McpClientConfig = McpClientOptions & { /** MCP Client for interacting with Model Context Protocol servers. */ export class McpClient { - /** Default TTL for task polling in milliseconds (60 seconds). */ + /** + * Default TTL for task polling in milliseconds (60 seconds). + * + * Unused while task support is rebuilt on the MCP tasks extension (#1659). + */ public static readonly DEFAULT_TTL = 60000 - /** Default poll timeout for task completion in milliseconds (5 minutes). */ + /** + * Default poll timeout for task completion in milliseconds (5 minutes). + * + * Unused while task support is rebuilt on the MCP tasks extension (#1659). + */ public static readonly DEFAULT_POLL_TIMEOUT = 300000 /** @@ -211,6 +213,9 @@ export class McpClient { }, { ...(this._elicitationCallback ? { capabilities: { elicitation: { form: {}, url: {} } } } : undefined), + // Probe for protocol revision 2026-07-28 and fall back to the legacy initialize + // handshake, mirroring the Python SDK's negotiate_auto posture. + versionNegotiation: { mode: 'auto' }, listChanged: { tools: { autoRefresh: false, @@ -223,11 +228,17 @@ export class McpClient { } ) - this._client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => { + this._client.setNotificationHandler('notifications/message', (notification) => { this._logHandler(notification.params) }) this._disableMcpInstrumentation = args.disableMcpInstrumentation ?? false + + if (this._tasksConfig !== undefined) { + logger.warn( + `client=<${this._clientName}> | tasksConfig is set but task-augmented execution is temporarily unavailable (#1659), callTool will throw` + ) + } } private static _resolveTransport(args: McpClientConfig): Transport { @@ -312,8 +323,10 @@ export class McpClient { if (this._elicitationCallback) { const callback = this._elicitationCallback - this._client.setRequestHandler(ElicitRequestSchema, async (request, extra) => { - return await callback(extra, request.params) + this._client.setRequestHandler('elicitation/create', async (request, requestContext) => { + // The top-level `signal` mirrors `mcpReq.signal` for callbacks written against the + // deprecated ElicitationContext.signal field. + return await callback({ ...requestContext, signal: requestContext.mcpReq.signal }, request.params) }) } @@ -445,16 +458,23 @@ export class McpClient { /** * Invoke a tool on the connected MCP server using an McpTool instance. * - * When `tasksConfig` was provided to the client constructor, uses experimental - * task-based invocation which supports long-running tools with progress tracking. - * Otherwise, calls tools directly without task management. - * * @param tool - The McpTool instance to invoke. * @param args - The arguments to pass to the tool. * @param options - Optional settings for the request. * @returns A promise that resolves with the result of the tool invocation. + * @throws Error when the client was constructed with `tasksConfig`: task-augmented execution + * is temporarily unavailable while task support is rebuilt on the MCP tasks extension + * (https://github.com/strands-agents/harness-sdk/issues/1659). */ public async callTool(tool: McpTool, args: JSONValue, options?: McpCallToolOptions): Promise { + if (this._tasksConfig !== undefined) { + throw new Error( + 'MCP task-augmented execution is temporarily unavailable while task support is rebuilt ' + + 'on the MCP tasks extension (https://github.com/strands-agents/harness-sdk/issues/1659). ' + + 'Unset tasksConfig to call tools now.' + ) + } + await this.connect() if (this._state === 'failed') throw new Error('MCP server failed to connect. Call connect(true) to retry.') @@ -476,21 +496,7 @@ export class McpClient { // Use the server-side name for server communication; tool.name may carry a prefix. const toolName = this._serverToolNames.get(tool) ?? tool.name - if (this._tasksConfig === undefined) { - return (await this._client.callTool({ name: toolName, arguments: toolArgs }, undefined, options)) as JSONValue - } - - // When tasksConfig is defined (even as empty object), use task-based invocation - // which supports long-running tools with progress tracking - const stream = this._client.experimental.tasks.callToolStream({ name: toolName, arguments: toolArgs }, undefined, { - timeout: this._tasksConfig.ttl ?? McpClient.DEFAULT_TTL, - maxTotalTimeout: this._tasksConfig.pollTimeout ?? McpClient.DEFAULT_POLL_TIMEOUT, - resetTimeoutOnProgress: true, - ...options, - }) - - const result = await takeResult(stream) - return result as JSONValue + return (await this._client.callTool({ name: toolName, arguments: toolArgs }, options)) as JSONValue } } diff --git a/strands-ts/src/mcp/config.node.ts b/strands-ts/src/mcp/config.node.ts index 11336a2132..21ed89feb2 100644 --- a/strands-ts/src/mcp/config.node.ts +++ b/strands-ts/src/mcp/config.node.ts @@ -1,8 +1,8 @@ import { readFile } from 'node:fs/promises' import { homedir } from 'node:os' import { join } from 'node:path' -import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js' -import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' +import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio' +import { SSEClientTransport } from '@modelcontextprotocol/client' import type { McpClientConfig, McpClientOptions, McpToolFilters, McpTransport } from './client.js' import type { McpLoadServersOptions, McpServerConfig } from './config.js' import { logger } from '../logging/index.js' diff --git a/strands-ts/src/mcp/config.ts b/strands-ts/src/mcp/config.ts index 2aa2c0434b..5a77d144ad 100644 --- a/strands-ts/src/mcp/config.ts +++ b/strands-ts/src/mcp/config.ts @@ -44,7 +44,12 @@ export interface McpServerConfig { disabled?: boolean /** When true, config or connection failures skip this server instead of throwing. */ continueOnError?: boolean - /** Task-augmented tool execution configuration (experimental). */ + /** + * Task-augmented tool execution configuration (experimental). + * + * Temporarily unavailable while task support is rebuilt on the MCP tasks extension + * (https://github.com/strands-agents/harness-sdk/issues/1659). When set, tool calls throw. + */ tasksConfig?: TasksConfig } diff --git a/strands-ts/src/tools/mcp-tool.ts b/strands-ts/src/tools/mcp-tool.ts index 5e4d195bc9..96c5c584e8 100644 --- a/strands-ts/src/tools/mcp-tool.ts +++ b/strands-ts/src/tools/mcp-tool.ts @@ -1,4 +1,4 @@ -import { McpError, ErrorCode, UrlElicitationRequiredError } from '@modelcontextprotocol/sdk/types.js' +import { ProtocolError, ProtocolErrorCode, UrlElicitationRequiredError } from '@modelcontextprotocol/client' import { createErrorResult, Tool, type ToolContext, type ToolStreamGenerator } from './tool.js' import type { ToolSpec } from './types.js' @@ -76,7 +76,7 @@ export class McpTool extends Tool { } catch (error) { if ( error instanceof UrlElicitationRequiredError || - (error instanceof McpError && error.code === ErrorCode.UrlElicitationRequired) + (error instanceof ProtocolError && error.code === ProtocolErrorCode.UrlElicitationRequired) ) { const elicitations = error instanceof UrlElicitationRequiredError diff --git a/strands-ts/src/types/elicitation.ts b/strands-ts/src/types/elicitation.ts index 99522b0245..7a99303c8a 100644 --- a/strands-ts/src/types/elicitation.ts +++ b/strands-ts/src/types/elicitation.ts @@ -1,15 +1,17 @@ -import type { - ElicitResult, - ElicitRequestParams, - ClientRequest, - ClientNotification, -} from '@modelcontextprotocol/sdk/types.js' -import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js' +import type { ElicitResult, ElicitRequestParams, ClientContext } from '@modelcontextprotocol/client' /** - * Context provided to an elicitation callback, including the abort signal for the in-flight request. + * Context provided to an elicitation callback. The abort signal for the in-flight request is + * available at `context.mcpReq.signal`. */ -export type ElicitationContext = RequestHandlerExtra +export type ElicitationContext = ClientContext & { + /** + * Abort signal for the in-flight request. + * + * @deprecated Read `context.mcpReq.signal` instead. + */ + signal: AbortSignal +} /** * Callback invoked when an MCP server sends an elicitation request to gather user input during tool execution. diff --git a/strands-ts/test/integ/mcp/mcp-tasks.test.node.ts b/strands-ts/test/integ/mcp/mcp-tasks.test.node.ts index c0182bcbf0..5b4eab2893 100644 --- a/strands-ts/test/integ/mcp/mcp-tasks.test.node.ts +++ b/strands-ts/test/integ/mcp/mcp-tasks.test.node.ts @@ -13,7 +13,7 @@ import type { TasksConfig } from '@strands-agents/sdk' * Returns the client - caller is responsible for disconnecting. * @param serverUrl - The URL of the MCP server * @param appName - The application name for the client - * @param tasksConfig - Optional tasks configuration. When provided, enables task-based tool invocation. + * @param tasksConfig - Optional tasks configuration. Currently makes tool calls throw (#1659). */ function createClient(serverUrl: string, appName: string, tasksConfig?: TasksConfig): McpClient { return new McpClient({ @@ -37,7 +37,10 @@ describe('MCP Task Integration Tests', () => { await Promise.all([taskServerInfo?.close(), nonTaskServerInfo?.close()]) }, 30000) - describe('McpClient.callTool() with Task-Enabled Server', () => { + // Task-augmented invocation returns with the redesigned MCP tasks extension + // (strands-agents/harness-sdk#1659); until then a client with tasksConfig throws on callTool, + // and the fixture's tools are registered task-only so plain calls would be rejected anyway. + describe.skip('McpClient.callTool() with Task-Enabled Server', () => { it('extracts result from task tool that completes immediately', async () => { if (!taskServerInfo) throw new Error('Task server not started') @@ -147,7 +150,8 @@ describe('MCP Task Integration Tests', () => { }, 30000) }) - describe('Agent Integration with Task Tools', () => { + // Skipped for the same reason as the task-enabled callTool suite above (#1659). + describe.skip('Agent Integration with Task Tools', () => { it('agent can use task tools in a conversation', async () => { if (!taskServerInfo) throw new Error('Task server not started')