Creating Custom Tools

Define your own functions as tools, extend apps, or use custom authentication

While Composio offers a vast library of pre-built tools, you often need to integrate your own custom logic or interact with APIs in specific ways. This guide covers how to create and use custom tools within the Composio ecosystem.

You can create custom tools to:

Defining Tools from Your Functions

The most straightforward way to create a custom tool is to wrap an existing function in your codebase. Composio provides decorators (Python) and methods (TypeScript) to make these functions discoverable and executable by LLMs.

Python (@action)

Use the @action decorator from composio to designate a Python function as a tool. Composio automatically infers the tool’s schema and description from the following:

  • Function Name: Becomes the default tool name (can be overridden).
  • Docstring: Used as the tool’s description for the LLM. Make it clear and concise!
  • Type Hints: Define the input parameters and their types for the LLM and for validation.
  • Return Type Hint: Informs the expected output structure.

Example:

Python
1from composio import action
2from typing import Annotated # Recommended for descriptions
3
4# Define a simple function
5@action # Decorate it to make it a Composio tool
6def add_numbers(
7 a: Annotated[int, "The first number to add"],
8 b: Annotated[int, "The second number to add"]
9) -> int:
10 """Adds two integers and returns the result."""
11 print(f"Executing add_numbers: Adding {a} and {b}")
12 return a + b
13
14# Optionally, provide a custom name for the tool
15@action(toolname="calculator_multiply")
16def multiply_numbers(
17 a: Annotated[int, "The first number"],
18 b: Annotated[int, "The second number"]
19) -> int:
20 """Multiplies two integers."""
21 print(f"Executing multiply_numbers: Multiplying {a} by {b}")
22 return a * b

TypeScript (createAction)

Use the createAction method on your ToolSet instance (OpenAIToolSet, LangchainToolSet, etc.). You provide the configuration, including a Zod schema for input parameters and an async callback function containing your logic.

Example:

TypeScript
1import { OpenAIToolSet } from "composio-core"; // Or your specific framework ToolSet
2import { z } from "zod";
3
4const toolset = new OpenAIToolSet(); // Initialize ToolSet
5
6// Define the input schema using Zod
7const addSchema = z.object({
8 a: z.number().describe("The first number to add"),
9 b: z.number().describe("The second number to add"),
10});
11
12// Register the custom action
13await toolset.createAction({
14 actionName: "add_numbers", // Unique name for this tool
15 description: "Adds two numbers and returns the sum.",
16 inputParams: addSchema, // Provide the Zod schema
17 // The callback function containing your logic
18 callback: async (input) => {
19 // Safely access validated input (casting based on schema)
20 const params = input as z.infer<typeof addSchema>;
21 console.log(`Executing add_numbers: Adding ${params.a} and ${params.b}`);
22 const sum = params.a + params.b;
23 // Return a JSON-serializable result
24 return { result: sum };
25 },
26});
27
28console.log("Custom action 'add_numbers' registered.");

Using Your Custom Function Tools

Once defined (@action) or registered (createAction), these tools behave like any other Composio tool:

  1. Fetch them: Use get_tools, referencing the function object (Python) or the actionName string (Python/TS).
  2. Execute them: Use framework handlers (like Vercel’s execute) or execute_action.
1# Fetch custom and built-in tools together
2tools = toolset.get_tools(
3 actions=[
4 Action.GITHUB_GET_THE_AUTHENTICATED_USER, # Built-in
5 add_numbers, # Custom (by function object)
6 "calculator_multiply" # Custom (by toolname string)
7 ]
8)
9# Pass 'tools' to your LLM or framework

Extending Composio Toolkits

A powerful feature is creating custom tools that leverage Composio’s managed authentication for an existing app (like GitHub, Slack, etc.). This allows you to call API endpoints for that app without handling credentials yourself.

Example: Get GitHub Repository Topics

Let’s create a tool to fetch topics for a GitHub repo, using Composio’s managed GitHub auth.

1# Python Example using execute_request
2from composio import action, ComposioToolSet
3import typing as t
4
5toolset = ComposioToolSet()
6
7@action(toolname="github") # Associate with GitHub app for auth
8def get_github_repo_topics(
9 owner: Annotated[str, "Repository owner username"],
10 repo: Annotated[str, "Repository name"],
11 execute_request: t.Callable # Injected by Composio
12) -> dict:
13 """Gets the topics associated with a specific GitHub repository."""
14 print(f"Getting topics for {owner}/{repo} using Composio-managed GitHub auth...")
15 try:
16 # Call the GitHub API endpoint using the injected function
17 response_data = execute_request(
18 endpoint=f"/repos/{owner}/{repo}/topics", # API path relative to base URL
19 method="GET"
20 # Body/parameters usually not needed when relying on managed auth
21 )
22 # Ensure response_data is a dictionary before accessing 'names'
23 if isinstance(response_data, dict):
24 return {"topics": response_data.get("names", [])}
25 else:
26 # Handle unexpected response format
27 print(f"Warning: Unexpected response format from execute_request: {type(response_data)}")
28 return {"error": "Failed to parse topics", "raw_response": response_data}
29
30 except Exception as e:
31 print(f"Error executing request for topics: {e}")
32 return {"error": str(e)}
33
34# --- Example Usage ---
35# You would fetch this tool like any other:
36# tools = toolset.get_tools(actions=[get_github_repo_topics])
37# result = toolset.execute_action(get_github_repo_topics, params={"owner": "composiohq", "repo": "composio"})
38# print(result)

This allows you to extend Composio’s capabilities for any integrated app without managing the authentication flow yourself.

Adding Custom Authentication to Tools

You can also execute any Composio tool (pre-built or custom-defined) using your own authentication credentials provided at runtime. This is useful if you manage tokens or API keys separately from Composio’s connection system.

Use the execute_action method and provide the auth parameter.

Example: Create GitHub Issue with a Provided Token

1# Python example providing a custom Bearer token
2from composio import ComposioToolSet, Action
3
4toolset = ComposioToolSet()
5bearer_token = "ghp_YourPersonalAccessToken..." # Replace with your actual token
6
7print("Creating issue using custom auth...")
8try:
9 result = toolset.execute_action(
10 action=Action.GITHUB_CREATE_ISSUE,
11 params={
12 "owner": "your-username",
13 "repo": "test-repo",
14 "title": "Issue Created with Custom Token",
15 "body": "This issue uses an externally provided auth token."
16 },
17 # Provide authentication details via the 'auth' parameter
18 auth={
19 "parameters": [
20 {
21 "name": "Authorization", # Header name
22 "value": f"Bearer {bearer_token}", # Header value
23 "in_": "header" # Placement (header, query, path, etc.)
24 }
25 ]
26 # 'base_url' could be added here for GitHub Enterprise
27 # 'body' could be added for complex auth flows if needed
28 }
29 # entity_id is typically not needed when providing full custom auth
30 )
31 print(result)
32except Exception as e:
33 print(f"An error occurred: {e}")

This gives you flexibility while still leveraging Composio’s standardized action execution and schema handling. The ParamPlacement enum (TS) or string values ("header", "query", "path") specify where the custom parameter should be injected into the API request.