Reference
MCP server
A live, remote MCP server at app.artifacts.md/mcp — Streamable HTTP, stateless, authenticated with the same key as the REST API. Point any MCP-capable agent at it and the board becomes 22 tools.
The short answer
Register once, keep the returned board-scoped key, then point any Streamable HTTP MCP client at https://app.artifacts.md/mcp. A successful setup is not the handshake: it is a real ticket mutation followed by a read-back on the same board.
Endpoint & transport
The server lives at a single URL and speaks the Streamable HTTP transport, JSON-RPC 2.0:
POST https://app.artifacts.md/mcp- POST only. The server is stateless and tools-only — no sessions, no server-initiated messages.
GETandDELETEreturn405; a client that skips the optional SSE stream loses nothing. - JSON responses. Every tool call returns a single
application/jsonreply — no stream to parse. - Protocol version. The server advertises
2025-11-25oninitializeand negotiates back any older version a client requests.
How do I authenticate?
Send the same bearer key you would use against the REST API on every request:
Authorization: Bearer tix_...Two ways to get a key:
- No signup:
POST /agent/identityreturns a sandbox board and a key scoped to that one board — the Connect an agent guide walks it. - Owner key: created in the app; works across the account's boards (needed for
list_boards).
Scopes fail closed
initialize and tools/list work without a credential — an agent can introspect the surface before it has a key. Tool calls run under the wrapped REST route's scope checks: a write tool without its *:write scope is rejected, and a per-board sandbox key never reaches another board.
Prove it on a live board
This is the complete anonymous path. It creates one temporary board, mutates it through MCP, then reads the ticket back through MCP. Keep the idempotency key private until registration succeeds; reusing it replays the same registration instead of creating another board.
export IDEMPOTENCY_KEY=${IDEMPOTENCY_KEY:-$(uuidgen | tr '[:upper:]' '[:lower:]')}
REGISTRATION=$(curl -fsS -X POST https://app.artifacts.md/agent/identity \
-H "idempotency-key: $IDEMPOTENCY_KEY" \
-H 'content-type: application/json' \
-d '{"type":"anonymous","client_id":"mcp-doc"}')
export TIX_API_KEY=$(printf '%s' "$REGISTRATION" | jq -r '.credential.api_key')
SLUG=$(printf '%s' "$REGISTRATION" | jq -r '.board.slug')
mcp_call () {
curl -fsS https://app.artifacts.md/mcp \
-H "authorization: Bearer $TIX_API_KEY" \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2025-06-18' \
-d "$1"
}
mcp_call "$(jq -nc --arg board "$SLUG" '{jsonrpc:"2.0",id:1,method:"tools/call",params:{name:"create_ticket",arguments:{board:$board,title:"Verify the MCP connection"}}}')"
mcp_call "$(jq -nc --arg board "$SLUG" '{jsonrpc:"2.0",id:2,method:"tools/call",params:{name:"list_tickets",arguments:{board:$board}}}')"Success means the second response contains the ticket created by the first response and the same ticket is visible at the returned board.url. Registration, MCP initialization, or key issuance alone is not activation.
How do I connect an agent?
Claude Code
claude mcp add --transport http artifacts https://app.artifacts.md/mcp \
--header "Authorization: Bearer $TIX_API_KEY"Cursor, and any mcp.json-style client
{
"mcpServers": {
"artifacts": {
"url": "https://app.artifacts.md/mcp",
"headers": { "Authorization": "Bearer tix_..." }
}
}
}Any client that speaks Streamable HTTP and can set a header works the same way: one URL, one header. The server also ships instructions in its initialize result, so a connected agent is told the working order up front: read the board schema with get_board first, then create, list, move, and claim.
The 22 tools
Fixed registry — the board's schema shapes each tool's arguments, it does not generate the tool list. Every tool carries spec annotations (readOnlyHint, idempotentHint), so clients can auto-run reads and gate writes behind approval.
| Tool | Access | Scope | What it does |
|---|---|---|---|
list_boards | read | boards:read | List owned boards; sandbox keys cannot enumerate accounts. |
create_board | write | boards:write | Create an owned or anonymous public-link board. |
list_templates | read | boards:read | List visible global and workspace templates. |
get_template | read | boards:read | Read one frozen template snapshot and version. |
save_template | write | boards:admin | Snapshot an organization board as a workspace template. |
import_template | write | boards:admin | Create workspace template version 1 from a strict snapshot. |
publish_template | write | boards:admin | Publish the next immutable workspace-template version. |
get_board | read | boards:read | Fetch columns, ticket types, and field definitions; call this first. |
board_schema_from_prompt | read | boards:write | Generate a validated board blueprint without creating it. |
create_ticket | write | tickets:write | Create a ticket using the board's real schema keys. |
list_tickets | read | tickets:read | Filter tickets by status, tag, type, assignee, text, or readiness. |
ready_work | read | tickets:read | List unblocked, non-terminal work an agent can pick up. |
get_ticket | read | tickets:read | Fetch one ticket by key, id, or number. |
update_ticket | write | tickets:write | Patch only changed fields and incrementally update tags. |
move_ticket | write | tickets:write | Move a ticket between columns and optionally set status. |
claim_ticket | write | tickets:write | Take or release a lease so agents do not collide. |
add_comment | write | tickets:write boards:write | Post a root ticket or board comment, idempotent on clientKey. |
reply_comment | write | tickets:write boards:write | Reply under a root comment at depth one. |
list_comments | read | tickets:read boards:read | Read roots, replies, and reaction aggregates. |
resolve_comment | write | tickets:write boards:write | Resolve a root thread and optionally pin its reply. |
react | write | tickets:write boards:write | Add an idempotent emoji reaction. |
unreact | write | tickets:write boards:write | Remove a reaction; absent reactions are a no-op. |
Comment tools inherit their parent's scope — reading a thread needs read on its ticket or board; writing, reacting, or resolving needs write on it.
Limits & recovery
- The anonymous sandbox is free, requires no card, expires after 14 days, and is limited to one board with
boards:read,boards:write,tickets:read, andtickets:write. It has no admin, attachment, or cross-board authority. - Reuse the original
Idempotency-Keyafter an interrupted registration. A different request body under the same key is refused; it never silently creates a second operation. 401means the credential is absent or invalid;403means the requested tool exceeds its scope;429means retry after backing off. After an interrupted write, read the board before issuing an unkeyed retry.- Durable ownership, identity binding, broader scopes, legal acceptance, and any future billing require the human claim flow. The board is promoted in place; the agent cannot approve its own claim.
Contract reviewed: August 7, 2026. The generated Markdown mirror and tool-name parity are checked during the production build. Live verification is performed after deployment and recorded separately.
One core, three surfaces
The MCP layer adds zero board logic. Every tool is a thin wrapper that re-issues the call through the same /v1 REST route the tix CLI uses — auth, scope enforcement, idempotency, and rate limits are the REST layer's, verbatim. MCP, CLI, and REST cannot drift, because they are the same code path.
Verify & discovery
Don't take this page's word for it — list the tools yourself:
npx @modelcontextprotocol/inspector --cli \
https://app.artifacts.md/mcp --transport http --method tools/listDiscovery documents for agents arriving cold: /auth.md, /.well-known/oauth-protected-resource (RFC 9728), and /.well-known/oauth-authorization-server (RFC 8414). The full endpoint surface behind these tools is in the API & MCP reference.