feat(search): add Amazon Bedrock AgentCore web search provider - #36331
Conversation
…ider Adds 'agentcore' to SearchProviders, backed by an AgentCore Gateway web-search connector target (MCP tools/call over Streamable HTTP). Web Search on Amazon Bedrock AgentCore is an AWS-managed web index (GA June 2026). Exposing it as a native search provider lets Bedrock users enable Claude Code / Anthropic-native WebSearch through websearch_interception with a pure-YAML config and AWS-native auth, keeping the whole search path inside AWS. Implementation: - New AgentCoreSearchConfig (litellm/llms/bedrock/search/) reusing BaseAWSLLM credential resolution. Auth follows the gateway's inbound authorizer type: AWS_IAM gateways get a SigV4-signed request (explicit aws_access_key_id/aws_secret_access_key params or the default credential chain); CUSTOM_JWT gateways get an OAuth2 bearer token via api_key / AGENTCORE_GATEWAY_TOKEN - SigV4 signing region is derived from the gateway URL so callers don't need aws_region_name to match their default region - Adds an optional sign_request() hook to BaseSearchConfig (no-op by default) and teaches the search HTTP handler to send a signed body verbatim, mirroring the existing anthropic_messages/chat pattern - Handles both plain-JSON and SSE-framed MCP responses, propagates MCP errors, truncates queries to the 200-char gateway limit Tested: - 13 unit tests: payload/signing, explicit AKSK passthrough, bearer token via api_key and env, query truncation, SSE frames, MCP error propagation, region derivation - Verified end-to-end against real AWS_IAM and CUSTOM_JWT gateways, including full Claude Code CLI WebSearch round-trips through the proxy with websearch_interception
- Refuse to send the server-managed AGENTCORE_GATEWAY_TOKEN to a caller-supplied api_base (reuses resolve_server_api_key's trusted-host guard) — closes the token-exfiltration path via /search_tools/test_connection - Disable BaseAWSLLM's AWS_BEARER_TOKEN_BEDROCK fallback when signing: that token is a Bedrock Runtime credential and must not reach an AgentCore gateway - Parse SSE responses per spec: join multi-line data fields, iterate events, and return the JSON-RPC response (result/error) instead of the first data line — progress notifications no longer shadow the result - Validate tool_name ends with ___WebSearch so a caller-supplied name cannot invoke unrelated tools on the same gateway with the proxy's credentials - Send the documented maxResults default (10) explicitly instead of leaving it to the gateway - Custom gateway hostnames: raise a clear error when no signing region can be derived and none is configured, instead of signing for a guessed region - 7 new unit tests covering each fix (20 total)
The previous check only consulted AWS_REGION* env vars before rejecting custom hostnames, breaking deployments that configure their region via the AWS shared config (profile). Resolve through boto3's session (env vars + shared config) and only error when that chain yields nothing — never sign with a silently guessed region.
Coverage collection runs against the sharded tests/test_litellm tree, so the provider tests living only in tests/search_tests were invisible to codecov (patch coverage reported ~31% despite the suite). Mirror them as tests/test_litellm/llms/bedrock/search/test_agentcore_search_transformation.py and add edge-case tests (malformed MCP content blocks, SSE without a JSON frame, notification-only streams, list request body, error-class mapping). transformation.py line coverage: 99% (26 tests x2 trees).
…on stale daily branch
Adds 'agentcore' as a search provider backed by an AgentCore Gateway MCP web-search target, usable from litellm.search()/`/search` and as a websearch_interception backend. Supports SigV4 (AWS_IAM gateways) and bearer tokens (CUSTOM_JWT gateways) via a new BaseSearchConfig.sign_request hook. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
Greptile SummaryThe PR adds Amazon Bedrock AgentCore as an MCP-backed web-search provider, including bearer and SigV4 authentication, normalized JSON/SSE responses, provider registration, pricing metadata, configuration, and tests
Confidence Score: 5/5The PR appears safe to merge No blocking failure remains
|
| Filename | Overview |
|---|---|
| litellm/llms/bedrock/search/transformation.py | Implements AgentCore MCP request transformation, authentication, destination validation, SSE/JSON parsing, and result normalization |
| litellm/llms/custom_httpx/llm_http_handler.py | Adds search-request signing while keeping signed headers separate from callback logging and preserving signed body bytes |
| litellm/llms/base_llm/search/transformation.py | Adds a fully parameterized optional signing contract to the shared search configuration |
| tests/test_litellm/llms/bedrock/search/test_agentcore_search_transformation.py | Covers request transformation, response formats, authentication paths, destination checks, transport enforcement, and provider registration |
Reviews (9): Last reviewed commit: "docs(search): stop advertising yaml lite..." | Re-trigger Greptile
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Log the pre-signing headers in the search pre_call hook so SigV4 and bearer Authorization values are never handed to user-configured logger callbacks, and tighten sign_request's annotations. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Fixed both findings in 15a6664: pre_call now logs the pre-signing headers, and sign_request carries precise annotations. @greptileai review |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
Refuse to SigV4-sign requests to hosts that are neither an AgentCore gateway hostname nor AGENTCORE_GATEWAY_URL's host, match gateway hostnames on the URL host instead of anywhere in the URL, accept the env token when api_base is a real gateway, raise on tools/call responses with result.isError, and split CRLF-framed SSE events. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
boto3's session resolution ignores AWS_REGION, so an operator following the old message still hit the same failure. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Verified live on a local proxy against a stub MCP gateway, runs and outputs are in the description's proof section. A real gateway call is still untested. |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 49dca49. Configure here.
|
bugbot run |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
1 issue from previous review remains unresolved.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ed84e82. Configure here.
Web-search connector 1.1.0 and later return the machine-readable results in result.structuredContent and may leave the text block as prose, which the parser dropped. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
A trusted hostname over plain http would expose the bearer token or a replayable SigV4 signature to network observers. Credentials now only ride https, with localhost exempt so local MCP stubs keep working.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 70a4f9a. Configure here.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 7744b91. Configure here.

TLDR
Problem this solves:
How it solves it:
agentcoresearch provider calling an AgentCore Gateway/searchand forwebsearch_interceptionUser Flow
Before: a developer on AWS wants Claude Code on Bedrock to search the web, but pointing the proxy at their AgentCore Gateway does nothing and every working option needs a non-AWS search key
search_provider: agentcorepointing at their AgentCore Gateway MCP endpoint and start the proxy; boot succeeds with no warning{"query": "latest AI developments", "search_tool_name": "agentcore-search", "max_results": 5}and get HTTP 500'agentcore' is not a valid SearchProvidersagentcoreis absentweb_searchtool and their Bedrock model; HTTP 200 comes back, but the web search result block is empty and the assistant apologizes about a search provider configuration errorAfter: the same developer points the proxy at their AgentCore Gateway and search works, with the search traffic staying inside AWS
web-searchconnector target and setAGENTCORE_GATEWAY_URLto its MCP endpointsearch_provider: agentcore(seelitellm/proxy/example_config_yaml/agentcore_websearch_config.yaml) and no third-party key, then start the proxytitle,url,snippet,dateagentcore("Web Search on Amazon Bedrock")api_baseat a host that is not their gateway, or at a plainhttp://URL, returns an error instead of sending the server'sAGENTCORE_GATEWAY_TOKENor a SigV4 signature, and a caller-suppliedtool_namethat is not a___WebSearchconnector tool is rejectedRelevant issues
Fixes #31819
Supersedes #34098, which this builds on
Docs live in the docs repo and are already merged: BerriAI/litellm-docs#834, plus BerriAI/litellm-docs#949 for the MCP protocol version env var
Follow-up filed while QA'ing this PR: #37538 (the search router silently drops provider-specific yaml
litellm_paramsfor every search provider; pre-existing, untouched here)Linear ticket
Resolves LIT-5375
Pre-Submission checklist
Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Both runs hit a real AgentCore Gateway (
litellm-qa-agentcore-tez4ymmxfc, us-east-1,web-searchconnector target, provisioned for this QA and deleted after) and real Bedrock Claude, no mocks or stubs anywhere. Shared setup, identical for both sides except the commit the proxy was booted from:Env for both proxies:
AGENTCORE_GATEWAY_URL(the real gateway), SigV4 credentials,AWS_REGION=us-east-1, andAWS_BEARER_TOKEN_BEDROCKfor the Bedrock model call. Before proxy booted from the merge base on port 46526, After proxy from the PR tip on port 26793. Claude Code runs are the real TUI (v2.1.236) driven interactively under tmux withANTHROPIC_BASE_URLpointed at the proxyBefore (2059033)
POST /search through the configured agentcore search tool
curl -sS -X POST http://127.0.0.1:46526/search -H 'Content-Type: application/json' -H 'Authorization: Bearer sk-qa-before-7731' -d '{"query": "latest AI developments", "search_tool_name": "agentcore-search", "max_results": 5}'{"error":{"message":"litellm.APIConnectionError: 'agentcore' is not a valid SearchProviders\nTraceback (most recent call last):\n File \".../litellm/search/main.py\", line 244, in search\n provider=SearchProviders(search_provider),\n...ValueError: 'agentcore' is not a valid SearchProviders\n. Received Model Group=agentcore-search\nAvailable Model Group Fallbacks=None","type":null,"param":null,"code":"500"}}Provider discovery
curl -s http://127.0.0.1:46526/search_tools/ui/available_providers -H 'Authorization: Bearer sk-qa-before-7731'perplexity, tavily, parallel_ai, exa_ai, brave, google_pse, dataforseo, firecrawl, fastcrw, searxng, linkup, duckduckgo, searchapi, serper, you_com, apiserpent, tinyfish, nimble),agentcoreabsent/v1/messages with the Anthropic web_search tool on real Bedrock Claude
curl -sS -X POST http://127.0.0.1:46526/v1/messages -H 'Content-Type: application/json' -H 'Authorization: Bearer sk-qa-before-7731' -d '{"model":"claude-sonnet","max_tokens":1024,"messages":[{"role":"user","content":"Use the web_search tool to look up the latest LiteLLM release, then list the result titles you got."}],"tools":[{"type":"web_search_20250305","name":"web_search","max_uses":2}]}'web_search_tool_resultblock came back empty ("content":[]) and the assistant text was:The web search tool encountered an error on my end (a configuration issue with the search provider — "'agentcore' is not a valid SearchProviders") and was unable to return any results.WebSearchInterception: Search failed for 'latest LiteLLM release': litellm.APIConnectionError: 'agentcore' is not a valid SearchProvidersInteractive Claude Code under tmux
ANTHROPIC_BASE_URL) to look up the latest LiteLLM release with web search; its WebFetch fallback was denied so the search path stayed the thing under testCredential and tool-name guards
After (70a4f9a)
The PR tip 7744b91 differs from 70a4f9a only by a comment edit in the example config (nothing loads that file at runtime or in tests), so this run stands for the tip
POST /search through the configured agentcore search tool
curl -sS -X POST http://127.0.0.1:26793/search -H 'Content-Type: application/json' -H 'Authorization: Bearer sk-qa-after-9917' -d '{"query": "latest AI developments", "search_tool_name": "agentcore-search", "max_results": 5}'{"title": "AI News & Artificial Intelligence", "url": "https://techcrunch.com/category/artificial-intelligence/", "date": "08:44AM, Wednesday, August 19 2026, PDT"} {"title": "The trends that will shape AI and tech in 2026", "url": "https://www.ibm.com/think/news/ai-tech-trends-predictions-2026", "date": "02:42PM, Friday, July 24 2026, PDT"}Provider discovery
curl -s http://127.0.0.1:26793/search_tools/ui/available_providers -H 'Authorization: Bearer sk-qa-after-9917'{"provider_name": "agentcore", "ui_friendly_name": "Web Search on Amazon Bedrock"}/v1/messages with the Anthropic web_search tool on real Bedrock Claude
curl -sS -X POST http://127.0.0.1:26793/v1/messages -H 'Content-Type: application/json' -H 'Authorization: Bearer sk-qa-after-9917' -d '{"model":"claude-sonnet","max_tokens":1024,"messages":[{"role":"user","content":"Use the web_search tool to look up the latest LiteLLM release, then list the result titles you got."}],"tools":[{"type":"web_search_20250305","name":"web_search","max_uses":2}]}'['server_tool_use', 'web_search_tool_result', 'text']:{"name": "web_search", "input": {"query": "latest LiteLLM release"}}, then 10 real results (docs.litellm.ai/release_notes/...,github.com/BerriAI/litellm/releases, ...), then a real answer listing the titles;stop_reason: end_turn, usage 10770 in / 581 outtools/callofweb-search-tool___WebSearchto the gatewayInteractive Claude Code under tmux
tools/callagainst the real gateway; no other search backend existed in the environmentCredential and tool-name guards
curl -sS -X POST http://127.0.0.1:26793/v1/search/agentcore-untrusted ...returned HTTP 500 with no request sent:Refusing to send SigV4-signed AgentCore requests to 'https://untrusted-host.example.com/mcp': it is neither an AgentCore gateway hostname nor the host in AGENTCORE_GATEWAY_URL. Set AGENTCORE_GATEWAY_URL to authorize a custom gateway hostname.curl -sS -X POST http://127.0.0.1:26793/v1/search/agentcore-plaintext ...returned HTTP 500 with no request sent:Refusing to send AgentCore credentials over plaintext HTTP to 'http://gw.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp': a bearer token or SigV4 signature would be readable in transit. Use an https gateway URL (plain http is allowed only for localhost).AGENTCORE_SEARCH_TOOL_NAME=my-target___ListFilesexported,/v1/search/agentcore-searchrefused withInvalid AgentCore search tool_name 'my-target___ListFiles': must end with '___WebSearch' (a web-search connector tool). Other gateway tools cannot be invoked through this provider.agentcore-badtoolyaml entry did NOT trigger that guard: it returned HTTP 200 because the search router forwards onlysearch_provider/api_key/api_basefrom yamllitellm_paramsand dropped thetool_name, so the request went out with the default tool name. That plumbing predates this PR and hits every search provider; filed as [Bug]: search router silently drops provider-specific litellm_params from yaml search_tools entries #37538, and the example config here no longer advertises yaml knobs the router drops. The guard itself stays load-bearing for the reachable paths: request-bodytool_name, the env var, and SDK callsObservations from the runs, for the reviewer:
Type
🆕 New Feature
Caveats (if any)
api_basefield, so UI setup needsAGENTCORE_GATEWAY_URLtool_nameand aws credential overrides work per request or via env vars, not yaml ([Bug]: search router silently drops provider-specific litellm_params from yaml search_tools entries #37538)Final Attestation
Changes
AgentCoreSearchConfig(litellm/llms/bedrock/search/transformation.py) is aBaseSearchConfigthat speaks MCP instead of a bespoke REST API:transform_search_requestbuilds a JSON-RPCtools/callbody, andtransform_search_responseaccepts both a plain JSON reply and the Streamable HTTP SSE framing, picking the event that carriesresultorerrorso a progress notification can't shadow the response.{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "web-search-tool___WebSearch", "arguments": {"query": query[:200], "maxResults": max_results}}}Results are parsed from the tool result's text blocks first, falling back to
result.structuredContent(the web-search connector 1.1.0+ can return machine-readable results there while the text blocks carry prose). Requests go out withMCP-Protocol-Version: 2025-03-26, the version a gateway with a defaultprotocolConfigurationaccepts, overridable viaAGENTCORE_MCP_PROTOCOL_VERSIONfor gateways pinningsupportedVersions.Signing needed a new optional hook, because a SigV4 signature covers the exact bytes on the wire:
BaseLLMHTTPHandler.search/async_searchcall it and, when a signed body comes back, send it verbatim asdata=rather than re-serializing it throughjson=.Three things the gateway makes easy to get wrong, all covered by tests: a caller-supplied
tool_namemust end in___WebSearch, so this provider can't be used to invoke unrelated tools on the same gateway with the proxy's credentials; server-managed credentials, bothAGENTCORE_GATEWAY_TOKENand a SigV4 signature, only go to a trusted host (a real*.gateway.bedrock-agentcore.<region>.amazonaws.comhostname or the host inAGENTCORE_GATEWAY_URL, which is how a custom or private gateway hostname gets authorized); and credentials only ride https, with plain http refused unless the host is localhost, so neither token nor signature is readable in transit. Atools/callthat fails in-band, HTTP 200 withresult.isError, raises instead of looking like a search with no results. The signing region comes from the gateway hostname, falling back to the AWS config chain and erroring out rather than guessing a region the gateway would reject.api_key=""is passed down toBaseAWSLLM._sign_requestso the Bedrock RuntimeAWS_BEARER_TOKEN_BEDROCKnever leaks to an AgentCore endpoint.Also adds
agentcore/searchto the cost map (AWS bills the gateway directly) and an example proxy config wiring it intowebsearch_interceptionfor Bedrock Claude Code.Search
pre_calllogs the headers as they were before signing, so a configuredlogger_fnnever sees the SigV4 or bearerAuthorizationvalue; only the outbound request carries itLink to Devin session: https://app.devin.ai/sessions/902d85f7efbc4becb45d3340ad7c6782
Requested by: @yassin-berriai