Repository navigation
Spurious "Failed to hydrate secrets" warning for MCP servers with no secrets #1484
Description
Summary
Every run that uses an MCP server with no secrets configured (e.g. a plain stdio server like uvx mcp-server-time with no env) logs a spurious warning:
Failed to hydrate secrets for MCP server 'Time' (time)
The run still succeeds — the warning is cosmetic — but it's misleading and adds noise to every run's logs for the common case of a secret-less MCP server.
Root cause
-
POST /api/v1/mcp/servers(apps/api/src/routes/mcp-servers.ts) only creates a corresponding record in Token Manager's ownmcpServerCollectionwhen the server is created with secrets:if (mcpSecretClient && hasSecrets) { await mcpSecretClient.deleteAllSecrets(projectId, slug); if (env && Object.keys(env).length > 0) { await mcpSecretClient.storeEnv(projectId, slug, env); } else if (headers && headers.length > 0) { await mcpSecretClient.storeHeaders(projectId, slug, headers); } }
A server created without
env/headersnever gets a Token Manager record at all. -
At dispatch time,
queue-processor.tsalways callsMcpSecretClient.resolveSecrets()for every MCP server attached to a run:const resolved = await secretClient.resolveSecrets(requestDoc.projectId, config.slug);
-
Token Manager's
GET /api/v1/mcp/servers/:id/secrets/resolve(apps/token-manager/src/mcp-secret-routes.ts) looks up the server by(projectId, slug)and returns 404{"error":"MCP server not found"}when there's no record — which is always the case for a secret-less server. -
queue-processor.tscatches that 404 as an unexpected failure and logs it as awarn:} catch (err) { await log("warn", `Failed to hydrate secrets for MCP server '${config.name}' (${config.slug})`, { error: err instanceof Error ? err.message : String(err), }); return config; }
The run proceeds correctly (falls back to the unhydrated config, which is right since there's nothing to hydrate), but the warning fires on every single run using a secret-less MCP server.
Suggested fix
Distinguish "server legitimately has no secrets" from "secret resolution actually failed" (e.g. Token Manager unreachable, non-404 error). Options:
- Have Token Manager's
secrets/resolveendpoint return200 {"env": {}}(or{"headers": []}, transport-aware) for a server it doesn't have a record for, instead of 404 — treating "unknown to Token Manager" as equivalent to "no secrets" rather than an error. - Or, in
queue-processor.ts, special-case a 404 response fromresolveSecretsas an expected "no secrets" outcome (skip the warning) and reserve thewarnlog for other failure modes (network errors, 5xx, etc.).
Repro
- Create an MCP server via
POST /api/v1/mcp/serverswith noenv/headers(e.g. a stdio server likemcp-server-time). - Submit a run that references it via
mcpServers: ["<slug>"]. - Observe the run log:
warn: Failed to hydrate secrets for MCP server '<name>' (<slug>)even though the run completes successfully.
Activity