Roamer MCP — Technical Architecture

Project truth,
composed live.

Roamer MCP is a hosted Model Context Protocol server that gives AI coding assistants a persistent, structured memory of a project — registry state, behavioral specs, and engineering threads, served as live database queries instead of markdown files that quietly go stale.

Runtime
.NET 10 / ASP.NET Core, on Azure Container Apps
Protocol
Model Context Protocol — streamable HTTP, direct from every client
Database
Azure SQL, schema managed via EF Core migrations
Auth
Built-in OAuth authorization server — Dynamic Client Registration, PKCE public clients, Entra ID sign-in
Client bridge
Optional: roamer-device-auth (npm), a local stdio bridge to the same endpoint
Edge / gateway
Cloudflare Tunnel — edge TLS and routing, no public ingress on the origin
Billing
Stripe — Checkout + Billing Portal, per-seat
Distribution
Optional Agent Skills plugin (Claude Code, GitHub Copilot, etc.) — dedicated plugin repo, one skill per workflow
CI/CD
GitHub Actions → Azure Container Apps

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 the workflows as an optional, 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 plugin is a convenience on top of the hosted connection, never a requirement. The server never ships client-side install logic; the plugin never touches the database directly.

RoamerMcp (server repo) ├── RoamerMcp/ │ ├── Models/ # Project, BranchSession, Blocker, ProjectEnvironment, │ │ # Spec, SpecArea, SpecItem, EngineeringThread, BrainSection │ ├── Tools/ # MCP tools — get_registry, log_spec_item, log_thread, │ │ # create_checkout_session, etc. │ ├── Resources/ # Brain, registry, spec and prompt resources — │ │ # serve the roamer:// URI scheme │ ├── Brain/ # BrainComposer — expands {{live:*}} tokens at request time │ ├── Prompts/ # WorkflowPrompts — the server-side workflow prompts │ └── Migrations/ # EF Core schema migrations │ ├── docs/ARCHITECTURE.md # Architecture reference for the server itself └── infra/main.bicep # Azure Bicep IaC RoamerMcpPlugin (distribution repo) ├── skills/ # One thin skill per server workflow prompt ├── .mcp.json # Points at the hosted HTTP endpoint └── .claude-plugin/ # Plugin manifest and marketplace listing

Each skill is a thin entry point that fetches its workflow text from the server at run time, so changing a workflow is a server deploy — no plugin release, and no copy of the workflow living anywhere but the server.

Core Systems

MCP Tools & Resources

The 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.

Dynamic Brain

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.

Behavioral Specs

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.

Engineering Threads

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.

Auth & Transport

Roamer MCP runs its own OAuth authorization server. Clients connect over streamable HTTP, discover it from the server's protected-resource metadata, register themselves with Dynamic Client Registration, and sign the user in through Entra ID with PKCE — public clients only, so there is no client secret to distribute or leak. Every request carries a Roamer-issued token, validated by the server itself.

Multi-Tenancy & Billing

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.

Data Layer

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.

Deployment & Edge

The container sits behind a Cloudflare Tunnel, so the origin has no public ingress. Cloudflare terminates TLS and routes traffic; the app authenticates every request itself, from a person or from another service.

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.

Formal definition
1

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.

2

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.

3

Minor and unprioritized items don't block the arc. They're real backlog — just not the bar for calling the work done.

Code-complete isn't the finish line

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.

Why an arc, not a checklist
  • 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, claude.ai, Claude Desktop, VS Code, Cursor — talks to the same hosted endpoint over streamable HTTP, using standard MCP authorization. Nothing needs to be installed locally, and no client ID or secret is handed out.

Every client — direct HTTP with standard OAuth
1

The client calls /mcp without a token and gets a 401 pointing at the server's protected-resource metadata.

2

It discovers Roamer MCP's own authorization server and registers itself with Dynamic Client Registration — a public client using PKCE.

3

The user signs in through Entra ID in the browser; the authorization server issues a short-lived Roamer access token and a refresh token.

4

Every later request carries that token, and the server validates it itself before resolving the caller's tenant.

Optional local bridge

Clients that prefer a local process can still run npx roamer-device-auth, a stdio bridge that relays every call to the same endpoint and signs in through the same authorization server.

Why one HTTP endpoint with standard OAuth
  • The same connection works on every MCP client, including ones that can't start a local process, such as web and mobile.
  • Dynamic Client Registration means no per-client setup to build, and no client ID or secret for users to copy.
  • Every request carries its own token, so a connection never runs on a stale cached identity.
  • Tools, workflows and resources all come with the connection; the plugin only adds slash commands on top.

Design Principles

01

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.

02

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.

03

One endpoint, standard authorization

Every client connects to the same hosted endpoint and signs in through the server's own OAuth authorization server, with Dynamic Client Registration and PKCE. No client secret, header or local install stands between a user and a working connection.

04

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.

05

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.