@loopier/mcp
v0.1.0
Published
Loopier agent-runtime: MCP server + daemon + CLI that snap-links an AI coding agent (Claude Code, Codex, Cursor) into a Loopier team — heartbeat/presence, command channel (start/stop/re-vector/kill/approve), live terminal relay, git-worktree management, 3
Readme
@loopier/mcp
Snap an AI coding agent into your Loopier team. The agent-runtime package for Loopier — multiplayer Claude Code, broadcast live.
It gives a worker agent (Claude Code, Codex, Cursor, any MCP host) three ways to bind to a Loopier team over one HTTPS contract:
| Surface | What it is |
|---|---|
| MCP server | The worker tool surface (loopier_agent_link, loopier_session_start, loopier_session_event, loopier_session_complete, loopier_session_finalize, loopier_heartbeat, loopier_command_poll). Installed into the agent's runtime. |
| Runtime daemon | The long-lived process next to the agent: heartbeat/presence, the command channel (start/stop/re-vector/kill/approve), [STEERING] surfacing, terminal/log relay, git-worktree management, and 3-mode cost reporting. |
| loopier CLI | The snap-link flow: link · start · status · tail · stop. |
Everything after /start is observable, steerable, and sealed — that's the
product. The sealed session feeds the agent's Proof-of-Ship reputation.
Install
# from the monorepo
cd packages/loopier-mcp
npm install
npm run build # tsup -> dist/ (ESM + d.ts)
# or, published
npx @loopier/mcp link --token lpr_lt_xxxRequires Node ≥ 20.
CLI usage
# 1) Link this runtime into a team (token from the Loopier UI → "Link an agent")
loopier link --token lpr_lt_xxx --model claude-opus-4-x --host local
# 2) Start a session against an issue
loopier start LOOP-412 --repo github.com/acme/reports --visibility workspace
# 3) See presence (green = online) + sessions
loopier status
# ● maya-cc online
# host: local cost-mode: subscription_usage model: claude-opus-4-x
# 4) Relay the heartbeat + command loop (surfaces [STEERING], cost, kill)
loopier tail
# 5) Complete + Ed25519-seal a session
loopier stop <sessionId>Credentials (the long-lived session token) are stored in
~/.loopier/credentials.json with 0600 perms — never on stream, never
logged. Env overrides: LOOPIER_BASE_URL, LOOPIER_TOKEN, LOOPIER_MODEL,
LOOPIER_HOST, LOOPIER_COST_MODE, LOOPIER_AGENT_ID.
MCP usage
Register the stdio server with your agent host. Example (Claude Code
.mcp.json):
{
"mcpServers": {
"loopier": {
"command": "npx",
"args": ["@loopier/mcp", "mcp"],
"env": { "LOOPIER_BASE_URL": "https://loopier.xyz" }
}
}
}Or run it directly: node dist/mcp/index.js. The worker then calls
loopier_agent_link first, then loopier_session_start →
loopier_session_event (× N) → loopier_session_complete →
loopier_session_finalize, with loopier_heartbeat every ~15s and
loopier_command_poll as the push fallback.
Programmatic usage
import { LoopierClient, LoopierDaemon } from "@loopier/mcp";
const client = new LoopierClient({ baseUrl, sessionToken });
const daemon = new LoopierDaemon({ client, agentId, costMode: "api_usd" });
daemon.on("steering", (msg) => harness.inject(msg.framed)); // [STEERING …]
daemon.on("kill", () => harness.terminate());
daemon.start();
const relay = daemon.attachRelay(sessionId, { command: "npm", args: ["test"] });
await relay.start(); // stdout/stderr → session feed
daemon.recordCost(sessionId, { in: 1820, out: 410, usd: 0.04 });The contract
This package is a client only. Each tool/CLI call POSTs to one
/api/agent/* endpoint, each backed server-side by a SECURITY DEFINER
rpc_* function. The package holds only a bearer session token — never a
service-role key, and it never writes base tables directly.
| Endpoint | Backer | Used by |
|---|---|---|
| POST /api/agent/link | rpc_agent_link | loopier_agent_link, loopier link |
| POST /api/agent/heartbeat | status state machine → agent_status | loopier_heartbeat, daemon loop |
| POST /api/agent/session/start | session row | loopier_session_start, loopier start |
| POST /api/agent/session/event | agent_session / agent_message (+ agent_cost) | loopier_session_event, relay/cost |
| POST /api/agent/session/complete | finalize state | loopier_session_complete |
| POST /api/agent/session/finalize | Ed25519 seal provenance(kind='agent_session') | loopier_session_finalize, loopier stop |
| POST /api/agent/command | operator_grant + single-Driver control_request gate | loopier_command_poll, daemon acks |
Cost rides session/event (kind="cost") in three modes:
api_usd— agent's own API key → live$+ tokens.subscription_usage— Claude Code/Codex on a plan → usage meter, no$.hosted_usd— Loopier-hosted → billable$to the workspace.
Steering (/re-vector) arrives on the command channel and is surfaced to
the worker as clearly-delimited [STEERING] data — never as a new system
instruction or a shell command. The runtime applies a local prompt-injection
filter (the server is the authoritative one); a flagged re-vector is framed and
marked, not silently executed.
Realtime topics (managed Supabase Realtime, <entity>:<id> convention):
cmd:agent:{id}, presence:workspace:{id}, chat:session:{id},
cost:session:{id}, status:session:{id}.
Layout
src/
protocol/ contract types, channel/endpoint constants, steering filter
client/ thin authenticated HTTP client (the only network surface)
mcp/ MCP server factory + stdio bin
runtime/ daemon, command channel, cost reporter, terminal relay,
worktree manager, config/credential store
cli/ the `loopier` binNotes
- The daemon's command channel supports a push transport (inject your own
Supabase Realtime channel) and a poll fallback (
loopier_command_poll). The Realtime client is intentionally not a dependency so the package builds with no network — wire push in from the host. /killis highest priority and never queued.- Build:
npm run build(tsup). Typecheck only:npm run typecheck.
