@tryvira/mcp 0.1.9 → 0.1.12
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/LICENSE +21 -21
- package/dist/cli.js +2038 -2042
- package/dist/index.d.ts +306 -306
- package/dist/index.js +224 -224
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,2042 +1,2038 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res, err) => function __init() {
|
|
5
|
-
if (err) throw err[0];
|
|
6
|
-
try {
|
|
7
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
-
} catch (e) {
|
|
9
|
-
throw err = [e], e;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
var __export = (target, all) => {
|
|
13
|
-
for (var name in all)
|
|
14
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ../domain/src/scope-composer.ts
|
|
18
|
-
import { createHash } from "node:crypto";
|
|
19
|
-
function canonical(value) {
|
|
20
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
21
|
-
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
22
|
-
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
|
|
23
|
-
}
|
|
24
|
-
function assertComposedScopeIntegrity(composed) {
|
|
25
|
-
const conflictSetDigest = digest(composed.conflicts);
|
|
26
|
-
if (conflictSetDigest !== composed.conflictSetDigest) throw new Error("SCOPE_CONFLICT_DIGEST_MISMATCH");
|
|
27
|
-
const compositionDigest = digest({ chain: composed.chain, permissions: composed.permissions, delegationIds: [...composed.delegationIds ?? []].sort(), conflictSetDigest, effectiveRevision: composed.effectiveRevision ?? null, sourceRevisionSetDigest: composed.sourceRevisionSetDigest ?? null, ancestorHardAuthorityDigest: composed.ancestorHardAuthorityDigest ?? null, effectiveProgramDigest: composed.effectiveProgramDigest ?? null });
|
|
28
|
-
if (compositionDigest !== composed.compositionDigest) throw new Error("SCOPE_COMPOSITION_DIGEST_MISMATCH");
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
var digest;
|
|
32
|
-
var init_scope_composer = __esm({
|
|
33
|
-
"../domain/src/scope-composer.ts"() {
|
|
34
|
-
"use strict";
|
|
35
|
-
digest = (value) => createHash("sha256").update(canonical(value)).digest("hex");
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// ../runtime/src/mcp-gateway.ts
|
|
40
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
41
|
-
function canonicalMcpJson(value) {
|
|
42
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
43
|
-
if (Array.isArray(value)) return `[${value.map(canonicalMcpJson).join(",")}]`;
|
|
44
|
-
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalMcpJson(value[key])}`).join(",")}}`;
|
|
45
|
-
}
|
|
46
|
-
function verifyMcpResult(result) {
|
|
47
|
-
const { provenanceDigest, ...body } = result;
|
|
48
|
-
const expected = createHash2("sha256").update(canonicalMcpJson(body)).digest("hex");
|
|
49
|
-
if (provenanceDigest !== expected) throw new Error("MCP_PROVENANCE_DIGEST_INVALID");
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
function createMcpCancellationRegistry() {
|
|
53
|
-
const controllers = /* @__PURE__ */ new Map();
|
|
54
|
-
return { register(requestId) {
|
|
55
|
-
if (!requestId.trim()) throw new Error("MCP_REQUEST_ID_REQUIRED");
|
|
56
|
-
const previous = controllers.get(requestId);
|
|
57
|
-
previous?.abort();
|
|
58
|
-
const controller = new AbortController();
|
|
59
|
-
controllers.set(requestId, controller);
|
|
60
|
-
return controller.signal;
|
|
61
|
-
}, cancel(requestId) {
|
|
62
|
-
const controller = controllers.get(requestId);
|
|
63
|
-
if (!controller) return false;
|
|
64
|
-
controller.abort();
|
|
65
|
-
return true;
|
|
66
|
-
}, unregister(requestId, signal) {
|
|
67
|
-
const controller = controllers.get(requestId);
|
|
68
|
-
if (controller && (!signal || controller.signal === signal)) controllers.delete(requestId);
|
|
69
|
-
} };
|
|
70
|
-
}
|
|
71
|
-
function combineMcpAbortSignals(...signals) {
|
|
72
|
-
const active = signals.filter((signal) => Boolean(signal));
|
|
73
|
-
if (active.length === 0) return void 0;
|
|
74
|
-
if (active.length === 1) return active[0];
|
|
75
|
-
const controller = new AbortController();
|
|
76
|
-
const abort = () => controller.abort();
|
|
77
|
-
for (const signal of active) {
|
|
78
|
-
if (signal.aborted) {
|
|
79
|
-
controller.abort();
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
83
|
-
}
|
|
84
|
-
return controller.signal;
|
|
85
|
-
}
|
|
86
|
-
async function handleMcpJsonRpc(input) {
|
|
87
|
-
const { request, server, tools: tools2, transport } = input;
|
|
88
|
-
if (request.jsonrpc !== "2.0" || !request.method) throw new Error("MCP_JSONRPC_INVALID");
|
|
89
|
-
if (request.method === "initialize") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { protocolVersion: "2025-03-26", serverInfo: { name: server, version: "1" }, capabilities: { tools: {}, resources: input.resources ? {} : void 0, prompts: input.prompts ? {} : void 0 } } };
|
|
90
|
-
if (request.method === "notifications/initialized") return null;
|
|
91
|
-
if (request.method === "notifications/cancelled") {
|
|
92
|
-
const requestId2 = String(request.params?.requestId ?? "");
|
|
93
|
-
input.cancellationRegistry?.cancel(requestId2);
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
if (request.method === "tools/list") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { tools: tools2 } };
|
|
97
|
-
if (request.method === "resources/list") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { resources: input.resources ?? [] } };
|
|
98
|
-
if (request.method === "prompts/list") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { prompts: input.prompts ?? [] } };
|
|
99
|
-
if (request.method === "resources/read") {
|
|
100
|
-
const value = input.readResource ? await input.readResource(String(request.params?.uri ?? "")) : null;
|
|
101
|
-
return request.id === void 0 ? null : value ? { jsonrpc: "2.0", id: request.id, result: value } : { jsonrpc: "2.0", id: request.id, error: { code: -32602, message: "RESOURCE_NOT_FOUND" } };
|
|
102
|
-
}
|
|
103
|
-
if (request.method === "prompts/get") {
|
|
104
|
-
const value = input.getPrompt ? await input.getPrompt(String(request.params?.name ?? ""), request.params?.arguments ?? {}) : null;
|
|
105
|
-
return request.id === void 0 ? null : value ? { jsonrpc: "2.0", id: request.id, result: value } : { jsonrpc: "2.0", id: request.id, error: { code: -32602, message: "PROMPT_NOT_FOUND" } };
|
|
106
|
-
}
|
|
107
|
-
if (request.method !== "tools/call") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, error: { code: -32601, message: "METHOD_NOT_FOUND" } };
|
|
108
|
-
const tool = String(request.params?.name ?? "");
|
|
109
|
-
const definition = tools2.find((item) => item.name === tool);
|
|
110
|
-
if (!definition) return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, error: { code: -32602, message: "TOOL_NOT_FOUND" } };
|
|
111
|
-
const invocation = { ...input.invocation, server, tool, arguments: request.params?.arguments ?? {} };
|
|
112
|
-
const authority = definition.effectful ? input.authorize ? await input.authorize({ invocation, definition }) : { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
113
|
-
if (!authority.allowed) return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, error: { code: -32003, message: authority.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority.decisionDigest } } };
|
|
114
|
-
const requestId = String(request.id ?? "");
|
|
115
|
-
const requestSignal = input.cancellationRegistry && requestId ? input.cancellationRegistry.register(requestId) : void 0;
|
|
116
|
-
try {
|
|
117
|
-
const result = await invokeMcpTool({ invocation, transport, authority, signal: combineMcpAbortSignals(input.signal, requestSignal) });
|
|
118
|
-
return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { content: [{ type: "text", text: JSON.stringify(result.result ?? result.error ?? null) }], isError: !result.ok, provenanceDigest: result.provenanceDigest } };
|
|
119
|
-
} finally {
|
|
120
|
-
if (requestSignal) input.cancellationRegistry?.unregister(requestId, requestSignal);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
async function invokeMcpTool(input) {
|
|
124
|
-
const { invocation, transport } = input;
|
|
125
|
-
if (!invocation.id || !invocation.server || !invocation.tool || !invocation.receiptId || !invocation.idempotencyKey) throw new Error("MCP_INVOCATION_FIELDS_REQUIRED");
|
|
126
|
-
if (invocation.authorityDigest !== void 0 && !/^[a-f0-9]{64}$/.test(invocation.authorityDigest)) throw new Error("MCP_AUTHORITY_DIGEST_INVALID");
|
|
127
|
-
if (invocation.composedScope) {
|
|
128
|
-
assertComposedScopeIntegrity(invocation.composedScope);
|
|
129
|
-
if (invocation.authorityDigest !== void 0 && invocation.authorityDigest !== invocation.composedScope.compositionDigest) throw new Error("MCP_AUTHORITY_MISMATCH");
|
|
130
|
-
if (!invocation.composedScope.serveable) throw new Error("MCP_SCOPE_COMPOSITION_CONFLICT");
|
|
131
|
-
}
|
|
132
|
-
if (input.authority && !input.authority.allowed) throw new Error("MCP_AUTHORITY_DENIED");
|
|
133
|
-
if (!transport || typeof transport.call !== "function") throw new Error("MCP_TRANSPORT_REQUIRED");
|
|
134
|
-
let response;
|
|
135
|
-
try {
|
|
136
|
-
response = await transport.call({ server: invocation.server, tool: invocation.tool, arguments: structuredClone(invocation.arguments), signal: input.signal });
|
|
137
|
-
} catch {
|
|
138
|
-
if (input.signal?.aborted) throw new Error("MCP_INVOCATION_CANCELLED");
|
|
139
|
-
response = { error: "MCP_TRANSPORT_ERROR" };
|
|
140
|
-
}
|
|
141
|
-
const argumentsDigest = createHash2("sha256").update(canonicalMcpJson(invocation.arguments)).digest("hex");
|
|
142
|
-
const body = { invocationId: invocation.id, organizationId: invocation.organizationId, projectId: invocation.projectId, environmentId: invocation.environmentId, server: invocation.server, tool: invocation.tool, receiptId: invocation.receiptId, idempotencyKey: invocation.idempotencyKey, argumentsDigest, authorityDecisionDigest: input.authority?.decisionDigest ?? "not-required", authorityDigest: invocation.authorityDigest ?? null, ok: !response.error, result: response.result, error: response.error };
|
|
143
|
-
return Object.freeze({ ...body, provenanceDigest: createHash2("sha256").update(canonicalMcpJson(body)).digest("hex") });
|
|
144
|
-
}
|
|
145
|
-
var init_mcp_gateway = __esm({
|
|
146
|
-
"../runtime/src/mcp-gateway.ts"() {
|
|
147
|
-
"use strict";
|
|
148
|
-
init_scope_composer();
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// ../runtime/src/mcp-provenance.ts
|
|
153
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
154
|
-
function verifyMcpResult2(result) {
|
|
155
|
-
const { provenanceDigest: stored, ...body } = result;
|
|
156
|
-
const expected = createHash3("sha256").update(canonicalMcpJson(body)).digest("hex");
|
|
157
|
-
if (stored !== expected) throw new Error("MCP_PROVENANCE_DIGEST_MISMATCH");
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
function createMcpResultFromResponse(input) {
|
|
161
|
-
const argumentsDigest = createHash3("sha256").update(canonicalMcpJson(input.invocation.arguments)).digest("hex");
|
|
162
|
-
const body = { invocationId: input.invocation.id, organizationId: input.invocation.organizationId, projectId: input.invocation.projectId, environmentId: input.invocation.environmentId, server: input.invocation.server, tool: input.invocation.tool, receiptId: input.invocation.receiptId, idempotencyKey: input.invocation.idempotencyKey, argumentsDigest, authorityDecisionDigest: input.authority?.decisionDigest ?? "not-required", authorityDigest: input.invocation.authorityDigest ?? null, ok: !input.response.error, result: input.response.result, error: input.response.error };
|
|
163
|
-
return Object.freeze({ ...body, provenanceDigest: createHash3("sha256").update(canonicalMcpJson(body)).digest("hex") });
|
|
164
|
-
}
|
|
165
|
-
var init_mcp_provenance = __esm({
|
|
166
|
-
"../runtime/src/mcp-provenance.ts"() {
|
|
167
|
-
"use strict";
|
|
168
|
-
init_mcp_gateway();
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// ../runtime/src/mcp-session.ts
|
|
173
|
-
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
174
|
-
function assertMcpSession(session) {
|
|
175
|
-
const { sessionDigest, ...base } = session;
|
|
176
|
-
if (sessionDigest !== digest2(base)) throw new Error("MCP_SESSION_DIGEST_MISMATCH");
|
|
177
|
-
return true;
|
|
178
|
-
}
|
|
179
|
-
function assertMcpSessionActive(session) {
|
|
180
|
-
assertMcpSession(session);
|
|
181
|
-
if (session.status !== "ACTIVE") throw new Error(`MCP_SESSION_NOT_ACTIVE:${session.status}`);
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
var canonical2, digest2;
|
|
185
|
-
var init_mcp_session = __esm({
|
|
186
|
-
"../runtime/src/mcp-session.ts"() {
|
|
187
|
-
"use strict";
|
|
188
|
-
canonical2 = (value) => value === null || typeof value !== "object" ? JSON.stringify(value) : Array.isArray(value) ? `[${value.map(canonical2).join(",")}]` : `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical2(value[key])}`).join(",")}}`;
|
|
189
|
-
digest2 = (value) => createHash4("sha256").update(canonical2(value)).digest("hex");
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// ../../apps/mcp-server/server.ts
|
|
194
|
-
import {
|
|
195
|
-
createServer
|
|
196
|
-
} from "node:http";
|
|
197
|
-
import { createInterface } from "node:readline";
|
|
198
|
-
async function assertConfiguredSession(options, sessionId, scope) {
|
|
199
|
-
if (!options.sessionRepository) return;
|
|
200
|
-
if (!sessionId?.trim()) throw new Error("MCP_SESSION_REQUIRED");
|
|
201
|
-
const session = await options.sessionRepository.get(sessionId, scope);
|
|
202
|
-
if (!session) throw new Error("MCP_SESSION_NOT_FOUND");
|
|
203
|
-
assertMcpSessionActive(session);
|
|
204
|
-
}
|
|
205
|
-
async function persistVerifiedMcpResult(repository, result) {
|
|
206
|
-
const persisted = await repository.save(result);
|
|
207
|
-
if (persisted && typeof persisted === "object") verifyMcpResult(persisted);
|
|
208
|
-
}
|
|
209
|
-
function createMcpServer(options) {
|
|
210
|
-
if (options.requireInvocationExecutor && !options.invocationExecutor)
|
|
211
|
-
throw new Error("MCP_INVOCATION_EXECUTOR_REQUIRED");
|
|
212
|
-
const maxBodyBytes = options.maxBodyBytes ?? 128 * 1024;
|
|
213
|
-
const cancellationRegistry = options.cancellationRegistry ?? createMcpCancellationRegistry();
|
|
214
|
-
return createServer(async (request, response) => {
|
|
215
|
-
if (request.method !== "POST")
|
|
216
|
-
return send(response, 405, { error: { code: "METHOD_NOT_ALLOWED" } });
|
|
217
|
-
if (options.requestAuthToken) {
|
|
218
|
-
const authorization = request.headers.authorization ?? "";
|
|
219
|
-
const token = authorization.match(/^Bearer\s+(.+)$/i)?.[1];
|
|
220
|
-
if (token !== options.requestAuthToken)
|
|
221
|
-
return send(response, 401, { error: { code: "MCP_AUTHENTICATION_REQUIRED" } });
|
|
222
|
-
}
|
|
223
|
-
const requestController = new AbortController();
|
|
224
|
-
const onAborted = () => requestController.abort();
|
|
225
|
-
request.once("aborted", onAborted);
|
|
226
|
-
try {
|
|
227
|
-
const body = JSON.parse(await readBody(request, maxBodyBytes));
|
|
228
|
-
const token = requestToken(body.id);
|
|
229
|
-
const requestInvocation = {
|
|
230
|
-
...options.invocation,
|
|
231
|
-
id: `${options.invocation.id}:${token}`,
|
|
232
|
-
receiptId: `${options.invocation.receiptId}:${token}`,
|
|
233
|
-
idempotencyKey: `${options.invocation.idempotencyKey}:${token}`
|
|
234
|
-
};
|
|
235
|
-
const scope = {
|
|
236
|
-
organizationId: requestInvocation.organizationId,
|
|
237
|
-
projectId: requestInvocation.projectId,
|
|
238
|
-
environmentId: requestInvocation.environmentId
|
|
239
|
-
};
|
|
240
|
-
const sessionId = request.headers[(options.sessionHeader ?? "x-mcp-session-id").toLowerCase()];
|
|
241
|
-
await assertConfiguredSession(options, typeof sessionId === "string" ? sessionId : void 0, scope);
|
|
242
|
-
if (options.invocationExecutor && body.method === "tools/call") {
|
|
243
|
-
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
244
|
-
if (definition) {
|
|
245
|
-
const invocation = { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} };
|
|
246
|
-
const authority2 = definition.effectful ? await options.authorize?.({ invocation, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
247
|
-
if (authority2.allowed) {
|
|
248
|
-
const result2 = await options.invocationExecutor({ invocation, authority: authority2, signal: requestController.signal });
|
|
249
|
-
if (!requestController.signal.aborted) send(response, 200, { jsonrpc: "2.0", id: body.id, result: { content: [{ type: "text", text: JSON.stringify(result2.result ?? result2.error ?? null) }], isError: !result2.ok, provenanceDigest: result2.provenanceDigest } });
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (!requestController.signal.aborted) send(response, 403, { jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
if (options.provenanceRepository && body.method === "tools/call") {
|
|
257
|
-
const replay = await claimInvocation(
|
|
258
|
-
options.provenanceRepository,
|
|
259
|
-
requestInvocation,
|
|
260
|
-
scope
|
|
261
|
-
);
|
|
262
|
-
if (replay) {
|
|
263
|
-
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
264
|
-
if (definition?.effectful) {
|
|
265
|
-
const authority2 = await options.authorize?.({ invocation: { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} }, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" };
|
|
266
|
-
if (!authority2.allowed) {
|
|
267
|
-
if (!requestController.signal.aborted) send(response, 403, { jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if (!requestController.signal.aborted)
|
|
272
|
-
send(response, 200, {
|
|
273
|
-
jsonrpc: "2.0",
|
|
274
|
-
id: body.id,
|
|
275
|
-
result: {
|
|
276
|
-
content: [
|
|
277
|
-
{
|
|
278
|
-
type: "text",
|
|
279
|
-
text: JSON.stringify(replay.result ?? replay.error ?? null)
|
|
280
|
-
}
|
|
281
|
-
],
|
|
282
|
-
isError: !replay.ok,
|
|
283
|
-
provenanceDigest: replay.provenanceDigest
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
let upstreamResponse;
|
|
290
|
-
let authority;
|
|
291
|
-
const requestCancelled = new Promise(
|
|
292
|
-
(resolve3) => request.once(
|
|
293
|
-
"aborted",
|
|
294
|
-
() => resolve3({ error: "MCP_REQUEST_CANCELLED" })
|
|
295
|
-
)
|
|
296
|
-
);
|
|
297
|
-
const transport = {
|
|
298
|
-
call: async (input) => {
|
|
299
|
-
upstreamResponse = await Promise.race([
|
|
300
|
-
options.transport.call({
|
|
301
|
-
...input,
|
|
302
|
-
signal: input.signal ?? requestController.signal
|
|
303
|
-
}),
|
|
304
|
-
requestCancelled
|
|
305
|
-
]);
|
|
306
|
-
return upstreamResponse;
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
const result = await handleMcpJsonRpc({
|
|
310
|
-
request: body,
|
|
311
|
-
server: options.serverName,
|
|
312
|
-
tools: options.tools,
|
|
313
|
-
resources: options.resources,
|
|
314
|
-
prompts: options.prompts,
|
|
315
|
-
readResource: options.readResource,
|
|
316
|
-
getPrompt: options.getPrompt,
|
|
317
|
-
invocation: requestInvocation,
|
|
318
|
-
transport,
|
|
319
|
-
cancellationRegistry,
|
|
320
|
-
authorize: async (input) => {
|
|
321
|
-
authority = await options.authorize?.(input) ?? (input.definition.effectful ? {
|
|
322
|
-
allowed: false,
|
|
323
|
-
decisionDigest: "",
|
|
324
|
-
reason: "MCP_AUTHORITY_REQUIRED"
|
|
325
|
-
} : { allowed: true, decisionDigest: "read-only" });
|
|
326
|
-
return authority;
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
if (options.provenanceRepository && body.method === "tools/call") {
|
|
330
|
-
const tool = String(body.params?.name ?? "");
|
|
331
|
-
await persistVerifiedMcpResult(
|
|
332
|
-
options.provenanceRepository,
|
|
333
|
-
createMcpResultFromResponse({
|
|
334
|
-
invocation: {
|
|
335
|
-
...requestInvocation,
|
|
336
|
-
server: options.serverName,
|
|
337
|
-
tool,
|
|
338
|
-
arguments: body.params?.arguments ?? {}
|
|
339
|
-
},
|
|
340
|
-
response: upstreamResponse ?? {
|
|
341
|
-
error: authority?.reason ?? "MCP_AUTHORITY_DENIED"
|
|
342
|
-
},
|
|
343
|
-
authority
|
|
344
|
-
})
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
if (!requestController.signal.aborted) send(response, 200, result);
|
|
348
|
-
} catch {
|
|
349
|
-
if (!requestController.signal.aborted)
|
|
350
|
-
send(response, 400, {
|
|
351
|
-
jsonrpc: "2.0",
|
|
352
|
-
id: null,
|
|
353
|
-
error: { code: -32600, message: "INVALID_MCP_REQUEST" }
|
|
354
|
-
});
|
|
355
|
-
} finally {
|
|
356
|
-
request.removeListener("aborted", onAborted);
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
function createMcpStdioServer(options, input = process.stdin, output = process.stdout) {
|
|
361
|
-
const cancellationRegistry = options.cancellationRegistry ?? createMcpCancellationRegistry();
|
|
362
|
-
const lines = createInterface({
|
|
363
|
-
input,
|
|
364
|
-
crlfDelay: Infinity
|
|
365
|
-
});
|
|
366
|
-
let closed = false;
|
|
367
|
-
const write = (body) => {
|
|
368
|
-
if (!closed) output.write(`${JSON.stringify(body)}
|
|
369
|
-
`);
|
|
370
|
-
};
|
|
371
|
-
const processLine = async (line) => {
|
|
372
|
-
if (!line.trim()) return;
|
|
373
|
-
let body;
|
|
374
|
-
try {
|
|
375
|
-
body = JSON.parse(line);
|
|
376
|
-
} catch {
|
|
377
|
-
write({
|
|
378
|
-
jsonrpc: "2.0",
|
|
379
|
-
id: null,
|
|
380
|
-
error: { code: -32700, message: "PARSE_ERROR" }
|
|
381
|
-
});
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
const token = requestToken(body.id);
|
|
385
|
-
const requestInvocation = {
|
|
386
|
-
...options.invocation,
|
|
387
|
-
id: `${options.invocation.id}:${token}`,
|
|
388
|
-
receiptId: `${options.invocation.receiptId}:${token}`,
|
|
389
|
-
idempotencyKey: `${options.invocation.idempotencyKey}:${token}`
|
|
390
|
-
};
|
|
391
|
-
const scope = {
|
|
392
|
-
organizationId: requestInvocation.organizationId,
|
|
393
|
-
projectId: requestInvocation.projectId,
|
|
394
|
-
environmentId: requestInvocation.environmentId
|
|
395
|
-
};
|
|
396
|
-
try {
|
|
397
|
-
await assertConfiguredSession(options, options.sessionId, scope);
|
|
398
|
-
} catch {
|
|
399
|
-
if (body.id !== void 0)
|
|
400
|
-
write({
|
|
401
|
-
jsonrpc: "2.0",
|
|
402
|
-
id: body.id,
|
|
403
|
-
error: { code: -32001, message: "MCP_SESSION_NOT_ACTIVE" }
|
|
404
|
-
});
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (options.invocationExecutor && body.method === "tools/call") {
|
|
408
|
-
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
409
|
-
if (definition) {
|
|
410
|
-
const invocation = { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} };
|
|
411
|
-
const authority2 = definition.effectful ? await options.authorize?.({ invocation, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
412
|
-
if (authority2.allowed) {
|
|
413
|
-
const result = await options.invocationExecutor({ invocation, authority: authority2 });
|
|
414
|
-
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, result: { content: [{ type: "text", text: JSON.stringify(result.result ?? result.error ?? null) }], isError: !result.ok, provenanceDigest: result.provenanceDigest } });
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
if (options.provenanceRepository && body.method === "tools/call") {
|
|
422
|
-
const replay = await claimInvocation(
|
|
423
|
-
options.provenanceRepository,
|
|
424
|
-
requestInvocation,
|
|
425
|
-
scope
|
|
426
|
-
);
|
|
427
|
-
if (replay) {
|
|
428
|
-
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
429
|
-
if (definition?.effectful) {
|
|
430
|
-
const authority2 = await options.authorize?.({ invocation: { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} }, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" };
|
|
431
|
-
if (!authority2.allowed) {
|
|
432
|
-
write({ jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
write({
|
|
437
|
-
jsonrpc: "2.0",
|
|
438
|
-
id: body.id,
|
|
439
|
-
result: {
|
|
440
|
-
content: [
|
|
441
|
-
{
|
|
442
|
-
type: "text",
|
|
443
|
-
text: JSON.stringify(replay.result ?? replay.error ?? null)
|
|
444
|
-
}
|
|
445
|
-
],
|
|
446
|
-
isError: !replay.ok,
|
|
447
|
-
provenanceDigest: replay.provenanceDigest
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
let upstreamResponse;
|
|
454
|
-
let authority;
|
|
455
|
-
const transport = {
|
|
456
|
-
call: async (value) => {
|
|
457
|
-
upstreamResponse = await options.transport.call({
|
|
458
|
-
...value,
|
|
459
|
-
signal: value.signal
|
|
460
|
-
});
|
|
461
|
-
return upstreamResponse;
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
try {
|
|
465
|
-
if (body.method === "server/discover") {
|
|
466
|
-
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, result: { supportedProtocolVersions: ["2026-07-28", "2025-11-25", "2025-03-26"], serverInfo: { name: options.serverName, version: "1.0.0" }, capabilities: { tools: {}, resources: options.resources ? {} : void 0, prompts: options.prompts ? {} : void 0 } } });
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
if (body.method === "initialize") {
|
|
470
|
-
const requested = String(body.params?.protocolVersion ?? "");
|
|
471
|
-
const protocolVersion = ["2026-07-28", "2025-11-25", "2025-03-26"].includes(requested) ? requested : "2025-03-26";
|
|
472
|
-
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, result: { protocolVersion, serverInfo: { name: options.serverName, version: "1.0.0" }, capabilities: { tools: {}, resources: options.resources ? {} : void 0, prompts: options.prompts ? {} : void 0 } } });
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
const result = await handleMcpJsonRpc({
|
|
476
|
-
request: body,
|
|
477
|
-
server: options.serverName,
|
|
478
|
-
tools: options.tools,
|
|
479
|
-
resources: options.resources,
|
|
480
|
-
prompts: options.prompts,
|
|
481
|
-
readResource: options.readResource,
|
|
482
|
-
getPrompt: options.getPrompt,
|
|
483
|
-
invocation: requestInvocation,
|
|
484
|
-
transport,
|
|
485
|
-
cancellationRegistry,
|
|
486
|
-
authorize: async (value) => {
|
|
487
|
-
authority = await options.authorize?.(value) ?? (value.definition.effectful ? {
|
|
488
|
-
allowed: false,
|
|
489
|
-
decisionDigest: "",
|
|
490
|
-
reason: "MCP_AUTHORITY_REQUIRED"
|
|
491
|
-
} : { allowed: true, decisionDigest: "read-only" });
|
|
492
|
-
return authority;
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
if (options.provenanceRepository && body.method === "tools/call") {
|
|
496
|
-
const tool = String(body.params?.name ?? "");
|
|
497
|
-
await persistVerifiedMcpResult(
|
|
498
|
-
options.provenanceRepository,
|
|
499
|
-
createMcpResultFromResponse({
|
|
500
|
-
invocation: {
|
|
501
|
-
...requestInvocation,
|
|
502
|
-
server: options.serverName,
|
|
503
|
-
tool,
|
|
504
|
-
arguments: body.params?.arguments ?? {}
|
|
505
|
-
},
|
|
506
|
-
response: upstreamResponse ?? {
|
|
507
|
-
error: authority?.reason ?? "MCP_AUTHORITY_DENIED"
|
|
508
|
-
},
|
|
509
|
-
authority
|
|
510
|
-
})
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
if (result !== null) write(result);
|
|
514
|
-
} catch {
|
|
515
|
-
if (body.id !== void 0)
|
|
516
|
-
write({
|
|
517
|
-
jsonrpc: "2.0",
|
|
518
|
-
id: body.id,
|
|
519
|
-
error: { code: -32600, message: "INVALID_MCP_REQUEST" }
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
|
-
lines.on("line", (line) => {
|
|
524
|
-
void processLine(line);
|
|
525
|
-
});
|
|
526
|
-
return {
|
|
527
|
-
close: () => {
|
|
528
|
-
closed = true;
|
|
529
|
-
lines.close();
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
function send(response, status, body) {
|
|
534
|
-
response.writeHead(status, {
|
|
535
|
-
"content-type": "application/json; charset=utf-8",
|
|
536
|
-
"cache-control": "no-store"
|
|
537
|
-
});
|
|
538
|
-
response.end(JSON.stringify(body));
|
|
539
|
-
}
|
|
540
|
-
function readBody(request, limit) {
|
|
541
|
-
return new Promise((resolve3, reject) => {
|
|
542
|
-
let size = 0;
|
|
543
|
-
const chunks = [];
|
|
544
|
-
request.on("data", (chunk) => {
|
|
545
|
-
size += chunk.length;
|
|
546
|
-
if (size > limit) {
|
|
547
|
-
reject(new Error("PAYLOAD_TOO_LARGE"));
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
chunks.push(chunk);
|
|
551
|
-
});
|
|
552
|
-
request.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
553
|
-
request.on("error", reject);
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
var requestToken, claimInvocation;
|
|
557
|
-
var init_server = __esm({
|
|
558
|
-
"../../apps/mcp-server/server.ts"() {
|
|
559
|
-
"use strict";
|
|
560
|
-
init_mcp_gateway();
|
|
561
|
-
init_mcp_provenance();
|
|
562
|
-
init_mcp_session();
|
|
563
|
-
requestToken = (value) => String(
|
|
564
|
-
value ?? `notification-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
565
|
-
);
|
|
566
|
-
claimInvocation = async (repository, invocation, scope) => {
|
|
567
|
-
const claim = await repository.claim?.({
|
|
568
|
-
invocationId: invocation.id,
|
|
569
|
-
idempotencyKey: invocation.idempotencyKey,
|
|
570
|
-
...scope
|
|
571
|
-
});
|
|
572
|
-
if (claim?.status === "IN_FLIGHT")
|
|
573
|
-
throw new Error("MCP_INVOCATION_IN_FLIGHT");
|
|
574
|
-
return claim?.result ?? await repository.getByIdempotencyKey?.(invocation.idempotencyKey, scope) ?? null;
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
// ../runtime/src/mcp-invocation-service.ts
|
|
580
|
-
import { createHash as createHash5 } from "node:crypto";
|
|
581
|
-
function assertReplayIdentity(invocation, authority, result) {
|
|
582
|
-
const argumentsDigest = createHash5("sha256").update(canonicalMcpJson(invocation.arguments)).digest("hex");
|
|
583
|
-
const expectedAuthorityDecisionDigest = authority?.decisionDigest ?? "not-required";
|
|
584
|
-
if (result.organizationId !== invocation.organizationId || result.projectId !== invocation.projectId || result.environmentId !== invocation.environmentId || result.server !== invocation.server || result.tool !== invocation.tool || result.receiptId !== invocation.receiptId || result.idempotencyKey !== invocation.idempotencyKey || result.argumentsDigest !== argumentsDigest || result.authorityDecisionDigest !== expectedAuthorityDecisionDigest || result.authorityDigest !== (invocation.authorityDigest ?? null)) throw new Error("MCP_INVOCATION_REPLAY_MISMATCH");
|
|
585
|
-
}
|
|
586
|
-
async function executeMcpInvocation(input) {
|
|
587
|
-
const started = Date.now();
|
|
588
|
-
const claim = await input.store.claim({ invocationId: input.invocation.id, idempotencyKey: input.invocation.idempotencyKey, organizationId: input.invocation.organizationId, projectId: input.invocation.projectId, environmentId: input.invocation.environmentId });
|
|
589
|
-
if (claim.status === "REPLAY" && claim.result) {
|
|
590
|
-
verifyMcpResult(claim.result);
|
|
591
|
-
assertReplayIdentity(input.invocation, input.authority, claim.result);
|
|
592
|
-
await input.observe?.({ result: claim.result, replay: true, durationMs: Date.now() - started });
|
|
593
|
-
return claim.result;
|
|
594
|
-
}
|
|
595
|
-
if (claim.status === "IN_FLIGHT") throw new Error("MCP_INVOCATION_IN_FLIGHT");
|
|
596
|
-
try {
|
|
597
|
-
const result = await invokeMcpTool({ invocation: input.invocation, transport: input.transport, authority: input.authority, signal: input.signal });
|
|
598
|
-
const persisted = await input.store.save(result);
|
|
599
|
-
verifyMcpResult(persisted);
|
|
600
|
-
await input.observe?.({ result: persisted, replay: false, durationMs: Date.now() - started });
|
|
601
|
-
return persisted;
|
|
602
|
-
} catch (error) {
|
|
603
|
-
if (input.signal?.aborted) await input.store.releaseClaim?.({ idempotencyKey: input.invocation.idempotencyKey, organizationId: input.invocation.organizationId, projectId: input.invocation.projectId, environmentId: input.invocation.environmentId });
|
|
604
|
-
throw error;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
var init_mcp_invocation_service = __esm({
|
|
608
|
-
"../runtime/src/mcp-invocation-service.ts"() {
|
|
609
|
-
"use strict";
|
|
610
|
-
init_mcp_gateway();
|
|
611
|
-
init_mcp_gateway();
|
|
612
|
-
}
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
// ../database/src/mcp-provenance-repository.ts
|
|
616
|
-
var assertClaimInput, McpProvenanceRepository, SqlMcpProvenanceRepository;
|
|
617
|
-
var init_mcp_provenance_repository = __esm({
|
|
618
|
-
"../database/src/mcp-provenance-repository.ts"() {
|
|
619
|
-
"use strict";
|
|
620
|
-
init_mcp_provenance();
|
|
621
|
-
init_scope_composer();
|
|
622
|
-
assertClaimInput = (input) => {
|
|
623
|
-
if (!input.invocationId?.trim() || !input.idempotencyKey?.trim() || !input.organizationId?.trim() || !input.projectId?.trim() || !input.environmentId?.trim()) throw new Error("MCP_CLAIM_INPUT_INVALID");
|
|
624
|
-
};
|
|
625
|
-
McpProvenanceRepository = class {
|
|
626
|
-
#items = /* @__PURE__ */ new Map();
|
|
627
|
-
#claims = /* @__PURE__ */ new Set();
|
|
628
|
-
claim(input) {
|
|
629
|
-
assertClaimInput(input);
|
|
630
|
-
const replay = this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
631
|
-
if (replay) return { status: "REPLAY", result: replay };
|
|
632
|
-
const key = `${input.organizationId}:${input.projectId}:${input.environmentId}:${input.idempotencyKey}`;
|
|
633
|
-
if (this.#claims.has(key)) return { status: "IN_FLIGHT" };
|
|
634
|
-
this.#claims.add(key);
|
|
635
|
-
return { status: "CLAIMED" };
|
|
636
|
-
}
|
|
637
|
-
save(result) {
|
|
638
|
-
verifyMcpResult2(result);
|
|
639
|
-
const prior = this.#items.get(result.invocationId);
|
|
640
|
-
const byKey = this.getByIdempotencyKey(result.idempotencyKey, result);
|
|
641
|
-
if (prior && prior.provenanceDigest !== result.provenanceDigest || byKey && byKey.provenanceDigest !== result.provenanceDigest)
|
|
642
|
-
throw new Error("MCP_PROVENANCE_CONFLICT");
|
|
643
|
-
this.#items.set(result.invocationId, structuredClone(result));
|
|
644
|
-
this.#claims.delete(
|
|
645
|
-
`${result.organizationId}:${result.projectId}:${result.environmentId}:${result.idempotencyKey}`
|
|
646
|
-
);
|
|
647
|
-
return structuredClone(prior ?? byKey ?? result);
|
|
648
|
-
}
|
|
649
|
-
releaseClaim(input) {
|
|
650
|
-
assertClaimInput({ ...input, invocationId: "release-claim" });
|
|
651
|
-
this.#claims.delete(`${input.organizationId}:${input.projectId}:${input.environmentId}:${input.idempotencyKey}`);
|
|
652
|
-
}
|
|
653
|
-
saveComposed(scope, result) {
|
|
654
|
-
assertComposedScopeIntegrity(scope);
|
|
655
|
-
const projected = { organizationId: scope.chain.find((item) => item.level === "organization")?.id ?? "", projectId: scope.chain.find((item) => item.level === "project")?.id ?? "", environmentId: scope.chain.find((item) => item.level === "application")?.id ?? "" };
|
|
656
|
-
if (result.organizationId !== projected.organizationId || result.projectId !== projected.projectId || result.environmentId !== projected.environmentId) throw new Error("MCP_SCOPE_MISMATCH");
|
|
657
|
-
if (result.authorityDigest !== scope.compositionDigest) throw new Error("MCP_AUTHORITY_MISMATCH");
|
|
658
|
-
return this.save(result);
|
|
659
|
-
}
|
|
660
|
-
get(invocationId) {
|
|
661
|
-
const result = this.#items.get(invocationId);
|
|
662
|
-
if (!result) return null;
|
|
663
|
-
verifyMcpResult2(result);
|
|
664
|
-
return structuredClone(result);
|
|
665
|
-
}
|
|
666
|
-
getByIdempotencyKey(key, scope) {
|
|
667
|
-
const result = [...this.#items.values()].find(
|
|
668
|
-
(item) => item.idempotencyKey === key && item.organizationId === scope.organizationId && item.projectId === scope.projectId && item.environmentId === scope.environmentId
|
|
669
|
-
);
|
|
670
|
-
if (!result) return null;
|
|
671
|
-
verifyMcpResult2(result);
|
|
672
|
-
return structuredClone(result);
|
|
673
|
-
}
|
|
674
|
-
list(scope, limit = 100) {
|
|
675
|
-
return [...this.#items.values()].filter((item) => item.organizationId === scope.organizationId && item.projectId === scope.projectId && item.environmentId === scope.environmentId).slice(0, Math.max(0, Math.min(500, limit))).map((item) => {
|
|
676
|
-
verifyMcpResult2(item);
|
|
677
|
-
return structuredClone(item);
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
};
|
|
681
|
-
SqlMcpProvenanceRepository = class {
|
|
682
|
-
db;
|
|
683
|
-
constructor(db) {
|
|
684
|
-
this.db = db;
|
|
685
|
-
this.db.run(
|
|
686
|
-
"CREATE TABLE IF NOT EXISTS mcp_invocation_claims (organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, idempotency_key TEXT NOT NULL, invocation_id TEXT NOT NULL, claimed_at TEXT NOT NULL, PRIMARY KEY (organization_id, project_id, environment_id, idempotency_key))",
|
|
687
|
-
[]
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
releaseClaim(input) {
|
|
691
|
-
assertClaimInput({ ...input, invocationId: "release-claim" });
|
|
692
|
-
this.db.run("DELETE FROM mcp_invocation_claims WHERE organization_id=? AND project_id=? AND environment_id=? AND idempotency_key=?", [input.organizationId, input.projectId, input.environmentId, input.idempotencyKey]);
|
|
693
|
-
}
|
|
694
|
-
claim(input) {
|
|
695
|
-
assertClaimInput(input);
|
|
696
|
-
const replay = this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
697
|
-
if (replay) return { status: "REPLAY", result: replay };
|
|
698
|
-
try {
|
|
699
|
-
this.db.run(
|
|
700
|
-
"INSERT INTO mcp_invocation_claims (organization_id, project_id, environment_id, idempotency_key, invocation_id, claimed_at) VALUES (?, ?, ?, ?, ?, ?)",
|
|
701
|
-
[
|
|
702
|
-
input.organizationId,
|
|
703
|
-
input.projectId,
|
|
704
|
-
input.environmentId,
|
|
705
|
-
input.idempotencyKey,
|
|
706
|
-
input.invocationId,
|
|
707
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
708
|
-
]
|
|
709
|
-
);
|
|
710
|
-
return { status: "CLAIMED" };
|
|
711
|
-
} catch {
|
|
712
|
-
const after = this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
713
|
-
return after ? { status: "REPLAY", result: after } : { status: "IN_FLIGHT" };
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
save(result) {
|
|
717
|
-
verifyMcpResult2(result);
|
|
718
|
-
const prior = this.getForScope(result, result.invocationId);
|
|
719
|
-
const byKey = this.getByIdempotencyKey(result.idempotencyKey, result);
|
|
720
|
-
const existing = prior ?? byKey;
|
|
721
|
-
if (existing) {
|
|
722
|
-
if (existing.provenanceDigest !== result.provenanceDigest)
|
|
723
|
-
throw new Error("MCP_PROVENANCE_CONFLICT");
|
|
724
|
-
return existing;
|
|
725
|
-
}
|
|
726
|
-
this.db.run(
|
|
727
|
-
"DELETE FROM mcp_invocation_claims WHERE organization_id=? AND project_id=? AND environment_id=? AND idempotency_key=?",
|
|
728
|
-
[
|
|
729
|
-
result.organizationId,
|
|
730
|
-
result.projectId,
|
|
731
|
-
result.environmentId,
|
|
732
|
-
result.idempotencyKey
|
|
733
|
-
]
|
|
734
|
-
);
|
|
735
|
-
this.db.run(
|
|
736
|
-
"INSERT INTO mcp_invocation_provenance (invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
737
|
-
[
|
|
738
|
-
result.invocationId,
|
|
739
|
-
result.organizationId,
|
|
740
|
-
result.projectId,
|
|
741
|
-
result.environmentId,
|
|
742
|
-
result.server,
|
|
743
|
-
result.tool,
|
|
744
|
-
result.receiptId,
|
|
745
|
-
result.idempotencyKey,
|
|
746
|
-
result.argumentsDigest,
|
|
747
|
-
result.ok ? 1 : 0,
|
|
748
|
-
result.result === void 0 ? null : JSON.stringify(result.result),
|
|
749
|
-
result.error ?? null,
|
|
750
|
-
result.authorityDecisionDigest,
|
|
751
|
-
result.authorityDigest ?? null,
|
|
752
|
-
result.provenanceDigest
|
|
753
|
-
]
|
|
754
|
-
);
|
|
755
|
-
const persisted = this.getForScope(result, result.invocationId);
|
|
756
|
-
if (!persisted || persisted.provenanceDigest !== result.provenanceDigest || persisted.receiptId !== result.receiptId || persisted.argumentsDigest !== result.argumentsDigest || persisted.authorityDecisionDigest !== result.authorityDecisionDigest || persisted.authorityDigest !== result.authorityDigest) throw new Error("MCP_PROVENANCE_READBACK_MISMATCH");
|
|
757
|
-
return persisted;
|
|
758
|
-
}
|
|
759
|
-
saveComposed(scope, result) {
|
|
760
|
-
assertComposedScopeIntegrity(scope);
|
|
761
|
-
const projected = { organizationId: scope.chain.find((item) => item.level === "organization")?.id ?? "", projectId: scope.chain.find((item) => item.level === "project")?.id ?? "", environmentId: scope.chain.find((item) => item.level === "application")?.id ?? "" };
|
|
762
|
-
if (result.organizationId !== projected.organizationId || result.projectId !== projected.projectId || result.environmentId !== projected.environmentId) throw new Error("MCP_SCOPE_MISMATCH");
|
|
763
|
-
if (result.authorityDigest !== scope.compositionDigest) throw new Error("MCP_AUTHORITY_MISMATCH");
|
|
764
|
-
return this.save(result);
|
|
765
|
-
}
|
|
766
|
-
get(invocationId) {
|
|
767
|
-
const row = this.db.exec(
|
|
768
|
-
"SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE invocation_id = ?",
|
|
769
|
-
[invocationId]
|
|
770
|
-
)[0]?.values[0];
|
|
771
|
-
return row ? this.fromRow(row) : null;
|
|
772
|
-
}
|
|
773
|
-
getByIdempotencyKey(key, scope) {
|
|
774
|
-
const row = this.db.exec(
|
|
775
|
-
"SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE idempotency_key = ? AND organization_id = ? AND project_id = ? AND environment_id = ?",
|
|
776
|
-
[key, scope.organizationId, scope.projectId, scope.environmentId]
|
|
777
|
-
)[0]?.values[0];
|
|
778
|
-
return row ? this.fromRow(row) : null;
|
|
779
|
-
}
|
|
780
|
-
getForScope(scope, invocationId) {
|
|
781
|
-
const row = this.db.exec(
|
|
782
|
-
"SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE invocation_id = ? AND organization_id = ? AND project_id = ? AND environment_id = ?",
|
|
783
|
-
[
|
|
784
|
-
invocationId,
|
|
785
|
-
scope.organizationId,
|
|
786
|
-
scope.projectId,
|
|
787
|
-
scope.environmentId
|
|
788
|
-
]
|
|
789
|
-
)[0]?.values[0];
|
|
790
|
-
return row ? this.fromRow(row) : null;
|
|
791
|
-
}
|
|
792
|
-
list(scope, limit = 100) {
|
|
793
|
-
const bounded = Math.max(0, Math.min(500, limit));
|
|
794
|
-
const rows = this.db.exec("SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE organization_id=? AND project_id=? AND environment_id=? ORDER BY invocation_id DESC LIMIT ?", [scope.organizationId, scope.projectId, scope.environmentId, bounded])[0]?.values ?? [];
|
|
795
|
-
return rows.map((row) => this.fromRow(row));
|
|
796
|
-
}
|
|
797
|
-
fromRow(row) {
|
|
798
|
-
const result = {
|
|
799
|
-
invocationId: String(row[0]),
|
|
800
|
-
organizationId: String(row[1]),
|
|
801
|
-
projectId: String(row[2]),
|
|
802
|
-
environmentId: String(row[3]),
|
|
803
|
-
server: String(row[4]),
|
|
804
|
-
tool: String(row[5]),
|
|
805
|
-
receiptId: String(row[6]),
|
|
806
|
-
idempotencyKey: String(row[7]),
|
|
807
|
-
argumentsDigest: String(row[8]),
|
|
808
|
-
ok: Number(row[9]) === 1,
|
|
809
|
-
result: row[10] === null || row[10] === void 0 ? void 0 : JSON.parse(String(row[10])),
|
|
810
|
-
error: row[11] === null || row[11] === void 0 ? void 0 : String(row[11]),
|
|
811
|
-
authorityDecisionDigest: String(row[12]),
|
|
812
|
-
authorityDigest: row[13] === null || row[13] === void 0 ? null : String(row[13]),
|
|
813
|
-
provenanceDigest: String(row[14])
|
|
814
|
-
};
|
|
815
|
-
verifyMcpResult2(result);
|
|
816
|
-
return result;
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
// ../../apps/mcp-server/vira-server.ts
|
|
823
|
-
function resolveViraMcpConfig(environment = process.env) {
|
|
824
|
-
const baseUrl = (environment.VIRA_API_URL ?? "http://127.0.0.1:4185").replace(/\/$/, "");
|
|
825
|
-
const apiKey = environment.VIRA_API_KEY ?? (environment.NODE_ENV === "production" ? "" : "vira_dev_local.local-development-only-32chars");
|
|
826
|
-
if (!apiKey) throw new Error("VIRA_API_KEY_REQUIRED_IN_PRODUCTION");
|
|
827
|
-
const authorityUrl = environment.VIRA_MCP_AUTHORITY_URL;
|
|
828
|
-
return { baseUrl, apiKey, authorityUrl, serverName: environment.VIRA_MCP_SERVER_NAME ?? "vira-behavior-control", requestAuthToken: environment.VIRA_MCP_AUTH_TOKEN, ...authorityUrl ? { authorizeEffectful: async (input) => {
|
|
829
|
-
const response = await fetch(`${authorityUrl}`, { method: "POST", headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" }, body: JSON.stringify(input) });
|
|
830
|
-
const payload = await response.json().catch(() => null);
|
|
831
|
-
return { allowed: response.ok && payload?.allowed === true, decisionDigest: payload?.decisionDigest ?? "", ...payload?.reason ? { reason: payload.reason } : {} };
|
|
832
|
-
} } : {} };
|
|
833
|
-
}
|
|
834
|
-
function assertScope(input, scope) {
|
|
835
|
-
for (const key of ["organizationId", "projectId", "environmentId"]) if (input[key] !== scope[key]) throw new Error("MCP_VIRA_SCOPE_MISMATCH");
|
|
836
|
-
}
|
|
837
|
-
function createViraMcpServer(config) {
|
|
838
|
-
return createMcpServer(createViraMcpOptions(config));
|
|
839
|
-
}
|
|
840
|
-
function createViraMcpOptions(config) {
|
|
841
|
-
if (!config.baseUrl?.trim() || !config.apiKey?.trim()) throw new Error("VIRA_MCP_CONFIG_REQUIRED");
|
|
842
|
-
const fetcher = config.fetcher ?? fetch;
|
|
843
|
-
const api = async (path, body, requestId) => {
|
|
844
|
-
const response = await fetcher(`${config.baseUrl.replace(/\/$/, "")}${path}`, { method: body ? "POST" : "GET", headers: { authorization: `Bearer ${config.apiKey}`, "content-type": "application/json", "idempotency-key": requestId }, ...body ? { body: JSON.stringify(body) } : {} });
|
|
845
|
-
const payload = await response.json().catch(() => null);
|
|
846
|
-
if (!response.ok) return { error: `VIRA_API_${response.status}`, result: payload };
|
|
847
|
-
return { result: payload };
|
|
848
|
-
};
|
|
849
|
-
const options = { serverName: config.serverName ?? "vira-behavior-control", requestAuthToken: config.requestAuthToken, tools, invocation: { ...config.scope, id: "mcp-vira", receiptId: "mcp-vira", idempotencyKey: "mcp-vira" }, transport: { call: async ({ tool, arguments: args }) => {
|
|
850
|
-
assertScope(args, config.scope);
|
|
851
|
-
const scopeQuery = `organizationId=${encodeURIComponent(config.scope.organizationId)}&projectId=${encodeURIComponent(config.scope.projectId)}&environmentId=${encodeURIComponent(config.scope.environmentId)}`;
|
|
852
|
-
if (tool === "list_behavior_programs") return api(`/v1/behavior-programs?projectId=${encodeURIComponent(config.scope.projectId)}&environmentId=${encodeURIComponent(config.scope.environmentId)}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
853
|
-
if (tool === "list_behavior_strategies") return api(`/v1/behavior-strategies?${scopeQuery}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
854
|
-
if (tool === "get_behavior_strategy") return api(`/v1/behavior-strategies/${encodeURIComponent(String(args.strategyId))}?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.strategyId)}`);
|
|
855
|
-
if (tool === "simulate_behavior_strategy") return api(`/v1/behavior-strategies/${encodeURIComponent(String(args.strategyId))}/simulate?${scopeQuery}`, { initialState: args.initialState ?? {} }, `mcp:${tool}:${String(args.strategyId)}`);
|
|
856
|
-
if (tool === "start_strategy_execution") return api("/v1/runtime/strategy-executions", args, `mcp:${tool}:${String(args.executionId ?? args.artifactDigest)}`);
|
|
857
|
-
if (tool === "get_strategy_execution") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.executionId)}`);
|
|
858
|
-
if (tool === "get_strategy_execution_trace") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/trace?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.executionId)}`);
|
|
859
|
-
if (tool === "continue_strategy_execution") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/continue`, args.authorityDigest ? { authorityDigest: args.authorityDigest } : {}, `mcp:${tool}:${String(args.executionId)}`);
|
|
860
|
-
if (tool === "cancel_strategy_execution") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/cancel`, {}, `mcp:${tool}:${String(args.executionId)}`);
|
|
861
|
-
if (tool === "list_ai_systems") return api(`/v1/ai-systems?${scopeQuery}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
862
|
-
if (tool === "list_discovery_candidates") return api(`/v1/discovery/candidates?${scopeQuery}${args.reviewState ? `&reviewState=${encodeURIComponent(String(args.reviewState))}` : ""}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
863
|
-
if (tool === "review_discovery_candidate") return api(`/v1/discovery/candidates/${encodeURIComponent(String(args.kind))}/${encodeURIComponent(String(args.key))}/review`, { ...args }, `mcp:${tool}:${String(args.kind)}:${String(args.key)}`);
|
|
864
|
-
if (tool === "create_observation") return api("/v1/observations", args, `mcp:${tool}:${String(args.id)}`);
|
|
865
|
-
if (tool === "start_evaluation") return api("/v1/evaluations", args, `mcp:${tool}:${String(args.id)}`);
|
|
866
|
-
if (tool === "compile_behavior_capsule") return api("/v1/runtime/behavior-capsule", args, `mcp:${tool}:${String(args.programId)}`);
|
|
867
|
-
if (tool === "serve_behavior_projection") return api("/v1/runtime/behavior-projection", args, `mcp:${tool}:${String(args.programId ?? "active")}`);
|
|
868
|
-
throw new Error("MCP_TOOL_NOT_IMPLEMENTED");
|
|
869
|
-
} }, authorize: ({ invocation, definition }) => definition.effectful ? config.authorizeEffectful ? config.authorizeEffectful({ tool: definition.name, arguments: invocation.arguments, scope: { organizationId: invocation.organizationId, projectId: invocation.projectId, environmentId: invocation.environmentId } }) : { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" }, ...config.provenanceRepository ? { provenanceRepository: config.provenanceRepository } : {}, ...config.sessionRepository ? { sessionRepository: config.sessionRepository } : {}, ...config.sessionId ? { sessionId: config.sessionId } : {}, ...config.invocationExecutor ? { invocationExecutor: config.invocationExecutor } : {} };
|
|
870
|
-
const originalTransport = options.transport;
|
|
871
|
-
const originalCall = originalTransport.call;
|
|
872
|
-
options.transport = { ...originalTransport, call: async (input) => {
|
|
873
|
-
const { tool, arguments: args } = input;
|
|
874
|
-
if (tool === "list_packs") return api("/v1/packs", void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
875
|
-
if (tool === "import_pack") return api("/v1/packs/import", args, `mcp:${tool}:${config.scope.projectId}`);
|
|
876
|
-
if (["install_pack", "evaluate_pack", "approve_pack", "release_pack", "preview_pack_upgrade"].includes(tool)) {
|
|
877
|
-
const action = { install_pack: "install", evaluate_pack: "evaluate", approve_pack: "approve", release_pack: "release", preview_pack_upgrade: "upgrade-preview" }[tool];
|
|
878
|
-
const { packId, ...body } = args;
|
|
879
|
-
return api(`/v1/packs/${encodeURIComponent(String(packId))}/${action}`, body, `mcp:${tool}:${String(packId)}`);
|
|
880
|
-
}
|
|
881
|
-
return originalCall(input);
|
|
882
|
-
} };
|
|
883
|
-
originalTransport.call = options.transport.call;
|
|
884
|
-
options.transport = { ...originalTransport, call: async (input) => {
|
|
885
|
-
if (input.tool === "resolve_semantic" || input.tool === "resolve_runtime") assertScope(input.arguments, config.scope);
|
|
886
|
-
return input.tool === "resolve_semantic" ? api("/v1/semantic/resolve", input.arguments, `mcp:resolve_semantic:${String(input.arguments.input)}`) : input.tool === "resolve_runtime" ? api("/v1/runtime/resolve", input.arguments, `mcp:resolve_runtime:${String(input.arguments.behaviorPoint)}`) : originalTransport.call(input);
|
|
887
|
-
} };
|
|
888
|
-
const signalTransport = options.transport;
|
|
889
|
-
options.transport = { ...signalTransport, call: async (input) => {
|
|
890
|
-
const { tool, arguments: args } = input;
|
|
891
|
-
if (tool.startsWith("signal_") || tool === "create_signal_discovery_proposal" || tool === "review_signal_discovery_proposal" || tool === "materialize_signal_discovery" || tool === "create_signal_mapping" || tool === "get_signal_mapping" || tool === "review_signal_mapping") assertScope(args, config.scope);
|
|
892
|
-
const scopeQuery = `organizationId=${encodeURIComponent(config.scope.organizationId)}&projectId=${encodeURIComponent(config.scope.projectId)}&environmentId=${encodeURIComponent(config.scope.environmentId)}`;
|
|
893
|
-
if (tool === "create_signal_discovery_proposal") return api("/v1/signals/discovery-proposals", args, `mcp:${tool}:${String(args.signalStableKey)}`);
|
|
894
|
-
if (tool === "review_signal_discovery_proposal") return api(`/v1/signals/discovery-proposals/${encodeURIComponent(String(args.proposalId))}/review`, { decision: args.decision }, `mcp:${tool}:${String(args.proposalId)}`);
|
|
895
|
-
if (tool === "materialize_signal_discovery") {
|
|
896
|
-
const { proposalId, ...body } = args;
|
|
897
|
-
return api(`/v1/signals/discovery-proposals/${encodeURIComponent(String(proposalId))}/materialize`, body, `mcp:${tool}:${String(proposalId)}`);
|
|
898
|
-
}
|
|
899
|
-
if (tool === "create_signal_mapping") return api("/v1/signals/mappings", args, `mcp:${tool}:${String(args.signalStableKey)}`);
|
|
900
|
-
if (tool === "get_signal_mapping") return api(`/v1/signals/mappings/${encodeURIComponent(String(args.mappingId))}?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.mappingId)}`);
|
|
901
|
-
if (tool === "review_signal_mapping") {
|
|
902
|
-
const { mappingId, ...body } = args;
|
|
903
|
-
return api(`/v1/signals/mappings/${encodeURIComponent(String(mappingId))}/review`, body, `mcp:${tool}:${String(mappingId)}`);
|
|
904
|
-
}
|
|
905
|
-
return signalTransport.call(input);
|
|
906
|
-
} };
|
|
907
|
-
const authoringTransport = options.transport;
|
|
908
|
-
options.transport = { ...authoringTransport, call: async (input) => {
|
|
909
|
-
const { tool, arguments: args } = input;
|
|
910
|
-
if (tool === "list_capabilities") return api(`/v1/capabilities${args.surface ? `?surface=${encodeURIComponent(String(args.surface))}` : ""}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
911
|
-
if (tool === "list_semantic_tasks") return api("/v1/semantic-tasks", void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
912
|
-
if (tool === "create_semantic_task") return api("/v1/semantic-tasks", args, `mcp:${tool}:${String(args.taskKey)}`);
|
|
913
|
-
if (tool === "list_behavior_points") return api("/v1/behavior-points", void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
914
|
-
if (tool === "create_behavior_point") return api("/v1/behavior-points", args, `mcp:${tool}:${String(args.key)}`);
|
|
915
|
-
if (tool === "
|
|
916
|
-
return
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
if (tool === "
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
{ name: "
|
|
964
|
-
{ name: "
|
|
965
|
-
{ name: "
|
|
966
|
-
{ name: "
|
|
967
|
-
{ name: "
|
|
968
|
-
{ name: "
|
|
969
|
-
{ name: "
|
|
970
|
-
{ name: "
|
|
971
|
-
{ name: "
|
|
972
|
-
{ name: "
|
|
973
|
-
{ name: "
|
|
974
|
-
{ name: "
|
|
975
|
-
{ name: "
|
|
976
|
-
{ name: "
|
|
977
|
-
{ name: "
|
|
978
|
-
{ name: "
|
|
979
|
-
{ name: "
|
|
980
|
-
{ name: "
|
|
981
|
-
{ name: "
|
|
982
|
-
{ name: "
|
|
983
|
-
{ name: "
|
|
984
|
-
{ name: "
|
|
985
|
-
{ name: "
|
|
986
|
-
{ name: "
|
|
987
|
-
{ name: "
|
|
988
|
-
{ name: "
|
|
989
|
-
{ name: "
|
|
990
|
-
{ name: "
|
|
991
|
-
{ name: "
|
|
992
|
-
{ name: "
|
|
993
|
-
{ name: "
|
|
994
|
-
{ name: "
|
|
995
|
-
{ name: "
|
|
996
|
-
{ name: "
|
|
997
|
-
{ name: "
|
|
998
|
-
{ name: "
|
|
999
|
-
{ name: "
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
client
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
"
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
if (
|
|
1140
|
-
if (
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
continue;
|
|
1145
|
-
}
|
|
1146
|
-
if (
|
|
1147
|
-
lineComment =
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const
|
|
1190
|
-
if (
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
END;
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
NEW.
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
import {
|
|
1328
|
-
import
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
)`);
|
|
1359
|
-
database.run(`CREATE TABLE IF NOT EXISTS
|
|
1360
|
-
receipt_id TEXT PRIMARY KEY,
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
["behavior_programs", "
|
|
1382
|
-
["
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
["
|
|
1393
|
-
["
|
|
1394
|
-
["
|
|
1395
|
-
["behavior_variants", "
|
|
1396
|
-
["behavior_variants", "
|
|
1397
|
-
["behavior_variants", "
|
|
1398
|
-
["behavior_variants", "
|
|
1399
|
-
["behavior_variants", "
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
const
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
if (
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
const
|
|
1444
|
-
if (existsSync(
|
|
1445
|
-
const
|
|
1446
|
-
if (existsSync(
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
"ALTER TABLE release_manifests ADD COLUMN
|
|
1537
|
-
"ALTER TABLE release_manifests ADD COLUMN
|
|
1538
|
-
"ALTER TABLE release_manifests ADD COLUMN
|
|
1539
|
-
"ALTER TABLE release_manifests ADD COLUMN
|
|
1540
|
-
"ALTER TABLE release_manifests ADD COLUMN
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
database.run("CREATE TABLE IF NOT EXISTS
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
if (
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
});
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
this.db
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
async
|
|
1901
|
-
await this.
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
await this.
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
return
|
|
1932
|
-
}
|
|
1933
|
-
async
|
|
1934
|
-
const row = (await this.db.query(`${provenanceSelect} WHERE
|
|
1935
|
-
return row ? this.fromRow(row) : null;
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
this.db
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
const
|
|
1973
|
-
const
|
|
1974
|
-
const
|
|
1975
|
-
|
|
1976
|
-
const
|
|
1977
|
-
const
|
|
1978
|
-
const
|
|
1979
|
-
const
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
const
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
const
|
|
2030
|
-
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
const
|
|
2034
|
-
|
|
2035
|
-
const
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
const port = Number(process.env.PORT ?? 4195);
|
|
2040
|
-
const host = process.env.VIRA_MCP_BIND_PUBLIC === "true" ? process.env.HOST ?? "0.0.0.0" : process.env.HOST ?? "127.0.0.1";
|
|
2041
|
-
server.listen(port, host, () => console.error(JSON.stringify({ status: "VIRA_MCP_STARTED", port, host, upstream: base.baseUrl, effectful: allowLocalEffects })));
|
|
2042
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ../domain/src/scope-composer.ts
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
function canonical(value) {
|
|
20
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
21
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
22
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
|
|
23
|
+
}
|
|
24
|
+
function assertComposedScopeIntegrity(composed) {
|
|
25
|
+
const conflictSetDigest = digest(composed.conflicts);
|
|
26
|
+
if (conflictSetDigest !== composed.conflictSetDigest) throw new Error("SCOPE_CONFLICT_DIGEST_MISMATCH");
|
|
27
|
+
const compositionDigest = digest({ chain: composed.chain, permissions: composed.permissions, delegationIds: [...composed.delegationIds ?? []].sort(), conflictSetDigest, effectiveRevision: composed.effectiveRevision ?? null, sourceRevisionSetDigest: composed.sourceRevisionSetDigest ?? null, ancestorHardAuthorityDigest: composed.ancestorHardAuthorityDigest ?? null, effectiveProgramDigest: composed.effectiveProgramDigest ?? null });
|
|
28
|
+
if (compositionDigest !== composed.compositionDigest) throw new Error("SCOPE_COMPOSITION_DIGEST_MISMATCH");
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
var digest;
|
|
32
|
+
var init_scope_composer = __esm({
|
|
33
|
+
"../domain/src/scope-composer.ts"() {
|
|
34
|
+
"use strict";
|
|
35
|
+
digest = (value) => createHash("sha256").update(canonical(value)).digest("hex");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ../runtime/src/mcp-gateway.ts
|
|
40
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
41
|
+
function canonicalMcpJson(value) {
|
|
42
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
43
|
+
if (Array.isArray(value)) return `[${value.map(canonicalMcpJson).join(",")}]`;
|
|
44
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalMcpJson(value[key])}`).join(",")}}`;
|
|
45
|
+
}
|
|
46
|
+
function verifyMcpResult(result) {
|
|
47
|
+
const { provenanceDigest, ...body } = result;
|
|
48
|
+
const expected = createHash2("sha256").update(canonicalMcpJson(body)).digest("hex");
|
|
49
|
+
if (provenanceDigest !== expected) throw new Error("MCP_PROVENANCE_DIGEST_INVALID");
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
function createMcpCancellationRegistry() {
|
|
53
|
+
const controllers = /* @__PURE__ */ new Map();
|
|
54
|
+
return { register(requestId) {
|
|
55
|
+
if (!requestId.trim()) throw new Error("MCP_REQUEST_ID_REQUIRED");
|
|
56
|
+
const previous = controllers.get(requestId);
|
|
57
|
+
previous?.abort();
|
|
58
|
+
const controller = new AbortController();
|
|
59
|
+
controllers.set(requestId, controller);
|
|
60
|
+
return controller.signal;
|
|
61
|
+
}, cancel(requestId) {
|
|
62
|
+
const controller = controllers.get(requestId);
|
|
63
|
+
if (!controller) return false;
|
|
64
|
+
controller.abort();
|
|
65
|
+
return true;
|
|
66
|
+
}, unregister(requestId, signal) {
|
|
67
|
+
const controller = controllers.get(requestId);
|
|
68
|
+
if (controller && (!signal || controller.signal === signal)) controllers.delete(requestId);
|
|
69
|
+
} };
|
|
70
|
+
}
|
|
71
|
+
function combineMcpAbortSignals(...signals) {
|
|
72
|
+
const active = signals.filter((signal) => Boolean(signal));
|
|
73
|
+
if (active.length === 0) return void 0;
|
|
74
|
+
if (active.length === 1) return active[0];
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const abort = () => controller.abort();
|
|
77
|
+
for (const signal of active) {
|
|
78
|
+
if (signal.aborted) {
|
|
79
|
+
controller.abort();
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
83
|
+
}
|
|
84
|
+
return controller.signal;
|
|
85
|
+
}
|
|
86
|
+
async function handleMcpJsonRpc(input) {
|
|
87
|
+
const { request, server, tools: tools2, transport } = input;
|
|
88
|
+
if (request.jsonrpc !== "2.0" || !request.method) throw new Error("MCP_JSONRPC_INVALID");
|
|
89
|
+
if (request.method === "initialize") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { protocolVersion: "2025-03-26", serverInfo: { name: server, version: "1" }, capabilities: { tools: {}, resources: input.resources ? {} : void 0, prompts: input.prompts ? {} : void 0 } } };
|
|
90
|
+
if (request.method === "notifications/initialized") return null;
|
|
91
|
+
if (request.method === "notifications/cancelled") {
|
|
92
|
+
const requestId2 = String(request.params?.requestId ?? "");
|
|
93
|
+
input.cancellationRegistry?.cancel(requestId2);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (request.method === "tools/list") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { tools: tools2 } };
|
|
97
|
+
if (request.method === "resources/list") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { resources: input.resources ?? [] } };
|
|
98
|
+
if (request.method === "prompts/list") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { prompts: input.prompts ?? [] } };
|
|
99
|
+
if (request.method === "resources/read") {
|
|
100
|
+
const value = input.readResource ? await input.readResource(String(request.params?.uri ?? "")) : null;
|
|
101
|
+
return request.id === void 0 ? null : value ? { jsonrpc: "2.0", id: request.id, result: value } : { jsonrpc: "2.0", id: request.id, error: { code: -32602, message: "RESOURCE_NOT_FOUND" } };
|
|
102
|
+
}
|
|
103
|
+
if (request.method === "prompts/get") {
|
|
104
|
+
const value = input.getPrompt ? await input.getPrompt(String(request.params?.name ?? ""), request.params?.arguments ?? {}) : null;
|
|
105
|
+
return request.id === void 0 ? null : value ? { jsonrpc: "2.0", id: request.id, result: value } : { jsonrpc: "2.0", id: request.id, error: { code: -32602, message: "PROMPT_NOT_FOUND" } };
|
|
106
|
+
}
|
|
107
|
+
if (request.method !== "tools/call") return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, error: { code: -32601, message: "METHOD_NOT_FOUND" } };
|
|
108
|
+
const tool = String(request.params?.name ?? "");
|
|
109
|
+
const definition = tools2.find((item) => item.name === tool);
|
|
110
|
+
if (!definition) return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, error: { code: -32602, message: "TOOL_NOT_FOUND" } };
|
|
111
|
+
const invocation = { ...input.invocation, server, tool, arguments: request.params?.arguments ?? {} };
|
|
112
|
+
const authority = definition.effectful ? input.authorize ? await input.authorize({ invocation, definition }) : { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
113
|
+
if (!authority.allowed) return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, error: { code: -32003, message: authority.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority.decisionDigest } } };
|
|
114
|
+
const requestId = String(request.id ?? "");
|
|
115
|
+
const requestSignal = input.cancellationRegistry && requestId ? input.cancellationRegistry.register(requestId) : void 0;
|
|
116
|
+
try {
|
|
117
|
+
const result = await invokeMcpTool({ invocation, transport, authority, signal: combineMcpAbortSignals(input.signal, requestSignal) });
|
|
118
|
+
return request.id === void 0 ? null : { jsonrpc: "2.0", id: request.id, result: { content: [{ type: "text", text: JSON.stringify(result.result ?? result.error ?? null) }], isError: !result.ok, provenanceDigest: result.provenanceDigest } };
|
|
119
|
+
} finally {
|
|
120
|
+
if (requestSignal) input.cancellationRegistry?.unregister(requestId, requestSignal);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function invokeMcpTool(input) {
|
|
124
|
+
const { invocation, transport } = input;
|
|
125
|
+
if (!invocation.id || !invocation.server || !invocation.tool || !invocation.receiptId || !invocation.idempotencyKey) throw new Error("MCP_INVOCATION_FIELDS_REQUIRED");
|
|
126
|
+
if (invocation.authorityDigest !== void 0 && !/^[a-f0-9]{64}$/.test(invocation.authorityDigest)) throw new Error("MCP_AUTHORITY_DIGEST_INVALID");
|
|
127
|
+
if (invocation.composedScope) {
|
|
128
|
+
assertComposedScopeIntegrity(invocation.composedScope);
|
|
129
|
+
if (invocation.authorityDigest !== void 0 && invocation.authorityDigest !== invocation.composedScope.compositionDigest) throw new Error("MCP_AUTHORITY_MISMATCH");
|
|
130
|
+
if (!invocation.composedScope.serveable) throw new Error("MCP_SCOPE_COMPOSITION_CONFLICT");
|
|
131
|
+
}
|
|
132
|
+
if (input.authority && !input.authority.allowed) throw new Error("MCP_AUTHORITY_DENIED");
|
|
133
|
+
if (!transport || typeof transport.call !== "function") throw new Error("MCP_TRANSPORT_REQUIRED");
|
|
134
|
+
let response;
|
|
135
|
+
try {
|
|
136
|
+
response = await transport.call({ server: invocation.server, tool: invocation.tool, arguments: structuredClone(invocation.arguments), signal: input.signal });
|
|
137
|
+
} catch {
|
|
138
|
+
if (input.signal?.aborted) throw new Error("MCP_INVOCATION_CANCELLED");
|
|
139
|
+
response = { error: "MCP_TRANSPORT_ERROR" };
|
|
140
|
+
}
|
|
141
|
+
const argumentsDigest = createHash2("sha256").update(canonicalMcpJson(invocation.arguments)).digest("hex");
|
|
142
|
+
const body = { invocationId: invocation.id, organizationId: invocation.organizationId, projectId: invocation.projectId, environmentId: invocation.environmentId, server: invocation.server, tool: invocation.tool, receiptId: invocation.receiptId, idempotencyKey: invocation.idempotencyKey, argumentsDigest, authorityDecisionDigest: input.authority?.decisionDigest ?? "not-required", authorityDigest: invocation.authorityDigest ?? null, ok: !response.error, result: response.result, error: response.error };
|
|
143
|
+
return Object.freeze({ ...body, provenanceDigest: createHash2("sha256").update(canonicalMcpJson(body)).digest("hex") });
|
|
144
|
+
}
|
|
145
|
+
var init_mcp_gateway = __esm({
|
|
146
|
+
"../runtime/src/mcp-gateway.ts"() {
|
|
147
|
+
"use strict";
|
|
148
|
+
init_scope_composer();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ../runtime/src/mcp-provenance.ts
|
|
153
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
154
|
+
function verifyMcpResult2(result) {
|
|
155
|
+
const { provenanceDigest: stored, ...body } = result;
|
|
156
|
+
const expected = createHash3("sha256").update(canonicalMcpJson(body)).digest("hex");
|
|
157
|
+
if (stored !== expected) throw new Error("MCP_PROVENANCE_DIGEST_MISMATCH");
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
function createMcpResultFromResponse(input) {
|
|
161
|
+
const argumentsDigest = createHash3("sha256").update(canonicalMcpJson(input.invocation.arguments)).digest("hex");
|
|
162
|
+
const body = { invocationId: input.invocation.id, organizationId: input.invocation.organizationId, projectId: input.invocation.projectId, environmentId: input.invocation.environmentId, server: input.invocation.server, tool: input.invocation.tool, receiptId: input.invocation.receiptId, idempotencyKey: input.invocation.idempotencyKey, argumentsDigest, authorityDecisionDigest: input.authority?.decisionDigest ?? "not-required", authorityDigest: input.invocation.authorityDigest ?? null, ok: !input.response.error, result: input.response.result, error: input.response.error };
|
|
163
|
+
return Object.freeze({ ...body, provenanceDigest: createHash3("sha256").update(canonicalMcpJson(body)).digest("hex") });
|
|
164
|
+
}
|
|
165
|
+
var init_mcp_provenance = __esm({
|
|
166
|
+
"../runtime/src/mcp-provenance.ts"() {
|
|
167
|
+
"use strict";
|
|
168
|
+
init_mcp_gateway();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// ../runtime/src/mcp-session.ts
|
|
173
|
+
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
174
|
+
function assertMcpSession(session) {
|
|
175
|
+
const { sessionDigest, ...base } = session;
|
|
176
|
+
if (sessionDigest !== digest2(base)) throw new Error("MCP_SESSION_DIGEST_MISMATCH");
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
function assertMcpSessionActive(session) {
|
|
180
|
+
assertMcpSession(session);
|
|
181
|
+
if (session.status !== "ACTIVE") throw new Error(`MCP_SESSION_NOT_ACTIVE:${session.status}`);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
var canonical2, digest2;
|
|
185
|
+
var init_mcp_session = __esm({
|
|
186
|
+
"../runtime/src/mcp-session.ts"() {
|
|
187
|
+
"use strict";
|
|
188
|
+
canonical2 = (value) => value === null || typeof value !== "object" ? JSON.stringify(value) : Array.isArray(value) ? `[${value.map(canonical2).join(",")}]` : `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical2(value[key])}`).join(",")}}`;
|
|
189
|
+
digest2 = (value) => createHash4("sha256").update(canonical2(value)).digest("hex");
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ../../apps/mcp-server/server.ts
|
|
194
|
+
import {
|
|
195
|
+
createServer
|
|
196
|
+
} from "node:http";
|
|
197
|
+
import { createInterface } from "node:readline";
|
|
198
|
+
async function assertConfiguredSession(options, sessionId, scope) {
|
|
199
|
+
if (!options.sessionRepository) return;
|
|
200
|
+
if (!sessionId?.trim()) throw new Error("MCP_SESSION_REQUIRED");
|
|
201
|
+
const session = await options.sessionRepository.get(sessionId, scope);
|
|
202
|
+
if (!session) throw new Error("MCP_SESSION_NOT_FOUND");
|
|
203
|
+
assertMcpSessionActive(session);
|
|
204
|
+
}
|
|
205
|
+
async function persistVerifiedMcpResult(repository, result) {
|
|
206
|
+
const persisted = await repository.save(result);
|
|
207
|
+
if (persisted && typeof persisted === "object") verifyMcpResult(persisted);
|
|
208
|
+
}
|
|
209
|
+
function createMcpServer(options) {
|
|
210
|
+
if (options.requireInvocationExecutor && !options.invocationExecutor)
|
|
211
|
+
throw new Error("MCP_INVOCATION_EXECUTOR_REQUIRED");
|
|
212
|
+
const maxBodyBytes = options.maxBodyBytes ?? 128 * 1024;
|
|
213
|
+
const cancellationRegistry = options.cancellationRegistry ?? createMcpCancellationRegistry();
|
|
214
|
+
return createServer(async (request, response) => {
|
|
215
|
+
if (request.method !== "POST")
|
|
216
|
+
return send(response, 405, { error: { code: "METHOD_NOT_ALLOWED" } });
|
|
217
|
+
if (options.requestAuthToken) {
|
|
218
|
+
const authorization = request.headers.authorization ?? "";
|
|
219
|
+
const token = authorization.match(/^Bearer\s+(.+)$/i)?.[1];
|
|
220
|
+
if (token !== options.requestAuthToken)
|
|
221
|
+
return send(response, 401, { error: { code: "MCP_AUTHENTICATION_REQUIRED" } });
|
|
222
|
+
}
|
|
223
|
+
const requestController = new AbortController();
|
|
224
|
+
const onAborted = () => requestController.abort();
|
|
225
|
+
request.once("aborted", onAborted);
|
|
226
|
+
try {
|
|
227
|
+
const body = JSON.parse(await readBody(request, maxBodyBytes));
|
|
228
|
+
const token = requestToken(body.id);
|
|
229
|
+
const requestInvocation = {
|
|
230
|
+
...options.invocation,
|
|
231
|
+
id: `${options.invocation.id}:${token}`,
|
|
232
|
+
receiptId: `${options.invocation.receiptId}:${token}`,
|
|
233
|
+
idempotencyKey: `${options.invocation.idempotencyKey}:${token}`
|
|
234
|
+
};
|
|
235
|
+
const scope = {
|
|
236
|
+
organizationId: requestInvocation.organizationId,
|
|
237
|
+
projectId: requestInvocation.projectId,
|
|
238
|
+
environmentId: requestInvocation.environmentId
|
|
239
|
+
};
|
|
240
|
+
const sessionId = request.headers[(options.sessionHeader ?? "x-mcp-session-id").toLowerCase()];
|
|
241
|
+
await assertConfiguredSession(options, typeof sessionId === "string" ? sessionId : void 0, scope);
|
|
242
|
+
if (options.invocationExecutor && body.method === "tools/call") {
|
|
243
|
+
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
244
|
+
if (definition) {
|
|
245
|
+
const invocation = { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} };
|
|
246
|
+
const authority2 = definition.effectful ? await options.authorize?.({ invocation, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
247
|
+
if (authority2.allowed) {
|
|
248
|
+
const result2 = await options.invocationExecutor({ invocation, authority: authority2, signal: requestController.signal });
|
|
249
|
+
if (!requestController.signal.aborted) send(response, 200, { jsonrpc: "2.0", id: body.id, result: { content: [{ type: "text", text: JSON.stringify(result2.result ?? result2.error ?? null) }], isError: !result2.ok, provenanceDigest: result2.provenanceDigest } });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!requestController.signal.aborted) send(response, 403, { jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (options.provenanceRepository && body.method === "tools/call") {
|
|
257
|
+
const replay = await claimInvocation(
|
|
258
|
+
options.provenanceRepository,
|
|
259
|
+
requestInvocation,
|
|
260
|
+
scope
|
|
261
|
+
);
|
|
262
|
+
if (replay) {
|
|
263
|
+
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
264
|
+
if (definition?.effectful) {
|
|
265
|
+
const authority2 = await options.authorize?.({ invocation: { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} }, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" };
|
|
266
|
+
if (!authority2.allowed) {
|
|
267
|
+
if (!requestController.signal.aborted) send(response, 403, { jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (!requestController.signal.aborted)
|
|
272
|
+
send(response, 200, {
|
|
273
|
+
jsonrpc: "2.0",
|
|
274
|
+
id: body.id,
|
|
275
|
+
result: {
|
|
276
|
+
content: [
|
|
277
|
+
{
|
|
278
|
+
type: "text",
|
|
279
|
+
text: JSON.stringify(replay.result ?? replay.error ?? null)
|
|
280
|
+
}
|
|
281
|
+
],
|
|
282
|
+
isError: !replay.ok,
|
|
283
|
+
provenanceDigest: replay.provenanceDigest
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
let upstreamResponse;
|
|
290
|
+
let authority;
|
|
291
|
+
const requestCancelled = new Promise(
|
|
292
|
+
(resolve3) => request.once(
|
|
293
|
+
"aborted",
|
|
294
|
+
() => resolve3({ error: "MCP_REQUEST_CANCELLED" })
|
|
295
|
+
)
|
|
296
|
+
);
|
|
297
|
+
const transport = {
|
|
298
|
+
call: async (input) => {
|
|
299
|
+
upstreamResponse = await Promise.race([
|
|
300
|
+
options.transport.call({
|
|
301
|
+
...input,
|
|
302
|
+
signal: input.signal ?? requestController.signal
|
|
303
|
+
}),
|
|
304
|
+
requestCancelled
|
|
305
|
+
]);
|
|
306
|
+
return upstreamResponse;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const result = await handleMcpJsonRpc({
|
|
310
|
+
request: body,
|
|
311
|
+
server: options.serverName,
|
|
312
|
+
tools: options.tools,
|
|
313
|
+
resources: options.resources,
|
|
314
|
+
prompts: options.prompts,
|
|
315
|
+
readResource: options.readResource,
|
|
316
|
+
getPrompt: options.getPrompt,
|
|
317
|
+
invocation: requestInvocation,
|
|
318
|
+
transport,
|
|
319
|
+
cancellationRegistry,
|
|
320
|
+
authorize: async (input) => {
|
|
321
|
+
authority = await options.authorize?.(input) ?? (input.definition.effectful ? {
|
|
322
|
+
allowed: false,
|
|
323
|
+
decisionDigest: "",
|
|
324
|
+
reason: "MCP_AUTHORITY_REQUIRED"
|
|
325
|
+
} : { allowed: true, decisionDigest: "read-only" });
|
|
326
|
+
return authority;
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
if (options.provenanceRepository && body.method === "tools/call") {
|
|
330
|
+
const tool = String(body.params?.name ?? "");
|
|
331
|
+
await persistVerifiedMcpResult(
|
|
332
|
+
options.provenanceRepository,
|
|
333
|
+
createMcpResultFromResponse({
|
|
334
|
+
invocation: {
|
|
335
|
+
...requestInvocation,
|
|
336
|
+
server: options.serverName,
|
|
337
|
+
tool,
|
|
338
|
+
arguments: body.params?.arguments ?? {}
|
|
339
|
+
},
|
|
340
|
+
response: upstreamResponse ?? {
|
|
341
|
+
error: authority?.reason ?? "MCP_AUTHORITY_DENIED"
|
|
342
|
+
},
|
|
343
|
+
authority
|
|
344
|
+
})
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (!requestController.signal.aborted) send(response, 200, result);
|
|
348
|
+
} catch {
|
|
349
|
+
if (!requestController.signal.aborted)
|
|
350
|
+
send(response, 400, {
|
|
351
|
+
jsonrpc: "2.0",
|
|
352
|
+
id: null,
|
|
353
|
+
error: { code: -32600, message: "INVALID_MCP_REQUEST" }
|
|
354
|
+
});
|
|
355
|
+
} finally {
|
|
356
|
+
request.removeListener("aborted", onAborted);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function createMcpStdioServer(options, input = process.stdin, output = process.stdout) {
|
|
361
|
+
const cancellationRegistry = options.cancellationRegistry ?? createMcpCancellationRegistry();
|
|
362
|
+
const lines = createInterface({
|
|
363
|
+
input,
|
|
364
|
+
crlfDelay: Infinity
|
|
365
|
+
});
|
|
366
|
+
let closed = false;
|
|
367
|
+
const write = (body) => {
|
|
368
|
+
if (!closed) output.write(`${JSON.stringify(body)}
|
|
369
|
+
`);
|
|
370
|
+
};
|
|
371
|
+
const processLine = async (line) => {
|
|
372
|
+
if (!line.trim()) return;
|
|
373
|
+
let body;
|
|
374
|
+
try {
|
|
375
|
+
body = JSON.parse(line);
|
|
376
|
+
} catch {
|
|
377
|
+
write({
|
|
378
|
+
jsonrpc: "2.0",
|
|
379
|
+
id: null,
|
|
380
|
+
error: { code: -32700, message: "PARSE_ERROR" }
|
|
381
|
+
});
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const token = requestToken(body.id);
|
|
385
|
+
const requestInvocation = {
|
|
386
|
+
...options.invocation,
|
|
387
|
+
id: `${options.invocation.id}:${token}`,
|
|
388
|
+
receiptId: `${options.invocation.receiptId}:${token}`,
|
|
389
|
+
idempotencyKey: `${options.invocation.idempotencyKey}:${token}`
|
|
390
|
+
};
|
|
391
|
+
const scope = {
|
|
392
|
+
organizationId: requestInvocation.organizationId,
|
|
393
|
+
projectId: requestInvocation.projectId,
|
|
394
|
+
environmentId: requestInvocation.environmentId
|
|
395
|
+
};
|
|
396
|
+
try {
|
|
397
|
+
await assertConfiguredSession(options, options.sessionId, scope);
|
|
398
|
+
} catch {
|
|
399
|
+
if (body.id !== void 0)
|
|
400
|
+
write({
|
|
401
|
+
jsonrpc: "2.0",
|
|
402
|
+
id: body.id,
|
|
403
|
+
error: { code: -32001, message: "MCP_SESSION_NOT_ACTIVE" }
|
|
404
|
+
});
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (options.invocationExecutor && body.method === "tools/call") {
|
|
408
|
+
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
409
|
+
if (definition) {
|
|
410
|
+
const invocation = { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} };
|
|
411
|
+
const authority2 = definition.effectful ? await options.authorize?.({ invocation, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
412
|
+
if (authority2.allowed) {
|
|
413
|
+
const result = await options.invocationExecutor({ invocation, authority: authority2 });
|
|
414
|
+
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, result: { content: [{ type: "text", text: JSON.stringify(result.result ?? result.error ?? null) }], isError: !result.ok, provenanceDigest: result.provenanceDigest } });
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (options.provenanceRepository && body.method === "tools/call") {
|
|
422
|
+
const replay = await claimInvocation(
|
|
423
|
+
options.provenanceRepository,
|
|
424
|
+
requestInvocation,
|
|
425
|
+
scope
|
|
426
|
+
);
|
|
427
|
+
if (replay) {
|
|
428
|
+
const definition = options.tools.find((item) => item.name === String(body.params?.name ?? ""));
|
|
429
|
+
if (definition?.effectful) {
|
|
430
|
+
const authority2 = await options.authorize?.({ invocation: { ...requestInvocation, server: options.serverName, tool: definition.name, arguments: body.params?.arguments ?? {} }, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" };
|
|
431
|
+
if (!authority2.allowed) {
|
|
432
|
+
write({ jsonrpc: "2.0", id: body.id, error: { code: -32003, message: authority2.reason ?? "MCP_AUTHORITY_DENIED", data: { decisionDigest: authority2.decisionDigest } } });
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
write({
|
|
437
|
+
jsonrpc: "2.0",
|
|
438
|
+
id: body.id,
|
|
439
|
+
result: {
|
|
440
|
+
content: [
|
|
441
|
+
{
|
|
442
|
+
type: "text",
|
|
443
|
+
text: JSON.stringify(replay.result ?? replay.error ?? null)
|
|
444
|
+
}
|
|
445
|
+
],
|
|
446
|
+
isError: !replay.ok,
|
|
447
|
+
provenanceDigest: replay.provenanceDigest
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
let upstreamResponse;
|
|
454
|
+
let authority;
|
|
455
|
+
const transport = {
|
|
456
|
+
call: async (value) => {
|
|
457
|
+
upstreamResponse = await options.transport.call({
|
|
458
|
+
...value,
|
|
459
|
+
signal: value.signal
|
|
460
|
+
});
|
|
461
|
+
return upstreamResponse;
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
try {
|
|
465
|
+
if (body.method === "server/discover") {
|
|
466
|
+
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, result: { supportedProtocolVersions: ["2026-07-28", "2025-11-25", "2025-03-26"], serverInfo: { name: options.serverName, version: "1.0.0" }, capabilities: { tools: {}, resources: options.resources ? {} : void 0, prompts: options.prompts ? {} : void 0 } } });
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (body.method === "initialize") {
|
|
470
|
+
const requested = String(body.params?.protocolVersion ?? "");
|
|
471
|
+
const protocolVersion = ["2026-07-28", "2025-11-25", "2025-03-26"].includes(requested) ? requested : "2025-03-26";
|
|
472
|
+
if (body.id !== void 0) write({ jsonrpc: "2.0", id: body.id, result: { protocolVersion, serverInfo: { name: options.serverName, version: "1.0.0" }, capabilities: { tools: {}, resources: options.resources ? {} : void 0, prompts: options.prompts ? {} : void 0 } } });
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const result = await handleMcpJsonRpc({
|
|
476
|
+
request: body,
|
|
477
|
+
server: options.serverName,
|
|
478
|
+
tools: options.tools,
|
|
479
|
+
resources: options.resources,
|
|
480
|
+
prompts: options.prompts,
|
|
481
|
+
readResource: options.readResource,
|
|
482
|
+
getPrompt: options.getPrompt,
|
|
483
|
+
invocation: requestInvocation,
|
|
484
|
+
transport,
|
|
485
|
+
cancellationRegistry,
|
|
486
|
+
authorize: async (value) => {
|
|
487
|
+
authority = await options.authorize?.(value) ?? (value.definition.effectful ? {
|
|
488
|
+
allowed: false,
|
|
489
|
+
decisionDigest: "",
|
|
490
|
+
reason: "MCP_AUTHORITY_REQUIRED"
|
|
491
|
+
} : { allowed: true, decisionDigest: "read-only" });
|
|
492
|
+
return authority;
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
if (options.provenanceRepository && body.method === "tools/call") {
|
|
496
|
+
const tool = String(body.params?.name ?? "");
|
|
497
|
+
await persistVerifiedMcpResult(
|
|
498
|
+
options.provenanceRepository,
|
|
499
|
+
createMcpResultFromResponse({
|
|
500
|
+
invocation: {
|
|
501
|
+
...requestInvocation,
|
|
502
|
+
server: options.serverName,
|
|
503
|
+
tool,
|
|
504
|
+
arguments: body.params?.arguments ?? {}
|
|
505
|
+
},
|
|
506
|
+
response: upstreamResponse ?? {
|
|
507
|
+
error: authority?.reason ?? "MCP_AUTHORITY_DENIED"
|
|
508
|
+
},
|
|
509
|
+
authority
|
|
510
|
+
})
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (result !== null) write(result);
|
|
514
|
+
} catch {
|
|
515
|
+
if (body.id !== void 0)
|
|
516
|
+
write({
|
|
517
|
+
jsonrpc: "2.0",
|
|
518
|
+
id: body.id,
|
|
519
|
+
error: { code: -32600, message: "INVALID_MCP_REQUEST" }
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
lines.on("line", (line) => {
|
|
524
|
+
void processLine(line);
|
|
525
|
+
});
|
|
526
|
+
return {
|
|
527
|
+
close: () => {
|
|
528
|
+
closed = true;
|
|
529
|
+
lines.close();
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function send(response, status, body) {
|
|
534
|
+
response.writeHead(status, {
|
|
535
|
+
"content-type": "application/json; charset=utf-8",
|
|
536
|
+
"cache-control": "no-store"
|
|
537
|
+
});
|
|
538
|
+
response.end(JSON.stringify(body));
|
|
539
|
+
}
|
|
540
|
+
function readBody(request, limit) {
|
|
541
|
+
return new Promise((resolve3, reject) => {
|
|
542
|
+
let size = 0;
|
|
543
|
+
const chunks = [];
|
|
544
|
+
request.on("data", (chunk) => {
|
|
545
|
+
size += chunk.length;
|
|
546
|
+
if (size > limit) {
|
|
547
|
+
reject(new Error("PAYLOAD_TOO_LARGE"));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
chunks.push(chunk);
|
|
551
|
+
});
|
|
552
|
+
request.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
553
|
+
request.on("error", reject);
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
var requestToken, claimInvocation;
|
|
557
|
+
var init_server = __esm({
|
|
558
|
+
"../../apps/mcp-server/server.ts"() {
|
|
559
|
+
"use strict";
|
|
560
|
+
init_mcp_gateway();
|
|
561
|
+
init_mcp_provenance();
|
|
562
|
+
init_mcp_session();
|
|
563
|
+
requestToken = (value) => String(
|
|
564
|
+
value ?? `notification-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
565
|
+
);
|
|
566
|
+
claimInvocation = async (repository, invocation, scope) => {
|
|
567
|
+
const claim = await repository.claim?.({
|
|
568
|
+
invocationId: invocation.id,
|
|
569
|
+
idempotencyKey: invocation.idempotencyKey,
|
|
570
|
+
...scope
|
|
571
|
+
});
|
|
572
|
+
if (claim?.status === "IN_FLIGHT")
|
|
573
|
+
throw new Error("MCP_INVOCATION_IN_FLIGHT");
|
|
574
|
+
return claim?.result ?? await repository.getByIdempotencyKey?.(invocation.idempotencyKey, scope) ?? null;
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// ../runtime/src/mcp-invocation-service.ts
|
|
580
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
581
|
+
function assertReplayIdentity(invocation, authority, result) {
|
|
582
|
+
const argumentsDigest = createHash5("sha256").update(canonicalMcpJson(invocation.arguments)).digest("hex");
|
|
583
|
+
const expectedAuthorityDecisionDigest = authority?.decisionDigest ?? "not-required";
|
|
584
|
+
if (result.organizationId !== invocation.organizationId || result.projectId !== invocation.projectId || result.environmentId !== invocation.environmentId || result.server !== invocation.server || result.tool !== invocation.tool || result.receiptId !== invocation.receiptId || result.idempotencyKey !== invocation.idempotencyKey || result.argumentsDigest !== argumentsDigest || result.authorityDecisionDigest !== expectedAuthorityDecisionDigest || result.authorityDigest !== (invocation.authorityDigest ?? null)) throw new Error("MCP_INVOCATION_REPLAY_MISMATCH");
|
|
585
|
+
}
|
|
586
|
+
async function executeMcpInvocation(input) {
|
|
587
|
+
const started = Date.now();
|
|
588
|
+
const claim = await input.store.claim({ invocationId: input.invocation.id, idempotencyKey: input.invocation.idempotencyKey, organizationId: input.invocation.organizationId, projectId: input.invocation.projectId, environmentId: input.invocation.environmentId });
|
|
589
|
+
if (claim.status === "REPLAY" && claim.result) {
|
|
590
|
+
verifyMcpResult(claim.result);
|
|
591
|
+
assertReplayIdentity(input.invocation, input.authority, claim.result);
|
|
592
|
+
await input.observe?.({ result: claim.result, replay: true, durationMs: Date.now() - started });
|
|
593
|
+
return claim.result;
|
|
594
|
+
}
|
|
595
|
+
if (claim.status === "IN_FLIGHT") throw new Error("MCP_INVOCATION_IN_FLIGHT");
|
|
596
|
+
try {
|
|
597
|
+
const result = await invokeMcpTool({ invocation: input.invocation, transport: input.transport, authority: input.authority, signal: input.signal });
|
|
598
|
+
const persisted = await input.store.save(result);
|
|
599
|
+
verifyMcpResult(persisted);
|
|
600
|
+
await input.observe?.({ result: persisted, replay: false, durationMs: Date.now() - started });
|
|
601
|
+
return persisted;
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (input.signal?.aborted) await input.store.releaseClaim?.({ idempotencyKey: input.invocation.idempotencyKey, organizationId: input.invocation.organizationId, projectId: input.invocation.projectId, environmentId: input.invocation.environmentId });
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
var init_mcp_invocation_service = __esm({
|
|
608
|
+
"../runtime/src/mcp-invocation-service.ts"() {
|
|
609
|
+
"use strict";
|
|
610
|
+
init_mcp_gateway();
|
|
611
|
+
init_mcp_gateway();
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// ../database/src/mcp-provenance-repository.ts
|
|
616
|
+
var assertClaimInput, McpProvenanceRepository, SqlMcpProvenanceRepository;
|
|
617
|
+
var init_mcp_provenance_repository = __esm({
|
|
618
|
+
"../database/src/mcp-provenance-repository.ts"() {
|
|
619
|
+
"use strict";
|
|
620
|
+
init_mcp_provenance();
|
|
621
|
+
init_scope_composer();
|
|
622
|
+
assertClaimInput = (input) => {
|
|
623
|
+
if (!input.invocationId?.trim() || !input.idempotencyKey?.trim() || !input.organizationId?.trim() || !input.projectId?.trim() || !input.environmentId?.trim()) throw new Error("MCP_CLAIM_INPUT_INVALID");
|
|
624
|
+
};
|
|
625
|
+
McpProvenanceRepository = class {
|
|
626
|
+
#items = /* @__PURE__ */ new Map();
|
|
627
|
+
#claims = /* @__PURE__ */ new Set();
|
|
628
|
+
claim(input) {
|
|
629
|
+
assertClaimInput(input);
|
|
630
|
+
const replay = this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
631
|
+
if (replay) return { status: "REPLAY", result: replay };
|
|
632
|
+
const key = `${input.organizationId}:${input.projectId}:${input.environmentId}:${input.idempotencyKey}`;
|
|
633
|
+
if (this.#claims.has(key)) return { status: "IN_FLIGHT" };
|
|
634
|
+
this.#claims.add(key);
|
|
635
|
+
return { status: "CLAIMED" };
|
|
636
|
+
}
|
|
637
|
+
save(result) {
|
|
638
|
+
verifyMcpResult2(result);
|
|
639
|
+
const prior = this.#items.get(result.invocationId);
|
|
640
|
+
const byKey = this.getByIdempotencyKey(result.idempotencyKey, result);
|
|
641
|
+
if (prior && prior.provenanceDigest !== result.provenanceDigest || byKey && byKey.provenanceDigest !== result.provenanceDigest)
|
|
642
|
+
throw new Error("MCP_PROVENANCE_CONFLICT");
|
|
643
|
+
this.#items.set(result.invocationId, structuredClone(result));
|
|
644
|
+
this.#claims.delete(
|
|
645
|
+
`${result.organizationId}:${result.projectId}:${result.environmentId}:${result.idempotencyKey}`
|
|
646
|
+
);
|
|
647
|
+
return structuredClone(prior ?? byKey ?? result);
|
|
648
|
+
}
|
|
649
|
+
releaseClaim(input) {
|
|
650
|
+
assertClaimInput({ ...input, invocationId: "release-claim" });
|
|
651
|
+
this.#claims.delete(`${input.organizationId}:${input.projectId}:${input.environmentId}:${input.idempotencyKey}`);
|
|
652
|
+
}
|
|
653
|
+
saveComposed(scope, result) {
|
|
654
|
+
assertComposedScopeIntegrity(scope);
|
|
655
|
+
const projected = { organizationId: scope.chain.find((item) => item.level === "organization")?.id ?? "", projectId: scope.chain.find((item) => item.level === "project")?.id ?? "", environmentId: scope.chain.find((item) => item.level === "application")?.id ?? "" };
|
|
656
|
+
if (result.organizationId !== projected.organizationId || result.projectId !== projected.projectId || result.environmentId !== projected.environmentId) throw new Error("MCP_SCOPE_MISMATCH");
|
|
657
|
+
if (result.authorityDigest !== scope.compositionDigest) throw new Error("MCP_AUTHORITY_MISMATCH");
|
|
658
|
+
return this.save(result);
|
|
659
|
+
}
|
|
660
|
+
get(invocationId) {
|
|
661
|
+
const result = this.#items.get(invocationId);
|
|
662
|
+
if (!result) return null;
|
|
663
|
+
verifyMcpResult2(result);
|
|
664
|
+
return structuredClone(result);
|
|
665
|
+
}
|
|
666
|
+
getByIdempotencyKey(key, scope) {
|
|
667
|
+
const result = [...this.#items.values()].find(
|
|
668
|
+
(item) => item.idempotencyKey === key && item.organizationId === scope.organizationId && item.projectId === scope.projectId && item.environmentId === scope.environmentId
|
|
669
|
+
);
|
|
670
|
+
if (!result) return null;
|
|
671
|
+
verifyMcpResult2(result);
|
|
672
|
+
return structuredClone(result);
|
|
673
|
+
}
|
|
674
|
+
list(scope, limit = 100) {
|
|
675
|
+
return [...this.#items.values()].filter((item) => item.organizationId === scope.organizationId && item.projectId === scope.projectId && item.environmentId === scope.environmentId).slice(0, Math.max(0, Math.min(500, limit))).map((item) => {
|
|
676
|
+
verifyMcpResult2(item);
|
|
677
|
+
return structuredClone(item);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
SqlMcpProvenanceRepository = class {
|
|
682
|
+
db;
|
|
683
|
+
constructor(db) {
|
|
684
|
+
this.db = db;
|
|
685
|
+
this.db.run(
|
|
686
|
+
"CREATE TABLE IF NOT EXISTS mcp_invocation_claims (organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, idempotency_key TEXT NOT NULL, invocation_id TEXT NOT NULL, claimed_at TEXT NOT NULL, PRIMARY KEY (organization_id, project_id, environment_id, idempotency_key))",
|
|
687
|
+
[]
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
releaseClaim(input) {
|
|
691
|
+
assertClaimInput({ ...input, invocationId: "release-claim" });
|
|
692
|
+
this.db.run("DELETE FROM mcp_invocation_claims WHERE organization_id=? AND project_id=? AND environment_id=? AND idempotency_key=?", [input.organizationId, input.projectId, input.environmentId, input.idempotencyKey]);
|
|
693
|
+
}
|
|
694
|
+
claim(input) {
|
|
695
|
+
assertClaimInput(input);
|
|
696
|
+
const replay = this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
697
|
+
if (replay) return { status: "REPLAY", result: replay };
|
|
698
|
+
try {
|
|
699
|
+
this.db.run(
|
|
700
|
+
"INSERT INTO mcp_invocation_claims (organization_id, project_id, environment_id, idempotency_key, invocation_id, claimed_at) VALUES (?, ?, ?, ?, ?, ?)",
|
|
701
|
+
[
|
|
702
|
+
input.organizationId,
|
|
703
|
+
input.projectId,
|
|
704
|
+
input.environmentId,
|
|
705
|
+
input.idempotencyKey,
|
|
706
|
+
input.invocationId,
|
|
707
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
708
|
+
]
|
|
709
|
+
);
|
|
710
|
+
return { status: "CLAIMED" };
|
|
711
|
+
} catch {
|
|
712
|
+
const after = this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
713
|
+
return after ? { status: "REPLAY", result: after } : { status: "IN_FLIGHT" };
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
save(result) {
|
|
717
|
+
verifyMcpResult2(result);
|
|
718
|
+
const prior = this.getForScope(result, result.invocationId);
|
|
719
|
+
const byKey = this.getByIdempotencyKey(result.idempotencyKey, result);
|
|
720
|
+
const existing = prior ?? byKey;
|
|
721
|
+
if (existing) {
|
|
722
|
+
if (existing.provenanceDigest !== result.provenanceDigest)
|
|
723
|
+
throw new Error("MCP_PROVENANCE_CONFLICT");
|
|
724
|
+
return existing;
|
|
725
|
+
}
|
|
726
|
+
this.db.run(
|
|
727
|
+
"DELETE FROM mcp_invocation_claims WHERE organization_id=? AND project_id=? AND environment_id=? AND idempotency_key=?",
|
|
728
|
+
[
|
|
729
|
+
result.organizationId,
|
|
730
|
+
result.projectId,
|
|
731
|
+
result.environmentId,
|
|
732
|
+
result.idempotencyKey
|
|
733
|
+
]
|
|
734
|
+
);
|
|
735
|
+
this.db.run(
|
|
736
|
+
"INSERT INTO mcp_invocation_provenance (invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
737
|
+
[
|
|
738
|
+
result.invocationId,
|
|
739
|
+
result.organizationId,
|
|
740
|
+
result.projectId,
|
|
741
|
+
result.environmentId,
|
|
742
|
+
result.server,
|
|
743
|
+
result.tool,
|
|
744
|
+
result.receiptId,
|
|
745
|
+
result.idempotencyKey,
|
|
746
|
+
result.argumentsDigest,
|
|
747
|
+
result.ok ? 1 : 0,
|
|
748
|
+
result.result === void 0 ? null : JSON.stringify(result.result),
|
|
749
|
+
result.error ?? null,
|
|
750
|
+
result.authorityDecisionDigest,
|
|
751
|
+
result.authorityDigest ?? null,
|
|
752
|
+
result.provenanceDigest
|
|
753
|
+
]
|
|
754
|
+
);
|
|
755
|
+
const persisted = this.getForScope(result, result.invocationId);
|
|
756
|
+
if (!persisted || persisted.provenanceDigest !== result.provenanceDigest || persisted.receiptId !== result.receiptId || persisted.argumentsDigest !== result.argumentsDigest || persisted.authorityDecisionDigest !== result.authorityDecisionDigest || persisted.authorityDigest !== result.authorityDigest) throw new Error("MCP_PROVENANCE_READBACK_MISMATCH");
|
|
757
|
+
return persisted;
|
|
758
|
+
}
|
|
759
|
+
saveComposed(scope, result) {
|
|
760
|
+
assertComposedScopeIntegrity(scope);
|
|
761
|
+
const projected = { organizationId: scope.chain.find((item) => item.level === "organization")?.id ?? "", projectId: scope.chain.find((item) => item.level === "project")?.id ?? "", environmentId: scope.chain.find((item) => item.level === "application")?.id ?? "" };
|
|
762
|
+
if (result.organizationId !== projected.organizationId || result.projectId !== projected.projectId || result.environmentId !== projected.environmentId) throw new Error("MCP_SCOPE_MISMATCH");
|
|
763
|
+
if (result.authorityDigest !== scope.compositionDigest) throw new Error("MCP_AUTHORITY_MISMATCH");
|
|
764
|
+
return this.save(result);
|
|
765
|
+
}
|
|
766
|
+
get(invocationId) {
|
|
767
|
+
const row = this.db.exec(
|
|
768
|
+
"SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE invocation_id = ?",
|
|
769
|
+
[invocationId]
|
|
770
|
+
)[0]?.values[0];
|
|
771
|
+
return row ? this.fromRow(row) : null;
|
|
772
|
+
}
|
|
773
|
+
getByIdempotencyKey(key, scope) {
|
|
774
|
+
const row = this.db.exec(
|
|
775
|
+
"SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE idempotency_key = ? AND organization_id = ? AND project_id = ? AND environment_id = ?",
|
|
776
|
+
[key, scope.organizationId, scope.projectId, scope.environmentId]
|
|
777
|
+
)[0]?.values[0];
|
|
778
|
+
return row ? this.fromRow(row) : null;
|
|
779
|
+
}
|
|
780
|
+
getForScope(scope, invocationId) {
|
|
781
|
+
const row = this.db.exec(
|
|
782
|
+
"SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE invocation_id = ? AND organization_id = ? AND project_id = ? AND environment_id = ?",
|
|
783
|
+
[
|
|
784
|
+
invocationId,
|
|
785
|
+
scope.organizationId,
|
|
786
|
+
scope.projectId,
|
|
787
|
+
scope.environmentId
|
|
788
|
+
]
|
|
789
|
+
)[0]?.values[0];
|
|
790
|
+
return row ? this.fromRow(row) : null;
|
|
791
|
+
}
|
|
792
|
+
list(scope, limit = 100) {
|
|
793
|
+
const bounded = Math.max(0, Math.min(500, limit));
|
|
794
|
+
const rows = this.db.exec("SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance WHERE organization_id=? AND project_id=? AND environment_id=? ORDER BY invocation_id DESC LIMIT ?", [scope.organizationId, scope.projectId, scope.environmentId, bounded])[0]?.values ?? [];
|
|
795
|
+
return rows.map((row) => this.fromRow(row));
|
|
796
|
+
}
|
|
797
|
+
fromRow(row) {
|
|
798
|
+
const result = {
|
|
799
|
+
invocationId: String(row[0]),
|
|
800
|
+
organizationId: String(row[1]),
|
|
801
|
+
projectId: String(row[2]),
|
|
802
|
+
environmentId: String(row[3]),
|
|
803
|
+
server: String(row[4]),
|
|
804
|
+
tool: String(row[5]),
|
|
805
|
+
receiptId: String(row[6]),
|
|
806
|
+
idempotencyKey: String(row[7]),
|
|
807
|
+
argumentsDigest: String(row[8]),
|
|
808
|
+
ok: Number(row[9]) === 1,
|
|
809
|
+
result: row[10] === null || row[10] === void 0 ? void 0 : JSON.parse(String(row[10])),
|
|
810
|
+
error: row[11] === null || row[11] === void 0 ? void 0 : String(row[11]),
|
|
811
|
+
authorityDecisionDigest: String(row[12]),
|
|
812
|
+
authorityDigest: row[13] === null || row[13] === void 0 ? null : String(row[13]),
|
|
813
|
+
provenanceDigest: String(row[14])
|
|
814
|
+
};
|
|
815
|
+
verifyMcpResult2(result);
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// ../../apps/mcp-server/vira-server.ts
|
|
823
|
+
function resolveViraMcpConfig(environment = process.env) {
|
|
824
|
+
const baseUrl = (environment.VIRA_API_URL ?? "http://127.0.0.1:4185").replace(/\/$/, "");
|
|
825
|
+
const apiKey = environment.VIRA_API_KEY ?? (environment.NODE_ENV === "production" ? "" : "vira_dev_local.local-development-only-32chars");
|
|
826
|
+
if (!apiKey) throw new Error("VIRA_API_KEY_REQUIRED_IN_PRODUCTION");
|
|
827
|
+
const authorityUrl = environment.VIRA_MCP_AUTHORITY_URL;
|
|
828
|
+
return { baseUrl, apiKey, authorityUrl, serverName: environment.VIRA_MCP_SERVER_NAME ?? "vira-behavior-control", requestAuthToken: environment.VIRA_MCP_AUTH_TOKEN, ...authorityUrl ? { authorizeEffectful: async (input) => {
|
|
829
|
+
const response = await fetch(`${authorityUrl}`, { method: "POST", headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" }, body: JSON.stringify(input) });
|
|
830
|
+
const payload = await response.json().catch(() => null);
|
|
831
|
+
return { allowed: response.ok && payload?.allowed === true, decisionDigest: payload?.decisionDigest ?? "", ...payload?.reason ? { reason: payload.reason } : {} };
|
|
832
|
+
} } : {} };
|
|
833
|
+
}
|
|
834
|
+
function assertScope(input, scope) {
|
|
835
|
+
for (const key of ["organizationId", "projectId", "environmentId"]) if (input[key] !== scope[key]) throw new Error("MCP_VIRA_SCOPE_MISMATCH");
|
|
836
|
+
}
|
|
837
|
+
function createViraMcpServer(config) {
|
|
838
|
+
return createMcpServer(createViraMcpOptions(config));
|
|
839
|
+
}
|
|
840
|
+
function createViraMcpOptions(config) {
|
|
841
|
+
if (!config.baseUrl?.trim() || !config.apiKey?.trim()) throw new Error("VIRA_MCP_CONFIG_REQUIRED");
|
|
842
|
+
const fetcher = config.fetcher ?? fetch;
|
|
843
|
+
const api = async (path, body, requestId, method) => {
|
|
844
|
+
const response = await fetcher(`${config.baseUrl.replace(/\/$/, "")}${path}`, { method: method ?? (body ? "POST" : "GET"), headers: { authorization: `Bearer ${config.apiKey}`, "content-type": "application/json", "idempotency-key": requestId }, ...body ? { body: JSON.stringify(body) } : {} });
|
|
845
|
+
const payload = await response.json().catch(() => null);
|
|
846
|
+
if (!response.ok) return { error: `VIRA_API_${response.status}`, result: payload };
|
|
847
|
+
return { result: payload };
|
|
848
|
+
};
|
|
849
|
+
const options = { serverName: config.serverName ?? "vira-behavior-control", requestAuthToken: config.requestAuthToken, tools, invocation: { ...config.scope, id: "mcp-vira", receiptId: "mcp-vira", idempotencyKey: "mcp-vira" }, transport: { call: async ({ tool, arguments: args }) => {
|
|
850
|
+
assertScope(args, config.scope);
|
|
851
|
+
const scopeQuery = `organizationId=${encodeURIComponent(config.scope.organizationId)}&projectId=${encodeURIComponent(config.scope.projectId)}&environmentId=${encodeURIComponent(config.scope.environmentId)}`;
|
|
852
|
+
if (tool === "list_behavior_programs") return api(`/v1/behavior-programs?projectId=${encodeURIComponent(config.scope.projectId)}&environmentId=${encodeURIComponent(config.scope.environmentId)}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
853
|
+
if (tool === "list_behavior_strategies") return api(`/v1/behavior-strategies?${scopeQuery}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
854
|
+
if (tool === "get_behavior_strategy") return api(`/v1/behavior-strategies/${encodeURIComponent(String(args.strategyId))}?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.strategyId)}`);
|
|
855
|
+
if (tool === "simulate_behavior_strategy") return api(`/v1/behavior-strategies/${encodeURIComponent(String(args.strategyId))}/simulate?${scopeQuery}`, { initialState: args.initialState ?? {} }, `mcp:${tool}:${String(args.strategyId)}`);
|
|
856
|
+
if (tool === "start_strategy_execution") return api("/v1/runtime/strategy-executions", args, `mcp:${tool}:${String(args.executionId ?? args.artifactDigest)}`);
|
|
857
|
+
if (tool === "get_strategy_execution") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.executionId)}`);
|
|
858
|
+
if (tool === "get_strategy_execution_trace") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/trace?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.executionId)}`);
|
|
859
|
+
if (tool === "continue_strategy_execution") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/continue`, args.authorityDigest ? { authorityDigest: args.authorityDigest } : {}, `mcp:${tool}:${String(args.executionId)}`);
|
|
860
|
+
if (tool === "cancel_strategy_execution") return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/cancel`, {}, `mcp:${tool}:${String(args.executionId)}`);
|
|
861
|
+
if (tool === "list_ai_systems") return api(`/v1/ai-systems?${scopeQuery}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
862
|
+
if (tool === "list_discovery_candidates") return api(`/v1/discovery/candidates?${scopeQuery}${args.reviewState ? `&reviewState=${encodeURIComponent(String(args.reviewState))}` : ""}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
863
|
+
if (tool === "review_discovery_candidate") return api(`/v1/discovery/candidates/${encodeURIComponent(String(args.kind))}/${encodeURIComponent(String(args.key))}/review`, { ...args }, `mcp:${tool}:${String(args.kind)}:${String(args.key)}`);
|
|
864
|
+
if (tool === "create_observation") return api("/v1/observations", args, `mcp:${tool}:${String(args.id)}`);
|
|
865
|
+
if (tool === "start_evaluation") return api("/v1/evaluations", args, `mcp:${tool}:${String(args.id)}`);
|
|
866
|
+
if (tool === "compile_behavior_capsule") return api("/v1/runtime/behavior-capsule", args, `mcp:${tool}:${String(args.programId)}`);
|
|
867
|
+
if (tool === "serve_behavior_projection") return api("/v1/runtime/behavior-projection", args, `mcp:${tool}:${String(args.programId ?? "active")}`);
|
|
868
|
+
throw new Error("MCP_TOOL_NOT_IMPLEMENTED");
|
|
869
|
+
} }, authorize: ({ invocation, definition }) => definition.effectful ? config.authorizeEffectful ? config.authorizeEffectful({ tool: definition.name, arguments: invocation.arguments, scope: { organizationId: invocation.organizationId, projectId: invocation.projectId, environmentId: invocation.environmentId } }) : { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" }, ...config.provenanceRepository ? { provenanceRepository: config.provenanceRepository } : {}, ...config.sessionRepository ? { sessionRepository: config.sessionRepository } : {}, ...config.sessionId ? { sessionId: config.sessionId } : {}, ...config.invocationExecutor ? { invocationExecutor: config.invocationExecutor } : {} };
|
|
870
|
+
const originalTransport = options.transport;
|
|
871
|
+
const originalCall = originalTransport.call;
|
|
872
|
+
options.transport = { ...originalTransport, call: async (input) => {
|
|
873
|
+
const { tool, arguments: args } = input;
|
|
874
|
+
if (tool === "list_packs") return api("/v1/packs", void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
875
|
+
if (tool === "import_pack") return api("/v1/packs/import", args, `mcp:${tool}:${config.scope.projectId}`);
|
|
876
|
+
if (["install_pack", "evaluate_pack", "approve_pack", "release_pack", "preview_pack_upgrade"].includes(tool)) {
|
|
877
|
+
const action = { install_pack: "install", evaluate_pack: "evaluate", approve_pack: "approve", release_pack: "release", preview_pack_upgrade: "upgrade-preview" }[tool];
|
|
878
|
+
const { packId, ...body } = args;
|
|
879
|
+
return api(`/v1/packs/${encodeURIComponent(String(packId))}/${action}`, body, `mcp:${tool}:${String(packId)}`);
|
|
880
|
+
}
|
|
881
|
+
return originalCall(input);
|
|
882
|
+
} };
|
|
883
|
+
originalTransport.call = options.transport.call;
|
|
884
|
+
options.transport = { ...originalTransport, call: async (input) => {
|
|
885
|
+
if (input.tool === "resolve_semantic" || input.tool === "resolve_runtime") assertScope(input.arguments, config.scope);
|
|
886
|
+
return input.tool === "resolve_semantic" ? api("/v1/semantic/resolve", input.arguments, `mcp:resolve_semantic:${String(input.arguments.input)}`) : input.tool === "resolve_runtime" ? api("/v1/runtime/resolve", input.arguments, `mcp:resolve_runtime:${String(input.arguments.behaviorPoint)}`) : originalTransport.call(input);
|
|
887
|
+
} };
|
|
888
|
+
const signalTransport = options.transport;
|
|
889
|
+
options.transport = { ...signalTransport, call: async (input) => {
|
|
890
|
+
const { tool, arguments: args } = input;
|
|
891
|
+
if (tool.startsWith("signal_") || tool === "create_signal_discovery_proposal" || tool === "review_signal_discovery_proposal" || tool === "materialize_signal_discovery" || tool === "create_signal_mapping" || tool === "get_signal_mapping" || tool === "review_signal_mapping") assertScope(args, config.scope);
|
|
892
|
+
const scopeQuery = `organizationId=${encodeURIComponent(config.scope.organizationId)}&projectId=${encodeURIComponent(config.scope.projectId)}&environmentId=${encodeURIComponent(config.scope.environmentId)}`;
|
|
893
|
+
if (tool === "create_signal_discovery_proposal") return api("/v1/signals/discovery-proposals", args, `mcp:${tool}:${String(args.signalStableKey)}`);
|
|
894
|
+
if (tool === "review_signal_discovery_proposal") return api(`/v1/signals/discovery-proposals/${encodeURIComponent(String(args.proposalId))}/review`, { decision: args.decision }, `mcp:${tool}:${String(args.proposalId)}`);
|
|
895
|
+
if (tool === "materialize_signal_discovery") {
|
|
896
|
+
const { proposalId, ...body } = args;
|
|
897
|
+
return api(`/v1/signals/discovery-proposals/${encodeURIComponent(String(proposalId))}/materialize`, body, `mcp:${tool}:${String(proposalId)}`);
|
|
898
|
+
}
|
|
899
|
+
if (tool === "create_signal_mapping") return api("/v1/signals/mappings", args, `mcp:${tool}:${String(args.signalStableKey)}`);
|
|
900
|
+
if (tool === "get_signal_mapping") return api(`/v1/signals/mappings/${encodeURIComponent(String(args.mappingId))}?${scopeQuery}`, void 0, `mcp:${tool}:${String(args.mappingId)}`);
|
|
901
|
+
if (tool === "review_signal_mapping") {
|
|
902
|
+
const { mappingId, ...body } = args;
|
|
903
|
+
return api(`/v1/signals/mappings/${encodeURIComponent(String(mappingId))}/review`, body, `mcp:${tool}:${String(mappingId)}`);
|
|
904
|
+
}
|
|
905
|
+
return signalTransport.call(input);
|
|
906
|
+
} };
|
|
907
|
+
const authoringTransport = options.transport;
|
|
908
|
+
options.transport = { ...authoringTransport, call: async (input) => {
|
|
909
|
+
const { tool, arguments: args } = input;
|
|
910
|
+
if (tool === "list_capabilities") return api(`/v1/capabilities${args.surface ? `?surface=${encodeURIComponent(String(args.surface))}` : ""}`, void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
911
|
+
if (tool === "list_semantic_tasks") return api("/v1/semantic-tasks", void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
912
|
+
if (tool === "create_semantic_task") return api("/v1/semantic-tasks", args, `mcp:${tool}:${String(args.taskKey)}`);
|
|
913
|
+
if (tool === "list_behavior_points") return api("/v1/behavior-points", void 0, `mcp:${tool}:${config.scope.projectId}`);
|
|
914
|
+
if (tool === "create_behavior_point") return api("/v1/behavior-points", args, `mcp:${tool}:${String(args.key)}`);
|
|
915
|
+
if (tool === "delete_ai_system") return api(`/v1/ai-systems/${encodeURIComponent(String(args.systemId))}`, { confirm: args.confirm === true }, `mcp:${tool}:${String(args.systemId)}`, "DELETE");
|
|
916
|
+
if (tool === "delete_semantic_task") return api(`/v1/semantic-tasks/${encodeURIComponent(String(args.taskId))}`, { confirm: args.confirm === true }, `mcp:${tool}:${String(args.taskId)}`, "DELETE");
|
|
917
|
+
if (tool === "delete_behavior_point") return api(`/v1/behavior-points/${encodeURIComponent(String(args.pointId))}`, { confirm: args.confirm === true }, `mcp:${tool}:${String(args.pointId)}`, "DELETE");
|
|
918
|
+
if (tool === "delete_behavior_strategy") return api(`/v1/behavior-strategies/${encodeURIComponent(String(args.strategyId))}`, { confirm: args.confirm === true }, `mcp:${tool}:${String(args.strategyId)}`, "DELETE");
|
|
919
|
+
if (tool === "activate_behavior_strategy") return api(`/v1/behavior-strategies/${encodeURIComponent(String(args.strategyId))}/activate`, {}, `mcp:${tool}:${String(args.strategyId)}`);
|
|
920
|
+
return authoringTransport.call(input);
|
|
921
|
+
} };
|
|
922
|
+
const lifecycleTransport = options.transport;
|
|
923
|
+
options.transport = { ...lifecycleTransport, call: async (input) => {
|
|
924
|
+
const { tool, arguments: args } = input;
|
|
925
|
+
if (tool === "submit_teach_evidence") return api("/v1/teach/evidence", args, `mcp:${tool}:${String(args.id)}`);
|
|
926
|
+
if (tool === "create_teach_proposal") return api("/v1/teach/proposals", args, `mcp:${tool}:${String(args.evidenceId)}`);
|
|
927
|
+
if (tool === "transition_teach_proposal") {
|
|
928
|
+
const { proposalId, transition, ...body } = args;
|
|
929
|
+
return api(`/v1/teach/proposals/${encodeURIComponent(String(proposalId))}/${encodeURIComponent(String(transition))}`, body, `mcp:${tool}:${String(proposalId)}`);
|
|
930
|
+
}
|
|
931
|
+
if (tool === "create_release") return api("/v1/releases", args, `mcp:${tool}:${String(args.releaseId ?? args.artifactId)}`);
|
|
932
|
+
return lifecycleTransport.call(input);
|
|
933
|
+
} };
|
|
934
|
+
const continuationTransport = options.transport;
|
|
935
|
+
options.transport = { ...continuationTransport, call: async (input) => {
|
|
936
|
+
if (input.tool === "continue_strategy_execution") {
|
|
937
|
+
const args = input.arguments;
|
|
938
|
+
const body = {
|
|
939
|
+
...typeof args.authorityDigest === "string" ? { authorityDigest: args.authorityDigest } : {},
|
|
940
|
+
...args.state && typeof args.state === "object" && !Array.isArray(args.state) ? { state: args.state } : {},
|
|
941
|
+
...args.references && typeof args.references === "object" && !Array.isArray(args.references) ? { references: args.references } : {}
|
|
942
|
+
};
|
|
943
|
+
return api(`/v1/runtime/strategy-executions/${encodeURIComponent(String(args.executionId))}/continue`, body, `mcp:${input.tool}:${String(args.executionId)}`);
|
|
944
|
+
}
|
|
945
|
+
return continuationTransport.call(input);
|
|
946
|
+
} };
|
|
947
|
+
const configuredRepository = config.provenanceRepository;
|
|
948
|
+
if (configuredRepository && typeof configuredRepository.claim !== "function") throw new Error("MCP_PROVENANCE_CLAIM_REQUIRED");
|
|
949
|
+
const provenanceRepository = configuredRepository ?? new McpProvenanceRepository();
|
|
950
|
+
options.provenanceRepository = provenanceRepository;
|
|
951
|
+
options.invocationExecutor ??= ({ invocation, authority, signal }) => executeMcpInvocation({ invocation, authority, signal, transport: options.transport, store: provenanceRepository });
|
|
952
|
+
options.requireInvocationExecutor = true;
|
|
953
|
+
return options;
|
|
954
|
+
}
|
|
955
|
+
var tools;
|
|
956
|
+
var init_vira_server = __esm({
|
|
957
|
+
"../../apps/mcp-server/vira-server.ts"() {
|
|
958
|
+
"use strict";
|
|
959
|
+
init_server();
|
|
960
|
+
init_mcp_invocation_service();
|
|
961
|
+
init_mcp_provenance_repository();
|
|
962
|
+
tools = Object.freeze([
|
|
963
|
+
{ name: "list_capabilities", description: "Show which Vira capabilities this scoped credential can use and on which public surfaces.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, surface: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
964
|
+
{ name: "list_semantic_tasks", description: "Read scoped Semantic Tasks.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
965
|
+
{ name: "create_semantic_task", description: "Create a scoped Semantic Task for later review/activation.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, id: { type: "string" }, taskKey: { type: "string" }, description: { type: "string" }, version: { type: "number" }, aliases: { type: "array" }, examples: { type: "array" }, negativeExamples: { type: "array" } }, required: ["organizationId", "projectId", "environmentId", "taskKey"] } },
|
|
966
|
+
{ name: "list_behavior_points", description: "Read scoped canonical BehaviorPoints.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
967
|
+
{ name: "create_behavior_point", description: "Create a scoped BehaviorPoint bound to one or more Semantic Tasks.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, id: { type: "string" }, key: { type: "string" }, taskId: { type: "string" }, taskIds: { type: "array" }, constraints: { type: "array" } }, required: ["organizationId", "projectId", "environmentId", "key"] } },
|
|
968
|
+
{ name: "activate_behavior_strategy", description: "Activate a validated scoped Behavior Strategy revision.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, strategyId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "strategyId"] } },
|
|
969
|
+
{ name: "submit_teach_evidence", description: "Submit scoped evidence to the Teach lifecycle.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, id: { type: "string" }, source: { type: "string" }, content: { type: "string" }, consent: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "id", "content", "consent"] } },
|
|
970
|
+
{ name: "create_teach_proposal", description: "Create a reviewed Teach proposal from admitted evidence.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, evidenceId: { type: "string" }, controlPointId: { type: "string" }, summary: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "evidenceId", "controlPointId", "summary"] } },
|
|
971
|
+
{ name: "transition_teach_proposal", description: "Review, evaluate, or promote a Teach proposal.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, proposalId: { type: "string" }, transition: { type: "string" }, decision: { type: "string" }, score: { type: "number" }, evaluationId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "proposalId", "transition"] } },
|
|
972
|
+
{ name: "create_release", description: "Create a scoped release after evaluation and approval.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, releaseId: { type: "string" }, artifactId: { type: "string" }, evaluationId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "artifactId"] } },
|
|
973
|
+
{ name: "resolve_semantic", description: "Resolve input through the governed semantic runtime.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, input: { type: "string" }, sessionId: { type: "string" }, profile: { type: "object" }, selection: { type: "object" }, candidates: { type: "array" }, timeoutMs: { type: "number" } }, required: ["organizationId", "projectId", "environmentId", "input"] } },
|
|
974
|
+
{ name: "resolve_runtime", description: "Resolve a persisted BehaviorPoint through the governed runtime boundary. behaviorPoint may be omitted when semanticContext supplies the selected canonical route.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, behaviorPoint: { type: "string" }, executionBoundary: { type: "object" }, semanticContext: { type: "object" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
975
|
+
{ name: "create_signal_discovery_proposal", description: "Propose a discovered signal for scoped human review.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, signalStableKey: { type: "string" }, evidenceEventIds: { type: "array", items: { type: "string" } }, suggestedProducerType: { type: "string" }, suggestedProducerKey: { type: "string" }, confidence: { type: "number" } }, required: ["organizationId", "projectId", "environmentId", "signalStableKey", "evidenceEventIds"] } },
|
|
976
|
+
{ name: "review_signal_discovery_proposal", description: "Approve or reject a discovered signal proposal.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, proposalId: { type: "string" }, decision: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "proposalId", "decision"] } },
|
|
977
|
+
{ name: "materialize_signal_discovery", description: "Materialize an approved signal into a definition and producer mapping.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, proposalId: { type: "string" }, valueType: { type: "string" }, description: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "proposalId"] } },
|
|
978
|
+
{ name: "create_signal_mapping", description: "Create a scoped signal producer mapping proposal.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, signalStableKey: { type: "string" }, signalVersion: { type: "number" }, producerType: { type: "string" }, producerKey: { type: "string" }, sourceEventType: { type: "string" }, evidenceEventIds: { type: "array", items: { type: "string" } } }, required: ["organizationId", "projectId", "environmentId", "signalStableKey", "signalVersion", "producerType", "producerKey"] } },
|
|
979
|
+
{ name: "get_signal_mapping", description: "Read one scoped signal producer mapping.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, mappingId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "mappingId"] } },
|
|
980
|
+
{ name: "review_signal_mapping", description: "Approve or reject a scoped signal producer mapping.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, mappingId: { type: "string" }, decision: { type: "string" }, eventId: { type: "string" }, definition: { type: "object" }, teachConsentRecord: { type: "object" } }, required: ["organizationId", "projectId", "environmentId", "mappingId", "decision"] } },
|
|
981
|
+
{ name: "list_behavior_programs", description: "Read scoped canonical behavior programs.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
982
|
+
{ name: "list_behavior_strategies", description: "Read persisted immutable Behavior Strategy revisions in the configured scope.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
983
|
+
{ name: "get_behavior_strategy", description: "Read one persisted Behavior Strategy definition and artifact binding.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, strategyId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "strategyId"] } },
|
|
984
|
+
{ name: "simulate_behavior_strategy", description: "Run the explicit structural dry-run for a persisted Behavior Strategy.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, strategyId: { type: "string" }, initialState: { type: "object" } }, required: ["organizationId", "projectId", "environmentId", "strategyId"] } },
|
|
985
|
+
{ name: "start_strategy_execution", description: "Start a persisted Strategy execution through the governed runtime boundary.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, behaviorPoint: { type: "string" }, executionBoundary: { type: "object" }, revisionId: { type: "string" }, idempotencyKey: { type: "string" }, nonce: { type: "string" }, expiresAt: { type: "number" }, decisionPlanId: { type: "string" }, candidates: { type: "array", items: { type: "object" } }, policy: { type: "string" }, allowedIds: { type: "array", items: { type: "string" } }, calibrationId: { type: "string" }, postModelJudge: { type: "object" }, shadow: { type: "object" }, artifactDigest: { type: "string" }, traceId: { type: "string" }, executionId: { type: "string" }, state: { type: "object" } }, required: ["organizationId", "projectId", "environmentId", "revisionId", "idempotencyKey", "nonce", "expiresAt", "decisionPlanId", "candidates", "policy", "artifactDigest"] } },
|
|
986
|
+
{ name: "get_strategy_execution", description: "Read one scoped Strategy execution.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, executionId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "executionId"] } },
|
|
987
|
+
{ name: "get_strategy_execution_trace", description: "Read persisted Strategy step receipts and trace evidence.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, executionId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "executionId"] } },
|
|
988
|
+
{ name: "continue_strategy_execution", description: "Continue a waiting Strategy execution with the same pinned artifact, state, and external references.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, executionId: { type: "string" }, authorityDigest: { type: "string" }, state: { type: "object", additionalProperties: true }, references: { type: "object", additionalProperties: true } }, required: ["organizationId", "projectId", "environmentId", "executionId"] } },
|
|
989
|
+
{ name: "cancel_strategy_execution", description: "Cancel a non-terminal scoped Strategy execution.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, executionId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "executionId"] } },
|
|
990
|
+
{ name: "list_ai_systems", description: "Read connected AI systems in the configured scope.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
991
|
+
{ name: "delete_ai_system", description: "Delete an unused scoped AI system after explicit confirmation.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, systemId: { type: "string" }, confirm: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "systemId", "confirm"] } },
|
|
992
|
+
{ name: "delete_semantic_task", description: "Delete an unused scoped Semantic Task after explicit confirmation.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, taskId: { type: "string" }, confirm: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "taskId", "confirm"] } },
|
|
993
|
+
{ name: "delete_behavior_point", description: "Delete an unused scoped BehaviorPoint after explicit confirmation.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, pointId: { type: "string" }, confirm: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "pointId", "confirm"] } },
|
|
994
|
+
{ name: "delete_behavior_strategy", description: "Delete a non-active unused scoped Behavior Strategy after explicit confirmation.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, strategyId: { type: "string" }, confirm: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "strategyId", "confirm"] } },
|
|
995
|
+
{ name: "list_discovery_candidates", description: "Read evidence-backed discovery candidates awaiting human review.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, reviewState: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
996
|
+
{ name: "review_discovery_candidate", description: "Record a human review decision for a scoped discovery candidate.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, kind: { type: "string" }, key: { type: "string" }, decision: { type: "string" }, payload: { type: "object" } }, required: ["organizationId", "projectId", "environmentId", "kind", "key", "decision"] } },
|
|
997
|
+
{ name: "create_observation", description: "Submit one scoped observation for discovery; it does not publish behavior.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, id: { type: "string" }, content: { type: "object" }, consent: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "id", "content", "consent"] } },
|
|
998
|
+
{ name: "start_evaluation", description: "Start a local evaluation for an existing artifact.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, id: { type: "string" }, targetArtifact: { type: "string" }, dataset: { type: "array" }, assertions: { type: "array" } }, required: ["organizationId", "projectId", "environmentId", "id", "targetArtifact", "dataset", "assertions"] } },
|
|
999
|
+
{ name: "compile_behavior_capsule", description: "Compile a persisted scoped Behavior VM capsule and return its receipt.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, programId: { type: "string" }, input: { type: "string" }, selection: { type: "object" }, candidates: { type: "array" } }, required: ["organizationId", "projectId", "environmentId", "programId", "input"] } },
|
|
1000
|
+
{ name: "serve_behavior_projection", description: "Serve the active scoped behavior projection through the governed runtime boundary.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, programId: { type: "string" }, input: { type: "string" }, provider: { type: "string" }, protocol: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "input", "provider"] } },
|
|
1001
|
+
{ name: "list_packs", description: "Read scoped behavior packs.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId"] } },
|
|
1002
|
+
{ name: "import_pack", description: "Import and validate a scoped behavior pack.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, manifest: { type: "object" }, objects: { type: "object" }, archiveBase64: { type: "string" }, fileName: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "manifest"] } },
|
|
1003
|
+
{ name: "install_pack", description: "Install a scoped behavior pack.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, packId: { type: "string" }, variables: { type: "object" } }, required: ["organizationId", "projectId", "environmentId", "packId"] } },
|
|
1004
|
+
{ name: "evaluate_pack", description: "Evaluate a scoped behavior pack.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, packId: { type: "string" }, passed: { type: "boolean" }, score: { type: "number" }, runId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "packId", "passed"] } },
|
|
1005
|
+
{ name: "approve_pack", description: "Approve a scoped behavior pack.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, packId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "packId"] } },
|
|
1006
|
+
{ name: "release_pack", description: "Release a scoped behavior pack.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, packId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "packId"] } },
|
|
1007
|
+
{ name: "preview_pack_upgrade", description: "Preview a scoped behavior pack upgrade.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, packId: { type: "string" }, incoming: { type: "object" } }, required: ["organizationId", "projectId", "environmentId", "packId", "incoming"] } }
|
|
1008
|
+
]);
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
// ../database/src/mcp-session-repository.ts
|
|
1013
|
+
var SqlMcpSessionRepository;
|
|
1014
|
+
var init_mcp_session_repository = __esm({
|
|
1015
|
+
"../database/src/mcp-session-repository.ts"() {
|
|
1016
|
+
"use strict";
|
|
1017
|
+
init_mcp_session();
|
|
1018
|
+
SqlMcpSessionRepository = class {
|
|
1019
|
+
db;
|
|
1020
|
+
constructor(db) {
|
|
1021
|
+
this.db = db;
|
|
1022
|
+
}
|
|
1023
|
+
save(session) {
|
|
1024
|
+
assertMcpSession(session);
|
|
1025
|
+
const row = this.db.exec("SELECT id,organization_id,project_id,environment_id,server,endpoint,protocol_version,status,auth_digest,generation,created_at,updated_at,session_digest FROM mcp_sessions WHERE id=?", [session.id])[0]?.values[0];
|
|
1026
|
+
if (row) {
|
|
1027
|
+
const prior = this.mapRow(row);
|
|
1028
|
+
if (prior.organizationId !== session.organizationId || prior.projectId !== session.projectId || prior.environmentId !== session.environmentId) throw new Error("MCP_SESSION_SCOPE_CONFLICT");
|
|
1029
|
+
if (prior.sessionDigest === session.sessionDigest) return prior;
|
|
1030
|
+
if (session.generation < prior.generation || session.generation === prior.generation && prior.status === "CLOSED") throw new Error("MCP_SESSION_CONFLICT");
|
|
1031
|
+
this.db.run("UPDATE mcp_sessions SET server=?,endpoint=?,protocol_version=?,status=?,auth_digest=?,generation=?,updated_at=?,session_digest=? WHERE id=? AND organization_id=? AND project_id=? AND environment_id=?", [session.server, session.endpoint, session.protocolVersion, session.status, session.authDigest, session.generation, session.updatedAt, session.sessionDigest, session.id, session.organizationId, session.projectId, session.environmentId]);
|
|
1032
|
+
const persisted2 = this.get(session.id, { organizationId: session.organizationId, projectId: session.projectId, environmentId: session.environmentId });
|
|
1033
|
+
if (!persisted2 || persisted2.sessionDigest !== session.sessionDigest || persisted2.generation !== session.generation || persisted2.status !== session.status) throw new Error("MCP_SESSION_READBACK_MISMATCH");
|
|
1034
|
+
return persisted2;
|
|
1035
|
+
}
|
|
1036
|
+
this.db.run("INSERT INTO mcp_sessions (id,organization_id,project_id,environment_id,server,endpoint,protocol_version,status,auth_digest,generation,created_at,updated_at,session_digest) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", [session.id, session.organizationId, session.projectId, session.environmentId, session.server, session.endpoint, session.protocolVersion, session.status, session.authDigest, session.generation, session.createdAt, session.updatedAt, session.sessionDigest]);
|
|
1037
|
+
const persisted = this.get(session.id, { organizationId: session.organizationId, projectId: session.projectId, environmentId: session.environmentId });
|
|
1038
|
+
if (!persisted || persisted.sessionDigest !== session.sessionDigest) throw new Error("MCP_SESSION_READBACK_MISMATCH");
|
|
1039
|
+
return persisted;
|
|
1040
|
+
}
|
|
1041
|
+
mapRow(row) {
|
|
1042
|
+
const session = { id: String(row[0]), organizationId: String(row[1]), projectId: String(row[2]), environmentId: String(row[3]), server: String(row[4]), endpoint: String(row[5]), protocolVersion: String(row[6]), authDigest: String(row[8]), status: String(row[7]), generation: Number(row[9]), createdAt: String(row[10]), updatedAt: String(row[11]), sessionDigest: String(row[12]) };
|
|
1043
|
+
assertMcpSession(session);
|
|
1044
|
+
return session;
|
|
1045
|
+
}
|
|
1046
|
+
get(id, scope) {
|
|
1047
|
+
const row = this.db.exec("SELECT id,organization_id,project_id,environment_id,server,endpoint,protocol_version,status,auth_digest,generation,created_at,updated_at,session_digest FROM mcp_sessions WHERE id=? AND organization_id=? AND project_id=? AND environment_id=?", [id, scope.organizationId, scope.projectId, scope.environmentId])[0]?.values[0];
|
|
1048
|
+
return row ? this.mapRow(row) : null;
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
// ../../apps/mcp-server/official-stdio.ts
|
|
1055
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
1056
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
1057
|
+
import { z } from "zod";
|
|
1058
|
+
function serveOfficialViraMcp(options) {
|
|
1059
|
+
return serveStdio(() => {
|
|
1060
|
+
const server = new McpServer({ name: options.serverName, version: "1.0.0" }, { capabilities: { tools: {}, resources: options.resources ? {} : void 0, prompts: options.prompts ? {} : void 0 } });
|
|
1061
|
+
for (const definition of options.tools) {
|
|
1062
|
+
server.registerTool(definition.name, { description: definition.description, inputSchema: z.record(z.string(), z.unknown()) }, async (argumentsValue) => {
|
|
1063
|
+
const invocation = { ...options.invocation, server: options.serverName, tool: definition.name, arguments: argumentsValue };
|
|
1064
|
+
const authority = definition.effectful ? await options.authorize?.({ invocation, definition }) ?? { allowed: false, decisionDigest: "", reason: "MCP_AUTHORITY_REQUIRED" } : { allowed: true, decisionDigest: "read-only" };
|
|
1065
|
+
if (!authority.allowed) return { isError: true, content: [{ type: "text", text: authority.reason ?? "MCP_AUTHORITY_DENIED" }] };
|
|
1066
|
+
const result = options.invocationExecutor ? await options.invocationExecutor({ invocation, authority }) : await invokeMcpTool({ invocation, authority, transport: options.transport });
|
|
1067
|
+
return { isError: !result.ok, content: [{ type: "text", text: JSON.stringify(result.result ?? result.error ?? null) }] };
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
return server;
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
var init_official_stdio = __esm({
|
|
1074
|
+
"../../apps/mcp-server/official-stdio.ts"() {
|
|
1075
|
+
"use strict";
|
|
1076
|
+
init_mcp_gateway();
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
// ../database/src/postgres-adapter.ts
|
|
1081
|
+
import { Pool } from "pg";
|
|
1082
|
+
function validateSql(sql) {
|
|
1083
|
+
if (!sql.trim()) throw new Error("POSTGRES_SQL_REQUIRED");
|
|
1084
|
+
if (sql.includes("\0")) throw new Error("POSTGRES_SQL_INVALID");
|
|
1085
|
+
}
|
|
1086
|
+
function createPostgresDatabase(input) {
|
|
1087
|
+
if (!input.pool || typeof input.pool.query !== "function" || typeof input.pool.connect !== "function") throw new Error("POSTGRES_POOL_INVALID");
|
|
1088
|
+
if (!input.databaseUrl?.trim() && !process.env.DATABASE_URL?.trim()) throw new Error("DATABASE_URL_REQUIRED");
|
|
1089
|
+
return { mode: "production", query: async (sql, params = []) => {
|
|
1090
|
+
validateSql(sql);
|
|
1091
|
+
return input.pool.query(sql, params);
|
|
1092
|
+
}, transaction: async (work) => {
|
|
1093
|
+
if (typeof work !== "function") throw new Error("DATABASE_WORK_REQUIRED");
|
|
1094
|
+
const client = await input.pool.connect();
|
|
1095
|
+
const timeoutMs = Math.max(1e3, Number(process.env.VIRA_DB_STATEMENT_TIMEOUT_MS ?? 6e5));
|
|
1096
|
+
try {
|
|
1097
|
+
await client.query("BEGIN");
|
|
1098
|
+
await client.query(`SET LOCAL statement_timeout = ${timeoutMs}`);
|
|
1099
|
+
const result = await work(client);
|
|
1100
|
+
await client.query("COMMIT");
|
|
1101
|
+
return result;
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
try {
|
|
1104
|
+
await client.query("ROLLBACK");
|
|
1105
|
+
} catch {
|
|
1106
|
+
}
|
|
1107
|
+
throw error;
|
|
1108
|
+
} finally {
|
|
1109
|
+
client.release();
|
|
1110
|
+
}
|
|
1111
|
+
}, health: () => ({ ready: true, mode: "production" }) };
|
|
1112
|
+
}
|
|
1113
|
+
function createPostgresPool(input = {}) {
|
|
1114
|
+
const connectionString = input.databaseUrl ?? process.env.DATABASE_URL;
|
|
1115
|
+
if (!connectionString?.startsWith("postgres")) throw new Error("POSTGRES_DATABASE_URL_REQUIRED");
|
|
1116
|
+
const statementTimeoutMs = Math.max(1e3, Number(process.env.VIRA_DB_STATEMENT_TIMEOUT_MS ?? 6e5));
|
|
1117
|
+
const config = { connectionString, max: input.max ?? 10, idleTimeoutMillis: 3e4, connectionTimeoutMillis: 1e4, statement_timeout: statementTimeoutMs, ...input.ssl ? { ssl: { rejectUnauthorized: false } } : {} };
|
|
1118
|
+
const pool = new Pool(config);
|
|
1119
|
+
return { pool, close: () => pool.end() };
|
|
1120
|
+
}
|
|
1121
|
+
var init_postgres_adapter = __esm({
|
|
1122
|
+
"../database/src/postgres-adapter.ts"() {
|
|
1123
|
+
"use strict";
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
// ../database/src/postgres-migrations.ts
|
|
1128
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
1129
|
+
function splitSql(sql) {
|
|
1130
|
+
const statements = [];
|
|
1131
|
+
let start = 0;
|
|
1132
|
+
let quote = null;
|
|
1133
|
+
let dollarQuote = null;
|
|
1134
|
+
let lineComment = false;
|
|
1135
|
+
let blockComment = false;
|
|
1136
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
1137
|
+
const ch = sql[i];
|
|
1138
|
+
const next = sql[i + 1];
|
|
1139
|
+
if (dollarQuote) {
|
|
1140
|
+
if (sql.startsWith(dollarQuote, i)) {
|
|
1141
|
+
i += dollarQuote.length - 1;
|
|
1142
|
+
dollarQuote = null;
|
|
1143
|
+
}
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (lineComment) {
|
|
1147
|
+
if (ch === "\n") lineComment = false;
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (blockComment) {
|
|
1151
|
+
if (ch === "*" && next === "/") {
|
|
1152
|
+
blockComment = false;
|
|
1153
|
+
i += 1;
|
|
1154
|
+
}
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
if (!quote && ch === "-" && next === "-") {
|
|
1158
|
+
lineComment = true;
|
|
1159
|
+
i += 1;
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
if (!quote && ch === "/" && next === "*") {
|
|
1163
|
+
blockComment = true;
|
|
1164
|
+
i += 1;
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
if (quote) {
|
|
1168
|
+
if (ch === quote && sql[i - 1] !== "\\") quote = null;
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
1172
|
+
quote = ch;
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
if (ch === "$") {
|
|
1176
|
+
const match = sql.slice(i).match(/^\$[A-Za-z_0-9]*\$/);
|
|
1177
|
+
if (match) {
|
|
1178
|
+
dollarQuote = match[0];
|
|
1179
|
+
i += match[0].length - 1;
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (ch === ";") {
|
|
1184
|
+
const statement = sql.slice(start, i).trim();
|
|
1185
|
+
if (statement) statements.push(statement);
|
|
1186
|
+
start = i + 1;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const tail = sql.slice(start).trim();
|
|
1190
|
+
if (tail) statements.push(tail);
|
|
1191
|
+
return statements;
|
|
1192
|
+
}
|
|
1193
|
+
function normalizePostgresMigrationSql(sql) {
|
|
1194
|
+
if (!sql.trim()) throw new Error("POSTGRES_MIGRATION_SQL_REQUIRED");
|
|
1195
|
+
if (/\bINSERT\s+OR\s+(?:REPLACE|IGNORE)\b/i.test(sql)) throw new Error("POSTGRES_MIGRATION_CONFLICT_SEMANTICS_UNSUPPORTED");
|
|
1196
|
+
if (/[?]|\$\d+/.test(sql)) throw new Error("POSTGRES_MIGRATION_PARAMETERS_UNSUPPORTED");
|
|
1197
|
+
if (/\bPRAGMA\b/i.test(sql)) throw new Error("POSTGRES_MIGRATION_SQLITE_PRAGMA_UNSUPPORTED");
|
|
1198
|
+
const hasWorkerLeaseTriggers = /CREATE\s+TRIGGER\s+worker_jobs_assign_default_lease/i.test(sql) || /CREATE\s+TRIGGER\s+worker_jobs_clear_lease/i.test(sql);
|
|
1199
|
+
if (/\bCREATE\s+TRIGGER\b/i.test(sql) && !hasWorkerLeaseTriggers) throw new Error("POSTGRES_MIGRATION_SQLITE_TRIGGER_UNSUPPORTED");
|
|
1200
|
+
const normalized = sql.replace(/\bINTEGER\s+PRIMARY\s+KEY\s+AUTOINCREMENT\b/gi, "BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY");
|
|
1201
|
+
if (/\bAUTOINCREMENT\b/i.test(normalized)) throw new Error("POSTGRES_MIGRATION_AUTOINCREMENT_UNSUPPORTED");
|
|
1202
|
+
const withoutWorkerTriggers = normalized.replace(/CREATE\s+TRIGGER\s+worker_jobs_assign_default_lease[\s\S]*?END\s*;?/i, "").replace(/CREATE\s+TRIGGER\s+worker_jobs_clear_lease[\s\S]*?END\s*;?/i, "");
|
|
1203
|
+
if (/\b(?:INSERT|UPDATE|DELETE|UPSERT)\b/i.test(withoutWorkerTriggers)) throw new Error("POSTGRES_MIGRATION_DML_UNSUPPORTED");
|
|
1204
|
+
if (!hasWorkerLeaseTriggers) return withoutWorkerTriggers;
|
|
1205
|
+
return `${withoutWorkerTriggers}
|
|
1206
|
+
CREATE OR REPLACE FUNCTION worker_jobs_assign_default_lease_fn() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
1207
|
+
BEGIN
|
|
1208
|
+
IF NEW.state = 'RUNNING' AND NEW.lease_owner IS NULL THEN
|
|
1209
|
+
NEW.lease_owner := 'worker-default';
|
|
1210
|
+
NEW.lease_expires_at := CURRENT_TIMESTAMP + INTERVAL '60 seconds';
|
|
1211
|
+
END IF;
|
|
1212
|
+
RETURN NEW;
|
|
1213
|
+
END;
|
|
1214
|
+
$$;
|
|
1215
|
+
DROP TRIGGER IF EXISTS worker_jobs_assign_default_lease ON worker_jobs;
|
|
1216
|
+
CREATE TRIGGER worker_jobs_assign_default_lease BEFORE UPDATE OF state ON worker_jobs
|
|
1217
|
+
FOR EACH ROW EXECUTE FUNCTION worker_jobs_assign_default_lease_fn();
|
|
1218
|
+
CREATE OR REPLACE FUNCTION worker_jobs_clear_lease_fn() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
1219
|
+
BEGIN
|
|
1220
|
+
IF NEW.state <> 'RUNNING' THEN
|
|
1221
|
+
NEW.lease_owner := NULL;
|
|
1222
|
+
NEW.lease_expires_at := NULL;
|
|
1223
|
+
END IF;
|
|
1224
|
+
RETURN NEW;
|
|
1225
|
+
END;
|
|
1226
|
+
$$;
|
|
1227
|
+
DROP TRIGGER IF EXISTS worker_jobs_clear_lease ON worker_jobs;
|
|
1228
|
+
CREATE TRIGGER worker_jobs_clear_lease BEFORE UPDATE OF state ON worker_jobs
|
|
1229
|
+
FOR EACH ROW EXECUTE FUNCTION worker_jobs_clear_lease_fn();`;
|
|
1230
|
+
}
|
|
1231
|
+
function migrationChecksums(sql) {
|
|
1232
|
+
const normalized = normalizePostgresMigrationSql(sql);
|
|
1233
|
+
const canonical3 = createHash6("sha256").update(normalized.replace(/\r\n?/g, "\n")).digest("hex");
|
|
1234
|
+
const legacy = createHash6("sha256").update(normalized.replace(/\r?\n/g, "\r\n")).digest("hex");
|
|
1235
|
+
return { canonical: canonical3, legacy };
|
|
1236
|
+
}
|
|
1237
|
+
async function applyPostgresMigrations(client, migrations) {
|
|
1238
|
+
if (!client || typeof client.query !== "function") throw new Error("POSTGRES_CLIENT_INVALID");
|
|
1239
|
+
await client.query("BEGIN");
|
|
1240
|
+
try {
|
|
1241
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", ["vira:schema:migrations"]);
|
|
1242
|
+
await client.query(`CREATE TABLE IF NOT EXISTS vira_schema_migrations (
|
|
1243
|
+
id TEXT PRIMARY KEY,
|
|
1244
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1245
|
+
checksum TEXT,
|
|
1246
|
+
code_version TEXT
|
|
1247
|
+
)`);
|
|
1248
|
+
await client.query("ALTER TABLE vira_schema_migrations ADD COLUMN IF NOT EXISTS checksum TEXT");
|
|
1249
|
+
await client.query("ALTER TABLE vira_schema_migrations ADD COLUMN IF NOT EXISTS code_version TEXT");
|
|
1250
|
+
const applied = await client.query("SELECT id, checksum FROM vira_schema_migrations");
|
|
1251
|
+
const appliedIds = new Map(applied.rows.map((row) => [row.id, row.checksum]));
|
|
1252
|
+
for (const migration of migrations) {
|
|
1253
|
+
if (!migration.id.trim() || !migration.sql.trim()) throw new Error("POSTGRES_MIGRATION_INVALID");
|
|
1254
|
+
const { canonical: checksum, legacy: legacyChecksum } = migrationChecksums(migration.sql);
|
|
1255
|
+
if (appliedIds.has(migration.id)) {
|
|
1256
|
+
const previous = appliedIds.get(migration.id);
|
|
1257
|
+
if (previous && previous !== checksum) {
|
|
1258
|
+
if (previous !== legacyChecksum && !migration.legacyChecksums?.includes(previous)) throw new Error(`POSTGRES_MIGRATION_CHECKSUM_MISMATCH:${migration.id}`);
|
|
1259
|
+
await client.query("UPDATE vira_schema_migrations SET checksum=$1, code_version=$2 WHERE id=$3", [checksum, migration.codeVersion ?? process.env.VIRA_VERSION ?? null, migration.id]);
|
|
1260
|
+
}
|
|
1261
|
+
if (!previous) await client.query("UPDATE vira_schema_migrations SET checksum=$1, code_version=$2 WHERE id=$3", [checksum, migration.codeVersion ?? process.env.VIRA_VERSION ?? null, migration.id]);
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
for (const statement of splitSql(normalizePostgresMigrationSql(migration.sql))) await client.query(statement);
|
|
1265
|
+
await client.query("INSERT INTO vira_schema_migrations (id, checksum, code_version) VALUES ($1, $2, $3)", [migration.id, checksum, migration.codeVersion ?? process.env.VIRA_VERSION ?? null]);
|
|
1266
|
+
}
|
|
1267
|
+
await client.query("COMMIT");
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
try {
|
|
1270
|
+
await client.query("ROLLBACK");
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
throw error;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
var init_postgres_migrations = __esm({
|
|
1277
|
+
"../database/src/postgres-migrations.ts"() {
|
|
1278
|
+
"use strict";
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
// ../database/src/postgres-runtime.ts
|
|
1283
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
1284
|
+
import { dirname, resolve } from "node:path";
|
|
1285
|
+
import { fileURLToPath } from "node:url";
|
|
1286
|
+
async function defaultMigrationPaths() {
|
|
1287
|
+
const migrationDirectory = resolve(here, "../migrations");
|
|
1288
|
+
const entries = await readdir(migrationDirectory, { withFileTypes: true });
|
|
1289
|
+
return entries.filter((entry) => entry.isFile() && /^\d+_.+\.sql$/i.test(entry.name)).map((entry) => resolve(migrationDirectory, entry.name)).sort((left, right) => left.localeCompare(right, "en"));
|
|
1290
|
+
}
|
|
1291
|
+
async function openPostgresRuntime(options = {}) {
|
|
1292
|
+
const { pool, close } = createPostgresPool({ databaseUrl: options.databaseUrl, max: options.maxConnections, ssl: options.ssl });
|
|
1293
|
+
const database = createPostgresDatabase({ pool, databaseUrl: options.databaseUrl });
|
|
1294
|
+
try {
|
|
1295
|
+
if (!options.skipMigrations && process.env.VIRA_WORKER_SKIP_MIGRATIONS !== "true") {
|
|
1296
|
+
const migrations = [];
|
|
1297
|
+
const migrationPaths = options.migrationPaths ?? await defaultMigrationPaths();
|
|
1298
|
+
for (const path of migrationPaths) migrations.push({ id: path.split(/[\\/]/).pop().replace(/\.sql$/i, ""), sql: await readFile(path, "utf8") });
|
|
1299
|
+
const client = await pool.connect();
|
|
1300
|
+
try {
|
|
1301
|
+
await applyPostgresMigrations(client, migrations);
|
|
1302
|
+
} finally {
|
|
1303
|
+
client.release();
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return { ...database, close };
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
await close();
|
|
1309
|
+
throw error;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
var here;
|
|
1313
|
+
var init_postgres_runtime = __esm({
|
|
1314
|
+
"../database/src/postgres-runtime.ts"() {
|
|
1315
|
+
"use strict";
|
|
1316
|
+
init_postgres_adapter();
|
|
1317
|
+
init_postgres_migrations();
|
|
1318
|
+
here = dirname(fileURLToPath(import.meta.url));
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
// ../database/src/production-sqlite.ts
|
|
1323
|
+
import { access, readFile as readFile2, writeFile } from "node:fs/promises";
|
|
1324
|
+
import { existsSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1325
|
+
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
1326
|
+
import { createRequire } from "node:module";
|
|
1327
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1328
|
+
import initSqlJs from "sql.js";
|
|
1329
|
+
function ensureColumn(database, table, column, definition) {
|
|
1330
|
+
try {
|
|
1331
|
+
database.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
const message = String(error);
|
|
1334
|
+
if (!message.includes("duplicate column name")) throw error;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
function reconcilePackSchema(database) {
|
|
1338
|
+
database.run(`CREATE TABLE IF NOT EXISTS control_point_tasks (
|
|
1339
|
+
control_point_id TEXT NOT NULL, task_id TEXT NOT NULL,
|
|
1340
|
+
organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL,
|
|
1341
|
+
PRIMARY KEY (control_point_id, task_id)
|
|
1342
|
+
)`);
|
|
1343
|
+
database.run(`CREATE TABLE IF NOT EXISTS behavior_packs (
|
|
1344
|
+
id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL,
|
|
1345
|
+
environment_id TEXT NOT NULL, digest TEXT NOT NULL, status TEXT NOT NULL,
|
|
1346
|
+
payload_json TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
1347
|
+
)`);
|
|
1348
|
+
database.run(`CREATE TABLE IF NOT EXISTS execution_receipts (
|
|
1349
|
+
id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL,
|
|
1350
|
+
environment_id TEXT NOT NULL, revision_id TEXT NOT NULL, actor_id TEXT NOT NULL,
|
|
1351
|
+
idempotency_key TEXT NOT NULL, decision TEXT NOT NULL, reason TEXT NOT NULL,
|
|
1352
|
+
created_at TEXT NOT NULL,
|
|
1353
|
+
UNIQUE (organization_id, project_id, environment_id, idempotency_key)
|
|
1354
|
+
)`);
|
|
1355
|
+
database.run(`CREATE TABLE IF NOT EXISTS execution_receipt_authority (
|
|
1356
|
+
receipt_id TEXT PRIMARY KEY, authority_digest TEXT NOT NULL,
|
|
1357
|
+
FOREIGN KEY (receipt_id) REFERENCES execution_receipts(id)
|
|
1358
|
+
)`);
|
|
1359
|
+
database.run(`CREATE TABLE IF NOT EXISTS execution_receipt_semantic (
|
|
1360
|
+
receipt_id TEXT PRIMARY KEY, semantic_task_ref TEXT NOT NULL,
|
|
1361
|
+
semantic_task_version INTEGER NOT NULL, semantic_task_digest TEXT NOT NULL,
|
|
1362
|
+
FOREIGN KEY (receipt_id) REFERENCES execution_receipts(id)
|
|
1363
|
+
)`);
|
|
1364
|
+
database.run(`CREATE TABLE IF NOT EXISTS runtime_decision_plans (
|
|
1365
|
+
id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL,
|
|
1366
|
+
environment_id TEXT NOT NULL, plan_json TEXT NOT NULL, decision_right_json TEXT NOT NULL,
|
|
1367
|
+
provider_health TEXT NOT NULL, fallback TEXT NOT NULL, authority_digest TEXT,
|
|
1368
|
+
plan_digest TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
1369
|
+
UNIQUE (organization_id, project_id, environment_id, id)
|
|
1370
|
+
)`);
|
|
1371
|
+
database.run(`CREATE TABLE IF NOT EXISTS runtime_decisions (
|
|
1372
|
+
request_id TEXT NOT NULL, organization_id TEXT NOT NULL, project_id TEXT NOT NULL,
|
|
1373
|
+
environment_id TEXT NOT NULL, decision_json TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
1374
|
+
PRIMARY KEY (organization_id, project_id, environment_id, request_id)
|
|
1375
|
+
)`);
|
|
1376
|
+
const columns = [
|
|
1377
|
+
["behavior_programs", "modules_json", "TEXT NOT NULL DEFAULT '[]'"],
|
|
1378
|
+
["behavior_programs", "provenance_json", "TEXT"],
|
|
1379
|
+
["behavior_programs", "temporal_contract_digest", "TEXT"],
|
|
1380
|
+
["behavior_programs", "temporal_contracts_json", "TEXT"],
|
|
1381
|
+
["behavior_programs", "recovery_plans_json", "TEXT"],
|
|
1382
|
+
["ai_system_connections", "config_json", "TEXT NOT NULL DEFAULT '{}'"]
|
|
1383
|
+
];
|
|
1384
|
+
for (const [table, column, definition] of columns) ensureColumn(database, table, column, definition);
|
|
1385
|
+
}
|
|
1386
|
+
function reconcileStrategySchema(database) {
|
|
1387
|
+
const columns = [
|
|
1388
|
+
["behavior_strategies", "definition_json", "TEXT"],
|
|
1389
|
+
["behavior_strategies", "definition_digest", "TEXT"],
|
|
1390
|
+
["behavior_strategies", "control_point_id", "TEXT"],
|
|
1391
|
+
["behavior_variants", "content_type", "TEXT NOT NULL DEFAULT 'legacy'"],
|
|
1392
|
+
["behavior_variants", "definition_schema_version", "INTEGER"],
|
|
1393
|
+
["behavior_variants", "revision_number", "INTEGER"],
|
|
1394
|
+
["behavior_variants", "definition_digest", "TEXT"],
|
|
1395
|
+
["behavior_variants", "program_id", "TEXT"],
|
|
1396
|
+
["behavior_variants", "program_digest", "TEXT"],
|
|
1397
|
+
["behavior_variants", "provenance_json", "TEXT"],
|
|
1398
|
+
["behavior_variants", "definition_json", "TEXT"],
|
|
1399
|
+
["behavior_variants", "immutable", "INTEGER NOT NULL DEFAULT 1"]
|
|
1400
|
+
];
|
|
1401
|
+
for (const [table, column, definition] of columns) ensureColumn(database, table, column, definition);
|
|
1402
|
+
database.run("CREATE UNIQUE INDEX IF NOT EXISTS behavior_variants_strategy_revision ON behavior_variants(strategy_id, revision_number) WHERE revision_number IS NOT NULL");
|
|
1403
|
+
database.run(`CREATE TABLE IF NOT EXISTS strategy_executions (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, trace_id TEXT NOT NULL, behavior_point_id TEXT NOT NULL, canonical_revision_id TEXT NOT NULL, program_id TEXT NOT NULL, program_digest TEXT NOT NULL, strategy_id TEXT NOT NULL, variant_id TEXT NOT NULL, strategy_digest TEXT NOT NULL, behavior_artifact_digest TEXT, strategy_artifact_digest TEXT, composition_digest TEXT, authority_digest TEXT NOT NULL, mode TEXT NOT NULL, status TEXT NOT NULL, current_step_key TEXT, state_json TEXT NOT NULL, references_json TEXT NOT NULL DEFAULT '{}', state_digest TEXT NOT NULL, step_executions INTEGER NOT NULL DEFAULT 0, model_calls INTEGER NOT NULL DEFAULT 0, tool_calls INTEGER NOT NULL DEFAULT 0, decision_calls INTEGER NOT NULL DEFAULT 0, repair_attempts INTEGER NOT NULL DEFAULT 0, cost_micros INTEGER NOT NULL DEFAULT 0, sequence INTEGER NOT NULL DEFAULT 0, idempotency_key TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, expires_at TEXT, UNIQUE(organization_id,project_id,environment_id,idempotency_key))`);
|
|
1404
|
+
ensureColumn(database, "strategy_executions", "references_json", "TEXT NOT NULL DEFAULT '{}'");
|
|
1405
|
+
database.run(`CREATE TABLE IF NOT EXISTS strategy_step_receipts (id TEXT PRIMARY KEY, execution_id TEXT NOT NULL, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, step_key TEXT NOT NULL, step_type TEXT NOT NULL, attempt INTEGER NOT NULL, status TEXT NOT NULL, input_state_digest TEXT NOT NULL, output_digest TEXT, verification_digest TEXT, transition_key TEXT, next_step_key TEXT, model_request_id TEXT, tool_invocation_id TEXT, decision_receipt_digest TEXT, recovery_action TEXT, usage_json TEXT NOT NULL DEFAULT '{}', latency_ms INTEGER, receipt_digest TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL, FOREIGN KEY(execution_id) REFERENCES strategy_executions(id), UNIQUE(execution_id,step_key,attempt))`);
|
|
1406
|
+
try {
|
|
1407
|
+
database.run("ALTER TABLE strategy_step_receipts ADD COLUMN recovery_action TEXT");
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1410
|
+
}
|
|
1411
|
+
database.run(`CREATE TABLE IF NOT EXISTS strategy_artifact_blobs (artifact_digest TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, strategy_digest TEXT NOT NULL, bytes TEXT NOT NULL, created_at TEXT NOT NULL)`);
|
|
1412
|
+
}
|
|
1413
|
+
function persistFile(filePath, data) {
|
|
1414
|
+
const maxBytes = Number(process.env.VIRA_SQLITE_MAX_BYTES ?? 5 * 1024 * 1024 * 1024);
|
|
1415
|
+
if (!Number.isFinite(maxBytes) || maxBytes < 64 * 1024 * 1024) throw new Error("SQLITE_STORAGE_LIMIT_INVALID");
|
|
1416
|
+
if (data.byteLength > maxBytes) throw new Error("SQLITE_STORAGE_LIMIT_EXCEEDED");
|
|
1417
|
+
const directory = dirname2(filePath);
|
|
1418
|
+
const prefix = `${filePath}.tmp-`;
|
|
1419
|
+
const staleBefore = Date.now() - 60 * 60 * 1e3;
|
|
1420
|
+
for (const name of readdirSync(directory)) {
|
|
1421
|
+
const candidate = resolve2(directory, name);
|
|
1422
|
+
if (!candidate.startsWith(prefix)) continue;
|
|
1423
|
+
try {
|
|
1424
|
+
if (statSync(candidate).mtimeMs < staleBefore) unlinkSync(candidate);
|
|
1425
|
+
} catch {
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
const temporaryPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
1429
|
+
writeFileSync(temporaryPath, Buffer.from(data));
|
|
1430
|
+
renameSync(temporaryPath, filePath);
|
|
1431
|
+
}
|
|
1432
|
+
async function openProductionSqlite({ databaseUrl = process.env.DATABASE_URL } = {}) {
|
|
1433
|
+
if (typeof databaseUrl !== "string" || !databaseUrl.startsWith("file:")) throw new Error("DATABASE_URL_FILE_REQUIRED");
|
|
1434
|
+
const filePath = fileURLToPath2(new URL(databaseUrl));
|
|
1435
|
+
if (!filePath || filePath === "/") throw new Error("DATABASE_PATH_REQUIRED");
|
|
1436
|
+
const migrationPath = migrationCandidates.find((candidate) => existsSync(candidate));
|
|
1437
|
+
if (!migrationPath) throw new Error("DATABASE_MIGRATION_ASSET_MISSING");
|
|
1438
|
+
const SQL = await initSqlJs({ locateFile: (file) => {
|
|
1439
|
+
const moduleAsset = resolve2(dirname2(require2.resolve("sql.js")), file);
|
|
1440
|
+
if (existsSync(moduleAsset)) return moduleAsset;
|
|
1441
|
+
const dependency = resolve2(root, "../../../node_modules/sql.js/dist", file);
|
|
1442
|
+
if (existsSync(dependency)) return dependency;
|
|
1443
|
+
const packagedDependency = resolve2(root, "../../../sql.js/dist", file);
|
|
1444
|
+
if (existsSync(packagedDependency)) return packagedDependency;
|
|
1445
|
+
const packaged = resolve2(root, "sql.js", file);
|
|
1446
|
+
if (existsSync(packaged)) return packaged;
|
|
1447
|
+
return dependency;
|
|
1448
|
+
} });
|
|
1449
|
+
let database;
|
|
1450
|
+
let existing = true;
|
|
1451
|
+
try {
|
|
1452
|
+
await access(filePath);
|
|
1453
|
+
database = new SQL.Database(new Uint8Array(await readFile2(filePath)));
|
|
1454
|
+
} catch {
|
|
1455
|
+
existing = false;
|
|
1456
|
+
database = new SQL.Database();
|
|
1457
|
+
}
|
|
1458
|
+
if (!existing) database.run(await readFile2(migrationPath, "utf8"));
|
|
1459
|
+
reconcilePackSchema(database);
|
|
1460
|
+
reconcileStrategySchema(database);
|
|
1461
|
+
try {
|
|
1462
|
+
database.run("ALTER TABLE session_states ADD COLUMN application_id TEXT");
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1465
|
+
}
|
|
1466
|
+
try {
|
|
1467
|
+
database.run("ALTER TABLE behavior_programs ADD COLUMN provenance_json TEXT");
|
|
1468
|
+
} catch (error) {
|
|
1469
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1470
|
+
}
|
|
1471
|
+
try {
|
|
1472
|
+
database.run("ALTER TABLE worker_jobs ADD COLUMN authority_digest TEXT");
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1475
|
+
}
|
|
1476
|
+
try {
|
|
1477
|
+
database.run("ALTER TABLE worker_jobs ADD COLUMN trace_id TEXT");
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1480
|
+
}
|
|
1481
|
+
try {
|
|
1482
|
+
database.run("ALTER TABLE worker_jobs ADD COLUMN available_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1485
|
+
}
|
|
1486
|
+
try {
|
|
1487
|
+
database.run("ALTER TABLE strategy_executions ADD COLUMN total_recoveries INTEGER NOT NULL DEFAULT 0");
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
database.run("ALTER TABLE learning_evidence ADD COLUMN outcome TEXT");
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1495
|
+
}
|
|
1496
|
+
try {
|
|
1497
|
+
database.run("ALTER TABLE learning_evidence ADD COLUMN provenance_json TEXT");
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1500
|
+
}
|
|
1501
|
+
try {
|
|
1502
|
+
database.run("ALTER TABLE mcp_invocation_provenance ADD COLUMN idempotency_key TEXT");
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1505
|
+
}
|
|
1506
|
+
try {
|
|
1507
|
+
database.run("ALTER TABLE mcp_invocation_provenance ADD COLUMN arguments_digest TEXT");
|
|
1508
|
+
} catch (error) {
|
|
1509
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1510
|
+
}
|
|
1511
|
+
try {
|
|
1512
|
+
database.run("ALTER TABLE ai_system_connections ADD COLUMN config_json TEXT NOT NULL DEFAULT '{}'");
|
|
1513
|
+
} catch (error) {
|
|
1514
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1515
|
+
}
|
|
1516
|
+
database.run("CREATE TABLE IF NOT EXISTS session_handoff_approvals (id TEXT PRIMARY KEY, source_session_id TEXT NOT NULL, target_session_id TEXT NOT NULL, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, application_id TEXT, environment_id TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('PENDING','APPROVED','REJECTED','CONSUMED')), requested_at TEXT NOT NULL, decided_at TEXT, expires_at TEXT NOT NULL, decision_digest TEXT NOT NULL UNIQUE)");
|
|
1517
|
+
database.run(`CREATE TABLE IF NOT EXISTS release_manifests (
|
|
1518
|
+
id TEXT PRIMARY KEY,
|
|
1519
|
+
surfaces_json TEXT NOT NULL,
|
|
1520
|
+
content_digest TEXT NOT NULL,
|
|
1521
|
+
approved_by TEXT NOT NULL,
|
|
1522
|
+
approved_at TEXT NOT NULL,
|
|
1523
|
+
status TEXT NOT NULL,
|
|
1524
|
+
organization_id TEXT,
|
|
1525
|
+
project_id TEXT,
|
|
1526
|
+
environment_id TEXT,
|
|
1527
|
+
authority_digest TEXT,
|
|
1528
|
+
artifact_id TEXT,
|
|
1529
|
+
promotion_version INTEGER
|
|
1530
|
+
)`);
|
|
1531
|
+
for (const statement of [
|
|
1532
|
+
"ALTER TABLE release_manifests ADD COLUMN authority_digest TEXT",
|
|
1533
|
+
"ALTER TABLE release_manifests ADD COLUMN artifact_id TEXT",
|
|
1534
|
+
"ALTER TABLE release_manifests ADD COLUMN promotion_version INTEGER",
|
|
1535
|
+
"ALTER TABLE release_manifests ADD COLUMN strategy_id TEXT",
|
|
1536
|
+
"ALTER TABLE release_manifests ADD COLUMN strategy_variant_id TEXT",
|
|
1537
|
+
"ALTER TABLE release_manifests ADD COLUMN strategy_digest TEXT",
|
|
1538
|
+
"ALTER TABLE release_manifests ADD COLUMN strategy_artifact_digest TEXT",
|
|
1539
|
+
"ALTER TABLE release_manifests ADD COLUMN program_digest TEXT",
|
|
1540
|
+
"ALTER TABLE release_manifests ADD COLUMN evaluation_digest TEXT"
|
|
1541
|
+
]) {
|
|
1542
|
+
try {
|
|
1543
|
+
database.run(statement);
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
database.run("CREATE TABLE IF NOT EXISTS telemetry_release_markers (organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, release_id TEXT NOT NULL, service TEXT NOT NULL, deployment_environment TEXT NOT NULL, revision TEXT NOT NULL, deployment_id TEXT NOT NULL, occurred_at TEXT NOT NULL, trace_id TEXT, marker_digest TEXT NOT NULL, PRIMARY KEY (organization_id,project_id,environment_id,release_id,service,deployment_id))");
|
|
1549
|
+
database.run(`CREATE TABLE IF NOT EXISTS optimization_artifacts (
|
|
1550
|
+
id TEXT PRIMARY KEY,
|
|
1551
|
+
idempotency_key TEXT UNIQUE,
|
|
1552
|
+
organization_id TEXT NOT NULL,
|
|
1553
|
+
project_id TEXT NOT NULL,
|
|
1554
|
+
environment_id TEXT NOT NULL,
|
|
1555
|
+
canonical_digest TEXT NOT NULL,
|
|
1556
|
+
policy_digest TEXT NOT NULL,
|
|
1557
|
+
target_model TEXT NOT NULL,
|
|
1558
|
+
dataset_digest TEXT NOT NULL,
|
|
1559
|
+
metric TEXT NOT NULL,
|
|
1560
|
+
baseline REAL NOT NULL,
|
|
1561
|
+
optimized_result REAL NOT NULL,
|
|
1562
|
+
provenance TEXT NOT NULL,
|
|
1563
|
+
status TEXT NOT NULL,
|
|
1564
|
+
payload_json TEXT NOT NULL DEFAULT '{}'
|
|
1565
|
+
)`);
|
|
1566
|
+
for (const [column, definition] of [["stable_key", "TEXT"], ["parent_authority_level", "TEXT"], ["child_scope_kind", "TEXT"], ["child_scope_digest", "TEXT"], ["parent_revision_digest", "TEXT"], ["exception_effect_types_json", "TEXT"], ["when_json", "TEXT"]]) {
|
|
1567
|
+
try {
|
|
1568
|
+
database.run(`ALTER TABLE scope_delegations ADD COLUMN ${column} ${definition}`);
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
database.run("CREATE TABLE IF NOT EXISTS evaluation_artifacts (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, target_artifact TEXT NOT NULL, dataset_digest TEXT NOT NULL, evaluator_version TEXT NOT NULL, score REAL NOT NULL, threshold REAL NOT NULL, status TEXT NOT NULL, failed_assertions_json TEXT NOT NULL, created_at TEXT NOT NULL)");
|
|
1574
|
+
try {
|
|
1575
|
+
database.run("ALTER TABLE evaluation_artifacts ADD COLUMN holdout_commitment_digest TEXT");
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
database.run("ALTER TABLE evaluation_artifacts ADD COLUMN grader_json TEXT");
|
|
1581
|
+
} catch (error) {
|
|
1582
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1583
|
+
}
|
|
1584
|
+
try {
|
|
1585
|
+
database.run("ALTER TABLE evaluation_artifacts ADD COLUMN suite_id TEXT");
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1588
|
+
}
|
|
1589
|
+
try {
|
|
1590
|
+
database.run("ALTER TABLE evaluation_artifacts ADD COLUMN suite_version TEXT");
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1593
|
+
}
|
|
1594
|
+
try {
|
|
1595
|
+
database.run("ALTER TABLE evaluation_artifacts ADD COLUMN suite_digest TEXT");
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1598
|
+
}
|
|
1599
|
+
try {
|
|
1600
|
+
database.run("ALTER TABLE artifact_shadow_evidence ADD COLUMN release_id TEXT");
|
|
1601
|
+
} catch (error) {
|
|
1602
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1603
|
+
}
|
|
1604
|
+
try {
|
|
1605
|
+
database.run("ALTER TABLE artifact_shadow_evidence ADD COLUMN authority_digest TEXT");
|
|
1606
|
+
} catch (error) {
|
|
1607
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1608
|
+
}
|
|
1609
|
+
database.run("CREATE TABLE IF NOT EXISTS gtm_funnel_events (event_id TEXT NOT NULL, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, event_digest TEXT NOT NULL, source TEXT NOT NULL, stage TEXT NOT NULL, journey_key TEXT NOT NULL, occurred_at TEXT NOT NULL, event_json TEXT NOT NULL, PRIMARY KEY (organization_id, project_id, environment_id, event_id))");
|
|
1610
|
+
database.run("CREATE INDEX IF NOT EXISTS gtm_funnel_events_window ON gtm_funnel_events (organization_id, project_id, environment_id, occurred_at)");
|
|
1611
|
+
for (const column of ["organization_id", "project_id", "environment_id"]) {
|
|
1612
|
+
try {
|
|
1613
|
+
database.run(`ALTER TABLE release_manifests ADD COLUMN ${column} TEXT`);
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
try {
|
|
1619
|
+
database.run("ALTER TABLE release_manifests ADD COLUMN authority_digest TEXT");
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1622
|
+
}
|
|
1623
|
+
try {
|
|
1624
|
+
database.run("ALTER TABLE accelerator_residency_leases ADD COLUMN authority_digest TEXT");
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1627
|
+
}
|
|
1628
|
+
try {
|
|
1629
|
+
database.run("ALTER TABLE trace_events ADD COLUMN authority_digest TEXT");
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1632
|
+
}
|
|
1633
|
+
for (const column of ["authority_digest", "evidence_json"]) {
|
|
1634
|
+
try {
|
|
1635
|
+
database.run(`ALTER TABLE decision_evidence ADD COLUMN ${column} TEXT`);
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
try {
|
|
1641
|
+
database.run("ALTER TABLE mcp_invocation_provenance ADD COLUMN authority_digest TEXT");
|
|
1642
|
+
} catch (error) {
|
|
1643
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1644
|
+
}
|
|
1645
|
+
try {
|
|
1646
|
+
database.run("ALTER TABLE activation_decisions ADD COLUMN authority_digest TEXT");
|
|
1647
|
+
} catch (error) {
|
|
1648
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1649
|
+
}
|
|
1650
|
+
try {
|
|
1651
|
+
database.run("ALTER TABLE impact_attributions ADD COLUMN authority_digest TEXT");
|
|
1652
|
+
} catch (error) {
|
|
1653
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1654
|
+
}
|
|
1655
|
+
try {
|
|
1656
|
+
database.run("ALTER TABLE decision_calibration_snapshots ADD COLUMN authority_digest TEXT");
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1659
|
+
}
|
|
1660
|
+
try {
|
|
1661
|
+
database.run("ALTER TABLE optimization_artifacts ADD COLUMN idempotency_key TEXT");
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1664
|
+
}
|
|
1665
|
+
try {
|
|
1666
|
+
database.run("ALTER TABLE optimization_artifacts ADD COLUMN authority_digest TEXT");
|
|
1667
|
+
} catch (error) {
|
|
1668
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1669
|
+
}
|
|
1670
|
+
try {
|
|
1671
|
+
database.run("ALTER TABLE teach_runs ADD COLUMN selection_digest TEXT");
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1674
|
+
}
|
|
1675
|
+
try {
|
|
1676
|
+
database.run("ALTER TABLE teach_runs ADD COLUMN expansion_digest TEXT");
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1679
|
+
}
|
|
1680
|
+
try {
|
|
1681
|
+
database.run("ALTER TABLE teach_runs ADD COLUMN provider_id TEXT");
|
|
1682
|
+
} catch (error) {
|
|
1683
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1684
|
+
}
|
|
1685
|
+
try {
|
|
1686
|
+
database.run("ALTER TABLE teach_runs ADD COLUMN provider_request_id TEXT");
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1689
|
+
}
|
|
1690
|
+
try {
|
|
1691
|
+
database.run("ALTER TABLE teach_runs ADD COLUMN provider_usage_json TEXT");
|
|
1692
|
+
} catch (error) {
|
|
1693
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1694
|
+
}
|
|
1695
|
+
try {
|
|
1696
|
+
database.run("ALTER TABLE teach_runs ADD COLUMN semantic_stages_json TEXT");
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1699
|
+
}
|
|
1700
|
+
database.run("CREATE TABLE IF NOT EXISTS scope_bindings (id TEXT PRIMARY KEY, level TEXT NOT NULL, parent_id TEXT, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, application_id TEXT, workspace_id TEXT, user_id TEXT, session_id TEXT, created_at TEXT NOT NULL)");
|
|
1701
|
+
database.run("CREATE TABLE IF NOT EXISTS organization_intelligence_snapshots (snapshot_digest TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, as_of TEXT NOT NULL, evidence_digest TEXT NOT NULL, payload_json TEXT NOT NULL, created_at TEXT NOT NULL)");
|
|
1702
|
+
database.run("CREATE TABLE IF NOT EXISTS mcp_sessions (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, server TEXT NOT NULL, endpoint TEXT NOT NULL, protocol_version TEXT NOT NULL, status TEXT NOT NULL, auth_digest TEXT NOT NULL, generation INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, session_digest TEXT NOT NULL UNIQUE)");
|
|
1703
|
+
database.run("CREATE TABLE IF NOT EXISTS oauth_states (state TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, provider TEXT NOT NULL, redirect_uri TEXT NOT NULL, scopes_json TEXT NOT NULL, expires_at TEXT NOT NULL, consumed_at TEXT)");
|
|
1704
|
+
database.run("CREATE TABLE IF NOT EXISTS optimization_runs (id TEXT PRIMARY KEY, idempotency_key TEXT NOT NULL UNIQUE, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, canonical_digest TEXT NOT NULL, policy_digest TEXT NOT NULL, dataset_digest TEXT NOT NULL, engine TEXT NOT NULL, status TEXT NOT NULL, attempts INTEGER NOT NULL DEFAULT 0, cancel_requested INTEGER NOT NULL DEFAULT 0, lease_owner TEXT, lease_expires_at TEXT, search_result_digest TEXT, error_code TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, authority_digest TEXT)");
|
|
1705
|
+
try {
|
|
1706
|
+
database.run("ALTER TABLE optimization_runs ADD COLUMN authority_digest TEXT");
|
|
1707
|
+
} catch (error) {
|
|
1708
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1709
|
+
}
|
|
1710
|
+
database.run("CREATE TABLE IF NOT EXISTS optimization_run_attempts (id TEXT PRIMARY KEY, run_id TEXT NOT NULL, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, attempt INTEGER NOT NULL, worker_id TEXT NOT NULL, status TEXT NOT NULL, request_digest TEXT, response_digest TEXT, error_code TEXT, claimed_at TEXT NOT NULL, finished_at TEXT, authority_digest TEXT)");
|
|
1711
|
+
try {
|
|
1712
|
+
database.run("ALTER TABLE optimization_run_attempts ADD COLUMN authority_digest TEXT");
|
|
1713
|
+
} catch (error) {
|
|
1714
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1715
|
+
}
|
|
1716
|
+
database.run("CREATE TABLE IF NOT EXISTS optimization_cost_reservations (run_id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, reserved_micros INTEGER NOT NULL, actual_micros INTEGER, state TEXT NOT NULL, created_at TEXT NOT NULL, settled_at TEXT, authority_digest TEXT)");
|
|
1717
|
+
try {
|
|
1718
|
+
database.run("ALTER TABLE optimization_cost_reservations ADD COLUMN authority_digest TEXT");
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1721
|
+
}
|
|
1722
|
+
database.run("CREATE TABLE IF NOT EXISTS recovery_runtime_states (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, application_id TEXT, environment_id TEXT NOT NULL, trajectory_key TEXT NOT NULL, recovery_plan_key TEXT NOT NULL, recovery_plan_digest TEXT NOT NULL, attempt INTEGER NOT NULL, next_step_index INTEGER NOT NULL, total_execution_attempts INTEGER NOT NULL, status TEXT NOT NULL, started_at TEXT NOT NULL, updated_at TEXT NOT NULL, step_results_json TEXT NOT NULL, pending_approval_json TEXT, authority_digest TEXT, state_digest TEXT NOT NULL UNIQUE)");
|
|
1723
|
+
try {
|
|
1724
|
+
database.run("ALTER TABLE recovery_runtime_states ADD COLUMN pending_approval_json TEXT");
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1727
|
+
}
|
|
1728
|
+
database.run("CREATE TABLE IF NOT EXISTS recovery_runtime_receipts (id TEXT PRIMARY KEY, runtime_state_id TEXT NOT NULL, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, application_id TEXT, environment_id TEXT NOT NULL, status TEXT NOT NULL, attempt INTEGER NOT NULL, step_results_json TEXT NOT NULL, recovery_plan_digest TEXT NOT NULL, authority_digest TEXT, completed_at TEXT NOT NULL, receipt_digest TEXT NOT NULL UNIQUE)");
|
|
1729
|
+
database.run("CREATE TABLE IF NOT EXISTS behavior_artifact_blobs (artifact_digest TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, bytes TEXT NOT NULL)");
|
|
1730
|
+
try {
|
|
1731
|
+
database.run("ALTER TABLE recovery_runtime_states ADD COLUMN application_id TEXT");
|
|
1732
|
+
} catch (error) {
|
|
1733
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1734
|
+
}
|
|
1735
|
+
try {
|
|
1736
|
+
database.run("ALTER TABLE recovery_runtime_states ADD COLUMN authority_digest TEXT");
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1739
|
+
}
|
|
1740
|
+
database.run("CREATE TABLE IF NOT EXISTS conflict_resolutions (inspection_digest TEXT PRIMARY KEY, resolution_digest TEXT NOT NULL, payload_json TEXT NOT NULL)");
|
|
1741
|
+
try {
|
|
1742
|
+
database.run("ALTER TABLE conflict_inspections ADD COLUMN payload_json TEXT");
|
|
1743
|
+
} catch (error) {
|
|
1744
|
+
if (!String(error).includes("duplicate column name")) throw error;
|
|
1745
|
+
}
|
|
1746
|
+
const persist = async () => {
|
|
1747
|
+
persistFile(filePath, database.export());
|
|
1748
|
+
};
|
|
1749
|
+
let transactionDepth = 0;
|
|
1750
|
+
const runtime = {
|
|
1751
|
+
mode: "production",
|
|
1752
|
+
exec: (sql, params) => {
|
|
1753
|
+
const result = database.exec(sql, params);
|
|
1754
|
+
if (transactionDepth === 0 && /^\s*(INSERT|UPDATE|DELETE|REPLACE)\b/i.test(sql)) persistFile(filePath, database.export());
|
|
1755
|
+
return result;
|
|
1756
|
+
},
|
|
1757
|
+
run: (sql, params) => {
|
|
1758
|
+
const result = database.run(sql, params);
|
|
1759
|
+
if (transactionDepth === 0) persistFile(filePath, database.export());
|
|
1760
|
+
return result;
|
|
1761
|
+
},
|
|
1762
|
+
async transaction(work) {
|
|
1763
|
+
if (typeof work !== "function") throw new Error("DATABASE_WORK_REQUIRED");
|
|
1764
|
+
transactionDepth += 1;
|
|
1765
|
+
database.run("BEGIN");
|
|
1766
|
+
try {
|
|
1767
|
+
const result = await work(runtime);
|
|
1768
|
+
database.run("COMMIT");
|
|
1769
|
+
transactionDepth -= 1;
|
|
1770
|
+
await persist();
|
|
1771
|
+
return result;
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
try {
|
|
1774
|
+
database.run("ROLLBACK");
|
|
1775
|
+
} catch {
|
|
1776
|
+
}
|
|
1777
|
+
transactionDepth = Math.max(0, transactionDepth - 1);
|
|
1778
|
+
throw error;
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
transactionSync(work) {
|
|
1782
|
+
if (typeof work !== "function") throw new Error("DATABASE_WORK_REQUIRED");
|
|
1783
|
+
transactionDepth += 1;
|
|
1784
|
+
database.run("BEGIN");
|
|
1785
|
+
try {
|
|
1786
|
+
const result = work(runtime);
|
|
1787
|
+
database.run("COMMIT");
|
|
1788
|
+
transactionDepth -= 1;
|
|
1789
|
+
persistFile(filePath, database.export());
|
|
1790
|
+
return result;
|
|
1791
|
+
} catch (error) {
|
|
1792
|
+
try {
|
|
1793
|
+
database.run("ROLLBACK");
|
|
1794
|
+
} catch {
|
|
1795
|
+
}
|
|
1796
|
+
transactionDepth = Math.max(0, transactionDepth - 1);
|
|
1797
|
+
throw error;
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
async persist() {
|
|
1801
|
+
await persist();
|
|
1802
|
+
},
|
|
1803
|
+
async reloadFromDisk() {
|
|
1804
|
+
const bytes = new Uint8Array(await readFile2(filePath));
|
|
1805
|
+
database.close();
|
|
1806
|
+
database = new SQL.Database(bytes);
|
|
1807
|
+
},
|
|
1808
|
+
async backup(destination) {
|
|
1809
|
+
if (!destination?.trim()) throw new Error("BACKUP_PATH_REQUIRED");
|
|
1810
|
+
await writeFile(destination, Buffer.from(database.export()));
|
|
1811
|
+
return destination;
|
|
1812
|
+
},
|
|
1813
|
+
health: () => ({ ready: true, mode: "production", storage: "file" }),
|
|
1814
|
+
close: () => database.close()
|
|
1815
|
+
};
|
|
1816
|
+
return runtime;
|
|
1817
|
+
}
|
|
1818
|
+
var root, require2, migrationCandidates;
|
|
1819
|
+
var init_production_sqlite = __esm({
|
|
1820
|
+
"../database/src/production-sqlite.ts"() {
|
|
1821
|
+
"use strict";
|
|
1822
|
+
root = dirname2(fileURLToPath2(import.meta.url));
|
|
1823
|
+
require2 = createRequire(import.meta.url);
|
|
1824
|
+
migrationCandidates = [
|
|
1825
|
+
resolve2(root, "../migrations/001_vnext_foundation.sql"),
|
|
1826
|
+
resolve2(root, "migrations/001_vnext_foundation.sql")
|
|
1827
|
+
];
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1831
|
+
// ../database/src/runtime-database.ts
|
|
1832
|
+
var runtime_database_exports = {};
|
|
1833
|
+
__export(runtime_database_exports, {
|
|
1834
|
+
createProductionDatabase: () => createProductionDatabase,
|
|
1835
|
+
openRuntimeDatabase: () => openRuntimeDatabase
|
|
1836
|
+
});
|
|
1837
|
+
async function createProductionDatabase(options = {}) {
|
|
1838
|
+
const databaseUrl = options.databaseUrl ?? process.env.DATABASE_URL;
|
|
1839
|
+
if (typeof databaseUrl !== "string" || !/^postgres(?:ql)?:\/\//.test(databaseUrl)) throw new Error("PRODUCTION_POSTGRES_DATABASE_REQUIRED");
|
|
1840
|
+
return openRuntimeDatabase({ ...options, databaseUrl });
|
|
1841
|
+
}
|
|
1842
|
+
async function openRuntimeDatabase(options = {}) {
|
|
1843
|
+
const databaseUrl = options.databaseUrl ?? process.env.DATABASE_URL;
|
|
1844
|
+
if (typeof databaseUrl !== "string" || !databaseUrl.trim()) throw new Error("DATABASE_URL_REQUIRED");
|
|
1845
|
+
if (databaseUrl.startsWith("file:")) {
|
|
1846
|
+
if (process.env.NODE_ENV === "production" || process.env.VIRA_STACK_PROFILE === "production") throw new Error("SQLITE_FORBIDDEN_IN_PRODUCTION");
|
|
1847
|
+
const database = await openProductionSqlite({ databaseUrl, ...options.sqlite });
|
|
1848
|
+
return {
|
|
1849
|
+
mode: "sqlite",
|
|
1850
|
+
database,
|
|
1851
|
+
health: () => ({ ready: true, mode: "sqlite" }),
|
|
1852
|
+
close: async () => {
|
|
1853
|
+
database.close();
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
if (databaseUrl.startsWith("postgres://") || databaseUrl.startsWith("postgresql://")) {
|
|
1858
|
+
const database = await openPostgresRuntime({ databaseUrl, ...options.postgres });
|
|
1859
|
+
return {
|
|
1860
|
+
mode: "postgres",
|
|
1861
|
+
database,
|
|
1862
|
+
health: () => ({ ready: true, mode: "postgres" }),
|
|
1863
|
+
close: async () => {
|
|
1864
|
+
await database.close();
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
throw new Error("DATABASE_URL_UNSUPPORTED");
|
|
1869
|
+
}
|
|
1870
|
+
var init_runtime_database = __esm({
|
|
1871
|
+
"../database/src/runtime-database.ts"() {
|
|
1872
|
+
"use strict";
|
|
1873
|
+
init_postgres_runtime();
|
|
1874
|
+
init_production_sqlite();
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
// ../database/src/postgres-mcp-repository.ts
|
|
1879
|
+
var postgres_mcp_repository_exports = {};
|
|
1880
|
+
__export(postgres_mcp_repository_exports, {
|
|
1881
|
+
PostgresMcpProvenanceRepository: () => PostgresMcpProvenanceRepository,
|
|
1882
|
+
PostgresMcpSessionRepository: () => PostgresMcpSessionRepository
|
|
1883
|
+
});
|
|
1884
|
+
var provenanceSelect, PostgresMcpProvenanceRepository, PostgresMcpSessionRepository;
|
|
1885
|
+
var init_postgres_mcp_repository = __esm({
|
|
1886
|
+
"../database/src/postgres-mcp-repository.ts"() {
|
|
1887
|
+
"use strict";
|
|
1888
|
+
init_mcp_provenance();
|
|
1889
|
+
init_mcp_session();
|
|
1890
|
+
provenanceSelect = `SELECT invocation_id, organization_id, project_id, environment_id, server, tool, receipt_id, idempotency_key, arguments_digest, ok, result_json, error, authority_decision_digest, authority_digest, provenance_digest FROM mcp_invocation_provenance`;
|
|
1891
|
+
PostgresMcpProvenanceRepository = class {
|
|
1892
|
+
constructor(db) {
|
|
1893
|
+
this.db = db;
|
|
1894
|
+
}
|
|
1895
|
+
db;
|
|
1896
|
+
async init() {
|
|
1897
|
+
await this.db.query(`CREATE TABLE IF NOT EXISTS mcp_invocation_claims (organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, idempotency_key TEXT NOT NULL, invocation_id TEXT NOT NULL, claimed_at TIMESTAMPTZ NOT NULL, PRIMARY KEY (organization_id, project_id, environment_id, idempotency_key))`);
|
|
1898
|
+
await this.db.query(`CREATE TABLE IF NOT EXISTS mcp_invocation_provenance (invocation_id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, server TEXT NOT NULL, tool TEXT NOT NULL, receipt_id TEXT NOT NULL, idempotency_key TEXT NOT NULL, arguments_digest TEXT NOT NULL, ok BOOLEAN NOT NULL, result_json JSONB, error TEXT, authority_decision_digest TEXT NOT NULL, authority_digest TEXT, provenance_digest TEXT NOT NULL, UNIQUE (organization_id, project_id, environment_id, idempotency_key))`);
|
|
1899
|
+
}
|
|
1900
|
+
async claim(input) {
|
|
1901
|
+
const replay = await this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
1902
|
+
if (replay) return { status: "REPLAY", result: replay };
|
|
1903
|
+
try {
|
|
1904
|
+
await this.db.query(`INSERT INTO mcp_invocation_claims (organization_id,project_id,environment_id,idempotency_key,invocation_id,claimed_at) VALUES ($1,$2,$3,$4,$5,NOW())`, [input.organizationId, input.projectId, input.environmentId, input.idempotencyKey, input.invocationId]);
|
|
1905
|
+
return { status: "CLAIMED" };
|
|
1906
|
+
} catch {
|
|
1907
|
+
const after = await this.getByIdempotencyKey(input.idempotencyKey, input);
|
|
1908
|
+
return after ? { status: "REPLAY", result: after } : { status: "IN_FLIGHT" };
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
async releaseClaim(input) {
|
|
1912
|
+
await this.db.query(`DELETE FROM mcp_invocation_claims WHERE organization_id=$1 AND project_id=$2 AND environment_id=$3 AND idempotency_key=$4`, [input.organizationId, input.projectId, input.environmentId, input.idempotencyKey]);
|
|
1913
|
+
}
|
|
1914
|
+
async save(result) {
|
|
1915
|
+
verifyMcpResult2(result);
|
|
1916
|
+
const existing = await this.get(result.invocationId);
|
|
1917
|
+
const byKey = await this.getByIdempotencyKey(result.idempotencyKey, result);
|
|
1918
|
+
if (existing || byKey) {
|
|
1919
|
+
const prior = existing ?? byKey;
|
|
1920
|
+
if (prior.provenanceDigest !== result.provenanceDigest) throw new Error("MCP_PROVENANCE_CONFLICT");
|
|
1921
|
+
return prior;
|
|
1922
|
+
}
|
|
1923
|
+
await this.db.query(`DELETE FROM mcp_invocation_claims WHERE organization_id=$1 AND project_id=$2 AND environment_id=$3 AND idempotency_key=$4`, [result.organizationId, result.projectId, result.environmentId, result.idempotencyKey]);
|
|
1924
|
+
await this.db.query(`INSERT INTO mcp_invocation_provenance (invocation_id,organization_id,project_id,environment_id,server,tool,receipt_id,idempotency_key,arguments_digest,ok,result_json,error,authority_decision_digest,authority_digest,provenance_digest) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)`, [result.invocationId, result.organizationId, result.projectId, result.environmentId, result.server, result.tool, result.receiptId, result.idempotencyKey, result.argumentsDigest, result.ok, result.result === void 0 ? null : JSON.stringify(result.result), result.error ?? null, result.authorityDecisionDigest, result.authorityDigest, result.provenanceDigest]);
|
|
1925
|
+
const persisted = await this.get(result.invocationId);
|
|
1926
|
+
if (!persisted || persisted.provenanceDigest !== result.provenanceDigest) throw new Error("MCP_PROVENANCE_READBACK_MISMATCH");
|
|
1927
|
+
return persisted;
|
|
1928
|
+
}
|
|
1929
|
+
async get(invocationId) {
|
|
1930
|
+
const row = (await this.db.query(`${provenanceSelect} WHERE invocation_id=$1`, [invocationId])).rows[0];
|
|
1931
|
+
return row ? this.fromRow(row) : null;
|
|
1932
|
+
}
|
|
1933
|
+
async getByIdempotencyKey(key, scope) {
|
|
1934
|
+
const row = (await this.db.query(`${provenanceSelect} WHERE idempotency_key=$1 AND organization_id=$2 AND project_id=$3 AND environment_id=$4`, [key, scope.organizationId, scope.projectId, scope.environmentId])).rows[0];
|
|
1935
|
+
return row ? this.fromRow(row) : null;
|
|
1936
|
+
}
|
|
1937
|
+
fromRow(row) {
|
|
1938
|
+
const result = { invocationId: String(row.invocation_id), organizationId: String(row.organization_id), projectId: String(row.project_id), environmentId: String(row.environment_id), server: String(row.server), tool: String(row.tool), receiptId: String(row.receipt_id), idempotencyKey: String(row.idempotency_key), argumentsDigest: String(row.arguments_digest), ok: Boolean(row.ok), result: row.result_json ?? void 0, error: row.error == null ? void 0 : String(row.error), authorityDecisionDigest: String(row.authority_decision_digest), authorityDigest: row.authority_digest == null ? null : String(row.authority_digest), provenanceDigest: String(row.provenance_digest) };
|
|
1939
|
+
verifyMcpResult2(result);
|
|
1940
|
+
return result;
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
PostgresMcpSessionRepository = class {
|
|
1944
|
+
constructor(db) {
|
|
1945
|
+
this.db = db;
|
|
1946
|
+
}
|
|
1947
|
+
db;
|
|
1948
|
+
async init() {
|
|
1949
|
+
await this.db.query(`CREATE TABLE IF NOT EXISTS mcp_sessions (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, project_id TEXT NOT NULL, environment_id TEXT NOT NULL, server TEXT NOT NULL, endpoint TEXT NOT NULL, protocol_version TEXT NOT NULL, status TEXT NOT NULL, auth_digest TEXT NOT NULL, generation INTEGER NOT NULL, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL, session_digest TEXT NOT NULL)`);
|
|
1950
|
+
}
|
|
1951
|
+
async get(id, scope) {
|
|
1952
|
+
const row = (await this.db.query(`SELECT id,organization_id,project_id,environment_id,server,endpoint,protocol_version,status,auth_digest,generation,created_at,updated_at,session_digest FROM mcp_sessions WHERE id=$1 AND organization_id=$2 AND project_id=$3 AND environment_id=$4`, [id, scope.organizationId, scope.projectId, scope.environmentId])).rows[0];
|
|
1953
|
+
if (!row) return null;
|
|
1954
|
+
const session = { id: String(row.id), organizationId: String(row.organization_id), projectId: String(row.project_id), environmentId: String(row.environment_id), server: String(row.server), endpoint: String(row.endpoint), protocolVersion: String(row.protocol_version), status: String(row.status), authDigest: String(row.auth_digest), generation: Number(row.generation), createdAt: new Date(String(row.created_at)).toISOString(), updatedAt: new Date(String(row.updated_at)).toISOString(), sessionDigest: String(row.session_digest) };
|
|
1955
|
+
assertMcpSession(session);
|
|
1956
|
+
return session;
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
// ../../apps/mcp-server/start.ts
|
|
1963
|
+
var start_exports = {};
|
|
1964
|
+
__export(start_exports, {
|
|
1965
|
+
startMcpStdio: () => startMcpStdio
|
|
1966
|
+
});
|
|
1967
|
+
async function startMcpStdio() {
|
|
1968
|
+
const base = resolveViraMcpConfig();
|
|
1969
|
+
const required = ["VIRA_ORGANIZATION_ID", "VIRA_PROJECT_ID", "VIRA_ENVIRONMENT_ID"];
|
|
1970
|
+
const missing = required.filter((name) => !process.env[name]?.trim());
|
|
1971
|
+
if (missing.length) throw new Error(`VIRA_MCP_SCOPE_REQUIRED:${missing.join(",")}`);
|
|
1972
|
+
const organizationId = process.env.VIRA_ORGANIZATION_ID;
|
|
1973
|
+
const projectId = process.env.VIRA_PROJECT_ID;
|
|
1974
|
+
const environmentId = process.env.VIRA_ENVIRONMENT_ID;
|
|
1975
|
+
const runtime = process.env.DATABASE_URL ? await (await Promise.resolve().then(() => (init_runtime_database(), runtime_database_exports))).openRuntimeDatabase() : null;
|
|
1976
|
+
const database = runtime?.mode === "sqlite" ? runtime.database : null;
|
|
1977
|
+
const postgres = runtime?.mode === "postgres" ? runtime.database : null;
|
|
1978
|
+
const postgresRepositories = postgres ? await Promise.resolve().then(() => (init_postgres_mcp_repository(), postgres_mcp_repository_exports)) : null;
|
|
1979
|
+
const provenanceRepository = database ? new SqlMcpProvenanceRepository(database) : postgres && postgresRepositories ? new postgresRepositories.PostgresMcpProvenanceRepository(postgres) : new McpProvenanceRepository();
|
|
1980
|
+
if (postgres && postgresRepositories) {
|
|
1981
|
+
await new postgresRepositories.PostgresMcpProvenanceRepository(postgres).init();
|
|
1982
|
+
}
|
|
1983
|
+
const options = createViraMcpOptions({
|
|
1984
|
+
...base,
|
|
1985
|
+
scope: {
|
|
1986
|
+
organizationId,
|
|
1987
|
+
projectId,
|
|
1988
|
+
environmentId
|
|
1989
|
+
},
|
|
1990
|
+
...database || postgres ? {
|
|
1991
|
+
provenanceRepository,
|
|
1992
|
+
sessionRepository: database ? new SqlMcpSessionRepository(database) : new postgresRepositories.PostgresMcpSessionRepository(postgres),
|
|
1993
|
+
sessionId: process.env.VIRA_MCP_SESSION_ID
|
|
1994
|
+
} : {}
|
|
1995
|
+
});
|
|
1996
|
+
options.invocationExecutor = ({ invocation, authority, signal }) => executeMcpInvocation({ invocation, authority, signal, transport: options.transport, store: provenanceRepository });
|
|
1997
|
+
options.requireInvocationExecutor = true;
|
|
1998
|
+
const useLegacy = process.env.VIRA_MCP_PROTOCOL_MODE === "legacy";
|
|
1999
|
+
const stdio = useLegacy ? createMcpStdioServer(options) : serveOfficialViraMcp(options);
|
|
2000
|
+
const close = () => {
|
|
2001
|
+
stdio.close();
|
|
2002
|
+
void runtime?.close();
|
|
2003
|
+
};
|
|
2004
|
+
process.once("SIGTERM", close);
|
|
2005
|
+
process.once("SIGINT", close);
|
|
2006
|
+
}
|
|
2007
|
+
var init_start = __esm({
|
|
2008
|
+
"../../apps/mcp-server/start.ts"() {
|
|
2009
|
+
"use strict";
|
|
2010
|
+
init_server();
|
|
2011
|
+
init_vira_server();
|
|
2012
|
+
init_mcp_session_repository();
|
|
2013
|
+
init_mcp_provenance_repository();
|
|
2014
|
+
init_mcp_provenance_repository();
|
|
2015
|
+
init_mcp_invocation_service();
|
|
2016
|
+
init_official_stdio();
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
|
|
2020
|
+
// src/cli.ts
|
|
2021
|
+
init_vira_server();
|
|
2022
|
+
if (process.argv.includes("--http") || process.env.VIRA_MCP_TRANSPORT === "http") {
|
|
2023
|
+
await startHttp();
|
|
2024
|
+
} else {
|
|
2025
|
+
const { startMcpStdio: startMcpStdio2 } = await Promise.resolve().then(() => (init_start(), start_exports));
|
|
2026
|
+
await startMcpStdio2();
|
|
2027
|
+
}
|
|
2028
|
+
async function startHttp() {
|
|
2029
|
+
const base = resolveViraMcpConfig();
|
|
2030
|
+
if (!base.requestAuthToken?.trim()) throw new Error("VIRA_MCP_AUTH_TOKEN_REQUIRED");
|
|
2031
|
+
const scope = { organizationId: process.env.VIRA_ORGANIZATION_ID ?? "", projectId: process.env.VIRA_PROJECT_ID ?? "", environmentId: process.env.VIRA_ENVIRONMENT_ID ?? "" };
|
|
2032
|
+
for (const value of Object.values(scope)) if (!value.trim()) throw new Error("VIRA_MCP_SCOPE_REQUIRED");
|
|
2033
|
+
const allowLocalEffects = process.env.VIRA_MCP_ALLOW_EFFECTFUL === "true" && process.env.NODE_ENV !== "production";
|
|
2034
|
+
const server = createViraMcpServer({ ...base, scope, ...allowLocalEffects ? { authorizeEffectful: () => ({ allowed: true, decisionDigest: "explicit-local-opt-in" }) } : {} });
|
|
2035
|
+
const port = Number(process.env.PORT ?? 4195);
|
|
2036
|
+
const host = process.env.VIRA_MCP_BIND_PUBLIC === "true" ? process.env.HOST ?? "0.0.0.0" : process.env.HOST ?? "127.0.0.1";
|
|
2037
|
+
server.listen(port, host, () => console.error(JSON.stringify({ status: "VIRA_MCP_STARTED", port, host, upstream: base.baseUrl, effectful: allowLocalEffects })));
|
|
2038
|
+
}
|