From 218ad2d3f51ef408f172e0b7dd250020b2301d5d Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 14:26:20 +0000 Subject: [PATCH] feat(everything): add resource collection example This adds a resource collection example demonstrating the pattern where a single resources/read request returns multiple ResourceContents with different URIs. This is useful for modeling collections, indexes, or composite resources. The example includes: - A summer specials collection that returns all items - Category-filtered collections for accessories, footwear, and skincare - Individual item resources for direct access Based on the gist provided by @olaservo: https://gist.github.com/olaservo/585b1ad042723a824b6a0e0e02ae60a8 Fixes #3130 Co-authored-by: Ola Hungerford --- src/everything/resources/collections.ts | 104 ++++++++++++++++++++++++ src/everything/resources/index.ts | 2 + 2 files changed, 106 insertions(+) create mode 100644 src/everything/resources/collections.ts diff --git a/src/everything/resources/collections.ts b/src/everything/resources/collections.ts new file mode 100644 index 0000000000..b14ef59edb --- /dev/null +++ b/src/everything/resources/collections.ts @@ -0,0 +1,104 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +/** + * Sample collection items + * - These items represent a simple product catalog for demonstration purposes + */ +const collectionItems = [ + { id: 1, name: "Summer Hat", price: 29.99, category: "accessories" }, + { id: 2, name: "Beach Towel", price: 19.99, category: "accessories" }, + { id: 3, name: "Sunglasses", price: 49.99, category: "accessories" }, + { id: 4, name: "Flip Flops", price: 14.99, category: "footwear" }, + { id: 5, name: "Sunscreen SPF50", price: 12.99, category: "skincare" }, +]; + +const collectionUriBase = "demo://collection"; +const itemUriBase = "demo://collection/item"; + +/** + * Helper to generate an item URI + * @param id + */ +const itemUri = (id: number) => `${itemUriBase}/${id}`; + +/** + * Register collection resources with the MCP server. + * + * This demonstrates the pattern where a single resources/read request returns + * multiple ResourceContents with different URIs - useful for modeling collections, + * indexes, or composite resources. + * + * Resources: + * - `demo://collection/summer-specials`: Returns all items as separate resources + * - `demo://collection/by-category/{category}`: Returns items filtered by category + * - `demo://collection/item/{id}`: Returns a single item (for individual access) + * + * @param server + */ +export const registerCollectionResources = (server: McpServer) => { + // Main collection resource returning all items + server.registerResource( + "Summer Specials Collection", + `${collectionUriBase}/summer-specials`, + { + mimeType: "application/json", + description: + "A collection resource that returns multiple items, each with its own URI.", + }, + async () => { + return { + contents: collectionItems.map((item) => ({ + uri: itemUri(item.id), + mimeType: "application/json", + text: JSON.stringify(item, null, 2), + })), + }; + } + ); + + // Category-filtered collections + const categories = [...new Set(collectionItems.map((i) => i.category))]; + for (const category of categories) { + server.registerResource( + `Collection: ${category}`, + `${collectionUriBase}/by-category/${category}`, + { + mimeType: "application/json", + description: `Items in the "${category}" category.`, + }, + async () => { + const filtered = collectionItems.filter((i) => i.category === category); + return { + contents: filtered.map((item) => ({ + uri: itemUri(item.id), + mimeType: "application/json", + text: JSON.stringify(item, null, 2), + })), + }; + } + ); + } + + // Individual item resources + for (const item of collectionItems) { + server.registerResource( + `Item: ${item.name}`, + itemUri(item.id), + { + mimeType: "application/json", + description: `Individual item: ${item.name} ($${item.price})`, + }, + async (uri) => { + return { + contents: [ + { + uri: uri.toString(), + mimeType: "application/json", + text: JSON.stringify(item, null, 2), + }, + ], + }; + } + ); + } +}; diff --git a/src/everything/resources/index.ts b/src/everything/resources/index.ts index 30c6f7dcf8..158105d52b 100644 --- a/src/everything/resources/index.ts +++ b/src/everything/resources/index.ts @@ -1,6 +1,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerResourceTemplates } from "./templates.js"; import { registerFileResources } from "./files.js"; +import { registerCollectionResources } from "./collections.js"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; import { readFileSync } from "fs"; @@ -12,6 +13,7 @@ import { readFileSync } from "fs"; export const registerResources = (server: McpServer) => { registerResourceTemplates(server); registerFileResources(server); + registerCollectionResources(server); }; /**