fix: add legacy flat _meta key for MCP App rendering in Claude (#585)

Claude.ai reads the flat `_meta["ui/resourceUri"]` key to discover UI apps,
not the nested `_meta.ui.resourceUri`. Without the flat key, tools like
n8n_health_check and n8n_list_workflows showed as collapsed accordions
instead of rendering rich UI. Now sets both keys, matching the behavior
of the official registerAppTool helper from @modelcontextprotocol/ext-apps.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Romuald Członkowski
2026-02-09 06:40:52 +01:00
committed by GitHub
parent 8217229e2f
commit 34159f4ece
5 changed files with 22 additions and 8 deletions

View File

@@ -72,7 +72,7 @@ describe('UI Meta Injection on Tool Definitions', () => {
expect(tools[2]._meta.ui.resourceUri).toBe('ui://n8n-mcp/validation-summary');
});
it('should produce _meta with exact shape { ui: { resourceUri: string } }', () => {
it('should produce _meta with both nested and flat resourceUri keys', () => {
const tools: any[] = [
{ name: 'n8n_create_workflow', description: 'Create', inputSchema: { type: 'object', properties: {} } },
];
@@ -83,10 +83,10 @@ describe('UI Meta Injection on Tool Definitions', () => {
ui: {
resourceUri: 'ui://n8n-mcp/operation-result',
},
'ui/resourceUri': 'ui://n8n-mcp/operation-result',
});
expect(Object.keys(tools[0]._meta)).toEqual(['ui']);
expect(Object.keys(tools[0]._meta.ui)).toEqual(['resourceUri']);
expect(typeof tools[0]._meta.ui.resourceUri).toBe('string');
expect(tools[0]._meta.ui.resourceUri).toBe('ui://n8n-mcp/operation-result');
expect(tools[0]._meta['ui/resourceUri']).toBe('ui://n8n-mcp/operation-result');
});
});

View File

@@ -308,7 +308,7 @@ describe('UIAppRegistry', () => {
{ name: 'n8n_create_workflow', description: 'Create', inputSchema: { type: 'object', properties: {} } },
];
UIAppRegistry.injectToolMeta(tools);
expect(tools[0]._meta).toEqual({ ui: { resourceUri: 'ui://n8n-mcp/operation-result' } });
expect(tools[0]._meta).toEqual({ ui: { resourceUri: 'ui://n8n-mcp/operation-result' }, 'ui/resourceUri': 'ui://n8n-mcp/operation-result' });
});
it('should set _meta.ui.resourceUri on matching validation tools', () => {
@@ -316,7 +316,7 @@ describe('UIAppRegistry', () => {
{ name: 'validate_node', description: 'Validate', inputSchema: { type: 'object', properties: {} } },
];
UIAppRegistry.injectToolMeta(tools);
expect(tools[0]._meta).toEqual({ ui: { resourceUri: 'ui://n8n-mcp/validation-summary' } });
expect(tools[0]._meta).toEqual({ ui: { resourceUri: 'ui://n8n-mcp/validation-summary' }, 'ui/resourceUri': 'ui://n8n-mcp/validation-summary' });
});
it('should not set _meta on tools without a matching UI app', () => {