Tools
Flapjack agents can call tools during conversations: webhooks, MCP servers, database integrations, web tools, memory, and computer use.
Tools extend what an agent can do beyond generating text. During a conversation, the agent can decide to call tools, execute them, and incorporate the results into its response.
Tool Types
| Type | Description | Configuration |
|---|---|---|
| Webhook Tools | Custom HTTP endpoints the agent can call | Dashboard → Agent → Tools |
| MCP Servers | External tool servers via Model Context Protocol | Dashboard → MCP Servers |
| Database Integrations | Query Postgres/Supabase tables directly | Dashboard → Integrations |
| Web Tools | Search, research, read, and crawl the web | Dashboard → Agent → Web |
| Memory | Store and recall information across conversations | Dashboard → Agent → Memory |
| Computer Use | Execute code in sandboxed environments | Dashboard → Agent → Computer |
Dashboard-configured tools (webhook, MCP, database, web, memory, computer) require no SDK-side setup. Custom tools are defined at runtime via the SDK.
How Tool Calling Works
User sends message
→ Agent receives message + tool definitions
→ Agent decides to call a tool
→ Flapjack executes the tool
→ Agent receives the result
→ Agent may call more tools (up to 10 per turn)
→ Agent generates final response
During streaming, tool activity produces these events:
tool_call— the agent decided to call a tool (includes name and arguments)tool_executing— execution has startedtool_result— execution completed with a result
Webhook Tools
Custom HTTP endpoints your agent can call. Define them in the dashboard with:
- Name: What the agent sees (e.g., "get_weather")
- Description: When to use it (the agent reads this)
- URL: The endpoint to POST to
- Parameters: JSON schema for the tool's input
When the agent calls a webhook tool, Flapjack POSTs the arguments to your endpoint with an HMAC signature for verification.
MCP Servers
Connect any Model Context Protocol server to give your agent access to external tools. Examples: GitHub, Slack, PostHog, Supabase.
- Tools are discovered automatically via
tools/list - Tool names are namespaced as
{slug}__{tool_name}to avoid collisions - Supports HTTP, SSE, and stdio transports
- OAuth 2.1 support for authenticated servers
Database Integrations
Connect a Postgres or Supabase database and the agent gets auto-generated tools:
query_{table}— Run read queries against a tablecount_{table}— Count rows matching conditions
The agent can query your data directly during conversations. Schema is introspected and cached automatically.
Web Tools
Enable per-agent in the dashboard:
| Tool | What It Does |
|---|---|
web_search | Search the web (powered by Perplexity) |
web_research | Extended research with multiple queries |
web_read | Read a specific URL (powered by Firecrawl) |
web_crawl | Crawl a website and extract content |
Memory
Agents with memory enabled can store and recall information across conversations:
- Store: Save a fact or preference for later
- Recall: Search stored memories by semantic similarity
Memory can be scoped to the agent (shared across all threads), a specific thread, or a resource.
→ Memory
Custom Tools (Client-Side)
You can define tools at runtime and execute them client-side. Pass tool definitions via the tools option in sendMessage, and handle calls with onToolCall:
const tools = [{
name: 'get_weather',
description: 'Get current weather for a city',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
}];
for await (const event of client.sendMessage(threadId, 'What is the weather in London?', {
tools,
onToolCall: async (call) => {
// Execute the tool and return the result
const data = await fetchWeather(call.arguments);
return JSON.stringify(data);
},
})) {
if (event.type === 'token') process.stdout.write(event.delta);
}
When the agent calls a custom tool, a requires_action event is emitted. If onToolCall is provided, results are submitted automatically and streaming continues. Without onToolCall, use client.submitToolResults() to resume the stream manually.
Tool definitions follow the OpenAI function calling schema format: name, description, and parameters (JSON Schema).
Showing Tool Activity in UI
When building a chat UI, display tool activity to users for transparency:
for await (const event of client.sendMessage(threadId, message)) {
switch (event.type) {
case 'token':
appendToResponse(event.delta);
break;
case 'tool_call':
showToolIndicator(`Using ${event.tool.name}...`);
break;
case 'tool_result':
hideToolIndicator();
break;
case 'done':
finalizeResponse(event.content);
break;
}
}
📋 Copy as prompt
Handle Flapjack streaming events to show tool activity in the UI. Display "Using {tool_name}..." when a tool_call event arrives, hide it on tool_result, and render token deltas as they stream.
Tool Profiles
Tool profiles let you define named subsets of tools on an agent, each with its own system prompt instructions. This enables mode-based interactions where the agent's capabilities change based on user intent.
Defining Profiles
Configure profiles on an agent via the API:
await client.updateAgent(agentId, {
toolProfiles: {
chat: {
label: 'Chat',
description: 'General Q&A and exploration',
icon: '💬',
tools: ['get_feature_tags', 'github_read_file', 'web_search'],
systemPromptAppend: 'You are in Chat mode. Help the user explore.',
},
triage: {
label: 'Triage',
description: 'Review changes and manage features',
icon: '📋',
tools: ['get_feature_tags', 'suggest_feature_tag', 'create_feature_tag'],
systemPromptAppend: 'You are in Triage mode. Help the user review changes.',
},
},
defaultProfile: 'chat',
});
How It Works
- Each profile specifies which tool names are visible to the LLM
- Tools not listed in the active profile are excluded from the request
- The profile's
systemPromptAppendis appended to the agent's preamble - A built-in
propose_profile_switchtool is auto-injected in all profiles
Profile Switching
The agent can call the propose_profile_switch tool to suggest changing modes. This emits a profile_switch_proposal SSE event. The client confirms the switch:
// In useChat options
onProfileSwitch: (proposal) => {
if (confirm(`Switch to ${proposal.target}? ${proposal.reason}`)) {
switchProfile(proposal.target);
}
}
Per-Message Override
Override the active profile for a single message:
await client.sendMessage(threadId, 'Review this PR', {
toolProfile: 'triage',
});
Thread Persistence
The active profile is stored on the thread. Set it at creation or update it later:
const thread = await client.createThread({ agentId, activeProfile: 'chat' });
await client.updateThread(threadId, { activeProfile: 'triage' });
Skills
Tool definitions, MCP server bindings, and prompt fragments can also be packaged as a Skill and reused across many agents. See Skills.
Next Steps
- MCP: Overview — connect external tool servers
- Skills — reusable bundles of prompts, tools, and MCP servers
- Knowledge — RAG with document upload
- Memory — persistent agent memory