View a markdown version of this page

Menyebarkan server MCP di Runtime AgentCore - Batuan Dasar Amazon AgentCore

Menyebarkan server MCP di Runtime AgentCore

Amazon Bedrock AgentCore Runtime memungkinkan Anda menerapkan dan menjalankan server Model Context Protocol (MCP) di Runtime. AgentCore Panduan ini memandu Anda melalui pembuatan, pengujian, dan penerapan server MCP pertama Anda.

Sebagai contoh, lihat https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server.

Di bagian ini, Anda belajar:

  • Cara membuat server MCP dengan alat

  • Cara menguji server Anda secara lokal

  • Cara menyebarkan server Anda ke AWS

  • Cara memanggil server yang Anda gunakan

Untuk informasi selengkapnya tentang MCP, lihat kontrak protokol MCP.

Bagaimana Amazon Bedrock AgentCore mendukung MCP

Saat Anda mengonfigurasi AgentCore Runtime Amazon Bedrock dengan protokol MCP, layanan mengharapkan kontainer server MCP tersedia di jalur0.0.0.0:8000/mcp, yang merupakan jalur default yang didukung oleh sebagian besar SDK server MCP resmi.

Amazon Bedrock AgentCore mendukung server MCP Streamable-HTTP stateless dan stateful. Secara default, stateless mode (stateless_http=True) direkomendasikan untuk server MCP dasar. Platform secara otomatis menambahkan Mcp-Session-Id header untuk permintaan apa pun tanpa satu, sehingga klien MCP dapat mempertahankan kontinuitas koneksi ke sesi Runtime Amazon Bedrock AgentCore yang sama.

Untuk server MCP yang memerlukan interaksi multi-putaran (elicitation), LLM-generated konten (sampling), atau pemberitahuan kemajuan, stateful mode () memungkinkan kemampuan ini. stateless_http=False Dalam mode stateful, runtime mempertahankan status sesi MCP di seluruh permintaan dalam pemanggilan yang sama. Untuk informasi selengkapnya, lihat Fitur server MCP stateful.

Muatan InvokeAgentRuntimeAPI dilewatkan secara langsung, memungkinkan pesan protokol RPC seperti MCP mudah diproksi.

Prasyarat

  • Python 3.10 atau lebih tinggi diinstal dan pemahaman dasar Python

  • AWS Akun dengan izin yang sesuai dan kredensi lokal yang dikonfigurasi

Langkah 1: Buat server MCP Anda

Menginstal paket yang diperlukan

Pertama, instal paket MCP:

pip install mcp

Buat server MCP pertama Anda

Buat file baru bernamamy_mcp_server.py:

# my_mcp_server.py from mcp.server.fastmcp import FastMCP from starlette.responses import JSONResponse mcp = FastMCP(host="0.0.0.0", stateless_http=True) @mcp.tool() def add_numbers(a: int, b: int) -> int: """Add two numbers together""" return a + b @mcp.tool() def multiply_numbers(a: int, b: int) -> int: """Multiply two numbers together""" return a * b @mcp.tool() def greet_user(name: str) -> str: """Greet a user by name""" return f"Hello, {name}! Nice to meet you." if __name__ == "__main__": mcp.run(transport="streamable-http")

Memahami kode

  • FastMCP: Membuat server MCP yang dapat meng-host alat Anda

  • @mcp .tool (): Dekorator yang mengubah fungsi Python Anda menjadi alat MCP

  • Alat: Tiga alat sederhana yang menunjukkan berbagai jenis operasi

  • stateless_http=True: Mengkonfigurasi server dalam mode stateless, yang merupakan default untuk server MCP dasar

Tip

Untuk server MCP yang memerlukan interaksi multi-putaran (elicitation) atau LLM-generated content (sampling), gunakan stateless_http=False untuk mengaktifkan mode stateful. Server MCP stateful mempertahankan konteks sesi di beberapa permintaan dalam pemanggilan alat yang sama. Untuk informasi selengkapnya, lihat Fitur server MCP stateful.

