aws/agent-toolkit-for-aws / Chapter 3

Programming /

E02_One_Server_Four_Configurations

# One Server, Four Configurations > If the AWS MCP Server is the same in every plugin, why are the four `.mcp.json` files different? That difference is the most under-read evidence in the repository — and it tells you exactly what AWS thinks its agent is for. ## Key Takeaways - There is **one managed AWS MCP Server**, accessed through `mcp-proxy-for-aws@1.6.3` — a Python proxy invoked by `uvx` against `https://aws-mcp.<region>.api.aws/mcp`. The four `.mcp.json` files are not four servers; they are **four postures**. - The proxy is a **client, a credential broker, and a metadata bus** in one binary. The agent never sees the developer's AWS credentials; the proxy injects them at transport time. - The version pin (`@1.6.3`, exact) is itself a security decision — pinned to make the supply chain auditable and the install reproducible. - The four configurations are: **aws-core** (general operator, full IAM), **aws-agents** (documentation only, no auth), **aws-data-analytics** (same as aws-core, domain-specific skills), **aws-agents-for-devsecops** (region-routed, Bearer-token, async jobs). - **Pin your proxy. Always.** A floating version of `mcp-proxy-for-aws` is a floating trust boundary. Open `plugins/aws-core/.mcp.json`. The whole file is this: ```json { "mcpServers": { "aws-mcp": { "command": "uvx", "args": [ "mcp-proxy-for-aws@1.6.3", "https://aws-mcp.us-east-1.api.aws/mcp", "--skip-auth", "--metadata", "INSTALL_SOURCE=agent-toolkit" ] } } } ``` That is the AWS MCP Server, for the `aws-core` plugin, in nine lines of JSON. A `uvx` invocation — `uv` is Astral's Python package manager — of `mcp-proxy-for-aws@1.6.3`, a proxy that wraps a remote HTTPS endpoint at `aws-mcp.us-east-1.api.aws`. Two flags: `--skip-auth` (the proxy will sign requests on the developer's behalf using the local AWS credential chain) and `--metadata` (a key-value pair the proxy attaches to every request, here tagging it with `INSTALL_SOURCE=agent-toolkit` so AWS's servers can see which distribution the developer came from). No secrets in the file. No tokens. No embedded credentials. The local `aws` CLI credential chain does the heavy lifting at call time. Now open `plugins/aws-agents/.mcp.json`. It is different in two ways: the endpoint changes, and the auth model changes. The endpoint is the AWS Knowledge MCP server, a regionless HTTPS service that does not require AWS credentials because it serves public documentation. The plugin uses an HTTP transport, not stdio, and it has no `command` to launch — the agent host connects directly to the URL. Now `plugins/aws-data-analytics/.mcp.json`. It is identical in shape to `aws-core`: a `uvx` invocation of `mcp-proxy-for-aws@1.6.3` against the same `aws-mcp.us-east-1.api.aws` endpoint. Same proxy, same version, same region. The metadata tag is the only thing that varies. Now `plugins/aws-agents-for-devsecops/.mcp.json`. Different again: a Bearer-token auth model, a region-routed endpoint at `https://connect.aidevops.${DEVOPS_AGENT_REGION:-us-east-1}.api.aws/mcp`, and a separate SigV4 fallback. The metadata tag is `DEVOPS_AGENT_REGION`, which reads from the environment. That is four configurations. The honest first reaction is that AWS has built a different MCP server for each plugin. That would be the obvious reading. It is also wrong. The four configurations are four *views* of the same architectural pattern, and the pattern is what matters. ## The shape of the architecture The proxy at `mcp-proxy-for-aws@1.6.3` is doing three jobs simultaneously, and the four configurations show all three: 1. **It is a client.** The proxy speaks MCP to the local agent host (Claude Code, Cursor, Codex) and converts those calls into HTTPS requests to the AWS regional endpoint. 2. **It is a credential broker.** When the agent calls `s3:ListBuckets`, the proxy reads the local AWS credential chain (environment variables, `~/.aws/credentials`, SSO, instance metadata) and signs the request with SigV4. The agent never sees the credentials; the proxy injects them at transport time. 3. **It is a metadata bus.** The `--metadata` flag attaches arbitrary key-value pairs to every request, which AWS's backend uses for routing, attribution, and audit. `INSTALL_SOURCE=agent-toolkit` tells AWS that this request came from the toolkit, not from AWS Labs MCP servers and not from a custom integration. The version pin (`@1.6.3`, exact) is itself an architectural decision. Most Python tools are invoked wi

Chapter 3 of 6 8m Article Audio Video Learning path

One Server, Four Configurations

If the AWS MCP Server is the same in every plugin, why are the four .mcp.json files different? That difference is the most under-read evidence in the repository — and it tells you exactly what AWS thinks its agent is for.

