Instatic MCP Connector Architecture: Secure AI Integration for External Tools

Instatic implements the Model Context Protocol (MCP) as a server-side subsystem that bridges external AI clients to the CMS through TypeBox schemas, a live NDJSON editor bridge, and capability-filtered endpoints.

The CoreBunch/Instatic repository provides a dedicated MCP connector architecture that enables external AI assistants to safely manipulate content management workflows. This server-side implementation exposes a capability-aware façade while ensuring plaintext credentials never leak to client applications. The architecture centers on three pillars: strict wire-format schemas, a live workspace bridge, and scoped security boundaries.

MCP Connector Schemas

The MCP Connector Schemas define the wire format for all MCP interactions using TypeBox definitions in src/core/ai/mcpConnectorSchemas.ts. These schemas govern access grants, OAuth flows, and connection metadata to guarantee that no plaintext credentials ever leak to the client side.

When creating access tokens, the CreateMcpAccessTokenResultSchema enforces a single-use plaintext disclosure pattern. The server returns the raw token exactly once during creation, then persists only hashed values for subsequent validation. This ensures that database projections or API responses never expose sensitive credentials.

Live Editor Bridge

Because most editor-side tools (e.g., page-tree mutations, content CRUD) lack pure-server implementations, Instatic routes MCP tool calls through the Live Editor Bridge. This component creates a long-lived NDJSON stream that connects external AI clients to the user’s active workspace in the admin UI.

The bridge implementation lives in server/ai/mcp/editorBridge.ts. Each bridge is created per user + workspace scope (either site or content) and stored in a registry keyed by userId and scope. The registry exposes guard functions hasEditorBridge and getEditorBridgeForUser to enforce that a connection cannot reach another user’s workspace or cross scope boundaries.

MCP Server Endpoints

The public MCP entry point at /_instatic/mcp (handled in server/ai/mcp/server.ts) processes three families of requests:

  1. Capability-filtered tool catalog – Clients fetch available tools (e.g., publishTool, documentTools) filtered according to the connection’s declared capabilities.
  2. Headless reads – Read-only operations (listing pages, content entries) execute directly against the database without touching the editor store.
  3. Live workspace relay – Mutating operations (insert node, update content, publish) forward to the appropriate AiBrowserBridge instance. The server emits toolRequest events on the NDJSON stream; the admin UI executes the tool and POSTs results back to /admin/api/ai/tool-result.

Security and Lifecycle

The architecture enforces strict security boundaries through multiple mechanisms:

OAuth and Bearer Tokens – Two credential lifecycles are supported: a one-time bearer token for CLI/automation clients, and an OAuth authorization-code flow with PKCE for hosted MCP clients. The OAuth handler in server/ai/mcp/oauth/handler.ts manages code exchange and hashing. All tokens are stored only as hashes, never exposed in list projections.

Scope-Bound Access – The bridge registry ensures an MCP connector interacts only with workspaces owned by the authenticated user. A connection cannot reach another user’s workspace or access the site editor when the scope is content, and vice-versa.

Lease and Heartbeat – Each bridge stream maintains a 2-minute lease (STREAM_LEASE_MS) with a 25-second heartbeat to prevent proxy idle timeouts. If the lease expires or the client aborts, the bridge tears down and the registry clears the entry.

Key Implementation Files

Practical Integration Examples

Creating a Bearer Token

import { apiRequest } from '@core/http'
import { CreateMcpAccessTokenBodySchema, CreateMcpAccessTokenResultSchema } from '@core/ai'

async function createToken(label: string, caps: string[]) {
  const body = { label, capabilities: caps }
  const result = await apiRequest('/_instatic/mcp/create-token', {
    method: 'POST',
    schema: CreateMcpAccessTokenResultSchema,
    json: body,
  })
  // `result.accessToken` is the only time the plaintext token appears.
  console.log('Save this token securely – it will not be shown again')
  return result
}

Calling a Live Tool via the Bridge

import { fetch } from 'node-fetch'

async function runPublishTool(mcpUrl: string, token: string) {
  // 1️⃣ Fetch the capability-filtered tool catalog
  const catalog = await fetch(`${mcpUrl}/tools`, {
    headers: { Authorization: `Bearer ${token}` },
  }).then(r => r.json())

  // 2️⃣ Open the NDJSON stream for tool requests/responses
  const stream = await fetch(`${mcpUrl}/stream`, {
    headers: { Authorization: `Bearer ${token}` },
  })

  // 3️⃣ Send a tool request (publishSite) down the stream
  const encoder = new TextEncoder()
  const writer = stream.body?.getWriter()
  writer?.write(encoder.encode(JSON.stringify({
    type: 'toolRequest',
    tool: 'site_publish',
    args: { /* publish options */ },
  }) + '\n'))

  // 4️⃣ Read the result event from the stream
  const reader = stream.body?.getReader()
  const { value } = await reader?.read()
  const result = JSON.parse(new TextDecoder().decode(value))
  console.log('Publish result:', result)
}

Server-Side Bridge Creation

// Inside server/ai/mcp/server.ts
import { createEditorBridgeStream } from './editorBridge'

export function handleMcpStream(req) {
  const { userId, scope } = req.auth // validated bearer token
  return createEditorBridgeStream(userId, scope, req.signal)
}

Summary

  • Instatic MCP connector architecture exposes a secure server-side façade for external AI tools using the Model Context Protocol.
  • TypeBox schemas in src/core/ai/mcpConnectorSchemas.ts enforce wire-format safety and prevent credential leakage.
  • Live Editor Bridge routes mutating operations through NDJSON streams to the user’s active admin UI workspace.
  • Scoped access controls ensure bridges are bound to specific users and workspace scopes (site or content).
  • Hashed token storage and PKCE OAuth flows protect against credential exposure, with tokens displayed only once at creation.
  • Automatic lease expiration (STREAM_LEASE_MS) and heartbeats manage connection lifecycle and resource cleanup.

Frequently Asked Questions

What is the Model Context Protocol in Instatic?

The Model Context Protocol (MCP) is a server-side subsystem in CoreBunch/Instatic that standardizes how external AI clients interact with the CMS. It provides capability-filtered tool catalogs, headless read operations, and a live relay for mutating operations that require the admin editor.

How does the live editor bridge handle mutations?

Mutations like inserting nodes or publishing content have no pure-server implementation, so the bridge in server/ai/mcp/editorBridge.ts relays them via an NDJSON stream. The server sends toolRequest events to the user’s active browser session, which executes the operation and returns results to /admin/api/ai/tool-result.

What security measures protect MCP credentials?

Credentials are protected through TypeBox schema validation, hashed storage (never plaintext), and single-use token disclosure. OAuth flows use PKCE, and bearer tokens are generated via /_instatic/mcp/create-token with the plaintext shown only once during the creation response.

How long do MCP bridge connections remain active?

Each bridge maintains a 2-minute lease (STREAM_LEASE_MS) refreshed by a 25-second heartbeat interval. If the client disconnects or the lease expires without renewal, the server automatically tears down the bridge and removes the registry entry to prevent stale connections.

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 →