Integrations: Langbase with Composio Tools

Let's take a look at how to integrate Langbase with Composio Tools.


In this integration guide, you will:

  • Learn how to connect your Langbase Pipe Agents with Composio tools.
  • Composio give you access to lots of real-world tools.
  • Build agents that can execute tools from Slack, GitHub, Gmail, Linear, Notion, etc.

Think of Composio as the universal adapter for tools. It standardizes tools into simple function calls that your agents can understand.

  • Pre-built tools for every major service
  • Authentication handled automatically

Let's take a look at how to integrate Langbase with Composio Tools.


You'll need:

  1. Langbase account - Sign up here
  2. Composio account - Get your API key
  3. Node.js 18+ or Python 3.8+

Step #1
Setup

Install the SDKs

Install dependencies

npm install langbase @composio/core dotenv

Environment setup

Create a .env file with your API keys:

.env

COMPOSIO_API_KEY="your_composio_api_key_here" LANGBASE_API_KEY="your_langbase_api_key_here" OPENAI_API_KEY="your_openai_api_key_here" USER_ID="your_user_id_here" ACCOUNT_ID="your_account_id_here"

Step #3

To connect Slack to Composio and enable tool access for your agent, follow these steps:

  1. Go to the Composio dashboard.
  2. In the navbar, click on All Toolkits button.
  3. Search for Slack, open it.
  4. Inside the Slack toolkit, click Add to Project button.
  5. Pick your authentication method OAuth2 or Bearer Token.
  6. After authentication click Connect Account button.
  7. Enter the External User ID you want to associate with this Slack connection.
  8. Enter the Token this is the Slack token xoxb-....
  9. Once connected, your account will show up under connected accounts.
  10. After connecting, copy the generated Account ID (e.g., ca_xxxxxxxx).

For a deeper walkthrough, check the Composio tools authentication docs.

Info

Store your USER_ID and ACCOUNT_ID in environment variables for easy, secure access in your code.

Step #3

We'll create a Langbase Pipe that can use Slack tools through Composio.

Create the pipe

create-slack-agent.ts

import 'dotenv/config'; import { Langbase, Tools } from 'langbase'; import { Composio } from '@composio/core'; async function main() { const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY!, }); const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY! }); const userId = process.env.USER_ID!; const accountId = process.env.ACCOUNT_ID!; const tools = await composio.tools.get(userId, { toolkits: ['slack'], }); const rawTools = extractRawTools(tools); const langbaseTools: Tools[] = rawTools .filter(isValidFunctionTool) .map(tool => ({ type: 'function' as const, function: { name: tool.function.name, description: tool.function.description, parameters: tool.function.parameters, }, })); await langbase.pipes.create({ name: 'slack-agent', description: 'AI agent that can interact with Slack Workspaces', model: 'openai:gpt-5-mini-2025-08-07', tools: langbaseTools, messages: [{ role: 'system', content: `You are a helpful Slack assistant. You can execute the available tools to help users with Slack tasks. Here are some things you can do: - Send messages to channels or users - Create and manage channels - Look up user or channel information - Automate reminders and notifications - And more! Always use the available tools to assist users with their Slack workspace needs. If you are unsure, ask clarifying questions or suggest helpful actions.`, }], upsert: true }); } main(); // Robustly extract tools and validate structure for production readiness function extractRawTools(tools: unknown): unknown[] { if (Array.isArray(tools)) { return tools; } if (tools && typeof tools === 'object' && 'tools' in tools && Array.isArray((tools as any).tools)) { return (tools as { tools: unknown[] }).tools; } return []; } function isValidFunctionTool( tool: unknown ): tool is { type: 'function'; function: { name: string; description?: string; parameters?: Record<string, unknown> } } { if ( typeof tool !== 'object' || tool === null || (tool as any).type !== 'function' || typeof (tool as any).function !== 'object' || (tool as any).function === null || typeof (tool as any).function.name !== 'string' ) { return false; } // Optionally, add more validation for parameters/description if needed return true; }

Step #4

Now the fun part. Let's run your Slack agent and see it in action.

run-agent.ts

