@shiftleftpt/sbd-toe-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +35 -0
- package/LICENSE +201 -0
- package/README.md +323 -0
- package/data/publish/algolia_docs_records.json +148847 -0
- package/data/publish/algolia_docs_records_enriched.json +194004 -0
- package/data/publish/algolia_entities_records.json +74715 -0
- package/data/publish/algolia_entities_records_enriched.json +177587 -0
- package/data/publish/algolia_index_settings.json +102 -0
- package/data/publish/sbd-toe-index-compact.json +111 -0
- package/data/reports/run_manifest.json +10 -0
- package/dist/backend/semantic-index-gateway.d.ts +25 -0
- package/dist/backend/semantic-index-gateway.js +555 -0
- package/dist/backend/semantic-index-gateway.js.map +1 -0
- package/dist/backend/semantic-index-gateway.test.d.ts +1 -0
- package/dist/backend/semantic-index-gateway.test.js +384 -0
- package/dist/backend/semantic-index-gateway.test.js.map +1 -0
- package/dist/bootstrap/checkout-backend.d.ts +31 -0
- package/dist/bootstrap/checkout-backend.js +136 -0
- package/dist/bootstrap/checkout-backend.js.map +1 -0
- package/dist/bootstrap/checkout-backend.test.d.ts +1 -0
- package/dist/bootstrap/checkout-backend.test.js +158 -0
- package/dist/bootstrap/checkout-backend.test.js.map +1 -0
- package/dist/bootstrap/release-checkout.d.ts +8 -0
- package/dist/bootstrap/release-checkout.js +168 -0
- package/dist/bootstrap/release-checkout.js.map +1 -0
- package/dist/bootstrap/release-checkout.test.d.ts +1 -0
- package/dist/bootstrap/release-checkout.test.js +137 -0
- package/dist/bootstrap/release-checkout.test.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/ask-manual.d.ts +13 -0
- package/dist/orchestrator/ask-manual.js +202 -0
- package/dist/orchestrator/ask-manual.js.map +1 -0
- package/dist/prompt/build-answer-prompt.d.ts +2 -0
- package/dist/prompt/build-answer-prompt.js +51 -0
- package/dist/prompt/build-answer-prompt.js.map +1 -0
- package/dist/prompt/system-prompt.d.ts +1 -0
- package/dist/prompt/system-prompt.js +94 -0
- package/dist/prompt/system-prompt.js.map +1 -0
- package/dist/resources/sbd-toe-resources.d.ts +18 -0
- package/dist/resources/sbd-toe-resources.js +164 -0
- package/dist/resources/sbd-toe-resources.js.map +1 -0
- package/dist/resources/sbd-toe-resources.test.d.ts +1 -0
- package/dist/resources/sbd-toe-resources.test.js +134 -0
- package/dist/resources/sbd-toe-resources.test.js.map +1 -0
- package/dist/test-utils.d.ts +153 -0
- package/dist/test-utils.js +176 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/tools/generate-document.d.ts +22 -0
- package/dist/tools/generate-document.js +392 -0
- package/dist/tools/generate-document.js.map +1 -0
- package/dist/tools/generate-document.test.d.ts +1 -0
- package/dist/tools/generate-document.test.js +189 -0
- package/dist/tools/generate-document.test.js.map +1 -0
- package/dist/tools/map-review-scope.d.ts +20 -0
- package/dist/tools/map-review-scope.js +299 -0
- package/dist/tools/map-review-scope.js.map +1 -0
- package/dist/tools/map-review-scope.test.d.ts +1 -0
- package/dist/tools/map-review-scope.test.js +204 -0
- package/dist/tools/map-review-scope.test.js.map +1 -0
- package/dist/tools/plan-repo-governance.d.ts +41 -0
- package/dist/tools/plan-repo-governance.js +509 -0
- package/dist/tools/plan-repo-governance.js.map +1 -0
- package/dist/tools/plan-repo-governance.test.d.ts +1 -0
- package/dist/tools/plan-repo-governance.test.js +237 -0
- package/dist/tools/plan-repo-governance.test.js.map +1 -0
- package/dist/tools/structured-tools.d.ts +5 -0
- package/dist/tools/structured-tools.js +310 -0
- package/dist/tools/structured-tools.js.map +1 -0
- package/dist/tools/structured-tools.test.d.ts +1 -0
- package/dist/tools/structured-tools.test.js +459 -0
- package/dist/tools/structured-tools.test.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/upstream/backend-contract.d.ts +3 -0
- package/dist/upstream/backend-contract.js +37 -0
- package/dist/upstream/backend-contract.js.map +1 -0
- package/dist/validators/ai-disclosure.d.ts +39 -0
- package/dist/validators/ai-disclosure.js +183 -0
- package/dist/validators/ai-disclosure.js.map +1 -0
- package/dist/validators/ai-disclosure.test.d.ts +1 -0
- package/dist/validators/ai-disclosure.test.js +244 -0
- package/dist/validators/ai-disclosure.test.js.map +1 -0
- package/examples/claude-desktop.json +8 -0
- package/examples/vscode.mcp.json +9 -0
- package/package.json +50 -0
- package/prompts/sbd-toe-chat-system.md +71 -0
- package/smithery.yaml +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import readline from "node:readline";
|
|
5
|
+
import { getConfig, resolveAppPath } from "./config.js";
|
|
6
|
+
import { formatSampledAnswerResult, inspectManualRetrieval, prepareManualAnsweringContext, searchManualQuestion } from "./orchestrator/ask-manual.js";
|
|
7
|
+
import { loadSystemPromptTemplate } from "./prompt/system-prompt.js";
|
|
8
|
+
import { getSnapshotCache, retrievePublishedContext } from "./backend/semantic-index-gateway.js";
|
|
9
|
+
import { handleGetSbdToeChapterBrief, handleListSbdToeChapters, handleMapSbdToeApplicability, handleQuerySbdToeEntities } from "./tools/structured-tools.js";
|
|
10
|
+
import { handleGenerateDocument } from "./tools/generate-document.js";
|
|
11
|
+
import { handleMapSbdToeReviewScope } from "./tools/map-review-scope.js";
|
|
12
|
+
import { handlePlanRepoGovernance } from "./tools/plan-repo-governance.js";
|
|
13
|
+
import { buildChapterApplicabilityJson, buildSetupAgentPrompt, buildSkillTemplateMarkdown } from "./resources/sbd-toe-resources.js";
|
|
14
|
+
const PROTOCOL_VERSION = "2025-03-26";
|
|
15
|
+
const LOG_LEVELS = [
|
|
16
|
+
"debug",
|
|
17
|
+
"info",
|
|
18
|
+
"notice",
|
|
19
|
+
"warning",
|
|
20
|
+
"error",
|
|
21
|
+
"critical",
|
|
22
|
+
"alert",
|
|
23
|
+
"emergency"
|
|
24
|
+
];
|
|
25
|
+
class McpRuntime {
|
|
26
|
+
nextRequestId = 10_000;
|
|
27
|
+
pending = new Map();
|
|
28
|
+
clientCapabilities = {};
|
|
29
|
+
initialized = false;
|
|
30
|
+
logLevel = "info";
|
|
31
|
+
constructor() {
|
|
32
|
+
const rl = readline.createInterface({
|
|
33
|
+
input: process.stdin,
|
|
34
|
+
crlfDelay: Infinity
|
|
35
|
+
});
|
|
36
|
+
rl.on("line", (line) => {
|
|
37
|
+
void this.handleIncomingLine(line);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
writeMessage(message) {
|
|
41
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
42
|
+
}
|
|
43
|
+
normalizeLogLevel(level) {
|
|
44
|
+
if (typeof level === "string") {
|
|
45
|
+
const normalized = LOG_LEVELS.find((candidate) => candidate === level);
|
|
46
|
+
if (normalized) {
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return "info";
|
|
51
|
+
}
|
|
52
|
+
shouldLog(level) {
|
|
53
|
+
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.logLevel);
|
|
54
|
+
}
|
|
55
|
+
getRequestId(id) {
|
|
56
|
+
return id === null || id === undefined ? undefined : String(id);
|
|
57
|
+
}
|
|
58
|
+
fingerprintQuestion(question) {
|
|
59
|
+
return createHash("sha256").update(question, "utf8").digest("hex").slice(0, 12);
|
|
60
|
+
}
|
|
61
|
+
getQuestionMetadata(args) {
|
|
62
|
+
const question = args.question;
|
|
63
|
+
if (typeof question !== "string") {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
question_length: question.length,
|
|
68
|
+
question_fingerprint: this.fingerprintQuestion(question)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
summarizeError(error) {
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
return {
|
|
74
|
+
error_name: error.name,
|
|
75
|
+
message: error.message.split("\n", 1)[0] ?? "Unexpected error"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
message: String(error)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
sendResponse(id, result) {
|
|
83
|
+
this.writeMessage({
|
|
84
|
+
jsonrpc: "2.0",
|
|
85
|
+
id,
|
|
86
|
+
result
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
sendError(id, code, message, data) {
|
|
90
|
+
this.writeMessage({
|
|
91
|
+
jsonrpc: "2.0",
|
|
92
|
+
id,
|
|
93
|
+
error: {
|
|
94
|
+
code,
|
|
95
|
+
message,
|
|
96
|
+
...(data === undefined ? {} : { data })
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
sendNotification(method, params) {
|
|
101
|
+
this.writeMessage({
|
|
102
|
+
jsonrpc: "2.0",
|
|
103
|
+
method,
|
|
104
|
+
...(params === undefined ? {} : { params })
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async handleIncomingLine(line) {
|
|
108
|
+
const trimmed = line.trim();
|
|
109
|
+
if (trimmed.length === 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let parsed;
|
|
113
|
+
try {
|
|
114
|
+
parsed = JSON.parse(trimmed);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
await this.log("warning", {
|
|
118
|
+
event_type: "rpc.parse_error",
|
|
119
|
+
outcome: "failed",
|
|
120
|
+
...this.summarizeError(error)
|
|
121
|
+
});
|
|
122
|
+
this.sendError(null, -32700, "Parse error", {
|
|
123
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(parsed)) {
|
|
128
|
+
for (const item of parsed) {
|
|
129
|
+
await this.handleMessage(item);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
await this.handleMessage(parsed);
|
|
134
|
+
}
|
|
135
|
+
resolvePending(message) {
|
|
136
|
+
if (message.id === null) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const pending = this.pending.get(message.id);
|
|
140
|
+
if (!pending) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
this.pending.delete(message.id);
|
|
144
|
+
if ("error" in message) {
|
|
145
|
+
pending.reject(new Error(message.error.message));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
pending.resolve(message.result);
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
async handleMessage(message) {
|
|
153
|
+
if ("id" in message && ("result" in message || "error" in message)) {
|
|
154
|
+
this.resolvePending(message);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!("method" in message)) {
|
|
158
|
+
await this.log("warning", {
|
|
159
|
+
event_type: "rpc.invalid_request",
|
|
160
|
+
outcome: "failed",
|
|
161
|
+
message: "Received JSON-RPC message without method"
|
|
162
|
+
});
|
|
163
|
+
this.sendError(null, -32600, "Invalid Request");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!("id" in message)) {
|
|
167
|
+
this.handleNotification(message);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await this.handleRequest(message);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
await this.log("error", {
|
|
175
|
+
event_type: "rpc.unhandled_error",
|
|
176
|
+
outcome: "failed",
|
|
177
|
+
request_id: this.getRequestId(message.id),
|
|
178
|
+
rpc_method: message.method,
|
|
179
|
+
...this.summarizeError(error)
|
|
180
|
+
});
|
|
181
|
+
this.sendError(message.id, -32603, error instanceof Error ? error.message : "Internal error");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
handleNotification(message) {
|
|
185
|
+
if (message.method === "notifications/initialized") {
|
|
186
|
+
this.initialized = true;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (message.method === "notifications/cancelled") {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async handleRequest(request) {
|
|
194
|
+
switch (request.method) {
|
|
195
|
+
case "initialize":
|
|
196
|
+
this.handleInitialize(request);
|
|
197
|
+
return;
|
|
198
|
+
case "ping":
|
|
199
|
+
this.sendResponse(request.id, {});
|
|
200
|
+
return;
|
|
201
|
+
case "logging/setLevel":
|
|
202
|
+
this.handleSetLogLevel(request);
|
|
203
|
+
return;
|
|
204
|
+
case "tools/list":
|
|
205
|
+
this.handleToolsList(request);
|
|
206
|
+
return;
|
|
207
|
+
case "tools/call":
|
|
208
|
+
await this.handleToolsCall(request);
|
|
209
|
+
return;
|
|
210
|
+
case "prompts/list":
|
|
211
|
+
this.handlePromptsList(request);
|
|
212
|
+
return;
|
|
213
|
+
case "prompts/get":
|
|
214
|
+
this.handlePromptGet(request);
|
|
215
|
+
return;
|
|
216
|
+
case "resources/list":
|
|
217
|
+
this.handleResourcesList(request);
|
|
218
|
+
return;
|
|
219
|
+
case "resources/read":
|
|
220
|
+
await this.handleResourcesRead(request);
|
|
221
|
+
return;
|
|
222
|
+
default:
|
|
223
|
+
this.sendError(request.id, -32601, `Method not found: ${request.method}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
handleInitialize(request) {
|
|
227
|
+
const params = request.params ?? {};
|
|
228
|
+
this.clientCapabilities =
|
|
229
|
+
typeof params.capabilities === "object" && params.capabilities !== null
|
|
230
|
+
? params.capabilities
|
|
231
|
+
: {};
|
|
232
|
+
this.sendResponse(request.id, {
|
|
233
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
234
|
+
capabilities: {
|
|
235
|
+
logging: {},
|
|
236
|
+
prompts: {
|
|
237
|
+
listChanged: false
|
|
238
|
+
},
|
|
239
|
+
resources: {
|
|
240
|
+
subscribe: false,
|
|
241
|
+
listChanged: false
|
|
242
|
+
},
|
|
243
|
+
tools: {
|
|
244
|
+
listChanged: false
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
serverInfo: {
|
|
248
|
+
name: "sbd-toe-mcp-poc",
|
|
249
|
+
version: "0.1.0"
|
|
250
|
+
},
|
|
251
|
+
instructions: "Use search_sbd_toe_manual to retrieve grounded SbD-ToE context before answering."
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
handleSetLogLevel(request) {
|
|
255
|
+
const previousLevel = this.logLevel;
|
|
256
|
+
const level = this.normalizeLogLevel(request.params?.level);
|
|
257
|
+
this.logLevel = level;
|
|
258
|
+
this.sendResponse(request.id, {});
|
|
259
|
+
void this.log("notice", {
|
|
260
|
+
event_type: "logging.level_changed",
|
|
261
|
+
outcome: "succeeded",
|
|
262
|
+
request_id: this.getRequestId(request.id),
|
|
263
|
+
rpc_method: request.method,
|
|
264
|
+
previous_level: previousLevel,
|
|
265
|
+
new_level: level,
|
|
266
|
+
message: "Updated runtime log level"
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
async log(level, event) {
|
|
270
|
+
if (!this.shouldLog(level)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
this.sendNotification("notifications/message", {
|
|
274
|
+
level,
|
|
275
|
+
logger: "sbd-toe-mcp-poc",
|
|
276
|
+
data: {
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
278
|
+
component: "mcp-runtime",
|
|
279
|
+
...event
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
handleToolsList(request) {
|
|
284
|
+
this.sendResponse(request.id, {
|
|
285
|
+
tools: [
|
|
286
|
+
{
|
|
287
|
+
name: "search_sbd_toe_manual",
|
|
288
|
+
title: "Search SbD-ToE Manual",
|
|
289
|
+
description: "Recupera contexto grounded do manual SbD-ToE a partir do snapshot semântico local embutido.",
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {
|
|
293
|
+
question: {
|
|
294
|
+
type: "string",
|
|
295
|
+
description: "Pergunta em linguagem natural sobre o manual."
|
|
296
|
+
},
|
|
297
|
+
debug: {
|
|
298
|
+
type: "boolean",
|
|
299
|
+
description: "Quando true, anexa o debug completo do retrieval."
|
|
300
|
+
},
|
|
301
|
+
topK: {
|
|
302
|
+
type: "integer",
|
|
303
|
+
minimum: 1,
|
|
304
|
+
maximum: 15,
|
|
305
|
+
description: "Número máximo de records usados como contexto."
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
required: ["question"],
|
|
309
|
+
additionalProperties: false
|
|
310
|
+
},
|
|
311
|
+
annotations: {
|
|
312
|
+
readOnlyHint: true
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "answer_sbd_toe_manual",
|
|
317
|
+
title: "Answer SbD-ToE Manual",
|
|
318
|
+
description: "Faz retrieval do manual SbD-ToE e pede a resposta final ao modelo configurado no cliente via sampling MCP.",
|
|
319
|
+
inputSchema: {
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
question: {
|
|
323
|
+
type: "string",
|
|
324
|
+
description: "Pergunta em linguagem natural sobre o manual."
|
|
325
|
+
},
|
|
326
|
+
debug: {
|
|
327
|
+
type: "boolean",
|
|
328
|
+
description: "Quando true, anexa o debug completo."
|
|
329
|
+
},
|
|
330
|
+
topK: {
|
|
331
|
+
type: "integer",
|
|
332
|
+
minimum: 1,
|
|
333
|
+
maximum: 15,
|
|
334
|
+
description: "Número máximo de records usados como contexto."
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
required: ["question"],
|
|
338
|
+
additionalProperties: false
|
|
339
|
+
},
|
|
340
|
+
annotations: {
|
|
341
|
+
readOnlyHint: true
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
name: "inspect_sbd_toe_retrieval",
|
|
346
|
+
title: "Inspect SbD-ToE Retrieval",
|
|
347
|
+
description: "Inspeciona retrieval, seleção de contexto e prompt final sem pedir resposta ao modelo do cliente.",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
question: {
|
|
352
|
+
type: "string",
|
|
353
|
+
description: "Pergunta a usar na inspeção do retrieval."
|
|
354
|
+
},
|
|
355
|
+
topK: {
|
|
356
|
+
type: "integer",
|
|
357
|
+
minimum: 1,
|
|
358
|
+
maximum: 15,
|
|
359
|
+
description: "Número máximo de records selecionados para o prompt."
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
required: ["question"],
|
|
363
|
+
additionalProperties: false
|
|
364
|
+
},
|
|
365
|
+
annotations: {
|
|
366
|
+
readOnlyHint: true
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "list_sbd_toe_chapters",
|
|
371
|
+
title: "List SbD-ToE Chapters",
|
|
372
|
+
description: "Lista os capítulos do manual SbD-ToE com id, título e aplicabilidade.",
|
|
373
|
+
inputSchema: {
|
|
374
|
+
type: "object",
|
|
375
|
+
properties: {
|
|
376
|
+
riskLevel: {
|
|
377
|
+
type: "string",
|
|
378
|
+
enum: ["L1", "L2", "L3"],
|
|
379
|
+
description: "Filtrar por nível de risco."
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
additionalProperties: false
|
|
383
|
+
},
|
|
384
|
+
annotations: { readOnlyHint: true }
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "query_sbd_toe_entities",
|
|
388
|
+
title: "Query SbD-ToE Entities",
|
|
389
|
+
description: "Consulta entidades do manual por query, tipo, capítulo ou nível de risco.",
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {
|
|
393
|
+
query: { type: "string", minLength: 1, maxLength: 200 },
|
|
394
|
+
entityType: { type: "string" },
|
|
395
|
+
chapterId: { type: "string" },
|
|
396
|
+
riskLevel: { type: "string", enum: ["L1", "L2", "L3"] },
|
|
397
|
+
topK: { type: "integer", minimum: 1, maximum: 15 }
|
|
398
|
+
},
|
|
399
|
+
required: ["query"],
|
|
400
|
+
additionalProperties: false
|
|
401
|
+
},
|
|
402
|
+
annotations: { readOnlyHint: true }
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
name: "get_sbd_toe_chapter_brief",
|
|
406
|
+
title: "Get SbD-ToE Chapter Brief",
|
|
407
|
+
description: "Devolve resumo operacional de um capítulo: papel, fases, artefactos, intent_topics.",
|
|
408
|
+
inputSchema: {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
chapterId: { type: "string", minLength: 1 }
|
|
412
|
+
},
|
|
413
|
+
required: ["chapterId"],
|
|
414
|
+
additionalProperties: false
|
|
415
|
+
},
|
|
416
|
+
annotations: { readOnlyHint: true }
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "plan_sbd_toe_repo_governance",
|
|
420
|
+
title: "Plan SbD-ToE Repo Governance",
|
|
421
|
+
description: "Dado o tipo de repositório, plataforma e nível de risco, devolve um plano de governança com controlos aplicáveis, checkpoints de baseline, checklist de evidências e recomendações de plataforma.",
|
|
422
|
+
inputSchema: {
|
|
423
|
+
type: "object",
|
|
424
|
+
properties: {
|
|
425
|
+
repoType: {
|
|
426
|
+
type: "string",
|
|
427
|
+
enum: ["library", "service", "webapp", "infrastructure", "pipeline", "monorepo"],
|
|
428
|
+
description: "Tipo de repositório."
|
|
429
|
+
},
|
|
430
|
+
platform: {
|
|
431
|
+
type: "string",
|
|
432
|
+
enum: ["github", "gitlab"],
|
|
433
|
+
description: "Plataforma de hosting do repositório."
|
|
434
|
+
},
|
|
435
|
+
riskLevel: {
|
|
436
|
+
type: "string",
|
|
437
|
+
enum: ["L1", "L2", "L3"],
|
|
438
|
+
description: "Nível de risco do projecto."
|
|
439
|
+
},
|
|
440
|
+
organizationContext: {
|
|
441
|
+
type: "object",
|
|
442
|
+
description: "Contexto organizacional opcional.",
|
|
443
|
+
properties: {
|
|
444
|
+
scale: { type: "string", enum: ["startup", "mid-size", "enterprise"] },
|
|
445
|
+
teamSize: { type: "integer", minimum: 1 },
|
|
446
|
+
enforcementLevel: { type: "string", enum: ["advisory", "enforced", "strict"] }
|
|
447
|
+
},
|
|
448
|
+
additionalProperties: false
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
required: ["repoType", "platform", "riskLevel"],
|
|
452
|
+
additionalProperties: false
|
|
453
|
+
},
|
|
454
|
+
annotations: { readOnlyHint: true }
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
name: "generate_document",
|
|
458
|
+
title: "Generate SbD-ToE Document",
|
|
459
|
+
description: "Gera o esqueleto estruturado de um documento SbD-ToE (secções, campos obrigatórios, critérios de aceitação) para um tipo e nível de risco.",
|
|
460
|
+
inputSchema: {
|
|
461
|
+
type: "object",
|
|
462
|
+
properties: {
|
|
463
|
+
type: {
|
|
464
|
+
type: "string",
|
|
465
|
+
enum: ["classification-template", "threat-model-template", "checklist", "training-plan", "secure-config"],
|
|
466
|
+
description: "Tipo de documento a gerar."
|
|
467
|
+
},
|
|
468
|
+
riskLevel: {
|
|
469
|
+
type: "string",
|
|
470
|
+
enum: ["L1", "L2", "L3"],
|
|
471
|
+
description: "Nível de risco do projecto."
|
|
472
|
+
},
|
|
473
|
+
context: {
|
|
474
|
+
type: "object",
|
|
475
|
+
description: "Contexto adicional do projecto (reservado, não usado na estrutura).",
|
|
476
|
+
additionalProperties: true
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
required: ["type", "riskLevel"],
|
|
480
|
+
additionalProperties: false
|
|
481
|
+
},
|
|
482
|
+
annotations: { readOnlyHint: true }
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: "map_sbd_toe_review_scope",
|
|
486
|
+
title: "Map SbD-ToE Review Scope",
|
|
487
|
+
description: "Dado um conjunto de ficheiros alterados, mapeia quais knowledge bundles SbD-ToE devem ser revistos, com reasoning explícito por path.",
|
|
488
|
+
inputSchema: {
|
|
489
|
+
type: "object",
|
|
490
|
+
properties: {
|
|
491
|
+
changedFiles: {
|
|
492
|
+
type: "array",
|
|
493
|
+
items: { type: "string" },
|
|
494
|
+
minItems: 1,
|
|
495
|
+
description: "Lista de paths relativos ao raiz do repositório."
|
|
496
|
+
},
|
|
497
|
+
riskLevel: {
|
|
498
|
+
type: "string",
|
|
499
|
+
enum: ["L1", "L2", "L3"],
|
|
500
|
+
description: "Nível de risco do projecto."
|
|
501
|
+
},
|
|
502
|
+
projectContext: {
|
|
503
|
+
type: "object",
|
|
504
|
+
description: "Contexto adicional do projecto (opcional).",
|
|
505
|
+
properties: {
|
|
506
|
+
repoRole: { type: "string" },
|
|
507
|
+
runtimeModel: { type: "string" },
|
|
508
|
+
distributionModel: { type: "string" },
|
|
509
|
+
hasCi: { type: "boolean" }
|
|
510
|
+
},
|
|
511
|
+
additionalProperties: false
|
|
512
|
+
},
|
|
513
|
+
diffSummary: {
|
|
514
|
+
type: "string",
|
|
515
|
+
description: "Resumo do diff (truncado a 500 chars)."
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
required: ["changedFiles", "riskLevel"],
|
|
519
|
+
additionalProperties: false
|
|
520
|
+
},
|
|
521
|
+
annotations: { readOnlyHint: true }
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "map_sbd_toe_applicability",
|
|
525
|
+
title: "Map SbD-ToE Applicability",
|
|
526
|
+
description: "Mapeia capítulos/controlos activos, condicionais e excluídos para um nível de risco L1/L2/L3. Suporta contexto de projecto para activar bundles relevantes.",
|
|
527
|
+
inputSchema: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: {
|
|
530
|
+
riskLevel: { type: "string", enum: ["L1", "L2", "L3"] },
|
|
531
|
+
technologies: {
|
|
532
|
+
type: "array",
|
|
533
|
+
items: {
|
|
534
|
+
type: "string",
|
|
535
|
+
enum: [
|
|
536
|
+
"containers", "serverless", "kubernetes", "ci-cd", "iac", "api-gateway",
|
|
537
|
+
"mobile", "spa", "microservices", "legacy-integration", "ml-ai", "data-pipeline",
|
|
538
|
+
"sca-sbom", "sast", "dast", "secrets-management", "monitoring", "iam",
|
|
539
|
+
"network-segmentation", "cryptography"
|
|
540
|
+
]
|
|
541
|
+
},
|
|
542
|
+
description: "Tecnologias usadas no projecto."
|
|
543
|
+
},
|
|
544
|
+
hasPersonalData: {
|
|
545
|
+
type: "boolean",
|
|
546
|
+
description: "O projecto processa dados pessoais?"
|
|
547
|
+
},
|
|
548
|
+
isPublicFacing: {
|
|
549
|
+
type: "boolean",
|
|
550
|
+
description: "O projecto tem exposição pública?"
|
|
551
|
+
},
|
|
552
|
+
projectRole: {
|
|
553
|
+
type: "string",
|
|
554
|
+
enum: ["developer", "architect", "security", "devops", "manager"],
|
|
555
|
+
description: "Papel do utilizador no projecto."
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
required: ["riskLevel"],
|
|
559
|
+
additionalProperties: false
|
|
560
|
+
},
|
|
561
|
+
annotations: { readOnlyHint: true }
|
|
562
|
+
}
|
|
563
|
+
]
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
getPromptDefinition() {
|
|
567
|
+
return {
|
|
568
|
+
name: "ask_sbd_toe_manual",
|
|
569
|
+
title: "Ask SbD-ToE Manual",
|
|
570
|
+
description: "Prompt MCP para orientar o chat do VS Code a responder perguntas sobre o manual SbD-ToE com grounding.",
|
|
571
|
+
arguments: [
|
|
572
|
+
{
|
|
573
|
+
name: "question",
|
|
574
|
+
description: "Pergunta sobre o manual SbD-ToE.",
|
|
575
|
+
required: true
|
|
576
|
+
}
|
|
577
|
+
]
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
handlePromptsList(request) {
|
|
581
|
+
this.sendResponse(request.id, {
|
|
582
|
+
prompts: [
|
|
583
|
+
this.getPromptDefinition(),
|
|
584
|
+
{
|
|
585
|
+
name: "setup_sbd_toe_agent",
|
|
586
|
+
title: "Setup SbD-ToE Agent",
|
|
587
|
+
description: "Prompt MCP para configurar um agente com o contexto e regras do manual SbD-ToE para um nível de risco.",
|
|
588
|
+
arguments: [
|
|
589
|
+
{
|
|
590
|
+
name: "riskLevel",
|
|
591
|
+
description: "Nível de risco do projecto: L1, L2 ou L3.",
|
|
592
|
+
required: true
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "projectRole",
|
|
596
|
+
description: "Papel ou descrição do projecto (opcional).",
|
|
597
|
+
required: false
|
|
598
|
+
}
|
|
599
|
+
]
|
|
600
|
+
}
|
|
601
|
+
]
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
handlePromptGet(request) {
|
|
605
|
+
const name = typeof request.params?.name === "string" ? request.params.name : "";
|
|
606
|
+
const args = typeof request.params?.arguments === "object" && request.params.arguments !== null
|
|
607
|
+
? request.params.arguments
|
|
608
|
+
: {};
|
|
609
|
+
if (name === "ask_sbd_toe_manual") {
|
|
610
|
+
const question = typeof args.question === "string" ? args.question : "";
|
|
611
|
+
const promptText = `${loadSystemPromptTemplate()}\n\n` +
|
|
612
|
+
"Use a ferramenta `search_sbd_toe_manual` antes de responder.\n" +
|
|
613
|
+
`Question: ${question}`;
|
|
614
|
+
this.sendResponse(request.id, {
|
|
615
|
+
description: "Prompt grounded para perguntas sobre o manual SbD-ToE.",
|
|
616
|
+
messages: [
|
|
617
|
+
{
|
|
618
|
+
role: "user",
|
|
619
|
+
content: {
|
|
620
|
+
type: "text",
|
|
621
|
+
text: promptText
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
]
|
|
625
|
+
});
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (name === "setup_sbd_toe_agent") {
|
|
629
|
+
const riskLevel = args["riskLevel"];
|
|
630
|
+
if (typeof riskLevel !== "string" || !["L1", "L2", "L3"].includes(riskLevel)) {
|
|
631
|
+
this.sendError(request.id, -32602, 'O argumento "riskLevel" é obrigatório e deve ser L1, L2 ou L3.');
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const projectRole = typeof args["projectRole"] === "string" ? args["projectRole"] : undefined;
|
|
635
|
+
const promptText = buildSetupAgentPrompt(riskLevel, projectRole);
|
|
636
|
+
this.sendResponse(request.id, {
|
|
637
|
+
description: "Prompt para configurar um agente com o contexto SbD-ToE.",
|
|
638
|
+
messages: [
|
|
639
|
+
{
|
|
640
|
+
role: "user",
|
|
641
|
+
content: {
|
|
642
|
+
type: "text",
|
|
643
|
+
text: promptText
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
]
|
|
647
|
+
});
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
this.sendError(request.id, -32602, `Prompt desconhecida: ${name}`);
|
|
651
|
+
}
|
|
652
|
+
handleResourcesList(request) {
|
|
653
|
+
this.sendResponse(request.id, {
|
|
654
|
+
resources: [
|
|
655
|
+
{
|
|
656
|
+
uri: "sbd://toe/skill-template/{riskLevel}/{projectRole}",
|
|
657
|
+
name: "SbD-ToE Skill Template",
|
|
658
|
+
description: "Template de skill/instructions SbD-ToE para um nível de risco e papel de projecto.",
|
|
659
|
+
mimeType: "text/markdown"
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
uri: "sbd://toe/chapter-applicability/{riskLevel}",
|
|
663
|
+
name: "SbD-ToE Chapter Applicability",
|
|
664
|
+
description: "Capítulos activos, condicionais e excluídos para um nível de risco L1/L2/L3.",
|
|
665
|
+
mimeType: "application/json"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
uri: "sbd://toe/index-compact",
|
|
669
|
+
name: "SbD-ToE Index Compact",
|
|
670
|
+
description: "Índice compacto do manual SbD-ToE. Injectável em system prompt para eliminar fase de descoberta exploratória.",
|
|
671
|
+
mimeType: "application/json"
|
|
672
|
+
}
|
|
673
|
+
]
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
async handleResourcesRead(request) {
|
|
677
|
+
const uri = typeof request.params?.uri === "string" ? request.params.uri : "";
|
|
678
|
+
const applicabilityMatch = /^\/\/toe\/chapter-applicability\/([^/]+)$/.exec(uri.startsWith("sbd:") ? uri.slice(4) : "");
|
|
679
|
+
if (applicabilityMatch !== null) {
|
|
680
|
+
const riskLevel = applicabilityMatch[1] ?? "";
|
|
681
|
+
if (!["L1", "L2", "L3"].includes(riskLevel)) {
|
|
682
|
+
this.sendError(request.id, -32602, `riskLevel inválido: "${riskLevel}". Valores permitidos: L1, L2, L3.`);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const data = buildChapterApplicabilityJson(riskLevel);
|
|
686
|
+
this.sendResponse(request.id, {
|
|
687
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }]
|
|
688
|
+
});
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const skillTemplateMatch = /^\/\/toe\/skill-template\/([^/]+)\/([^/]+)$/.exec(uri.startsWith("sbd:") ? uri.slice(4) : "");
|
|
692
|
+
if (skillTemplateMatch !== null) {
|
|
693
|
+
const riskLevel = skillTemplateMatch[1] ?? "";
|
|
694
|
+
const projectRole = skillTemplateMatch[2] ?? "";
|
|
695
|
+
if (!["L1", "L2", "L3"].includes(riskLevel)) {
|
|
696
|
+
this.sendError(request.id, -32602, `riskLevel inválido: "${riskLevel}". Valores permitidos: L1, L2, L3.`);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const text = buildSkillTemplateMarkdown(riskLevel, projectRole);
|
|
700
|
+
this.sendResponse(request.id, {
|
|
701
|
+
contents: [{ uri, mimeType: "text/markdown", text }]
|
|
702
|
+
});
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
if (uri === "sbd://toe/index-compact") {
|
|
706
|
+
const indexPath = resolveAppPath("data/publish/sbd-toe-index-compact.json");
|
|
707
|
+
let indexText;
|
|
708
|
+
try {
|
|
709
|
+
indexText = readFileSync(indexPath, "utf-8");
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
this.sendError(request.id, -32603, "Não foi possível ler o índice compacto SbD-ToE.");
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
this.sendResponse(request.id, {
|
|
716
|
+
contents: [{ uri, mimeType: "application/json", text: indexText }]
|
|
717
|
+
});
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
this.sendError(request.id, -32602, `URI de resource desconhecida: ${uri}`);
|
|
721
|
+
}
|
|
722
|
+
getStringArg(args, key) {
|
|
723
|
+
const value = args[key];
|
|
724
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
725
|
+
throw new Error(`O argumento "${key}" é obrigatório.`);
|
|
726
|
+
}
|
|
727
|
+
return value;
|
|
728
|
+
}
|
|
729
|
+
getOptionalBooleanArg(args, key) {
|
|
730
|
+
const value = args[key];
|
|
731
|
+
return typeof value === "boolean" ? value : undefined;
|
|
732
|
+
}
|
|
733
|
+
getOptionalIntegerArg(args, key) {
|
|
734
|
+
const value = args[key];
|
|
735
|
+
return typeof value === "number" && Number.isInteger(value) ? value : undefined;
|
|
736
|
+
}
|
|
737
|
+
supportsSampling() {
|
|
738
|
+
return Boolean(this.clientCapabilities.sampling &&
|
|
739
|
+
typeof this.clientCapabilities.sampling === "object");
|
|
740
|
+
}
|
|
741
|
+
async requestSampling(systemPrompt, userPrompt) {
|
|
742
|
+
if (!this.supportsSampling()) {
|
|
743
|
+
throw new Error("O cliente MCP atual não declarou suporte para sampling.");
|
|
744
|
+
}
|
|
745
|
+
const startedAt = Date.now();
|
|
746
|
+
await this.log("debug", {
|
|
747
|
+
event_type: "sampling.request",
|
|
748
|
+
outcome: "started",
|
|
749
|
+
sampling_max_tokens: getConfig().prompt.samplingMaxTokens,
|
|
750
|
+
message: "Requesting client-side sampling"
|
|
751
|
+
});
|
|
752
|
+
const id = this.nextRequestId++;
|
|
753
|
+
const promise = new Promise((resolve, reject) => {
|
|
754
|
+
this.pending.set(id, { resolve, reject });
|
|
755
|
+
});
|
|
756
|
+
this.writeMessage({
|
|
757
|
+
jsonrpc: "2.0",
|
|
758
|
+
id,
|
|
759
|
+
method: "sampling/createMessage",
|
|
760
|
+
params: {
|
|
761
|
+
messages: [
|
|
762
|
+
{
|
|
763
|
+
role: "user",
|
|
764
|
+
content: {
|
|
765
|
+
type: "text",
|
|
766
|
+
text: userPrompt
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
],
|
|
770
|
+
systemPrompt,
|
|
771
|
+
temperature: 0.1,
|
|
772
|
+
maxTokens: getConfig().prompt.samplingMaxTokens
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
const result = (await promise);
|
|
776
|
+
const content = result.content;
|
|
777
|
+
const text = this.extractSamplingText(content);
|
|
778
|
+
const model = typeof result.model === "string" ? result.model : undefined;
|
|
779
|
+
await this.log("debug", {
|
|
780
|
+
event_type: "sampling.request",
|
|
781
|
+
outcome: "succeeded",
|
|
782
|
+
duration_ms: Date.now() - startedAt,
|
|
783
|
+
sampling_max_tokens: getConfig().prompt.samplingMaxTokens,
|
|
784
|
+
message: "Client-side sampling completed"
|
|
785
|
+
});
|
|
786
|
+
return model === undefined ? { text } : { model, text };
|
|
787
|
+
}
|
|
788
|
+
extractSamplingText(content) {
|
|
789
|
+
if (typeof content === "string") {
|
|
790
|
+
return content.trim();
|
|
791
|
+
}
|
|
792
|
+
if (Array.isArray(content)) {
|
|
793
|
+
const parts = content
|
|
794
|
+
.map((item) => {
|
|
795
|
+
if (!item || typeof item !== "object") {
|
|
796
|
+
return undefined;
|
|
797
|
+
}
|
|
798
|
+
const typed = item;
|
|
799
|
+
return typeof typed.text === "string" ? typed.text : undefined;
|
|
800
|
+
})
|
|
801
|
+
.filter((item) => Boolean(item));
|
|
802
|
+
if (parts.length > 0) {
|
|
803
|
+
return parts.join("\n").trim();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (content && typeof content === "object") {
|
|
807
|
+
const typed = content;
|
|
808
|
+
if (typeof typed.text === "string") {
|
|
809
|
+
return typed.text.trim();
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return JSON.stringify(content, null, 2);
|
|
813
|
+
}
|
|
814
|
+
async handleToolsCall(request) {
|
|
815
|
+
const params = request.params ?? {};
|
|
816
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
817
|
+
const args = typeof params.arguments === "object" && params.arguments !== null
|
|
818
|
+
? params.arguments
|
|
819
|
+
: {};
|
|
820
|
+
const requestId = this.getRequestId(request.id);
|
|
821
|
+
const startedAt = Date.now();
|
|
822
|
+
const metadata = {
|
|
823
|
+
request_id: requestId,
|
|
824
|
+
rpc_method: request.method,
|
|
825
|
+
tool_name: name,
|
|
826
|
+
...this.getQuestionMetadata(args),
|
|
827
|
+
...(typeof args.debug === "boolean" ? { debug_enabled: args.debug } : {}),
|
|
828
|
+
...(typeof args.topK === "number" && Number.isInteger(args.topK)
|
|
829
|
+
? { top_k: args.topK }
|
|
830
|
+
: {})
|
|
831
|
+
};
|
|
832
|
+
await this.log("info", {
|
|
833
|
+
event_type: "tool.call",
|
|
834
|
+
outcome: "started",
|
|
835
|
+
...metadata,
|
|
836
|
+
message: "Tool invocation started"
|
|
837
|
+
});
|
|
838
|
+
try {
|
|
839
|
+
switch (name) {
|
|
840
|
+
case "search_sbd_toe_manual": {
|
|
841
|
+
const question = this.getStringArg(args, "question");
|
|
842
|
+
const debug = this.getOptionalBooleanArg(args, "debug");
|
|
843
|
+
const topK = this.getOptionalIntegerArg(args, "topK");
|
|
844
|
+
const result = await searchManualQuestion(question, debug, topK);
|
|
845
|
+
this.sendResponse(request.id, {
|
|
846
|
+
content: [{ type: "text", text: result.text }]
|
|
847
|
+
});
|
|
848
|
+
await this.log("info", {
|
|
849
|
+
event_type: "tool.call",
|
|
850
|
+
outcome: "succeeded",
|
|
851
|
+
duration_ms: Date.now() - startedAt,
|
|
852
|
+
...metadata,
|
|
853
|
+
message: "Tool invocation completed"
|
|
854
|
+
});
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
case "inspect_sbd_toe_retrieval": {
|
|
858
|
+
const question = this.getStringArg(args, "question");
|
|
859
|
+
const topK = this.getOptionalIntegerArg(args, "topK");
|
|
860
|
+
const result = await inspectManualRetrieval(question, topK);
|
|
861
|
+
this.sendResponse(request.id, {
|
|
862
|
+
content: [{ type: "text", text: result.text }]
|
|
863
|
+
});
|
|
864
|
+
await this.log("info", {
|
|
865
|
+
event_type: "tool.call",
|
|
866
|
+
outcome: "succeeded",
|
|
867
|
+
duration_ms: Date.now() - startedAt,
|
|
868
|
+
...metadata,
|
|
869
|
+
message: "Tool invocation completed"
|
|
870
|
+
});
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
case "answer_sbd_toe_manual": {
|
|
874
|
+
const question = this.getStringArg(args, "question");
|
|
875
|
+
const debug = this.getOptionalBooleanArg(args, "debug");
|
|
876
|
+
const topK = this.getOptionalIntegerArg(args, "topK");
|
|
877
|
+
if (!this.supportsSampling()) {
|
|
878
|
+
// Fallback gracioso: devolver top-3 documentos sem sampling
|
|
879
|
+
const bundle = await retrievePublishedContext(question, 3);
|
|
880
|
+
const fallbackResult = {
|
|
881
|
+
sampling_unavailable: true,
|
|
882
|
+
note: "Sampling não disponível neste cliente. Apresentando os 3 documentos mais relevantes como contexto.",
|
|
883
|
+
results: bundle.retrieved
|
|
884
|
+
};
|
|
885
|
+
this.sendResponse(request.id, {
|
|
886
|
+
content: [{ type: "text", text: JSON.stringify(fallbackResult, null, 2) }]
|
|
887
|
+
});
|
|
888
|
+
await this.log("info", {
|
|
889
|
+
event_type: "tool.call",
|
|
890
|
+
outcome: "succeeded",
|
|
891
|
+
duration_ms: Date.now() - startedAt,
|
|
892
|
+
...metadata,
|
|
893
|
+
message: "Tool invocation completed (sampling fallback)"
|
|
894
|
+
});
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
const prepared = await prepareManualAnsweringContext(question, topK);
|
|
898
|
+
const sampled = await this.requestSampling(prepared.prompt.systemPrompt, prepared.prompt.userPrompt);
|
|
899
|
+
const result = formatSampledAnswerResult(question, prepared, sampled.text, sampled.model, debug);
|
|
900
|
+
this.sendResponse(request.id, {
|
|
901
|
+
content: [{ type: "text", text: result.text }]
|
|
902
|
+
});
|
|
903
|
+
await this.log("info", {
|
|
904
|
+
event_type: "tool.call",
|
|
905
|
+
outcome: "succeeded",
|
|
906
|
+
duration_ms: Date.now() - startedAt,
|
|
907
|
+
...metadata,
|
|
908
|
+
message: "Tool invocation completed"
|
|
909
|
+
});
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
case "list_sbd_toe_chapters": {
|
|
913
|
+
const cache = getSnapshotCache();
|
|
914
|
+
const result = handleListSbdToeChapters(args, cache);
|
|
915
|
+
this.sendResponse(request.id, {
|
|
916
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
917
|
+
});
|
|
918
|
+
await this.log("info", {
|
|
919
|
+
event_type: "tool.call",
|
|
920
|
+
outcome: "succeeded",
|
|
921
|
+
duration_ms: Date.now() - startedAt,
|
|
922
|
+
...metadata,
|
|
923
|
+
message: "Tool invocation completed"
|
|
924
|
+
});
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
case "query_sbd_toe_entities": {
|
|
928
|
+
const cache = getSnapshotCache();
|
|
929
|
+
const result = await handleQuerySbdToeEntities(args, cache);
|
|
930
|
+
this.sendResponse(request.id, {
|
|
931
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
932
|
+
});
|
|
933
|
+
await this.log("info", {
|
|
934
|
+
event_type: "tool.call",
|
|
935
|
+
outcome: "succeeded",
|
|
936
|
+
duration_ms: Date.now() - startedAt,
|
|
937
|
+
...metadata,
|
|
938
|
+
message: "Tool invocation completed"
|
|
939
|
+
});
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
case "get_sbd_toe_chapter_brief": {
|
|
943
|
+
const cache = getSnapshotCache();
|
|
944
|
+
const result = handleGetSbdToeChapterBrief(args, cache);
|
|
945
|
+
this.sendResponse(request.id, {
|
|
946
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
947
|
+
});
|
|
948
|
+
await this.log("info", {
|
|
949
|
+
event_type: "tool.call",
|
|
950
|
+
outcome: "succeeded",
|
|
951
|
+
duration_ms: Date.now() - startedAt,
|
|
952
|
+
...metadata,
|
|
953
|
+
message: "Tool invocation completed"
|
|
954
|
+
});
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
case "plan_sbd_toe_repo_governance": {
|
|
958
|
+
const result = handlePlanRepoGovernance(args);
|
|
959
|
+
this.sendResponse(request.id, {
|
|
960
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
961
|
+
});
|
|
962
|
+
await this.log("info", {
|
|
963
|
+
event_type: "tool.call",
|
|
964
|
+
outcome: "succeeded",
|
|
965
|
+
duration_ms: Date.now() - startedAt,
|
|
966
|
+
...metadata,
|
|
967
|
+
message: "Tool invocation completed"
|
|
968
|
+
});
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
case "generate_document": {
|
|
972
|
+
const result = handleGenerateDocument(args);
|
|
973
|
+
this.sendResponse(request.id, {
|
|
974
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
975
|
+
});
|
|
976
|
+
await this.log("info", {
|
|
977
|
+
event_type: "tool.call",
|
|
978
|
+
outcome: "succeeded",
|
|
979
|
+
duration_ms: Date.now() - startedAt,
|
|
980
|
+
...metadata,
|
|
981
|
+
message: "Tool invocation completed"
|
|
982
|
+
});
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
case "map_sbd_toe_review_scope": {
|
|
986
|
+
const result = handleMapSbdToeReviewScope(args);
|
|
987
|
+
this.sendResponse(request.id, {
|
|
988
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
989
|
+
});
|
|
990
|
+
await this.log("info", {
|
|
991
|
+
event_type: "tool.call",
|
|
992
|
+
outcome: "succeeded",
|
|
993
|
+
duration_ms: Date.now() - startedAt,
|
|
994
|
+
...metadata,
|
|
995
|
+
message: "Tool invocation completed"
|
|
996
|
+
});
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
case "map_sbd_toe_applicability": {
|
|
1000
|
+
const cache = getSnapshotCache();
|
|
1001
|
+
const result = handleMapSbdToeApplicability(args, cache);
|
|
1002
|
+
this.sendResponse(request.id, {
|
|
1003
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1004
|
+
});
|
|
1005
|
+
await this.log("info", {
|
|
1006
|
+
event_type: "tool.call",
|
|
1007
|
+
outcome: "succeeded",
|
|
1008
|
+
duration_ms: Date.now() - startedAt,
|
|
1009
|
+
...metadata,
|
|
1010
|
+
message: "Tool invocation completed"
|
|
1011
|
+
});
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
default:
|
|
1015
|
+
await this.log("warning", {
|
|
1016
|
+
event_type: "tool.call",
|
|
1017
|
+
outcome: "failed",
|
|
1018
|
+
duration_ms: Date.now() - startedAt,
|
|
1019
|
+
...metadata,
|
|
1020
|
+
error_code: -32602,
|
|
1021
|
+
message: "Unknown tool requested"
|
|
1022
|
+
});
|
|
1023
|
+
this.sendError(request.id, -32602, `Tool desconhecida: ${name}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
catch (error) {
|
|
1027
|
+
// Erros com rpcError emitem JSON-RPC error (ex: -32602 para input inválido)
|
|
1028
|
+
if (error instanceof Error &&
|
|
1029
|
+
"rpcError" in error &&
|
|
1030
|
+
error.rpcError !== null &&
|
|
1031
|
+
typeof error.rpcError === "object") {
|
|
1032
|
+
const rpcError = error.rpcError;
|
|
1033
|
+
await this.log("warning", {
|
|
1034
|
+
event_type: "tool.call",
|
|
1035
|
+
outcome: "failed",
|
|
1036
|
+
duration_ms: Date.now() - startedAt,
|
|
1037
|
+
...metadata,
|
|
1038
|
+
error_code: rpcError.code,
|
|
1039
|
+
message: rpcError.message
|
|
1040
|
+
});
|
|
1041
|
+
this.sendError(request.id, rpcError.code, rpcError.message, rpcError.data);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const message = error instanceof Error ? error.message : "Erro inesperado.";
|
|
1045
|
+
await this.log("error", {
|
|
1046
|
+
event_type: "tool.call",
|
|
1047
|
+
outcome: "failed",
|
|
1048
|
+
duration_ms: Date.now() - startedAt,
|
|
1049
|
+
...metadata,
|
|
1050
|
+
...this.summarizeError(error)
|
|
1051
|
+
});
|
|
1052
|
+
this.sendResponse(request.id, {
|
|
1053
|
+
isError: true,
|
|
1054
|
+
content: [{ type: "text", text: message }]
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
function main() {
|
|
1060
|
+
new McpRuntime();
|
|
1061
|
+
}
|
|
1062
|
+
main();
|
|
1063
|
+
//# sourceMappingURL=index.js.map
|