Key Takeaways

  • There is one managed AWS MCP Server, accessed through mcp-proxy-for-aws@1.6.3 — a Python proxy invoked by uvx against https://aws-mcp.<region>.api.aws/mcp. The four .mcp.json files are not four servers; they are four postures.
  • The proxy is a client, a credential broker, and a metadata bus in one binary. The agent never sees the developer's AWS credentials; the proxy injects them at transport time.
  • The version pin (@1.6.3, exact) is itself a security decision — pinned to make the supply chain auditable and the install reproducible.
  • The four configurations are: aws-core (general operator, full IAM), aws-agents (documentation only, no auth), aws-data-analytics (same as aws-core, domain-specific skills), aws-agents-for-devsecops (region-routed, Bearer-token, async jobs).
  • Pin your proxy. Always. A floating version of mcp-proxy-for-aws is a floating trust boundary.

Open plugins/aws-core/.mcp.json. The whole file is this:

{
  "mcpServers": {
    "aws-mcp": {
      "command": "uvx",
      "args": [
        "mcp-proxy-for-aws@1.6.3",
        "https://aws-mcp.us-east-1.api.aws/mcp",
        "--skip-auth",
        "--metadata",
        "INSTALL_SOURCE=agent-toolkit"
      ]
    }
  }
}

