Sessions and recording
Everything stretto learns comes from sessions: what an agent did with a server's tools, recorded by stretto-proxy as it happened.
The proxy
stretto-proxy takes the place of an MCP server in the host's configuration. The host starts it as a stdio server; it starts the real server as a child process, from the command after --, or connects to one over Streamable HTTP (--upstream). It forwards every line in both directions byte for byte, except what you ask it to act on, such as a result a flow adds its lookups to.
stretto-proxy --record ~/.stretto/logs/orders --domain orders \
-- npx -y some-mcp-serverstretto init prints this for your host, in the host's own format (integrations).
--record DIRwrites one log per session,DIR/<session>.jsonl, and createsDIRif it is missing. Keep one directory per server, sincestretto learnreads every session in a directory;stretto inituses~/.stretto/logs/NAME. Without--record, the proxy only forwards.--domain NAMEnames the domain in the log's header, and later the flow's.--agent-model MODELnames the model that drives the agent, which the proxy cannot see. Without it, the log names the host application frominitialize.
Its stdout carries only the protocol. Its own messages go to stderr, prefixed stretto-proxy:, and the first says where the log is. It exits with the server's status, or with 125 if the proxy itself fails. When nothing but recording is asked of it, it parses nothing it forwards. A writer thread writes the log off the forwarding path, so forwarding never waits for the disk.
What a session log holds
A log is JSON lines. The first line is a header:
{"stretto_mcp_log":2,"session":"20260923T212000.123Z-4242","started_unix_ms":1790198400123,"server_command":["npx","-y","some-mcp-server"],"domain":"orders","agent_model":null}Every other line is one line from the wire, with when the proxy read it (t_ms, milliseconds since it started) and who sent it (from): client (the host), server, and, when the proxy acts, proxy (its own requests) and context (the conversation):
{"t_ms":12,"from":"client","message":{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"lookup","arguments":{"text":"hello"}}}}
{"t_ms":14,"from":"server","message":{"id":3,"jsonrpc":"2.0","result":{"content":[{"text":"{\"text\":\"hello\"}","type":"text"}],"isError":false}}}So a log holds every tool call and result verbatim: whatever the tools read or return. Keep logs where that data may live, and never commit them. Privacy and redaction covers retention and pseudonymized copies. The log format documents every field.
Tool kinds
A flow only ever calls tools that read. It learns which ones do from the server's own tools/list response, recorded in the first session: a tool annotated readOnlyHint: true reads, one annotated false writes, and one with no hint is generic, which a flow never calls. Annotations are the server's claims, so check them against what the tools do (reviewing a flow).
If a server gives no hints, write a manifest and pass it to stretto learn --manifest:
{"domain": "notes", "tools": {"search_files": "read", "read_text_file": "read", "write_file": "write"}}The conversation
MCP carries tool calls, not the conversation. --context FILE gives the proxy the conversation from a file the host appends to, one JSON line per message:
{"role": "user", "content": "Summarize the October meetings."}With it, a flow can prefer a value the user mentioned when it binds a lookup's arguments, and the logs hold the user's turns. Without it, a flow still works. The proxy reads the file from its start, so give each session its own file, or empty it when a session starts. The MCP hosts on the integrations pages do not write such a file themselves; a harness that drives the agent can, as the pilots' does.
What the proxy cannot see
- LLM turns. They are inferred from timing: a call sent while an earlier call of the turn still awaits its response joins that turn; any other call starts a new one.
- Outcomes, tokens and cost. Whether a session succeeded is unknown.
stretto learn --rewards FILEtakes rewards by session id; a session without one counts as successful. - Other servers. One proxy wraps one server. An agent with several servers leaves one log per wrapped server, and a flow learns from one server's calls.
How many sessions
A flow only knows what the sessions showed: which lookup followed which call, and where each lookup's arguments came from. Which sessions matter more than how many, so record the kinds of request the agent will see; a request type never recorded gets no help. Replayed on τ²-bench, ten of an agent's own sessions gave 96% (retail) and 93% (airline) of what all of them did, and telecom needed about thirty (the paper, §4.3).
Try it
stretto-mcp-demo is a tiny server for trying the proxy: lookup (read-only) and update (a write) answer with their arguments. stretto-mcp-demo --world retail serves a tiny shop with four of τ²-bench retail's tool names and canned data instead. The quick start's demo records six sessions on that shop and learns a flow from them, and the proxy's reference records one from the shell.
The console lists every recorded session, by domain and mode, and shows one as a timeline: the conversation, each call with its arguments and result, and the lookups the flow made after it.
Related
stretto-proxyreference and its options- Integrations: the configuration for each host
- Flows: what
stretto learnmakes of the sessions - The console: every session, as a timeline