Est.
FeaturesLong read

Secrets Management for MCP Server Credentials in CI/CD

How static credentials expose CI/CD pipelines to automated agent attacks.

Senior Writer · · 13 min read
Cover illustration for “Secrets Management for MCP Server Credentials in CI/CD”
Features · September 12, 2026 · 13 min read · 2,813 words

MCP servers now sit between AI agents and the real infrastructure those agents touch: databases, cloud APIs, SaaS platforms, CI/CD pipelines. Most of them still log into that infrastructure the way software did in 2015, with a static key typed into a config file and left there for months. That gap between what MCP servers get asked to do and how they're actually secured gets worse the closer you look at CI/CD, where the static-secret default is a serious structural flaw. It's the wrong architecture, full stop, and teams still treating it as a config detail are going to get burned.

A single MCP server routinely needs to log into several backend systems at once. That's not a bug in how someone set it up. It's the design: one server, many credentials, all live at the same time. Add to that the fact that MCP sessions are stateful and long-lived, unlike a normal API call that fires once and forgets. Context sticks around. That means credentials can end up sitting in conversation memory, in prompt history, in logs, even in vector stores, long after the task that needed them is done. Call it contextual secret leakage: the protocol layer itself starts acting like an accidental secrets repository nobody signed up to run.

There's also no human in the loop for most of this. The model decides, at runtime, which tool to call and what data to hand it. Nobody's reviewing each credential use before it happens. And the spec itself doesn't tell implementers how to handle secrets. GitHub discussion #1066 on the official MCP repository, filed in July 2025, asked for standardized secrets handling at the SDK level. It's still open, still has just three community comments, still no accepted answer. Every server author is inventing their own approach. Aggregated credentials plus autonomous invocation, with no shared standard tying either down, is what makes CI/CD the sharpest edge of this problem.

How bad the static-secrets default actually is across the ecosystem

Diagram: The Static-Secrets Problem by the Numbers. Visualizes: Visualize the scale of the MCP static-secrets crisis using five concrete figures from Astrix Security's 2025 study and related research: 88% of MCP servers require credentials to run…

Astrix Security's "State of MCP Server Security 2025" study is the largest look at this so far, covering more than 5,200 open-source MCP server builds. The numbers aren't close. 88% of servers need credentials just to run. Of those, 53% depend on long-lived static secrets, API keys or personal access tokens that mostly never get rotated. Only 8.5% use OAuth, the standard built for exactly this kind of delegated access. And 79% of API keys get passed through plain environment variables, with no encryption layer between the key and whatever process reads it.

Doppler's February 2026 data adds another angle: 48% of the MCP servers it reviewed actually recommend storing sensitive credentials in plaintext.env or JSON files. That's not an oversight some developer stumbled into. It's advice, baked into setup docs, telling people to put every secret in one file that becomes a single target worth a lot to whoever finds it. Bad practice written down and repeated beats bad practice nobody thought about, in the worst way.

Scale that against roughly 20,000 MCP server implementations sitting on GitHub, and every percentage above turns into a large raw number of exposed systems. Snyk's 2026 figures show why this sticks around: 64% of secrets leaked in 2022 were still active in 2026. Four years later, still live. Static credentials don't just get exposed once. They stay exploitable for years because nobody circles back to kill them.

Clutch Security's December 2025 research found that 38% of MCP servers in the wild come from unofficial sources, and 3% of those contained credentials hardcoded specifically to bait developers into connecting real production keys. This is an ecosystem that scaled adoption faster than it built any discipline around security, and closing that gap is the point of everything that follows.

The specific ways static secrets fail in a CI/CD context

Five patterns show up again and again, and each fails in its own way. None of them are theoretical.

Secrets baked directly into Docker images sit there permanently. If that image leaks, gets pushed to a public registry by accident, or gets reused somewhere outside its intended environment, the credentials inside are exposed right away, with no way to rotate them without rebuilding and redeploying everything.

Credentials stuffed into.env or JSON files committed alongside pipeline config create a single point of total failure. One exfiltration event, one exposed file, and an attacker has every key in that file, not just one.

Secrets typed straight into pipeline YAML or Dockerfiles get forgotten fast. Someone hardcodes a token to get a build working, means to remove it later, and six months on it's sitting in a public repo or a published package.

Token lifetimes that outlast the job or session they were issued for, with no rotation enforced, show up often enough that OWASP names them directly under MCP01:2025, Token Mismanagement and Secret Exposure.

Shared service accounts used across multiple pipeline stages and multiple MCP servers turn one compromise into a system-wide incident. Compromise one stage, and the same credential opens every other stage that reused it.

Research published by Practical DevSecOps found 492 MCP servers exposed directly to the public internet with no authentication and no encryption at all, granting access to 1,402 distinct MCP tools. More than 90% of those allowed direct read access. Nearly three-quarters were hosted on major cloud providers, meaning this wasn't obscure homegrown infrastructure. It was running on the same platforms enterprise workloads run on.

