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 <olaservo@users.noreply.github.com>
This commit is contained in:
claude[bot]
2026-01-11 14:26:20 +00:00
parent 861c11b786
commit 218ad2d3f5
2 changed files with 106 additions and 0 deletions

View File

@@ -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),
},
],
};
}
);
}
};

View File

@@ -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);
};
/**