MCP and A2A
How an LLM client calls ifURI URI routes as tools.
> In one sentence: ifURI turns your project's actions into URI routes, the > urirun runtime compiles them into a registry, > and ifuri-app urirun-mcp *projects* that registry as MCP > tools (and an A2A agent card) — so a client like Claude can discover and call > them, while every call still passes the urirun policy gate.
Sites: ifuri.com · docs.ifuri.com · examples.ifuri.com · get.ifuri.com · connect.ifuri.com · logo.ifuri.com · roadmap.ifuri.com Repos: if-uri/app · tellmesh/urirun
The contract
A route is a stable address scheme://target/resource/operation. A binding describes how to run it (adapter + JSON-Schema input). Many bindings compile into one registry. The same registry is what the CLI runs, what the HTTP/MCP server serves, and what flows call. See docs: registry-and-bindings and transports.
Pipeline (what happens, step by step)
project artifacts ──scan──▶ bindings.v2.json ──compile──▶ registry.json
│
┌──────────────────────────┼───────────────────────────┐
▼ ▼ ▼
MCP tools/list A2A agent card tools/call
(to_mcp_tools) (to_a2a_card) (call_tool)
│ │
▼ ▼
Claude / MCP client ───── JSON-RPC over stdio ─────▶ urirun.v2.run
│
resolve_route → validate_input
→ adapter + policy gate → result
1. Declare / scan → bindings
You write bindings by hand, generate them with decorators, or scan a project.
- CLI:
ifuri-app urirun-scan ./project --registry-out generated/registry.json - App code:
scan_project()→cmd_urirun_scan() - urirun does the scanning in
urirun._scan(Dockerfile labels, package scripts, Python entry points, Makefile, shell, explicit binding files).
2. Compile → registry
urirun.v2.compile_registry()produces{version, routes, index, routeCount, …}.- The app finds the active registry via
default_urirun_registry()(envIFURI_URIRUN_REGISTRYorworkspace.urirun.registry).
3. Project → MCP tools / A2A card
This is the bridge from "URI registry" to "agent tools".
ifuri-app urirun-mcp tools→ MCPtools/list(mcp_tools()→urirun.v2_mcp.to_mcp_tools()). Each route becomes a tool whose name is sanitised bytool_name()and whoseinputSchemais the binding's JSON Schema.ifuri-app urirun-mcp card→ A2A (Agent2Agent) agent card (a2a_card()→urirun.v2_mcp.to_a2a_card()) — every route is published as an agent skill.- CLI wiring:
cmd_urirun_mcp().
Example:
ifuri-app urirun-mcp tools --registry generated/registry.json
# -> {"ok": true, "tools": [{"name": "sys_local_echo_hello", "inputSchema": {...}}, ...]}
4. Serve over stdio → a client connects
ifuri-app urirun-mcp serve --registry generated/registry.jsonstarts an MCP stdio server (serve_mcp()→urirun.v2_mcp.serve_mcp()), speaking JSON-RPC 2.0 over stdin/stdout — the transport MCP clients expect.- Add
--executeto allow real runs (otherwise it stays dry-run). A standalone equivalent ispython -m urirun.v2_mcp serve registry.json(main()).
Register it in an MCP client (e.g. Claude Desktop / Claude Code) as a command server:
{
"mcpServers": {
"ifuri": {
"command": "ifuri-app",
"args": ["urirun-mcp", "serve", "--registry", "/path/generated/registry.json"]
}
}
}
5. The client discovers → calls
1. Client sends tools/list → server returns the projected tools. 2. Client sends tools/call {name, arguments} → urirun.v2_mcp.call_tool() maps the tool name back to a URI (via build_tool_index()) and runs it.
6. Run → resolve, validate, gate, execute
call_tool (and the in-process path dispatch_local()) both end in urirun.v2.run():
resolve_route()finds the binding (URI parsed bytranslate());validate_input()checks the payload against the JSON Schema withjsonschemaDraft 2020-12;- the policy gate decides allow/deny (default-deny in execute mode); an approved run uses an allow policy (see
_load_urirun_policy()); - an adapter runs it — e.g.
run_argv_template()(safe argv, no shell) — and a result envelope{ok, decision, result}comes back.
The same registry, other surfaces
Because the contract is constant, the registry feeds more than MCP:
- CLI
ifuri-app urirun-call URI --registry … [--execute](cmd_urirun_call()). - HTTP
ifuri-app urirun-serve→/health,/routes,POST /run(serve_http()). - Flows route URIs (local packs → urirun → remote node) in
RuntimeState.run_flow()andcall_uri().
Libraries used
- urirun — the URI runtime:
v2(run/compile),v2_mcp(MCP/A2A),_registry,_scan,_runtime. - Model Context Protocol — tool discovery/call for LLM clients.
- A2A (Agent2Agent) — agent capability card (one skill per route).
- JSON-RPC 2.0 — the stdio wire format.
- jsonschema — input validation (Draft 2020-12).
- pydantic — optional schema generation from decorated functions.
Try it
pip install "git+https://github.com/tellmesh/urirun.git@v0.3.12#subdirectory=adapters/python"
ifuri-app urirun-scan ./project --registry-out generated/registry.json
ifuri-app urirun-mcp tools --registry generated/registry.json # what a client sees
ifuri-app urirun-mcp serve --registry generated/registry.json # connect Claude here
More: commands · getting-started · runnable examples.ifuri.com.