fa-mcp-sdk 0.2.38 → 0.2.78

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 (121) hide show
  1. package/bin/fa-mcp.js +781 -0
  2. package/cli-template/.editorconfig +13 -0
  3. package/cli-template/.env.example +29 -0
  4. package/cli-template/.envrc +3 -0
  5. package/cli-template/.run/== START ==.run.xml +14 -0
  6. package/cli-template/.run/TEST HTTP.run.xml +5 -0
  7. package/cli-template/.run/TEST SSE.run.xml +5 -0
  8. package/cli-template/.run/TEST STDIO.run.xml +5 -0
  9. package/cli-template/.run/TEST search.run.xml +11 -0
  10. package/cli-template/.run/cb.run.xml +12 -0
  11. package/cli-template/.run/ci.run.xml +12 -0
  12. package/cli-template/.run/kill-port 3030.run.xml +5 -0
  13. package/cli-template/.run/lint.run.xml +12 -0
  14. package/cli-template/.run/lint_fix.run.xml +12 -0
  15. package/cli-template/.run/reinstall.run.xml +12 -0
  16. package/cli-template/.run/remove-nul.js.run.xml +5 -0
  17. package/cli-template/LICENSE +21 -0
  18. package/cli-template/config/_local.yaml +64 -0
  19. package/cli-template/config/custom-environment-variables.yaml +33 -0
  20. package/cli-template/config/default.yaml +101 -0
  21. package/cli-template/config/development.yaml +4 -0
  22. package/cli-template/config/production.yaml +4 -0
  23. package/cli-template/config/test.yaml +26 -0
  24. package/cli-template/deploy/.gitkeep +0 -0
  25. package/cli-template/deploy/config.example.yml +3 -0
  26. package/cli-template/deploy/mcp-template.com.conf +58 -0
  27. package/cli-template/deploy/pm2.config.js +30 -0
  28. package/cli-template/deploy/pm2reg.sh +49 -0
  29. package/cli-template/deploy/srv.sh +359 -0
  30. package/cli-template/deploy/srv.sh.readme.md +347 -0
  31. package/cli-template/eslint.config.js +139 -0
  32. package/cli-template/jest.config.js +30 -0
  33. package/cli-template/package.json +73 -0
  34. package/cli-template/scripts/kill-port.js +107 -0
  35. package/cli-template/scripts/npm/patch_node_modules.js +9 -0
  36. package/cli-template/scripts/npm/run.js +31 -0
  37. package/cli-template/scripts/npm/yarn-ci.ps1 +16 -0
  38. package/cli-template/scripts/npm/yarn-ci.sh +8 -0
  39. package/cli-template/scripts/npm/yarn-reinstall.ps1 +54 -0
  40. package/cli-template/scripts/npm/yarn-reinstall.sh +10 -0
  41. package/cli-template/scripts/pre-commit +58 -0
  42. package/cli-template/scripts/remove-nul.js +53 -0
  43. package/cli-template/src/_types_/common.d.ts +27 -0
  44. package/cli-template/src/api/router.ts +35 -0
  45. package/cli-template/src/api/swagger.ts +167 -0
  46. package/cli-template/src/asset/favicon.svg +4 -0
  47. package/cli-template/src/custom-resources.ts +11 -0
  48. package/cli-template/src/prompts/agent-brief.ts +8 -0
  49. package/cli-template/src/prompts/agent-prompt.ts +1 -0
  50. package/cli-template/src/prompts/custom-prompts.ts +12 -0
  51. package/cli-template/src/start.ts +84 -0
  52. package/cli-template/src/tools/handle-tool-call.ts +55 -0
  53. package/cli-template/src/tools/tools.ts +88 -0
  54. package/cli-template/tests/jest-simple-reporter.js +10 -0
  55. package/cli-template/tests/mcp/sse/mcp-sse-client-handling.md +111 -0
  56. package/cli-template/tests/mcp/sse/test-sse-npm-package.js +96 -0
  57. package/cli-template/tests/mcp/test-cases.js +143 -0
  58. package/cli-template/tests/mcp/test-http.js +63 -0
  59. package/cli-template/tests/mcp/test-sse.js +67 -0
  60. package/cli-template/tests/mcp/test-stdio.js +78 -0
  61. package/cli-template/tests/utils.ts +154 -0
  62. package/cli-template/tsconfig.json +48 -0
  63. package/cli-template/update.cjs +631 -0
  64. package/dist/core/_types_/active-directory-config.d.ts +24 -0
  65. package/dist/core/_types_/active-directory-config.d.ts.map +1 -0
  66. package/dist/core/_types_/active-directory-config.js +2 -0
  67. package/dist/core/_types_/active-directory-config.js.map +1 -0
  68. package/dist/core/bootstrap/init-config.d.ts.map +1 -1
  69. package/dist/core/bootstrap/init-config.js +14 -3
  70. package/dist/core/bootstrap/init-config.js.map +1 -1
  71. package/dist/core/bootstrap/startup-info.js +1 -1
  72. package/dist/core/bootstrap/startup-info.js.map +1 -1
  73. package/dist/core/index.d.ts +3 -2
  74. package/dist/core/index.d.ts.map +1 -1
  75. package/dist/core/index.js +5 -2
  76. package/dist/core/index.js.map +1 -1
  77. package/dist/core/init-mcp-server.js +1 -1
  78. package/dist/core/init-mcp-server.js.map +1 -1
  79. package/dist/core/token/gen-token-app/gen-token-server.d.ts.map +1 -1
  80. package/dist/core/token/gen-token-app/gen-token-server.js +85 -9
  81. package/dist/core/token/gen-token-app/gen-token-server.js.map +1 -1
  82. package/dist/core/token/gen-token-app/html.d.ts +8 -1
  83. package/dist/core/token/gen-token-app/html.d.ts.map +1 -1
  84. package/dist/core/token/gen-token-app/html.js +98 -2
  85. package/dist/core/token/gen-token-app/html.js.map +1 -1
  86. package/dist/core/token/gen-token-app/ntlm-auth-options.d.ts +4 -0
  87. package/dist/core/token/gen-token-app/ntlm-auth-options.d.ts.map +1 -0
  88. package/dist/core/token/gen-token-app/ntlm-auth-options.js +94 -0
  89. package/dist/core/token/gen-token-app/ntlm-auth-options.js.map +1 -0
  90. package/dist/core/token/gen-token-app/ntlm-domain-config.d.ts +16 -0
  91. package/dist/core/token/gen-token-app/ntlm-domain-config.d.ts.map +1 -0
  92. package/dist/core/token/gen-token-app/ntlm-domain-config.js +71 -0
  93. package/dist/core/token/gen-token-app/ntlm-domain-config.js.map +1 -0
  94. package/dist/core/token/gen-token-app/ntlm-integration.d.ts +3 -0
  95. package/dist/core/token/gen-token-app/ntlm-integration.d.ts.map +1 -0
  96. package/dist/core/token/gen-token-app/ntlm-integration.js +69 -0
  97. package/dist/core/token/gen-token-app/ntlm-integration.js.map +1 -0
  98. package/dist/core/token/gen-token-app/ntlm-session-storage.d.ts +16 -0
  99. package/dist/core/token/gen-token-app/ntlm-session-storage.d.ts.map +1 -0
  100. package/dist/core/token/gen-token-app/ntlm-session-storage.js +74 -0
  101. package/dist/core/token/gen-token-app/ntlm-session-storage.js.map +1 -0
  102. package/dist/core/token/gen-token-app/ntlm-templates.d.ts +21 -0
  103. package/dist/core/token/gen-token-app/ntlm-templates.d.ts.map +1 -0
  104. package/dist/core/token/gen-token-app/ntlm-templates.js +246 -0
  105. package/dist/core/token/gen-token-app/ntlm-templates.js.map +1 -0
  106. package/dist/core/token/{token.d.ts → token-auth.d.ts} +1 -1
  107. package/dist/core/token/token-auth.d.ts.map +1 -0
  108. package/dist/core/token/{token.js → token-auth.js} +4 -6
  109. package/dist/core/token/token-auth.js.map +1 -0
  110. package/dist/core/token/token-core.d.ts +5 -1
  111. package/dist/core/token/token-core.d.ts.map +1 -1
  112. package/dist/core/token/token-core.js +13 -3
  113. package/dist/core/token/token-core.js.map +1 -1
  114. package/dist/core/web/about-page/render.d.ts.map +1 -1
  115. package/dist/core/web/about-page/render.js +26 -3
  116. package/dist/core/web/about-page/render.js.map +1 -1
  117. package/dist/core/web/server-http.js +1 -1
  118. package/dist/core/web/server-http.js.map +1 -1
  119. package/package.json +10 -3
  120. package/dist/core/token/token.d.ts.map +0 -1
  121. package/dist/core/token/token.js.map +0 -1
