Agentic AI

Ein Gateway, sechs Tools: AgentCore Gateway als einziger Weg nach außen für Ihren Agent

17 min Lesezeit
· Zuletzt aktualisiert: 31. August 2026
Ein Gateway, sechs Tools: AgentCore Gateway als einziger Weg nach außen für Ihren Agent

Hinweis: Dieser Artikel wurde mit Unterstützung von KI aus dem englischen Original übersetzt.

TL;DR: Ich habe das eine AgentCore Gateway aufgeschlüsselt, über das dieser Agent jedes Tool aufruft: sechs Targets hinter einem MCP-Endpoint, unterlegt mit drei Lambdas und einem Managed-Knowledge-Base-Connector, wobei sowohl HubSpot als auch Partner Central in Lambda gekapselt sind statt als MCP-Server angebunden zu werden – wegen einer Auth-Inkompatibilität und eines Transport-Mismatches. Der schärfste Befund war die allowed_tools-Glob-Syntax: Macht man sie falsch, wirft das Modell keinen Fehler, sondern erzählt einfach erfundene Tool-Aufrufe, während die Lambda-Logs leer bleiben. Der einzige Weg, das zu entdecken, war die Beobachtung, dass die Converse-Token-Zahlen unabhängig von der Eingabe konstant bei 411 Tokens lagen.

Teil 4 der Serie „Einen Partner-Sales-Agent auf Amazon Bedrock AgentCore bauen“, aufgebaut um ein reales Projekt: ein dialogbasierter Agent, der HubSpot CRM und AWS Partner Central für das Vertriebsteam eines AWS-Partners verbindet. Der letzte Artikel hat die Harness-Runtime konfiguriert; dieser Artikel öffnet das eine Gateway, über das diese Runtime jedes Tool aufruft. Alles, was hier beschrieben wird, ist deployt und läuft (und jeder Einrichtungsschritt wird zunächst als Konsolen-Konfiguration beschrieben).

Inhaltsverzeichnis

Die teuerste Zeile im Terraform dieses Projekts ist eine Liste von sieben Strings. allowed_tools, der harness-seitige Filter, der entscheidet, welche Gateway-Tools das Modell sehen darf, verwendet eine Glob-Syntax, die im AgentCore Developer Guide dokumentiert ist – aber nirgends in den Terraform-Provider-Docs. Bis ich die richtige Seite gefunden hatte, beantwortete der deployte Agent jede Anfrage, indem er Tool-Aufrufe erzählte, die nie stattfanden, mit einem erfundenen Parameternamen. Nichts warf einen Fehler. Die Lambda-Logs blieben leer.

Dieser Artikel führt Sie durch die Tool-Ebene, die diese Zeile absichert: ein Gateway mit sechs Targets, die zwei externen Systeme, die beide ihre eigenen MCP-Server mitbringen und trotzdem hinter Lambda-Funktionen landeten, den Input-Envelope, den diese Lambdas tatsächlich erhalten, und die Berechtigungen, die die eigene IAM-Rolle des Gateways verlangte (die kein terraform validate geprüft hat). Der erste Artikel versprach einen einzigen Engpass, durch den jeder externe Effekt läuft. Hier ist er, Feld für Feld.

Ein Gateway, sechs Targets

AgentCore to Gateway Architecture

