14 KiB
Connect any agent
Nederlands: connect-any-agent.nl.md
ReversePort works the same for every agent, whoever made it and however large or small the model. Pick the door that fits your agent; all of them reach the same tools.
| Your agent | Door | One command |
|---|---|---|
| Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and other installed agents | MCP (stdio), configured for you | npx -y reverseport@latest setup |
| Any MCP client, configured by hand (Windsurf, Cline, Roo Code, Continue, Zed, Goose, LM Studio, VS Code…) | MCP (stdio) | reverseport connect <client> prints the exact snippet |
Clients that take an MCP URL (n8n, LangChain MultiServerMCPClient, Open WebUI via mcpo…) |
MCP over HTTP | reverseport serve → http://127.0.0.1:32147/mcp |
| OpenAI Chat Completions / Responses, OpenAI-compatible servers (vLLM, Ollama, LM Studio, llama.cpp) | Function calling | GET /api/agent/tools?format=openai-strict (or responses) |
| Anthropic Messages API | Tool use | GET /api/agent/tools?format=anthropic |
| Gemini API | Function declarations | GET /api/agent/tools?format=gemini |
| LangChain/LlamaIndex OpenAPI tools, Semantic Kernel, CrewAI, AutoGen, n8n HTTP | OpenAPI 3.1 | GET /api/openapi.json |
| Agents that can only run commands | CLI with JSON | reverseport describe <tool>, reverseport <tool> --args '{…}' |
reverseport connect without a client lists them all. Add --mode local to point a snippet at your checkout
instead of npx. Setup and connect both register the server under the name reverseport, so doing both never
adds it twice. Earlier releases used rea; see the server name change. In
Claude Code, use either setup or the plugin, not both: the plugin brings its own copy of the server, listed as
plugin_reverseport_reverseport, so with both you run two servers.
The Claude Code plugin installed from GitHub has no built deep-analysis engine; it starts with ReversePort's own
tools. Turn on the plugin's Deep-analysis engine setting (/plugin → reverseport → configure) to have it run
the published engine through npx (downloaded once); REVERSEPORT_ENGINE_FETCH=npx does the same. The plugin's
other settings choose the tool list size, the workspace folder and your Ghidra folder, so nobody edits
settings.json by hand.
Function calling over HTTP
reverseport serve # http://127.0.0.1:32147 (REVERSEPORT_PORT to change)
curl -s "http://127.0.0.1:32147/api/agent/tools?format=openai-strict" > tools.json
curl -s -X POST http://127.0.0.1:32147/api/agent/call \
-H 'content-type: application/json' \
-d '{"name":"re_scan","arguments":{"path":"/absolute/path/to/game"}}'
A complete loop with the OpenAI Python SDK (works the same against Ollama, vLLM or LM Studio by changing
base_url):
import json, requests
from openai import OpenAI
api = "http://127.0.0.1:32147"
tools = requests.get(f"{api}/api/agent/tools", params={"format": "openai-strict"}).json()
client = OpenAI()
messages = [{"role": "user", "content": "What engine is /games/demo made with?"}]
while True:
reply = client.chat.completions.create(model="gpt-4.1", messages=messages, tools=tools).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
result = requests.post(f"{api}/api/agent/call", json={"name": call.function.name, "arguments": json.loads(call.function.arguments)}).json()
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
print(reply.content)
LangChain, through MCP (pip install langchain-mcp-adapters):
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({"reverseport": {"command": "npx", "args": ["-y", "reverseport@latest", "mcp"], "transport": "stdio"}})
tools = await client.get_tools()
Formats: mcp, openai, openai-strict, responses, anthropic, gemini, json-schema. In the strict formats
every argument is required and optional ones accept null; null means "not given". Every door also accepts
numbers and booleans sent as strings ("20", "true") and objects sent as JSON text. For ReversePort's own tools,
out-of-range numbers are clamped and the result says so under adjusted. In openai-strict and responses the
tools are strict: a free-form object (such as arguments in run_batch) is sent as a JSON string, which ReversePort
reads back. Only a schema that still cannot be strict (a property with no type at all) goes out with strict: false.
Unions keep every option: a field shared by several shapes keeps all its allowed values, and the description says
which fields belong together. Limits a format does not take (minLength, multipleOf, …) move into the description.
Function descriptions stay within 1024 characters; OpenAPI operation descriptions within 300 (the GPT Actions limit),
cut at a sentence — /api/openapi.json?descriptions=full gives the whole text.
The server answers only on localhost. Pages served from localhost may call it from a browser (CORS). Set
REVERSEPORT_TOKEN to require Authorization: Bearer <token>. To reach it from Docker or through a tunnel (hosted
agents such as GPT Actions need a public HTTPS URL), set REVERSEPORT_TOKEN, REVERSEPORT_HOST=0.0.0.0 and
REVERSEPORT_ALLOWED_HOSTS=<host names>; without a token it refuses to listen beyond this machine.
MCP versions and long-running calls
The server speaks both MCP eras on the same connection. Clients that open with initialize get the classic
session (2025-11-25 and older). Clients on MCP 2026-07-28 send server/discover or any request with
io.modelcontextprotocol/protocolVersion in _meta and are served without a handshake: every result carries
resultType, list results carry ttlMs and cacheScope, and tools/list keeps a fixed order so prompt caches
stay warm.
A 2026-07-28 client that declares the io.modelcontextprotocol/tasks extension gets long calls as tasks: a
tools/call still running after 2.5 seconds returns a task id instead of blocking, and tasks/get shows its status
(progress from the engine becomes statusMessage) and, at the end, the normal tool result. tasks/cancel stops
it. Quick calls answer directly, so nothing gets slower. Task ids last six hours and do not survive a restart of
the server. REVERSEPORT_TASK_AFTER_MS changes the 2.5 seconds. Claude Code uses tasks in its CLI from v2.1.304.
subscriptions/listen (stdio) delivers notifications/tools/list_changed when the engine starts, stops or the
tool profile changes. Over HTTP the endpoint answers with JSON only, so a stream cannot stay open there; call
tools/list again instead.
Fit the tool list to the agent
Every agent can reach every tool. What changes is how much is listed up front:
| Profile | Lists | Tools | For |
|---|---|---|---|
compact |
9 ReversePort tools for recon and porting + 6 engine essentials, one-line descriptions | 19 tools (11 without the engine) | small or older models, small context windows |
standard (default) |
all 33 ReversePort tools + 20 core engine tools | 57 tools (35 without the engine) | most agents |
full |
every tool | over 200 tools | large-context agents whose client allows that many |
The counts include set_tool_profile and run_batch, and with the engine engine_tools and engine_call.
reverseport tools --profile compact prints the list itself.
Some clients cap the number of tools (Cursor has used about 40, VS Code and OpenAI 128, Windsurf 100). Pick
compact for a tight cap. Set the profile with REVERSEPORT_PROFILE, or let the agent switch at any time with
set_tool_profile. A tool left out of the list still works when called by name; with the engine,
engine_tools {query} finds any other engine tool and engine_call {tool, arguments} calls it.
REVERSEPORT_TOOLS=all|port|name,glob* picks the listed tools exactly.
Over HTTP every MCP session keeps its own profile and its own repeat guard, so one client never changes what another
sees. Requests without a session remember nothing; ask for a profile in the address instead: /mcp?profile=compact,
/api/agent/tools?profile=compact or /api/openapi.json?profile=compact. A session that ended (or never existed)
gets HTTP 404 with JSON-RPC error -32001: send initialize again.
Strict schema validators (Gemini, OpenAI strict mode, some local-model servers) reject some JSON-Schema features the
deep-analysis engine uses ($ref, unions, dependent fields). REVERSEPORT_SCHEMA=portable lists every schema in a
form they accept (the compact profile always does), and the HTTP formats openai-strict, responses and gemini
always do. The server still validates the full schema.
Big results do not flood the context
Over MCP and HTTP, a tool result larger than 256 KB is saved whole to ~/.reverseport/tool-output/ (or under
REVERSEPORT_HOME). The agent gets a shortened result: the longest lists keep their first items, long text keeps its
first lines, and outputTruncated (or a closing line for plain text) says what was shortened and where the full
result is. Over MCP the result also carries a resource_link to reverseport://tool-output/<file>, so clients
without file access read the complete result with resources/read. Saved results are removed after 7 days.
REVERSEPORT_MAX_OUTPUT_BYTES sets another limit (at least 4096 bytes), or off. The CLI always prints the full
result.
Most results stay far below that limit: re_scan, re_graph and re_symbols return short lists by default, with
counts (orphanCount, cycleCount, functionCount…) for what was left out, and limit/match for more.
Several steps in one call
run_batch runs up to 20 tool calls in order and answers once. select keeps only the fields an agent needs from
each result, which saves context and round trips:
{
"calls": [
{
"tool": "re_scan",
"arguments": { "path": "/games/demo" },
"select": ["primary.id", "entryPoints[*].file"]
},
{
"tool": "re_graph",
"arguments": { "path": "/games/demo" },
"select": ["cycles", "portOrder"]
},
{
"tool": "port_map",
"arguments": { "from": "godot", "to": "unity" },
"select": ["conventions", "hint"]
}
]
}
It stops at the first error and says which call to fix and where to resume; continueOnError: true runs the rest.
A select path that finds nothing is listed under unresolved for that step, with the reason and the path that works
("primry.id" → "primary.id", "entryPoints.file" → "entryPoints[*].file").
Engine tools work too. In strict function-calling formats, arguments is a JSON object written as a string.
Errors look the same everywhere
Successful workflow results include a next field wherever there is an obvious next step (graphs, truncated
results and every porting step), with the exact call the agent can make next.
Tool calls on every door return the same error object, so an agent can act on it without guessing:
{
"error": {
"code": "NOT_FOUND",
"category": "invalid_input",
"retryable": false,
"message": "project folder not found: /games/demo",
"remediation": {
"action": "Check the path; absolute paths are safest. Ask the user where the file is if you cannot find it."
}
}
}
ReversePort's codes are UPPERCASE (NOT_FOUND, INVALID_ARGUMENT, ENGINE_STOPPED…); the deep-analysis engine's
are lowercase (provider_unavailable, resource_constraint…). Both use the same categories and carry retryable
and remediation. An unknown tool name is a tool result with UNKNOWN_TOOL and the closest name. On the CLI, usage
errors exit with 2 and other failures with 1; the error is JSON on stderr (at an interactive terminal the
reverseport command prints plain text instead). Over HTTP the status follows the code: 400 for input, 404 for an
unknown tool, 503 when the engine or a public source is unavailable, 500 for unexpected failures.
When the exact same call fails twice in one session (three times for errors marked retryable), the error comes back
with repeated and retryable: false and says plainly not to send it unchanged again: change the input, use another
tool, or tell the user what is blocking. A success of that same call clears it; other calls in between do not.
The engine states are distinct, so the next step is always right: ENGINE_UNAVAILABLE (not built — install it),
ENGINE_OFF (turned off — unset REVERSEPORT_ENGINE), ENGINE_STOPPED (crashed — call again once; it restarts),
ENGINE_RESTART_LIMIT (kept crashing — tell the user).
Tell us what got in the way
Agents can leave a note with share_feedback at any time: what helped, what was confusing or slow, what would
help next time. Notes stay on the user's computer (reverseport feedback shows them) until the user shares them.
They shape the next release.