devworkspace 0.3.3 → 0.4.1
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/CHANGELOG.md +53 -0
- package/README.md +220 -90
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.js +16 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.js +28 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/render.d.ts +2 -0
- package/dist/cli/render.js +10 -0
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/run.js +62 -2
- package/dist/cli/run.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/protocol.d.ts +35 -0
- package/dist/mcp/protocol.js +175 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/server.d.ts +25 -0
- package/dist/mcp/server.js +163 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/setup.d.ts +54 -0
- package/dist/mcp/setup.js +288 -0
- package/dist/mcp/setup.js.map +1 -0
- package/dist/mcp/tools.d.ts +8 -0
- package/dist/mcp/tools.js +446 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +80 -0
- package/dist/mcp/types.js +15 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/services/taskContext.js +204 -41
- package/dist/services/taskContext.js.map +1 -1
- package/docs/agents.md +32 -0
- package/docs/context-accuracy-report.md +60 -0
- package/docs/context-evaluation.md +170 -0
- package/docs/mcp.md +264 -0
- package/package.json +3 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { JSONRPC_VERSION, JSONRPC_ERRORS, } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Transport layer for newline-delimited JSON-RPC 2.0 over stdio streams.
|
|
4
|
+
* Strictly writes protocol responses to stdout and diagnostic logs to stderr.
|
|
5
|
+
*/
|
|
6
|
+
export class StdioTransport {
|
|
7
|
+
input;
|
|
8
|
+
output;
|
|
9
|
+
stderr;
|
|
10
|
+
debug;
|
|
11
|
+
buffer = '';
|
|
12
|
+
requestHandler;
|
|
13
|
+
notificationHandler;
|
|
14
|
+
closed = false;
|
|
15
|
+
constructor(opts = {}) {
|
|
16
|
+
this.input = opts.input ?? process.stdin;
|
|
17
|
+
this.output = opts.output ?? process.stdout;
|
|
18
|
+
this.stderr = opts.stderr ?? process.stderr;
|
|
19
|
+
this.debug = opts.debug ?? false;
|
|
20
|
+
}
|
|
21
|
+
setRequestHandler(handler) {
|
|
22
|
+
this.requestHandler = handler;
|
|
23
|
+
}
|
|
24
|
+
setNotificationHandler(handler) {
|
|
25
|
+
this.notificationHandler = handler;
|
|
26
|
+
}
|
|
27
|
+
log(message) {
|
|
28
|
+
if (this.debug) {
|
|
29
|
+
this.stderr.write(`[devworkspace-mcp] ${message}\n`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
warn(message) {
|
|
33
|
+
this.stderr.write(`[devworkspace-mcp:warn] ${message}\n`);
|
|
34
|
+
}
|
|
35
|
+
start() {
|
|
36
|
+
this.input.setEncoding('utf8');
|
|
37
|
+
this.input.on('data', (chunk) => {
|
|
38
|
+
if (this.closed)
|
|
39
|
+
return;
|
|
40
|
+
this.buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
41
|
+
this.processBuffer();
|
|
42
|
+
});
|
|
43
|
+
this.input.on('end', () => {
|
|
44
|
+
this.log('Input stream ended');
|
|
45
|
+
this.close();
|
|
46
|
+
});
|
|
47
|
+
this.input.on('error', (err) => {
|
|
48
|
+
this.warn(`Input stream error: ${err.message}`);
|
|
49
|
+
this.close();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
sendResponse(response) {
|
|
53
|
+
if (this.closed)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
const line = JSON.stringify(response) + '\n';
|
|
57
|
+
this.output.write(line);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
this.warn(`Failed to serialize response: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
close() {
|
|
64
|
+
if (this.closed)
|
|
65
|
+
return;
|
|
66
|
+
this.closed = true;
|
|
67
|
+
this.log('Transport closed');
|
|
68
|
+
}
|
|
69
|
+
isClosed() {
|
|
70
|
+
return this.closed;
|
|
71
|
+
}
|
|
72
|
+
processBuffer() {
|
|
73
|
+
let newlineIdx = this.buffer.indexOf('\n');
|
|
74
|
+
while (newlineIdx !== -1) {
|
|
75
|
+
const line = this.buffer.slice(0, newlineIdx).trim();
|
|
76
|
+
this.buffer = this.buffer.slice(newlineIdx + 1);
|
|
77
|
+
if (line.length > 0) {
|
|
78
|
+
this.handleLine(line);
|
|
79
|
+
}
|
|
80
|
+
newlineIdx = this.buffer.indexOf('\n');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
handleLine(line) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(line);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
this.sendResponse({
|
|
90
|
+
jsonrpc: JSONRPC_VERSION,
|
|
91
|
+
id: null,
|
|
92
|
+
error: {
|
|
93
|
+
code: JSONRPC_ERRORS.PARSE_ERROR,
|
|
94
|
+
message: 'Parse error: invalid JSON',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
100
|
+
this.sendResponse({
|
|
101
|
+
jsonrpc: JSONRPC_VERSION,
|
|
102
|
+
id: null,
|
|
103
|
+
error: {
|
|
104
|
+
code: JSONRPC_ERRORS.INVALID_REQUEST,
|
|
105
|
+
message: 'Invalid Request: expected a JSON-RPC object',
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const obj = parsed;
|
|
111
|
+
if (obj.jsonrpc !== JSONRPC_VERSION || typeof obj.method !== 'string') {
|
|
112
|
+
const maybeId = (typeof obj.id === 'string' || typeof obj.id === 'number') ? obj.id : null;
|
|
113
|
+
this.sendResponse({
|
|
114
|
+
jsonrpc: JSONRPC_VERSION,
|
|
115
|
+
id: maybeId,
|
|
116
|
+
error: {
|
|
117
|
+
code: JSONRPC_ERRORS.INVALID_REQUEST,
|
|
118
|
+
message: 'Invalid Request: jsonrpc must be "2.0" and method must be a string',
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const hasId = Object.prototype.hasOwnProperty.call(obj, 'id') && obj.id !== undefined;
|
|
124
|
+
if (!hasId) {
|
|
125
|
+
// Notification
|
|
126
|
+
const notif = {
|
|
127
|
+
jsonrpc: JSONRPC_VERSION,
|
|
128
|
+
method: obj.method,
|
|
129
|
+
params: typeof obj.params === 'object' && obj.params !== null ? obj.params : undefined,
|
|
130
|
+
};
|
|
131
|
+
if (this.notificationHandler) {
|
|
132
|
+
this.notificationHandler(notif).catch((err) => {
|
|
133
|
+
this.warn(`Error in notification handler for "${notif.method}": ${err.message}`);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Request
|
|
139
|
+
const rawId = obj.id;
|
|
140
|
+
const id = typeof rawId === 'string' || typeof rawId === 'number' || rawId === null ? rawId : null;
|
|
141
|
+
const req = {
|
|
142
|
+
jsonrpc: JSONRPC_VERSION,
|
|
143
|
+
id,
|
|
144
|
+
method: obj.method,
|
|
145
|
+
params: typeof obj.params === 'object' && obj.params !== null ? obj.params : undefined,
|
|
146
|
+
};
|
|
147
|
+
if (this.requestHandler) {
|
|
148
|
+
this.requestHandler(req)
|
|
149
|
+
.then((resp) => {
|
|
150
|
+
this.sendResponse(resp);
|
|
151
|
+
})
|
|
152
|
+
.catch((err) => {
|
|
153
|
+
this.sendResponse({
|
|
154
|
+
jsonrpc: JSONRPC_VERSION,
|
|
155
|
+
id,
|
|
156
|
+
error: {
|
|
157
|
+
code: JSONRPC_ERRORS.INTERNAL_ERROR,
|
|
158
|
+
message: `Internal error: ${err.message}`,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
this.sendResponse({
|
|
165
|
+
jsonrpc: JSONRPC_VERSION,
|
|
166
|
+
id,
|
|
167
|
+
error: {
|
|
168
|
+
code: JSONRPC_ERRORS.INTERNAL_ERROR,
|
|
169
|
+
message: 'No request handler registered',
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/mcp/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Readable, Writable } from 'node:stream';\nimport {\n JSONRPC_VERSION,\n JSONRPC_ERRORS,\n type JsonRpcId,\n type JsonRpcRequest,\n type JsonRpcNotification,\n type JsonRpcResponse,\n} from './types.js';\n\nexport type RequestHandler = (req: JsonRpcRequest) => Promise<JsonRpcResponse>;\nexport type NotificationHandler = (notif: JsonRpcNotification) => Promise<void>;\n\nexport interface TransportOptions {\n input?: Readable;\n output?: Writable;\n stderr?: Writable;\n debug?: boolean;\n}\n\n/**\n * Transport layer for newline-delimited JSON-RPC 2.0 over stdio streams.\n * Strictly writes protocol responses to stdout and diagnostic logs to stderr.\n */\nexport class StdioTransport {\n private input: Readable;\n private output: Writable;\n private stderr: Writable;\n private debug: boolean;\n private buffer = '';\n private requestHandler?: RequestHandler;\n private notificationHandler?: NotificationHandler;\n private closed = false;\n\n constructor(opts: TransportOptions = {}) {\n this.input = opts.input ?? process.stdin;\n this.output = opts.output ?? process.stdout;\n this.stderr = opts.stderr ?? process.stderr;\n this.debug = opts.debug ?? false;\n }\n\n public setRequestHandler(handler: RequestHandler): void {\n this.requestHandler = handler;\n }\n\n public setNotificationHandler(handler: NotificationHandler): void {\n this.notificationHandler = handler;\n }\n\n public log(message: string): void {\n if (this.debug) {\n this.stderr.write(`[devworkspace-mcp] ${message}\\n`);\n }\n }\n\n public warn(message: string): void {\n this.stderr.write(`[devworkspace-mcp:warn] ${message}\\n`);\n }\n\n public start(): void {\n this.input.setEncoding('utf8');\n\n this.input.on('data', (chunk: string | Buffer) => {\n if (this.closed) return;\n this.buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n this.processBuffer();\n });\n\n this.input.on('end', () => {\n this.log('Input stream ended');\n this.close();\n });\n\n this.input.on('error', (err: Error) => {\n this.warn(`Input stream error: ${err.message}`);\n this.close();\n });\n }\n\n public sendResponse(response: JsonRpcResponse): void {\n if (this.closed) return;\n try {\n const line = JSON.stringify(response) + '\\n';\n this.output.write(line);\n } catch (err) {\n this.warn(`Failed to serialize response: ${(err as Error).message}`);\n }\n }\n\n public close(): void {\n if (this.closed) return;\n this.closed = true;\n this.log('Transport closed');\n }\n\n public isClosed(): boolean {\n return this.closed;\n }\n\n private processBuffer(): void {\n let newlineIdx = this.buffer.indexOf('\\n');\n while (newlineIdx !== -1) {\n const line = this.buffer.slice(0, newlineIdx).trim();\n this.buffer = this.buffer.slice(newlineIdx + 1);\n\n if (line.length > 0) {\n this.handleLine(line);\n }\n newlineIdx = this.buffer.indexOf('\\n');\n }\n }\n\n private handleLine(line: string): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n this.sendResponse({\n jsonrpc: JSONRPC_VERSION,\n id: null,\n error: {\n code: JSONRPC_ERRORS.PARSE_ERROR,\n message: 'Parse error: invalid JSON',\n },\n });\n return;\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n this.sendResponse({\n jsonrpc: JSONRPC_VERSION,\n id: null,\n error: {\n code: JSONRPC_ERRORS.INVALID_REQUEST,\n message: 'Invalid Request: expected a JSON-RPC object',\n },\n });\n return;\n }\n\n const obj = parsed as Record<string, unknown>;\n if (obj.jsonrpc !== JSONRPC_VERSION || typeof obj.method !== 'string') {\n const maybeId = (typeof obj.id === 'string' || typeof obj.id === 'number') ? obj.id : null;\n this.sendResponse({\n jsonrpc: JSONRPC_VERSION,\n id: maybeId,\n error: {\n code: JSONRPC_ERRORS.INVALID_REQUEST,\n message: 'Invalid Request: jsonrpc must be \"2.0\" and method must be a string',\n },\n });\n return;\n }\n\n const hasId = Object.prototype.hasOwnProperty.call(obj, 'id') && obj.id !== undefined;\n\n if (!hasId) {\n // Notification\n const notif: JsonRpcNotification = {\n jsonrpc: JSONRPC_VERSION,\n method: obj.method,\n params: typeof obj.params === 'object' && obj.params !== null ? (obj.params as Record<string, unknown>) : undefined,\n };\n if (this.notificationHandler) {\n this.notificationHandler(notif).catch((err: unknown) => {\n this.warn(`Error in notification handler for \"${notif.method}\": ${(err as Error).message}`);\n });\n }\n return;\n }\n\n // Request\n const rawId = obj.id;\n const id: JsonRpcId = typeof rawId === 'string' || typeof rawId === 'number' || rawId === null ? rawId : null;\n const req: JsonRpcRequest = {\n jsonrpc: JSONRPC_VERSION,\n id,\n method: obj.method,\n params: typeof obj.params === 'object' && obj.params !== null ? (obj.params as Record<string, unknown>) : undefined,\n };\n\n if (this.requestHandler) {\n this.requestHandler(req)\n .then((resp) => {\n this.sendResponse(resp);\n })\n .catch((err: unknown) => {\n this.sendResponse({\n jsonrpc: JSONRPC_VERSION,\n id,\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: `Internal error: ${(err as Error).message}`,\n },\n });\n });\n } else {\n this.sendResponse({\n jsonrpc: JSONRPC_VERSION,\n id,\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: 'No request handler registered',\n },\n });\n }\n }\n}\n\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
export interface McpServerOptions {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
input?: Readable;
|
|
5
|
+
output?: Writable;
|
|
6
|
+
stderr?: Writable;
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class McpServer {
|
|
10
|
+
private workspaceRoot;
|
|
11
|
+
private transport;
|
|
12
|
+
private initReceived;
|
|
13
|
+
private initialized;
|
|
14
|
+
constructor(root: string, opts?: McpServerOptions);
|
|
15
|
+
getRoot(): string;
|
|
16
|
+
isInitialized(): boolean;
|
|
17
|
+
start(): void;
|
|
18
|
+
stop(): void;
|
|
19
|
+
private handleNotification;
|
|
20
|
+
private handleRequest;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Start the MCP server over stdio for the workspace resolved from cwd.
|
|
24
|
+
*/
|
|
25
|
+
export declare function startMcpServer(opts?: McpServerOptions): Promise<McpServer>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { packageVersion } from '../core/version.js';
|
|
2
|
+
import { requireWorkspace } from '../core/workspace.js';
|
|
3
|
+
import { JSONRPC_VERSION, JSONRPC_ERRORS, MCP_PROTOCOL_VERSION, MCP_SERVER_NAME, } from './types.js';
|
|
4
|
+
import { StdioTransport } from './protocol.js';
|
|
5
|
+
import { MCP_TOOLS, handleFileContext, handleProjectOverview, handleTaskContext, } from './tools.js';
|
|
6
|
+
export class McpServer {
|
|
7
|
+
workspaceRoot;
|
|
8
|
+
transport;
|
|
9
|
+
initReceived = false;
|
|
10
|
+
initialized = false;
|
|
11
|
+
constructor(root, opts = {}) {
|
|
12
|
+
this.workspaceRoot = root;
|
|
13
|
+
this.transport = new StdioTransport({
|
|
14
|
+
input: opts.input,
|
|
15
|
+
output: opts.output,
|
|
16
|
+
stderr: opts.stderr,
|
|
17
|
+
debug: opts.debug,
|
|
18
|
+
});
|
|
19
|
+
this.transport.setRequestHandler(this.handleRequest.bind(this));
|
|
20
|
+
this.transport.setNotificationHandler(this.handleNotification.bind(this));
|
|
21
|
+
}
|
|
22
|
+
getRoot() {
|
|
23
|
+
return this.workspaceRoot;
|
|
24
|
+
}
|
|
25
|
+
isInitialized() {
|
|
26
|
+
return this.initialized;
|
|
27
|
+
}
|
|
28
|
+
start() {
|
|
29
|
+
this.transport.log(`Starting DevWorkspace MCP server for root: ${this.workspaceRoot}`);
|
|
30
|
+
this.transport.start();
|
|
31
|
+
}
|
|
32
|
+
stop() {
|
|
33
|
+
this.transport.log('Stopping MCP server');
|
|
34
|
+
this.transport.close();
|
|
35
|
+
}
|
|
36
|
+
async handleNotification(notif) {
|
|
37
|
+
if (notif.method === 'notifications/initialized') {
|
|
38
|
+
if (!this.initReceived) {
|
|
39
|
+
this.transport.log('Ignored notifications/initialized received before initialize request');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.initialized = true;
|
|
43
|
+
this.transport.log('Client acknowledged initialization');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async handleRequest(req) {
|
|
47
|
+
const { id, method, params } = req;
|
|
48
|
+
if (!this.initialized && method !== 'initialize' && method !== 'ping') {
|
|
49
|
+
return {
|
|
50
|
+
jsonrpc: JSONRPC_VERSION,
|
|
51
|
+
id,
|
|
52
|
+
error: {
|
|
53
|
+
code: JSONRPC_ERRORS.INVALID_REQUEST,
|
|
54
|
+
message: 'Server not initialized: must complete initialize handshake before calling tools',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
switch (method) {
|
|
59
|
+
case 'initialize': {
|
|
60
|
+
this.initReceived = true;
|
|
61
|
+
const initParams = (params ?? {});
|
|
62
|
+
this.transport.log(`Received initialize request from client: ${initParams.clientInfo?.name ?? 'unknown'}`);
|
|
63
|
+
const result = {
|
|
64
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
65
|
+
capabilities: {
|
|
66
|
+
tools: {},
|
|
67
|
+
},
|
|
68
|
+
serverInfo: {
|
|
69
|
+
name: MCP_SERVER_NAME,
|
|
70
|
+
version: packageVersion(),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
jsonrpc: JSONRPC_VERSION,
|
|
75
|
+
id,
|
|
76
|
+
result,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
case 'ping': {
|
|
80
|
+
return {
|
|
81
|
+
jsonrpc: JSONRPC_VERSION,
|
|
82
|
+
id,
|
|
83
|
+
result: {},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
case 'tools/list': {
|
|
87
|
+
return {
|
|
88
|
+
jsonrpc: JSONRPC_VERSION,
|
|
89
|
+
id,
|
|
90
|
+
result: {
|
|
91
|
+
tools: MCP_TOOLS,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
case 'tools/call': {
|
|
96
|
+
if (!params || typeof params !== 'object') {
|
|
97
|
+
return {
|
|
98
|
+
jsonrpc: JSONRPC_VERSION,
|
|
99
|
+
id,
|
|
100
|
+
error: {
|
|
101
|
+
code: JSONRPC_ERRORS.INVALID_PARAMS,
|
|
102
|
+
message: 'Invalid params: expected object with name and arguments',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const toolCall = params;
|
|
107
|
+
if (typeof toolCall.name !== 'string') {
|
|
108
|
+
return {
|
|
109
|
+
jsonrpc: JSONRPC_VERSION,
|
|
110
|
+
id,
|
|
111
|
+
error: {
|
|
112
|
+
code: JSONRPC_ERRORS.INVALID_PARAMS,
|
|
113
|
+
message: 'Invalid params: "name" must be a string',
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const toolName = toolCall.name;
|
|
118
|
+
const toolArgs = toolCall.arguments;
|
|
119
|
+
this.transport.log(`Executing tool: ${toolName}`);
|
|
120
|
+
if (toolName === 'get_project_overview') {
|
|
121
|
+
const res = await handleProjectOverview(this.workspaceRoot, toolArgs);
|
|
122
|
+
return { jsonrpc: JSONRPC_VERSION, id, result: res };
|
|
123
|
+
}
|
|
124
|
+
if (toolName === 'get_task_context') {
|
|
125
|
+
const res = await handleTaskContext(this.workspaceRoot, toolArgs);
|
|
126
|
+
return { jsonrpc: JSONRPC_VERSION, id, result: res };
|
|
127
|
+
}
|
|
128
|
+
if (toolName === 'get_file_context') {
|
|
129
|
+
const res = await handleFileContext(this.workspaceRoot, toolArgs);
|
|
130
|
+
return { jsonrpc: JSONRPC_VERSION, id, result: res };
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
jsonrpc: JSONRPC_VERSION,
|
|
134
|
+
id,
|
|
135
|
+
result: {
|
|
136
|
+
content: [{ type: 'text', text: `Unknown tool: "${toolName}". Available tools: ${MCP_TOOLS.map((t) => t.name).join(', ')}` }],
|
|
137
|
+
isError: true,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
default: {
|
|
142
|
+
return {
|
|
143
|
+
jsonrpc: JSONRPC_VERSION,
|
|
144
|
+
id,
|
|
145
|
+
error: {
|
|
146
|
+
code: JSONRPC_ERRORS.METHOD_NOT_FOUND,
|
|
147
|
+
message: `Method not found: ${method}`,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Start the MCP server over stdio for the workspace resolved from cwd.
|
|
156
|
+
*/
|
|
157
|
+
export async function startMcpServer(opts = {}) {
|
|
158
|
+
const ws = await requireWorkspace(opts.cwd ?? process.cwd());
|
|
159
|
+
const server = new McpServer(ws.root, opts);
|
|
160
|
+
server.start();
|
|
161
|
+
return server;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Readable, Writable } from 'node:stream';\nimport { packageVersion } from '../core/version.js';\nimport { requireWorkspace } from '../core/workspace.js';\nimport {\n JSONRPC_VERSION,\n JSONRPC_ERRORS,\n MCP_PROTOCOL_VERSION,\n MCP_SERVER_NAME,\n type JsonRpcRequest,\n type JsonRpcResponse,\n type JsonRpcNotification,\n type McpInitializeParams,\n type McpInitializeResult,\n} from './types.js';\nimport { StdioTransport } from './protocol.js';\nimport {\n MCP_TOOLS,\n handleFileContext,\n handleProjectOverview,\n handleTaskContext,\n} from './tools.js';\n\nexport interface McpServerOptions {\n cwd?: string;\n input?: Readable;\n output?: Writable;\n stderr?: Writable;\n debug?: boolean;\n}\n\nexport class McpServer {\n private workspaceRoot: string;\n private transport: StdioTransport;\n private initReceived = false;\n private initialized = false;\n\n constructor(root: string, opts: McpServerOptions = {}) {\n this.workspaceRoot = root;\n this.transport = new StdioTransport({\n input: opts.input,\n output: opts.output,\n stderr: opts.stderr,\n debug: opts.debug,\n });\n\n this.transport.setRequestHandler(this.handleRequest.bind(this));\n this.transport.setNotificationHandler(this.handleNotification.bind(this));\n }\n\n public getRoot(): string {\n return this.workspaceRoot;\n }\n\n public isInitialized(): boolean {\n return this.initialized;\n }\n\n public start(): void {\n this.transport.log(`Starting DevWorkspace MCP server for root: ${this.workspaceRoot}`);\n this.transport.start();\n }\n\n public stop(): void {\n this.transport.log('Stopping MCP server');\n this.transport.close();\n }\n\n private async handleNotification(notif: JsonRpcNotification): Promise<void> {\n if (notif.method === 'notifications/initialized') {\n if (!this.initReceived) {\n this.transport.log('Ignored notifications/initialized received before initialize request');\n return;\n }\n this.initialized = true;\n this.transport.log('Client acknowledged initialization');\n }\n }\n\n private async handleRequest(req: JsonRpcRequest): Promise<JsonRpcResponse> {\n const { id, method, params } = req;\n\n if (!this.initialized && method !== 'initialize' && method !== 'ping') {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: {\n code: JSONRPC_ERRORS.INVALID_REQUEST,\n message: 'Server not initialized: must complete initialize handshake before calling tools',\n },\n };\n }\n\n switch (method) {\n case 'initialize': {\n this.initReceived = true;\n const initParams = (params ?? {}) as unknown as McpInitializeParams;\n this.transport.log(`Received initialize request from client: ${initParams.clientInfo?.name ?? 'unknown'}`);\n\n const result: McpInitializeResult = {\n protocolVersion: MCP_PROTOCOL_VERSION,\n capabilities: {\n tools: {},\n },\n serverInfo: {\n name: MCP_SERVER_NAME,\n version: packageVersion(),\n },\n };\n\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n result,\n };\n }\n\n case 'ping': {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n result: {},\n };\n }\n\n case 'tools/list': {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n result: {\n tools: MCP_TOOLS,\n },\n };\n }\n\n case 'tools/call': {\n if (!params || typeof params !== 'object') {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: {\n code: JSONRPC_ERRORS.INVALID_PARAMS,\n message: 'Invalid params: expected object with name and arguments',\n },\n };\n }\n\n const toolCall = params as { name?: unknown; arguments?: unknown };\n if (typeof toolCall.name !== 'string') {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: {\n code: JSONRPC_ERRORS.INVALID_PARAMS,\n message: 'Invalid params: \"name\" must be a string',\n },\n };\n }\n\n const toolName = toolCall.name;\n const toolArgs = toolCall.arguments;\n\n this.transport.log(`Executing tool: ${toolName}`);\n\n if (toolName === 'get_project_overview') {\n const res = await handleProjectOverview(this.workspaceRoot, toolArgs);\n return { jsonrpc: JSONRPC_VERSION, id, result: res };\n }\n\n if (toolName === 'get_task_context') {\n const res = await handleTaskContext(this.workspaceRoot, toolArgs);\n return { jsonrpc: JSONRPC_VERSION, id, result: res };\n }\n\n if (toolName === 'get_file_context') {\n const res = await handleFileContext(this.workspaceRoot, toolArgs);\n return { jsonrpc: JSONRPC_VERSION, id, result: res };\n }\n\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n result: {\n content: [{ type: 'text', text: `Unknown tool: \"${toolName}\". Available tools: ${MCP_TOOLS.map((t) => t.name).join(', ')}` }],\n isError: true,\n },\n };\n }\n\n default: {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: {\n code: JSONRPC_ERRORS.METHOD_NOT_FOUND,\n message: `Method not found: ${method}`,\n },\n };\n }\n }\n }\n}\n\n/**\n * Start the MCP server over stdio for the workspace resolved from cwd.\n */\nexport async function startMcpServer(opts: McpServerOptions = {}): Promise<McpServer> {\n const ws = await requireWorkspace(opts.cwd ?? process.cwd());\n const server = new McpServer(ws.root, opts);\n server.start();\n return server;\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface McpSetupOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
agent?: string;
|
|
4
|
+
check?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface McpPlannedFileChange {
|
|
7
|
+
path: string;
|
|
8
|
+
kind: 'create' | 'update' | 'unchanged' | 'conflict';
|
|
9
|
+
mode: 'owned' | 'section';
|
|
10
|
+
content: string;
|
|
11
|
+
note: string;
|
|
12
|
+
}
|
|
13
|
+
export interface McpSetupResult {
|
|
14
|
+
agent: string;
|
|
15
|
+
check: boolean;
|
|
16
|
+
changes: Omit<McpPlannedFileChange, 'content'>[];
|
|
17
|
+
conflicts: number;
|
|
18
|
+
notes: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare const SUPPORTED_MCP_AGENTS: readonly ["claude-code", "claude", "codex", "antigravity", "all"];
|
|
21
|
+
export declare function isSupportedMcpAgent(id: string): boolean;
|
|
22
|
+
export declare const MCP_SECTION_START = "<!-- devworkspace-mcp:start (managed by DevWorkspace MCP - edit outside this block) -->";
|
|
23
|
+
export declare const MCP_SECTION_END = "<!-- devworkspace-mcp:end -->";
|
|
24
|
+
export declare const MCP_SECTION_RE: RegExp;
|
|
25
|
+
export declare const LEGACY_MCP_SECTION_RE: RegExp;
|
|
26
|
+
/**
|
|
27
|
+
* Builds instructions for an AI coding agent on how to use DevWorkspace MCP tools.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildMcpInstructions(projectName: string, agentDisplayName: string): string;
|
|
30
|
+
/** Merge the managed MCP block into existing instructions, preserving everything the user wrote and any standard agent section. */
|
|
31
|
+
export declare function mergeMcpSection(existing: string | null, block: string, prefix?: string): {
|
|
32
|
+
content: string;
|
|
33
|
+
kind: 'create' | 'update' | 'unchanged' | 'conflict';
|
|
34
|
+
note: string;
|
|
35
|
+
};
|
|
36
|
+
/** Remove the managed MCP block (and nothing else). Returns null if there is no balanced block. */
|
|
37
|
+
export declare function removeManagedMcpSection(text: string): {
|
|
38
|
+
content: string;
|
|
39
|
+
empty: boolean;
|
|
40
|
+
} | null;
|
|
41
|
+
/**
|
|
42
|
+
* Merges a server entry into an existing or new MCP configuration JSON file.
|
|
43
|
+
* Preserves all unrelated settings, existing servers, and formatting.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeMcpJsonConfig(existingText: string | null, serverName: string, serverDef: {
|
|
46
|
+
command: string;
|
|
47
|
+
args: string[];
|
|
48
|
+
}): {
|
|
49
|
+
content: string;
|
|
50
|
+
kind: 'create' | 'update' | 'unchanged' | 'conflict';
|
|
51
|
+
note: string;
|
|
52
|
+
};
|
|
53
|
+
export declare function planMcpAgentChanges(root: string, projectName: string, agentId: string): Promise<McpPlannedFileChange[]>;
|
|
54
|
+
export declare function setupMcpAgent(opts?: McpSetupOptions): Promise<McpSetupResult>;
|