fa-mcp-sdk 0.2.121 → 0.2.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/bin/fa-mcp.js +44 -12
  2. package/cli-template/config/_local.yaml +40 -2
  3. package/cli-template/config/custom-environment-variables.yaml +5 -1
  4. package/cli-template/config/default.yaml +35 -5
  5. package/cli-template/fa-mcp-sdk-spec.md +620 -30
  6. package/cli-template/package.json +72 -73
  7. package/dist/core/_types_/TNtlm.d.ts +5 -0
  8. package/dist/core/_types_/TNtlm.d.ts.map +1 -0
  9. package/dist/core/_types_/TNtlm.js +2 -0
  10. package/dist/core/_types_/TNtlm.js.map +1 -0
  11. package/dist/core/_types_/config.d.ts +75 -0
  12. package/dist/core/_types_/config.d.ts.map +1 -0
  13. package/dist/core/_types_/config.js +2 -0
  14. package/dist/core/_types_/config.js.map +1 -0
  15. package/dist/core/_types_/types.d.ts +14 -9
  16. package/dist/core/_types_/types.d.ts.map +1 -1
  17. package/dist/core/{token/token-core.d.ts → auth/jwt-validation.d.ts} +2 -2
  18. package/dist/core/auth/jwt-validation.d.ts.map +1 -0
  19. package/dist/core/{token/token-core.js → auth/jwt-validation.js} +4 -4
  20. package/dist/core/auth/jwt-validation.js.map +1 -0
  21. package/dist/core/auth/middleware.d.ts +19 -0
  22. package/dist/core/auth/middleware.d.ts.map +1 -0
  23. package/dist/core/{token/token-auth.js → auth/middleware.js} +52 -55
  24. package/dist/core/auth/middleware.js.map +1 -0
  25. package/dist/core/auth/multi-auth.d.ts +23 -0
  26. package/dist/core/auth/multi-auth.d.ts.map +1 -0
  27. package/dist/core/auth/multi-auth.js +213 -0
  28. package/dist/core/auth/multi-auth.js.map +1 -0
  29. package/dist/core/auth/token-generator/html.d.ts.map +1 -0
  30. package/dist/core/{token/gen-token-app → auth/token-generator}/html.js +2 -2
  31. package/dist/core/auth/token-generator/html.js.map +1 -0
  32. package/dist/core/auth/token-generator/ntlm-auth-options.d.ts.map +1 -0
  33. package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-auth-options.js +1 -1
  34. package/dist/core/auth/token-generator/ntlm-auth-options.js.map +1 -0
  35. package/dist/core/auth/token-generator/ntlm-domain-config.d.ts.map +1 -0
  36. package/dist/core/auth/token-generator/ntlm-domain-config.js.map +1 -0
  37. package/dist/core/auth/token-generator/ntlm-integration.d.ts.map +1 -0
  38. package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-integration.js +4 -4
  39. package/dist/core/auth/token-generator/ntlm-integration.js.map +1 -0
  40. package/dist/core/auth/token-generator/ntlm-session-storage.d.ts.map +1 -0
  41. package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-session-storage.js +1 -1
  42. package/dist/core/auth/token-generator/ntlm-session-storage.js.map +1 -0
  43. package/dist/core/auth/token-generator/ntlm-templates.d.ts.map +1 -0
  44. package/dist/core/auth/token-generator/ntlm-templates.js.map +1 -0
  45. package/dist/core/{token/gen-token-app/gen-token-server.d.ts → auth/token-generator/server.d.ts} +1 -1
  46. package/dist/core/auth/token-generator/server.d.ts.map +1 -0
  47. package/dist/core/{token/gen-token-app/gen-token-server.js → auth/token-generator/server.js} +3 -3
  48. package/dist/core/auth/token-generator/server.js.map +1 -0
  49. package/dist/core/auth/types.d.ts +29 -0
  50. package/dist/core/auth/types.d.ts.map +1 -0
  51. package/dist/core/auth/types.js +5 -0
  52. package/dist/core/auth/types.js.map +1 -0
  53. package/dist/core/bootstrap/init-config.d.ts.map +1 -1
  54. package/dist/core/bootstrap/init-config.js +4 -0
  55. package/dist/core/bootstrap/init-config.js.map +1 -1
  56. package/dist/core/cache/cache.d.ts.map +1 -1
  57. package/dist/core/cache/cache.js +3 -2
  58. package/dist/core/cache/cache.js.map +1 -1
  59. package/dist/core/index.d.ts +6 -4
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +6 -3
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/utils/utils.d.ts +6 -0
  64. package/dist/core/utils/utils.d.ts.map +1 -1
  65. package/dist/core/utils/utils.js +25 -0
  66. package/dist/core/utils/utils.js.map +1 -1
  67. package/dist/core/web/server-http.d.ts.map +1 -1
  68. package/dist/core/web/server-http.js +32 -18
  69. package/dist/core/web/server-http.js.map +1 -1
  70. package/package.json +2 -2
  71. package/cli-template/src/_types_/common.d.ts +0 -27
  72. package/cli-template/src/api/router.ts +0 -35
  73. package/cli-template/src/api/swagger.ts +0 -167
  74. package/cli-template/src/asset/favicon.svg +0 -3
  75. package/cli-template/src/custom-resources.ts +0 -11
  76. package/cli-template/src/prompts/agent-brief.ts +0 -8
  77. package/cli-template/src/prompts/agent-prompt.ts +0 -10
  78. package/cli-template/src/prompts/custom-prompts.ts +0 -12
  79. package/cli-template/src/start.ts +0 -71
  80. package/cli-template/src/tools/handle-tool-call.ts +0 -55
  81. package/cli-template/src/tools/tools.ts +0 -88
  82. package/cli-template/tests/jest-simple-reporter.js +0 -10
  83. package/cli-template/tests/mcp/sse/mcp-sse-client-handling.md +0 -111
  84. package/cli-template/tests/mcp/sse/test-sse-npm-package.js +0 -96
  85. package/cli-template/tests/mcp/test-cases.js +0 -143
  86. package/cli-template/tests/mcp/test-http.js +0 -63
  87. package/cli-template/tests/mcp/test-sse.js +0 -67
  88. package/cli-template/tests/mcp/test-stdio.js +0 -78
  89. package/cli-template/tests/utils.ts +0 -154
  90. package/dist/core/token/gen-token-app/gen-token-server.d.ts.map +0 -1
  91. package/dist/core/token/gen-token-app/gen-token-server.js.map +0 -1
  92. package/dist/core/token/gen-token-app/html.d.ts.map +0 -1
  93. package/dist/core/token/gen-token-app/html.js.map +0 -1
  94. package/dist/core/token/gen-token-app/ntlm-auth-options.d.ts.map +0 -1
  95. package/dist/core/token/gen-token-app/ntlm-auth-options.js.map +0 -1
  96. package/dist/core/token/gen-token-app/ntlm-domain-config.d.ts.map +0 -1
  97. package/dist/core/token/gen-token-app/ntlm-domain-config.js.map +0 -1
  98. package/dist/core/token/gen-token-app/ntlm-integration.d.ts.map +0 -1
  99. package/dist/core/token/gen-token-app/ntlm-integration.js.map +0 -1
  100. package/dist/core/token/gen-token-app/ntlm-session-storage.d.ts.map +0 -1
  101. package/dist/core/token/gen-token-app/ntlm-session-storage.js.map +0 -1
  102. package/dist/core/token/gen-token-app/ntlm-templates.d.ts.map +0 -1
  103. package/dist/core/token/gen-token-app/ntlm-templates.js.map +0 -1
  104. package/dist/core/token/i-token.d.ts +0 -13
  105. package/dist/core/token/i-token.d.ts.map +0 -1
  106. package/dist/core/token/i-token.js +0 -2
  107. package/dist/core/token/i-token.js.map +0 -1
  108. package/dist/core/token/token-auth.d.ts +0 -17
  109. package/dist/core/token/token-auth.d.ts.map +0 -1
  110. package/dist/core/token/token-auth.js.map +0 -1
  111. package/dist/core/token/token-core.d.ts.map +0 -1
  112. package/dist/core/token/token-core.js.map +0 -1
  113. /package/dist/core/{token/gen-token-app → auth/token-generator}/html.d.ts +0 -0
  114. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-auth-options.d.ts +0 -0
  115. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-domain-config.d.ts +0 -0
  116. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-domain-config.js +0 -0
  117. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-integration.d.ts +0 -0
  118. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-session-storage.d.ts +0 -0
  119. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-templates.d.ts +0 -0
  120. /package/dist/core/{token/gen-token-app → auth/token-generator}/ntlm-templates.js +0 -0
