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");
}
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")?;
}
}
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})
}