Ein Gateway in der AgentCore-Konsole ist eine kurze Form: ein Name, ein Protokolltyp, ein Autorisierer-Typ, eine Execution Role. Das einzige Gateway dieses Projekts ist partner-growth-agent-gateway, mit protocol_type = "MCP" und authorizer_type = "AWS_IAM" (die andere Option ist CUSTOM_JWT). Diese beiden Werte tragen den Großteil des Designs. Der MCP-Protokolltyp bedeutet, dass sich das Gateway gegenüber der Harness als Standard-MCP-Server präsentiert: Die Harness listet Tools mit tools/list auf und ruft sie mit tools/call auf. Der IAM-Autorisierer-Typ bedeutet, dass dieser Endpoint überhaupt keine endnutzerseitige Authentifizierung hat. Sein einziger Aufrufer ist die Harness, die jede Anfrage mit den SigV4-Credentials ihrer Execution Role signiert, über den in Artikel 1 beschriebenen und unten vollständig geöffneten Gateway-Tool-Block. Endnutzer kommen mit diesem Endpoint nie in Berührung; sie authentifizieren sich gegen Cognito in einer separaten Frontend-Anwendung. Zwei Authentifizierungsebenen, absichtlich getrennt gehalten: Menschen an der API-Grenze, ein IAM-Principal an der Tool-Grenze.

In der automatisierten Umsetzung ist die Gateway-Ressource entsprechend klein:

HCL
resource "aws_bedrockagentcore_gateway" "agent" {  name     = "partner-growth-agent-gateway"  role_arn = aws_iam_role.pc_gateway.arn  authorizer_type = "AWS_IAM"  protocol_type   = "MCP"  depends_on = [aws_iam_role_policy.pc_gateway_read_policy]  policy_engine_configuration {    arn  = aws_bedrockagentcore_policy_engine.pc.policy_engine_arn    mode = "ENFORCE"  }}

Der policy_engine_configuration-Block bindet die Cedar-Engine des nächsten Artikels im ENFORCE-Modus ein. Beachten Sie, was fehlt: kein JWT-Issuer, keine erlaubten Clients, kein OAuth irgendeiner Art.

AgentCore Gateway Targets configured

Hinter diesem einen Endpoint stehen die sechs Targets:

TargetBackendTool, das das Modell sieht
list-oppsPartner Central Lambda (Python 3.13)list_matching_opportunities
get-oppPartner Central Lambdaget_opportunity
reasonReasoning-Lambdareason_about
hs-searchHubSpot-Lambdasearch_contact
hs-writeHubSpot-Lambdaupsert_contact
managed-kbKnowledge-Base-ConnectorRetrieve, AgenticRetrieveStream

Sechs Targets tragen sieben Tools, weil managed-kb zwei Retrieval-Tools bereitstellt und die anderen fünf je eines. Das Muster „ein Tool pro Target" folgt dem Provider: Die Provider-Docs, die ich zum Build-Zeitpunkt geprüft habe, modellieren genau ein inline_payload-Tool-Schema pro Target, weshalb die beiden Partner-Central-Opportunity-Tools zu zwei Targets auf demselben Gateway wurden, statt dass ein Target zwei Schemas trägt.

Noch zwei Dinge zu dieser Tabelle, bevor wir tiefer in die Abschnitte einsteigen. Jeder Target-Name ist knapp gehalten, weil jeder in einen allowed_tools-Glob eingeht, den AWS auf 64 Zeichen begrenzt, und die ursprünglichen beschreibenden Namen die Grenze sprengten, sobald sie qualifiziert wurden. Das managed-kb-Target ist anders konfiguriert als seine fünf Nachbarn. In der Konsole ist es ein gewöhnliches Connector-Target auf demselben Gateway. In der automatisierten Umsetzung ist es eine terraform_data-Ressource, die local-exec ausführt, weil weder der hashicorp/aws- noch der hashicorp/awscc-Provider bisher ein Gateway-Connector-Target modelliert. Ein Konsolen-Leser sieht diese Lücke nie; sie existiert nur auf dem Automatisierungspfad. Artikel 6 behandelt die dahinterliegende Knowledge Base.

Ein Lambda-Target zu erstellen braucht fünf Werte, ob Konsole oder Code: einen Target-Namen, eine Beschreibung, die Lambda-Funktions-ARN, einen Credential-Provider und ein Tool-Schema. Also den Namen des Tools, eine Beschreibung, die das Modell liest, sowie typisierte Input- und Output-Eigenschaften. Das Terraform für das get-opp-Target, verdichtet auf die relevanten Teile (der Cedar-Guard des nächsten Artikels referenziert genau dieses Target):

