@nuxtjs/mcp-toolkit 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/module.mjs +16 -3
- package/dist/runtime/server/mcp/badge-image.d.ts +1 -1
- package/dist/runtime/server/mcp/badge-image.js +42 -126
- package/dist/runtime/server/mcp/config.js +11 -2
- package/dist/runtime/server/mcp/definitions/cache.d.ts +1 -1
- package/dist/runtime/server/mcp/definitions/cache.js +33 -2
- package/dist/runtime/server/mcp/definitions/handlers.d.ts +4 -4
- package/dist/runtime/server/mcp/definitions/prompts.d.ts +7 -0
- package/dist/runtime/server/mcp/definitions/resources.d.ts +13 -0
- package/dist/runtime/server/mcp/definitions/tools.d.ts +9 -0
- package/dist/runtime/server/mcp/definitions/utils.js +6 -1
- package/dist/runtime/server/mcp/handler.js +6 -4
- package/dist/runtime/server/mcp/loaders/utils.js +1 -1
- package/dist/runtime/server/mcp/providers/cloudflare.js +1 -0
- package/dist/runtime/server/mcp/providers/node.js +25 -21
- package/dist/runtime/server/mcp/providers/types.d.ts +1 -1
- package/dist/runtime/server/mcp/server.d.ts +27 -0
- package/dist/runtime/server/mcp/server.js +44 -0
- package/dist/runtime/server/mcp/session.d.ts +12 -0
- package/dist/runtime/server/mcp/session.js +21 -0
- package/dist/runtime/server/mcp/utils.d.ts +13 -4
- package/dist/runtime/server/mcp/utils.js +29 -7
- package/package.json +1 -6
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,6 @@ import { logger, createResolver, defineNuxtModule, addComponent, addServerTempla
|
|
|
2
2
|
import { loadAllDefinitions } from '../dist/runtime/server/mcp/loaders/index.js';
|
|
3
3
|
import { defaultMcpConfig, getMcpConfig } from '../dist/runtime/server/mcp/config.js';
|
|
4
4
|
import { ROUTES } from '../dist/runtime/server/mcp/constants.js';
|
|
5
|
-
import { addDevToolsCustomTabs } from '../dist/runtime/server/mcp/devtools/index.js';
|
|
6
5
|
import { execSync } from 'node:child_process';
|
|
7
6
|
import { existsSync, readFileSync } from 'node:fs';
|
|
8
7
|
import { homedir } from 'node:os';
|
|
@@ -72,7 +71,7 @@ function generateDeeplinkUrl(baseUrl, route, ide, serverName) {
|
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
const name = "@nuxtjs/mcp-toolkit";
|
|
75
|
-
const version = "0.
|
|
74
|
+
const version = "0.10.0";
|
|
76
75
|
|
|
77
76
|
const log = logger.withTag("@nuxtjs/mcp-toolkit");
|
|
78
77
|
const { resolve } = createResolver(import.meta.url);
|
|
@@ -99,6 +98,10 @@ const module$1 = defineNuxtModule({
|
|
|
99
98
|
if (!options.enabled) {
|
|
100
99
|
return;
|
|
101
100
|
}
|
|
101
|
+
if (mcpConfig.sessions.enabled && nitroOptions) {
|
|
102
|
+
nitroOptions.storage ??= {};
|
|
103
|
+
nitroOptions.storage["mcp:sessions"] ??= { driver: "memory" };
|
|
104
|
+
}
|
|
102
105
|
addComponent({
|
|
103
106
|
name: "InstallButton",
|
|
104
107
|
filePath: resolver.resolve("runtime/components/InstallButton.vue")
|
|
@@ -188,6 +191,8 @@ const module$1 = defineNuxtModule({
|
|
|
188
191
|
}
|
|
189
192
|
});
|
|
190
193
|
const mcpDefinitionsPath = resolver.resolve("runtime/server/mcp/definitions");
|
|
194
|
+
const mcpSessionPath = resolver.resolve("runtime/server/mcp/session");
|
|
195
|
+
const mcpServerPath = resolver.resolve("runtime/server/mcp/server");
|
|
191
196
|
addServerImports([
|
|
192
197
|
"defineMcpTool",
|
|
193
198
|
"defineMcpResource",
|
|
@@ -198,6 +203,10 @@ const module$1 = defineNuxtModule({
|
|
|
198
203
|
"errorResult",
|
|
199
204
|
"imageResult"
|
|
200
205
|
].map((name2) => ({ name: name2, from: mcpDefinitionsPath })));
|
|
206
|
+
addServerImports([
|
|
207
|
+
{ name: "useMcpSession", from: mcpSessionPath },
|
|
208
|
+
{ name: "useMcpServer", from: mcpServerPath }
|
|
209
|
+
]);
|
|
201
210
|
addServerHandler({
|
|
202
211
|
route: options.route,
|
|
203
212
|
handler: resolver.resolve("runtime/server/mcp/handler")
|
|
@@ -210,7 +219,11 @@ const module$1 = defineNuxtModule({
|
|
|
210
219
|
route: `${options.route}/badge.svg`,
|
|
211
220
|
handler: resolver.resolve("runtime/server/mcp/badge-image")
|
|
212
221
|
});
|
|
213
|
-
|
|
222
|
+
if (nuxt.options.dev) {
|
|
223
|
+
import('../dist/runtime/server/mcp/devtools/index.js').then(({ addDevToolsCustomTabs }) => {
|
|
224
|
+
addDevToolsCustomTabs(nuxt, options);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
214
227
|
}
|
|
215
228
|
});
|
|
216
229
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { defineEventHandler, getQuery, setHeader } from "h3";
|
|
2
|
-
import satori from "satori";
|
|
3
2
|
const IDE_CONFIG = {
|
|
4
3
|
cursor: {
|
|
5
4
|
defaultLabel: "Install MCP in Cursor"
|
|
@@ -8,125 +7,51 @@ const IDE_CONFIG = {
|
|
|
8
7
|
defaultLabel: "Install MCP in VS Code"
|
|
9
8
|
}
|
|
10
9
|
};
|
|
11
|
-
function
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
children: [
|
|
20
|
-
{
|
|
21
|
-
type: "path",
|
|
22
|
-
props: {
|
|
23
|
-
fill: "#666",
|
|
24
|
-
d: "M11.925 24l10.425-6-10.425-6L1.5 18l10.425 6z"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
type: "path",
|
|
29
|
-
props: {
|
|
30
|
-
fill: "#888",
|
|
31
|
-
d: "M22.35 18V6L11.925 0v12l10.425 6z"
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
type: "path",
|
|
36
|
-
props: {
|
|
37
|
-
fill: "#777",
|
|
38
|
-
d: "M11.925 0L1.5 6v12l10.425-6V0z"
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
type: "path",
|
|
43
|
-
props: {
|
|
44
|
-
fill: "#555",
|
|
45
|
-
d: "M22.35 6L11.925 24V12L22.35 6z"
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
type: "path",
|
|
50
|
-
props: {
|
|
51
|
-
fill: "#333",
|
|
52
|
-
d: "M22.35 6l-10.425 6L1.5 6h20.85z"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
};
|
|
10
|
+
function cursorIconSvg() {
|
|
11
|
+
return `<g transform="translate(8,7) scale(0.75)">
|
|
12
|
+
<path fill="#999" d="M11.925 24l10.425-6-10.425-6L1.5 18l10.425 6z"/>
|
|
13
|
+
<path fill="#bbb" d="M22.35 18V6L11.925 0v12l10.425 6z"/>
|
|
14
|
+
<path fill="#aaa" d="M11.925 0L1.5 6v12l10.425-6V0z"/>
|
|
15
|
+
<path fill="#888" d="M22.35 6L11.925 24V12L22.35 6z"/>
|
|
16
|
+
<path fill="#fff" d="M22.35 6l-10.425 6L1.5 6h20.85z"/>
|
|
17
|
+
</g>`;
|
|
58
18
|
}
|
|
59
|
-
function
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
width: 18,
|
|
64
|
-
height: 18,
|
|
65
|
-
viewBox: "0 0 24 24",
|
|
66
|
-
children: [
|
|
67
|
-
{
|
|
68
|
-
type: "path",
|
|
69
|
-
props: {
|
|
70
|
-
fill: "#007ACC",
|
|
71
|
-
d: "M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63l-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12L.326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128l9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
]
|
|
75
|
-
}
|
|
76
|
-
};
|
|
19
|
+
function vscodeIconSvg() {
|
|
20
|
+
return `<g transform="translate(8,7) scale(0.75)">
|
|
21
|
+
<path fill="#007ACC" d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63l-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12L.326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128l9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"/>
|
|
22
|
+
</g>`;
|
|
77
23
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
24
|
+
function estimateTextWidth(text, fontSize) {
|
|
25
|
+
const ratio = fontSize / 13;
|
|
26
|
+
let width = 0;
|
|
27
|
+
for (const ch of text) {
|
|
28
|
+
if (ch === " ") width += 3.6;
|
|
29
|
+
else if ("iIl|1!:;.,".includes(ch)) width += 4.2;
|
|
30
|
+
else if (`fjrt()[]{}'"/`.includes(ch)) width += 5.2;
|
|
31
|
+
else if ("mwMW".includes(ch)) width += 10;
|
|
32
|
+
else if (ch >= "A" && ch <= "Z") width += 8.5;
|
|
33
|
+
else width += 7;
|
|
34
|
+
}
|
|
35
|
+
return width * ratio;
|
|
80
36
|
}
|
|
81
|
-
|
|
37
|
+
function generateBadgeSVG(options) {
|
|
82
38
|
const { label, color, textColor, borderColor, showIcon, ide } = options;
|
|
83
|
-
const icon = getIcon(ide);
|
|
84
|
-
const element = {
|
|
85
|
-
type: "div",
|
|
86
|
-
props: {
|
|
87
|
-
style: {
|
|
88
|
-
display: "flex",
|
|
89
|
-
alignItems: "center",
|
|
90
|
-
gap: "8px",
|
|
91
|
-
padding: "6px 8px",
|
|
92
|
-
fontSize: "14px",
|
|
93
|
-
fontWeight: 500,
|
|
94
|
-
color: `#${textColor}`,
|
|
95
|
-
backgroundColor: `#${color}`,
|
|
96
|
-
border: `1px solid #${borderColor}`
|
|
97
|
-
},
|
|
98
|
-
children: showIcon ? [icon, { type: "span", props: { children: label } }] : [{ type: "span", props: { children: label } }]
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
39
|
const iconWidth = showIcon ? 26 : 0;
|
|
102
|
-
const textWidth = label
|
|
103
|
-
const padding =
|
|
104
|
-
const width = Math.
|
|
40
|
+
const textWidth = estimateTextWidth(label, 13);
|
|
41
|
+
const padding = showIcon ? 24 : 22;
|
|
42
|
+
const width = Math.ceil(iconWidth + textWidth + padding);
|
|
105
43
|
const height = 32;
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
]
|
|
117
|
-
});
|
|
118
|
-
return svg;
|
|
44
|
+
const textX = showIcon ? 34 : width / 2;
|
|
45
|
+
const textAnchor = showIcon ? "start" : "middle";
|
|
46
|
+
const icon = showIcon ? ide === "vscode" ? vscodeIconSvg() : cursorIconSvg() : "";
|
|
47
|
+
const escapedLabel = label.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
48
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
|
|
49
|
+
<rect x="0.5" y="0.5" width="${width - 1}" height="${height - 1}" rx="4" fill="#${color}" stroke="#${borderColor}"/>
|
|
50
|
+
${icon}
|
|
51
|
+
<text x="${textX}" y="21" fill="#${textColor}" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="500" text-anchor="${textAnchor}">${escapedLabel}</text>
|
|
52
|
+
</svg>`;
|
|
119
53
|
}
|
|
120
|
-
|
|
121
|
-
const response = await fetch(
|
|
122
|
-
"https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuI6fAZ9hjp-Ek-_EeA.woff"
|
|
123
|
-
);
|
|
124
|
-
if (!response.ok) {
|
|
125
|
-
throw new Error(`Failed to load font: ${response.status} ${response.statusText}`);
|
|
126
|
-
}
|
|
127
|
-
return response.arrayBuffer();
|
|
128
|
-
}
|
|
129
|
-
export default defineEventHandler(async (event) => {
|
|
54
|
+
export default defineEventHandler((event) => {
|
|
130
55
|
const query = getQuery(event);
|
|
131
56
|
const ide = query.ide || "cursor";
|
|
132
57
|
const ideConfig = IDE_CONFIG[ide] || IDE_CONFIG.cursor;
|
|
@@ -138,17 +63,8 @@ export default defineEventHandler(async (event) => {
|
|
|
138
63
|
borderColor: query.borderColor || "404040",
|
|
139
64
|
showIcon: query.icon !== "false"
|
|
140
65
|
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return svg;
|
|
146
|
-
} catch {
|
|
147
|
-
setHeader(event, "Content-Type", "image/svg+xml");
|
|
148
|
-
setHeader(event, "Cache-Control", "no-cache");
|
|
149
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="140" height="32">
|
|
150
|
-
<rect width="140" height="32" fill="#171717" stroke="#404040"/>
|
|
151
|
-
<text x="70" y="20" fill="#fff" font-size="12" text-anchor="middle">${options.label}</text>
|
|
152
|
-
</svg>`;
|
|
153
|
-
}
|
|
66
|
+
const svg = generateBadgeSVG(options);
|
|
67
|
+
setHeader(event, "Content-Type", "image/svg+xml");
|
|
68
|
+
setHeader(event, "Cache-Control", "public, max-age=86400");
|
|
69
|
+
return svg;
|
|
154
70
|
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { defu } from "defu";
|
|
2
1
|
export const defaultMcpConfig = {
|
|
3
2
|
enabled: true,
|
|
4
3
|
route: "/mcp",
|
|
@@ -13,5 +12,15 @@ export const defaultMcpConfig = {
|
|
|
13
12
|
}
|
|
14
13
|
};
|
|
15
14
|
export function getMcpConfig(partial) {
|
|
16
|
-
|
|
15
|
+
if (!partial) return { ...defaultMcpConfig };
|
|
16
|
+
const sessions = partial.sessions ? { ...defaultMcpConfig.sessions, ...partial.sessions } : defaultMcpConfig.sessions;
|
|
17
|
+
return {
|
|
18
|
+
enabled: partial.enabled ?? defaultMcpConfig.enabled,
|
|
19
|
+
route: partial.route ?? defaultMcpConfig.route,
|
|
20
|
+
browserRedirect: partial.browserRedirect ?? defaultMcpConfig.browserRedirect,
|
|
21
|
+
name: partial.name ?? defaultMcpConfig.name,
|
|
22
|
+
version: partial.version ?? defaultMcpConfig.version,
|
|
23
|
+
dir: partial.dir ?? defaultMcpConfig.dir,
|
|
24
|
+
sessions
|
|
25
|
+
};
|
|
17
26
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cache duration strings
|
|
2
|
+
* Cache duration strings (e.g. '1h', '30m', '2 days')
|
|
3
3
|
*/
|
|
4
4
|
export type MsCacheDuration = '1s' | '5s' | '10s' | '15s' | '30s' | '45s' | '1m' | '2m' | '5m' | '10m' | '15m' | '30m' | '45m' | '1h' | '2h' | '3h' | '4h' | '6h' | '8h' | '12h' | '24h' | '1d' | '2d' | '3d' | '7d' | '14d' | '30d' | '1w' | '2w' | '4w' | '1 second' | '1 minute' | '1 hour' | '1 day' | '1 week' | '2 seconds' | '5 seconds' | '10 seconds' | '30 seconds' | '2 minutes' | '5 minutes' | '10 minutes' | '15 minutes' | '30 minutes' | '2 hours' | '3 hours' | '6 hours' | '12 hours' | '24 hours' | '2 days' | '3 days' | '7 days' | '14 days' | '30 days' | '2 weeks' | '4 weeks' | (string & Record<never, never>);
|
|
5
5
|
/**
|
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
import { defineCachedFunction } from "nitropack/runtime";
|
|
2
|
-
|
|
2
|
+
const DURATION_UNITS = {
|
|
3
|
+
ms: 1,
|
|
4
|
+
millisecond: 1,
|
|
5
|
+
milliseconds: 1,
|
|
6
|
+
s: 1e3,
|
|
7
|
+
sec: 1e3,
|
|
8
|
+
second: 1e3,
|
|
9
|
+
seconds: 1e3,
|
|
10
|
+
m: 6e4,
|
|
11
|
+
min: 6e4,
|
|
12
|
+
minute: 6e4,
|
|
13
|
+
minutes: 6e4,
|
|
14
|
+
h: 36e5,
|
|
15
|
+
hr: 36e5,
|
|
16
|
+
hour: 36e5,
|
|
17
|
+
hours: 36e5,
|
|
18
|
+
d: 864e5,
|
|
19
|
+
day: 864e5,
|
|
20
|
+
days: 864e5,
|
|
21
|
+
w: 6048e5,
|
|
22
|
+
week: 6048e5,
|
|
23
|
+
weeks: 6048e5
|
|
24
|
+
};
|
|
25
|
+
function parseDurationToMs(str) {
|
|
26
|
+
const match = str.trim().match(/^(\d+)\s*([a-z]+)$/i);
|
|
27
|
+
if (!match) return void 0;
|
|
28
|
+
const value = Number(match[1]);
|
|
29
|
+
const unit = match[2].toLowerCase();
|
|
30
|
+
const multiplier = DURATION_UNITS[unit];
|
|
31
|
+
if (multiplier === void 0) return void 0;
|
|
32
|
+
return value * multiplier;
|
|
33
|
+
}
|
|
3
34
|
export function parseCacheDuration(duration) {
|
|
4
35
|
if (typeof duration === "number") {
|
|
5
36
|
return duration;
|
|
6
37
|
}
|
|
7
|
-
const parsed =
|
|
38
|
+
const parsed = parseDurationToMs(duration);
|
|
8
39
|
if (parsed === void 0) {
|
|
9
40
|
throw new Error(`Invalid cache duration: ${duration}`);
|
|
10
41
|
}
|
|
@@ -29,7 +29,7 @@ import type { McpPromptDefinition } from './prompts.js';
|
|
|
29
29
|
* }
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
export type McpMiddleware = (event: H3Event, next: () => Promise<Response
|
|
32
|
+
export type McpMiddleware = (event: H3Event, next: () => Promise<Response>) => Promise<Response | void> | Response | void;
|
|
33
33
|
/**
|
|
34
34
|
* Options for defining a custom MCP handler
|
|
35
35
|
* @see https://mcp-toolkit.nuxt.dev/core-concepts/handlers
|
|
@@ -70,9 +70,9 @@ export interface McpHandlerOptions {
|
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
72
|
middleware?: McpMiddleware;
|
|
73
|
-
tools?: Array<McpToolDefinition<any, any
|
|
74
|
-
resources?: McpResourceDefinition[];
|
|
75
|
-
prompts?: McpPromptDefinition[];
|
|
73
|
+
tools?: Array<McpToolDefinition<any, any>> | ((event: H3Event) => Array<McpToolDefinition<any, any>> | Promise<Array<McpToolDefinition<any, any>>>);
|
|
74
|
+
resources?: McpResourceDefinition[] | ((event: H3Event) => McpResourceDefinition[] | Promise<McpResourceDefinition[]>);
|
|
75
|
+
prompts?: McpPromptDefinition[] | ((event: H3Event) => McpPromptDefinition[] | Promise<McpPromptDefinition[]>);
|
|
76
76
|
}
|
|
77
77
|
export interface McpHandlerDefinition extends Required<Omit<McpHandlerOptions, 'tools' | 'resources' | 'prompts' | 'middleware'>> {
|
|
78
78
|
tools: Array<McpToolDefinition<any, any>>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
1
2
|
import type { ZodRawShape } from 'zod';
|
|
2
3
|
import type { GetPromptResult, ServerRequest, ServerNotification } from '@modelcontextprotocol/sdk/types.js';
|
|
3
4
|
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
@@ -18,6 +19,12 @@ export interface McpPromptDefinition<Args extends ZodRawShape | undefined = unde
|
|
|
18
19
|
inputSchema?: Args;
|
|
19
20
|
_meta?: Record<string, unknown>;
|
|
20
21
|
handler: McpPromptCallback<Args>;
|
|
22
|
+
/**
|
|
23
|
+
* Guard that controls whether this prompt is registered for a given request.
|
|
24
|
+
* Receives the H3 event (with `event.context` populated by middleware) and
|
|
25
|
+
* returns `true` to include the prompt or `false` to hide it.
|
|
26
|
+
*/
|
|
27
|
+
enabled?: (event: H3Event) => boolean | Promise<boolean>;
|
|
21
28
|
}
|
|
22
29
|
/**
|
|
23
30
|
* Register a prompt from a McpPromptDefinition
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
1
2
|
import type { McpServer, ResourceTemplate, ReadResourceCallback, ReadResourceTemplateCallback, ResourceMetadata } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
3
|
import { type McpCacheOptions, type McpCache } from './cache.js';
|
|
3
4
|
export type McpResourceCacheOptions = McpCacheOptions<URL>;
|
|
@@ -34,6 +35,12 @@ export interface StandardMcpResourceDefinition {
|
|
|
34
35
|
* @see https://nitro.build/guide/cache#options
|
|
35
36
|
*/
|
|
36
37
|
cache?: McpResourceCache;
|
|
38
|
+
/**
|
|
39
|
+
* Guard that controls whether this resource is registered for a given request.
|
|
40
|
+
* Receives the H3 event (with `event.context` populated by middleware) and
|
|
41
|
+
* returns `true` to include the resource or `false` to hide it.
|
|
42
|
+
*/
|
|
43
|
+
enabled?: (event: H3Event) => boolean | Promise<boolean>;
|
|
37
44
|
}
|
|
38
45
|
/**
|
|
39
46
|
* Definition of a file-based MCP resource
|
|
@@ -61,6 +68,12 @@ export interface FileMcpResourceDefinition {
|
|
|
61
68
|
* @see https://nitro.build/guide/cache#options
|
|
62
69
|
*/
|
|
63
70
|
cache?: McpResourceCache;
|
|
71
|
+
/**
|
|
72
|
+
* Guard that controls whether this resource is registered for a given request.
|
|
73
|
+
* Receives the H3 event (with `event.context` populated by middleware) and
|
|
74
|
+
* returns `true` to include the resource or `false` to hide it.
|
|
75
|
+
*/
|
|
76
|
+
enabled?: (event: H3Event) => boolean | Promise<boolean>;
|
|
64
77
|
}
|
|
65
78
|
/**
|
|
66
79
|
* Definition of an MCP resource matching the SDK's registerResource signature
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
1
2
|
import type { ZodRawShape } from 'zod';
|
|
2
3
|
import type { CallToolResult, ServerRequest, ServerNotification } from '@modelcontextprotocol/sdk/types.js';
|
|
3
4
|
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
@@ -51,6 +52,14 @@ export interface McpToolDefinition<InputSchema extends ZodRawShape | undefined =
|
|
|
51
52
|
* @see https://nitro.build/guide/cache#options
|
|
52
53
|
*/
|
|
53
54
|
cache?: McpToolCache<InputSchema extends ZodRawShape ? ShapeOutput<InputSchema> : undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Guard that controls whether this tool is registered for a given request.
|
|
57
|
+
* Receives the H3 event (with `event.context` populated by middleware) and
|
|
58
|
+
* returns `true` to include the tool or `false` to hide it.
|
|
59
|
+
*
|
|
60
|
+
* Evaluated after middleware runs, so authentication context is available.
|
|
61
|
+
*/
|
|
62
|
+
enabled?: (event: H3Event) => boolean | Promise<boolean>;
|
|
54
63
|
}
|
|
55
64
|
/**
|
|
56
65
|
* Register a tool from a McpToolDefinition
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
function kebabCase(str) {
|
|
2
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").toLowerCase();
|
|
3
|
+
}
|
|
4
|
+
function titleCase(str) {
|
|
5
|
+
return str.replace(/[-_]+/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
6
|
+
}
|
|
2
7
|
export function enrichNameTitle(options) {
|
|
3
8
|
const { name, title, _meta, type } = options;
|
|
4
9
|
const filename = _meta?.filename;
|
|
@@ -27,14 +27,16 @@ export default createMcpHandler((event) => {
|
|
|
27
27
|
}
|
|
28
28
|
const defaultHandlerDef = defaultHandler;
|
|
29
29
|
if (defaultHandlerDef) {
|
|
30
|
+
const globalTools = tools;
|
|
31
|
+
const globalResources = resources;
|
|
32
|
+
const globalPrompts = prompts;
|
|
30
33
|
return {
|
|
31
34
|
name: defaultHandlerDef.name ?? config.name ?? "MCP Server",
|
|
32
35
|
version: defaultHandlerDef.version ?? config.version,
|
|
33
36
|
browserRedirect: defaultHandlerDef.browserRedirect ?? config.browserRedirect,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
prompts: defaultHandlerDef.prompts ?? prompts,
|
|
37
|
+
tools: defaultHandlerDef.tools ?? globalTools,
|
|
38
|
+
resources: defaultHandlerDef.resources ?? globalResources,
|
|
39
|
+
prompts: defaultHandlerDef.prompts ?? globalPrompts,
|
|
38
40
|
middleware: defaultHandlerDef.middleware
|
|
39
41
|
};
|
|
40
42
|
}
|
|
@@ -8,6 +8,7 @@ const fallbackCtx = {
|
|
|
8
8
|
};
|
|
9
9
|
export default createMcpTransportHandler(async (createServer, event) => {
|
|
10
10
|
const server = createServer();
|
|
11
|
+
event.context._mcpServer = server;
|
|
11
12
|
const { createMcpHandler } = await import("agents/mcp");
|
|
12
13
|
const handler = createMcpHandler(server, {
|
|
13
14
|
route: ""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
2
|
+
import { toWebRequest, getHeader } from "h3";
|
|
3
|
+
import { useStorage } from "nitropack/runtime";
|
|
4
4
|
import config from "#nuxt-mcp-toolkit/config.mjs";
|
|
5
5
|
import { createMcpTransportHandler } from "./types.js";
|
|
6
6
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -14,6 +14,7 @@ function ensureCleanup(maxDuration) {
|
|
|
14
14
|
session.transport.close();
|
|
15
15
|
session.server.close();
|
|
16
16
|
sessions.delete(id);
|
|
17
|
+
useStorage(`mcp:sessions:${id}`).clear();
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
if (sessions.size === 0 && cleanupInterval) {
|
|
@@ -25,41 +26,41 @@ function ensureCleanup(maxDuration) {
|
|
|
25
26
|
export default createMcpTransportHandler(async (createServer, event) => {
|
|
26
27
|
const sessionsConfig = config.sessions;
|
|
27
28
|
const sessionsEnabled = sessionsConfig?.enabled ?? false;
|
|
29
|
+
const request = toWebRequest(event);
|
|
28
30
|
if (!sessionsEnabled) {
|
|
29
31
|
const server2 = createServer();
|
|
30
|
-
|
|
32
|
+
event.context._mcpServer = server2;
|
|
33
|
+
const transport2 = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
31
34
|
event.node.res.on("close", () => {
|
|
32
35
|
transport2.close();
|
|
33
36
|
server2.close();
|
|
34
37
|
});
|
|
35
38
|
await server2.connect(transport2);
|
|
36
|
-
|
|
37
|
-
await transport2.handleRequest(event.node.req, event.node.res, body2);
|
|
38
|
-
return;
|
|
39
|
+
return transport2.handleRequest(request);
|
|
39
40
|
}
|
|
40
41
|
const maxDuration = sessionsConfig?.maxDuration ?? 30 * 60 * 1e3;
|
|
41
|
-
const method = getMethod(event);
|
|
42
42
|
const sessionId = getHeader(event, "mcp-session-id");
|
|
43
43
|
if (sessionId) {
|
|
44
44
|
const session = sessions.get(sessionId);
|
|
45
45
|
if (!session) {
|
|
46
|
-
|
|
47
|
-
event.node.res.end(JSON.stringify({
|
|
46
|
+
return new Response(JSON.stringify({
|
|
48
47
|
jsonrpc: "2.0",
|
|
49
48
|
error: { code: -32001, message: "Session not found" },
|
|
50
49
|
id: null
|
|
51
|
-
})
|
|
52
|
-
|
|
50
|
+
}), {
|
|
51
|
+
status: 404,
|
|
52
|
+
headers: { "Content-Type": "application/json" }
|
|
53
|
+
});
|
|
53
54
|
}
|
|
54
55
|
session.lastAccessed = Date.now();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return;
|
|
56
|
+
event.context._mcpServer = session.server;
|
|
57
|
+
return session.transport.handleRequest(request);
|
|
58
58
|
}
|
|
59
59
|
const server = createServer();
|
|
60
|
+
event.context._mcpServer = server;
|
|
60
61
|
let sessionStored = false;
|
|
61
|
-
const transport = new
|
|
62
|
-
sessionIdGenerator: () => randomUUID(),
|
|
62
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
63
|
+
sessionIdGenerator: () => globalThis.crypto.randomUUID(),
|
|
63
64
|
onsessioninitialized: (id) => {
|
|
64
65
|
sessionStored = true;
|
|
65
66
|
sessions.set(id, { server, transport, lastAccessed: Date.now() });
|
|
@@ -70,14 +71,17 @@ export default createMcpTransportHandler(async (createServer, event) => {
|
|
|
70
71
|
const sid = transport.sessionId;
|
|
71
72
|
if (sid && sessions.has(sid)) {
|
|
72
73
|
sessions.delete(sid);
|
|
74
|
+
useStorage(`mcp:sessions:${sid}`).clear();
|
|
73
75
|
}
|
|
74
76
|
server.close();
|
|
75
77
|
};
|
|
76
78
|
await server.connect(transport);
|
|
77
|
-
const
|
|
78
|
-
await transport.handleRequest(event.node.req, event.node.res, body);
|
|
79
|
+
const response = await transport.handleRequest(request);
|
|
79
80
|
if (!sessionStored) {
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
event.node.res.on("close", () => {
|
|
82
|
+
transport.close();
|
|
83
|
+
server.close();
|
|
84
|
+
});
|
|
82
85
|
}
|
|
86
|
+
return response;
|
|
83
87
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { H3Event } from 'h3';
|
|
3
|
-
export type McpTransportHandler = (createServer: () => McpServer, event: H3Event) => Promise<Response
|
|
3
|
+
export type McpTransportHandler = (createServer: () => McpServer, event: H3Event) => Promise<Response>;
|
|
4
4
|
export declare const createMcpTransportHandler: (handler: McpTransportHandler) => McpTransportHandler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
export interface McpServerHelper {
|
|
3
|
+
/** Register a new tool mid-session. The client is notified automatically. */
|
|
4
|
+
registerTool: McpServer['registerTool'];
|
|
5
|
+
/** Register a new prompt mid-session. The client is notified automatically. */
|
|
6
|
+
registerPrompt: McpServer['registerPrompt'];
|
|
7
|
+
/** Register a new resource mid-session. The client is notified automatically. */
|
|
8
|
+
registerResource: McpServer['registerResource'];
|
|
9
|
+
/** Remove a dynamically registered tool by name. Returns `true` if found. */
|
|
10
|
+
removeTool(name: string): boolean;
|
|
11
|
+
/** Remove a dynamically registered prompt by name. Returns `true` if found. */
|
|
12
|
+
removePrompt(name: string): boolean;
|
|
13
|
+
/** Remove a dynamically registered resource by name. Returns `true` if found. */
|
|
14
|
+
removeResource(name: string): boolean;
|
|
15
|
+
/** The underlying `McpServer` instance for advanced SDK operations. */
|
|
16
|
+
server: McpServer;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns a helper to mutate the MCP server mid-session.
|
|
20
|
+
*
|
|
21
|
+
* Use inside tool, resource, or prompt handlers to register, remove,
|
|
22
|
+
* or update definitions while a session is active. The SDK automatically
|
|
23
|
+
* sends `list_changed` notifications to the client.
|
|
24
|
+
*
|
|
25
|
+
* Requires `nitro.experimental.asyncContext: true` in your Nuxt config.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useMcpServer(): McpServerHelper;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useEvent } from "nitropack/runtime";
|
|
2
|
+
const registrations = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
function getRegistrations(server) {
|
|
4
|
+
let reg = registrations.get(server);
|
|
5
|
+
if (!reg) {
|
|
6
|
+
reg = { tools: /* @__PURE__ */ new Map(), prompts: /* @__PURE__ */ new Map(), resources: /* @__PURE__ */ new Map() };
|
|
7
|
+
registrations.set(server, reg);
|
|
8
|
+
}
|
|
9
|
+
return reg;
|
|
10
|
+
}
|
|
11
|
+
function removeByName(map, name) {
|
|
12
|
+
const handle = map.get(name);
|
|
13
|
+
if (!handle) return false;
|
|
14
|
+
handle.remove();
|
|
15
|
+
map.delete(name);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
function wrapRegister(server, method, map) {
|
|
19
|
+
const fn = server[method].bind(server);
|
|
20
|
+
return (...args) => {
|
|
21
|
+
const handle = fn(...args);
|
|
22
|
+
map.set(args[0], handle);
|
|
23
|
+
return handle;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function useMcpServer() {
|
|
27
|
+
const event = useEvent();
|
|
28
|
+
const server = event.context._mcpServer;
|
|
29
|
+
if (!server) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"No MCP server instance available. Ensure this is called within an MCP tool/resource/prompt handler and `nitro.experimental.asyncContext` is true."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const reg = getRegistrations(server);
|
|
35
|
+
return {
|
|
36
|
+
registerTool: wrapRegister(server, "registerTool", reg.tools),
|
|
37
|
+
registerPrompt: wrapRegister(server, "registerPrompt", reg.prompts),
|
|
38
|
+
registerResource: wrapRegister(server, "registerResource", reg.resources),
|
|
39
|
+
removeTool: (name) => removeByName(reg.tools, name),
|
|
40
|
+
removePrompt: (name) => removeByName(reg.prompts, name),
|
|
41
|
+
removeResource: (name) => removeByName(reg.resources, name),
|
|
42
|
+
server
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Storage } from 'unstorage';
|
|
2
|
+
export interface McpSessionStore<T = Record<string, unknown>> {
|
|
3
|
+
get<K extends keyof T & string>(key: K): Promise<T[K] | null>;
|
|
4
|
+
set<K extends keyof T & string>(key: K, value: T[K]): Promise<void>;
|
|
5
|
+
remove<K extends keyof T & string>(key: K): Promise<void>;
|
|
6
|
+
has<K extends keyof T & string>(key: K): Promise<boolean>;
|
|
7
|
+
keys(): Promise<string[]>;
|
|
8
|
+
clear(): Promise<void>;
|
|
9
|
+
/** Access the underlying unstorage instance */
|
|
10
|
+
storage: Storage;
|
|
11
|
+
}
|
|
12
|
+
export declare function useMcpSession<T = Record<string, unknown>>(): McpSessionStore<T>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useStorage, useEvent } from "nitropack/runtime";
|
|
2
|
+
import { getHeader } from "h3";
|
|
3
|
+
export function useMcpSession() {
|
|
4
|
+
const event = useEvent();
|
|
5
|
+
const sessionId = getHeader(event, "mcp-session-id");
|
|
6
|
+
if (!sessionId) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"No active MCP session. Ensure `mcp.sessions` is enabled and `nitro.experimental.asyncContext` is true."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
const storage = useStorage(`mcp:sessions:${sessionId}`);
|
|
12
|
+
return {
|
|
13
|
+
get: (key) => storage.getItem(key),
|
|
14
|
+
set: (key, value) => storage.setItem(key, value),
|
|
15
|
+
remove: (key) => storage.removeItem(key),
|
|
16
|
+
has: (key) => storage.hasItem(key),
|
|
17
|
+
keys: () => storage.getKeys(),
|
|
18
|
+
clear: () => storage.clear(),
|
|
19
|
+
storage
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -6,15 +6,24 @@ import type { McpResourceDefinition } from './definitions/resources.js';
|
|
|
6
6
|
import type { McpToolDefinition } from './definitions/tools.js';
|
|
7
7
|
export type { McpTransportHandler } from './providers/types.js';
|
|
8
8
|
export { createMcpTransportHandler } from './providers/types.js';
|
|
9
|
+
type MaybeDynamic<T> = T | ((event: H3Event) => T | Promise<T>);
|
|
10
|
+
type MaybeDynamicTools = MaybeDynamic<Array<McpToolDefinition<any, any>>>;
|
|
9
11
|
export interface ResolvedMcpConfig {
|
|
10
12
|
name: string;
|
|
11
13
|
version: string;
|
|
12
14
|
browserRedirect: string;
|
|
13
|
-
tools?:
|
|
14
|
-
resources?: McpResourceDefinition[]
|
|
15
|
-
prompts?: McpPromptDefinition[]
|
|
15
|
+
tools?: MaybeDynamicTools;
|
|
16
|
+
resources?: MaybeDynamic<McpResourceDefinition[]>;
|
|
17
|
+
prompts?: MaybeDynamic<McpPromptDefinition[]>;
|
|
16
18
|
middleware?: McpMiddleware;
|
|
17
19
|
}
|
|
20
|
+
interface StaticMcpConfig {
|
|
21
|
+
name: string;
|
|
22
|
+
version: string;
|
|
23
|
+
tools: McpToolDefinition[];
|
|
24
|
+
resources: McpResourceDefinition[];
|
|
25
|
+
prompts: McpPromptDefinition[];
|
|
26
|
+
}
|
|
18
27
|
export type CreateMcpHandlerConfig = ResolvedMcpConfig | ((event: H3Event) => ResolvedMcpConfig);
|
|
19
|
-
export declare function createMcpServer(config:
|
|
28
|
+
export declare function createMcpServer(config: StaticMcpConfig): McpServer;
|
|
20
29
|
export declare function createMcpHandler(config: CreateMcpHandlerConfig): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
|
|
@@ -8,14 +8,35 @@ export { createMcpTransportHandler } from "./providers/types.js";
|
|
|
8
8
|
function resolveConfig(config, event) {
|
|
9
9
|
return typeof config === "function" ? config(event) : config;
|
|
10
10
|
}
|
|
11
|
+
async function filterByEnabled(definitions, event) {
|
|
12
|
+
const results = await Promise.all(
|
|
13
|
+
definitions.map(async (def) => {
|
|
14
|
+
if (!def.enabled) return true;
|
|
15
|
+
return def.enabled(event);
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
return definitions.filter((_, i) => results[i]);
|
|
19
|
+
}
|
|
20
|
+
async function resolveDynamicDefinitions(config, event) {
|
|
21
|
+
const tools = typeof config.tools === "function" ? await config.tools(event) : config.tools || [];
|
|
22
|
+
const resources = typeof config.resources === "function" ? await config.resources(event) : config.resources || [];
|
|
23
|
+
const prompts = typeof config.prompts === "function" ? await config.prompts(event) : config.prompts || [];
|
|
24
|
+
return {
|
|
25
|
+
name: config.name,
|
|
26
|
+
version: config.version,
|
|
27
|
+
tools: await filterByEnabled(tools, event),
|
|
28
|
+
resources: await filterByEnabled(resources, event),
|
|
29
|
+
prompts: await filterByEnabled(prompts, event)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
11
32
|
function registerEmptyDefinitionFallbacks(server, config) {
|
|
12
|
-
if (!config.tools
|
|
33
|
+
if (!config.tools.length) {
|
|
13
34
|
server.registerTool("__init__", {}, async () => ({ content: [] })).remove();
|
|
14
35
|
}
|
|
15
|
-
if (!config.resources
|
|
36
|
+
if (!config.resources.length) {
|
|
16
37
|
server.registerResource("__init__", "noop://init", {}, async () => ({ contents: [] })).remove();
|
|
17
38
|
}
|
|
18
|
-
if (!config.prompts
|
|
39
|
+
if (!config.prompts.length) {
|
|
19
40
|
server.registerPrompt("__init__", {}, async () => ({ messages: [] })).remove();
|
|
20
41
|
}
|
|
21
42
|
}
|
|
@@ -24,13 +45,13 @@ export function createMcpServer(config) {
|
|
|
24
45
|
name: config.name,
|
|
25
46
|
version: config.version
|
|
26
47
|
});
|
|
27
|
-
for (const tool of config.tools
|
|
48
|
+
for (const tool of config.tools) {
|
|
28
49
|
registerToolFromDefinition(server, tool);
|
|
29
50
|
}
|
|
30
|
-
for (const resource of config.resources
|
|
51
|
+
for (const resource of config.resources) {
|
|
31
52
|
registerResourceFromDefinition(server, resource);
|
|
32
53
|
}
|
|
33
|
-
for (const prompt of config.prompts
|
|
54
|
+
for (const prompt of config.prompts) {
|
|
34
55
|
registerPromptFromDefinition(server, prompt);
|
|
35
56
|
}
|
|
36
57
|
registerEmptyDefinitionFallbacks(server, config);
|
|
@@ -43,7 +64,8 @@ export function createMcpHandler(config) {
|
|
|
43
64
|
return sendRedirect(event, resolvedConfig.browserRedirect);
|
|
44
65
|
}
|
|
45
66
|
const handler = async () => {
|
|
46
|
-
|
|
67
|
+
const staticConfig = await resolveDynamicDefinitions(resolvedConfig, event);
|
|
68
|
+
return handleMcpRequest(() => createMcpServer(staticConfig), event);
|
|
47
69
|
};
|
|
48
70
|
if (resolvedConfig.middleware) {
|
|
49
71
|
let nextCalled = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtjs/mcp-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Create MCP servers directly in your Nuxt application. Define tools, resources, and prompts with a simple and intuitive API.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,11 +41,6 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
43
43
|
"@nuxt/kit": "^4.4.2",
|
|
44
|
-
"defu": "^6.1.4",
|
|
45
|
-
"ms": "^2.1.3",
|
|
46
|
-
"pathe": "^2.0.3",
|
|
47
|
-
"satori": "^0.25.0",
|
|
48
|
-
"scule": "^1.3.0",
|
|
49
44
|
"tinyglobby": "^0.2.15"
|
|
50
45
|
},
|
|
51
46
|
"peerDependencies": {
|