Home / Articles / Practical notes: Semantic search across MCP tools with Amazon Bedrock AgentCore

This article is published in English.

Practical notes: Semantic search across MCP tools with Amazon Bedrock AgentCore

Operable walkthrough of Practical notes: Semantic search across MCP tools with Amazon Bedrock AgentCore: contracts, checks, and drop-in code slots for teams shipping this pattern.

903 words

This walkthrough rebuilds the path from raw materials to a working system for: Semantic search across MCP tools with Amazon Bedrock AgentCore Gateway. The focus is operable steps, explicit checks, and code that you can drop into a repo without guessing intent. For Overview, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion.

What you build

When working through What you build, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Log tool name, args hash, latency, and outcome for every call. Debugging agent loops without that trail wastes hours.

Why semantic search on the Gateway side

When working through Why semantic search on the Gateway side, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Log tool name, args hash, latency, and outcome for every call. Debugging agent loops without that trail wastes hours.

Prerequisites

When working through Prerequisites, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish. Log tool name, args hash, latency, and outcome for every call. Debugging agent loops without that trail wastes hours. When working through Prerequisites, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion.

Run a semantic search

Run a semantic search works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Expose tools with narrow schemas and explicit side-effect labels. Hosts need to know which calls mutate state before they auto-approve.

Step 1: Query the search tool

Step 1: Query the search tool works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Expose tools with narrow schemas and explicit side-effect labels. Hosts need to know which calls mutate state before they auto-approve.

from mcp.client.streamable_http import streamablehttp_client
from mcp.client.session import ClientSession
async with streamablehttp_client(gateway_url) as (r, w, _):
    async with ClientSession(r, w) as session:
        await session.initialize()        result = await session.call_tool(
            "x_amz_bedrock_agentcore_search",
            {"query": "find a customer by phone number"},
        )        for match in result.content:
            print(match.text)

Step 2: Use the results to filter the tool list

Step 2: Use the results to filter the tool list works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish. Expose tools with narrow schemas and explicit side-effect labels. Hosts need to know which calls mutate state before they auto-approve.

async def smart_tool_selection(session, user_request: str, top_k: int = 5):
    search = await session.call_tool(
        "x_amz_bedrock_agentcore_search",
        {"query": user_request},
    )
    relevant_tool_names = [match.text for match in search.content[:top_k]]    all_tools = await session.list_tools()
    return [t for t in all_tools.tools if t.name in relevant_tool_names]

Step 3: Wire it into a Strands agent

Step 3: Wire it into a Strands agent works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline.

from strands import Agent
from strands.tools.mcp import MCPClient
async def run(user_message: str):
    async with MCPClient(gateway_url) as mcp:
        relevant_tools = await smart_tool_selection(mcp.session, user_message)        agent = Agent(
            model="anthropic.claude-opus-4-7-v1:0",
            tools=relevant_tools,
            system_prompt="Use only the provided tools to answer.",
        )
        return await agent.run_async(user_message)

Tuning the search

Reference

What’s next

Operational checklist