HCL
resource "aws_bedrockagentcore_gateway_target" "get_opportunity" {  name               = "get-opp"  gateway_identifier = aws_bedrockagentcore_gateway.agent.gateway_id  description        = "Fetch one Partner Central opportunity by id, including its BANT source-field view"  credential_provider_configuration {    gateway_iam_role {}  }  target_configuration {    mcp {      lambda {        lambda_arn = aws_lambda_function.partnercentral.arn        tool_schema {          inline_payload {            name = "get_opportunity"            input_schema {              type = "object"              property {                name        = "opportunity_id"                type        = "string"                description = "The opportunity id returned by list_matching_opportunities (a 'matched' status result)."                required    = true              }            }            output_schema {              type = "object"              property {                name     = "Identifier"                type     = "string"                required = true              }              property {                name = "bant_source_fields"                type = "object"              }            }          }        }      }    }  }}

Ich habe ein Feld herausgekürzt, das einen Satz verdient: Der Beschreibungsstring des Tools trägt in der vollständigen Version einen Teil des Verhaltensvertrags und teilt dem Modell mit, dass jedes fehlende BANT-Feld (Budget, Authority, Need, Timeline) den wörtlichen Ausdruck "Not stated in opportunity data" wiedergibt, statt zu raten. Das Modell liest Schema-Beschreibungen wie Dokumentation. Eine Tool-Beschreibung ist Konfiguration und Prompt zugleich, und sie lebt hier im Target. Der Vertrag ist auf beiden Ebenen festgehalten, und im echten BANT-Roundtrip hat sich das Modell an die Formulierung des Prompts gehalten.

AgentCore Gateway Target configuration for get-opp

Warum Lambda-Targets, wenn beide externen Systeme MCP-Server mitbringen?

Die Tabelle oben sollte eine Frage aufwerfen. HubSpot bringt einen Remote-MCP-Server mit. AWS Partner Central bringt einen Remote-MCP-Server mit. AgentCore Gateway unterstützt MCP-Server-Targets. Warum ist dann jedes Target in diesem Projekt ein Lambda? Zwei getrennte Gründe, jeder für sich ausreichend, und alle drei verallgemeinern sich auf andere Builds.

Grund eins ist HubSpots Auth-Modell. HubSpots MCP-Server unter mcp.hubspot.com ist real und spricht Standard-Streamable-HTTP, verlangt aber OAuth Authorization Code mit PKCE [1]. Auf AgentCore-Seite würde dieser Flow über AgentCore Identity als benutzerdefinierten OAuth2-Credential-Provider laufen, und zum Build-Zeitpunkt war dieser Pfad durch einen offenen Bug im AgentCore-Python-SDK für genau diesen Fall blockiert, aws/bedrock-agentcore-sdk-python Issue #158, zum Zeitpunkt dieses Artikels im August 2026 noch offen [2].

Grund zwei ist der Transport von Partner Central. Der MCP-Server von Partner Central liegt unter https://partnercentral-agents-mcp.us-east-1.api.aws/mcp, signiert mit SigV4 für den Service-Namen partnercentral-agents-mcp in us-east-1 (eine der beiden Regionen, in denen sein Endpoint veröffentlicht ist [3]). Der MCP stellt nur die zwei Tools sendMessage und getSession bereit, während die Konversation über eine Session-ID im Tool-Argument abgewickelt wird [4]. Dieser Mechanismus passt nicht zum Session-ID-Handshake, den der MCP-Server-Target-Typ des AgentCore Gateways durchführt. Das ist ein Transport-Mismatch, keine Policy-Entscheidung; keine Konfiguration überbrückt das. Die Lösung ist ein Lambda-Target, und das Lambda nutzt botocores AWSRequest. Die Rolle des Gateways in diesem Hop ist reines Routing. Ein Hop, keine Protokollübersetzung dazwischen.

Ein Leser, der der Serie aufmerksam gefolgt ist, könnte einen dritten Weg vorschlagen: das Gateway für Partner Central überspringen und den remote_mcp-Tool-Typ der Harness selbst nutzen, der eine Harness direkt mit jedem MCP-Endpoint per URL verbindet, ganz ohne Gateway [6]. Das habe ich auch geprüft. Seine Konfiguration ist eine URL plus eine optionale Map sensibler Header, und seine dokumentierten Auth-Optionen sind genau drei: kein Auth, ein statischer Bearer-Token-Header oder ein Header-Wert, der aus einem AgentCore-Identity-API-Key-Credential-Provider aufgelöst wird [6]. Es gibt keine SigV4-Signieroption. Der Endpoint von Partner Central verlangt eine kontoübergreifende IAM-Rolle und SigV4-signierte Requests, die remote_mcp strukturell nicht erzeugen kann, egal wie der Transport aussieht. Für einen Drittanbieter-MCP-Server, der sich mit einem einfachen API-Key-Header authentifiziert, ist remote_mcp ein legitimer und deutlich kürzerer Weg; dieses Projekt hat nur keinen solchen Fall.

Wo das alles landet: Jedes Target auf diesem Gateway ist ein Lambda oder der Managed-KB-Connector, und das Projekt enthält nirgends eine OAuth-Konfiguration oder eine AgentCore-Identity-Ressource.

Die allowed_tools-Glob-Syntax

Für Leser, die neu bei MCP sind: tools/list ist die JSON-RPC-Methode, die ein MCP-Client aufruft, um die Tools eines Servers aufzulisten, bevor er eines davon aufruft. Die Harness führt diese Discovery gegen das Gateway aus und filtert das Ergebnis dann durch allowed_tools, bevor das Modell überhaupt ein Tool-Schema zu sehen bekommt. Ein Tool, das den Filter übersteht, bekommt sein Schema an die Anfrage des Modells angehängt. Ein Tool, das das nicht tut, ist unsichtbar.

Deshalb ist der Fehlermodus, wenn der Filter nichts trifft, so leise. Nachdem jede IAM-Lücke der Execution Role aus Artikel 3 behoben war, deployte die Harness sauber und der Chat wirkte lebendig. Nach einer Opportunity gefragt, verkündete das Modell, es rufe list_matching_opportunities auf, druckte einen plausiblen Aufruf mit einem Parameter namens company_name und fasste Ergebnisse zusammen, die es sich ausgedacht hatte. Der einzige Parameter des echten Tools ist spoken_name. Die CloudWatch-Log-Gruppe des Lambdas verzeichnete null Aufrufe. Nirgends gab es einen Fehler, denn aus Sicht des Modells war nichts falsch: Es war nach Opportunities gefragt worden, hatte keine Tools bekommen und lieferte seine beste Imitation eines arbeitenden Agents.

Ich habe mehrere plausible Wege ausprobiert. Nackte Tool-Namen. Die namespaced MCP-Namen des Gateways in der Form <target>___<tool>, die ich durch einen direkten SigV4-signierten tools/list-Aufruf gegen den MCP-Endpoint des Gateways bestätigt habe. Eine explizit leere Liste. Eine frisch neu erstellte Harness, bei der das Feld nie gesetzt wurde. GetHarness gab in jedem Fall allowedTools: [] zurück. Die endgültige Antwort: Die Converse-Input-Token-Zahlen waren bei jedem Versuch bytegleich (konstant 411 Tokens pro Request). Tool-Schemas sind Teil des Modell-Inputs. Wäre irgendein Schema angehängt gewesen, hätte sich diese Zahl bewegt.

