MCP for agents
An MCP-enabled REST API (RAPI) instance describes itself to the agent that connects: the server's instructions name the instance and the conventions, the models tool lists what there is to query, and a per-instance guide spells out the attributes, reference paths and worked examples. This page is what that agent sees and how it should use it. To turn MCP on and connect a client, see MCP endpoint.
Before an agent can connect
MCP is served only where it is turned on, and the agent's client has to send what the instance's data routes require:
- Turned on. MCP is part of a paid plan that dcupl turns on for your project; ask your dcupl contact. The deployed API template must set
"mcp": { "enabled": true }; otherwisePOST …/instance/{instanceKey}/mcpanswers404. See Turning it on. - API key. A template with an
api-keyauth entry needs that key as theapi-keyheader on every request; without an auth entry the instance is open to anyone with the URL. - Version guard. With
vGuardEnabled: trueevery request also needs the currentv, and most clients report the missing header only as a failed connection. Turn the guard off for instances agents connect to. See Authentication and the version guard. - Ready. An instance that is not
readyanswers JSON-RPC-32002until it is.
Everything below is read-only: no tool, resource or endpoint script can write to the instance.
What the agent sees
The instructions
On connect (initialize) the server sends its instructions, which clients that support them put into the agent's context before the first call. They are generated per instance and kept short:
- an opening sentence that names the instance by its template's name and description (or its instance key when the template has no name) and the dcupl SDK version it runs
- the recommended flow:
models, then the guide, thenfacetorsuggest, thenquery;aggregate,facetorpivotfor counts - the cross-tool conventions, one sentence each: string keys, references and dotted paths,
findandsuggestmatching,isTruthywithfalse, paging withcount, errors that name the field, and checking an empty result withfacetorsuggest(see Conventions) - when the template has scripted endpoints, how many there are and that the agent should prefer one that fits the question
The exact text comes from the runner and follows it when it changes; read it from the initialize answer rather than from a copy (see Reading it yourself). The same name appears as the server's serverInfo.title (<template name> · dcupl) and in its serverInfo.description.
The tools
| Tool | What the agent uses it for |
|---|---|
models |
The first call: the instance (key, name, description) and its models with item counts. Every other data tool takes one of these modelKey values |
guide |
The instance guide as markdown — the same text as the guide resource, for clients that do not surface resources |
query |
Items of a model matching conditions, with projection, sort and paging (count, start) |
facet |
Distinct values of an attribute with their item counts: breakdowns, and the values that really exist to filter on |
suggest |
Complete a term to the stored values of an attribute, to find the exact value to filter on |
aggregate |
min, max, sum, count, avg, distinct or group over an attribute, optionally filtered |
pivot |
Row and column attributes with aggregations per cell, for two-dimensional breakdowns |
metadata |
A model's total and filtered item counts |
endpoint_* |
One tool per scripted endpoint of the template; it runs the application's own script and returns its JSON |
Each tool's input schema in tools/list carries short descriptions per field, the same contract the REST routes validate against, so an agent can build its arguments from the schema alone. aggregate, facet, pivot, metadata and suggest take an optional query with the query tool's arguments to filter first. How endpoint tools are named and called: One tool per scripted endpoint.
The guide resource
Every MCP-enabled instance lists one resource:
dcupl://instance/{instanceKey}/guideIts title is "How to query this instance", its type text/markdown. It is generated from the instance when it is read, so it always matches the deployed data, and costs nothing until an agent asks for it. It holds:
- Models: every model with its item count and number of attributes (up to 50 models; the guide names the next ten of the rest, and
modelslists them all) - Attributes: per model, each attribute's type and notes —
filter: true, references (target model, single- or multi-valued, "comes back as{ key }") and derived attributes (up to 60 per model;metadatashows the rest) - Reference paths: the dotted paths each reference opens, such as
vendor.country - Scripted endpoints: each
endpoint_*tool with its route, path parameters, input and the description from the template - Worked examples: one
query,facet,aggregate,pivotandsuggestcall on the instance's own most telling model - Conventions: the rules below in long form, with examples
A client that supports resources shows it next to the tools; for every other client the guide tool returns the same markdown. When the instance cannot build it, reading the resource fails with a JSON-RPC error that carries the reason, the same text the tools answer with.
Reading it yourself
Any MCP client shows the same as the agent. With curl (add the api-key header where the template has one):
URL='https://runner.example.com/instance/your-project-id-orders-api/mcp'
H=(-H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -H 'api-key: your-secure-api-key')
# the instructions: result.instructions
curl -s -X POST "$URL" "${H[@]}" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
# the guide resource
curl -s -X POST "$URL" "${H[@]}" -d '{"jsonrpc":"2.0","id":2,"method":"resources/list"}'
curl -s -X POST "$URL" "${H[@]}" -d '{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"dcupl://instance/your-project-id-orders-api/guide"}}'The MCP Inspector CLI reads the guide with --method resources/read --uri dcupl://instance/your-project-id-orders-api/guide.
Conventions
The instructions state most of these rules in one sentence each; the guide states all of them in long form, and the tools' field descriptions repeat them where they apply. The examples use a model Article with a productName, a category, a price and a reference vendor to a model Vendor.
Matching is case-sensitive
In a find condition a plain string matches the whole value, exactly and case-sensitively. "football" does not find "Football", and "ball" does not find "Basketball". eq compares the whole value too.
{ "modelKey": "Article", "queries": [{ "operator": "find", "attribute": "category", "value": "Football" }] }Regex is /…/, flags go into regexFlags
A find value written as "/…/" is a JavaScript regex matched anywhere in the value; anchor it with ^ and $. It is case-sensitive unless options.regexFlags is "i". Flags are never read from the value: "/ball/i" is a plain string, not a regex.
{
"modelKey": "Article",
"queries": [{ "operator": "find", "attribute": "productName", "value": "/ball/", "options": { "regexFlags": "i" } }]
}suggest completes a prefix
In suggest a plain value is a case-insensitive prefix: "bask" completes to "Basketball", "ball" does not. A "/…/" value is a regex, case-sensitive unless options.regexFlags is "i".
{ "modelKey": "Article", "options": { "attribute": "productName", "value": "/ball/", "regexFlags": "i", "max": 5 } }Item keys are strings
Every item has a string key, also when it looks like a number. Filter with "42", not 42; path parameters of endpoint tools are strings as well.
{ "modelKey": "Article", "queries": [{ "operator": "eq", "attribute": "key", "value": "42" }] }References come back as { key }, filter through them with a dotted path
A reference attribute returns the key of the referenced item, { "key": "v1" }, not the item itself. A nested projection inlines the target's attributes; a dotted path filters, facets, aggregates or pivots on them. Keep paths to 2–3 levels; the guide lists the paths each reference opens.
{
"modelKey": "Article",
"queries": [{ "operator": "eq", "attribute": "vendor.country", "value": "Austria" }],
"projection": { "productName": true, "vendor": { "name": true } }
}Paging uses count and start
query returns at most count items (default 100, max 1000) from the zero-based offset start; start: 20, count: 10 returns items 21–30. There is no limit or page: an unknown top-level key is a tool error (isError) that names the key, points a paging alias to the real key and lists the valid keys, for example unknown option limit (use count); valid keys: modelKey, …, so the agent calls again with count. Over REST the same request still runs with the key ignored (without count it returns every matching item) and the runner logs a warning; a runner started with REST_VALIDATION=strict answers 400 instead. For totals use aggregate or metadata instead of paging through a model.
{ "modelKey": "Article", "count": 10, "start": 10 }Sorting is upper case
sort takes the attributes and one direction per attribute, ASC or DESC in upper case.
{ "modelKey": "Article", "sort": { "attributes": ["price"], "order": ["DESC"] }, "count": 5 }Empty values: isTruthy with false
isTruthy with value false finds null, missing, empty strings and 0; true finds the rest. There is no separate null or empty operator; for a list without entries use size with 0.
{ "modelKey": "Article", "queries": [{ "operator": "isTruthy", "attribute": "description", "value": false }] }Errors name the field
A wrong input is answered with a tool error (isError) that names the field and what it expected. The agent fixes that field and calls again instead of guessing another shape:
MCP error -32602: Input validation error: Invalid arguments for tool query: Invalid option: expected one of "ASC"|"DESC" at sort.order[0]An unknown operator lists the valid ones, including the custom operators registered on the instance:
unknown operator "contains"; expected one of "eq"|"in"|"find"|"gt"|"gte"|"lt"|"lte"|"typeof"|"isTruthy"|"size" at queries.0.operatorAn empty result is a question, not an answer
An empty result usually means a wrong value, case or matching mode, not missing data. Before reporting "none", the agent checks the value with facet or suggest — see step 5 below.
The first five calls
An agent asked "What are the most expensive football articles?" on an instance it has never seen:
1. models — what is there, and how big.
{
"instance": { "key": "your-project-id-shop-api", "name": "Shop API" },
"models": [
{ "key": "Article", "count": 100 },
{ "key": "Vendor", "count": 99 }
]
}2. guide (or the resource) — the attributes of Article, that vendor is a reference to Vendor, the path vendor.country, and a worked example per tool. No arguments.
3. facet — the values category really holds, before filtering on one.
{ "modelKey": "Article", "options": { "attribute": "category", "count": 10, "sort": "size-desc" } }[
{ "key": "Fitness", "value": "Fitness", "size": 13 },
{ "key": "Football", "value": "Football", "size": 9 }
]4. query with sort — the items, with the value spelled as the facet showed it, the most expensive first and only the attributes the answer needs.
{
"modelKey": "Article",
"queries": [{ "operator": "eq", "attribute": "category", "value": "Football" }],
"sort": { "attributes": ["price"], "order": ["DESC"] },
"projection": { "productName": true, "price": true },
"count": 5
}5. suggest with regexFlags — when a term from the question is not a stored value (the user said "soccer"), resolve it before filtering, instead of reporting an empty result.
{ "modelKey": "Article", "options": { "attribute": "productName", "value": "/soccer/", "regexFlags": "i", "max": 5 } }[{ "value": "Soccer Ball!", "keys": ["2"] }]Counts and breakdowns
For "how many", "total", "average" or "per vendor" the agent does not read items into the conversation: aggregate, facet and pivot answer from the whole model in one call.
{
"modelKey": "Article",
"options": { "attribute": "price", "types": ["avg", "max"] },
"query": { "queries": [{ "operator": "eq", "attribute": "category", "value": "Football" }] }
}{ "modelKey": "Article", "options": { "attribute": "vendor.country", "sort": "size-desc" } }{
"modelKey": "Article",
"options": {
"rows": [{ "attribute": "category" }],
"columns": [{ "attribute": "vendor.country" }],
"values": [{ "attribute": "price", "types": ["sum"] }]
}
}When the template has a scripted endpoint that answers the question, the agent should prefer its endpoint_* tool: it returns what the application's own script computes.
Monitoring
The guide shows up in the instance's invocation log like the tools: mcp:guide for the tool, mcp:resource:guide for a resource read. See Monitoring.