isthmus-cli 0.5.0 → 0.7.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/README.ko.md +170 -45
- package/README.md +178 -36
- package/Skills/isthmus/SKILL.md +104 -5
- package/compatibility.json +10 -0
- package/dist/cli/check-command.d.ts +2 -2
- package/dist/cli/check-command.js +22 -9
- package/dist/cli/check-command.js.map +1 -1
- package/dist/cli/command-support.d.ts +20 -2
- package/dist/cli/command-support.js +38 -9
- package/dist/cli/command-support.js.map +1 -1
- package/dist/cli/diff-command.js +4 -3
- package/dist/cli/diff-command.js.map +1 -1
- package/dist/cli/doctor-command.d.ts +11 -0
- package/dist/cli/doctor-command.js +128 -0
- package/dist/cli/doctor-command.js.map +1 -0
- package/dist/cli/extract-js-command.d.ts +33 -0
- package/dist/cli/extract-js-command.js +233 -0
- package/dist/cli/extract-js-command.js.map +1 -0
- package/dist/cli/graph-command.js +13 -6
- package/dist/cli/graph-command.js.map +1 -1
- package/dist/cli/impact-command.d.ts +5 -0
- package/dist/cli/impact-command.js +111 -0
- package/dist/cli/impact-command.js.map +1 -0
- package/dist/cli/init-command.d.ts +11 -0
- package/dist/cli/init-command.js +115 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli/main.js +67 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp-server.d.ts +16 -0
- package/dist/cli/mcp-server.js +534 -0
- package/dist/cli/mcp-server.js.map +1 -0
- package/dist/cli/preflight-command.d.ts +5 -0
- package/dist/cli/preflight-command.js +83 -0
- package/dist/cli/preflight-command.js.map +1 -0
- package/dist/cli/query-command.js +13 -6
- package/dist/cli/query-command.js.map +1 -1
- package/dist/cli/retentions-command.js +14 -7
- package/dist/cli/retentions-command.js.map +1 -1
- package/dist/cli/runtime-command.d.ts +5 -0
- package/dist/cli/runtime-command.js +42 -0
- package/dist/cli/runtime-command.js.map +1 -0
- package/dist/cli/runtime-json-reader.d.ts +12 -0
- package/dist/cli/runtime-json-reader.js +37 -0
- package/dist/cli/runtime-json-reader.js.map +1 -0
- package/dist/cli/serve-command.d.ts +13 -0
- package/dist/cli/serve-command.js +43 -0
- package/dist/cli/serve-command.js.map +1 -0
- package/dist/exchange/capture-config.d.ts +18 -0
- package/dist/exchange/capture-config.js +100 -0
- package/dist/exchange/capture-config.js.map +1 -0
- package/dist/exchange/impact-selection.d.ts +13 -0
- package/dist/exchange/impact-selection.js +35 -0
- package/dist/exchange/impact-selection.js.map +1 -0
- package/dist/exchange/kartograph-impact.d.ts +4 -0
- package/dist/exchange/kartograph-impact.js +169 -0
- package/dist/exchange/kartograph-impact.js.map +1 -0
- package/dist/exchange/messages.d.ts +36 -0
- package/dist/exchange/messages.js +96 -0
- package/dist/exchange/messages.js.map +1 -0
- package/dist/exchange/parse.d.ts +69 -0
- package/dist/exchange/parse.js +127 -20
- package/dist/exchange/parse.js.map +1 -1
- package/dist/exchange/preflight-context.d.ts +71 -0
- package/dist/exchange/preflight-context.js +317 -0
- package/dist/exchange/preflight-context.js.map +1 -0
- package/dist/exchange/producer-impact.d.ts +17 -0
- package/dist/exchange/producer-impact.js +226 -0
- package/dist/exchange/producer-impact.js.map +1 -0
- package/dist/exchange/runtime.d.ts +69 -0
- package/dist/exchange/runtime.js +160 -0
- package/dist/exchange/runtime.js.map +1 -0
- package/dist/extract/js-document.d.ts +22 -0
- package/dist/extract/js-document.js +230 -0
- package/dist/extract/js-document.js.map +1 -0
- package/dist/extract/js-scan.d.ts +93 -0
- package/dist/extract/js-scan.js +1205 -0
- package/dist/extract/js-scan.js.map +1 -0
- package/dist/extract/lexer.d.ts +26 -0
- package/dist/extract/lexer.js +316 -0
- package/dist/extract/lexer.js.map +1 -0
- package/dist/join/join.d.ts +66 -1
- package/dist/join/join.js +171 -1
- package/dist/join/join.js.map +1 -1
- package/dist/join/message-address.d.ts +7 -0
- package/dist/join/message-address.js +37 -0
- package/dist/join/message-address.js.map +1 -0
- package/dist/join/messages.d.ts +26 -0
- package/dist/join/messages.js +87 -0
- package/dist/join/messages.js.map +1 -0
- package/dist/report/check-report.d.ts +20 -3
- package/dist/report/check-report.js +240 -8
- package/dist/report/check-report.js.map +1 -1
- package/dist/report/codequality.d.ts +45 -0
- package/dist/report/codequality.js +74 -0
- package/dist/report/codequality.js.map +1 -0
- package/dist/report/diff.d.ts +25 -3
- package/dist/report/diff.js +91 -10
- package/dist/report/diff.js.map +1 -1
- package/dist/report/graph.d.ts +10 -4
- package/dist/report/graph.js +60 -6
- package/dist/report/graph.js.map +1 -1
- package/dist/report/impact.d.ts +62 -0
- package/dist/report/impact.js +168 -0
- package/dist/report/impact.js.map +1 -0
- package/dist/report/preflight-runtime.d.ts +42 -0
- package/dist/report/preflight-runtime.js +193 -0
- package/dist/report/preflight-runtime.js.map +1 -0
- package/dist/report/preflight-view.d.ts +146 -0
- package/dist/report/preflight-view.js +216 -0
- package/dist/report/preflight-view.js.map +1 -0
- package/dist/report/preflight.d.ts +104 -0
- package/dist/report/preflight.js +331 -0
- package/dist/report/preflight.js.map +1 -0
- package/dist/report/query.d.ts +11 -4
- package/dist/report/query.js +84 -17
- package/dist/report/query.js.map +1 -1
- package/dist/report/retentions.d.ts +17 -5
- package/dist/report/retentions.js +85 -8
- package/dist/report/retentions.js.map +1 -1
- package/dist/report/rules.d.ts +16 -0
- package/dist/report/rules.js +30 -0
- package/dist/report/rules.js.map +1 -0
- package/dist/report/runtime-impact.d.ts +38 -0
- package/dist/report/runtime-impact.js +94 -0
- package/dist/report/runtime-impact.js.map +1 -0
- package/dist/report/runtime.d.ts +54 -0
- package/dist/report/runtime.js +137 -0
- package/dist/report/runtime.js.map +1 -0
- package/dist/report/sarif.d.ts +2 -8
- package/dist/report/sarif.js +2 -10
- package/dist/report/sarif.js.map +1 -1
- package/dist/report/sorted-json.d.ts +1 -1
- package/dist/report/sorted-json.js +2 -2
- package/dist/report/sorted-json.js.map +1 -1
- package/docs/BRIDGE-EVENTS.md +87 -0
- package/docs/BRIDGE-MESSAGES.md +117 -0
- package/docs/GRAPH-EXCHANGE.md +382 -0
- package/docs/IMPACT.md +96 -0
- package/docs/MCP.md +62 -0
- package/docs/PREFLIGHT.md +311 -0
- package/docs/RUNTIME.md +172 -0
- package/docs/TOOLCHAIN.md +101 -0
- package/package.json +14 -2
- package/scripts/build-preflight-toolchain.mjs +186 -0
- package/scripts/capture-preflight.mjs +380 -0
- package/scripts/run-child.mjs +17 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { runCheckCommand } from "./check-command.js";
|
|
2
|
+
import { runDiffCommand } from "./diff-command.js";
|
|
3
|
+
import { runGraphCommand } from "./graph-command.js";
|
|
4
|
+
import { runImpactCommand } from "./impact-command.js";
|
|
5
|
+
import { runPreflightCommand } from "./preflight-command.js";
|
|
6
|
+
import { runQueryCommand } from "./query-command.js";
|
|
7
|
+
import { runRetentionsCommand } from "./retentions-command.js";
|
|
8
|
+
/**
|
|
9
|
+
* stdio 위의 newline-delimited JSON-RPC 2.0 세션이다.
|
|
10
|
+
*
|
|
11
|
+
* MCP(Model Context Protocol) 클라이언트가 `isthmus serve`를 자식 프로세스로
|
|
12
|
+
* 띄우고 한 줄에 메시지 하나씩 주고받는다. 이 모듈은 프레이밍만 담고, 각
|
|
13
|
+
* 도구 호출은 CLI 명령의 argv로 변환해 같은 실행 경로를 재사용한다 —
|
|
14
|
+
* 보고서 형식·종료 코드·한계 보고가 명령과 정확히 일치하도록 하기 위해서다.
|
|
15
|
+
*/
|
|
16
|
+
/** 현재 서버가 협상할 수 있는 MCP 프로토콜 개정 목록이다. */
|
|
17
|
+
const SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'];
|
|
18
|
+
/** JSON-RPC 오류 코드다. 알 수 없는 상수를 만들지 않는다. */
|
|
19
|
+
const PARSE_ERROR = -32700;
|
|
20
|
+
const INVALID_REQUEST = -32600;
|
|
21
|
+
const METHOD_NOT_FOUND = -32601;
|
|
22
|
+
const INVALID_PARAMS = -32602;
|
|
23
|
+
const INTERNAL_ERROR = -32603;
|
|
24
|
+
const DOCUMENTS_PROPERTY = {
|
|
25
|
+
type: 'array',
|
|
26
|
+
items: { type: 'string' },
|
|
27
|
+
minItems: 2,
|
|
28
|
+
description: 'Bridge fact document paths to join (GRAPH-EXCHANGE v1/v2 JSON).',
|
|
29
|
+
};
|
|
30
|
+
const TOOL_DEFINITIONS = [
|
|
31
|
+
{
|
|
32
|
+
name: 'check',
|
|
33
|
+
description: 'Join bridge fact documents and report unmatched calls and handlers. '
|
|
34
|
+
+ 'Findings are review candidates with limitations preserved, not '
|
|
35
|
+
+ 'delete approvals.',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
documents: DOCUMENTS_PROPERTY,
|
|
40
|
+
strict: {
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
description: 'Exit nonzero when any finding exists.',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: ['documents'],
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'query',
|
|
51
|
+
description: 'Find both sides of a bridge channel or method name across the joined '
|
|
52
|
+
+ 'documents.',
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
name: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
minLength: 1,
|
|
59
|
+
description: 'Channel, method, or qualifiedName to look up.',
|
|
60
|
+
},
|
|
61
|
+
documents: DOCUMENTS_PROPERTY,
|
|
62
|
+
},
|
|
63
|
+
required: ['name', 'documents'],
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'graph',
|
|
69
|
+
description: 'Render matched bridge boundary edges as a JSON, DOT, or Mermaid graph.',
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
documents: DOCUMENTS_PROPERTY,
|
|
74
|
+
format: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
enum: ['json', 'dot', 'mermaid'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
required: ['documents'],
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'diff',
|
|
85
|
+
description: 'Compare bridge observations before and after a change. Reports '
|
|
86
|
+
+ 'observed differences only; it does not infer revision order.',
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
before: DOCUMENTS_PROPERTY,
|
|
91
|
+
after: DOCUMENTS_PROPERTY,
|
|
92
|
+
strict: {
|
|
93
|
+
type: 'boolean',
|
|
94
|
+
description: 'Exit nonzero only for new errors.',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
required: ['before', 'after'],
|
|
98
|
+
additionalProperties: false,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'impact',
|
|
103
|
+
description: 'Inspect which bridge dependencies a changed file or symbol reaches. '
|
|
104
|
+
+ 'Exactly one of file, symbol, or changes selects the subject.',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
documents: DOCUMENTS_PROPERTY,
|
|
109
|
+
file: { type: 'string', description: 'Changed relative file path.' },
|
|
110
|
+
symbol: { type: 'string', description: 'Changed symbol name or USR.' },
|
|
111
|
+
changes: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
description: 'Path to a changes.json document.',
|
|
114
|
+
},
|
|
115
|
+
runtime: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: 'Optional runtime evidence document path.',
|
|
118
|
+
},
|
|
119
|
+
revision: { type: 'string' },
|
|
120
|
+
strict: { type: 'boolean' },
|
|
121
|
+
compact: { type: 'boolean' },
|
|
122
|
+
},
|
|
123
|
+
required: ['documents'],
|
|
124
|
+
additionalProperties: false,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'preflight',
|
|
129
|
+
description: 'Trace cross-language impact from a producer analysis context. The '
|
|
130
|
+
+ 'context JSON is produced by scripts/capture-preflight.mjs.',
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
context: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'Path to the captured preflight context JSON.',
|
|
137
|
+
},
|
|
138
|
+
runtime: {
|
|
139
|
+
type: 'array',
|
|
140
|
+
items: { type: 'string' },
|
|
141
|
+
description: 'Optional runtime evidence document paths.',
|
|
142
|
+
},
|
|
143
|
+
expectations: { type: 'string' },
|
|
144
|
+
revision: { type: 'string' },
|
|
145
|
+
summary: { type: 'boolean' },
|
|
146
|
+
limit: { type: 'integer', minimum: 1, maximum: 100 },
|
|
147
|
+
explain: { type: 'string' },
|
|
148
|
+
strict: { type: 'boolean' },
|
|
149
|
+
compact: { type: 'boolean' },
|
|
150
|
+
},
|
|
151
|
+
required: ['context'],
|
|
152
|
+
additionalProperties: false,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'retentions',
|
|
157
|
+
description: 'Produce external retention evidence for a native analyzer. Pass the '
|
|
158
|
+
+ 'result to cartograph dead --external-retentions.',
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
documents: DOCUMENTS_PROPERTY,
|
|
163
|
+
producer: {
|
|
164
|
+
type: 'string',
|
|
165
|
+
enum: ['cartograph'],
|
|
166
|
+
description: 'Only cartograph is supported today.',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
required: ['documents', 'producer'],
|
|
170
|
+
additionalProperties: false,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
const TOOL_NAMES = new Map(TOOL_DEFINITIONS.map((tool) => [tool.name, tool]));
|
|
175
|
+
/**
|
|
176
|
+
* MCP 세션을 만든다. 반환된 handleLine은 한 줄의 JSON-RPC 메시지를 받아
|
|
177
|
+
* 응답이 필요하면 직렬화된 한 줄을, 알림이면 undefined를 돌려준다.
|
|
178
|
+
*/
|
|
179
|
+
export function createMcpSession(dependencies) {
|
|
180
|
+
return {
|
|
181
|
+
handleLine: (line) => handleMessage(dependencies, line),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
async function handleMessage(dependencies, line) {
|
|
185
|
+
let message;
|
|
186
|
+
try {
|
|
187
|
+
message = JSON.parse(line);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return JSON.stringify(errorResponse(undefined, PARSE_ERROR, 'Parse error'));
|
|
191
|
+
}
|
|
192
|
+
if (Array.isArray(message)) {
|
|
193
|
+
// MCP는 배치를 쓰지 않는다. 첫 요소의 id도 모르므로 요청 전체를 거부한다.
|
|
194
|
+
return JSON.stringify(errorResponse(undefined, INVALID_REQUEST, 'Batch requests are not supported'));
|
|
195
|
+
}
|
|
196
|
+
if (!isRequestMessage(message)) {
|
|
197
|
+
// JSON-RPC 2.0은 검출 가능한 id를 그대로 에코하라고 요구한다 — 형식이
|
|
198
|
+
// 스칼라인 요청의 id는 거절 응답에도 보존한다.
|
|
199
|
+
return JSON.stringify(errorResponse(scalarId(message), INVALID_REQUEST, 'Invalid request'));
|
|
200
|
+
}
|
|
201
|
+
if (message.id === undefined) {
|
|
202
|
+
// 알림은 응답하지 않는다. initialized 등 어떤 알림이든 조용히 무시한다.
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
if (!isScalarId(message.id)) {
|
|
206
|
+
return JSON.stringify(errorResponse(null, INVALID_REQUEST, 'Request id must be a string, number, or null'));
|
|
207
|
+
}
|
|
208
|
+
let response;
|
|
209
|
+
try {
|
|
210
|
+
response = await dispatchRequest(dependencies, message.method, message.id, message.params);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// 명령 실행이나 파일 읽기의 내부 예외로 서버가 죽으면 안 된다 — 세션을 유지하며
|
|
214
|
+
// -32603으로 돌려준다. 원인 본문은 입력 경로·내용을 담을 수 있어 노출하지 않는다.
|
|
215
|
+
response = errorResponse(message.id, INTERNAL_ERROR, 'Internal error');
|
|
216
|
+
}
|
|
217
|
+
return JSON.stringify(response);
|
|
218
|
+
}
|
|
219
|
+
function isScalarId(value) {
|
|
220
|
+
return value === null || typeof value === 'string' || typeof value === 'number';
|
|
221
|
+
}
|
|
222
|
+
function scalarId(message) {
|
|
223
|
+
if (typeof message !== 'object' || message === null)
|
|
224
|
+
return null;
|
|
225
|
+
const id = message.id;
|
|
226
|
+
return isScalarId(id) ? id : null;
|
|
227
|
+
}
|
|
228
|
+
function isRequestMessage(value) {
|
|
229
|
+
if (typeof value !== 'object' || value === null)
|
|
230
|
+
return false;
|
|
231
|
+
const candidate = value;
|
|
232
|
+
return candidate.jsonrpc === '2.0' && typeof candidate.method === 'string';
|
|
233
|
+
}
|
|
234
|
+
async function dispatchRequest(dependencies, method, id, params) {
|
|
235
|
+
switch (method) {
|
|
236
|
+
case 'initialize':
|
|
237
|
+
return {
|
|
238
|
+
jsonrpc: '2.0',
|
|
239
|
+
id,
|
|
240
|
+
result: initializeResult(dependencies, params),
|
|
241
|
+
};
|
|
242
|
+
case 'ping':
|
|
243
|
+
return { jsonrpc: '2.0', id, result: {} };
|
|
244
|
+
case 'tools/list':
|
|
245
|
+
return {
|
|
246
|
+
jsonrpc: '2.0',
|
|
247
|
+
id,
|
|
248
|
+
result: { tools: TOOL_DEFINITIONS },
|
|
249
|
+
};
|
|
250
|
+
case 'tools/call':
|
|
251
|
+
return toolsCallResult(dependencies, id, params);
|
|
252
|
+
default:
|
|
253
|
+
return errorResponse(id, METHOD_NOT_FOUND, 'Method not found');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/** initialize 요청의 프로토콜 버전을 협상하고 서버 정보를 돌려준다. */
|
|
257
|
+
function initializeResult(dependencies, params) {
|
|
258
|
+
const requested = isInitializeParams(params) ? params.protocolVersion : undefined;
|
|
259
|
+
const protocolVersion = requested !== undefined
|
|
260
|
+
&& SUPPORTED_PROTOCOL_VERSIONS.includes(requested)
|
|
261
|
+
? requested
|
|
262
|
+
: SUPPORTED_PROTOCOL_VERSIONS[0];
|
|
263
|
+
return {
|
|
264
|
+
protocolVersion,
|
|
265
|
+
capabilities: { tools: {} },
|
|
266
|
+
serverInfo: {
|
|
267
|
+
name: 'isthmus',
|
|
268
|
+
version: dependencies.producerVersion ?? 'unknown',
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function isInitializeParams(value) {
|
|
273
|
+
if (typeof value !== 'object' || value === null)
|
|
274
|
+
return false;
|
|
275
|
+
return typeof value.protocolVersion === 'string';
|
|
276
|
+
}
|
|
277
|
+
async function toolsCallResult(dependencies, id, params) {
|
|
278
|
+
if (!isToolsCallParams(params)) {
|
|
279
|
+
return errorResponse(id, INVALID_PARAMS, 'tools/call requires name and arguments');
|
|
280
|
+
}
|
|
281
|
+
const tool = TOOL_NAMES.get(params.name);
|
|
282
|
+
if (tool === undefined) {
|
|
283
|
+
return errorResponse(id, INVALID_PARAMS, `Unknown tool: ${params.name}`);
|
|
284
|
+
}
|
|
285
|
+
const invalid = validateToolArguments(tool, params.arguments ?? {});
|
|
286
|
+
if (invalid !== undefined) {
|
|
287
|
+
return errorResponse(id, INVALID_PARAMS, invalid);
|
|
288
|
+
}
|
|
289
|
+
const argv = buildToolArgv(params.name, params.arguments ?? {});
|
|
290
|
+
if (argv === undefined) {
|
|
291
|
+
return errorResponse(id, INVALID_PARAMS, 'Invalid tool arguments');
|
|
292
|
+
}
|
|
293
|
+
const result = await runToolCommand(dependencies, argv);
|
|
294
|
+
return { jsonrpc: '2.0', id, result: commandResultContent(result) };
|
|
295
|
+
}
|
|
296
|
+
function isToolsCallParams(value) {
|
|
297
|
+
if (typeof value !== 'object' || value === null)
|
|
298
|
+
return false;
|
|
299
|
+
const candidate = value;
|
|
300
|
+
return typeof candidate.name === 'string';
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* tools/list에 광고한 inputSchema 그대로 인자를 검증한다. 스키마와 검증이
|
|
304
|
+
* 같은 정의에서 파생되므로 둘이 어긋날 수 없다 — 알 수 없는 키·타입 위반·
|
|
305
|
+
* enum 밖 값·범위 밖 정수는 명령 실행 전에 -32602로 거부한다.
|
|
306
|
+
*/
|
|
307
|
+
function validateToolArguments(tool, args) {
|
|
308
|
+
const { properties, required = [] } = tool.inputSchema;
|
|
309
|
+
for (const key of Object.keys(args)) {
|
|
310
|
+
if (!(key in properties))
|
|
311
|
+
return `Unknown argument: ${key}`;
|
|
312
|
+
}
|
|
313
|
+
for (const key of required) {
|
|
314
|
+
if (args[key] === undefined)
|
|
315
|
+
return `Missing required argument: ${key}`;
|
|
316
|
+
}
|
|
317
|
+
for (const [key, value] of Object.entries(args)) {
|
|
318
|
+
const property = properties[key];
|
|
319
|
+
switch (property.type) {
|
|
320
|
+
case 'boolean':
|
|
321
|
+
if (typeof value !== 'boolean')
|
|
322
|
+
return `Argument ${key} must be a boolean`;
|
|
323
|
+
break;
|
|
324
|
+
case 'string':
|
|
325
|
+
if (typeof value !== 'string')
|
|
326
|
+
return `Argument ${key} must be a string`;
|
|
327
|
+
if (property.minLength !== undefined && value.length < property.minLength) {
|
|
328
|
+
return `Argument ${key} is too short`;
|
|
329
|
+
}
|
|
330
|
+
break;
|
|
331
|
+
case 'integer':
|
|
332
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
333
|
+
return `Argument ${key} must be an integer`;
|
|
334
|
+
}
|
|
335
|
+
if (property.minimum !== undefined && value < property.minimum) {
|
|
336
|
+
return `Argument ${key} is below the minimum`;
|
|
337
|
+
}
|
|
338
|
+
if (property.maximum !== undefined && value > property.maximum) {
|
|
339
|
+
return `Argument ${key} exceeds the maximum`;
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
case 'array':
|
|
343
|
+
if (!Array.isArray(value) || (property.items?.type === 'string'
|
|
344
|
+
&& !value.every((item) => typeof item === 'string'))) {
|
|
345
|
+
return `Argument ${key} must be an array of strings`;
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
default:
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
if (property.enum !== undefined && !property.enum.includes(value)) {
|
|
352
|
+
return `Argument ${key} is not an allowed value`;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 도구 인자를 CLI argv로 변환한다. 스키마 검증을 통과한 인자만 들어오지만,
|
|
359
|
+
* selector 조합처럼 스키마로 표현 못 하는 제약은 여기서도 거른다.
|
|
360
|
+
*/
|
|
361
|
+
function buildToolArgv(toolName, args) {
|
|
362
|
+
switch (toolName) {
|
|
363
|
+
case 'check':
|
|
364
|
+
return withDocuments('check', args, (documents) => [
|
|
365
|
+
...booleanFlag('--strict', args.strict),
|
|
366
|
+
...documents,
|
|
367
|
+
]);
|
|
368
|
+
case 'query':
|
|
369
|
+
return typeof args.name === 'string' && args.name.length > 0
|
|
370
|
+
? withDocuments('query', args, (documents) => [args.name, ...documents])
|
|
371
|
+
: undefined;
|
|
372
|
+
case 'graph':
|
|
373
|
+
return withDocuments('graph', args, (documents) => [
|
|
374
|
+
...enumFlag('--format', args.format, ['json', 'dot', 'mermaid']),
|
|
375
|
+
...documents,
|
|
376
|
+
]);
|
|
377
|
+
case 'diff': {
|
|
378
|
+
const before = stringArray(args.before);
|
|
379
|
+
const after = stringArray(args.after);
|
|
380
|
+
if (before === undefined || after === undefined)
|
|
381
|
+
return undefined;
|
|
382
|
+
return [
|
|
383
|
+
'diff',
|
|
384
|
+
'--before',
|
|
385
|
+
...before,
|
|
386
|
+
'--after',
|
|
387
|
+
...after,
|
|
388
|
+
...booleanFlag('--strict', args.strict),
|
|
389
|
+
];
|
|
390
|
+
}
|
|
391
|
+
case 'impact': {
|
|
392
|
+
const selectorCount = ['file', 'symbol', 'changes']
|
|
393
|
+
.filter((key) => args[key] !== undefined).length;
|
|
394
|
+
if (selectorCount !== 1)
|
|
395
|
+
return undefined;
|
|
396
|
+
return withDocuments('impact', args, (documents) => [
|
|
397
|
+
...valueFlag('--file', args.file),
|
|
398
|
+
...valueFlag('--symbol', args.symbol),
|
|
399
|
+
...valueFlag('--changes', args.changes),
|
|
400
|
+
...documents,
|
|
401
|
+
...valueFlag('--runtime', args.runtime),
|
|
402
|
+
...valueFlag('--revision', args.revision),
|
|
403
|
+
...booleanFlag('--strict', args.strict),
|
|
404
|
+
...booleanFlag('--compact', args.compact),
|
|
405
|
+
]);
|
|
406
|
+
}
|
|
407
|
+
case 'preflight': {
|
|
408
|
+
if (typeof args.context !== 'string' || args.context.length === 0) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
const runtimePaths = args.runtime === undefined
|
|
412
|
+
? []
|
|
413
|
+
: stringArray(args.runtime);
|
|
414
|
+
if (runtimePaths === undefined)
|
|
415
|
+
return undefined;
|
|
416
|
+
const argv = [
|
|
417
|
+
'preflight',
|
|
418
|
+
args.context,
|
|
419
|
+
...runtimePaths,
|
|
420
|
+
...valueFlag('--expectations', args.expectations),
|
|
421
|
+
...valueFlag('--revision', args.revision),
|
|
422
|
+
...booleanFlag('--summary', args.summary),
|
|
423
|
+
...booleanFlag('--strict', args.strict),
|
|
424
|
+
...booleanFlag('--compact', args.compact),
|
|
425
|
+
];
|
|
426
|
+
if (typeof args.limit === 'number' && Number.isInteger(args.limit)) {
|
|
427
|
+
argv.push('--limit', String(args.limit));
|
|
428
|
+
}
|
|
429
|
+
argv.push(...valueFlag('--explain', args.explain));
|
|
430
|
+
return argv;
|
|
431
|
+
}
|
|
432
|
+
case 'retentions':
|
|
433
|
+
if (args.producer !== 'cartograph')
|
|
434
|
+
return undefined;
|
|
435
|
+
return withDocuments('retentions', args, (documents) => [
|
|
436
|
+
...documents,
|
|
437
|
+
'--for',
|
|
438
|
+
'cartograph',
|
|
439
|
+
]);
|
|
440
|
+
default:
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/** documents 인자를 검증하고 argv 앞에 명령 이름을 붙인다. */
|
|
445
|
+
function withDocuments(command, args, build) {
|
|
446
|
+
const documents = stringArray(args.documents);
|
|
447
|
+
if (documents === undefined || documents.length < 2)
|
|
448
|
+
return undefined;
|
|
449
|
+
return [command, ...build(documents)];
|
|
450
|
+
}
|
|
451
|
+
function stringArray(value) {
|
|
452
|
+
if (!Array.isArray(value))
|
|
453
|
+
return undefined;
|
|
454
|
+
return value.every((item) => typeof item === 'string')
|
|
455
|
+
? value
|
|
456
|
+
: undefined;
|
|
457
|
+
}
|
|
458
|
+
function booleanFlag(flag, value) {
|
|
459
|
+
return value === true ? [flag] : [];
|
|
460
|
+
}
|
|
461
|
+
function valueFlag(flag, value) {
|
|
462
|
+
return typeof value === 'string' && value.length > 0 ? [flag, value] : [];
|
|
463
|
+
}
|
|
464
|
+
function enumFlag(flag, value, allowed) {
|
|
465
|
+
return typeof value === 'string' && allowed.includes(value)
|
|
466
|
+
? [flag, value]
|
|
467
|
+
: [];
|
|
468
|
+
}
|
|
469
|
+
/** 변환된 argv를 대응하는 명령 실행 함수로 라우팅한다. */
|
|
470
|
+
async function runToolCommand(dependencies, argv) {
|
|
471
|
+
switch (argv[0]) {
|
|
472
|
+
case 'check':
|
|
473
|
+
return runCheckCommand(argv, dependencies.readTextFile, dependencies.writeTextFile, undefined, dependencies.producerVersion);
|
|
474
|
+
case 'query':
|
|
475
|
+
return runQueryCommand(argv, dependencies.readTextFile);
|
|
476
|
+
case 'graph':
|
|
477
|
+
return runGraphCommand(argv, dependencies.readTextFile);
|
|
478
|
+
case 'diff':
|
|
479
|
+
return runDiffCommand(argv, dependencies.readTextFile);
|
|
480
|
+
case 'impact':
|
|
481
|
+
return runImpactCommand(argv, dependencies.readTextFile);
|
|
482
|
+
case 'preflight':
|
|
483
|
+
return runPreflightCommand(argv, dependencies.readTextFile);
|
|
484
|
+
case 'retentions':
|
|
485
|
+
return dependencies.producerVersion === undefined
|
|
486
|
+
? {
|
|
487
|
+
standardOutput: '',
|
|
488
|
+
standardError: 'Unable to read package metadata.\n',
|
|
489
|
+
exitCode: 2,
|
|
490
|
+
}
|
|
491
|
+
: runRetentionsCommand(argv, dependencies.readTextFile, () => new Date(), dependencies.producerVersion);
|
|
492
|
+
default:
|
|
493
|
+
return {
|
|
494
|
+
standardOutput: '',
|
|
495
|
+
standardError: 'Unknown command.\n',
|
|
496
|
+
exitCode: 64,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 명령 결과를 MCP 도구 응답으로 옮긴다.
|
|
502
|
+
*
|
|
503
|
+
* stdout에 보고서 문서가 있으면 도구는 일을 한 것이므로 isError를 세지 않는다 —
|
|
504
|
+
* query notFound(64)나 check strict(1)의 정답 문서를 프로토콜 오류로 보내면
|
|
505
|
+
* 소비자가 한계와 조사 결과를 놓친다. 문서가 없는 실패(usage 64·internal 2)만
|
|
506
|
+
* isError로 표시하고 stderr를 함께 실어 원인을 남긴다.
|
|
507
|
+
*/
|
|
508
|
+
function commandResultContent(result) {
|
|
509
|
+
const content = [];
|
|
510
|
+
if (result.standardOutput.length > 0) {
|
|
511
|
+
content.push({ type: 'text', text: result.standardOutput });
|
|
512
|
+
}
|
|
513
|
+
if (result.standardError.length > 0) {
|
|
514
|
+
content.push({ type: 'text', text: result.standardError });
|
|
515
|
+
}
|
|
516
|
+
if (content.length === 0) {
|
|
517
|
+
content.push({ type: 'text', text: '(no output)\n' });
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
content,
|
|
521
|
+
// 종료 코드 2는 항상 내부 오류다 — 부분 stdout이 있어도 도구 오류로 표시한다.
|
|
522
|
+
// 1/64는 문서가 있으면 정답(조사 결과·notFound)이고, 문서 없는 실패만 오류다.
|
|
523
|
+
isError: result.exitCode === 2
|
|
524
|
+
|| (result.exitCode !== 0 && result.standardOutput.length === 0),
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function errorResponse(id, code, message) {
|
|
528
|
+
return {
|
|
529
|
+
jsonrpc: '2.0',
|
|
530
|
+
id: id ?? null,
|
|
531
|
+
error: { code, message },
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../src/cli/mcp-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { runCheckCommand } from './check-command.ts';\nimport { runDiffCommand } from './diff-command.ts';\nimport { runGraphCommand } from './graph-command.ts';\nimport { runImpactCommand } from './impact-command.ts';\nimport { runPreflightCommand } from './preflight-command.ts';\nimport { runQueryCommand } from './query-command.ts';\nimport { runRetentionsCommand } from './retentions-command.ts';\nimport type { CommandResult, ReadTextFile, WriteTextFile } from './command-support.ts';\n\n/**\n * stdio 위의 newline-delimited JSON-RPC 2.0 세션이다.\n *\n * MCP(Model Context Protocol) 클라이언트가 `isthmus serve`를 자식 프로세스로\n * 띄우고 한 줄에 메시지 하나씩 주고받는다. 이 모듈은 프레이밍만 담고, 각\n * 도구 호출은 CLI 명령의 argv로 변환해 같은 실행 경로를 재사용한다 —\n * 보고서 형식·종료 코드·한계 보고가 명령과 정확히 일치하도록 하기 위해서다.\n */\n\n/** 현재 서버가 협상할 수 있는 MCP 프로토콜 개정 목록이다. */\nconst SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'] as const;\n\n/** JSON-RPC 오류 코드다. 알 수 없는 상수를 만들지 않는다. */\nconst PARSE_ERROR = -32700;\nconst INVALID_REQUEST = -32600;\nconst METHOD_NOT_FOUND = -32601;\nconst INVALID_PARAMS = -32602;\nconst INTERNAL_ERROR = -32603;\n\n/** 한 도구가 tools/list에 노출하는 스키마다. */\ninterface McpToolDefinition {\n readonly name: string;\n readonly description: string;\n readonly inputSchema: {\n readonly type: 'object';\n readonly properties: Record<string, unknown>;\n readonly required?: readonly string[];\n readonly additionalProperties: false;\n };\n}\n\nconst DOCUMENTS_PROPERTY = {\n type: 'array',\n items: { type: 'string' },\n minItems: 2,\n description:\n 'Bridge fact document paths to join (GRAPH-EXCHANGE v1/v2 JSON).',\n} as const;\n\nconst TOOL_DEFINITIONS: readonly McpToolDefinition[] = [\n {\n name: 'check',\n description:\n 'Join bridge fact documents and report unmatched calls and handlers. '\n + 'Findings are review candidates with limitations preserved, not '\n + 'delete approvals.',\n inputSchema: {\n type: 'object',\n properties: {\n documents: DOCUMENTS_PROPERTY,\n strict: {\n type: 'boolean',\n description: 'Exit nonzero when any finding exists.',\n },\n },\n required: ['documents'],\n additionalProperties: false,\n },\n },\n {\n name: 'query',\n description:\n 'Find both sides of a bridge channel or method name across the joined '\n + 'documents.',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n minLength: 1,\n description: 'Channel, method, or qualifiedName to look up.',\n },\n documents: DOCUMENTS_PROPERTY,\n },\n required: ['name', 'documents'],\n additionalProperties: false,\n },\n },\n {\n name: 'graph',\n description:\n 'Render matched bridge boundary edges as a JSON, DOT, or Mermaid graph.',\n inputSchema: {\n type: 'object',\n properties: {\n documents: DOCUMENTS_PROPERTY,\n format: {\n type: 'string',\n enum: ['json', 'dot', 'mermaid'],\n },\n },\n required: ['documents'],\n additionalProperties: false,\n },\n },\n {\n name: 'diff',\n description:\n 'Compare bridge observations before and after a change. Reports '\n + 'observed differences only; it does not infer revision order.',\n inputSchema: {\n type: 'object',\n properties: {\n before: DOCUMENTS_PROPERTY,\n after: DOCUMENTS_PROPERTY,\n strict: {\n type: 'boolean',\n description: 'Exit nonzero only for new errors.',\n },\n },\n required: ['before', 'after'],\n additionalProperties: false,\n },\n },\n {\n name: 'impact',\n description:\n 'Inspect which bridge dependencies a changed file or symbol reaches. '\n + 'Exactly one of file, symbol, or changes selects the subject.',\n inputSchema: {\n type: 'object',\n properties: {\n documents: DOCUMENTS_PROPERTY,\n file: { type: 'string', description: 'Changed relative file path.' },\n symbol: { type: 'string', description: 'Changed symbol name or USR.' },\n changes: {\n type: 'string',\n description: 'Path to a changes.json document.',\n },\n runtime: {\n type: 'string',\n description: 'Optional runtime evidence document path.',\n },\n revision: { type: 'string' },\n strict: { type: 'boolean' },\n compact: { type: 'boolean' },\n },\n required: ['documents'],\n additionalProperties: false,\n },\n },\n {\n name: 'preflight',\n description:\n 'Trace cross-language impact from a producer analysis context. The '\n + 'context JSON is produced by scripts/capture-preflight.mjs.',\n inputSchema: {\n type: 'object',\n properties: {\n context: {\n type: 'string',\n description: 'Path to the captured preflight context JSON.',\n },\n runtime: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional runtime evidence document paths.',\n },\n expectations: { type: 'string' },\n revision: { type: 'string' },\n summary: { type: 'boolean' },\n limit: { type: 'integer', minimum: 1, maximum: 100 },\n explain: { type: 'string' },\n strict: { type: 'boolean' },\n compact: { type: 'boolean' },\n },\n required: ['context'],\n additionalProperties: false,\n },\n },\n {\n name: 'retentions',\n description:\n 'Produce external retention evidence for a native analyzer. Pass the '\n + 'result to cartograph dead --external-retentions.',\n inputSchema: {\n type: 'object',\n properties: {\n documents: DOCUMENTS_PROPERTY,\n producer: {\n type: 'string',\n enum: ['cartograph'],\n description: 'Only cartograph is supported today.',\n },\n },\n required: ['documents', 'producer'],\n additionalProperties: false,\n },\n },\n];\n\nconst TOOL_NAMES = new Map(TOOL_DEFINITIONS.map((tool) => [tool.name, tool]));\n\n/** 세션이 외부에 필요로 하는 주입이다. */\nexport interface McpServerDependencies {\n readonly readTextFile: ReadTextFile;\n readonly writeTextFile?: WriteTextFile | undefined;\n readonly producerVersion?: string | undefined;\n}\n\n/** JSON-RPC 메시지 한 건을 처리해 응답 라인을 반환하는 세션이다. */\nexport interface McpSession {\n readonly handleLine: (line: string) => Promise<string | undefined>;\n}\n\n/**\n * MCP 세션을 만든다. 반환된 handleLine은 한 줄의 JSON-RPC 메시지를 받아\n * 응답이 필요하면 직렬화된 한 줄을, 알림이면 undefined를 돌려준다.\n */\nexport function createMcpSession(dependencies: McpServerDependencies): McpSession {\n return {\n handleLine: (line) => handleMessage(dependencies, line),\n };\n}\n\nasync function handleMessage(\n dependencies: McpServerDependencies,\n line: string,\n): Promise<string | undefined> {\n let message: unknown;\n try {\n message = JSON.parse(line);\n } catch {\n return JSON.stringify(errorResponse(undefined, PARSE_ERROR, 'Parse error'));\n }\n if (Array.isArray(message)) {\n // MCP는 배치를 쓰지 않는다. 첫 요소의 id도 모르므로 요청 전체를 거부한다.\n return JSON.stringify(\n errorResponse(undefined, INVALID_REQUEST, 'Batch requests are not supported'),\n );\n }\n if (!isRequestMessage(message)) {\n // JSON-RPC 2.0은 검출 가능한 id를 그대로 에코하라고 요구한다 — 형식이\n // 스칼라인 요청의 id는 거절 응답에도 보존한다.\n return JSON.stringify(\n errorResponse(scalarId(message), INVALID_REQUEST, 'Invalid request'),\n );\n }\n if (message.id === undefined) {\n // 알림은 응답하지 않는다. initialized 등 어떤 알림이든 조용히 무시한다.\n return undefined;\n }\n if (!isScalarId(message.id)) {\n return JSON.stringify(\n errorResponse(null, INVALID_REQUEST, 'Request id must be a string, number, or null'),\n );\n }\n let response: JsonRpcResponse;\n try {\n response = await dispatchRequest(dependencies, message.method, message.id, message.params);\n } catch {\n // 명령 실행이나 파일 읽기의 내부 예외로 서버가 죽으면 안 된다 — 세션을 유지하며\n // -32603으로 돌려준다. 원인 본문은 입력 경로·내용을 담을 수 있어 노출하지 않는다.\n response = errorResponse(message.id, INTERNAL_ERROR, 'Internal error');\n }\n return JSON.stringify(response);\n}\n\ntype JsonRpcId = string | number | null;\n\nfunction isScalarId(value: unknown): value is JsonRpcId {\n return value === null || typeof value === 'string' || typeof value === 'number';\n}\n\nfunction scalarId(message: unknown): JsonRpcId {\n if (typeof message !== 'object' || message === null) return null;\n const id = (message as Record<string, unknown>).id;\n return isScalarId(id) ? id : null;\n}\n\ninterface RequestMessage {\n readonly jsonrpc: '2.0';\n readonly id?: string | number | null;\n readonly method: string;\n readonly params?: unknown;\n}\n\nfunction isRequestMessage(value: unknown): value is RequestMessage {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return candidate.jsonrpc === '2.0' && typeof candidate.method === 'string';\n}\n\ninterface JsonRpcResponse {\n readonly jsonrpc: '2.0';\n readonly id: string | number | null;\n readonly result?: unknown;\n readonly error?: { readonly code: number; readonly message: string };\n}\n\nasync function dispatchRequest(\n dependencies: McpServerDependencies,\n method: string,\n id: string | number | null,\n params: unknown,\n): Promise<JsonRpcResponse> {\n switch (method) {\n case 'initialize':\n return {\n jsonrpc: '2.0',\n id,\n result: initializeResult(dependencies, params),\n };\n case 'ping':\n return { jsonrpc: '2.0', id, result: {} };\n case 'tools/list':\n return {\n jsonrpc: '2.0',\n id,\n result: { tools: TOOL_DEFINITIONS },\n };\n case 'tools/call':\n return toolsCallResult(dependencies, id, params);\n default:\n return errorResponse(id, METHOD_NOT_FOUND, 'Method not found');\n }\n}\n\n/** initialize 요청의 프로토콜 버전을 협상하고 서버 정보를 돌려준다. */\nfunction initializeResult(\n dependencies: McpServerDependencies,\n params: unknown,\n): Record<string, unknown> {\n const requested = isInitializeParams(params) ? params.protocolVersion : undefined;\n const protocolVersion = requested !== undefined\n && (SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(requested)\n ? requested\n : SUPPORTED_PROTOCOL_VERSIONS[0];\n return {\n protocolVersion,\n capabilities: { tools: {} },\n serverInfo: {\n name: 'isthmus',\n version: dependencies.producerVersion ?? 'unknown',\n },\n };\n}\n\nfunction isInitializeParams(\n value: unknown,\n): value is { readonly protocolVersion: string } {\n if (typeof value !== 'object' || value === null) return false;\n return typeof (value as Record<string, unknown>).protocolVersion === 'string';\n}\n\nasync function toolsCallResult(\n dependencies: McpServerDependencies,\n id: string | number | null,\n params: unknown,\n): Promise<JsonRpcResponse> {\n if (!isToolsCallParams(params)) {\n return errorResponse(id, INVALID_PARAMS, 'tools/call requires name and arguments');\n }\n const tool = TOOL_NAMES.get(params.name);\n if (tool === undefined) {\n return errorResponse(id, INVALID_PARAMS, `Unknown tool: ${params.name}`);\n }\n const invalid = validateToolArguments(tool, params.arguments ?? {});\n if (invalid !== undefined) {\n return errorResponse(id, INVALID_PARAMS, invalid);\n }\n const argv = buildToolArgv(params.name, params.arguments ?? {});\n if (argv === undefined) {\n return errorResponse(id, INVALID_PARAMS, 'Invalid tool arguments');\n }\n const result = await runToolCommand(dependencies, argv);\n return { jsonrpc: '2.0', id, result: commandResultContent(result) };\n}\n\nfunction isToolsCallParams(\n value: unknown,\n): value is { readonly name: string; readonly arguments?: Record<string, unknown> } {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return typeof candidate.name === 'string';\n}\n\n/**\n * tools/list에 광고한 inputSchema 그대로 인자를 검증한다. 스키마와 검증이\n * 같은 정의에서 파생되므로 둘이 어긋날 수 없다 — 알 수 없는 키·타입 위반·\n * enum 밖 값·범위 밖 정수는 명령 실행 전에 -32602로 거부한다.\n */\nfunction validateToolArguments(\n tool: McpToolDefinition,\n args: Record<string, unknown>,\n): string | undefined {\n const { properties, required = [] } = tool.inputSchema;\n for (const key of Object.keys(args)) {\n if (!(key in properties)) return `Unknown argument: ${key}`;\n }\n for (const key of required) {\n if (args[key] === undefined) return `Missing required argument: ${key}`;\n }\n for (const [key, value] of Object.entries(args)) {\n const property = properties[key] as {\n type?: string; enum?: readonly unknown[]; items?: { type?: string };\n minimum?: number; maximum?: number; minLength?: number;\n };\n switch (property.type) {\n case 'boolean':\n if (typeof value !== 'boolean') return `Argument ${key} must be a boolean`;\n break;\n case 'string':\n if (typeof value !== 'string') return `Argument ${key} must be a string`;\n if (property.minLength !== undefined && value.length < property.minLength) {\n return `Argument ${key} is too short`;\n }\n break;\n case 'integer':\n if (typeof value !== 'number' || !Number.isInteger(value)) {\n return `Argument ${key} must be an integer`;\n }\n if (property.minimum !== undefined && value < property.minimum) {\n return `Argument ${key} is below the minimum`;\n }\n if (property.maximum !== undefined && value > property.maximum) {\n return `Argument ${key} exceeds the maximum`;\n }\n break;\n case 'array':\n if (!Array.isArray(value) || (property.items?.type === 'string'\n && !value.every((item) => typeof item === 'string'))) {\n return `Argument ${key} must be an array of strings`;\n }\n break;\n default:\n break;\n }\n if (property.enum !== undefined && !property.enum.includes(value)) {\n return `Argument ${key} is not an allowed value`;\n }\n }\n return undefined;\n}\n\n/**\n * 도구 인자를 CLI argv로 변환한다. 스키마 검증을 통과한 인자만 들어오지만,\n * selector 조합처럼 스키마로 표현 못 하는 제약은 여기서도 거른다.\n */\nfunction buildToolArgv(\n toolName: string,\n args: Record<string, unknown>,\n): readonly string[] | undefined {\n switch (toolName) {\n case 'check':\n return withDocuments('check', args, (documents) => [\n ...booleanFlag('--strict', args.strict),\n ...documents,\n ]);\n case 'query':\n return typeof args.name === 'string' && args.name.length > 0\n ? withDocuments('query', args, (documents) => [args.name as string, ...documents])\n : undefined;\n case 'graph':\n return withDocuments('graph', args, (documents) => [\n ...enumFlag('--format', args.format, ['json', 'dot', 'mermaid']),\n ...documents,\n ]);\n case 'diff': {\n const before = stringArray(args.before);\n const after = stringArray(args.after);\n if (before === undefined || after === undefined) return undefined;\n return [\n 'diff',\n '--before',\n ...before,\n '--after',\n ...after,\n ...booleanFlag('--strict', args.strict),\n ];\n }\n case 'impact': {\n const selectorCount = ['file', 'symbol', 'changes']\n .filter((key) => args[key] !== undefined).length;\n if (selectorCount !== 1) return undefined;\n return withDocuments('impact', args, (documents) => [\n ...valueFlag('--file', args.file),\n ...valueFlag('--symbol', args.symbol),\n ...valueFlag('--changes', args.changes),\n ...documents,\n ...valueFlag('--runtime', args.runtime),\n ...valueFlag('--revision', args.revision),\n ...booleanFlag('--strict', args.strict),\n ...booleanFlag('--compact', args.compact),\n ]);\n }\n case 'preflight': {\n if (typeof args.context !== 'string' || args.context.length === 0) {\n return undefined;\n }\n const runtimePaths = args.runtime === undefined\n ? []\n : stringArray(args.runtime);\n if (runtimePaths === undefined) return undefined;\n const argv = [\n 'preflight',\n args.context,\n ...runtimePaths,\n ...valueFlag('--expectations', args.expectations),\n ...valueFlag('--revision', args.revision),\n ...booleanFlag('--summary', args.summary),\n ...booleanFlag('--strict', args.strict),\n ...booleanFlag('--compact', args.compact),\n ];\n if (typeof args.limit === 'number' && Number.isInteger(args.limit)) {\n argv.push('--limit', String(args.limit));\n }\n argv.push(...valueFlag('--explain', args.explain));\n return argv;\n }\n case 'retentions':\n if (args.producer !== 'cartograph') return undefined;\n return withDocuments('retentions', args, (documents) => [\n ...documents,\n '--for',\n 'cartograph',\n ]);\n default:\n return undefined;\n }\n}\n\n/** documents 인자를 검증하고 argv 앞에 명령 이름을 붙인다. */\nfunction withDocuments(\n command: string,\n args: Record<string, unknown>,\n build: (documents: readonly string[]) => readonly string[],\n): readonly string[] | undefined {\n const documents = stringArray(args.documents);\n if (documents === undefined || documents.length < 2) return undefined;\n return [command, ...build(documents)];\n}\n\nfunction stringArray(value: unknown): readonly string[] | undefined {\n if (!Array.isArray(value)) return undefined;\n return value.every((item) => typeof item === 'string')\n ? (value as readonly string[])\n : undefined;\n}\n\nfunction booleanFlag(flag: string, value: unknown): readonly string[] {\n return value === true ? [flag] : [];\n}\n\nfunction valueFlag(flag: string, value: unknown): readonly string[] {\n return typeof value === 'string' && value.length > 0 ? [flag, value] : [];\n}\n\nfunction enumFlag(\n flag: string,\n value: unknown,\n allowed: readonly string[],\n): readonly string[] {\n return typeof value === 'string' && allowed.includes(value)\n ? [flag, value]\n : [];\n}\n\n/** 변환된 argv를 대응하는 명령 실행 함수로 라우팅한다. */\nasync function runToolCommand(\n dependencies: McpServerDependencies,\n argv: readonly string[],\n): Promise<CommandResult> {\n switch (argv[0]) {\n case 'check':\n return runCheckCommand(\n argv,\n dependencies.readTextFile,\n dependencies.writeTextFile,\n undefined,\n dependencies.producerVersion,\n );\n case 'query':\n return runQueryCommand(argv, dependencies.readTextFile);\n case 'graph':\n return runGraphCommand(argv, dependencies.readTextFile);\n case 'diff':\n return runDiffCommand(argv, dependencies.readTextFile);\n case 'impact':\n return runImpactCommand(argv, dependencies.readTextFile);\n case 'preflight':\n return runPreflightCommand(argv, dependencies.readTextFile);\n case 'retentions':\n return dependencies.producerVersion === undefined\n ? {\n standardOutput: '',\n standardError: 'Unable to read package metadata.\\n',\n exitCode: 2,\n }\n : runRetentionsCommand(\n argv,\n dependencies.readTextFile,\n () => new Date(),\n dependencies.producerVersion,\n );\n default:\n return {\n standardOutput: '',\n standardError: 'Unknown command.\\n',\n exitCode: 64,\n };\n }\n}\n\n/**\n * 명령 결과를 MCP 도구 응답으로 옮긴다.\n *\n * stdout에 보고서 문서가 있으면 도구는 일을 한 것이므로 isError를 세지 않는다 —\n * query notFound(64)나 check strict(1)의 정답 문서를 프로토콜 오류로 보내면\n * 소비자가 한계와 조사 결과를 놓친다. 문서가 없는 실패(usage 64·internal 2)만\n * isError로 표시하고 stderr를 함께 실어 원인을 남긴다.\n */\nfunction commandResultContent(result: CommandResult): Record<string, unknown> {\n const content: { type: 'text'; text: string }[] = [];\n if (result.standardOutput.length > 0) {\n content.push({ type: 'text', text: result.standardOutput });\n }\n if (result.standardError.length > 0) {\n content.push({ type: 'text', text: result.standardError });\n }\n if (content.length === 0) {\n content.push({ type: 'text', text: '(no output)\\n' });\n }\n return {\n content,\n // 종료 코드 2는 항상 내부 오류다 — 부분 stdout이 있어도 도구 오류로 표시한다.\n // 1/64는 문서가 있으면 정답(조사 결과·notFound)이고, 문서 없는 실패만 오류다.\n isError: result.exitCode === 2\n || (result.exitCode !== 0 && result.standardOutput.length === 0),\n };\n}\n\nfunction errorResponse(\n id: string | number | null | undefined,\n code: number,\n message: string,\n): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code, message },\n };\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CommandResult, ReadTextFile } from './command-support.ts';
|
|
2
|
+
/** 같은 실행 문맥의 언어별 영향 결과를 연결해 사람이거나 AI인 소비자에게 전달한다. */
|
|
3
|
+
export declare function runPreflightCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
|
|
4
|
+
/** 입력 생성은 producer workflow가 맡으며 CLI는 JSON만 읽는다. */
|
|
5
|
+
export declare const preflightUsage: string;
|