Documentation
SDK

SDK: FlapjackClient

Complete method reference for FlapjackClient — agents, threads, streaming, knowledge, MCP servers, integrations, plans, runners, skills, analytics, and marketplace.

FlapjackClient is the main class for interacting with the Flapjack API programmatically.

import { FlapjackClient } from '@flapjack/sdk';

const client = new FlapjackClient({
  apiKey: process.env.FLAPJACK_API_KEY!,
});

Agents

listAgents(orgId?)

List all agents in your organization.

const agents: Agent[] = await client.listAgents();

Returns: Promise<Agent[]>

type Agent = {
  id: string;
  org_id: string;
  name: string;
  description: string | null;
  /** Populated by getAgent() but not by listAgents(). */
  stable_preamble?: string;
  default_model: string;
  /** Populated by getAgent() but not by listAgents(). */
  temperature?: number;
  created_at: string;
  published_chats?: { slug: string; is_active: boolean }[];
  computer?: ComputerConfig | { enabled: false };
  tool_profiles?: ToolProfiles | null;
  default_profile?: string | null;
};

getAgent(agentId)

Get a specific agent by ID.

const agent: Agent = await client.getAgent('agent-id');
NameTypeDescription
agentIdstringThe agent's UUID

Returns: Promise<Agent> — throws if not found.


createAgent(options)

Create a new agent.

const agent = await client.createAgent({
  name: 'Support Bot',
  stablePreamble: 'You are a helpful support agent.',
  defaultModel: 'gpt-5.4',
});
NameTypeDescription
options.namestringDisplay name (required)
options.descriptionstring?What this agent does
options.stablePreamblestring?System prompt
options.defaultModelstring?LLM model ID (defaults to gpt-5.4)
options.orgIdstring?Target org (multi-org only)

Returns: Promise<Agent>


updateAgent(agentId, options)

Update an existing agent. Only changed fields are required.

await client.updateAgent('agent-id', { name: 'Updated Bot' });

Returns: Promise<Agent>


deleteAgent(agentId)

Delete an agent. Threads are not automatically deleted.

await client.deleteAgent('agent-id');

Returns: Promise<void>


Agent Feature Configs

Get or update per-agent feature settings. All config methods follow the same pattern:

// Read config
const config = await client.getWebConfig('agent-id');

// Update config
await client.updateWebConfig('agent-id', { enabled: true, searchEnabled: true });
MethodConfig TypeKey Fields
getWebConfig(agentId) / updateWebConfig(agentId, config)WebConfigenabled, searchEnabled, researchEnabled, readEnabled, crawlEnabled
getMemoryConfig(agentId) / updateMemoryConfig(agentId, config)MemoryConfigenabled, autoCollect, autoInject, injectCount, maxMemories, agentScope, threadScope, resourceScope
getPlanConfig(agentId) / updatePlanConfig(agentId, config)PlanConfigenabled, remindOnTurnEnd, dependenciesEnabled
getComputerConfig(agentId) / updateComputerConfig(agentId, config)ComputerConfigenabled, mode (ephemeral/persistent/custom), scope, sizeClass
getDelegatedConfig(agentId) / updateDelegatedConfig(agentId, config)DelegatedConfig / UpdateDelegatedConfigGetter: url, timeout_ms. Updater: url, secret (required), timeoutMs?
getCredentialConfig(agentId) / updateCredentialConfig(agentId, config)CredentialConfig / UpdateCredentialConfigGetter: enabled, resolver_url, timeout_ms. Updater: enabled, resolverUrl?, timeoutMs?

All getters return a Promise of the config type. All updaters use PUT and return the saved config.


Agent MCP Bindings

Attach or detach MCP servers from an agent.

listAgentMcps(agentId)

const mcps: AgentMcp[] = await client.listAgentMcps('agent-id');

Returns: Promise<AgentMcp[]>


attachMcp(agentId, mcpServerId, allowedTools?)

await client.attachMcp('agent-id', 'mcp-server-id', ['tool_a', 'tool_b']);
NameTypeDescription
agentIdstringThe agent's UUID
mcpServerIdstringMCP server UUID
allowedToolsstring[]?Optional tool filter

Returns: Promise<AgentMcp>


detachMcp(agentId, mcpServerId)

await client.detachMcp('agent-id', 'mcp-server-id');

