# How the Instatic AI Agent Connects to Claude, OpenAI, and Ollama: A Provider-Agnostic Architecture

> Discover how Instatic AI seamlessly connects to Claude, OpenAI, and Ollama. Learn about its provider-agnostic architecture and unified interface for universal model access.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: architecture
- Published: 2026-07-30

---

**The Instatic AI agent connects to Claude, OpenAI, and Ollama through a unified `ProviderAdapter` interface that translates each provider's native API into a standardized OpenAI-Responses wire format, enabling a single tool-execution loop to work across all models.**

The CoreBunch/Instatic repository implements a provider-agnostic AI agent capable of integrating with multiple large language model providers without requiring changes to the core runtime. By abstracting provider-specific HTTP endpoints, authentication schemes, and response schemas behind a common interface, Instatic allows the same visual editing tools and MCP clients to interface seamlessly with Anthropic's Claude, OpenAI's GPT models, and local Ollama instances.

## Provider Adapter Interface and Driver Architecture

At the heart of Instatic's multi-provider support lies the `ProviderAdapter` interface defined in [`server/ai/drivers/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/types.ts). This contract requires each driver to export a `createAdapter(config)` function that returns a standardized adapter object, allowing the runtime to treat Claude, OpenAI, and Ollama as interchangeable backends.

The repository includes three primary driver implementations:

- **[`server/ai/drivers/anthropic.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/anthropic.ts)** – Maps Claude's proprietary API schema to the internal OpenAI-Responses format.
- **[`server/ai/drivers/openai.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/openai.ts)** – Implements direct HTTP integration with the official OpenAI Responses API.
- **[`server/ai/drivers/ollama.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/ollama.ts)** – Wraps local Ollama instances using the OpenAI-compatible chat completions endpoint.

Each driver receives a configuration object containing `apiKey` and `baseUrl` parameters sourced from [`server/auth/tokens.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/auth/tokens.ts), enabling dynamic credential injection at runtime.

## The Shared HTTP Chat Completions Layer

Rather than implementing separate HTTP clients for each provider, all drivers delegate to the shared helper in [`server/ai/drivers/http/chatCompletions.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/chatCompletions.ts). This module centralizes request construction, Server-Sent Events (SSE) streaming, and error classification for OpenAI-compatible chat completion endpoints.

The `makeChatCompletionsAdapter` function provided by this layer accepts provider-specific configuration and returns a streaming interface that emits normalized `MessageStreamEvent` objects. Whether the underlying model is Claude 3.5 Sonnet or GPT-4, the UI receives a consistent event stream with standardized `role: 'assistant'|'tool'` payloads.

## Unified Tool Execution with runToolLoop

The agent's intelligence operates through the `runToolLoop` runtime, which works exclusively against the **OpenAI-Responses** schema defined in [`server/ai/drivers/http/toolArgs.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolArgs.ts). This design choice ensures that tool-calling logic remains decoupled from provider-specific SDKs.

When a model returns a tool call—such as `insertHtml` or `setTokens`—the `executeAgentTool` function in the executor validates the schema against the live editor store and returns a uniform `AiToolOutput` shape (`{ ok: true, result }` or `{ ok: false, error }`). Because every provider adapter normalizes responses to this schema, the tool execution pipeline requires no branching logic to handle Claude's XML-based tool format versus OpenAI's JSON-based function calling.

## MCP Bridge for External Agent Integration

Instatic exposes its editor capabilities to external agents through the **Model-Client-Protocol (MCP)** bridge implemented in [`src/admin/pages/site/agent/useEditorMcpBridge.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/useEditorMcpBridge.ts). When an admin editor mounts, this hook opens a long-lived NDJSON stream at `/admin/api/ai/editor-bridge`.

External MCP clients—including Claude Code, Codex, or custom agents—receive `toolRequest` events through this stream, execute the same `executeAgentTool` pipeline that the UI uses, and post results back via `postToolResult`. This architecture effectively transforms the visual editor into a remote agent-enabled session, allowing external AI systems to manipulate Instatic content through the standardized tool interface.

## Credential Storage and Configuration

Provider credentials are managed through the admin UI at `/admin/ai/providers` and persisted via [`server/auth/tokens.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/auth/tokens.ts). The credential store maintains a uniform shape (`{ apiKey, baseUrl, ... }`) across all providers, meaning the Anthropic, OpenAI, and Ollama drivers all consume the same configuration structure.

