Troubleshooting
Symptom first, in the words the server actually uses. Looking for a shape rather
than a symptom — what api_error means, which code values exist, what a
truncated result looks like — see
Results and errors.
Connecting
The host cannot be resolved
You used mcp.opusdns.com. There is no such host. The endpoint is
https://api.opusdns.com/mcp.
401 with {"error":"authentication_required"}
No bearer token was sent, or you sent an X-Api-Key. API keys are not accepted
by the MCP endpoint — complete the OAuth sign-in instead. In Claude Code that is
/mcp, or claude mcp login <name>. See
OAuth details.
401 with {"error":"invalid_token","error_description":"Bearer token could not be validated"}
The token was rejected. The usual causes, in order of likelihood: it expired; it
was minted for the wrong realm (a sandbox token against production, or the
reverse); or it lacks the required audience because the opusdns:mcp scope was
not requested. Sign out and back in.
invalid_scope during sign-in
Your client asked the authorization server for a scope it was not registered
with. This is the case the resource metadata is deliberately shaped to avoid —
see Why offline_access is
advertised.
The server never appears in my client
Check the wrapper key and the transport type. VS Code nests servers under
servers; Claude Code and Cursor use mcpServers. In Claude Code and VS Code an
entry with a url and no "type": "http" is read as a local command and
skipped. See Connect Cursor, VS Code, and
ChatGPT.
ChatGPT signed in but reports no tools
Developer mode is off, or your plan does not offer it. Without it ChatGPT
surfaces only tools named search and fetch, and this server exposes neither,
so a connector whose OAuth flow succeeded still looks empty. Developer mode is a
paid-plan feature on the web and does not exist on the free plan — an OpenAI
limitation, not something the server can work around. See
ChatGPT.
403 from a tool call
Authentication worked; your organization role does not permit that operation. Approval is not authorization — see Roles & permissions.
503 with Retry-After: 1
{ "error": "too_many_requests", "error_description": "Server is busy, retry shortly." }
The concurrency cap. It counts requests in flight on one server instance across all callers, not just yours, so it can trip because the service is busy rather than because of anything you did. Retry after a second.
I removed the connector but the agent still has access
Removing the server from one client does not invalidate a token another client
already holds, and a mcp-remote bridge keeps its own copy on disk. See
Disconnecting for what each step stops and
how to cut access off outright.
Approvals
Approval keeps being requested
One of:
- The retried call was not byte-identical. The approval binds the path parameters, the query and the body.
- A bulk selector resolved to a different set of domains than when you approved. Preview again and approve the new set.
- More than about five minutes elapsed.
- The token had already been used. Each one works once.
- You signed in again, or a different client is doing the retry. The approval binds the user, the client and the granted scopes.
The agent says the action was declined
{"status":"declined"} means the prompt was declined or dismissed. Nothing ran,
and the token was not consumed — you can still approve the same action until it
expires.
My client never shows an approval prompt
It is on the text path, and it should be showing you the
confirmation_required payload before it retries. If it retries silently, that
is a client behaviour to raise with the client's vendor: the server can verify
that a token matches an action, but not that a human saw it. See Path
B.
I approved it and nothing happened
The approval reached your client but the retry never left it. Nothing was consumed, so simply ask again. If it repeats with one particular client, that is a client bug worth reporting — the server cannot retry on its behalf.
Using the tools
The agent called one operation per domain instead of one batch
Say so: "do that as one bulk operation, not one call per domain". A loop of
call_operation costs an approval per domain and is exactly what
the bulk tools exist to replace.
The agent acted on the wrong organization
organizationId applies to a single call, never to a session. Every call in
a sequence needs it, and a preview and a submit that disagree about it are two
different actions. See Sub-organizations.
A new tool or a changed parameter is not showing up
Tool lists are cacheable for about fifteen minutes. Reconnect the server in your
client to force a fresh tools/list.
A filter worked but status_tags came back null
The domain list only populates tags and status_tags when the request asks
for them. Add "include": ["tags"] to the selector.
Bulk
| Symptom | Cause |
|---|---|
selector matched no domains; nothing to submit… |
Filters too narrow — or tag_ids was given a tag label instead of a tag ID |
selector matched 1204 domains, exceeding the limit of 1000… |
One batch is one command, and a command holds 1,000 instances. Narrow the selector and submit in parts |
unknown templateType "..." |
The error lists the allowed values. See Bulk templates |
hostnamePrefix is only valid for hostname-addressed templates… |
Only the forward templates take a prefix |
Invalid query parameter "error_class" |
errorClass is a list — ["BillingInsufficientFundsError"], not a bare string |
"httpStatus": 204 with empty data |
Success. Pause, resume and cancel return no content |
Results
"truncated": true
The API response exceeded the per-call size limit and was cut short. Narrow the
request — for portfolio_query, ask for fewer fields or a smaller pageSize.
"status": "api_error"
A normal HTTP error from the OpusDNS API, not a protocol failure. httpStatus
carries the status and data carries the API's problem detail.
My client sends numbers as strings and it still works
It does. Common client serialisation quirks — a page number as "2", a list as a
JSON string — are repaired before the request is validated. You do not need to
work around it.
Getting help
Send an X-Request-Id header on your requests and it is echoed back. Results
that came from an OpusDNS API call — anything with an httpStatus — also carry
headers["x-server-request-id"], the API's own request ID, when the API sent
one. Quote whichever you have when you contact
support, and check
status.opusdns.com first for anything that looks
like an outage.

