Archer MCP server
Archer's MCP server lets an AI agent do what you do in the app: read and write architecture documents, manage your component types, review history, and record whether the code still matches the design. It works with any client that speaks the Model Context Protocol: Claude Code, Claude, Cursor, VS Code, Codex and more.
- Address:
https://tryarcher.dev/mcp(Streamable HTTP) - Sign-in: in your browser, with GitHub. There are no API keys to create or paste.
- Tools: 26, covering projects, the language, component types, the canvas, history, drift, workspaces and feedback. See the tool reference.
The agent acts as you, in the workspaces you belong to, with your role in each. Everything it saves is checked the same way as the canvas and recorded in the project's history under the agent's name.
Connect an agent
Add the server to your client, then sign in when it asks. Signed in to the app, Connect an agent in the account menu shows the same commands for the deployment you're using.
Claude Code
claude mcp add --transport http archer https://tryarcher.dev/mcp
Then run /mcp in Claude Code, choose archer, and pick Authenticate.
Your browser opens to sign in to Archer.
The server is added for the current project, for you only. --scope user
adds it for all your projects. --scope project writes it to the project's
.mcp.json instead, to share with your team (see
Set up a repository). Claude Code's
MCP documentation has more options.
Claude (web and desktop)
Add Archer as a custom connector: in Claude, go to Customize → Connectors,
choose Add custom connector, and paste
https://tryarcher.dev/mcp. Then Connect and sign in. Archer needs
no OAuth client ID or secret: if Claude asks how to register, choose automatic
registration. On Team and Enterprise plans an owner adds the connector for the
organization first. See
Custom connectors.
Cursor
Add the server to .cursor/mcp.json in your project, or to
~/.cursor/mcp.json for every project:
{
"mcpServers": {
"archer": {
"url": "https://tryarcher.dev/mcp"
}
}
}
Then connect archer in Cursor's MCP settings and sign in.
VS Code
For GitHub Copilot's agent mode, add the server to .vscode/mcp.json, or run
MCP: Add Server from the Command Palette:
{
"servers": {
"archer": {
"type": "http",
"url": "https://tryarcher.dev/mcp"
}
}
}
VS Code opens the browser to sign in the first time the server starts. See MCP servers in VS Code.
Codex
codex mcp add archer --url https://tryarcher.dev/mcp
Codex detects the sign-in and opens the browser. To sign in again later, run
codex mcp login archer. See Codex MCP.
Gemini CLI
gemini mcp add --transport http archer https://tryarcher.dev/mcp
Then run /mcp auth archer in Gemini CLI to sign in. See
MCP servers with Gemini CLI.
Other clients
Add a remote MCP server that uses the Streamable HTTP transport, at
https://tryarcher.dev/mcp. Don't configure headers or keys: the
client finds the sign-in on its own, registers itself, and opens your
browser. The details are under Protocol.
Clients that only run local servers
Some clients can only start a server as a local command (stdio). For those, the Archer CLI relays the hosted server. Sign in once:
npx archerctl login
Then register the command npx -y archerctl mcp. For example, in Claude
Desktop's claude_desktop_config.json:
{
"mcpServers": {
"archer": {
"command": "npx",
"args": ["-y", "archerctl", "mcp"]
}
}
}
The relay passes every message to the hosted server with the CLI's sign-in,
so the tools are identical. Saves are attributed to "Archer CLI". Until you
sign in, the relay answers every request with an error telling you to run
archer login; after that, reconnect the server in your client.
Set up a repository
A project's .mcp.json tells Claude Code (and other clients that read it)
which servers the project uses. The CLI writes the
Archer entry for you, along with a starter document:
npx archerctl init
{
"mcpServers": {
"archer": {
"type": "http",
"url": "https://tryarcher.dev/mcp"
}
}
}
The file names only the server. Commit it: each teammate, and each agent, signs in for themselves the first time they connect.
Other deployments
A self-hosted Archer, or a local development server, serves the same MCP
server at its own /mcp. Use that address instead, for example
http://localhost:3000/mcp.
Sign-in and access
Connecting a client starts the standard MCP sign-in. The client registers itself with Archer's sign-in service, your browser opens, you sign in to Archer with GitHub and approve the client, and the client receives a token it renews on its own. Nothing is copied by hand, and nothing secret is stored in your project.
From then on the agent acts as you:
- It sees every workspace you belong to, and can do what your role allows in each. Owners and editors can use every tool. Viewers can use every tool that doesn't change the workspace; their writes are refused.
- Everything it saves is recorded in the project's history under the client's name, as registered at sign-in ("Claude Code", "Cursor", "Archer CLI"…), with the one-line summary the agent gives. The canvas highlights what an agent changed until you review it.
- Sharing links, workspace members and invitations, and image export stay in the app.
To disconnect an agent, remove the server from the client or clear its
sign-in there (in Claude Code, from /mcp).
Workspaces
You have a Personal workspace, and you may belong to team workspaces. Project
tools act on one of them, the current workspace, which starts as
Personal. An agent switches with use_workspace, and the CLI with
archer workspace.
The current workspace is stored with your account, not in the client. When one agent switches, every agent you've connected and the CLI switch with it. That's why agents are told to switch only when you ask them to, and to say which workspace they're working in.
What agents do with it
A project is one Archer document, the same text you see in the editor's code view. The canvas is drawn from it, so when an agent saves, open editors update at once. The main things agents do:
- Learn the language. Agents are told to read the guide (
get_guide) before they write anything. It holds the workflows, the error codes and the complete language specification. The worked examples are available too (list_examples,get_example). - Map a codebase. The agent reads the repository and saves what it finds
as a new project (
create_project), withsrcpaths that point back into the code. - Change a design. The agent reads the project (
get_project), edits the source and saves the whole document (update_project). Your arrangement of the canvas is kept. - Work in a repository-stored project. A project can live in a
.archfile in its GitHub repository (created From GitHub in the app, or moved there with Move to GitHub).get_projectthen returns the file asrepository.path, with its sync state, and tells agents working in that repository to edit the file and commit it with the code.update_projectstill works, but only changes the project's unpublished draft (the result says so), which a person publishes back to the repository as a pull request. - Explain what changed.
get_historyanddiff_projectshow who changed what, and when.get_revisionreturns any past version, to compare or restore. - Hand off a spec.
compile_for_agentreturns the document as an implementation spec for a coding agent. It's the same text as Copy for agent in the editor. - Check for drift. The agent compares each component with the code and
reports what's missing, changed or extra (
report_drift). Findings appear as badges on the canvas, and you can add the extra parts to the document in one click.
Things to ask your agent:
- "Map this repository into a new Archer project."
- "Add a Redis cache in front of the orders database in the Shop project."
- "What changed in the Shop architecture since yesterday?"
- "Check the Shop project against this repository and report drift."
- "Implement the Shop project's API, following its spec."
What the server guarantees
- Nothing broken is saved. A document with errors, such as a connection to a component that doesn't exist, is rejected with its diagnostics, and nothing changes. The agent fixes the errors and tries again.
- Your layout survives. Agents send the architecture without the
layoutblock. Components you've placed keep their positions, and only new ones are placed automatically. - Every save is reviewable. Each agent save is its own revision, with its author, time, summary and a count of what changed.
Tool reference
This part of the page is generated from the server itself (version 1.7.0, 26 tools). Each tool's title, description and parameters are what your agent reads when it lists the tools, so the descriptions speak to the agent. Every tool has an example call and its result, all in one scenario: you have a Personal workspace and are an editor in the team workspace Acme, and Shop is a project that Claude Code created and you then edited on the canvas.
Guide and examples
Reading for the agent. get_guide comes first: everything else on the server assumes the agent knows the language, and the guide ends with the whole specification.
get_guide
Get the Archer guide · Stateless
Archer is an architecture-design tool: a drag-and-drop canvas for humans and a strict text language (".arch" documents) for agents, compiling losslessly into each other. Through this MCP server you can do everything a human can do in the app: create/read/update/delete projects, design systems (components nested to any depth, planes that group them, connections, validated schema payloads), define custom component types, and control presentation.
Returns the complete agent guide: the mental model (project = one text document; canvas is a live render of it), standard read/modify/create workflows, how validation gates writes, layout rules, an error-code reference, writing advice with examples, and the full language specification. CALL THIS ONCE BEFORE YOUR FIRST WRITE in a session — everything else on this server assumes you know the language. Also available as the resource archer://guide.
No parameters.
Example result
# Archer MCP Server — Agent Guide
Archer is an architecture-design tool: …- Markdown: the mental model, the standard workflows, how validation gates writes, the error codes, advice on writing good documents, then the complete language specification. The same text is the resource
archer://guide.
list_examples
List the worked examples · Stateless
List the worked examples from Archer's docs (the /language/examples page): complete documents that validate clean, each showing a few language features, from the basics up to a whole product. Returns each one's slug, title, summary and features, with two links to hand the user: docs_url (its section of the docs, diagram included) and sandbox_url (opens it in the editor without an account; nothing is saved). Read one with get_example; each is also the resource archer://examples/<slug>.
No parameters.
Example result
[
{
"slug": "components-and-connections",
"title": "Link Shortener",
"summary": "The smallest useful document: a header, a few typed components and the connections between them. …",
"features": [
"components",
"types",
"connections",
"labels"
],
"docs_url": "https://tryarcher.dev/language/examples#components-and-connections",
"sandbox_url": "https://tryarcher.dev/sandbox/components-and-connections"
},
…
]- The documents on the Examples page.
sandbox_urlopens one in the editor without an account; nothing there is saved.
get_example
Read a worked example · Stateless
Return one of the docs' worked examples in full: its list_examples entry plus the complete source, in the canonical form the app saves. Read one to see idiomatic Archer before writing (ids, type tags, desc and src, payloads, planes, nested components, labeled connections), to show the user how something is done, or to give them an editable copy by passing the source to create_project. An example that ends in a layout block was arranged by hand on the canvas: that block is presentation, so leave it out of documents you write.
Parameters
slug"components-and-connections" | "descriptions-and-payloads" | "shapes" | "icons" | "planes" | "nesting" | "music-streaming"required- Which example; list_examples describes each.
Example call
{
"slug": "components-and-connections"
}Result
{
"slug": "components-and-connections",
"title": "Link Shortener",
"summary": "The smallest useful document: …",
"features": [
"components",
"types",
"connections",
"labels"
],
"docs_url": "https://tryarcher.dev/language/examples#components-and-connections",
"sandbox_url": "https://tryarcher.dev/sandbox/components-and-connections",
"source": "arch \"Link Shortener\" v0.2\n\ncomponent browser \"Browser\" @browser {\n shape browser\n}\n…"
}sourceis canonical and validates clean, so it can go straight tocreate_projectto give the user an editable copy. Each example is also the resourcearcher://examples/<slug>.
Language
These read the source you pass and save nothing, so they need no project. validate_source and compile_for_agent also know the current workspace's custom types.
validate_source
Validate Archer source · Reads the current workspace
Parse and validate an Archer document WITHOUT saving anything. Returns { valid, errors, warnings } with codes and line numbers (see the guide's error reference). Use this to check work-in-progress source before create_project/update_project, or to explain why a document is invalid. Errors block writes; warnings never do. Validation knows the built-in catalog AND this workspace's custom types.
Parameters
sourcestringrequired- Full Archer document text.
Example call
{
"source": <source below>
}source
arch "Shop" v0.2
component api "API" @vercel.functions
component api "Orders DB" @postgres
api -> payments @httpResult
{
"valid": false,
"errors": [
"E002 [error] line 3: Duplicate component id 'api' in the top level.",
"E101 [error] line 4: Connection target 'payments' does not resolve to a declared component."
],
"warnings": []
}- The same checks as the write gate on
create_projectandupdate_project: a source that validates clean will save. Only errors makevalidfalse.
format_source
Format Archer source · Stateless
Return the canonical form of an Archer document (fixed section order, sorted connections, normalized indentation and fences). Does not save anything and does not require validity beyond being parseable. The app always stores canonical form, so diff your changes against this.
Parameters
sourcestringrequired- Full Archer document text.
Example call
{
"source": <source below>
}source
arch "Shop" v0.2
component db "Orders DB" @postgres
component api "API" @vercel.functions
api->db @sqlResult
arch "Shop" v0.2
component db "Orders DB" @postgres
component api "API" @vercel.functions
api -> db @sql- Components and planes keep the order they were written in (a plane goes where its first component was); connections are sorted. A
layoutblock is kept but never added (that'sauto_layout_source).#comments are dropped, since they aren't part of the document. - It formats whatever parses: after a syntax error (E001) the output can be missing the broken part and what the parser skipped after it. Call
validate_sourcefirst.
compile_for_agent
Compile an implementation spec · Reads the current workspace
Compile an Archer document into the agent-handoff form: canonical text, including a complete layout block (where each component sits on the canvas, for context), with a short reading-guide preamble prepended. This is what you paste to a coding agent (or use yourself) as the implementation spec. Pass either raw source OR a project_id (loads the stored document). Fails if the document has validation errors.
Parameters
sourcestring- Archer source (omit when passing project_id)
project_idstring- Stored project to compile (omit when passing source)
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5"
}Result
# This is an Archer architecture document — a complete, machine-readable
# specification of a software system, exported from a canvas the author designed.
# Treat it as the source of truth for the system's intended architecture.
#
# How to read it:
…
# - If you write Archer back, follow the grammar above and leave the `layout`
# block out: Archer keeps every surviving component where it was and places
# new ones itself. Only write layout when asked to arrange the diagram.
arch "Shop" v0.2
component web "Storefront" @vercel.nextjs {
shape browser
}
plane backend "Backend" {
component api "API" @vercel.functions {
src "apps/api"
}
component db "Orders DB" @postgres {
shape cylinder
}
}
component stripe "Stripe" @stripe {
shape cloud
}
api -> db @sql
api -> stripe @http : "charges"
web -> api @http
layout {
api: { at: [1, 2], size: [4, 3] }
backend: { at: [7, 0], size: [13, 7] }
db: { at: [8, 2], size: [4, 3] }
stripe: { at: [0, 9], size: [4, 3] }
web: { at: [0, 2], size: [4, 3] }
}- Pass
sourceorproject_id. The output is what the editor's Copy for agent puts on the clipboard. - The layout block is always complete: every component has the position and size the canvas draws it at, so the agent sees the diagram as the author laid it out.
- A document with errors is refused: the error result is
{ message, errors }, with the diagnostics.
auto_layout_source
Auto-layout Archer source · Stateless
Complete or rebuild the layout block of a raw Archer document (no saving). Each canvas (the top level, and the inside of every component with nested components) is laid out on its own. A canvas with no positions is arranged by the layered auto-layout (connections flow left to right with few crossings and straight edges, planes wrap what's on them); otherwise existing positions are kept and new components are placed next to what they connect to. reset=true discards every position and size and arranges everything from scratch. You rarely need this — create_project/update_project auto-layout for you — but it's useful to preview canonical output or fully re-arrange.
Parameters
sourcestringrequired- Full Archer document text.
resetboolean- Discard existing positions and re-place everything (default false)
Example call
{
"source": <source below>
}source
arch "Shop" v0.2
component api "API" @vercel.functions
component db "Orders DB" @postgres
api -> db @sqlResult
arch "Shop" v0.2
component api "API" @vercel.functions
component db "Orders DB" @postgres
api -> db @sql
layout {
…
}- Each entry is
path: { at: [column, row], size: [width, height] }on the canvas grid, for components and planes alike. Something on a plane is placed relative to the plane, and each component's inside is its own canvas. Withreset: trueevery position is recomputed. create_projectandupdate_projectlay out for you, so this is for previewing or re-arranging a source before you save it.
Component types
A type tag gives a component its icon and color. Tags are optional, and an unknown one is only a warning. Custom types belong to the workspace: one agent creates it, and it appears in every member's palette.
list_types
List component & connection types · Reads the current workspace
List usable type tags: the built-in catalog (~350 types — vendor-neutral building blocks grouped by concern in generic/ai/network/compute/storage/messaging/data/observability/security/platform/topology/patterns, e.g. @load_balancer, @rate_limiter, @cdn, @queue, @database, with ai holding the building blocks of AI apps: @llm, @agent, @mcp, @rag, @vector_db, ...; plus real technologies across ai_platforms/aws/gcp/azure/vercel/databases/infra/saas), this workspace's custom types, the standard connection types (@http, @sql, @queue, @mcp, ...), the shapes a component can be drawn as (shape cylinder, cloud, person, robot, ... each with what it usually stands for), and the icons a component can show (icon shopping_cart, icon database, ... Phosphor icons in snake case, by group, where Phosphor's own name also works for a catalog icon listed under another, e.g. hard_drives for server; a custom type's glyph is one of them too), and the colors a component can be drawn in (color blue, color green, ... or color "#rrggbb"). Optional search filters by tag/name/namespace. Type tags are OPTIONAL on components — untyped components are valid and render as generic boxes; unknown tags are only a warning.
Parameters
searchstring- Case-insensitive filter on tag, name, or namespace.
Example call
{
"search": "postgres"
}Result
{
"catalog": [
{
"tag": "postgres",
"name": "Postgres",
"namespace": "databases"
},
{
"tag": "postgres.table",
"name": "Table",
"namespace": "databases"
},
{
"tag": "supabase.postgres",
"name": "Supabase",
"namespace": "databases"
}
],
"custom_types": [],
"connection_types": [
"http",
"grpc",
"ws",
"sql",
"queue",
"event",
…
],
"shapes": [
{
"shape": "box",
"name": "Box",
"use": "anything (the default)"
},
{
"shape": "pill",
"name": "Pill",
"use": "starts, ends and states"
},
{
"shape": "circle",
"name": "Circle",
"use": "events, triggers and endpoints"
},
…
],
"icons": [
{
"group": "Basic",
"icons": [
"circle",
"box",
"triangle",
"diamond",
…
]
},
{
"group": "Commerce & finance",
"icons": [
"shopping_cart",
"shopping_bag",
"basket",
…
]
},
…
],
"colors": [
{
"color": "red",
"hex": "#ef4444"
},
{
"color": "orange",
"hex": "#f97316"
},
…
]
}searchfilters the catalog and custom types only; connection types, shapes, icons and colors are always listed in full. Custom types come from the current workspace; if they can't be read,notesays so.- A shape goes in a component's body,
shape cylinder, and draws the component as that outline; the type still picks the icon and color inside it. - An icon goes in a component's body too,
icon shopping_cart, and replaces its type's icon; an untyped component shows none without one. - So does a color,
color greenorcolor "#b45309", which replaces its type's color: the icon is drawn in it and the component takes a tint of it.
create_custom_type
Create/update a custom component type · Writes to the current workspace (editor or owner)
Add a reusable component type to this workspace's library — exactly like a human clicking '+' in the app's palette. Use when the catalog lacks a technology or the user has domain-specific building blocks (e.g. 'Billing Engine'). The type gets a @custom.<slug> tag, appears in the human's palette immediately, renders with the chosen icon/color, and validates cleanly in every project of the workspace. Calling it again with the same tag updates name/glyph/color.
Parameters
namestringrequired- Display name, e.g. 'Billing Engine'. Not empty.
glyphstring- Its icon: any icon name list_types lists under `icons`, e.g. 'gear', 'shopping_cart' (default 'box')
colorstring- Hex accent color like #6366f1 (default slate)
tagstring- Override tag; 'custom.' prefix is enforced. Default: custom.<slug of name>.
Example call
{
"name": "Fraud Scorer",
"glyph": "shield",
"color": "#dc2626"
}Result
{
"created": "custom.fraud_scorer",
"usage": "component my_id \"Fraud Scorer\" @custom.fraud_scorer"
}- The tag is
custom.plus the name in snake case, unless you passtag. Calling it again with the same tag updates the name, glyph and color. - Refused with an icon name
list_typesdoesn't list or a color that isn't#rrggbb.
delete_custom_type
Delete a custom component type · Writes to the current workspace (editor or owner)
Remove a custom type from the workspace library. Existing components using the tag stay valid but start showing a W301 unknown-type warning and render generic. Built-in catalog types cannot be deleted.
Parameters
tagstringrequired- The @custom.* tag (without @)
Example call
{
"tag": "custom.billing_engine"
}Result
{
"deleted": "custom.billing_engine"
}- Components that use the tag stay valid, but draw as generic boxes with a
W301warning. Deleting a tag that doesn't exist succeeds.
Projects
A project is one Archer document. Reads return the full source. Writes replace it whole, pass the same validation as the canvas, and are saved as revisions under the agent's name.
list_projects
List projects · Reads the current workspace
List every Archer project in the user's workspace: id, title, last-updated timestamp, and editor URL. Start here whenever the user refers to an existing architecture.
No parameters.
Example result
[
{
"id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"title": "Shop",
"updated": "2026-09-01T10:00:00.000Z",
"editor_url": "https://tryarcher.dev/documents/k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5"
},
…
]- Most recently updated first. Only the current workspace: see
list_workspaces.
get_project
Read a project · Reads the current workspace
Fetch a project's full Archer source plus its current diagnostics and a drift summary. ALWAYS call this before update_project and base your edit on the exact source returned — updates replace the whole document. Components may carry a src path pointing into the codebase — use it to locate implementations. The layout block at the end is machine-managed presentation: keep it out of your mental model, and normally omit it from your edits (the server preserves it for you). If repository.path is set, the project is stored in that .arch file in the repository: when you are working in that repository, edit the file instead of calling update_project.
Parameters
project_idstringrequired
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5"
}Result
{
"id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"title": "Shop",
"editor_url": "https://tryarcher.dev/documents/k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"valid": true,
"errors": [],
"warnings": [],
"drift": {
"reported_at": "2026-09-01T10:10:00.000Z",
"findings_count": 2
},
"repository": {
"full_name": "acme/shop",
"branch": "main",
"url": "https://github.com/acme/shop/tree/main",
"hint": "Drift checks and `src` paths refer to this repository at this branch."
},
"source": "arch \"Shop\" v0.2\n\ncomponent web \"Storefront\" @vercel.nextjs {\n shape browser\n}\n…\n\nlayout {\n…\n}\n"
}sourceis the stored document,layoutblock included. Agents leave that block out of their edits and the server keeps it.driftis null until a drift check is reported, andrepositoryis null until one is linked.
create_project
Create a project · Writes to the current workspace (editor or owner)
Create a new Archer project from source. The document is validated first: any ERROR (syntax, unresolved connection endpoint, invalid payload, duplicate id) REJECTS the write and returns the diagnostics — fix and retry. On success the source is auto-laid-out, canonicalized, saved, and the result includes the id and an editor URL to share with the user. Write semantic content only; omit the layout block. Minimum viable document: 'arch "Title" v0.2'.
Parameters
sourcestringrequired- Full Archer document text (see get_guide for the language)
summarystring- One line describing what you changed, shown in the human's activity feed next to your name. Always provide it. At most 200 characters.
Example call
{
"source": <source below>,
"summary": "Mapped the repo's three services"
}source
arch "Link Shortener" v0.2
component app "Web App" @vercel.nextjs {
shape browser
}
component api "API" @vercel.functions
component db "Links" @postgres {
shape cylinder
}
app -> api @http : "shorten"
api -> db @sqlResult
{
"id": "k1792cxn5wm0fq8hq6dhz4v3sr7b2eay",
"title": "Link Shortener",
"editor_url": "https://tryarcher.dev/documents/k1792cxn5wm0fq8hq6dhz4v3sr7b2eay",
"warnings": []
}- The title comes from the
archheader. The document is saved canonical and laid out. Without asummarythe revision reads "Created the project". - A document with errors saves nothing. The error result lists them:
{ "message": "REJECTED — fix these errors and retry.", "errors": ["E101 [error] line 3: …"], "warnings": [] }.
update_project
Update a project (full replace) · Writes to the current workspace (editor or owner)
Replace a project's document with new source. Same validation gates as create_project: errors REJECT the write (nothing saved) and are returned for you to fix. Layout preservation is automatic: components that survive your edit keep their on-canvas positions; new components are auto-placed — so simply omit the layout block. The human's open canvas updates live. Always get_project first and edit that source; sending unrelated source will silently discard their content.
Parameters
project_idstringrequiredsourcestringrequired- Complete replacement document text.
summarystring- One line describing what you changed, shown in the human's activity feed next to your name. Always provide it. At most 200 characters.
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"source": <source below>,
"summary": "Added an order-events queue"
}source
arch "Shop" v0.2
component web "Storefront" @vercel.nextjs {
shape browser
}
plane backend "Backend" {
component api "API" @vercel.functions {
src "apps/api"
}
component db "Orders DB" @postgres {
shape cylinder
}
component events "Order Events" @queue {
shape queue
}
}
component stripe "Stripe" @stripe {
shape cloud
}
api -> db @sql
api -> events @queue : "order placed" {
message json ```
{ "order_id": "uuid", "total_cents": "int" }
```
}
api -> stripe @http : "charges"
web -> api @httpResult
{
"id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"title": "Shop",
"saved": true,
"attributed_to": "Claude Code",
"editor_url": "https://tryarcher.dev/documents/k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"warnings": []
}- The source replaces the whole document, so start from what
get_projectreturned. Rejected writes return the same error result ascreate_projectand save nothing. - Components that still exist keep their canvas positions and new ones are placed. Entries in a
layoutblock you send win over the stored ones. - An update that changes nothing adds no revision.
delete_project
Delete a project · Writes to the current workspace (editor or owner)
Permanently delete a project. Irreversible — there is no trash. Requires confirm=true, and you should only call this when the user explicitly asked for deletion of this specific project.
Parameters
project_idstringrequiredconfirmtruerequired- Must be true; acknowledges permanent deletion.
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"confirm": true
}Result
{
"deleted": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"title": "Shop"
}- The project's history and drift report go with it. There is no undo.
Canvas
Change how the diagram is drawn without touching the architecture. It saves a revision. (Planes and nesting are architecture: write them in the source.)
auto_layout_project
Re-arrange a project's canvas · Writes to the current workspace (editor or owner)
Run the auto-layout on a stored project and save. With reset=false (default) only components and planes missing positions are placed, next to what they connect to; with reset=true ALL manual positioning is discarded and every canvas (the top level and the inside of each component) is re-arranged — only do that when the user asks for a tidy-up, since it destroys their hand arrangement.
Parameters
project_idstringrequiredresetboolean
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"reset": true
}Result
{
"id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"reset": true,
"saved": true
}- Without
reset, only components and planes that have no position are placed, next to what they connect to ("Placed unpositioned components"). Withreset: trueevery position is recomputed ("Re-arranged the canvas"), the top level and the inside of every component: the user's arrangement survives only in the previous revision.
History
Every save is a revision: who made it (the user, or an agent by its client's name), when, and the agent's one-line summary. The app shows the same history.
get_history
Project history · Reads the current workspace
List a project's revisions, newest first: who saved (the human, or an agent by client name), when, their one-line summary, and a count of components, planes and connections added, removed and changed versus the previous revision. Humans see this same feed in the app, which is why every write you make should carry a summary. Use it to understand what happened since you last looked, to find a revision id for get_revision/diff_project, or to explain a document's evolution to the user.
Parameters
project_idstringrequiredlimitinteger- Max revisions to return (default 20) From 1 to 200.
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"limit": 10
}Result
[
{
"revision_id": "kd7e1q8vd2m3p5sz0b6tyhc9w4xnrj8a",
"created_at": "2026-09-01T10:00:00.000Z",
"author": {
"kind": "user"
},
"title": "Shop",
"changes": {
"added": 1,
"removed": 0,
"changed": 1,
"connectionsAdded": 1,
"connectionsRemoved": 0,
"connectionsChanged": 0,
"planesAdded": 0,
"planesRemoved": 0,
"planesChanged": 0
},
"description": "+1 component, ~1 changed, +1 connection"
},
{
"revision_id": "kd73ac5x2dmbn4xq8v1ng6ytzh7p9wsf",
"created_at": "2026-09-01T09:00:00.000Z",
"author": {
"kind": "agent",
"name": "Claude Code"
},
"summary": "Created the project",
"title": "Shop",
"changes": {
"added": 3,
"removed": 0,
"changed": 0,
"connectionsAdded": 2,
"connectionsRemoved": 0,
"connectionsChanged": 0,
"planesAdded": 1,
"planesRemoved": 0,
"planesChanged": 0
},
"description": "+3 components, +1 plane, +2 connections"
}
]- Newest first.
changescounts the differences from the revision before; moving things on the canvas doesn't count. - Every agent save is its own revision. The user's saves in the app have no
summary, and one made within five minutes of their last joins that revision.
get_revision
Read a past revision · Reads the current workspace
Fetch the full Archer source of one historical revision (ids come from get_history). Read-only: to roll a project back, send this source to update_project with a summary saying so.
Parameters
revision_idstringrequired
Example call
{
"revision_id": "kd73ac5x2dmbn4xq8v1ng6ytzh7p9wsf"
}Result
{
"revision_id": "kd73ac5x2dmbn4xq8v1ng6ytzh7p9wsf",
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"created_at": "2026-09-01T09:00:00.000Z",
"author": {
"kind": "agent",
"name": "Claude Code"
},
"summary": "Created the project",
"source": "arch \"Shop\" v0.2\n\ncomponent web \"Storefront\" @vercel.nextjs {\n shape browser\n}\n…"
}- To roll back, send
sourcetoupdate_projectwith a summary that says so.
diff_project
Diff two revisions · Reads the current workspace
Structured architecture diff between two points in a project's history: components and planes added/removed/changed (with which fields; moving a component onto another plane changes its plane), connections added/removed, title changes. Layout moves never count. Defaults compare the latest change (the current document vs. the revision before it) — the fastest way to answer 'what did you / the human just change?'. Pass revision ids from get_history to compare arbitrary points.
Parameters
project_idstringrequiredfrom_revision_idstring- Older revision (default: the one just before `to`)
to_revision_idstring- Newer revision (default: the current document)
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5"
}Result
{
"from": "kd73ac5x2dmbn4xq8v1ng6ytzh7p9wsf",
"to": "current",
"description": "+1 component, ~1 changed, +1 connection",
"summary": {
"added": 1,
"removed": 0,
"changed": 1,
"connectionsAdded": 1,
"connectionsRemoved": 0,
"connectionsChanged": 0,
"planesAdded": 0,
"planesRemoved": 0,
"planesChanged": 0
},
"diff": {
"components": [
{
"path": "api",
"kind": "changed",
"fields": [
"src"
]
},
{
"path": "stripe",
"kind": "added"
}
],
"planes": [],
"connections": [
{
"key": "api|->|stripe|http|charges",
"kind": "added",
"connection": {
"kind": "connection",
"source": [
"api"
],
"target": [
"stripe"
],
"arrow": "->",
"payloads": [],
"span": …,
"type": "http",
"label": "charges"
}
}
]
}
}fromandtoare revision ids,current(the project as it is now) orempty(before the first revision). Only the newest 200 revisions can be named.- A changed component lists its changed
fields:name,type,shape,icon,color,desc,src,payloadsorplane(it sits on a different plane).planeslists planes added, removed and changed (name,type,descorplane) the same way. A connection ischangedwhen only its payloads differ, andtitleappears when the document was renamed.
Code and drift
Tie a project to the code it describes: link its repository, then record whether the code still matches the document. Findings appear on the canvas.
link_repository
Link the project to a GitHub repository · Writes to the current workspace (editor or owner)
Record which GitHub repository (and branch) this architecture describes — the same as the human clicking 'Link repository' in the app's History panel. Once linked, the app shows the repo's commits alongside architecture revisions, src paths deep-link into the repo, and drift checks are understood to be against that branch. Use it when the user tells you which repo a project belongs to. Pass repository: null to unlink (for a project stored in a repository file, that also stops syncing it). Storing a project in a .arch file in the repository is done in the app (From GitHub / Move to GitHub), which checks the user's GitHub access.
Parameters
project_idstringrequiredrepositoryobject | nullrequired- The repository to link, or null to unlink.
repository.full_namestringrequired- owner/name, e.g. acme/platform.
repository.branchstring- Branch to track (default main)
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"repository": {
"full_name": "acme/shop",
"branch": "main"
}
}Result
{
"id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"repository": {
"full_name": "acme/shop",
"branch": "main"
},
"note": "Commits from this repository now appear in the human's History panel next to your revisions."
}branchdefaults tomain, andfull_namemay end in.git. Passrepository: nullto unlink; the result is then{ id, repository: null, unlinked: true }.
report_drift
Report code ↔ architecture drift · Writes to the current workspace (editor or owner)
Record the result of a drift check: after comparing each component (via its src path or by searching the repo) against the actual code, send one finding per discrepancy. The report REPLACES the previous one, and an EMPTY list means 'in sync' — send it too, so the human sees a green state. Findings show as badges on the components in the canvas; 'extra' findings appear in a list the human can add to the document with one click. Do not modify the document during a drift check unless the user asked.
Parameters
project_idstringrequiredfindingsobject[]requiredfindings[].status"missing" | "changed" | "extra"required- missing = declared but no implementation found; changed = implementation differs from desc/payloads; extra = exists in code but not in the document.
findings[].pathstring- Dot-path of the component (required for missing/changed)
findings[].namestring- Suggested display name for an undeclared part (required for extra)
findings[].srcstring- Where in the code you looked / found it (repo path or URL)
findings[].notestring- What differs, in one or two sentences. At most 1000 characters.
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"findings": [
{
"status": "missing",
"path": "stripe",
"src": "apps/api/src/payments",
"note": "No Stripe client anywhere in the repo."
},
{
"status": "extra",
"name": "Email Worker",
"src": "apps/worker/email"
}
]
}Result
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5",
"reported_as": "Claude Code",
"in_sync": false,
"missing": 1,
"changed": 0,
"extra": 1
}- Replaces the previous report. An empty
findingslist records that the code is in sync. missingandchangedneed apaththat exists in the document, andextraneeds aname. Otherwise nothing is recorded, and the error names each bad finding.
get_drift
Read the latest drift report · Reads the current workspace
The most recent drift report for a project (who ran it, when, and every finding), or report: null if none has been run. Use it to pick up where a previous check left off or to see what the human still has to act on.
Parameters
project_idstringrequired
Example call
{
"project_id": "k57bmtyf4pmj3fmrbrz9hq7qwn6ypcp5"
}Result
{
"report": {
"reported_at": "2026-09-01T10:10:00.000Z",
"author": {
"kind": "agent",
"name": "Claude Code"
},
"in_sync": false,
"findings": [
{
"status": "changed",
"path": "db",
"src": "apps/api/src/db/schema.ts",
"note": "Orders now carry a currency column the document doesn't mention."
},
{
"status": "extra",
"name": "Email Worker",
"src": "apps/worker/email"
}
]
}
}reportis null until a drift check is reported.
Workspaces
Project tools act on one workspace at a time, the current one. It starts as Personal. The choice is stored for the user, so the CLI and every agent they connect move together.
get_workspace_info
Workspace status · Your account
Report who you are acting for and where: the signed-in user, the CURRENT workspace (tools operate on it; switch with use_workspace, see list_workspaces), your agent name, whether the backend is reachable, and project/custom-type counts. Call this first if any project tool errors.
No parameters.
Example result
{
"auth": "oauth",
"user_id": "user_2wKx9TqLm4RbVc7NpZ3hJd8sFyA",
"workspace_id": "user_2wKx9TqLm4RbVc7NpZ3hJd8sFyA",
"workspace_name": "Personal",
"workspace_role": "owner",
"agent_name": "Claude Code",
"convex_url": "https://<deployment>.convex.cloud",
"app_url": "https://tryarcher.dev",
"backend_reachable": true,
"project_count": 1,
"custom_type_count": 1
}agent_nameis the name the user's history will show for this client's saves.backend_reachable: false(with null counts) means the server couldn't reach Archer's database.
list_workspaces
List the user's workspaces · Your account
A signed-in user can belong to several workspaces: their own Personal one plus any team workspaces they were invited to, each with a role (owner/editor can write, viewer can only read). Every project tool on this server operates on ONE of them — the current workspace, which defaults to Personal. This lists them all and marks the current one. Call it when the user mentions a team or shared project you can't find in list_projects, then switch with use_workspace.
No parameters.
Example result
{
"current_workspace_id": "user_2wKx9TqLm4RbVc7NpZ3hJd8sFyA",
"workspaces": [
{
"id": "user_2wKx9TqLm4RbVc7NpZ3hJd8sFyA",
"name": "Personal",
"kind": "personal",
"role": "owner",
"current": true
},
{
"id": "jn7dt5pzq1b3wk8m2c9x4v6hre0fs7ya",
"name": "Acme",
"kind": "team",
"role": "editor",
"current": false
}
]
}- Personal comes first; its id is the user's id.
use_workspace
Switch the current workspace · Your account
Make another of the user's workspaces the current one, so list_projects, create_project and every other project tool operate there. The choice is remembered for this user across sessions and agents until changed again, so only switch when the user asks for (or clearly means) a different workspace, and say which workspace you are working in. Fails if the user is not a member of the target. Pass the id from list_workspaces; the Personal workspace's id is the user id.
Parameters
workspace_idstringrequired- Workspace id from list_workspaces. Not empty.
Example call
{
"workspace_id": "jn7dt5pzq1b3wk8m2c9x4v6hre0fs7ya"
}Result
{
"current_workspace_id": "jn7dt5pzq1b3wk8m2c9x4v6hre0fs7ya",
"name": "Acme",
"kind": "team",
"role": "editor",
"visible_projects": 0
}- Also switches the CLI (
archer workspace) and the user's other agents. In a workspace where the user is a viewer, the result carries anoteand writes are refused.
Feedback
Pass the user's feedback on to the people who build Archer, as "Send feedback" in the app does. Agents send it only when the user asks.
send_feedback
Send feedback to the Archer team · Your account
Send feedback about Archer (the app, this MCP server, the CLI or the language) to the people who build it, from the signed-in user — the same as "Send feedback" in the app. Something broken, confusing or missing, or an idea: it is all read. ONLY call this when the user asks you to send feedback, or agrees when you offer; never on your own initiative, and send each piece of feedback once. Write it as the user means it, in their words where you can, and include what helps reproduce a problem (what they tried, what happened, error messages). Leave out secrets, credentials and code the user didn't ask to share. At most 4000 characters.
Parameters
messagestringrequired- The feedback, as plain text or markdown, written for the Archer team. Not empty. At most 4000 characters.
Example call
{
"message": "Exporting a project with a long description cuts the text off in the PNG. It would also help to choose the export's background color."
}Result
{
"sent": true,
"characters": 133,
"note": "Sent to the Archer team, from the signed-in user. Tell the user it was sent."
}- Sent as the signed-in user, labelled with the agent's name, whichever workspace is current. Up to 4000 characters.
Resources
Clients that support MCP resources can read the guide and the worked examples directly, without a tool call.
archer://guidetext/markdown- Archer agent guide. Complete guide to Archer and this server: workflows, validation, the language spec.
archer://examples/components-and-connectionstext/plain- Example: Link Shortener. The smallest useful document: a header, a few typed components and the connections between them. A
@typepicks the icon and color, ashapethe outline; a label says what travels on an arrow. Acoloroverrides the type's: here orange marks the hot path, the cache most requests stop at. archer://examples/descriptions-and-payloadstext/plain- Example: Billing Service. A component body holds a
desc,srcpointers into the codebase (a line for each place it lives, a folder, a file or a URL), and named payloads.json,yamlandtablepayloads are validated;markdownis freeform. The editor shows them in the detail panel, and agents read them as the spec. archer://examples/shapestext/plain- Example: Support Desk. A
shapein a component's body draws it as what it is: a person for the customer, people for the support team, a robot for the agent, a cloud for the model provider, a book for the help center, a cylinder for the database. The type still picks the icon and color inside the outline (aniconand acolorcan change them: amber marks the way to a person), and anything without a shape stays a box. Shapes are for readers; an agent implementing the document can ignore them. archer://examples/iconstext/plain- Example: Coffee Shop. An
iconin a component's body picks the icon it shows: any Phosphor icon by its snake-case name (coffee,receipt,device_tablet) or one of the catalog's (database,queue). An untyped component shows no icon until it picks one, so an icon is the quickest way to say what something is without a type. On a typed component it replaces the type's icon and keeps its color, like the book on the Postgres menu. Acolordoes the same for the color: a palette name (amber) or any"#rrggbb", like the coffee brown of the barista's screen. Icons and colors are for readers; an agent implementing the document can ignore them.list_typeslists every icon, and the editor's Icon picker searches them. archer://examples/planestext/plain- Example: Photo Sharing. A
planeis a titled region of the canvas, a background you put components on: the user's devices on one, the AWS account on another, and a VPC inside that. Planes are only for the reader. They don't change a component's path (api, notcloud.api), connections never attach to them, and like a component a plane can take a@typeand adesc. Colors cut across planes: the upload path is pink wherever it runs. archer://examples/nestingtext/plain- Example: Food Delivery. Any component can hold components. Orders owns its API, checkout, tracker and database: on the canvas it's one box with a count of what's inside, and opening it shows those parts, with whatever outside they connect to along the edges. A path reaches a nested component (
orders.checkout), and a connection can join one or the whole component (app -> orders). The courier's side is orange. archer://examples/music-streamingtext/plain- Example: Music Streaming. A whole product in one file: listener apps, the edge, six services in one Kubernetes cluster, their storage, analytics, and the pipeline that turns a label's delivery into playable audio. Planes mark out the regions, while the cluster and the apps nest their parts, so the top level stays readable. Opening Services shows all six, on a plane per Kubernetes namespace: a plane inside a component is a region of its own canvas, and the services on it are still
services.playbackand friends. All three arrow kinds appear:->,<->for the remote-control socket, and--for the signed URLs Playback and the CDN agree on. It was laid out by hand, so it ends with thelayoutblock the canvas writes when you drag and resize. Agents may read that block to see the diagram as drawn, but never implement anything from it, and write it only when asked to arrange the diagram. Where the money flows, in and out, is emerald.
Results and errors
Tools answer with a single text item. Most results are JSON. get_guide,
format_source, auto_layout_source and compile_for_agent answer with
plain text: markdown or Archer source.
When a call fails, the result is marked as an error (isError: true) and its
text says why. Rejected writes answer with JSON that lists the problems:
{
"message": "REJECTED — fix these errors and retry.",
"errors": ["E101 [error] line 4: Connection target 'payments' does not resolve to a declared component."],
"warnings": []
}
Each diagnostic reads <code> [<severity>] line <line>: <message>. Errors
(E…) block a save, and warnings (W…) never do. The codes are listed under
Validation in the
language reference.
| Error | What it means |
|---|---|
REJECTED — … |
The document has errors. Nothing was saved. |
No project with id … Use list_projects. |
The project isn't in the current workspace, or was deleted. |
… This needs editor access; you are a viewer. |
Viewers can't save in this workspace. |
The user is not a member of workspace '…' |
use_workspace was given an id that isn't one of yours. The message lists the valid ones. |
Input validation error: … |
The arguments don't match the tool's parameters. |
Invalid findings — nothing recorded: … |
A drift finding has no valid path, or an extra has no name. |
Protocol
For client developers and the curious:
- Transport. Streamable HTTP at
/mcp, stateless: every JSON-RPC request is aPOSTanswered with plain JSON. The server keeps no sessions and sends no notifications of its own. A request can run for up to 60 seconds. The server identifies itself asarcher. - Authorization. The standard MCP authorization flow, with Clerk as the
authorization server:
GET /.well-known/oauth-protected-resource/mcp(RFC 9728) names the authorization server and the scopes,profileandemail./.well-known/oauth-authorization-server(RFC 8414) serves the authorization server's metadata too, for clients that look there.- Clients register with dynamic client registration (RFC 7591) and sign in
with the authorization code flow and PKCE (
S256). The client name they register is the name their saves appear under. - Every request carries
Authorization: Bearer <access token>. Without a valid token the server answers401withWWW-Authenticate: Bearer error="invalid_token", error_description="…", resource_metadata="https://tryarcher.dev/.well-known/oauth-protected-resource/mcp", which is where clients discover all of the above.
- CORS. Any origin may call the server from a browser.
- Resources. Besides tools, the server offers the agent guide as
archer://guide(markdown) and each worked example asarcher://examples/<slug>(plain text).
Troubleshooting
The client says it needs authentication. Start the sign-in from the
client: in Claude Code, /mcp → archer → Authenticate. If the browser
didn't open, the client usually shows the address to open yourself.
The agent can't find a project. It's probably in another workspace. Ask
the agent which workspace it's in, or have it call list_workspaces and
switch.
The agent's saves are refused. If the error says you're a viewer, ask a
workspace owner to make you an editor. If it starts with REJECTED, the
document has errors: the agent should fix what the diagnostics name and try
again, or check its work with validate_source first.
Custom types show a W301 warning. The type belongs to another
workspace, or it was deleted. Warnings don't block anything.
Agents keep working in the wrong workspace. The current workspace is
shared by all your agents and the CLI. Switch back with use_workspace or
archer workspace.