blog-zero-secrets-mcp

blog-zero-secrets-mcp

Demonstrates deploying an MCP server on AgentCore in standalone or gateway mode, using Cognito public client with PKCE for zero-secret user authentication.

Category
Visit Server

README

AgentCore + Cognito Public Client MCP PoC

End-to-end proof of concept demonstrating two deployment modes for an MCP server on AgentCore:

  1. Standalone — Runtime with Cognito JWT auth directly (no gateway)
  2. Gateway — Runtime behind an AgentCore Gateway with Cognito PKCE inbound auth and IAM outbound auth

Both modes use a public Cognito client (no client_secret) with PKCE for user authentication.

Architecture

Mode A: Standalone (Runtime with direct JWT auth)

Claude Code / Kiro
    │ PKCE → Cognito Hosted UI → browser
    │ Bearer JWT
    ▼
AgentCore Runtime (CUSTOM_JWT validates token)
    │
    ▼
MCP Server (FastMCP, Python)

Mode B: Gateway (recommended)

Claude Code / Kiro
    │ PKCE → Cognito Hosted UI → browser
    │ Bearer JWT
    ▼
AgentCore Gateway (CUSTOM_JWT validates token)
    │ SigV4 (gateway IAM role)
    ▼
AgentCore Runtime (AWS_IAM auth)
    │
    ▼
MCP Server (FastMCP, Python)

