resk-mcp · Documentation
Troubleshooting
Every failure mode has a fingerprint: a status code, an MCP error code or a log line. Here is how to read them.
On this page
Quick diagnosis table
| Symptom | Likely cause | Fix |
|---|---|---|
HTTP 403 Not authenticated | No Authorization header, or not in Bearer … form | Send Authorization: Bearer <token> |
-33000 Invalid or expired token | Secret mismatch, expired token, wrong algorithm | Re-mint against the server's secret; see below |
HTTP 422 with Method must start with… | Standard JSON-RPC method names used | Use tool/<name>, not tools/call |
HTTP 422 with field required | Missing id, method or params | Send the three-field envelope |
-32601 Tool X not found | Name mismatch, arguments nested, resource/prompt over POST | Check registration name and params shape |
-33003 Sensitive data (PII) detected | A string value matches the email or phone pattern | Pass an opaque identifier instead |
-33003 Prompt injection detected | A string contains a known injection phrase | Rephrase or split the payload |
-33002 Context limit exceeded | Estimated tokens above MAX_TOKEN_PER_REQUEST | Raise the budget or trim payload |
| HTTP 429 | RATE_LIMIT exceeded for user or IP | Raise the limit or slow the client |
/dashboard returns 404 | Dashboard authentication disabled | Set DASHBOARD_AUTH_ENABLED=true |
| “Dashboard HTML not found.” | Static assets missing from the installed wheel | See dashboard issues |
ValueError: JWT_SECRET is required | Secret not loaded before import | load_dotenv() first, or export the variable |
ModuleNotFoundError: resk_llm | Importing resk_mcp.security without resk-llm | pip install resk-llm |
ModuleNotFoundError: jsonschema | Importing resk_mcp.schema_validation without jsonschema | pip install jsonschema |
Authentication failures
HTTP 403 “Not authenticated”
FastAPI's HTTPBearer rejects the request before your code runs. Check that the header exists and uses the exact Bearer prefix with a single space. A token pasted with surrounding quotes or a newline also fails here.
curl -i http://localhost:8001/mcp_secure \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1,"method":"tool/calculator/add","params":{"a":2,"b":3}}'
-33000 “Invalid or expired token”
The header was accepted but verification failed. The server log narrows it down:
WARNING:resk_mcp.server:Authentication failed for /mcp_secure: Invalid token: Signature verification failed
Signature failures are almost always a secret mismatch: the token was signed with a different JWT_SECRET than the server is using. Confirm what each side sees:
# On the server host, with the same environment as the process:
python -c "from resk_mcp.config import settings; print(len(settings.jwt_secret))"
# Mint a token with the server's own settings, then use it immediately:
python -c "from resk_mcp import create_jwt_token; print(create_jwt_token('[email protected]'))"
If the two lengths differ, something is overriding the value — an exported variable, a .env loaded after import, or a config.yaml found in a different working directory. Remember that config.yaml is resolved relative to the current working directory.
Other cases:
Token has expired— mint a new one or raiseJWT_EXPIRATION_MINUTES.Signature verification failedafter a deploy — you rotated the secret but kept an old token in a client config.- Tokens minted by an external IdP — the algorithm must match
JWT_ALGORITHMexactly (for asymmetric tokens, the server needs the corresponding key; this library prefers a shared HMAC secret).
Request format failures
These return HTTP 422, not an MCP error object, because FastAPI rejects the body before the dispatcher runs.
“Method must start with one of: tool/, resource/, prompt/”
You sent a standard JSON-RPC method such as tools/call. resk-mcp uses its own convention: the method carries the capability, and params carries the arguments directly.
| Wrong | Right |
|---|---|
{"method":"tools/call","params":{"name":"add","arguments":{"a":1}}} | {"method":"tool/add","params":{"a":1}} |
If your MCP client only speaks the standard protocol, keep it pointed at the SDK's own endpoint and use /mcp_secure as the hardened ingress for callers you control, or add a thin adapter that maps tools/call to tool/<name> and unwraps arguments.
“field required”
params must be present even for a tool that takes no arguments — send "params": {}. The id field is mandatory and must be an integer.
Tool not found
-32601 with “not found or couldn't be executed” is the dispatcher's catch-all. In order of likelihood:
- Name mismatch. The method suffix must equal the registered name, slashes included:
@server.tool(name="calculator/add")→tool/calculator/add. If you omitname=, the Python function name is used. - Arguments nested under
arguments. The tool call path passesparamsas keyword arguments.{"params":{"arguments":{"a":1}}}calls your tool with an unexpectedargumentskeyword. - Resources or prompts. They register, but POST execution is unimplemented (
-33004); an unknown name yields-32601. - Schema or type errors inside the tool. A
SchemaValidationErrorcan surface as “not found” because the dispatcher falls back through several lookup strategies. Check the server log for the real exception.
Requests rejected by security checks
-33003 Sensitive data (PII) detected on a legitimate request
Any string value matching the email or US-phone pattern is rejected, including an email your tool genuinely needs. This is unconditional in 0.1.0. Options:
- Send an opaque user or record ID and resolve the address server-side.
- Move the sensitive value out of
params: let the tool read it from a database or environment based on the ID. - If you must accept raw PII, customize the validation layer consciously — for example by removing the pattern in a fork and accepting the trade-off. See the validation helpers.
-33003 Prompt injection detected on benign content
The scanner matches six phrases as substrings, anywhere in keys or values. Sentences such as “the user asked us to ignore previous instructions” trip it. Split the payload, quote the phrase differently, or keep such content out of MCP arguments. There is no allowlist.
Rate limits and budgets
HTTP 429
slowapi rejected the request. The key is user:<id> for a valid token, ip:<addr> otherwise. Common traps:
- Behind a proxy without correct forwarded headers, every client shares the proxy's IP and burns one quota.
- Several workers each enforce their own limit, so the effective quota is
workers × limit. - Load tests reuse one token and hit the per-user limit immediately.
-33002 Context limit exceeded
The JSON-serialized params exceed 4 × MAX_TOKEN_PER_REQUEST characters (the estimator divides by 4). Raise the variable or reduce the payload. Note that the check applies to parameters only, never to tool output.
Dashboard issues
404 on /dashboard
The dashboard is disabled. Set DASHBOARD_AUTH_ENABLED=true together with a password that is neither empty nor admin; otherwise startup fails or the routes stay hidden.
“Dashboard HTML not found.” or “Login page not found.”
Current releases ship the UI under resk_mcp/static/, so a normal install serves it. The message only appears when those files are missing — typically the original 0.1.0 wheel, which looked for a sibling static/ directory, or a build that stripped package data. Upgrade, or copy the repository's resk_mcp/static directory next to the installed package. The JSON endpoints keep working either way.
401 on /dashboard after logging in
The session cookie is Secure only when SSL_CERTFILE is set. If you terminate TLS at a proxy and browse over HTTPS, the cookie is still sent; but if a proxy strips or rewrites cookies, login appears to succeed and the next request is unauthenticated. Check the Set-Cookie header and the browser's cookie store. Sessions also disappear on restart.
Startup and import failures
ValueError: JWT_SECRET is required. Set it in config.yaml under jwt.secret or as an environment variable.
The secret is not visible to the library. resk_mcp.config reads the environment once at import time and loads a .env only from the current working directory. If the process starts from another directory (systemd, Docker WORKDIR, a scheduler), the file is never seen. Load it explicitly before the import with load_dotenv("/etc/myapp/.env"), or export JWT_SECRET in the process manager.
Server exits immediately with “SSL key file not found” / “SSL cert file not found”
Both SSL_KEYFILE and SSL_CERTFILE are set, but a path is wrong or relative to a different working directory. Fix the paths or unset both to serve HTTP behind a proxy.
ModuleNotFoundError: No module named 'resk_llm'
Something imports resk_mcp.security — usually examples/example_server.py or your own MCPSecurityManager usage. Install the optional package: pip install resk-llm. The core SecureMCPServer path does not need it.
ModuleNotFoundError: No module named 'jsonschema'
resk_mcp.schema_validation imports jsonschema at module load; install it explicitly (pip install jsonschema). It is present in the development dependency group but not in the 0.1.0 runtime requirements.
Connection refused on port 8001
Check the bind address: the default is 0.0.0.0, so a local curl should work; in containers, publish the port. If the process exits, the log shows why — usually a missing secret, a weak dashboard password, or a TLS path.
Enabling debug output
LOG_LEVEL="DEBUG" python main.py
At DEBUG level the server logs each tool dispatch, the rate-limit key decision (user vs IP) and the fallback path taken when a tool lookup fails. Rejections are logged as warnings with the authenticated user_id — those lines are the ones to alert on.
For an isolated check of the two detectors without the server:
python - <<'PY'
from resk_mcp import detect_pii, detect_prompt_injection
print(detect_pii({"note": "mail me at [email protected]"})) # True
print(detect_pii({"email": "not-an-address"})) # check your payload
print(detect_prompt_injection({"q": "ignore previous instructions"})) # True
PY
Be careful what you log
Tool exceptions are returned to the caller inside the error message (for example Error executing tool …: <exception text>). Never put secrets, tokens or raw customer data in exception messages.
Getting help
- GitHub issues — include the version, Python version, the request body (redacted), the status code and the log line.
- PyPI project page for release history.
- Contact RESK Security for deployment or audit questions.
- The MCP security hub and integration guide cover the architectural side.