Shared context for teams where everyone uses their own AI coding agent — Claude Code, Cursor, Antigravity, OpenCode, doesn't matter which — on the same repo.
If everyone on your team is running their own AI agent session, nobody's agent knows what a teammate already decided, what they're building right now, or why some file looks the way it does. People end up duplicating each other's work or quietly stepping on it, and eventually one person is doing everything by hand because the AI-assisted parallel work just doesn't hold together.
The normal fix is git pull, then tell your agent "look at this and build X" — which just moves the burden back onto a human, every single handoff. And it gets worse when teammates use different models: Claude, Gemini, and GPT will happily draw different conclusions from the same paragraph of context.
teamhub-mcp is git-native. No hosted backend, nothing to deploy, no account to make. It's a small MCP server that each person runs locally against their own clone (the CLI is called hub-server). State is just JSON files under .hub/ in the repo, and it moves around the same way your code does — git push, git pull. If you have access to the repo, you have access to the shared context. There's no separate workspace ID or token to hand out.
cd your-repo
npx -y teamhub-mcp initThen restart your coding agent — config changes don't get picked up mid-session, and this trips people up.
To confirm it worked, ask your agent what MCP tools it has (you should see get_handoff_brief, declare_task and friends), or run npx -y teamhub-mcp dashboard in that folder for a plain-text summary with no agent involved.
init writes .mcp.json (Claude Code), .agents/mcp_config.json (Antigravity), .claude/settings.json for the hooks, and the hook scripts themselves. It skips anything that already exists unless you pass --force, and merges into an existing .claude/settings.json rather than overwriting your other settings. Commit what it creates so teammates get the same setup when they clone.
Antigravity doesn't read .mcp.json — that's a Claude Code convention. init writes .agents/mcp_config.json for it, but if your version doesn't pick that up, add this to ~/.gemini/config/mcp_config.json yourself:
{ "mcpServers": { "hub": { "command": "npx", "args": ["-y", "teamhub-mcp"] } } }Cursor and OpenCode both use their own config locations too — check their docs and point them at the same command (npx -y teamhub-mcp).
The npm package is teamhub-mcp, but the command it installs is hub-server. There's an unrelated package literally called hub-server on npm, so npx -y hub-server silently fetches the wrong thing. Always npx -y teamhub-mcp <command>.
If you're working on teamhub-mcp itself rather than using the published package:
npm install && npm run build && npm link
cd your-repo && hub-server init- A living plan, not just a to-do list.
requirements.mdanddesign.mdlive in.hub/plan/and get updated as the project evolves, so the big picture isn't stuck in one person's head or buried in a chat transcript somewhere. - Completion reports with a fixed shape, not a paragraph. When someone finishes a task, they fill in what was built, the decisions they made and why, which files changed, known gaps, and what's next — as structured fields, not prose. The reason this matters more than it sounds: if your teammates are on different models, free text gets reinterpreted differently by whoever reads it next. A fixed shape doesn't have that problem.
- File notes that check their own freshness. Every note about a file is pinned to the commit it was written against. Read it later and it'll tell you
verified(nothing's changed),changed(go re-read the file, don't trust this blindly), orgone. A note that's quietly out of date is worse than no note at all. - One call that gets you everything.
get_handoff_briefbundles the plan, open tasks, recent completions, and file history into a single response, and it's what gets auto-injected at session start. The point is that it's the same for everyone — nobody's agent has to decide on the fly how much digging is "enough." - Conflict detection that looks at real git history, not just declared tasks.
declare_taskchecks other declared tasks and actual recent commits touching the same files, so it catches a teammate who's mid-edit even if they never bothered to declare anything. - Dependencies, for the case file-overlap can't catch. "Build
POST /api/auth" and "wire the login form to it" touch completely different files, so nothing about overlapping scopes will ever flag that the second can't start until the first exists. Mark it withdependsOnand you get told at claim time — and the handoff brief shows which tasks are actually startable versus waiting on something. - Completions written from the real diff, not memory.
get_diff_for_taskshows everything that changed since you claimed it, committed and uncommitted, so the completion report lists what you actually touched. In a long session it's easy to forget files you edited early, or to describe an approach you later reverted — and since the uncommitted-file check only inspects the paths you list, an incomplete list quietly defeats that check too. - It tells you which repo it's using, and you can change it by just saying so. Every context response names the active repo, so if it picked the wrong one you'll see it straight away. Tell your agent to switch and it calls
set_repo— by GitHub URL or folder path. Whoever's working on the same repo is on the same team; there's nothing else to set up, no accounts, no workspace IDs.
Handoffs here are sequential — one person finishes and pushes, the next person pulls and continues — not simultaneous editing, so a plain git-synced log is enough to keep everyone in sync. No CRDTs, no custom merge logic, and (after actually looking into it) no live messaging between agents either — more on that below.
your-repo/
.hub/
plan/requirements.md the why/what
plan/design.md the architecture/how
tasks/<taskId>.json one file per task
activity/<ts>-<id>.json one file per activity event
notes/<file/path>/<ts>-<id>.json one file per file note, anchored to a commit
One file per record is the important decision here. Two people writing "at the same time" never touch the same file, so git can't produce a merge conflict from ordinary use. The one real race condition — two people claiming the same task at once — is handled explicitly: claim_task re-checks after a rejected push and tells you the truth instead of quietly overwriting the other person's claim.
| Tool | What it does |
|---|---|
get_handoff_brief |
Call this when you're picking up someone else's work. Bundles the plan, open tasks, recent completions, and file history in one shot. Can filter by scope. |
get_context |
The lightweight version — just tasks and recent activity. |
get_plan / update_plan |
Read or replace requirements.md / design.md. |
declare_task |
Propose a task with a scope. Comes back with any declared tasks that overlap, plus real git commits touching the same files in the last 10 minutes from anyone else. Takes an optional dependsOn list of task IDs. Leaves the task unclaimed on purpose — call claim_task if you're the one building it. |
claim_task |
Claim an unclaimed task, or your own. Tells you about recent activity on those files and any dependencies that aren't finished yet. |
get_diff_for_task |
What actually changed since you claimed the task, committed and uncommitted. Call this before writing a completion. |
set_repo |
Point this session at a different repo, by GitHub URL or local path. Use it when you switch projects, or when the repo it picked isn't the one you meant. reset: true goes back to automatic detection. |
update_task_status |
Move a task forward. Marking it done needs a structured completion, not a one-liner. Marking it abandoned needs a reason — that way a half-finished task doesn't just look stuck forever, and it becomes claimable again. |
log_activity |
"Here's what I'm doing right now." Use kind: "pivoted" if you're changing approach mid-task without abandoning it. |
record_file_note |
Leave a note on a file you just finished touching. |
get_file_history |
Read a file's notes, each one tagged with whether it's still trustworthy. |
check_file_before_edit |
A fast check on one file before you touch it — cheaper than the full handoff brief, useful in a long session where your original context might be stale. This is what backs the Claude Code enforcement gate. |
get_task_history |
The full status history of a task. .hub/tasks/<id>.json gets overwritten in place on every change, so without this the todo → claimed → done sequence isn't visible even though git already has it. |
No login, no auth tool. Every tool takes an optional memberName, and if you don't pass one it just uses git config user.name.
There are also two MCP prompts — start_work and finish_task — for when you want to force the right sequence yourself instead of hoping the model calls the tools unprompted (in Claude Code these show up as /mcp__hub-server__start_work etc). start_work loads the live handoff brief and states the order to work in; finish_task pulls the real diff for a task and walks through closing it out properly. Every tool result also carries a nextStep field computed from the actual state that came back — a real task ID to claim, a real warning to act on — because a model reads a tool's description once, when deciding what to call, but reads the result in the middle of the work, which is the moment that actually decides whether it keeps following the sequence.
Hooks are plain Node scripts (.mjs), not bash — Node's already a hard requirement, so this avoids needing Git Bash or WSL on Windows. This wasn't just a style choice: npm installs hub-server as a .cmd file on Windows, which Node can't run directly without a shell, and getting that right (safely, without the argument-injection issues that come with shelling out carelessly) took some real work.
Claude Code is the one I've actually verified end to end. The SessionStart hook really does get its output into the model's context — I checked by putting a made-up string in the hook and asking a fresh session if it saw anything unusual, and it reported the string back. The PreToolUse enforcement gate works too: I ran a real claude -p session, watched it get denied on a raw edit, watched it correctly call get_handoff_brief to clear the gate, then watched the edit go through. It's not bulletproof — a blocked model can still route around it through Bash instead of Edit, and it can only catch built-in tool calls, not MCP calls directly, so the gate works by having the MCP server itself leave a marker the hook can check. It also only enforces checking in before you edit, not recording what you did afterward — there's no equally clean hook for that yet.
Antigravity gets context injected via its PreInvocation hook, which fires before every model call and is the only one of its hooks that can actually put text in front of the model — I checked, and PostToolUse there is documented to return nothing usable. On the first model call of a conversation it injects the full handoff brief; on later calls, if nothing in this repo's coordination tools has been touched in the last hour, it injects a one-line correction telling the model to go call get_handoff_brief, throttled to once every 5 minutes so it doesn't nag before every single turn. There's also a PreToolUse hook that fires before an edit and, deliberately, stays completely silent unless a teammate has committed to that exact file in the last 15 minutes — at which point it returns ask rather than deny, since a person is right there and this is a judgement call, not a rule violation. Both are covered by an automated test suite that drives the real hook scripts and a real throwaway git repo end to end, but I don't have a live Antigravity install to confirm against, and there's an open upstream report (cmux #5358) of Antigravity rejecting PreToolUse responses outright — which is exactly why that hook is built to fail toward silence, not toward blocking, and can be turned off entirely with HUB_GATE=off if it misbehaves for you.
Cursor and OpenCode have hooks written for them but I haven't tested either against a real install. Codex CLI has nothing yet — there's no session-start hook to attach to upstream, so it'd need a different approach (wrapping the binary) that isn't built.
This is asked once per session and then cached for the rest of that process:
- If you set
HUB_REPO_PATH, that wins, full stop. - Otherwise it asks you directly — "which GitHub repo are you working on?" — through MCP's elicitation feature, and then works out your local clone from the answer: checking if the auto-detected folder's git remote matches, checking a cache of past answers on this machine, doing a quick scan of
~,~/Desktop, and~/Documentsfor a matching clone, or just asking where you put it. - If the client doesn't support elicitation, or you don't answer, it falls back to MCP's
rootsprotocol and then plainprocess.cwd().
I built a test client that supports elicitation to confirm this actually works, including launching from a totally unrelated folder and having it find the right clone anyway. Claude Code itself doesn't trigger the prompt as of writing — I tested with claude -p and no prompt showed up, so it just falls through to the cwd-based fallback, which still works fine. Whether Antigravity supports elicitation, I don't know yet.
So in practice you probably won't be asked, and usually won't need to be. Every context response carries an activeRepo field naming the repo in use, so a wrong one shows up immediately instead of being silently wrong. If it isn't what you meant, just tell your agent to switch — it calls set_repo, which works on every harness because it's an ordinary tool call rather than a protocol feature the client has to support. Setting a repo by local path also teaches it that URL-to-folder mapping, so asking for the same repo by URL later works even when the clone lives somewhere the folder scan would never look.
That override lasts for the session and isn't saved machine-wide, on purpose: if it were global, having two projects open in two windows would mean one silently hijacking the other.
One thing to be aware of: if you've got more than one local clone of the same repo, it'll pick whichever one it finds first, which might not be the one you meant. Normal one-clone-per-person setups are unaffected.
The reason there's a fallback chain at all is that some harnesses — Antigravity is the one I ran into — launch a globally registered MCP server from their own install folder instead of your actual project. That's a known issue on their end (there are open GitHub issues about it), not something specific to this tool.
The obvious fix for "my teammate's agent doesn't know what happened" is to hand it more text — a longer summary, more files. That doesn't really solve it, for two reasons:
Different models read the same prose differently. Claude, Gemini, and GPT can draw genuinely different conclusions from the same free-text explanation. A fixed set of fields doesn't have that problem — the shape stays the same no matter which model wrote it or which one is reading it.
And a summary that's gone stale is worse than no summary, because it looks trustworthy while being wrong. Pinning every note to the commit it was written against, and checking that on every read, means you're told explicitly when something's moved on instead of quietly building on outdated information.
I looked into this seriously before deciding against it. There's a study measuring exactly this tradeoff that found adding messaging channels increases overhead for sequential handoffs like this one — one person finishes, another continues later — because the files already carry the coordination; messaging mainly helps when work is genuinely simultaneous. Every option I found for doing this without standing up a hosted service either broke the "no server to run" idea or had already been tried and dropped by similar tools for good reasons (auto-committing on every file save floods a shared branch with half-finished code, for instance).
- Conflict detection on
declare_taskis a plain string match on scope — good for "same file," won't catch two people building the same feature under different names. (Dependencies between tasks are handled separately, viadependsOn.) - Dependencies are a warning at claim time, not a hard block. If you want to start something that isn't ready yet, nothing stops you — you're just told.
- Small
hub:commits pile up fast. Might batch these later if it turns out to bother people in practice. - If there's no git remote (a solo project, or just testing), everything still works — it just skips the sync step.
- Cursor and OpenCode hooks exist but haven't been tested against real installs. Antigravity's hooks are built and covered by automated tests, but not yet confirmed against a live Antigravity install.
- I'm maintaining this alone, and it depends on hook/MCP APIs from several companies that are all still changing quickly. Expect some breakage as those move.
- Support for Codex CLI, probably via wrapping the binary since there's no hook to attach to.
- A fallback that keeps
AGENTS.mdin sync for harnesses without real hooks at all. - A small local web dashboard on top of the same data
dashboard --jsonalready returns. - Turning the scope-overlap warning into an actual block, with a way to negotiate instead of just flagging it.
- The
PreToolUsegate for Cursor, once I can confirm it actually works there. - Confirming the Antigravity hooks against a real install, not just the test suite.
- Role-based visibility, surfacing tasks on GitHub PRs, multi-repo setups.
Issues and PRs are welcome, especially reports of what happens when you try this on a harness I haven't verified yet (Cursor, OpenCode, Antigravity's gate). Right now, an honest "I tried it and here's what broke" is more useful than a feature request.
MIT — see LICENSE.