Returns: Promise<void>


Agent Integration Bindings

Attach or detach database integrations from an agent.

listAgentIntegrations(agentId)

const integrations: AgentIntegration[] = await client.listAgentIntegrations('agent-id');

Returns: Promise<AgentIntegration[]>


attachIntegration(agentId, integrationId, allowedTables?)

await client.attachIntegration('agent-id', 'integration-id', ['users', 'orders']);

Returns: Promise<AgentIntegration>


detachIntegration(agentId, integrationId)

await client.detachIntegration('agent-id', 'integration-id');

Returns: Promise<void>


Agent Memories

listMemories(agentId, options?)

List stored memories for an agent, optionally filtered by scope.

const result: MemoryListResult = await client.listMemories('agent-id', {
  scope: 'agent',
  limit: 20,
});
// result.memories, result.total
NameTypeDescription
options.scopestring?agent, thread, or resource
options.resourceTypestring?Filter by resource type
options.resourceIdstring?Filter by resource ID
options.limitnumber?Max results
options.offsetnumber?Pagination offset

Returns: Promise<MemoryListResult>{ memories: Memory[], total: number }


deleteMemories(agentId, filter)

Delete memories matching a filter.

await client.deleteMemories('agent-id', { scope: 'thread', scopeId: 'thread-id' });

Returns: Promise<void>


Threads

createThread(agentIdOrOptions)

Create a new conversation thread. Accepts a simple agent ID string or an options object.

// Simple
const thread = await client.createThread('agent-id');

// With options
const thread = await client.createThread({
  agentId: 'agent-id',
  title: 'Support Chat',
  kind: 'multiplayer',
  resourceBindings: [{ type: 'customer', id: 'cust-123' }],
});
NameTypeDescription
agentIdstringThe agent's UUID (required)
titlestring?Thread title
kind'single' | 'multiplayer'?Thread mode
resourceBindingsResourceBinding[]?Attach external resource context
activeProfilestring?Set the initial tool profile
systemPromptOverlaystring | string[]?Text appended to the agent's system prompt for this thread
orgIdstring?Target org (multi-org only)

Returns: Promise<Thread>

type Thread = {
  id: string;
  org_id: string;
  agent_id: string;
  title: string | null;
  kind: 'single' | 'multiplayer';
  status?: string;
  active_profile?: string | null;
  created_at: string;
  updated_at?: string;
};

updateThread(threadId, updates)

Update a thread's configuration (active profile, title, etc.).

await client.updateThread('thread-id', { activeProfile: 'triage' });
NameTypeDescription
threadIdstringThe thread's UUID
updates.activeProfilestring | null?Set or clear the active tool profile
updates.titlestring?Update the thread title
updates.systemPromptOverlaystring | string[] | null?Update or clear the thread-level system prompt overlay

Returns: Promise<Thread>


stopThread(threadId)

Stop a streaming response in progress.

const result = await client.stopThread('thread-id');
// { ok: true, stopped: true }

Returns: Promise<{ ok: boolean; stopped: boolean }>


listThreads(agentId, options?)

List conversation threads for an agent. For API key auth, returns all org threads. For JWT auth (internal dashboard), returns only the current user's threads.

const result = await client.listThreads('agent-id');
console.log(result.threads); // Thread[]
console.log(result.nextCursor); // string | null

// With pagination
const page2 = await client.listThreads('agent-id', {
  cursor: result.nextCursor!,
  limit: 20,
});
NameTypeDescription
agentIdstringThe agent's UUID
options.cursorstring?ISO 8601 timestamp for cursor pagination
options.limitnumber?Max threads per page (default 50, max 100)
options.orgIdstring?Target org (multi-org only)

Returns: Promise<ListThreadsResult>

type ListThreadsResult = {
  threads: Thread[];
  nextCursor: string | null;
};

getMessages(threadId, options?)

Retrieve message history for a thread. Returns only user and assistant messages in chronological order.

const result = await client.getMessages('thread-id');
console.log(result.messages); // Array of messages
console.log(result.total);    // Total message count

// With pagination
const page = await client.getMessages('thread-id', { limit: 50, offset: 50 });
NameTypeDescription
threadIdstringThe thread's UUID
options.limitnumber?Max messages (default 100, max 200)
options.offsetnumber?Skip N messages (default 0)