The Gateway mode provides:

  • Centralized authentication (gateway handles all JWT validation)
  • Tool discovery and semantic search across multiple targets
  • Protocol-level MCP routing
  • Separation of concerns (runtime doesn't need to know about user auth)

Project Structure

.
├── server/
│   ├── cognitopocmcp/              # Runtime deployed via agentcore CLI
│   │   ├── app/cognito_poc_mcp/
│   │   │   └── main.py            # FastMCP server with sample tools
│   │   └── agentcore/             # agentcore CLI config
│   ├── mcp_server.py              # MCP server source (standalone mode)
│   └── requirements.txt
├── src/
│   ├── config.mjs                 # Shared config (project name, region, helpers)
│   ├── auth.mjs                   # PKCE auth module (no secrets!)
│   ├── mcp-server.mjs             # Stdio MCP server (proxy mode)
│   └── test-auth.mjs              # Standalone auth flow test
├── scripts/
│   ├── setup-cognito.mjs          # Creates Cognito pool + public client + user
│   ├── deploy.sh                  # Deploys runtime (standalone mode, with JWT auth)
│   ├── deploy-infrastructure.mjs  # Creates gateway + IAM role + target (gateway mode)
│   ├── test-gateway.mjs           # Tests gateway end-to-end
│   ├── test-deployed.mjs          # Tests standalone runtime end-to-end
│   └── teardown-cognito.mjs       # Deletes all infrastructure
├── .env                           # Generated by setup (Cognito config)
├── .mcp.json                      # Generated by deploy-infra (gateway URL + OAuth)
├── claude-mcp-config.json         # Same as .mcp.json (for copying to Claude/Kiro)
└── package.json

Prerequisites

# AWS CLI + credentials configured
aws sts get-caller-identity

# Node.js 20+
node --version

# AgentCore CLI
npm install -g @aws/agentcore

# Python 3.10+ (for the MCP server)
python3 --version

Quick Start: Gateway Mode (recommended)

Step 1: Install dependencies

npm install

Step 2: Create Cognito infrastructure

npm run setup

Creates a Cognito User Pool with a public app client (no secret), a hosted UI domain, and a test user (testuser / TestPass123!). Config is saved to .env.

Step 3: Deploy the runtime

npm run deploy-runtime

Deploys the MCP server to AgentCore Runtime using the agentcore CLI. The runtime uses default IAM auth (the gateway will authenticate users).

Step 4: Deploy the gateway

npm run deploy-infra

Creates:

  • An IAM role for the gateway (with permission to invoke the runtime)
  • An AgentCore Gateway with CUSTOM_JWT inbound auth (Cognito PKCE)
  • A gateway target pointing at the runtime via GATEWAY_IAM_ROLE (SigV4)

Updates .mcp.json and claude-mcp-config.json with the gateway URL.

Step 5: Test

npm run test-gateway

Authenticates via Cognito (non-interactive using test user), then:

  • Verifies unauthenticated requests are rejected (401)
  • Initializes MCP session
  • Lists discovered tools
  • Calls tools (greet_user, add_numbers, get_server_info)

Step 6: Connect Claude Code / Kiro

Copy the generated config:

# For Kiro — .mcp.json is already in the project root
# For Claude Code
cp claude-mcp-config.json ~/.claude/mcp.json

The config looks like:

{
  "mcpServers": {
    "cognito-poc": {
      "type": "http",
      "url": "https://<gateway-id>.gateway.bedrock-agentcore.<region>.amazonaws.com/mcp",
      "oauth": {
        "clientId": "<public-client-id>",
        "callbackPort": 8976
      }
    }
  }
}

On first tool invocation, Claude/Kiro opens your browser for Cognito login. After that, tokens are cached and refreshed automatically.

Quick Start: Standalone Mode

If you don't need a gateway and want the runtime to handle JWT auth directly:

npm run setup         # Create Cognito pool
npm run deploy        # Deploy runtime with CUSTOM_JWT auth
npm run test-deployed # Test via PKCE (opens browser)

npm Scripts

Script Description
npm run setup Create Cognito User Pool + public client + test user
npm run deploy-runtime Deploy MCP runtime via agentcore CLI (IAM auth, for gateway)
npm run deploy-infra Create gateway + IAM role + target via Control Plane API
npm run deploy Deploy runtime with direct JWT auth (standalone, no gateway)
npm run test-gateway Test gateway end-to-end (non-interactive)
npm run test-gateway -- --pkce Test gateway with browser-based PKCE login
npm run test-deployed Test standalone runtime via PKCE
npm run test-auth Test PKCE auth flow only (opens browser)
npm run test-local Run MCP server locally for development
npm run teardown Delete all infrastructure (gateway, IAM role, Cognito pools)

MCP Tools Available

The sample MCP server exposes:

Tool Description
add_numbers Add two numbers together
multiply_numbers Multiply two numbers together
greet_user Greet a user by name
get_server_info Return deployment and version info
analyze_text Analyze text and return basic statistics

When accessed through the gateway, tool names are prefixed with the target name: mcp-runtime___add_numbers.

Cleanup

npm run teardown

This deletes:

  • AgentCore Gateway (targets + gateway)
  • Gateway IAM role
  • Cognito User Pool(s)
  • Local files (.env, .mcp.json, claude-mcp-config.json)

The AgentCore Runtime is NOT deleted (managed separately by agentcore CLI). To remove it:

cd server/cognitopocmcp && agentcore destroy

Key Concepts

Zero-secrets authentication

  • Cognito public client: GenerateSecret: false — no client secret exists
  • PKCE (code_challenge + code_verifier) proves the requester without a shared secret
  • Only the client_id is stored locally (a public identifier, not a credential)
  • Tokens are in-memory with 1-hour expiry + auto-refresh

Gateway outbound auth

The gateway authenticates to the runtime using its own IAM role (SigV4). This avoids the complexity of OAuth machine-to-machine flows between the gateway and runtime. The IAM role has bedrock-agentcore:* permission scoped to the runtime ARN.

Portability

All environment-specific values are derived at runtime:

  • AWS Account ID: resolved via STS.GetCallerIdentity
  • Gateway URL: read from .mcp.json (generated by deploy-infra)
  • Runtime ARN: read from agentcore deployed state
  • Project constants: centralized in src/config.mjs

To deploy in a different account/region, just configure AWS credentials and re-run the setup steps.

Security

See CONTRIBUTING for information on reporting security issues.

License

This library is licensed under the MIT-0 License. See the LICENSE file.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured