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.ts
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):Expected behavior
The script above should print
Listing 20 products on page 1but instead it errors with: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
UriTemplatesuch that the following test cases pass: