synco-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@synco-mcpregister me as agent-1, show open tasks, and claim the first available one"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
synco-mcp
Self-hosted coordination server for multiple AI coding agents working on the same software project.
Agents register, claim tasks, declare file intents, publish structured ChangeReports, and hand off context. A local dashboard shows the same state in near real time.
Git remains the source of truth for source code. synco-mcp stores coordination metadata only. A ChangeReport is agent-declared, not a verified Git diff.
Quick start
npm install
npm --prefix dashboard install
npm run dashboard:build
npm startThen open http://127.0.0.1:3847/ and connect an MCP client to /mcp. The dashboard starts empty until a real agent registers.
Optional env: copy .env.example and export the values before npm start.
cp .env.example .envtsx does not auto-load .env. Export variables in the shell or use your process manager.
Related MCP server: Geond Agent Protocol
What you get
Endpoint | Role |
| Dashboard (login + project picker) |
| MCP Streamable HTTP |
| Whether any user exists yet |
| Dashboard username/password |
| Session + current project |
| List / create your projects |
| Dashboard snapshot |
| SSE live feed |
| Edit guard for host hooks: may this agent write this path? |
| Liveness |
Default bind: 127.0.0.1:3847. A blank database seeds one leftover project named default so tests and first boot have somewhere to write; it is not the live desk.
stdio mode does not serve the page. For several agents on one project use HTTP. stdio is only for clients that must spawn a local process:
npm run stdioPoint both HTTP and stdio at the same SYNCO_DATABASE_PATH if you mix them. Prefer one HTTP process for a multi-agent demo.
Architecture
MCP tool / dashboard API
↓
application services
↓
repositories + event store
↓
SQLite (WAL)
persist event → in-process EventBus → dashboard SSEMCP is how agents talk to the system.
SQLite is the source of truth for coordination state.
Git is the source of truth for code. synco-mcp never claims a file changed just because an agent said so.
SSE is for the dashboard. Agents should poll
get_project_state/get_recent_changes. Hosts do not reliably push MCP notifications into a running model.
Later hosted mode (Convex + Render) is out of scope. Services do not import better-sqlite3, so storage can be swapped without rewriting tools.
MCP tools
Tool | Purpose |
| Register and get a compact snapshot |
| Compact agents, tasks, claims, reports, warnings |
| Create a todo |
| Transactional reserve; structured refusal if taken |
|
|
| Resource claims + overlap warnings (not a Git lock) |
| Structured ChangeReport ( |
| Compact by default; |
| Active claims |
| Drop your own claims. Needed when you claimed without a task |
| Structured context for the next agent |
| Task, claims, reports, handoffs, warnings — not the full log |
Mutating tools accept optional idempotencyKey. The same key does not create a duplicate event.
MCP client config
HTTP is the product transport (one server, many agents).
Cursor IDE (URL-based):
{
"mcpServers": {
"synco-mcp": {
"url": "http://127.0.0.1:3847/mcp"
}
}
}Cursor CLI and Claude Code — use "type": "http", not "streamable-http" (Cursor CLI currently drops the whole config on that alias):
{
"mcpServers": {
"synco-mcp": {
"type": "http",
"url": "http://127.0.0.1:3847/mcp"
}
}
}stdio example is in examples/mcp.stdio.json. Run it from the repo root so tsx src/stdio.ts resolves.
If SYNCO_API_KEY is set, send Authorization: Bearer <key> on /mcp. Dashboard auth is a separate cookie session (see below).
Make agents actually use synco-mcp
Connecting the MCP server only exposes the tools. Models treat AGENT_INSTRUCTIONS.md and the server instructions string as optional docs. After a long chat or a context summary they skip register_agent / report_change unless the host injects the protocol into every turn.
You cannot 100% force an LLM to call a tool. The stack below is what actually works, strongest last. Do all three.
The agent-facing protocol itself lives in AGENT_INSTRUCTIONS.md. This section is the host checklist.
What “before and after every request” means
Moment | Required calls | Skip when |
First tool calls in a session |
| Never skip on a new chat / after compaction |
Start of every later turn |
| Never skip if synco-mcp tools are available |
Before any file edit |
| Read-only turns |
After a work unit that touched files |
| Pure Q&A — do not invent a ChangeReport |
End of a claimed task |
| No owned task |
Give each host a stable unique agentId (cursor-grok, claude-code, opencode, …). Do not reuse another agent's id. projectId is the dashboard picker. Agents may omit it; the server uses the desk selection. default is only a leftover bootstrap id on a blank database, not a special project.
0. One command, every host, every repo
npm run init # or: npx synco-mcp initnpx synco-mcp init installs the edit guard into every agent host it finds on the machine, at
the user level, so it covers all your repositories at once:
Host | What it writes | Blocking mechanism |
Cursor |
|
|
Claude Code |
|
|
opencode |
| plugin |
Useful flags: --dry-run, --agent-id=cursor-andrew, --url=https://synco.example.com,
--api-key=…, --project=<id>, or a host name (npx synco-mcp init cursor) to limit the scope.
Existing hooks in those files are preserved, and re-running is a no-op.
A repo opts in with .synco.json at its root (npx synco-mcp init writes one in the current
project):
{ "url": "http://127.0.0.1:3847" }Without that file the guard stays completely silent, so a global install does not interfere
with unrelated repositories. Per-host identity lives in synco-host.json next to the
adapter — that is where each host gets its own agentId.
How the guard decides
The policy lives on the server, not in the hook. Before a file write the adapter asks
GET /api/guard/edit?projectId=…&agentId=…&path=…, and the server answers whether that
agent holds an active claim covering that exact path:
Answer | Meaning |
| Call |
| No active claim at all — call |
| Claims exist but not for this file; the message lists what was declared |
| Allowed, plus |
This is why it is host-agnostic: adding a host means writing a ~20-line adapter that
translates one payload shape and one refusal format. It is also why the check survives
opencode never firing tool.execute.before for MCP calls — nothing local has to observe
the declare_change_intent call.
The adapters fail open on every unknown: no .synco.json, no agentId, an unreachable
server, a non-write tool, or a malformed payload all allow the edit. A coordination tool
must not be able to make a repository uneditable. Note that shell commands are not gated —
an agent that runs sed -i bypasses the guard.
1. Project files that every host reads
Put the copy-paste block from AGENT_INSTRUCTIONS.md into files the agent host loads automatically. A file the agent must Read on its own is not enough.
Host | File | Notes |
Cursor |
| Injected into every conversation in this repo |
Cursor, Claude Code, Codex, many others |
| Same block; keep |
Claude Code |
| Paste the same block if you do not rely on |
This repo ships .cursor/rules/synco-mcp.mdc already. Its shape:
---
description: Mandatory synco-mcp protocol before and after every turn
alwaysApply: true
---
# synco-mcp is mandatory
If synco-mcp tools are available, you MUST use them. Documentation is not optional.
The project is the dashboard picker. Omit `projectId`. Agent id: `<AGENT_ID>`.
First tool calls in a session: `register_agent` then `get_agent_context`.
Start of every later turn: `get_agent_context` before other work.
Before any file edit: `declare_change_intent` with every path you will touch.
After file edits in this turn: `report_change` before the final user-facing reply.
No file edits: do not invent a ChangeReport.
Git is the source of truth. ChangeReports are agent-declared, not verified diffs.
Claims warn; they do not lock. Poll — hosts do not push events into you.The shipped rule tells each host to derive a stable agentId from its own name instead of
carrying a <AGENT_ID> placeholder that an agent would paste literally. AGENTS.md at the
root covers hosts that do not read .cursor/rules.
2. User rule in the editor (survives chat summaries)
Project rules still get dropped under context pressure. A user-level rule is what makes one developer's agent keep the protocol after Cursor compact/summarize.
Cursor: Settings → Rules → add a user rule. Scope it so it only fires when synco-mcp tools exist (otherwise it will nag in unrelated repos):
When synco-mcp / user-synco-mcp tools are available, the protocol is mandatory.
First tools this session: register_agent (once) then get_agent_context.
Every later user message: get_agent_context before any other work.
Before Write/StrReplace/edit: declare_change_intent.
If you edited files this turn: report_change before the final answer.
If you only answered a question: do not fake a ChangeReport.
Stable agentId for this host. Do not skip because the turn looks small.Do this on every machine / every developer profile that talks to the shared server.
3. Hooks — the only layer that interrupts the agent
Rules can still be ignored. Hooks run outside the model.
The blocking guard comes from npx synco-mcp init (section 0) because it needs a per-developer
agentId. On top of it, this repo ships .cursor/hooks.json with three prompt hooks that
nudge the protocol at session, turn and stop boundaries:
{
"version": 1,
"hooks": {
"sessionStart": [
{
"type": "prompt",
"prompt": "synco-mcp is connected. Your first tools this session must be register_agent then get_agent_context. Do not answer the user before those calls."
}
],
"beforeSubmitPrompt": [
{
"type": "prompt",
"prompt": "Remind the agent: call get_agent_context before other work. If this turn will edit files, declare_change_intent first. If this turn edited files, report_change before the final reply."
}
],
"stop": [
{
"type": "prompt",
"prompt": "If this turn created or edited files and the agent did not call report_change, send a follow-up that it must call report_change now. If it only answered a question, do nothing.",
"loop_limit": 1
}
]
}
}Check Cursor → Hooks after pulling; restart Cursor if they do not load.
4. What reaches hosts with no hook system at all
Windsurf, Cline, Copilot and friends have rules but no interception point. For them the server does the talking, and it needs no local setup:
The MCP
instructionsstring states the protocol as mandatory at connect time.register_agent,get_agent_context,declare_change_intentandreport_changeall return aprotocolobject withnextRequiredCall, plusopenItemsnaming actual lapses — for example anin_progresstask with declared intent and no ChangeReport, or claims held with no open task.
That is advisory, not enforcement, but it lands inside the model's context on every call and cannot be skipped by a developer forgetting to configure something.
Checklist
A new developer on a shared project is not done after npm start and pasting the MCP URL. They need:
HTTP MCP client pointed at
http://127.0.0.1:3847/mcp(see above).npm run initonce per machine, with--agent-id=if they want a personal id..synco.jsonin every repo that should be guarded.User rule on their own Cursor (or other host) profile.
Confirmation on the dashboard: after the first real prompt, an
agent_registeredevent and laterintent_declared/change_reported— not only chat replies.
If the dashboard stays empty, the agent is not using MCP. Fix the host, not the server.
Dashboard login and projects
The desk is no longer an open single-project page.
Open
/. If nobody has registered yet, create the first account.The first user on a blank install inherits an unowned leftover project if one exists (historically named
default). That id is not special.Later users start with no projects — they create one from the desk. They never see someone else's leftover.
The header select is the active project. Snapshot, live events, and Add log follow that project.
+ Projectcreates another project and switches to it.
Username: 3–32 characters, starts with a letter. Password: at least 8 characters. Session cookie: synco_session (httpOnly, 30 days).
Agents do not log in. The project they write to is the one selected in the dashboard picker. projectId on MCP tools is optional and ignored when a desk selection exists.
Security
Without
SYNCO_API_KEYthe server refuses to bind anything except loopback.Dashboard
/api/*(except register/login/status) requires a signed-in user who is a member of that project./mcpstill uses the optional sharedSYNCO_API_KEY, not the dashboard password.Do not expose
/mcpor/apito the internet without a key.Secrets and tokens are not logged.
This is username/password on a self-hosted box, not OAuth or per-agent ACLs.
Tests
npm testCoverage includes transactional task claims, idempotent task creation, resource overlap, declared ChangeReports, handoffs, SSE fan-out, and MCP Streamable HTTP tool calls.
Limitations (MVP)
ChangeReports are declared by agents. There is no Git verification.
Resource claims warn; they do not lock files or prevent merge conflicts.
Dashboard live updates do not imply that an LLM host will interrupt a running agent.
One Node process, in-memory EventBus. Not multi-instance.
No OAuth, RAG, vector DB, or agent orchestration engine.
Dashboard users only see projects they own.
defaultis not shared and is not the live desk unless someone actually selects it.Agent ids are global. Registering again on a new desk selection moves the agent to that project.
An agent is marked
offlineafterSYNCO_AGENT_OFFLINE_MS(default 5 min) without a tool call. It is inferred fromlastSeenAt, not a real heartbeat.
Stack
Node.js 20+, TypeScript, @modelcontextprotocol/sdk v1.x (currently installed from npm), Zod, Express, better-sqlite3, Drizzle ORM, Vite, React, Tailwind CSS, Vitest.
v1 is a compatibility choice for current Cursor / Claude Code hosts. The official SDK main branch is v2; this repo can migrate later.
Scripts
Command | What it does |
| HTTP + MCP + dashboard |
| HTTP with reload |
| Vite on :5173, proxies API to :3847 |
| MCP over stdio |
| Vitest |
This server cannot be deployed
Maintenance
Related MCP Connectors
- ParleyOAuthdev.weldra
Coordination hub for AI coding agents: message teammates, ask humans, audit every event.
Shared control plane for AI coding agents — tasks, memory, decisions, file locks. 12 tools.
The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.
Coordination for AI coding agents: declare plans, catch design conflicts early, share team memory.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables multiple AI agents like Claude and Codex to coordinate on the same project through shared tasks, file locks, and a real-time dashboard, preventing conflicts and streamlining collaborative development.8 npm1MIT
- AlicenseAqualityBmaintenanceLocal-first shared memory and coordination layer for AI coding agents, with repository evidence, reservations, handoffs, code graph context, and dashboard review backed by PostgreSQL/pgvector.303Apache 2.0
- AlicenseNot gradedqualityAmaintenanceCoordinates parallel AI coding agents by providing task ownership, scoped file locks, handoffs, and verification workflows.MIT
- AlicenseNot gradedqualityBmaintenanceEnables multiple AI coding agents to collaborate on a project by coordinating tasks, file leases, and messages through a shared hub, preventing conflicts and enabling parallel development.MIT