Skip to content

Conversation

@cpsievert
Copy link
Collaborator

@cpsievert cpsievert commented Dec 30, 2025

Add provider-agnostic tool_web_search() and tool_web_fetch() functions that enable built-in web search and URL fetching capabilities across supported providers (pairs with tidyverse/ellmer#829).

Supported providers

Tool OpenAI Claude Google
tool_web_search()
tool_web_fetch() ✅ (beta)

For providers without native fetch support (like OpenAI), use the MCP Fetch server via register_mcp_tools_stdio_async(). (turns out this will require some fixes to the MCP tool logic, so this will come in a follow up PR)

Key changes

  • New functions: tool_web_search() and tool_web_fetch() provide a unified interface that translates to each provider's native format
  • New content types: ContentWebSearchRequest, ContentWebSearchResults, ContentWebFetchRequest, ContentWebFetchResults capture web tool interactions in conversation history
  • Provider response handling: Anthropic and OpenAI providers now correctly parse server tool use responses
  • Streaming support: Citation deltas are accumulated during Claude streaming

Example usage

from chatlas import ChatOpenAI, tool_web_search

chat = ChatOpenAI()
chat.register_tool(tool_web_search())
chat.chat("What are the top news stories today?")

Test plan

  • Unit tests for tool configuration and provider definitions
  • Type checking passes
  • Integration tests (deferred to separate VCR PR)

TODO

  • Update tool calling documentation to include built-in tools

@cpsievert cpsievert force-pushed the feat/builtin-search branch 13 times, most recently from 16639ff to 96f4bd2 Compare December 30, 2025 22:33
@cpsievert cpsievert force-pushed the feat/builtin-search branch 2 times, most recently from 29fb577 to 405ec43 Compare January 2, 2026 16:50
cpsievert and others added 14 commits January 2, 2026 14:13
Add built-in web search and URL fetch tools that work across providers:

- tool_web_search(): Works with OpenAI, Anthropic, and Google
- tool_web_fetch(): Works with Anthropic and Google

Each provider automatically translates the tool configuration to its
specific API format. Supports options like allowed_domains, blocked_domains,
user_location, and max_uses where applicable.

This is equivalent to tidyverse/ellmer#829 but with a cleaner provider-agnostic
API (one function instead of separate functions per provider).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GoogleSearch and UrlContext must be passed as keyword arguments
to GoogleTool(), not directly to config.tools.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add _warn_unsupported() helper to reduce warning code duplication
- Update examples to use generic domains (wikipedia.org, python.org)
  instead of Posit/Tidyverse-specific ones

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ContentWebSearchRequest, ContentWebSearchResults, ContentWebFetchRequest,
and ContentWebFetchResults to represent built-in web tool interactions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Anthropic: Handle server_tool_use, web_search_tool_result, and
  web_fetch_tool_result content types
- OpenAI: Handle web_search_call output type

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Handle citations_delta chunk type during streaming to accumulate
citations on content blocks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update tool_web_fetch docstring to explain that Claude requires
the anthropic-beta: web-fetch-2025-09-10 header.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document the new tool_web_search() and tool_web_fetch() functions
and associated content types.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add new "Built-in tools" section with tool_web_search and tool_web_fetch
- Export new content types from chatlas.types module

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Quick start section with MCP Fetch server example
- Add Finding MCP servers section with awesome-mcp-servers link
- Reorder to show Stdio before HTTP (more common for 3rd party)
- Move "Building your own server" section later
- Rename "Motivating example" to "Advanced example: Code execution"
- Add callout linking to built-in tool_web_fetch/tool_web_search alternatives
- Update tools.qmd link to point to quick start section

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The MCP Fetch server is run via `uvx mcp-server-fetch`, not npx.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
OpenAI's function calling API doesn't support the "format": "uri"
JSON Schema format used by the MCP Fetch server. Switch examples
to use ChatAnthropic which has better MCP compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
OpenAI's function calling API doesn't support JSON Schema format hints
like "uri" or "date-time". Strip these from MCP tool schemas so they
work across all providers.

Also reverts docs to use ChatOpenAI for MCP Fetch examples since the
fix enables cross-provider compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cpsievert and others added 3 commits January 2, 2026 14:14
OpenAI requires all properties to be listed in the required array.
Update sanitize_schema to add all property keys to required.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
OpenAI requires all properties in the required array with strict mode.
Optional parameters are indicated using anyOf with null type, matching
how pydantic_function_tool generates schemas.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MCP tools use standard JSON Schema conventions where optional params
are simply not in the required array. Setting strict=False for OpenAI
allows this to work properly, so the LLM won't be forced to provide
values for all parameters.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@cpsievert cpsievert force-pushed the feat/builtin-search branch from 405ec43 to 9b39472 Compare January 2, 2026 20:14
cpsievert and others added 4 commits January 2, 2026 15:35
Port integration tests from tidyverse/ellmer#829:
- Add assert_tool_web_fetch and assert_tool_web_search helpers to conftest.py
- Add test_anthropic_web_fetch and test_anthropic_web_search
- Add test_google_web_fetch and test_google_web_search
- Add test_openai_web_search (OpenAI doesn't support web_fetch)

Fix Anthropic provider to handle web search/fetch content in multi-turn
conversations by explicitly storing only API-input-accepted fields
(the API response includes output-only fields like citations, text, url
that aren't accepted when replaying turns).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ContentWebSearchRequest → ContentToolRequestSearch
- ContentWebSearchResults → ContentToolResponseSearch
- ContentWebFetchRequest → ContentToolRequestFetch
- ContentWebFetchResults → ContentToolResponseFetch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@cpsievert cpsievert force-pushed the feat/builtin-search branch from efda95c to 94b6723 Compare January 2, 2026 23:26
The MCP tool compatibility improvements (strict=False, sanitize_schema
with format removal, doc restructure) have been moved to a separate PR
(feat/mcp-tool-compat branch).

This PR now focuses solely on the built-in web search/fetch tools:
- tool_web_search() and tool_web_fetch() functions
- Content types for web search/fetch requests and results
- Provider integration tests

The callout tip about built-in tools is kept in mcp-tools.qmd to help
users discover the simpler alternative to MCP Fetch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@cpsievert cpsievert force-pushed the feat/builtin-search branch from 58b1619 to 3fe8d30 Compare January 2, 2026 23:40
@cpsievert cpsievert marked this pull request as ready for review January 3, 2026 00:17
@cpsievert cpsievert requested a review from Copilot January 3, 2026 00:17

This comment was marked as resolved.

@cpsievert cpsievert merged commit a7d47e4 into main Jan 3, 2026
9 checks passed
@cpsievert cpsievert deleted the feat/builtin-search branch January 3, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants