@outfitter/mcp 0.2.0 → 0.4.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/README.md +179 -5
- package/dist/actions.d.ts +4 -0
- package/dist/actions.js +11 -0
- package/dist/core-tools.d.ts +4 -0
- package/dist/core-tools.js +13 -0
- package/dist/index.d.ts +140 -28
- package/dist/index.js +158 -51
- package/dist/logging.d.ts +2 -0
- package/dist/logging.js +9 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.js +7 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +18 -0
- package/dist/shared/@outfitter/mcp-2vqyt1fj.d.ts +104 -0
- package/dist/shared/@outfitter/mcp-9m5hs2z0.js +49 -0
- package/dist/shared/@outfitter/mcp-9whem1wr.js +28 -0
- package/dist/shared/@outfitter/mcp-a0cgfsnw.d.ts +8 -0
- package/dist/shared/@outfitter/mcp-cqpyer9m.d.ts +27 -0
- package/dist/shared/@outfitter/mcp-dwd800vf.d.ts +96 -0
- package/dist/shared/@outfitter/mcp-f91wbr49.js +341 -0
- package/dist/shared/@outfitter/mcp-fjtxsa0x.js +36 -0
- package/dist/shared/@outfitter/mcp-h2twz77x.d.ts +699 -0
- package/dist/shared/@outfitter/mcp-jk0ka9hw.d.ts +30 -0
- package/dist/shared/@outfitter/mcp-k8r6kefr.js +538 -0
- package/dist/shared/@outfitter/mcp-mzky3ck8.js +165 -0
- package/dist/shared/@outfitter/mcp-s80bqcsb.d.ts +60 -0
- package/dist/shared/@outfitter/mcp-zv3ej45k.js +143 -0
- package/dist/transport.d.ts +4 -0
- package/dist/transport.js +13 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.js +13 -0
- package/package.json +43 -7
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { McpServer } from "./mcp-h2twz77x";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
|
|
4
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types";
|
|
5
|
+
type McpToolResponse = CallToolResult;
|
|
6
|
+
/**
|
|
7
|
+
* Wrap a handler success value into an MCP CallToolResult.
|
|
8
|
+
*
|
|
9
|
+
* If the value is already a valid McpToolResponse (has a `content` array),
|
|
10
|
+
* it is returned as-is. Otherwise it is wrapped in a text content block.
|
|
11
|
+
* Plain objects are also attached as `structuredContent` for SDK clients
|
|
12
|
+
* that support structured output.
|
|
13
|
+
*/
|
|
14
|
+
declare function wrapToolResult(value: unknown): McpToolResponse;
|
|
15
|
+
/**
|
|
16
|
+
* Wrap an error into an MCP CallToolResult with `isError: true`.
|
|
17
|
+
*
|
|
18
|
+
* Serializes the error (preserving `_tag`, `message`, `code`, `context` if
|
|
19
|
+
* present) and wraps it as a text content block.
|
|
20
|
+
*/
|
|
21
|
+
declare function wrapToolError(error: unknown): McpToolResponse;
|
|
22
|
+
/**
|
|
23
|
+
* Create an MCP SDK server from an Outfitter MCP server.
|
|
24
|
+
*/
|
|
25
|
+
declare function createSdkServer(server: McpServer): Server;
|
|
26
|
+
/**
|
|
27
|
+
* Connect an MCP server over stdio transport.
|
|
28
|
+
*/
|
|
29
|
+
declare function connectStdio(server: McpServer, transport?: StdioServerTransport): Promise<Server>;
|
|
30
|
+
export { McpToolResponse, wrapToolResult, wrapToolError, createSdkServer, connectStdio };
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
shouldEmitLog
|
|
4
|
+
} from "./mcp-fjtxsa0x.js";
|
|
5
|
+
import {
|
|
6
|
+
McpError
|
|
7
|
+
} from "./mcp-9m5hs2z0.js";
|
|
8
|
+
import {
|
|
9
|
+
zodToJsonSchema
|
|
10
|
+
} from "./mcp-f91wbr49.js";
|
|
11
|
+
|
|
12
|
+
// packages/mcp/src/server.ts
|
|
13
|
+
import { getEnvironment, getEnvironmentDefaults } from "@outfitter/config";
|
|
14
|
+
import { generateRequestId, Result } from "@outfitter/contracts";
|
|
15
|
+
import {
|
|
16
|
+
createOutfitterLoggerFactory,
|
|
17
|
+
createPrettyFormatter
|
|
18
|
+
} from "@outfitter/logging";
|
|
19
|
+
var VALID_MCP_LOG_LEVELS = new Set([
|
|
20
|
+
"debug",
|
|
21
|
+
"info",
|
|
22
|
+
"notice",
|
|
23
|
+
"warning",
|
|
24
|
+
"error",
|
|
25
|
+
"critical",
|
|
26
|
+
"alert",
|
|
27
|
+
"emergency"
|
|
28
|
+
]);
|
|
29
|
+
var DEFAULTS_TO_MCP = {
|
|
30
|
+
debug: "debug",
|
|
31
|
+
info: "info",
|
|
32
|
+
warn: "warning",
|
|
33
|
+
error: "error"
|
|
34
|
+
};
|
|
35
|
+
function createDefaultMcpSink() {
|
|
36
|
+
const formatter = createPrettyFormatter({ colors: false });
|
|
37
|
+
return {
|
|
38
|
+
formatter,
|
|
39
|
+
write(record, formatted) {
|
|
40
|
+
const serialized = formatted ?? formatter.format(record);
|
|
41
|
+
const line = serialized.endsWith(`
|
|
42
|
+
`) ? serialized : `${serialized}
|
|
43
|
+
`;
|
|
44
|
+
if (typeof process !== "undefined" && process.stderr?.write) {
|
|
45
|
+
process.stderr.write(line);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function resolveDefaultLogLevel(options) {
|
|
51
|
+
const envLogLevel = process.env["OUTFITTER_LOG_LEVEL"];
|
|
52
|
+
if (envLogLevel !== undefined && VALID_MCP_LOG_LEVELS.has(envLogLevel)) {
|
|
53
|
+
return envLogLevel;
|
|
54
|
+
}
|
|
55
|
+
if (options.defaultLogLevel !== undefined && (options.defaultLogLevel === null || VALID_MCP_LOG_LEVELS.has(options.defaultLogLevel))) {
|
|
56
|
+
return options.defaultLogLevel;
|
|
57
|
+
}
|
|
58
|
+
const env = getEnvironment();
|
|
59
|
+
const defaults = getEnvironmentDefaults(env);
|
|
60
|
+
if (defaults.logLevel !== null) {
|
|
61
|
+
const mapped = DEFAULTS_TO_MCP[defaults.logLevel];
|
|
62
|
+
if (mapped !== undefined) {
|
|
63
|
+
return mapped;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function createMcpServer(options) {
|
|
69
|
+
const { name, version, logger: providedLogger } = options;
|
|
70
|
+
let loggerFactory = null;
|
|
71
|
+
const logger = providedLogger ?? (() => {
|
|
72
|
+
loggerFactory = createOutfitterLoggerFactory({
|
|
73
|
+
defaults: { sinks: [createDefaultMcpSink()] }
|
|
74
|
+
});
|
|
75
|
+
return loggerFactory.createLogger({
|
|
76
|
+
name: "mcp",
|
|
77
|
+
context: { serverName: name, serverVersion: version, surface: "mcp" }
|
|
78
|
+
});
|
|
79
|
+
})();
|
|
80
|
+
const tools = new Map;
|
|
81
|
+
const resources = new Map;
|
|
82
|
+
const resourceTemplates = new Map;
|
|
83
|
+
const prompts = new Map;
|
|
84
|
+
let sdkServer = null;
|
|
85
|
+
const subscriptions = new Set;
|
|
86
|
+
let clientLogLevel = resolveDefaultLogLevel(options);
|
|
87
|
+
function createHandlerContext(toolName, requestId, signal, progressToken) {
|
|
88
|
+
const ctx = {
|
|
89
|
+
requestId,
|
|
90
|
+
logger: logger.child({ tool: toolName, requestId }),
|
|
91
|
+
cwd: process.cwd(),
|
|
92
|
+
env: process.env
|
|
93
|
+
};
|
|
94
|
+
if (signal !== undefined) {
|
|
95
|
+
ctx.signal = signal;
|
|
96
|
+
}
|
|
97
|
+
if (progressToken !== undefined && sdkServer) {
|
|
98
|
+
ctx.progress = {
|
|
99
|
+
report(progress, total, message) {
|
|
100
|
+
sdkServer?.notification?.({
|
|
101
|
+
method: "notifications/progress",
|
|
102
|
+
params: {
|
|
103
|
+
progressToken,
|
|
104
|
+
progress,
|
|
105
|
+
...total !== undefined ? { total } : {},
|
|
106
|
+
...message ? { message } : {}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return ctx;
|
|
113
|
+
}
|
|
114
|
+
function translateError(error) {
|
|
115
|
+
const codeMap = {
|
|
116
|
+
validation: -32602,
|
|
117
|
+
not_found: -32601,
|
|
118
|
+
permission: -32600,
|
|
119
|
+
internal: -32603,
|
|
120
|
+
timeout: -32603,
|
|
121
|
+
network: -32603,
|
|
122
|
+
rate_limit: -32603,
|
|
123
|
+
auth: -32600,
|
|
124
|
+
conflict: -32603,
|
|
125
|
+
cancelled: -32603
|
|
126
|
+
};
|
|
127
|
+
const code = codeMap[error.category] ?? -32603;
|
|
128
|
+
return new McpError({
|
|
129
|
+
message: error.message,
|
|
130
|
+
code,
|
|
131
|
+
context: {
|
|
132
|
+
originalTag: error._tag,
|
|
133
|
+
category: error.category
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const server = {
|
|
138
|
+
name,
|
|
139
|
+
version,
|
|
140
|
+
registerTool(tool) {
|
|
141
|
+
logger.debug("Registering tool", { name: tool.name });
|
|
142
|
+
const description = tool.description?.trim() ?? "";
|
|
143
|
+
if (description.length < 8) {
|
|
144
|
+
logger.warn("Tool description may be too short for search discovery", {
|
|
145
|
+
name: tool.name,
|
|
146
|
+
description
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const jsonSchema = zodToJsonSchema(tool.inputSchema);
|
|
150
|
+
const handler = (input, ctx) => tool.handler(input, ctx);
|
|
151
|
+
const deferLoading = tool.deferLoading ?? true;
|
|
152
|
+
const stored = {
|
|
153
|
+
name: tool.name,
|
|
154
|
+
description,
|
|
155
|
+
inputSchema: jsonSchema,
|
|
156
|
+
deferLoading,
|
|
157
|
+
handler,
|
|
158
|
+
zodSchema: tool.inputSchema
|
|
159
|
+
};
|
|
160
|
+
if (tool.annotations !== undefined) {
|
|
161
|
+
stored.annotations = tool.annotations;
|
|
162
|
+
}
|
|
163
|
+
tools.set(tool.name, stored);
|
|
164
|
+
logger.info("Tool registered", { name: tool.name });
|
|
165
|
+
if (sdkServer) {
|
|
166
|
+
sdkServer.sendToolListChanged?.();
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
registerResource(resource) {
|
|
170
|
+
logger.debug("Registering resource", {
|
|
171
|
+
uri: resource.uri,
|
|
172
|
+
name: resource.name
|
|
173
|
+
});
|
|
174
|
+
resources.set(resource.uri, resource);
|
|
175
|
+
logger.info("Resource registered", { uri: resource.uri });
|
|
176
|
+
if (sdkServer) {
|
|
177
|
+
sdkServer.sendResourceListChanged?.();
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
registerResourceTemplate(template) {
|
|
181
|
+
logger.debug("Registering resource template", {
|
|
182
|
+
uriTemplate: template.uriTemplate,
|
|
183
|
+
name: template.name
|
|
184
|
+
});
|
|
185
|
+
resourceTemplates.set(template.uriTemplate, template);
|
|
186
|
+
logger.info("Resource template registered", {
|
|
187
|
+
uriTemplate: template.uriTemplate
|
|
188
|
+
});
|
|
189
|
+
if (sdkServer) {
|
|
190
|
+
sdkServer.sendResourceListChanged?.();
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
getTools() {
|
|
194
|
+
return Array.from(tools.values()).map((tool) => ({
|
|
195
|
+
name: tool.name,
|
|
196
|
+
description: tool.description,
|
|
197
|
+
inputSchema: tool.inputSchema,
|
|
198
|
+
defer_loading: tool.deferLoading,
|
|
199
|
+
...tool.annotations ? { annotations: tool.annotations } : {}
|
|
200
|
+
}));
|
|
201
|
+
},
|
|
202
|
+
getResources() {
|
|
203
|
+
return Array.from(resources.values());
|
|
204
|
+
},
|
|
205
|
+
getResourceTemplates() {
|
|
206
|
+
return Array.from(resourceTemplates.values());
|
|
207
|
+
},
|
|
208
|
+
async complete(ref, argumentName, value) {
|
|
209
|
+
if (ref.type === "ref/prompt") {
|
|
210
|
+
const prompt = prompts.get(ref.name);
|
|
211
|
+
if (!prompt) {
|
|
212
|
+
return Result.err(new McpError({
|
|
213
|
+
message: `Prompt not found: ${ref.name}`,
|
|
214
|
+
code: -32601,
|
|
215
|
+
context: { prompt: ref.name }
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
const arg = prompt.arguments.find((a) => a.name === argumentName);
|
|
219
|
+
if (!arg?.complete) {
|
|
220
|
+
return Result.ok({ values: [] });
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
const result = await arg.complete(value);
|
|
224
|
+
return Result.ok(result);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return Result.err(new McpError({
|
|
227
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
228
|
+
code: -32603,
|
|
229
|
+
context: {
|
|
230
|
+
prompt: ref.name,
|
|
231
|
+
argument: argumentName,
|
|
232
|
+
thrown: true
|
|
233
|
+
}
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (ref.type === "ref/resource") {
|
|
238
|
+
const template = resourceTemplates.get(ref.uri);
|
|
239
|
+
if (!template) {
|
|
240
|
+
return Result.err(new McpError({
|
|
241
|
+
message: `Resource template not found: ${ref.uri}`,
|
|
242
|
+
code: -32601,
|
|
243
|
+
context: { uri: ref.uri }
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
const handler = template.complete?.[argumentName];
|
|
247
|
+
if (!handler) {
|
|
248
|
+
return Result.ok({ values: [] });
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const result = await handler(value);
|
|
252
|
+
return Result.ok(result);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return Result.err(new McpError({
|
|
255
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
256
|
+
code: -32603,
|
|
257
|
+
context: { uri: ref.uri, argument: argumentName, thrown: true }
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return Result.err(new McpError({
|
|
262
|
+
message: "Invalid completion reference type",
|
|
263
|
+
code: -32602,
|
|
264
|
+
context: { ref }
|
|
265
|
+
}));
|
|
266
|
+
},
|
|
267
|
+
registerPrompt(prompt) {
|
|
268
|
+
logger.debug("Registering prompt", { name: prompt.name });
|
|
269
|
+
prompts.set(prompt.name, prompt);
|
|
270
|
+
logger.info("Prompt registered", { name: prompt.name });
|
|
271
|
+
if (sdkServer) {
|
|
272
|
+
sdkServer.sendPromptListChanged?.();
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
getPrompts() {
|
|
276
|
+
return Array.from(prompts.values()).map((p) => ({
|
|
277
|
+
name: p.name,
|
|
278
|
+
...p.description ? { description: p.description } : {},
|
|
279
|
+
arguments: p.arguments
|
|
280
|
+
}));
|
|
281
|
+
},
|
|
282
|
+
async getPrompt(promptName, args) {
|
|
283
|
+
const prompt = prompts.get(promptName);
|
|
284
|
+
if (!prompt) {
|
|
285
|
+
return Result.err(new McpError({
|
|
286
|
+
message: `Prompt not found: ${promptName}`,
|
|
287
|
+
code: -32601,
|
|
288
|
+
context: { prompt: promptName }
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
for (const arg of prompt.arguments) {
|
|
292
|
+
if (arg.required && (args[arg.name] === undefined || args[arg.name] === "")) {
|
|
293
|
+
return Result.err(new McpError({
|
|
294
|
+
message: `Missing required argument: ${arg.name}`,
|
|
295
|
+
code: -32602,
|
|
296
|
+
context: { prompt: promptName, argument: arg.name }
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
const result = await prompt.handler(args);
|
|
302
|
+
if (result.isErr()) {
|
|
303
|
+
return Result.err(translateError(result.error));
|
|
304
|
+
}
|
|
305
|
+
return Result.ok(result.value);
|
|
306
|
+
} catch (error) {
|
|
307
|
+
return Result.err(new McpError({
|
|
308
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
309
|
+
code: -32603,
|
|
310
|
+
context: { prompt: promptName, thrown: true }
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
async readResource(uri) {
|
|
315
|
+
const resource = resources.get(uri);
|
|
316
|
+
if (resource) {
|
|
317
|
+
if (!resource.handler) {
|
|
318
|
+
return Result.err(new McpError({
|
|
319
|
+
message: `Resource not readable: ${uri}`,
|
|
320
|
+
code: -32002,
|
|
321
|
+
context: { uri }
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
324
|
+
const requestId = generateRequestId();
|
|
325
|
+
const ctx = {
|
|
326
|
+
requestId,
|
|
327
|
+
logger: logger.child({ resource: uri, requestId }),
|
|
328
|
+
cwd: process.cwd(),
|
|
329
|
+
env: process.env
|
|
330
|
+
};
|
|
331
|
+
try {
|
|
332
|
+
const result = await resource.handler(uri, ctx);
|
|
333
|
+
if (result.isErr()) {
|
|
334
|
+
return Result.err(translateError(result.error));
|
|
335
|
+
}
|
|
336
|
+
return Result.ok(result.value);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
return Result.err(new McpError({
|
|
339
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
340
|
+
code: -32603,
|
|
341
|
+
context: { uri, thrown: true }
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
for (const template of resourceTemplates.values()) {
|
|
346
|
+
const variables = matchUriTemplate(template.uriTemplate, uri);
|
|
347
|
+
if (variables) {
|
|
348
|
+
const templateRequestId = generateRequestId();
|
|
349
|
+
const templateCtx = {
|
|
350
|
+
requestId: templateRequestId,
|
|
351
|
+
logger: logger.child({
|
|
352
|
+
resource: uri,
|
|
353
|
+
requestId: templateRequestId
|
|
354
|
+
}),
|
|
355
|
+
cwd: process.cwd(),
|
|
356
|
+
env: process.env
|
|
357
|
+
};
|
|
358
|
+
try {
|
|
359
|
+
const result = await template.handler(uri, variables, templateCtx);
|
|
360
|
+
if (result.isErr()) {
|
|
361
|
+
return Result.err(translateError(result.error));
|
|
362
|
+
}
|
|
363
|
+
return Result.ok(result.value);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return Result.err(new McpError({
|
|
366
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
367
|
+
code: -32603,
|
|
368
|
+
context: { uri, thrown: true }
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return Result.err(new McpError({
|
|
374
|
+
message: `Resource not found: ${uri}`,
|
|
375
|
+
code: -32002,
|
|
376
|
+
context: { uri }
|
|
377
|
+
}));
|
|
378
|
+
},
|
|
379
|
+
async invokeTool(toolName, input, invokeOptions) {
|
|
380
|
+
const requestId = invokeOptions?.requestId ?? generateRequestId();
|
|
381
|
+
logger.debug("Invoking tool", { tool: toolName, requestId });
|
|
382
|
+
const tool = tools.get(toolName);
|
|
383
|
+
if (!tool) {
|
|
384
|
+
logger.warn("Tool not found", { tool: toolName, requestId });
|
|
385
|
+
return Result.err(new McpError({
|
|
386
|
+
message: `Tool not found: ${toolName}`,
|
|
387
|
+
code: -32601,
|
|
388
|
+
context: { tool: toolName }
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
const parseResult = tool.zodSchema.safeParse(input);
|
|
392
|
+
if (!parseResult.success) {
|
|
393
|
+
const errorMessages = parseResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
394
|
+
logger.warn("Input validation failed", {
|
|
395
|
+
tool: toolName,
|
|
396
|
+
requestId,
|
|
397
|
+
errors: errorMessages
|
|
398
|
+
});
|
|
399
|
+
return Result.err(new McpError({
|
|
400
|
+
message: `Invalid input: ${errorMessages}`,
|
|
401
|
+
code: -32602,
|
|
402
|
+
context: {
|
|
403
|
+
tool: toolName,
|
|
404
|
+
validationErrors: parseResult.error.issues
|
|
405
|
+
}
|
|
406
|
+
}));
|
|
407
|
+
}
|
|
408
|
+
const ctx = createHandlerContext(toolName, requestId, invokeOptions?.signal, invokeOptions?.progressToken);
|
|
409
|
+
try {
|
|
410
|
+
const result = await tool.handler(parseResult.data, ctx);
|
|
411
|
+
if (result.isErr()) {
|
|
412
|
+
logger.debug("Tool returned error", {
|
|
413
|
+
tool: toolName,
|
|
414
|
+
requestId,
|
|
415
|
+
error: result.error._tag
|
|
416
|
+
});
|
|
417
|
+
return Result.err(translateError(result.error));
|
|
418
|
+
}
|
|
419
|
+
logger.debug("Tool completed successfully", {
|
|
420
|
+
tool: toolName,
|
|
421
|
+
requestId
|
|
422
|
+
});
|
|
423
|
+
return Result.ok(result.value);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
logger.error("Tool threw exception", {
|
|
426
|
+
tool: toolName,
|
|
427
|
+
requestId,
|
|
428
|
+
error: error instanceof Error ? error.message : String(error)
|
|
429
|
+
});
|
|
430
|
+
return Result.err(new McpError({
|
|
431
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
432
|
+
code: -32603,
|
|
433
|
+
context: {
|
|
434
|
+
tool: toolName,
|
|
435
|
+
thrown: true
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
subscribe(uri) {
|
|
441
|
+
subscriptions.add(uri);
|
|
442
|
+
logger.debug("Resource subscription added", { uri });
|
|
443
|
+
},
|
|
444
|
+
unsubscribe(uri) {
|
|
445
|
+
subscriptions.delete(uri);
|
|
446
|
+
logger.debug("Resource subscription removed", { uri });
|
|
447
|
+
},
|
|
448
|
+
notifyResourceUpdated(uri) {
|
|
449
|
+
if (subscriptions.has(uri)) {
|
|
450
|
+
sdkServer?.sendResourceUpdated?.({ uri });
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
notifyToolsChanged() {
|
|
454
|
+
sdkServer?.sendToolListChanged?.();
|
|
455
|
+
},
|
|
456
|
+
notifyResourcesChanged() {
|
|
457
|
+
sdkServer?.sendResourceListChanged?.();
|
|
458
|
+
},
|
|
459
|
+
notifyPromptsChanged() {
|
|
460
|
+
sdkServer?.sendPromptListChanged?.();
|
|
461
|
+
},
|
|
462
|
+
setLogLevel(level) {
|
|
463
|
+
clientLogLevel = level;
|
|
464
|
+
logger.debug("Client log level set", { level });
|
|
465
|
+
},
|
|
466
|
+
sendLogMessage(level, data, loggerName) {
|
|
467
|
+
if (!sdkServer || clientLogLevel === null || !shouldEmitLog(level, clientLogLevel)) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const params = {
|
|
471
|
+
level,
|
|
472
|
+
data
|
|
473
|
+
};
|
|
474
|
+
if (loggerName !== undefined) {
|
|
475
|
+
params.logger = loggerName;
|
|
476
|
+
}
|
|
477
|
+
sdkServer.sendLoggingMessage?.(params);
|
|
478
|
+
},
|
|
479
|
+
bindSdkServer(server2) {
|
|
480
|
+
sdkServer = server2;
|
|
481
|
+
clientLogLevel = resolveDefaultLogLevel(options);
|
|
482
|
+
logger.debug("SDK server bound for notifications");
|
|
483
|
+
},
|
|
484
|
+
async start() {
|
|
485
|
+
logger.info("MCP server starting", { name, version, tools: tools.size });
|
|
486
|
+
},
|
|
487
|
+
async stop() {
|
|
488
|
+
logger.info("MCP server stopping", { name, version });
|
|
489
|
+
if (loggerFactory !== null) {
|
|
490
|
+
await loggerFactory.flush();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
return server;
|
|
495
|
+
}
|
|
496
|
+
function defineTool(definition) {
|
|
497
|
+
return definition;
|
|
498
|
+
}
|
|
499
|
+
function escapeRegex(str) {
|
|
500
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
501
|
+
}
|
|
502
|
+
function matchUriTemplate(template, uri) {
|
|
503
|
+
const paramNames = [];
|
|
504
|
+
const parts = template.split(/(\{[^}]+\})/);
|
|
505
|
+
const regexSource = parts.map((part) => {
|
|
506
|
+
const paramMatch = part.match(/^\{([^}]+)\}$/);
|
|
507
|
+
if (paramMatch?.[1]) {
|
|
508
|
+
paramNames.push(paramMatch[1]);
|
|
509
|
+
return "([^/]+)";
|
|
510
|
+
}
|
|
511
|
+
return escapeRegex(part);
|
|
512
|
+
}).join("");
|
|
513
|
+
const regex = new RegExp(`^${regexSource}$`);
|
|
514
|
+
const match = uri.match(regex);
|
|
515
|
+
if (!match) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
const variables = {};
|
|
519
|
+
for (let i = 0;i < paramNames.length; i++) {
|
|
520
|
+
const name = paramNames[i];
|
|
521
|
+
const value = match[i + 1];
|
|
522
|
+
if (name !== undefined && value !== undefined) {
|
|
523
|
+
variables[name] = value;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return variables;
|
|
527
|
+
}
|
|
528
|
+
function defineResource(definition) {
|
|
529
|
+
return definition;
|
|
530
|
+
}
|
|
531
|
+
function defineResourceTemplate(definition) {
|
|
532
|
+
return definition;
|
|
533
|
+
}
|
|
534
|
+
function definePrompt(definition) {
|
|
535
|
+
return definition;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export { createMcpServer, defineTool, defineResource, defineResourceTemplate, definePrompt };
|