@@ -1,71 +0,0 @@
1
- // Import all project data from existing files
2
- // @ts-ignore
3
- import { appConfig, initMcpServer, McpServerData, getAsset } from 'fa-mcp-sdk';
4
- import { tools } from './tools/tools.js';
5
- import { handleToolCall } from './tools/handle-tool-call.js';
6
- import { AGENT_BRIEF } from './prompts/agent-brief.js';
7
- import { AGENT_PROMPT } from './prompts/agent-prompt.js';
8
- import { customPrompts } from './prompts/custom-prompts.js';
9
- import { customResources } from './custom-resources.js';
10
- import { apiRouter, endpointsOn404 } from './api/router.js';
11
- import { swagger } from './api/swagger.js';
12
-
13
- const isConsulProd = (process.env.NODE_CONSUL_ENV || process.env.NODE_ENV) === 'production';
14
-
15
- /**
16
- * Main function that assembles all project data and starts the MCP server
17
- */
18
- const startProject = async (): Promise<void> => {
19
- // Read favicon from assets
20
- const favicon = getAsset('favicon.svg')!;
21
-
22
- // Assemble all data to pass to the core
23
- const serverData: McpServerData = {
24
- // MCP components
25
- tools,
26
- toolHandler: handleToolCall,
27
-
28
- // Prompts
29
- agentBrief: AGENT_BRIEF,
30
- agentPrompt: AGENT_PROMPT,
31
- customPrompts,
32
- requiredHttpHeaders: [{ name: 'Authorization', description: 'JWT Token issued on request' }],
33
- // Resources
34
- customResources,
35
-
36
- // HTTP components
37
- httpComponents: {
38
- apiRouter,
39
- endpointsOn404,
40
- swagger: {
41
- swaggerSpecs: swagger.swaggerSpecs,
42
- swaggerUi: swagger.swaggerUi,
43
- },
44
- },
45
-
46
- // Assets
47
- assets: {
48
- favicon,
49
- maintainerHtml: '<a href="https://support.com/page/2805" target="_blank" rel="noopener" class="clickable">Support</a>',
50
- },
51
- // Function to get Consul UI address (if consul enabled: consul.service.enable = true)
52
- getConsulUIAddress: (serviceId: string) => {
53
- const { agent } = appConfig.consul || {};
54
- if (!agent?.dev?.host || !agent?.prd?.host) {
55
- return '--consul-ui-not-configured--';
56
- }
57
- return `${isConsulProd
58
- ? `https://${agent.prd.host}/ui/dc-msk-infra`
59
- : `https://${agent.dev.host}/ui/dc-dev`
60
- }/services/${serviceId}/instances`;
61
- },
62
- };
63
-
64
- // Start MCP server with assembled data
65
- await initMcpServer(serverData);
66
- };
67
-
68
- startProject().catch(error => {
69
- console.error('Failed to start project:', error);
70
- process.exit(1);
71
- });
@@ -1,55 +0,0 @@
1
- import { logger as lgr, formatToolResult, ToolExecutionError } from 'fa-mcp-sdk';
2
- import chalk from 'chalk';
3
-
4
- const logger = lgr.getSubLogger({ name: chalk.bgGrey('tools') });
5
-
6
- /**
7
- * Template tool handler - customize this for your specific tools
8
- * This handles MCP tool execution requests
9
- */
10
- export const handleToolCall = async (params: { name: string, arguments?: any }): Promise<any> => {
11
- const { name, arguments: args } = params;
12
-
13
- logger.info(`Tool called: ${name}`);
14
-
15
- try {
16
- // TODO: Implement your tool routing logic here
17
- switch (name) {
18
- case 'example_tool':
19
- return await handleExampleTool(args);
20
-
21
- default:
22
- throw new ToolExecutionError(name, `Unknown tool: ${name}`);
23
- }
24
- } catch (error) {
25
- logger.error(`Tool execution failed for ${name}:`, error);
26
- throw error;
27
- }
28
- };
29
-
30
- /**
31
- * Example tool implementation
32
- * Replace this with your actual tool logic
33
- */
34
- async function handleExampleTool (args: any): Promise<string> {
35
- const { query } = args || {};
36
-
37
- if (!query) {
38
- throw new ToolExecutionError('example_tool', 'Query parameter is required');
39
- }
40
-
41
- // Simulate some work
42
- await new Promise(resolve => setTimeout(resolve, 100));
43
-
44
- const result = {
45
- message: `Processed query: ${query}`,
46
- timestamp: new Date().toISOString(),
47
- };
48
-
49
- return formatToolResult(result);
50
- }
51
-
52
- // TODO: Add more tool handlers here
53
- // async function handleAnotherTool(args: any): Promise<string> {
54
- // // Your implementation
55
- // }
@@ -1,88 +0,0 @@
1
- import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
- import { IToolInputSchema, IToolProperties } from 'fa-mcp-sdk';
3
-
4
- /**
5
- * Template tools configuration for MCP Server
6
- * Define your tools according to your server's functionality
7
- */
8
-
9
-
10
- const getGenericInputSchema = (
11
- queryDescription?: string,
12
- additionalProperties?: IToolProperties,
13
- ): IToolInputSchema => {
14
- const properties = {
15
- query: {
16
- type: 'string',
17
- description: queryDescription || 'Input query or text',
18
- },
19
- ...additionalProperties,
20
- };
21
-
22
- return {
23
- type: 'object',
24
- properties,
25
- required: ['query'],
26
- };
27
- };
28
-
29
- const getSearchInputSchema = (): IToolInputSchema => {
30
- return {
31
- type: 'object',
32
- properties: {
33
- query: {
34
- type: 'string',
35
- description: 'Search query',
36
- },
37
- limit: {
38
- type: 'number',
39
- description: 'Maximum number of results to return (1-100, default: 20)',
40
- minimum: 1,
41
- maximum: 100,
42
- },
43
- threshold: {
44
- type: 'number',
45
- description: 'Minimum similarity threshold (0-1)',
46
- minimum: 0,
47
- maximum: 1,
48
- },
49
- },
50
- required: ['query'],
51
- };
52
- };
53
-
54
- // Template tools - customize according to your needs
55
- export const tools: Tool[] = [
56
- {
57
- name: 'example_tool',
58
- description: 'Example tool that processes text input. Replace with your actual tools.',
59
- inputSchema: getGenericInputSchema('Text to process'),
60
- },
61
- {
62
- name: 'example_search',
63
- description: 'Example search tool with pagination and filtering. Template for search-based tools.',
64
- inputSchema: getSearchInputSchema(),
65
- },
66
- // TODO: Add your actual tools here
67
- // {
68
- // name: 'your_tool_name',
69
- // description: 'Description of what your tool does',
70
- // inputSchema: getGenericInputSchema('Your query description', {
71
- // // additional parameters
72
- // param1: {
73
- // type: 'string',
74
- // description: 'Description of param1',
75
- // },
76
- // }),
77
- // },
78
- ];
79
-
80
- // Helper to get tool by name
81
- export const getToolByName = (name: string): Tool | undefined => {
82
- return tools.find(tool => tool.name === name);
83
- };
84
-
85
- // Helper to get all tool names
86
- export const getToolNames = (): string[] => {
87
- return tools.map(tool => tool.name);
88
- };
@@ -1,10 +0,0 @@
1
- export default class SimpleReporter {
2
- onRunComplete (contexts, runResults) {
3
- const { numPassedTests, numFailedTests, numPendingTests } = runResults;
4
- const total = numPassedTests + numFailedTests + numPendingTests;
5
- const actualSuccess = numFailedTests === 0 && total > 0;
6
-
7
- console.log(`\nTest Results: ${numPassedTests} passed, ${numFailedTests} failed, ${numPendingTests} skipped (${total} total)`);
8
- console.log(`Status: ${actualSuccess ? 'PASSED' : 'FAILED'}`);
9
- }
10
- }
@@ -1,111 +0,0 @@
1
- # MCP SSE Client - Preventing unhandledRejection
2
-
3
- ## Problem
4
-
5
- When using `fa-mcp-sdk` as an npm package in tests where `McpSseClient` is imported, `unhandledRejection` errors may occur. This happens because MCP errors are handled asynchronously through SSE (Server-Sent Events) and may not be properly caught in the test context.
6
-
7
- ## Why this happens
8
-
9
- 1. **Local tests**: Run in the same process as the MCP server
10
- 2. **NPM package**: The test runs in a separate process
11
- 3. **SSE asynchronicity**: Errors may occur after the promise is created but before they are handled
12
-
13
- ## Solutions
14
-
15
- ### Solution 1: Use a static method (recommended)
16
-
17
- ```javascript
18
- import { McpSseClient } from 'fa-mcp-sdk';
19
-
20
- // Create a client with automatic unhandledRejection handling
21
- const client = McpSseClient.createWithErrorHandler('http://localhost:3000');
22
-
23
- try {
24
- const response = await client.callTool('execute_sql_query', { sql: test.sql });
25
- console.log('✅ Success:', response);
26
- } catch (error) {
27
- console.log('❌ Error:', error.message);
28
- }
29
- ```
30
-
31
- ### Solution 2: Global unhandledRejection handler
32
-
33
- Add at the beginning of the test file:
34
-
35
- ```javascript
36
- // Global handling of MCP errors
37
- process.on('unhandledRejection', (reason) => {
38
- if (typeof reason === 'object' && reason?.message?.includes('MCP Error:')) {
39
- // Ignore MCP errors — they are handled in try-catch
40
- return;
41
- }
42
- });
43
-
44
- // Or using the built-in method
45
- import { McpSseClient } from 'fa-mcp-sdk';
46
- McpSseClient.setupGlobalErrorHandler();
47
- ```
48
-
49
- ### Solution 3: Promise with error handling
50
-
51
- ```javascript
52
- import { McpSseClient } from 'fa-mcp-sdk';
53
-
54
- const client = new McpSseClient('http://localhost:3000');
55
-
56
- // Wrap all calls in an additional catch
57
- async function safeCallTool(toolName, args) {
58
- try {
59
- return await client.callTool(toolName, args);
60
- } catch (error) {
61
- // Additional handling to prevent unhandledRejection
62
- if (error.message.includes('MCP Error:')) {
63
- throw error;
64
- }
65
- throw new Error(`Unexpected error: ${error.message}`);
66
- }
67
- }
68
- ```
69
-
70
- ## Full test example
71
-
72
- ```javascript
73
- import { McpSseClient } from 'fa-mcp-sdk';
74
-
75
- async function testToolExecution() {
76
- // Create a client with error handling
77
- const client = McpSseClient.createWithErrorHandler('http://localhost:3000');
78
-
79
- try {
80
- // Test successful execution
81
- const successResponse = await client.callTool('example_tool', { query: 'ping' });
82
- console.log('✅ Tool executed successfully:', successResponse);
83
-
84
- // Test error handling (as in your example)
85
- try {
86
- await client.callTool('example_tool', {}); // No query — should error
87
- console.log('❌ Expected error but got success');
88
- } catch (error) {
89
- console.log('✅ Expected error caught:', error.message);
90
- }
91
-
92
- } catch (error) {
93
- console.error('❌ Unexpected test error:', error);
94
- } finally {
95
- await client.close();
96
- }
97
- }
98
-
99
- testToolExecution();
100
- ```
101
-
102
- ## Important notes
103
-
104
- 1. **For npm packages only**: The issue usually does not occur in local tests
105
- 2. **MCP errors**: The handler filters only errors containing 'MCP Error:'
106
- 3. **Global handler**: Installed once per process
107
- 4. **Resources**: Don’t forget to call `client.close()` for cleanup
108
-
109
- ## Technical details
110
-
111
- The issue arises because SSE errors are handled in `McpSseClient.handleSseEvent()` and may cause a promise rejection that is not yet caught at the moment the error occurs. New methods provide an additional layer of handling to prevent unhandledRejection.
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Example of using fa-mcp-sdk as an npm package without unhandledRejection issues
5
- *
6
- * To use this example:
7
- * 1. Install fa-mcp-sdk: npm install fa-mcp-sdk
8
- * 2. Start MCP server: npm run template:start (in fa-mcp-sdk project)
9
- * 3. Run this example: node test-npm-package.js
10
- */
11
-
12
- import { McpSseClient } from 'fa-mcp-sdk';
13
-
14
- const SERVER_URL = 'http://localhost:{{port}}';
15
-
16
- async function testMcpClient () {
17
- console.log('🧪 Testing MCP client as npm package');
18
- console.log('='.repeat(50));
19
-
20
- // Use the new method that handles unhandledRejection
21
- const client = McpSseClient.createWithErrorHandler(SERVER_URL);
22
-
23
- try {
24
- // Health check
25
- console.log('1. Health check...');
26
- try {
27
- const health = await client.health();
28
- console.log('✅ Health check passed:', health.status);
29
- } catch (error) {
30
- console.log('⚠️ Health check failed:', error.message);
31
- }
32
-
33
- // Initialize
34
- console.log('2. Initializing...');
35
- await client.sendRequest('initialize', {
36
- protocolVersion: '2024-11-05',
37
- capabilities: { tools: {} },
38
- clientInfo: { name: 'npm-test', version: '1.0.0' },
39
- });
40
- console.log('✅ Initialized successfully');
41
-
42
- // List tools
43
- console.log('3. Listing tools...');
44
- const tools = await client.listTools();
45
- const toolNames = tools.tools?.map(t => t.name) || [];
46
- console.log('✅ Available tools:', toolNames.join(', '));
47
-
48
- // Test successful tool call
49
- console.log('4. Testing successful tool call...');
50
- try {
51
- const response = await client.callTool('example_tool', { query: 'ping' });
52
- console.log('✅ Tool call successful:', response.result?.structuredContent?.message || response.result?.content?.[0]?.text);
53
- } catch (error) {
54
- console.log('❌ Tool call failed:', error.message);
55
- }
56
-
57
- // Test expected error (like the test case in test-cases.js)
58
- console.log('5. Testing expected error handling...');
59
- try {
60
- await client.callTool('example_tool', {}); // Missing query parameter
61
- console.log('❌ Expected error but got success');
62
- } catch (error) {
63
- console.log('✅ Expected error caught:', error.message);
64
- // This error should NOT cause unhandledRejection
65
- }
66
-
67
- // Test with invalid tool name
68
- console.log('6. Testing invalid tool name...');
69
- try {
70
- await client.callTool('nonexistent_tool', {});
71
- console.log('❌ Expected error but got success');
72
- } catch (error) {
73
- console.log('✅ Expected error caught:', error.message);
74
- // This error should NOT cause unhandledRejection
75
- }
76
-
77
- console.log('\n🎉 All tests completed successfully!');
78
- console.log('✨ No unhandledRejection errors occurred');
79
-
80
- } catch (error) {
81
- console.error('❌ Unexpected error:', error.message);
82
- } finally {
83
- await client.close();
84
- console.log('🔚 Client closed');
85
- }
86
- }
87
-
88
- // Global handler for any unexpected unhandled rejections
89
- process.on('unhandledRejection', (reason, promise) => {
90
- console.error('💥 UNHANDLED REJECTION (this should not happen):');
91
- console.error('Reason:', reason);
92
- console.error('Promise:', promise);
93
- process.exit(1);
94
- });
95
-
96
- testMcpClient().catch(console.error);
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Shared test cases for the template MCP server (src/template)
5
- * Covers: prompts, resources, tools
6
- *
7
- * Each test case is a function(client) -> Promise<{ name, passed, details? }>
8
- * where client provides methods:
9
- * - listPrompts(), getPrompt(name, args?)
10
- * - listResources(), readResource(uri)
11
- * - listTools(), callTool(name, args?)
12
- */
13
-
14
- const ok = (name, details) => ({ name, passed: true, details });
15
- const fail = (name, details) => ({ name, passed: false, details });
16
-
17
- // Utility: extract system text from prompts/get response
18
- const extractPromptText = (resp) => {
19
- // resp may be raw result or wrapped; support both shapes used by clients
20
- const r = resp?.result || resp;
21
- const msg = r?.messages?.[0];
22
- const text = msg?.content?.text || msg?.content?.[0]?.text || r?.messages?.[0]?.content?.[0]?.text;
23
- return typeof text === 'string' ? text : undefined;
24
- };
25
-
26
- export const TEMPLATE_TESTS = {
27
- prompts: [
28
- async (client) => {
29
- const name = 'List prompts contains agent_brief and agent_prompt';
30
- try {
31
- const list = await client.listPrompts();
32
- const prompts = list?.prompts || list;
33
- const names = Array.isArray(prompts) ? prompts.map(p => p.name) : [];
34
- const okBrief = names.includes('agent_brief');
35
- const okPrompt = names.includes('agent_prompt');
36
- return (okBrief && okPrompt) ? ok(name, { names }) : fail(name, { names });
37
- } catch (e) {
38
- return fail(name, { error: e?.message });
39
- }
40
- },
41
- async (client) => {
42
- const name = 'Get agent_brief returns text';
43
- try {
44
- const resp = await client.getPrompt('agent_brief');
45
- const text = extractPromptText(resp);
46
- return text ? ok(name, { text }) : fail(name, { text });
47
- } catch (e) {
48
- return fail(name, { error: e?.message });
49
- }
50
- },
51
- async (client) => {
52
- const name = 'Get agent_prompt returns text';
53
- try {
54
- const resp = await client.getPrompt('agent_prompt');
55
- const text = extractPromptText(resp);
56
- return text ? ok(name, { text }) : fail(name, { text });
57
- } catch (e) {
58
- return fail(name, { error: e?.message });
59
- }
60
- },
61
- async (client) => {
62
- const name = 'Get custom_prompt returns dynamic text';
63
- try {
64
- const resp = await client.getPrompt('custom_prompt', { sample: '1' });
65
- const text = extractPromptText(resp);
66
- const hasWord = typeof text === 'string' && text.includes('Custom prompt content');
67
- return hasWord ? ok(name, { text }) : fail(name, { text });
68
- } catch (e) {
69
- return fail(name, { error: e?.message });
70
- }
71
- },
72
- ],
73
-
74
- resources: [
75
- async (client) => {
76
- const name = 'List resources contains custom-resource://resource1';
77
- try {
78
- const list = await client.listResources();
79
- const resources = list?.resources || list;
80
- const uris = Array.isArray(resources) ? resources.map(r => r.uri) : [];
81
- const found = uris.includes('custom-resource://resource1');
82
- return found ? ok(name, { uris }) : fail(name, { uris });
83
- } catch (e) {
84
- return fail(name, { error: e?.message });
85
- }
86
- },
87
- async (client) => {
88
- const name = 'Read resource custom-resource://resource1 returns content';
89
- try {
90
- const resp = await client.readResource('custom-resource://resource1');
91
- // Different clients return differently; normalize
92
- const r = resp?.result || resp;
93
- const text = r?.resource?.text || r?.contents?.[0]?.text || r?.text || r?.resource?.content;
94
- const okText = typeof text === 'string' && text.length > 0;
95
- return okText ? ok(name, { text }) : fail(name, { response: r });
96
- } catch (e) {
97
- return fail(name, { error: e?.message });
98
- }
99
- },
100
- ],
101
-
102
- tools: [
103
- async (client) => {
104
- const name = 'List tools contains example_tool and example_search';
105
- try {
106
- const list = await client.listTools();
107
- const tools = list?.tools || list;
108
- const names = Array.isArray(tools) ? tools.map(t => t.name) : [];
109
- const ok1 = names.includes('example_tool');
110
- const ok2 = names.includes('example_search');
111
- return (ok1 && ok2) ? ok(name, { names }) : fail(name, { names });
112
- } catch (e) {
113
- return fail(name, { error: e?.message });
114
- }
115
- },
116
- async (client) => {
117
- const name = 'Call example_tool returns formatted result';
118
- try {
119
- const resp = await client.callTool('example_tool', { query: 'ping' });
120
- const r = resp?.result || resp;
121
- // Both structuredContent and text are acceptable; check message echo
122
- const structured = r?.structuredContent;
123
- const text = r?.content?.[0]?.text;
124
- const hasProcessed = (structured && structured.message?.includes('Processed query'))
125
- || (typeof text === 'string' && text.includes('Processed query'));
126
- return hasProcessed ? ok(name, { response: r }) : fail(name, { response: r });
127
- } catch (e) {
128
- return fail(name, { error: e?.message });
129
- }
130
- },
131
- async (client) => {
132
- const name = 'Call example_tool without query should fail';
133
- try {
134
- await client.callTool('example_tool', {});
135
- return fail(name, { error: 'Expected failure, got success' });
136
- } catch (e) {
137
- return ok(name, { error: e?.message });
138
- }
139
- },
140
- ],
141
- };
142
-
143
- export default TEMPLATE_TESTS;
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * HTTP transport tests for the template MCP server (src/template)
5
- * Uses McpHttpClient (simple POST requests)
6
- */
7
-
8
- import { appConfig, McpHttpClient } from 'fa-mcp-sdk';
9
- import TEMPLATE_TESTS from './test-cases.js';
10
-
11
- const baseUrl = (process.env.TEST_MCP_SERVER_URL || `http://localhost:${appConfig.webServer.port}`).replace(/\/+$/,'');
12
-
13
- async function runTestGroup (title, tests, client) {
14
- console.log(`\n${title}:`);
15
- let passed = 0;
16
- for (const test of tests) {
17
- try {
18
- const res = await test(client);
19
- if (res.passed) {
20
- console.log(` ✅ ${res.name}`);
21
- passed++;
22
- } else {
23
- console.log(` ❌ ${res.name}`);
24
- if (res.details) {
25
- console.log(' ', res.details);
26
- }
27
- }
28
- } catch (e) {
29
- console.log(` ❌ ${(await test).name || 'test'}:`, e.message);
30
- }
31
- }
32
- console.log(` Result: ${passed}/${tests.length} passed`);
33
- return passed;
34
- }
35
-
36
- async function main () {
37
- console.log('🧪 HTTP tests for template MCP server');
38
- console.log('='.repeat(60));
39
-
40
- const client = new McpHttpClient(baseUrl);
41
- try {
42
- await client.initialize({
43
- protocolVersion: '2024-11-05',
44
- capabilities: { tools: {} },
45
- clientInfo: { name: 'http-test', version: '1.0.0' },
46
- });
47
-
48
- const p1 = await runTestGroup('Prompts', TEMPLATE_TESTS.prompts, client);
49
- const p2 = await runTestGroup('Resources', TEMPLATE_TESTS.resources, client);
50
- const p3 = await runTestGroup('Tools', TEMPLATE_TESTS.tools, client);
51
-
52
- const total = TEMPLATE_TESTS.prompts.length + TEMPLATE_TESTS.resources.length + TEMPLATE_TESTS.tools.length;
53
- const sum = p1 + p2 + p3;
54
- console.log(`\nSummary: ${sum}/${total} tests passed`);
55
- } finally {
56
- await client.close().catch(() => undefined);
57
- }
58
- }
59
-
60
- main().catch((e) => {
61
- console.error('Test failed:', e?.message || e);
62
- process.exit(1);
63
- });