Returns: Promise<GetMessagesResult>

type GetMessagesResult = {
  messages: Array<{
    id: string;
    role: 'user' | 'assistant';
    content: string;
    sender_name: string | null;
    created_at: string;
  }>;
  total: number;
};

Messages (Streaming)

sendMessage(threadId, content, options?)

Send a message to a thread and stream the agent's response via SSE.

for await (const event of client.sendMessage('thread-id', 'Hello!')) {
  // handle events
}

Parameters:

NameTypeDescription
threadIdstringThe thread's UUID
contentstringThe user's message text
options.toolsToolDef[]?Custom tool definitions for client-side execution
options.onToolCallFunction?Handler for client-side tool calls (auto-submits results)
options.modelOverridestring?Override the agent's default model for this message
options.webOverridesobject?Override web tool settings
options.memoryOverridesobject?Override memory settings
options.planOverridesobject?Override plan settings
options.computerOverridesobject?Override computer settings
options.anthropicOverridesAnthropicOverrides?Override Anthropic features (thinking, fallback) — Claude models only
options.resourceBindingsResourceBinding[]?Attach resource context
options.toolProfilestring?Use a specific tool profile for this message
options.systemPromptOverlaystring | string[]?Text appended to the system prompt for this message only
options.senderNamestring?Display name (multiplayer)
options.senderIdstring?Sender user ID (multiplayer)

Returns: AsyncGenerator<ChatEvent>

type ChatEvent =
  | { type: 'meta'; startedAt: string }
  | { type: 'token'; delta: string }
  | { type: 'tool_call'; tool: { id: string; name: string; arguments: string } }
  | { type: 'tool_executing'; tool_name: string }
  | { type: 'tool_result'; tool_name: string; tool_call_id: string; result: unknown }
  | { type: 'custom'; kind: string; payload: unknown }
  | { type: 'auth_challenge'; toolName: string; challenge: { provider: string; redirectUrl: string; description?: string } }
  | { type: 'requires_action'; toolCalls: ToolCall[] }
  | { type: 'client_event'; kind: string; payload: unknown }
  | { type: 'profile_switch_proposal'; target: string; reason: string }
  | { type: 'done'; ok: boolean; messageId?: string; content: string; skipped?: boolean; reason?: string }
  | { type: 'error'; code: string; detail?: string };

Event sequence: meta → token* → [tool_call → tool_executing → tool_result → custom*]* → done

When custom tools are provided and the agent calls one, a requires_action event is emitted. If onToolCall is set, results are submitted automatically and streaming continues. Otherwise, use submitToolResults() manually.

Note: This method does not throw on HTTP errors. Instead, it yields an error event.


submitToolResults(threadId, toolResults, options?)

Submit results after receiving a requires_action event. Only needed if you don't use onToolCall.

for await (const event of client.submitToolResults('thread-id', [
  { toolCallId: 'call-id', output: '{"result": 42}' },
])) {
  // handle continued stream
}

Returns: AsyncGenerator<ChatEvent>


Participants (Multiplayer)

Manage participants in multiplayer threads.

listParticipants(threadId)

const participants: ThreadParticipant[] = await client.listParticipants('thread-id');

Returns: Promise<ThreadParticipant[]>


addParticipant(threadId, userId, options?)

await client.addParticipant('thread-id', 'user-id', {
  displayName: 'Alice',
  role: 'member',
});

Returns: Promise<ThreadParticipant>


removeParticipant(threadId, userId)

await client.removeParticipant('thread-id', 'user-id');

Returns: Promise<void>


Knowledge

uploadDocument(agentId, file, title)

Upload a document to an agent's knowledge base for RAG.

const doc: KnowledgeDoc = await client.uploadDocument(
  'agent-id',
  new Blob(['content...'], { type: 'text/plain' }),
  'My Document'
);
NameTypeDescription
agentIdstringThe agent's UUID
fileFile | BlobThe document file
titlestringDisplay title

Returns: Promise<KnowledgeDoc>

type KnowledgeDoc = {
  id: string;
  title: string;
  source_ref: string;
  chunk_count: number;
  created_at: string;
};

listDocuments(agentId)

List all knowledge documents for an agent.

const docs: KnowledgeDoc[] = await client.listDocuments('agent-id');

Returns: Promise<KnowledgeDoc[]>


