Skip to main content
Glama

documcp

by tosin2013
mcp-responses.test.tsâ€ĸ15.3 kB
// API tests for MCP response format compliance and standardization import { formatMCPResponse, MCPToolResponse } from "../../src/types/api"; describe("API Response Standardization Tests", () => { describe("MCPToolResponse Interface Compliance", () => { it("should validate successful response structure", () => { const successResponse: MCPToolResponse<{ data: string }> = { success: true, data: { data: "test-data" }, metadata: { toolVersion: "1.0.0", executionTime: 100, timestamp: "2023-01-01T00:00:00.000Z", }, recommendations: [ { type: "info", title: "Test Recommendation", description: "This is a test recommendation", }, ], nextSteps: [ { action: "Next Action", toolRequired: "next_tool", description: "Description of next step", priority: "high", }, ], }; expect(successResponse.success).toBe(true); expect(successResponse.data).toBeDefined(); expect(successResponse.metadata).toBeDefined(); expect(successResponse.metadata.toolVersion).toBe("1.0.0"); expect(successResponse.metadata.executionTime).toBe(100); expect(successResponse.recommendations).toHaveLength(1); expect(successResponse.nextSteps).toHaveLength(1); }); it("should validate error response structure", () => { const errorResponse: MCPToolResponse = { success: false, error: { code: "TEST_ERROR", message: "Test error message", details: { context: "test" }, resolution: "Test resolution steps", }, metadata: { toolVersion: "1.0.0", executionTime: 50, timestamp: "2023-01-01T00:00:00.000Z", }, }; expect(errorResponse.success).toBe(false); expect(errorResponse.error).toBeDefined(); expect(errorResponse.error!.code).toBe("TEST_ERROR"); expect(errorResponse.error!.message).toBe("Test error message"); expect(errorResponse.error!.resolution).toBe("Test resolution steps"); expect(errorResponse.data).toBeUndefined(); }); it("should validate recommendation types", () => { const recommendations = [ { type: "info" as const, title: "Info", description: "Info description", }, { type: "warning" as const, title: "Warning", description: "Warning description", }, { type: "critical" as const, title: "Critical", description: "Critical description", }, ]; recommendations.forEach((rec) => { expect(["info", "warning", "critical"]).toContain(rec.type); expect(rec.title).toBeDefined(); expect(rec.description).toBeDefined(); }); }); it("should validate next step priorities", () => { const nextSteps = [ { action: "Low Priority", toolRequired: "tool1", priority: "low" as const, }, { action: "Medium Priority", toolRequired: "tool2", priority: "medium" as const, }, { action: "High Priority", toolRequired: "tool3", priority: "high" as const, }, ]; nextSteps.forEach((step) => { expect(["low", "medium", "high"]).toContain(step.priority); expect(step.action).toBeDefined(); expect(step.toolRequired).toBeDefined(); }); }); }); describe("formatMCPResponse Function", () => { it("should format successful response correctly", () => { const response: MCPToolResponse<{ result: string }> = { success: true, data: { result: "success" }, metadata: { toolVersion: "1.0.0", executionTime: 123, timestamp: "2023-01-01T12:00:00.000Z", }, recommendations: [ { type: "info", title: "Success", description: "Operation completed successfully", }, ], nextSteps: [ { action: "Proceed to next step", toolRequired: "next_tool", priority: "medium", }, ], }; const formatted = formatMCPResponse(response); expect(formatted.content).toBeDefined(); expect(formatted.content.length).toBeGreaterThan(0); expect(formatted.isError).toBeFalsy(); // Check main data is included const dataContent = formatted.content.find((c) => c.text.includes("success"), ); expect(dataContent).toBeDefined(); // Check metadata is included const metadataContent = formatted.content.find((c) => c.text.includes("123ms"), ); expect(metadataContent).toBeDefined(); // Check recommendations are included const recommendationContent = formatted.content.find((c) => c.text.includes("Recommendations:"), ); expect(recommendationContent).toBeDefined(); // Check next steps are included const nextStepContent = formatted.content.find((c) => c.text.includes("Next Steps:"), ); expect(nextStepContent).toBeDefined(); }); it("should format error response correctly", () => { const errorResponse: MCPToolResponse = { success: false, error: { code: "VALIDATION_ERROR", message: "Input validation failed", resolution: "Check your input parameters", }, metadata: { toolVersion: "1.0.0", executionTime: 25, timestamp: "2023-01-01T12:00:00.000Z", }, }; const formatted = formatMCPResponse(errorResponse); expect(formatted.content).toBeDefined(); expect(formatted.isError).toBe(true); // Check error message is included const errorContent = formatted.content.find((c) => c.text.includes("Input validation failed"), ); expect(errorContent).toBeDefined(); // Check resolution is included const resolutionContent = formatted.content.find((c) => c.text.includes("Check your input parameters"), ); expect(resolutionContent).toBeDefined(); }); it("should handle responses without optional fields", () => { const minimalResponse: MCPToolResponse<string> = { success: true, data: "minimal data", metadata: { toolVersion: "1.0.0", executionTime: 10, timestamp: "2023-01-01T12:00:00.000Z", }, }; const formatted = formatMCPResponse(minimalResponse); expect(formatted.content).toBeDefined(); expect(formatted.isError).toBeFalsy(); // Should not include recommendations or next steps sections const fullText = formatted.content.map((c) => c.text).join("\n"); expect(fullText).not.toContain("Recommendations:"); expect(fullText).not.toContain("Next Steps:"); }); it("should include recommendation icons correctly", () => { const response: MCPToolResponse<{}> = { success: true, data: {}, metadata: { toolVersion: "1.0.0", executionTime: 10, timestamp: "2023-01-01T12:00:00.000Z", }, recommendations: [ { type: "info", title: "Info", description: "Info description" }, { type: "warning", title: "Warning", description: "Warning description", }, { type: "critical", title: "Critical", description: "Critical description", }, ], }; const formatted = formatMCPResponse(response); const recommendationText = formatted.content.find((c) => c.text.includes("Recommendations:")) ?.text || ""; expect(recommendationText).toContain("â„šī¸"); // Info icon expect(recommendationText).toContain("âš ī¸"); // Warning icon expect(recommendationText).toContain("🔴"); // Critical icon }); it("should format next steps without toolRequired but with description", () => { const response: MCPToolResponse<{}> = { success: true, data: {}, metadata: { toolVersion: "1.0.0", executionTime: 10, timestamp: "2023-01-01T12:00:00.000Z", }, nextSteps: [ { action: "Manual Step", description: "This step requires manual intervention", priority: "high", }, ], }; const formatted = formatMCPResponse(response); const nextStepText = formatted.content.find((c) => c.text.includes("Next Steps:"))?.text || ""; expect(nextStepText).toContain("Manual Step"); expect(nextStepText).toContain("This step requires manual intervention"); expect(nextStepText).not.toContain("use "); // Should not have "use" since no toolRequired }); }); describe("Response Consistency Across Tools", () => { it("should ensure all tools follow the same metadata structure", () => { const commonMetadata = { toolVersion: "1.0.0", executionTime: 100, timestamp: "2023-01-01T12:00:00.000Z", }; // Test that metadata structure is consistent expect(commonMetadata.toolVersion).toMatch(/^\d+\.\d+\.\d+$/); expect(typeof commonMetadata.executionTime).toBe("number"); expect(commonMetadata.executionTime).toBeGreaterThanOrEqual(0); expect(commonMetadata.timestamp).toMatch( /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/, ); }); it("should validate error code consistency", () => { const errorCodes = [ "ANALYSIS_FAILED", "RECOMMENDATION_FAILED", "CONFIG_GENERATION_FAILED", "STRUCTURE_SETUP_FAILED", "DEPLOYMENT_SETUP_FAILED", "VERIFICATION_FAILED", ]; errorCodes.forEach((code) => { expect(code).toMatch(/^[A-Z_]+$/); expect(code).toContain("_"); expect(code.endsWith("_FAILED")).toBe(true); }); }); it("should validate next step tool references", () => { const validTools = [ "analyze_repository", "recommend_ssg", "generate_config", "setup_structure", "deploy_pages", "verify_deployment", ]; validTools.forEach((tool) => { expect(tool).toMatch(/^[a-z_]+$/); expect(tool).not.toContain("-"); expect(tool).not.toContain(" "); }); }); it("should validate recommendation action patterns", () => { const recommendationActions = [ "Get SSG Recommendation", "Generate Configuration", "Setup Documentation Structure", "Setup GitHub Pages Deployment", "Verify Deployment Setup", ]; recommendationActions.forEach((action) => { expect(action).toMatch(/^[A-Z]/); // Starts with capital expect(action.length).toBeGreaterThan(5); // Meaningful length expect(action.endsWith(".")).toBe(false); // No trailing period }); }); }); describe("Backward Compatibility", () => { it("should maintain MCP content format compatibility", () => { const response: MCPToolResponse<{ test: boolean }> = { success: true, data: { test: true }, metadata: { toolVersion: "1.0.0", executionTime: 50, timestamp: "2023-01-01T12:00:00.000Z", }, }; const formatted = formatMCPResponse(response); // Must have content array for MCP compatibility expect(formatted.content).toBeDefined(); expect(Array.isArray(formatted.content)).toBe(true); // Each content item must have type and text formatted.content.forEach((item) => { expect(item.type).toBe("text"); expect(typeof item.text).toBe("string"); expect(item.text.length).toBeGreaterThan(0); }); }); it("should handle legacy response format gracefully", () => { // Test that we can still process responses that don't have all new fields const legacyStyleData = { success: true, result: "legacy result", timestamp: "2023-01-01T12:00:00.000Z", }; // Should not throw even if not strictly typed expect(() => { const formatted = formatMCPResponse({ success: true, data: legacyStyleData, metadata: { toolVersion: "1.0.0", executionTime: 100, timestamp: "2023-01-01T12:00:00.000Z", }, }); return formatted; }).not.toThrow(); }); }); describe("Error Boundary Testing", () => { it("should handle undefined data gracefully", () => { const response: MCPToolResponse = { success: true, // data is undefined metadata: { toolVersion: "1.0.0", executionTime: 10, timestamp: "2023-01-01T12:00:00.000Z", }, }; const formatted = formatMCPResponse(response); expect(formatted.content).toBeDefined(); expect(formatted.content.length).toBeGreaterThan(0); }); it("should handle null values in data", () => { const response: MCPToolResponse<{ value: null }> = { success: true, data: { value: null }, metadata: { toolVersion: "1.0.0", executionTime: 10, timestamp: "2023-01-01T12:00:00.000Z", }, }; expect(() => formatMCPResponse(response)).not.toThrow(); }); it("should handle very large data objects", () => { const largeData = { items: Array.from({ length: 1000 }, (_, i) => ({ id: i, value: `item-${i}`, })), }; const response: MCPToolResponse<typeof largeData> = { success: true, data: largeData, metadata: { toolVersion: "1.0.0", executionTime: 1000, timestamp: "2023-01-01T12:00:00.000Z", }, }; const formatted = formatMCPResponse(response); expect(formatted.content).toBeDefined(); // Should include the large data in JSON format const dataContent = formatted.content.find((c) => c.text.includes('"items"'), ); expect(dataContent).toBeDefined(); }); it("should handle circular references safely", () => { const circularData: any = { name: "test" }; circularData.self = circularData; // Should not cause JSON.stringify to throw expect(() => { JSON.stringify(circularData); }).toThrow(); // But our formatter should handle it (though we should avoid circular refs) // This test documents the expected behavior const response: MCPToolResponse<any> = { success: true, data: { safe: "data" }, // Use safe data instead metadata: { toolVersion: "1.0.0", executionTime: 10, timestamp: "2023-01-01T12:00:00.000Z", }, }; expect(() => formatMCPResponse(response)).not.toThrow(); }); }); });

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/tosin2013/documcp'

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