Langkah 2: Uji server MCP Anda secara lokal

Mulai server MCP Anda

Jalankan server MCP Anda secara lokal:

python my_mcp_server.py

Anda akan melihat output yang menunjukkan server berjalan pada port8000.

Uji dengan klien MCP

Dari terminal baru, buat file baru my_mcp_client.py dan jalankan menggunakan python my_mcp_client.py

# my_mcp_client.py import asyncio from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client async def main(): mcp_url = "http://localhost:8000/mcp" headers = {} async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as ( read_stream, write_stream, _, ): async with ClientSession(read_stream, write_stream) as session: await session.initialize() tool_result = await session.list_tools() print(tool_result) asyncio.run(main())

Anda juga dapat menguji server Anda menggunakan MCP Inspector seperti yang dijelaskan dalam pengujian Lokal dengan inspektur MCP.

Langkah 3: Menyebarkan server MCP Anda ke AWS

Instal alat penyebaran

Instal AgentCore CLI:

npm install -g @aws/agentcore

Anda menggunakan AgentCore CLI untuk menyebarkan agen Anda ke Runtime. AgentCore

Buat folder proyek dengan struktur berikut:

## Project Folder Structure your_project_directory/ ├── mcp_server.py # Your main agent code ├── requirements.txt # Dependencies for your agent └── __init__.py # Makes the directory a Python package

Buat file baru bernamarequirements.txt, tambahkan yang berikut ini:

mcp

requirements.txtmenentukan persyaratan yang dibutuhkan agen untuk penyebaran ke AgentCore Runtime.

Buat proyek Anda untuk penerapan

Sebelum membuat project, Anda perlu menyiapkan kumpulan pengguna Cognito untuk autentikasi seperti yang dijelaskan dalam Mengatur kumpulan pengguna Cognito untuk autentikasi. Ini menyediakan token OAuth yang diperlukan untuk akses aman ke server yang Anda gunakan.

catatan

Mulai 7 Oktober 2025, Amazon Bedrock AgentCore menggunakan Service-Linked Peran untuk izin identitas beban kerja saat menggunakan otentikasi OAuth. Untuk informasi mendetail tentang perubahan ini, lihat Peran terkait layanan identitas.

Setelah menyiapkan otentikasi, perancah proyek baru dengan protokol MCP:

agentcore create --protocol MCP

Ikuti petunjuk interaktif untuk memberikan nama proyek. CLI merancah struktur proyek termasuk file konfigurasi. agentcore/agentcore.json Salin my_mcp_server.py file Anda ke direktori kode agen proyek yang dihasilkan, dan pastikan titik masuk agentcore/agentcore.json menunjuk ke file server Anda.

Menyebarkan ke AWS

Menyebarkan agen Anda:

agentcore deploy

Perintah ini akan:

  1. Package kode agen dan dependensi Anda

  2. Unggah artefak penerapan ke Amazon S3

  3. Buat runtime Amazon Bedrock AgentCore

  4. Menyebarkan agen Anda ke AWS

Setelah penerapan, Anda akan menerima ARN runtime agen yang terlihat seperti:

arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_mcp_server-xyz123

Langkah 4: Panggil server MCP yang Anda gunakan

Uji dengan klien MCP (jarak jauh)

Sebelum pengujian, atur variabel lingkungan berikut:

  • Agen ekspor ARN sebagai variabel lingkungan: export AGENT_ARN="agent_arn"

  • Ekspor token pembawa sebagai variabel lingkungan: export BEARER_TOKEN="bearer_token"

jika Anda memasukkan Accept header, itu harus mengikuti standar MCP. Jenis media yang dapat diterima adalah application/json dantext/event-stream.

Buat file baru my_mcp_client_remote.py dan jalankan menggunakan python my_mcp_client_remote.py

