Skip to content

API and MCP#

Ticket Board exposes its domain through two transports: a REST API for humans and bots, and a Model Context Protocol (MCP) server for bots driven by language models. Both transports talk to the same service layer, so any rule enforced for one is enforced for the other.

This section is the operator-and-integrator reference for both surfaces. For the conceptual "why" behind a single service layer with two transports, see the architecture overview.

The two surfaces at a glance#

Surface Base path Auth Audience Wire format
REST /api/v1 Session cookie (humans) or X-API-Key header (bots) Browser UI, bot SDKs, curl, anything that speaks HTTP/JSON JSON over HTTP
MCP /mcp/bot/{token} Token embedded in the URL path LLM agents speaking the Model Context Protocol JSON-RPC over HTTP

A bot has exactly one token. That token authenticates it on both surfaces — as X-API-Key over REST and as the path segment over MCP. There is no separate identity for the two transports; one bot, one credential, one audit identity.

Which surface do I use?#

  • Building a web client or an integration script. Use REST. The OpenAPI document is the contract; auto-generated TypeScript and Python clients are the easiest path.
  • Wiring up an LLM agent (Claude Code, a custom MCP client, etc.). Use MCP. You get a curated tool catalog that the model already knows how to introspect via tools/list.
  • Bots that do both. Common. The bot reads tickets through MCP tool calls and uses REST for the occasional batch import where idempotency keys matter. The same token works for both.

Shared semantics#

Everything in the list below holds across both transports because it lives in the service layer, not in the routers.

  • Authorization. A bot is bound to a project allowlist and a read/write mode. Write attempts outside scope return auth.forbidden; write attempts by a read-only bot return auth.read_only_bot.
  • Workspace scope. All requests resolve to a single workspace (v1 has exactly one). Workspace ids never appear in URLs or tool arguments.
  • Identifiers. Resources are addressed by UUIDv7. Tickets also accept their human-readable display id (PROJ-123) anywhere a single ticket is addressed.
  • Errors. A single envelope shape, machine-readable code, human-readable message, optional details. See REST error envelope and the MCP error mapping table.
  • Pagination. Opaque cursor pagination with the same data / page.next_cursor / page.has_more envelope on both surfaces. Cursors are portable: a cursor handed out over REST can be passed back through an MCP tool call.
  • Rate limiting. Login is rate-limited per-IP and per-username today; per-actor REST/MCP limits are planned (the user.rate_limit_per_minute column is reserved). See Rate limiting.
  • Audit. Every write produces an audit_event attributed to the calling actor. MCP tool calls additionally produce a tool_invocation_log row carrying the redacted arguments and outcome.

OpenAPI#

The REST surface is described by an OpenAPI 3 document FastAPI generates at runtime. The frontend regenerates its TypeScript types from it on every API change. See OpenAPI spec for where it lives at runtime, how to point a code generator at it, and the conventions the schema follows (operation ids, discriminated unions, etc.).

Reference material#

  • REST API — auth, error envelope, pagination, idempotency, ETags, and the full endpoint catalog by resource group.
  • MCP server — per-bot mount, the 29-tool catalog, MCP resources, and the tool-invocation log.
  • OpenAPI spec — where the schema is served, code generation tips, and stability guarantees.

Cross-references#