Skip to main content
Most MCP failures are one of a handful of things: an OAuth sign-in that never came back, a malformed key, a client that cannot speak the transport, a revoked credential, a token missing the scope a tool needs, a model that was never queried, or a request that ran past the upstream budget. Each has a distinct signature.

The browser opened but never came back

You started an OAuth connect, a tab opened, you signed in — and the client still says it is waiting. The sign-in worked; the redirect back to the client did not. For a local client — Claude Code, mcp-remote, most editors — the callback goes to a loopback address such as http://127.0.0.1:51234/callback, on a port the client picked when it started. If that tab shows “connection refused” or hangs, the client is no longer listening on it. That happens when the client was restarted, quit, or timed out while you were signing in. Close the tab, restart the connect from the client, and complete the sign-in without leaving it sitting. If the callback tab loads but the client never notices, something is intercepting loopback traffic. A corporate proxy or VPN configured to route all traffic, including 127.0.0.1, breaks this — the browser sends the callback to the proxy and it never reaches your machine. Exempt localhost, 127.0.0.1 and ::1 from the proxy, or connect off the VPN once and let the client refresh from then on. A proxy that intercepts TLS can also break the step before that. The client fetches discovery documents from mcp.attensira.com and clerk.attensira.com; if either is rewritten or blocked, the flow dies before a browser ever opens. Check both are reachable from the machine running the client:
Shell
Both should return JSON. If one returns HTML, a proxy is answering for it.
If the browser opens to a sign-in you cannot complete because the client runs on a server with no display, that client should be using a static API key instead. See Connect a client.

Why did it stop working after a while?

An OAuth access token is short-lived. A connection that works, keeps working for a while, and then 401s on every call has a client that is not refreshing. Well-behaved clients refresh silently and you never see this. When you do, it is usually one of three things:
  • The client did not store the refresh token. Reconnect; if it comes back an hour later, the client is at fault and the workaround is a static key.
  • The client kept an old refresh token. Refresh tokens rotate — each use returns a new one and consumes the old. Replaying a consumed refresh token revokes the whole chain, which looks exactly like being logged out for no reason. Reconnect once; if it keeps happening, the client is holding a stale copy, often from a config file it restores on start.
  • The connection was withdrawn. Withdrawing stops the next refresh, but the access token in flight keeps working until it expires — which is precisely the “worked for a while, then died” shape. See Connected applications.
Time skew produces the same symptom early: a machine whose clock is more than a minute off can have a token that reads as expired the moment it is issued. Check the clock before blaming the client.

Why is it reading the wrong workspace?

Approving a client fixes the organisation, never a workspace, and a static key carries the workspace it was minted in. Either way it is a default rather than a limit: if get_account names a workspace you did not intend, you do not need to reconnect — tell the client to name the right one:
1

List what the credential can reach

Call get_account. Its workspaces array carries every workspace id in the organisation, and project.id is the one being used by default.
2

Name the one you want

Every tool takes an optional project. Ask for the workspace by name and the client will pass the id — “check visibility for the acme.com workspace”.
Working in two workspaces at once needs one connection, not two server entries. What the approval fixes is the organisation, and only reaching a different organisation needs a fresh sign-in.

A tool says the organisation has several workspaces

The message reads roughly “this organisation has several workspaces: pass project=<id> to say which one.” A signed-in connection names your organisation and nothing narrower, so when the organisation owns more than one workspace there is no default for a tool to fall back on. Call get_account. It is the one tool that answers without a workspace — that is why the message points at it — and its workspaces array carries the ids. Then name one: “check visibility for the acme.com workspace”, and the client passes the id as project. You will see project: null in that reply, which is correct and not a fault. It says no workspace resolved for this call, and platforms is empty for the same reason — platforms belong to a workspace, not to the organisation. Name a workspace and both fill in. A static key does not hit this: a key is minted inside one workspace and carries it as its default. See API keys.

Why does one tool fail with 403 when the rest work?

Reads succeed and a write tool — add_prompts, create_automation, run_automation, remove_prompt, delete_automation, resolve_inbox_task, add_competitor, ask — fails. The credential is a static key minted as read-only, and it simply does not carry write access:
403 response
Response header
It is a 403 and not a 401 deliberately — the credential is fine, so a client that throws it away and reconnects is doing the wrong thing. Keys carry a scope chosen at mint time and cannot be widened afterwards: mint a read-and-write key and swap it in. get_account returns the scopes in force, which is the fastest way to settle the question. An OAuth connection never sees this. Approving a client grants read and write — there is no read-only consent — so a 403 on a signed-in connection is not a scope problem. The one tool that refuses a signed-in connection on purpose is get_ai_traffic_install: its response carries your workspace’s signing secret, and a consented token must not be able to read one credential out and mint another. The refusal says so, and the fix is to call it with an API key.

Why am I getting a 401?