import asyncio import os import sys from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client async def main(): agent_arn = os.getenv('AGENT_ARN') bearer_token = os.getenv('BEARER_TOKEN') if not agent_arn or not bearer_token: print("Error: AGENT_ARN or BEARER_TOKEN environment variable is not set") sys.exit(1) encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F') mcp_url = f"https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT" headers = {"authorization": f"Bearer {bearer_token}","Content-Type":"application/json"} print(f"Invoking: {mcp_url}, \nwith headers: {headers}\n") async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as ( read_stream, write_stream, _, ): async with ClientSession(read_stream, write_stream) as session: await session.initialize() tool_result = await session.list_tools() print(tool_result) asyncio.run(main())

Anda juga dapat menguji server yang digunakan menggunakan MCP Inspector seperti yang dijelaskan dalam Pengujian jarak jauh dengan inspektur MCP.

Tanggapan Kesalahan Otentikasi untuk Agen OAuth-Configured

OAuth-configured agen mengikuti standar otentikasi RFC 6749 (OAuth 2.0). Ketika otentikasi tidak ada, layanan mengembalikan respons 401 Tidak Sah dengan WWW-Authenticate header (per RFC 7235), memungkinkan klien menemukan titik akhir server otorisasi melalui API. GetRuntimeProtectedResourceMetadata

401 Tidak Sah - Otentikasi Hilang

Ketika tidak ada token Bearer yang disediakan di header Otorisasi, responsnya adalah:

HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"

End to End Flow dengan Auth0

Bagian ini menunjukkan otentikasi OAuth menggunakan Auth0 sebagai penyedia identitas. Kami menggunakan Auth0 untuk contoh ini karena mendukung Dynamic Client Registration (DCR), yang menyederhanakan proses penyiapan klien dengan memungkinkan klien mendaftarkan diri secara terprogram saat runtime.

Langkah 1 - Langkah 3: Buat dan uji server MCP Anda

Ikuti Langkah 1-3 dari Langkah 1: Buat server MCP Anda melalui Langkah 3: Menyebarkan server MCP Anda AWS untuk membuat dan menguji server MCP Anda.

Langkah 4: Buat aplikasi Auth0

Ikuti instruksi pengaturan Auth0 di Auth0 oleh Okta.

Aktifkan Pendaftaran Klien Dinamis:

  1. Dasbor → Pengaturan → Lanjutan

  2. Alihkan “Pendaftaran Aplikasi Dinamis OIDC” → AKTIF

  3. Simpan perubahan

Untuk informasi selengkapnya, lihat dokumentasi Pendaftaran Klien Dinamis Auth0.

Langkah 5: Buat proyek Anda untuk penyebaran

Setelah menyiapkan otentikasi, perancah proyek baru dengan protokol MCP:

agentcore create --protocol MCP

Ikuti petunjuk interaktif untuk memberikan nama proyek. CLI merancah struktur proyek termasuk file konfigurasi. agentcore/agentcore.json Salin my_mcp_server.py file Anda ke direktori kode agen proyek yang dihasilkan, dan pastikan titik masuk agentcore/agentcore.json menunjuk ke file server Anda.

Langkah 6: Menyebarkan ke AWS

Menyebarkan agen Anda:

agentcore deploy

Perintah ini akan:

  • Package kode agen dan dependensi Anda

  • Unggah artefak penerapan ke Amazon S3

  • Buat runtime Amazon Bedrock AgentCore

  • Menyebarkan agen Anda ke AWS

Setelah penerapan, Anda akan menerima ARN runtime agen yang terlihat seperti:

arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_mcp_server-xyz123

Langkah 7: Panggil Agen yang Anda gunakan

Klien ini didasarkan pada contoh MCP SDK simple-auth-client resmi dengan modifikasi. Auth0-specific

catatan

