Hydra Engine

Clients

Every head names itself, and leaves cleanly.

A client is any process holding a connection to the daemon: an agent harness, an MCP server, a script, a test. The daemon doesn't care which. It cares about names, because names are how everyone else learns who holds what.

Name yourself

hello carries client (your tool) and harness (the agent running it). status lists both, and a busy or conflict answer names whoever is in the way:

json
{"id": 9, "ok": false, "error": {"code": "busy",  "message": "...",  "details": {"holder": "client 3 (agent-b, claude-code) script Export.java",              "holderInfo": {"client": 3, "name": "agent-b", "harness": "claude-code",                             "command": "script Export.java", "heldMs": 41200},              "suggest": "retry"}}}

Names are free-form. Use the harness's usual name, such as claude-code, codex, opencode or qoder, so people reading status know what they're looking at.

One connection, several agents

An MCP server serves several agents over one connection. Any request may carry its own client and harness, which replace the connection's names for that request in holders, status, the import queue and cancel.

python
1agent_a = s.labelled(client="agent-a", harness="claude-code")2agent_a.call("decompile", address="0x1000")3agent_a.cancel_all()          # cancels agent-a's requests only

Labels tell callers apart and scope cancellation. They aren't access control: anyone with the token can send any label, and ownership stays with the connection.

Ordering

Without parallel, one client's requests run in the order it sent them; different clients always run in parallel. Send parallel: true when a connection carries independent callers, or when you want to cancel or ask for status while a long request runs.

Other languages

The Python client is a convenience, not a requirement. Any language that can open a TCP socket can be a head:

  1. Run python ghidra_session.py ensure once, so a daemon is up.
  2. Read default.json in the state directory: %LOCALAPPDATA%\GhidraSession on Windows, ~/Library/Application Support/GhidraSession on macOS, ~/.local/state/ghidra-session on Linux.
  3. Connect to its loopback port and send JSON lines. The first must be hello with the file's token.
  4. Refuse to continue if the answer's protocol isn't 2, or if your client version is older than its minClient.
json
{"id": 1, "cmd": "hello", "client": "my-tool", "harness": "codex", "clientVersion": "1.0.0", "token": "<from default.json>"}{"id": 1, "ok": true, "ms": 1, "result": {"protocol": 2, "minClient": "1.0.0", "hydraVersion": "12.3-hydra.2", "ghidraVersion": "12.3", "installId": "...", ...}}

Leaving

Closing the connection is the whole goodbye. Your requests are cancelled, uncommitted changes rolled back, unsaved changes to programs you owned discarded (and logged), and those programs return to read-only for everyone else. A crash is handled the same way, because the OS resets the socket.

Read-only projects you opened stay warm for the next client. A writable project closes, releasing its lock, when its last user leaves.