Tommy — memnos-native coding orchestrator
Tommy is a lightweight CLI orchestrator that sits between your editor and your coding harness (Claude Code, Codex, etc.). It enriches every task with long-term memory from memnos, routes work to the right harness, and lets you steer a running sub-agent mid-run without waiting for it to finish.
This file is the mechanical reference (CLI flags, config keys, MCP tool signatures, the control-channel wire protocol). For what Tommy is, why it exists, how the pieces fit together, and what's genuinely still prompted behavior vs. code-enforced, see
docs/guides/tommy.md.
┌─────────────┐ tommy --mcp ┌──────────────────────────────────────────┐
│ Editor / │ ─────────────→ │ Tommy (stdio process) │
│ IDE │ ←────────────── │ 8 MCP tools · memnos · harness mgr │
└─────────────┘ JSON-RPC/stdio └──────────────────────┬───────────────────┘
│ Popen
TOMMY_CTRL_PORT
│
┌──────────────▼──────────────┐
│ Harness (Claude Code, etc.) │
│ progress / wrap_up / abort │
└─────────────────────────────┘
Key design decisions:
- Tommy is a stdio subprocess, not a daemon or HTTP server. The editor
spawns it (
tommy --mcp) and owns its lifecycle. - memnos is the only persistent server. Tommy talks to memnos for memory; it exposes no long-lived listening ports.
- A TCP loopback control channel (
TOMMY_CTRL_PORT) is opened transiently — one ephemeral127.0.0.1:0socket per dispatch, closed when the sub-agent exits. This lets Tommy sendwrap_up/abort/pivotto a running sub-agent and receive live progress without polling.
Requirements
| Dependency | Version |
|---|---|
| Python | ≥ 3.10 |
| uv | any recent |
| memnos | installed and running (HTTP or stdio) |
| A supported harness | Claude Code (claude), Codex, etc. |
Install
# From the memnos repo root (editable, uv-managed):
uv tool install -e ~/git/memnos/agents/tommy
# Verify:
tommy --version
Note: always use
--forcewhen pyproject.toml dependencies change:uv tool install -e ~/git/memnos/agents/tommy --force
First-time setup
tommy --install
This creates ~/.memnos/agents/tommy/tommy.conf with sensible defaults.
Configuration
Edit ~/.memnos/agents/tommy/tommy.conf:
# Who you are
TOMMY_USER=YourName
ORG=your-org
# memnos namespaces
TOMMY_NS=user:yourname:tommy # where Tommy journals its own sessions
DEFAULT_NS=org:your-org:engineering # default namespace for new memories
# Model & harness
DEFAULT_MODEL=claude-sonnet-4-5
HARNESS=claude # claude | codex | auto
SMART_ROUTING=on
# Projects — format: key:Name:JIRA_PROJECT:absolute/path/to/repo
# (one per line, comma-separated)
PROJECTS=\
myapp:MyApp:MYAPP:~/git/myapp,\
platform:Platform:PLAT:~/git/platform,\
infra:Infra:PLAT:~/git/infra
Project fields
| Field | Meaning |
|---|---|
key |
Short identifier used in tommy --project <key> |
Name |
Human-readable label |
JIRA_PROJECT |
Jira project key (used in commit messages, ticket links) |
path |
Absolute path — the workspace Tommy gives to the harness |
Usage
CLI
# Launch harness with memory context
tommy
# Activate a project (workspace + namespace auto-set)
tommy --project myapp
# List configured projects
tommy --list-projects
# List detected harnesses
tommy --list-harnesses
# Upgrade (respects uv/pipx/pip — never mixes installers)
tommy --upgrade
MCP stdio mode (for editors)
tommy --mcp
The editor spawns this process, sends JSON-RPC over stdin/stdout, and kills
the process when done. In MCP mode Tommy itself has no persistent listening
port — but each tommy_dispatch call opens a transient 127.0.0.1:0 TCP
control channel that is closed when the sub-agent exits.
Editor integration
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"tommy": {
"command": "tommy",
"args": ["--mcp"]
}
}
}
Cursor
Add to .cursor/mcp.json in your project root (or the global
~/.cursor/mcp.json):
{
"servers": {
"tommy": {
"command": "tommy",
"args": ["--mcp"]
}
}
}
VS Code + Continue
In .continue/config.json:
{
"mcpServers": [
{
"name": "tommy",
"command": "tommy",
"args": ["--mcp"]
}
]
}
Zed
In ~/.config/zed/settings.json:
{
"context_servers": {
"tommy": {
"command": {
"path": "tommy",
"args": ["--mcp"]
}
}
}
}
MCP tools
| Tool | Description |
|---|---|
tommy_recall |
Query memnos memory for context |
tommy_remember |
Persist a fact / decision to memnos |
tommy_dispatch |
Launch a harness task (async by default) |
tommy_status |
Check a running task's output / exit code |
tommy_control |
Send wrap_up / abort / pivot / answer to a running task |
tommy_switch_project |
Set active project (workspace + namespace) |
tommy_route |
Dry-run: which harness would Tommy pick? |
tommy_list_harnesses |
Available harnesses + active routing config |
tommy_dispatch
tommy_dispatch(
task="refactor the auth module to use PKCE",
harness="auto", # or "claude", "codex", …
workspace="/path/to/repo",
async_run=True, # return task_id immediately
inject_memory=True, # prepend memnos recall to prompt
)
→ {"task_id": "a3f1b2c4", "status": "running", "harness": "claude"}
tommy_control — steer a running task
# Ask the harness to wrap up (gives it 60 s to finish gracefully)
tommy_control(task_id="a3f1b2c4", action="wrap_up", budget_seconds=60)
# Stop immediately
tommy_control(task_id="a3f1b2c4", action="abort")
# Redirect to a different goal mid-run
tommy_control(task_id="a3f1b2c4", action="pivot",
message="focus only on the login flow, skip registration")
# Answer a question the harness asked
tommy_control(task_id="a3f1b2c4", action="answer", message="yes, overwrite")
The harness receives the message over a TCP loopback control channel
(TOMMY_CTRL_PORT env var) — no polling required.
Control channel (for harness authors)
If you write a custom harness in Python, connect back to Tommy using the
bundled ControlClient:
from tommy.control import ControlClient
def handle_tommy_message(msg: dict) -> None:
if msg["type"] == "wrap_up":
# save state and exit within msg["budget_seconds"]
...
elif msg["type"] == "abort":
raise SystemExit(1)
elif msg["type"] == "pivot":
current_goal = msg["new_goal"]
elif msg["type"] == "answer":
# Reply to a question you sent via client.question()
user_answer = msg["text"]
client = ControlClient(on_control=handle_tommy_message)
# Report progress
client.progress(25, "parsed 250 / 1000 files")
client.checkpoint("analysis", "found 3 duplicate patterns")
# Send a question to Tommy/user; answer arrives via the on_control callback as {"type": "answer", "text": ...}
client.question("Should I overwrite existing tests?", options=["yes", "no"])
client.done("refactoring complete — 12 files changed")
client.close()
The client auto-reads TOMMY_CTRL_PORT from the environment.
Protocol (newline-delimited JSON):
| Direction | type |
Extra fields |
|---|---|---|
| Harness → Tommy | progress |
pct, detail |
| Harness → Tommy | checkpoint |
phase, summary |
| Harness → Tommy | done |
summary |
| Harness → Tommy | error |
message |
| Harness → Tommy | question |
text, options |
| Tommy → Harness | wrap_up |
reason, budget_seconds |
| Tommy → Harness | abort |
— |
| Tommy → Harness | pivot |
new_goal |
| Tommy → Harness | answer |
text |
The control channel uses TCP loopback (127.0.0.1), which works on macOS,
Linux, and Windows without any extra setup.
Upgrade
tommy --upgrade
Tommy detects whether it was installed with uv, pipx, or pip and uses
the same tool to upgrade — so the venv is never mixed.
To upgrade manually with uv:
uv tool install -e ~/git/memnos/agents/tommy --force
Project structure
agents/tommy/
├── README.md ← you are here
├── pyproject.toml
└── tommy/
├── __init__.py
├── cli.py ← click entrypoint, _launch_harness
├── config.py ← TommyConfig, ProjectEntry
├── control.py ← ControlServer + ControlClient (TCP IPC)
├── install.py ← tommy --install
├── mcp_server.py ← FastMCP stdio server, 8 tools
├── prompt.py ← memnos-enriched system prompt builder
└── discovery/
└── harnesses.py ← auto-detect installed harnesses
Roadmap
- Supervision loop: idle + wall-clock timeout with automatic
wrap_up - Smart harness routing by task type (coding vs. research vs. review)
- memnos lease heartbeat while harness is running
- Multi-harness fan-out (run two harnesses in parallel, merge outputs)
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tommy_orchestrator-0.1.2.tar.gz.
File metadata
- Download URL: tommy_orchestrator-0.1.2.tar.gz
- Upload date:
- Size: 31.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84dba088a5d80470f7b527bdb0e309d3739986fcb5a660a2d4428b42e3547855
|
|
| MD5 |
ac178ad121acf40507cdba84cda7e54a
|
|
| BLAKE2b-256 |
e70a31536b5145feda910f5cede095c46de869ef11ee63cbd60c65936c8434ec
|
Provenance
The following attestation bundles were made for tommy_orchestrator-0.1.2.tar.gz:
Publisher:
release.yml on thameema/memnos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tommy_orchestrator-0.1.2.tar.gz -
Subject digest:
84dba088a5d80470f7b527bdb0e309d3739986fcb5a660a2d4428b42e3547855 - Sigstore transparency entry: 2507358101
- Sigstore integration time:
-
Permalink:
thameema/memnos@2934d9337bd41fff7d1cbfa94b845bc576ba7902 -
Branch / Tag:
refs/tags/v0.1.25 - Owner: https://github.com/thameema
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2934d9337bd41fff7d1cbfa94b845bc576ba7902 -
Trigger Event:
release
-
Statement type:
File details
Details for the file tommy_orchestrator-0.1.2-py3-none-any.whl.
File metadata
- Download URL: tommy_orchestrator-0.1.2-py3-none-any.whl
- Upload date:
- Size: 38.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
162ce35ac8950b65f7dce0a9475977dc538d4b5045e668db5669cd582e2f3736
|
|
| MD5 |
9c490ca98f35603c6f6f0eab32eedc4a
|
|
| BLAKE2b-256 |
61c87a3ecd7faa365ba892df0dd0483444ebfb05f46fd1bb19d702f0aa68c13d
|
Provenance
The following attestation bundles were made for tommy_orchestrator-0.1.2-py3-none-any.whl:
Publisher:
release.yml on thameema/memnos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tommy_orchestrator-0.1.2-py3-none-any.whl -
Subject digest:
162ce35ac8950b65f7dce0a9475977dc538d4b5045e668db5669cd582e2f3736 - Sigstore transparency entry: 2507358119
- Sigstore integration time:
-
Permalink:
thameema/memnos@2934d9337bd41fff7d1cbfa94b845bc576ba7902 -
Branch / Tag:
refs/tags/v0.1.25 - Owner: https://github.com/thameema
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2934d9337bd41fff7d1cbfa94b845bc576ba7902 -
Trigger Event:
release
-
Statement type: