@outfitter/mcp 0.4.3 → 0.5.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 +92 -0
- package/dist/actions.d.ts +7 -2
- package/dist/actions.js +35 -9
- package/dist/core-tools.d.ts +7 -2
- package/dist/index.d.ts +12 -6
- package/dist/index.js +1 -0
- package/dist/internal/content-types.d.ts +2 -0
- package/dist/internal/content-types.js +1 -0
- package/dist/internal/log-config.d.ts +24 -0
- package/dist/internal/log-config.js +13 -0
- package/dist/internal/prompt-types.d.ts +3 -0
- package/dist/internal/prompt-types.js +1 -0
- package/dist/internal/resource-types.d.ts +4 -0
- package/dist/internal/resource-types.js +1 -0
- package/dist/internal/server-types.d.ts +7 -0
- package/dist/internal/server-types.js +20 -0
- package/dist/internal/tool-types.d.ts +2 -0
- package/dist/{shared/@outfitter/mcp-9m5hs2z0.js → internal/tool-types.js} +4 -16
- package/dist/internal/uri-template.d.ts +8 -0
- package/dist/internal/uri-template.js +7 -0
- package/dist/progress.d.ts +2 -0
- package/dist/progress.js +7 -0
- package/dist/server.d.ts +7 -2
- package/dist/server.js +5 -2
- package/dist/shared/@outfitter/{mcp-knq080yt.d.ts → mcp-3hxaatj9.d.ts} +37 -6
- package/dist/shared/@outfitter/{mcp-s6afm4ff.d.ts → mcp-4s22693j.d.ts} +1 -1
- package/dist/shared/@outfitter/mcp-7btcghjj.d.ts +304 -0
- package/dist/shared/@outfitter/mcp-9ry52yg3.d.ts +187 -0
- package/dist/shared/@outfitter/mcp-dgwj3jna.d.ts +103 -0
- package/dist/shared/@outfitter/{mcp-d8vs6vry.d.ts → mcp-f67dnr72.d.ts} +1 -1
- package/dist/shared/@outfitter/mcp-knc1gq0g.d.ts +130 -0
- package/dist/shared/@outfitter/mcp-n9vzcp37.js +55 -0
- package/dist/shared/@outfitter/mcp-q5hr7227.d.ts +24 -0
- package/dist/shared/@outfitter/mcp-q70dtfj6.js +53 -0
- package/dist/shared/@outfitter/mcp-r27vbpc1.d.ts +45 -0
- package/dist/shared/@outfitter/mcp-s2vnhzav.js +2 -0
- package/dist/shared/@outfitter/{mcp-7kcw2814.d.ts → mcp-yf0w5cgh.d.ts} +1 -1
- package/dist/shared/@outfitter/{mcp-b502y16n.js → mcp-yf1n85e9.js} +76 -116
- package/dist/shared/@outfitter/mcp-zt2s3r38.js +33 -0
- package/dist/transport.d.ts +7 -2
- package/dist/types.d.ts +7 -2
- package/dist/types.js +1 -1
- package/package.json +26 -20
- package/dist/shared/@outfitter/mcp-gqjg15f5.d.ts +0 -669
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Handler, OutfitterError } from "@outfitter/contracts";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Behavioral hints for MCP tools.
|
|
5
|
+
*
|
|
6
|
+
* Annotations help clients understand tool behavior without invoking them.
|
|
7
|
+
* All fields are optional — only include hints that apply.
|
|
8
|
+
*
|
|
9
|
+
* @see https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/#annotations
|
|
10
|
+
*/
|
|
11
|
+
interface ToolAnnotations {
|
|
12
|
+
/** When true, the tool may perform destructive operations (e.g., deleting data). */
|
|
13
|
+
destructiveHint?: boolean;
|
|
14
|
+
/** When true, calling the tool multiple times with the same input has the same effect. */
|
|
15
|
+
idempotentHint?: boolean;
|
|
16
|
+
/** When true, the tool may interact with external systems beyond the server. */
|
|
17
|
+
openWorldHint?: boolean;
|
|
18
|
+
/** When true, the tool does not modify any state. */
|
|
19
|
+
readOnlyHint?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Common annotation presets for MCP tools.
|
|
23
|
+
*
|
|
24
|
+
* Use these as a starting point and spread-override individual hints:
|
|
25
|
+
*
|
|
26
|
+
* ```typescript
|
|
27
|
+
* annotations: { ...TOOL_ANNOTATIONS.readOnly, openWorldHint: true }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* For multi-action tools (e.g., a single tool with read and write actions),
|
|
31
|
+
* use the most conservative union of hints — if any action is destructive,
|
|
32
|
+
* mark the whole tool as destructive. Per-action annotations are an MCP spec
|
|
33
|
+
* limitation; presets + spread cover most edge cases.
|
|
34
|
+
*/
|
|
35
|
+
declare const TOOL_ANNOTATIONS: {
|
|
36
|
+
readonly destructive: ToolAnnotations;
|
|
37
|
+
readonly openWorld: ToolAnnotations;
|
|
38
|
+
readonly readOnly: ToolAnnotations;
|
|
39
|
+
readonly write: ToolAnnotations;
|
|
40
|
+
readonly writeIdempotent: ToolAnnotations;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Definition of an MCP tool that can be invoked by clients.
|
|
44
|
+
*
|
|
45
|
+
* Tools are the primary way clients interact with MCP servers.
|
|
46
|
+
* Each tool has a name, description, input schema (for validation),
|
|
47
|
+
* and a handler function that processes requests.
|
|
48
|
+
*
|
|
49
|
+
* @typeParam TInput - The validated input type (inferred from Zod schema)
|
|
50
|
+
* @typeParam TOutput - The success output type
|
|
51
|
+
* @typeParam TError - The error type (must extend OutfitterError)
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const getUserTool: ToolDefinition<
|
|
56
|
+
* { userId: string },
|
|
57
|
+
* { name: string; email: string },
|
|
58
|
+
* NotFoundError
|
|
59
|
+
* > = {
|
|
60
|
+
* name: "get-user",
|
|
61
|
+
* description: "Retrieve a user by ID",
|
|
62
|
+
* inputSchema: z.object({ userId: z.string().uuid() }),
|
|
63
|
+
* handler: async (input, ctx) => {
|
|
64
|
+
* ctx.logger.debug("Fetching user", { userId: input.userId });
|
|
65
|
+
* const user = await db.users.find(input.userId);
|
|
66
|
+
* if (!user) {
|
|
67
|
+
* return Result.err(new NotFoundError({
|
|
68
|
+
* message: `User ${input.userId} not found`,
|
|
69
|
+
* resourceType: "user",
|
|
70
|
+
* resourceId: input.userId,
|
|
71
|
+
* }));
|
|
72
|
+
* }
|
|
73
|
+
* return Result.ok({ name: user.name, email: user.email });
|
|
74
|
+
* },
|
|
75
|
+
* };
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
interface ToolDefinition<
|
|
79
|
+
TInput,
|
|
80
|
+
TOutput,
|
|
81
|
+
TError extends OutfitterError = OutfitterError
|
|
82
|
+
> {
|
|
83
|
+
/**
|
|
84
|
+
* Optional behavioral annotations for the tool.
|
|
85
|
+
* Helps clients understand tool behavior without invoking it.
|
|
86
|
+
*/
|
|
87
|
+
annotations?: ToolAnnotations;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the tool should be deferred for tool search.
|
|
90
|
+
* Defaults to true for domain tools; core tools set this to false.
|
|
91
|
+
*/
|
|
92
|
+
deferLoading?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Human-readable description of what the tool does.
|
|
95
|
+
* Shown to clients and used by LLMs to understand tool capabilities.
|
|
96
|
+
*/
|
|
97
|
+
description: string;
|
|
98
|
+
/**
|
|
99
|
+
* Handler function that processes the tool invocation.
|
|
100
|
+
* Receives validated input and HandlerContext, returns Result.
|
|
101
|
+
*/
|
|
102
|
+
handler: Handler<TInput, TOutput, TError>;
|
|
103
|
+
/**
|
|
104
|
+
* Zod schema for validating and parsing input.
|
|
105
|
+
* The schema defines the expected input structure.
|
|
106
|
+
*/
|
|
107
|
+
inputSchema: z.ZodType<TInput>;
|
|
108
|
+
/**
|
|
109
|
+
* Unique tool name (kebab-case recommended).
|
|
110
|
+
* Used by clients to invoke the tool.
|
|
111
|
+
*/
|
|
112
|
+
name: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Serialized tool information for MCP protocol.
|
|
116
|
+
* This is the format sent to clients during tool listing.
|
|
117
|
+
*/
|
|
118
|
+
interface SerializedTool {
|
|
119
|
+
/** Behavioral annotations for the tool */
|
|
120
|
+
annotations?: ToolAnnotations;
|
|
121
|
+
/** MCP tool-search hint: whether tool is deferred */
|
|
122
|
+
defer_loading?: boolean;
|
|
123
|
+
/** Tool description */
|
|
124
|
+
description: string;
|
|
125
|
+
/** JSON Schema representation of the input schema */
|
|
126
|
+
inputSchema: Record<string, unknown>;
|
|
127
|
+
/** Tool name */
|
|
128
|
+
name: string;
|
|
129
|
+
}
|
|
130
|
+
export { ToolAnnotations, TOOL_ANNOTATIONS, ToolDefinition, SerializedTool };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/mcp/src/internal/log-config.ts
|
|
3
|
+
import { getEnvironment, getEnvironmentDefaults } from "@outfitter/config";
|
|
4
|
+
import { createPrettyFormatter } from "@outfitter/logging";
|
|
5
|
+
var VALID_MCP_LOG_LEVELS = new Set([
|
|
6
|
+
"debug",
|
|
7
|
+
"info",
|
|
8
|
+
"notice",
|
|
9
|
+
"warning",
|
|
10
|
+
"error",
|
|
11
|
+
"critical",
|
|
12
|
+
"alert",
|
|
13
|
+
"emergency"
|
|
14
|
+
]);
|
|
15
|
+
var DEFAULTS_TO_MCP = {
|
|
16
|
+
debug: "debug",
|
|
17
|
+
info: "info",
|
|
18
|
+
warn: "warning",
|
|
19
|
+
error: "error"
|
|
20
|
+
};
|
|
21
|
+
function createDefaultMcpSink() {
|
|
22
|
+
const formatter = createPrettyFormatter({ colors: false });
|
|
23
|
+
return {
|
|
24
|
+
formatter,
|
|
25
|
+
write(record, formatted) {
|
|
26
|
+
const serialized = formatted ?? formatter.format(record);
|
|
27
|
+
const line = serialized.endsWith(`
|
|
28
|
+
`) ? serialized : `${serialized}
|
|
29
|
+
`;
|
|
30
|
+
if (typeof process !== "undefined" && process.stderr?.write) {
|
|
31
|
+
process.stderr.write(line);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function resolveDefaultLogLevel(options) {
|
|
37
|
+
const envLogLevel = process.env["OUTFITTER_LOG_LEVEL"];
|
|
38
|
+
if (envLogLevel !== undefined && VALID_MCP_LOG_LEVELS.has(envLogLevel)) {
|
|
39
|
+
return envLogLevel;
|
|
40
|
+
}
|
|
41
|
+
if (options.defaultLogLevel !== undefined && (options.defaultLogLevel === null || VALID_MCP_LOG_LEVELS.has(options.defaultLogLevel))) {
|
|
42
|
+
return options.defaultLogLevel;
|
|
43
|
+
}
|
|
44
|
+
const env = getEnvironment();
|
|
45
|
+
const defaults = getEnvironmentDefaults(env);
|
|
46
|
+
if (defaults.logLevel !== null) {
|
|
47
|
+
const mapped = DEFAULTS_TO_MCP[defaults.logLevel];
|
|
48
|
+
if (mapped !== undefined) {
|
|
49
|
+
return mapped;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { VALID_MCP_LOG_LEVELS, DEFAULTS_TO_MCP, createDefaultMcpSink, resolveDefaultLogLevel };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared content type definitions used across prompt and resource types.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Annotations for content items (resource content, prompt messages).
|
|
8
|
+
*
|
|
9
|
+
* Provides hints about content audience and priority.
|
|
10
|
+
*
|
|
11
|
+
* @see https://spec.modelcontextprotocol.io/specification/2025-03-26/server/utilities/annotations/
|
|
12
|
+
*/
|
|
13
|
+
interface ContentAnnotations {
|
|
14
|
+
/**
|
|
15
|
+
* Who the content is intended for.
|
|
16
|
+
* Can include "user", "assistant", or both.
|
|
17
|
+
*/
|
|
18
|
+
audience?: Array<"user" | "assistant">;
|
|
19
|
+
/**
|
|
20
|
+
* Priority level from 0.0 (least) to 1.0 (most important).
|
|
21
|
+
*/
|
|
22
|
+
priority?: number;
|
|
23
|
+
}
|
|
24
|
+
export { ContentAnnotations };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/mcp/src/progress.ts
|
|
3
|
+
function mapStreamEventToNotification(token, event, latestProgress) {
|
|
4
|
+
switch (event.type) {
|
|
5
|
+
case "start": {
|
|
6
|
+
return {
|
|
7
|
+
method: "notifications/progress",
|
|
8
|
+
params: {
|
|
9
|
+
progressToken: token,
|
|
10
|
+
progress: latestProgress,
|
|
11
|
+
message: `[start] ${event.command}`
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
case "step": {
|
|
16
|
+
const durationSuffix = event.duration_ms !== undefined ? ` (${event.duration_ms}ms)` : "";
|
|
17
|
+
return {
|
|
18
|
+
method: "notifications/progress",
|
|
19
|
+
params: {
|
|
20
|
+
progressToken: token,
|
|
21
|
+
progress: latestProgress,
|
|
22
|
+
message: `[step] ${event.name}: ${event.status}${durationSuffix}`
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
case "progress": {
|
|
27
|
+
const params = {
|
|
28
|
+
progressToken: token,
|
|
29
|
+
progress: event.current,
|
|
30
|
+
total: event.total
|
|
31
|
+
};
|
|
32
|
+
if (event.message !== undefined) {
|
|
33
|
+
params.message = event.message;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
method: "notifications/progress",
|
|
37
|
+
params
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function createMcpProgressCallback(progressToken, send) {
|
|
43
|
+
let latestProgress = 0;
|
|
44
|
+
return (event) => {
|
|
45
|
+
if (event.type === "progress") {
|
|
46
|
+
latestProgress = event.current;
|
|
47
|
+
}
|
|
48
|
+
const notification = mapStreamEventToNotification(progressToken, event, latestProgress);
|
|
49
|
+
send(notification);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { createMcpProgressCallback };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ProgressCallback } from "@outfitter/contracts/stream";
|
|
2
|
+
/**
|
|
3
|
+
* Function signature for sending a raw MCP notification.
|
|
4
|
+
*
|
|
5
|
+
* Matches the `sdkServer.notification()` method shape from the MCP SDK.
|
|
6
|
+
*/
|
|
7
|
+
type McpNotificationSender = (notification: unknown) => void;
|
|
8
|
+
/**
|
|
9
|
+
* MCP progress notification payload matching the MCP specification.
|
|
10
|
+
*
|
|
11
|
+
* @see https://spec.modelcontextprotocol.io/specification/2025-03-26/server/utilities/progress/
|
|
12
|
+
*/
|
|
13
|
+
interface McpProgressNotification {
|
|
14
|
+
method: "notifications/progress";
|
|
15
|
+
params: {
|
|
16
|
+
progressToken: string | number;
|
|
17
|
+
progress: number;
|
|
18
|
+
total?: number;
|
|
19
|
+
message?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a {@link ProgressCallback} that emits MCP `notifications/progress`.
|
|
24
|
+
*
|
|
25
|
+
* Each call to the returned callback translates a {@link StreamEvent} into
|
|
26
|
+
* an MCP progress notification and sends it via the provided sender function.
|
|
27
|
+
*
|
|
28
|
+
* @param progressToken - The progress token from the MCP client request
|
|
29
|
+
* @param send - Function to send the raw MCP notification (typically `sdkServer.notification`)
|
|
30
|
+
* @returns A `ProgressCallback` compatible with `ctx.progress`
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const progress = createMcpProgressCallback(
|
|
35
|
+
* "tok-123",
|
|
36
|
+
* (n) => sdkServer.notification(n)
|
|
37
|
+
* );
|
|
38
|
+
*
|
|
39
|
+
* // In handler:
|
|
40
|
+
* ctx.progress?.({ type: "start", command: "deploy", ts: new Date().toISOString() });
|
|
41
|
+
* ctx.progress?.({ type: "progress", current: 5, total: 10 });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function createMcpProgressCallback(progressToken: string | number, send: McpNotificationSender): ProgressCallback;
|
|
45
|
+
export { McpNotificationSender, McpProgressNotification, createMcpProgressCallback };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from "./mcp-
|
|
1
|
+
import { McpServer } from "./mcp-7btcghjj.js";
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -1,70 +1,33 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
VALID_MCP_LOG_LEVELS,
|
|
4
|
+
createDefaultMcpSink,
|
|
5
|
+
resolveDefaultLogLevel
|
|
6
|
+
} from "./mcp-n9vzcp37.js";
|
|
7
|
+
import {
|
|
8
|
+
matchUriTemplate
|
|
9
|
+
} from "./mcp-zt2s3r38.js";
|
|
10
|
+
import {
|
|
11
|
+
createMcpProgressCallback
|
|
12
|
+
} from "./mcp-q70dtfj6.js";
|
|
2
13
|
import {
|
|
3
14
|
shouldEmitLog
|
|
4
15
|
} from "./mcp-fjtxsa0x.js";
|
|
5
16
|
import {
|
|
6
17
|
McpError
|
|
7
|
-
} from "./mcp-
|
|
18
|
+
} from "./mcp-s2vnhzav.js";
|
|
8
19
|
import {
|
|
9
20
|
zodToJsonSchema
|
|
10
21
|
} from "./mcp-hw5wz4gb.js";
|
|
11
22
|
|
|
12
23
|
// packages/mcp/src/server.ts
|
|
13
|
-
import { getEnvironment, getEnvironmentDefaults } from "@outfitter/config";
|
|
14
|
-
import { generateRequestId, Result } from "@outfitter/contracts";
|
|
15
24
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
}
|
|
25
|
+
formatZodIssues,
|
|
26
|
+
generateRequestId,
|
|
27
|
+
Result,
|
|
28
|
+
ValidationError
|
|
29
|
+
} from "@outfitter/contracts";
|
|
30
|
+
import { createOutfitterLoggerFactory } from "@outfitter/logging";
|
|
68
31
|
function createMcpServer(options) {
|
|
69
32
|
const { name, version, logger: providedLogger } = options;
|
|
70
33
|
let loggerFactory = null;
|
|
@@ -84,10 +47,10 @@ function createMcpServer(options) {
|
|
|
84
47
|
let sdkServer = null;
|
|
85
48
|
const subscriptions = new Set;
|
|
86
49
|
let clientLogLevel = resolveDefaultLogLevel(options);
|
|
87
|
-
function createHandlerContext(
|
|
50
|
+
function createHandlerContext(label, requestId, signal, progressToken, loggerMeta) {
|
|
88
51
|
const ctx = {
|
|
89
52
|
requestId,
|
|
90
|
-
logger: logger.child({ tool:
|
|
53
|
+
logger: logger.child(loggerMeta ?? { tool: label, requestId }),
|
|
91
54
|
cwd: process.cwd(),
|
|
92
55
|
env: process.env
|
|
93
56
|
};
|
|
@@ -95,19 +58,27 @@ function createMcpServer(options) {
|
|
|
95
58
|
ctx.signal = signal;
|
|
96
59
|
}
|
|
97
60
|
if (progressToken !== undefined && sdkServer) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
...total !== undefined ? { total } : {},
|
|
106
|
-
...message ? { message } : {}
|
|
107
|
-
}
|
|
61
|
+
const sender = (notification) => {
|
|
62
|
+
const maybePromise = sdkServer?.notification?.(notification);
|
|
63
|
+
if (typeof maybePromise === "object" && maybePromise !== null && "then" in maybePromise) {
|
|
64
|
+
maybePromise.catch((error) => {
|
|
65
|
+
logger.warn("Failed to send MCP progress notification", {
|
|
66
|
+
error: error instanceof Error ? error.message : String(error)
|
|
67
|
+
});
|
|
108
68
|
});
|
|
109
69
|
}
|
|
110
70
|
};
|
|
71
|
+
const streamProgress = createMcpProgressCallback(progressToken, sender);
|
|
72
|
+
const progress = streamProgress;
|
|
73
|
+
progress.report = (value, total, message) => {
|
|
74
|
+
streamProgress({
|
|
75
|
+
type: "progress",
|
|
76
|
+
current: value,
|
|
77
|
+
total: total ?? value,
|
|
78
|
+
...message !== undefined ? { message } : {}
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
ctx.progress = progress;
|
|
111
82
|
}
|
|
112
83
|
return ctx;
|
|
113
84
|
}
|
|
@@ -125,13 +96,17 @@ function createMcpServer(options) {
|
|
|
125
96
|
cancelled: -32603
|
|
126
97
|
};
|
|
127
98
|
const code = codeMap[error.category] ?? -32603;
|
|
99
|
+
const context = {
|
|
100
|
+
originalTag: error._tag,
|
|
101
|
+
category: error.category
|
|
102
|
+
};
|
|
103
|
+
if (error._tag === "ValidationError" && "field" in error && typeof error.field === "string") {
|
|
104
|
+
context["field"] = error.field;
|
|
105
|
+
}
|
|
128
106
|
return new McpError({
|
|
129
107
|
message: error.message,
|
|
130
108
|
code,
|
|
131
|
-
context
|
|
132
|
-
originalTag: error._tag,
|
|
133
|
-
category: error.category
|
|
134
|
-
}
|
|
109
|
+
context
|
|
135
110
|
});
|
|
136
111
|
}
|
|
137
112
|
const server = {
|
|
@@ -322,12 +297,10 @@ function createMcpServer(options) {
|
|
|
322
297
|
}));
|
|
323
298
|
}
|
|
324
299
|
const requestId = generateRequestId();
|
|
325
|
-
const ctx = {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
env: process.env
|
|
330
|
-
};
|
|
300
|
+
const ctx = createHandlerContext(uri, requestId, undefined, undefined, {
|
|
301
|
+
resource: uri,
|
|
302
|
+
requestId
|
|
303
|
+
});
|
|
331
304
|
try {
|
|
332
305
|
const result = await resource.handler(uri, ctx);
|
|
333
306
|
if (result.isErr()) {
|
|
@@ -346,18 +319,15 @@ function createMcpServer(options) {
|
|
|
346
319
|
const variables = matchUriTemplate(template.uriTemplate, uri);
|
|
347
320
|
if (variables) {
|
|
348
321
|
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
|
-
};
|
|
322
|
+
const templateCtx = createHandlerContext(uri, templateRequestId, undefined, undefined, { resource: uri, requestId: templateRequestId });
|
|
358
323
|
try {
|
|
359
324
|
const result = await template.handler(uri, variables, templateCtx);
|
|
360
325
|
if (result.isErr()) {
|
|
326
|
+
logger.warn("Resource template handler returned error", {
|
|
327
|
+
uri,
|
|
328
|
+
requestId: templateRequestId,
|
|
329
|
+
error: result.error._tag
|
|
330
|
+
});
|
|
361
331
|
return Result.err(translateError(result.error));
|
|
362
332
|
}
|
|
363
333
|
return Result.ok(result.value);
|
|
@@ -390,7 +360,7 @@ function createMcpServer(options) {
|
|
|
390
360
|
}
|
|
391
361
|
const parseResult = tool.zodSchema.safeParse(input);
|
|
392
362
|
if (!parseResult.success) {
|
|
393
|
-
const errorMessages = parseResult.error.issues
|
|
363
|
+
const errorMessages = formatZodIssues(parseResult.error.issues);
|
|
394
364
|
logger.warn("Input validation failed", {
|
|
395
365
|
tool: toolName,
|
|
396
366
|
requestId,
|
|
@@ -460,6 +430,10 @@ function createMcpServer(options) {
|
|
|
460
430
|
sdkServer?.sendPromptListChanged?.();
|
|
461
431
|
},
|
|
462
432
|
setLogLevel(level) {
|
|
433
|
+
if (!VALID_MCP_LOG_LEVELS.has(level)) {
|
|
434
|
+
logger.warn("Ignoring invalid client log level", { level });
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
463
437
|
clientLogLevel = level;
|
|
464
438
|
logger.debug("Client log level set", { level });
|
|
465
439
|
},
|
|
@@ -496,39 +470,25 @@ function createMcpServer(options) {
|
|
|
496
470
|
function defineTool(definition) {
|
|
497
471
|
return definition;
|
|
498
472
|
}
|
|
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
473
|
function defineResource(definition) {
|
|
529
474
|
return definition;
|
|
530
475
|
}
|
|
531
476
|
function defineResourceTemplate(definition) {
|
|
477
|
+
if ("paramSchema" in definition && definition.paramSchema !== undefined) {
|
|
478
|
+
const { paramSchema, handler: typedHandler, ...rest } = definition;
|
|
479
|
+
const wrappedHandler = async (uri, variables, ctx) => {
|
|
480
|
+
const parseResult = paramSchema.safeParse(variables);
|
|
481
|
+
if (!parseResult.success) {
|
|
482
|
+
const errorMessages = formatZodIssues(parseResult.error.issues);
|
|
483
|
+
return Result.err(new ValidationError({
|
|
484
|
+
message: `Invalid resource parameters: ${errorMessages}`,
|
|
485
|
+
field: "params"
|
|
486
|
+
}));
|
|
487
|
+
}
|
|
488
|
+
return typedHandler(uri, parseResult.data, ctx);
|
|
489
|
+
};
|
|
490
|
+
return { ...rest, handler: wrappedHandler };
|
|
491
|
+
}
|
|
532
492
|
return definition;
|
|
533
493
|
}
|
|
534
494
|
function definePrompt(definition) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/mcp/src/internal/uri-template.ts
|
|
3
|
+
function escapeRegex(str) {
|
|
4
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5
|
+
}
|
|
6
|
+
function matchUriTemplate(template, uri) {
|
|
7
|
+
const paramNames = [];
|
|
8
|
+
const parts = template.split(/(\{[^}]+\})/);
|
|
9
|
+
const regexSource = parts.map((part) => {
|
|
10
|
+
const paramMatch = part.match(/^\{([^}]+)\}$/);
|
|
11
|
+
if (paramMatch?.[1]) {
|
|
12
|
+
paramNames.push(paramMatch[1]);
|
|
13
|
+
return "([^/]+)";
|
|
14
|
+
}
|
|
15
|
+
return escapeRegex(part);
|
|
16
|
+
}).join("");
|
|
17
|
+
const regex = new RegExp(`^${regexSource}$`);
|
|
18
|
+
const match = uri.match(regex);
|
|
19
|
+
if (!match) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const variables = {};
|
|
23
|
+
for (let i = 0;i < paramNames.length; i++) {
|
|
24
|
+
const name = paramNames[i];
|
|
25
|
+
const value = match[i + 1];
|
|
26
|
+
if (name !== undefined && value !== undefined) {
|
|
27
|
+
variables[name] = value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return variables;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { matchUriTemplate };
|
package/dist/transport.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import { McpToolResponse, connectStdio, createSdkServer, wrapToolError, wrapToolResult } from "./shared/@outfitter/mcp-
|
|
2
|
-
import "./shared/@outfitter/mcp-
|
|
1
|
+
import { McpToolResponse, connectStdio, createSdkServer, wrapToolError, wrapToolResult } from "./shared/@outfitter/mcp-yf0w5cgh.js";
|
|
2
|
+
import "./shared/@outfitter/mcp-qmdmgxa1.js";
|
|
3
|
+
import "./shared/@outfitter/mcp-7btcghjj.js";
|
|
4
|
+
import "./shared/@outfitter/mcp-knc1gq0g.js";
|
|
5
|
+
import "./shared/@outfitter/mcp-9ry52yg3.js";
|
|
6
|
+
import "./shared/@outfitter/mcp-dgwj3jna.js";
|
|
7
|
+
import "./shared/@outfitter/mcp-q5hr7227.js";
|
|
3
8
|
import "./shared/@outfitter/mcp-cqpyer9m.js";
|
|
4
9
|
export { wrapToolResult, wrapToolError, createSdkServer, connectStdio, McpToolResponse };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./shared/@outfitter/mcp-qmdmgxa1.js";
|
|
2
|
+
import { InvokeToolOptions, McpError, McpHandlerContext, McpServer, McpServerOptions, ProgressReporter, Result, TaggedError, adaptHandler } from "./shared/@outfitter/mcp-7btcghjj.js";
|
|
3
|
+
import { SerializedTool, TOOL_ANNOTATIONS, ToolAnnotations, ToolDefinition } from "./shared/@outfitter/mcp-knc1gq0g.js";
|
|
4
|
+
import { BlobResourceContent, ResourceContent, ResourceDefinition, ResourceReadHandler, ResourceTemplateDefinition, ResourceTemplateReadHandler, TextResourceContent, TypedResourceTemplateDefinition, TypedResourceTemplateReadHandler } from "./shared/@outfitter/mcp-9ry52yg3.js";
|
|
5
|
+
import { CompletionHandler, CompletionRef, CompletionResult, PromptArgument, PromptDefinition, PromptHandler, PromptMessage, PromptMessageContent, PromptResult } from "./shared/@outfitter/mcp-dgwj3jna.js";
|
|
6
|
+
import { ContentAnnotations } from "./shared/@outfitter/mcp-q5hr7227.js";
|
|
2
7
|
import "./shared/@outfitter/mcp-cqpyer9m.js";
|
|
3
|
-
export { adaptHandler, ToolDefinition, ToolAnnotations, TextResourceContent, TaggedError, TOOL_ANNOTATIONS, SerializedTool, Result, ResourceTemplateReadHandler, ResourceTemplateDefinition, ResourceReadHandler, ResourceDefinition, ResourceContent, PromptResult, PromptMessageContent, PromptMessage, PromptHandler, PromptDefinition, PromptArgument, ProgressReporter, McpServerOptions, McpServer, McpHandlerContext, McpError, InvokeToolOptions, ContentAnnotations, CompletionResult, CompletionRef, CompletionHandler, BlobResourceContent };
|
|
8
|
+
export { adaptHandler, TypedResourceTemplateReadHandler, TypedResourceTemplateDefinition, ToolDefinition, ToolAnnotations, TextResourceContent, TaggedError, TOOL_ANNOTATIONS, SerializedTool, Result, ResourceTemplateReadHandler, ResourceTemplateDefinition, ResourceReadHandler, ResourceDefinition, ResourceContent, PromptResult, PromptMessageContent, PromptMessage, PromptHandler, PromptDefinition, PromptArgument, ProgressReporter, McpServerOptions, McpServer, McpHandlerContext, McpError, InvokeToolOptions, ContentAnnotations, CompletionResult, CompletionRef, CompletionHandler, BlobResourceContent };
|