diff --git a/src/everything/README.md b/src/everything/README.md index e30b0a7d..86194614 100644 --- a/src/everything/README.md +++ b/src/everything/README.md @@ -80,6 +80,15 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is - `pets` (enum): Favorite pet - Returns: Confirmation of the elicitation demo with selection summary. +10. `structuredContent` + - Demonstrates a tool returning structured content using the example in the specification + - Provides an output schema to allow testing of client SHOULD advisory to validate the result using the schema + - Inputs: + - `location` (string): A location or ZIP code, mock data is returned regardless of value + - Returns: a response with + - `structuredContent` field conformant to the output schema + - A backward compatible Text Content field, a SHOULD advisory in the specification + ### Resources The server provides 100 test resources in two formats: diff --git a/src/everything/everything.ts b/src/everything/everything.ts index c1f1b4eb..f1a2a11d 100644 --- a/src/everything/everything.ts +++ b/src/everything/everything.ts @@ -31,6 +31,9 @@ const instructions = readFileSync(join(__dirname, "instructions.md"), "utf-8"); const ToolInputSchema = ToolSchema.shape.inputSchema; type ToolInput = z.infer; +const ToolOutputSchema = ToolSchema.shape.outputSchema; +type ToolOutput = z.infer; + /* Input schemas for tools implemented in this server */ const EchoSchema = z.object({ message: z.string().describe("Message to echo"), @@ -93,6 +96,28 @@ const GetResourceLinksSchema = z.object({ .describe("Number of resource links to return (1-10)"), }); +const StructuredContentSchema = { + input: z.object({ + location: z + .string() + .trim() + .min(1) + .describe("City name or zip code"), + }), + + output: z.object({ + temperature: z + .number() + .describe("Temperature in celsius"), + conditions: z + .string() + .describe("Weather conditions description"), + humidity: z + .number() + .describe("Humidity percentage"), + }) +}; + enum ToolName { ECHO = "echo", ADD = "add", @@ -104,6 +129,7 @@ enum ToolName { GET_RESOURCE_REFERENCE = "getResourceReference", ELICITATION = "startElicitation", GET_RESOURCE_LINKS = "getResourceLinks", + STRUCTURED_CONTENT = "structuredContent" } enum PromptName { @@ -503,6 +529,13 @@ export const createServer = () => { "Returns multiple resource links that reference different types of resources", inputSchema: zodToJsonSchema(GetResourceLinksSchema) as ToolInput, }, + { + name: ToolName.STRUCTURED_CONTENT, + description: + "Returns structured content along with an output schema for client data validation", + inputSchema: zodToJsonSchema(StructuredContentSchema.input) as ToolInput, + outputSchema: zodToJsonSchema(StructuredContentSchema.output) as ToolOutput, + }, ]; return { tools }; @@ -777,6 +810,27 @@ export const createServer = () => { return { content }; } + if (name === ToolName.STRUCTURED_CONTENT) { + // The same response is returned for every input. + const validatedArgs = StructuredContentSchema.input.parse(args); + + const weather = { + temperature: 22.5, + conditions: "Partly cloudy", + humidity: 65 + } + + const backwardCompatiblecontent = { + type: "text", + text: JSON.stringify(weather) + } + + return { + content: [ backwardCompatiblecontent ], + structuredContent: weather + }; + } + throw new Error(`Unknown tool: ${name}`); });