deleteDocument(docId)

Delete a knowledge document and all its chunks.

await client.deleteDocument('doc-id');

Returns: Promise<void>


MCP Servers

Full CRUD for MCP server connections at the organization level.

listMcpServers(orgId?)

const servers: McpServer[] = await client.listMcpServers();

Returns: Promise<McpServer[]>


createMcpServer(options)

const server = await client.createMcpServer({
  name: 'GitHub',
  transport: 'streamable_http',
  url: 'https://mcp.github.com/sse',
});
NameTypeDescription
options.namestringDisplay name (required)
options.transport'streamable_http' | 'sse' | 'stdio'Transport type (required)
options.urlstring?Server URL (required for HTTP/SSE)
options.commandstring?Command (required for stdio)
options.argsstring[]?Arguments (stdio only)
options.credentialsobject?{ headers?, env? } — encrypted at rest
options.allowedToolsstring[]?Org-level tool filter

Returns: Promise<McpServer>


getMcpServer(mcpId) / updateMcpServer(mcpId, options) / deleteMcpServer(mcpId)

const server = await client.getMcpServer('mcp-id');
await client.updateMcpServer('mcp-id', { name: 'New Name' });
await client.deleteMcpServer('mcp-id');

testMcpServer(mcpId)

Test an MCP server connection and discover available tools.

const result = await client.testMcpServer('mcp-id');
// { ok: true, tools: [{ name: 'search', description: '...', inputSchema: {...} }] }

Returns: Promise<{ ok: boolean; tools?: McpToolDef[] }>


Integrations

CRUD for database integrations (Postgres/Supabase).

listIntegrations(orgId?)

const integrations: Integration[] = await client.listIntegrations();

Returns: Promise<Integration[]>


createIntegration(options)

const integration = await client.createIntegration({
  name: 'Production DB',
  kind: 'postgres',
  connectionString: 'postgresql://user:pass@host:5432/dbname',
});

Returns: Promise<Integration>


getIntegration(integrationId) / deleteIntegration(integrationId)

const integration = await client.getIntegration('integration-id');
await client.deleteIntegration('integration-id');

Plans & Todos

Manage agent plans (structured task lists) within threads.

listPlans(threadId, status?)

const plans: Plan[] = await client.listPlans('thread-id', 'active');

Returns: Promise<Plan[]>


createPlan(options)

const plan = await client.createPlan({
  threadId: 'thread-id',
  title: 'Migration Plan',
  todos: [{ content: 'Back up database' }, { content: 'Run migrations' }],
});
NameTypeDescription
options.threadIdstringThread UUID (required)
options.titlestringPlan title (required)
options.todosArray<{ content: string; parent_todo_index?: number }>?Initial todos (use index for nesting)

Returns: Promise<Plan>

type Plan = {
  id: string;
  org_id: string;
  agent_id: string;
  thread_id: string;
  title: string;
  status: string; // 'active' | 'paused' | 'completed'
  created_at: string;
  updated_at: string;
  todos?: PlanTodo[];
  deps?: TodoDep[];   // dependency edges (when dependenciesEnabled)
};

getPlan(planId) / updatePlan(planId, updates) / deletePlan(planId)

const plan = await client.getPlan('plan-id');
await client.updatePlan('plan-id', { status: 'completed' });
await client.deletePlan('plan-id');

addTodos(planId, todos)

Add todos to a plan. Supports nesting via parent_id.

const todos: PlanTodo[] = await client.addTodos('plan-id', [
  { content: 'Sub-task A', parent_id: 'parent-todo-id' },
]);

Returns: Promise<PlanTodo[]>


updateTodo(planId, todoId, updates) / deleteTodo(planId, todoId)

await client.updateTodo('plan-id', 'todo-id', { status: 'done' });
await client.deleteTodo('plan-id', 'todo-id');

Todo status values: pending, in_progress, done, skipped.


addTodoDeps(planId, todoId, blockerIds)

Add dependency edges to a todo. The todo becomes blocked until all specified blocker todos are done or skipped. Requires dependenciesEnabled: true in the agent's plan config.

await client.addTodoDeps('plan-id', 'todo-b-id', ['todo-a-id']);
// todo-b is now blocked until todo-a is done/skipped

Returns: Promise<TodoDep[]>

