The MCP server
Reference for M1rror's MCP server, covering Streamable HTTP transport, OAuth 2.1 and agent tokens, scopes, effective permissions, limits and errors.
On this page
Endpoint and transport
| URL | https://mcp.m1rror.com/mcp (self-hosted deployments show theirs in Connect your agent) |
| Transport | Streamable HTTP, stateless: each POST /mcp carries its own Authorization: Bearer header |
| Health | GET /healthz |
| Server name | m1rror |
A local stdio mode also exists for development (--stdio, with the token in M1RROR_TOKEN).
Authentication
The server accepts two kinds of token. Anything else gets 401 invalid_token.
OAuth 2.1 (recommended)
M1rror follows the MCP authorization specification:
- Protected resource metadata at
/.well-known/oauth-protected-resource/mcppoints clients at the authorization server,https://m1rror.com/api/auth. - Clients register dynamically (RFC 7591) as public clients and must use PKCE with
S256. Tokens are bound to the MCP server with resource indicators (RFC 8707). - On the consent screen the person chooses the workspace and the permissions.
- Access tokens last 1 hour; refresh tokens rotate and last 30 days. An OAuth grant lasts 90 days.
Agent tokens
Personal agent tokens are created in Connect your agent or Agent access. They're shown once, stored only as a hash, can be limited to one project and expire after at most 90 days.
Scopes
| Scope | Permissions |
|---|---|
m1rror:read | Read projects, references, designs, releases, build plans and runs. |
m1rror:draft | Write references and draft designs, ask for a review, submit evidence and prepare build plans. |
Effective permissions
On every request, an agent's authority is the intersection of its grant, its token's scopes and the current permissions of the person who connected it. If that person loses a role, the agent loses it too.
Agents never receive approval, billing, membership, ownership, policy or integration permissions, can't open pull requests and never see actions that require explicit human confirmation.
Tool profiles
Every grant also has a tool profile, which decides which tools the agent sees and may call. A profile never adds permissions: the scopes and the connecting person's permissions still apply on top.
| Profile | Tools | Default for |
|---|---|---|
implementer | 14 tools for building an approved release and reporting back (listed below) | OAuth connections, tokens from Connect your agent, and new tokens in Agent access |
full | Every tool the grant's permissions allow (223 tools in all) | Grants made before profiles existed, or a token created with Tools › Full (every permitted tool) |
The implementer tools are:
- What to build:
taste_get_implementation_brief,taste_get_project_context,taste_list_releases,taste_get_release,taste_diff_releases,taste_list_implementation_contracts,taste_get_implementation_contract. - Reporting back:
taste_link_pull_request,taste_submit_implementation_progress,taste_submit_evidence. - Where it stands:
taste_get_ship_ledger,taste_list_pr_checks,taste_get_pr_check,taste_start_ci_check(check a commit from CI).
Coding agents work better with a short tool list: the implementer listing is about 11 KB, against about 181 KB for every tool.
Choose a profile
- Agent access › new agent token › Tools: Implementer (14 tools) or Full (every permitted tool).
- Connect your agent creates an implementer token in one click.
- OAuth connections get the implementer profile.
Narrow a connection
Add ?profile=implementer to the server URL to use only the implementer tools on that connection, even with a token that has the full set:
https://mcp.m1rror.com/mcp?profile=implementerIn local stdio mode, set M1RROR_TOOL_PROFILE=implementer instead. A profile on the URL can only narrow a grant, never widen it: an implementer token stays implementer with ?profile=full.
Calling a tool outside the profile fails with forbidden and the reason outside_tool_profile.
Resources
Besides tools, the server offers read-only resources: context a coding agent can load without calling a tool. Each one is JSON (application/json).
| Resource | URI | What it holds |
|---|---|---|
| Design system | m1rror://projects/{project_id}/system | The token document with resolved values, contrast checks and diagnostics, and the component registry with binding status |
| Screen spec | m1rror://projects/{project_id}/screens/{screen_id} | The screen's component tree, its states and any agent draft waiting for review |
| Screen flow | m1rror://projects/{project_id}/flow | Links between screens, broken links and the named flows |
| Release | m1rror://projects/{project_id}/releases/{release_id} | An immutable design release: payload, digest and content |
| Implementation contract | m1rror://projects/{project_id}/contracts/{contract_id} | Digest, scope, budget and authorization status |
| Surface brand | m1rror://projects/{project_id}/surfaces/{surface_id}/brand | The brand a surface uses and its resolved tokens, with the layer each value comes from |
| Audit findings | m1rror://projects/{project_id}/findings{?repository_id} | Findings from the newest design audit, with file and line |
resources/templates/listreturns the URI templates above.resources/listreturns concrete URIs for your projects, 10 projects per page.- Reading a resource runs the same governed read as the matching tool, so the same permissions and tool profile apply. You only see the resources your connection can read. With the implementer profile, that is releases and implementation contracts.
- An unknown id, or an id from another workspace, fails with "Resource not found" (
-32002). - Subscriptions are not offered. The server is stateless, so it can't notify you when a resource changes. Read it again instead.
Prompts
Prompts are ready-made instructions that pull in the right resources. Your client lists them with prompts/list and shows them as slash commands or templates.
| Prompt | Arguments | Embeds |
|---|---|---|
implement_screen | project_id, screen_id, optional surface_id and contract_id | The screen spec, the design system (or the surface brand), and the contract if given |
implement_release | project_id, release_id, optional contract_id | The release, and the contract if given |
fix_design_drift | project_id, optional repository_id | The latest audit findings and the design system |
build_flow | project_id, optional flow (a flow name) | The screen flow and the design system |
If your connection can't read an extra resource, such as the design system, the prompt leaves it out and says so. If it can't read the main one, the prompt fails.
Waiting for a run
Scans, captures and other long jobs return a run id. Call taste_wait_for_run with the run id and a timeout_s of up to 50 seconds. It returns as soon as the run finishes (done: true), or with the current status and done: false when the time runs out. Call it again to keep waiting.
Limits
- 120 requests per minute per token.
- Request bodies up to 40 MB.
- Browser requests from other origins are refused.
Retries
Every tool that changes something accepts an optional idempotency_key: a string of 8 to 128 characters, such as a UUID. If a call times out and you retry it with the same key and the same arguments, M1rror returns the first result. Nothing is created or charged twice.
- The same key with different arguments fails with
idempotency_mismatch. - If the first call is still running, the retry fails with
conflict. Wait and try again. - If the first call failed, the retry runs again.
Keys belong to your connection and to one tool. Use a new key for each new change.
Work that runs later
Scans, captures, generations and previews run in the background. Before one starts, M1rror checks again that your connection and the person who created it still have access. If access was removed in the meantime, the run stops with forbidden: access changed after this was queued, and nothing is charged.
Errors
Tool errors use M1rror's error codes, for example forbidden, not_found, validation_failed, stale_revision (someone changed it first: re-read and retry), conflict, idempotency_mismatch (an idempotency_key reused with different arguments), budget_exhausted, gated_capability (not set up on this deployment) and rate_limited. Each error says whether it's safe to retry.
Two you are likely to meet with the implementer tools:
forbiddenwithoutside_tool_profile: the tool isn't in this connection's profile. Use a token with the full profile, or a tool from the list above.conflictwithrelease_not_approved: the release isn't approved, so there is nothing to implement or link yet.
Last updated
Edit this page on GitHub