Skip to content

Commit c798393

Browse files
authored
Merge branch 'main' into ajoslin/memory
2 parents d28f616 + c5d2865 commit c798393

File tree

10 files changed

+352
-133
lines changed

10 files changed

+352
-133
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ jobs:
104104
105105
publish-pypi:
106106
needs: [update-packages, create-metadata]
107+
if: ${{ needs.create-metadata.outputs.pypi_packages != '[]' && needs.create-metadata.outputs.pypi_packages != '' }}
107108
strategy:
108109
fail-fast: false
109110
matrix:
@@ -145,6 +146,7 @@ jobs:
145146

146147
publish-npm:
147148
needs: [update-packages, create-metadata]
149+
if: ${{ needs.create-metadata.outputs.npm_packages != '[]' && needs.create-metadata.outputs.npm_packages != '' }}
148150
strategy:
149151
fail-fast: false
150152
matrix:

README.md

Lines changed: 15 additions & 10 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/everything/README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,24 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is
2727
- Returns: Completion message with duration and steps
2828
- Sends progress notifications during execution
2929

30-
4. `sampleLLM`
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`
3137
- Demonstrates LLM sampling capability using MCP sampling feature
3238
- Inputs:
3339
- `prompt` (string): The prompt to send to the LLM
3440
- `maxTokens` (number, default: 100): Maximum tokens to generate
3541
- Returns: Generated LLM response
3642

37-
5. `getTinyImage`
43+
6. `getTinyImage`
3844
- Returns a small test image
3945
- No inputs required
4046
- Returns: Base64 encoded PNG image data
4147

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-
4848
7. `annotatedMessage`
4949
- Demonstrates how annotations can be used to provide metadata about content
5050
- Inputs:
@@ -80,6 +80,15 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is
8080
- `pets` (enum): Favorite pet
8181
- Returns: Confirmation of the elicitation demo with selection summary.
8282

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+
8392
### Resources
8493

8594
The server provides 100 test resources in two formats:

src/everything/everything.ts

Lines changed: 105 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ const instructions = readFileSync(join(__dirname, "instructions.md"), "utf-8");
3131
const ToolInputSchema = ToolSchema.shape.inputSchema;
3232
type ToolInput = z.infer<typeof ToolInputSchema>;
3333

34+
const ToolOutputSchema = ToolSchema.shape.outputSchema;
35+
type ToolOutput = z.infer<typeof ToolOutputSchema>;
36+
3437
/* Input schemas for tools implemented in this server */
3538
const EchoSchema = z.object({
3639
message: z.string().describe("Message to echo"),
@@ -46,7 +49,10 @@ const LongRunningOperationSchema = z.object({
4649
.number()
4750
.default(10)
4851
.describe("Duration of the operation in seconds"),
49-
steps: z.number().default(5).describe("Number of steps in the operation"),
52+
steps: z
53+
.number()
54+
.default(5)
55+
.describe("Number of steps in the operation"),
5056
});
5157

5258
const PrintEnvSchema = z.object({});
@@ -59,13 +65,6 @@ const SampleLLMSchema = z.object({
5965
.describe("Maximum number of tokens to generate"),
6066
});
6167

62-
// Example completion values
63-
const EXAMPLE_COMPLETIONS = {
64-
style: ["casual", "formal", "technical", "friendly"],
65-
temperature: ["0", "0.5", "0.7", "1.0"],
66-
resourceId: ["1", "2", "3", "4", "5"],
67-
};
68-
6968
const GetTinyImageSchema = z.object({});
7069

7170
const AnnotatedMessageSchema = z.object({
@@ -97,6 +96,28 @@ const GetResourceLinksSchema = z.object({
9796
.describe("Number of resource links to return (1-10)"),
9897
});
9998

99+
const StructuredContentSchema = {
100+
input: z.object({
101+
location: z
102+
.string()
103+
.trim()
104+
.min(1)
105+
.describe("City name or zip code"),
106+
}),
107+
108+
output: z.object({
109+
temperature: z
110+
.number()
111+
.describe("Temperature in celsius"),
112+
conditions: z
113+
.string()
114+
.describe("Weather conditions description"),
115+
humidity: z
116+
.number()
117+
.describe("Humidity percentage"),
118+
})
119+
};
120+
100121
enum ToolName {
101122
ECHO = "echo",
102123
ADD = "add",
@@ -108,6 +129,7 @@ enum ToolName {
108129
GET_RESOURCE_REFERENCE = "getResourceReference",
109130
ELICITATION = "startElicitation",
110131
GET_RESOURCE_LINKS = "getResourceLinks",
132+
STRUCTURED_CONTENT = "structuredContent"
111133
}
112134

113135
enum PromptName {
@@ -116,10 +138,18 @@ enum PromptName {
116138
RESOURCE = "resource_prompt",
117139
}
118140

141+
// Example completion values
142+
const EXAMPLE_COMPLETIONS = {
143+
style: ["casual", "formal", "technical", "friendly"],
144+
temperature: ["0", "0.5", "0.7", "1.0"],
145+
resourceId: ["1", "2", "3", "4", "5"],
146+
};
147+
119148
export const createServer = () => {
120149
const server = new Server(
121150
{
122151
name: "example-servers/everything",
152+
title: "Everything Example Server",
123153
version: "1.0.0",
124154
},
125155
{
@@ -179,18 +209,6 @@ export const createServer = () => {
179209
}, 20000);
180210

181211

182-
// Set up update interval for stderr messages
183-
stdErrUpdateInterval = setInterval(() => {
184-
const shortTimestamp = new Date().toLocaleTimeString([], {
185-
hour: "2-digit",
186-
minute: "2-digit",
187-
second: "2-digit"
188-
});
189-
server.notification({
190-
method: "notifications/stderr",
191-
params: { content: `${shortTimestamp}: A stderr message` },
192-
});
193-
}, 30000);
194212

195213
// Helper method to request sampling from client
196214
const requestSampling = async (
@@ -454,18 +472,18 @@ export const createServer = () => {
454472
description: "Adds two numbers",
455473
inputSchema: zodToJsonSchema(AddSchema) as ToolInput,
456474
},
457-
{
458-
name: ToolName.PRINT_ENV,
459-
description:
460-
"Prints all environment variables, helpful for debugging MCP server configuration",
461-
inputSchema: zodToJsonSchema(PrintEnvSchema) as ToolInput,
462-
},
463475
{
464476
name: ToolName.LONG_RUNNING_OPERATION,
465477
description:
466478
"Demonstrates a long running operation with progress updates",
467479
inputSchema: zodToJsonSchema(LongRunningOperationSchema) as ToolInput,
468480
},
481+
{
482+
name: ToolName.PRINT_ENV,
483+
description:
484+
"Prints all environment variables, helpful for debugging MCP server configuration",
485+
inputSchema: zodToJsonSchema(PrintEnvSchema) as ToolInput,
486+
},
469487
{
470488
name: ToolName.SAMPLE_LLM,
471489
description: "Samples from an LLM using MCP's sampling feature",
@@ -499,6 +517,13 @@ export const createServer = () => {
499517
"Returns multiple resource links that reference different types of resources",
500518
inputSchema: zodToJsonSchema(GetResourceLinksSchema) as ToolInput,
501519
},
520+
{
521+
name: ToolName.STRUCTURED_CONTENT,
522+
description:
523+
"Returns structured content along with an output schema for client data validation",
524+
inputSchema: zodToJsonSchema(StructuredContentSchema.input) as ToolInput,
525+
outputSchema: zodToJsonSchema(StructuredContentSchema.output) as ToolOutput,
526+
},
502527
];
503528

504529
return { tools };
@@ -608,35 +633,6 @@ export const createServer = () => {
608633
};
609634
}
610635

611-
if (name === ToolName.GET_RESOURCE_REFERENCE) {
612-
const validatedArgs = GetResourceReferenceSchema.parse(args);
613-
const resourceId = validatedArgs.resourceId;
614-
615-
const resourceIndex = resourceId - 1;
616-
if (resourceIndex < 0 || resourceIndex >= ALL_RESOURCES.length) {
617-
throw new Error(`Resource with ID ${resourceId} does not exist`);
618-
}
619-
620-
const resource = ALL_RESOURCES[resourceIndex];
621-
622-
return {
623-
content: [
624-
{
625-
type: "text",
626-
text: `Returning resource reference for Resource ${resourceId}:`,
627-
},
628-
{
629-
type: "resource",
630-
resource: resource,
631-
},
632-
{
633-
type: "text",
634-
text: `You can access this resource using the URI: ${resource.uri}`,
635-
},
636-
],
637-
};
638-
}
639-
640636
if (name === ToolName.ANNOTATED_MESSAGE) {
641637
const { messageType, includeImage } = AnnotatedMessageSchema.parse(args);
642638

@@ -688,6 +684,35 @@ export const createServer = () => {
688684
return { content };
689685
}
690686

687+
if (name === ToolName.GET_RESOURCE_REFERENCE) {
688+
const validatedArgs = GetResourceReferenceSchema.parse(args);
689+
const resourceId = validatedArgs.resourceId;
690+
691+
const resourceIndex = resourceId - 1;
692+
if (resourceIndex < 0 || resourceIndex >= ALL_RESOURCES.length) {
693+
throw new Error(`Resource with ID ${resourceId} does not exist`);
694+
}
695+
696+
const resource = ALL_RESOURCES[resourceIndex];
697+
698+
return {
699+
content: [
700+
{
701+
type: "text",
702+
text: `Returning resource reference for Resource ${resourceId}:`,
703+
},
704+
{
705+
type: "resource",
706+
resource: resource,
707+
},
708+
{
709+
type: "text",
710+
text: `You can access this resource using the URI: ${resource.uri}`,
711+
},
712+
],
713+
};
714+
}
715+
691716
if (name === ToolName.ELICITATION) {
692717
ElicitationSchema.parse(args);
693718

@@ -709,13 +734,13 @@ export const createServer = () => {
709734

710735
// Handle different response actions
711736
const content = [];
712-
737+
713738
if (elicitationResult.action === 'accept' && elicitationResult.content) {
714739
content.push({
715740
type: "text",
716741
text: `✅ User provided their favorite things!`,
717742
});
718-
743+
719744
// Only access elicitationResult.content when action is accept
720745
const { color, number, pets } = elicitationResult.content;
721746
content.push({
@@ -733,7 +758,7 @@ export const createServer = () => {
733758
text: `⚠️ User cancelled the elicitation dialog.`,
734759
});
735760
}
736-
761+
737762
// Include raw result for debugging
738763
content.push({
739764
type: "text",
@@ -742,7 +767,7 @@ export const createServer = () => {
742767

743768
return { content };
744769
}
745-
770+
746771
if (name === ToolName.GET_RESOURCE_LINKS) {
747772
const { count } = GetResourceLinksSchema.parse(args);
748773
const content = [];
@@ -773,6 +798,27 @@ export const createServer = () => {
773798
return { content };
774799
}
775800

801+
if (name === ToolName.STRUCTURED_CONTENT) {
802+
// The same response is returned for every input.
803+
const validatedArgs = StructuredContentSchema.input.parse(args);
804+
805+
const weather = {
806+
temperature: 22.5,
807+
conditions: "Partly cloudy",
808+
humidity: 65
809+
}
810+
811+
const backwardCompatiblecontent = {
812+
type: "text",
813+
text: JSON.stringify(weather)
814+
}
815+
816+
return {
817+
content: [ backwardCompatiblecontent ],
818+
structuredContent: weather
819+
};
820+
}
821+
776822
throw new Error(`Unknown tool: ${name}`);
777823
});
778824

0 commit comments

Comments
 (0)