johnandersen777.bsky.social/atlibs-socialweb-computer-requester
socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons
0
0
// process-mcp: computer.socialweb.cicd.process.* over a guest agent's MCP shell, until the guest agent
// serves the process contract as plain HTTP. Each entry is one MCP tools/call of the same name to `url`
// (iroh:///<ref>/shell/mcp, webrtc:///<ref>/shell/mcp) through com.fedfork.exec.http.fetch; its
// structuredContent is the result. The arguments are the input less url and what the host adds.
// exec start read write kill list {url, ...the tool's arguments} -> the tool's result
use abi::{Call, Event, Handle, Run, entries};
use serde_json::{Map, Value, json};
const HOST_KEYS: [&str; 4] = ["url", "did", "now", "caller"];
fn tool(name: &str, run: Run<Map<String, Value>>) -> Result<Handle, String> {
Ok(match run.event {
Event::Start => {
let url = run.args.get("url").and_then(Value::as_str).ok_or("url is required: the machine's shell (iroh:///... or webrtc:///...)")?;
let arguments: Map<String, Value> = run.args.iter().filter(|(k, _)| !HOST_KEYS.contains(&k.as_str())).map(|(k, v)| (k.clone(), v.clone())).collect();
let body = json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": name, "arguments": arguments } });
let headers = json!({ "content-type": "application/json", "accept": "application/json, text/event-stream", "mcp-protocol-version": "2025-11-25" });
Handle::new().call(Call::new("mcp", "com.fedfork.exec.http.fetch").input(json!({ "method": "POST", "url": url, "headers": headers, "body": body.to_string() })))
}
Event::Result { result, .. } => {
let res = result.output()?;
let status = res["status"].as_u64().unwrap_or(0);
let text = res["body"].as_str().unwrap_or_default();
if status != 200 {
return Err(format!("{name}: HTTP {status} {}", text.chars().take(200).collect::<String>()));
}
let msg: Value = serde_json::from_str(text).map_err(|_| format!("{name}: the answer is not JSON"))?;
if let Some(e) = msg.get("error") {
return Err(format!("{name}: {}", e["message"].as_str().unwrap_or("error")));
}
let out = &msg["result"];
if out["isError"] == true {
return Err(format!("{name}: {}", out["content"][0]["text"].as_str().unwrap_or("error")));
}
let value = match out.get("structuredContent") {
Some(v) => v.clone(),
None => serde_json::from_str(out["content"][0]["text"].as_str().unwrap_or("null")).map_err(|_| format!("{name}: no result"))?,
};
Handle::new().result(value).done()
}
_ => Handle::new(),
})
}
fn exec_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
tool("exec", run)
}
fn start_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
tool("start", run)
}
fn read_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
tool("read", run)
}
fn write_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
tool("write", run)
}
fn kill_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
tool("kill", run)
}
fn list_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
tool("list", run)
}
entries!(exec => exec_handler, start => start_handler, read => read_handler, write => write_handler, kill => kill_handler, list => list_handler);