Proxy NDC agent MCP through AI workspace hub

This commit is contained in:
Codex 2026-06-01 13:24:22 +03:00
parent 4e57287b5a
commit 8154890ac9
1 changed files with 40 additions and 0 deletions

View File

@ -60,6 +60,8 @@ app.post("/api/ai-workspace/hub/v1/agents/:pairingCode/dispatch", requireInterna
res.json({ ok: true, requestId: dispatch.requestId }); res.json({ ok: true, requestId: dispatch.requestId });
}); });
app.use("/api/ai-workspace/hub/v1/ndc-agent-mcp/:pairingCode", asyncRoute(proxyNdcAgentMcp));
app.use((error, _req, res, _next) => { app.use((error, _req, res, _next) => {
const status = Number(error?.status || 500); const status = Number(error?.status || 500);
res.status(status >= 400 && status < 600 ? status : 500).json({ res.status(status >= 400 && status < 600 ? status : 500).json({
@ -218,6 +220,42 @@ function dispatchAgent(pairingCodeRaw, command, payload = {}, timeoutMs = 30000,
return { requestId }; return { requestId };
} }
async function proxyNdcAgentMcp(req, res) {
if (!config.engineInternalUrl || !config.engineInternalAccessToken) {
res.status(503).json({ ok: false, error: "engine_proxy_not_configured" });
return;
}
const pairingCode = cleanPairingCode(req.params.pairingCode);
const agent = agentsByCode.get(pairingCode);
if (!agent || !isAgentOnline(agent)) {
res.status(503).json({ ok: false, error: "bridge_agent_offline" });
return;
}
const marker = `/api/ai-workspace/hub/v1/ndc-agent-mcp/${encodeURIComponent(req.params.pairingCode)}`;
const suffix = String(req.originalUrl || "").startsWith(marker)
? String(req.originalUrl || "").slice(marker.length)
: String(req.url || "");
const targetUrl = `${config.engineInternalUrl.replace(/\/+$/, "")}/api/ndc-agent-mcp${suffix || ""}`;
const method = String(req.method || "GET").toUpperCase();
const hasBody = !["GET", "HEAD"].includes(method);
const upstream = await fetch(targetUrl, {
method,
redirect: "manual",
headers: {
Accept: "application/json",
Authorization: `Bearer ${config.engineInternalAccessToken}`,
...(hasBody ? { "Content-Type": "application/json" } : {}),
},
...(hasBody ? { body: JSON.stringify(req.body || {}) } : {}),
});
const contentType = upstream.headers.get("content-type") || "application/json; charset=utf-8";
const text = await upstream.text();
res.status(upstream.status);
res.setHeader("content-type", contentType);
res.send(text);
}
function startAgentRequest(pairingCodeRaw, command, payload = {}, timeoutMs = 30000, options = {}) { function startAgentRequest(pairingCodeRaw, command, payload = {}, timeoutMs = 30000, options = {}) {
const pairingCode = cleanPairingCode(pairingCodeRaw); const pairingCode = cleanPairingCode(pairingCodeRaw);
const agent = agentsByCode.get(pairingCode); const agent = agentsByCode.get(pairingCode);
@ -432,6 +470,8 @@ function readConfig() {
port: Number(process.env.PORT || 18081), port: Number(process.env.PORT || 18081),
wsPath: process.env.AI_WORKSPACE_HUB_WS_PATH || DEFAULT_WS_PATH, wsPath: process.env.AI_WORKSPACE_HUB_WS_PATH || DEFAULT_WS_PATH,
internalAccessTokens, internalAccessTokens,
engineInternalUrl: cleanString(process.env.NODEDC_ENGINE_INTERNAL_URL, 1000).replace(/\/+$/, ""),
engineInternalAccessToken: cleanString(process.env.NODEDC_INTERNAL_ACCESS_TOKEN, 1000),
}; };
} }