Common Litmus MCP failure modes and how to fix each one.
Troubleshooting
If you hit something that isn't covered here, email founders@litmushiring.com with the tool you called, the arguments, and the response — we'll dig in.
401 Unauthorized
Common causes, in order of likelihood:
Missing trailing slash on the URL.
Use https://litmushiring.com/mcp/ — the trailing / matters. Hitting
/mcp triggers a 308 redirect, and most HTTP clients drop the
Authorization header across that redirect. The 401 you see is from the
second request — the one without your bearer token — not from a bad key.
This is the single most common setup mistake. If your key worked yesterday and stopped today, double-check the URL hasn't lost its trailing slash.
OAuth token expired. Clerk-issued tokens follow your org's session length. A previously-working OAuth client that suddenly 401s usually just needs a fresh sign-in — most clients trigger this automatically on the next call.
Not an admin (OAuth only).
OAuth access is admin-only. A non-admin member who completes Clerk sign-in
still 401s against /mcp/. Either promote the user from your dashboard or
hand them an API key.
Member of multiple Clerk orgs (OAuth only).
If your Clerk user belongs to more than one org and you connect via the
legacy /mcp/ URL (no org segment), the OAuth verifier can't pick which
org you mean and refuses with 401. Switch to the org-scoped URL —
https://litmushiring.com/mcp/<your-org-id>/ — copy the exact value
from Settings → API Keys in your dashboard. Add the connector with:
claude mcp add --transport http litmus https://litmushiring.com/mcp/<your-org-id>/Same fix applies to Cursor's mcp.json and any other client.
Key was revoked. Revoked API keys stop working on the next request. Check Settings → API Keys in your dashboard — if the key isn't listed, it was deleted; mint a new one and roll your client.
Header is malformed.
The header must be exactly Authorization: Bearer <token> with one space
between Bearer and the token.
429 Too Many Requests
Litmus rate-limits the MCP surface per-org. The default is 60 requests per
minute, refilled continuously, so short bursts up to the full limit are
fine. All API keys on an org share one bucket — and artifact streaming
URLs (zipUrl, videoUrl from get_submission)
count against the same bucket, so switching endpoints won't get you more
headroom.
When you exceed the limit, the response is:
- HTTP status
429 - JSON body
{"error": "rate_limited", "error_description": "MCP rate limit exceeded"} Retry-Afterheader — integer seconds until one more request will succeed.
The fix is to read Retry-After, sleep, and retry. For artifact downloads
(plain HTTP), that looks like:
import asyncio, httpx
async def get_with_backoff(client, url, headers, *, max_retries=5):
for attempt in range(max_retries):
r = await client.get(url, headers=headers)
if r.status_code != 429:
r.raise_for_status()
return r
if attempt == max_retries - 1:
r.raise_for_status()
await asyncio.sleep(int(r.headers.get("Retry-After", "1")))The same principle applies to MCP tool calls — your client will surface the
429 from the transport layer; catch it, sleep Retry-After seconds, retry.
If you're consistently hitting the limit on a real workload (nightly exports, large pipeline backfills), email founders@litmushiring.com and we can raise your org's ceiling. Changes take effect within about a minute.
"Not found" on a tool that should exist
Litmus returns "not found" for any resource that doesn't belong to your org — we never leak cross-tenant existence. So the same error covers two cases:
- It really doesn't exist — wrong ID, typo, deleted record. Confirm the
ID by listing first (
list_submissions,list_candidates). - It belongs to a different org — you're hitting Litmus with the wrong API key. Each key is bound to exactly one org; if your team has multiple orgs, make sure you're using the right one.
Version conflict on update_assessment_files
When you supply expected_version and someone else updated the assessment
since your last get_assessment_files, the write is refused with an error
that includes the current version. The fix:
- Re-fetch with
get_assessment_filesto pick up the new state. - Merge your local edits against the new file set.
- Retry
update_assessment_fileswith the new version.
This is intentional — silently overwriting a concurrent edit would lose
work. If you genuinely want last-write-wins (e.g. a one-shot scripted
deploy with no other writers), omit expected_version entirely.
Artifact URLs return 404
zipUrl and videoUrl from get_submission 404 when:
- The candidate didn't record a walkthrough video, or
- The submission predates 2026 and is still stored on the legacy filesystem path without a zip blob, or
- The bearer token doesn't match the submission's org (same "no cross-tenant existence" rule as above).
get_submission returns null for either URL when it knows the artifact
isn't available, so a null here is expected — only an explicit 404 against
a non-null URL points at a real problem.
The MCP client hangs on initialize()
Streamable HTTP clients hold the connection open for the whole session. If the call hangs forever, either:
- A network proxy or firewall is buffering the response — try from an uncorporate-firewalled network to confirm.
- The URL is wrong (no trailing slash again, or pointing at
localhostinstead oflitmushiring.com). The connection still opens but no initialize handshake comes back.
Still stuck
Email founders@litmushiring.com with:
- The tool name and arguments you called.
- The full error message and any stack trace.
- The first few characters of your API key (e.g.
litmus_sk_abc123…) so we can find the request in our logs without you sharing the secret.
