How the Instatic AI Agent Integrates with Claude, OpenAI, and Ollama

The Instatic AI agent uses a provider-driver architecture with a unified MCP (Multi-Connector Protocol) endpoint to abstract differences between Claude, OpenAI, and Ollama, routing requests through credential-specific drivers and shared streaming adapters.

Instatic is an open-source content management system that ships with a server-side AI agent layer capable of communicating with multiple LLM providers through a clean separation of concerns. The integration mechanism follows a provider-agnostic core pattern that allows Claude (Anthropic), OpenAI, and Ollama to plug into the same workflow while preserving capabilities like tool-calling, vision input, and streaming responses.

Provider-Driver Architecture

The integration relies on a driver-based abstraction where each LLM provider implements a common interface defined in server/ai/drivers/types.ts. This approach decouples the agent's runtime logic from provider-specific wire protocols.

The AiProvider Interface

Every driver exports an AiProvider object that registers:

  • Provider ID (e.g., 'anthropic', 'ollama', 'openai')
  • Label for UI display
  • Supported auth modes (apiKey for Claude/OpenAI, baseUrl for Ollama)
  • Capability flags (toolCalling, visionInput, streaming, promptCache)

The driver also implements:

  • fetchModels(): Returns available models with capabilities
  • stream(): Handles the streaming request lifecycle
  • TurnTranslator: Converts provider-specific SSE streams into generic AiStreamEvent objects

Credential Management