Saat menggunakan Auth0 dengan Pendaftaran Klien Dinamis, Anda harus menyertakan audience parameter dalam permintaan otorisasi untuk menerima token JWT. Tanpa parameter ini, Auth0 mengembalikan token buram atau token JWE (terenkripsi) alih-alih token JWT standar. MCP SDK mengirimkan parameter OAuth 2.0 (RFC 8707), tetapi Auth0 memerlukan resource parameter OIDC untuk token JWT. audience Kedua parameter melayani tujuan yang sama tetapi Auth0 memprioritaskanaudience. Untuk informasi selengkapnya, lihat Komunitas Auth0 - Token JWT dengan Pendaftaran Aplikasi Dinamis.

Buat file bernama mcp_auth0_client.py dengan kode berikut. Klien ini menangani Auth0-specific persyaratan termasuk parameter audiens:

catatan

Kode ini mencakup patch httpx untuk menyuntikkan User-Agent header ke semua permintaan HTTP. Ini diperlukan karena MCP Python SDK saat ini tidak User-Agent menyertakan header dalam permintaan HTTP-nya, yang dapat menyebabkan masalah AWS dengan aturan WAF yang memerlukan header. User-Agent Untuk informasi selengkapnya, lihat MCP Python SDK Issue #1664 AWS dan grup aturan terkelola WAF.