type TodoDep = {
  id: string;
  dependent_id: string;
  blocker_id: string;
  created_at?: string;
};

getTodoDeps(planId, todoId)

Get all dependency edges for a todo — both the todos that block it and the todos it blocks.

const { blockers, dependents } = await client.getTodoDeps('plan-id', 'todo-id');

Returns: Promise<{ blockers: TodoDep[]; dependents: TodoDep[] }>


removeTodoDep(planId, todoId, blockerId)

Remove a dependency edge between two todos.

await client.removeTodoDep('plan-id', 'todo-b-id', 'todo-a-id');
// todo-b is no longer blocked by todo-a

getReadyTodos(planId)

Get all todos that are ready to work on — pending or in_progress todos whose blocking dependencies are all done or skipped.

const ready: PlanTodo[] = await client.getReadyTodos('plan-id');

Returns: Promise<PlanTodo[]>


Analytics

getAnalytics(options?)

Get usage analytics for your organization or a specific agent.

const analytics: AnalyticsResult = await client.getAnalytics({
  period: '30d',
  agentId: 'agent-id', // optional filter
});
NameTypeDescription
options.period'7d' | '30d' | '90d'?Time range
options.agentIdstring?Filter by agent
options.orgIdstring?Target org

Returns: Promise<AnalyticsResult> — includes summary, daily[], by_agent[], by_model[], top_tools[].


Marketplace Profile

Manage an agent's marketplace listing.

getMarketplaceProfile(agentId)

const profile: MarketplaceProfileResponse = await client.getMarketplaceProfile('agent-id');
// { profile, capabilities, readiness: { score, checklist } }

Returns: Promise<MarketplaceProfileResponse>


updateMarketplaceProfile(agentId, profile)

await client.updateMarketplaceProfile('agent-id', {
  handle: 'my-agent',
  category: 'productivity',
  tags: ['ai', 'support'],
});

Returns: Promise<MarketplaceProfileResponse>


deleteMarketplaceProfile(agentId)

await client.deleteMarketplaceProfile('agent-id');

Returns: Promise<void>


uploadAgentAvatar(agentId, file) / deleteAgentAvatar(agentId)

const { avatarUrl, avatarSmUrl } = await client.uploadAgentAvatar('agent-id', file);
await client.deleteAgentAvatar('agent-id');

generateMarketplaceDescription(agentId)

AI-generate a marketplace description based on the agent's configuration.

const { description } = await client.generateMarketplaceDescription('agent-id');

detectCapabilities(agentId) / updateCapabilities(agentId, capabilities)

Auto-detect or manually set agent capabilities for the marketplace.

const detected: MarketplaceCapability[] = await client.detectCapabilities('agent-id');
await client.updateCapabilities('agent-id', detected);

checkHandleAvailability(handle)

const { available, reason } = await client.checkHandleAvailability('my-agent');

Official Marketplace Agents

Browse and use official agent templates from the Flapjack marketplace.

listOfficialAgents(options?)

const agents: OfficialMarketplaceAgent[] = await client.listOfficialAgents({ featured: true });
NameTypeDescription
options.categorystring?Filter by category
options.featuredboolean?Filter to featured agents only

Returns: Promise<OfficialMarketplaceAgent[]>


useOfficialTemplate(options)

Create a new agent from an official template.

const agent = await client.useOfficialTemplate({
  templateId: 'template-id',
  name: 'My Support Agent',
});
NameTypeDescription
options.templateIdstringTemplate agent ID (required)
options.namestring?Custom name for the new agent
options.orgIdstring?Target org (multi-org only)

Returns: Promise<Agent>


Runners

Runners are headless, schedulable AI pipelines. See Runners for concepts and architecture.

Runner CRUD

const runners = await client.listRunners();
const runner  = await client.createRunner({ name: 'Daily Report', defaultModel: 'gpt-5.4' });
const runner  = await client.getRunner('runner-id');
await client.updateRunner('runner-id', { name: 'Weekly Report' });
await client.deleteRunner('runner-id');

Runner Steps

const steps = await client.listRunnerSteps('runner-id');
const step  = await client.addRunnerStep('runner-id', {
  kind: 'agent',
  name: 'Analyze',
  agentId: 'agent-id',
});
await client.updateRunnerStep('runner-id', 'step-id', { name: 'Updated' });
await client.removeRunnerStep('runner-id', 'step-id');

