可易

用推送式架构自动更新 AWS Agent Registry

可易团队阅读约 16 分钟

当 MCP 服务器更新工具时,CloudTrail 与 EventBridge 触发 Lambda,经 AgentCore Identity 拉取模式并写回 AWS Agent Registry,更新后回到草稿等待审批。

目录

#用推送式架构自动更新 AWS Agent Registry

每当发布在 AWS Agent Registry(Amazon Bedrock AgentCore 的一项能力)中的模型上下文协议(Model Context Protocol,MCP)服务器新增工具或更新其模式时,注册表记录也必须更新。如果没有自动的推送式同步,消费者会发现过时的元数据。对于跨账户管理多个 MCP 服务器的组织,这种同步能在现有治理工作流内保持目录记录最新。

在大多数企业部署中,目录的价值取决于它的新鲜程度。如果开发者周一部署了新工具,但注册表记录仍反映上周的模式,消费者就会失去对目录的信任。过时元数据直接降低注册表可靠性,导致团队无意中建立在不准确信息之上的依赖。

当工程师向 MCP 服务器添加工具却忘记更新目录时:

  • 消费者会完全错过这项新能力。
  • 应用可能用过时模式调用该工具,造成意外的运行时行为或静默的工作流偏离。

这种缺少自动化的情况在团队之间放大后,会侵蚀对注册表的信任。组织最终退回到临时发现机制,例如硬编码端点、非正式的知识分享和过时文档。

本文概述一种事件驱动架构,使用 AWS CloudTrail、Amazon EventBridge 和 AWS Lambda 自动完成这些更新。

架构概览

以下方案把这些服务组合成一条事件驱动流水线,检测运行时变更并自动同步到注册表。

该架构同时支持多账户和单账户环境:

  • 单账户设置:对于直接的部署,所有组件都位于账户 A。不需要跨账户转发设置,因为事件直接在账户 A 内流动。
  • 多账户设置:涉及两个账户,账户 B 的运行时事件被安全转发到账户 A。
  • 账户 A(注册表账户) 拥有 AWS Agent Registry、Amazon EventBridge 规则、推送同步 Lambda 函数和 CloudTrail 日志。
  • 账户 B(MCP 服务器账户) 托管 MCP 服务器运行时,以及用于身份认证的 Amazon Cognito OAuth 提供方。

示例使用两个账户,但你可以把架构扩展到多个账户:账户 A 作为注册表账户,多个类似于账户 B 的 AWS 账户用于不同的 MCP 服务器。

下图展示账户 B 中一次 MCP 服务器更新的端到端事件流。捕获的事件被转发到账户 A,Lambda 函数在那里查询 MCP 服务器的工具,并更新匹配的注册表记录。

端到端事件流架构图:账户 B 中的 MCP 服务器更新由 AWS CloudTrail 捕获,经 Amazon EventBridge 转发到账户 A 的 Lambda 函数,该函数查询 MCP 服务器并更新匹配的 AWS Agent Registry 记录。

图 1:跨账户 MCP 服务器更新的端到端事件流,从账户 B 的 CloudTrail 事件到账户 A 的注册表记录更新。

该架构使用五项 AWS 服务:

  • AWS CloudTrail 把 UpdateAgentRuntime API 调用捕获为管理事件。
  • Amazon EventBridge 把匹配事件路由到 Lambda 函数,并支持跨账户转发。
  • AWS Lambda 向 MCP 服务器查询其当前工具,与注册表做差异比较,并在发生变化时更新记录。
  • AWS Agent Registry 存储 MCP 服务器记录及其工具模式,作为中心事实来源。
  • AgentCore Identity(Amazon Bedrock AgentCore 的一项能力) 安全地代理 OAuth 令牌交换,因此 Lambda 无需存储密钥即可完成身份认证。

工作原理:业务逻辑流