That is the AWS MCP Server, for the aws-core plugin, in nine lines of JSON. A uvx invocation — uv is Astral's Python package manager — of mcp-proxy-for-aws@1.6.3, a proxy that wraps a remote HTTPS endpoint at aws-mcp.us-east-1.api.aws. Two flags: --skip-auth (the proxy will sign requests on the developer's behalf using the local AWS credential chain) and --metadata (a key-value pair the proxy attaches to every request, here tagging it with INSTALL_SOURCE=agent-toolkit so AWS's servers can see which distribution the developer came from). No secrets in the file. No tokens. No embedded credentials. The local aws CLI credential chain does the heavy lifting at call time.

Now open plugins/aws-agents/.mcp.json. It is different in two ways: the endpoint changes, and the auth model changes. The endpoint is the AWS Knowledge MCP server, a regionless HTTPS service that does not require AWS credentials because it serves public documentation. The plugin uses an HTTP transport, not stdio, and it has no command to launch — the agent host connects directly to the URL.

Now plugins/aws-data-analytics/.mcp.json. It is identical in shape to aws-core: a uvx invocation of mcp-proxy-for-aws@1.6.3 against the same aws-mcp.us-east-1.api.aws endpoint. Same proxy, same version, same region. The metadata tag is the only thing that varies.

Now plugins/aws-agents-for-devsecops/.mcp.json. Different again: a Bearer-token auth model, a region-routed endpoint at https://connect.aidevops.${DEVOPS_AGENT_REGION:-us-east-1}.api.aws/mcp, and a separate SigV4 fallback. The metadata tag is DEVOPS_AGENT_REGION, which reads from the environment.

That is four configurations. The honest first reaction is that AWS has built a different MCP server for each plugin. That would be the obvious reading. It is also wrong. The four configurations are four *views* of the same architectural pattern, and the pattern is what matters.

The shape of the architecture

The proxy at mcp-proxy-for-aws@1.6.3 is doing three jobs simultaneously, and the four configurations show all three:

1. It is a client. The proxy speaks MCP to the local agent host (Claude Code, Cursor, Codex) and converts those calls into HTTPS requests to the AWS regional endpoint. 2. It is a credential broker. When the agent calls s3:ListBuckets, the proxy reads the local AWS credential chain (environment variables, ~/.aws/credentials, SSO, instance metadata) and signs the request with SigV4. The agent never sees the credentials; the proxy injects them at transport time. 3. It is a metadata bus. The --metadata flag attaches arbitrary key-value pairs to every request, which AWS's backend uses for routing, attribution, and audit. INSTALL_SOURCE=agent-toolkit tells AWS that this request came from the toolkit, not from AWS Labs MCP servers and not from a custom integration.

The version pin (@1.6.3, exact) is itself an architectural decision. Most Python tools are invoked with version ranges or with @latest. Pinning to an exact version means the developer gets reproducible behavior across machines, and the supply chain is auditable — if a malicious version is published to PyPI, the developer's install does not pick it up automatically. The README is explicit about this: "It is recommended to pin to a specific version (e.g., @1.6.3) to ensure reproducible behavior and protect against supply chain risks. We recommend regularly checking PyPI for new stable versions and updating accordingly."

The endpoint at aws-mcp.<region>.api.aws is regional. The default in the aws-core manifest is us-east-1, but the README's Kiro configuration shows the override: --metadata AWS_REGION=us-west-2. The regional endpoint matters for two reasons. First, data residency — a developer in Frankfurt who needs to keep data in eu-central-1 can route the proxy there. Second, latency — the agent's calls hit the AWS backbone, not the public internet, which keeps the round-trip tight.

What the four configurations tell you

The four .mcp.json files are not four servers. They are four *postures* the agent takes when it talks to AWS:

  • aws-core posture: "I am a general-purpose operator. I can call any of the 300+ AWS services through the managed MCP server. I have the developer's full IAM permissions. I leave a CloudTrail record and a CloudWatch metric on every call."
  • aws-agents posture: "I am an agent builder. I need documentation, not API access. I connect to the AWS Knowledge MCP server, which is unauthenticated because it serves public docs. I do not need (and should not have) the developer's IAM credentials for documentation lookups."
  • aws-data-analytics posture: "I am a data engineer. I work with S3 Tables, Glue, Athena, OpenSearch, S3 Vectors. I need the same managed MCP server as aws-core because I am calling the same APIs, but my skills are domain-specific." This is the most important configuration to read carefully: the manifest is *identical* to aws-core because the surface is the same, only the skill library is different.
  • aws-agents-for-devsecops posture: "I am an incident responder. I need a separate, region-routed MCP server that handles long-running scan jobs, threat-model reviews, and release-readiness analyses. The Bearer-token auth model is because these jobs run asynchronously and the developer does not need to keep a CLI session open."
sequenceDiagram
  participant Agent as Coding Agent<br/>(Claude Code, Codex, Cursor, Kiro)
  participant Proxy as mcp-proxy-for-aws@1.6.3<br/>(local uvx process)
  participant IDP as AWS Credential Chain<br/>(env, ~/.aws, SSO, IMDS)
  participant AWS as Regional MCP Endpoint<br/>(aws-mcp.<region>.api.aws)
  participant CT as CloudTrail + CloudWatch

  Agent->>Proxy: MCP call: s3:ListBuckets
  Proxy->>IDP: read credentials
  IDP-->>Proxy: SigV4 signing keys
  Proxy->>AWS: HTTPS POST /mcp<br/>+ SigV4 Authorization<br/>+ metadata: INSTALL_SOURCE=agent-toolkit
  AWS->>CT: log event + emit metric
  AWS-->>Proxy: response payload
  Proxy-->>Agent: MCP result

  Note over Agent,Proxy: For aws-agents: knowledge endpoint<br/>(no auth, public docs)
  Note over Agent,Proxy: For aws-agents-for-devsecops:<br/>Bearer token + region-routed

The diagram tells the story: every call from the agent to AWS goes through the proxy, the proxy signs the call, AWS's backend logs and meters the call, and the agent receives the result. The agent itself never has the AWS credentials. This is the credential isolation primitive that the AWS Labs MCP servers did not provide.

The credential chain is the boundary

The most important sentence in any of the four .mcp.json files is not the endpoint. It is the version pin. AWS chose to pin mcp-proxy-for-aws@1.6.3 rather than float it, and that choice is the boundary. A floating version means the developer gets whatever the registry serves at install time — convenient, but impossible to audit, and a supply-chain risk. A pinned version means the developer can read the exact code that runs on their machine, hash it against the official release, and refuse to upgrade until they have reviewed the diff. For a tool that brokers AWS credentials on behalf of an AI agent, "I can read the code that signs my calls" is not a luxury. It is the minimum bar for a security team to allow the tool in production.

The uvx invocation is the second-most important sentence. uvx runs the tool in an ephemeral virtual environment managed by Astral's uv runtime. The proxy does not pollute the developer's global Python environment; it does not write to a shared site-packages; it cannot be hijacked by another package. When the agent stops, the proxy stops. When the agent restarts, the proxy is freshly installed from the pinned version. The blast radius of a compromised mcp-proxy-for-aws is exactly the duration of one agent session.

The regional endpoint pattern is the third. By making the endpoint a function of the region, AWS lets the developer keep the data in the jurisdiction they need. By making the metadata bus a function of the call, AWS lets itself attribute traffic — and charge for it, if AWS ever decides to. None of the four configurations mentions cost, but the metadata bus is the place cost attribution will land.

Where the architectural choice breaks

The architecture has one sharp edge. The mcp-proxy-for-aws@1.6.3 package is the only place in the entire stack where Python is the implementation language. The skills are Markdown, the manifests are JSON, the hook in aws-core is a 30-line Python script. The proxy is the only component with a real dependency surface, and it is the only component that runs an HTTP client against a remote service. If you are going to compromise the agent's AWS boundary, the proxy is the place to do it. The version pin, the ephemeral venv, and the fact that the proxy is the only uvx invocation in any of the four configurations are all defensive choices for that exact reason.

The four .mcp.json files together are a single architectural answer: the AWS MCP Server is one service, the proxy is one binary, and the four plugin configurations are four points of view on the same trust model. The plugin name does not change the server. The plugin name changes what the *agent* is allowed to do — and that is the next chapter's question, because the boundary is enforced not in the proxy, not in the manifest, but in a 30-line Python script that runs before every Bash call.

Pin your proxy. Always. If you take one operational lesson from this chapter, take this one. A floating version of mcp-proxy-for-aws is a floating trust boundary. You would not deploy a Lambda function without a version pin; do not deploy an agent without one.