{"_id":"@platformatic/mcp","_rev":"20-f921377c050ed8f965a90bc7e0725596","name":"@platformatic/mcp","dist-tags":{"latest":"2.6.0"},"versions":{"0.1.0":{"name":"@platformatic/mcp","version":"0.1.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@0.1.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"4abdc8e3b61e3df0fe8b20b22fe892a679b4e67e","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-0.1.0.tgz","fileCount":88,"integrity":"sha512-KS75Qz4zNKM5tIcH6dmpA069OKDIvSEYUpeR/0wwtdURrAXFy9H6F7DHPJOeD6lXb0v3POmMRKZ2r4cNQvt6nw==","signatures":[{"sig":"MEQCIFHCC9lyTThjuIvyMXoWc2jOoctoWIXM+nhnyLuZTc+5AiAmfe5UuAC19JfHeUCLpMR8IqLLJ9k+AX1CXE1/JUvUyA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":485294},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"5c0beb847ec6a93e7692add56c5e9e1b04ebad88","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.4.2","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"ioredis":"^5.0.0","mqemitter":"^7.1.0","fastify-plugin":"^5.0.1","safe-stable-stringify":"^2.5.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","mqemitter-redis":"^7.1.0","@sinclair/typebox":"^0.34.37","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.1.0_1753091409568_0.8752475591387487","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@platformatic/mcp","version":"0.1.1","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@0.1.1","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"aa8bc8445a8ccc6785bfeb1a41c9f0a145bbec8e","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-0.1.1.tgz","fileCount":88,"integrity":"sha512-FUghy7jjHwvEMzUP0WpRcp2a7VhsHZu5Tnm/9hNQxEn7NA+63S1kKtgFem2IAvailOtEZb6W+AXFRaIrzlhBNg==","signatures":[{"sig":"MEUCIQDyF9BvX1TUL3P7SPnuwXwnRkg6y4q/AuqKWGiulX0kuwIgOoUAPTbU22zOcmCiitBEOo9wvz7a3wf/vaXw12KuySs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":485294},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"d62e9089c16fa2351e934429d53e540f98624925","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.4.2","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"ioredis":"^5.0.0","mqemitter":"^7.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.1.1_1753091448134_0.978471132742333","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@platformatic/mcp","version":"0.2.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@0.2.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"363e053374aa3898151a98f624670f0de4764393","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-0.2.0.tgz","fileCount":88,"integrity":"sha512-y2k35lKCM4IFJFM0VJI7WTp+Sg9CmHR7X7fj8//92JfnAF7/q43L/7Dsvl4SZiJyYsm45denXqkxzEEdCAxniQ==","signatures":[{"sig":"MEYCIQDT12Peh+dDPTHDa9ZjTmmYZPcHNFRkCWKBkw3I6Gj8EgIhAJzR0zkrI6KzHSRpPtYlTiGFa/5/aS2JQ+w6BefM6g+y","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":485294},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"890b5aa5f395e4632be5b9aad7e61f4f6642a971","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.4.2","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"ioredis":"^5.0.0","mqemitter":"^7.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.2.0_1753092246682_0.22627401428954852","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@platformatic/mcp","version":"1.0.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@1.0.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"90d3aaded93da92117875eb55ee06018785031bf","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-1.0.0.tgz","fileCount":88,"integrity":"sha512-+YOtOEd6FGlJwa6x9Sxe9ldGxcbqSDCuO9r+ouVl57tFF3em26F6IVwSDR+mpc2k1tGDrwZxXFaIXIF8ZNPkVQ==","signatures":[{"sig":"MEQCIC6WgibHF99T6aHu8+MHtq7lUTfxzcnE/sUxzx89tIFSAiAbRajAn3VdLRH667J2QHK4Qx8UzETEOfbPCeFtkxbKfw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":485294},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"674bacf1f4e0f39d1bbfacf0301fde0f2b41e2db","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.4.2","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"ioredis":"^5.0.0","mqemitter":"^7.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_1.0.0_1753092281940_0.02689078438911352","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@platformatic/mcp","version":"1.1.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@1.1.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"e102368119d30b340aa60a77b4c8046d0bc132c3","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-1.1.0.tgz","fileCount":142,"integrity":"sha512-kp/v2lCd+IVsFVZdyZ1Wq5wJpFRhX+LmF13mIDcOH905kipTeg1dhNsjCAZfuPQIS4IJkzjs3Q5RNgFDnFmMJg==","signatures":[{"sig":"MEQCIA9Jn3vUZnesnowjEqoDMh04YY9nrxedg6SYbnCXVqVAAiAnbhs6KeiiL4hhoT+TFZKKa346xDgbiXXoeENaogJCuQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":920450},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"8e24775def74e530113c92f90365d749b67878f4","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"10.9.2","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.17.1","dependencies":{"ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^9.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^5.2.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_1.1.0_1754080926985_0.6542916965325638","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@platformatic/mcp","version":"1.2.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@1.2.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"d96cd388837bf4b5a31d2bb6d4ca3e40624e65e3","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-1.2.0.tgz","fileCount":69,"integrity":"sha512-UKobQyU7sv8oZoomAd+K5bPk67v8vHQa2jYqfrC+j6sgvDr83IvKyszXXADlJS1JFrgmlbktm0uJeDSNzFAmSQ==","signatures":[{"sig":"MEYCIQCVmVxtYase2qyFoaosMcSCK83bfRTYkALHD2mVEPjsGgIhAIE2XfznFnB2mnuzEbMAy+UYf714OSHD0knSZkHTZJqb","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356280},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"10.9.3","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^9.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^5.2.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_1.2.0_1756236898448_0.15382489049106307","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@platformatic/mcp","version":"1.2.1","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@1.2.1","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"6dfa1aa51b2bb4b80894b0a5a64c7b469ee0078f","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-1.2.1.tgz","fileCount":69,"integrity":"sha512-RQzZFqgzXj31gxrH7dgzzWmVfxBgHZGIDoPpe4BA3f54AZz4OasWFiy9cWReu7UbGfjxZJhf9gfPBOBdAIoiTA==","signatures":[{"sig":"MEUCIGn8pFGAfjVFipa+JEArvTpH4QFqQSxQ1hWCqCnZn84MAiEA6Vz8CQx0MmvIoKknVTd4TDgOWQllDfvmMzxB11Wfu0o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":359798},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"6d012761478682f5f49edb5347e053e8f250888f","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","prepublish":"npm run build","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"10.9.3","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^9.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^5.2.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_1.2.1_1756278098963_0.9515828906852797","host":"s3://npm-registry-packages-npm-production"}},"1.2.2":{"name":"@platformatic/mcp","version":"1.2.2","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@1.2.2","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"22d31966bb340b2fecd2d376d2db7824e5d35e15","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-1.2.2.tgz","fileCount":71,"integrity":"sha512-ixT6dsNTgTbZrpA9XF6LyZf4AQwLorr28Wisc6PS1NRHczSwUc4xuDhBNLfV3LyAudloBx5AA+ifrBLFmKyUoA==","signatures":[{"sig":"MEUCID8qeK2DY99uw7uUblJFOxU4VNtSYLIR6EDnVjz+H8KBAiEAk3dZx/2F/S6IS795eP8xvtnm6JCwaDkt+GjKWc8V8UY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":365621},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"e792cdc6eb0832577a6039bab437ab062ba6d5b1","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"10.9.3","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^9.1.0","@fastify/cors":"^11.1.0","fastify-plugin":"^5.0.1","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^5.2.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.12.1","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.16.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_1.2.2_1757599436310_0.6584326205692519","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@platformatic/mcp","version":"1.3.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@1.3.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"8586b7b798a58e98f0c7d8cebdead63e3b83bac4","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-1.3.0.tgz","fileCount":70,"integrity":"sha512-IYmlNyWu0hGCwl9DY80ZNQNcEHAzpTA1Rx/sQJWYzZMQ/4InBjr/0bUBj41Vqc5ABiaAd8Li45iE0WRm7wWI9Q==","signatures":[{"sig":"MEUCIGUmd4Yx1LqWXEBKyjXAuKr3ENSX/tQNva4awbQoFtK5AiEAvhbYeJGfDRMl+dOfenTCBsTEoxqxGI7rUeC/xPGA5CA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":382812},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"18239ac5f58b706185b5e3b7c04ed14c58deb99b","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.12.1","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^9.1.0","@fastify/cors":"^11.1.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_1.3.0_1784704928758_0.6262917006132842","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@platformatic/mcp","version":"2.1.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.1.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"c9117be2a8744e3f4a4bce407cf6808a2e42aa0f","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.1.0.tgz","fileCount":78,"integrity":"sha512-miqlOWCC1q8mt61d+MuFzDUAqsTC4RtodHdHbmVFdOyyktLI6ihu15d6tFIpFLIoaT+NVOfFwMJ9FN3pHxLv4Q==","signatures":[{"sig":"MEYCIQDhlwYSXXd1wtSYfGK77nP44Po05i4rwCCb21kUkl4dfAIhAPPbvpu2Fee6ftvMzv2wyuc0XMAEietE09IQF1lq1fcD","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":482668},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"d0d90794cfcf24b59b1f7b2ed056751e2c6f6bf9","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.12.1","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.1.0_1784812763341_0.6337571244873355","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@platformatic/mcp","version":"2.2.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.2.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"2f531dd06c48d167bd90a2f0dadb2376df1c18f0","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.2.0.tgz","fileCount":82,"integrity":"sha512-Qgl0+jDgHFOzk7BoLIsjyZ57LdEJVZT3kbFAhjwHFjvIQ1G1XmaeJVVAWTX+VNXYGyj7CCY/rDpJzymaiBmtbA==","signatures":[{"sig":"MEYCIQD4XKm+V/gzW5W8jPW2wj2482caavNJfBYsDHUw0QrFrwIhALhF5EkRDEHpwa0ltseHNwrWdSMZ/jU1JOWvtJs4+BZB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":503364},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"9946dd8eb94a37e745a75f4fb72b44daaca2c4e0","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.12.1","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.2.0_1785767597443_0.7238745593742291","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"name":"@platformatic/mcp","version":"2.3.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.3.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"fa6e76c2dc995acdf95b2142531b7c9641fa5aec","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.3.0.tgz","fileCount":84,"integrity":"sha512-WC0HCLUur6pJdLuD/xkzWe/zQjwXS4x+yp5u3L5o3sTY2bCzcePQpYHAq9CW/BIAJIUsSDCXH5R4z1qXoliIFA==","signatures":[{"sig":"MEQCICPCzGOgtbJHqaIJqGfY2OMMrCrdbzCqiMh63ExGqW9jAiBApvqGNOUKboF47WL2Qj2bB9B/0ejzaL9XoKQlaCeQNw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":530025},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"5d9de95cd1ffaf72a0019d97f376e15b9ce7c307","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.19.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.18.1","dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-error":"^0.3.1","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.3.0_1787165796266_0.10074221690175023","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"@platformatic/mcp","version":"2.4.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.4.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"bc42103074a1df1675a1449b7f45f31f5b27bfc4","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.4.0.tgz","fileCount":84,"integrity":"sha512-CjZGx951/q7hrdALOBIlQadcKxJd54rPUsOInuPHVVbusnoDaQMHAl/tg37rX7FOsrht0Q9oak7ROCHFlOZPdg==","signatures":[{"sig":"MEUCIQCwWXd0a96JodPDm86FJqRTyKF4RCH+Ap7V4C2Uz8E7gAIgIJL3k4VUkGmwf0vd4J07hiwFmmZ7VD4cyxeXfJuRwGM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":537685},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"75937d3c28ef79bcd2ed2edb53ab9cba75f5a3cc","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.16.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-error":"^1.0.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.4.0_1787340868791_0.7226106834482184","host":"s3://npm-registry-packages-npm-production"}},"2.5.0":{"name":"@platformatic/mcp","version":"2.5.0","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.5.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"b1167cccabff255f9891ec608df33cab2939e54b","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.5.0.tgz","fileCount":88,"integrity":"sha512-XCfiXAki9UrZ5hGsdq1reqxLTufOu1NUWaruxPoFCHWosDGt4ec5t3TuLHQnL896O20yqHrRd6BdAl0Td3XWqw==","signatures":[{"sig":"MEQCIAClCz8MB6G36AvLN3pQWcvTMx2RZLxtE1/zg5ilm8IRAiA6f251QixwX+SWftUJt9YZYUJ7+J1iQbFYmuk0wNlQnA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIE028IE5RLY/gZyHaaheXuwG03AJHqT9qQSxyW2EZ5XvAiA6VNe0/+oxGkyrIvsVlmb4YlkL16mRJISl2hdiepLCKA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":551589},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"0514a329b1334c130b565fb370f398dfd811f60b","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.16.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"24.18.0","allowScripts":{"@fastify/pre-commit@2.2.1":true,"@modelcontextprotocol/ext-apps@0.3.1":true,"@modelcontextprotocol/ext-apps@1.1.0":true},"dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-error":"^1.0.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@sinclair/typebox":"^0.34.37","@opentelemetry/api":"^1.9.0","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.5.0_1789571559172_0.6521447281594643","host":"s3://npm-registry-packages-npm-production"}},"2.5.1":{"name":"@platformatic/mcp","version":"2.5.1","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.5.1","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"5b07751932511ee19fb9d907001874cb67d8361a","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.5.1.tgz","fileCount":88,"integrity":"sha512-iiRpCa+lVFWUmyXlsAZedQoPqSJ6oLl0aIcHyIMBeOJd8GyqQN8UtUEM3UxwpX0ChUlpwNzpwTwcIBnV7kpjWw==","signatures":[{"sig":"MEUCIBa/5XBUQadeYZG4k14t/f+eHz+EFbJ6V+NRxIXu2JpNAiEA5bH5HMhUFyOmseHdY3a7JnFdylpbttmCUkaUkj8YekA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDBVqXiiTSwjw4af+VMEgJVuTbIloutxEdoVHk4hCedCQIhAJ8HKY6Yv3P1K1EvbAhtPfkXUB1QuQ84ti0sa+o7GHdT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fmcp@2.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":551766},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"7d65044e17d8106f5c73d0b63402c4965df849ed","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd","postpublish":"git push origin && git push origin -f --tags","prepublishOnly":"npm run build && npm run lint"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eff78de8-386a-4ebe-9adf-012022643faa"}},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.16.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.23.2","allowScripts":{"@fastify/pre-commit@2.2.1":true,"@modelcontextprotocol/ext-apps@0.3.1":true,"@modelcontextprotocol/ext-apps@1.1.0":true},"dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-error":"^1.0.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.1.0","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","semver":"^7.7.2","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@types/semver":"^7.7.1","@sinclair/typebox":"^0.34.37","@opentelemetry/api":"^1.9.0","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.5.1_1789634483263_0.5628144893906266","host":"s3://npm-registry-packages-npm-production"}},"2.5.2":{"name":"@platformatic/mcp","version":"2.5.2","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.5.2","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"b6e17c2bd7b4ca0d9b758997e368e91188f08696","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.5.2.tgz","fileCount":88,"integrity":"sha512-CXFX3T7wCOmVIZ8EA/Lg4uCgxWh95CGgP5Wmd5M3basLCAzcKrGbmKj3m+u9LMHcNKcs5ebtDixrbEkRYnqe6A==","signatures":[{"sig":"MEUCIHzqO/8N7HcbXByFDdJ3g38jd46ixjncok/h4vzaYHecAiEA2Og/+Uxe0gv7Y8xzH8dbSDvp/DaYS/jhvi8W95tX+is=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQC1TCGS5meIi5G9PDAOUltdOa5xUFzSDN5+g/DkHCHwVwIhALapbVoTLHAJMSOvcd89GhN8ZP1JxFdk7+YhyPTJTo75","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fmcp@2.5.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":551766},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"585dd828f2b99756abe0173c6c2189b2f51c8c06","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd","postpublish":"git push origin && git push origin -f --tags","prepublishOnly":"npm run build && npm run lint"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eff78de8-386a-4ebe-9adf-012022643faa"}},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.16.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.23.2","allowScripts":{"@fastify/pre-commit@2.2.1":true,"@modelcontextprotocol/ext-apps@0.3.1":true,"@modelcontextprotocol/ext-apps@1.1.0":true},"dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-error":"^1.0.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.2.1","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","semver":"^7.7.2","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@types/semver":"^7.7.1","@sinclair/typebox":"^0.34.37","@opentelemetry/api":"^1.9.0","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.5.2_1789670230942_0.649133136176822","host":"s3://npm-registry-packages-npm-production"}},"2.5.3":{"name":"@platformatic/mcp","version":"2.5.3","keywords":["fastify","mcp","model","context","protocol"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/mcp@2.5.3","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"homepage":"https://github.com/platformatic/mcp#readme","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"bdc776bd93b6d3f37d5186a3927c50ae7ba9004d","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.5.3.tgz","fileCount":88,"integrity":"sha512-JtW0nnZDVYZjFXzjZedh+NyDeTfSTFTh6CF5B30DZpHVqQ+OPjikLdcmf5z6zwXP7v5cGWl6jP8RLLQtQ6j7sQ==","signatures":[{"sig":"MEYCIQDEoSN1FWaTpKgMsTvTs0/4INwT3r4VWeSnOodlg/+YaQIhAIi1fHv/T6LR+sHR7fsN2FXtQHnBtVWSye54pUNTyNXw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIDq1u6ydUz3ihWETbAhwqjwg+n4QOTBB2fmR9F7Hj/u3AiAwxwtwOz3MCEqo7DCJ/QaXavEKxqVAHKSuM5BwtvU8BA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fmcp@2.5.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":552164},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"f4f4590b7de12d7f1f61d4a608243890ad4f2751","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd","postpublish":"git push origin && git push origin -f --tags","prepublishOnly":"npm run build && npm run lint"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"eff78de8-386a-4ebe-9adf-012022643faa"}},"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.16.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"_nodeVersion":"22.23.3","allowScripts":{"@fastify/pre-commit@2.2.1":true,"@modelcontextprotocol/ext-apps@0.3.1":true,"@modelcontextprotocol/ext-apps@1.1.0":true},"dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","@fastify/cors":"^11.1.0","fastify-error":"^1.0.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.2.1","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","semver":"^7.7.2","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@types/semver":"^7.7.1","@sinclair/typebox":"^0.34.37","@opentelemetry/api":"^1.9.0","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mcp_2.5.3_1791208421447_0.7365474093153705","host":"s3://npm-registry-packages-npm-production"}},"2.6.0":{"_id":"@platformatic/mcp@2.6.0","bugs":{"url":"https://github.com/platformatic/mcp/issues"},"dist":{"shasum":"dcce5e33724e0cda35c19c48f1030f6a24c97e60","tarball":"https://registry.npmjs.org/@platformatic/mcp/-/mcp-2.6.0.tgz","fileCount":93,"integrity":"sha512-pe3Trlbyv5diyoRYt80A7X7OR0jAJBgNfJUjjp/4TtjgVOdhL1jYf/s8uQai1UbSxiA9NQt8UUBuxkLXIRiVAA==","signatures":[{"sig":"MEUCIQC/BEWHYx3994xfA0oj/fQ2txAh28ayspzZB9VMGbDYngIgK7BKoqbtQTqM2dfFB6+vACA5J7277NAbdZZe+U0B8Rw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDX6gwZZkpJdmpXifQVwXXaekQdg+9W6qAu4v17crvsNgIhAOMxIK4e+tjY7uH4Ej8FlJJMZxCWZiFc0yeJ9B+bwLHX"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fmcp@2.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":648162},"main":"dist/index.js","name":"@platformatic/mcp","type":"module","types":"dist/index.d.ts","author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"gitHead":"5260dd9c8bc1fe3f60c667f70a43d9cd24ed2ac5","license":"Apache-2.0","scripts":{"ci":"npm run build && npm run lint && npm run test","lint":"eslint --cache","test":"npm run test:types && node --experimental-strip-types --no-warnings --test --test-timeout=30000 --test-concurrency=1 test/*.test.ts","build":"rm -rf dist && tsc -p tsconfig.base.json","prepare":"npm run build","lint:fix":"eslint --cache --fix","typecheck":"tsc -p . --noEmit","start-demo":"node --watch examples/file-listing-server.ts | pino-pretty --colorize --translateTime 'SYS:standard' --ignore pid,hostname","test:types":"tsd","postpublish":"git push origin && git push origin -f --tags","prepublishOnly":"npm run build && npm run lint"},"version":"2.6.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"eff78de8-386a-4ebe-9adf-012022643faa"}},"homepage":"https://github.com/platformatic/mcp#readme","keywords":["fastify","mcp","model","context","protocol"],"pre-commit":["lint","typecheck"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"_npmVersion":"11.16.0","description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","directories":{},"maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"_nodeVersion":"22.23.4","allowScripts":{"@fastify/pre-commit@2.2.1":true,"@modelcontextprotocol/ext-apps@0.3.1":true,"@modelcontextprotocol/ext-apps@1.1.0":true},"dependencies":{"ajv":"^8.17.1","ioredis":"^5.0.0","fast-jwt":"^6.0.2","get-jwks":"^11.0.1","mqemitter":"^7.1.0","@fastify/jwt":"^10.0.0","fastify-error":"^1.0.0","fastify-plugin":"^6.0.0","mqemitter-redis":"^7.2.1","safe-stable-stringify":"^2.5.0","@fastify/type-provider-typebox":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","eslint":"^9.30.0","semver":"^7.7.2","undici":"^7.11.0","fastify":"^5.4.0","typescript":"^5.8.3","@types/node":"^24.0.10","neostandard":"^0.13.0","pino-pretty":"^13.0.0","@fastify/cors":"^11.1.0","@types/semver":"^7.7.1","@sinclair/typebox":"^0.34.37","@opentelemetry/api":"^1.9.0","@fastify/pre-commit":"^2.2.0","@modelcontextprotocol/sdk":"^1.13.3","@modelcontextprotocol/inspector":"^0.22.0"},"peerDependencies":{"@sinclair/typebox":"^0.34.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_2.6.0_1793274484686_0.6536938553452621"}}},"time":{"created":"2025-07-21T09:50:09.567Z","modified":"2026-10-29T11:48:05.377Z","0.1.0":"2025-07-21T09:50:09.778Z","0.1.1":"2025-07-21T09:50:48.314Z","0.2.0":"2025-07-21T10:04:06.885Z","1.0.0":"2025-07-21T10:04:42.118Z","1.1.0":"2025-08-01T20:42:07.238Z","1.2.0":"2025-08-26T19:34:59.060Z","1.2.1":"2025-08-27T07:01:39.175Z","1.2.2":"2025-09-11T14:03:56.509Z","1.3.0":"2026-07-22T07:22:08.898Z","2.1.0":"2026-07-23T13:19:23.492Z","2.2.0":"2026-08-03T14:33:17.631Z","2.3.0":"2026-08-19T18:56:36.426Z","2.4.0":"2026-08-21T19:34:28.975Z","2.5.0":"2026-09-16T15:12:39.271Z","2.5.1":"2026-09-17T08:41:23.351Z","2.5.2":"2026-09-17T18:37:11.039Z","2.5.3":"2026-10-05T13:53:41.573Z","2.6.0":"2026-10-29T11:48:04.797Z"},"bugs":{"url":"https://github.com/platformatic/mcp/issues"},"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","homepage":"https://github.com/platformatic/mcp#readme","keywords":["fastify","mcp","model","context","protocol"],"repository":{"url":"git+https://github.com/platformatic/mcp.git","type":"git"},"description":"Scalable Fastify adapter for the Model Context Protocol (MCP)","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"}],"readme":"# Fastify MCP Server\n\nA Fastify plugin that implements the Model Context Protocol (MCP) server using JSON-RPC 2.0. This plugin enables Fastify applications to expose tools, resources, and prompts following the MCP 2025-06-18 specification with full elicitation support.\n\n## Installation\n\n```bash\nnpm install @platformatic/mcp\n```\n\n### TypeBox Support (Optional)\n\nFor type-safe schema validation, install TypeBox:\n\n```bash\nnpm install @sinclair/typebox\n```\n\n## Features\n\n- **Complete MCP 2025-11-25 Support**: Implements the current Model Context Protocol revision, negotiating down to `2025-06-18`, `2025-03-26` and `2024-11-05` for older clients\n- **Tasks (experimental)**: Task-augmented tool calls with polling, deferred result retrieval and cancellation\n- **Elicitation Support**: Server-to-client information requests in both form and URL mode, with schema validation\n- **Icons**: Optional icon metadata on tools, resources, resource templates and prompts\n- **TypeBox Validation**: Type-safe schema validation with automatic TypeScript inference\n- **Security Enhancements**: Input sanitization, rate limiting, and security assessment\n- **Multiple Transport Support**: HTTP/SSE and stdio transports for flexible communication\n- **SSE Streaming**: Server-Sent Events for real-time communication\n- **Horizontal Scaling**: Redis-backed session management and message broadcasting\n- **Session Persistence**: Message history and reconnection support with Last-Event-ID\n- **Memory & Redis Backends**: Seamless switching between local and distributed storage\n- **Production Ready**: Comprehensive test coverage, security features, and authentication support\n\n## Quick Start\n\n```typescript\nimport Fastify from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n// Or use named import:\n// import { mcpPlugin } from '@platformatic/mcp'\n\nconst app = Fastify({ logger: true })\n\n// Register the MCP plugin\nawait app.register(mcpPlugin, {\n  serverInfo: {\n    name: 'my-mcp-server',\n    version: '1.0.0'\n  },\n  capabilities: {\n    tools: { listChanged: true },\n    resources: { subscribe: true },\n    prompts: {}\n  },\n  instructions: 'This server provides custom tools and resources'\n})\n\n// Add tools, resources, and prompts with handlers\napp.mcpAddTool({\n  name: 'calculator',\n  description: 'Performs basic arithmetic operations',\n  inputSchema: {\n    type: 'object',\n    properties: {\n      operation: { type: 'string', enum: ['add', 'subtract', 'multiply', 'divide'] },\n      a: { type: 'number' },\n      b: { type: 'number' }\n    },\n    required: ['operation', 'a', 'b']\n  }\n}, async (params) => {\n  const { operation, a, b } = params\n  let result\n  switch (operation) {\n    case 'add': result = a + b; break\n    case 'subtract': result = a - b; break\n    case 'multiply': result = a * b; break\n    case 'divide': result = a / b; break\n    default: throw new Error('Invalid operation')\n  }\n  return {\n    content: [{ type: 'text', text: `Result: ${result}` }]\n  }\n})\n\napp.mcpAddResource({\n  uri: 'file://config.json',\n  name: 'Application Config',\n  description: 'Server configuration file',\n  mimeType: 'application/json'\n}, async (uri, context) => {\n  // Read and return the configuration file\n  const config = { setting1: 'value1', setting2: 'value2' }\n  return {\n    contents: [{\n      uri,\n      text: JSON.stringify(config, null, 2),\n      mimeType: 'application/json'\n    }]\n  }\n})\n\napp.mcpAddPrompt({\n  name: 'code-review',\n  description: 'Generates code review comments',\n  arguments: [{\n    name: 'language',\n    description: 'Programming language',\n    required: true\n  }]\n}, async (name, args, context) => {\n  const language = args?.language || 'javascript'\n  return {\n    messages: [{\n      role: 'user',\n      content: {\n        type: 'text',\n        text: `Please review this ${language} code for best practices, potential bugs, and improvements.`\n      }\n    }]\n  }\n})\n\nawait app.listen({ port: 3000 })\n```\n\n## In-process MCP Client\n\nThe plugin decorates the Fastify instance with `mcpClient()`, a client that talks to the\nserver through `app.inject()` — no port binding, so it's equally useful for tests or for\ndriving the server from other in-process code:\n\n```typescript\nimport { test } from 'node:test'\nimport assert from 'node:assert/strict'\nimport Fastify from 'fastify'\nimport type { FastifyInstance } from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n\ntest('calls an MCP tool', async (t) => {\n  const app: FastifyInstance = Fastify()\n  t.after(() => app.close())\n\n  await app.register(mcpPlugin, { enableSSE: true })\n\n  app.mcpAddTool({\n    name: 'echo',\n    description: 'Echo a message',\n    inputSchema: {\n      type: 'object',\n      properties: {\n        message: { type: 'string' }\n      },\n      required: ['message'],\n      additionalProperties: false\n    }\n  }, async ({ message }) => {\n    return {\n      content: [{ type: 'text', text: message }]\n    }\n  })\n\n  await app.ready()\n\n  const client = app.mcpClient()\n\n  await client.initialize()\n\n  const response = await client.callTool('echo', {\n    message: 'hello'\n  })\n\n  assert.equal(response.statusCode, 200)\n  assert.ok('result' in response.body)\n  assert.deepEqual(response.body.result, {\n    content: [{ type: 'text', text: 'hello' }]\n  })\n})\n```\n\nThe client:\n\n- Uses `app.inject()` only (no port binding).\n- Manages sequential JSON-RPC request IDs per client instance.\n- `initialize()` performs the complete MCP lifecycle handshake (`initialize` plus `notifications/initialized`).\n- `initialize()` rejects when `notifications/initialized` is not accepted with an empty `202` or `204` response.\n- Commits `mcp-session-id` and negotiated protocol version only after the full initialization handshake succeeds.\n- Forwards headers passed to `initialize()` to both lifecycle requests, except MCP-managed `mcp-session-id` and `mcp-protocol-version` on `notifications/initialized`.\n- Captures committed `mcp-session-id` from successful initialization and sends it automatically on later requests.\n- Lets you pass custom headers (including authorization) globally or per request.\n\nResponses are a discriminated union — narrow with `'result' in response.body` or\n`'error' in response.body`. Malformed or non-JSON-RPC responses throw a coded Fastify error\ninstead of returning.\n\nNote: no OAuth/JWT credentials are generated for you.\n\n## Protocol Version Negotiation\n\nThe server answers `initialize` with the client's requested revision when it is one it\nsupports, and otherwise offers the newest one it has:\n\n```typescript\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from '@platformatic/mcp'\n\nLATEST_PROTOCOL_VERSION      // '2025-11-25'\nSUPPORTED_PROTOCOL_VERSIONS  // ['2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05']\n```\n\nOn the HTTP transport, requests after `initialize` must carry the agreed revision in the\n`MCP-Protocol-Version` header. An unsupported value is answered with `400`; an absent header\nis treated as `2025-03-26`, which predates the header.\n\n**Responses are shaped to the revision the client negotiated.** A client on an older revision\nnever sees a field or method that revision does not define:\n\n| | `< 2025-11-25` | `2025-11-25` |\n|---|---|---|\n| `tasks` capability | not advertised | advertised |\n| `tasks/*` methods | `-32601` method not found | available |\n| `task` field on `tools/call` | ignored | honoured |\n| `icons` on listings | stripped | present |\n| `execution.taskSupport` | stripped | present |\n| `$schema` on tool schemas | omitted | JSON Schema 2020-12 |\n| URL mode elicitation | refused | available |\n\nThe negotiated revision is stored on the session and is **authoritative**:\na request whose `MCP-Protocol-Version` header contradicts what the session agreed is rejected\nwith `400`, so a client cannot opt into newer behaviour after negotiating an older revision.\nWhen the header is omitted the session's revision is used in preference to the `2025-03-26`\ndefault. `initialize` is exempt, so a client may re-negotiate on an existing session.\n\n> A request sent without an `Mcp-Session-Id` has no session to remember the negotiation, so it\n> is judged solely by its header. A client that omits it falls back to `2025-03-26` and will not\n> see `2025-11-25` features. Compliant clients always send the header.\n\n## Sessions\n\nSession management is part of the Streamable HTTP transport and works whether or not SSE is\nenabled:\n\n- `initialize` on `POST /mcp` creates a session and returns its id in the `Mcp-Session-Id`\n  response header. Clients send it back on later requests.\n- `DELETE /mcp` with the `Mcp-Session-Id` header terminates the session (`204`), answering\n  `404` for an unknown session and `400` when the header is missing.\n- `enableSSE` only controls `GET /mcp`. Without it, `GET /mcp` answers `405` with\n  `Allow: POST, DELETE`.\n\nWith SSE disabled, requests that do not carry an `Mcp-Session-Id` keep working statelessly and\nno session is created for them. With SSE enabled, every request without a known session id gets\na new session, as before.\n\nSessions expire after `sessionTtlMs` milliseconds without activity (default `3600000`, one\nhour), so clients that disconnect without sending `DELETE` do not leave sessions behind. Every\nrequest carrying the session id, every message stored for it, and every heartbeat on an open SSE\nstream counts as activity, so a session in use is never expired from under a live connection.\nThe Redis store applies this as the key TTL; the memory store checks it on every read and sweeps\nexpired sessions on an unref'd timer that is cleared when the server closes.\n\n```typescript\nawait app.register(mcpPlugin, {\n  sessionTtlMs: 15 * 60_000 // expire sessions after 15 idle minutes\n})\n```\n\n## Request Body Errors\n\nBody-parser failures and bodies that are not JSON-RPC messages on `POST /mcp` are answered\nwith a JSON-RPC error (with `id: null`) while keeping the HTTP status code:\n\n| Cause | Status | JSON-RPC error |\n| --- | --- | --- |\n| Malformed or empty JSON | `400` | `-32700` Parse error |\n| Body larger than Fastify's `bodyLimit` | `413` | `-32600` Invalid Request |\n| Unsupported `Content-Type` | `415` | `-32600` Invalid Request |\n| Not a JSON-RPC request, notification or response (`null`, a number, a string, a `text/plain` body, or an object with none of `method`, `result` or `error`) | `400` | `-32600` Invalid Request |\n\nA body that is not a JSON-RPC message is rejected before any SSE session is created for it.\nA JSON-RPC response posted by the client is answered with `202` and handed to\n`onClientResponse`.\n\nThe body-parser error handler is scoped to the `POST /mcp` route. Any other error on that route, and every\nerror on your other routes, still goes to your own (or Fastify's default) error handler.\n\n## Origin Validation\n\nBrowser clients can be protected against DNS rebinding by allow-listing origins. A rejected\norigin is answered with `403`:\n\n```typescript\nawait app.register(mcpPlugin, {\n  allowedOrigins: ['https://app.example.com'] // omit to disable, '*' or true to allow any\n})\n```\n\nRequests without an `Origin` header are always accepted — the header is set by browsers, so\nits absence means the request did not come from one.\n\n`allowedOrigins` must be `true`, `'*'` or an array of strings; any other value (including a\nsingle origin string) makes registration throw. Wrap a single origin in an array.\n\n## CORS\n\nThe plugin does not set CORS headers on `/mcp`; browser clients need the application to\nconfigure CORS for it, for example with [`@fastify/cors`](https://github.com/fastify/fastify-cors):\n\n```typescript\nimport cors from '@fastify/cors'\n\nawait app.register(cors, {\n  origin: ['https://app.example.com'],\n  methods: ['GET', 'POST', 'DELETE'],\n  allowedHeaders: [\n    'Content-Type',\n    'Authorization',\n    'Last-Event-ID',\n    'Mcp-Session-Id',\n    'Mcp-Protocol-Version',\n    'Mcp-Method',\n    'Mcp-Name'\n  ],\n  exposedHeaders: ['Mcp-Session-Id']\n})\nawait app.register(mcpPlugin, { /* ... */ })\n```\n\n`allowedHeaders` does not accept wildcards, so list any `Mcp-Param-*` headers your clients send.\nWhen authorization is enabled, the `/.well-known/*` discovery endpoints answer cross-origin\nrequests from any origin on their own; that CORS handling is scoped to those routes and does not\naffect the rest of the application.\n\n## Tasks (MCP 2025-11-25, experimental)\n\nTasks let a tool call return immediately with a task handle while the work continues in the\nbackground. The client then polls `tasks/get` and collects the result with `tasks/result`.\n\nEnable the feature on the plugin, then opt individual tools in with `execution.taskSupport`:\n\n```typescript\nawait app.register(mcpPlugin, { enableTasks: true })\n\napp.mcpAddTool({\n  name: 'generate-report',\n  description: 'Builds a large report',\n  inputSchema: Type.Object({ month: Type.String() }),\n  execution: { taskSupport: 'optional' } // 'forbidden' (default) | 'optional' | 'required'\n}, async (params) => {\n  return { content: [{ type: 'text', text: await buildReport(params.month) }] }\n})\n```\n\nA client opts in per call by adding a `task` field:\n\n```jsonc\n// -> tools/call\n{ \"name\": \"generate-report\", \"arguments\": { \"month\": \"July\" }, \"task\": { \"ttl\": 60000 } }\n// <- CreateTaskResult\n{ \"task\": { \"taskId\": \"…\", \"status\": \"working\", \"ttl\": 60000, \"pollInterval\": 1000 } }\n```\n\nSupported operations: `tasks/get`, `tasks/result` (blocks until the task is terminal),\n`tasks/list` and `tasks/cancel`, plus optional `notifications/tasks/status` pushes over SSE.\nA blocked `tasks/result` stops waiting when its client disconnects, and when the server\ncloses it answers with a JSON-RPC `-32603` \"Server shutting down\" error, so `app.close()`\nnever waits on a task running on another instance.\n\nRetention defaults to 60 seconds when the client does not request a `ttl`. Tasks are meant\nfor long-running work, so if your tools can outlive that, raise the bounds — otherwise a task\ncan expire before it finishes and its `tasks/result` returns \"not found\":\n\n```typescript\nawait app.register(mcpPlugin, {\n  enableTasks: true,\n  taskDefaultTtlMs: 15 * 60_000, // retention when the client requests no ttl\n  taskMaxTtlMs: 60 * 60_000      // ceiling on a client-requested ttl\n})\n```\n\nA client-requested `ttl` must be a non-negative integer number of milliseconds; anything else\n(a string, a negative or fractional number) is rejected with `-32602` Invalid params. A valid\n`ttl` is clamped between 1 second and `taskMaxTtlMs`, and the effective value is returned in the\n`CreateTaskResult`.\n\nTasks are stored in memory by default and in Redis when a `redis` option is given, so any\ninstance can serve a poll for a task created on another.\n\n`tasks/list` returns the caller's tasks newest first, 50 per page. When more remain, the\nresult carries an opaque `nextCursor`; pass it back as `params.cursor` to fetch the next\npage. An unrecognised cursor is rejected with `-32602` (Invalid params). The Redis store keeps\na per-owner index, so a listing only reads the caller's own tasks.\n\nCustom `TaskStore` implementations must implement `list(owner, { cursor, limit })`, where\n`owner` is `{ subject, issuer }` and must match a record's `authSubject` and `authIssuer`\nexactly, and return `{ tasks, nextCursor? }`, throwing `InvalidTaskCursorError` for a cursor\nthey did not issue.\n\nEach caller (token issuer and subject) may keep at most `taskMaxRetainedPerPrincipal` tasks stored\n(default 100). Finished tasks count until their `ttl` expires, so one caller cannot fill the\nstore with results it never collects. When a caller is at its quota, its oldest finished task\nis evicted to make room; if all of them are still running, the `tools/call` fails with\n`-32029` \"Task limit reached\". Without authorization every caller shares a single quota, so\nraise it if you expect many concurrent anonymous tasks. The in-memory store also holds at most\n1000 tasks in total; when full, it evicts the caller's oldest finished task, then the oldest\nfinished task of any caller, before refusing. The Redis store enforces the quota atomically\nwith a per-caller sorted set (`mcp:tasks:owner:<issuer>:<subject>`, each part percent-encoded,\nor `mcp:tasks:anonymous`), so it holds across instances. Custom `TaskStore` implementations\nshould throw `TaskLimitError` from `create()` to produce the same response.\n\nThe instance running a task holds a lease on it and renews it every third of `taskLeaseMs`\n(default 30 seconds). If that instance dies or shuts down before the task finishes, renewals\nstop. Once the lease lapses, the next `tasks/get` or `tasks/result` (including one already\nwaiting) marks the task `failed` with the status message \"Task owner lost\", so the task no\nlonger reads `working` until its ttl runs out. A caller at `taskMaxRetainedPerPrincipal` gets\nthe same treatment for their orphaned tasks when they create a new one, so tasks nobody polls\ncannot keep them locked out of their quota. The check is a compare-and-set, so a renewal or\ncompletion that lands first always wins. With Redis, the lease is set and checked against the\nRedis server clock, so clock skew between instances can never fail a task that is still running,\nnor delay failing one whose owner is gone:\n\n```typescript\nawait app.register(mcpPlugin, {\n  enableTasks: true,\n  taskMaxRetainedPerPrincipal: 20, // tasks each caller may keep, finished ones included\n  taskLeaseMs: 30_000 // a dead instance's tasks are failed this long after its last renewal\n})\n```\n\n**Security**: when authorization is enabled, tasks are bound to the issuer and `sub` of the\ntoken that created them (a subject is only unique per issuer, so both must match, as for\nsessions) and a requestor can only reach its own. Another user's task answers\n`tasks/get`, `tasks/result` and `tasks/cancel` exactly like an unknown task id, and never\nappears in `tasks/list`. Tasks created by a version that only recorded the subject have no\nissuer; with authorization enabled they are unreachable and simply expire with their ttl.\nWithout authorization no requestor can be identified, so\ntasks are reachable by anyone holding the (random UUID) task id, and `tasks/list` is both\nunadvertised **and refused** — otherwise it would hand every anonymous task's id to any\ncaller and defeat that model.\n\n## Elicitation Support (MCP 2025-11-25)\n\nThe plugin supports the elicitation capability, allowing servers to request structured information from clients. This enables dynamic data collection with schema validation.\n\nTwo modes are available. **Form mode** collects structured data through the client, and\n**URL mode** sends the user out of band for anything sensitive — credentials, payments,\nthird-party OAuth — so it never passes through the MCP client:\n\n```typescript\n// URL mode: returns the elicitation id, or null if the request could not be sent\nconst elicitationId = await app.mcpElicitUrl(\n  sessionId,\n  'Authorize access to your Example Co files',\n  'https://mcp.example.com/connect'\n)\n\n// Later, once the out-of-band interaction finishes\nif (elicitationId) {\n  await app.mcpNotifyElicitationComplete(sessionId, elicitationId)\n}\n```\n\nServers **MUST NOT** request passwords, API keys or payment details through form mode — use\nURL mode for those. URLs are rejected if they are malformed, use a scheme other than\nhttp(s), or embed credentials.\n\n### Basic Elicitation\n\nElicitation is a **client** capability: there is nothing to declare in the server's\n`capabilities`. The client opts in by sending `capabilities: { elicitation: {} }` (form\nmode only), or `{ elicitation: { form: {}, url: {} } }`, in its `initialize` request.\n`mcpElicit()` returns `false`, and `mcpElicitUrl()` `null`, when the client did not declare\nthe mode being requested, as the spec forbids sending it.\n\nThe request is delivered over the session's SSE stream, so the client must have it open\n(`GET /mcp`). The client answers by POSTing a JSON-RPC response with the same `id`; the\nplugin acknowledges it with `202` and passes it to `onClientResponse`.\n\n```typescript\nimport { Type } from '@sinclair/typebox'\n\n// Register plugin with elicitation support\nawait app.register(mcpPlugin, {\n  enableSSE: true, // Required for elicitation\n  // Receives the client's answer (an ElicitResult) to each elicitation request\n  onClientResponse: async (response, { sessionId }) => {\n    if ('result' in response) {\n      app.log.info({ sessionId, id: response.id, result: response.result }, 'Elicitation answered')\n    }\n  }\n})\n\n// In your tool handler, request information from the client\napp.mcpAddTool({\n  name: 'collect-user-info',\n  description: 'Collect user information',\n  inputSchema: Type.Object({})\n}, async (_params, { sessionId }) => {\n  if (!sessionId) {\n    return { \n      content: [{ type: 'text', text: 'No session available' }],\n      isError: true \n    }\n  }\n\n  // Request user details with schema validation\n  const success = await app.mcpElicit(sessionId, 'Please enter your details', {\n    type: 'object',\n    properties: {\n      name: { \n        type: 'string', \n        description: 'Your full name',\n        minLength: 1,\n        maxLength: 100\n      },\n      email: {\n        type: 'string',\n        description: 'Your email address',\n        format: 'email'\n      },\n      age: {\n        type: 'integer',\n        description: 'Your age',\n        minimum: 0,\n        maximum: 150\n      },\n      preferences: {\n        type: 'array',\n        items: {\n          type: 'string',\n          enum: ['newsletter', 'updates', 'marketing']\n        },\n        description: 'Communication preferences'\n      }\n    },\n    required: ['name', 'email']\n  })\n\n  if (success) {\n    return { \n      content: [{ type: 'text', text: 'Information request sent to client' }] \n    }\n  } else {\n    return { \n      content: [{ type: 'text', text: 'Failed to send elicitation request' }],\n      isError: true \n    }\n  }\n})\n```\n\n### Advanced Elicitation with Custom Request IDs\n\n```typescript\n// Request with custom ID for tracking\nconst requestId = 'user-profile-123'\nconst success = await app.mcpElicit(\n  sessionId, \n  'Complete your profile setup',\n  {\n    type: 'object',\n    properties: {\n      avatar: { type: 'string', description: 'Avatar URL' },\n      bio: { type: 'string', maxLength: 500, description: 'Short bio' },\n      skills: {\n        type: 'array',\n        items: {\n          type: 'string',\n          enum: ['typescript', 'rust', 'go', 'python']\n        },\n        maxItems: 4,\n        description: 'Your skills'\n      }\n    }\n  },\n  requestId\n)\n```\n\n### Security Considerations for Elicitation\n\n⚠️ **Important Security Notes:**\n\n- Elicitation requests are automatically validated for length and complexity\n- Schema depth and size are limited to prevent DoS attacks\n- Rate limiting should be implemented for production use\n- Always validate client responses before using the data\n- Never request sensitive information without explicit user consent\n\n## TypeBox Schema Validation\n\nThe plugin supports TypeBox schemas for type-safe validation with automatic TypeScript inference. This eliminates the need for manual type definitions and provides compile-time type checking.\n\n### Benefits\n\n- **Type Safety**: Automatic TypeScript type inference from schemas\n- **Runtime Validation**: Input validation with structured error messages\n- **Zero Duplication**: Single source of truth for both types and validation\n- **IDE Support**: Full autocomplete and IntelliSense for validated parameters\n- **Performance**: Compiled validators with caching for optimal performance\n\n### Basic Usage\n\n```typescript\nimport { Type } from '@sinclair/typebox'\nimport Fastify from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n\nconst app = Fastify({ logger: true })\n\nawait app.register(mcpPlugin, {\n  serverInfo: { name: 'my-server', version: '1.0.0' },\n  capabilities: { tools: {} }\n})\n\n// Define TypeBox schema\nconst SearchToolSchema = Type.Object({\n  query: Type.String({ minLength: 1, description: 'Search query' }),\n  limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, description: 'Maximum results' })),\n  filters: Type.Optional(Type.Array(Type.String(), { description: 'Filter criteria' }))\n})\n\n// Register tool with TypeBox schema\napp.mcpAddTool({\n  name: 'search',\n  description: 'Search for files',\n  inputSchema: SearchToolSchema\n}, async (params) => {\n  // params is automatically typed as:\n  // {\n  //   query: string;\n  //   limit?: number;\n  //   filters?: string[];\n  // }\n  const { query, limit = 10, filters = [] } = params\n  \n  return {\n    content: [{\n      type: 'text',\n      text: `Searching for \"${query}\" with limit ${limit} and filters: ${filters.join(', ')}`\n    }]\n  }\n})\n```\n\n### Schema Types\n\n#### Tool Input Schemas\n\n```typescript\n// Complex nested schema\nconst ComplexToolSchema = Type.Object({\n  user: Type.Object({\n    name: Type.String(),\n    age: Type.Number({ minimum: 0 })\n  }),\n  preferences: Type.Object({\n    theme: Type.Union([\n      Type.Literal('light'),\n      Type.Literal('dark'),\n      Type.Literal('auto')\n    ]),\n    notifications: Type.Boolean()\n  }),\n  tags: Type.Array(Type.String())\n})\n\napp.mcpAddTool({\n  name: 'update-profile',\n  description: 'Update user profile',\n  inputSchema: ComplexToolSchema\n}, async (params) => {\n  // Fully typed nested object\n  const { user, preferences, tags } = params\n  return { content: [{ type: 'text', text: `Updated profile for ${user.name}` }] }\n})\n```\n\n#### Resource URI Schemas\n\n```typescript\n// URI validation schema\nconst FileUriSchema = Type.String({\n  pattern: '^file://.+',\n  description: 'File URI pattern'\n})\n\napp.mcpAddResource({\n  uriPattern: 'file://documents/*',\n  name: 'Document Files',\n  description: 'Access document files',\n  uriSchema: FileUriSchema\n}, async (uri, context) => {\n  // uri is validated against the schema\n  const content = await readFile(uri)\n  return {\n    contents: [{ uri, text: content, mimeType: 'text/plain' }]\n  }\n})\n```\n\n#### Prompt Argument Schemas\n\n```typescript\n// Prompt with automatic argument generation\nconst CodeReviewSchema = Type.Object({\n  language: Type.Union([\n    Type.Literal('javascript'),\n    Type.Literal('typescript'),\n    Type.Literal('python')\n  ], { description: 'Programming language' }),\n  complexity: Type.Optional(Type.Union([\n    Type.Literal('low'),\n    Type.Literal('medium'),\n    Type.Literal('high')\n  ], { description: 'Code complexity level' }))\n})\n\napp.mcpAddPrompt({\n  name: 'code-review',\n  description: 'Generate code review',\n  argumentSchema: CodeReviewSchema\n  // arguments array is automatically generated from schema\n}, async (name, args, context) => {\n  // args is typed as: { language: 'javascript' | 'typescript' | 'python', complexity?: 'low' | 'medium' | 'high' }\n  return {\n    messages: [{\n      role: 'user',\n      content: {\n        type: 'text',\n        text: `Review this ${args.language} code with ${args.complexity || 'medium'} complexity`\n      }\n    }]\n  }\n})\n```\n\n### Error Handling\n\nTypeBox validation provides structured error messages:\n\n```typescript\n// When validation fails, structured errors are returned:\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": {\n    \"isError\": true,\n    \"content\": [{\n      \"type\": \"text\",\n      \"text\": \"Invalid tool arguments: Validation failed with 2 errors:\\n/query: Expected string, received number\\n/limit: Expected number <= 100, received 150\"\n    }]\n  }\n}\n```\n\nPrompts and resources report failures as JSON-RPC errors instead of successful results:\n\n- `prompts/get` returns `-32602` (Invalid params) when the arguments fail the TypeBox `argumentSchema`, or, for prompts without a TypeBox schema, when an argument declared with `required: true` in `arguments` is missing. The handler is not invoked.\n- `prompts/get` returns `-32603` (Internal error) with the message `Prompt execution failed: <error.message>` when the prompt handler throws.\n- `resources/read` returns `-32602` when the URI fails the resource's `uriSchema`, and `-32603` with `Resource read failed: <error.message>` when the resource handler throws.\n\nHandler errors are logged on the request logger; only the error message is sent to the client, never the stack trace.\n\n### Backward Compatibility\n\nThe plugin maintains backward compatibility with JSON Schema and unvalidated tools:\n\n```typescript\n// JSON Schema (accepted, but NOT validated at runtime by default — see below)\napp.mcpAddTool({\n  name: 'legacy-tool',\n  description: 'Uses JSON Schema',\n  inputSchema: {\n    type: 'object',\n    properties: {\n      param: { type: 'string' }\n    }\n  }\n}, async (params) => {\n  // params is typed as 'any'\n  return { content: [{ type: 'text', text: 'OK' }] }\n})\n\n// Unvalidated tool (unsafe)\napp.mcpAddTool({\n  name: 'unsafe-tool',\n  description: 'No validation'\n}, async (params) => {\n  // params is typed as 'any' - no validation performed\n  return { content: [{ type: 'text', text: 'OK' }] }\n})\n```\n\n**Important:** only TypeBox schemas are validated at runtime by default. A tool registered with a plain JSON Schema `inputSchema` receives its arguments **unvalidated** unless you opt in to AJV validation.\n\n### Validating plain JSON Schema inputs with AJV\n\nSet `validateJsonSchemaInputs: {}` to validate plain-JSON-Schema tool inputs with [AJV](https://ajv.js.org/) (draft 2020-12, matching the plugin's published schema dialect) before the handler runs. TypeBox tools are unaffected — they keep their existing TypeBox validation.\n\n```typescript\nawait app.register(mcpPlugin, {\n  validateJsonSchemaInputs: {\n    allErrors: true,\n    useDefaults: false\n  }\n})\n```\n\nBehavior when enabled:\n\n- Invalid arguments return a tool execution error (`isError: true` with an `Invalid tool arguments: ...` message, capped at 5 reported errors), not a protocol error — the same SEP-1303 semantics as TypeBox validation.\n- Validation is non-mutating: no type coercion, no defaults injection, no property removal. Handlers receive the arguments exactly as the client sent them. `format` keywords are annotation-only (JSON Schema 2020-12's own default).\n- A plain JSON Schema that AJV cannot compile makes `mcpAddTool` throw, so a misconfigured tool fails at startup instead of running unvalidated.\n- Compiled validators are cached per schema, and the option also applies to stdio transports and task-augmented (`task: {}`) calls.\n\n### Performance\n\nTypeBox validation is highly optimized:\n\n- **Compiled Validators**: Schemas are compiled to optimized validation functions\n- **Caching**: Compiled validators are cached for reuse\n- **Minimal Overhead**: Less than 1ms validation overhead for typical schemas\n- **Memory Efficient**: Shared validator instances across requests\n\n## Server-Sent Events (SSE) Support\n\nThis plugin supports the MCP Streamable HTTP transport specification, enabling both regular JSON responses and Server-Sent Events for streaming communication.\n\n### SSE Configuration\n\n```typescript\nawait app.register(mcpPlugin, {\n  enableSSE: true, // Enable SSE support (default: false)\n  // Optionally close streams after a while so clients fall back to polling and\n  // resume with Last-Event-ID (SEP-1699). Omit to keep streams open indefinitely.\n  sseMaxConnectionMs: 300_000,\n  // ... other options\n})\n```\n\nWhen authorization is enabled, the access token is only checked when a `GET /mcp` stream opens. So that a stream never outlives that token, the server closes it at the token's `exp` (or after `sseMaxConnectionMs`, whichever comes first). The client then reconnects with a fresh token and resumes with `Last-Event-ID`. Tokens without an `exp` claim are bounded only by `sseMaxConnectionMs`, so set it if your authorization server issues them or if you want revocation and scope changes to take effect within a bounded time.\n\n## Redis Configuration for Horizontal Scaling\n\nThe plugin supports Redis-backed session management and message broadcasting for horizontal scaling across multiple server instances.\n\n### Why Redis is Critical for Scalability\n\n**Without Redis (Memory-only):**\n- Each server instance maintains isolated session stores\n- SSE connections are tied to specific server instances  \n- No cross-instance message broadcasting\n- Session data is lost when servers restart\n- Load balancers can't route clients to different instances\n\n**With Redis (Distributed):**\n- **Shared Session State**: All instances access the same session data from Redis\n- **Cross-Instance Broadcasting**: Messages sent from any instance reach all connected clients\n- **Session Persistence**: Sessions survive server restarts, expiring after one idle hour by default (`sessionTtlMs`)\n- **High Availability**: Clients can reconnect to any instance and resume from last event\n- **True Horizontal Scaling**: Add more instances without architectural changes\n\nThis transforms the plugin from a single-instance application into a distributed system capable of serving thousands of concurrent SSE connections with real-time global synchronization.\n\n### Redis Setup\n\n```typescript\nimport Fastify from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n\nconst app = Fastify({ logger: true })\n\nawait app.register(mcpPlugin, {\n  enableSSE: true,\n  redis: {\n    host: process.env.REDIS_HOST || 'localhost',\n    port: parseInt(process.env.REDIS_PORT || '6379'),\n    db: parseInt(process.env.REDIS_DB || '0'),\n    password: process.env.REDIS_PASSWORD,\n    // Any other ioredis RedisOptions are accepted and passed through\n    maxRetriesPerRequest: 3,\n    retryStrategy: (times) => Math.min(times * 100, 2000)\n  },\n  serverInfo: {\n    name: 'scalable-mcp-server',\n    version: '1.0.0'\n  }\n})\n```\n\n### Message Broker Exports\n\n`MemoryMessageBroker`, `RedisMessageBroker`, and the `MessageBroker` interface are exported from the package's public entry point, so you can use them directly without deep-importing into `dist/brokers/...`:\n\n```typescript\nimport { MessageBroker, MemoryMessageBroker, RedisMessageBroker } from '@platformatic/mcp'\n\nconst broker: MessageBroker = new MemoryMessageBroker()\n// or: new RedisMessageBroker(redis)\n```\n\n- **`MessageBroker`**: the pub/sub interface the plugin uses internally to deliver messages to SSE clients — `publish(topic, message)`, `subscribe(topic, handler)`, `unsubscribe(topic)`, and `close()`.\n- **`MemoryMessageBroker`**: in-process implementation backed by [MQEmitter](https://github.com/mcollina/mqemitter). Used automatically when no `redis` option is passed; messages only reach clients connected to the same server instance.\n- **`RedisMessageBroker`**: distributed implementation backed by [mqemitter-redis](https://github.com/mcollina/mqemitter-redis). Used automatically when the `redis` option is passed; publishes go through Redis so messages reach clients connected to *any* server instance.\n\nThese are the same broker classes the plugin instantiates internally based on the `redis` option. Exporting them lets you construct one directly for custom pub/sub logic, tests, or scripts that need to publish/subscribe without spinning up the full plugin.\n\n### Session Store Exports\n\n`MemorySessionStore`, `RedisSessionStore`, and the `SessionStore`/`SessionMetadata` types are exported from the package's public entry point, matching the broker and task store exports:\n\n```typescript\nimport { SessionStore, MemorySessionStore, RedisSessionStore } from '@platformatic/mcp'\n\nconst store: SessionStore = new MemorySessionStore()\n// or: new RedisSessionStore({ redis })\n```\n\n- **`SessionStore`**: the interface the plugin uses to persist SSE session metadata, message history, token-to-session mappings, and per-session resource subscriptions (`addResourceSubscription` / `removeResourceSubscription`, exposed as `resourceSubscriptions` on the metadata). The optional `touch(sessionId)` records activity so the session is not expired while in use.\n- **`MemorySessionStore`**: in-process implementation used automatically when no `redis` option is passed. Takes an optional `maxMessages` history cap (default 100) and `{ sessionTtlMs }` idle expiry (default 1 hour); call `cleanup()` periodically to free expired sessions when using it outside the plugin.\n- **`RedisSessionStore`**: distributed implementation used automatically when the `redis` option is passed; sessions and message history live in Redis so any instance can serve any client.\n\nThese are the same store classes the plugin instantiates internally based on the `redis` option. Exporting them lets you construct or inspect one directly in tests and scripts.\n\n### Multi-Instance Deployment\n\nWith Redis configuration, you can run multiple instances of your MCP server:\n\n```bash\n# Instance 1\nPORT=3000 REDIS_HOST=redis.example.com node server.js\n\n# Instance 2  \nPORT=3001 REDIS_HOST=redis.example.com node server.js\n\n# Instance 3\nPORT=3002 REDIS_HOST=redis.example.com node server.js\n```\n\n### Graceful Shutdown\n\n`RedisMessageBroker.close()` is bounded: it waits up to `closeTimeoutMs` (default 2000ms) for\nmqemitter-redis to close its Redis connections gracefully, then falls back to forcibly\ndisconnecting them so Fastify's `onClose` hooks - and the process shutdown deadline enforced by\nmost orchestrators - are never blocked by a Redis outage or a stuck reconnect. Pass\n`closeTimeoutMs` and `onCloseTimeout` as a second argument to `RedisMessageBroker` to customize\nthis behavior, e.g. to log when a shutdown had to fall back to the forced path.\n\n### Connection Errors\n\nA dropped Redis connection (for example during a Redis restart or failover) does not crash the\nprocess: the plugin logs errors from its Redis client and from the message broker's pub/sub\nconnections through the Fastify logger, and ioredis keeps reconnecting. When constructing\n`RedisMessageBroker` yourself, pass an `onError` callback in its options to observe these errors.\n\n### Session Persistence Features\n\n**Automatic Session Management:**\n- Sessions persist across server restarts\n- Idle session TTL (`sessionTtlMs`, default 1 hour) with automatic cleanup\n- Message history stored in Redis Streams\n\n**Message Replay:**\n```javascript\n// Client reconnection with Last-Event-ID\nconst eventSource = new EventSource('/mcp', {\n  headers: { \n    'Accept': 'text/event-stream',\n    'Last-Event-ID': '1234' // Resume from this event\n  }\n})\n```\n\n**Cross-Instance Broadcasting:**\n```typescript\n// Any server instance can broadcast to all connected clients\napp.mcpBroadcastNotification({\n  jsonrpc: '2.0',\n  method: 'notifications/message',\n  params: { message: 'Global update from instance 2' }\n})\n\n// Send to specific session (works across instances)\napp.mcpSendToSession('session-xyz', {\n  jsonrpc: '2.0',\n  method: 'notifications/progress',\n  params: { progress: 75 }\n})\n```\n\n### Implementation Design Decisions\n\nThis plugin implements certain behaviors that differ from the strict MCP specification to provide enhanced scalability and resilience in production environments:\n\n#### Multiple Connections per Session\n\n**MCP Specification Intent**: The official MCP specification (line 144-145) states that servers \"**MUST** send each of its JSON-RPC messages on only one of the connected streams; that is, it **MUST NOT** broadcast the same message across multiple streams.\"\n\n**Our Implementation Choice**: We allow multiple SSE connections to share the same session ID and broadcast session-specific messages (`mcpSendToSession`) to all connections within that session.\n\n**Why We Made This Choice**:\n\n1. **Horizontal Scalability**: In distributed deployments with Redis, sessions can span multiple server instances. Broadcasting ensures messages reach all connections regardless of which instance they're connected to.\n\n2. **Connection Resilience**: If a client has multiple connections (e.g., main application + background tools), both can receive notifications even if one connection fails.\n\n3. **Tool Integration**: External tools can join existing sessions by providing the same `mcp-session-id` header, enabling seamless integration with ongoing workflows.\n\n4. **Message Persistence**: All connections in a session benefit from the same message history and can replay missed events using `Last-Event-ID`.\n\n**Trade-off**: This approach may deliver duplicate messages to multiple connections within the same session, but the benefits in production reliability and scalability outweigh this consideration for most use cases.\n\n### Dual-Endpoint Architecture\n\nThe plugin uses a dual-endpoint architecture that separates regular MCP protocol communication from SSE streaming:\n\n```mermaid\nsequenceDiagram\n    participant C1 as Client 1\n    participant Tool as Tool/Client 2\n    participant Server as Fastify MCP Server\n    participant Redis as Redis (Optional)\n    participant Broker as Message Broker\n\n    Note over C1,Broker: Session Creation and Multiple Connection Support\n    \n    C1->>Server: POST /mcp (initialize)\n    Server->>C1: Response + Session ID: abc123\n    \n    C1->>Server: GET /mcp (SSE stream)\n    Note right of C1: Accept: text/event-stream<br/>mcp-session-id: abc123\n    Server-->>C1: SSE connection established\n    \n    Note over Tool,Broker: Tool Joins SAME Session (Implementation Choice)\n    \n    Tool->>Server: POST /mcp (any request)\n    Note right of Tool: mcp-session-id: abc123<br/>(reuses existing session)\n    Server->>Tool: Response (no new session created)\n    \n    Tool->>Server: GET /mcp (SSE stream)\n    Note right of Tool: Accept: text/event-stream<br/>mcp-session-id: abc123\n    Server-->>Tool: SSE connection established\n    \n    rect rgb(255, 240, 240)\n        Note over Server: Implementation Detail:<br/>localStreams.get('abc123') = Set{C1, Tool}<br/>Multiple connections share same session\n    end\n    \n    Note over C1,Broker: Session-Specific Broadcasting (Implementation Choice)\n    \n    rect rgb(240, 248, 255)\n        Note over C1,Tool: Our Implementation: mcpSendToSession('abc123')<br/>broadcasts to ALL connections in session abc123<br/>(Differs from MCP spec for scalability)\n        Server->>Broker: mcpSendToSession('abc123', message)\n        alt Redis Mode (Cross-Instance)\n            Broker->>Redis: Publish to mcp/session/abc123/message\n            Redis-->>Broker: Distribute to instance with session abc123\n        else Memory Mode\n            Broker->>Broker: Route to session abc123 locally\n        end\n        Broker-->>Server: Forward to ALL streams in session abc123\n        Server-->>C1: SSE notification (shared session)\n        Server-->>Tool: SSE notification (shared session)\n    end\n    \n    Note over C1,Broker: Reconnection Resilience\n    \n    Tool->>Server: Disconnect (network issue)\n    Note over Server: Session abc123 persists with C1 still connected\n    \n    Server->>Broker: mcpSendToSession('abc123', message)\n    Broker-->>Server: Deliver to remaining connections\n    Server-->>C1: SSE notification continues\n    \n    Tool->>Server: GET /mcp (reconnect)\n    Note right of Tool: mcp-session-id: abc123<br/>Last-Event-ID: 15\n    alt Redis Mode\n        Server->>Redis: Get message history for session abc123\n        Redis-->>Server: Return events 16-20\n    else Memory Mode\n        Server->>Server: Get local history for session abc123\n    end\n    Server-->>Tool: Replay missed events\n    Server-->>Tool: Resume receiving notifications\n    \n    Note over C1,Broker: Cross-Instance Scalability\n    \n    rect rgb(240, 255, 240)\n        Note over Server,Redis: With Redis: Session abc123 can be accessed<br/>from any server instance. Messages route<br/>to the instance hosting the SSE connections.\n        Server->>Broker: mcpSendToSession('abc123', message)<br/>(from any instance)\n        Broker->>Redis: Publish to mcp/session/abc123/message\n        Redis-->>Broker: Route to correct instance\n        Broker-->>Server: Deliver to session abc123 connections\n        Server-->>C1: SSE notification\n        Server-->>Tool: SSE notification\n    end\n```\n\n#### POST `/mcp` - MCP Protocol Communication\n\nHandles all standard MCP protocol requests and returns JSON responses:\n\n```javascript\n// Regular MCP request (always returns JSON)\nconst response = await fetch('/mcp', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'Accept': 'application/json'\n  },\n  body: JSON.stringify({ \n    jsonrpc: '2.0', \n    method: 'tools/call',\n    params: { name: 'my-tool', arguments: {} },\n    id: 1 \n  })\n})\nconst result = await response.json()\n```\n\n#### GET `/mcp` - SSE Stream Establishment\n\nEstablishes long-lived Server-Sent Events streams for real-time communication:\n\n```javascript\n// Establish SSE stream (separate from protocol requests)\nconst eventSource = new EventSource('/mcp', {\n  headers: { \n    'Accept': 'text/event-stream',\n    'mcp-session-id': sessionId  // Use session from POST request\n  }\n})\n\neventSource.onmessage = (event) => {\n  const message = JSON.parse(event.data)\n  console.log('Server notification:', message)\n}\n```\n\n#### Complete Workflow Example\n\n```javascript\n// 1. Create session and make MCP requests via POST\nconst initResponse = await fetch('/mcp', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'Accept': 'application/json'\n  },\n  body: JSON.stringify({\n    jsonrpc: '2.0',\n    method: 'initialize',\n    params: {\n      protocolVersion: '2025-06-18',\n      capabilities: {},\n      clientInfo: { name: 'my-client', version: '1.0.0' }\n    },\n    id: 1\n  })\n})\n\nconst sessionId = initResponse.headers.get('mcp-session-id')\n\n// 2. Establish SSE stream for notifications via GET\nconst eventSource = new EventSource('/mcp', {\n  headers: {\n    'Accept': 'text/event-stream',\n    'mcp-session-id': sessionId\n  }\n})\n\n// 3. Continue making MCP requests via POST\nconst toolResponse = await fetch('/mcp', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'Accept': 'application/json',\n    'mcp-session-id': sessionId\n  },\n  body: JSON.stringify({\n    jsonrpc: '2.0',\n    method: 'tools/call',\n    params: { name: 'my-tool', arguments: {} },\n    id: 2\n  })\n})\n\n// Server can send notifications via the SSE stream\n// while client makes requests via POST\n```\n\n### Server-Initiated Notifications\n\nThe plugin provides methods to send notifications and messages to connected SSE clients:\n\n```typescript\nimport Fastify from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n\nconst app = Fastify({ logger: true })\n\nawait app.register(mcpPlugin, {\n  enableSSE: true,\n  // ... other options\n})\n\n// Broadcast a notification to all connected SSE clients\napp.mcpBroadcastNotification({\n  jsonrpc: '2.0',\n  method: 'notifications/message',\n  params: {\n    level: 'info',\n    message: 'Server status update'\n  }\n})\n\n// Send a message to a specific session\nconst success = app.mcpSendToSession('session-id', {\n  jsonrpc: '2.0',\n  method: 'notifications/progress',\n  params: {\n    progressToken: 'task-123',\n    progress: 50,\n    total: 100\n  }\n})\n\n// Send a request to a specific session (expecting a response)\napp.mcpSendToSession('session-id', {\n  jsonrpc: '2.0',\n  id: 'req-456',\n  method: 'sampling/createMessage',\n  params: {\n    messages: [\n      {\n        role: 'user',\n        content: { type: 'text', text: 'Hello from server!' }\n      }\n    ]\n  }\n})\n\n// Example: Broadcast tool list changes\napp.mcpBroadcastNotification({\n  jsonrpc: '2.0',\n  method: 'notifications/tools/list_changed'\n})\n\n// Example: Send resource updates. `notifications/resources/updated` is only\n// delivered to sessions that called `resources/subscribe` for this URI (and\n// have not unsubscribed); every other broadcast reaches all sessions.\napp.mcpBroadcastNotification({\n  jsonrpc: '2.0',\n  method: 'notifications/resources/updated',\n  params: {\n    uri: 'file://config.json'\n  }\n})\n```\n\n### Real-time Updates Example\n\n```typescript\n// Set up a timer to send periodic updates\nsetInterval(() => {\n  app.mcpBroadcastNotification({\n    jsonrpc: '2.0',\n    method: 'notifications/message',\n    params: {\n      level: 'info',\n      message: `Server time: ${new Date().toISOString()}`\n    }\n  })\n}, 30000) // Every 30 seconds\n\n// Send updates when data changes\nfunction onDataChange(newData: any) {\n  app.mcpBroadcastNotification({\n    jsonrpc: '2.0',\n    method: 'notifications/resources/list_changed'\n  })\n}\n```\n\n## Stdio Transport\n\nThe plugin includes a built-in stdio transport utility for MCP communication over stdin/stdout, following the MCP stdio transport specification. This enables command-line tools and local applications to communicate with your Fastify MCP server.\n\n### Key Features\n\n- **Complete MCP stdio transport implementation** following the official specification\n- **Fastify integration** using the `.inject()` method for consistency with HTTP routes\n- **Comprehensive error handling** with proper JSON-RPC error responses\n- **Batch request support** for processing multiple messages at once\n- **Debug logging** to stderr without interfering with the stdio protocol\n\n### Quick Start\n\n```typescript\nimport fastify from 'fastify'\nimport mcpPlugin, { runStdioServer } from '@platformatic/mcp'\n\nconst app = fastify({\n  logger: false // Disable HTTP logging to avoid interference with stdio\n})\n\nawait app.register(mcpPlugin, {\n  serverInfo: {\n    name: 'my-mcp-server',\n    version: '1.0.0'\n  },\n  capabilities: {\n    tools: {},\n    resources: {},\n    prompts: {}\n  }\n})\n\n// Register your tools, resources, and prompts\napp.mcpAddTool({\n  name: 'echo',\n  description: 'Echo back the input text',\n  inputSchema: {\n    type: 'object',\n    properties: {\n      text: { type: 'string' }\n    },\n    required: ['text']\n  }\n}, async (args) => {\n  return {\n    content: [{\n      type: 'text',\n      text: `Echo: ${args.text}`\n    }]\n  }\n})\n\nawait app.ready()\n\n// Start the stdio transport\nawait runStdioServer(app, {\n  debug: process.env.DEBUG === 'true'\n})\n```\n\n### Usage Examples\n\n```bash\n# Initialize the server\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test-client\",\"version\":\"1.0.0\"}}}' | node server.js\n\n# Ping the server\necho '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"ping\"}' | node server.js\n\n# List available tools\necho '{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/list\"}' | node server.js\n\n# Call a tool\necho '{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"tools/call\",\"params\":{\"name\":\"echo\",\"arguments\":{\"text\":\"Hello, stdio!\"}}}' | node server.js\n```\n\n### API Reference\n\n#### `runStdioServer(app, options)`\n\nStarts a Fastify MCP server in stdio mode.\n\n**Parameters:**\n- `app` - Fastify instance with MCP plugin registered\n- `options` - Optional stdio transport options\n\n**Options:**\n- `debug` - Enable debug logging to stderr (default: false)\n- `input` - Custom input stream (default: process.stdin)\n- `output` - Custom output stream (default: process.stdout)\n- `error` - Custom error stream (default: process.stderr)\n\n#### `createStdioTransport(app, options)`\n\nCreates a stdio transport instance without starting it.\n\n**Parameters:**\n- `app` - Fastify instance with MCP plugin registered\n- `options` - Optional stdio transport options\n\n**Returns:** `StdioTransport` instance with `start()` and `stop()` methods\n\n### Transport Protocol\n\nThe stdio transport follows the MCP stdio transport specification:\n\n- Messages are exchanged over stdin/stdout\n- Each message is a single line of JSON\n- Messages are delimited by newlines\n- Messages must NOT contain embedded newlines\n- Server logs can be written to stderr\n- Supports both single messages and batch requests\n- The protocol version negotiated by `initialize` applies to every later message on the stream (a new `initialize` re-negotiates it)\n\n### Error Handling\n\nThe stdio transport provides comprehensive error handling:\n\n- JSON parsing errors return appropriate JSON-RPC error responses\n- Invalid method calls return \"Method not found\" errors\n- Tool execution errors are captured and returned in the response\n- Connection errors are logged to stderr\n\n### Use Cases\n\nThe stdio transport is particularly useful for:\n\n- **Command-line tools** that need to communicate with MCP servers\n- **Local development and testing** without HTTP overhead\n- **Integration with text editors and IDEs** that support stdio protocols\n- **Simple client-server communication** in controlled environments\n- **Batch processing** of MCP requests from scripts\n\n## OAuth 2.1 Authorization Integration\n\nThe plugin includes comprehensive OAuth 2.1 authorization support for secure MCP communication. This enables token-based authentication, session management, and secure multi-user environments.\n\n### Key Features\n\n- **Complete OAuth 2.1 Support**: Authorization Code Flow with PKCE\n- **Session-Based Authorization**: Token-to-session mapping with secure context\n- **Authorization-Aware SSE**: Authorization context bound to each SSE session\n- **JWT Token Validation**: Support for JWT tokens with JWKS endpoints\n- **Token Introspection**: RFC 7662 compliant token introspection\n- **Dynamic Client Registration**: RFC 7591 compliant client registration\n- **Horizontal Scaling**: Redis-backed authorization context persistence\n\n### Quick OAuth Setup\n\n```typescript\nimport Fastify from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n\nconst app = Fastify({ logger: true })\n\nawait app.register(mcpPlugin, {\n  serverInfo: {\n    name: 'secure-mcp-server',\n    version: '1.0.0'\n  },\n  capabilities: {\n    tools: {},\n    resources: {},\n    prompts: {}\n  },\n  enableSSE: true, // Required for session-based authorization\n  // Enable OAuth 2.1 authorization\n  authorization: {\n    enabled: true,\n    authorizationServers: ['https://auth.example.com'],\n    resourceUri: 'https://mcp.example.com',\n    // JWT Token Validation\n    tokenValidation: {\n      jwksUri: 'https://auth.example.com/.well-known/jwks.json',\n      validateAudience: true\n    }\n  },\n  // Redis for session persistence (recommended)\n  redis: {\n    host: process.env.REDIS_HOST || 'localhost',\n    port: parseInt(process.env.REDIS_PORT || '6379')\n  }\n})\n\nawait app.listen({ port: 3000 })\n```\n\n### Authorization Workflow\n\n#### 1. OAuth Authorization Flow\n\nThe MCP client discovers the authorization server through the MCP server's\nprotected-resource metadata. It then registers or identifies itself with that\nauthorization server and performs Authorization Code with PKCE directly:\n\n1. The MCP client sends its registered `redirect_uri` to the authorization server.\n2. The authorization server returns an authorization code to the client callback.\n3. The MCP client exchanges the code with its PKCE verifier.\n4. The MCP client sends the resulting bearer token to the MCP server.\n\nClient redirect URIs are not sent to the MCP server. The MCP server does not\nforward access tokens through browser redirects.\n\n#### 2. Authenticated MCP Requests\n\n```typescript\n// Use access token for MCP requests\nconst mcpResponse = await fetch('/mcp', {\n  method: 'POST',\n  headers: {\n    'Authorization': `Bearer ${access_token}`,\n    'Content-Type': 'application/json',\n    'Accept': 'application/json'\n  },\n  body: JSON.stringify({\n    jsonrpc: '2.0',\n    method: 'tools/list',\n    id: 1\n  })\n})\n```\n\n#### 3. Session-Based SSE Connections\n\n```typescript\n// SSE connection with authorization\nconst eventSource = new EventSource('/mcp', {\n  headers: {\n    'Authorization': `Bearer ${access_token}`,\n    'Accept': 'text/event-stream'\n  }\n})\n\neventSource.onmessage = (event) => {\n  const message = JSON.parse(event.data)\n  console.log('Received:', message)\n}\n```\n\n### Authorization Configuration\n\n#### Choosing Which Routes Require a Token\n\nBy default the plugin adds the bearer-token check as a `preHandler` hook on\nthe Fastify instance it is registered on. Because the plugin is not\nencapsulated, that check also covers every route you register yourself on\nthat instance. The well-known metadata endpoints and the OAuth flow endpoints\n(`/oauth/authorize`, `/oauth/callback`, `/oauth/register`) are always skipped.\n\nSet `protectedRoutes: 'mcp'` to protect only the MCP endpoints the plugin\nregisters (`POST`, `GET` and `DELETE` `/mcp`) and leave your own routes alone:\n\n```typescript\nawait app.register(mcpPlugin, {\n  authorization: {\n    enabled: true,\n    authorizationServers: ['https://auth.example.com'],\n    resourceUri: 'https://mcp.example.com',\n    tokenValidation: {\n      jwksUri: 'https://auth.example.com/.well-known/jwks.json'\n    },\n    protectedRoutes: 'mcp' // default: 'all'\n  }\n})\n\n// Reachable without a token\napp.get('/health', async () => ({ status: 'ok' }))\n```\n\n| Value | Behaviour |\n|-------|-----------|\n| `'all'` (default) | Every route of the app requires a token, except the well-known and OAuth endpoints. |\n| `'mcp'` | Only the `/mcp` routes require a token. |\n\nIf you keep the default and only need to open a few paths, list them in\n`excludedPaths` instead. Each entry is a URL prefix string or a `RegExp`:\n\n```typescript\nauthorization: {\n  enabled: true,\n  // ...\n  excludedPaths: ['/health', /^\\/public\\//]\n}\n```\n\n`excludedPaths` also applies with `protectedRoutes: 'mcp'`, but there it only\nmatters for the `/mcp` routes.\n\n#### JWT Token Validation\n\n```typescript\nauthorization: {\n  enabled: true,\n  tokenValidation: {\n    // JWKS endpoint for public key retrieval\n    jwksUri: 'https://auth.example.com/.well-known/jwks.json',\n    \n    // Validate token audience\n    validateAudience: true\n  }\n}\n```\n\n#### Token Introspection (RFC 7662)\n\n```typescript\nauthorization: {\n  enabled: true,\n  tokenValidation: {\n    // Use token introspection instead of JWT\n    introspectionEndpoint: 'https://auth.example.com/oauth/introspect',\n    clientId: process.env.OAUTH_CLIENT_ID,\n    clientSecret: process.env.OAUTH_CLIENT_SECRET\n  }\n}\n```\n\n#### Required Scopes and Scope Hierarchies\n\n`requiredScopes` lists the scopes a token must carry to reach the MCP\nendpoints. A valid token missing any of them gets a `403` with a\n`WWW-Authenticate: Bearer error=\"insufficient_scope\"` challenge.\n\nBy default, scopes are matched exactly. If your authorization server issues\nhierarchical scopes, pass `scopeImplies(granted, required)` to decide whether a\ngranted scope satisfies a required one. The built-in `colonScopeHierarchy`\nhelper implements the `resource:action` convention: `files` implies\n`files:read`, but `files:read` does not imply `files`, and `filesystem` does\nnot imply `files:read`.\n\n```typescript\nimport mcpPlugin, { colonScopeHierarchy } from '@platformatic/mcp'\n\nawait app.register(mcpPlugin, {\n  authorization: {\n    enabled: true,\n    authorizationServers: ['https://auth.example.com'],\n    resourceUri: 'https://mcp.example.com',\n    tokenValidation: { jwksUri: 'https://auth.example.com/.well-known/jwks.json' },\n    requiredScopes: ['files:read'],\n    // A token granted `files` is accepted\n    scopeImplies: colonScopeHierarchy\n  }\n})\n```\n\n#### OAuth Client Configuration\n\nThis optional configuration is for server-side OAuth client operations. It is\nnot required for MCP clients, which authorize directly with the advertised\nauthorization server.\n\n```typescript\nauthorization: {\n  enabled: true,\n  oauth2Client: {\n    clientId: process.env.OAUTH_CLIENT_ID,\n    clientSecret: process.env.OAUTH_CLIENT_SECRET,\n    \n    // Authorization server\n    authorizationServer: 'https://auth.example.com',\n    \n    // Application configuration\n    scopes: ['read', 'write', 'admin'],\n    \n    // Optional: Dynamic client registration\n    dynamicRegistration: true\n  }\n}\n```\n\n### Session-Based Authorization\n\nThe plugin maps OAuth tokens to MCP sessions for efficient authorization:\n\n```typescript\n// Authorization context automatically added to sessions\ninterface SessionMetadata {\n  id: string\n  authorization?: {\n    userId: string\n    clientId: string\n    scopes: string[]\n    tokenHash: string // Secure hash of access token\n    expiresAt: Date\n    // ... additional context\n  }\n  tokenRefresh?: {\n    refreshToken: string\n    clientId: string\n    authorizationServer: string\n    scopes: string[]\n  }\n}\n```\n\n### Token Refresh\n\nAutomatic background token refresh is not available yet: there is no\n`authorization.tokenRefresh` option and no `app.tokenRefreshService`\ndecorator. Clients are expected to refresh their own access tokens (for\nexample through `POST /oauth/refresh` when `oauth2Client` is configured).\nEnd-to-end token refresh is tracked in\n[#146](https://github.com/platformatic/mcp/issues/146).\n\n### Authorization-Aware Tools\n\nAccess authorization context in tool handlers:\n\n```typescript\napp.mcpAddTool({\n  name: 'user-profile',\n  description: 'Get user profile information',\n  inputSchema: Type.Object({})\n}, async (params, { authContext }) => {\n  if (!authContext?.userId) {\n    return {\n      content: [{ type: 'text', text: 'Authentication required' }],\n      isError: true\n    }\n  }\n  \n  // Check required scopes\n  if (!authContext.scopes?.includes('profile:read')) {\n    return {\n      content: [{ type: 'text', text: 'Insufficient permissions' }],\n      isError: true\n    }\n  }\n  \n  // Use user context\n  const profile = await getUserProfile(authContext.userId)\n  \n  return {\n    content: [{\n      type: 'text',\n      text: `Profile: ${JSON.stringify(profile, null, 2)}`\n    }]\n  }\n})\n```\n\n### Per-Tool Authorization\n\nTool-level checks like the one above keep the tool visible to every client and only fail at call time. To gate tools per request — hiding them from `tools/list` and rejecting `tools/call` in one place — provide a `canAccessTool` hook:\n\n```typescript\nawait app.register(mcpPlugin, {\n  authorization: { /* ... */ },\n  canAccessTool: (toolName, context) => {\n    if (context.operation === 'list') {\n      return canSeeTool(toolName, context.authContext)\n    }\n\n    return canExecuteTool(toolName, context.authContext)\n  }\n})\n```\n\nThe hook receives the tool name and a per-request context (`authContext`, `request`, `sessionId`, `operation`), and may be sync or async. It is consulted at both dispatch points, so list and call can never disagree:\n\n- `tools/list` omits every tool the hook denies for that request, calling the hook with `operation: 'list'`. Per-tool checks run concurrently with a fixed cap (8 at a time), so an async hook backed by a database or HTTP service is not flooded with one lookup per registered tool; the listing keeps registration order.\n- `tools/call` on a denied tool returns the same `-32602` (Invalid params) \"Tool 'name' not found\" protocol error as an unknown tool, calling the hook with `operation: 'call'`. The hook runs even for unknown names, so callers cannot probe for tools they are not allowed to see by response shape. Response timing is not guaranteed to be indistinguishable — it depends on what the hook itself does per name. Task-augmented calls (`task` field) and `app.mcpCallTool()` also use `operation: 'call'`.\n\nVisibility and execution policies may intentionally differ — a tool hidden from `tools/list` can still be callable by a client that already knows its name.\n\nA hook that throws denies access (fail closed) and logs a warning. Only an explicit `true` grants access. Without the hook, every registered tool stays visible and callable.\n\n## Observing Tool Calls\n\nProvide `onToolCallComplete` to observe every tool call, regardless of transport, in one place:\n\n```typescript\nawait app.register(mcpPlugin, {\n  onToolCallComplete: async (event) => {\n    logger.info({\n      tool: event.toolName,\n      source: event.source,\n      durationMs: event.durationMs,\n      ok: event.outcome.ok\n    }, 'tool call completed')\n  }\n})\n```\n\nThe hook fires exactly once per tool call, after it settles: for JSON-RPC `tools/call`, for `app.mcpCallTool()`, and for a task-augmented call once execution finishes (not when the task is merely accepted). It also fires for access denial, unknown tools, invalid arguments, and task-required outcomes. It does not fire for `tools/list`, initialization/ping, or malformed requests where no tool name was resolved.\n\n`event.source` is `'json-rpc'`, `'in-process'`, or `'task'`. `event.outcome` is the same `McpCallToolOutcome` returned by `mcpCallTool()`. The hook is awaited before the response is sent, so keep it fast; a throwing or rejecting hook is logged and otherwise ignored — it never changes the tool response.\n\nSynchronous sources (`'json-rpc'` and `'in-process'`) expose the active `request`/`reply`. Task events never do: a task can finish after the original HTTP response has already completed, so `request`/`reply` are absent on `source: 'task'` events. Every event, regardless of source, carries `requestId` for correlation.\n\nDuration semantics also differ by source: `'json-rpc'` and `'in-process'` durations include authorization, validation, sanitization, and execution. Task durations measure only actual execution — validation, sanitization, and the tool call — excluding the time the task spent queued.\n\n`event.arguments` are the raw, unredacted tool arguments. Redact sensitive values yourself before logging or persisting them.\n\n## Customizing MCP Route Schemas\n\nUse `transformRouteSchema` to customize Fastify/OpenAPI schema metadata on MCP transport routes without replacing handlers or route definitions.\n\n```typescript\nimport Fastify from 'fastify'\nimport mcpPlugin from '@platformatic/mcp'\n\nconst app = Fastify()\n\nawait app.register(mcpPlugin, {\n  transformRouteSchema (schema, { routeId }) {\n    return {\n      ...schema,\n      tags: ['MCP'],\n      security: [{ oauth2: ['tools:read'] }],\n      operationId: routeId.replace('.', '-'),\n      'x-roles': ['admin']\n    }\n  }\n})\n```\n\nRoute IDs are stable and map to MCP transport routes:\n\n| Route ID | Method | Purpose |\n|---|---|---|\n| `mcp.post` | `POST` | MCP JSON-RPC messages |\n| `mcp.get` | `GET` | MCP streaming/SSE connection |\n| `mcp.delete` | `DELETE` | MCP session termination |\n\nBehavior notes:\n\n- The callback runs once per registered MCP transport route during startup.\n- The plugin passes the o","readmeFilename":"README.md"}