Skip to main content
Glama

1MCP Server

templateEngine.test.ts13.6 kB
import { ClientStatus } from '@src/core/types/client.js'; import type { InboundConnectionConfig, OutboundConnection, OutboundConnections } from '@src/core/types/index.js'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { InstructionAggregator } from './instructionAggregator.js'; // Mock dependencies vi.mock('@src/logger/logger.js', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), }, debugIf: vi.fn(), })); describe('InstructionAggregator - Template Engine', () => { let instructionAggregator: InstructionAggregator; let mockOutboundConnections: OutboundConnections; beforeEach(() => { // Create mock outbound connections with different servers mockOutboundConnections = new Map([ [ 'web-server', { name: 'web-server', transport: { tags: ['web', 'frontend'], timeout: 5000 }, client: {} as any, status: ClientStatus.Connected, instructions: 'Web server instructions for frontend development', } as OutboundConnection, ], [ 'api-server', { name: 'api-server', transport: { tags: ['api', 'backend'], timeout: 5000 }, client: {} as any, status: ClientStatus.Connected, instructions: 'API server instructions for backend services', } as OutboundConnection, ], [ 'database-server', { name: 'database-server', transport: { tags: ['database', 'backend'], timeout: 5000 }, client: {} as any, status: ClientStatus.Connected, instructions: 'Database server instructions for data management', } as OutboundConnection, ], ]); // Create instruction aggregator and populate it instructionAggregator = new InstructionAggregator(); for (const [name, conn] of mockOutboundConnections) { if (conn.instructions) { instructionAggregator.setInstructions(name, conn.instructions); } } }); describe('Basic Template Variables', () => { it('should render simple template with server count', () => { const template = 'Connected servers: {{serverCount}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toBe('Connected servers: 3'); }); it('should render template with server list', () => { const template = 'Servers:\n{{serverList}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toBe('Servers:\napi-server\ndatabase-server\nweb-server'); }); it('should render template with plural/singular forms', () => { const template = '{{serverCount}} {{pluralServers}} {{isAre}} available'; // Test plural const configPlural: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const resultPlural = instructionAggregator.getFilteredInstructions(configPlural, mockOutboundConnections); expect(resultPlural).toBe('3 servers are available'); // Test singular const singleServerConnections = new Map([['web-server', mockOutboundConnections.get('web-server')!]]); const configSingular: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const resultSingular = instructionAggregator.getFilteredInstructions(configSingular, singleServerConnections); expect(resultSingular).toBe('1 server is available'); }); it('should render template with custom title and tool pattern', () => { const template = '# {{title}}\nTool pattern: {{toolPattern}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, title: 'My Custom MCP Proxy', toolPattern: '{server}::{tool}', }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toBe('# My Custom MCP Proxy\nTool pattern: {server}::{tool}'); }); }); describe('Conditional Templates', () => { it('should render conditional content when servers are available', () => { const template = `{{#if hasServers}} {{serverCount}} servers connected: {{serverList}} {{else}} No servers available {{/if}}`; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('3 servers connected'); expect(result).toContain('api-server'); expect(result).not.toContain('No servers available'); }); it('should render else content when no servers are available', () => { const template = `{{#if hasServers}} Servers: {{serverList}} {{else}} No servers available {{/if}}`; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const emptyConnections = new Map<string, OutboundConnection>(); const result = instructionAggregator.getFilteredInstructions(config, emptyConnections); expect(result).toContain('No servers available'); expect(result).not.toContain('Servers:'); }); }); describe('Loop Templates', () => { it('should render server names using each loop', () => { const template = `{{#each serverNames}} - Server: {{this}} {{/each}}`; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('- Server: api-server'); expect(result).toContain('- Server: database-server'); expect(result).toContain('- Server: web-server'); }); it('should render examples using each loop', () => { const template = `{{#each examples}} - {{name}}: {{description}} {{/each}}`; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('filesystem_1mcp_read_file: Read files through filesystem server'); expect(result).toContain('web_1mcp_search: Search the web through web server'); expect(result).toContain('database_1mcp_query: Query databases through database server'); }); }); describe('Instructions Content', () => { it('should render server instructions in XML format', () => { const template = 'Instructions:\\n{{{instructions}}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('<api-server>'); expect(result).toContain('API server instructions for backend services'); expect(result).toContain('</api-server>'); expect(result).toContain('<web-server>'); expect(result).toContain('Web server instructions for frontend development'); expect(result).toContain('</web-server>'); }); it('should handle empty instructions gracefully', () => { const template = '{{#if hasServers}}{{{instructions}}}{{else}}No instructions{{/if}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; // Clear all instructions instructionAggregator.clear(); const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toBe('No instructions'); }); }); describe('Filtering with Templates', () => { it('should render filtered results correctly', () => { const template = `Filtered servers ({{serverCount}}): {{#each serverNames}} - {{this}} {{/each}} {{filterContext}}`; const config: InboundConnectionConfig = { tagFilterMode: 'simple-or', tags: ['backend'], customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('Filtered servers (2)'); expect(result).toContain('- api-server'); expect(result).toContain('- database-server'); expect(result).not.toContain('- web-server'); expect(result).toContain('(filtered by tags: backend)'); }); it('should handle empty filter results', () => { const template = `{{#if hasServers}} Found {{serverCount}} servers {{else}} No matching servers found {{/if}}`; const config: InboundConnectionConfig = { tagFilterMode: 'simple-or', tags: ['nonexistent'], customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('No matching servers found'); expect(result).not.toContain('Found'); }); }); describe('Template Error Handling', () => { it('should fallback to default template on invalid syntax', () => { const template = '{{invalid syntax {{unclosed'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); // Should fall back to default template, not show error template expect(result).toContain('# 1MCP - Model Context Protocol Proxy'); expect(result).toContain('You are interacting with 1MCP'); expect(result).toContain('Currently Connected Servers'); expect(result).toContain('3 MCP servers are currently available'); expect(result).not.toContain('Template Rendering Error'); }); it('should handle template with undefined variables gracefully', () => { const template = 'Server: {{nonexistentVariable}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); // Should render empty string for undefined variables expect(result).toBe('Server: '); }); }); describe('Template Caching', () => { it('should cache compiled templates', () => { const template = 'Servers: {{serverCount}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; // First render const result1 = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); // Second render (should use cached template) const result2 = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result1).toBe(result2); expect(result1).toBe('Servers: 3'); }); it('should handle multiple template renders without caching', () => { const template = 'Test: {{serverCount}}'; const config: InboundConnectionConfig = { tagFilterMode: 'none', customTemplate: template, }; // Multiple renders should work (no cache issues) const result1 = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); const result2 = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); const result3 = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result1).toBe('Test: 3'); expect(result2).toBe('Test: 3'); expect(result3).toBe('Test: 3'); }); }); describe('Complex Template Examples', () => { it('should render advanced template with multiple features', () => { const template = `# {{title}} ## Status {{#if hasServers}} ✅ **{{serverCount}} {{pluralServers}} active**{{filterContext}} ### Connected Servers {{#each serverNames}} - ✅ {{this}} {{/each}} ### Instructions {{{instructions}}} ### Examples {{#each examples}} - \`{{name}}\` - {{description}} {{/each}} --- *Tools follow pattern: \`{{toolPattern}}\`* {{else}} ⏳ **Waiting for connections...** {{/if}}`; const config: InboundConnectionConfig = { tagFilterMode: 'simple-or', tags: ['backend'], customTemplate: template, title: 'My MCP Gateway', toolPattern: '{server}::{tool}', }; const result = instructionAggregator.getFilteredInstructions(config, mockOutboundConnections); expect(result).toContain('# My MCP Gateway'); expect(result).toContain('✅ **2 servers active** (filtered by tags: backend)'); expect(result).toContain('- ✅ api-server'); expect(result).toContain('- ✅ database-server'); expect(result).toContain('<api-server>'); expect(result).toContain('API server instructions for backend services'); expect(result).toContain('*Tools follow pattern: `{server}::{tool}`*'); expect(result).not.toContain('web-server'); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/1mcp-app/agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server