Server-side built-in tools
What you will achieve
Section titled “What you will achieve”Enable the provider’s code-execution built-in, prompt 'Compute 12345 * 6789 using code', and confirm the answer 83810205 appears in the response — using one tool descriptor that works on any supporting provider.
When and why you need this
Section titled “When and why you need this”Some tools run entirely on the provider’s servers — the model writes code, the provider’s sandbox executes it, and the result flows back to the model automatically, all without your server being involved. Common built-ins:
- Code interpreter / code execution — the model writes Python, the sandbox runs it, the model sees stdout/result.
- Web search / grounding — the model queries a search index provider-side, results are injected into context.
- File search — the model searches a provider-managed vector store.
- Image generation — the model generates an image provider-side, result returned as a media part.
The challenge: each provider uses a completely different object shape to declare these tools. You cannot write one tool entry that works across providers.
Step by step
Section titled “Step by step”Step 1 — Enable code execution
Section titled “Step 1 — Enable code execution”import { complete } from '@combycode/llm-sdk';
const { text } = await complete({ model: process.env.LLM_MODEL!, apiKey: process.env.LLM_API_KEY, prompt: 'Compute 12345 * 6789 using code and tell me the result.', tools: [{ type: 'code_interpreter' }], maxTokens: 1024,});
console.log(text); // includes "83810205"No execute function — there is nothing to execute client-side. The { type: 'code_interpreter' } object is a BuiltinTool (from llm/types/tools.ts). The SDK maps it to the provider’s native shape before sending the request.
Step 2 — Mix client and server tools
Section titled “Step 2 — Mix client and server tools”You can pass both defineTool tools and built-in tools in the same tools array. The model will call whichever is appropriate for the task:
import { complete, defineTool } from '@combycode/llm-sdk';
const getDbRecord = defineTool({ name: 'get_db_record', description: 'Look up a record by ID from the database.', params: { id: 'string' }, execute: async ({ id }) => { return JSON.stringify({ id, name: 'Widget', price: 9.99 }); },});
const { text } = await complete({ model: process.env.LLM_MODEL!, apiKey: process.env.LLM_API_KEY, prompt: 'Look up record abc-123 and compute its price in euros (rate: 0.92).', tools: [ getDbRecord, // client-side: your server executes this { type: 'code_interpreter' }, // server-side: provider executes this ], maxTokens: 1024,});When the model calls get_db_record, the SDK invokes your execute function. When it uses code execution, nothing happens client-side — the provider handles it.
Step 3 — Use web search for grounded answers
Section titled “Step 3 — Use web search for grounded answers”import { complete } from '@combycode/llm-sdk';
const { text } = await complete({ model: process.env.LLM_MODEL!, apiKey: process.env.LLM_API_KEY, prompt: 'What are the latest developments in fusion energy research?', tools: [{ type: 'web_search' }], maxTokens: 1024,});
console.log(text); // answer grounded in current web resultsThe provider performs the search, injects results into context, and the model synthesises a grounded answer. For a dedicated web search scenario with citations see Web search.
Step 4 — Pass params to built-in tools
Section titled “Step 4 — Pass params to built-in tools”Some built-in tools accept configuration via an optional params field on the descriptor:
// OpenAI file search: point at a specific vector storetools: [{ type: 'file_search', params: { vector_store_ids: ['vs_abc123'] } }]
// OpenAI code interpreter: attach files for the sandbox to usetools: [{ type: 'code_interpreter', params: { file_ids: ['file-xyz'] } }]Provider-specific params that have no cross-provider equivalent are passed through in params. Check provider docs for the exact keys.
Your options
Section titled “Your options”Built-in tool types (BuiltinTool.type):
| Type | What it does | Provider support |
|---|---|---|
'code_interpreter' | Run Python in a sandboxed environment | OpenAI, Google, Anthropic (via computer_*) |
'web_search' | Search the web and inject grounded results | OpenAI, Google (native), Anthropic (Brave/Tavily via params) |
'file_search' | Search a provider-managed vector store | OpenAI (requires vector store setup) |
'image_generation' | Generate images provider-side | OpenAI (gpt-image-1) |
'mcp' | Model Context Protocol external server | OpenAI (remote MCP) |
Client-side vs server-side — the key distinction:
Client-side tools (defineTool) | Server-side built-ins (BuiltinTool) | |
|---|---|---|
| Who executes | Your execute function on your server | Provider’s infrastructure |
execute function required | Yes | No (no-op internally) |
| Network round-trip | Yes (your server) | No (provider-internal) |
| Customizable logic | Fully | Only via params configuration |
| Result visible in response | text + message history | Embedded in text |
| Works offline | Yes (local execution) | No |
Provider-native shape mapping:
ORXA maps { type: 'code_interpreter' } to:
- Anthropic:
{ type: 'computer_20250124', name: 'computer', display_width_px: ..., ... }(or bash/text-editor variants depending on the request context) - OpenAI:
{ type: 'code_interpreter' }(same shape) - Google:
{ codeExecution: {} }
This mapping is transparent — you never write the provider-native shape.
BuiltinTool type definition:
interface BuiltinTool { type: 'image_generation' | 'web_search' | 'code_interpreter' | 'file_search' | 'mcp'; params?: Record<string, unknown>;}params is a pass-through — values are merged into the provider’s native tool entry before sending.
Compare the SDKs
Section titled “Compare the SDKs”The structural difference: Anthropic’s built-in tool types are named computer_20250124, bash_20250124, and text_editor_20250124 with required width/height/action fields. OpenAI uses code_interpreter (no extra fields). Google uses { codeExecution: {} }. ORXA exposes one generic type name, maps it per-provider before sending, and handles the built-in tool’s result (code output, search snippets) without any client-side processing.
Gotchas and next steps
Section titled “Gotchas and next steps”Not every provider supports every built-in. file_search is OpenAI-only. image_generation requires specific model support. If you pass an unsupported built-in to a provider that does not implement it, the request will fail at the provider with an API error. Test your model/provider pair before deploying.
Built-in tools can add cost. Code interpreter and file search calls have per-call or per-query pricing on top of the base token cost. Check provider pricing pages for current rates.
Built-in results appear inline in text. Unlike client tools that produce a discrete tool result message, built-in tools’ outputs are often embedded in the model’s final text reply. There is no separate structured result field.
execute is never called for built-ins. Internally, ORXA wraps BuiltinTool as an AgentTool with a no-op execute: async () => ''. If the provider somehow routes the call back to the client (misconfigured provider), the no-op fires and the model receives an empty result.
Next steps:
- Web search — dedicated web search example with citation handling
- MCP tools — connect external tool servers via Model Context Protocol
- Single tool call — client-side tools with
defineTool