diff --git a/.env.example b/.env.example index d9e5f7c70..a5fe49c5f 100644 --- a/.env.example +++ b/.env.example @@ -26,3 +26,7 @@ NUXT_OAUTH_GITHUB_CLIENT_SECRET= # Session encryption password (generate a secure random string) # You can use: openssl rand -base64 32 NUXT_SESSION_PASSWORD= + +# MCP Evaluation (Optional - for running evalite tests) +OPENAI_API_KEY= +MCP_URL=http://localhost:3000/mcp diff --git a/README.md b/README.md index cffa5dc34..351757147 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,34 @@ Build the application for production: pnpm generate ``` +### Running Evals for the MCP Server + +To run the evals for the MCP server, follow these steps: + +1. **Ensure your development server is running** + Start the local Nuxt development server: + ```bash + pnpm dev + ``` + +2. **Create an AI Gateway API key** + Go to https://vercel.com/ai-gateway and create an API key. + Add the following variable to your `.env` file (replace `sk-...` with your actual key): + ``` + AI_GATEWAY_API_KEY= + ``` + +3. **Run the evals** + You can execute the evals from the command line: + ```bash + pnpm eval + ``` + + Or launch the interactive UI to run them via a web interface: + ```bash + pnpm eval:ui + ``` + ## License [MIT License](./LICENSE) diff --git a/evalite.config.ts b/evalite.config.ts new file mode 100644 index 000000000..fb65c13e6 --- /dev/null +++ b/evalite.config.ts @@ -0,0 +1,6 @@ +import 'dotenv/config' +import { defineConfig } from 'evalite/config' + +export default defineConfig({ + cache: true +}) diff --git a/package.json b/package.json index 0d43ede92..52da91d85 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "lint": "eslint . --cache", "typecheck": "nuxt typecheck", "test": "pnpm lint && pnpm typecheck", + "eval": "evalite", + "eval:ui": "evalite serve", "db:generate": "nuxt hub database generate", "db:migrate": "nuxt hub database migrate" }, @@ -54,15 +56,19 @@ "valibot": "1.1.0" }, "devDependencies": { + "@ai-sdk/mcp": "^0.0.8", + "@ai-sdk/openai": "3.0.0-beta.60", "@iconify-json/vscode-icons": "^1.2.36", "@nuxt/eslint": "^1.10.0", "@nuxt/test-utils": "^3.20.1", "@nuxtjs/turnstile": "^1.1.1", "@testing-library/vue": "^8.1.0", "@types/youtube": "^0.1.2", + "ai": "6.0.0-beta.99", "capture-website": "^5.1.0", "drizzle-kit": "^0.31.7", "eslint": "^9.39.1", + "evalite": "1.0.0-beta.13", "nuxt-content-twoslash": "0.1.2", "shiki": "^3.15.0", "twoslash": "^0.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d92d9dfac..614021b8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -133,6 +133,12 @@ importers: specifier: 1.1.0 version: 1.1.0(typescript@5.9.3) devDependencies: + '@ai-sdk/mcp': + specifier: ^0.0.8 + version: 0.0.8(zod@4.1.12) + '@ai-sdk/openai': + specifier: 3.0.0-beta.60 + version: 3.0.0-beta.60(zod@4.1.12) '@iconify-json/vscode-icons': specifier: ^1.2.36 version: 1.2.36 @@ -151,6 +157,9 @@ importers: '@types/youtube': specifier: ^0.1.2 version: 0.1.2 + ai: + specifier: 6.0.0-beta.99 + version: 6.0.0-beta.99(zod@4.1.12) capture-website: specifier: ^5.1.0 version: 5.1.0(typescript@5.9.3) @@ -160,6 +169,9 @@ importers: eslint: specifier: ^9.39.1 version: 9.39.1(jiti@2.6.1) + evalite: + specifier: 1.0.0-beta.13 + version: 1.0.0-beta.13(ai@6.0.0-beta.99(zod@4.1.12))(better-sqlite3@12.4.6) nuxt-content-twoslash: specifier: 0.1.2 version: 0.1.2(@nuxtjs/mdc@0.18.4(magicast@0.5.1))(magicast@0.5.1) @@ -199,22 +211,60 @@ packages: bcrypt: optional: true + '@ai-sdk/gateway@2.0.0-beta.52': + resolution: {integrity: sha512-xH1J+Fn7sLjDQVB2XPMsha/gCWUhJ+DvNvLzhbNf7P1XdTRsHY3HMx3xnZLXfaHBXOVCJ+JtAtxS3+3xGa2u2A==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/gateway@2.0.13': resolution: {integrity: sha512-q8M+7+VEKp91I295cjNDgQ4LyGImKj5cDLVARDay7nBTXGjIRZOlthYE7K6Rbz2yHKFyTmKH7MMkYavAM7L/UQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/mcp@0.0.8': + resolution: {integrity: sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@3.0.0-beta.60': + resolution: {integrity: sha512-rJw42J/inb407sDvJYet+E135NH5Vi/Sfk1STDpmIOAU+dVzrFsjg1f5UY+czf7R7M+iEE75xk4XAeSOFM+Kzw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@3.0.17': resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@4.0.0-beta.33': + resolution: {integrity: sha512-8UnWOiWP5Fm0X+tU0ne4X3OnbDq5mMMxympHcW5VjVmx+Mc1TgD8KGPl0XsS9l3f61qPSVn1vZC3FthRnTlvhA==} + engines: {node: '>=18'} + peerDependencies: + '@valibot/to-json-schema': ^1.3.0 + arktype: ^2.1.22 + effect: ^3.18.4 + zod: ^3.25.76 || ^4.1.8 + peerDependenciesMeta: + '@valibot/to-json-schema': + optional: true + arktype: + optional: true + effect: + optional: true + '@ai-sdk/provider@2.0.0': resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} + '@ai-sdk/provider@3.0.0-beta.16': + resolution: {integrity: sha512-R62Z0fziX467Eu6MtVhkmHm0VFtJrq4vPGo8w4mcc4LhSPncHwn+b9yoyxv3f2pkWyUAhPR4ttgWyZoFG/lXIA==} + engines: {node: '>=18'} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -359,6 +409,9 @@ packages: '@barbapapazes/plausible-tracker@0.5.6': resolution: {integrity: sha512-GRZxn3ZngYQ1+QbdP8d66D/lQg+T2oEevG8kBGfNwVbt9VZB67sgMx/gkRo/Ww2lH7QelgjUNzvOeG+DsJX2HQ==} + '@borewit/text-codec@0.1.1': + resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + '@capsizecss/unpack@3.0.1': resolution: {integrity: sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg==} engines: {node: '>=18'} @@ -1133,10 +1186,37 @@ packages: '@fastify/accept-negotiator@2.0.1': resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==} + '@fastify/ajv-compiler@4.0.5': + resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} + '@fastify/busboy@2.1.1': resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} + '@fastify/error@4.2.0': + resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} + + '@fastify/fast-json-stringify-compiler@5.0.3': + resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} + + '@fastify/forwarded@3.0.1': + resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} + + '@fastify/merge-json-schemas@0.2.1': + resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} + + '@fastify/proxy-addr@5.1.0': + resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} + + '@fastify/send@4.1.0': + resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==} + + '@fastify/static@8.3.0': + resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==} + + '@fastify/websocket@11.2.0': + resolution: {integrity: sha512-3HrDPbAG1CzUCqnslgJxppvzaAZffieOVbLp1DAy1huCSynUWPifSvfdEDUR8HlJLp3sp1A36uOM2tJogADS8w==} + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -2272,6 +2352,9 @@ packages: resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} engines: {node: '>=10'} + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2855,6 +2938,13 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@stricli/auto-complete@1.2.4': + resolution: {integrity: sha512-MxGgeBbFyH+YtzhIjJeOr68NSx0r8mn4+hWRGgWn31YTG9u0Oa2y9oGHPY+PHeoTfvS7vhgllFvOrHcTVpwTrA==} + hasBin: true + + '@stricli/core@1.2.4': + resolution: {integrity: sha512-ujvJDQpC2FINWvlTjkFz+Qzw/vsB8p/LyZEW18idisqIyjXR6yb+sF3WTUPksl+5ZON5r4fHQnCqQWnJxeqSzg==} + '@stylistic/eslint-plugin@5.6.1': resolution: {integrity: sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2989,6 +3079,9 @@ packages: '@vue/compiler-sfc': optional: true + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} @@ -3454,6 +3547,10 @@ packages: engines: {node: '>=18'} hasBin: true + '@vercel/oidc@3.0.3': + resolution: {integrity: sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg==} + engines: {node: '>= 20'} + '@vercel/oidc@3.0.5': resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} engines: {node: '>= 20'} @@ -3730,6 +3827,9 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3768,6 +3868,12 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + ai@6.0.0-beta.99: + resolution: {integrity: sha512-Z/TQByUwZepN2425FepmvDNNpqVufNtqv1QJju6tGL8uiWLwHc9HKN36+joSuCozeZq9JE77FaQ0TBuRh6Hh/A==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -3861,6 +3967,10 @@ packages: resolution: {integrity: sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==} engines: {node: '>=20.19.0'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} @@ -3870,6 +3980,10 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + autoprefixer@10.4.22: resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} engines: {node: ^10 || ^12 || >=14} @@ -3881,6 +3995,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + avvio@9.1.0: + resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} + aws4fetch@1.0.20: resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} @@ -4225,6 +4342,10 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + content-disposition@1.0.1: resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} engines: {node: '>=18'} @@ -4837,6 +4958,9 @@ packages: duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + earcut@2.2.4: resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} @@ -5175,6 +5299,18 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + evalite@1.0.0-beta.13: + resolution: {integrity: sha512-mEUBSgA8IN/aHJV1dwbsDrxiAqgGeUICXH1oNmoSvr5c739lykbQWwpkC2lX2woVj73g0KwXTeIAJwUOn3OKog==} + hasBin: true + peerDependencies: + ai: ^5 + better-sqlite3: ^11.6.0 + peerDependenciesMeta: + ai: + optional: true + better-sqlite3: + optional: true + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -5239,6 +5375,9 @@ packages: resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} engines: {node: '>=18'} + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -5252,15 +5391,27 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-json-stringify@6.1.1: + resolution: {integrity: sha512-DbgptncYEXZqDUOEl4krff4mUiVrTZZVI7BBrQR/T3BqMj/eM1flTC1Uk2uUoLcWCxjT95xKulV/Lc6hhOZsBQ==} + fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fast-npm-meta@0.4.7: resolution: {integrity: sha512-aZU3i3eRcSb2NCq8i6N6IlyiTyF6vqAqzBGl2NBF6ngNx/GIqfYbkLDIKZ4z4P0o/RmtsFnVqHwdrSm13o4tnQ==} + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastify-plugin@5.1.0: + resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} + + fastify@5.6.2: + resolution: {integrity: sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -5295,6 +5446,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-type@19.6.0: + resolution: {integrity: sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==} + engines: {node: '>=18'} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -5310,6 +5465,10 @@ packages: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} + find-my-way@9.3.0: + resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} + engines: {node: '>=20'} + find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} @@ -5443,6 +5602,10 @@ packages: get-port-please@3.2.0: resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -5504,6 +5667,11 @@ packages: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + hasBin: true + global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} @@ -5774,6 +5942,10 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + ipx@3.1.1: resolution: {integrity: sha512-7Xnt54Dco7uYkfdAw0r2vCly3z0rSaVhEXMzPvl3FndsTVm5p26j+PO+gyinkYmcsEUvX2Rh7OGK7KzYWRu6BA==} hasBin: true @@ -5985,6 +6157,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -6004,6 +6180,10 @@ packages: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -6033,6 +6213,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-ref-resolver@3.0.0: + resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} + json-schema-to-typescript-lite@15.0.0: resolution: {integrity: sha512-5mMORSQm9oTLyjM4mWnyNBi2T042Fhg1/0gCIB6X8U/LVpM2A+Nmj2yEyArqVouDmFThDxpEXcnTgSrjkGJRFA==} @@ -6109,6 +6292,9 @@ packages: cpu: [x64, arm64, wasm32, arm] os: [darwin, linux, win32] + light-my-request@6.6.0: + resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} + lighthouse-logger@2.0.2: resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} @@ -6231,6 +6417,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} @@ -6243,6 +6432,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -6755,6 +6948,10 @@ packages: resolution: {integrity: sha512-P7o0hkMahOhjb1niG28vLNAXsJrRcfpJvYWcTmPt/Tf4xedcF2PA1E9++N1tufY8/vIsaiJgHhjQp53hJCe+zw==} engines: {node: '>=20'} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -6906,6 +7103,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -6930,6 +7131,10 @@ packages: resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} hasBin: true + peek-readable@5.4.2: + resolution: {integrity: sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==} + engines: {node: '>=14.16'} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -6947,6 +7152,16 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@10.1.0: + resolution: {integrity: sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==} + hasBin: true + pkce-challenge@5.0.0: resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} engines: {node: '>=16.20.0'} @@ -7179,6 +7394,12 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@4.0.1: + resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -7246,6 +7467,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quickselect@2.0.0: resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==} @@ -7291,6 +7515,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -7413,6 +7641,10 @@ packages: restructure@3.0.2: resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + ret@0.5.0: + resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} + engines: {node: '>=10'} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -7538,6 +7770,9 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safe-regex2@5.0.0: + resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} + safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} @@ -7592,6 +7827,9 @@ packages: resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} engines: {node: '>= 18'} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -7689,6 +7927,10 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + slugify@1.6.6: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} @@ -7716,6 +7958,9 @@ packages: resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -7751,6 +7996,10 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + srvx@0.9.6: resolution: {integrity: sha512-5L4rT6qQqqb+xcoDoklUgCNdmzqJ6vbcDRwPVGRXewF55IJH0pqh0lQlrJ266ZWTKJ4mfeioqHQJeAYesS+RrQ==} engines: {node: '>=20.16.0'} @@ -7785,6 +8034,9 @@ packages: resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} engines: {node: '>=4', npm: '>=6'} + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamx@2.23.0: resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} @@ -7842,6 +8094,10 @@ packages: striptags@3.2.0: resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} + strtok3@9.1.1: + resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==} + engines: {node: '>=16'} + structured-clone-es@1.0.0: resolution: {integrity: sha512-FL8EeKFFyNQv5cMnXI31CIMCsFarSVI2bF0U0ImeNE3g/F1IvJQyqzOXxPBRXiwQfyBTlbNe88jh1jFW0O/jiQ==} @@ -7882,6 +8138,10 @@ packages: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} @@ -7931,6 +8191,9 @@ packages: text-decoder@1.2.3: resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + three@0.135.0: resolution: {integrity: sha512-kuEpuuxRzLv0MDsXai9huCxOSQPZ4vje6y0gn80SRmQvgz6/+rI0NAvCRAw56zYaWKMGMfqKWsxF9Qa2Z9xymQ==} @@ -7990,10 +8253,18 @@ packages: resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} engines: {node: '>=20'} + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@6.1.1: + resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} + engines: {node: '>=14.16'} + topojson-client@3.1.0: resolution: {integrity: sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==} hasBin: true @@ -8114,6 +8385,10 @@ packages: ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + ultrahtml@1.6.0: resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} @@ -8803,6 +9078,17 @@ snapshots: '@phc/format': 1.0.0 '@poppinss/utils': 6.10.1 + '@ai-sdk/gateway@2.0.0-beta.52(zod@4.1.12)': + dependencies: + '@ai-sdk/provider': 3.0.0-beta.16 + '@ai-sdk/provider-utils': 4.0.0-beta.33(zod@4.1.12) + '@vercel/oidc': 3.0.3 + zod: 4.1.12 + transitivePeerDependencies: + - '@valibot/to-json-schema' + - arktype + - effect + '@ai-sdk/gateway@2.0.13(zod@4.1.12)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -8810,6 +9096,23 @@ snapshots: '@vercel/oidc': 3.0.5 zod: 4.1.12 + '@ai-sdk/mcp@0.0.8(zod@4.1.12)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.17(zod@4.1.12) + pkce-challenge: 5.0.0 + zod: 4.1.12 + + '@ai-sdk/openai@3.0.0-beta.60(zod@4.1.12)': + dependencies: + '@ai-sdk/provider': 3.0.0-beta.16 + '@ai-sdk/provider-utils': 4.0.0-beta.33(zod@4.1.12) + zod: 4.1.12 + transitivePeerDependencies: + - '@valibot/to-json-schema' + - arktype + - effect + '@ai-sdk/provider-utils@3.0.17(zod@4.1.12)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -8817,10 +9120,21 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.1.12 + '@ai-sdk/provider-utils@4.0.0-beta.33(zod@4.1.12)': + dependencies: + '@ai-sdk/provider': 3.0.0-beta.16 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.6 + zod: 4.1.12 + '@ai-sdk/provider@2.0.0': dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@3.0.0-beta.16': + dependencies: + json-schema: 0.4.0 + '@alloc/quick-lru@5.2.0': {} '@antfu/install-pkg@1.1.0': @@ -9012,6 +9326,8 @@ snapshots: '@barbapapazes/plausible-tracker@0.5.6': {} + '@borewit/text-codec@0.1.1': {} + '@capsizecss/unpack@3.0.1': dependencies: fontkit: 2.0.4 @@ -9526,11 +9842,59 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@fastify/accept-negotiator@2.0.1': - optional: true + '@fastify/accept-negotiator@2.0.1': {} + + '@fastify/ajv-compiler@4.0.5': + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + fast-uri: 3.1.0 '@fastify/busboy@2.1.1': {} + '@fastify/error@4.2.0': {} + + '@fastify/fast-json-stringify-compiler@5.0.3': + dependencies: + fast-json-stringify: 6.1.1 + + '@fastify/forwarded@3.0.1': {} + + '@fastify/merge-json-schemas@0.2.1': + dependencies: + dequal: 2.0.3 + + '@fastify/proxy-addr@5.1.0': + dependencies: + '@fastify/forwarded': 3.0.1 + ipaddr.js: 2.2.0 + + '@fastify/send@4.1.0': + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.1 + mime: 3.0.0 + + '@fastify/static@8.3.0': + dependencies: + '@fastify/accept-negotiator': 2.0.1 + '@fastify/send': 4.1.0 + content-disposition: 0.5.4 + fastify-plugin: 5.1.0 + fastq: 1.19.1 + glob: 11.1.0 + + '@fastify/websocket@11.2.0': + dependencies: + duplexify: 4.1.3 + fastify-plugin: 5.1.0 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -11128,6 +11492,8 @@ snapshots: '@phc/format@1.0.0': {} + '@pinojs/redact@0.4.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -11603,6 +11969,12 @@ snapshots: '@standard-schema/spec@1.0.0': {} + '@stricli/auto-complete@1.2.4': + dependencies: + '@stricli/core': 1.2.4 + + '@stricli/core@1.2.4': {} + '@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@2.6.1))': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -11727,6 +12099,8 @@ snapshots: optionalDependencies: '@vue/compiler-sfc': 3.5.25 + '@tokenizer/token@0.3.0': {} + '@tootallnate/quickjs-emscripten@0.23.0': {} '@tybys/wasm-util@0.10.1': @@ -12268,6 +12642,8 @@ snapshots: - rollup - supports-color + '@vercel/oidc@3.0.3': {} + '@vercel/oidc@3.0.5': {} '@vercel/speed-insights@1.2.0(vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))': @@ -12597,6 +12973,8 @@ snapshots: dependencies: event-target-shim: 5.0.1 + abstract-logging@2.0.1: {} + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -12626,6 +13004,18 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.1.12 + ai@6.0.0-beta.99(zod@4.1.12): + dependencies: + '@ai-sdk/gateway': 2.0.0-beta.52(zod@4.1.12) + '@ai-sdk/provider': 3.0.0-beta.16 + '@ai-sdk/provider-utils': 4.0.0-beta.33(zod@4.1.12) + '@opentelemetry/api': 1.9.0 + zod: 4.1.12 + transitivePeerDependencies: + - '@valibot/to-json-schema' + - arktype + - effect + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -12725,6 +13115,8 @@ snapshots: '@babel/parser': 7.28.5 ast-kit: 2.2.0 + astral-regex@2.0.0: {} + async-retry@1.3.3: dependencies: retry: 0.13.1 @@ -12733,6 +13125,8 @@ snapshots: async@3.2.6: {} + atomic-sleep@1.0.0: {} + autoprefixer@10.4.22(postcss@8.5.6): dependencies: browserslist: 4.28.0 @@ -12747,6 +13141,11 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + avvio@9.1.0: + dependencies: + '@fastify/error': 4.2.0 + fastq: 1.19.1 + aws4fetch@1.0.20: {} b4a@1.7.3: {} @@ -13098,6 +13497,10 @@ snapshots: consola@3.4.2: {} + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + content-disposition@1.0.1: {} content-type@1.0.5: {} @@ -13630,6 +14033,13 @@ snapshots: duplexer@0.1.2: {} + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + earcut@2.2.4: {} eastasianwidth@0.2.0: {} @@ -14096,6 +14506,29 @@ snapshots: etag@1.8.1: {} + evalite@1.0.0-beta.13(ai@6.0.0-beta.99(zod@4.1.12))(better-sqlite3@12.4.6): + dependencies: + '@fastify/static': 8.3.0 + '@fastify/websocket': 11.2.0 + '@stricli/auto-complete': 1.2.4 + '@stricli/core': 1.2.4 + '@vitest/runner': 4.0.13 + '@vitest/utils': 4.0.13 + dotenv: 16.6.1 + fastify: 5.6.2 + file-type: 19.6.0 + get-port: 7.1.0 + jiti: 2.6.1 + js-levenshtein: 1.1.6 + table: 6.9.0 + tinyrainbow: 3.0.3 + optionalDependencies: + ai: 6.0.0-beta.99(zod@4.1.12) + better-sqlite3: 12.4.6 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + event-target-shim@5.0.1: {} events-universal@1.0.1: @@ -14197,6 +14630,8 @@ snapshots: fake-indexeddb@6.2.5: {} + fast-decode-uri-component@1.0.1: {} + fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} @@ -14211,12 +14646,45 @@ snapshots: fast-json-stable-stringify@2.1.0: {} + fast-json-stringify@6.1.1: + dependencies: + '@fastify/merge-json-schemas': 0.2.1 + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + fast-uri: 3.1.0 + json-schema-ref-resolver: 3.0.0 + rfdc: 1.4.1 + fast-levenshtein@2.0.6: {} fast-npm-meta@0.4.7: {} + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + fast-uri@3.1.0: {} + fastify-plugin@5.1.0: {} + + fastify@5.6.2: + dependencies: + '@fastify/ajv-compiler': 4.0.5 + '@fastify/error': 4.2.0 + '@fastify/fast-json-stringify-compiler': 5.0.3 + '@fastify/proxy-addr': 5.1.0 + abstract-logging: 2.0.1 + avvio: 9.1.0 + fast-json-stringify: 6.1.1 + find-my-way: 9.3.0 + light-my-request: 6.6.0 + pino: 10.1.0 + process-warning: 5.0.0 + rfdc: 1.4.1 + secure-json-parse: 4.1.0 + semver: 7.7.3 + toad-cache: 3.7.0 + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -14248,6 +14716,13 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-type@19.6.0: + dependencies: + get-stream: 9.0.1 + strtok3: 9.1.1 + token-types: 6.1.1 + uint8array-extras: 1.5.0 + file-uri-to-path@1.0.0: {} file-url@4.0.0: {} @@ -14267,6 +14742,12 @@ snapshots: transitivePeerDependencies: - supports-color + find-my-way@9.3.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 5.0.0 + find-root@1.1.0: {} find-up-simple@1.0.1: {} @@ -14419,6 +14900,8 @@ snapshots: get-port-please@3.2.0: {} + get-port@7.1.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -14492,6 +14975,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.1.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.1 + global-directory@4.0.1: dependencies: ini: 4.1.1 @@ -14837,6 +15329,8 @@ snapshots: ipaddr.js@1.9.1: {} + ipaddr.js@2.2.0: {} + ipx@3.1.1(@vercel/blob@2.0.0)(aws4fetch@1.0.20)(db0@0.3.4(@libsql/client@0.15.15)(better-sqlite3@12.4.6)(drizzle-orm@0.44.7(@cloudflare/workers-types@4.20251121.0)(@libsql/client@0.15.15)(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.6)))(ioredis@5.8.2): dependencies: '@fastify/accept-negotiator': 2.0.1 @@ -15052,6 +15546,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + jiti@2.6.1: {} jose@6.1.2: {} @@ -15068,6 +15566,8 @@ snapshots: js-cookie@3.0.5: {} + js-levenshtein@1.1.6: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -15086,6 +15586,10 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-schema-ref-resolver@3.0.0: + dependencies: + dequal: 2.0.3 + json-schema-to-typescript-lite@15.0.0: dependencies: '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) @@ -15164,6 +15668,12 @@ snapshots: '@libsql/linux-x64-musl': 0.5.22 '@libsql/win32-x64-msvc': 0.5.22 + light-my-request@6.6.0: + dependencies: + cookie: 1.0.2 + process-warning: 4.0.1 + set-cookie-parser: 2.7.2 + lighthouse-logger@2.0.2: dependencies: debug: 4.4.3 @@ -15280,6 +15790,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.truncate@4.4.2: {} + lodash.uniq@4.5.0: {} lodash@4.17.21: {} @@ -15288,6 +15800,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -16262,6 +16776,8 @@ snapshots: on-change@6.0.1: {} + on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -16486,6 +17002,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.2 + minipass: 7.1.2 + path-to-regexp@6.3.0: {} path-to-regexp@8.3.0: {} @@ -16503,6 +17024,8 @@ snapshots: ieee754: 1.2.1 resolve-protobuf-schema: 2.1.0 + peek-readable@5.4.2: {} + pend@1.2.0: {} perfect-debounce@2.0.0: {} @@ -16513,6 +17036,26 @@ snapshots: picomatch@4.0.3: {} + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.0.0: {} + + pino@10.1.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + pkce-challenge@5.0.0: {} pkg-types@1.3.1: @@ -16735,6 +17278,10 @@ snapshots: process-nextick-args@2.0.1: {} + process-warning@4.0.1: {} + + process-warning@5.0.0: {} + process@0.11.10: {} progress@2.0.3: {} @@ -16825,6 +17372,8 @@ snapshots: queue-microtask@1.2.3: {} + quick-format-unescaped@4.0.4: {} + quickselect@2.0.0: {} radix3@1.1.2: {} @@ -16888,6 +17437,8 @@ snapshots: dependencies: picomatch: 2.3.1 + real-require@0.2.0: {} + redis-errors@1.2.0: {} redis-parser@3.0.0: @@ -17090,6 +17641,8 @@ snapshots: restructure@3.0.2: {} + ret@0.5.0: {} + retry@0.13.1: {} reusify@1.1.0: {} @@ -17239,6 +17792,10 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safe-regex2@5.0.0: + dependencies: + ret: 0.5.0 + safe-stable-stringify@2.5.0: {} safer-buffer@2.1.2: {} @@ -17312,6 +17869,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-cookie-parser@2.7.2: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -17496,6 +18055,12 @@ snapshots: slash@5.1.0: {} + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + slugify@1.6.6: {} smart-buffer@4.2.0: {} @@ -17533,6 +18098,10 @@ snapshots: ip-address: 10.1.0 smart-buffer: 4.2.0 + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -17559,6 +18128,8 @@ snapshots: speakingurl@14.0.1: {} + split2@4.2.0: {} + srvx@0.9.6: {} stable-hash-x@0.2.0: {} @@ -17580,6 +18151,8 @@ snapshots: stoppable@1.1.0: {} + stream-shift@1.0.3: {} + streamx@2.23.0: dependencies: events-universal: 1.0.1 @@ -17640,6 +18213,11 @@ snapshots: striptags@3.2.0: {} + strtok3@9.1.1: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 5.4.2 + structured-clone-es@1.0.0: {} stylehacks@7.0.7(postcss@8.5.6): @@ -17678,6 +18256,14 @@ snapshots: system-architecture@0.1.0: {} + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + tagged-tag@1.0.0: {} tailwind-merge@3.4.0: {} @@ -17749,6 +18335,10 @@ snapshots: transitivePeerDependencies: - react-native-b4a + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + three@0.135.0: {} throttle-debounce@5.0.2: {} @@ -17797,8 +18387,16 @@ snapshots: '@sindresorhus/base62': 1.0.0 reserved-identifiers: 1.2.0 + toad-cache@3.7.0: {} + toidentifier@1.0.1: {} + token-types@6.1.1: + dependencies: + '@borewit/text-codec': 0.1.1 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + topojson-client@3.1.0: dependencies: commander: 2.20.3 @@ -17917,6 +18515,8 @@ snapshots: ufo@1.6.1: {} + uint8array-extras@1.5.0: {} + ultrahtml@1.6.0: {} unconfig-core@7.4.1: diff --git a/server/routes/mcp.ts b/server/routes/mcp.ts index 741889e0a..9696067e7 100644 --- a/server/routes/mcp.ts +++ b/server/routes/mcp.ts @@ -77,7 +77,9 @@ function createServer() { title: 'Find Documentation for Topic', description: 'Find the best Nuxt documentation for a specific topic or feature', argsSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints topic: z.string().describe('Describe what you want to learn about (e.g., "server-side rendering", "data fetching", "routing")'), + // @ts-expect-error - MCP SDK has overly strict Zod type constraints version: z.enum(['3.x', '4.x']).optional().describe('Documentation version to search (defaults to 4.x)') } }, @@ -105,13 +107,14 @@ function createServer() { title: 'Deployment Guide', description: 'Get deployment instructions for a specific hosting provider', argsSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints provider: z.string().describe('Hosting provider name (e.g., "Vercel", "Netlify", "AWS", "Cloudflare")') } }, async ({ provider }: { provider: string }) => { const deployProviders = await $fetch('/api/mcp/list-deploy-providers') const matchingProvider = deployProviders.find(p => - p.title.toLowerCase().includes(provider.toLowerCase()) + p.title.toLowerCase().includes(provider!.toLowerCase()) ) let providerDetails = null @@ -141,7 +144,9 @@ function createServer() { title: 'Migration Help', description: 'Get help with migrating between Nuxt versions', argsSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints fromVersion: z.string().describe('Current Nuxt version (e.g., "2", "3.x")'), + // @ts-expect-error - MCP SDK has overly strict Zod type constraints toVersion: z.string().describe('Target Nuxt version (e.g., "3.x", "4.x")') } }, @@ -172,15 +177,22 @@ To find relevant migration guides, please: 'list_documentation_pages', { title: 'List Documentation Pages', - description: 'Lists all available Nuxt documentation pages with their categories and basic information. Use this tool to find relevant pages by examining titles and descriptions, then use get_documentation_page to retrieve full content.', + description: `Lists all available Nuxt documentation pages with their categories and basic information. + +WHEN TO USE: Use this tool when you need to EXPLORE or SEARCH for documentation about a topic but don't know the exact page path. For example: "Find documentation about hydration errors", "What pages cover rendering modes?", "Search for migration guides". + +WHEN NOT TO USE: If you already know the specific page path (e.g., "/docs/4.x/getting-started/introduction"), use get_documentation_page directly instead. + +WORKFLOW: This tool returns page titles, descriptions, and paths. After finding relevant pages, use get_documentation_page to retrieve the full content.`, inputSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints version: z.enum(['3.x', '4.x', 'all']).optional().default('4.x').describe('Documentation version to fetch') } }, - async (params: { version?: '3.x' | '4.x' | 'all' }) => { + async (params: any) => { const result = await $fetch('/api/mcp/list-documentation-pages', { query: params }) return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -189,15 +201,43 @@ To find relevant migration guides, please: 'get_documentation_page', { title: 'Get Documentation Page', - description: 'Retrieves Nuxt documentation page content and details. Parameters: path (string, required) - the documentation path starting with /docs/.', + description: `Retrieves the full content and details of a specific Nuxt documentation page. + +WHEN TO USE: Use this tool when you know the EXACT path to a documentation page. Common use cases: +- User asks for a specific page: "Show me the introduction page" → /docs/4.x/getting-started/introduction +- User asks about a known topic with a dedicated page +- You found a relevant path from list_documentation_pages and want the full content + +WHEN NOT TO USE: If you don't know the exact path and need to search/explore, use list_documentation_pages first. + +COMMON PAGES (Nuxt 4.x): +Getting Started: +- "/docs/4.x/getting-started/introduction" - main intro +- "/docs/4.x/getting-started/installation" - setup +- "/docs/4.x/getting-started/upgrade" - migration from v3 + +Core Concepts: +- "/docs/4.x/guide/concepts/rendering" - SSR/CSR/SSG modes +- "/docs/4.x/guide/concepts/auto-imports" - auto-imports +- "/docs/4.x/guide/concepts/server-engine" - server features + +Directory Structure: +- "/docs/4.x/guide/directory-structure/composables" - composables +- "/docs/4.x/guide/directory-structure/components" - components +- "/docs/4.x/guide/directory-structure/pages" - routing + +Common Issues: +- "/docs/4.x/guide/going-further/debugging" - debugging +- "/docs/4.x/guide/going-further/error-handling" - errors`, inputSchema: { - path: z.string().describe('The path to the documentation page (e.g., /docs/3.x/getting-started/introduction)') + // @ts-expect-error - MCP SDK has overly strict Zod type constraints + path: z.string().describe('The path to the documentation page (e.g., /docs/4.x/getting-started/introduction)') } }, - async (params: { path: string }) => { + async (params: any) => { const result = await $fetch('/api/mcp/get-documentation-page', { query: params }) return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -206,13 +246,22 @@ To find relevant migration guides, please: 'list_blog_posts', { title: 'List Blog Posts', - description: 'Lists all Nuxt blog posts with metadata including dates, categories, and tags.', - inputSchema: {} + description: `Lists all Nuxt blog posts with metadata including titles, dates, categories, tags, and descriptions. + +WHEN TO USE: Use this tool when you need to DISCOVER or SEARCH for blog posts. Common scenarios: +- "What are the latest announcements?" - browse recent posts +- "Has there been any post about X feature?" - search by topic +- "Show me performance improvements" - find relevant posts by topic +- "What's new in Nuxt?" - explore recent updates + +WHEN NOT TO USE: If you already know the exact blog post path (e.g., "/blog/v4"), use get_blog_post directly. + +OUTPUT: Returns list of posts with title, description, date, path. Use get_blog_post to retrieve full content of specific posts.` }, - async () => { + async (_args: any, _extra: any) => { const result = await $fetch('/api/mcp/list-blog-posts') return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -221,15 +270,25 @@ To find relevant migration guides, please: 'get_blog_post', { title: 'Get Blog Post', - description: 'Retrieves blog post content and details. Parameters: path (string, required) - the blog post path starting with /blog/.', + description: `Retrieves the full content and details of a specific Nuxt blog post. + +WHEN TO USE: Use this tool when you know the EXACT path to a blog post. Common scenarios: +- User asks for a specific post: "Get the blog post about Nuxt 4" → /blog/v4 +- You found a relevant post from list_blog_posts and want the full content +- You know the post slug from context + +WHEN NOT TO USE: If you don't know the exact path and need to search/discover, use list_blog_posts first. + +EXAMPLES: "/blog/v4", "/blog/nuxt3", "/blog/nuxt-on-the-edge"`, inputSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints path: z.string().describe('The path to the blog post (e.g., /blog/v4)') } }, - async (params: { path: string }) => { + async (params: any) => { const result = await $fetch('/api/mcp/get-blog-post', { query: params }) return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -238,13 +297,22 @@ To find relevant migration guides, please: 'list_deploy_providers', { title: 'List Deploy Providers', - description: 'Lists all deployment providers and hosting platforms for Nuxt applications.', - inputSchema: {} + description: `Lists all deployment providers and hosting platforms for Nuxt applications with their features and capabilities. + +WHEN TO USE: Use this tool when you need to DISCOVER or COMPARE deployment options. Common scenarios: +- "What deployment platforms are available?" - browse all options +- "I need edge functions support" - compare features across providers +- "Show me platforms with free tiers" - search for specific features +- "What are my deployment options?" - general exploration + +WHEN NOT TO USE: If you know the exact provider (e.g., "Vercel", "Cloudflare"), you can use get_deploy_provider directly with the path. + +OUTPUT: Returns list of providers with titles, descriptions, and paths. Use get_deploy_provider for detailed deployment instructions.` }, - async () => { + async (_args: any, _extra: any) => { const result = await $fetch('/api/mcp/list-deploy-providers') return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -253,15 +321,25 @@ To find relevant migration guides, please: 'get_deploy_provider', { title: 'Get Deploy Provider', - description: 'Retrieves deployment provider details and instructions. Parameters: path (string, required) - the deploy provider path starting with /deploy/.', + description: `Retrieves detailed deployment instructions and setup guide for a specific hosting provider. + +WHEN TO USE: Use this tool when you know EXACTLY which provider the user wants. Common scenarios: +- User asks for a specific provider: "How do I deploy to Vercel?" → /deploy/vercel +- User mentions a known platform: "Cloudflare deployment" → /deploy/cloudflare +- You found a relevant provider from list_deploy_providers and want full details + +WHEN NOT TO USE: If the user is asking about options or comparing providers, use list_deploy_providers first. + +EXAMPLES: "/deploy/vercel", "/deploy/cloudflare", "/deploy/netlify", "/deploy/aws", "/deploy/node-server"`, inputSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints path: z.string().describe('The path to the deploy provider (e.g., /deploy/vercel)') } }, - async (params: { path: string }) => { + async (params: any) => { const result = await $fetch('/api/mcp/get-deploy-provider', { query: params }) return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -272,15 +350,16 @@ To find relevant migration guides, please: title: 'Get Getting Started Guide', description: 'Gets the getting started guide for Nuxt. Parameters: version (enum, optional) - Nuxt version.', inputSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints version: z.enum(['3.x', '4.x']).optional().default('4.x').describe('Nuxt version') } }, - async ({ version }: { version?: '3.x' | '4.x' }) => { + async ({ version }: any) => { const gettingStarted = await $fetch('/api/mcp/get-documentation-page', { query: { path: `/docs/${version}/getting-started/introduction` } }) return { - content: [{ type: 'text', text: JSON.stringify(gettingStarted, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(gettingStarted, null, 2) }] } } ) @@ -289,18 +368,38 @@ To find relevant migration guides, please: 'list_modules', { title: 'List Modules', - description: 'Lists all available Nuxt modules with optional filtering and sorting. Use this to search for modules by name, description, or category, and find the best module for your needs.', + description: `Lists all available Nuxt modules with optional filtering and sorting capabilities. + +WHEN TO USE: Use this tool when you need to DISCOVER or SEARCH for modules. Common scenarios: +- "I need an authentication module" - search by category or keyword +- "What UI libraries are available?" - filter by category +- "Show me popular image optimization modules" - filter + sort by downloads +- "Find a module for X feature" - general exploration + +PARAMETERS: +- search: Filter by name, description, or npm package name +- category: Filter by category (e.g., "ui", "auth", "database", "media", "seo") +- sort: Order by downloads, stars, publishedAt, or createdAt +- order: asc or desc + +WHEN NOT TO USE: If you already know the exact module slug (e.g., "@nuxt/ui"), use get_module directly. + +OUTPUT: Returns list of modules with name, description, category, stats. Use get_module for complete details including README and compatibility.`, inputSchema: { + // @ts-expect-error - MCP SDK has overly strict Zod type constraints search: z.string().optional().describe('Search term to filter modules by name, description, or npm package name'), + // @ts-expect-error - MCP SDK has overly strict Zod type constraints category: z.string().optional().describe('Filter modules by category (e.g., "ui", "database", "auth", "seo")'), + // @ts-expect-error - MCP SDK has overly strict Zod type constraints sort: z.enum(['downloads', 'stars', 'publishedAt', 'createdAt']).optional().default('downloads').describe('Sort modules by downloads, stars, published date, or created date'), + // @ts-expect-error - MCP SDK has overly strict Zod type constraints order: z.enum(['asc', 'desc']).optional().default('desc').describe('Sort order (ascending or descending)') } }, - async (params: { search?: string, category?: string, sort?: 'downloads' | 'stars' | 'publishedAt' | 'createdAt', order?: 'asc' | 'desc' }) => { + async (params: any) => { const result = await $fetch('/api/mcp/list-modules', { query: params }) return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) @@ -309,15 +408,32 @@ To find relevant migration guides, please: 'get_module', { title: 'Get Module', - description: 'Retrieves detailed information about a specific Nuxt module by its slug/name. Use this after finding a module with list_modules to get complete details including maintainers, contributors, compatibility, and README.', + description: `Retrieves complete details about a specific Nuxt module including README, compatibility, maintainers, and stats. + +WHEN TO USE: Use this tool when you know the EXACT module identifier. Common scenarios: +- User asks for a specific module: "Get details about @nuxt/ui" +- User mentions a known module: "Show me nuxt-icon module" +- You found a relevant module from list_modules and want full details +- You need to check Nuxt 4 compatibility for a specific module + +WHEN NOT TO USE: If you don't know the exact module identifier and need to search/discover modules, use list_modules first. + +PARAMETER: slug (required) - The unique module identifier +EXAMPLES: +- slug: "@nuxt/ui" +- slug: "@nuxtjs/i18n" +- slug: "nuxt-icon" +- slug: "@nuxt/image" +- slug: "nuxt-auth"`, inputSchema: { - slug: z.string().describe('The module slug/name (e.g., "@nuxt/ui", "nuxt-auth", "nuxt-icon")') + // @ts-expect-error - MCP SDK has overly strict Zod type constraints + slug: z.string().describe('The unique module identifier, exactly as shown in list_modules (e.g., "@nuxt/ui", "@nuxtjs/i18n", "nuxt-icon")') } }, - async (params: { slug: string }) => { + async (params: any) => { const result = await $fetch('/api/mcp/get-module', { query: params }) return { - content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] + content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] } } ) diff --git a/test/mcp.eval.ts b/test/mcp.eval.ts new file mode 100644 index 000000000..724a8520b --- /dev/null +++ b/test/mcp.eval.ts @@ -0,0 +1,138 @@ +import { experimental_createMCPClient as createMCPClient } from '@ai-sdk/mcp' +import { generateText } from 'ai' +import { evalite } from 'evalite' +import { toolCallAccuracy } from 'evalite/scorers' + +/** + * MCP Evaluation Tests + * + * Note: The MCP server has prompts (find_documentation_for_topic, deployment_guide, migration_help) + * that would improve these scenarios, but @ai-sdk/mcp doesn't support converting prompts to tools yet. + * + * TODO: Once @ai-sdk/mcp supports prompt-to-tool conversion or prompt usage in generateText, + * uncomment the tests below that require search/topic-based navigation. + * + * Related: https://ai-sdk.dev/docs/reference/ai-sdk-core/create-mcp-client + */ +const model = 'openai/gpt-5.1-codex-mini' +const MCP_URL = process.env.MCP_URL ?? 'http://localhost:3000/mcp' + +evalite('Evaluate Nuxt MCP Documentation Tools', { + data: async () => [ + // TODO: Uncomment when find_documentation_for_topic prompt becomes usable as a tool + // { + // input: 'I keep getting hydration mismatch errors in my Nuxt app. Find the documentation that explains this.', + // expected: [{ toolName: 'find_documentation_for_topic', input: { topic: 'hydration errors' } }], + // }, + // { + // input: 'What are the different rendering modes available in Nuxt 4 and which one should I use for SEO?', + // expected: [{ toolName: 'find_documentation_for_topic', input: { topic: 'rendering modes SSR SEO' } }], + // }, + // { + // input: 'How do I migrate my composables from Nuxt 3 to Nuxt 4?', + // expected: [{ toolName: 'migration_help', input: { fromVersion: '3.x', toVersion: '4.x' } }], + // }, + { + input: 'Show me the introduction page for Nuxt 4', + expected: [{ toolName: 'get_documentation_page', input: { path: '/docs/4.x/getting-started/introduction' } }] + } + ], + task: async (input) => { + const mcpClient = await createMCPClient({ transport: { type: 'http', url: MCP_URL } }) + try { + const result = await generateText({ + model, + prompt: input, + tools: await mcpClient.tools() + }) + return result.toolCalls ?? [] + } finally { + await mcpClient.close() + } + }, + scorers: [async ({ output, expected }) => toolCallAccuracy({ actualCalls: output, expectedCalls: expected })] +}) + +evalite('Evaluate Nuxt MCP Blog Tools', { + data: async () => [ + { input: 'What are the latest performance improvements announced for Nuxt?', expected: [{ toolName: 'list_blog_posts' }] }, + { input: 'Show me announcements about major version releases', expected: [{ toolName: 'list_blog_posts' }] }, + { input: 'Has there been any blog post about server components or islands architecture?', expected: [{ toolName: 'list_blog_posts' }] }, + { input: 'Get the blog post about Nuxt 4', expected: [{ toolName: 'get_blog_post', input: { path: '/blog/v4' } }] } + ], + task: async (input) => { + const mcpClient = await createMCPClient({ transport: { type: 'http', url: MCP_URL } }) + try { + const result = await generateText({ + model, + prompt: input, + tools: await mcpClient.tools() + }) + return result.toolCalls ?? [] + } finally { + await mcpClient.close() + } + }, + scorers: [async ({ output, expected }) => toolCallAccuracy({ actualCalls: output, expectedCalls: expected })] +}) + +evalite('Evaluate Nuxt MCP Deploy Tools', { + data: async () => [ + { input: 'I need a deployment platform that supports edge functions and has a generous free tier. What are my options?', expected: [{ toolName: 'list_deploy_providers' }] }, + { input: 'What deployment providers support Docker containerization?', expected: [{ toolName: 'list_deploy_providers' }] }, + { input: 'I want to self-host my Nuxt app with automatic SSL. Show me how to deploy to a VPS.', expected: [{ toolName: 'list_deploy_providers' }] } + // TODO: Uncomment when deployment_guide prompt becomes usable as a tool + // { input: 'How do I deploy to Vercel?', expected: [{ toolName: 'deployment_guide', input: { provider: 'Vercel' } }] }, + ], + task: async (input) => { + const mcpClient = await createMCPClient({ transport: { type: 'http', url: MCP_URL } }) + try { + const result = await generateText({ + model, + prompt: input, + tools: await mcpClient.tools() + }) + return result.toolCalls ?? [] + } finally { + await mcpClient.close() + } + }, + scorers: [async ({ output, expected }) => toolCallAccuracy({ actualCalls: output, expectedCalls: expected })] +}) + +evalite('Evaluate Nuxt MCP Module Tools', { + data: async () => [ + { input: 'I need to add authentication with social login providers to my app. Find me a suitable module.', expected: [{ toolName: 'list_modules', input: { category: 'authentication' } }] }, + { input: 'What modules are available for image optimization and lazy loading?', expected: [{ toolName: 'list_modules', input: { category: 'media' } }] }, + { input: 'Show me popular UI component libraries for Nuxt 4', expected: [{ toolName: 'list_modules', input: { category: 'ui' } }] }, + { input: 'I want to add i18n support for multiple languages. What module should I use and does it support Nuxt 4?', expected: [{ toolName: 'list_modules' }, { toolName: 'get_module', input: { slug: '@nuxtjs/i18n' } }] }, + { input: 'Get details about @nuxt/ui module', expected: [{ toolName: 'get_module', input: { slug: '@nuxt/ui' } }] } + ], + task: async (input) => { + const mcpClient = await createMCPClient({ transport: { type: 'http', url: MCP_URL } }) + try { + const result = await generateText({ model, prompt: input, tools: await mcpClient.tools(), maxSteps: 3 }) + return result.toolCalls ?? [] + } finally { + await mcpClient.close() + } + }, + scorers: [async ({ output, expected }) => toolCallAccuracy({ actualCalls: output, expectedCalls: expected })] +}) + +evalite('Evaluate Nuxt MCP Cross-Tool Workflows', { + data: async () => [ + { input: 'I want to build an e-commerce site with Nuxt 4. What modules do I need and where should I deploy it?', expected: [{ toolName: 'list_modules' }, { toolName: 'list_deploy_providers' }] }, + { input: 'Show me the latest features in Nuxt 4 and link to the relevant documentation', expected: [{ toolName: 'list_blog_posts' }, { toolName: 'get_documentation_page', input: { path: '/docs/4.x/getting-started/introduction' } }] } + ], + task: async (input) => { + const mcpClient = await createMCPClient({ transport: { type: 'http', url: MCP_URL } }) + try { + const result = await generateText({ model, prompt: input, tools: await mcpClient.tools(), maxSteps: 5 }) + return result.toolCalls ?? [] + } finally { + await mcpClient.close() + } + }, + scorers: [async ({ output, expected }) => toolCallAccuracy({ actualCalls: output, expectedCalls: expected })] +})