@modelcontextprotocol/server-everything 2025.12.18 → 2026.1.26
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 +9 -158
- package/dist/docs/architecture.md +44 -0
- package/dist/docs/extension.md +23 -0
- package/dist/docs/features.md +103 -0
- package/dist/docs/how-it-works.md +45 -0
- package/dist/docs/instructions.md +28 -0
- package/dist/docs/startup.md +73 -0
- package/dist/docs/structure.md +182 -0
- package/dist/index.js +19 -14
- package/dist/prompts/args.js +34 -0
- package/dist/prompts/completions.js +52 -0
- package/dist/prompts/index.js +15 -0
- package/dist/prompts/resource.js +60 -0
- package/dist/prompts/simple.js +23 -0
- package/dist/resources/files.js +83 -0
- package/dist/resources/index.js +33 -0
- package/dist/resources/session.js +44 -0
- package/dist/resources/subscriptions.js +125 -0
- package/dist/resources/templates.js +171 -0
- package/dist/server/index.js +93 -0
- package/dist/server/logging.js +64 -0
- package/dist/server/roots.js +65 -0
- package/dist/tools/echo.js +29 -0
- package/dist/tools/get-annotated-message.js +81 -0
- package/dist/tools/get-env.js +28 -0
- package/dist/tools/get-resource-links.js +62 -0
- package/dist/tools/get-resource-reference.js +74 -0
- package/dist/tools/get-roots-list.js +71 -0
- package/dist/tools/get-structured-content.js +72 -0
- package/dist/tools/get-sum.js +40 -0
- package/dist/tools/get-tiny-image.js +41 -0
- package/dist/tools/gzip-file-as-resource.js +182 -0
- package/dist/tools/index.js +50 -0
- package/dist/tools/simulate-research-query.js +249 -0
- package/dist/tools/toggle-simulated-logging.js +41 -0
- package/dist/tools/toggle-subscriber-updates.js +44 -0
- package/dist/tools/trigger-elicitation-request-async.js +202 -0
- package/dist/tools/trigger-elicitation-request.js +210 -0
- package/dist/tools/trigger-long-running-operation.js +59 -0
- package/dist/tools/trigger-sampling-request-async.js +168 -0
- package/dist/tools/trigger-sampling-request.js +71 -0
- package/dist/{sse.js → transports/sse.js} +25 -17
- package/dist/transports/stdio.js +27 -0
- package/dist/transports/streamableHttp.js +206 -0
- package/package.json +10 -7
- package/dist/everything.js +0 -978
- package/dist/instructions.md +0 -23
- package/dist/stdio.js +0 -23
- package/dist/streamableHttp.js +0 -174
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { ElicitResultSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
// Tool configuration
|
|
3
|
+
const name = "trigger-elicitation-request";
|
|
4
|
+
const config = {
|
|
5
|
+
title: "Trigger Elicitation Request Tool",
|
|
6
|
+
description: "Trigger a Request from the Server for User Elicitation",
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Registers the 'trigger-elicitation-request' tool.
|
|
11
|
+
*
|
|
12
|
+
* If the client does not support the elicitation capability, the tool is not registered.
|
|
13
|
+
*
|
|
14
|
+
* The registered tool sends an elicitation request for the user to provide information
|
|
15
|
+
* based on a pre-defined schema of fields including text inputs, booleans, numbers,
|
|
16
|
+
* email, dates, enums of various types, etc. It uses validation and handles multiple
|
|
17
|
+
* possible outcomes from the user's response, such as acceptance with content, decline,
|
|
18
|
+
* or cancellation of the dialog. The process also ensures parsing and validating
|
|
19
|
+
* the elicitation input arguments at runtime.
|
|
20
|
+
*
|
|
21
|
+
* The elicitation dialog response is returned, formatted into a structured result,
|
|
22
|
+
* which contains both user-submitted input data (if provided) and debugging information,
|
|
23
|
+
* including raw results.
|
|
24
|
+
*
|
|
25
|
+
* @param {McpServer} server - TThe McpServer instance where the tool will be registered.
|
|
26
|
+
*/
|
|
27
|
+
export const registerTriggerElicitationRequestTool = (server) => {
|
|
28
|
+
// Does the client support elicitation?
|
|
29
|
+
const clientCapabilities = server.server.getClientCapabilities() || {};
|
|
30
|
+
const clientSupportsElicitation = clientCapabilities.elicitation !== undefined;
|
|
31
|
+
// If so, register tool
|
|
32
|
+
if (clientSupportsElicitation) {
|
|
33
|
+
server.registerTool(name, config, async (args, extra) => {
|
|
34
|
+
const elicitationResult = await extra.sendRequest({
|
|
35
|
+
method: "elicitation/create",
|
|
36
|
+
params: {
|
|
37
|
+
message: "Please provide inputs for the following fields:",
|
|
38
|
+
requestedSchema: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
name: {
|
|
42
|
+
title: "String",
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Your full, legal name",
|
|
45
|
+
},
|
|
46
|
+
check: {
|
|
47
|
+
title: "Boolean",
|
|
48
|
+
type: "boolean",
|
|
49
|
+
description: "Agree to the terms and conditions",
|
|
50
|
+
},
|
|
51
|
+
firstLine: {
|
|
52
|
+
title: "String with default",
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Favorite first line of a story",
|
|
55
|
+
default: "It was a dark and stormy night.",
|
|
56
|
+
},
|
|
57
|
+
email: {
|
|
58
|
+
title: "String with email format",
|
|
59
|
+
type: "string",
|
|
60
|
+
format: "email",
|
|
61
|
+
description: "Your email address (will be verified, and never shared with anyone else)",
|
|
62
|
+
},
|
|
63
|
+
homepage: {
|
|
64
|
+
type: "string",
|
|
65
|
+
format: "uri",
|
|
66
|
+
title: "String with uri format",
|
|
67
|
+
description: "Portfolio / personal website",
|
|
68
|
+
},
|
|
69
|
+
birthdate: {
|
|
70
|
+
title: "String with date format",
|
|
71
|
+
type: "string",
|
|
72
|
+
format: "date",
|
|
73
|
+
description: "Your date of birth",
|
|
74
|
+
},
|
|
75
|
+
integer: {
|
|
76
|
+
title: "Integer",
|
|
77
|
+
type: "integer",
|
|
78
|
+
description: "Your favorite integer (do not give us your phone number, pin, or other sensitive info)",
|
|
79
|
+
minimum: 1,
|
|
80
|
+
maximum: 100,
|
|
81
|
+
default: 42,
|
|
82
|
+
},
|
|
83
|
+
number: {
|
|
84
|
+
title: "Number in range 1-1000",
|
|
85
|
+
type: "number",
|
|
86
|
+
description: "Favorite number (there are no wrong answers)",
|
|
87
|
+
minimum: 0,
|
|
88
|
+
maximum: 1000,
|
|
89
|
+
default: 3.14,
|
|
90
|
+
},
|
|
91
|
+
untitledSingleSelectEnum: {
|
|
92
|
+
type: "string",
|
|
93
|
+
title: "Untitled Single Select Enum",
|
|
94
|
+
description: "Choose your favorite friend",
|
|
95
|
+
enum: [
|
|
96
|
+
"Monica",
|
|
97
|
+
"Rachel",
|
|
98
|
+
"Joey",
|
|
99
|
+
"Chandler",
|
|
100
|
+
"Ross",
|
|
101
|
+
"Phoebe",
|
|
102
|
+
],
|
|
103
|
+
default: "Monica",
|
|
104
|
+
},
|
|
105
|
+
untitledMultipleSelectEnum: {
|
|
106
|
+
type: "array",
|
|
107
|
+
title: "Untitled Multiple Select Enum",
|
|
108
|
+
description: "Choose your favorite instruments",
|
|
109
|
+
minItems: 1,
|
|
110
|
+
maxItems: 3,
|
|
111
|
+
items: {
|
|
112
|
+
type: "string",
|
|
113
|
+
enum: ["Guitar", "Piano", "Violin", "Drums", "Bass"],
|
|
114
|
+
},
|
|
115
|
+
default: ["Guitar"],
|
|
116
|
+
},
|
|
117
|
+
titledSingleSelectEnum: {
|
|
118
|
+
type: "string",
|
|
119
|
+
title: "Titled Single Select Enum",
|
|
120
|
+
description: "Choose your favorite hero",
|
|
121
|
+
oneOf: [
|
|
122
|
+
{ const: "hero-1", title: "Superman" },
|
|
123
|
+
{ const: "hero-2", title: "Green Lantern" },
|
|
124
|
+
{ const: "hero-3", title: "Wonder Woman" },
|
|
125
|
+
],
|
|
126
|
+
default: "hero-1",
|
|
127
|
+
},
|
|
128
|
+
titledMultipleSelectEnum: {
|
|
129
|
+
type: "array",
|
|
130
|
+
title: "Titled Multiple Select Enum",
|
|
131
|
+
description: "Choose your favorite types of fish",
|
|
132
|
+
minItems: 1,
|
|
133
|
+
maxItems: 3,
|
|
134
|
+
items: {
|
|
135
|
+
anyOf: [
|
|
136
|
+
{ const: "fish-1", title: "Tuna" },
|
|
137
|
+
{ const: "fish-2", title: "Salmon" },
|
|
138
|
+
{ const: "fish-3", title: "Trout" },
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
default: ["fish-1"],
|
|
142
|
+
},
|
|
143
|
+
legacyTitledEnum: {
|
|
144
|
+
type: "string",
|
|
145
|
+
title: "Legacy Titled Single Select Enum",
|
|
146
|
+
description: "Choose your favorite type of pet",
|
|
147
|
+
enum: ["pet-1", "pet-2", "pet-3", "pet-4", "pet-5"],
|
|
148
|
+
enumNames: ["Cats", "Dogs", "Birds", "Fish", "Reptiles"],
|
|
149
|
+
default: "pet-1",
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
required: ["name"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}, ElicitResultSchema, { timeout: 10 * 60 * 1000 /* 10 minutes */ });
|
|
156
|
+
// Handle different response actions
|
|
157
|
+
const content = [];
|
|
158
|
+
if (elicitationResult.action === "accept" &&
|
|
159
|
+
elicitationResult.content) {
|
|
160
|
+
content.push({
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `✅ User provided the requested information!`,
|
|
163
|
+
});
|
|
164
|
+
// Only access elicitationResult.content when action is accept
|
|
165
|
+
const userData = elicitationResult.content;
|
|
166
|
+
const lines = [];
|
|
167
|
+
if (userData.name)
|
|
168
|
+
lines.push(`- Name: ${userData.name}`);
|
|
169
|
+
if (userData.check !== undefined)
|
|
170
|
+
lines.push(`- Agreed to terms: ${userData.check}`);
|
|
171
|
+
if (userData.color)
|
|
172
|
+
lines.push(`- Favorite Color: ${userData.color}`);
|
|
173
|
+
if (userData.email)
|
|
174
|
+
lines.push(`- Email: ${userData.email}`);
|
|
175
|
+
if (userData.homepage)
|
|
176
|
+
lines.push(`- Homepage: ${userData.homepage}`);
|
|
177
|
+
if (userData.birthdate)
|
|
178
|
+
lines.push(`- Birthdate: ${userData.birthdate}`);
|
|
179
|
+
if (userData.integer !== undefined)
|
|
180
|
+
lines.push(`- Favorite Integer: ${userData.integer}`);
|
|
181
|
+
if (userData.number !== undefined)
|
|
182
|
+
lines.push(`- Favorite Number: ${userData.number}`);
|
|
183
|
+
if (userData.petType)
|
|
184
|
+
lines.push(`- Pet Type: ${userData.petType}`);
|
|
185
|
+
content.push({
|
|
186
|
+
type: "text",
|
|
187
|
+
text: `User inputs:\n${lines.join("\n")}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else if (elicitationResult.action === "decline") {
|
|
191
|
+
content.push({
|
|
192
|
+
type: "text",
|
|
193
|
+
text: `❌ User declined to provide the requested information.`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
else if (elicitationResult.action === "cancel") {
|
|
197
|
+
content.push({
|
|
198
|
+
type: "text",
|
|
199
|
+
text: `⚠️ User cancelled the elicitation dialog.`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Include raw result for debugging
|
|
203
|
+
content.push({
|
|
204
|
+
type: "text",
|
|
205
|
+
text: `\nRaw result: ${JSON.stringify(elicitationResult, null, 2)}`,
|
|
206
|
+
});
|
|
207
|
+
return { content };
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// Tool input schema
|
|
3
|
+
const TriggerLongRunningOperationSchema = z.object({
|
|
4
|
+
duration: z
|
|
5
|
+
.number()
|
|
6
|
+
.default(10)
|
|
7
|
+
.describe("Duration of the operation in seconds"),
|
|
8
|
+
steps: z.number().default(5).describe("Number of steps in the operation"),
|
|
9
|
+
});
|
|
10
|
+
// Tool configuration
|
|
11
|
+
const name = "trigger-long-running-operation";
|
|
12
|
+
const config = {
|
|
13
|
+
title: "Trigger Long Running Operation Tool",
|
|
14
|
+
description: "Demonstrates a long running operation with progress updates.",
|
|
15
|
+
inputSchema: TriggerLongRunningOperationSchema,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Registers the 'trigger-tong-running-operation' tool.
|
|
19
|
+
*
|
|
20
|
+
* The registered tool starts a long-running operation defined by a specific duration and
|
|
21
|
+
* number of steps.
|
|
22
|
+
*
|
|
23
|
+
* Progress notifications are sent back to the client at each step if a `progressToken`
|
|
24
|
+
* is provided in the metadata.
|
|
25
|
+
*
|
|
26
|
+
* At the end of the operation, the tool returns a message indicating the completion of the
|
|
27
|
+
* operation, including the total duration and steps.
|
|
28
|
+
*
|
|
29
|
+
* @param {McpServer} server - The McpServer instance where the tool will be registered.
|
|
30
|
+
*/
|
|
31
|
+
export const registerTriggerLongRunningOperationTool = (server) => {
|
|
32
|
+
server.registerTool(name, config, async (args, extra) => {
|
|
33
|
+
const validatedArgs = TriggerLongRunningOperationSchema.parse(args);
|
|
34
|
+
const { duration, steps } = validatedArgs;
|
|
35
|
+
const stepDuration = duration / steps;
|
|
36
|
+
const progressToken = extra._meta?.progressToken;
|
|
37
|
+
for (let i = 1; i < steps + 1; i++) {
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, stepDuration * 1000));
|
|
39
|
+
if (progressToken !== undefined) {
|
|
40
|
+
await server.server.notification({
|
|
41
|
+
method: "notifications/progress",
|
|
42
|
+
params: {
|
|
43
|
+
progress: i,
|
|
44
|
+
total: steps,
|
|
45
|
+
progressToken,
|
|
46
|
+
},
|
|
47
|
+
}, { relatedRequestId: extra.requestId });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: `Long running operation completed. Duration: ${duration} seconds, Steps: ${steps}.`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// Tool input schema
|
|
3
|
+
const TriggerSamplingRequestAsyncSchema = z.object({
|
|
4
|
+
prompt: z.string().describe("The prompt to send to the LLM"),
|
|
5
|
+
maxTokens: z
|
|
6
|
+
.number()
|
|
7
|
+
.default(100)
|
|
8
|
+
.describe("Maximum number of tokens to generate"),
|
|
9
|
+
});
|
|
10
|
+
// Tool configuration
|
|
11
|
+
const name = "trigger-sampling-request-async";
|
|
12
|
+
const config = {
|
|
13
|
+
title: "Trigger Async Sampling Request Tool",
|
|
14
|
+
description: "Trigger an async sampling request that the CLIENT executes as a background task. " +
|
|
15
|
+
"Demonstrates bidirectional MCP tasks where the server sends a request and the client " +
|
|
16
|
+
"executes it asynchronously, allowing the server to poll for progress and results.",
|
|
17
|
+
inputSchema: TriggerSamplingRequestAsyncSchema,
|
|
18
|
+
};
|
|
19
|
+
// Poll interval in milliseconds
|
|
20
|
+
const POLL_INTERVAL = 1000;
|
|
21
|
+
// Maximum poll attempts before timeout
|
|
22
|
+
const MAX_POLL_ATTEMPTS = 60;
|
|
23
|
+
/**
|
|
24
|
+
* Registers the 'trigger-sampling-request-async' tool.
|
|
25
|
+
*
|
|
26
|
+
* This tool demonstrates bidirectional MCP tasks:
|
|
27
|
+
* - Server sends sampling request to client with task metadata
|
|
28
|
+
* - Client creates a task and returns CreateTaskResult
|
|
29
|
+
* - Server polls client's tasks/get endpoint for status
|
|
30
|
+
* - Server fetches final result from client's tasks/result endpoint
|
|
31
|
+
*
|
|
32
|
+
* @param {McpServer} server - The McpServer instance where the tool will be registered.
|
|
33
|
+
*/
|
|
34
|
+
export const registerTriggerSamplingRequestAsyncTool = (server) => {
|
|
35
|
+
// Check client capabilities
|
|
36
|
+
const clientCapabilities = server.server.getClientCapabilities() || {};
|
|
37
|
+
// Client must support sampling AND tasks.requests.sampling
|
|
38
|
+
const clientSupportsSampling = clientCapabilities.sampling !== undefined;
|
|
39
|
+
const clientTasksCapability = clientCapabilities.tasks;
|
|
40
|
+
const clientSupportsAsyncSampling = clientTasksCapability?.requests?.sampling?.createMessage !== undefined;
|
|
41
|
+
if (clientSupportsSampling && clientSupportsAsyncSampling) {
|
|
42
|
+
server.registerTool(name, config, async (args, extra) => {
|
|
43
|
+
const validatedArgs = TriggerSamplingRequestAsyncSchema.parse(args);
|
|
44
|
+
const { prompt, maxTokens } = validatedArgs;
|
|
45
|
+
// Create the sampling request WITH task metadata
|
|
46
|
+
// The params.task field signals to the client that this should be executed as a task
|
|
47
|
+
const request = {
|
|
48
|
+
method: "sampling/createMessage",
|
|
49
|
+
params: {
|
|
50
|
+
task: {
|
|
51
|
+
ttl: 300000, // 5 minutes
|
|
52
|
+
},
|
|
53
|
+
messages: [
|
|
54
|
+
{
|
|
55
|
+
role: "user",
|
|
56
|
+
content: {
|
|
57
|
+
type: "text",
|
|
58
|
+
text: `Resource ${name} context: ${prompt}`,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
systemPrompt: "You are a helpful test server.",
|
|
63
|
+
maxTokens,
|
|
64
|
+
temperature: 0.7,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
// Send the sampling request
|
|
68
|
+
// Client may return either:
|
|
69
|
+
// - CreateMessageResult (synchronous execution)
|
|
70
|
+
// - CreateTaskResult (task-based execution with { task } object)
|
|
71
|
+
const samplingResponse = await extra.sendRequest(request, z.union([
|
|
72
|
+
// CreateTaskResult - client created a task
|
|
73
|
+
z.object({
|
|
74
|
+
task: z.object({
|
|
75
|
+
taskId: z.string(),
|
|
76
|
+
status: z.string(),
|
|
77
|
+
pollInterval: z.number().optional(),
|
|
78
|
+
statusMessage: z.string().optional(),
|
|
79
|
+
}),
|
|
80
|
+
}),
|
|
81
|
+
// CreateMessageResult - synchronous execution
|
|
82
|
+
z.object({
|
|
83
|
+
role: z.string(),
|
|
84
|
+
content: z.any(),
|
|
85
|
+
model: z.string(),
|
|
86
|
+
stopReason: z.string().optional(),
|
|
87
|
+
}),
|
|
88
|
+
]));
|
|
89
|
+
// Check if client returned CreateTaskResult (has task object)
|
|
90
|
+
const isTaskResult = "task" in samplingResponse && samplingResponse.task;
|
|
91
|
+
if (!isTaskResult) {
|
|
92
|
+
// Client executed synchronously - return the direct response
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: "text",
|
|
97
|
+
text: `[SYNC] Client executed synchronously:\n${JSON.stringify(samplingResponse, null, 2)}`,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const taskId = samplingResponse.task.taskId;
|
|
103
|
+
const statusMessages = [];
|
|
104
|
+
statusMessages.push(`Task created: ${taskId}`);
|
|
105
|
+
// Poll for task completion
|
|
106
|
+
let attempts = 0;
|
|
107
|
+
let taskStatus = samplingResponse.task.status;
|
|
108
|
+
let taskStatusMessage;
|
|
109
|
+
while (taskStatus !== "completed" &&
|
|
110
|
+
taskStatus !== "failed" &&
|
|
111
|
+
taskStatus !== "cancelled" &&
|
|
112
|
+
attempts < MAX_POLL_ATTEMPTS) {
|
|
113
|
+
// Wait before polling
|
|
114
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
|
|
115
|
+
attempts++;
|
|
116
|
+
// Get task status from client
|
|
117
|
+
const pollResult = await extra.sendRequest({
|
|
118
|
+
method: "tasks/get",
|
|
119
|
+
params: { taskId },
|
|
120
|
+
}, z
|
|
121
|
+
.object({
|
|
122
|
+
status: z.string(),
|
|
123
|
+
statusMessage: z.string().optional(),
|
|
124
|
+
})
|
|
125
|
+
.passthrough());
|
|
126
|
+
taskStatus = pollResult.status;
|
|
127
|
+
taskStatusMessage = pollResult.statusMessage;
|
|
128
|
+
statusMessages.push(`Poll ${attempts}: ${taskStatus}${taskStatusMessage ? ` - ${taskStatusMessage}` : ""}`);
|
|
129
|
+
}
|
|
130
|
+
// Check for timeout
|
|
131
|
+
if (attempts >= MAX_POLL_ATTEMPTS) {
|
|
132
|
+
return {
|
|
133
|
+
content: [
|
|
134
|
+
{
|
|
135
|
+
type: "text",
|
|
136
|
+
text: `[TIMEOUT] Task timed out after ${MAX_POLL_ATTEMPTS} poll attempts\n\nProgress:\n${statusMessages.join("\n")}`,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// Check for failure/cancellation
|
|
142
|
+
if (taskStatus === "failed" || taskStatus === "cancelled") {
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: `[${taskStatus.toUpperCase()}] ${taskStatusMessage || "No message"}\n\nProgress:\n${statusMessages.join("\n")}`,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Fetch the final result
|
|
153
|
+
const result = await extra.sendRequest({
|
|
154
|
+
method: "tasks/result",
|
|
155
|
+
params: { taskId },
|
|
156
|
+
}, z.any());
|
|
157
|
+
// Return the result with status history
|
|
158
|
+
return {
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `[COMPLETED] Async sampling completed!\n\n**Progress:**\n${statusMessages.join("\n")}\n\n**Result:**\n${JSON.stringify(result, null, 2)}`,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CreateMessageResultSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// Tool input schema
|
|
4
|
+
const TriggerSamplingRequestSchema = z.object({
|
|
5
|
+
prompt: z.string().describe("The prompt to send to the LLM"),
|
|
6
|
+
maxTokens: z
|
|
7
|
+
.number()
|
|
8
|
+
.default(100)
|
|
9
|
+
.describe("Maximum number of tokens to generate"),
|
|
10
|
+
});
|
|
11
|
+
// Tool configuration
|
|
12
|
+
const name = "trigger-sampling-request";
|
|
13
|
+
const config = {
|
|
14
|
+
title: "Trigger Sampling Request Tool",
|
|
15
|
+
description: "Trigger a Request from the Server for LLM Sampling",
|
|
16
|
+
inputSchema: TriggerSamplingRequestSchema,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Registers the 'trigger-sampling-request' tool.
|
|
20
|
+
*
|
|
21
|
+
* If the client does not support the sampling capability, the tool is not registered.
|
|
22
|
+
*
|
|
23
|
+
* The registered tool performs the following operations:
|
|
24
|
+
* - Validates incoming arguments using `TriggerSamplingRequestSchema`.
|
|
25
|
+
* - Constructs a `sampling/createMessage` request object using provided prompt and maximum tokens.
|
|
26
|
+
* - Sends the request to the server for sampling.
|
|
27
|
+
* - Formats and returns the sampling result content to the client.
|
|
28
|
+
*
|
|
29
|
+
* @param {McpServer} server - The McpServer instance where the tool will be registered.
|
|
30
|
+
*/
|
|
31
|
+
export const registerTriggerSamplingRequestTool = (server) => {
|
|
32
|
+
// Does the client support sampling?
|
|
33
|
+
const clientCapabilities = server.server.getClientCapabilities() || {};
|
|
34
|
+
const clientSupportsSampling = clientCapabilities.sampling !== undefined;
|
|
35
|
+
// If so, register tool
|
|
36
|
+
if (clientSupportsSampling) {
|
|
37
|
+
server.registerTool(name, config, async (args, extra) => {
|
|
38
|
+
const validatedArgs = TriggerSamplingRequestSchema.parse(args);
|
|
39
|
+
const { prompt, maxTokens } = validatedArgs;
|
|
40
|
+
// Create the sampling request
|
|
41
|
+
const request = {
|
|
42
|
+
method: "sampling/createMessage",
|
|
43
|
+
params: {
|
|
44
|
+
messages: [
|
|
45
|
+
{
|
|
46
|
+
role: "user",
|
|
47
|
+
content: {
|
|
48
|
+
type: "text",
|
|
49
|
+
text: `Resource ${name} context: ${prompt}`,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
systemPrompt: "You are a helpful test server.",
|
|
54
|
+
maxTokens,
|
|
55
|
+
temperature: 0.7,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
// Send the sampling request to the client
|
|
59
|
+
const result = await extra.sendRequest(request, CreateMessageResultSchema);
|
|
60
|
+
// Return the result to the client
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: `LLM sampling result: \n${JSON.stringify(result, null, 2)}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -1,43 +1,50 @@
|
|
|
1
1
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
2
2
|
import express from "express";
|
|
3
|
-
import { createServer } from "
|
|
4
|
-
import cors from
|
|
5
|
-
console.error(
|
|
3
|
+
import { createServer } from "../server/index.js";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
console.error("Starting SSE server...");
|
|
6
|
+
// Express app with permissive CORS for testing with Inspector direct connect mode
|
|
6
7
|
const app = express();
|
|
7
8
|
app.use(cors({
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
9
|
+
origin: "*", // use "*" with caution in production
|
|
10
|
+
methods: "GET,POST",
|
|
11
|
+
preflightContinue: false,
|
|
12
|
+
optionsSuccessStatus: 204,
|
|
13
|
+
}));
|
|
14
|
+
// Map sessionId to transport for each client
|
|
13
15
|
const transports = new Map();
|
|
16
|
+
// Handle GET requests for new SSE streams
|
|
14
17
|
app.get("/sse", async (req, res) => {
|
|
15
18
|
let transport;
|
|
16
|
-
const { server, cleanup
|
|
19
|
+
const { server, cleanup } = createServer();
|
|
20
|
+
// Session Id should not exist for GET /sse requests
|
|
17
21
|
if (req?.query?.sessionId) {
|
|
18
22
|
const sessionId = req?.query?.sessionId;
|
|
19
23
|
transport = transports.get(sessionId);
|
|
20
24
|
console.error("Client Reconnecting? This shouldn't happen; when client has a sessionId, GET /sse should not be called again.", transport.sessionId);
|
|
21
25
|
}
|
|
22
26
|
else {
|
|
23
|
-
// Create and store transport for new session
|
|
27
|
+
// Create and store transport for the new session
|
|
24
28
|
transport = new SSEServerTransport("/message", res);
|
|
25
29
|
transports.set(transport.sessionId, transport);
|
|
26
30
|
// Connect server to transport
|
|
27
31
|
await server.connect(transport);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
startNotificationIntervals(transport.sessionId);
|
|
32
|
+
const sessionId = transport.sessionId;
|
|
33
|
+
console.error("Client Connected: ", sessionId);
|
|
31
34
|
// Handle close of connection
|
|
32
|
-
server.onclose = async () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
server.server.onclose = async () => {
|
|
36
|
+
const sessionId = transport.sessionId;
|
|
37
|
+
console.error("Client Disconnected: ", sessionId);
|
|
38
|
+
transports.delete(sessionId);
|
|
39
|
+
cleanup(sessionId);
|
|
36
40
|
};
|
|
37
41
|
}
|
|
38
42
|
});
|
|
43
|
+
// Handle POST requests for client messages
|
|
39
44
|
app.post("/message", async (req, res) => {
|
|
45
|
+
// Session Id should exist for POST /message requests
|
|
40
46
|
const sessionId = req?.query?.sessionId;
|
|
47
|
+
// Get the transport for this session and use it to handle the request
|
|
41
48
|
const transport = transports.get(sessionId);
|
|
42
49
|
if (transport) {
|
|
43
50
|
console.error("Client Message from", sessionId);
|
|
@@ -47,6 +54,7 @@ app.post("/message", async (req, res) => {
|
|
|
47
54
|
console.error(`No transport found for sessionId ${sessionId}`);
|
|
48
55
|
}
|
|
49
56
|
});
|
|
57
|
+
// Start the express server
|
|
50
58
|
const PORT = process.env.PORT || 3001;
|
|
51
59
|
app.listen(PORT, () => {
|
|
52
60
|
console.error(`Server is running on port ${PORT}`);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "../server/index.js";
|
|
4
|
+
console.error("Starting default (STDIO) server...");
|
|
5
|
+
/**
|
|
6
|
+
* The main method
|
|
7
|
+
* - Initializes the StdioServerTransport, sets up the server,
|
|
8
|
+
* - Handles cleanup on process exit.
|
|
9
|
+
*
|
|
10
|
+
* @return {Promise<void>} A promise that resolves when the main function has executed and the process exits.
|
|
11
|
+
*/
|
|
12
|
+
async function main() {
|
|
13
|
+
const transport = new StdioServerTransport();
|
|
14
|
+
const { server, cleanup } = createServer();
|
|
15
|
+
// Connect transport to server
|
|
16
|
+
await server.connect(transport);
|
|
17
|
+
// Cleanup on exit
|
|
18
|
+
process.on("SIGINT", async () => {
|
|
19
|
+
await server.close();
|
|
20
|
+
cleanup();
|
|
21
|
+
process.exit(0);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
main().catch((error) => {
|
|
25
|
+
console.error("Server error:", error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|