@modelcontextprotocol/server-everything 2025.5.12 → 2025.7.29
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 +25 -8
- package/dist/everything.js +199 -40
- package/dist/instructions.md +23 -0
- package/dist/sse.js +32 -13
- package/dist/streamableHttp.js +26 -27
- package/package.json +3 -3
package/README.md
CHANGED
@@ -27,24 +27,24 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is
|
|
27
27
|
- Returns: Completion message with duration and steps
|
28
28
|
- Sends progress notifications during execution
|
29
29
|
|
30
|
-
4. `
|
30
|
+
4. `printEnv`
|
31
|
+
- Prints all environment variables
|
32
|
+
- Useful for debugging MCP server configuration
|
33
|
+
- No inputs required
|
34
|
+
- Returns: JSON string of all environment variables
|
35
|
+
|
36
|
+
5. `sampleLLM`
|
31
37
|
- Demonstrates LLM sampling capability using MCP sampling feature
|
32
38
|
- Inputs:
|
33
39
|
- `prompt` (string): The prompt to send to the LLM
|
34
40
|
- `maxTokens` (number, default: 100): Maximum tokens to generate
|
35
41
|
- Returns: Generated LLM response
|
36
42
|
|
37
|
-
|
43
|
+
6. `getTinyImage`
|
38
44
|
- Returns a small test image
|
39
45
|
- No inputs required
|
40
46
|
- Returns: Base64 encoded PNG image data
|
41
47
|
|
42
|
-
6. `printEnv`
|
43
|
-
- Prints all environment variables
|
44
|
-
- Useful for debugging MCP server configuration
|
45
|
-
- No inputs required
|
46
|
-
- Returns: JSON string of all environment variables
|
47
|
-
|
48
48
|
7. `annotatedMessage`
|
49
49
|
- Demonstrates how annotations can be used to provide metadata about content
|
50
50
|
- Inputs:
|
@@ -72,6 +72,23 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is
|
|
72
72
|
- Embedded resource with `type: "resource"`
|
73
73
|
- Text instruction for using the resource URI
|
74
74
|
|
75
|
+
9. `startElicitation`
|
76
|
+
- Initiates an elicitation (interaction) within the MCP client.
|
77
|
+
- Inputs:
|
78
|
+
- `color` (string): Favorite color
|
79
|
+
- `number` (number, 1-100): Favorite number
|
80
|
+
- `pets` (enum): Favorite pet
|
81
|
+
- Returns: Confirmation of the elicitation demo with selection summary.
|
82
|
+
|
83
|
+
10. `structuredContent`
|
84
|
+
- Demonstrates a tool returning structured content using the example in the specification
|
85
|
+
- Provides an output schema to allow testing of client SHOULD advisory to validate the result using the schema
|
86
|
+
- Inputs:
|
87
|
+
- `location` (string): A location or ZIP code, mock data is returned regardless of value
|
88
|
+
- Returns: a response with
|
89
|
+
- `structuredContent` field conformant to the output schema
|
90
|
+
- A backward compatible Text Content field, a SHOULD advisory in the specification
|
91
|
+
|
75
92
|
### Resources
|
76
93
|
|
77
94
|
The server provides 100 test resources in two formats:
|
package/dist/everything.js
CHANGED
@@ -2,7 +2,14 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { CallToolRequestSchema, CompleteRequestSchema, CreateMessageResultSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, SetLevelRequestSchema, SubscribeRequestSchema, ToolSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
3
3
|
import { z } from "zod";
|
4
4
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
5
|
+
import { readFileSync } from "fs";
|
6
|
+
import { fileURLToPath } from "url";
|
7
|
+
import { dirname, join } from "path";
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
9
|
+
const __dirname = dirname(__filename);
|
10
|
+
const instructions = readFileSync(join(__dirname, "instructions.md"), "utf-8");
|
5
11
|
const ToolInputSchema = ToolSchema.shape.inputSchema;
|
12
|
+
const ToolOutputSchema = ToolSchema.shape.outputSchema;
|
6
13
|
/* Input schemas for tools implemented in this server */
|
7
14
|
const EchoSchema = z.object({
|
8
15
|
message: z.string().describe("Message to echo"),
|
@@ -16,7 +23,10 @@ const LongRunningOperationSchema = z.object({
|
|
16
23
|
.number()
|
17
24
|
.default(10)
|
18
25
|
.describe("Duration of the operation in seconds"),
|
19
|
-
steps: z
|
26
|
+
steps: z
|
27
|
+
.number()
|
28
|
+
.default(5)
|
29
|
+
.describe("Number of steps in the operation"),
|
20
30
|
});
|
21
31
|
const PrintEnvSchema = z.object({});
|
22
32
|
const SampleLLMSchema = z.object({
|
@@ -26,12 +36,6 @@ const SampleLLMSchema = z.object({
|
|
26
36
|
.default(100)
|
27
37
|
.describe("Maximum number of tokens to generate"),
|
28
38
|
});
|
29
|
-
// Example completion values
|
30
|
-
const EXAMPLE_COMPLETIONS = {
|
31
|
-
style: ["casual", "formal", "technical", "friendly"],
|
32
|
-
temperature: ["0", "0.5", "0.7", "1.0"],
|
33
|
-
resourceId: ["1", "2", "3", "4", "5"],
|
34
|
-
};
|
35
39
|
const GetTinyImageSchema = z.object({});
|
36
40
|
const AnnotatedMessageSchema = z.object({
|
37
41
|
messageType: z
|
@@ -49,6 +53,35 @@ const GetResourceReferenceSchema = z.object({
|
|
49
53
|
.max(100)
|
50
54
|
.describe("ID of the resource to reference (1-100)"),
|
51
55
|
});
|
56
|
+
const ElicitationSchema = z.object({});
|
57
|
+
const GetResourceLinksSchema = z.object({
|
58
|
+
count: z
|
59
|
+
.number()
|
60
|
+
.min(1)
|
61
|
+
.max(10)
|
62
|
+
.default(3)
|
63
|
+
.describe("Number of resource links to return (1-10)"),
|
64
|
+
});
|
65
|
+
const StructuredContentSchema = {
|
66
|
+
input: z.object({
|
67
|
+
location: z
|
68
|
+
.string()
|
69
|
+
.trim()
|
70
|
+
.min(1)
|
71
|
+
.describe("City name or zip code"),
|
72
|
+
}),
|
73
|
+
output: z.object({
|
74
|
+
temperature: z
|
75
|
+
.number()
|
76
|
+
.describe("Temperature in celsius"),
|
77
|
+
conditions: z
|
78
|
+
.string()
|
79
|
+
.describe("Weather conditions description"),
|
80
|
+
humidity: z
|
81
|
+
.number()
|
82
|
+
.describe("Humidity percentage"),
|
83
|
+
})
|
84
|
+
};
|
52
85
|
var ToolName;
|
53
86
|
(function (ToolName) {
|
54
87
|
ToolName["ECHO"] = "echo";
|
@@ -59,6 +92,9 @@ var ToolName;
|
|
59
92
|
ToolName["GET_TINY_IMAGE"] = "getTinyImage";
|
60
93
|
ToolName["ANNOTATED_MESSAGE"] = "annotatedMessage";
|
61
94
|
ToolName["GET_RESOURCE_REFERENCE"] = "getResourceReference";
|
95
|
+
ToolName["ELICITATION"] = "startElicitation";
|
96
|
+
ToolName["GET_RESOURCE_LINKS"] = "getResourceLinks";
|
97
|
+
ToolName["STRUCTURED_CONTENT"] = "structuredContent";
|
62
98
|
})(ToolName || (ToolName = {}));
|
63
99
|
var PromptName;
|
64
100
|
(function (PromptName) {
|
@@ -66,9 +102,16 @@ var PromptName;
|
|
66
102
|
PromptName["COMPLEX"] = "complex_prompt";
|
67
103
|
PromptName["RESOURCE"] = "resource_prompt";
|
68
104
|
})(PromptName || (PromptName = {}));
|
105
|
+
// Example completion values
|
106
|
+
const EXAMPLE_COMPLETIONS = {
|
107
|
+
style: ["casual", "formal", "technical", "friendly"],
|
108
|
+
temperature: ["0", "0.5", "0.7", "1.0"],
|
109
|
+
resourceId: ["1", "2", "3", "4", "5"],
|
110
|
+
};
|
69
111
|
export const createServer = () => {
|
70
112
|
const server = new Server({
|
71
113
|
name: "example-servers/everything",
|
114
|
+
title: "Everything Example Server",
|
72
115
|
version: "1.0.0",
|
73
116
|
}, {
|
74
117
|
capabilities: {
|
@@ -77,7 +120,9 @@ export const createServer = () => {
|
|
77
120
|
tools: {},
|
78
121
|
logging: {},
|
79
122
|
completions: {},
|
123
|
+
elicitation: {},
|
80
124
|
},
|
125
|
+
instructions
|
81
126
|
});
|
82
127
|
let subscriptions = new Set();
|
83
128
|
let subsUpdateInterval;
|
@@ -120,9 +165,9 @@ export const createServer = () => {
|
|
120
165
|
// Set up update interval for stderr messages
|
121
166
|
stdErrUpdateInterval = setInterval(() => {
|
122
167
|
const shortTimestamp = new Date().toLocaleTimeString([], {
|
123
|
-
hour:
|
124
|
-
minute:
|
125
|
-
second:
|
168
|
+
hour: "2-digit",
|
169
|
+
minute: "2-digit",
|
170
|
+
second: "2-digit"
|
126
171
|
});
|
127
172
|
server.notification({
|
128
173
|
method: "notifications/stderr",
|
@@ -151,6 +196,16 @@ export const createServer = () => {
|
|
151
196
|
};
|
152
197
|
return await server.request(request, CreateMessageResultSchema);
|
153
198
|
};
|
199
|
+
const requestElicitation = async (message, requestedSchema) => {
|
200
|
+
const request = {
|
201
|
+
method: 'elicitation/create',
|
202
|
+
params: {
|
203
|
+
message,
|
204
|
+
requestedSchema
|
205
|
+
}
|
206
|
+
};
|
207
|
+
return await server.request(request, z.any());
|
208
|
+
};
|
154
209
|
const ALL_RESOURCES = Array.from({ length: 100 }, (_, i) => {
|
155
210
|
const uri = `test://static/resource/${i + 1}`;
|
156
211
|
if (i % 2 === 0) {
|
@@ -347,16 +402,16 @@ export const createServer = () => {
|
|
347
402
|
description: "Adds two numbers",
|
348
403
|
inputSchema: zodToJsonSchema(AddSchema),
|
349
404
|
},
|
350
|
-
{
|
351
|
-
name: ToolName.PRINT_ENV,
|
352
|
-
description: "Prints all environment variables, helpful for debugging MCP server configuration",
|
353
|
-
inputSchema: zodToJsonSchema(PrintEnvSchema),
|
354
|
-
},
|
355
405
|
{
|
356
406
|
name: ToolName.LONG_RUNNING_OPERATION,
|
357
407
|
description: "Demonstrates a long running operation with progress updates",
|
358
408
|
inputSchema: zodToJsonSchema(LongRunningOperationSchema),
|
359
409
|
},
|
410
|
+
{
|
411
|
+
name: ToolName.PRINT_ENV,
|
412
|
+
description: "Prints all environment variables, helpful for debugging MCP server configuration",
|
413
|
+
inputSchema: zodToJsonSchema(PrintEnvSchema),
|
414
|
+
},
|
360
415
|
{
|
361
416
|
name: ToolName.SAMPLE_LLM,
|
362
417
|
description: "Samples from an LLM using MCP's sampling feature",
|
@@ -377,6 +432,22 @@ export const createServer = () => {
|
|
377
432
|
description: "Returns a resource reference that can be used by MCP clients",
|
378
433
|
inputSchema: zodToJsonSchema(GetResourceReferenceSchema),
|
379
434
|
},
|
435
|
+
{
|
436
|
+
name: ToolName.ELICITATION,
|
437
|
+
description: "Demonstrates the Elicitation feature by asking the user to provide information about their favorite color, number, and pets.",
|
438
|
+
inputSchema: zodToJsonSchema(ElicitationSchema),
|
439
|
+
},
|
440
|
+
{
|
441
|
+
name: ToolName.GET_RESOURCE_LINKS,
|
442
|
+
description: "Returns multiple resource links that reference different types of resources",
|
443
|
+
inputSchema: zodToJsonSchema(GetResourceLinksSchema),
|
444
|
+
},
|
445
|
+
{
|
446
|
+
name: ToolName.STRUCTURED_CONTENT,
|
447
|
+
description: "Returns structured content along with an output schema for client data validation",
|
448
|
+
inputSchema: zodToJsonSchema(StructuredContentSchema.input),
|
449
|
+
outputSchema: zodToJsonSchema(StructuredContentSchema.output),
|
450
|
+
},
|
380
451
|
];
|
381
452
|
return { tools };
|
382
453
|
});
|
@@ -467,31 +538,6 @@ export const createServer = () => {
|
|
467
538
|
],
|
468
539
|
};
|
469
540
|
}
|
470
|
-
if (name === ToolName.GET_RESOURCE_REFERENCE) {
|
471
|
-
const validatedArgs = GetResourceReferenceSchema.parse(args);
|
472
|
-
const resourceId = validatedArgs.resourceId;
|
473
|
-
const resourceIndex = resourceId - 1;
|
474
|
-
if (resourceIndex < 0 || resourceIndex >= ALL_RESOURCES.length) {
|
475
|
-
throw new Error(`Resource with ID ${resourceId} does not exist`);
|
476
|
-
}
|
477
|
-
const resource = ALL_RESOURCES[resourceIndex];
|
478
|
-
return {
|
479
|
-
content: [
|
480
|
-
{
|
481
|
-
type: "text",
|
482
|
-
text: `Returning resource reference for Resource ${resourceId}:`,
|
483
|
-
},
|
484
|
-
{
|
485
|
-
type: "resource",
|
486
|
-
resource: resource,
|
487
|
-
},
|
488
|
-
{
|
489
|
-
type: "text",
|
490
|
-
text: `You can access this resource using the URI: ${resource.uri}`,
|
491
|
-
},
|
492
|
-
],
|
493
|
-
};
|
494
|
-
}
|
495
541
|
if (name === ToolName.ANNOTATED_MESSAGE) {
|
496
542
|
const { messageType, includeImage } = AnnotatedMessageSchema.parse(args);
|
497
543
|
const content = [];
|
@@ -540,6 +586,119 @@ export const createServer = () => {
|
|
540
586
|
}
|
541
587
|
return { content };
|
542
588
|
}
|
589
|
+
if (name === ToolName.GET_RESOURCE_REFERENCE) {
|
590
|
+
const validatedArgs = GetResourceReferenceSchema.parse(args);
|
591
|
+
const resourceId = validatedArgs.resourceId;
|
592
|
+
const resourceIndex = resourceId - 1;
|
593
|
+
if (resourceIndex < 0 || resourceIndex >= ALL_RESOURCES.length) {
|
594
|
+
throw new Error(`Resource with ID ${resourceId} does not exist`);
|
595
|
+
}
|
596
|
+
const resource = ALL_RESOURCES[resourceIndex];
|
597
|
+
return {
|
598
|
+
content: [
|
599
|
+
{
|
600
|
+
type: "text",
|
601
|
+
text: `Returning resource reference for Resource ${resourceId}:`,
|
602
|
+
},
|
603
|
+
{
|
604
|
+
type: "resource",
|
605
|
+
resource: resource,
|
606
|
+
},
|
607
|
+
{
|
608
|
+
type: "text",
|
609
|
+
text: `You can access this resource using the URI: ${resource.uri}`,
|
610
|
+
},
|
611
|
+
],
|
612
|
+
};
|
613
|
+
}
|
614
|
+
if (name === ToolName.ELICITATION) {
|
615
|
+
ElicitationSchema.parse(args);
|
616
|
+
const elicitationResult = await requestElicitation('What are your favorite things?', {
|
617
|
+
type: 'object',
|
618
|
+
properties: {
|
619
|
+
color: { type: 'string', description: 'Favorite color' },
|
620
|
+
number: { type: 'integer', description: 'Favorite number', minimum: 1, maximum: 100 },
|
621
|
+
pets: {
|
622
|
+
type: 'string',
|
623
|
+
enum: ['cats', 'dogs', 'birds', 'fish', 'reptiles'],
|
624
|
+
description: 'Favorite pets'
|
625
|
+
},
|
626
|
+
}
|
627
|
+
});
|
628
|
+
// Handle different response actions
|
629
|
+
const content = [];
|
630
|
+
if (elicitationResult.action === 'accept' && elicitationResult.content) {
|
631
|
+
content.push({
|
632
|
+
type: "text",
|
633
|
+
text: `✅ User provided their favorite things!`,
|
634
|
+
});
|
635
|
+
// Only access elicitationResult.content when action is accept
|
636
|
+
const { color, number, pets } = elicitationResult.content;
|
637
|
+
content.push({
|
638
|
+
type: "text",
|
639
|
+
text: `Their favorites are:\n- Color: ${color || 'not specified'}\n- Number: ${number || 'not specified'}\n- Pets: ${pets || 'not specified'}`,
|
640
|
+
});
|
641
|
+
}
|
642
|
+
else if (elicitationResult.action === 'decline') {
|
643
|
+
content.push({
|
644
|
+
type: "text",
|
645
|
+
text: `❌ User declined to provide their favorite things.`,
|
646
|
+
});
|
647
|
+
}
|
648
|
+
else if (elicitationResult.action === 'cancel') {
|
649
|
+
content.push({
|
650
|
+
type: "text",
|
651
|
+
text: `⚠️ User cancelled the elicitation dialog.`,
|
652
|
+
});
|
653
|
+
}
|
654
|
+
// Include raw result for debugging
|
655
|
+
content.push({
|
656
|
+
type: "text",
|
657
|
+
text: `\nRaw result: ${JSON.stringify(elicitationResult, null, 2)}`,
|
658
|
+
});
|
659
|
+
return { content };
|
660
|
+
}
|
661
|
+
if (name === ToolName.GET_RESOURCE_LINKS) {
|
662
|
+
const { count } = GetResourceLinksSchema.parse(args);
|
663
|
+
const content = [];
|
664
|
+
// Add intro text
|
665
|
+
content.push({
|
666
|
+
type: "text",
|
667
|
+
text: `Here are ${count} resource links to resources available in this server (see full output in tool response if your client does not support resource_link yet):`,
|
668
|
+
});
|
669
|
+
// Return resource links to actual resources from ALL_RESOURCES
|
670
|
+
const actualCount = Math.min(count, ALL_RESOURCES.length);
|
671
|
+
for (let i = 0; i < actualCount; i++) {
|
672
|
+
const resource = ALL_RESOURCES[i];
|
673
|
+
content.push({
|
674
|
+
type: "resource_link",
|
675
|
+
uri: resource.uri,
|
676
|
+
name: resource.name,
|
677
|
+
description: `Resource ${i + 1}: ${resource.mimeType === "text/plain"
|
678
|
+
? "plaintext resource"
|
679
|
+
: "binary blob resource"}`,
|
680
|
+
mimeType: resource.mimeType,
|
681
|
+
});
|
682
|
+
}
|
683
|
+
return { content };
|
684
|
+
}
|
685
|
+
if (name === ToolName.STRUCTURED_CONTENT) {
|
686
|
+
// The same response is returned for every input.
|
687
|
+
const validatedArgs = StructuredContentSchema.input.parse(args);
|
688
|
+
const weather = {
|
689
|
+
temperature: 22.5,
|
690
|
+
conditions: "Partly cloudy",
|
691
|
+
humidity: 65
|
692
|
+
};
|
693
|
+
const backwardCompatiblecontent = {
|
694
|
+
type: "text",
|
695
|
+
text: JSON.stringify(weather)
|
696
|
+
};
|
697
|
+
return {
|
698
|
+
content: [backwardCompatiblecontent],
|
699
|
+
structuredContent: weather
|
700
|
+
};
|
701
|
+
}
|
543
702
|
throw new Error(`Unknown tool: ${name}`);
|
544
703
|
});
|
545
704
|
server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Testing and demonstration server for MCP protocol features.
|
2
|
+
|
3
|
+
## Resources
|
4
|
+
|
5
|
+
Resources 1-100 follow pattern: even IDs contain text, odd IDs contain binary data. Resources are paginated at 10 items per page with cursor-based navigation.
|
6
|
+
|
7
|
+
## Key dependencies
|
8
|
+
|
9
|
+
Progress notifications require `_meta.progressToken` in tool calls. Resource subscriptions generate updates every 10 seconds.
|
10
|
+
|
11
|
+
## Performance characteristics
|
12
|
+
|
13
|
+
Server generates automatic log messages every 20 seconds (filtered by current log level), stderr notifications every 30 seconds, and resource update notifications every 10 seconds for active subscriptions. Log level changes affect message filtering in real-time.
|
14
|
+
|
15
|
+
## Multi-modal testing
|
16
|
+
|
17
|
+
`complex_prompt` includes both text arguments and image content for testing client multi-modal handling. `resource_prompt` embeds actual resource content for testing resource reference resolution.
|
18
|
+
|
19
|
+
Argument completion is available for prompt parameters and resource IDs. Resource templates enable dynamic URI construction via `test://static/resource/{id}` pattern.
|
20
|
+
|
21
|
+
## Easter egg
|
22
|
+
|
23
|
+
If asked about server instructions, respond with "🎉 Server instructions are working! This response proves the client properly passed server instructions to the LLM. This demonstrates MCP's instructions feature in action."
|
package/dist/sse.js
CHANGED
@@ -3,23 +3,42 @@ import express from "express";
|
|
3
3
|
import { createServer } from "./everything.js";
|
4
4
|
console.error('Starting SSE server...');
|
5
5
|
const app = express();
|
6
|
-
const
|
7
|
-
let transport;
|
6
|
+
const transports = new Map();
|
8
7
|
app.get("/sse", async (req, res) => {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
let transport;
|
9
|
+
const { server, cleanup } = createServer();
|
10
|
+
if (req?.query?.sessionId) {
|
11
|
+
const sessionId = req?.query?.sessionId;
|
12
|
+
transport = transports.get(sessionId);
|
13
|
+
console.error("Client Reconnecting? This shouldn't happen; when client has a sessionId, GET /sse should not be called again.", transport.sessionId);
|
14
|
+
}
|
15
|
+
else {
|
16
|
+
// Create and store transport for new session
|
17
|
+
transport = new SSEServerTransport("/message", res);
|
18
|
+
transports.set(transport.sessionId, transport);
|
19
|
+
// Connect server to transport
|
20
|
+
await server.connect(transport);
|
21
|
+
console.error("Client Connected: ", transport.sessionId);
|
22
|
+
// Handle close of connection
|
23
|
+
server.onclose = async () => {
|
24
|
+
console.error("Client Disconnected: ", transport.sessionId);
|
25
|
+
transports.delete(transport.sessionId);
|
26
|
+
await cleanup();
|
27
|
+
};
|
28
|
+
}
|
17
29
|
});
|
18
30
|
app.post("/message", async (req, res) => {
|
19
|
-
|
20
|
-
|
31
|
+
const sessionId = req?.query?.sessionId;
|
32
|
+
const transport = transports.get(sessionId);
|
33
|
+
if (transport) {
|
34
|
+
console.error("Client Message from", sessionId);
|
35
|
+
await transport.handlePostMessage(req, res);
|
36
|
+
}
|
37
|
+
else {
|
38
|
+
console.error(`No transport found for sessionId ${sessionId}`);
|
39
|
+
}
|
21
40
|
});
|
22
41
|
const PORT = process.env.PORT || 3001;
|
23
42
|
app.listen(PORT, () => {
|
24
|
-
console.
|
43
|
+
console.error(`Server is running on port ${PORT}`);
|
25
44
|
});
|
package/dist/streamableHttp.js
CHANGED
@@ -5,19 +5,19 @@ import { createServer } from "./everything.js";
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
6
6
|
console.error('Starting Streamable HTTP server...');
|
7
7
|
const app = express();
|
8
|
-
const
|
9
|
-
const transports = {};
|
8
|
+
const transports = new Map();
|
10
9
|
app.post('/mcp', async (req, res) => {
|
11
|
-
console.
|
10
|
+
console.error('Received MCP POST request');
|
12
11
|
try {
|
13
12
|
// Check for existing session ID
|
14
13
|
const sessionId = req.headers['mcp-session-id'];
|
15
14
|
let transport;
|
16
|
-
if (sessionId && transports
|
15
|
+
if (sessionId && transports.has(sessionId)) {
|
17
16
|
// Reuse existing transport
|
18
|
-
transport = transports
|
17
|
+
transport = transports.get(sessionId);
|
19
18
|
}
|
20
19
|
else if (!sessionId) {
|
20
|
+
const { server, cleanup } = createServer();
|
21
21
|
// New initialization request
|
22
22
|
const eventStore = new InMemoryEventStore();
|
23
23
|
transport = new StreamableHTTPServerTransport({
|
@@ -26,16 +26,17 @@ app.post('/mcp', async (req, res) => {
|
|
26
26
|
onsessioninitialized: (sessionId) => {
|
27
27
|
// Store the transport by session ID when session is initialized
|
28
28
|
// This avoids race conditions where requests might come in before the session is stored
|
29
|
-
console.
|
30
|
-
transports
|
29
|
+
console.error(`Session initialized with ID: ${sessionId}`);
|
30
|
+
transports.set(sessionId, transport);
|
31
31
|
}
|
32
32
|
});
|
33
33
|
// Set up onclose handler to clean up transport when closed
|
34
|
-
|
34
|
+
server.onclose = async () => {
|
35
35
|
const sid = transport.sessionId;
|
36
|
-
if (sid && transports
|
37
|
-
console.
|
38
|
-
delete
|
36
|
+
if (sid && transports.has(sid)) {
|
37
|
+
console.error(`Transport closed for session ${sid}, removing from transports map`);
|
38
|
+
transports.delete(sid);
|
39
|
+
await cleanup();
|
39
40
|
}
|
40
41
|
};
|
41
42
|
// Connect the transport to the MCP server BEFORE handling the request
|
@@ -77,9 +78,9 @@ app.post('/mcp', async (req, res) => {
|
|
77
78
|
});
|
78
79
|
// Handle GET requests for SSE streams (using built-in support from StreamableHTTP)
|
79
80
|
app.get('/mcp', async (req, res) => {
|
80
|
-
console.
|
81
|
+
console.error('Received MCP GET request');
|
81
82
|
const sessionId = req.headers['mcp-session-id'];
|
82
|
-
if (!sessionId || !transports
|
83
|
+
if (!sessionId || !transports.has(sessionId)) {
|
83
84
|
res.status(400).json({
|
84
85
|
jsonrpc: '2.0',
|
85
86
|
error: {
|
@@ -93,18 +94,18 @@ app.get('/mcp', async (req, res) => {
|
|
93
94
|
// Check for Last-Event-ID header for resumability
|
94
95
|
const lastEventId = req.headers['last-event-id'];
|
95
96
|
if (lastEventId) {
|
96
|
-
console.
|
97
|
+
console.error(`Client reconnecting with Last-Event-ID: ${lastEventId}`);
|
97
98
|
}
|
98
99
|
else {
|
99
|
-
console.
|
100
|
+
console.error(`Establishing new SSE stream for session ${sessionId}`);
|
100
101
|
}
|
101
|
-
const transport = transports
|
102
|
+
const transport = transports.get(sessionId);
|
102
103
|
await transport.handleRequest(req, res);
|
103
104
|
});
|
104
105
|
// Handle DELETE requests for session termination (according to MCP spec)
|
105
106
|
app.delete('/mcp', async (req, res) => {
|
106
107
|
const sessionId = req.headers['mcp-session-id'];
|
107
|
-
if (!sessionId || !transports
|
108
|
+
if (!sessionId || !transports.has(sessionId)) {
|
108
109
|
res.status(400).json({
|
109
110
|
jsonrpc: '2.0',
|
110
111
|
error: {
|
@@ -115,9 +116,9 @@ app.delete('/mcp', async (req, res) => {
|
|
115
116
|
});
|
116
117
|
return;
|
117
118
|
}
|
118
|
-
console.
|
119
|
+
console.error(`Received session termination request for session ${sessionId}`);
|
119
120
|
try {
|
120
|
-
const transport = transports
|
121
|
+
const transport = transports.get(sessionId);
|
121
122
|
await transport.handleRequest(req, res);
|
122
123
|
}
|
123
124
|
catch (error) {
|
@@ -138,24 +139,22 @@ app.delete('/mcp', async (req, res) => {
|
|
138
139
|
// Start the server
|
139
140
|
const PORT = process.env.PORT || 3001;
|
140
141
|
app.listen(PORT, () => {
|
141
|
-
console.
|
142
|
+
console.error(`MCP Streamable HTTP Server listening on port ${PORT}`);
|
142
143
|
});
|
143
144
|
// Handle server shutdown
|
144
145
|
process.on('SIGINT', async () => {
|
145
|
-
console.
|
146
|
+
console.error('Shutting down server...');
|
146
147
|
// Close all active transports to properly clean up resources
|
147
148
|
for (const sessionId in transports) {
|
148
149
|
try {
|
149
|
-
console.
|
150
|
-
await transports
|
151
|
-
delete
|
150
|
+
console.error(`Closing transport for session ${sessionId}`);
|
151
|
+
await transports.get(sessionId).close();
|
152
|
+
transports.delete(sessionId);
|
152
153
|
}
|
153
154
|
catch (error) {
|
154
155
|
console.error(`Error closing transport for session ${sessionId}:`, error);
|
155
156
|
}
|
156
157
|
}
|
157
|
-
|
158
|
-
await server.close();
|
159
|
-
console.log('Server shutdown complete');
|
158
|
+
console.error('Server shutdown complete');
|
160
159
|
process.exit(0);
|
161
160
|
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@modelcontextprotocol/server-everything",
|
3
|
-
"version": "2025.
|
3
|
+
"version": "2025.7.29",
|
4
4
|
"description": "MCP server that exercises all the features of the MCP protocol",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Anthropic, PBC (https://anthropic.com)",
|
@@ -14,7 +14,7 @@
|
|
14
14
|
"dist"
|
15
15
|
],
|
16
16
|
"scripts": {
|
17
|
-
"build": "tsc && shx chmod +x dist/*.js",
|
17
|
+
"build": "tsc && shx cp instructions.md dist/ && shx chmod +x dist/*.js",
|
18
18
|
"prepare": "npm run build",
|
19
19
|
"watch": "tsc --watch",
|
20
20
|
"start": "node dist/index.js",
|
@@ -22,7 +22,7 @@
|
|
22
22
|
"start:streamableHttp": "node dist/streamableHttp.js"
|
23
23
|
},
|
24
24
|
"dependencies": {
|
25
|
-
"@modelcontextprotocol/sdk": "^1.
|
25
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
26
26
|
"express": "^4.21.1",
|
27
27
|
"zod": "^3.23.8",
|
28
28
|
"zod-to-json-schema": "^3.23.5"
|