Repository Structure
The system is split across two repositories: a server repo (RoamerMcp) that owns the hosted MCP server and its data model, and a distribution repo (RoamerMcpPlugin) that packages a stdio bridge into an installable Agent Skills plugin — a format multiple AI coding clients can consume as-is, from Claude Code to GitHub Copilot, not a Claude-specific integration. The server never ships client-side install logic; the plugin never touches the database directly.
Both repos' bridge scripts are kept in lockstep by hand and pinned to the same published version of the roamer-device-auth package — a change to the auth flow lands as a coordinated commit across both repos, never one alone.
Core Systems
16 tools cover the full surface — registry (get_registry, update_project), specs (list_specs, get_spec_area, log_spec_item), threads (get_threads, log_thread), blockers, and billing. The same data is also exposed read-only as MCP resources under a roamer:// URI scheme — roamer://project/{name}.md, roamer://spec/{name}/{area}.md — so a client can @-reference project state directly without a tool round trip.
The ecosystem overview a session opens with isn't a file — it's BrainSection rows composed at request time by BrainComposer. Sections that mirror the registry, specs, or threads use {{live:*}} tokens that expand against the database on every read, so the parts most likely to drift — project counts, open thread lists, spec item totals — structurally can't. Editing is a tool call (log_brain_section); there is no rebuild or redeploy step.
Spec / SpecArea / SpecItem model each project's requirements as versioned, queryable records instead of markdown a human has to remember to update. Every item carries a priority and a status (draft → ready → code-complete-*), which is what lets "done" be computed rather than asserted — see The Arc Model, below.
EngineeringThread holds the open questions, live incidents, and deferred decisions that don't yet belong in a formal spec — cross-cutting, tied to one or more projects, and explicitly separate from spec items so exploratory reasoning never gets forced into a done/not-done shape before it's ready.
Clients authenticate through a local stdio bridge — a device-code flow against Entra ID, brokered at the edge by Cloudflare Access Managed OAuth (PKCE, public client, no secret to leak). The claude.ai browser connector instead talks HTTP directly, using its own separately-registered Cloudflare OAuth client.
Tenant isolation, Owner/Member roles, and per-seat entitlement checks are enforced server-side on every call, not left to the client to respect. create_checkout_session and create_billing_portal_session wrap Stripe Checkout and the Billing Portal directly, gated to tenant Owners.
Project tracks exactly where a project sits on its arc — spec-item status, branch and session history, environments, and both resolved and unresolved blockers — backed by an Azure SQL schema managed through EF Core migrations.
The container sits behind a Cloudflare Tunnel and Cloudflare Access, so every request — from a person or from another service — is authenticated at the edge before it ever reaches the app.
The Arc Model
An arc is the unit of work Roamer MCP actually trusts. Not a status someone typed into a ticket, but something computed live from real data — so "done" means done, not "I think it's done" or "it compiled." It's the mechanism that lets an AI-native workflow claim completion without asking a person to go double-check the claim by hand.
One arc = one spec area — a feature, migration, or fix, tracked from its first spec item through genuine completion, however many threads or branches it touches along the way.
An arc is finished when every blocker, critical, and major item in its area reaches a code-complete status — driven to zero, not just mostly done.
Minor and unprioritized items don't block the arc. They're real backlog — just not the bar for calling the work done.
A spec item's status describes the code and its test coverage — nothing more. Roamer MCP separately tracks whether that code actually shipped: a project's real deployment state is compared against its arc position, so work that's "code-complete" but sitting unmerged, or merged but never deployed, doesn't get to call itself done.
- Closure is pulled, not pushed — a loose thread tied to a finished piece of work surfaces for review on its own, instead of being quietly forgotten.
- Identity survives reorganization — splitting or merging a spec area never duplicates or desyncs an item's status.
- Idle work resurfaces on its own — nothing goes stale silently; it gets flagged for a fresh judgment call instead of aging out of sight.
- Coverage is a gate, not an afterthought — done means tested, not just written.
Request Flow
Every client — Claude Code, VS Code, Cursor, Claude Desktop — talks to the same hosted server through one of two paths, chosen by whether the client can spawn a local process. Stdio-first was a deliberate choice over building per-client OAuth/Dynamic Client Registration for each one.
The client spawns a local process (npx roamer-device-auth) over stdio — it never opens the HTTPS connection itself.
First run only: the bridge starts an Entra ID device-code flow, persisting the pending flow to disk so a respawned process resumes it instead of minting a new code.
Cloudflare Access validates the resulting token at the edge before any request reaches the container.
Subsequent tool calls reuse the cached token — no repeated browser round trips.
claude.ai's connector talks HTTP directly and authenticates via its own, separately-registered Cloudflare Managed OAuth client — it never touches Entra or the stdio bridge.
- One device-code flow, cached locally, works identically across Claude Code, VS Code, and Cursor.
- No per-client Dynamic Client Registration to build or maintain going forward.
- Cloudflare Access still gates every request at the edge, regardless of transport.
- The browser-hosted connector is the one deliberate exception — it already has its own working OAuth path.
Design Principles
Docs are live queries, not files
The brain overview, registry, and specs are composed from the database on every read. There is no markdown file that a human has to remember to update, and none that can quietly fall out of sync with what's actually true.
One arc, one spec area, gated by priority
A feature counts as done when every blocker, critical, and major item in its spec area reaches code-complete status — directly computable from the data, not a status someone declared in standup. See The Arc Model, above, for the full definition.
stdio-first authentication
Any client that can spawn a local process authenticates once via a cached, reactively-invalidated device-code flow. Only the browser-hosted connector — which already has its own working OAuth path — talks HTTP with server-brokered auth directly.
Threads for what specs can't yet formalize
Open questions, live incidents, and deferred decisions get their own tracked, project-linked record instead of disappearing into chat history or getting force-fit into a spec item before they're ready to be one.
Multi-tenant from the start
Owner/Member roles, per-seat Stripe billing, and tenant-scoped data isolation are enforced server-side as core parts of the data model — not a retrofit bolted onto a server that was originally built single-tenant.