Step kinds: agent, webhook, condition, computer.

Runner Triggers

const triggers = await client.listRunnerTriggers('runner-id');
const trigger  = await client.addRunnerTrigger('runner-id', {
  kind: 'cron',
  cronExpression: '0 9 * * MON',
});
await client.updateRunnerTrigger('runner-id', 'trigger-id', { enabled: false });
await client.removeRunnerTrigger('runner-id', 'trigger-id');

Trigger kinds: manual, api, cron, webhook, poll, bulk_import, button.

Runner Runs

const run = await client.triggerRun('runner-id', { input: { topic: 'AI' } });
const runs = await client.triggerRunBulk('runner-id', [{ input: { topic: 'AI' } }, { input: { topic: 'ML' } }]);
const allRuns = await client.listRuns('runner-id', { status: 'completed', limit: 10 });
const run = await client.getRun('runner-id', 'run-id');
await client.cancelRun('runner-id', 'run-id');

Runner Analytics

const analytics: RunnerAnalytics = await client.getRunnerAnalytics('runner-id', { period: '30d' });
// { runner, period, summary: { success_rate, avg_duration_ms, ... }, daily[] }

Runner Connections

Attach MCP servers, database integrations, web tools, and plans directly to runners. These runner-level connections serve as defaults for all steps in the pipeline.

MCP Servers

const mcps: RunnerMcp[] = await client.listRunnerMcps('runner-id');
await client.attachRunnerMcp('runner-id', 'mcp-server-id', ['tool_a']);
await client.detachRunnerMcp('runner-id', 'mcp-server-id');

Database Integrations

const integrations: RunnerIntegration[] = await client.listRunnerIntegrations('runner-id');
await client.attachRunnerIntegration('runner-id', 'integration-id', ['users', 'orders']);
await client.detachRunnerIntegration('runner-id', 'integration-id');

Web Config

const config: RunnerWebConfig = await client.getRunnerWebConfig('runner-id');
await client.updateRunnerWebConfig('runner-id', {
  enabled: true,
  searchEnabled: true,
  researchEnabled: true,
});

Plan Config

const config: RunnerPlanConfig = await client.getRunnerPlanConfig('runner-id');
await client.updateRunnerPlanConfig('runner-id', {
  enabled: true,
  remindOnTurnEnd: true,
});

Persistent Computer

For external-app integrations where each app gets its own agent with a persistent Linux computer. See the computer API reference and the external-app integration guide for the full flow.

createAgentFromTemplate(options)

Creates an agent with a persistent agent-scoped sandbox in one call. Idempotent on externalAppId.

const { agent, bootstrapRunId } = await client.createAgentFromTemplate({
  name: 'Grocery Tracker',
  template: 'nextjs-fullstack',
  repo: { url: 'https://github.com/acme/grocery', installCmd: 'pnpm install' },
  envVars: [{ key: 'NODE_ENV', value: 'development' }],
  sizeClass: 'medium',
  webhookUrl: 'https://dassie.example.com/api/flapjack/webhook',
  externalAppId: 'app_abc123',
});

getComputerStatus(agentId)

Aggregate status of the persistent sandbox. Server-cached 10 s, safe to poll at 6/minute.

const status = await client.getComputerStatus(agent.id);
// status.computer.status: 'bootstrapping' | 'ready' | 'idle' | ...
// status.signals.devServer?.{ port, listening }
// status.signals.disk?.{ used, total }

execComputer(agentId, options)

Runs a shell command in the sandbox. Returns an AsyncIterable of events; streams stdout/stderr in real time.

for await (const ev of client.execComputer(agent.id, { command: 'pnpm test' })) {
  if (ev.type === 'stdout') process.stdout.write(ev.chunk);
  if (ev.type === 'exit')   console.log('exit', ev.exitCode);
}

Rate limits: 60/min per agent, 3 concurrent per agent. 429 responses include Retry-After.

verifyWebhookSignature(body, signature, secret)

Standalone helper (imported separately) that verifies inbound Flapjack webhooks. Uses Web Crypto, works in Node 16+ and browsers.

import { verifyWebhookSignature } from '@maats/flapjack';

const ok = await verifyWebhookSignature(rawBody, sigHeader, secret);

Skills (Skill Marketplace)