Der Mechanismus ist auf der Seite "Tools" des AgentCore Developer Guide dokumentiert, nicht in den Terraform-Provider-Docs, die das Muster gar nicht beschreiben [6]. allowedTools-Einträge sind Glob-Muster mit einer kleinen Grammatik: @server, @server/tool, @server/glob, @*/tool [6]. Ein nackter Name wie shell trifft immer nur ein eingebautes Tool (shell, file_operations). Alles, was über ein Gateway oder einen MCP-Server entdeckt wird, braucht die @-Form, wobei server der Name des eigenen Tool-Blocks der Harness ist und tool der namespaced Name des Gateways. Für dieses Projekt ist das @pc/get-opp___get_opportunity: pc aus dem Harness-Tool-Block, get-opp___get_opportunity aus dem eigenen tools/list des Gateways.

Ich habe die korrigierten Globs angewendet, und der allernächste Aufruf produzierte einen echten contentBlockStart.start.toolUse-Block mit stopReason: "tool_use" und dem korrekten spoken_name-Argument. Die Converse-Input-Tokens sprangen über 411.

Eine Einschränkung blieb bestehen. AWS begrenzt jeden Glob-String auf 64 Zeichen und lehnt längere mit einer ValidationException ab. Die ursprünglichen Namen des Projekts waren für sich genommen vernünftig: ein Tool-Block namens partner_central_opportunities, Targets namens list-matching-opportunities und get-opportunity. Qualifiziert wäre der Glob des Listing-Tools auf 88 Zeichen gekommen. Alles wurde gekürzt, um zu passen (pc, list-opps, get-opp), und jedes seither hinzugefügte Target wurde von Anfang an kurz benannt.

Die deklarierte Konfiguration wie ausgeliefert:

HCL
tool {  type = "agentcore_gateway"  name = "pc"  config {    agentcore_gateway {      gateway_arn = aws_bedrockagentcore_gateway.agent.gateway_arn      outbound_auth {        aws_iam = true      }    }  }}allowed_tools = [  "@pc/list-opps___list_matching_opportunities",  "@pc/get-opp___get_opportunity",  "@pc/reason___reason_about",  "@pc/managed-kb___Retrieve",  "@pc/managed-kb___AgenticRetrieveStream",  "@pc/hs-search___search_contact",  "@pc/hs-write___upsert_contact",]

Die beiden Knowledge-Base-Tools liegen unter demselben @pc/-Präfix wie alles andere. Das managed-kb-Connector-Target hängt am selben Gateway und am selben Tool-Block, weshalb ein separates @kb/...-Präfix lautlos null Tools treffen würde – genau die Falle, die dieser Abschnitt dokumentieren soll.

Der Lambda-Input-Envelope

Die Log-Gruppe des Lambdas zeigte schließlich Aufrufe. Jeder einzelne scheiterte mit ValueError: Unknown tool name: None. Da ich den initialen Lambda-Handler geschrieben hatte, bevor das Schema korrigiert war, konnte ich nicht prüfen, ob die Konfiguration stimmte. Nun, jedes Teil war falsch.

Der tatsächliche Vertrag, laut der Lambda-Input-Format-Seite des Developer Guide [7]: event ist die flache Map der inputSchema-Eigenschaften des Tools, direkt, ohne jeden Wrapper-Key. Ein get_opportunity-Aufruf kommt als {"opportunity_id": "..."} an und sonst nichts. Der Tool-Name steht überhaupt nicht in event. Er reist im Lambda-Context-Objekt, unter context.client_context.custom['bedrockAgentCoreToolName'], formatiert als <target>___<tool>, und der Developer Guide warnt, dass das Präfix "manually stripped" werden muss [7]. Das korrigierte Dispatching, die gesamte Integrationsfläche zwischen dem Gateway und diesem Lambda:

Python
_TOOL_NAME_DELIMITER = "___"def _extract_tool_name(context) -> str:    raw_tool_name = context.client_context.custom["bedrockAgentCoreToolName"]    if _TOOL_NAME_DELIMITER in raw_tool_name:        return raw_tool_name.split(_TOOL_NAME_DELIMITER, 1)[1]    return raw_tool_namedef _json_safe(value):    if isinstance(value, datetime.date):        return value.isoformat()    if isinstance(value, dict):        return {k: _json_safe(v) for k, v in value.items()}    if isinstance(value, list):        return [_json_safe(v) for v in value]    return valuedef lambda_handler(event, context):    tool_name = _extract_tool_name(context)    arguments = event or {}    if tool_name == "list_matching_opportunities":        result = list_matching_opportunities(arguments.get("spoken_name"))    elif tool_name == "get_opportunity":        result = get_opportunity(arguments.get("opportunity_id"))    else:        raise ValueError(f"Unknown tool name: {tool_name!r}")    return _json_safe(result)

_json_safe in diesem Snippet ist die Live-Daten-Lektion dieses Abschnitts. list_matching_opportunities funktionierte sofort End-to-End. get_opportunity nicht: Runtime.MarshalError: Unable to marshal response: Object of type datetime is not JSON serializable. boto3 deserialisiert die Timestamp-Felder von Partner Central, darunter LifeCycle.TargetCloseDate und LastModifiedDate, in native Python-datetime-Objekte, und die Lambda-Runtime kann diese in einem Rückgabewert nicht JSON-marshaln. Die Unit-Test-Fixtures hatten einfache Datumsstrings verwendet, weshalb die Suite die ganze Zeit grün war. Der Fix ist der rekursive Konverter oben, angewendet auf den gesamten Rückgabewert, damit er einen Timestamp findet, wo auch immer er sitzt, plus ein Regressionstest, der echte date- und datetime-Objekte in die gemockte Response injiziert und prüft, dass json.dumps sie verlustfrei round-trippt. Fixtures, die auf Annahmen darüber gebaut sind, was eine API zurückgibt, fangen diese Bug-Klasse nicht ab. Ein einziger Live-Aufruf gegen echte Daten schon.

Eine Portierungsnotiz: _json_safe wurde nicht in das Reasoning-Lambda übernommen. Dieses Lambda parst rohes HTTP-JSON mit json.loads, seine Werte sind also bereits Strings.

Das IAM, das das Gateway selbst brauchte

Der vorherige Artikel hat diese Geschichte für die Execution Role der Harness erzählt: Jede neue Capability brachte ihren IAM-Bedarf als Live-Fehler zutage. Das Gateway wiederholt das Muster mit seiner eigenen Execution Role, und in diesem Projekt geschah das in dem Moment, in dem die Cedar-Policy-Engine damit verknüpft wurde.

Die Verknüpfung selbst ist eine Konsolen-Aktion oder der policy_engine_configuration-Block aus dem ersten Abschnitt. Ausgelöst hat sie eine Kette von fünf serverseitigen AccessDenied-Fehlern bei UpdateGateway, einer pro Apply, jeder mit dem Namen einer Berechtigung, die der Execution Role des Gateways fehlte:

  1. bedrock-agentcore:GetPolicyEngine auf der ARN der Policy-Engine. Der Fehler lautet "Access denied while calling GetPolicyEngine on Policy Engine ... with Gateway role": Die Gateway-Rolle muss die Engine lesen können, mit der sie verknüpft wird.
  2. bedrock-agentcore:AuthorizeAction auf der ARN der Policy-Engine. Die Metadaten der Engine zu lesen reicht nicht; die Rolle muss auch berechtigt sein, deren Autorisierungsentscheidung zur Laufzeit aufzurufen.
  3. bedrock-agentcore:AuthorizeAction erneut, diesmal auf die eigene ARN des Gateways bezogen. Cedars Auswertung läuft gegen die Gateway-Ressource, weshalb dieselbe Action einen zweiten Grant auf einen zweiten ARN-Typ braucht.
  4. bedrock-agentcore:PartiallyAuthorizeActions auf der ARN der Policy-Engine. Der eigene Validierungsschritt von UpdateGateway, sichtbar im Fehler als angenommene Rolle mit dem Suffix GenesisPolicyEngineCheck, ruft dies als eigenständige Action auf, die AuthorizeAction nicht impliziert.
  5. bedrock-agentcore:PartiallyAuthorizeActions auf der eigenen ARN des Gateways, was dasselbe Zwei-ARN-Muster wie Schritt 3 vervollständigt.

