What happened?
A resource registered with a ResourceTemplate stays listed and stays readable after disable(). The enabled flag flips to false correctly, but it is not properly applied in the four request methods below:
resources/list — the template loop calls listCallback and merges its results without checking enabled
resources/templates/list — no filter at all, every registered template is returned
resources/read — the template branch invokes readCallback as soon as the URI matches
completion/complete with a ref/resource — handleResourceCompletion runs the completer without checking enabled
What did you expect?
The same behavior as a disabled static URI resource in resource requests:
- the listing methods leave it out of the response
resources/read throws a ProtocolError (InvalidParams)
The same behavior as a disabled prompt in a completion request:
completion/complete throws a ProtocolError
Code to reproduce
import { Client, InMemoryTransport } from '@modelcontextprotocol/client';
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
const server = new McpServer({ name: 'repro', version: '0.0.0' });
const tool = server.registerTool('echo', { description: 'echo' }, async () => ({
content: [{ type: 'text' as const, text: 'echo' }]
}));
const fixed = server.registerResource('static', 'demo://static', {}, async uri => ({
contents: [{ uri: uri.href, text: 'static content' }]
}));
const template = server.registerResource(
'user',
new ResourceTemplate('demo://users/{id}', {
list: async () => ({ resources: [{ uri: 'demo://users/1', name: 'user 1' }] })
}),
{},
async (uri, vars) => ({ contents: [{ uri: uri.href, text: `profile of ${vars.id}` }] })
);
const [ct, st] = InMemoryTransport.createLinkedPair();
const client = new Client({ name: 'repro-client', version: '0.0.0' });
await Promise.all([client.connect(ct), server.server.connect(st)]);
tool.disable();
fixed.disable();
template.disable();
console.log('tools/list :', (await client.listTools()).tools.map(t => t.name));
// [] — the tool honors disable()
console.log('read demo://static :', await client.readResource({ uri: 'demo://static' }).then(r => r.contents[0]).catch(e => 'threw: ' + e.message));
// threw: Resource demo://static disabled — the static resource honors disable()
console.log('resources/list :', (await client.listResources()).resources.map(r => r.uri));
// expected [], got [ 'demo://users/1' ]
console.log('templates/list :', (await client.listResourceTemplates()).resourceTemplates.map(t => t.uriTemplate));
// expected [], got [ 'demo://users/{id}' ]
console.log('read demo://users/1:', await client.readResource({ uri: 'demo://users/1' }).then(r => r.contents[0]).catch(e => 'threw: ' + e.message));
// expected a throw, got { uri: 'demo://users/1', text: 'profile of 1' }
Result
tools/list : []
read demo://static : threw: Resource demo://static disabled
resources/list : [ 'demo://users/1' ] # expected []
templates/list : [ 'demo://users/{id}' ] # expected []
read demo://users/1: { uri: 'demo://users/1', text: 'profile of 1' } # expected a throw
SDK version
@modelcontextprotocol/server 2.0.0, @modelcontextprotocol/client 2.0.0 (main @ 3924de9)
Area
Server
What happened?
A resource registered with a
ResourceTemplatestays listed and stays readable afterdisable(). Theenabledflag flips to false correctly, but it is not properly applied in the four request methods below:resources/list— the template loop callslistCallbackand merges its results without checkingenabledresources/templates/list— no filter at all, every registered template is returnedresources/read— the template branch invokesreadCallbackas soon as the URI matchescompletion/completewith aref/resource—handleResourceCompletionruns the completer without checkingenabledWhat did you expect?
The same behavior as a disabled static URI resource in resource requests:
resources/readthrows aProtocolError(InvalidParams)The same behavior as a disabled prompt in a completion request:
completion/completethrows aProtocolErrorCode to reproduce
Result
SDK version
@modelcontextprotocol/server 2.0.0, @modelcontextprotocol/client 2.0.0 (main @ 3924de9)
Area
Server