部署之后,流水线运行以下端到端工作流:

  1. 触发:账户 B 中的开发者更新 MCP 服务器,这会调用 Amazon Bedrock AgentCore 中的 UpdateAgentRuntime API,AWS CloudTrail 捕获该事件。
  2. 路由:Amazon EventBridge 捕获 CloudTrail 事件,并转发到 账户 A 的中心事件总线。
  3. 解析:账户 A 中的同步 Lambda 从事件载荷提取运行时 Amazon Resource Name(ARN),并动态构造 MCP 服务器的调用 URL。它由此知道要查询账户 B 中的哪一台服务器。
  4. 身份认证:Lambda 通过 AgentCore Identity 生成安全的 OAuth 持有者令牌。
  5. 获取:Lambda 调用 MCP 服务器(先 initialize,再 tools/list),检索最新工具信息。
  6. 同步:Lambda 按 ARN 找到匹配的注册表记录,评估差异,并在模式已变化时更新注册表。

该流程假定账户 A 中的 Agent Registry 已经包含该 MCP 服务器的一条预先注册记录(在初始接入时创建)。本示例中的 Lambda 函数只更新已有记录。它不生成新记录。

同一流程也适用于单账户设置,其中所有资源(注册表、Lambda、Amazon EventBridge 规则和 MCP 服务器)位于一个账户。在那种情况下,跳过跨账户转发步骤。事件直接在同一账户的 Amazon EventBridge 总线内流动。

技术演练

本节按架构图所示,演练两个账户下单次 MCP 服务器更新的工作流。

发出 UpdateAgentRuntime 事件的每台服务器都会独立执行同一流程。Lambda 一次处理一个事件,并随 Amazon EventBridge 调用模型自然扩展。

步骤 1:跨账户设置的事件捕获与路由

当开发者部署或更新 MCP 服务器时,会调用 UpdateAgentRuntime API,调用方可以是 AgentCore CLI(agentcore deploy),也可以是直接调用。

AWS CloudTrail 在账户 B 中把这记录为管理事件,投递延迟大约 5–15 分钟。事件随后被投递到账户 A 的默认 Amazon EventBridge 总线。

对于跨账户设置,账户 B 的 Amazon EventBridge 通过跨账户规则把匹配事件转发到账户 A 的事件总线。

账户 A 中的 Amazon EventBridge 规则目标调用同步 Lambda,并把完整的 CloudTrail 事件 JSON 作为输入载荷传入。

Amazon EventBridge 规则模式:

{
  "source": ["aws.bedrock-agentcore"],
  "detail-type": ["API Call via AWS CloudTrail"],
  "detail": { "eventName": ["UpdateAgentRuntime"] }
}

JSON

步骤 2:提取 MCP 服务器 URL

Lambda 从事件中解析运行时 ARN。它提取托管账户 ID 和 AWS 区域,以动态构造目标 MCP 服务器经过 URL 编码的调用端点。

# Extract the runtime ARN from the AWS CloudTrail event payload
def _extract_mcp_url(event):
    detail = event.get("detail", {})
    runtime_arn = detail.get("responseElements", {}).get("agentRuntimeArn", "")
    if not runtime_arn:
        return None, None
    # Parse the account ID from the ARN (5th colon-separated segment)
    arn_parts = runtime_arn.split(":")
    account_id = arn_parts[4] if len(arn_parts) > 4 else None
    region = detail.get("awsRegion", "us-west-2")
    # URL-encode the ARN and build the MCP invocation endpoint
    encoded_arn = runtime_arn.replace(":", "%3A").replace("/", "%2F")
    mcp_url = (f"https://bedrock-agentcore.{region}.amazonaws.com"
               f"/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT")
    return mcp_url, account_id

Python

步骤 3:使用 AgentCore Identity 做安全身份认证

Lambda 使用两步身份流进行身份认证。它把自己的执行身份交换为临时工作负载令牌。然后 Lambda 通过目标账户的凭证提供方,把该令牌换成限定范围的 OAuth 持有者令牌。

