scoped
Provides visibility into file claim/release activity by posting comments to Linear issues when agents claim or release files, if LINEAR_API_KEY is configured.
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., "@scopedClaim the files for issue #42 before starting the refactor."
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.
scoped
Part of sous: tools for checking what coding agents actually do.
scoped stops two Claude Code sessions from editing the same file at the same time: file claims between concurrent sessions, enforced by a PreToolUse hook, with an MCP server for claim, release, check and status.
Why
Several Claude Code sessions against one codebase is normal now: one per Linear issue, one per worktree, several in the same repo. Nothing stops two of them from editing the same file at once.
Related MCP server: Blackboard MCP
How it works
Enforcement. A PreToolUse hook runs before every Edit, MultiEdit, Write and NotebookEdit. If another session holds the file, the edit is denied with a reason that names the holder. If the file is free, the hook claims it for the calling session, so enforcement does not depend on the model calling anything first.
Tools. For planning and visibility:
claim(issue_id, file_paths, session_id): reserve files before a multi-file change, so a conflict shows up before you start.release(issue_id, session_id): free claims on completion or handoff instead of waiting for them to expire.check(file_path): is this file claimed, and by whom.status(): every active claim, by session and issue.
Locking is local. Claims live in SQLite (node:sqlite, no extra dependency) at ~/.scoped/claims.db. A unique key on the canonical file path makes claiming atomic: two sessions racing for one file get one winner.
One identity for hook and server. Claude Code gives an MCP server no session id that matches the one a hook receives. A SessionStart hook puts the real session_id into the session's context, and claim/release require it, so an explicit claim and the hook's enforcement agree on the owner and a session is never blocked by its own claim.
Stale claims expire. Every read reaps dead claims first. A claim made through the MCP server records the server's pid and dies with it (same host). The hook exits after every call, so its claims have no live pid; they expire by TTL instead, and each edit by the owning session restarts that TTL.
Linear is visibility, not the lock. With LINEAR_API_KEY set, explicit claim/release calls post a comment on the issue. The hook's automatic claims stay local and silent. See SECURITY.md for what is sent.
Install
As a plugin
claude plugin marketplace add OrenSegal/scoped
claude plugin install scoped@scopedThe plugin registers both hooks, the MCP server, the scoped CLI (the plugin's bin/ is on the Bash tool's PATH) and the /scoped:* slash commands. On first start the MCP launcher installs the server's runtime dependencies into ${CLAUDE_PLUGIN_DATA}/deps/<lockfile hash>/ with npm ci --omit=dev --ignore-scripts, in a temp dir that is renamed into place, so it survives plugin updates, works on a read-only plugin directory, and two sessions starting together don't corrupt it. That needs npm and the network once. Without them the server does not start and says why on stderr (/mcp shows it); the hooks and the CLI need no dependencies and keep enforcing.
From a clone
Requires Node.js 22.13.0 or later (23.4 or later on the odd-numbered line), where node:sqlite needs no flag, and the claude CLI on your PATH.
git clone https://github.com/OrenSegal/scoped.git
cd scoped
npm install
npm run setupnpm run setup registers the MCP server at user scope (claude mcp add scoped --scope user -- node <repo>/bin/scoped-mcp) and adds the SessionStart and PreToolUse hooks to the global ~/.claude/settings.json, written atomically. It is safe to re-run: anything already registered, at any scope or from any checkout path, is left alone. It refuses, changing nothing, when scoped is already installed as a plugin, because every hook would then run twice (--force overrides). Restart running sessions afterwards. npm run uninstall removes only what setup added.
From a clone the CLI is node bin/scoped, or npm link to put scoped on your PATH.
MCP server, in your MCP config:
{
"mcpServers": {
"scoped": {
"command": "node",
"args": ["/absolute/path/to/scoped/src/index.mjs"],
"env": { "LINEAR_API_KEY": "optional, enables the visibility comments" }
}
}
}Hooks, in the global ~/.claude/settings.json (append to existing SessionStart/PreToolUse arrays rather than replacing them):
{
"hooks": {
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "node \"/absolute/path/to/scoped/hooks/session-start.mjs\"", "timeout": 15 }] }
],
"PreToolUse": [
{
"matcher": "Edit|MultiEdit|Write|NotebookEdit",
"hooks": [{ "type": "command", "command": "node \"/absolute/path/to/scoped/hooks/pretool-enforce.mjs\"", "timeout": 15 }]
}
]
}
}CLI and slash commands
Command | Slash command | What it does |
|
| Every active claim: file, issue, session (8-char prefix), age, time left |
| Exit 1 and name the holder if | |
|
| Free every claim a session holds. A prefix needs at least 4 characters and must match one session; deny messages print the 8-character one |
| Reap expired and dead-pid claims now (every read does this anyway) | |
|
| Blocked edits from the block log (default 30 days) by file, session pair and issue, with what to change if one file keeps colliding |
|
| Node and |
/scoped:release with another session's id shows that session's claims and asks before releasing them.
Configuration
All optional. Set them in the environment Claude Code starts with; hooks inherit it.
Variable | Default | Effect |
|
| Directory for the claims db, block log and (outside a plugin) the MCP server's dependencies |
|
| The claims db alone. Sessions that should see each other must share it |
|
| Block log path, or |
|
| Issue the hook files its automatic claims under, e.g. |
| unset |
|
|
|
|
|
| How long |
| unset | Stack traces instead of one-line errors on stderr |
| unset | Linear comments on explicit |
Fail open or closed. By default an internal hook error (no node:sqlite, an unwritable or corrupt db, a malformed payload) lets the edit through, because a coordination bug should not stop real work. It is not silent: the first such error in a session shows the user "edits in this session are NOT enforced", with the reason, and every one goes to stderr. With SCOPED_FAIL_CLOSED=1 the same error denies the edit. SQLite waits at most 5 s for a locked db, well inside the hooks' 15 s timeout, so a stuck db becomes one of these errors rather than a killed hook.
Troubleshooting
Start with scoped doctor (or /scoped:doctor): it names the broken piece and exits 1.
Edits aren't blocked, or claims never show up. Doctor says whether each hook is registered, and where. Hooks apply only to sessions started after registration: restart the session.
The
scopedMCP server shows as failed in/mcp. Its stderr says why: Node too old fornode:sqlite, nonpmonPATH, or the dependency install failed (offline). The hooks keep enforcing meanwhile.claim/releasefail for a missingsession_id. TheSessionStarthook supplies it; restart a session that was already running when scoped was installed.A hook runs twice. scoped is registered both as a plugin and through
npm run setup. Runnpm run uninstallin the clone, or remove the plugin.
The hook costs one Node process start per edit, tens of milliseconds; the claim itself is well under a millisecond. Doctor measures it on your machine.
Limitations
Only Claude Code's edit tools. A
Bashcommand that writes a file (sed -i,>, a codegen script), or a process outside Claude Code, is not blocked.Fails open by default. See Configuration.
One machine. Claims are in a local SQLite file, and pid-based reaping only works on the same host.
Worktrees are separate files. Claims key on the canonical absolute path (symlinks and
..resolved), sosrc/app.jsin two worktrees is two files; that collision surfaces at merge.Case-insensitive file systems, partly. On macOS an existing file resolves to its on-disk case, so
app.jsandApp.jsare one claim. Two spellings of a file that does not exist yet are two claims until it is created.Windows is untested. CI runs Linux and macOS. Claim keys fold drive letter and case on Windows (tested with
path.win32), and the launcher usesnpm.cmd, but nobody has run the hooks there. Reports welcome.Per file, not per region. Two sessions cannot edit different functions of one file at the same time.
Hook claims expire by TTL. A session that stops editing a file but keeps running holds it until the TTL runs out or it calls
release.Not yet measured on a real fleet. The claim race is tested with real processes sharing one db (
test/concurrency.test.mjs,test/hook.test.mjs); a count of collisions prevented in real use will be published once there is one.
Development
npm install
npm testTests run the store directly against a temp SQLite file, and the hooks, MCP server, launcher, CLI and setup script as real subprocesses with HOME, SCOPED_HOME and CLAUDE_CONFIG_DIR pointed at temp dirs. evals/ is a claude plugin eval suite, not run in CI because runs are paid. See CONTRIBUTING.md.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
- AxisOAuthdev.useaxis
Coding agents from Claude Code, Cursor and Codex claim jobs and lock files on one shared board.
The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.
Shared control plane for AI coding agents — tasks, memory, decisions, file locks. 12 tools.
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenancePrevents AI coding agents from conflicting by coordinating file claims and resolving conflicts in real-time across multiple sessions.52 npm1MIT
- AlicenseNot gradedqualityDmaintenanceCross-session coordination server for Claude Code that manages file claims, build locks, shared knowledge, and provides a real-time dashboard to prevent conflicts across multiple sessions.30 npmMIT
- AlicenseAqualityBmaintenanceEnables multiple AI agents to collaborate on the same git repository by coordinating work via a shared claims branch, detecting file conflicts before they happen.927 PyPIPolyForm Noncommercial 1.0.0
- AlicenseAqualityDmaintenanceProvides file-level exclusive locking across multiple Claude Code instances to prevent edit conflicts when multiple agents edit the same project simultaneously.4MIT