The viewer is one client of orbit-service. An agent is another. It should be able to do what the Record button does — pick a process, hook a function that is already running, capture, read the numbers, unhook, capture again — without a human clicking, and without a dump of the event ring in the tool result.
What shipped
orbit-service mcp is a subcommand of the same binary that serves the viewer. The default speaks JSON-RPC on stdin, which is the config Claude Code and Cursor paste. --http 127.0.0.1:44767 is the same tools as streamable HTTP. Both are clients of the service’s HTTP API. The capture keeps running in orbit-service when the agent disconnects.
A separate Python server, orbit-optimize-loop, exists on the loop branch. It builds a project, benches it, and can apply a patch. That is a different product. It does not arm hooks, and applying a patch is not profiling. It was not merged. An agent that wants to edit source already has its editor. This server will not do it.
The twelve tools
orbit_capabilities comes first. It says whether the service is up, and which features this operating system actually has. Then orbit_list_processes, orbit_attach, orbit_search_functions, orbit_arm_hooks, orbit_start_capture, orbit_stop_capture. After the stop: orbit_function_stats for hooked functions and manual scopes, and on Linux orbit_sampling_report and orbit_callstacks. orbit_unhook edits the set the next capture will install. A capture that is already running keeps the hooks it started with.
Results are pages, not dumps. Twenty processes, fifteen functions, fifteen hotspots, eight stacks. Times in the function table are integer nanoseconds. A search that asks to skip past 200 hits is refused: narrow the query. At most sixteen functions are armed, which is the cap the service already enforces, said out loud instead of silently truncated.
Linux, and macOS for what macOS does
Frida hooks and manual scopes work on both. Sampling, the scheduler, kernel uprobes, and auto-profiling are Linux. On macOS those tools return supported: false and a sentence that says to use orbit_function_stats. Asking to start a capture with sampling turned on does not start the capture. An empty hotspot list would look like a quiet program. It is not that.
On Linux a report can still come back with zero samples, when perf_event_paranoid is too high. That result says supported: true and samples: 0. The distinction is the point.
What it will not do
The HTTP API has no token. The MCP server does not add one. It refuses a service URL that is not loopback unless --allow-remote is set, and that flag is the warning: anyone who can reach the service can attach to processes as the user running it. --http refuses a bind that is not loopback, and a browser Origin that is not loopback. Nothing in the tool list deletes a file, rewrites source, or kills the target. The destructive operation is the one Orbit already had: a hook in a running process, removed when the capture stops.
What is proven
Unit tests drive the twelve tools against a fake service: paging, an ambiguous symbol that is not armed, a start body that contains the hook, an unhook, and a second start whose body does not. A macOS platform refuses sampling without posting a capture. An end-to-end test in the Frida workflow starts the real service, runs the Frida sample target, and goes through attach, hook, capture, stats, unhook, and a second capture where that function is gone. The manual has the Claude Code and Cursor configs to paste.