Drei Actions, zwei ARN-Typen, fünf live verlangte Grants. GetPolicyEngine war die einzige Action, die nie gegen die eigene ARN des Gateways verlangt wurde, weshalb die deployte Policy fünf Action-zu-ARN-Grants trägt, verpackt in zwei IAM-Statements. Wenn Sie planen, dasselbe zu bauen, vergeben Sie die vollständigen Berechtigungen von vornherein und überspringen Sie die Discovery-Schleife; jede dieser Berechtigungen kostete einen Apply-Fail-Diagnose-Zyklus, und keine davon ist vor dem Live-UpdateGateway-Aufruf sichtbar.

Zwei Implementierungsnotizen aus dem automatisierten Pfad. Die gateway-bezogenen Statements verwenden ein ARN-Muster, das am statisch bekannten Gateway-Namen mit einem Wildcard-Suffix verankert ist, arn:aws:bedrock-agentcore:eu-central-1:<account>:gateway/partner-growth-agent-gateway-*, statt der wörtlichen ID aus einem historischen Apply. Das Muster ist zur Plan-Zeit bekannt und übersteht ein Teardown und Neuerstellen; ein hartkodiertes Suffix nicht.

Übertragbare Muster

Fünf Dinge aus diesem Gateway verallgemeinern sich über diese Demo hinaus auf jeden AgentCore-Build.

  • Die 64-Zeichen-Grenze von allowed_tools ist eine Namensregel. Wählen Sie kurze Gateway-Target-Namen, bevor Sie wissen, wie viele Präfixe sich darauf stapeln werden. Ein Name, der für sich genommen bequem passt, kann die Grenze sprengen, sobald er mit dem Tool-Block-Präfix und dem Tool-Namen-Suffix qualifiziert wird, und ein Target umzubenennen, nachdem eine Demo davon abhängt, ist weit disruptiver, als es von Anfang an kurz zu benennen.
  • Nackte Namen treffen nur eingebaute Tools. Jedes Gateway- oder MCP-Tool braucht die Glob-Form @<block>/<target>___<tool>, dokumentiert auf der Seite "Tools" des Developer Guide und abwesend in den Terraform-Provider-Docs. Wenn Ihr Modell Tool-Aufrufe erzählt, statt sie zu tätigen, prüfen Sie das vor allem anderen – und prüfen Sie es mit Token-Zahlen, nicht mit Prosa.
  • Kapseln Sie einen externen MCP-Server in ein Lambda-Target, wenn dessen Transport oder Auth nicht zum Gateway passt. HubSpots OAuth-plus-PKCE-Pflicht und der sendMessage/getSession-Handshake von Partner Central sind zwei verschiedene Mismatches mit demselben Fix: ein Lambda, das auf der einen Seite das echte Protokoll des externen Systems spricht und auf der anderen ein gewöhnliches inline_payload-Schema bereitstellt.
  • Marshaln Sie Datetimes, bevor Live-Daten eintreffen. Jedes Lambda-Tool, das einen boto3-Client kapselt, braucht eine rekursive Datetime-zu-ISO-Konvertierung auf seinem gesamten Rückgabewert, weil Test-Fixtures aus einfachen Strings den Runtime.MarshalError echter Responses nicht sichtbar machen.
  • Prüfen Sie die Tool-Exposition mit einer direkten SigV4-tools/list-Sonde. Das Modell und die Harness zu umgehen, um das Gateway direkt zu fragen, was es bereitstellt, trennt „das Gateway stellt dieses Tool nicht bereit" von „das Modell kann es nicht sehen" in einer einzigen Anfrage. Das ist schneller und sicherer, als sich durch das Tool-Auswahlverhalten eines Modells zu debuggen.