#!/usr/bin/env python3 """ MCP client with OAuth authentication support for Auth0. Based on the official MCP SDK simple-auth-client example with Auth0 compatibility. Adds support for Auth0's 'audience' parameter requirement. Usage: # Required export AGENT_ARN="arn:aws:bedrock:us-west-2:123456789012:agent/ABCD1234" # Required for Auth0 export AUTH0_API_IDENTIFIER="your-api-identifier" # Optional - custom endpoint for beta/dev environments export CUSTOM_ENDPOINT="https://beta.example.com" python mcp_auth0_client.py The client will automatically: - Encode the Agent ARN for use in the URL - Construct the MCP invocation endpoint URL - Add Auth0 'audience' parameter to authorization requests (when using Auth0) - Work with any OAuth 2.0 compliant identity provider """ import asyncio import httpx import os import threading import time import webbrowser from datetime import timedelta from http.server import BaseHTTPRequestHandler, HTTPServer from typing import Any from urllib.parse import parse_qs, urlencode, urlparse, urlunparse # Patch httpx at the request level to inject User-Agent header # This ensures ALL HTTP requests have the User-Agent header, including OAuth discovery calls _original_httpx_request = httpx.Request.__init__ def _patched_httpx_request_init(self, method, url, *args, **kwargs): """Patched Request.__init__ that injects User-Agent header into all HTTP requests.""" # Get or create headers headers = kwargs.get('headers') if headers is None: headers = {} kwargs['headers'] = headers # Convert to mutable dict if needed if not isinstance(headers, dict): headers = dict(headers) kwargs['headers'] = headers # Inject User-Agent if not present (case-insensitive check) if 'User-Agent' not in headers and 'user-agent' not in headers: headers['User-Agent'] = 'python-mcp-sdk/1.0 (BedrockAgentCore-Runtime)' # Call original __init__ _original_httpx_request(self, method, url, *args, **kwargs) # Apply the patch globally before importing MCP modules httpx.Request.__init__ = _patched_httpx_request_init # Now import MCP modules - they will use patched httpx from mcp.client.auth import OAuthClientProvider, TokenStorage from mcp.client.session import ClientSession from mcp.client.sse import sse_client from mcp.client.streamable_http import streamablehttp_client from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken class InMemoryTokenStorage(TokenStorage): """Simple in-memory token storage implementation.""" def __init__(self): self._tokens: OAuthToken | None = None self._client_info: OAuthClientInformationFull | None = None async def get_tokens(self) -> OAuthToken | None: return self._tokens async def set_tokens(self, tokens: OAuthToken) -> None: self._tokens = tokens async def get_client_info(self) -> OAuthClientInformationFull | None: return self._client_info async def set_client_info(self, client_info: OAuthClientInformationFull) -> None: self._client_info = client_info class CallbackHandler(BaseHTTPRequestHandler): """Simple HTTP handler to capture OAuth callback.""" def __init__(self, request, client_address, server, callback_data): """Initialize with callback data storage.""" self.callback_data = callback_data super().__init__(request, client_address, server) def do_GET(self): """Handle GET request from OAuth redirect.""" parsed = urlparse(self.path) query_params = parse_qs(parsed.query) if "code" in query_params: self.callback_data["authorization_code"] = query_params["code"][0] self.callback_data["state"] = query_params.get("state", [None])[0] self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(b""" <html> <body> <h1>Authorization Successful!</h1> <p>You can close this window and return to the terminal.</p> <script>setTimeout(() => window.close(), 2000);</script> </body> </html> """) elif "error" in query_params: self.callback_data["error"] = query_params["error"][0] self.send_response(400) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write( f""" <html> <body> <h1>Authorization Failed</h1> <p>Error: {query_params["error"][0]}</p> <p>You can close this window and return to the terminal.</p> </body> </html> """.encode() ) else: self.send_response(404) self.end_headers() def log_message(self, format, *args): """Suppress default logging.""" pass class CallbackServer: """Simple server to handle OAuth callbacks.""" def __init__(self, port=3030): self.port = port self.server = None self.thread = None self.callback_data = {"authorization_code": None, "state": None, "error": None} def _create_handler_with_data(self): """Create a handler class with access to callback data.""" callback_data = self.callback_data class DataCallbackHandler(CallbackHandler): def __init__(self, request, client_address, server): super().__init__(request, client_address, server, callback_data) return DataCallbackHandler def start(self): """Start the callback server in a background thread.""" handler_class = self._create_handler_with_data() self.server = HTTPServer(("localhost", self.port), handler_class) self.thread = threading.Thread(target=self.server.serve_forever, daemon=True) self.thread.start() print(f"🖥️ Started callback server on http://localhost:{self.port}") def stop(self): """Stop the callback server.""" if self.server: self.server.shutdown() self.server.server_close() if self.thread: self.thread.join(timeout=1) def wait_for_callback(self, timeout=300): """Wait for OAuth callback with timeout.""" start_time = time.time() while time.time() - start_time < timeout: if self.callback_data["authorization_code"]: return self.callback_data["authorization_code"] elif self.callback_data["error"]: raise Exception(f"OAuth error: {self.callback_data['error']}") time.sleep(0.1) raise Exception("Timeout waiting for OAuth callback") def get_state(self): """Get the received state parameter.""" return self.callback_data["state"] def add_auth0_audience_parameter(authorization_url: str, audience: str) -> str: """ Add Auth0 'audience' parameter to authorization URL. Auth0 requires the 'audience' parameter to identify which API's token settings to use. Without it, Auth0 returns opaque tokens or JWE instead of JWT. This function properly adds the audience parameter while preserving all existing query parameters (including the OAuth 'resource' parameter). Args: authorization_url: The authorization URL from the OAuth flow audience: The Auth0 API identifier (e.g., "runtime-api") Returns: Modified URL with audience parameter added Reference: https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens """ # Only apply to Auth0 URLs that don't already have audience if 'auth0.com' not in authorization_url or 'audience=' in authorization_url: return authorization_url # Parse URL and query parameters parsed = urlparse(authorization_url) query_params = parse_qs(parsed.query, keep_blank_values=True) # Add audience parameter query_params['audience'] = [audience] # Rebuild URL with new parameter new_query = urlencode(query_params, doseq=True) return urlunparse(( parsed.scheme, parsed.netloc, parsed.path, parsed.params, new_query, parsed.fragment )) class SimpleAuthClient: """Simple MCP client with Auth0 OAuth support.""" def __init__( self, server_url: str, transport_type: str = "streamable-http", auth0_audience: str | None = None, ): self.server_url = server_url self.transport_type = transport_type self.auth0_audience = auth0_audience self.session: ClientSession | None = None async def connect(self): """Connect to the MCP server.""" print(f"🔗 Attempting to connect to {self.server_url}...") try: callback_server = CallbackServer(port=3030) callback_server.start() async def callback_handler() -> tuple[str, str | None]: """Wait for OAuth callback and return auth code and state.""" print("⏳ Waiting for authorization callback...") try: auth_code = callback_server.wait_for_callback(timeout=300) return auth_code, callback_server.get_state() finally: callback_server.stop() client_metadata_dict = { "client_name": "MCP Auth0 Client", "redirect_uris": ["http://localhost:3030/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], } async def redirect_handler(authorization_url: str) -> None: """Redirect handler that opens the URL in a browser with Auth0 audience parameter.""" # Add Auth0 audience parameter if configured if self.auth0_audience: authorization_url = add_auth0_audience_parameter( authorization_url, self.auth0_audience ) webbrowser.open(authorization_url) print("\n🔧 Creating OAuth client provider...") # Create OAuth authentication handler # Note: httpx.AsyncClient is globally patched to inject User-Agent header oauth_auth = OAuthClientProvider( server_url=self.server_url, client_metadata=OAuthClientMetadata.model_validate(client_metadata_dict), storage=InMemoryTokenStorage(), redirect_handler=redirect_handler, callback_handler=callback_handler, ) print("🔧 OAuth client provider created successfully") # Create transport with auth handler based on transport type if self.transport_type == "sse": print("📡 Opening SSE transport connection with auth...") async with sse_client( url=self.server_url, auth=oauth_auth, timeout=60, ) as (read_stream, write_stream): await self._run_session(read_stream, write_stream, None) else: print("📡 Opening StreamableHTTP transport connection with auth...") async with streamablehttp_client( url=self.server_url, auth=oauth_auth, timeout=timedelta(seconds=60), ) as (read_stream, write_stream, get_session_id): await self._run_session(read_stream, write_stream, get_session_id) except Exception as e: print(f"❌ Failed to connect: {e}") import traceback traceback.print_exc() async def _run_session(self, read_stream, write_stream, get_session_id): """Run the MCP session with the given streams.""" print("🤝 Initializing MCP session...") async with ClientSession(read_stream, write_stream) as session: self.session = session print("⚡ Starting session initialization...") await session.initialize() print("✨ Session initialization complete!") print(f"\n✅ Connected to MCP server at {self.server_url}") if get_session_id: session_id = get_session_id() if session_id: print(f"Session ID: {session_id}") # Run interactive loop await self.interactive_loop() async def list_tools(self): """List available tools from the server.""" if not self.session: print("❌ Not connected to server") return try: result = await self.session.list_tools() if hasattr(result, "tools") and result.tools: print("\n📋 Available tools:") for i, tool in enumerate(result.tools, 1): print(f"{i}. {tool.name}") if tool.description: print(f" Description: {tool.description}") print() else: print("No tools available") except Exception as e: print(f"❌ Failed to list tools: {e}") async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None = None): """Call a specific tool.""" if not self.session: print("❌ Not connected to server") return try: result = await self.session.call_tool(tool_name, arguments or {}) print(f"\n🔧 Tool '{tool_name}' result:") if hasattr(result, "content"): for content in result.content: if content.type == "text": print(content.text) else: print(content) else: print(result) except Exception as e: print(f"❌ Failed to call tool '{tool_name}': {e}") async def interactive_loop(self): """Run interactive command loop.""" print("\n🎯 Interactive MCP Client") print("Commands:") print(" list - List available tools") print(" call <tool_name> [args] - Call a tool") print(" quit - Exit the client") print() while True: try: command = input("mcp> ").strip() if not command: continue if command == "quit": break elif command == "list": await self.list_tools() elif command.startswith("call "): parts = command.split(maxsplit=2) tool_name = parts[1] if len(parts) > 1 else "" if not tool_name: print("❌ Please specify a tool name") continue # Parse arguments (simple JSON-like format) arguments = {} if len(parts) > 2: import json try: arguments = json.loads(parts[2]) except json.JSONDecodeError: print("❌ Invalid arguments format (expected JSON)") continue await self.call_tool(tool_name, arguments) else: print("❌ Unknown command. Try 'list', 'call <tool_name>', or 'quit'") except KeyboardInterrupt: print("\n\n👋 Goodbye!") break except EOFError: break async def main(): """Main entry point.""" # Get Agent ARN from environment agent_arn = os.getenv("AGENT_ARN") if not agent_arn: print("❌ Please set AGENT_ARN environment variable") print("Example: export AGENT_ARN='arn:aws:bedrock:us-west-2:123456789012:agent/ABCD1234'") return # Encode the ARN for use in URL encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F') # Get base URL - use custom endpoint or default to production base_endpoint = os.getenv("CUSTOM_ENDPOINT", "https://bedrock-agentcore.us-west-2.amazonaws.com") # Construct MCP URL from encoded ARN (no qualifier - SDK discovers it from PRM API) server_url = f"{base_endpoint}/runtimes/{encoded_arn}/invocations" # Get Auth0 configuration (required only for Auth0) auth0_audience = os.getenv("AUTH0_API_IDENTIFIER") # Get optional transport type transport_type = os.getenv("MCP_TRANSPORT_TYPE", "streamable-http") print("🚀 MCP Auth0 Client") print(f"Agent ARN: {agent_arn}") print(f"Endpoint: {base_endpoint}") print(f"Connecting to: {server_url}") print(f"Transport type: {transport_type}") if auth0_audience: print(f"Auth0 audience: {auth0_audience}") # Start connection flow - OAuth will be handled automatically client = SimpleAuthClient( server_url, transport_type, auth0_audience, ) await client.connect() def cli(): """CLI entry point for uv script.""" asyncio.run(main()) if __name__ == "__main__": cli()

