-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Labels
P1Significant bug affecting many users, highly requested featureSignificant bug affecting many users, highly requested featurebugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working on
Description
Describe the bug
MCP server does not match resource requests that do no have all query parameters specified in the order they were declared in the ResourceTemplate / UriTemplate. Instead, clients receive a not found error (MCP error -32602).
To Reproduce
Run the following reproducer (e.g. npx tsx repro.ts):
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import {
McpServer,
ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
const server = new McpServer({
name: "test-server",
version: "0.0.0",
});
server.resource(
"example",
new ResourceTemplate("acme://products{?page,limit}", { list: undefined }),
async (uri, vars): Promise<ReadResourceResult> => {
const page = vars.page ?? "1"
const limit = vars.limit ?? "20"
return {
contents: [
{
text: `Listing ${vars.limit} products on page ${vars.page}`,
mimeType: "text/plain",
uri: "uri",
},
],
};
}
);
const client = new Client({
name: "test-client",
version: "1.0.0",
});
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
server.connect(serverTransport),
client.connect(clientTransport),
]);
const result = await client.readResource({
uri: "acme://products",
});
console.log(result.contents.map((c) => c.text).join("\n"));Expected behavior
The script above should print Listing 20 products on page 1 but instead it errors with:
McpError: MCP error -32602: MCP error -32602: Resource acme://products not found
Additional context
I think the current handling of query parameters makes resource templates a little too rigid. My current workaround is to convert certain dynamic resources to tools but I'm not sure that's a good long term solution. I propose changing the behavior of UriTemplate such that the following test cases pass:
import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js";
import { expect, test } from "vitest";
test("UriTemplate::match treats query parameters as optional", () => {
const template = new UriTemplate("acme://products{?page,limit}");
expect(template.match("acme://products")).toEqual({});
expect(template.match("acme://products?page=2")).toEqual({ page: "2" });
});
test("UriTemplate::match accepts query parameters in arbitrary order", () => {
const template = new UriTemplate("acme://products{?page,limit,q*}");
expect(template.match("acme://products?q=cat,dog&limit=40&page=5")).toEqual({
page: "5",
limit: "40",
q: ["cat", "dog"],
});
});
// npx vitest uritemplate.test.tsjspahrsummers, zanemcca, jongwoo328, dr3s, jeteipel and 1 more
Metadata
Metadata
Assignees
Labels
P1Significant bug affecting many users, highly requested featureSignificant bug affecting many users, highly requested featurebugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working on