def get_bearer_token(account_id=None):
    """Get OAuth bearer token via AgentCore Identity credential provider.

    Two-step process:
    1. Get a workload access token from AgentCore Identity (identifies this Lambda)
    2. Use it to fetch an OAuth token from the credential provider (M2M flow)

    The credential provider stores the Cognito/OAuth config securely in AgentCore
    Identity, so no client secrets are needed in Lambda env vars.
    """
    acct = account_id or ""
    provider_name = os.environ.get(f"CREDENTIAL_PROVIDER_{acct}") or os.environ.get("CREDENTIAL_PROVIDER", "")
    scope_str = os.environ.get(f"CREDENTIAL_SCOPE_{acct}") or os.environ.get("CREDENTIAL_SCOPE", "")
    scopes = [s.strip() for s in scope_str.split(",") if s.strip()] if scope_str else []
    workload_name = os.environ.get("WORKLOAD_IDENTITY_NAME", "")
    if not provider_name:
        raise ValueError(f"No CREDENTIAL_PROVIDER configured for account {acct}")
    if not workload_name:
        raise ValueError("WORKLOAD_IDENTITY_NAME env var not set")
    region = os.environ.get("AWS_REGION", "us-west-2")
    client = boto3.client("bedrock-agentcore", region_name=region)
    # Step 1: Get workload access token (identifies this Lambda as a trusted workload)
    wat_response = client.get_workload_access_token(
        workloadName=workload_name,
    )
    print(f"Workload access token response keys: {list(wat_response.keys())}")
    workload_token = wat_response.get("workloadAccessToken") or wat_response.get("accessToken", "")
    if not workload_token:
        raise ValueError(f"No access token in workload response: {list(wat_response.keys())}")
    # Step 2: Use workload token to get OAuth token from the credential provider
    response = client.get_resource_oauth2_token(
        workloadIdentityToken=workload_token,
        resourceCredentialProviderName=provider_name,
        oauth2Flow="M2M",
        scopes=scopes,
    )
    return response["accessToken"]

Python

凭证提供方按账户配置(CREDENTIAL_PROVIDER_{ACCT_ID}),因此一个 Lambda 可以为多个账户中的 MCP 服务器做身份认证。

步骤 4:查询 MCP 服务器

Lambda 使用标准 MCP 可流式 HTTP 机制,通过 initialize 请求建立活动会话。会话活动后,它调用 tools/list,获取当前暴露的工具和模式的完整数组。

def call_tools_list(mcp_url, token):
    """Call the MCP server's initialize + tools/list methods and return the result.

    MCP streamable-http requires initialize before tools/list.
    The session_id from initialize is passed to tools/list if the server uses sessions.
    """
    # Validate URL scheme to prevent file:// or custom scheme access
    if not mcp_url.startswith("https://"):
        raise ValueError(f"Only HTTPS URLs are allowed, got: {mcp_url[:50]}")
    # Step 1: Initialize MCP session
    init_payload = {
        "jsonrpc": "2.0",
        "id": "init-1",
        "method": "initialize",
        "params": {
            "protocolVersion": "2025-03-26",
            "capabilities": {},
            "clientInfo": {"name": "tool-sync-lambda", "version": "1.0.0"},
        },
    }
    init_resp = requests.post(
        mcp_url,
        json=init_payload,
        headers=_mcp_headers(token),
        timeout=30,
    )
    init_resp.raise_for_status()
    session_id = init_resp.headers.get("Mcp-Session-Id")
    init_result = _parse_sse_json(init_resp.text)
    print(f"MCP session initialized, session_id={session_id}")
    # Step 2: Call tools/list
    list_payload = {
        "jsonrpc": "2.0",
        "id": "list-1",
        "method": "tools/list",
        "params": {},
    }
    list_resp = requests.post(
        mcp_url,
        json=list_payload,
        headers=_mcp_headers(token, session_id),
        timeout=30,
    )
    list_resp.raise_for_status()
    return _parse_sse_json(list_resp.text)

