windows-exe-decompiler-mcp-server 0.1.0
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/CODEX_INSTALLATION.md +69 -0
- package/COPILOT_INSTALLATION.md +77 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/bin/windows-exe-decompiler-mcp-server.js +3 -0
- package/dist/analysis-provenance.d.ts +184 -0
- package/dist/analysis-provenance.js +74 -0
- package/dist/analysis-task-runner.d.ts +31 -0
- package/dist/analysis-task-runner.js +160 -0
- package/dist/artifact-inventory.d.ts +23 -0
- package/dist/artifact-inventory.js +175 -0
- package/dist/cache-manager.d.ts +128 -0
- package/dist/cache-manager.js +454 -0
- package/dist/confidence-semantics.d.ts +66 -0
- package/dist/confidence-semantics.js +122 -0
- package/dist/config.d.ts +335 -0
- package/dist/config.js +193 -0
- package/dist/database.d.ts +227 -0
- package/dist/database.js +601 -0
- package/dist/decompiler-worker.d.ts +441 -0
- package/dist/decompiler-worker.js +1962 -0
- package/dist/dynamic-trace.d.ts +95 -0
- package/dist/dynamic-trace.js +629 -0
- package/dist/env-validator.d.ts +15 -0
- package/dist/env-validator.js +249 -0
- package/dist/error-handler.d.ts +28 -0
- package/dist/error-handler.example.d.ts +22 -0
- package/dist/error-handler.example.js +141 -0
- package/dist/error-handler.js +139 -0
- package/dist/ghidra-analysis-status.d.ts +49 -0
- package/dist/ghidra-analysis-status.js +178 -0
- package/dist/ghidra-config.d.ts +134 -0
- package/dist/ghidra-config.js +464 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +200 -0
- package/dist/job-queue.d.ts +169 -0
- package/dist/job-queue.js +407 -0
- package/dist/logger.d.ts +106 -0
- package/dist/logger.js +176 -0
- package/dist/policy-guard.d.ts +115 -0
- package/dist/policy-guard.js +243 -0
- package/dist/process-output.d.ts +15 -0
- package/dist/process-output.js +90 -0
- package/dist/prompts/function-explanation-review.d.ts +5 -0
- package/dist/prompts/function-explanation-review.js +64 -0
- package/dist/prompts/semantic-name-review.d.ts +5 -0
- package/dist/prompts/semantic-name-review.js +63 -0
- package/dist/runtime-correlation.d.ts +34 -0
- package/dist/runtime-correlation.js +279 -0
- package/dist/runtime-paths.d.ts +3 -0
- package/dist/runtime-paths.js +11 -0
- package/dist/selection-diff.d.ts +667 -0
- package/dist/selection-diff.js +53 -0
- package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
- package/dist/semantic-name-suggestion-artifacts.js +314 -0
- package/dist/server.d.ts +129 -0
- package/dist/server.js +578 -0
- package/dist/tools/artifact-read.d.ts +235 -0
- package/dist/tools/artifact-read.js +317 -0
- package/dist/tools/artifacts-diff.d.ts +728 -0
- package/dist/tools/artifacts-diff.js +304 -0
- package/dist/tools/artifacts-list.d.ts +515 -0
- package/dist/tools/artifacts-list.js +389 -0
- package/dist/tools/attack-map.d.ts +290 -0
- package/dist/tools/attack-map.js +519 -0
- package/dist/tools/cache-observability.d.ts +4 -0
- package/dist/tools/cache-observability.js +36 -0
- package/dist/tools/code-function-cfg.d.ts +50 -0
- package/dist/tools/code-function-cfg.js +102 -0
- package/dist/tools/code-function-decompile.d.ts +55 -0
- package/dist/tools/code-function-decompile.js +103 -0
- package/dist/tools/code-function-disassemble.d.ts +43 -0
- package/dist/tools/code-function-disassemble.js +185 -0
- package/dist/tools/code-function-explain-apply.d.ts +255 -0
- package/dist/tools/code-function-explain-apply.js +225 -0
- package/dist/tools/code-function-explain-prepare.d.ts +535 -0
- package/dist/tools/code-function-explain-prepare.js +276 -0
- package/dist/tools/code-function-explain-review.d.ts +397 -0
- package/dist/tools/code-function-explain-review.js +589 -0
- package/dist/tools/code-function-rename-apply.d.ts +248 -0
- package/dist/tools/code-function-rename-apply.js +220 -0
- package/dist/tools/code-function-rename-prepare.d.ts +506 -0
- package/dist/tools/code-function-rename-prepare.js +279 -0
- package/dist/tools/code-function-rename-review.d.ts +574 -0
- package/dist/tools/code-function-rename-review.js +761 -0
- package/dist/tools/code-functions-list.d.ts +37 -0
- package/dist/tools/code-functions-list.js +91 -0
- package/dist/tools/code-functions-rank.d.ts +34 -0
- package/dist/tools/code-functions-rank.js +90 -0
- package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
- package/dist/tools/code-functions-reconstruct.js +2807 -0
- package/dist/tools/code-functions-search.d.ts +39 -0
- package/dist/tools/code-functions-search.js +90 -0
- package/dist/tools/code-reconstruct-export.d.ts +1212 -0
- package/dist/tools/code-reconstruct-export.js +4002 -0
- package/dist/tools/code-reconstruct-plan.d.ts +274 -0
- package/dist/tools/code-reconstruct-plan.js +342 -0
- package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
- package/dist/tools/dotnet-metadata-extract.js +355 -0
- package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
- package/dist/tools/dotnet-reconstruct-export.js +1151 -0
- package/dist/tools/dotnet-types-list.d.ts +325 -0
- package/dist/tools/dotnet-types-list.js +201 -0
- package/dist/tools/dynamic-dependencies.d.ts +115 -0
- package/dist/tools/dynamic-dependencies.js +213 -0
- package/dist/tools/dynamic-memory-import.d.ts +10 -0
- package/dist/tools/dynamic-memory-import.js +567 -0
- package/dist/tools/dynamic-trace-import.d.ts +10 -0
- package/dist/tools/dynamic-trace-import.js +235 -0
- package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
- package/dist/tools/entrypoint-fallback-disasm.js +89 -0
- package/dist/tools/ghidra-analyze.d.ts +88 -0
- package/dist/tools/ghidra-analyze.js +208 -0
- package/dist/tools/ghidra-health.d.ts +37 -0
- package/dist/tools/ghidra-health.js +212 -0
- package/dist/tools/ioc-export.d.ts +209 -0
- package/dist/tools/ioc-export.js +542 -0
- package/dist/tools/packer-detect.d.ts +165 -0
- package/dist/tools/packer-detect.js +284 -0
- package/dist/tools/pe-exports-extract.d.ts +175 -0
- package/dist/tools/pe-exports-extract.js +253 -0
- package/dist/tools/pe-fingerprint.d.ts +234 -0
- package/dist/tools/pe-fingerprint.js +269 -0
- package/dist/tools/pe-imports-extract.d.ts +105 -0
- package/dist/tools/pe-imports-extract.js +245 -0
- package/dist/tools/report-generate.d.ts +157 -0
- package/dist/tools/report-generate.js +457 -0
- package/dist/tools/report-summarize.d.ts +2131 -0
- package/dist/tools/report-summarize.js +596 -0
- package/dist/tools/runtime-detect.d.ts +135 -0
- package/dist/tools/runtime-detect.js +247 -0
- package/dist/tools/sample-ingest.d.ts +94 -0
- package/dist/tools/sample-ingest.js +327 -0
- package/dist/tools/sample-profile-get.d.ts +183 -0
- package/dist/tools/sample-profile-get.js +121 -0
- package/dist/tools/sandbox-execute.d.ts +441 -0
- package/dist/tools/sandbox-execute.js +392 -0
- package/dist/tools/strings-extract.d.ts +375 -0
- package/dist/tools/strings-extract.js +314 -0
- package/dist/tools/strings-floss-decode.d.ts +143 -0
- package/dist/tools/strings-floss-decode.js +259 -0
- package/dist/tools/system-health.d.ts +434 -0
- package/dist/tools/system-health.js +446 -0
- package/dist/tools/task-cancel.d.ts +21 -0
- package/dist/tools/task-cancel.js +70 -0
- package/dist/tools/task-status.d.ts +27 -0
- package/dist/tools/task-status.js +106 -0
- package/dist/tools/task-sweep.d.ts +22 -0
- package/dist/tools/task-sweep.js +77 -0
- package/dist/tools/tool-help.d.ts +340 -0
- package/dist/tools/tool-help.js +261 -0
- package/dist/tools/yara-scan.d.ts +554 -0
- package/dist/tools/yara-scan.js +313 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.js +41 -0
- package/dist/worker-pool.d.ts +204 -0
- package/dist/worker-pool.js +650 -0
- package/dist/workflows/deep-static.d.ts +104 -0
- package/dist/workflows/deep-static.js +276 -0
- package/dist/workflows/function-explanation-review.d.ts +655 -0
- package/dist/workflows/function-explanation-review.js +440 -0
- package/dist/workflows/reconstruct.d.ts +2053 -0
- package/dist/workflows/reconstruct.js +666 -0
- package/dist/workflows/semantic-name-review.d.ts +2418 -0
- package/dist/workflows/semantic-name-review.js +521 -0
- package/dist/workflows/triage.d.ts +659 -0
- package/dist/workflows/triage.js +1374 -0
- package/dist/workspace-manager.d.ts +150 -0
- package/dist/workspace-manager.js +411 -0
- package/ghidra_scripts/DecompileFunction.java +487 -0
- package/ghidra_scripts/DecompileFunction.py +150 -0
- package/ghidra_scripts/ExtractCFG.java +256 -0
- package/ghidra_scripts/ExtractCFG.py +233 -0
- package/ghidra_scripts/ExtractFunctions.java +442 -0
- package/ghidra_scripts/ExtractFunctions.py +101 -0
- package/ghidra_scripts/README.md +125 -0
- package/ghidra_scripts/SearchFunctionReferences.java +380 -0
- package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
- package/helpers/DotNetMetadataProbe/Program.cs +566 -0
- package/install-to-codex.ps1 +178 -0
- package/install-to-copilot.ps1 +303 -0
- package/package.json +101 -0
- package/requirements.txt +9 -0
- package/workers/requirements-dynamic.txt +11 -0
- package/workers/requirements.txt +8 -0
- package/workers/speakeasy_compat.py +175 -0
- package/workers/static_worker.py +5183 -0
- package/workers/yara_rules/default.yar +33 -0
- package/workers/yara_rules/malware_families.yar +93 -0
- package/workers/yara_rules/packers.yar +80 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server implementation
|
|
3
|
+
* Implements the Model Context Protocol with JSON-RPC 2.0 message handling
|
|
4
|
+
*/
|
|
5
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
|
+
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import pino from 'pino';
|
|
10
|
+
/**
|
|
11
|
+
* MCP Server class implementing the Model Context Protocol
|
|
12
|
+
*/
|
|
13
|
+
export class MCPServer {
|
|
14
|
+
server;
|
|
15
|
+
logger;
|
|
16
|
+
tools;
|
|
17
|
+
handlers;
|
|
18
|
+
prompts;
|
|
19
|
+
promptHandlers;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
// Create logger that writes to stderr to avoid interfering with MCP protocol on stdout
|
|
22
|
+
const destination = pino.destination({ dest: 2, sync: false }); // fd 2 = stderr
|
|
23
|
+
this.logger = pino({
|
|
24
|
+
level: config.logging.level,
|
|
25
|
+
}, destination);
|
|
26
|
+
this.tools = new Map();
|
|
27
|
+
this.handlers = new Map();
|
|
28
|
+
this.prompts = new Map();
|
|
29
|
+
this.promptHandlers = new Map();
|
|
30
|
+
// Initialize MCP SDK server
|
|
31
|
+
this.server = new Server({
|
|
32
|
+
name: 'windows-exe-decompiler-mcp-server',
|
|
33
|
+
version: '0.1.0',
|
|
34
|
+
}, {
|
|
35
|
+
capabilities: {
|
|
36
|
+
tools: {},
|
|
37
|
+
prompts: {},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
this.setupHandlers();
|
|
41
|
+
this.logger.info('MCP Server initialized');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Setup MCP protocol handlers
|
|
45
|
+
*/
|
|
46
|
+
setupHandlers() {
|
|
47
|
+
// Handle tools/list request
|
|
48
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
49
|
+
this.logger.debug('Handling tools/list request');
|
|
50
|
+
return {
|
|
51
|
+
tools: await this.listTools(),
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
55
|
+
this.logger.debug('Handling prompts/list request');
|
|
56
|
+
return {
|
|
57
|
+
prompts: await this.listPrompts(),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
61
|
+
this.logger.debug({ prompt: request.params.name }, 'Handling prompts/get request');
|
|
62
|
+
return (await this.getPrompt(request.params.name, request.params.arguments || {}));
|
|
63
|
+
});
|
|
64
|
+
// Handle tools/call request
|
|
65
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
66
|
+
this.logger.debug({ tool: request.params.name }, 'Handling tools/call request');
|
|
67
|
+
const result = await this.callTool(request.params.name, request.params.arguments || {});
|
|
68
|
+
return result;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Register a tool with its definition and handler
|
|
73
|
+
*/
|
|
74
|
+
registerTool(definition, handler) {
|
|
75
|
+
this.logger.info({ tool: definition.name }, 'Registering tool');
|
|
76
|
+
this.tools.set(definition.name, definition);
|
|
77
|
+
this.handlers.set(definition.name, handler);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Register a prompt with its definition and handler
|
|
81
|
+
*/
|
|
82
|
+
registerPrompt(definition, handler) {
|
|
83
|
+
this.logger.info({ prompt: definition.name }, 'Registering prompt');
|
|
84
|
+
this.prompts.set(definition.name, definition);
|
|
85
|
+
this.promptHandlers.set(definition.name, handler);
|
|
86
|
+
}
|
|
87
|
+
getToolDefinitions() {
|
|
88
|
+
return Array.from(this.tools.values());
|
|
89
|
+
}
|
|
90
|
+
getToolDefinition(name) {
|
|
91
|
+
return this.tools.get(name);
|
|
92
|
+
}
|
|
93
|
+
getPromptDefinitions() {
|
|
94
|
+
return Array.from(this.prompts.values());
|
|
95
|
+
}
|
|
96
|
+
getPromptDefinition(name) {
|
|
97
|
+
return this.prompts.get(name);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* List all available tools (MCP protocol method)
|
|
101
|
+
*/
|
|
102
|
+
async listTools() {
|
|
103
|
+
const tools = [];
|
|
104
|
+
for (const [name, definition] of this.tools.entries()) {
|
|
105
|
+
// Convert Zod schema to JSON Schema format for MCP protocol
|
|
106
|
+
const inputSchema = this.zodToJsonSchema(definition.inputSchema);
|
|
107
|
+
tools.push({
|
|
108
|
+
name,
|
|
109
|
+
description: definition.description,
|
|
110
|
+
inputSchema: inputSchema,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
this.logger.debug({ count: tools.length }, 'Listed tools');
|
|
114
|
+
return tools;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* List all available prompts (MCP protocol method)
|
|
118
|
+
*/
|
|
119
|
+
async listPrompts() {
|
|
120
|
+
const prompts = [];
|
|
121
|
+
for (const [name, definition] of this.prompts.entries()) {
|
|
122
|
+
prompts.push({
|
|
123
|
+
name,
|
|
124
|
+
title: definition.title,
|
|
125
|
+
description: definition.description,
|
|
126
|
+
arguments: definition.arguments?.map((item) => ({
|
|
127
|
+
name: item.name,
|
|
128
|
+
description: item.description,
|
|
129
|
+
required: item.required,
|
|
130
|
+
})),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
this.logger.debug({ count: prompts.length }, 'Listed prompts');
|
|
134
|
+
return prompts;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Convert Zod schema to JSON Schema format
|
|
138
|
+
* Basic implementation for common Zod types
|
|
139
|
+
*/
|
|
140
|
+
zodToJsonSchema(schema) {
|
|
141
|
+
const converted = this.zodFieldToJsonSchema(schema);
|
|
142
|
+
if (converted && typeof converted === 'object') {
|
|
143
|
+
return converted;
|
|
144
|
+
}
|
|
145
|
+
return { type: 'object', properties: {} };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Determine whether a field is required in object schema.
|
|
149
|
+
* Optional/default/catch wrappers should not be marked as required.
|
|
150
|
+
*/
|
|
151
|
+
isFieldRequired(schema) {
|
|
152
|
+
if (schema instanceof z.ZodOptional) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (schema instanceof z.ZodDefault) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (schema instanceof z.ZodCatch) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
if (schema instanceof z.ZodEffects) {
|
|
162
|
+
return this.isFieldRequired(schema._def.schema);
|
|
163
|
+
}
|
|
164
|
+
if (schema instanceof z.ZodNullable) {
|
|
165
|
+
return this.isFieldRequired(schema._def.innerType);
|
|
166
|
+
}
|
|
167
|
+
if (schema instanceof z.ZodBranded) {
|
|
168
|
+
return this.isFieldRequired(schema._def.type);
|
|
169
|
+
}
|
|
170
|
+
if (schema instanceof z.ZodReadonly) {
|
|
171
|
+
return this.isFieldRequired(schema._def.innerType);
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Attach schema description when available.
|
|
177
|
+
*/
|
|
178
|
+
withDescription(jsonSchema, schema) {
|
|
179
|
+
if (schema.description) {
|
|
180
|
+
return {
|
|
181
|
+
...jsonSchema,
|
|
182
|
+
description: schema.description,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return jsonSchema;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Convert Zod field schema to JSON Schema property
|
|
189
|
+
*/
|
|
190
|
+
zodFieldToJsonSchema(schema) {
|
|
191
|
+
// Handle optional
|
|
192
|
+
if (schema instanceof z.ZodOptional) {
|
|
193
|
+
return this.zodFieldToJsonSchema(schema._def.innerType);
|
|
194
|
+
}
|
|
195
|
+
// Handle nullable
|
|
196
|
+
if (schema instanceof z.ZodNullable) {
|
|
197
|
+
const innerSchema = this.zodFieldToJsonSchema(schema._def.innerType);
|
|
198
|
+
return this.withDescription({
|
|
199
|
+
anyOf: [innerSchema, { type: 'null' }],
|
|
200
|
+
}, schema);
|
|
201
|
+
}
|
|
202
|
+
// Handle defaults
|
|
203
|
+
if (schema instanceof z.ZodDefault) {
|
|
204
|
+
const innerSchema = this.zodFieldToJsonSchema(schema._def.innerType);
|
|
205
|
+
try {
|
|
206
|
+
return this.withDescription({
|
|
207
|
+
...innerSchema,
|
|
208
|
+
default: schema._def.defaultValue(),
|
|
209
|
+
}, schema);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return this.withDescription(innerSchema, schema);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Handle catch fallback values
|
|
216
|
+
if (schema instanceof z.ZodCatch) {
|
|
217
|
+
return this.zodFieldToJsonSchema(schema._def.innerType);
|
|
218
|
+
}
|
|
219
|
+
// Handle effects/transform wrappers
|
|
220
|
+
if (schema instanceof z.ZodEffects) {
|
|
221
|
+
return this.zodFieldToJsonSchema(schema._def.schema);
|
|
222
|
+
}
|
|
223
|
+
// Handle branded types
|
|
224
|
+
if (schema instanceof z.ZodBranded) {
|
|
225
|
+
return this.zodFieldToJsonSchema(schema._def.type);
|
|
226
|
+
}
|
|
227
|
+
// Handle readonly wrapper
|
|
228
|
+
if (schema instanceof z.ZodReadonly) {
|
|
229
|
+
return this.zodFieldToJsonSchema(schema._def.innerType);
|
|
230
|
+
}
|
|
231
|
+
// Handle string
|
|
232
|
+
if (schema instanceof z.ZodString) {
|
|
233
|
+
return this.withDescription({ type: 'string' }, schema);
|
|
234
|
+
}
|
|
235
|
+
// Handle number
|
|
236
|
+
if (schema instanceof z.ZodNumber) {
|
|
237
|
+
return this.withDescription({ type: 'number' }, schema);
|
|
238
|
+
}
|
|
239
|
+
// Handle boolean
|
|
240
|
+
if (schema instanceof z.ZodBoolean) {
|
|
241
|
+
return this.withDescription({ type: 'boolean' }, schema);
|
|
242
|
+
}
|
|
243
|
+
// Handle array
|
|
244
|
+
if (schema instanceof z.ZodArray) {
|
|
245
|
+
return this.withDescription({
|
|
246
|
+
type: 'array',
|
|
247
|
+
items: this.zodFieldToJsonSchema(schema._def.type),
|
|
248
|
+
}, schema);
|
|
249
|
+
}
|
|
250
|
+
// Handle enum
|
|
251
|
+
if (schema instanceof z.ZodEnum) {
|
|
252
|
+
return this.withDescription({
|
|
253
|
+
type: 'string',
|
|
254
|
+
enum: schema._def.values,
|
|
255
|
+
}, schema);
|
|
256
|
+
}
|
|
257
|
+
// Handle literal
|
|
258
|
+
if (schema instanceof z.ZodLiteral) {
|
|
259
|
+
const literalValue = schema._def.value;
|
|
260
|
+
const literalType = literalValue === null ? 'null' : typeof literalValue;
|
|
261
|
+
return this.withDescription({
|
|
262
|
+
type: literalType,
|
|
263
|
+
const: literalValue,
|
|
264
|
+
}, schema);
|
|
265
|
+
}
|
|
266
|
+
// Handle object
|
|
267
|
+
if (schema instanceof z.ZodObject) {
|
|
268
|
+
const shape = schema.shape;
|
|
269
|
+
const properties = {};
|
|
270
|
+
const required = [];
|
|
271
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
272
|
+
properties[key] = this.zodFieldToJsonSchema(fieldSchema);
|
|
273
|
+
if (this.isFieldRequired(fieldSchema)) {
|
|
274
|
+
required.push(key);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return this.withDescription({
|
|
278
|
+
type: 'object',
|
|
279
|
+
properties,
|
|
280
|
+
...(required.length > 0 ? { required } : {}),
|
|
281
|
+
}, schema);
|
|
282
|
+
}
|
|
283
|
+
// Handle union
|
|
284
|
+
if (schema instanceof z.ZodUnion) {
|
|
285
|
+
const options = schema._def.options;
|
|
286
|
+
return this.withDescription({
|
|
287
|
+
anyOf: options.map((option) => this.zodFieldToJsonSchema(option)),
|
|
288
|
+
}, schema);
|
|
289
|
+
}
|
|
290
|
+
// Handle discriminated union
|
|
291
|
+
if (schema instanceof z.ZodDiscriminatedUnion) {
|
|
292
|
+
const options = Array.from(schema.options.values());
|
|
293
|
+
return this.withDescription({
|
|
294
|
+
anyOf: options.map((option) => this.zodFieldToJsonSchema(option)),
|
|
295
|
+
}, schema);
|
|
296
|
+
}
|
|
297
|
+
// Handle record
|
|
298
|
+
if (schema instanceof z.ZodRecord) {
|
|
299
|
+
return this.withDescription({
|
|
300
|
+
type: 'object',
|
|
301
|
+
additionalProperties: this.zodFieldToJsonSchema(schema._def.valueType),
|
|
302
|
+
}, schema);
|
|
303
|
+
}
|
|
304
|
+
// Handle tuple
|
|
305
|
+
if (schema instanceof z.ZodTuple) {
|
|
306
|
+
return this.withDescription({
|
|
307
|
+
type: 'array',
|
|
308
|
+
items: schema._def.items.map((item) => this.zodFieldToJsonSchema(item)),
|
|
309
|
+
}, schema);
|
|
310
|
+
}
|
|
311
|
+
// Default
|
|
312
|
+
return this.withDescription({ type: 'string' }, schema);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Call a tool by name with arguments (MCP protocol method)
|
|
316
|
+
*/
|
|
317
|
+
async callTool(name, args) {
|
|
318
|
+
const startTime = Date.now();
|
|
319
|
+
this.logger.info({ tool: name, args }, 'Calling tool');
|
|
320
|
+
try {
|
|
321
|
+
// Check if tool exists
|
|
322
|
+
const definition = this.tools.get(name);
|
|
323
|
+
if (!definition) {
|
|
324
|
+
throw new Error(`Tool not found: ${name}`);
|
|
325
|
+
}
|
|
326
|
+
// Validate input arguments
|
|
327
|
+
const validatedArgs = this.validateArgs(definition.inputSchema, args);
|
|
328
|
+
// Get handler
|
|
329
|
+
const handler = this.handlers.get(name);
|
|
330
|
+
if (!handler) {
|
|
331
|
+
throw new Error(`Handler not found for tool: ${name}`);
|
|
332
|
+
}
|
|
333
|
+
// Execute handler
|
|
334
|
+
const result = await handler(validatedArgs);
|
|
335
|
+
const elapsed = Date.now() - startTime;
|
|
336
|
+
// Check if result is ToolResult or WorkerResult
|
|
337
|
+
if ('content' in result) {
|
|
338
|
+
// It's a ToolResult - use directly
|
|
339
|
+
this.logger.info({ tool: name, elapsed, isError: result.isError }, 'Tool execution completed');
|
|
340
|
+
return {
|
|
341
|
+
content: result.content, // MCP SDK Content type
|
|
342
|
+
isError: result.isError
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
// It's a WorkerResult - convert to ToolResult
|
|
347
|
+
this.logger.info({ tool: name, elapsed, ok: result.ok }, 'Tool execution completed');
|
|
348
|
+
return this.workerResultToToolResult(result);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
const elapsed = Date.now() - startTime;
|
|
353
|
+
this.logger.error({ tool: name, elapsed, error }, 'Tool execution failed');
|
|
354
|
+
return {
|
|
355
|
+
content: [
|
|
356
|
+
{
|
|
357
|
+
type: 'text',
|
|
358
|
+
text: JSON.stringify({
|
|
359
|
+
ok: false,
|
|
360
|
+
errors: [error.message],
|
|
361
|
+
}),
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
isError: true,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Resolve a prompt by name and arguments (MCP protocol method)
|
|
370
|
+
*/
|
|
371
|
+
async getPrompt(name, args) {
|
|
372
|
+
const definition = this.prompts.get(name);
|
|
373
|
+
if (!definition) {
|
|
374
|
+
throw new Error(`Prompt not found: ${name}`);
|
|
375
|
+
}
|
|
376
|
+
const handler = this.promptHandlers.get(name);
|
|
377
|
+
if (!handler) {
|
|
378
|
+
throw new Error(`Handler not found for prompt: ${name}`);
|
|
379
|
+
}
|
|
380
|
+
const validatedArgs = this.validatePromptArgs(definition, args);
|
|
381
|
+
return handler(validatedArgs);
|
|
382
|
+
}
|
|
383
|
+
validatePromptArgs(definition, args) {
|
|
384
|
+
const validated = {};
|
|
385
|
+
const provided = args || {};
|
|
386
|
+
for (const [key, value] of Object.entries(provided)) {
|
|
387
|
+
if (value === undefined || value === null) {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
validated[key] = String(value);
|
|
391
|
+
}
|
|
392
|
+
for (const item of definition.arguments || []) {
|
|
393
|
+
if (item.required && (!validated[item.name] || validated[item.name].trim().length === 0)) {
|
|
394
|
+
throw new Error(`Missing required prompt argument: ${item.name}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return validated;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Validate tool arguments against schema
|
|
401
|
+
* Provides clear error messages with field paths and validation details
|
|
402
|
+
*/
|
|
403
|
+
validateArgs(schema, args) {
|
|
404
|
+
try {
|
|
405
|
+
return schema.parse(args);
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
if (error instanceof z.ZodError) {
|
|
409
|
+
// Build detailed validation error message
|
|
410
|
+
const errorDetails = error.errors.map((e) => {
|
|
411
|
+
const path = e.path.length > 0 ? e.path.join('.') : 'root';
|
|
412
|
+
return ` - ${path}: ${e.message}`;
|
|
413
|
+
});
|
|
414
|
+
// Generate example based on schema
|
|
415
|
+
const example = this.generateSchemaExample(schema);
|
|
416
|
+
const exampleStr = example ? `\n\nExample:\n${JSON.stringify(example, null, 2)}` : '';
|
|
417
|
+
throw new Error(`Invalid arguments:\n${errorDetails.join('\n')}${exampleStr}`);
|
|
418
|
+
}
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Generate an example object from a Zod schema
|
|
424
|
+
* Helps users understand the expected input format
|
|
425
|
+
*/
|
|
426
|
+
generateSchemaExample(schema) {
|
|
427
|
+
try {
|
|
428
|
+
// Handle ZodObject
|
|
429
|
+
if (schema instanceof z.ZodObject) {
|
|
430
|
+
const shape = schema.shape;
|
|
431
|
+
const example = {};
|
|
432
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
433
|
+
example[key] = this.generateFieldExample(fieldSchema);
|
|
434
|
+
}
|
|
435
|
+
return example;
|
|
436
|
+
}
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Generate an example value for a specific field schema
|
|
445
|
+
*/
|
|
446
|
+
generateFieldExample(schema) {
|
|
447
|
+
// Handle optional fields
|
|
448
|
+
if (schema instanceof z.ZodOptional) {
|
|
449
|
+
return this.generateFieldExample(schema._def.innerType);
|
|
450
|
+
}
|
|
451
|
+
// Handle nullable fields
|
|
452
|
+
if (schema instanceof z.ZodNullable) {
|
|
453
|
+
return this.generateFieldExample(schema._def.innerType);
|
|
454
|
+
}
|
|
455
|
+
// Handle default values
|
|
456
|
+
if (schema instanceof z.ZodDefault) {
|
|
457
|
+
return schema._def.defaultValue();
|
|
458
|
+
}
|
|
459
|
+
// Handle string
|
|
460
|
+
if (schema instanceof z.ZodString) {
|
|
461
|
+
return 'string';
|
|
462
|
+
}
|
|
463
|
+
// Handle number
|
|
464
|
+
if (schema instanceof z.ZodNumber) {
|
|
465
|
+
return 0;
|
|
466
|
+
}
|
|
467
|
+
// Handle boolean
|
|
468
|
+
if (schema instanceof z.ZodBoolean) {
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
// Handle array
|
|
472
|
+
if (schema instanceof z.ZodArray) {
|
|
473
|
+
const elementExample = this.generateFieldExample(schema._def.type);
|
|
474
|
+
return [elementExample];
|
|
475
|
+
}
|
|
476
|
+
// Handle object
|
|
477
|
+
if (schema instanceof z.ZodObject) {
|
|
478
|
+
const shape = schema.shape;
|
|
479
|
+
const example = {};
|
|
480
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
481
|
+
example[key] = this.generateFieldExample(fieldSchema);
|
|
482
|
+
}
|
|
483
|
+
return example;
|
|
484
|
+
}
|
|
485
|
+
// Handle enum
|
|
486
|
+
if (schema instanceof z.ZodEnum) {
|
|
487
|
+
const values = schema._def.values;
|
|
488
|
+
return values[0];
|
|
489
|
+
}
|
|
490
|
+
// Handle literal
|
|
491
|
+
if (schema instanceof z.ZodLiteral) {
|
|
492
|
+
return schema._def.value;
|
|
493
|
+
}
|
|
494
|
+
// Handle union
|
|
495
|
+
if (schema instanceof z.ZodUnion) {
|
|
496
|
+
const options = schema._def.options;
|
|
497
|
+
return this.generateFieldExample(options[0]);
|
|
498
|
+
}
|
|
499
|
+
// Default fallback
|
|
500
|
+
return 'value';
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Convert worker result to MCP tool result
|
|
504
|
+
*/
|
|
505
|
+
workerResultToToolResult(result) {
|
|
506
|
+
const content = [];
|
|
507
|
+
// Add text representation
|
|
508
|
+
content.push({
|
|
509
|
+
type: 'text',
|
|
510
|
+
text: JSON.stringify({
|
|
511
|
+
ok: result.ok,
|
|
512
|
+
data: result.data,
|
|
513
|
+
warnings: result.warnings,
|
|
514
|
+
errors: result.errors,
|
|
515
|
+
artifacts: result.artifacts,
|
|
516
|
+
metrics: result.metrics,
|
|
517
|
+
}, null, 2),
|
|
518
|
+
});
|
|
519
|
+
return {
|
|
520
|
+
content,
|
|
521
|
+
isError: !result.ok,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Start the MCP server with stdio transport
|
|
526
|
+
*/
|
|
527
|
+
async start() {
|
|
528
|
+
this.logger.info('Starting MCP Server with stdio transport');
|
|
529
|
+
const transport = new StdioServerTransport();
|
|
530
|
+
await this.server.connect(transport);
|
|
531
|
+
this.logger.info('MCP Server started and listening on stdio');
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Stop the MCP server
|
|
535
|
+
*/
|
|
536
|
+
async stop() {
|
|
537
|
+
this.logger.info('Stopping MCP Server');
|
|
538
|
+
await this.server.close();
|
|
539
|
+
this.logger.info('MCP Server stopped');
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Get server instance for testing
|
|
543
|
+
*/
|
|
544
|
+
getServer() {
|
|
545
|
+
return this.server;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Get connected client capabilities after MCP initialization.
|
|
549
|
+
*/
|
|
550
|
+
getClientCapabilities() {
|
|
551
|
+
return this.server.getClientCapabilities();
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Get connected client implementation info after MCP initialization.
|
|
555
|
+
*/
|
|
556
|
+
getClientVersion() {
|
|
557
|
+
return this.server.getClientVersion();
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Whether the connected MCP client advertised sampling support.
|
|
561
|
+
*/
|
|
562
|
+
supportsSampling() {
|
|
563
|
+
return Boolean(this.getClientCapabilities()?.sampling);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Request client-mediated MCP sampling from the connected client.
|
|
567
|
+
*/
|
|
568
|
+
async createMessage(params) {
|
|
569
|
+
return this.server.createMessage(params);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Get logger instance
|
|
573
|
+
*/
|
|
574
|
+
getLogger() {
|
|
575
|
+
return this.logger;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
//# sourceMappingURL=server.js.map
|