214 lines
5.9 KiB
JavaScript
214 lines
5.9 KiB
JavaScript
const URL = process.env.MCP_996_GUI_URL;
|
|
const AUTH = process.env.MCP_996_GUI_AUTH;
|
|
const fs = require("fs");
|
|
const LOG = process.env.MCP_996_GUI_LOG || "C:\\Users\\Administrator\\.codex\\mcp_996_gui_proxy.log";
|
|
|
|
function log(message) {
|
|
try {
|
|
fs.appendFileSync(LOG, `${new Date().toISOString()} ${message}\n`, "utf8");
|
|
} catch {
|
|
// Logging must never break the MCP server.
|
|
}
|
|
}
|
|
|
|
log(`start pid=${process.pid}`);
|
|
|
|
const tools = [
|
|
{
|
|
name: "list_rule_files",
|
|
description: "List all client GUI API rule files.",
|
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
},
|
|
{
|
|
name: "list_versions",
|
|
description: "List available client GUI API versions.",
|
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
},
|
|
{
|
|
name: "list_apis",
|
|
description: "Search client GUI APIs by keyword.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
keyword: { type: "string", default: "" },
|
|
limit: { type: "integer", default: 50 },
|
|
version: { type: "string", default: "" },
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
{
|
|
name: "generate_lua",
|
|
description: "Generate client Lua code from the original natural language query.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
query: { type: "string" },
|
|
top_k: { type: "integer", default: 3 },
|
|
version: { type: "string", default: "" },
|
|
},
|
|
required: ["query"],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
{
|
|
name: "generate_lua_996",
|
|
description: "Alias of generate_lua for 996 API client GUI code generation.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
query: { type: "string" },
|
|
top_k: { type: "integer", default: 3 },
|
|
version: { type: "string", default: "" },
|
|
},
|
|
required: ["query"],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
];
|
|
|
|
let input = Buffer.alloc(0);
|
|
|
|
process.stdin.on("data", (chunk) => {
|
|
log(`stdin bytes=${chunk.length}`);
|
|
input = Buffer.concat([input, chunk]);
|
|
readMessages();
|
|
});
|
|
|
|
process.stdin.on("end", () => log("stdin end"));
|
|
process.on("uncaughtException", (error) => log(`uncaught ${error.stack || error.message || error}`));
|
|
|
|
function readMessages() {
|
|
while (true) {
|
|
let headerEnd = input.indexOf("\r\n\r\n");
|
|
let separatorLength = 4;
|
|
if (headerEnd < 0) {
|
|
headerEnd = input.indexOf("\n\n");
|
|
separatorLength = 2;
|
|
}
|
|
if (headerEnd < 0) return;
|
|
|
|
const header = input.slice(0, headerEnd).toString("ascii");
|
|
const match = /Content-Length:\s*(\d+)/i.exec(header);
|
|
if (!match) {
|
|
input = input.slice(headerEnd + separatorLength);
|
|
continue;
|
|
}
|
|
|
|
const length = Number(match[1]);
|
|
const start = headerEnd + separatorLength;
|
|
const end = start + length;
|
|
if (input.length < end) return;
|
|
|
|
const body = input.slice(start, end).toString("utf8");
|
|
input = input.slice(end);
|
|
|
|
log(`message ${body}`);
|
|
handle(JSON.parse(body)).catch((error) => {
|
|
respond(JSON.parse(body).id, null, {
|
|
code: -32603,
|
|
message: error && error.message ? error.message : String(error),
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
async function handle(message) {
|
|
if (message.id === undefined) return;
|
|
log(`handle ${message.method}`);
|
|
|
|
if (message.method === "initialize") {
|
|
respond(message.id, {
|
|
protocolVersion: message.params?.protocolVersion || "2025-03-26",
|
|
capabilities: { tools: { listChanged: false } },
|
|
serverInfo: { name: "996 Client GUI Proxy", version: "1.0.0" },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (message.method === "tools/list") {
|
|
respond(message.id, { tools });
|
|
return;
|
|
}
|
|
|
|
if (message.method === "tools/call") {
|
|
const name = message.params?.name;
|
|
const args = message.params?.arguments || {};
|
|
const result = await callRemote(name, args);
|
|
respond(message.id, { content: [{ type: "text", text: stringifyResult(result) }] });
|
|
return;
|
|
}
|
|
|
|
respond(message.id, null, { code: -32601, message: `Unknown method: ${message.method}` });
|
|
}
|
|
|
|
async function callRemote(name, args) {
|
|
if (!URL || !AUTH) throw new Error("MCP_996_GUI_URL or MCP_996_GUI_AUTH is missing");
|
|
|
|
const init = await post(null, {
|
|
jsonrpc: "2.0",
|
|
id: 1,
|
|
method: "initialize",
|
|
params: {
|
|
protocolVersion: "2025-03-26",
|
|
capabilities: {},
|
|
clientInfo: { name: "codex-996-gui-proxy", version: "1.0.0" },
|
|
},
|
|
});
|
|
|
|
const session = init.session;
|
|
await post(session, { jsonrpc: "2.0", method: "notifications/initialized", params: {} });
|
|
|
|
const response = await post(session, {
|
|
jsonrpc: "2.0",
|
|
id: 2,
|
|
method: "tools/call",
|
|
params: { name, arguments: args },
|
|
});
|
|
return response.data?.result ?? response.data;
|
|
}
|
|
|
|
async function post(session, payload) {
|
|
const headers = {
|
|
Authorization: AUTH,
|
|
Accept: "application/json, text/event-stream",
|
|
"Content-Type": "application/json",
|
|
};
|
|
if (session) headers["mcp-session-id"] = session;
|
|
|
|
const response = await fetch(URL, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
const text = await response.text();
|
|
if (!response.ok) throw new Error(`Remote MCP HTTP ${response.status}: ${text}`);
|
|
|
|
return {
|
|
session: response.headers.get("mcp-session-id"),
|
|
data: parseMcpBody(text),
|
|
};
|
|
}
|
|
|
|
function parseMcpBody(text) {
|
|
if (!text || !text.trim()) return null;
|
|
const dataLine = text
|
|
.split(/\r?\n/)
|
|
.find((line) => line.startsWith("data:"));
|
|
return JSON.parse(dataLine ? dataLine.slice(5).trim() : text);
|
|
}
|
|
|
|
function stringifyResult(result) {
|
|
if (typeof result === "string") return result;
|
|
return JSON.stringify(result, null, 2);
|
|
}
|
|
|
|
function respond(id, result, error) {
|
|
const payload = error
|
|
? { jsonrpc: "2.0", id, error }
|
|
: { jsonrpc: "2.0", id, result };
|
|
const body = JSON.stringify(payload);
|
|
log(`respond id=${id} bytes=${Buffer.byteLength(body, "utf8")} error=${Boolean(error)}`);
|
|
process.stdout.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
|
|
}
|