Python

步骤 5:找到匹配的注册表记录

Lambda 查询中心 Agent Registry 以定位对应记录。它在 APPROVED 记录中搜索一条记录,其 mcpServer.data 包含记录创建时嵌入的运行时 ARN(见部署一节)。

# Decode the runtime ARN from the MCP URL for matching
def _find_record_by_mcp_url(client, registry_id, mcp_url):
    """Search registry records to find one whose server schema contains the runtime ARN.

    Tries three matching strategies:
    1. Decoded ARN (e.g. arn:aws:bedrock-agentcore:...:runtime/TrimMCP-xxx)
    2. URL-encoded ARN (e.g. arn%3Aaws%3Abedrock-agentcore%3A...%2FTrimMCP-xxx)
    3. Full MCP URL match

    Returns:
        (record_id, full_record) tuple. Both None if no match found.
    """
    # Extract the runtime ARN from the MCP URL for matching
    # URL format: .../runtimes/arn%3A...%2Fruntime%2F<id>/invocations...
    # Decode to get: arn:aws:bedrock-agentcore:region:account:runtime/id
    try:
        decoded_url = urllib.parse.unquote(mcp_url)
        # Extract just the runtime path: arn:aws:bedrock-agentcore:...:runtime/xxx
        runtime_marker = "/runtimes/"
        idx = decoded_url.find(runtime_marker)
        if idx >= 0:
            runtime_arn = decoded_url[idx + len(runtime_marker) :].split("/invocations")[0]
        else:
            runtime_arn = None
    except Exception:  # noqa: BLE001
        runtime_arn = None
    records = client.list_registry_records(registryId=registry_id)
    record_list = records.get("registryRecords", [])
    print(f"Found {len(record_list)} registry records")
    for rec in record_list:
        record_id = rec.get("registryRecordId") or rec.get("recordId") or rec.get("id", "")
        record_name = rec.get("name", "?")
        record_status = rec.get("status", "?")
        print(f" Record: {record_id} | {record_name} | status={record_status} | keys={list(rec.keys())}")
        if not record_id:
            print(f" Warning: could not get record ID from: {list(rec.keys())}")
            continue
        if record_status == "DRAFT":
            print(f" Skipping DRAFT record {record_id} ({record_name}) — must be APPROVED first")
            continue
        try:
            full = client.get_registry_record(
                registryId=registry_id,
                recordId=record_id,
            )
            descriptors = full.get("descriptors", {})
            mcp_desc = descriptors.get("mcpServer", {})
            inline = mcp_desc.get("data", "")
            # Match on runtime ARN (decoded or encoded) in the server schema
            if runtime_arn and runtime_arn in inline:
                print(f"Found matching record (by ARN): {record_id} ({rec.get('name', '?')})")
                return record_id, full
            # Also check URL-encoded ARN
            encoded_arn = runtime_arn.replace(":", "%3A").replace("/", "%2F") if runtime_arn else None
            if encoded_arn and encoded_arn in inline:
                print(f"Found matching record (by encoded ARN): {record_id} ({rec.get('name', '?')})")
                return record_id, full
            if mcp_url in inline or urllib.parse.unquote(mcp_url).replace("?qualifier=DEFAULT", "") in inline:
                print(f"Found matching record (by URL): {record_id} ({rec.get('name', '?')})")
                return record_id, full
        except Exception as e:  # noqa: BLE001
            print(f"Error checking record {record_id}: {e}")
            continue
    print(f"No matching record found among {len(record_list)} records.")
    print(f" Looking for runtime ARN: {runtime_arn}")
    print(f" Looking for MCP URL: {mcp_url}")
    return None, None

Python

步骤 6:比较差异并更新