@@ -0,0 +1,167 @@
1
+ import swaggerJsdoc from 'swagger-jsdoc';
2
+ import swaggerUi from 'swagger-ui-express';
3
+ import { appConfig } from 'fa-mcp-sdk';
4
+
5
+ /**
6
+ * Generic Swagger configuration template for MCP Server
7
+ * Customize this file according to your API endpoints and schemas
8
+ */
9
+
10
+ // Build servers array from config with fallback
11
+ const buildServers = () => {
12
+ const servers = [];
13
+
14
+ // Use servers from config if available
15
+ if (appConfig.swagger?.servers?.length) {
16
+ appConfig.swagger.servers.forEach((server: any) => {
17
+ servers.push({
18
+ url: server.url,
19
+ description: server.description
20
+ });
21
+ });
22
+ } else {
23
+ // Fallback to default development server
24
+ servers.push({
25
+ url: `http://localhost:${appConfig.webServer.port}`,
26
+ description: 'Development server'
27
+ });
28
+ }
29
+
30
+ return servers;
31
+ };
32
+
33
+ const options = {
34
+ definition: {
35
+ openapi: '3.0.0',
36
+ info: {
37
+ title: 'MCP Server API',
38
+ version: appConfig.version,
39
+ description: `
40
+ REST API for your MCP Server. This is a template configuration.
41
+ Customize the endpoints, schemas, and documentation according to your needs.
42
+ `,
43
+ },
44
+ servers: buildServers(),
45
+ components: {
46
+ schemas: {
47
+ HealthResponse: {
48
+ type: 'object',
49
+ description: 'Health check response',
50
+ properties: {
51
+ status: {
52
+ type: 'string',
53
+ example: 'ok'
54
+ },
55
+ timestamp: {
56
+ type: 'string',
57
+ format: 'date-time',
58
+ example: '2024-11-05T12:00:00.000Z'
59
+ },
60
+ version: {
61
+ type: 'string',
62
+ example: '1.0.0'
63
+ }
64
+ },
65
+ required: ['status', 'timestamp', 'version']
66
+ },
67
+ ErrorResponse: {
68
+ type: 'object',
69
+ description: 'Error response format',
70
+ properties: {
71
+ success: {
72
+ type: 'boolean',
73
+ description: 'Indicates failed operation',
74
+ example: false
75
+ },
76
+ error: {
77
+ type: 'string',
78
+ description: 'Human-readable error message',
79
+ example: 'Validation failed'
80
+ },
81
+ code: {
82
+ type: 'string',
83
+ description: 'Error code for programmatic handling',
84
+ example: 'VALIDATION_ERROR'
85
+ }
86
+ },
87
+ required: ['success', 'error', 'code']
88
+ }
89
+ }
90
+ },
91
+ paths: {
92
+ '/health': {
93
+ get: {
94
+ summary: 'Health check',
95
+ description: 'Simple health check endpoint for monitoring',
96
+ tags: ['Server'],
97
+ responses: {
98
+ '200': {
99
+ description: 'Service is healthy',
100
+ content: {
101
+ 'application/json': {
102
+ schema: {
103
+ $ref: '#/components/schemas/HealthResponse'
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ },
111
+ '/example': {
112
+ get: {
113
+ summary: 'Example endpoint',
114
+ description: 'Template endpoint - customize as needed',
115
+ tags: ['Example'],
116
+ responses: {
117
+ '200': {
118
+ description: 'Success response',
119
+ content: {
120
+ 'application/json': {
121
+ schema: {
122
+ type: 'object',
123
+ properties: {
124
+ success: {
125
+ type: 'boolean',
126
+ example: true
127
+ },
128
+ message: {
129
+ type: 'string',
130
+ example: 'Template endpoint response'
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ },
137
+ '400': {
138
+ description: 'Bad request',
139
+ content: {
140
+ 'application/json': {
141
+ schema: {
142
+ $ref: '#/components/schemas/ErrorResponse'
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+ },
151
+ tags: [
152
+ {
153
+ name: 'Server',
154
+ description: 'Server management endpoints'
155
+ },
156
+ {
157
+ name: 'Example',
158
+ description: 'Template endpoints to customize'
159
+ }
160
+ ]
161
+ },
162
+ apis: [] // Add your API files here if using JSDoc comments
163
+ };
164
+
165
+ const swaggerSpecs = swaggerJsdoc(options);
166
+
167
+ export const swagger = { swaggerSpecs, swaggerUi };
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
2
+ <path fill="currentColor"
3
+ d="m 17.622437,19.155864 v -2.611959 h 2.251659 v 2.611959 H 22.468 v 2.251688 h -2.593904 v 2.581936 h -2.251659 v -2.581936 h -2.632933 v -2.251688 z m -5.686183,2.592444 v 2.251688 H 1.4 V 0 H 22.404961 V 15.011256 H 20.153302 V 2.251689 H 3.6516577 V 21.748308 H 11.937755 Z M 7.4959862,7.535651 V 6.034525 h 8.9705968 v 1.501126 z m 0.02402,4.552914 v -1.501126 h 8.9180578 v 1.501126 z m 0.02252,4.420814 v -1.501125 h 5.9278628 v 1.501125 z" />
4
+ </svg>
@@ -0,0 +1,11 @@
1
+ import { IResourceData } from 'fa-mcp-sdk';
2
+
3
+ export const customResources: IResourceData[] = [
4
+ {
5
+ uri: 'custom-resource://resource1',
6
+ name: 'Custom Resource',
7
+ description: 'Custom resource description',
8
+ mimeType: 'text/plain',
9
+ content: 'Custom resource content',
10
+ }
11
+ ];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Level 1: Brief agent description
3
+ * Used when LLM selects agents from a list based on user query
4
+ * LLM doesn't see tools at this level
5
+ */
6
+
7
+ export const AGENT_BRIEF = 'Agent brief';
8
+
@@ -0,0 +1 @@
1
+ export const AGENT_PROMPT = 'Agent Prompt';
@@ -0,0 +1,12 @@
1
+ import { IPromptData } from 'fa-mcp-sdk';
2
+
3
+ export const customPrompts: IPromptData[] = [
4
+ {
5
+ name: 'custom_prompt',
6
+ description: 'Custom prompt',
7
+ arguments: [],
8
+ content: (request) => {
9
+ return `Custom prompt content ${request.method}`;
10
+ },
11
+ },
12
+ ];
@@ -0,0 +1,84 @@
1
+ // Import all project data from existing files
2
+ // @ts-ignore
3
+ import { appConfig, initMcpServer, McpServerData } from 'fa-mcp-sdk';
4
+ import { readFileSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { tools } from './tools/tools.js';
7
+ import { handleToolCall } from './tools/handle-tool-call.js';
8
+ import { AGENT_BRIEF } from './prompts/agent-brief.js';
9
+ import { AGENT_PROMPT } from './prompts/agent-prompt.js';
10
+ import { customPrompts } from './prompts/custom-prompts.js';
11
+ import { customResources } from './custom-resources.js';
12
+ import { apiRouter, endpointsOn404 } from './api/router.js';
13
+ import { swagger } from './api/swagger.js';
14
+
15
+ const isConsulProd = (process.env.NODE_CONSUL_ENV || process.env.NODE_ENV) === 'production';
16
+
17
+ /**
18
+ * Template main function that assembles all project data and starts the MCP server
19
+ * This serves as an example of how to use the fa-mcp-sdk library
20
+ */
21
+ const startProject = async (): Promise<void> => {
22
+ // Read favicon from assets
23
+ const faviconPath = join(process.cwd(), 'src/template/asset/favicon.svg');
24
+ let favicon: string;
25
+
26
+ try {
27
+ favicon = readFileSync(faviconPath, 'utf-8');
28
+ } catch (_error) {
29
+ // Fallback if favicon not found
30
+ favicon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
31
+ <rect width="16" height="16" fill="${appConfig.uiColor.primary || '#007ACC'}"/>
32
+ </svg>`;
33
+ }
34
+
35
+ // Assemble all data to pass to the core
36
+ const serverData: McpServerData = {
37
+ // MCP components
38
+ tools,
39
+ toolHandler: handleToolCall,
40
+
41
+ // Prompts
42
+ agentBrief: AGENT_BRIEF,
43
+ agentPrompt: AGENT_PROMPT,
44
+ customPrompts,
45
+ requiredHttpHeaders: [{ name: 'Authorization', description: 'JWT Token issued on request' }],
46
+ // Resources
47
+ customResources,
48
+
49
+ // HTTP components
50
+ httpComponents: {
51
+ apiRouter,
52
+ endpointsOn404,
53
+ swagger: {
54
+ swaggerSpecs: swagger.swaggerSpecs,
55
+ swaggerUi: swagger.swaggerUi,
56
+ },
57
+ },
58
+
59
+ // Assets
60
+ assets: {
61
+ favicon,
62
+ maintainerHtml: '<a href="https://support.com/page/2805" target="_blank" rel="noopener" class="clickable">Support</a>',
63
+ },
64
+ // Function to get Consul UI address (if consul enabled: consul.service.enable = true)
65
+ getConsulUIAddress: (serviceId: string) => {
66
+ const { agent } = appConfig.consul || {};
67
+ if (!agent?.dev?.host || !agent?.prd?.host) {
68
+ return '--consul-ui-not-configured--';
69
+ }
70
+ return `${isConsulProd
71
+ ? `https://${agent.prd.host}/ui/dc-msk-infra`
72
+ : `https://${agent.dev.host}/ui/dc-dev`
73
+ }/services/${serviceId}/instances`;
74
+ },
75
+ };
76
+
77
+ // Start MCP server with assembled data
78
+ await initMcpServer(serverData);
79
+ };
80
+
81
+ startProject().catch(error => {
82
+ console.error('Failed to start project:', error);
83
+ process.exit(1);
84
+ });
@@ -0,0 +1,55 @@
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
+ // }
@@ -0,0 +1,88 @@
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
+ };
@@ -0,0 +1,10 @@
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
+ }
@@ -0,0 +1,111 @@
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.
@@ -0,0 +1,96 @@
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);