Return explicit method intents in reference indexes while preserving all full contracts, topic matching and complete MCP catalogs. Separate provider-free contracts from session admission and defer native loading until open. Validate 82 related tests, exact SDK/MCP full-contract parity, real zlib CLI/SDK reads and native/inspection cleanup retries. Freeze the unchanged two-level question for an order-reversed real-model repeat. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
137 lines
5.2 KiB
JavaScript
137 lines
5.2 KiB
JavaScript
// Complete experimental catalog; it does not truncate the production REA catalog.
|
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
import { z } from "zod";
|
|
import { methods, reference } from "./api.mjs";
|
|
import { AtomicSession } from "./session.mjs";
|
|
import { executeJavaScript } from "./executor.mjs";
|
|
import { referenceInput } from "./reference.mjs";
|
|
import { EXECUTION_LIMITS } from "./limits.mjs";
|
|
import { TARGET_CONTENT_RESULT_META } from "../../dist/server/toolResultEncoding.js";
|
|
import { appendFile } from "node:fs/promises";
|
|
|
|
const DIRECT_REFERENCE_LANGUAGE =
|
|
"Direct MCP calls: use the displayed facts_* tools with the same JSON arguments; results have {ok,context,data}.";
|
|
const executionInput = z.strictObject({
|
|
code: z.string().min(1).max(EXECUTION_LIMITS.code_utf8_bytes),
|
|
timeout_ms: z
|
|
.number()
|
|
.int()
|
|
.min(EXECUTION_LIMITS.timeout_ms.minimum)
|
|
.max(EXECUTION_LIMITS.timeout_ms.maximum)
|
|
.default(EXECUTION_LIMITS.timeout_ms.default),
|
|
});
|
|
const session = new AtomicSession();
|
|
const createServer = () => {
|
|
const server = new McpServer({ name: "rea-codemode", version: "0.1.0" });
|
|
const direct = process.env.REA_ATOMIC_ADAPTER === "direct";
|
|
const apiNames = Object.fromEntries(
|
|
Object.keys(methods).map((name) => [
|
|
name,
|
|
direct ? `facts_${name}` : `rea.${name}`,
|
|
]),
|
|
);
|
|
const encode = (value) => ({
|
|
content: [{ type: "text", text: JSON.stringify(value) }],
|
|
_meta: TARGET_CONTENT_RESULT_META,
|
|
});
|
|
const register = (name, description, inputSchema, handler) =>
|
|
server.registerTool(
|
|
name,
|
|
{ description, inputSchema, annotations: { readOnlyHint: true } },
|
|
async (input, request) => {
|
|
try {
|
|
const result = await handler(input, request.mcpReq.signal);
|
|
if (process.env.REA_ATOMIC_TRACE)
|
|
await appendFile(
|
|
process.env.REA_ATOMIC_TRACE,
|
|
JSON.stringify({ tool: name, input, result }) + "\n",
|
|
{ mode: 0o600 },
|
|
);
|
|
if (name === "execute_javascript") {
|
|
const { calls, ...answer } = result;
|
|
return encode({
|
|
...answer,
|
|
execution: { host_calls: calls?.length ?? 0 },
|
|
authority: "script-derived",
|
|
context: answer.context ?? null,
|
|
});
|
|
}
|
|
return encode(result);
|
|
} catch (error) {
|
|
return encode({
|
|
ok: false,
|
|
context: session.context,
|
|
error: {
|
|
code: error.code ?? "analysis_failed",
|
|
message: error.message,
|
|
details: error.details ?? {},
|
|
},
|
|
});
|
|
}
|
|
},
|
|
);
|
|
register(
|
|
"open_binary",
|
|
"Open one local binary for static Kuna analysis. Returns artifact/engine identity and a target lease. Does not execute or modify the target.",
|
|
z.strictObject({ path: z.string().min(1) }),
|
|
(input) => session.open(input.path),
|
|
);
|
|
register(
|
|
"close_binary",
|
|
"Close this target lease and owned native resources; retained snapshots and Evidence expire.",
|
|
z.strictObject({}),
|
|
() => session.close(),
|
|
);
|
|
register(
|
|
"reference",
|
|
"Discover fact methods with view:index and an empty query: returns names and short intent summaries without schemas. Then request only needed full contracts and semantics with view:contracts and exact displayed names or canonical IDs separated by spaces/commas. One topic query searches full descriptions; empty query with view:contracts returns all contracts. These are static declared methods, not live provider availability.",
|
|
referenceInput,
|
|
(input) =>
|
|
reference(input.query, {
|
|
names: apiNames,
|
|
view: input.view,
|
|
includeExamples: !direct,
|
|
language: direct ? DIRECT_REFERENCE_LANGUAGE : undefined,
|
|
execution: !direct,
|
|
}),
|
|
);
|
|
if (direct) {
|
|
for (const [name, contract] of Object.entries(methods))
|
|
register(
|
|
apiNames[name],
|
|
contract.description,
|
|
contract.input,
|
|
(input, signal) => session.call(name, input, signal),
|
|
);
|
|
} else {
|
|
register(
|
|
"execute_javascript",
|
|
`Execute a JavaScript async function body against the open binary's atomic rea API. Use reference first. Await rea methods, compose/filter locally, and return a small JSON answer with evidence IDs and unknowns. Omit timeout_ms for the ${EXECUTION_LIMITS.timeout_ms.default} ms wall deadline (also the maximum); guest CPU is separately bounded. No Node/filesystem/network APIs. Fresh JS environment per call; analysis facts are reused across calls.`,
|
|
executionInput,
|
|
(input, signal) =>
|
|
executeJavaScript(session, input.code, {
|
|
signal,
|
|
timeoutMs: input.timeout_ms,
|
|
}),
|
|
);
|
|
}
|
|
return server;
|
|
};
|
|
const handle = serveStdio(createServer, {
|
|
onerror: (error) => process.stderr.write(`${error.message}\n`),
|
|
});
|
|
let closing;
|
|
const shutdown = () =>
|
|
(closing ??= (async () => {
|
|
session.abort();
|
|
await session.close();
|
|
await handle.close();
|
|
})().catch((error) => {
|
|
process.stderr.write(`${error.message}\n`);
|
|
process.exitCode = 1;
|
|
}));
|
|
process.once("SIGTERM", shutdown);
|
|
process.once("SIGINT", shutdown);
|
|
process.stdin.once("end", shutdown);
|