Skip to main content
Glama
argument-compatibility.test.ts7.7 kB
/** * Test suite for Issue #344: MCP tool call argument compatibility * Tests both wrapped and unwrapped argument formats using universal tools * Tests migration from legacy tools to universal tools */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js'; import { registerToolHandlers } from '../../../src/handlers/tools/index.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'; // Mock the dispatcher vi.mock('../../../src/handlers/tools/dispatcher.js', () => ({ executeToolRequest: vi.fn().mockResolvedValue({ content: [{ type: 'text', text: 'Mocked response' }], }), })); describe('MCP Tool Argument Compatibility (Issue #344)', () => { let server: Server; let requestHandler: any; beforeEach(() => { vi.clearAllMocks(); // Create a mock server server = { setRequestHandler: vi.fn((schema, handler) => { if (schema === CallToolRequestSchema) { requestHandler = handler; } }), } as any; // Register handlers registerToolHandlers(server); }); describe('Argument Format Normalization', () => { it('should handle properly wrapped arguments (standard MCP format) with universal tools', async () => { const request: any = { params: { name: 'search-records', arguments: { resource_type: 'companies', query: 'Lenox Hill Plastic Surgery', }, }, }; const result = await requestHandler(request); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle unwrapped arguments (Issue #344 format) with universal tools', async () => { // This is the problematic format from Issue #344 adapted for universal tools const request = { params: { name: 'search-records', resource_type: 'companies', query: 'Lenox Hill Plastic Surgery', // Arguments directly in params }, } as any; const result = await requestHandler(request); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle tools with minimal arguments using universal tools', async () => { const request: any = { params: { name: 'get-record-details', arguments: { resource_type: 'companies', // Could include record_id for specific record, but testing minimal args }, }, }; const result = await requestHandler(request); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle multiple unwrapped arguments with universal tools', async () => { const request = { params: { name: 'create-record', // Multiple arguments directly in params resource_type: 'companies', record_data: { name: 'Test Company', domain: 'test.com', }, }, } as any; const result = await requestHandler(request); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should preserve properly wrapped arguments without modification using universal tools', async () => { const { executeToolRequest } = await import( '../../../src/handlers/tools/dispatcher.js' ); const mockedExecute = vi.mocked(executeToolRequest); const request: any = { params: { name: 'search-records', arguments: { resource_type: 'companies', query: 'Test Query', limit: 10, }, }, }; await requestHandler(request); // Verify the normalized request preserves the wrapped structure const calledWith = mockedExecute.mock.calls[0][0]; expect(calledWith.params.arguments).toEqual({ resource_type: 'companies', query: 'Test Query', limit: 10, }); }); it('should wrap loose arguments correctly using universal tools', async () => { const { executeToolRequest } = await import( '../../../src/handlers/tools/dispatcher.js' ); const mockedExecute = vi.mocked(executeToolRequest); const request = { params: { name: 'search-records', resource_type: 'companies', query: 'Test Query', limit: 10, }, } as any; await requestHandler(request); // Verify the arguments were wrapped const calledWith = mockedExecute.mock.calls[0][0]; expect(calledWith.params.name).toBe('search-records'); expect(calledWith.params.arguments).toEqual({ resource_type: 'companies', query: 'Test Query', limit: 10, }); }); }); describe('Edge Cases', () => { it('should handle request with only name parameter using universal tools', async () => { const request = { params: { name: 'get-record-details', resource_type: 'companies', // Minimal args for universal tool }, } as any; const result = await requestHandler(request); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle malformed requests gracefully using universal tools', async () => { const request = { params: { name: 'search-records', // Missing required resource_type argument }, } as any; const result = await requestHandler(request); // Should still return a result (error handling is in the dispatcher) expect(result).toBeDefined(); }); it('should not wrap arguments if they already exist using universal tools', async () => { const { executeToolRequest } = await import( '../../../src/handlers/tools/dispatcher.js' ); const mockedExecute = vi.mocked(executeToolRequest); const request = { params: { name: 'search-records', arguments: { resource_type: 'companies', query: 'Already wrapped' }, query: 'Should be ignored', // This should be ignored }, } as any; await requestHandler(request); const calledWith = mockedExecute.mock.calls[0][0]; expect(calledWith.params.arguments).toEqual({ resource_type: 'companies', query: 'Already wrapped', }); // The loose 'query' parameter should not be included expect((calledWith.params as any).query).toBeUndefined(); }); it('should validate request structure', async () => { const request = { params: { // Missing name }, } as any; const result = await requestHandler(request); expect(result.isError).toBe(true); expect(result.error?.type).toBe('normalization_error'); expect(result.error?.message).toContain('missing params or tool name'); }); it('should reject oversized arguments using universal tools', async () => { const largeString = 'x'.repeat(1024 * 1024 + 1); // Over 1MB const request = { params: { name: 'search-records', arguments: { resource_type: 'companies', query: largeString, }, }, } as any; const result = await requestHandler(request); expect(result.isError).toBe(true); expect(result.error?.type).toBe('normalization_error'); expect(result.error?.message).toContain('Tool arguments too large'); }); }); });

Latest Blog Posts

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/kesslerio/attio-mcp-server'

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