Skills are reusable bundles installed once at the org level and attached to any agent or runner. Three sources are supported:

  • skill_md — Prompt content from a SKILL.md file in a GitHub repo. Appended to the agent's stable_preamble.
  • openapi_action — REST tools auto-generated from an OpenAPI spec URL. Each operation becomes a callable tool.
  • mcp_registry — A remote MCP server pulled from the registry. Tools are loaded at runtime over HTTP/SSE.

When a skill is attached and enabled, its content is composed into the agent's system prompt and tool list at the next message. No restart needed.

listSkills(options?)

List skills installed in the org.

const skills: Skill[] = await client.listSkills();
const onlyMd = await client.listSkills({ source: 'skill_md' });
NameTypeDescription
options.sourceSkillSource?Filter by skill_md, mcp_registry, openapi_action, or custom

Returns: Promise<Skill[]>


getSkill(skillId)

const skill = await client.getSkill('skill-id');

Returns: Promise<Skill>


installSkill(options)

Install a skill into the org from an external source.

// SKILL.md from a GitHub repo
await client.installSkill({
  source: 'skill_md',
  sourceUrl: 'https://github.com/anthropics/skills',
  sourceRef: 'anthropics/skills/superpowers/git',
});

// OpenAPI spec → REST action skill
await client.installSkill({
  source: 'openapi_action',
  sourceUrl: 'https://api.example.com/openapi.json',
  name: 'Example API',
});

// Remote MCP server from the MCP registry
await client.installSkill({
  source: 'mcp_registry',
  sourceUrl: 'https://registry.modelcontextprotocol.io/servers/abc',
  sourceRef: 'abc-server-id',
});
FieldTypeDescription
source'skill_md' | 'openapi_action' | 'mcp_registry'Source type. custom skills are created in the dashboard, not installed via this method.
sourceUrlstringGitHub repo URL, OpenAPI spec URL, or registry server URL
sourceRefstring?For skill_md: owner/repo/path; for mcp_registry: registry server ID
namestring?Override the imported skill name
descriptionstring?Override the imported skill description

Returns: Promise<Skill> — the installed skill, or 409 SKILL_ALREADY_INSTALLED.


uninstallSkill(skillId)

Removes the skill from the org and detaches it from all agents/runners.

await client.uninstallSkill('skill-id');

Returns: Promise<void>


syncSkill(skillId)

Re-fetch source content for skill_md (re-reads SKILL.md) or openapi_action (re-fetches the spec). mcp_registry skills sync via the MCP server's tool cache.

const { syncedAt } = await client.syncSkill('skill-id');

Returns: Promise<{ ok: true; syncedAt: string }>


browseSkills(options?)

Browse the unified skill marketplace across all sources. Already-installed entries carry an installedId.

const { skills, nextCursor } = await client.browseSkills({
  q: 'github',
  source: 'mcp_registry',
  limit: 20,
});
FieldTypeDescription
qstring?Free-text search
sourceSkillSource?Restrict to a single source
limitnumber?Max per source (default 20, max 50)
cursorstring?Pagination cursor (MCP registry)

Returns: Promise<{ skills: SkillCatalogEntry[]; nextCursor?: string }>


Agent Bindings

Attach a skill to an agent. The skill's preamble (or its tools, for openapi_action/mcp_registry) becomes part of that agent's runtime config.

const attached = await client.listAgentSkills(agentId);

await client.attachSkillToAgent(agentId, {
  skillId: 'skill-id',
  promptOverride: 'Optional per-agent override of the skill prompt',
  credentialId: 'cred-id', // optional, for OAuth/API-key skills
});

await client.detachSkillFromAgent(agentId, 'skill-id');

listAgentSkills returns AttachedSkill[]:

type AttachedSkill = {
  id: string;
  skillId: string;
  enabled: boolean;
  promptOverride: string | null;
  hasCredentials: boolean;
  createdAt: string;
  skill: { id; name; slug; description; iconUrl; source; category; tags; authType; version; status } | null;
};

Runner Bindings

Identical shape, scoped to a runner:

await client.listRunnerSkills(runnerId);
await client.attachSkillToRunner(runnerId, { skillId: 'skill-id' });
await client.detachSkillFromRunner(runnerId, 'skill-id');

Next Steps

Docs last updated May 11, 2026