magi-code 0.97.5

Repository-aware CLI coding agent for terminal work
Documentation
//! Inventory-only MCP endpoint for Claude CLI. Never executes host tools.
use anyhow::{Context, Result, bail};
use serde_json::{Value, json};
use std::io::{self, BufRead, Read, Write};

const MAX_INERT_MCP_BYTES: usize = 1024 * 1024;
const JSON_RPC_METHOD_NOT_FOUND: i64 = -32601;

pub(crate) fn serve_inventory(path: &std::path::Path) -> Result<()> {
    let source = std::fs::read(path).context("reading Claude tool inventory")?;
    if source.len() > MAX_INERT_MCP_BYTES {
        bail!("Claude tool inventory too large");
    }
    let tools: Value = serde_json::from_slice(&source).context("parsing Claude tool inventory")?;
    let tools = tools
        .as_array()
        .context("Claude tool inventory must be a list")?;
    let stdin = io::stdin();
    let mut input = stdin.lock();
    let mut output = io::stdout().lock();
    let mut line = Vec::new();
    loop {
        line.clear();
        let count = (&mut input)
            .take(MAX_INERT_MCP_BYTES as u64 + 1)
            .read_until(b'\n', &mut line)
            .context("reading Claude MCP request")?;
        if count == 0 {
            return Ok(());
        }
        if count > MAX_INERT_MCP_BYTES {
            bail!("Claude MCP request too large");
        }
        // A final request without a trailing newline is still a complete JSON value at EOF.
        let request: Value = serde_json::from_slice(&line).context("invalid Claude MCP request")?;
        let Some(id) = request.get("id") else {
            continue;
        };
        let message =
            response_for_request(id, request.get("method").and_then(Value::as_str), tools);
        serde_json::to_writer(&mut output, &message).context("writing Claude MCP response")?;
        output
            .write_all(b"\n")
            .and_then(|()| output.flush())
            .context("writing Claude MCP response")?;
    }
}

/// Builds the JSON-RPC response for one request; unknown methods get a method-not-found error.
fn response_for_request(id: &Value, method: Option<&str>, tools: &[Value]) -> Value {
    let result = match method {
        Some("initialize") => {
            json!({"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"magi-inert-inventory","version":"1"}})
        }
        Some("tools/list") => json!({"tools": tools}),
        Some("tools/call") => {
            json!({"isError":true,"content":[{"type":"text","text":"Denied: only magi-code executes tools."}]})
        }
        Some("ping") => json!({}),
        _ => {
            return json!({
                "jsonrpc": "2.0",
                "id": id,
                "error": {"code": JSON_RPC_METHOD_NOT_FOUND, "message": "method not found"},
            });
        }
    };
    json!({"jsonrpc": "2.0", "id": id, "result": result})
}