Layer 1 — Provider-native tool APIs (OpenAI, Anthropic, Google, etc.)
**The pattern:** Declare tools as JSON-schema descriptors. Pass them with the model call. Parse `tool_calls` from the response. Execute. Return tool results in the next turn. Loop until the model returns a final assistant message without tool calls.
**OpenAI's flavor:** Per OpenAI's function calling docs at platform.openai.com, tools are declared in `tools=[{type:'function', function:{name, description, parameters}}]`. Response has `message.tool_calls[]` with `id`, `function.name`, `function.arguments` (JSON string). Tool results go back as messages with `role:'tool'` and matching `tool_call_id`.
**Anthropic's flavor:** Per Anthropic's tool use guide at docs.anthropic.com, tools are declared in `tools=[{name, description, input_schema}]`. Response content array contains `tool_use` blocks with `id`, `name`, `input` (already-parsed object). Tool results go back as `tool_result` content blocks with matching `tool_use_id`.
**Google's flavor:** Per Google's Gemini function calling docs at ai.google.dev, tools are declared as `tools=[{functionDeclarations:[...]}]`. Response has `parts[].functionCall` with `name`, `args`. Tool results go back as `parts[].functionResponse`.
**The reality:** Schemas differ. Argument shapes differ. Naming conventions differ. The mental model is identical; the wire-level code is provider-specific. This is where the next two layers (framework abstractions + MCP) become important.