Configuration
The MCP server runs embedded in the ProtoPoke UI process, bound to the
same ProtoPokeAPI instance. AI clients connect to it over HTTP
(streamable-http) at http://<host>:<port>/mcp and share live state with
the operator: sessions, frames, rules, playbooks, the tamper queue, and
everything else visible in the TUI.
CLI Flags¶
| Flag | Default | Description |
|---|---|---|
--mcp |
off | Enable the embedded MCP server on startup |
--mcp-host HOST |
127.0.0.1 |
Bind host for the MCP HTTP endpoint |
--mcp-port PORT |
7878 |
Bind port for the MCP HTTP endpoint |
--mcp-profile PROFILE |
full |
Tool surface: full or analysis (see below) |
The same settings can be toggled at runtime from the Config tab (Enabled
switch + Host / Port inputs). They are persisted per-project in the .pp
file (see mcp.json).
Tool profiles¶
Every MCP tool's description and parameter schema is re-sent to the AI on
every turn, so the size of the exposed tool catalogue is a fixed,
per-turn token cost. The profile setting controls how big that catalogue
is:
| Profile | Tools | Use when |
|---|---|---|
full (default) |
all tools | You want the AI to drive everything: forwarders, rules, tamper, playbooks, replay, variables, TLS. |
analysis |
reverse-engineering subset | The AI is reverse-engineering a protocol and only needs to inspect, analyse, probe, and record findings. Roughly halves the per-turn catalogue cost. |
The analysis profile keeps: session/frame inspection, all analysis
tools, the knowledge base (findings/notes), read-only protocol-definition
tools, and the active-probe send/inject/forge-session tools (so
bisect_field_meaning still works). It drops the operational surface:
forwarder lifecycle/config, replace & intercept rules, the tamper queue,
playbooks, replay, variables, and TLS CA. Those actions remain
available to the operator in the TUI — they are just not exposed to the AI.
The profile is persisted per-project in mcp.json. Set it from the
Profile selector in the Config tab's MCP section, or with
--mcp-profile at launch. Changing it at runtime restarts the embedded
server (the tool surface is fixed when the server is built).
Launch¶
pip install -e ".[mcp]"
protopoke --mcp # 127.0.0.1:7878
protopoke --mcp --mcp-port 7878 # explicit port
Once enabled, http://127.0.0.1:7878/mcp is the URL to register with an
MCP client.
Missing mcp dependency¶
The embedded server needs the optional mcp package (the [mcp] extra).
If it is not installed, ProtoPoke will not crash: enabling MCP — whether
via the Config tab switch, the --mcp flag, or opening a .pp project that
has it enabled — logs a warning, shows a notification, and keeps the server
disabled. Install the dependency with pip install protopoke[mcp] (or
pip install -e ".[mcp]" from a checkout) and re-enable it.
Why a stdio bridge?¶
The embedded MCP server only speaks streamable-http because it lives in
the long-running TUI process and shares one ProtoPokeAPI with the
operator. The standard Claude Desktop client (and many other AI agents)
only support launching stdio MCP servers from their config file —
they silently ignore "url" entries. To bridge the two, ProtoPoke ships
a tiny Python forwarder, protopoke-mcp, installed as a console script
alongside protopoke. It runs as a stdio MCP server, forwards every
message to/from the HTTP endpoint, and exits when the client closes.
┌──────────────────┐ stdio ┌──────────────────┐ HTTP ┌─────────────┐
│ AI client │ ───────► │ protopoke-mcp │ ──────► │ TUI proc │
│ (Claude Desktop │ ◄─────── │ (stdio bridge) │ ◄────── │ + MCP srv │
│ / Cursor / ...) │ └──────────────────┘ └─────────────┘
└──────────────────┘
protopoke-mcp takes an optional --url (default
http://127.0.0.1:7878/mcp, also honoured via $PROTOPOKE_MCP_URL).
Always start the TUI first with protopoke --mcp so the bridge has
somewhere to connect to.
Running from a local checkout without pip install (uv)¶
If you have ProtoPoke cloned locally and don't want to pip install it
into a system or user Python, uv can run
both the TUI and the stdio bridge straight out of the checkout. uv run
materialises a .venv inside the project on first invocation, then reuses
it.
Run the TUI in a terminal (replace /path/to/ProtoPoke with your clone):
Point the AI client at uv run instead of protopoke-mcp:
{
"mcpServers": {
"protopoke": {
"command": "uv",
"args": [
"run",
"--project", "/path/to/ProtoPoke",
"--extra", "mcp",
"protopoke-mcp"
]
}
}
}
Use an absolute path for --project — AI clients launch the bridge from
their own working directory. If the client can't find uv on its PATH,
replace "uv" with the absolute path (which uv on macOS/Linux).
Claude Desktop¶
Add a protopoke entry to your Claude Desktop MCP configuration:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
If you moved the MCP port, pass it through:
{
"mcpServers": {
"protopoke": {
"command": "protopoke-mcp",
"args": ["--url", "http://127.0.0.1:9000/mcp"]
}
}
}
Start the TUI first (protopoke --mcp), then restart Claude Desktop. A
hammer icon will appear in the chat input bar when the server is connected.
Claude Code¶
Claude Code supports streamable-http directly, so the bridge is not needed:
Or add it directly to .mcp.json / ~/.claude/mcp.json:
Other AI agents (Cursor, Windsurf, Cline, ChatGPT Desktop, …)¶
Any client that supports the standard stdio config format works with the same snippet as Claude Desktop:
If the client supports streamable-http natively (e.g. Cursor), you can
skip the bridge and point it at http://127.0.0.1:7878/mcp directly.
mcp-inspector¶
Both transports work:
# Via the bridge (stdio):
npx @modelcontextprotocol/inspector protopoke-mcp
# Direct HTTP:
npx @modelcontextprotocol/inspector --transport http http://127.0.0.1:7878/mcp
Programmatic Usage¶
Build and embed the MCP server yourself (e.g. for custom hosting):
import asyncio
from protopoke.api import ProtoPokeAPI
from protopoke.config import ForwarderConfig
from protopoke.mcp.host import MCPHost, MCPSettings
async def main():
fwd = ForwarderConfig(
name="Default",
listen_port=8080,
upstream_host="10.0.0.1",
upstream_port=9090,
tamper_enabled=True,
)
api = ProtoPokeAPI([fwd])
await api.start()
host = MCPHost(api, MCPSettings(enabled=True, host="127.0.0.1", port=7878))
await host.start()
try:
await asyncio.Event().wait() # run forever
finally:
await host.stop()
await api.stop()
asyncio.run(main())
If you just need the raw FastMCP instance (for example to mount it under
another ASGI app), call build_mcp_server(api) directly:
from protopoke.mcp.server import build_mcp_server
mcp = build_mcp_server(api, name="ProtoPoke") # full tool surface
mcp = build_mcp_server(api, profile="analysis") # RE subset only
# mcp is a FastMCP instance; call mcp.run_async(transport="streamable-http")
# or embed it via mcp.settings.host / mcp.settings.port.