Skip to content

Spurious "Failed to hydrate secrets" warning for MCP servers with no secrets #1484

Open

Description

@duffney

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 own mcpServerCollection when 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/headers never gets a Token Manager record at all.

  • At dispatch time, queue-processor.ts always calls McpSecretClient.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.ts catches that 404 as an unexpected failure and logs it as a warn:

    } 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:

  1. Have Token Manager's secrets/resolve endpoint return 200 {"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.
  2. Or, in queue-processor.ts, special-case a 404 response from resolveSecrets as an expected "no secrets" outcome (skip the warning) and reserve the warn log for other failure modes (network errors, 5xx, etc.).

Repro

  1. Create an MCP server via POST /api/v1/mcp/servers with no env/headers (e.g. a stdio server like mcp-server-time).
  2. Submit a run that references it via mcpServers: ["<slug>"].
  3. Observe the run log: warn: Failed to hydrate secrets for MCP server '<name>' (<slug>) even though the run completes successfully.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions