@outfitter/mcp 0.1.0 → 0.2.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 +22 -0
- package/dist/index.d.ts +378 -1
- package/dist/index.js +419 -18
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -103,6 +103,7 @@ interface ResourceDefinition {
|
|
|
103
103
|
name: string; // Human-readable name
|
|
104
104
|
description?: string; // Optional description
|
|
105
105
|
mimeType?: string; // Content MIME type
|
|
106
|
+
handler?: ResourceReadHandler; // Optional resources/read handler
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
const configResource = defineResource({
|
|
@@ -110,9 +111,27 @@ const configResource = defineResource({
|
|
|
110
111
|
name: "Application Config",
|
|
111
112
|
description: "Main configuration file",
|
|
112
113
|
mimeType: "application/json",
|
|
114
|
+
handler: async (uri, ctx) => {
|
|
115
|
+
ctx.logger.debug("Reading config resource", { uri });
|
|
116
|
+
return Result.ok([
|
|
117
|
+
{
|
|
118
|
+
uri,
|
|
119
|
+
mimeType: "application/json",
|
|
120
|
+
text: JSON.stringify({ debug: true }),
|
|
121
|
+
},
|
|
122
|
+
]);
|
|
123
|
+
},
|
|
113
124
|
});
|
|
114
125
|
```
|
|
115
126
|
|
|
127
|
+
Registered resources with handlers are exposed through MCP `resources/read`.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
server.registerResource(configResource);
|
|
131
|
+
|
|
132
|
+
const contentResult = await server.readResource("file:///etc/app/config.json");
|
|
133
|
+
```
|
|
134
|
+
|
|
116
135
|
### Server Methods
|
|
117
136
|
|
|
118
137
|
```typescript
|
|
@@ -123,12 +142,15 @@ interface McpServer {
|
|
|
123
142
|
// Registration
|
|
124
143
|
registerTool<TInput, TOutput, TError>(tool: ToolDefinition): void;
|
|
125
144
|
registerResource(resource: ResourceDefinition): void;
|
|
145
|
+
registerResourceTemplate(template: ResourceTemplateDefinition): void;
|
|
126
146
|
|
|
127
147
|
// Introspection
|
|
128
148
|
getTools(): SerializedTool[];
|
|
129
149
|
getResources(): ResourceDefinition[];
|
|
150
|
+
getResourceTemplates(): ResourceTemplateDefinition[];
|
|
130
151
|
|
|
131
152
|
// Invocation
|
|
153
|
+
readResource(uri: string): Promise<Result<ResourceContent[], McpError>>;
|
|
132
154
|
invokeTool<T>(name: string, input: unknown, options?: InvokeToolOptions): Promise<Result<T, McpError>>;
|
|
133
155
|
|
|
134
156
|
// Lifecycle
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,24 @@ interface McpServerOptions {
|
|
|
33
33
|
logger?: Logger;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
+
* Behavioral hints for MCP tools.
|
|
37
|
+
*
|
|
38
|
+
* Annotations help clients understand tool behavior without invoking them.
|
|
39
|
+
* All fields are optional — only include hints that apply.
|
|
40
|
+
*
|
|
41
|
+
* @see https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/#annotations
|
|
42
|
+
*/
|
|
43
|
+
interface ToolAnnotations {
|
|
44
|
+
/** When true, the tool does not modify any state. */
|
|
45
|
+
readOnlyHint?: boolean;
|
|
46
|
+
/** When true, the tool may perform destructive operations (e.g., deleting data). */
|
|
47
|
+
destructiveHint?: boolean;
|
|
48
|
+
/** When true, calling the tool multiple times with the same input has the same effect. */
|
|
49
|
+
idempotentHint?: boolean;
|
|
50
|
+
/** When true, the tool may interact with external systems beyond the server. */
|
|
51
|
+
openWorldHint?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
36
54
|
* Definition of an MCP tool that can be invoked by clients.
|
|
37
55
|
*
|
|
38
56
|
* Tools are the primary way clients interact with MCP servers.
|
|
@@ -94,6 +112,11 @@ interface ToolDefinition<
|
|
|
94
112
|
*/
|
|
95
113
|
inputSchema: z.ZodType<TInput>;
|
|
96
114
|
/**
|
|
115
|
+
* Optional behavioral annotations for the tool.
|
|
116
|
+
* Helps clients understand tool behavior without invoking it.
|
|
117
|
+
*/
|
|
118
|
+
annotations?: ToolAnnotations;
|
|
119
|
+
/**
|
|
97
120
|
* Handler function that processes the tool invocation.
|
|
98
121
|
* Receives validated input and HandlerContext, returns Result.
|
|
99
122
|
*/
|
|
@@ -112,8 +135,66 @@ interface SerializedTool {
|
|
|
112
135
|
inputSchema: Record<string, unknown>;
|
|
113
136
|
/** MCP tool-search hint: whether tool is deferred */
|
|
114
137
|
defer_loading?: boolean;
|
|
138
|
+
/** Behavioral annotations for the tool */
|
|
139
|
+
annotations?: ToolAnnotations;
|
|
115
140
|
}
|
|
116
141
|
/**
|
|
142
|
+
* Annotations for content items (resource content, prompt messages).
|
|
143
|
+
*
|
|
144
|
+
* Provides hints about content audience and priority.
|
|
145
|
+
*
|
|
146
|
+
* @see https://spec.modelcontextprotocol.io/specification/2025-03-26/server/utilities/annotations/
|
|
147
|
+
*/
|
|
148
|
+
interface ContentAnnotations {
|
|
149
|
+
/**
|
|
150
|
+
* Who the content is intended for.
|
|
151
|
+
* Can include "user", "assistant", or both.
|
|
152
|
+
*/
|
|
153
|
+
audience?: Array<"user" | "assistant">;
|
|
154
|
+
/**
|
|
155
|
+
* Priority level from 0.0 (least) to 1.0 (most important).
|
|
156
|
+
*/
|
|
157
|
+
priority?: number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Text content returned from a resource read.
|
|
161
|
+
*/
|
|
162
|
+
interface TextResourceContent {
|
|
163
|
+
/** Resource URI */
|
|
164
|
+
uri: string;
|
|
165
|
+
/** Text content */
|
|
166
|
+
text: string;
|
|
167
|
+
/** Optional MIME type */
|
|
168
|
+
mimeType?: string;
|
|
169
|
+
/** Optional content annotations */
|
|
170
|
+
annotations?: ContentAnnotations;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Binary (base64-encoded) content returned from a resource read.
|
|
174
|
+
*/
|
|
175
|
+
interface BlobResourceContent {
|
|
176
|
+
/** Resource URI */
|
|
177
|
+
uri: string;
|
|
178
|
+
/** Base64-encoded binary content */
|
|
179
|
+
blob: string;
|
|
180
|
+
/** Optional MIME type */
|
|
181
|
+
mimeType?: string;
|
|
182
|
+
/** Optional content annotations */
|
|
183
|
+
annotations?: ContentAnnotations;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Content returned from reading a resource.
|
|
187
|
+
*/
|
|
188
|
+
type ResourceContent = TextResourceContent | BlobResourceContent;
|
|
189
|
+
/**
|
|
190
|
+
* Handler for reading a resource's content.
|
|
191
|
+
*
|
|
192
|
+
* @param uri - The resource URI being read
|
|
193
|
+
* @param ctx - Handler context with logger and requestId
|
|
194
|
+
* @returns Array of resource content items
|
|
195
|
+
*/
|
|
196
|
+
type ResourceReadHandler = (uri: string, ctx: HandlerContext) => Promise<Result<ResourceContent[], OutfitterError>>;
|
|
197
|
+
/**
|
|
117
198
|
* Definition of an MCP resource that can be read by clients.
|
|
118
199
|
*
|
|
119
200
|
* Resources represent data that clients can access, such as files,
|
|
@@ -126,6 +207,10 @@ interface SerializedTool {
|
|
|
126
207
|
* name: "Application Config",
|
|
127
208
|
* description: "Main application configuration file",
|
|
128
209
|
* mimeType: "application/json",
|
|
210
|
+
* handler: async (uri, ctx) => {
|
|
211
|
+
* const content = await readFile(uri);
|
|
212
|
+
* return Result.ok([{ uri, text: content }]);
|
|
213
|
+
* },
|
|
129
214
|
* };
|
|
130
215
|
* ```
|
|
131
216
|
*/
|
|
@@ -150,6 +235,151 @@ interface ResourceDefinition {
|
|
|
150
235
|
* Helps clients understand how to process the resource.
|
|
151
236
|
*/
|
|
152
237
|
mimeType?: string;
|
|
238
|
+
/**
|
|
239
|
+
* Optional handler for reading the resource content.
|
|
240
|
+
* If not provided, the resource is metadata-only.
|
|
241
|
+
*/
|
|
242
|
+
handler?: ResourceReadHandler;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Handler for reading a resource template's content.
|
|
246
|
+
*
|
|
247
|
+
* @param uri - The matched URI
|
|
248
|
+
* @param variables - Extracted template variables
|
|
249
|
+
* @param ctx - Handler context
|
|
250
|
+
*/
|
|
251
|
+
type ResourceTemplateReadHandler = (uri: string, variables: Record<string, string>, ctx: HandlerContext) => Promise<Result<ResourceContent[], OutfitterError>>;
|
|
252
|
+
/**
|
|
253
|
+
* Definition of an MCP resource template with URI pattern matching.
|
|
254
|
+
*
|
|
255
|
+
* Templates use RFC 6570 Level 1 URI templates (e.g., `{param}`)
|
|
256
|
+
* to match and extract variables from URIs.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const userTemplate: ResourceTemplateDefinition = {
|
|
261
|
+
* uriTemplate: "db:///users/{userId}/profile",
|
|
262
|
+
* name: "User Profile",
|
|
263
|
+
* handler: async (uri, variables) => {
|
|
264
|
+
* const profile = await getProfile(variables.userId);
|
|
265
|
+
* return Result.ok([{ uri, text: JSON.stringify(profile) }]);
|
|
266
|
+
* },
|
|
267
|
+
* };
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
interface ResourceTemplateDefinition {
|
|
271
|
+
/** URI template with `{param}` placeholders (RFC 6570 Level 1). */
|
|
272
|
+
uriTemplate: string;
|
|
273
|
+
/** Human-readable name for the template. */
|
|
274
|
+
name: string;
|
|
275
|
+
/** Optional description. */
|
|
276
|
+
description?: string;
|
|
277
|
+
/** Optional MIME type. */
|
|
278
|
+
mimeType?: string;
|
|
279
|
+
/** Optional completion handlers keyed by parameter name. */
|
|
280
|
+
complete?: Record<string, CompletionHandler>;
|
|
281
|
+
/** Handler for reading matched resources. */
|
|
282
|
+
handler: ResourceTemplateReadHandler;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Result of a completion request.
|
|
286
|
+
*/
|
|
287
|
+
interface CompletionResult {
|
|
288
|
+
/** Completion values */
|
|
289
|
+
values: string[];
|
|
290
|
+
/** Total number of available values (for pagination) */
|
|
291
|
+
total?: number;
|
|
292
|
+
/** Whether there are more values */
|
|
293
|
+
hasMore?: boolean;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Handler for generating completions.
|
|
297
|
+
*/
|
|
298
|
+
type CompletionHandler = (value: string) => Promise<CompletionResult>;
|
|
299
|
+
/**
|
|
300
|
+
* Reference to a prompt or resource for completion.
|
|
301
|
+
*/
|
|
302
|
+
type CompletionRef = {
|
|
303
|
+
type: "ref/prompt";
|
|
304
|
+
name: string;
|
|
305
|
+
} | {
|
|
306
|
+
type: "ref/resource";
|
|
307
|
+
uri: string;
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* Argument definition for a prompt.
|
|
311
|
+
*/
|
|
312
|
+
interface PromptArgument {
|
|
313
|
+
/** Argument name */
|
|
314
|
+
name: string;
|
|
315
|
+
/** Human-readable description */
|
|
316
|
+
description?: string;
|
|
317
|
+
/** Whether this argument is required */
|
|
318
|
+
required?: boolean;
|
|
319
|
+
/** Optional completion handler for this argument */
|
|
320
|
+
complete?: CompletionHandler;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Content block within a prompt message.
|
|
324
|
+
*/
|
|
325
|
+
interface PromptMessageContent {
|
|
326
|
+
/** Content type */
|
|
327
|
+
type: "text";
|
|
328
|
+
/** Text content */
|
|
329
|
+
text: string;
|
|
330
|
+
/** Optional content annotations */
|
|
331
|
+
annotations?: ContentAnnotations;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* A message in a prompt response.
|
|
335
|
+
*/
|
|
336
|
+
interface PromptMessage {
|
|
337
|
+
/** Message role */
|
|
338
|
+
role: "user" | "assistant";
|
|
339
|
+
/** Message content */
|
|
340
|
+
content: PromptMessageContent;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Result returned from getting a prompt.
|
|
344
|
+
*/
|
|
345
|
+
interface PromptResult {
|
|
346
|
+
/** Prompt messages */
|
|
347
|
+
messages: PromptMessage[];
|
|
348
|
+
/** Optional description override */
|
|
349
|
+
description?: string;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Handler for generating prompt messages.
|
|
353
|
+
*/
|
|
354
|
+
type PromptHandler = (args: Record<string, string | undefined>) => Promise<Result<PromptResult, OutfitterError>>;
|
|
355
|
+
/**
|
|
356
|
+
* Definition of an MCP prompt.
|
|
357
|
+
*
|
|
358
|
+
* Prompts are reusable templates that generate messages for LLMs.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* const reviewPrompt: PromptDefinition = {
|
|
363
|
+
* name: "code-review",
|
|
364
|
+
* description: "Review code changes",
|
|
365
|
+
* arguments: [
|
|
366
|
+
* { name: "language", description: "Programming language", required: true },
|
|
367
|
+
* ],
|
|
368
|
+
* handler: async (args) => Result.ok({
|
|
369
|
+
* messages: [{ role: "user", content: { type: "text", text: `Review this ${args.language} code` } }],
|
|
370
|
+
* }),
|
|
371
|
+
* };
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
interface PromptDefinition {
|
|
375
|
+
/** Unique prompt name */
|
|
376
|
+
name: string;
|
|
377
|
+
/** Human-readable description */
|
|
378
|
+
description?: string;
|
|
379
|
+
/** Prompt arguments */
|
|
380
|
+
arguments: PromptArgument[];
|
|
381
|
+
/** Handler to generate messages */
|
|
382
|
+
handler: PromptHandler;
|
|
153
383
|
}
|
|
154
384
|
declare const McpErrorBase: TaggedErrorClass<"McpError", {
|
|
155
385
|
message: string;
|
|
@@ -191,6 +421,8 @@ interface InvokeToolOptions {
|
|
|
191
421
|
signal?: AbortSignal;
|
|
192
422
|
/** Custom request ID (auto-generated if not provided) */
|
|
193
423
|
requestId?: string;
|
|
424
|
+
/** Progress token from client for tracking progress */
|
|
425
|
+
progressToken?: string | number;
|
|
194
426
|
}
|
|
195
427
|
/**
|
|
196
428
|
* MCP Server instance.
|
|
@@ -236,11 +468,56 @@ interface McpServer {
|
|
|
236
468
|
*/
|
|
237
469
|
getTools(): SerializedTool[];
|
|
238
470
|
/**
|
|
471
|
+
* Register a resource template with the server.
|
|
472
|
+
* @param template - Resource template definition to register
|
|
473
|
+
*/
|
|
474
|
+
registerResourceTemplate(template: ResourceTemplateDefinition): void;
|
|
475
|
+
/**
|
|
239
476
|
* Get all registered resources.
|
|
240
477
|
* @returns Array of resource definitions
|
|
241
478
|
*/
|
|
242
479
|
getResources(): ResourceDefinition[];
|
|
243
480
|
/**
|
|
481
|
+
* Get all registered resource templates.
|
|
482
|
+
* @returns Array of resource template definitions
|
|
483
|
+
*/
|
|
484
|
+
getResourceTemplates(): ResourceTemplateDefinition[];
|
|
485
|
+
/**
|
|
486
|
+
* Complete an argument value.
|
|
487
|
+
* @param ref - Reference to the prompt or resource template
|
|
488
|
+
* @param argumentName - Name of the argument to complete
|
|
489
|
+
* @param value - Current value to complete
|
|
490
|
+
* @returns Result with completion values or McpError
|
|
491
|
+
*/
|
|
492
|
+
complete(ref: CompletionRef, argumentName: string, value: string): Promise<Result<CompletionResult, InstanceType<typeof McpError>>>;
|
|
493
|
+
/**
|
|
494
|
+
* Register a prompt with the server.
|
|
495
|
+
* @param prompt - Prompt definition to register
|
|
496
|
+
*/
|
|
497
|
+
registerPrompt(prompt: PromptDefinition): void;
|
|
498
|
+
/**
|
|
499
|
+
* Get all registered prompts.
|
|
500
|
+
* @returns Array of prompt definitions (without handlers)
|
|
501
|
+
*/
|
|
502
|
+
getPrompts(): Array<{
|
|
503
|
+
name: string;
|
|
504
|
+
description?: string;
|
|
505
|
+
arguments: PromptArgument[];
|
|
506
|
+
}>;
|
|
507
|
+
/**
|
|
508
|
+
* Get a specific prompt's messages.
|
|
509
|
+
* @param name - Prompt name
|
|
510
|
+
* @param args - Prompt arguments
|
|
511
|
+
* @returns Result with prompt result or McpError
|
|
512
|
+
*/
|
|
513
|
+
getPrompt(name: string, args: Record<string, string | undefined>): Promise<Result<PromptResult, InstanceType<typeof McpError>>>;
|
|
514
|
+
/**
|
|
515
|
+
* Read a resource by URI.
|
|
516
|
+
* @param uri - Resource URI
|
|
517
|
+
* @returns Result with resource content or McpError
|
|
518
|
+
*/
|
|
519
|
+
readResource(uri: string): Promise<Result<ResourceContent[], InstanceType<typeof McpError>>>;
|
|
520
|
+
/**
|
|
244
521
|
* Invoke a tool by name.
|
|
245
522
|
* @param name - Tool name
|
|
246
523
|
* @param input - Tool input (will be validated)
|
|
@@ -249,6 +526,46 @@ interface McpServer {
|
|
|
249
526
|
*/
|
|
250
527
|
invokeTool<T = unknown>(name: string, input: unknown, options?: InvokeToolOptions): Promise<Result<T, InstanceType<typeof McpError>>>;
|
|
251
528
|
/**
|
|
529
|
+
* Subscribe to updates for a resource URI.
|
|
530
|
+
* @param uri - Resource URI to subscribe to
|
|
531
|
+
*/
|
|
532
|
+
subscribe(uri: string): void;
|
|
533
|
+
/**
|
|
534
|
+
* Unsubscribe from updates for a resource URI.
|
|
535
|
+
* @param uri - Resource URI to unsubscribe from
|
|
536
|
+
*/
|
|
537
|
+
unsubscribe(uri: string): void;
|
|
538
|
+
/**
|
|
539
|
+
* Notify connected clients that a specific resource has been updated.
|
|
540
|
+
* Only emits for subscribed URIs.
|
|
541
|
+
* @param uri - URI of the updated resource
|
|
542
|
+
*/
|
|
543
|
+
notifyResourceUpdated(uri: string): void;
|
|
544
|
+
/**
|
|
545
|
+
* Notify connected clients that the tool list has changed.
|
|
546
|
+
*/
|
|
547
|
+
notifyToolsChanged(): void;
|
|
548
|
+
/**
|
|
549
|
+
* Notify connected clients that the resource list has changed.
|
|
550
|
+
*/
|
|
551
|
+
notifyResourcesChanged(): void;
|
|
552
|
+
/**
|
|
553
|
+
* Notify connected clients that the prompt list has changed.
|
|
554
|
+
*/
|
|
555
|
+
notifyPromptsChanged(): void;
|
|
556
|
+
/**
|
|
557
|
+
* Set the client-requested log level.
|
|
558
|
+
* Only log messages at or above this level will be forwarded.
|
|
559
|
+
* @param level - MCP log level string
|
|
560
|
+
*/
|
|
561
|
+
setLogLevel?(level: string): void;
|
|
562
|
+
/**
|
|
563
|
+
* Bind the SDK server instance for notifications.
|
|
564
|
+
* Called internally by the transport layer.
|
|
565
|
+
* @param sdkServer - The MCP SDK Server instance
|
|
566
|
+
*/
|
|
567
|
+
bindSdkServer?(sdkServer: any): void;
|
|
568
|
+
/**
|
|
252
569
|
* Start the MCP server.
|
|
253
570
|
* Begins listening for client connections.
|
|
254
571
|
*/
|
|
@@ -260,12 +577,26 @@ interface McpServer {
|
|
|
260
577
|
stop(): Promise<void>;
|
|
261
578
|
}
|
|
262
579
|
/**
|
|
580
|
+
* Reporter for sending progress updates to clients.
|
|
581
|
+
*/
|
|
582
|
+
interface ProgressReporter {
|
|
583
|
+
/**
|
|
584
|
+
* Report progress for the current operation.
|
|
585
|
+
* @param progress - Current progress value
|
|
586
|
+
* @param total - Optional total value (for percentage calculation)
|
|
587
|
+
* @param message - Optional human-readable status message
|
|
588
|
+
*/
|
|
589
|
+
report(progress: number, total?: number, message?: string): void;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
263
592
|
* Extended handler context for MCP tools.
|
|
264
593
|
* Includes MCP-specific information in addition to standard HandlerContext.
|
|
265
594
|
*/
|
|
266
595
|
interface McpHandlerContext extends HandlerContext {
|
|
267
596
|
/** The name of the tool being invoked */
|
|
268
597
|
toolName?: string;
|
|
598
|
+
/** Progress reporter, present when client provides a progressToken */
|
|
599
|
+
progress?: ProgressReporter;
|
|
269
600
|
}
|
|
270
601
|
interface BuildMcpToolsOptions {
|
|
271
602
|
readonly includeSurfaces?: readonly ActionSurface[];
|
|
@@ -366,6 +697,32 @@ interface CoreToolsOptions {
|
|
|
366
697
|
type NormalizedQueryInput = Required<Pick<QueryToolInput, "q">> & Omit<QueryToolInput, "q">;
|
|
367
698
|
type CoreToolDefinition = ToolDefinition<DocsToolInput, DocsToolResponse> | ToolDefinition<ConfigToolInput, ConfigToolResponse> | ToolDefinition<QueryToolInput, QueryToolResponse>;
|
|
368
699
|
declare function createCoreTools(options?: CoreToolsOptions): CoreToolDefinition[];
|
|
700
|
+
/**
|
|
701
|
+
* @outfitter/mcp - Logging Bridge
|
|
702
|
+
*
|
|
703
|
+
* Maps Outfitter log levels to MCP log levels for
|
|
704
|
+
* server-to-client log message notifications.
|
|
705
|
+
*
|
|
706
|
+
* @packageDocumentation
|
|
707
|
+
*/
|
|
708
|
+
/**
|
|
709
|
+
* MCP log levels as defined in the MCP specification.
|
|
710
|
+
* Ordered from least to most severe.
|
|
711
|
+
*/
|
|
712
|
+
type McpLogLevel = "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
|
|
713
|
+
/**
|
|
714
|
+
* Outfitter log levels.
|
|
715
|
+
*/
|
|
716
|
+
type OutfitterLogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
717
|
+
/**
|
|
718
|
+
* Map an Outfitter log level to the corresponding MCP log level.
|
|
719
|
+
*/
|
|
720
|
+
declare function mapLogLevelToMcp(level: OutfitterLogLevel): McpLogLevel;
|
|
721
|
+
/**
|
|
722
|
+
* Check whether a message at the given level should be emitted
|
|
723
|
+
* based on the client-requested threshold.
|
|
724
|
+
*/
|
|
725
|
+
declare function shouldEmitLog(messageLevel: McpLogLevel, threshold: McpLogLevel): boolean;
|
|
369
726
|
import { z as z2 } from "zod";
|
|
370
727
|
/**
|
|
371
728
|
* JSON Schema representation.
|
|
@@ -507,6 +864,26 @@ declare function defineTool<
|
|
|
507
864
|
* ```
|
|
508
865
|
*/
|
|
509
866
|
declare function defineResource(definition: ResourceDefinition): ResourceDefinition;
|
|
867
|
+
/**
|
|
868
|
+
* Define a resource template.
|
|
869
|
+
*
|
|
870
|
+
* Helper function for creating resource template definitions
|
|
871
|
+
* with URI pattern matching.
|
|
872
|
+
*
|
|
873
|
+
* @param definition - Resource template definition object
|
|
874
|
+
* @returns The same resource template definition
|
|
875
|
+
*/
|
|
876
|
+
declare function defineResourceTemplate(definition: ResourceTemplateDefinition): ResourceTemplateDefinition;
|
|
877
|
+
/**
|
|
878
|
+
* Define a prompt.
|
|
879
|
+
*
|
|
880
|
+
* Helper function for creating prompt definitions
|
|
881
|
+
* with consistent typing.
|
|
882
|
+
*
|
|
883
|
+
* @param definition - Prompt definition object
|
|
884
|
+
* @returns The same prompt definition
|
|
885
|
+
*/
|
|
886
|
+
declare function definePrompt(definition: PromptDefinition): PromptDefinition;
|
|
510
887
|
import { Server } from "@modelcontextprotocol/sdk/server/index";
|
|
511
888
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
|
|
512
889
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types";
|
|
@@ -519,4 +896,4 @@ declare function createSdkServer(server: McpServer): Server;
|
|
|
519
896
|
* Connect an MCP server over stdio transport.
|
|
520
897
|
*/
|
|
521
898
|
declare function connectStdio(server: McpServer, transport?: StdioServerTransport): Promise<Server>;
|
|
522
|
-
export { zodToJsonSchema, defineTool, defineResource, defineQueryTool, defineDocsTool, defineConfigTool, createSdkServer, createMcpServer, createCoreTools, connectStdio, buildMcpTools, ToolDefinition, SerializedTool, ResourceDefinition, QueryToolResponse, QueryToolOptions, QueryToolInput, McpToolResponse, McpServerOptions, McpServer, McpHandlerContext, McpError, JsonSchema, InvokeToolOptions, DocsToolResponse, DocsToolOptions, DocsToolInput, DocsToolEntry, DocsSection, CoreToolsOptions, ConfigToolResponse, ConfigToolOptions, ConfigToolInput, ConfigStore, ConfigAction, BuildMcpToolsOptions };
|
|
899
|
+
export { zodToJsonSchema, shouldEmitLog, mapLogLevelToMcp, defineTool, defineResourceTemplate, defineResource, defineQueryTool, definePrompt, defineDocsTool, defineConfigTool, createSdkServer, createMcpServer, createCoreTools, connectStdio, buildMcpTools, ToolDefinition, ToolAnnotations, TextResourceContent, SerializedTool, ResourceTemplateReadHandler, ResourceTemplateDefinition, ResourceReadHandler, ResourceDefinition, ResourceContent, QueryToolResponse, QueryToolOptions, QueryToolInput, PromptResult, PromptMessageContent, PromptMessage, PromptHandler, PromptDefinition, PromptArgument, ProgressReporter, McpToolResponse, McpServerOptions, McpServer, McpLogLevel, McpHandlerContext, McpError, JsonSchema, InvokeToolOptions, DocsToolResponse, DocsToolOptions, DocsToolInput, DocsToolEntry, DocsSection, CoreToolsOptions, ContentAnnotations, ConfigToolResponse, ConfigToolOptions, ConfigToolInput, ConfigStore, ConfigAction, CompletionResult, CompletionRef, CompletionHandler, BuildMcpToolsOptions, BlobResourceContent };
|
package/dist/index.js
CHANGED
|
@@ -357,14 +357,13 @@ class McpError extends McpErrorBase {
|
|
|
357
357
|
|
|
358
358
|
// src/server.ts
|
|
359
359
|
function createNoOpLogger() {
|
|
360
|
-
const noop = () => {};
|
|
361
360
|
return {
|
|
362
|
-
trace:
|
|
363
|
-
debug:
|
|
364
|
-
info:
|
|
365
|
-
warn:
|
|
366
|
-
error:
|
|
367
|
-
fatal:
|
|
361
|
+
trace: (..._args) => {},
|
|
362
|
+
debug: (..._args) => {},
|
|
363
|
+
info: (..._args) => {},
|
|
364
|
+
warn: (..._args) => {},
|
|
365
|
+
error: (..._args) => {},
|
|
366
|
+
fatal: (..._args) => {},
|
|
368
367
|
child: () => createNoOpLogger()
|
|
369
368
|
};
|
|
370
369
|
}
|
|
@@ -372,8 +371,12 @@ function createMcpServer(options) {
|
|
|
372
371
|
const { name, version, logger: providedLogger } = options;
|
|
373
372
|
const logger = providedLogger ?? createNoOpLogger();
|
|
374
373
|
const tools = new Map;
|
|
375
|
-
const resources =
|
|
376
|
-
|
|
374
|
+
const resources = new Map;
|
|
375
|
+
const resourceTemplates = new Map;
|
|
376
|
+
const prompts = new Map;
|
|
377
|
+
let sdkServer = null;
|
|
378
|
+
const subscriptions = new Set;
|
|
379
|
+
function createHandlerContext(toolName, requestId, signal, progressToken) {
|
|
377
380
|
const ctx = {
|
|
378
381
|
requestId,
|
|
379
382
|
logger: logger.child({ tool: toolName, requestId }),
|
|
@@ -383,6 +386,21 @@ function createMcpServer(options) {
|
|
|
383
386
|
if (signal !== undefined) {
|
|
384
387
|
ctx.signal = signal;
|
|
385
388
|
}
|
|
389
|
+
if (progressToken !== undefined && sdkServer) {
|
|
390
|
+
ctx.progress = {
|
|
391
|
+
report(progress, total, message) {
|
|
392
|
+
sdkServer?.notification?.({
|
|
393
|
+
method: "notifications/progress",
|
|
394
|
+
params: {
|
|
395
|
+
progressToken,
|
|
396
|
+
progress,
|
|
397
|
+
...total !== undefined ? { total } : {},
|
|
398
|
+
...message ? { message } : {}
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
386
404
|
return ctx;
|
|
387
405
|
}
|
|
388
406
|
function translateError(error) {
|
|
@@ -423,34 +441,232 @@ function createMcpServer(options) {
|
|
|
423
441
|
const jsonSchema = zodToJsonSchema(tool.inputSchema);
|
|
424
442
|
const handler = (input, ctx) => tool.handler(input, ctx);
|
|
425
443
|
const deferLoading = tool.deferLoading ?? true;
|
|
426
|
-
|
|
444
|
+
const stored = {
|
|
427
445
|
name: tool.name,
|
|
428
446
|
description,
|
|
429
447
|
inputSchema: jsonSchema,
|
|
430
448
|
deferLoading,
|
|
431
449
|
handler,
|
|
432
450
|
zodSchema: tool.inputSchema
|
|
433
|
-
}
|
|
451
|
+
};
|
|
452
|
+
if (tool.annotations !== undefined) {
|
|
453
|
+
stored.annotations = tool.annotations;
|
|
454
|
+
}
|
|
455
|
+
tools.set(tool.name, stored);
|
|
434
456
|
logger.info("Tool registered", { name: tool.name });
|
|
457
|
+
if (sdkServer) {
|
|
458
|
+
sdkServer.sendToolListChanged?.();
|
|
459
|
+
}
|
|
435
460
|
},
|
|
436
461
|
registerResource(resource) {
|
|
437
462
|
logger.debug("Registering resource", {
|
|
438
463
|
uri: resource.uri,
|
|
439
464
|
name: resource.name
|
|
440
465
|
});
|
|
441
|
-
resources.
|
|
466
|
+
resources.set(resource.uri, resource);
|
|
442
467
|
logger.info("Resource registered", { uri: resource.uri });
|
|
468
|
+
if (sdkServer) {
|
|
469
|
+
sdkServer.sendResourceListChanged?.();
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
registerResourceTemplate(template) {
|
|
473
|
+
logger.debug("Registering resource template", {
|
|
474
|
+
uriTemplate: template.uriTemplate,
|
|
475
|
+
name: template.name
|
|
476
|
+
});
|
|
477
|
+
resourceTemplates.set(template.uriTemplate, template);
|
|
478
|
+
logger.info("Resource template registered", {
|
|
479
|
+
uriTemplate: template.uriTemplate
|
|
480
|
+
});
|
|
481
|
+
if (sdkServer) {
|
|
482
|
+
sdkServer.sendResourceListChanged?.();
|
|
483
|
+
}
|
|
443
484
|
},
|
|
444
485
|
getTools() {
|
|
445
486
|
return Array.from(tools.values()).map((tool) => ({
|
|
446
487
|
name: tool.name,
|
|
447
488
|
description: tool.description,
|
|
448
489
|
inputSchema: tool.inputSchema,
|
|
449
|
-
defer_loading: tool.deferLoading
|
|
490
|
+
defer_loading: tool.deferLoading,
|
|
491
|
+
...tool.annotations ? { annotations: tool.annotations } : {}
|
|
450
492
|
}));
|
|
451
493
|
},
|
|
452
494
|
getResources() {
|
|
453
|
-
return
|
|
495
|
+
return Array.from(resources.values());
|
|
496
|
+
},
|
|
497
|
+
getResourceTemplates() {
|
|
498
|
+
return Array.from(resourceTemplates.values());
|
|
499
|
+
},
|
|
500
|
+
async complete(ref, argumentName, value) {
|
|
501
|
+
if (ref.type === "ref/prompt") {
|
|
502
|
+
const prompt = prompts.get(ref.name);
|
|
503
|
+
if (!prompt) {
|
|
504
|
+
return Result.err(new McpError({
|
|
505
|
+
message: `Prompt not found: ${ref.name}`,
|
|
506
|
+
code: -32601,
|
|
507
|
+
context: { prompt: ref.name }
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
510
|
+
const arg = prompt.arguments.find((a) => a.name === argumentName);
|
|
511
|
+
if (!arg?.complete) {
|
|
512
|
+
return Result.ok({ values: [] });
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const result = await arg.complete(value);
|
|
516
|
+
return Result.ok(result);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
return Result.err(new McpError({
|
|
519
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
520
|
+
code: -32603,
|
|
521
|
+
context: {
|
|
522
|
+
prompt: ref.name,
|
|
523
|
+
argument: argumentName,
|
|
524
|
+
thrown: true
|
|
525
|
+
}
|
|
526
|
+
}));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (ref.type === "ref/resource") {
|
|
530
|
+
const template = resourceTemplates.get(ref.uri);
|
|
531
|
+
if (!template) {
|
|
532
|
+
return Result.err(new McpError({
|
|
533
|
+
message: `Resource template not found: ${ref.uri}`,
|
|
534
|
+
code: -32601,
|
|
535
|
+
context: { uri: ref.uri }
|
|
536
|
+
}));
|
|
537
|
+
}
|
|
538
|
+
const handler = template.complete?.[argumentName];
|
|
539
|
+
if (!handler) {
|
|
540
|
+
return Result.ok({ values: [] });
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const result = await handler(value);
|
|
544
|
+
return Result.ok(result);
|
|
545
|
+
} catch (error) {
|
|
546
|
+
return Result.err(new McpError({
|
|
547
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
548
|
+
code: -32603,
|
|
549
|
+
context: { uri: ref.uri, argument: argumentName, thrown: true }
|
|
550
|
+
}));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return Result.err(new McpError({
|
|
554
|
+
message: "Invalid completion reference type",
|
|
555
|
+
code: -32602,
|
|
556
|
+
context: { ref }
|
|
557
|
+
}));
|
|
558
|
+
},
|
|
559
|
+
registerPrompt(prompt) {
|
|
560
|
+
logger.debug("Registering prompt", { name: prompt.name });
|
|
561
|
+
prompts.set(prompt.name, prompt);
|
|
562
|
+
logger.info("Prompt registered", { name: prompt.name });
|
|
563
|
+
if (sdkServer) {
|
|
564
|
+
sdkServer.sendPromptListChanged?.();
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
getPrompts() {
|
|
568
|
+
return Array.from(prompts.values()).map((p) => ({
|
|
569
|
+
name: p.name,
|
|
570
|
+
...p.description ? { description: p.description } : {},
|
|
571
|
+
arguments: p.arguments
|
|
572
|
+
}));
|
|
573
|
+
},
|
|
574
|
+
async getPrompt(promptName, args) {
|
|
575
|
+
const prompt = prompts.get(promptName);
|
|
576
|
+
if (!prompt) {
|
|
577
|
+
return Result.err(new McpError({
|
|
578
|
+
message: `Prompt not found: ${promptName}`,
|
|
579
|
+
code: -32601,
|
|
580
|
+
context: { prompt: promptName }
|
|
581
|
+
}));
|
|
582
|
+
}
|
|
583
|
+
for (const arg of prompt.arguments) {
|
|
584
|
+
if (arg.required && (args[arg.name] === undefined || args[arg.name] === "")) {
|
|
585
|
+
return Result.err(new McpError({
|
|
586
|
+
message: `Missing required argument: ${arg.name}`,
|
|
587
|
+
code: -32602,
|
|
588
|
+
context: { prompt: promptName, argument: arg.name }
|
|
589
|
+
}));
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
const result = await prompt.handler(args);
|
|
594
|
+
if (result.isErr()) {
|
|
595
|
+
return Result.err(translateError(result.error));
|
|
596
|
+
}
|
|
597
|
+
return Result.ok(result.value);
|
|
598
|
+
} catch (error) {
|
|
599
|
+
return Result.err(new McpError({
|
|
600
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
601
|
+
code: -32603,
|
|
602
|
+
context: { prompt: promptName, thrown: true }
|
|
603
|
+
}));
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
async readResource(uri) {
|
|
607
|
+
const resource = resources.get(uri);
|
|
608
|
+
if (resource) {
|
|
609
|
+
if (!resource.handler) {
|
|
610
|
+
return Result.err(new McpError({
|
|
611
|
+
message: `Resource not readable: ${uri}`,
|
|
612
|
+
code: -32002,
|
|
613
|
+
context: { uri }
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
const requestId = generateRequestId();
|
|
617
|
+
const ctx = {
|
|
618
|
+
requestId,
|
|
619
|
+
logger: logger.child({ resource: uri, requestId }),
|
|
620
|
+
cwd: process.cwd(),
|
|
621
|
+
env: process.env
|
|
622
|
+
};
|
|
623
|
+
try {
|
|
624
|
+
const result = await resource.handler(uri, ctx);
|
|
625
|
+
if (result.isErr()) {
|
|
626
|
+
return Result.err(translateError(result.error));
|
|
627
|
+
}
|
|
628
|
+
return Result.ok(result.value);
|
|
629
|
+
} catch (error) {
|
|
630
|
+
return Result.err(new McpError({
|
|
631
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
632
|
+
code: -32603,
|
|
633
|
+
context: { uri, thrown: true }
|
|
634
|
+
}));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
for (const template of resourceTemplates.values()) {
|
|
638
|
+
const variables = matchUriTemplate(template.uriTemplate, uri);
|
|
639
|
+
if (variables) {
|
|
640
|
+
const templateRequestId = generateRequestId();
|
|
641
|
+
const templateCtx = {
|
|
642
|
+
requestId: templateRequestId,
|
|
643
|
+
logger: logger.child({
|
|
644
|
+
resource: uri,
|
|
645
|
+
requestId: templateRequestId
|
|
646
|
+
}),
|
|
647
|
+
cwd: process.cwd(),
|
|
648
|
+
env: process.env
|
|
649
|
+
};
|
|
650
|
+
try {
|
|
651
|
+
const result = await template.handler(uri, variables, templateCtx);
|
|
652
|
+
if (result.isErr()) {
|
|
653
|
+
return Result.err(translateError(result.error));
|
|
654
|
+
}
|
|
655
|
+
return Result.ok(result.value);
|
|
656
|
+
} catch (error) {
|
|
657
|
+
return Result.err(new McpError({
|
|
658
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
659
|
+
code: -32603,
|
|
660
|
+
context: { uri, thrown: true }
|
|
661
|
+
}));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return Result.err(new McpError({
|
|
666
|
+
message: `Resource not found: ${uri}`,
|
|
667
|
+
code: -32002,
|
|
668
|
+
context: { uri }
|
|
669
|
+
}));
|
|
454
670
|
},
|
|
455
671
|
async invokeTool(toolName, input, invokeOptions) {
|
|
456
672
|
const requestId = invokeOptions?.requestId ?? generateRequestId();
|
|
@@ -481,7 +697,7 @@ function createMcpServer(options) {
|
|
|
481
697
|
}
|
|
482
698
|
}));
|
|
483
699
|
}
|
|
484
|
-
const ctx = createHandlerContext(toolName, requestId, invokeOptions?.signal);
|
|
700
|
+
const ctx = createHandlerContext(toolName, requestId, invokeOptions?.signal, invokeOptions?.progressToken);
|
|
485
701
|
try {
|
|
486
702
|
const result = await tool.handler(parseResult.data, ctx);
|
|
487
703
|
if (result.isErr()) {
|
|
@@ -513,6 +729,35 @@ function createMcpServer(options) {
|
|
|
513
729
|
}));
|
|
514
730
|
}
|
|
515
731
|
},
|
|
732
|
+
subscribe(uri) {
|
|
733
|
+
subscriptions.add(uri);
|
|
734
|
+
logger.debug("Resource subscription added", { uri });
|
|
735
|
+
},
|
|
736
|
+
unsubscribe(uri) {
|
|
737
|
+
subscriptions.delete(uri);
|
|
738
|
+
logger.debug("Resource subscription removed", { uri });
|
|
739
|
+
},
|
|
740
|
+
notifyResourceUpdated(uri) {
|
|
741
|
+
if (subscriptions.has(uri)) {
|
|
742
|
+
sdkServer?.sendResourceUpdated?.({ uri });
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
notifyToolsChanged() {
|
|
746
|
+
sdkServer?.sendToolListChanged?.();
|
|
747
|
+
},
|
|
748
|
+
notifyResourcesChanged() {
|
|
749
|
+
sdkServer?.sendResourceListChanged?.();
|
|
750
|
+
},
|
|
751
|
+
notifyPromptsChanged() {
|
|
752
|
+
sdkServer?.sendPromptListChanged?.();
|
|
753
|
+
},
|
|
754
|
+
setLogLevel(level) {
|
|
755
|
+
logger.debug("Client log level set", { level });
|
|
756
|
+
},
|
|
757
|
+
bindSdkServer(server2) {
|
|
758
|
+
sdkServer = server2;
|
|
759
|
+
logger.debug("SDK server bound for notifications");
|
|
760
|
+
},
|
|
516
761
|
async start() {
|
|
517
762
|
logger.info("MCP server starting", { name, version, tools: tools.size });
|
|
518
763
|
},
|
|
@@ -525,9 +770,44 @@ function createMcpServer(options) {
|
|
|
525
770
|
function defineTool(definition) {
|
|
526
771
|
return definition;
|
|
527
772
|
}
|
|
773
|
+
function escapeRegex(str) {
|
|
774
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
775
|
+
}
|
|
776
|
+
function matchUriTemplate(template, uri) {
|
|
777
|
+
const paramNames = [];
|
|
778
|
+
const parts = template.split(/(\{[^}]+\})/);
|
|
779
|
+
const regexSource = parts.map((part) => {
|
|
780
|
+
const paramMatch = part.match(/^\{([^}]+)\}$/);
|
|
781
|
+
if (paramMatch?.[1]) {
|
|
782
|
+
paramNames.push(paramMatch[1]);
|
|
783
|
+
return "([^/]+)";
|
|
784
|
+
}
|
|
785
|
+
return escapeRegex(part);
|
|
786
|
+
}).join("");
|
|
787
|
+
const regex = new RegExp(`^${regexSource}$`);
|
|
788
|
+
const match = uri.match(regex);
|
|
789
|
+
if (!match) {
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
const variables = {};
|
|
793
|
+
for (let i = 0;i < paramNames.length; i++) {
|
|
794
|
+
const name = paramNames[i];
|
|
795
|
+
const value = match[i + 1];
|
|
796
|
+
if (name !== undefined && value !== undefined) {
|
|
797
|
+
variables[name] = value;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return variables;
|
|
801
|
+
}
|
|
528
802
|
function defineResource(definition) {
|
|
529
803
|
return definition;
|
|
530
804
|
}
|
|
805
|
+
function defineResourceTemplate(definition) {
|
|
806
|
+
return definition;
|
|
807
|
+
}
|
|
808
|
+
function definePrompt(definition) {
|
|
809
|
+
return definition;
|
|
810
|
+
}
|
|
531
811
|
|
|
532
812
|
// src/actions.ts
|
|
533
813
|
function isActionRegistry(source) {
|
|
@@ -689,12 +969,55 @@ function createCoreTools(options = {}) {
|
|
|
689
969
|
defineQueryTool(options.query)
|
|
690
970
|
];
|
|
691
971
|
}
|
|
972
|
+
// src/logging.ts
|
|
973
|
+
var MCP_LEVEL_ORDER = [
|
|
974
|
+
"debug",
|
|
975
|
+
"info",
|
|
976
|
+
"notice",
|
|
977
|
+
"warning",
|
|
978
|
+
"error",
|
|
979
|
+
"critical",
|
|
980
|
+
"alert",
|
|
981
|
+
"emergency"
|
|
982
|
+
];
|
|
983
|
+
function mapLogLevelToMcp(level) {
|
|
984
|
+
switch (level) {
|
|
985
|
+
case "trace":
|
|
986
|
+
case "debug":
|
|
987
|
+
return "debug";
|
|
988
|
+
case "info":
|
|
989
|
+
return "info";
|
|
990
|
+
case "warn":
|
|
991
|
+
return "warning";
|
|
992
|
+
case "error":
|
|
993
|
+
return "error";
|
|
994
|
+
case "fatal":
|
|
995
|
+
return "emergency";
|
|
996
|
+
default: {
|
|
997
|
+
const _exhaustiveCheck = level;
|
|
998
|
+
return _exhaustiveCheck;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
function shouldEmitLog(messageLevel, threshold) {
|
|
1003
|
+
return MCP_LEVEL_ORDER.indexOf(messageLevel) >= MCP_LEVEL_ORDER.indexOf(threshold);
|
|
1004
|
+
}
|
|
692
1005
|
// src/transport.ts
|
|
693
1006
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
694
1007
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
695
1008
|
import {
|
|
696
1009
|
CallToolRequestSchema,
|
|
697
|
-
|
|
1010
|
+
CompleteRequestSchema,
|
|
1011
|
+
GetPromptRequestSchema,
|
|
1012
|
+
ListPromptsRequestSchema,
|
|
1013
|
+
ListResourcesRequestSchema,
|
|
1014
|
+
ListResourceTemplatesRequestSchema,
|
|
1015
|
+
ListToolsRequestSchema,
|
|
1016
|
+
ReadResourceRequestSchema,
|
|
1017
|
+
McpError as SdkMcpError,
|
|
1018
|
+
SetLevelRequestSchema,
|
|
1019
|
+
SubscribeRequestSchema,
|
|
1020
|
+
UnsubscribeRequestSchema
|
|
698
1021
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
699
1022
|
import { safeStringify } from "@outfitter/contracts";
|
|
700
1023
|
function isMcpToolResponse(value) {
|
|
@@ -751,19 +1074,93 @@ function wrapToolError(error) {
|
|
|
751
1074
|
isError: true
|
|
752
1075
|
};
|
|
753
1076
|
}
|
|
1077
|
+
function toSdkError(error) {
|
|
1078
|
+
return new SdkMcpError(error.code, error.message, error.context);
|
|
1079
|
+
}
|
|
754
1080
|
function createSdkServer(server) {
|
|
755
|
-
const
|
|
1081
|
+
const capabilities = {
|
|
1082
|
+
tools: { listChanged: true }
|
|
1083
|
+
};
|
|
1084
|
+
if (server.getResources().length > 0 || server.getResourceTemplates().length > 0) {
|
|
1085
|
+
capabilities["resources"] = { listChanged: true, subscribe: true };
|
|
1086
|
+
}
|
|
1087
|
+
if (server.getPrompts().length > 0) {
|
|
1088
|
+
capabilities["prompts"] = { listChanged: true };
|
|
1089
|
+
}
|
|
1090
|
+
capabilities["completions"] = {};
|
|
1091
|
+
capabilities["logging"] = {};
|
|
1092
|
+
const sdkServer = new Server({ name: server.name, version: server.version }, { capabilities });
|
|
756
1093
|
sdkServer.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
757
1094
|
tools: server.getTools()
|
|
758
1095
|
}));
|
|
759
1096
|
sdkServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
760
1097
|
const { name, arguments: args } = request.params;
|
|
761
|
-
const
|
|
1098
|
+
const progressToken = request.params._meta?.progressToken;
|
|
1099
|
+
const options = progressToken !== undefined ? { progressToken } : undefined;
|
|
1100
|
+
const result = await server.invokeTool(name, args ?? {}, options);
|
|
762
1101
|
if (result.isErr()) {
|
|
763
1102
|
return wrapToolError(result.error);
|
|
764
1103
|
}
|
|
765
1104
|
return wrapToolResult(result.value);
|
|
766
1105
|
});
|
|
1106
|
+
sdkServer.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
1107
|
+
resources: server.getResources().map((r) => ({
|
|
1108
|
+
uri: r.uri,
|
|
1109
|
+
name: r.name,
|
|
1110
|
+
...r.description ? { description: r.description } : {},
|
|
1111
|
+
...r.mimeType ? { mimeType: r.mimeType } : {}
|
|
1112
|
+
}))
|
|
1113
|
+
}));
|
|
1114
|
+
sdkServer.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
1115
|
+
resourceTemplates: server.getResourceTemplates().map((t) => ({
|
|
1116
|
+
uriTemplate: t.uriTemplate,
|
|
1117
|
+
name: t.name,
|
|
1118
|
+
...t.description ? { description: t.description } : {},
|
|
1119
|
+
...t.mimeType ? { mimeType: t.mimeType } : {}
|
|
1120
|
+
}))
|
|
1121
|
+
}));
|
|
1122
|
+
sdkServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1123
|
+
const { uri } = request.params;
|
|
1124
|
+
const result = await server.readResource(uri);
|
|
1125
|
+
if (result.isErr()) {
|
|
1126
|
+
throw toSdkError(result.error);
|
|
1127
|
+
}
|
|
1128
|
+
return { contents: result.value };
|
|
1129
|
+
});
|
|
1130
|
+
sdkServer.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
1131
|
+
server.subscribe(request.params.uri);
|
|
1132
|
+
return {};
|
|
1133
|
+
});
|
|
1134
|
+
sdkServer.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
1135
|
+
server.unsubscribe(request.params.uri);
|
|
1136
|
+
return {};
|
|
1137
|
+
});
|
|
1138
|
+
sdkServer.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
1139
|
+
prompts: server.getPrompts()
|
|
1140
|
+
}));
|
|
1141
|
+
sdkServer.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1142
|
+
const { name, arguments: args } = request.params;
|
|
1143
|
+
const result = await server.getPrompt(name, args ?? {});
|
|
1144
|
+
if (result.isErr()) {
|
|
1145
|
+
throw toSdkError(result.error);
|
|
1146
|
+
}
|
|
1147
|
+
return { ...result.value };
|
|
1148
|
+
});
|
|
1149
|
+
sdkServer.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
1150
|
+
const { ref, argument } = request.params;
|
|
1151
|
+
const completionRef = ref.type === "ref/prompt" ? { type: "ref/prompt", name: ref.name } : { type: "ref/resource", uri: ref.uri };
|
|
1152
|
+
const result = await server.complete(completionRef, argument.name, argument.value);
|
|
1153
|
+
if (result.isErr()) {
|
|
1154
|
+
throw toSdkError(result.error);
|
|
1155
|
+
}
|
|
1156
|
+
return { completion: result.value };
|
|
1157
|
+
});
|
|
1158
|
+
sdkServer.setRequestHandler(SetLevelRequestSchema, async (request) => {
|
|
1159
|
+
const level = request.params.level;
|
|
1160
|
+
server.setLogLevel?.(level);
|
|
1161
|
+
return {};
|
|
1162
|
+
});
|
|
1163
|
+
server.bindSdkServer?.(sdkServer);
|
|
767
1164
|
return sdkServer;
|
|
768
1165
|
}
|
|
769
1166
|
async function connectStdio(server, transport = new StdioServerTransport) {
|
|
@@ -773,9 +1170,13 @@ async function connectStdio(server, transport = new StdioServerTransport) {
|
|
|
773
1170
|
}
|
|
774
1171
|
export {
|
|
775
1172
|
zodToJsonSchema,
|
|
1173
|
+
shouldEmitLog,
|
|
1174
|
+
mapLogLevelToMcp,
|
|
776
1175
|
defineTool,
|
|
1176
|
+
defineResourceTemplate,
|
|
777
1177
|
defineResource,
|
|
778
1178
|
defineQueryTool,
|
|
1179
|
+
definePrompt,
|
|
779
1180
|
defineDocsTool,
|
|
780
1181
|
defineConfigTool,
|
|
781
1182
|
createSdkServer,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outfitter/mcp",
|
|
3
3
|
"description": "MCP server framework with typed tools for Outfitter",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
43
|
-
"@outfitter/contracts": "0.
|
|
44
|
-
"@outfitter/logging": "0.
|
|
43
|
+
"@outfitter/contracts": "0.2.0",
|
|
44
|
+
"@outfitter/logging": "0.2.0",
|
|
45
45
|
"zod": "^4.3.5"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|