mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-25 15:25:15 +02:00
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:
104
src/everything/resources/collections.ts
Normal file
104
src/everything/resources/collections.ts
Normal 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),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import { registerResourceTemplates } from "./templates.js";
|
import { registerResourceTemplates } from "./templates.js";
|
||||||
import { registerFileResources } from "./files.js";
|
import { registerFileResources } from "./files.js";
|
||||||
|
import { registerCollectionResources } from "./collections.js";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
@@ -12,6 +13,7 @@ import { readFileSync } from "fs";
|
|||||||
export const registerResources = (server: McpServer) => {
|
export const registerResources = (server: McpServer) => {
|
||||||
registerResourceTemplates(server);
|
registerResourceTemplates(server);
|
||||||
registerFileResources(server);
|
registerFileResources(server);
|
||||||
|
registerCollectionResources(server);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user