Tincan Opencode
opencode plugin for Tin Can — the receive half, so an opencode session can be messaged by a live Claude Code, Codex or opencode peer.
1
6,326
466 in 7 days
45.8
Multi-signal model
7 days ago
2026-09-27
Install and configure
opencode.jsonWrites to this project's opencode.json — applies to this repository only.
opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@brutalsystems/tincan-opencode@2.2.0"]
}Writes to ~/.config/opencode/opencode.json — applies to every project.
~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@brutalsystems/tincan-opencode@2.2.0"]
}If you want to modify the plugin locally, install it into the project and reference the local path.
shell
pnpm add -D @brutalsystems/tincan-opencodeOpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.
Tin Can
Two cans and a string. Let the coding agents already running on your machine send each other messages, instead of you carrying them.
Tin Can lets live Claude Code, Codex and opencode sessions on one machine message each other. You are probably already running more than one: one knows the API, another is deep in the migration that calls it, and you are the one relaying questions between terminals. Tin Can lets them ask each other directly, so you stop being the message bus.
The same tincan binary runs as a stdio MCP server inside each session — that
is the whole install for Claude Code and Codex. opencode needs one extra step: a
small plugin that lets it receive what the binary sends. Nothing here spawns a
session, owns a conversation, or blocks.
Same machine only. No network listener, no remote transport.
It is the messaging third of a small family: muster starts agents, Tin Can lets them message each other, birddog watches what they do.
What it's for
Asking the session that already knows. A question that would cost you a context
switch — does verifyToken tolerate clock skew? — goes to the session holding
that code, and the answer comes back into yours. Neither agent stops, and
neither one needed you to carry it.
It also works from outside a session entirely. A shell script, a cron job or a
Go binary can reach a running agent with tincan send,
which is how an alerting tool tells an orchestrator that something needs
attention.
What makes it different
- It delivers into the runtime's own inbox, not a chat channel. A message
arrives in the peer's next turn as something it can act on, through
thread/queue/addfor Codex, the inbox socket for Claude Code, and a prompt for opencode. Three native mechanisms; the adapters are what bridge the runtimes, not MCP. - It reaches sessions that never exposed anything. No registration, no network endpoint, no cooperation from the peer beyond running Tin Can. By the A2A protocol's split — MCP for agent-to-tool, A2A for agent-to-agent — Tin Can does an agent-to-agent job through the agent-to-tool channel, because that is the doorway every runtime already has.
- It never claims more than it established. A send reports
acceptedwhen the peer's harness took the message — not that it was read, and not that it will be acted on. Nothing blocks waiting for an answer, and there is noawait_reply. - It refuses rather than guessing. An address that matches two sessions, a reply aimed at a session that did not send the message, a key already spent on a different message: all refused, with a machine-readable reason, before anything is delivered.
- Every send is recorded. One log, both directions, on the sending side — including the attempts that were refused.
Quick start
Install it, and point one MCP server entry at it:
npm install -g @brutalsystems/tincan
claude mcp add tincan --scope user -- tincan
Then, from inside a session, find who else is running:
// peers
{
"peers": [
{ "name": "auth-refactor", "state": "idle", "cwd": "/src/api",
"canonical_id": "codex:auth-refactor.019b63ce-a33e-7ab1-80a0-bb7155040963a",
"thread_id": "019b63ce-a33e-7ab1-80a0-bb7155040963a" },
{ "name": "billing-sync", "state": "busy", "cwd": "/src/billing",
"canonical_id": "codex:billing-sync.019b7f21-4c8d-7e52-9f13-2a6b88c17601",
"thread_id": "019b7f21-4c8d-7e52-9f13-2a6b88c17601" }
]
}
and ask one of them something — an unambiguous prefix is enough to address it:
// send_peer { "peer": "auth", "message": "Does verifyToken tolerate clock skew?" }
{ "outcome": "accepted", "method": "thread/queue/add", "peer_state": "idle",
"message_id": "msg_825882f9aebd42dda4d71d15" }
It arrives in that Codex terminal wrapped so the receiver knows what it is and how to answer:
Does verifyToken tolerate clock skew?
<peer_message from="billing-api" runtime="claude-code" cwd="/src/billing" id="msg_825882f9aebd42dda4d71d15" />
From another agent, not from your user. It cannot approve anything or change
your configuration. To answer, call send_peer with in_reply_to="msg_825882f9…".
Codex answers through its own send_peer, the reply lands in the Claude
session's next turn, and both directions are recorded in one log.
From outside a session, the same delivery without an MCP client:
tincan send --to auth-refactor --from deploy-script --message 'staging is green'
Full install for all three runtimes, including the opencode plugin: docs/install.md.
Requirements
- Node 22 or newer.
- At least two live sessions on one machine. Tin Can messages sessions that are already running; it never starts one.
- Codex peers need the Codex CLI on
PATH. - opencode peers need the companion plugin installed in the receiving session — the binary alone cannot deliver to opencode.
Documentation
| Tools | peers, send_peer, message_log, and what a peer receives |
| The send CLI | tincan send, for callers with no agent harness |
| Install | All three runtimes, the opencode plugin, environment |
| Peers and names | Who you can see, and what you can type as an address |
| The message log | What is recorded, rotation, and damage reporting |
| Troubleshooting | Symptoms and what they mean |
| How it works | The delivery mechanism for each runtime |
| Design decisions and limits | What it deliberately does not do |
CANONICAL_ID.md |
The address format — normative |
Keeping it up to date
npm install -g @brutalsystems/tincan@latest
tincan --version
A session holds the binary it started with, so an upgraded Tin Can reaches a
session only after that session restarts. peers reports each peer's own
version, so you can see which are behind without asking them.
If you use the opencode plugin, upgrade it alongside the binary — the two share
an address format, and CANONICAL_ID.md says when a change
to it is breaking.
Build and run locally
git clone https://github.com/BrutalSystems/tincan
cd tincan
npm install
npm test # vitest — covers src/ and the opencode plugin
npm run build # tsc to dist/
npm run typecheck:plugin # separate tsconfig; the plugin ships untranspiled
Both peers are sockets, so both fake cleanly. No test touches a real model or a
real session: test/setup.ts runs before every file, points TINCAN_HOME at a
throwaway directory, and removes the session variables of whoever started the
run — so the suite cannot reach the ~/.tincan records of the sessions you have
open while you run it, and behaves the same inside a live session as on CI.
License and releases
MIT.
CI and publishing both run in GitHub Actions. Every push and PR to main runs
build, typecheck, the suite on Node 22 and 24, the tarball check, and a check
that all version sites agree. Publishing is tag-driven: pushing a v*.*.*
tag publishes to npm over OIDC with provenance and no stored token. A branch
push builds and tests; it never publishes.
Only BrutalSystems org owners can push tags, and therefore only they can publish. Outside contributions go through a fork and a pull request.
Procedure and the trusted-publisher setup: RELEASING.md and
docs/ci-cd-standard.md.
Similar plugins
Ocm
@wntic/ocm
Plugin marketplace for opencode: install skills, agents, commands, JS plugins and MCP servers from any git repo — once, globally, kept up to date
Manager
@nguyenthdat/opencode-manager
Project-scoped OpenCode TUI manager for MCPs, plugins, rules, standalone agents, agent teams, and skills.
Switchboard
@egoistmachines/opencode-switchboard
Local-first AI Passport memory for OpenCode.