Connect an external capability through MCP¶
At the end of this tutorial a model in Core looks up tickets through the MCP server of Noordwind's ticket system. You have registered the server, chosen which of its tools you allow, and given them to a request and to an agent.
| Building blocks | MCP: Core uses an MCP server. Tools: toolsets and allowed destinations. Keys, capabilities and policy |
| You need | a key with the capability tooling (and agents for step 6); the address of an MCP server and a token for it; an administrator of your organisation, or a key with management, for step 1; for the trace in the check, trace storage must be on for your organisation (Read a trace) |
| Builds on | nothing. Step 6 uses the agent from Build your first agent if you have it |
MCP (Model Context Protocol) is a standard through which a server offers tools. Choose this way when the system you want to connect already has an MCP server: you then get all of its tools, with their descriptions and schemas, in one registration. When the system only has an HTTP API, an API connector is shorter.
The examples use https://mcp.tickets.example.com/ with the tools search_tickets, get_ticket
and close_ticket. Replace the address with that of your own server.
1. Allow the host¶
Core reads the server's tool list and later calls its tools. Both are outbound calls, and an
organisation reaches no host until it allows one. In the console: open Settings, the tab
Access & policy, choose Add a host on the card Allowed hosts, enter
mcp.tickets.example.com and choose Allow host.
With a key that has management:
curl -s -X POST "$CORE_URL/organisation/allowed-hosts" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"host": "mcp.tickets.example.com"}'
You do this first so that the first read after registering succeeds straight away.
2. Register the server¶
curl -s -X POST "$CORE_URL/mcp_servers" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "tickets",
"url": "https://mcp.tickets.example.com/",
"auth": {"type": "bearer", "secret": "Bearer <the server token>"}}'
The answer is 201 (abridged):
{
"id": "mcp_6Tq1...",
"name": "tickets",
"allowed_tools": null,
"last_synced_at": null,
"last_sync_error": null,
"tool_count": 0,
"egress": [{ "host": "mcp.tickets.example.com", "allowed": true, "blocked": false }]
}
export SERVER_ID=mcp_6Tq1...
The name tickets returns in every tool name (mcp__tickets__<tool>) and does not change
afterwards. You keep the token with the registration: Core sends it when reading the tool list and
on every call, and never returns it.
3. Wait for the first read¶
Core reads the tool list in the background shortly after you register.
curl -s "$CORE_URL/mcp_servers/$SERVER_ID" -H "Authorization: Bearer $CORE_KEY"
Repeat this until last_synced_at holds a time. When last_sync_error is then null and
tool_count is greater than zero, the server has been read. A server without a successful read
offers a turn no tools. You ask for a new read with POST /mcp_servers/$SERVER_ID/sync, which
answers 202.
4. Choose which tools you allow¶
curl -s "$CORE_URL/mcp_servers/$SERVER_ID/tools" -H "Authorization: Bearer $CORE_KEY"
{
"items": [
{ "name": "search_tickets", "description": "Searches tickets by keyword or customer.", "offered": true },
{ "name": "get_ticket", "description": "Reads one ticket.", "offered": true },
{ "name": "close_ticket", "description": "Closes a ticket.", "offered": true }
]
}
While allowed_tools is null, the registration offers every tool of the server, including a
tool the server adds later. For an assistant that only needs to read, that is too wide:
curl -s -X PATCH "$CORE_URL/mcp_servers/$SERVER_ID" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"allowed_tools": ["search_tickets", "get_ticket"]}'
close_ticket is then offered: false and reaches no turn at all. You narrow at the registration
and not per request because the server is somebody else's: what it offers tomorrow is not yours to
decide.
In the console you do steps 2 to 4 under MCP servers.
5. Offer the tools on a request¶
Every registered server is also a toolset of its own tools. You name the set, or one tool:
curl -s -X POST "$CORE_URL/responses" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"model": "<a model id>",
"tools": [{"type": "toolset", "name": "mcp__tickets"}],
"input": "Which tickets are open about damaged orders?"}'
You name one tool with {"type": "tool", "name": "mcp__tickets__search_tickets"}. The toolset
moves with what the registration allows; a single tool name is fixed. Use the set when you mean
the whole server and the single name when a turn may do exactly one thing.
6. Give the tools to an agent¶
curl -s -X POST "$CORE_URL/agents/$AGENT_ID/versions" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"model": "<a model id>",
"instructions": "You are the support assistant of Noordwind. Answer briefly. Look up existing tickets before you say anything about the status of a report.",
"temperature": 0.2,
"tools": [{"type": "toolset", "name": "mcp__tickets"}]}'
A version is the whole definition. When your agent already has other tools from earlier tutorials,
include them in the same tools list.
Check that it works¶
The answer to step 5 (abridged) holds the call to the MCP tool:
{
"status": "completed",
"output": [
{ "type": "mcp_call", "name": "mcp__tickets__search_tickets", "server_label": "mcp__tickets",
"arguments": "{\"query\": \"damaged\"}", "status": "completed", "output": "..." },
{ "type": "message", "role": "assistant",
"content": [{ "type": "output_text", "text": "Two tickets are open: ..." }] }
]
}
The arguments and the output depend on your server. GET /tools?source=mcp shows the MCP tools
your key can offer; close_ticket is no longer among them. The trace of the turn
(GET /responses/{id}/trace) shows the execution under tool_calls, with source: "mcp".
If it does not work¶
| You see | Cause | Fix |
|---|---|---|
404 on POST /mcp_servers |
the capability tooling is off on your key or in your organisation's ceiling |
ask your administrator to switch it on (Capabilities) |
last_sync_error is filled |
Core could not read the server, and the sentence says why: the host is not allowed, the address is unreachable, the server refused the credential with 401 or 403, it listed more than 1,000 tools or more than 2 MiB of tool definitions, two of its tools reach one name, or the address is one of your own toolsets on this Core |
check egress on GET /mcp_servers/{id}, fix the cause and send POST .../sync |
| the model gets no tools | the server has not been read yet, or allowed_tools excludes them |
steps 3 and 4 |
404 on POST /responses |
the tool name or the toolset does not exist for your key | read the names with GET /tools?source=mcp |
400 with tools or tools.N.name in error.param |
two tools on the request have the same name for the model; tools.N.name is a tool of your own named exactly like a tool Core runs in the same request |
offer each tool once, or rename your own |
"Tool endpoint refused the credential." in the answer |
the server answered the call with 401 or 403 |
check the credential stored with the server |
"Denied by policy." in the answer |
the server's host is not allowed, or a rule over Destinations of your key excludes it | check egress on GET /mcp_servers/{id} and repeat step 1 |
| the host is on your list and the call is still refused | an organisation above yours does not permit the host (egress then reads allowed: false), or it restricts regions the host is not served in |
ask the administrator of the organisation above to permit the host (Policy) |
the model does not get a tool that is offered: true |
your key's policy does not allow the tool; Core then does not offer it | read the rules over Tools with GET /key |
Read on¶
- MCP: the two directions, and when you use which.
- MCP servers in the console and MCP servers through the API.
- A person's own login: a server where every person works with their own account.
- Use Core from an MCP client: the other direction, in which a client outside Core uses your toolsets.
- Marketplace: ready-made Artific Connect bundles for well-known services.
Next tutorial: Build an agent that combines knowledge and tools.