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.
README
AgentCore + Cognito Public Client MCP PoC
End-to-end proof of concept demonstrating two deployment modes for an MCP server on AgentCore:
- Standalone — Runtime with Cognito JWT auth directly (no gateway)
- 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_JWTinbound 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_idis 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 bydeploy-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
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.