Untuk menggunakan klien:

  1. Tetapkan variabel lingkungan yang diperlukan:

    export AGENT_ARN="arn:aws:bedrock:us-west-2:123456789012:agent/ABCD1234"
  2. Setel variabel Auth0-specific lingkungan (hanya diperlukan untuk Auth0):

    export AUTH0_API_IDENTIFIER="your-api-identifier"
  3. Jalankan klien:

    python mcp_auth0_client.py

Klien akan secara otomatis:

  • Encode Agen ARN untuk digunakan di URL

  • Membangun URL endpoint pemanggilan MCP

  • Tambahkan audience parameter Auth0 ke permintaan otorisasi (saat menggunakan Auth0)

  • Bekerja dengan penyedia identitas yang sesuai dengan OAuth 2.0

Lampiran

Siapkan kumpulan pengguna Cognito untuk otentikasi

Buat file baru setup_cognito.sh dan tambahkan konten berikut.

#!/bin/bash # Create User Pool and capture Pool ID directly export POOL_ID=$(aws cognito-idp create-user-pool \ --pool-name "MyUserPool" \ --policies '{"PasswordPolicy":{"MinimumLength":8}}' \ --region $REGION | jq -r '.UserPool.Id') # Create App Client and capture Client ID directly export CLIENT_ID=$(aws cognito-idp create-user-pool-client \ --user-pool-id $POOL_ID \ --client-name "MyClient" \ --no-generate-secret \ --explicit-auth-flows "ALLOW_USER_PASSWORD_AUTH" "ALLOW_REFRESH_TOKEN_AUTH" \ --region $REGION | jq -r '.UserPoolClient.ClientId') # Create User aws cognito-idp admin-create-user \ --user-pool-id $POOL_ID \ --username $USERNAME \ --region $REGION \ --message-action SUPPRESS > /dev/null # Set Permanent Password aws cognito-idp admin-set-user-password \ --user-pool-id $POOL_ID \ --username $USERNAME \ --password $PASSWORD \ --region $REGION \ --permanent > /dev/null # Authenticate User and capture Access Token export BEARER_TOKEN=$(aws cognito-idp initiate-auth \ --client-id "$CLIENT_ID" \ --auth-flow USER_PASSWORD_AUTH \ --auth-parameters USERNAME=$USERNAME,PASSWORD=$PASSWORD \ --region $REGION | jq -r '.AuthenticationResult.AccessToken') # Output the required values echo "Pool id: $POOL_ID" echo "Discovery URL: https://cognito-idp.$REGION.amazonaws.com/$POOL_ID/.well-known/openid-configuration" echo "Client ID: $CLIENT_ID" echo "Bearer Token: $BEARER_TOKEN"

