Archer

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), with src paths 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 .arch file in its GitHub repository (created From GitHub in the app, or moved there with Move to GitHub). get_project then returns the file as repository.path, with its sync state, and tells agents working in that repository to edit the file and commit it with the code. update_project still 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_history and diff_project show who changed what, and when. get_revision returns any past version, to compare or restore.
  • Hand off a spec. compile_for_agent returns 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 layout block. 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_url opens 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…"
}
  • source is canonical and validates clean, so it can go straight to create_project to give the user an editable copy. Each example is also the resource archer://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

source stringrequired
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 @http

Result

{
  "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_project and update_project: a source that validates clean will save. Only errors make valid false.

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

source stringrequired
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 @sql

Result

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 layout block is kept but never added (that's auto_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_source first.

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

source string
Archer source (omit when passing project_id)
project_id string
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 source or project_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

source stringrequired
Full Archer document text.
reset boolean
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 @sql

Result

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. With reset: true every position is recomputed.
  • create_project and update_project lay 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

search string
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"
    },
    …
  ]
}
  • search filters 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, note says 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 green or color "#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

name stringrequired
Display name, e.g. 'Billing Engine'. Not empty.
glyph string
Its icon: any icon name list_types lists under `icons`, e.g. 'gear', 'shopping_cart' (default 'box')
color string
Hex accent color like #6366f1 (default slate)
tag string
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 pass tag. Calling it again with the same tag updates the name, glyph and color.
  • Refused with an icon name list_types doesn'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

tag stringrequired
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 W301 warning. 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_id stringrequired

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"
}
  • source is the stored document, layout block included. Agents leave that block out of their edits and the server keeps it.
  • drift is null until a drift check is reported, and repository is 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

source stringrequired
Full Archer document text (see get_guide for the language)
summary string
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 @sql

Result

{
  "id": "k1792cxn5wm0fq8hq6dhz4v3sr7b2eay",
  "title": "Link Shortener",
  "editor_url": "https://tryarcher.dev/documents/k1792cxn5wm0fq8hq6dhz4v3sr7b2eay",
  "warnings": []
}
  • The title comes from the arch header. The document is saved canonical and laid out. Without a summary the 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_id stringrequired
source stringrequired
Complete replacement document text.
summary string
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 @http

Result

{
  "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_project returned. Rejected writes return the same error result as create_project and save nothing.
  • Components that still exist keep their canvas positions and new ones are placed. Entries in a layout block 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_id stringrequired
confirm truerequired
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_id stringrequired
reset boolean

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"). With reset: true every 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_id stringrequired
limit integer
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. changes counts 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_id stringrequired

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 source to update_project with 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_id stringrequired
from_revision_id string
Older revision (default: the one just before `to`)
to_revision_id string
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"
        }
      }
    ]
  }
}
  • from and to are revision ids, current (the project as it is now) or empty (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, payloads or plane (it sits on a different plane). planes lists planes added, removed and changed (name, type, desc or plane) the same way. A connection is changed when only its payloads differ, and title appears 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 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_id stringrequired
repository object | nullrequired
The repository to link, or null to unlink.
repository.full_name stringrequired
owner/name, e.g. acme/platform.
repository.branch string
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."
}
  • branch defaults to main, and full_name may end in .git. Pass repository: null to 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_id stringrequired
findings object[]required
findings[].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[].path string
Dot-path of the component (required for missing/changed)
findings[].name string
Suggested display name for an undeclared part (required for extra)
findings[].src string
Where in the code you looked / found it (repo path or URL)
findings[].note string
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 findings list records that the code is in sync.
  • missing and changed need a path that exists in the document, and extra needs a name. 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_id stringrequired

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"
      }
    ]
  }
}
  • report is 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_name is 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_id stringrequired
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 a note and 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

message stringrequired
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://guide text/markdown
Archer agent guide. Complete guide to Archer and this server: workflows, validation, the language spec.
archer://examples/components-and-connections text/plain
Example: Link Shortener. The smallest useful document: a header, a few typed components and the connections between them. A @type picks the icon and color, a shape the outline; a label says what travels on an arrow. A color overrides the type's: here orange marks the hot path, the cache most requests stop at.
archer://examples/descriptions-and-payloads text/plain
Example: Billing Service. A component body holds a desc, src pointers into the codebase (a line for each place it lives, a folder, a file or a URL), and named payloads. json, yaml and table payloads are validated; markdown is freeform. The editor shows them in the detail panel, and agents read them as the spec.
archer://examples/shapes text/plain
Example: Support Desk. A shape in 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 (an icon and a color can 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/icons text/plain
Example: Coffee Shop. An icon in 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. A color does 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_types lists every icon, and the editor's Icon picker searches them.
archer://examples/planes text/plain
Example: Photo Sharing. A plane is 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, not cloud.api), connections never attach to them, and like a component a plane can take a @type and a desc. Colors cut across planes: the upload path is pink wherever it runs.
archer://examples/nesting text/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-streaming text/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.playback and 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 the layout block 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 a POST answered 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 as archer.
  • 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, profile and email. /.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 answers 401 with WWW-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 as archer://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.