Skip to content

Quick Start

Add Configure to an existing agent with the same shape as the packaged agent template: hosted auth, profile context, model tool calling, hosted UI surfaces, and background profile updates. Configure does not host the model; bring an Anthropic, OpenAI, OpenRouter, or equivalent provider key.

Stable install

Install configure from npm latest.

Prerequisites

bash
npm init -y   # if this folder does not already have a package.json
npm install configure
npx configure setup

Your environment needs Configure keys plus one model provider key:

bash
CONFIGURE_API_KEY=sk_...
CONFIGURE_PUBLISHABLE_KEY=pk_...
CONFIGURE_AGENT=your-agent-name

MODEL_PROVIDER=anthropic
MODEL_NAME=claude-sonnet-4-6
ANTHROPIC_API_KEY=sk-ant-...

Only the selected provider key is required. For OpenAI use OPENAI_API_KEY; for OpenRouter use OPENROUTER_API_KEY.

Canonical starter

Start from the template that ships with the package:

  • node_modules/configure/template/ — canonical packaged Atlas-derived shell

Treat it as the default shell. Do not invent a new layout unless the existing app already has a strong shell worth preserving. Keep hosted Configure surfaces inline where the template places them and change only .env, public/brand.css, public/brand.js, and backend prompt/tool behavior.

Agent-driven setup

If a coding agent is wiring this up for you, point it at configure.dev/skill.md and tell it to use configure.

API-only profile path

If you only need profile reads and writes, you do not have to put hosted auth or UI components in the chat hot path. Use Server-Side Users with userId / X-User-Id from your server to create an unlinked developer-scoped profile, then link it later with Configure.auth({ externalId }) when you need connected tools or cross-agent sharing.

1. Authenticate in the browser

Use the hosted auth surface from https://configure.dev/js/configure.js. Phone OTP and approval happen inside a Configure-owned iframe. Your app receives only an agent-scoped token.

html
<div id="configure-auth"></div>
<script src="https://configure.dev/js/configure.js"></script>
<script>
  Configure.auth({
    el: "#configure-auth",
    publishableKey: "pk_...",
    agent: "your-agent",
    agentName: "Your Agent",
    theme: "light"
  });

  document.addEventListener("configure:authenticated", (event) => {
    const { token, userId } = event.detail;
    // Send token + userId to your backend chat route.
  });
</script>

2. Load profile context server-side

typescript
import {
  ConfigureClient,
  CONFIGURE_TOOLS,
  UI_TOOLS,
  parseUIToolCall,
  toOpenAIFunctions,
} from 'configure';

const client = new ConfigureClient(process.env.CONFIGURE_API_KEY, {
  agent: process.env.CONFIGURE_AGENT,
});

const profile = await client.profile.get(token, userId, {
  sections: ['identity', 'summary', 'integrations'],
});

const context = profile.format({ guidelines: true });
const systemPrompt = `You are my agent.\n\n${context}`;
const tools = [...CONFIGURE_TOOLS, ...UI_TOOLS];

CONFIGURE_TOOLS covers profile reads/writes and connected tool search. UI_TOOLS adds show_ui_component so the model can ask your frontend to render hosted Configure surfaces.

3. Stream your model with tools

Anthropic accepts Configure's tool definitions directly:

typescript
const stream = anthropic.messages.stream({
  model: process.env.MODEL_NAME || 'claude-sonnet-4-6',
  max_tokens: 4096,
  system: systemPrompt,
  tools,
  messages,
});

OpenAI-compatible providers use the converter:

typescript
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
  },
  body: JSON.stringify({
    model: process.env.MODEL_NAME || 'gpt-4o',
    stream: true,
    messages: [{ role: 'system', content: systemPrompt }, ...messages],
    tools: toOpenAIFunctions(tools),
    tool_choice: 'auto',
  }),
});

4. Route tool calls

When the model calls a normal Configure tool, execute it with the SDK. When it calls show_ui_component, send a UI event to the browser instead of mounting raw <configure-*> tags.

typescript
async function executeTool(call, token, userId, sendEvent) {
  if (call.name === 'show_ui_component') {
    const ui = parseUIToolCall(call.name, call.input);
    sendEvent({ type: 'ui_component', ...ui });
    return ui;
  }

  switch (call.name) {
    case 'get_profile':
      return client.profile.get(token, userId);
    case 'search_emails':
      return client.tools.searchEmails(token, userId, call.input.query);
    case 'search_files':
      return client.tools.searchFiles(token, userId, call.input.query);
    case 'search_web':
      return client.tools.searchWeb(token, userId, call.input.query);
    case 'remember':
      return client.profile.remember(token, userId, call.input.fact || call.input.content);
    default:
      return { error: `Unhandled tool: ${call.name}` };
  }
}

The full template handles every Configure tool in node_modules/configure/template/server.mjs or the mirrored examples/quickstart/server.mjs.

5. Render hosted UI surfaces

The browser maps ui_component events to hosted methods from configure.js:

js
const mounts = {
  connection_list: Configure.connections,
  single_connector: Configure.singleConnector,
  memory_import: Configure.memoryImport,
  profile_editor: Configure.profileEditor,
  memory_card: Configure.memoryCard,
  confirmation: Configure.confirmation,
};

function renderConfigureSurface(event) {
  const mount = mounts[event.component];
  if (!mount) return;

  const row = document.createElement("div");
  row.className = "message-row assistant";

  const shell = document.createElement("div");
  shell.className = "surface-shell";

  const el = document.createElement("div");
  shell.appendChild(el);
  row.appendChild(shell);
  document.querySelector("#messages").appendChild(row);

  mount({
    el,
    publishableKey,
    agent,
    token,
    userId,
    ...event.props,
  });
}

This keeps the production path honest: your app calls Configure.*() and Configure owns the iframe-rendered browser surfaces. In the packaged template, these surfaces are appended inline into the main thread instead of a detached side area.

6. Save profile updates after each turn

After the assistant responds, fire-and-forget ingestion so future sessions improve without blocking the user:

typescript
client.profile.ingest(
  token,
  userId,
  [
    { role: 'user', content: userMessage },
    { role: 'assistant', content: assistantResponse },
  ],
  { sync: false },
).catch(() => {});

Reference starter

Use the packaged template (node_modules/configure/template/) when you want the whole compact loop:

  • one Express server
  • one HTML file
  • hosted auth with Configure.auth()
  • SSE streaming
  • Anthropic, OpenAI, and OpenRouter provider paths
  • CONFIGURE_TOOLS + UI_TOOLS
  • hosted Configure surfaces
  • background profile ingestion

Next:

Identity layer for AI agents