recker 1.0.11-alpha.d342d95 → 1.0.11-alpha.e84cc95
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/cli/index.js +27 -5
- package/dist/mcp/server.d.ts +12 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +156 -23
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -329,13 +329,20 @@ ${pc.bold(pc.yellow('Examples:'))}
|
|
|
329
329
|
program
|
|
330
330
|
.command('mcp')
|
|
331
331
|
.description('Start MCP server for AI agents to access Recker documentation')
|
|
332
|
-
.option('-
|
|
332
|
+
.option('-t, --transport <mode>', 'Transport mode: stdio, http, sse', 'stdio')
|
|
333
|
+
.option('-p, --port <number>', 'Server port (for http/sse modes)', '3100')
|
|
333
334
|
.option('-d, --docs <path>', 'Path to documentation folder')
|
|
334
335
|
.option('--debug', 'Enable debug logging')
|
|
335
336
|
.addHelpText('after', `
|
|
337
|
+
${pc.bold(pc.yellow('Transport Modes:'))}
|
|
338
|
+
${pc.cyan('stdio')} ${pc.gray('(default)')} For Claude Code and other CLI tools
|
|
339
|
+
${pc.cyan('http')} Simple HTTP POST endpoint
|
|
340
|
+
${pc.cyan('sse')} HTTP + Server-Sent Events for real-time notifications
|
|
341
|
+
|
|
336
342
|
${pc.bold(pc.yellow('Usage:'))}
|
|
337
|
-
${pc.green('$ rek mcp')} ${pc.gray('Start
|
|
338
|
-
${pc.green('$ rek mcp -
|
|
343
|
+
${pc.green('$ rek mcp')} ${pc.gray('Start in stdio mode (for Claude Code)')}
|
|
344
|
+
${pc.green('$ rek mcp -t http')} ${pc.gray('Start HTTP server on port 3100')}
|
|
345
|
+
${pc.green('$ rek mcp -t sse -p 8080')} ${pc.gray('Start SSE server on custom port')}
|
|
339
346
|
${pc.green('$ rek mcp --debug')} ${pc.gray('Enable debug logging')}
|
|
340
347
|
|
|
341
348
|
${pc.bold(pc.yellow('Tools provided:'))}
|
|
@@ -354,19 +361,34 @@ ${pc.bold(pc.yellow('Claude Code config (~/.claude.json):'))}
|
|
|
354
361
|
`)
|
|
355
362
|
.action(async (options) => {
|
|
356
363
|
const { MCPServer } = await import('../mcp/server.js');
|
|
364
|
+
const transport = options.transport;
|
|
357
365
|
const server = new MCPServer({
|
|
366
|
+
transport,
|
|
358
367
|
port: parseInt(options.port),
|
|
359
368
|
docsPath: options.docs,
|
|
360
369
|
debug: options.debug,
|
|
361
370
|
});
|
|
371
|
+
if (transport === 'stdio') {
|
|
372
|
+
await server.start();
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
362
375
|
await server.start();
|
|
376
|
+
const endpoints = transport === 'sse'
|
|
377
|
+
? `
|
|
378
|
+
│ POST / - JSON-RPC endpoint │
|
|
379
|
+
│ GET /sse - Server-Sent Events │
|
|
380
|
+
│ GET /health - Health check │`
|
|
381
|
+
: `
|
|
382
|
+
│ POST / - JSON-RPC endpoint │`;
|
|
363
383
|
console.log(pc.green(`
|
|
364
384
|
┌─────────────────────────────────────────────┐
|
|
365
385
|
│ ${pc.bold('Recker MCP Server')} │
|
|
366
386
|
├─────────────────────────────────────────────┤
|
|
367
|
-
│
|
|
387
|
+
│ Transport: ${pc.cyan(transport.padEnd(31))}│
|
|
388
|
+
│ Endpoint: ${pc.cyan(`http://localhost:${options.port}`.padEnd(32))}│
|
|
368
389
|
│ Docs indexed: ${pc.yellow(String(server.getDocsCount()).padEnd(28))}│
|
|
369
|
-
|
|
390
|
+
├─────────────────────────────────────────────┤${endpoints}
|
|
391
|
+
├─────────────────────────────────────────────┤
|
|
370
392
|
│ Tools: │
|
|
371
393
|
│ • ${pc.cyan('search_docs')} - Search documentation │
|
|
372
394
|
│ • ${pc.cyan('get_doc')} - Get full doc content │
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
import type { JsonRpcRequest, JsonRpcResponse } from './types.js';
|
|
2
|
+
export type MCPTransportMode = 'stdio' | 'http' | 'sse';
|
|
1
3
|
export interface MCPServerOptions {
|
|
2
4
|
name?: string;
|
|
3
5
|
version?: string;
|
|
4
6
|
docsPath?: string;
|
|
5
7
|
port?: number;
|
|
8
|
+
transport?: MCPTransportMode;
|
|
6
9
|
debug?: boolean;
|
|
7
10
|
}
|
|
8
11
|
export declare class MCPServer {
|
|
9
12
|
private options;
|
|
10
13
|
private server?;
|
|
11
14
|
private docsIndex;
|
|
15
|
+
private sseClients;
|
|
16
|
+
private initialized;
|
|
12
17
|
constructor(options?: MCPServerOptions);
|
|
18
|
+
private log;
|
|
13
19
|
private findDocsPath;
|
|
14
20
|
private buildIndex;
|
|
15
21
|
private walkDir;
|
|
@@ -20,11 +26,16 @@ export declare class MCPServer {
|
|
|
20
26
|
private searchDocs;
|
|
21
27
|
private extractSnippet;
|
|
22
28
|
private getDoc;
|
|
23
|
-
|
|
29
|
+
handleRequest(req: JsonRpcRequest): JsonRpcResponse;
|
|
30
|
+
private sendNotification;
|
|
31
|
+
private startStdio;
|
|
32
|
+
private startHttp;
|
|
33
|
+
private startSSE;
|
|
24
34
|
start(): Promise<void>;
|
|
25
35
|
stop(): Promise<void>;
|
|
26
36
|
getPort(): number;
|
|
27
37
|
getDocsCount(): number;
|
|
38
|
+
getTransport(): MCPTransportMode;
|
|
28
39
|
}
|
|
29
40
|
export declare function createMCPServer(options?: MCPServerOptions): MCPServer;
|
|
30
41
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAMhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAsBD,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAkC;IACjD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,GAAE,gBAAqB;IAa1C,OAAO,CAAC,GAAG;IAWX,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,UAAU;IAiClB,OAAO,CAAC,OAAO;IAyBf,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,QAAQ;IAyChB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,UAAU;IA2DlB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,MAAM;IAmCd,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,eAAe;IAkEnD,OAAO,CAAC,gBAAgB;YAUV,UAAU;YAqCV,SAAS;YAkDT,QAAQ;IA2FhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,OAAO,IAAI,MAAM;IAIjB,YAAY,IAAI,MAAM;IAItB,YAAY,IAAI,gBAAgB;CAGjC;AAoBD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAErE"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,27 +1,42 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
2
|
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
|
3
3
|
import { join, relative } from 'path';
|
|
4
|
+
import { createInterface } from 'readline';
|
|
4
5
|
export class MCPServer {
|
|
5
6
|
options;
|
|
6
7
|
server;
|
|
7
8
|
docsIndex = [];
|
|
9
|
+
sseClients = new Set();
|
|
10
|
+
initialized = false;
|
|
8
11
|
constructor(options = {}) {
|
|
9
12
|
this.options = {
|
|
10
13
|
name: options.name || 'recker-docs',
|
|
11
14
|
version: options.version || '1.0.0',
|
|
12
15
|
docsPath: options.docsPath || this.findDocsPath(),
|
|
13
16
|
port: options.port || 3100,
|
|
17
|
+
transport: options.transport || 'stdio',
|
|
14
18
|
debug: options.debug || false,
|
|
15
19
|
};
|
|
16
20
|
this.buildIndex();
|
|
17
21
|
}
|
|
22
|
+
log(message, data) {
|
|
23
|
+
if (this.options.debug) {
|
|
24
|
+
if (this.options.transport === 'stdio') {
|
|
25
|
+
console.error(`[MCP] ${message}`, data ? JSON.stringify(data) : '');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log(`[MCP] ${message}`, data ? JSON.stringify(data) : '');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
findDocsPath() {
|
|
19
33
|
const possiblePaths = [
|
|
20
34
|
join(process.cwd(), 'docs'),
|
|
21
35
|
join(process.cwd(), '..', 'docs'),
|
|
22
|
-
join(__dirname, '..', '..', 'docs'),
|
|
23
|
-
join(__dirname, '..', '..', '..', 'docs'),
|
|
24
36
|
];
|
|
37
|
+
if (typeof __dirname !== 'undefined') {
|
|
38
|
+
possiblePaths.push(join(__dirname, '..', '..', 'docs'), join(__dirname, '..', '..', '..', 'docs'));
|
|
39
|
+
}
|
|
25
40
|
for (const p of possiblePaths) {
|
|
26
41
|
if (existsSync(p)) {
|
|
27
42
|
return p;
|
|
@@ -31,9 +46,7 @@ export class MCPServer {
|
|
|
31
46
|
}
|
|
32
47
|
buildIndex() {
|
|
33
48
|
if (!existsSync(this.options.docsPath)) {
|
|
34
|
-
|
|
35
|
-
console.log(`[MCP] Docs path not found: ${this.options.docsPath}`);
|
|
36
|
-
}
|
|
49
|
+
this.log(`Docs path not found: ${this.options.docsPath}`);
|
|
37
50
|
return;
|
|
38
51
|
}
|
|
39
52
|
const files = this.walkDir(this.options.docsPath);
|
|
@@ -55,14 +68,10 @@ export class MCPServer {
|
|
|
55
68
|
});
|
|
56
69
|
}
|
|
57
70
|
catch (err) {
|
|
58
|
-
|
|
59
|
-
console.log(`[MCP] Failed to index ${file}:`, err);
|
|
60
|
-
}
|
|
71
|
+
this.log(`Failed to index ${file}:`, err);
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
|
-
|
|
64
|
-
console.log(`[MCP] Indexed ${this.docsIndex.length} documentation files`);
|
|
65
|
-
}
|
|
74
|
+
this.log(`Indexed ${this.docsIndex.length} documentation files`);
|
|
66
75
|
}
|
|
67
76
|
walkDir(dir) {
|
|
68
77
|
const files = [];
|
|
@@ -255,9 +264,11 @@ export class MCPServer {
|
|
|
255
264
|
}
|
|
256
265
|
handleRequest(req) {
|
|
257
266
|
const { method, params, id } = req;
|
|
267
|
+
this.log(`Request: ${method}`, params);
|
|
258
268
|
try {
|
|
259
269
|
switch (method) {
|
|
260
270
|
case 'initialize': {
|
|
271
|
+
this.initialized = true;
|
|
261
272
|
const response = {
|
|
262
273
|
protocolVersion: '2024-11-05',
|
|
263
274
|
capabilities: {
|
|
@@ -270,6 +281,9 @@ export class MCPServer {
|
|
|
270
281
|
};
|
|
271
282
|
return { jsonrpc: '2.0', id: id, result: response };
|
|
272
283
|
}
|
|
284
|
+
case 'notifications/initialized': {
|
|
285
|
+
return { jsonrpc: '2.0', id: id, result: {} };
|
|
286
|
+
}
|
|
273
287
|
case 'ping':
|
|
274
288
|
return { jsonrpc: '2.0', id: id, result: {} };
|
|
275
289
|
case 'tools/list': {
|
|
@@ -281,6 +295,10 @@ export class MCPServer {
|
|
|
281
295
|
const result = this.handleToolCall(name, args || {});
|
|
282
296
|
return { jsonrpc: '2.0', id: id, result };
|
|
283
297
|
}
|
|
298
|
+
case 'resources/list':
|
|
299
|
+
return { jsonrpc: '2.0', id: id, result: { resources: [] } };
|
|
300
|
+
case 'prompts/list':
|
|
301
|
+
return { jsonrpc: '2.0', id: id, result: { prompts: [] } };
|
|
284
302
|
default:
|
|
285
303
|
return {
|
|
286
304
|
jsonrpc: '2.0',
|
|
@@ -297,11 +315,46 @@ export class MCPServer {
|
|
|
297
315
|
};
|
|
298
316
|
}
|
|
299
317
|
}
|
|
300
|
-
|
|
318
|
+
sendNotification(notification) {
|
|
319
|
+
const data = JSON.stringify(notification);
|
|
320
|
+
for (const client of this.sseClients) {
|
|
321
|
+
client.write(`data: ${data}\n\n`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async startStdio() {
|
|
325
|
+
const rl = createInterface({
|
|
326
|
+
input: process.stdin,
|
|
327
|
+
output: process.stdout,
|
|
328
|
+
terminal: false,
|
|
329
|
+
});
|
|
330
|
+
this.log('Starting in stdio mode');
|
|
331
|
+
rl.on('line', (line) => {
|
|
332
|
+
if (!line.trim())
|
|
333
|
+
return;
|
|
334
|
+
try {
|
|
335
|
+
const request = JSON.parse(line);
|
|
336
|
+
const response = this.handleRequest(request);
|
|
337
|
+
process.stdout.write(JSON.stringify(response) + '\n');
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
const errorResponse = {
|
|
341
|
+
jsonrpc: '2.0',
|
|
342
|
+
id: 0,
|
|
343
|
+
error: { code: -32700, message: 'Parse error' },
|
|
344
|
+
};
|
|
345
|
+
process.stdout.write(JSON.stringify(errorResponse) + '\n');
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
rl.on('close', () => {
|
|
349
|
+
this.log('stdin closed, exiting');
|
|
350
|
+
process.exit(0);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
async startHttp() {
|
|
301
354
|
return new Promise((resolve) => {
|
|
302
355
|
this.server = createServer((req, res) => {
|
|
303
356
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
304
|
-
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
357
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
|
|
305
358
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
306
359
|
if (req.method === 'OPTIONS') {
|
|
307
360
|
res.writeHead(204);
|
|
@@ -318,13 +371,7 @@ export class MCPServer {
|
|
|
318
371
|
req.on('end', () => {
|
|
319
372
|
try {
|
|
320
373
|
const request = JSON.parse(body);
|
|
321
|
-
if (this.options.debug) {
|
|
322
|
-
console.log('[MCP] Request:', JSON.stringify(request, null, 2));
|
|
323
|
-
}
|
|
324
374
|
const response = this.handleRequest(request);
|
|
325
|
-
if (this.options.debug) {
|
|
326
|
-
console.log('[MCP] Response:', JSON.stringify(response, null, 2));
|
|
327
|
-
}
|
|
328
375
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
329
376
|
res.end(JSON.stringify(response));
|
|
330
377
|
}
|
|
@@ -339,16 +386,99 @@ export class MCPServer {
|
|
|
339
386
|
});
|
|
340
387
|
});
|
|
341
388
|
this.server.listen(this.options.port, () => {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
389
|
+
this.log(`HTTP server listening on http://localhost:${this.options.port}`);
|
|
390
|
+
resolve();
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
async startSSE() {
|
|
395
|
+
return new Promise((resolve) => {
|
|
396
|
+
this.server = createServer((req, res) => {
|
|
397
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
398
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
|
|
399
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
400
|
+
const url = req.url || '/';
|
|
401
|
+
if (req.method === 'OPTIONS') {
|
|
402
|
+
res.writeHead(204);
|
|
403
|
+
res.end();
|
|
404
|
+
return;
|
|
346
405
|
}
|
|
406
|
+
if (req.method === 'GET' && url === '/sse') {
|
|
407
|
+
res.writeHead(200, {
|
|
408
|
+
'Content-Type': 'text/event-stream',
|
|
409
|
+
'Cache-Control': 'no-cache',
|
|
410
|
+
'Connection': 'keep-alive',
|
|
411
|
+
});
|
|
412
|
+
res.write(`data: ${JSON.stringify({ type: 'connected' })}\n\n`);
|
|
413
|
+
this.sseClients.add(res);
|
|
414
|
+
this.log(`SSE client connected (${this.sseClients.size} total)`);
|
|
415
|
+
req.on('close', () => {
|
|
416
|
+
this.sseClients.delete(res);
|
|
417
|
+
this.log(`SSE client disconnected (${this.sseClients.size} total)`);
|
|
418
|
+
});
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (req.method === 'POST') {
|
|
422
|
+
let body = '';
|
|
423
|
+
req.on('data', chunk => body += chunk);
|
|
424
|
+
req.on('end', () => {
|
|
425
|
+
try {
|
|
426
|
+
const request = JSON.parse(body);
|
|
427
|
+
const response = this.handleRequest(request);
|
|
428
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
429
|
+
res.end(JSON.stringify(response));
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
433
|
+
res.end(JSON.stringify({
|
|
434
|
+
jsonrpc: '2.0',
|
|
435
|
+
id: null,
|
|
436
|
+
error: { code: -32700, message: 'Parse error' },
|
|
437
|
+
}));
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (req.method === 'GET' && url === '/health') {
|
|
443
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
444
|
+
res.end(JSON.stringify({
|
|
445
|
+
status: 'ok',
|
|
446
|
+
name: this.options.name,
|
|
447
|
+
version: this.options.version,
|
|
448
|
+
docsCount: this.docsIndex.length,
|
|
449
|
+
sseClients: this.sseClients.size,
|
|
450
|
+
}));
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
res.writeHead(404);
|
|
454
|
+
res.end('Not found');
|
|
455
|
+
});
|
|
456
|
+
this.server.listen(this.options.port, () => {
|
|
457
|
+
this.log(`SSE server listening on http://localhost:${this.options.port}`);
|
|
458
|
+
this.log(` POST / - JSON-RPC endpoint`);
|
|
459
|
+
this.log(` GET /sse - Server-Sent Events`);
|
|
460
|
+
this.log(` GET /health - Health check`);
|
|
347
461
|
resolve();
|
|
348
462
|
});
|
|
349
463
|
});
|
|
350
464
|
}
|
|
465
|
+
async start() {
|
|
466
|
+
switch (this.options.transport) {
|
|
467
|
+
case 'stdio':
|
|
468
|
+
return this.startStdio();
|
|
469
|
+
case 'http':
|
|
470
|
+
return this.startHttp();
|
|
471
|
+
case 'sse':
|
|
472
|
+
return this.startSSE();
|
|
473
|
+
default:
|
|
474
|
+
throw new Error(`Unknown transport: ${this.options.transport}`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
351
477
|
async stop() {
|
|
478
|
+
for (const client of this.sseClients) {
|
|
479
|
+
client.end();
|
|
480
|
+
}
|
|
481
|
+
this.sseClients.clear();
|
|
352
482
|
return new Promise((resolve) => {
|
|
353
483
|
if (this.server) {
|
|
354
484
|
this.server.close(() => resolve());
|
|
@@ -364,6 +494,9 @@ export class MCPServer {
|
|
|
364
494
|
getDocsCount() {
|
|
365
495
|
return this.docsIndex.length;
|
|
366
496
|
}
|
|
497
|
+
getTransport() {
|
|
498
|
+
return this.options.transport;
|
|
499
|
+
}
|
|
367
500
|
}
|
|
368
501
|
export function createMCPServer(options) {
|
|
369
502
|
return new MCPServer(options);
|