At server startup, [`server/ai/drivers/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/index.ts) loads all registered drivers and injects the appropriate credential records into each `createAdapter` constructor. The `pricing` module in `server/ai/pricing/*` further enriches raw model lists with pricing, context window limits, and capability flags, presenting a unified catalog to the UI's model picker regardless of provider origin.

## Implementing a New OpenAI-Compatible Provider

Because every driver ultimately speaks the **OpenAI-Responses** protocol, adding support for a new provider requires only a minimal wrapper. The Ollama driver demonstrates this pattern:

```typescript
// server/ai/drivers/ollama.ts
import { makeChatCompletionsAdapter } from './http/chatCompletions';
import type { ProviderConfig } from './types';

export function createAdapter(cfg: ProviderConfig) {
  return makeChatCompletionsAdapter({
    baseUrl: cfg.baseUrl,          // e.g. http://localhost:11434
    apiKey: cfg.apiKey,            // typically empty for local Ollama
    // Ollama uses the same `/v1/chat/completions` endpoint
  });
}

```

No changes are required to `runToolLoop`, the MCP bridge, or UI components—the new provider immediately inherits full tool execution and streaming capabilities.

## Code Examples: Interacting with Providers

### Sending a Chat Request

```typescript
import { getProviderAdapter } from '@core/ai/drivers';
import { ChatMessage } from '@core/ai/runtime/types';

async function chatWithProvider(providerId: string, messages: ChatMessage[]) {
  const adapter = await getProviderAdapter(providerId); // resolves to Claude / OpenAI / Ollama
  const stream = await adapter.chatCompletions({
    model: 'claude-3-5-sonnet-20241022', // or any OpenAI model ID
    messages,
    temperature: 0.7,
  });

  for await (const event of stream) {
    console.log(event); // unified MessageStreamEvent shape
  }
}

```

*Implementation reference*: [`server/ai/drivers/http/chatCompletions.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/chatCompletions.ts).

### Running an MCP Tool from an External Agent

```typescript
// Remote client (e.g. Claude Code) – pseudo-code
const bridge = new EventSource('/admin/api/ai/editor-bridge');
bridge.onmessage = async e => {
  const { type, requestId, toolName, input } = JSON.parse(e.data);
  if (type === 'toolRequest') {
    const result = await runTool(toolName, input); // same executor used locally
    await fetch(`/admin/api/ai/editor-bridge/${requestId}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(result),
    });
  }
};

```

*Local counterpart*: [`src/admin/pages/site/agent/useEditorMcpBridge.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/useEditorMcpBridge.ts) (client) ↔ [`src/admin/pages/site/agent/executor.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/executor.ts) (server).

## Summary

- **Provider adapters** in `server/ai/drivers/` normalize Claude, OpenAI, and Ollama APIs to the OpenAI-Responses wire format.
- The shared **chat completions layer** at [`server/ai/drivers/http/chatCompletions.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/chatCompletions.ts) handles SSE streaming and error classification for all providers.
- **Tool execution** remains provider-agnostic through `runToolLoop`, which operates against the standardized response schema in [`server/ai/drivers/http/toolArgs.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolArgs.ts).
- The **MCP bridge** exposes editor functionality to external agents via [`src/admin/pages/site/agent/useEditorMcpBridge.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/useEditorMcpBridge.ts) using NDJSON streams.
- **Credentials** are stored uniformly in [`server/auth/tokens.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/auth/tokens.ts) and injected into driver constructors at runtime.
- Adding new OpenAI-compatible services requires only a minimal adapter wrapper without touching core execution logic.

## Frequently Asked Questions

### How does Instatic handle different authentication methods for each provider?

Instatic normalizes authentication through the credential store in [`server/auth/tokens.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/auth/tokens.ts). All providers receive the same configuration shape (`{ apiKey, baseUrl, ... }`), with the Anthropic and OpenAI drivers using the API key for bearer token authentication while the Ollama driver typically receives an empty key for local instances. Each driver implementation in `server/ai/drivers/` handles the specific header formatting required by its respective API.

### Can I use a custom OpenAI-compatible endpoint with Instatic?

Yes. Because the architecture relies on the OpenAI-Responses wire format, any service implementing the `/v1/chat/completions` endpoint—including custom proxies or alternative model providers—can integrate by creating a minimal driver in `server/ai/drivers/` that wraps `makeChatCompletionsAdapter` from [`server/ai/drivers/http/chatCompletions.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/chatCompletions.ts) and points to your custom `baseUrl`.

### What is the MCP bridge and how does it interact with external agents?

The MCP (Model-Client-Protocol) bridge, implemented in [`src/admin/pages/site/agent/useEditorMcpBridge.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/useEditorMcpBridge.ts), opens an NDJSON stream at `/admin/api/ai/editor-bridge` that allows external agents like Claude Code or Codex to execute Instatic's editor tools remotely. When the bridge receives a `toolRequest` event, it runs the same `executeAgentTool` pipeline used by the local UI and returns results through a POST endpoint, effectively exposing the visual editor's capabilities as an MCP-compatible service.

### How does the tool execution loop remain provider-agnostic?

The `runToolLoop` runtime interacts exclusively with the **OpenAI-Responses** schema defined in [`server/ai/drivers/http/toolArgs.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolArgs.ts), rather than provider-specific SDKs. Because every adapter in `server/ai/drivers/`—whether for Anthropic, OpenAI, or Ollama—translates native responses into this standardized format, the tool execution logic in `executeAgentTool` processes tool calls identically regardless of which model generated the request.