Connect your AI agent (MCP)
Tarno ships a neutral, read-only MCP server so an AI agent can query the INAPI trademark corpus through the Model Context Protocol. It exposes the same read contract as the REST API — backed by the same query layer and the same Zod schemas — so the two transports return identical results.
Endpoint and transport
| Property | Value |
|---|---|
| Endpoint | https://mcp.tarno.cl |
| Transport | Streamable HTTP (MCP) |
| Auth | X-API-Key header, sent per request |
| Server identity | tarno-read-mcp |
The server answers MCP JSON-RPC over the Streamable HTTP transport. A GET /health probe is the
only unauthenticated route; every tool call is authenticated.
Authentication
Authentication is per tool, not per connection. Each tool handler verifies the presented key
before it reads anything, so a missing or invalid key never returns corpus data. Send your
operator-issued key in the X-API-Key header on the transport's requests (placeholder <API_KEY>
in these docs):
X-API-Key: <API_KEY>
Both tools require the brands:read scope (an unrestricted, empty-scope key also works) — see
the auth guide. A key that is missing/invalid is rejected as an unauthorized tool
error; a valid key lacking the scope is rejected as forbidden.
The transport-level
initializehandshake may succeed without a key — the hard invariant is that a tool call never returns corpus data unless the key is valid and scoped. Always send the header.
Quota
The same monthly quota that governs the REST API applies to the MCP. When a key is over
quota the server responds with HTTP 429 and a Retry-After header (seconds until reset),
byte-for-byte matching the REST quota response:
HTTP/1.1 429 Too Many Requests
Retry-After: 1209600
{ "error": { "code": "quota_exceeded", "message": "Monthly quota exceeded", "requestId": "..." } }
Tool catalog
The server registers read-only tools only, each the exact twin of a REST route: same
validation, same data, same error codes. The two below are the entry points; your MCP client
enumerates the full catalogue with tools/list, which includes among others get_feriados and
get_feriados_cobertura (the holiday calendar and the range it claims to cover), get_freshness,
get_sync_runs, search_personas and the watch_* watchlist family.
People say what KIND of party they are, since 2026-09-08. Every person — inside
get_brand_detail, insideget_brand_reportand insearch_personas— carries five added fields:tipo(natural/juridica/desconocido),tipoOrigen(rut/sufijo/manual, omitted, nevernull, when there was no signal),observacion,nombrePublicadoandrevision. They are additive — no field was removed, renamed or re-typed — and they are read under the samebrands:readscope, so no key in circulation needs re-minting. REST≡MCP parity for these fields is verified by a deep-equal test against a real Postgres, including the omission oftipoOrigen. What each one means: people search.
search_brands
Search the neutral brand corpus by free text (full-text + fuzzy) with optional filters and
stable keyset pagination. Identical to REST GET /v1/brands.
Input (the shared search shape — all fields optional):
| Field | Type | Notes |
|---|---|---|
q |
string | Free-text query. When present, results are ranked. |
clase |
integer | Niza class, 1–45. |
estado |
string | Status code (controlled catalog). |
tipoSigno |
string | Sign-type code (controlled catalog). |
fechaDesde |
YYYY-MM-DD |
Filing date lower bound. |
fechaHasta |
YYYY-MM-DD |
Filing date upper bound. |
limit |
integer | Page size, default 20, max 100. |
cursor |
string | Opaque keyset cursor for the next page. |
Output: a JSON page of brand summaries — { items: [...], nextCursor: string | null } —
identical to REST GET /v1/brands. The payload is returned as the tool result's first text
content block (parse it as JSON).
get_brand_detail
Fetch one brand by its application number, composed with its classes, persons (with role), and
Estado-Diario annotations. Identical to REST GET /v1/brands/:nroSolicitud.
Input:
| Field | Type | Notes |
|---|---|---|
nroSolicitud |
string | The brand application number (required). |
Output: the full brand detail as JSON. An unknown application number returns a structured
not_found tool error (see Error shape below), mirroring the REST equivalent's 404 not_found.
Error shape
Every tool error shares one shape: a tool result with isError: true whose text is the JSON
{ "error": { "code": "<code>", "message": "<message>" } } — identical to the {error:{code}}
envelope the REST API returns for 4xx/5xx (REST≡MCP). A client can JSON.parse(text).error.code
uniformly for any tool error. The codes you may see:
code |
Meaning |
|---|---|
unauthorized |
Missing, invalid, revoked or expired key. |
forbidden |
Valid key lacking the required scope (e.g. brands:read). |
invalid_input |
Invalid input (e.g. the "exactly one of nroSolicitud or q" rule). |
not_found |
By-id lookup of an unknown (or another tenant's) resource. |
query_timeout |
The query exceeded the time budget; retryable. |
service_unavailable |
Dependency temporarily unavailable; retryable. |
fuera_de_cobertura |
A range was requested that the holiday calendar does not claim to cover. The only code that also carries coverage inside the error, with the real range. |
internal_error |
Generic internal error — never leaks internal details. |
Internal database or transport details are never included in the message.
Connect your AI client
Generic Streamable-HTTP MCP client
Most MCP clients accept an HTTP server entry with custom headers. Point it at the endpoint and
send your key in the X-API-Key header:
{
"mcpServers": {
"tarno": {
"type": "streamable-http",
"url": "https://mcp.tarno.cl",
"headers": { "X-API-Key": "<API_KEY>" }
}
}
}
Claude Desktop (via the mcp-remote bridge)
Claude Desktop launches MCP servers over stdio, and its built-in custom connector UI only
supports OAuth or no-auth servers — it has no field for a custom X-API-Key header. To reach
a header-authenticated endpoint like Tarno, bridge to it with the mcp-remote
helper, which runs locally over stdio and injects the header on each request.
Prerequisite: Node.js 18 or newer on your PATH (Claude Desktop invokes
npx). Check with node --version.
1. Open your Claude Desktop config file (create it if it does not exist):
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
You can also reach it from Settings → Developer → Edit Config.
2. Add Tarno to mcpServers. Keep your key in an env entry (not inline in args) — this is
the safe pattern and avoids a known Claude Desktop/Windows bug where a space inside an args
value is mis-escaped when it invokes npx:
{
"mcpServers": {
"tarno": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://mcp.tarno.cl",
"--header", "X-API-Key:${TARNO_API_KEY}"
],
"env": {
"TARNO_API_KEY": "<API_KEY>"
}
}
}
}
Replace <API_KEY> with your operator-issued key. (Because an API key never contains spaces, the
inline form "--header", "X-API-Key:<API_KEY>" also works — but the env pattern above keeps the
key out of the args list and dodges the space bug.)
3. Restart Claude Desktop fully (quit and reopen). Tarno then appears as a connected tool
source; you can ask Claude to search brands or fetch a brand's detail, and it will call
search_brands / get_brand_detail for you.
Troubleshooting: if the server does not appear, confirm node --version works in a terminal,
that the JSON is valid (no trailing commas), and that the key is a live, scoped key — an
unauthorized/forbidden tool error means the key is missing, invalid, or lacks brands:read.
For a programmatic client (connect + callTool) using @modelcontextprotocol/sdk, see the
code samples.
See also
- Authentication and scopes —
X-API-Key,brands:read, lifecycle, error codes. - Code samples — a minimal MCP SDK client.
- Getting started — the equivalent REST onboarding.