From b26439a6a210a19937dc158dd08499941f1d8058 Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 17:41:30 +0900 Subject: [PATCH 1/9] test(server): assert disabled resources and template listings are omitted from resources/list --- test/integration/test/server/mcp.test.ts | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index 4b9a3865f0..777aa5882c 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -2529,6 +2529,52 @@ describe('Zod v4', () => { expect(result.resources[1]!.uri).toBe('test://resource/2'); }); + /*** + * Test: Disabled Resources and Resource Templates Are Omitted from resources/list + */ + test('should omit disabled resources and resource template listings from resources/list', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + const disabledResource = mcpServer.registerResource('inactive', 'test://static/inactive', {}, async uri => ({ + contents: [{ uri: uri.href, text: 'Inactive content' }] + })); + + mcpServer.registerResource( + 'active-template', + new ResourceTemplate('test://active/{id}', { + list: async () => ({ resources: [{ name: 'Active 1', uri: 'test://active/1' }] }) + }), + {}, + async uri => ({ contents: [{ uri: uri.href, text: 'Active template content' }] }) + ); + const disabledTemplate = mcpServer.registerResource( + 'inactive-template', + new ResourceTemplate('test://inactive/{id}', { + list: async () => ({ resources: [{ name: 'Inactive 1', uri: 'test://inactive/1' }] }) + }), + {}, + async uri => ({ contents: [{ uri: uri.href, text: 'Inactive template content' }] }) + ); + + disabledResource.disable(); + disabledTemplate.disable(); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + const result = await client.request({ method: 'resources/list' }); + + expect(result.resources.map(resource => resource.uri)).toEqual(['test://active/1']); + }); + /*** * Test: Template Variables to Read Callback */ From 2f43fa6d7c151341e09f934099a0f01dc29a3808 Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 18:01:36 +0900 Subject: [PATCH 2/9] test(server): assert disabled resource templates are omitted from resources/templates/list --- test/integration/test/server/mcp.test.ts | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index 777aa5882c..a090e73035 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -2385,6 +2385,52 @@ describe('Zod v4', () => { expect(result2.resourceTemplates).toHaveLength(0); }); + /*** + * Test: Disabled Resource Templates Are Omitted from resources/templates/list + */ + test('should omit disabled resource templates from resources/templates/list', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + // Register resource template + const resourceTemplate = mcpServer.registerResource( + 'template', + new ResourceTemplate('test://resource/{id}', { list: undefined }), + {}, + async uri => ({ + contents: [ + { + uri: uri.href, + text: 'Template content' + } + ] + }) + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.connect(serverTransport)]); + + // Verify template is registered + const result = await client.request({ method: 'resources/templates/list' }); + + expect(result.resourceTemplates).toHaveLength(1); + + // Disable the template + resourceTemplate.disable(); + + // Verify the template was disabled + const result2 = await client.request({ method: 'resources/templates/list' }); + + expect(result2.resourceTemplates).toHaveLength(0); + }); + /*** * Test: Resource Registration with Metadata */ From d1cca53b7fd82d69ac958b1ee8c59f22bf335f88 Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 18:29:57 +0900 Subject: [PATCH 3/9] test(server): assert reading a disabled resource template is rejected --- test/integration/test/server/mcp.test.ts | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index a090e73035..dfb8a4cfc0 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -2950,6 +2950,53 @@ describe('Zod v4', () => { }); }); + /*** + * Test: ProtocolError for Disabled Resource Template + */ + test('should throw ProtocolError for disabled resource template', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + const resourceTemplate = mcpServer.registerResource( + 'test', + new ResourceTemplate('test://resource/{id}', { list: undefined }), + {}, + async uri => ({ + contents: [ + { + uri: uri.href, + text: 'Template content' + } + ] + }) + ); + + resourceTemplate.disable(); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + await expect( + client.request({ + method: 'resources/read', + params: { + uri: 'test://resource/1' + } + }) + ).rejects.toMatchObject({ + code: ProtocolErrorCode.InvalidParams, + message: expect.stringContaining('disabled') + }); + }); + /*** * Test: Registering a resource template without a complete callback should not update server capabilities to advertise support for completion */ From 4ad6cd7ba6b8578449e311603e22fe8c0733a92e Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 18:50:23 +0900 Subject: [PATCH 4/9] test(server): assert completion on a disabled resource template is rejected --- test/integration/test/server/mcp.test.ts | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index dfb8a4cfc0..86dbc1e62b 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -2997,6 +2997,65 @@ describe('Zod v4', () => { }); }); + /*** + * Test: ProtocolError for Completion on a Disabled Resource Template + */ + test('should throw ProtocolError for completion of a disabled resource template', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + const resourceTemplate = mcpServer.registerResource( + 'test', + new ResourceTemplate('test://resource/{category}', { + list: undefined, + complete: { + category: () => ['books', 'movies', 'music'] + } + }), + {}, + async () => ({ + contents: [ + { + uri: 'test://resource/test', + text: 'Test content' + } + ] + }) + ); + + resourceTemplate.disable(); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + await expect( + client.request({ + method: 'completion/complete', + params: { + ref: { + type: 'ref/resource', + uri: 'test://resource/{category}' + }, + argument: { + name: 'category', + value: '' + } + } + }) + ).rejects.toMatchObject({ + code: ProtocolErrorCode.InvalidParams, + message: expect.stringContaining('disabled') + }); + }); + /*** * Test: Registering a resource template without a complete callback should not update server capabilities to advertise support for completion */ From ad41f3a7ba1b0c2f98911fba9d7979abe98da17b Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 19:26:55 +0900 Subject: [PATCH 5/9] fix(server): skip disabled templates when enumerating resources --- packages/server/src/server/mcp.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index d2e40181e4..5d32dc785d 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -448,6 +448,7 @@ export class McpServer { const templateResources: Resource[] = []; for (const template of Object.values(this._registeredResourceTemplates)) { + if (!template.enabled) continue; if (!template.resourceTemplate.listCallback) { continue; } From e428605fbf25e79431c2a0e422834a79a25e7ef2 Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 19:33:15 +0900 Subject: [PATCH 6/9] fix(server): exclude disabled templates from resources/templates/list --- packages/server/src/server/mcp.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index 5d32dc785d..83ad27cf44 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -467,11 +467,13 @@ export class McpServer { }); this.server.setRequestHandler('resources/templates/list', async () => { - const resourceTemplates = Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({ - name, - uriTemplate: template.resourceTemplate.uriTemplate.toString(), - ...template.metadata - })); + const resourceTemplates = Object.entries(this._registeredResourceTemplates) + .filter(([_, t]) => t.enabled) + .map(([name, template]) => ({ + name, + uriTemplate: template.resourceTemplate.uriTemplate.toString(), + ...template.metadata + })); return { resourceTemplates }; }); From c6c6e695f3173243b32786a39db6979465bc156d Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 19:54:02 +0900 Subject: [PATCH 7/9] fix(server): throw when a read resolves to a disabled template --- packages/server/src/server/mcp.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index 83ad27cf44..1cab22cd11 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -505,6 +505,9 @@ export class McpServer { for (const template of Object.values(this._registeredResourceTemplates)) { const variables = template.resourceTemplate.uriTemplate.match(uri.toString()); if (variables) { + if (!template.enabled) { + throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource template ${uri} disabled`); + } return attachCacheHintFallback(await template.readCallback(uri, variables, ctx), template.cacheHint); } } From d960a24e018ef80e1224531ae828c86eeb9bb864 Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 19:54:41 +0900 Subject: [PATCH 8/9] fix(server): throw when completing against a disabled template --- packages/server/src/server/mcp.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index 1cab22cd11..b7cc14905f 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -411,6 +411,10 @@ export class McpServer { throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} not found`); } + if (!template.enabled) { + throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} disabled`); + } + const completer = template.resourceTemplate.completeCallback(request.params.argument.name); if (!completer) { return EMPTY_COMPLETION_RESULT; From cd3d9e18ad1d8817c856229e048fe985b8e5e2d6 Mon Sep 17 00:00:00 2001 From: GyuriKim Date: Fri, 21 Aug 2026 23:09:22 +0900 Subject: [PATCH 9/9] chore: add changeset --- .changeset/honor-disable-on-resource-templates.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/honor-disable-on-resource-templates.md diff --git a/.changeset/honor-disable-on-resource-templates.md b/.changeset/honor-disable-on-resource-templates.md new file mode 100644 index 0000000000..7d273906ab --- /dev/null +++ b/.changeset/honor-disable-on-resource-templates.md @@ -0,0 +1,5 @@ +--- +'@modelcontextprotocol/server': patch +--- + +Honor `disable()` on resource templates. `resources/list`, `resources/templates/list`, `resources/read` and `completion/complete` never read the `enabled` flag, so a disabled template stayed listed, readable, and completable.