The blast radius matters more here than in a typical leak, because MCP agents often act without a human watching in real time. A leaked pipeline token doesn't just grant read access somewhere. It can trigger unauthorized code changes, tamper with a repository, or move laterally into cloud storage and issue trackers, all before anyone notices. And because MCP sessions carry context forward, pipeline logs and telemetry that record full prompts or tool responses without redaction become a second leak surface, entirely separate from the credential itself. OWASP's MCP01:2025 entry names this specific failure pattern directly.

How the threat landscape makes static CI/CD credentials an active target, not just a latent risk

Attackers have stopped waiting to find weak MCP servers and started building malicious ones on purpose. Some ship with trojanized logic that exfiltrates credentials the moment a client connects. Others look clean at first, get adopted, then push an update that adds session-hijacking code after trust has already been established. The first malicious MCP package showed up in September 2025, and the Postmark MCP backdoor was the first one caught in the wild doing exactly this.

CVE-2025-6514 shows what the technical failure looks like up close. It's a critical OS command injection bug, CVSS score of 9.6, found in mcp-remote versions 0.0.5 through 0.1.15, an OAuth proxy package with over 437,000 downloads. A malicious MCP server could send a crafted authorization_endpoint URL that got passed straight to a shell and run as code. Fixed in version 0.1.16, but the download count tells you how many installs were exposed before the patch landed.

More than 30 CVEs got filed against MCP servers in just the first 60 days of 2026. That pace makes "secure it later" a bet nobody should be taking. Prompt injection adds a subtler angle specific to CI/CD: public content like project documentation files, pull request comments, or issue bodies can carry embedded instructions that trick an agent into pasting live credentials directly into an MCP tool call. This risk has been recognized at the platform level, with major providers taking steps to address credential exposure through their MCP integrations. CVE-2026-32211, filed against the Azure MCP Server, is a reminder that this isn't a small-vendor problem. The auth layer there was missing entirely, in a product from one of the largest cloud providers in the world.

The threat side of this has moved faster than the default tooling. Everything from here on is about catching up.

What the MCP specification and OWASP now require, and where that leaves teams today

OWASP's MCP Top 10, still in beta as of 2025, ranks Token Mismanagement and Secret Exposure as MCP01, the top risk on the list. The failure patterns it names read like a checklist of what's already covered above: hardcoded tokens sitting in client, server, or tool configs, secrets retained in conversational memory, logs and telemetry recording full prompts without redaction, tokens that outlive the session they were issued for, and shared or static service accounts.

The spec itself has moved, just slower than the threats. Version 2025-03-26 introduced OAuth 2.1 as the authorization framework for remote MCP servers. Version 2025-06-18 went further, classifying MCP servers formally as OAuth 2.1 resource servers, and requiring that clients bind tokens to the specific server they're meant for using resource indicators, aimed directly at stopping token theft and token replay. Version 2025-11-25 refined that OAuth 2.1 model further still.

The Cloud Security Alliance's read on all this is blunt: MCP adoption has outrun security governance, and the protocol's architecture opens attack surfaces that ordinary application security controls weren't built to catch.

Here's the gap left even after all that spec work. OAuth 2.1 and resource indicators solve authentication between a client and a server. They don't solve how a CI/CD pipeline actually gets the secret it needs to start an MCP server in the first place. That's a separate, operational problem, and the rest of this piece is built around solving it. Discussion #1066 is still open. Standardized secrets handling at the SDK level is still a request, not a shipped feature. Teams have to build the bridge themselves, and most of them are building it badly right now.

Runtime injection as the foundational shift: keeping secrets out of the build artifact entirely

The fix starts with a simple rule. Secrets get pulled from a secrets manager while the pipeline runs, get injected straight into process memory when the job starts, and never touch disk, never get baked into an image, never persist in the build artifact. The secret exists only inside the container's running process, for as long as that process runs. When the job ends, it's gone. Nothing downstream carries it forward.

Compare that to the.env pattern, which loses on every count. A.env file aggregates every credential into one file, creating exactly the kind of plaintext, single-target storage that the evidence above flags as a widespread problem across MCP deployments. Runtime injection has none of those three problems.

The actual flow is straightforward. At pipeline start, the secrets manager gets called. The secret lands in process.env if it's a Node.js service, or os.environ if it's Python. The MCP server starts up with credentials already sitting in memory. The job finishes. No artifact anywhere holds onto the value.

This buys two things right away. Rotation gets simple, since no config file holds the actual value: changing a credential means no config edits and no manual restarts, the next pipeline run just fetches whatever's current. And every fetch becomes a logged event tied to a specific pipeline run's identity, instead of a static file read that leaves no trail at all. Runtime injection is the floor everything else in this piece stands on. Dynamic secrets and federated identity both assume this fetch mechanism already exists, so skipping it means the more advanced patterns below have nothing to build on.