Buka jendela terminal dan atur variabel lingkungan berikut:

  • REGION— AWS Wilayah yang ingin Anda gunakan

  • USERNAME— nama pengguna untuk pengguna baru

  • PASSWORD— kata sandi untuk pengguna baru

export REGION=us-east-1 // set your desired Region export USERNAME=USER NAME export PASSWORD=PASSWORD

Jalankan skrip menggunakan perintahsource setup_cognito.sh.

catatan

Untuk mengetahui detail pengaturan otentikasi OAuth dan informasi Service-Linked Peran, lihat Mengautentikasi dan mengotorisasi dengan Auth Masuk dan Auth Keluar.

Setelah menjalankan skrip ini, perhatikan nilai berikut untuk digunakan dalam konfigurasi penerapan:

  • URL Penemuan: Digunakan selama agentcore create langkah

  • ID Klien: Digunakan selama agentcore create langkah

  • Token Pembawa: Digunakan saat menjalankan server yang Anda gunakan

Pengujian lokal dengan inspektur MCP

MCP Inspector adalah alat visual untuk menguji server MCP. Untuk menggunakannya, Anda perlu:

  • Node.js dan npm diinstal

Instal dan jalankan MCP Inspector:

npx @modelcontextprotocol/inspector

Ini akan:

  • Mulai server MCP Inspector

  • Menampilkan URL di terminal Anda (biasanyahttp://localhost:6274)

Untuk menggunakan Inspector:

  1. Arahkan ke http://localhost:6274 di browser Anda

  2. Tempelkan URL server MCP (http://localhost:8000/mcp) ke bidang koneksi MCP Inspector

  3. Anda akan melihat alat Anda tercantum di sidebar

  4. Klik pada alat apa saja untuk mengujinya

  5. Isi parameter (misalnya, untukadd_numbers, masukkan nilai untuk a danb)

  6. Klik “Alat Panggilan” untuk melihat hasilnya

Pengujian jarak jauh dengan inspektur MCP

Anda juga dapat menguji server yang digunakan menggunakan MCP Inspector. Pertama, agen ARN URL-encode Anda:

export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my_mcp_server-xyz123" echo -n $AGENT_ARN | jq -sRr '@uri'

Ini menghasilkan URL-encoded ARN:

arn%3Aaws%3Abedrock-agentcore%3Aus-west-2%3A123456789012%3Aruntime%2Fmy_mcp_server-xyz123

Kemudian terhubung dengan MCP Inspector:

  1. Mulai MCP Inspector:

    npx @modelcontextprotocol/inspector
  2. Di antarmuka web:

    • Pilih “Streamable HTTP” sebagai transportasi

    • Masukkan URL titik akhir agen Anda menggunakan ARN yang dikodekan. Pastikan untuk menggunakan wilayah yang sama dengan ARN agen Anda:

      https://bedrock-agentcore.REGION.amazonaws.com/runtimes/ENCODED_ARN/invocations?qualifier=DEFAULT

      Contoh untuk us-west-2:

      https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-west-2%3A123456789012%3Aruntime%2Fmy_mcp_server-xyz123/invocations?qualifier=DEFAULT
    • Tambahkan token Bearer Anda di bagian Otentikasi dengan nama Authorization dan nilai header Bearer YOUR_TOKEN

    • Klik “Connect”

  3. Uji alat Anda seperti yang Anda lakukan secara lokal