fix(server): return Invalid Params for malformed resource URIs (#2451)

This commit is contained in:
Matt 2026-07-07 13:48:48 +01:00 • committed by GitHub
commit 7e697354de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 88 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
'@modelcontextprotocol/server': patch
---
Return JSON-RPC Invalid Params with the original URI and an `invalid_uri` reason when `resources/read` receives a syntactically malformed URI.

View file

@ -476,7 +476,15 @@ export class McpServer {
});
this.server.setRequestHandler('resources/read', async (request, ctx) => {
const uri = new URL(request.params.uri);
let uri: URL;
try {
uri = new URL(request.params.uri);
} catch {
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource URI ${request.params.uri} is invalid`, {
uri: request.params.uri,
reason: 'invalid_uri'
});
}
// First check for exact resource match
const resource = this._registeredResources[uri.toString()];

View file

@ -2743,6 +2743,80 @@ describe('Zod v4', () => {
});
});
test('should echo the exact requested URI for nonexistent resources', async () => {
const mcpServer = new McpServer({
name: 'test server',
version: '1.0'
});
const client = new Client({
name: 'test client',
version: '1.0'
});
const requestedUri = 'HTTP://example.com:80/docs/../missing file.txt';
mcpServer.registerResource('test', 'test://resource', {}, async () => ({
contents: [
{
uri: 'test://resource',
text: 'Test content'
}
]
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
await expect(
client.request({
method: 'resources/read',
params: {
uri: requestedUri
}
})
).rejects.toMatchObject({
code: ProtocolErrorCode.InvalidParams,
message: expect.stringContaining('not found'),
data: { uri: requestedUri }
});
});
test('should return invalid params for syntactically invalid resource URIs', async () => {
const mcpServer = new McpServer({
name: 'test server',
version: '1.0'
});
const client = new Client({
name: 'test client',
version: '1.0'
});
const requestedUri = 'not a valid URI';
mcpServer.registerResource('test', 'test://resource', {}, async () => ({
contents: [
{
uri: 'test://resource',
text: 'Test content'
}
]
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
await expect(
client.request({
method: 'resources/read',
params: {
uri: requestedUri
}
})
).rejects.toMatchObject({
code: ProtocolErrorCode.InvalidParams,
message: expect.stringContaining('invalid'),
data: { uri: requestedUri, reason: 'invalid_uri' }
});
});
/***
* Test: ProtocolError for Disabled Resource
*/