The system supports multiple authentication strategies:

  • API Key mode: Used by Claude (anthropicDriver) and OpenAI, requiring secret tokens stored in the database
  • Base URL mode: Used by Ollama (ollamaDriver) for local deployments, requiring only the server endpoint (e.g., http://localhost:11434)

Claude (Anthropic) Integration

The Anthropic driver (server/ai/drivers/anthropic.ts) provides first-class support for Claude's native API.

Provider Registration

The anthropicDriver registers with id: 'anthropic' and advertises advanced capabilities including toolCalling, visionInput, promptCache, and streaming (lines 50-68). It requires an API key credential and validates this key before initiating requests.

Model Catalogue Discovery

Unlike static model lists, the driver implements fetchAnthropicModels which performs a live HTTP request to GET https://api.anthropic.com/v1/models (lines 26-67). This returns AiProviderModel objects with real-time capability information rather than hardcoded definitions.

Request Mapping and Streaming

The driver translates generic AiStreamRequest objects into Anthropic's specific JSON format:

  • buildRequestBody: Constructs the request payload with model, max_tokens, system blocks, and tool definitions (lines 34-41)
  • mapHistory: Converts the canonical message log into Anthropic's required alternating user/assistant block structure (lines 92-108)

Streaming requests delegate to runToolLoop(anthropicAdapter, req) (lines 73-86), which manages the tool-use loop while the AnthropicTurnTranslator parses SSE frames, assembles tool calls, and aggregates token usage into text, toolCall, and error events (lines 71-77, 78-110).

Ollama Integration

For local LLM deployments, the ollamaDriver (server/ai/drivers/ollama.ts) provides integration with Ollama's OpenAI-compatible API.

Local Deployment Support

Ollama uses authMode: 'baseUrl' rather than API keys, connecting to user-managed instances (lines 64-72). The driver implements fetchOllamaModels to query GET <baseUrl>/api/tags for available models, with a hard-coded fallback list when the catalogue request fails (lines 39-79, 84-90).

Dynamic Capability Resolution

Unlike Claude's static flags, Ollama models require runtime capability detection. The resolveCapabilities function calls fetchOllamaDeclaredCapabilities to read model-specific capabilities from Ollama's /api/show endpoint (lines 73-82), dynamically determining vision support based on the local model's configuration.

Shared OpenAI Adapter

Rather than implementing a custom translator, Ollama reuses the shared chat-completions adapter (server/ai/drivers/http/chatCompletions.ts). The driver supplies only the base URL and label to makeChatCompletionsAdapter (lines 33-36), leveraging the fact that Ollama's wire format is identical to OpenAI's. This adapter handles request building and SSE parsing, emitting standardized events through the TurnTranslator interface.

OpenAI Integration

OpenAI integration follows the same pattern as Ollama but uses direct API authentication. The system utilizes the makeChatCompletionsAdapter in server/ai/drivers/http/chatCompletions.ts to handle OpenAI's chat completions protocol.

While Ollama repurposes this adapter for local OpenAI-compatible endpoints, the OpenAI driver (following the same structure as anthropicDriver) would register with id: 'openai', require apiKey authentication, and delegate streaming to the shared chat-completions adapter with the base URL set to https://api.openai.com/v1. This design avoids code duplication while maintaining provider-specific credential handling and model catalogues.

The MCP Protocol Layer

The Multi-Connector Protocol (MCP) server (server/ai/mcp/server.ts) exposes the AI agent as an HTTP endpoint at /_instatic/mcp, enabling external clients like Claude Code, Claude Desktop, or custom editors to invoke the agent.

Endpoint Routing

The MCP server registers all available providers and forwards /mcp/... HTTP calls to the appropriate driver layer. When a client sends a request to POST /_instatic/mcp/ai/chat, the server:

  1. Validates the MCP token
  2. Routes to the specified providerId (anthropic, ollama, or openai)
  3. Invokes the driver's stream method
  4. Returns SSE events to the client

This architecture allows Claude Desktop to communicate with Instatic's agent using Claude models, while the same endpoint can serve requests to local Ollama instances without protocol changes.

Streaming Architecture and Tool Loops

All three integrations rely on the runToolLoop function (server/ai/drivers/http/toolLoop.ts) to manage multi-turn conversations involving tool use. The loop:

  1. Sends the current conversation state to the LLM via the driver's adapter
  2. Parses the streaming response for tool call requests
  3. Executes tools server-side
  4. Feeds results back into the conversation context
  5. Continues until completion

Each driver supplies a TurnTranslator implementation (or uses the shared OpenAI translator) to convert provider-specific SSE formats into the internal AiStreamEvent type, ensuring the UI receives uniform events regardless of the backend LLM.

Code Examples

Configuring Claude credentials:

// Stored credential configuration
{
  "providerId": "anthropic",
  "authMode": "apiKey",
  "apiKey": "sk-••••••••••••••••••••••••••••••••••"
}

Streaming from Claude:

import { anthropicDriver } from '@/server/ai/drivers/anthropic';
import { runToolLoop } from '@/server/ai/drivers/http/toolLoop';

const req = {
  credentials: { authMode: 'apiKey', apiKey: process.env.ANTHROPIC_API_KEY },
  modelId: 'claude-3-5-sonnet-20240620',
  systemPrompt: ['You are a helpful assistant.'],
  messages: [{ role: 'user', content: [{ kind: 'text', text: 'Explain Instatic.' }] }],
  tools: [],
  stream: true,
};

for await (const ev of anthropicDriver.stream(req)) {
  // ev.type === 'text' | 'toolCall' | 'error'
  console.log(ev);
}

Connecting to local Ollama:

import { ollamaDriver } from '@/server/ai/drivers/ollama';

const req = {
  credentials: { authMode: 'baseUrl', baseUrl: 'http://localhost:11434' },
  modelId: 'llama4',
  systemPrompt: ['You are a local LLM.'],
  messages: [{ role: 'user', content: [{ kind: 'text', text: 'What is Instatic?' }] }],
  tools: [],
};

for await (const ev of ollamaDriver.stream(req)) {
  console.log(ev);
}

MCP endpoint request:

POST /_instatic/mcp/ai/chat
Content-Type: application/json
Authorization: Bearer <mcp-token>

{
  "providerId": "anthropic",
  "modelId": "claude-3-opus-20240229",
  "messages": [{ "role": "user", "content": [{ "kind": "text", "text": "Summarize the docs." }] }]
}

Summary

  • Provider-driver pattern: Clean abstraction in server/ai/drivers/types.ts allows Claude, OpenAI, and Ollama to implement a common AiProvider interface
  • Credential flexibility: Supports API key authentication for cloud providers (Claude, OpenAI) and base URL configuration for local deployments (Ollama)
  • Shared adapters: Ollama leverages the OpenAI-compatible chat-completions adapter, while Claude uses a native Anthropic adapter with custom AnthropicTurnTranslator
  • MCP exposure: The server/ai/mcp/server.ts endpoint exposes all providers through a uniform HTTP API, enabling external tools like Claude Code to interact with the agent
  • Runtime validation: TypeBox schemas ensure type safety at every boundary, with no external SDKs required (runs on Bun)

Frequently Asked Questions

How does Instatic handle different authentication methods for Claude versus Ollama?

Claude requires an apiKey credential that gets validated in anthropicDriver.stream before calling the Anthropic API, while Ollama uses a baseUrl credential pointing to a local server (e.g., http://localhost:11434). The authMode field in the credential store determines which validation path the driver takes, allowing the same stream interface to handle both cloud API keys and local endpoint configuration.

Can Instatic use OpenAI models through the Ollama driver?

No, while Ollama uses the same OpenAI-compatible chat-completions adapter internally, the drivers remain distinct. The Ollama driver specifically queries Ollama's /api/tags and /api/show endpoints for model discovery and capability detection. To use OpenAI's API directly, the system would use a dedicated OpenAI driver (following the same pattern as the Anthropic implementation) that authenticates with OpenAI API keys and connects to api.openai.com.

What is the MCP endpoint and why is it important?

The MCP (Multi-Connector Protocol) endpoint at /_instatic/mcp exposes the AI agent as a standardized HTTP service that external clients can consume. This allows tools like Claude Code or Claude Desktop to invoke Instatic's agent capabilities—such as content generation or tool use—without needing to know which LLM provider (Claude, OpenAI, or Ollama) is actually handling the request behind the scenes.

How does the tool loop work across different providers?

The runToolLoop function in server/ai/drivers/http/toolLoop.ts manages conversation state and tool execution independently of the LLM provider. Each driver supplies an adapter (such as anthropicAdapter or makeChatCompletionsAdapter for Ollama/OpenAI) that handles provider-specific request formatting and response parsing. The loop continues calling the LLM and executing tools until the conversation completes, with each driver's TurnTranslator converting provider-specific streaming events into the generic AiStreamEvent format used by the UI.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →