Skip to main content

Architecture

The pieces​

PieceWhat it doesWhere it runs
@frankieai/reactThe chat widget: transport, a state reducer and the UIYour app's bundle
Data plane (/v1/*, /mcp/*)Sign-in handshake, the agent, chat historyFrankie cloud
Control plane + dashboardWorkspaces, agents, actions, settingsFrankie cloud

A single turn​

  1. The widget posts to /v1/chat and reads an event stream.
  2. The server authenticates, applies limits, loads the workspace's actions, and runs the agent loop in-process.
  3. The model layer sends the conversation to the configured model. The default is Kimi K3 on Fireworks; about 30 providers (Anthropic, OpenAI, Google, Groq, OpenRouter and more) can be swapped in by config.

There is no subprocess and no translation layer: the only thing between the browser and the model provider is Frankie's own event mapping.

Agent eventWhat the widget receives
Text being writtentext_delta
An action startstool_start
An action finishestool_end
An action is blocked by policytool_denied
The turn endsturn_end, with usage and cost

Actions​

Frankie turns two kinds of source into actions, merged into one list the agent can't tell apart:

SourceAction names
MCP serversmcp__<server>__<tool>
OpenAPI documentsapi__<name>__<operationId>

Two choices worth knowing:

  • Read-only by default. An OpenAPI source builds actions for GET only until you widen it. An agent pointed at a whole API can't delete anything on day one.
  • The request body is nested under body, separate from path and query parameters, so a body field and a query parameter with the same name can't collide.

Policy is applied twice: when actions are built (a forbidden action never reaches the model), and again when each call runs (which catches anything added mid-turn, and produces the tool_denied event).

Connections are made per turn, not pooled: no connection outlives the request that authorised it, and every tool call runs on a connection made with the current user's credentials. To keep setup fast, what a source offers is cached briefly between turns: a spec URL is re-read at least every 30 seconds (with a conditional request, so an unchanged spec costs a 304), and an MCP server's tool list is re-listed at least every minute. A cached tool list is only reused for the same user presenting the same credentials, never shared between users. Changing a connection, or testing or refreshing it in the dashboard, drops its cached copy at once. A source that is down is logged and skipped, so one broken server doesn't take the assistant with it; a spec URL that fails to load falls back to a copy read in the last 10 minutes. Set FRANKIE_TOOL_CACHE=off on the server to read every source on every message instead.

Sessions​

A session owns its transcript. It's handed to the agent at the start of each turn and written back when the turn settles, so:

  • the agent loop keeps no state between turns,
  • a reply keeps running if the user closes the tab, and is there when they come back,
  • and every transcript is available for review in Conversations.