Your credential is missing, malformed, expired, or not for this server. The MCP endpoint answers:
401 response
Response header
That message is identical for every cause — missing header, wrong shape, expired token, wrong audience, bad signature. It does not narrow anything down on purpose, because a 401 that explained itself would help someone probing with stolen tokens more than it helps you. On OAuth, read the two sections above first: an hour-long lifetime and a revoked connection cover most of it. A token minted for a different service will also fail here — Attensira access tokens are bound to https://mcp.attensira.com/mcp specifically and are not accepted anywhere else, nor is anything else accepted here. On a static key, check the shape. A valid key matches ^atn_live_[0-9a-fA-F]{32}$ — the literal prefix atn_live_ followed by exactly 32 hexadecimal characters. Count them. Copying from a terminal that soft-wraps, or from a config file where an editor trimmed the line, is the usual cause of a key that is one or two characters short. The server checks the format before it makes any upstream call, so a truncated paste fails instantly and the request never reaches your workspace. Then check the surrounding shape. The header value must be Bearer plus the key, with a single space and no quotes around the key itself. If your config uses an environment variable, confirm the variable is actually set in the environment the client runs in — a GUI app launched from the dock does not inherit a variable exported in your shell profile. Print it with echo ${ATTENSIRA_API_KEY:0:9} to confirm the prefix without exposing the rest. To separate a network problem from an auth problem, hit the unauthenticated health path: curl -i https://mcp.attensira.com/healthz. If that answers and your authenticated call does not, the credential is the problem.

Why can’t my client see the tools?

The server connected but no tools appeared, or the client reports the server as failed with no detail. This is almost always transport: Attensira speaks Streamable HTTP only, and a client that speaks stdio alone cannot reach an HTTPS endpoint directly. Clients that connect natively include Claude Code, Claude Desktop, Claude.ai, Cursor, VS Code + Copilot, Windsurf, Cline, Codex CLI, ChatGPT and n8n. Zed and older builds of several others launch local processes only, and need the mcp-remote bridge:
Shell
The bridge requires Node.js, and its first run downloads the package, so give it a few seconds before deciding it failed. Full per-client configuration is in Connect a client. If your client does connect natively and still shows nothing, check two more things. Some clients only load servers added before the conversation started — open a new chat. And some cache the tool list, so a restart or an explicit refresh in the MCP panel is needed after editing config. A healthy connection lists 33 tools.

Why did my key stop working?

A key that worked yesterday and 401s today has been revoked, either by you or by a workspace admin at app.attensira.com/settings/developer. Revocation takes effect on the very next request, for every client using that key. Withdrawing an OAuth connection is not as immediate: the access token already in flight survives until it expires, because the MCP server verifies tokens locally and never asks us whether one is still wanted. Keys cannot be recovered. Only a hash is stored, so there is no screen anywhere that will show you the original value again — not for you, and not for support. The fix is always to mint a new key and update every client that used the old one.
When you rotate, update every place the key lives: shell profiles, claude_desktop_config.json, ~/.cursor/mcp.json, ~/.codex/config.toml, VS Code secret storage, n8n credentials, and any connector configured with a header. A client left on the old key fails silently in the background. Clients connected over OAuth are unaffected by a key rotation — they hold a different credential entirely.
If nobody revoked anything, re-read the 401 section — a config edit that dropped a character produces the identical error.

Why does a model show no data?

Three different states look like “nothing” and mean very different things. Read the fields before concluding the model failed you.
  • tracked: false — this model is not configured for your workspace, so it was never queried. Reporting it as 0% would invent a failure that did not happen. A new workspace tracks two models by default, chatgpt and perplexity; add more in the dashboard.
  • readable: false — no reader exists for that surface over this route, so Attensira collects nothing from it. google_ai has no API reader, for example; it is still read through the consumer surface, so Google AI Overview answers do arrive.
  • value: null — not measured in this window. Null is not zero.
  • value: 0 with n > 0 — a real, measured zero. The model was asked n times and never named you. That is a finding.
Deltas work the same way. A delta is {value, real} and must pass a two-proportion z-test at 95% before it is reported. Below that noise floor you get {real: false, value: null}, which means “no change we can prove” — never “no change”. Sampling depth also explains thin results: each prompt × model × country runs at most once per workspace-local calendar day, with n=1 on Starter and n=3 on Growth and Business. A prompt added an hour ago may have no readings until the next day’s run. Failed runs are stored but excluded from n. For the full reading guide, see Reading the numbers.

Why is my request timing out?

The upstream budget is 35 seconds. A tool call that needs longer than that upstream is cut off, and your client reports a timeout or an aborted stream rather than a partial answer. The usual cause is an over-broad query: a wide date range across every prompt, page and source at once. Narrow it — one area at a time, a shorter window, a filter on a single topic or model — and the same question usually answers in a fraction of the budget. Long-running agent work has its own path. ask may return status: "running" instead of a result; that is not a failure. Poll get_session for the outcome, passing include: ["events"] when you want the intermediate steps. Do not re-issue ask while a session is running — it spends credits again. Other server limits worth knowing when you are debugging a stalled connection: request header timeout 10 seconds, full request read 60 seconds, idle connection 120 seconds, and a maximum response size of 8 MiB. A response that would exceed 8 MiB is a signal to page or filter the query rather than retry it.
Still stuck? Start from Connect a client and reproduce with the exact block for your client, then check https://mcp.attensira.com/healthz to rule out reachability. If you are writing the client yourself, the wire detail is in the OAuth reference.