Vault-backed dynamic credentials: giving each MCP server instance its own short-lived identity

Dynamic secrets change the question being asked. Instead of "fetch the credential that already exists," the pipeline asks for one made specifically for this run, scoped to this server, set to expire the moment this job finishes.

HashiCorp's Vault MCP Server, currently in beta, allows pipelines to interact with Vault's secrets management capabilities directly through MCP. In a CI/CD setting, that means an AI agent can pull temporary database credentials to run integration tests inside a sandboxed environment, and those credentials expire the second the test run ends.

Astrix released an open-source MCP Secret Wrapper on October 15, 2025, built to solve a narrower but very common problem: wrapping any existing MCP server, without touching the server's own code, so it pulls secrets from a vault (currently AWS Secrets Manager) at startup and injects them as environment variables before the server process even begins. Static credentials sitting in config files get killed right at the moment the server initializes.

For teams already living inside one cloud, there's a simpler path: IAM-role-based authentication straight to the cloud provider's native secrets manager. The MCP server logs in using its own service account identity, so there's no bootstrap secret to protect in the first place, and no Vault tokens or root credentials to manage on top of it. For single-cloud shops, this is worth evaluating before reaching for Vault at all, not after.

The scoping benefit compounds across a whole fleet of servers. A GitHub MCP server gets credentials scoped only to GitHub, nothing else. It has no path to a database credential it was never issued. Blast radius stops being a hope and becomes a design constraint.

One practical warning worth building into any rollout: if Vault is unreachable the moment an MCP server tries to start, that server crashes right away. Retry logic with exponential backoff on the initial connection handles startup timing issues, and a caching layer keeps things running through brief Vault outages once the server's already up.

Federated identity and OIDC: eliminating credential distribution entirely

Dynamic secrets are still secrets. They're made on demand, but they're still something to manage, still something that could leak in the window before it expires. Federated identity removes that window by removing the secret entirely. The pipeline proves who it is through a signed assertion from a provider both sides already trust, and the resource server checks that assertion without ever holding a shared credential.

In practice: a CI/CD platform like GitLab issues an OIDC token for the running job, carrying verifiable claims about which repository, which branch, which environment, which caller. Vault or a cloud IAM system gets configured ahead of time to trust that issuer. The pipeline hands over its OIDC token and gets back a short-lived Vault token or cloud credential in exchange. At no point does a static secret sit anywhere in the pipeline's configuration. GitLab CI/CD paired with HashiCorp Vault over OIDC/JWT is a documented working version of exactly this setup.

This lines up directly with what the MCP spec started requiring in June 2025: clients binding tokens to the specific server they're meant for, using resource indicators. OIDC-based identity meets that requirement cleanly, without adding yet another credential type to manage on top of everything else.

Call it the zero-static-secrets model, and treat it as the target state, not one option among several. No keys get handed out. No keys need rotating. No keys sit around waiting to leak, because there's no key, only a trust relationship the resource server checks each time. That relationship, not any stored value, becomes the actual security boundary.

None of this is free to set up. The CI/CD platform has to support OIDC token issuance for jobs, and the secrets backend on the other end has to be configured to accept and verify those specific claims. That's a one-time infrastructure cost, not something repeated for every new MCP server added later, which is exactly why it's worth paying up front instead of layering more Vault tokens on indefinitely.

Credential rotation patterns that keep production MCP servers running without downtime

Rotation fails constantly in static-credential setups for a boring reason: doing it by hand means editing a value, updating a config file, and restarting every server that depends on it. Faced with that workload, engineers put it off, again and again, usually until a breach forces the issue. Manual rotation is a process that slips as a matter of course. It's a process built to slip.

The baseline worth holding to is rotating every 30 to 90 days on a fixed schedule, never by hand, and never only after something's already gone wrong.

A webhook-driven rotation flow handles this without anyone touching a keyboard. The secrets manager rotates the token on schedule. That rotation fires a webhook to a server manager. The server manager checks the signature on that webhook, then kicks off a rolling restart across affected servers. As each one restarts, the MCP process fetches whatever secret is currently active.

For workloads that can't tolerate downtime at all, dual-credential rotation adds a transition window where both the old and new credential stay valid at the same time. Replicas migrate over to the new credential one by one. Only once every replica has switched does the old credential get retired. That overlap is what stops a wave of 401 errors from hitting whatever replicas haven't picked up the new value yet, the exact failure mode that shows up when rotation happens all at once with no buffer.

Sources

  1. owasp.org
  2. MCP security best practices for credentials, tokens, and secrets | Doppler
  3. State of MCP Server Security 2025: Research Report | Astrix
  4. Understanding MCP servers and the risks of poor secrets management
  5. Standardized Secrets Management for MCP Servers · modelcontextprotocol modelcontextprotocol · Discussion #1066
  6. practical-devsecops.com
  7. developer.hashicorp.com

More in Features