modelcontextprotocol / modelcontextprotocol/typescript-sdk

Unordered and partial query parameter matching

Open
#149 5 comments 6 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug fix proposed P2 ready for work
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

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.ts

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with shared/uriTemplate.js and run the proposed uritemplate.test.ts cases with npx vitest. Verify that omitted query parameters match as optional and that parameters supplied in arbitrary order, including exploded values, produce the expected variables and make the resource reproducer succeed.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.