import 'dotenv/config'; import { getToolsFromRun, Langbase, type Tools } from 'langbase'; import { Composio } from '@composio/core'; async function main() { const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY!, }); // 1. Initialize Composio. const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY! }); const userId = process.env.USER_ID!; const account_id = process.env.ACCOUNT_ID!; const tools = await composio.tools.get(userId, { toolkits: ['slack'], }); const rawTools = extractRawTools(tools); const langbaseTools: Tools[] = rawTools .filter(isValidFunctionTool) .map(tool => ({ type: 'function' as const, function: { name: tool.function.name, description: tool.function.description, parameters: tool.function.parameters, }, })); // You can uncomment this to create the pipe agent and change any parameters // await createSlackAgent(); const response = await langbase.pipes.run({ name: 'slack-agent', messages: [{ role: 'user', content: 'Send hello in #social' }], stream: false, tools: langbaseTools }); const toolCalls = await getToolsFromRun(response); const hasToolCalls = toolCalls.length > 0; const threadId = response.threadId; if (hasToolCalls) { // Process each tool call const toolResultPromises = toolCalls.map(async (toolCall): Promise<any> => { const toolName = toolCall.function.name; const toolParameters = JSON.parse(toolCall.function.arguments); const toolResponse = await composio.tools.execute(toolName, { userId: userId, arguments: toolParameters, connectedAccountId: account_id }); return { role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(toolResponse.data), }; }); // Wait for all tool calls to complete const toolResults = await Promise.all(toolResultPromises); // Call the agent pipe again with the updated messages const finalResponse = await langbase.pipes.run({ threadId, stream: false, name: 'slack-agent', messages: toolResults, tools: langbaseTools, }); console.log('Final response:', finalResponse.completion); } async function createSlackAgent(){ await langbase.pipes.create({ name: 'slack-agent', description: 'AI agent that can interact with Slack Workspaces', model: 'openai:gpt-5-mini-2025-08-07', tools: langbaseTools, messages: [{ role: 'system', content: `You are a helpful Slack assistant. You can execute the available tools to help users with Slack tasks. Here are some things you can do: - Send messages to channels or users - Create and manage channels - Look up user or channel information - Automate reminders and notifications - And more! Always use the available tools to assist users with their Slack workspace needs. If you are unsure, ask clarifying questions or suggest helpful actions.`, }], upsert: true }); } } main(); // Robustly extract tools and validate structure for production readiness function extractRawTools(tools: unknown): unknown[] { if (Array.isArray(tools)) { return tools; } if (tools && typeof tools === 'object' && 'tools' in tools && Array.isArray((tools as any).tools)) { return (tools as { tools: unknown[] }).tools; } return []; } function isValidFunctionTool( tool: unknown ): tool is { type: 'function'; function: { name: string; description?: string; parameters?: Record<string, unknown> } } { if ( typeof tool !== 'object' || tool === null || (tool as any).type !== 'function' || typeof (tool as any).function !== 'object' || (tool as any).function === null || typeof (tool as any).function.name !== 'string' ) { return false; } // Optionally, add more validation for parameters/description if needed return true; }

Using Langbase Memory to Retrieve Information and Send to Slack

You can combine Langbase's memory retrieval with your Slack agent to build more context-aware agents. For example, you might want to fetch a notes from memory, then send a summary or reminder to a Slack channel.

Here's a TypeScript example that demonstrates this workflow:

memory-to-slack.ts

import { Composio } from '@composio/core'; import 'dotenv/config'; import {getToolsFromRun, Langbase, Tools} from 'langbase'; const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY!, }); async function main() { const isMemoryExists = await langbase.memories.list().then((memories) => memories.find((memory) => memory.name === 'notes-slack')); if (!isMemoryExists) { // 1. Create the memory await langbase.memories.create({ name: 'notes-slack', embedding_model: 'openai:text-embedding-3-large' }); // 2. Upload the document to the memory await langbase.memories.documents.upload({ document: Buffer.from('Langbase is the most powerful serverless AI cloud for building and deploying AI agents. Build, deploy, and scale serverless AI agents with tools and memory (RAG). Simple AI primitives no bloated frameworks, all with a world-class developer experience without using any frameworks. '), memoryName: 'notes-slack', documentName: 'notes.txt', contentType: 'text/plain', }); } // 3. Initialize Composio. const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY! }); const userId = process.env.USER_ID!; const account_id = process.env.ACCOUNT_ID!; // 4. Get the tools from Composio const tools = await composio.tools.get(userId, { toolkits: ['slack'], }); const rawTools = extractRawTools(tools); // 5. Convert the tools to Langbase format const langbaseTools: Tools[] = rawTools .filter(isValidFunctionTool) .map(tool => ({ type: 'function' as const, function: { name: tool.function.name, description: tool.function.description, parameters: tool.function.parameters, }, })); const memory = await langbase.memories.retrieve({ memory: [{name: 'notes-slack'}], query: 'What is Langbase?', }); console.log(memory); // 6. Run the agent pipe const response = await langbase.pipes.run({ name: 'slack-agent', messages: [{ role: 'user', content: `Send a message in #social with the summary of the notes : ${memory[0].text}` }], stream: false, tools: langbaseTools }); const toolCalls = await getToolsFromRun(response); const hasToolCalls = toolCalls.length > 0; const threadId = response.threadId; if (hasToolCalls) { // Process each tool call const toolResultPromises = toolCalls.map(async (toolCall): Promise<any> => { const toolName = toolCall.function.name; const toolParameters = JSON.parse(toolCall.function.arguments); const toolResponse = await composio.tools.execute(toolName, { userId: userId, arguments: toolParameters, connectedAccountId: account_id }); return { role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(toolResponse.data), }; }); // Wait for all tool calls to complete const toolResults = await Promise.all(toolResultPromises); // Call the agent pipe again with the updated messages const finalResponse = await langbase.pipes.run({ threadId, stream: false, name: 'slack-agent', messages: toolResults, tools: langbaseTools, }); console.log('Final response:', finalResponse.completion); } } main(); function isValidFunctionTool( tool: unknown ): tool is { type: 'function'; function: { name: string; description?: string; parameters?: Record<string, unknown> } } { if ( typeof tool !== 'object' || tool === null || (tool as any).type !== 'function' || typeof (tool as any).function !== 'object' || (tool as any).function === null || typeof (tool as any).function.name !== 'string' ) { return false; } // Optionally, add more validation for parameters/description if needed return true; } function extractRawTools(tools: unknown): unknown[] { if (Array.isArray(tools)) { return tools; } if (tools && typeof tools === 'object' && 'tools' in tools && Array.isArray((tools as any).tools)) { return (tools as { tools: unknown[] }).tools; } return []; }