Fix Bifrost intake MCP mounting

This commit is contained in:
Meta Vens 2026-05-19 17:49:07 +01:00
commit 25fcf46da5
5 changed files with 70 additions and 7 deletions

View file

@ -64,6 +64,7 @@ The `snapshot` command writes a MessagePack-encoded `cultnet.snapshot_response_r
Tools:
- `get_intake_context`
- `enqueue_update_request`
- `list_update_requests`
- `claim_update_request`
@ -74,6 +75,8 @@ Tools:
The plugin is listed in `.agents/plugins/marketplace.json` as `bifrost-intake`.
`get_intake_context` is the normal Codex-facing entry point. It claims the next matching request for the current repo and returns a context packet immediately. If nothing is queued, it returns a direct no-work message so the agent can stop worrying about intake and continue the live turn.
## Next Integration Points
- Teach the VoidBot consensus feeder to enqueue Bifrost update requests after it writes a packet.

View file

@ -33,7 +33,7 @@
"privacyPolicyURL": "https://github.com/GameCult/Bifrost",
"termsOfServiceURL": "https://github.com/GameCult/Bifrost",
"defaultPrompt": [
"Claim the next Bifrost update request for this repo.",
"Get Bifrost intake context for this repo.",
"Enqueue this consensus packet for a repo Face.",
"List queued Bifrost agent requests."
],

View file

@ -2,7 +2,10 @@
"mcpServers": {
"bifrost-intake": {
"command": "node",
"args": ["./scripts/mcp-server.mjs"],
"args": ["E:/Projects/Bifrost/plugins/bifrost-intake/scripts/mcp-server.mjs"],
"env": {
"BIFROST_ROOT": "E:/Projects/Bifrost"
},
"note": "Local Bifrost intake MCP server. Wraps Bifrost's CultCache/CultNet agent transport CLI."
}
}

View file

@ -1,14 +1,30 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const pluginRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const bifrostRoot = resolve(pluginRoot, "..", "..");
const bifrostRoot = resolveBifrostRoot();
const transportCli = resolve(bifrostRoot, "tools", "agent-transport.mjs");
const serverInfo = { name: "bifrost-intake", version: "0.1.0" };
const tools = [
{
name: "get_intake_context",
description: "Claim the next matching Bifrost request and return a Codex-ready context packet, or say there is no queued intake work.",
inputSchema: {
type: "object",
additionalProperties: false,
required: ["repo"],
properties: {
repo: { type: "string", description: "Current repository short name, such as AetheriaLore." },
agent: { type: "string", description: "Optional current Face identity, such as nibu." },
claimedBy: { type: "string" },
store: { type: "string" }
}
}
},
{
name: "enqueue_update_request",
description: "Enqueue a Bifrost agent update request in the CultCache-backed intake store.",
@ -190,6 +206,8 @@ async function callTool(params) {
const args = params.arguments ?? {};
switch (name) {
case "get_intake_context":
return textToolResult(await getIntakeContext(args));
case "enqueue_update_request":
return jsonToolResult(await runTransport([
"enqueue",
@ -251,6 +269,26 @@ async function callTool(params) {
}
}
async function getIntakeContext(args) {
const repo = requireString(args.repo, "repo");
const claimed = await runTransport([
"claim",
"--repo", repo,
...optionalArg("--agent", args.agent),
...optionalArg("--claimed-by", args.claimedBy ?? args.agent),
...storeArg(args.store),
]);
if (!claimed) {
const face = typeof args.agent === "string" && args.agent.trim().length > 0
? ` for ${args.agent.trim()}`
: "";
return `No Bifrost intake requests are queued for ${repo}${face}. Do not stall on intake for this turn; continue with the user's direct request or the repo's normal next action.`;
}
return formatClaimedRequest(claimed);
}
async function requireRequestById(args) {
const id = requireString(args.id, "id");
const requests = await runTransport(["list", ...storeArg(args.store)]);
@ -364,3 +402,22 @@ function sendMessage(message) {
process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`);
process.stdout.write(body);
}
function resolveBifrostRoot() {
const candidates = [
process.env.BIFROST_ROOT,
resolve(pluginRoot, "..", ".."),
"E:/Projects/Bifrost",
].filter((candidate) => typeof candidate === "string" && candidate.trim().length > 0);
for (const candidate of candidates) {
const root = resolve(candidate);
if (existsSync(resolve(root, "tools", "agent-transport.mjs"))) {
return root;
}
}
throw new Error(
"Could not find Bifrost root. Set BIFROST_ROOT to the repository that contains tools/agent-transport.mjs.",
);
}

View file

@ -11,9 +11,9 @@ Use these tools when a user asks to feed a consensus packet to a repo Face, clai
Default flow:
1. Use `list_update_requests` to inspect queued work for a repo or Face.
2. Use `claim_update_request` with the repo name before injecting the request into Codex.
3. Use `format_claimed_request` when you need a compact prompt packet for a claimed request.
4. Use `close_update_request` when the work is completed or cancelled.
1. Use `get_intake_context` at the start of a repo-agent turn when the agent should check Bifrost intake.
2. If it returns a request packet, treat that packet as live context for the turn.
3. If it says no request is queued, do not ask the user what to do because of intake; continue with the user's direct request or the repo's normal next action.
4. Use `close_update_request` when the claimed work is completed or cancelled.
Do not store a parallel queue in VoidBot or Codex session memory. If the request is intended to persist, put it in Bifrost intake.