From 713eb6c1805cf5c46de2c4fa392ef2da727c5977 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:28:30 +0000 Subject: [PATCH 1/3] fix: align everything server resource URI numbering with array indices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes issue where array indices (0-based) didn't match URI numbers (1-based), causing confusion where: - Resource at index 0 was accessed via 'test://static/resource/1' - Even indices (text resources) mapped to odd URI numbers Changes: - Resource URIs now use 0-based numbering to match array indices - Resource 0 is now accessed via 'test://static/resource/0' - Even indices now map to even URI numbers (more intuitive) Resolves #475 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Ola Hungerford --- src/everything/everything.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/everything/everything.ts b/src/everything/everything.ts index 26e4352193..4885a3e00b 100644 --- a/src/everything/everything.ts +++ b/src/everything/everything.ts @@ -254,7 +254,7 @@ export const createServer = () => { }; const ALL_RESOURCES: Resource[] = Array.from({ length: 100 }, (_, i) => { - const uri = `test://static/resource/${i + 1}`; + const uri = `test://static/resource/${i}`; if (i % 2 === 0) { return { uri, @@ -316,7 +316,7 @@ export const createServer = () => { const uri = request.params.uri; if (uri.startsWith("test://static/resource/")) { - const index = parseInt(uri.split("/").pop() ?? "", 10) - 1; + const index = parseInt(uri.split("/").pop() ?? "", 10); if (index >= 0 && index < ALL_RESOURCES.length) { const resource = ALL_RESOURCES[index]; return { From 9d1a63cd4255a07ff0c3b4a0ba1052d405828481 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:46:39 +0000 Subject: [PATCH 2/3] feat: replace array index-based resource URIs with UUID-based semantic paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements @domdomegg's proposal to use UUIDs for resource identification instead of array indices: - Generate UUIDs for all 100 resources (50 text, 50 blob) - Use semantic paths: test://static/resource/text/{uuid} and test://static/resource/blob/{uuid} - Remove dependency on array indices for URI construction - Update resource templates to reflect new URI patterns - Modify getResourceReference tool to accept resourceUri instead of resourceId - Update resource_prompt argument from resourceId to resourceUri - Improve resource link descriptions to mention UUID-based identification - Update README documentation to reflect UUID-based system This eliminates the confusion between array indices (0-based) and URI numbers (1-based) while providing more intuitive resource categorization through semantic paths. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: adam jones --- src/everything/README.md | 23 +++--- src/everything/everything.ts | 134 +++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 72 deletions(-) diff --git a/src/everything/README.md b/src/everything/README.md index 7c98b8588b..5d3b2ba679 100644 --- a/src/everything/README.md +++ b/src/everything/README.md @@ -66,7 +66,7 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is 8. `getResourceReference` - Returns a resource reference that can be used by MCP clients - Inputs: - - `resourceId` (number, 1-100): ID of the resource to reference + - `resourceUri` (string): URI of the resource to reference (e.g., test://static/resource/text/{uuid}) - Returns: A resource reference with: - Text introduction - Embedded resource with `type: "resource"` @@ -91,18 +91,23 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is ### Resources -The server provides 100 test resources in two formats: -- Even numbered resources: +The server provides 100 test resources (50 text, 50 blob) using UUID-based identifiers: + +- Text resources: - Plaintext format - - URI pattern: `test://static/resource/{even_number}` - - Content: Simple text description + - URI pattern: `test://static/resource/text/{uuid}` + - Content: Simple text description with UUID + - Names: "Text Resource 1" through "Text Resource 50" -- Odd numbered resources: +- Blob resources: - Binary blob format - - URI pattern: `test://static/resource/{odd_number}` - - Content: Base64 encoded binary data + - URI pattern: `test://static/resource/blob/{uuid}` + - Content: Base64 encoded binary data with UUID + - Names: "Blob Resource 1" through "Blob Resource 50" Resource features: +- UUID-based identifiers eliminate index/URI confusion +- Semantic paths clearly indicate resource type (text vs blob) - Supports pagination (10 items per page) - Allows subscribing to resource updates - Demonstrates resource templates @@ -125,7 +130,7 @@ Resource features: 3. `resource_prompt` - Demonstrates embedding resource references in prompts - Required arguments: - - `resourceId` (number): ID of the resource to embed (1-100) + - `resourceUri` (string): URI of the resource to embed (e.g., test://static/resource/text/{uuid}) - Returns: Multi-turn conversation with an embedded resource reference - Shows how to include resources directly in prompt messages diff --git a/src/everything/everything.ts b/src/everything/everything.ts index 14550a2b0e..03351b6584 100644 --- a/src/everything/everything.ts +++ b/src/everything/everything.ts @@ -23,6 +23,7 @@ import { zodToJsonSchema } from "zod-to-json-schema"; import { readFileSync } from "fs"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; +import { randomUUID } from "crypto"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -78,11 +79,9 @@ const AnnotatedMessageSchema = z.object({ }); const GetResourceReferenceSchema = z.object({ - resourceId: z - .number() - .min(1) - .max(100) - .describe("ID of the resource to reference (1-100)"), + resourceUri: z + .string() + .describe("URI of the resource to reference (e.g., test://static/resource/text/{uuid})"), }); const ElicitationSchema = z.object({}); @@ -91,9 +90,9 @@ const GetResourceLinksSchema = z.object({ count: z .number() .min(1) - .max(10) - .default(3) - .describe("Number of resource links to return (1-10)"), + .max(100) + .default(5) + .describe("Number of resource links to return (1-100)"), }); const StructuredContentSchema = { @@ -138,11 +137,11 @@ enum PromptName { RESOURCE = "resource_prompt", } -// Example completion values -const EXAMPLE_COMPLETIONS = { - style: ["casual", "formal", "technical", "friendly"], - temperature: ["0", "0.5", "0.7", "1.0"], - resourceId: ["1", "2", "3", "4", "5"], +// Example completion values - initialized after ALL_RESOURCES is defined +let EXAMPLE_COMPLETIONS: { + style: string[]; + temperature: string[]; + resourceUri: string[]; }; export const createServer = () => { @@ -258,28 +257,36 @@ export const createServer = () => { return await server.request(request, z.any()); }; - const ALL_RESOURCES: Resource[] = Array.from({ length: 100 }, (_, i) => { - const uri = `test://static/resource/${i}`; - if (i % 2 === 0) { - return { - uri, - name: `Resource ${i + 1}`, - mimeType: "text/plain", - text: `Resource ${i + 1}: This is a plaintext resource`, - }; - } else { - const buffer = Buffer.from(`Resource ${i + 1}: This is a base64 blob`); - return { - uri, - name: `Resource ${i + 1}`, - mimeType: "application/octet-stream", - blob: buffer.toString("base64"), - }; - } - }); + // Generate UUIDs for resources (50 text, 50 blob) + const TEXT_RESOURCE_UUIDS = Array.from({ length: 50 }, () => randomUUID()); + const BLOB_RESOURCE_UUIDS = Array.from({ length: 50 }, () => randomUUID()); + + const ALL_RESOURCES: Resource[] = [ + // Text resources + ...TEXT_RESOURCE_UUIDS.map((uuid, i) => ({ + uri: `test://static/resource/text/${uuid}`, + name: `Text Resource ${i + 1}`, + mimeType: "text/plain", + text: `Text Resource ${i + 1}: This is a plaintext resource with UUID ${uuid}`, + } as Resource)), + // Blob resources + ...BLOB_RESOURCE_UUIDS.map((uuid, i) => ({ + uri: `test://static/resource/blob/${uuid}`, + name: `Blob Resource ${i + 1}`, + mimeType: "application/octet-stream", + blob: Buffer.from(`Blob Resource ${i + 1}: This is a base64 blob with UUID ${uuid}`).toString("base64"), + } as Resource)), + ]; const PAGE_SIZE = 10; + // Initialize completion values after ALL_RESOURCES is defined + EXAMPLE_COMPLETIONS = { + style: ["casual", "formal", "technical", "friendly"], + temperature: ["0", "0.5", "0.7", "1.0"], + resourceUri: ALL_RESOURCES.slice(0, 5).map(r => r.uri), + }; + server.setRequestHandler(ListResourcesRequestSchema, async (request) => { const cursor = request.params?.cursor; let startIndex = 0; @@ -309,9 +316,14 @@ export const createServer = () => { return { resourceTemplates: [ { - uriTemplate: "test://static/resource/{id}", - name: "Static Resource", - description: "A static resource with a numeric ID", + uriTemplate: "test://static/resource/text/{uuid}", + name: "Text Resource", + description: "A static text resource with a UUID", + }, + { + uriTemplate: "test://static/resource/blob/{uuid}", + name: "Blob Resource", + description: "A static blob resource with a UUID", }, ], }; @@ -321,9 +333,8 @@ export const createServer = () => { const uri = request.params.uri; if (uri.startsWith("test://static/resource/")) { - const index = parseInt(uri.split("/").pop() ?? "", 10); - if (index >= 0 && index < ALL_RESOURCES.length) { - const resource = ALL_RESOURCES[index]; + const resource = ALL_RESOURCES.find(r => r.uri === uri); + if (resource) { return { contents: [resource], }; @@ -375,8 +386,8 @@ export const createServer = () => { description: "A prompt that includes an embedded resource reference", arguments: [ { - name: "resourceId", - description: "Resource ID to include (1-100)", + name: "resourceUri", + description: "Resource URI to include (e.g., test://static/resource/text/{uuid})", required: true, }, ], @@ -432,15 +443,17 @@ export const createServer = () => { } if (name === PromptName.RESOURCE) { - const resourceId = parseInt(args?.resourceId as string, 10); - if (isNaN(resourceId) || resourceId < 1 || resourceId > 100) { + const resourceUri = args?.resourceUri as string; + if (!resourceUri) { throw new Error( - `Invalid resourceId: ${args?.resourceId}. Must be a number between 1 and 100.` + `Invalid resourceUri: ${args?.resourceUri}. Must be a valid resource URI.` ); } - const resourceIndex = resourceId - 1; - const resource = ALL_RESOURCES[resourceIndex]; + const resource = ALL_RESOURCES.find(r => r.uri === resourceUri); + if (!resource) { + throw new Error(`Resource with URI ${resourceUri} does not exist`); + } return { messages: [ @@ -448,7 +461,7 @@ export const createServer = () => { role: "user", content: { type: "text", - text: `This prompt includes Resource ${resourceId}. Please analyze the following resource:`, + text: `This prompt includes ${resource.name}. Please analyze the following resource:`, }, }, { @@ -691,20 +704,18 @@ export const createServer = () => { if (name === ToolName.GET_RESOURCE_REFERENCE) { const validatedArgs = GetResourceReferenceSchema.parse(args); - const resourceId = validatedArgs.resourceId; + const resourceUri = validatedArgs.resourceUri; - const resourceIndex = resourceId - 1; - if (resourceIndex < 0 || resourceIndex >= ALL_RESOURCES.length) { - throw new Error(`Resource with ID ${resourceId} does not exist`); + const resource = ALL_RESOURCES.find(r => r.uri === resourceUri); + if (!resource) { + throw new Error(`Resource with URI ${resourceUri} does not exist`); } - const resource = ALL_RESOURCES[resourceIndex]; - return { content: [ { type: "text", - text: `Returning resource reference for Resource ${resourceId}:`, + text: `Returning resource reference for ${resource.name}:`, }, { type: "resource", @@ -791,10 +802,10 @@ export const createServer = () => { type: "resource_link", uri: resource.uri, name: resource.name, - description: `Resource ${i + 1}: ${ + description: `${resource.name}: ${ resource.mimeType === "text/plain" - ? "plaintext resource" - : "binary blob resource" + ? "plaintext resource with UUID" + : "binary blob resource with UUID" }`, mimeType: resource.mimeType, }); @@ -831,12 +842,9 @@ export const createServer = () => { const { ref, argument } = request.params; if (ref.type === "ref/resource") { - const resourceId = ref.uri.split("/").pop(); - if (!resourceId) return { completion: { values: [] } }; - - // Filter resource IDs that start with the input value - const values = EXAMPLE_COMPLETIONS.resourceId.filter((id) => - id.startsWith(argument.value) + // Filter resource URIs that contain the input value + const values = EXAMPLE_COMPLETIONS.resourceUri.filter((uri) => + uri.toLowerCase().includes(argument.value.toLowerCase()) ); return { completion: { values, hasMore: false, total: values.length } }; } @@ -848,7 +856,7 @@ export const createServer = () => { if (!completions) return { completion: { values: [] } }; const values = completions.filter((value) => - value.startsWith(argument.value) + value.toLowerCase().includes(argument.value.toLowerCase()) ); return { completion: { values, hasMore: false, total: values.length } }; } From 7d88d4e932c8398fb3cceb64502085cd1c7143d3 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:48:34 +0000 Subject: [PATCH 3/3] feat: interleave text and blob resources for better variety Previously resources were generated as 50 text resources followed by 50 blob resources. Now they alternate: Text, Blob, Text, Blob, etc. This provides better variety when using tools like getResourceLinks and maintains the useful interleaving pattern that was present before the UUID-based refactor. Co-authored-by: Ola Hungerford --- src/everything/everything.ts | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/everything/everything.ts b/src/everything/everything.ts index 03351b6584..df1e965d84 100644 --- a/src/everything/everything.ts +++ b/src/everything/everything.ts @@ -257,26 +257,28 @@ export const createServer = () => { return await server.request(request, z.any()); }; - // Generate UUIDs for resources (50 text, 50 blob) - const TEXT_RESOURCE_UUIDS = Array.from({ length: 50 }, () => randomUUID()); - const BLOB_RESOURCE_UUIDS = Array.from({ length: 50 }, () => randomUUID()); - - const ALL_RESOURCES: Resource[] = [ - // Text resources - ...TEXT_RESOURCE_UUIDS.map((uuid, i) => ({ - uri: `test://static/resource/text/${uuid}`, + // Generate resources with alternating types (50 text, 50 blob, interleaved) + const ALL_RESOURCES: Resource[] = []; + + for (let i = 0; i < 50; i++) { + // Add text resource + const textUuid = randomUUID(); + ALL_RESOURCES.push({ + uri: `test://static/resource/text/${textUuid}`, name: `Text Resource ${i + 1}`, mimeType: "text/plain", - text: `Text Resource ${i + 1}: This is a plaintext resource with UUID ${uuid}`, - } as Resource)), - // Blob resources - ...BLOB_RESOURCE_UUIDS.map((uuid, i) => ({ - uri: `test://static/resource/blob/${uuid}`, + text: `Text Resource ${i + 1}: This is a plaintext resource with UUID ${textUuid}`, + } as Resource); + + // Add blob resource + const blobUuid = randomUUID(); + ALL_RESOURCES.push({ + uri: `test://static/resource/blob/${blobUuid}`, name: `Blob Resource ${i + 1}`, mimeType: "application/octet-stream", - blob: Buffer.from(`Blob Resource ${i + 1}: This is a base64 blob with UUID ${uuid}`).toString("base64"), - } as Resource)), - ]; + blob: Buffer.from(`Blob Resource ${i + 1}: This is a base64 blob with UUID ${blobUuid}`).toString("base64"), + } as Resource); + } const PAGE_SIZE = 10;