Wenn Sie das nachbauen

Drei Hinweise für Leser, die dieses Subsystem selbst aufbauen. Alle drei stammen aus den oben beschriebenen Fehlern.

Erstens: Bevor Sie eine Policy-Engine mit einem Gateway verknüpfen, richten Sie die Policy-Engine-Berechtigungen von vornherein auf der Execution Role des Gateways ein: GetPolicyEngine, AuthorizeAction und PartiallyAuthorizeActions, bezogen auf die ARN der Policy-Engine und auf das eigene ARN-Muster des Gateways.

Zweitens: Halten Sie jeden Gateway-Target-Namen kurz, bevor er in einen allowed_tools-Glob qualifiziert wird. Das Budget beträgt insgesamt 64 Zeichen für @<block>/<target>___<tool>. Geben Sie sie für den Tool-Namen aus, den das Modell liest, nicht für den Target-Namen, den nur das Routing sieht.

Drittens: Bevor Sie Code gegen einen externen MCP-Server schreiben, prüfen Sie dessen Session-Handshake und Auth-Modell gegen die unterstützten Target-Typen des Gateways und die Outbound-Auth-Matrix. Beide Mismatches dieses Projekts waren in der öffentlichen Dokumentation sichtbar, bevor überhaupt Lambda-Code existierte. Ein Mismatch, der erst nach dem Bau des Lambdas entdeckt wird, bedeutet, die Integration neu zu schreiben, nicht nur den Code drumherum.

Ich hoffe, dieser Artikel war hilfreich für Sie. Ich freue mich über Feedback, was Ihnen gefallen hat und was nicht, damit ich künftige Artikel verbessern kann.


Quellen

[1] HubSpot-Entwicklerdokumentation für den HubSpot-MCP-Server (mcp.hubspot.com): Streamable-HTTP-Transport und die Anforderung OAuth Authorization Code + PKCE. https://developers.hubspot.com/docs/apps/developer-platform/build-apps/integrate-with-the-remote-hubspot-mcp-server

[2] GitHub, aws/bedrock-agentcore-sdk-python Issue #158: offener AgentCore-Identity-Bug für benutzerdefinierte OAuth2-Provider. https://github.com/aws/bedrock-agentcore-sdk-python/issues/158

[3] AWS Partner Central Developer Guide, MCP-Konfigurationsreferenz: Endpoint-URL, SigV4-Service-Name partnercentral-agents-mcp, us-east-1. https://docs.aws.amazon.com/partner-central/latest/developer-guide/mcp-configuration-reference.html

[4] AWS Partner Central Developer Guide, MCP-Tools-Referenz: die einzelne tools/call-Methode mit sendMessage/getSession, sowie der Abschnitt zur Fehlerbehandlung mit -32004 LIMIT_EXCEEDED. https://docs.aws.amazon.com/partner-central/latest/developer-guide/mcp-tools-reference.html

[5] Amazon Bedrock AgentCore Developer Guide: Gateway-Target-Typen und die Outbound-Auth-Support-Matrix (Lambda-Targets unterstützen nur die Gateway-Service-Rolle; API-Key- und OAuth-Varianten sind für diesen Target-Typ nicht unterstützt). https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-outbound-auth.html

[6] Amazon Bedrock AgentCore Developer Guide, "Tools": die allowedTools-Glob-Muster (@server, @server/tool, @server/glob, @*/tool), nackte Namen, die nur eingebaute Tools treffen, und die URL-plus-Header-Konfiguration des remote_mcp-Tool-Typs. https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-tools.html

[7] Amazon Bedrock AgentCore Developer Guide, Lambda-Funktions-Input-Format für Gateway-Targets: event als flache inputSchema-Property-Map, und der Tool-Name in context.client_context.custom['bedrockAgentCoreToolName'] mit dem zu entfernenden Target-Präfix. https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-add-target-lambda.html