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.
- package/bin/fa-mcp.js +781 -0
- package/cli-template/.editorconfig +13 -0
- package/cli-template/.env.example +29 -0
- package/cli-template/.envrc +3 -0
- package/cli-template/.run/== START ==.run.xml +14 -0
- package/cli-template/.run/TEST HTTP.run.xml +5 -0
- package/cli-template/.run/TEST SSE.run.xml +5 -0
- package/cli-template/.run/TEST STDIO.run.xml +5 -0
- package/cli-template/.run/TEST search.run.xml +11 -0
- package/cli-template/.run/cb.run.xml +12 -0
- package/cli-template/.run/ci.run.xml +12 -0
- package/cli-template/.run/kill-port 3030.run.xml +5 -0
- package/cli-template/.run/lint.run.xml +12 -0
- package/cli-template/.run/lint_fix.run.xml +12 -0
- package/cli-template/.run/reinstall.run.xml +12 -0
- package/cli-template/.run/remove-nul.js.run.xml +5 -0
- package/cli-template/LICENSE +21 -0
- package/cli-template/config/_local.yaml +64 -0
- package/cli-template/config/custom-environment-variables.yaml +33 -0
- package/cli-template/config/default.yaml +101 -0
- package/cli-template/config/development.yaml +4 -0
- package/cli-template/config/production.yaml +4 -0
- package/cli-template/config/test.yaml +26 -0
- package/cli-template/deploy/.gitkeep +0 -0
- package/cli-template/deploy/config.example.yml +3 -0
- package/cli-template/deploy/mcp-template.com.conf +58 -0
- package/cli-template/deploy/pm2.config.js +30 -0
- package/cli-template/deploy/pm2reg.sh +49 -0
- package/cli-template/deploy/srv.sh +359 -0
- package/cli-template/deploy/srv.sh.readme.md +347 -0
- package/cli-template/eslint.config.js +139 -0
- package/cli-template/jest.config.js +30 -0
- package/cli-template/package.json +73 -0
- package/cli-template/scripts/kill-port.js +107 -0
- package/cli-template/scripts/npm/patch_node_modules.js +9 -0
- package/cli-template/scripts/npm/run.js +31 -0
- package/cli-template/scripts/npm/yarn-ci.ps1 +16 -0
- package/cli-template/scripts/npm/yarn-ci.sh +8 -0
- package/cli-template/scripts/npm/yarn-reinstall.ps1 +54 -0
- package/cli-template/scripts/npm/yarn-reinstall.sh +10 -0
- package/cli-template/scripts/pre-commit +58 -0
- package/cli-template/scripts/remove-nul.js +53 -0
- package/cli-template/src/_types_/common.d.ts +27 -0
- package/cli-template/src/api/router.ts +35 -0
- package/cli-template/src/api/swagger.ts +167 -0
- package/cli-template/src/asset/favicon.svg +4 -0
- package/cli-template/src/custom-resources.ts +11 -0
- package/cli-template/src/prompts/agent-brief.ts +8 -0
- package/cli-template/src/prompts/agent-prompt.ts +1 -0
- package/cli-template/src/prompts/custom-prompts.ts +12 -0
- package/cli-template/src/start.ts +84 -0
- package/cli-template/src/tools/handle-tool-call.ts +55 -0
- package/cli-template/src/tools/tools.ts +88 -0
- package/cli-template/tests/jest-simple-reporter.js +10 -0
- package/cli-template/tests/mcp/sse/mcp-sse-client-handling.md +111 -0
- package/cli-template/tests/mcp/sse/test-sse-npm-package.js +96 -0
- package/cli-template/tests/mcp/test-cases.js +143 -0
- package/cli-template/tests/mcp/test-http.js +63 -0
- package/cli-template/tests/mcp/test-sse.js +67 -0
- package/cli-template/tests/mcp/test-stdio.js +78 -0
- package/cli-template/tests/utils.ts +154 -0
- package/cli-template/tsconfig.json +48 -0
- package/cli-template/update.cjs +631 -0
- package/dist/core/_types_/active-directory-config.d.ts +24 -0
- package/dist/core/_types_/active-directory-config.d.ts.map +1 -0
- package/dist/core/_types_/active-directory-config.js +2 -0
- package/dist/core/_types_/active-directory-config.js.map +1 -0
- package/dist/core/bootstrap/init-config.d.ts.map +1 -1
- package/dist/core/bootstrap/init-config.js +14 -3
- package/dist/core/bootstrap/init-config.js.map +1 -1
- package/dist/core/bootstrap/startup-info.js +1 -1
- package/dist/core/bootstrap/startup-info.js.map +1 -1
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/init-mcp-server.js +1 -1
- package/dist/core/init-mcp-server.js.map +1 -1
- package/dist/core/token/gen-token-app/gen-token-server.d.ts.map +1 -1
- package/dist/core/token/gen-token-app/gen-token-server.js +85 -9
- package/dist/core/token/gen-token-app/gen-token-server.js.map +1 -1
- package/dist/core/token/gen-token-app/html.d.ts +8 -1
- package/dist/core/token/gen-token-app/html.d.ts.map +1 -1
- package/dist/core/token/gen-token-app/html.js +98 -2
- package/dist/core/token/gen-token-app/html.js.map +1 -1
- package/dist/core/token/gen-token-app/ntlm-auth-options.d.ts +4 -0
- package/dist/core/token/gen-token-app/ntlm-auth-options.d.ts.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-auth-options.js +94 -0
- package/dist/core/token/gen-token-app/ntlm-auth-options.js.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-domain-config.d.ts +16 -0
- package/dist/core/token/gen-token-app/ntlm-domain-config.d.ts.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-domain-config.js +71 -0
- package/dist/core/token/gen-token-app/ntlm-domain-config.js.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-integration.d.ts +3 -0
- package/dist/core/token/gen-token-app/ntlm-integration.d.ts.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-integration.js +69 -0
- package/dist/core/token/gen-token-app/ntlm-integration.js.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-session-storage.d.ts +16 -0
- package/dist/core/token/gen-token-app/ntlm-session-storage.d.ts.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-session-storage.js +74 -0
- package/dist/core/token/gen-token-app/ntlm-session-storage.js.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-templates.d.ts +21 -0
- package/dist/core/token/gen-token-app/ntlm-templates.d.ts.map +1 -0
- package/dist/core/token/gen-token-app/ntlm-templates.js +246 -0
- package/dist/core/token/gen-token-app/ntlm-templates.js.map +1 -0
- package/dist/core/token/{token.d.ts → token-auth.d.ts} +1 -1
- package/dist/core/token/token-auth.d.ts.map +1 -0
- package/dist/core/token/{token.js → token-auth.js} +4 -6
- package/dist/core/token/token-auth.js.map +1 -0
- package/dist/core/token/token-core.d.ts +5 -1
- package/dist/core/token/token-core.d.ts.map +1 -1
- package/dist/core/token/token-core.js +13 -3
- package/dist/core/token/token-core.js.map +1 -1
- package/dist/core/web/about-page/render.d.ts.map +1 -1
- package/dist/core/web/about-page/render.js +26 -3
- package/dist/core/web/about-page/render.js.map +1 -1
- package/dist/core/web/server-http.js +1 -1
- package/dist/core/web/server-http.js.map +1 -1
- package/package.json +10 -3
- package/dist/core/token/token.d.ts.map +0 -1
- 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 @@
|
|
|
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);
|