UNPKG

@sellable/install

Version:

One-command installer for Sellable MCP in Claude Code, Codex, and Hermes

54 lines (50 loc) • 1.95 kB
// Claude Code's only MCP server for a tool-lane run. It starts the profile's // own `sellable` server (the context proxy, with exactly its configured // environment) and adds this run's endpoint session to every tools/list and // tools/call. The proxy then loads the run's signed turn descriptor, lists // only the run's allowed tools and stamps the run identity on each call; the // backend checks it again. Nothing in this file grants anything. import { spawn } from "node:child_process"; import { readFileSync } from "node:fs"; import { createInterface } from "node:readline"; import { fileURLToPath } from "node:url"; export function tagEndpointSession(line, sessionId) { let message; try { message = JSON.parse(line); } catch { return line; } if ( !message || typeof message !== "object" || !["tools/list", "tools/call"].includes(message.method) ) return line; const params = message.params && typeof message.params === "object" ? message.params : {}; // The proxy accepts exactly this shape and nothing else in _meta. return JSON.stringify({ ...message, params: { ...params, _meta: { sellableEndpoint: { sessionId } } }, }); } function main(specPath) { const spec = JSON.parse(readFileSync(specPath, "utf8")); if (!/^sellable_endpoint_[a-f0-9]{32}$/.test(spec.sessionId ?? "")) throw new Error("claude_runs_mcp_session_rejected"); const child = spawn(spec.command, spec.args, { stdio: ["pipe", "inherit", "ignore"], env: spec.env, }); child.once("exit", (code) => process.exit(code ?? 1)); child.stdin.on("error", () => {}); const lines = createInterface({ input: process.stdin, crlfDelay: Infinity }); lines.on("line", (line) => child.stdin.write(`${tagEndpointSession(line, spec.sessionId)}\n`) ); lines.on("close", () => child.stdin.end()); } if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { main(process.argv[2]); }