为尽量减少不必要的写入,工具列表会被规范化(按名称排序,使用名称 + 描述 + inputSchema 比较)。仅在检测到变化时才触发注册表更新。如果模式不同,Lambda 记录确切的新增或移除,并把新的定义推送到注册表记录。

# Normalize tool definitions for comparison (sort by name, extract key fields)
def _normalize_tools(tools):
    """Normalize tool list for comparison — extract name, description, inputSchema."""
    normalized = []
    for t in sorted(tools, key=lambda x: x.get("name", "")):
        normalized.append(
            {
                "name": t.get("name", ""),
                "description": t.get("description", ""),
                "inputSchema": t.get("inputSchema", {}),
            }
        )
    return normalized

def _normalize_tools(tools):
    """Normalize tool list for comparison — extract name, description, inputSchema."""
    normalized = []
    for t in sorted(tools, key=lambda x: x.get("name", "")):
        normalized.append(
            {
                "name": t.get("name", ""),
                "description": t.get("description", ""),
                "inputSchema": t.get("inputSchema", {}),
            }
        )
    return normalized

def sync_registry_if_changed(mcp_tools, mcp_url):
    """Compare MCP server tools with AWS Agent Registry record tools. Update only if different.

    Steps:
    1. Find the AWS Agent Registry record matching this MCP server's URL
    2. Extract existing tools from the record's tools.inlineContent
    3. Normalize both tool lists (sort by name, compare name/description/inputSchema)
    4. If identical → skip update
    5. If different → log the diff and update the registry record

    Returns:
        dict with 'action' key: 'no_change', 'updated', or 'skipped'
    """
    registry_id = os.environ["REGISTRY_ID"]
    client = _get_registry_client()
    # Find the matching record
    record_id, full_record = _find_record_by_mcp_url(client, registry_id, mcp_url)
    if not record_id:
        print(f"No matching registry record found for {mcp_url}")
        return {"action": "skipped", "reason": "no matching record"}
    # Get current tools from registry
    registry_tools = _get_registry_tools_from_record(full_record)
    # Compare normalized tool lists
    mcp_normalized = _normalize_tools(mcp_tools)
    registry_normalized = _normalize_tools(registry_tools)
    if mcp_normalized == registry_normalized:
        print(f"No change detected. Registry record {record_id} is up to date ({len(registry_tools)} tools).")
        return {
            "action": "no_change",
            "record_id": record_id,
            "tool_count": len(registry_tools),
        }
    # Tools differ — update the registry
    print(f"Change detected! Registry has {len(registry_tools)} tools, MCP server has {len(mcp_tools)} tools.")
    # Log the diff
    mcp_names = {t["name"] for t in mcp_normalized}
    reg_names = {t["name"] for t in registry_normalized}
    added = mcp_names - reg_names
    removed = reg_names - mcp_names
    if added:
        print(f" Added: {added}")
    if removed:
        print(f" Removed: {removed}")
    if not added and not removed:
        print(" Tool definitions changed (same names, different schemas/descriptions)")
    tool_schema_content = json.dumps({"tools": mcp_tools})
    client.update_registry_record(
        registryId=registry_id,
        recordId=record_id,
        descriptors={
            "optionalValue": {
                "mcpServer": {
                    "optionalValue": {
                        "additionalData": {
                            "optionalValue": {
                                "tools": {
                                    "optionalValue": {
                                        "data": {
                                            "optionalValue": tool_schema_content,
                                        },
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
    )
    print(f"Updated registry record {record_id} in registry {registry_id}")
    return {
        "action": "updated",
        "record_id": record_id,
        "old_count": len(registry_tools),
        "new_count": len(mcp_tools),
    }

Python

治理与审批工作流

AWS Agent Registry 对每条记录强制执行治理生命周期:Draft -> Pending Approval -> Approved(或 Rejected/Deprecated)。管理员在注册表级别配置审批设置。这些设置控制什么会被发布、谁来复核,以及记录在变得可发现之前是否需要人工审批。只有处于 Approved 状态的记录,才能通过搜索、目录浏览或注册表的 MCP 端点对消费者可见。

当 Lambda 用新的工具定义更新记录时,该记录会自动回到 Draft 状态。消费者继续看到先前已批准的版本。策展人或管理员随后必须批准该更新,新元数据才会变得可发现。这样,自动更新不能绕过组织复核,管理员对向消费者暴露的内容保留完全控制。

部署

下表列出自动化流水线中的步骤:

步骤说明
定义配置区域、Lambda 名称、注册表 ID 和 MCP 服务器 ARN
创建并批准注册表记录注册 MCP 服务器条目,并把它移到 APPROVED
供给 AgentCore Identity创建工作负载身份和 OAuth2 凭证提供方
部署 Lambda 函数创建 IAM 执行角色、打包处理程序、部署函数
创建 Amazon EventBridge 规则把 UpdateAgentRuntime 事件路由到 Lambda
跨账户转发(可选)从远程账户转发运行时事件

前置条件

部署本方案之前,确认以下各项已就绪:

  • 一个 AWS 账户,具备创建 Lambda 函数、Amazon EventBridge 规则、AWS Identity and Access Management(IAM)角色和 Agent Registry 资源的权限。
  • 已配置凭证的 AWS Command Line Interface(AWS CLI),可访问示例中使用的全部服务。更多信息见 Getting started with the AWS CLI。
  • Python 3.12 运行时。
  • 一个已有的 AWS Agent Registry,至少有一条 MCP 服务器记录(在初始接入时创建;创建记录的说明在部署一节提供)。
  • 一台部署在 Amazon Bedrock AgentCore runtime 上的 MCP 服务器。
  • 为每个 MCP 服务器账户配置的 AgentCore Identity 工作负载身份和 OAuth2 凭证提供方(设置见示例仓库)。
  • 对于多账户设置,账户 B 与账户 A 之间的跨账户 Amazon EventBridge 权限。

组织常常使用多个账户,注册表在一个账户,MCP 服务器在其他账户。本示例概述多账户场景,你可以按说明把它改成单账户场景。

创建注册表记录

在本示例中,同步流水线能够工作之前,必须存在一条注册表记录,并在其服务器模式中显式嵌入运行时 ARN。你可以用以下代码创建注册表记录,并把运行时 ARN 放入记录的 mcpServer.data 字段。这就是 Lambda 用来把 AWS CloudTrail 事件关联到记录的依据:

# The websiteUrl contains the MCP server invocation URL with the encoded
# runtime ARN: this is what the Lambda matches against later.
encoded_arn = MCP_RUNTIME_ARN.replace(":", "%3A").replace("/", "%2F")
mcp_server_url = (
    f"https://bedrock-agentcore.{AWS_REGION}.amazonaws.com"
    f"/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT"
)
server_schema = json.dumps({
    "name": MCP_SERVER_NAME,
    "description": MCP_SERVER_DESCRIPTION,
    "websiteUrl": mcp_server_url,
})
registry_cp.create_registry_record(
    registryId=REGISTRY_ID,
    name=MCP_SERVER_NAME,
    description=MCP_SERVER_DESCRIPTION,
    recordType="MCP",
    recordVersion="1.0",
    descriptors={"mcpServer": {"data": server_schema}}
)

Python

批准记录

Lambda 只同步到 APPROVED 记录。

更新之后,注册表记录处于 DRAFT 阶段。它必须走过治理工作流,变更才会变得可发现。

# Submit for approval (DRAFT → PENDING_APPROVAL)
registry_cp.submit_registry_record_for_approval(
    registryId=REGISTRY_ID,
    recordId=RECORD_ID,
)
# Approve (PENDING_APPROVAL → APPROVED)
registry_cp.update_registry_record_status(
    registryId=REGISTRY_ID,
    recordId=RECORD_ID,
    status="APPROVED",
)

Python

Lambda 环境变量

为保持强安全态势,Lambda 环境变量中不存储任何客户端密钥。函数完全依靠环境变量映射,来引用运行时工作负载身份、提供方名称和权限范围。

# No client secrets here. Only provider names and scopes.
# The Lambda uses WORKLOAD_IDENTITY_NAME to get a workload token,
# then CREDENTIAL_PROVIDER_{ACCT} to fetch the OAuth token.
env_vars = {
    "REGISTRY_ID": REGISTRY_ID,
    "WORKLOAD_IDENTITY_NAME": WORKLOAD_IDENTITY_NAME,
}
for acct_id, config in ACCOUNT_CONFIGS.items():
    env_vars[f"CREDENTIAL_PROVIDER_{acct_id}"] = config["provider_name"]
    env_vars[f"CREDENTIAL_SCOPE_{acct_id}"] = config.get("scope", "")

Python

Amazon EventBridge 规则

该规则作为流水线触发器,监听本账户内的,或从跨账户 Amazon EventBridge 总线转发器到达的 UpdateAgentRuntime 管理事件:

# Match UpdateAgentRuntime events from AgentCore.
# Fires when any runtime is created or updated in this account
# (or forwarded from a cross-account EventBridge bus).
event_pattern = {
    "source": ["aws.bedrock-agentcore"],
    "detail-type": ["API Call via AWS CloudTrail"],
    "detail": {"eventName": ["UpdateAgentRuntime"]}
}
events_client.put_rule(
    Name=f"{LAMBDA_NAME}-trigger",
    EventPattern=json.dumps(event_pattern),
    State="ENABLED",
)

Python

清理

为避免持续费用,完成后删除以下资源:

  • Lambda 函数 – 删除推送同步 Lambda 及其 IAM 执行角色。
  • Amazon EventBridge 规则 – 删除该规则及其 Lambda 目标。
  • AgentCore Identity – 删除工作负载身份和 OAuth2 凭证提供方。
  • 注册表记录 – 删除或弃用测试用 MCP 服务器记录。
  • 注册表 – 如果注册表仅为测试创建,则删除它。
  • (多账户) 移除账户 B 中的跨账户 Amazon EventBridge 规则和资源策略。

清理代码见示例仓库。

已知限制与后续改进

这里概述的示例代码(以及完整示例)可以用几种方式扩展:

  • 新 MCP 服务器的自动注册 – 目前,匹配的注册表记录必须已经存在。未来的增强可以在首次检测到先前未知的 MCP 服务器时自动创建记录。
  • 手动“强制同步”触发 – 对于时间敏感的部署,可以通过 Amazon API Gateway 或 CLI 命令提供按需调用路径,在常规 CloudTrail 投递流之外送达变更。
  • 冷启动韧性 – 如果 MCP 服务器处于冷状态并返回零个工具,Lambda 目前不会重试。加入指数退避,并在工具数量为零时跳过更新,会提高可靠性。

结论

AWS Agent Registry 为组织提供一个受治理、可发现的 AI 智能体与 MCP 服务器目录。

推送同步 Lambda 模式补上新鲜度缺口:当检测到服务器变更时,注册表记录会在已知限制一节所述约束内被自动且安全地更新。更新会使记录回到 DRAFT 状态,并且必须通过审批工作流后才会再次变得可发现。

当你的智能体群体从少数几台增长到数百台 MCP 服务器时,该模式使注册表保持为单一事实来源,持续新鲜并经过复核。

开始使用

完整实现(Lambda 代码、部署脚本和架构图)以可直接部署的示例提供。要在你的环境中部署该方案,克隆该仓库,并按 README 中的部署步骤操作。 PRIMARY_SOURCE: https://aws.amazon.com/blogs/machine-learning/automating-aws-agent-registry-updates-with-push-based-architecture

  • #AWS Agent Registry
  • #MCP
  • #EventBridge
  • #Lambda
  • #审批流