Skip to content

Build for MCP clients

Ramose’s MCP server is a projection of your application model, not a second API to maintain. Every deployment exposes three tools at POST /mcp: describe, query, and mutate.

Web app query · mutate · offline
One deployed model Schema · policy · operations authorization is applied before either caller sees data
Agent describe · query · mutate
The application and MCP endpoint are two doors into the same model, not two backends that must be kept aligned.

An agent can only use what the application declares and the principal may access. A good MCP experience therefore starts with a good Ramose model:

  1. Give entities, fields, and operations short descriptions that explain product meaning.
  2. Model product actions as narrow operations with validated inputs.
  3. Grant the exact reads and operations each role needs.
  4. Keep common queries bounded and index-friendly.

There are no MCP-specific handlers to keep in sync. Catalog descriptions become discovery cards, queries compile from QueryDocumentV1, and writes invoke ordinary operations.

Agents should not place an entire application schema in context. Start broad and cheap:

{
"browse": { "kinds": ["entity", "operation"] },
"page": { "limit": 20 }
}

Then ask describe for exact definitions only when needed. Search terms help when the catalog is large. Results include an opaque catalogToken; pass it as ifCatalog to query and mutate. If the model changes, Ramose returns catalog_changed so the client can rediscover instead of guessing.

Descriptions are part of your agent interface. Prefer “A work item that can be assigned and completed” to “Task table.” Say what an operation does and what its input means; do not duplicate implementation details.

query accepts the portable query document rather than source code or SQL. Production limits should cap page size, traversal depth, projection width, execution time, and total work.

Teach clients to recover from query_budget_exceeded by doing less work:

  • Filter before projecting nested data.
  • Select only fields needed for the current step.
  • Lower recursive depth.
  • Request a smaller page and continue with the opaque cursor.
  • Split broad exploration into several focused queries.

Never relax global limits in response to one agent prompt. A smaller, composable result is usually better for model context as well as the database.

mutate identifies one exact operation, optional target, validated input, and client-generated invocationId. The same id may be retried after a timeout; a different intent needs a new id.

Agent-facing operations should express outcomes:

Task.setDone({ done: true })

Avoid ambiguous commands such as toggleDone, wide “update anything” payloads, and raw write access. Precise verbs make authorization, audit trails, retries, and confirmations easier to reason about.

For consequential actions, put confirmation in the calling product and keep the server operation exact. Ramose enforces authorization and consistency; it does not decide whether a human intended a high-impact action.

The endpoint uses Streamable HTTP and OAuth protected-resource discovery. Treat MCP access tokens like application access tokens:

  • Keep them short-lived and audience-bound.
  • Derive the principal and authorized root from the token.
  • Never accept a database id or catalog id from the caller.
  • Apply the same operation and read policy used by the web app.
  • Log operation identity and receipt outcome without logging secrets or unnecessary input data.

An inaccessible response deliberately does not distinguish missing from unauthorized resources. Do not add a discovery side channel in error messages.

Test product behavior at the operation and query layers first. Add protocol coverage for the fixed MCP adapter: discovery pagination, catalog changes, bounded query errors, OAuth failures, repeated invocation ids, and inaccessible targets.

The strongest end-to-end check is simple: perform a mutation through MCP and observe the ordinary web app converge through its normal synchronization path.