ccjk 2.4.4 → 2.6.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/dist/chunks/api-providers.mjs +73 -1
- package/dist/chunks/ccjk-config.mjs +13 -77
- package/dist/chunks/ccr.mjs +9 -4
- package/dist/chunks/check-updates.mjs +4 -2
- package/dist/chunks/claude-code-config-manager.mjs +9 -15
- package/dist/chunks/claude-code-incremental-manager.mjs +5 -8
- package/dist/chunks/codex.mjs +10 -569
- package/dist/chunks/config-switch.mjs +7 -5
- package/dist/chunks/config.mjs +573 -0
- package/dist/chunks/config2.mjs +454 -0
- package/dist/chunks/doctor.mjs +89 -1
- package/dist/chunks/features.mjs +13 -10
- package/dist/chunks/index.mjs +10 -1164
- package/dist/chunks/index2.mjs +10 -2
- package/dist/chunks/init.mjs +14 -11
- package/dist/chunks/json-config.mjs +59 -0
- package/dist/chunks/mcp-server.mjs +776 -0
- package/dist/chunks/mcp.mjs +10 -8
- package/dist/chunks/menu.mjs +5 -5
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/permissions.mjs +428 -0
- package/dist/chunks/prompts.mjs +2 -1
- package/dist/chunks/providers.mjs +261 -0
- package/dist/chunks/session.mjs +484 -41
- package/dist/chunks/skills.mjs +553 -0
- package/dist/chunks/stats.mjs +411 -0
- package/dist/chunks/uninstall.mjs +4 -3
- package/dist/chunks/update.mjs +6 -3
- package/dist/chunks/workflows2.mjs +140 -0
- package/dist/cli.mjs +290 -10
- package/dist/i18n/locales/en/hooks.json +47 -0
- package/dist/i18n/locales/en/mcp.json +55 -0
- package/dist/i18n/locales/en/permissions.json +43 -0
- package/dist/i18n/locales/en/registry.json +54 -0
- package/dist/i18n/locales/en/sandbox.json +44 -0
- package/dist/i18n/locales/en/skills.json +89 -129
- package/dist/i18n/locales/en/stats.json +20 -0
- package/dist/i18n/locales/zh-CN/hooks.json +47 -0
- package/dist/i18n/locales/zh-CN/mcp.json +55 -0
- package/dist/i18n/locales/zh-CN/permissions.json +43 -0
- package/dist/i18n/locales/zh-CN/registry.json +54 -0
- package/dist/i18n/locales/zh-CN/sandbox.json +44 -0
- package/dist/i18n/locales/zh-CN/skills.json +88 -128
- package/dist/i18n/locales/zh-CN/stats.json +20 -0
- package/dist/index.mjs +12 -8
- package/dist/shared/ccjk.B-lZxV2u.mjs +1162 -0
- package/dist/shared/{ccjk.CURU8gbR.mjs → ccjk.CUdzQluX.mjs} +1 -1
- package/dist/shared/{ccjk.ByTIGCUC.mjs → ccjk.Dut3wyoP.mjs} +1 -1
- package/dist/shared/ccjk.J8YiPsOw.mjs +259 -0
- package/dist/shared/{ccjk.CGTmRqsu.mjs → ccjk.rLRHmcqD.mjs} +5 -134
- package/dist/shared/{ccjk.QbS8EAOd.mjs → ccjk.uVUeWAt8.mjs} +2 -1
- package/package.json +1 -1
- package/templates/common/skills/code-review.md +343 -0
- package/templates/common/skills/summarize.md +312 -0
- package/templates/common/skills/translate.md +202 -0
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import process__default from 'node:process';
|
|
3
|
+
|
|
4
|
+
const MCP_TOOLS = [
|
|
5
|
+
{
|
|
6
|
+
name: "ccjk_chat",
|
|
7
|
+
description: "Send a message to Claude via CCJK and get a response. Supports custom provider and model selection.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
message: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "The message to send to Claude"
|
|
14
|
+
},
|
|
15
|
+
provider: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: 'Optional API provider name (e.g., "302ai", "openai", "anthropic")'
|
|
18
|
+
},
|
|
19
|
+
model: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: 'Optional model name (e.g., "claude-3-5-sonnet-20241022", "gpt-4")'
|
|
22
|
+
},
|
|
23
|
+
systemPrompt: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Optional system prompt to guide the AI response"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
required: ["message"]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "ccjk_providers",
|
|
33
|
+
description: "List all available API providers configured in CCJK",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "ccjk_stats",
|
|
41
|
+
description: "Get usage statistics from CCJK (requires CCusage tool)",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
period: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Time period for statistics (7d, 30d, all)",
|
|
48
|
+
enum: ["7d", "30d", "all"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "ccjk_workflows",
|
|
55
|
+
description: "List available workflows in CCJK",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
category: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: "Filter by workflow category (git, sixStep, common-tools, etc.)"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "ccjk_mcp_services",
|
|
68
|
+
description: "List configured MCP services in Claude Code",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
detailed: {
|
|
73
|
+
type: "boolean",
|
|
74
|
+
description: "Include detailed configuration information"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "ccjk_config",
|
|
81
|
+
description: "Get or set CCJK configuration values",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
action: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "Action to perform (get, set, list)",
|
|
88
|
+
enum: ["get", "set", "list"]
|
|
89
|
+
},
|
|
90
|
+
key: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "Configuration key (for get/set actions)"
|
|
93
|
+
},
|
|
94
|
+
value: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Configuration value (for set action)"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
required: ["action"]
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "ccjk_init",
|
|
104
|
+
description: "Initialize or update Claude Code configuration via CCJK",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
mode: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "Initialization mode (full, workflows-only, api-only)",
|
|
111
|
+
enum: ["full", "workflows-only", "api-only"]
|
|
112
|
+
},
|
|
113
|
+
force: {
|
|
114
|
+
type: "boolean",
|
|
115
|
+
description: "Force overwrite existing configuration"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "ccjk_doctor",
|
|
122
|
+
description: "Run CCJK health check and diagnostics",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
verbose: {
|
|
127
|
+
type: "boolean",
|
|
128
|
+
description: "Show detailed diagnostic information"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
];
|
|
134
|
+
function getToolByName(name) {
|
|
135
|
+
return MCP_TOOLS.find((tool) => tool.name === name);
|
|
136
|
+
}
|
|
137
|
+
function validateToolInput(tool, input) {
|
|
138
|
+
const errors = [];
|
|
139
|
+
if (tool.inputSchema.required) {
|
|
140
|
+
for (const field of tool.inputSchema.required) {
|
|
141
|
+
if (!(field in input)) {
|
|
142
|
+
errors.push(`Missing required field: ${field}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const [key, value] of Object.entries(input)) {
|
|
147
|
+
const schema = tool.inputSchema.properties[key];
|
|
148
|
+
if (!schema) {
|
|
149
|
+
errors.push(`Unknown field: ${key}`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (schema.type === "string" && typeof value !== "string") {
|
|
153
|
+
errors.push(`Field ${key} must be a string`);
|
|
154
|
+
} else if (schema.type === "boolean" && typeof value !== "boolean") {
|
|
155
|
+
errors.push(`Field ${key} must be a boolean`);
|
|
156
|
+
} else if (schema.type === "number" && typeof value !== "number") {
|
|
157
|
+
errors.push(`Field ${key} must be a number`);
|
|
158
|
+
}
|
|
159
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
160
|
+
errors.push(`Field ${key} must be one of: ${schema.enum.join(", ")}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
valid: errors.length === 0,
|
|
165
|
+
errors: errors.length > 0 ? errors : void 0
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
class MCPHandler {
|
|
170
|
+
/**
|
|
171
|
+
* Handle a tool call from MCP client
|
|
172
|
+
*/
|
|
173
|
+
async handleToolCall(toolName, args) {
|
|
174
|
+
const tool = getToolByName(toolName);
|
|
175
|
+
if (!tool) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: `Unknown tool: ${toolName}`
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const validation = validateToolInput(tool, args);
|
|
182
|
+
if (!validation.valid) {
|
|
183
|
+
return {
|
|
184
|
+
success: false,
|
|
185
|
+
error: `Invalid input: ${validation.errors?.join(", ")}`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
switch (toolName) {
|
|
190
|
+
case "ccjk_chat":
|
|
191
|
+
return await this.handleChat(args);
|
|
192
|
+
case "ccjk_providers":
|
|
193
|
+
return await this.handleProviders(args);
|
|
194
|
+
case "ccjk_stats":
|
|
195
|
+
return await this.handleStats(args);
|
|
196
|
+
case "ccjk_workflows":
|
|
197
|
+
return await this.handleWorkflows(args);
|
|
198
|
+
case "ccjk_mcp_services":
|
|
199
|
+
return await this.handleMcpServices(args);
|
|
200
|
+
case "ccjk_config":
|
|
201
|
+
return await this.handleConfig(args);
|
|
202
|
+
case "ccjk_init":
|
|
203
|
+
return await this.handleInit(args);
|
|
204
|
+
case "ccjk_doctor":
|
|
205
|
+
return await this.handleDoctor(args);
|
|
206
|
+
default:
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
error: `Tool not implemented: ${toolName}`
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
error: error instanceof Error ? error.message : String(error)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Handle ccjk_chat tool
|
|
221
|
+
*/
|
|
222
|
+
async handleChat(args) {
|
|
223
|
+
const { message, provider, model } = args;
|
|
224
|
+
return {
|
|
225
|
+
success: true,
|
|
226
|
+
data: {
|
|
227
|
+
response: `[CCJK MCP] Received message: "${message}"`,
|
|
228
|
+
provider: provider || "default",
|
|
229
|
+
model: model || "default",
|
|
230
|
+
note: "This is a placeholder. Actual Claude API integration pending."
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Handle ccjk_providers tool
|
|
236
|
+
*/
|
|
237
|
+
async handleProviders(_args) {
|
|
238
|
+
try {
|
|
239
|
+
const { readMcpConfig } = await import('./config.mjs').then(function (n) { return n.C; });
|
|
240
|
+
const config = readMcpConfig();
|
|
241
|
+
const providers = [];
|
|
242
|
+
if (config?.mcpServers) {
|
|
243
|
+
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
244
|
+
providers.push({
|
|
245
|
+
name,
|
|
246
|
+
command: serverConfig.command,
|
|
247
|
+
args: serverConfig.args,
|
|
248
|
+
env: serverConfig.env
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
data: {
|
|
255
|
+
providers,
|
|
256
|
+
count: providers.length
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
success: false,
|
|
262
|
+
error: `Failed to read providers: ${error instanceof Error ? error.message : String(error)}`
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Handle ccjk_stats tool
|
|
268
|
+
*/
|
|
269
|
+
async handleStats(args) {
|
|
270
|
+
const { period = "all" } = args;
|
|
271
|
+
try {
|
|
272
|
+
const { x } = await import('tinyexec');
|
|
273
|
+
const result = await x("ccusage", ["--json", "--period", period]);
|
|
274
|
+
if (result.exitCode !== 0) {
|
|
275
|
+
return {
|
|
276
|
+
success: false,
|
|
277
|
+
error: "CCusage tool not available or failed"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const stats = JSON.parse(result.stdout);
|
|
281
|
+
return {
|
|
282
|
+
success: true,
|
|
283
|
+
data: stats
|
|
284
|
+
};
|
|
285
|
+
} catch (error) {
|
|
286
|
+
return {
|
|
287
|
+
success: false,
|
|
288
|
+
error: `Failed to get stats: ${error instanceof Error ? error.message : String(error)}`
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Handle ccjk_workflows tool
|
|
294
|
+
*/
|
|
295
|
+
async handleWorkflows(args) {
|
|
296
|
+
const { category } = args;
|
|
297
|
+
try {
|
|
298
|
+
const { getWorkflowConfigs } = await import('./workflows2.mjs');
|
|
299
|
+
let workflows = getWorkflowConfigs();
|
|
300
|
+
if (category) {
|
|
301
|
+
workflows = workflows.filter((w) => w.category === category);
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
success: true,
|
|
305
|
+
data: {
|
|
306
|
+
workflows: workflows.map((w) => ({
|
|
307
|
+
id: w.id,
|
|
308
|
+
name: w.name,
|
|
309
|
+
category: w.category,
|
|
310
|
+
description: w.description
|
|
311
|
+
})),
|
|
312
|
+
count: workflows.length
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
} catch (error) {
|
|
316
|
+
return {
|
|
317
|
+
success: false,
|
|
318
|
+
error: `Failed to list workflows: ${error instanceof Error ? error.message : String(error)}`
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Handle ccjk_mcp_services tool
|
|
324
|
+
*/
|
|
325
|
+
async handleMcpServices(args) {
|
|
326
|
+
const { detailed = false } = args;
|
|
327
|
+
try {
|
|
328
|
+
const { readMcpConfig } = await import('./config.mjs').then(function (n) { return n.C; });
|
|
329
|
+
const config = await readMcpConfig();
|
|
330
|
+
const services = config?.mcpServers || {};
|
|
331
|
+
const serviceList = Object.entries(services).map(([name, config2]) => {
|
|
332
|
+
if (detailed) {
|
|
333
|
+
return { name, config: config2 };
|
|
334
|
+
}
|
|
335
|
+
return { name };
|
|
336
|
+
});
|
|
337
|
+
return {
|
|
338
|
+
success: true,
|
|
339
|
+
data: {
|
|
340
|
+
services: serviceList,
|
|
341
|
+
count: serviceList.length
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
} catch (error) {
|
|
345
|
+
return {
|
|
346
|
+
success: false,
|
|
347
|
+
error: `Failed to list MCP services: ${error instanceof Error ? error.message : String(error)}`
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Handle ccjk_config tool
|
|
353
|
+
*/
|
|
354
|
+
async handleConfig(args) {
|
|
355
|
+
const { action, key, value } = args;
|
|
356
|
+
try {
|
|
357
|
+
const { readZcfConfig, updateZcfConfig } = await import('./ccjk-config.mjs');
|
|
358
|
+
if (action === "list") {
|
|
359
|
+
const config = await readZcfConfig();
|
|
360
|
+
return {
|
|
361
|
+
success: true,
|
|
362
|
+
data: config || {}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (action === "get") {
|
|
366
|
+
if (!key) {
|
|
367
|
+
return {
|
|
368
|
+
success: false,
|
|
369
|
+
error: "Key is required for get action"
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
const config = await readZcfConfig();
|
|
373
|
+
return {
|
|
374
|
+
success: true,
|
|
375
|
+
data: {
|
|
376
|
+
key,
|
|
377
|
+
value: config?.[key]
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (action === "set") {
|
|
382
|
+
if (!key || value === void 0) {
|
|
383
|
+
return {
|
|
384
|
+
success: false,
|
|
385
|
+
error: "Key and value are required for set action"
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
await updateZcfConfig({ [key]: value });
|
|
389
|
+
return {
|
|
390
|
+
success: true,
|
|
391
|
+
data: {
|
|
392
|
+
key,
|
|
393
|
+
value,
|
|
394
|
+
message: "Configuration updated"
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
success: false,
|
|
400
|
+
error: `Unknown action: ${action}`
|
|
401
|
+
};
|
|
402
|
+
} catch (error) {
|
|
403
|
+
return {
|
|
404
|
+
success: false,
|
|
405
|
+
error: `Failed to handle config: ${error instanceof Error ? error.message : String(error)}`
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Handle ccjk_init tool
|
|
411
|
+
*/
|
|
412
|
+
async handleInit(args) {
|
|
413
|
+
const { mode = "full", force = false } = args;
|
|
414
|
+
try {
|
|
415
|
+
const { init } = await import('./init.mjs').then(function (n) { return n.H; });
|
|
416
|
+
const options = {
|
|
417
|
+
force,
|
|
418
|
+
skipPrompt: true
|
|
419
|
+
};
|
|
420
|
+
if (mode === "workflows-only") {
|
|
421
|
+
options.configAction = "skip";
|
|
422
|
+
options.apiType = "skip";
|
|
423
|
+
} else if (mode === "api-only") {
|
|
424
|
+
options.workflows = false;
|
|
425
|
+
}
|
|
426
|
+
await init(options);
|
|
427
|
+
return {
|
|
428
|
+
success: true,
|
|
429
|
+
data: {
|
|
430
|
+
message: `Initialization completed in ${mode} mode`
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
} catch (error) {
|
|
434
|
+
return {
|
|
435
|
+
success: false,
|
|
436
|
+
error: `Initialization failed: ${error instanceof Error ? error.message : String(error)}`
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Handle ccjk_doctor tool
|
|
442
|
+
*/
|
|
443
|
+
async handleDoctor(args) {
|
|
444
|
+
const { verbose = false } = args;
|
|
445
|
+
try {
|
|
446
|
+
const { doctor } = await import('./doctor.mjs');
|
|
447
|
+
const originalLog = console.log;
|
|
448
|
+
const output = [];
|
|
449
|
+
console.log = (...args2) => {
|
|
450
|
+
output.push(args2.join(" "));
|
|
451
|
+
};
|
|
452
|
+
await doctor();
|
|
453
|
+
console.log = originalLog;
|
|
454
|
+
return {
|
|
455
|
+
success: true,
|
|
456
|
+
data: {
|
|
457
|
+
output: output.join("\n"),
|
|
458
|
+
verbose
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
} catch (error) {
|
|
462
|
+
return {
|
|
463
|
+
success: false,
|
|
464
|
+
error: `Doctor check failed: ${error instanceof Error ? error.message : String(error)}`
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
class MCPServer {
|
|
471
|
+
handler;
|
|
472
|
+
httpServer;
|
|
473
|
+
options;
|
|
474
|
+
serverInfo;
|
|
475
|
+
initialized = false;
|
|
476
|
+
constructor(options = {}) {
|
|
477
|
+
this.options = {
|
|
478
|
+
transport: options.transport || "stdio",
|
|
479
|
+
port: options.port || 3e3,
|
|
480
|
+
host: options.host || "localhost",
|
|
481
|
+
debug: options.debug || false
|
|
482
|
+
};
|
|
483
|
+
this.handler = new MCPHandler();
|
|
484
|
+
this.serverInfo = {
|
|
485
|
+
name: "ccjk-mcp-server",
|
|
486
|
+
version: "1.0.0",
|
|
487
|
+
protocolVersion: "2024-11-05",
|
|
488
|
+
capabilities: {
|
|
489
|
+
tools: true,
|
|
490
|
+
prompts: false,
|
|
491
|
+
resources: false
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Start the MCP server
|
|
497
|
+
*/
|
|
498
|
+
async start() {
|
|
499
|
+
if (this.options.transport === "stdio") {
|
|
500
|
+
await this.startStdio();
|
|
501
|
+
} else {
|
|
502
|
+
await this.startHttp();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Stop the MCP server
|
|
507
|
+
*/
|
|
508
|
+
async stop() {
|
|
509
|
+
if (this.httpServer) {
|
|
510
|
+
return new Promise((resolve, reject) => {
|
|
511
|
+
this.httpServer.close((err) => {
|
|
512
|
+
if (err)
|
|
513
|
+
reject(err);
|
|
514
|
+
else resolve();
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Start stdio transport mode
|
|
521
|
+
*/
|
|
522
|
+
async startStdio() {
|
|
523
|
+
this.log("Starting MCP server in stdio mode...");
|
|
524
|
+
process__default.stdin.setEncoding("utf8");
|
|
525
|
+
let buffer = "";
|
|
526
|
+
process__default.stdin.on("data", (chunk) => {
|
|
527
|
+
buffer += chunk;
|
|
528
|
+
const lines = buffer.split("\n");
|
|
529
|
+
buffer = lines.pop() || "";
|
|
530
|
+
for (const line of lines) {
|
|
531
|
+
if (line.trim()) {
|
|
532
|
+
this.handleStdioMessage(line).catch((error) => {
|
|
533
|
+
this.logError("Error handling stdio message:", error);
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
process__default.stdin.on("end", () => {
|
|
539
|
+
this.log("Stdin closed, shutting down...");
|
|
540
|
+
process__default.exit(0);
|
|
541
|
+
});
|
|
542
|
+
this.log("MCP server ready (stdio mode)");
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Start HTTP transport mode
|
|
546
|
+
*/
|
|
547
|
+
async startHttp() {
|
|
548
|
+
this.log(`Starting MCP server in HTTP mode on ${this.options.host}:${this.options.port}...`);
|
|
549
|
+
this.httpServer = createServer(async (req, res) => {
|
|
550
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
551
|
+
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
552
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
553
|
+
if (req.method === "OPTIONS") {
|
|
554
|
+
res.writeHead(200);
|
|
555
|
+
res.end();
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (req.method !== "POST") {
|
|
559
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
560
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
let body = "";
|
|
564
|
+
req.on("data", (chunk) => body += chunk);
|
|
565
|
+
req.on("end", async () => {
|
|
566
|
+
try {
|
|
567
|
+
const request = JSON.parse(body);
|
|
568
|
+
const response = await this.handleRequest(request);
|
|
569
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
570
|
+
res.end(JSON.stringify(response));
|
|
571
|
+
} catch (error) {
|
|
572
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
573
|
+
res.end(JSON.stringify({
|
|
574
|
+
jsonrpc: "2.0",
|
|
575
|
+
error: {
|
|
576
|
+
code: -32700,
|
|
577
|
+
message: "Parse error",
|
|
578
|
+
data: error instanceof Error ? error.message : String(error)
|
|
579
|
+
}
|
|
580
|
+
}));
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
return new Promise((resolve, reject) => {
|
|
585
|
+
this.httpServer.listen(this.options.port, this.options.host, () => {
|
|
586
|
+
this.log(`MCP server listening on http://${this.options.host}:${this.options.port}`);
|
|
587
|
+
resolve();
|
|
588
|
+
});
|
|
589
|
+
this.httpServer.on("error", reject);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Handle stdio message
|
|
594
|
+
*/
|
|
595
|
+
async handleStdioMessage(message) {
|
|
596
|
+
try {
|
|
597
|
+
const request = JSON.parse(message);
|
|
598
|
+
const response = await this.handleRequest(request);
|
|
599
|
+
process__default.stdout.write(`${JSON.stringify(response)}
|
|
600
|
+
`);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
const errorResponse = {
|
|
603
|
+
jsonrpc: "2.0",
|
|
604
|
+
error: {
|
|
605
|
+
code: -32700,
|
|
606
|
+
message: "Parse error",
|
|
607
|
+
data: error instanceof Error ? error.message : String(error)
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
process__default.stdout.write(`${JSON.stringify(errorResponse)}
|
|
611
|
+
`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Handle MCP request
|
|
616
|
+
*/
|
|
617
|
+
async handleRequest(request) {
|
|
618
|
+
this.log(`Received request: ${request.method}`);
|
|
619
|
+
try {
|
|
620
|
+
switch (request.method) {
|
|
621
|
+
case "initialize":
|
|
622
|
+
return this.handleInitialize(request);
|
|
623
|
+
case "tools/list":
|
|
624
|
+
return this.handleToolsList(request);
|
|
625
|
+
case "tools/call":
|
|
626
|
+
return await this.handleToolsCall(request);
|
|
627
|
+
case "ping":
|
|
628
|
+
return this.handlePing(request);
|
|
629
|
+
default:
|
|
630
|
+
return {
|
|
631
|
+
jsonrpc: "2.0",
|
|
632
|
+
id: request.id,
|
|
633
|
+
error: {
|
|
634
|
+
code: -32601,
|
|
635
|
+
message: `Method not found: ${request.method}`
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
} catch (error) {
|
|
640
|
+
return {
|
|
641
|
+
jsonrpc: "2.0",
|
|
642
|
+
id: request.id,
|
|
643
|
+
error: {
|
|
644
|
+
code: -32603,
|
|
645
|
+
message: "Internal error",
|
|
646
|
+
data: error instanceof Error ? error.message : String(error)
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Handle initialize request
|
|
653
|
+
*/
|
|
654
|
+
handleInitialize(request) {
|
|
655
|
+
this.initialized = true;
|
|
656
|
+
this.log("Server initialized");
|
|
657
|
+
return {
|
|
658
|
+
jsonrpc: "2.0",
|
|
659
|
+
id: request.id,
|
|
660
|
+
result: {
|
|
661
|
+
protocolVersion: this.serverInfo.protocolVersion,
|
|
662
|
+
capabilities: this.serverInfo.capabilities,
|
|
663
|
+
serverInfo: {
|
|
664
|
+
name: this.serverInfo.name,
|
|
665
|
+
version: this.serverInfo.version
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Handle tools/list request
|
|
672
|
+
*/
|
|
673
|
+
handleToolsList(request) {
|
|
674
|
+
if (!this.initialized) {
|
|
675
|
+
return {
|
|
676
|
+
jsonrpc: "2.0",
|
|
677
|
+
id: request.id,
|
|
678
|
+
error: {
|
|
679
|
+
code: -32002,
|
|
680
|
+
message: "Server not initialized"
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
jsonrpc: "2.0",
|
|
686
|
+
id: request.id,
|
|
687
|
+
result: {
|
|
688
|
+
tools: MCP_TOOLS
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Handle tools/call request
|
|
694
|
+
*/
|
|
695
|
+
async handleToolsCall(request) {
|
|
696
|
+
if (!this.initialized) {
|
|
697
|
+
return {
|
|
698
|
+
jsonrpc: "2.0",
|
|
699
|
+
id: request.id,
|
|
700
|
+
error: {
|
|
701
|
+
code: -32002,
|
|
702
|
+
message: "Server not initialized"
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const { name, arguments: args } = request.params || {};
|
|
707
|
+
if (!name) {
|
|
708
|
+
return {
|
|
709
|
+
jsonrpc: "2.0",
|
|
710
|
+
id: request.id,
|
|
711
|
+
error: {
|
|
712
|
+
code: -32602,
|
|
713
|
+
message: "Invalid params: missing tool name"
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
this.log(`Calling tool: ${name}`);
|
|
718
|
+
const result = await this.handler.handleToolCall(name, args || {});
|
|
719
|
+
if (!result.success) {
|
|
720
|
+
return {
|
|
721
|
+
jsonrpc: "2.0",
|
|
722
|
+
id: request.id,
|
|
723
|
+
error: {
|
|
724
|
+
code: -32e3,
|
|
725
|
+
message: result.error || "Tool execution failed"
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
return {
|
|
730
|
+
jsonrpc: "2.0",
|
|
731
|
+
id: request.id,
|
|
732
|
+
result: {
|
|
733
|
+
content: [
|
|
734
|
+
{
|
|
735
|
+
type: "text",
|
|
736
|
+
text: JSON.stringify(result.data, null, 2)
|
|
737
|
+
}
|
|
738
|
+
]
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Handle ping request
|
|
744
|
+
*/
|
|
745
|
+
handlePing(request) {
|
|
746
|
+
return {
|
|
747
|
+
jsonrpc: "2.0",
|
|
748
|
+
id: request.id,
|
|
749
|
+
result: {
|
|
750
|
+
status: "ok",
|
|
751
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Log message (only in debug mode)
|
|
757
|
+
*/
|
|
758
|
+
log(...args) {
|
|
759
|
+
if (this.options.debug) {
|
|
760
|
+
console.error("[MCP Server]", ...args);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Log error (always)
|
|
765
|
+
*/
|
|
766
|
+
logError(...args) {
|
|
767
|
+
console.error("[MCP Server Error]", ...args);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function startMCPServer(options = {}) {
|
|
771
|
+
const server = new MCPServer(options);
|
|
772
|
+
await server.start();
|
|
773
|
+
return server;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export { MCPServer, startMCPServer };
|