How the Instatic AI Agent Connects to Claude, OpenAI, and Ollama: A Provider-Agnostic Architecture
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. 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– Maps Claude's proprietary API schema to the internal OpenAI-Responses format.server/ai/drivers/openai.ts– Implements direct HTTP integration with the official OpenAI Responses API.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, 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. 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. 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. 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. 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 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:
// 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
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.
Running an MCP Tool from an External Agent
// 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 (client) ↔ 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.tshandles SSE streaming and error classification for all providers. - Tool execution remains provider-agnostic through
runToolLoop, which operates against the standardized response schema inserver/ai/drivers/http/toolArgs.ts. - The MCP bridge exposes editor functionality to external agents via
src/admin/pages/site/agent/useEditorMcpBridge.tsusing NDJSON streams. - Credentials are stored uniformly in
server/auth/tokens.tsand 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. 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 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, 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, 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →