Files
composio-skills-reference/composio-sdk/rules/app-custom-tools.md
sohamganatra b8b711dff6 Add Composio SDK skill with rules and agent config
Adds composio-sdk/ with SKILL.md, AGENTS.md, and 18 rule files
covering Tool Router, direct execution, triggers, and auth patterns.

Source: composiohq/skills

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 22:54:21 -08:00

215 lines
5.0 KiB
Markdown

---
title: Creating Custom Tools
impact: MEDIUM
description: Build standalone and toolkit-based custom tools with proper authentication and validation
tags: [custom-tools, extensibility, authentication, zod, development]
---
# Creating Custom Tools
Create your own tools that integrate with Composio:
- **Standalone tools** - No external authentication required
- **Toolkit-based tools** - Use toolkit credentials for API requests
## Standalone Tools
For tools that don't need external authentication:
```typescript
import { z } from 'zod';
const tool = await composio.tools.createCustomTool({
slug: 'CALCULATE_SQUARE',
name: 'Calculate Square',
description: 'Calculates the square of a number',
inputParams: z.object({
number: z.number().describe('The number to calculate the square of'),
}),
execute: async (input) => {
return {
data: { result: input.number * input.number },
error: null,
successful: true,
};
},
});
```
**Use for:** Math, string operations, data transformations, internal logic.
## Toolkit-Based Tools
For tools that call authenticated APIs.
### Using executeToolRequest (Recommended)
Automatically handles authentication and baseURL:
```typescript
const tool = await composio.tools.createCustomTool({
slug: 'GITHUB_STAR_REPOSITORY',
name: 'Star GitHub Repository',
toolkitSlug: 'github',
description: 'Star a repository under composiohq',
inputParams: z.object({
repository: z.string().describe('Repository name'),
page: z.number().optional().describe('Page number'),
}),
execute: async (input, connectionConfig, executeToolRequest) => {
return await executeToolRequest({
endpoint: `/user/starred/composiohq/${input.repository}`,
method: 'PUT',
parameters: [
{
name: 'page',
value: input.page?.toString() || '1',
in: 'query', // Adds ?page=1
},
],
});
},
});
```
### Using connectionConfig (Direct API Calls)
For custom HTTP requests:
```typescript
const tool = await composio.tools.createCustomTool({
slug: 'GITHUB_DIRECT_API',
name: 'Direct GitHub API',
toolkitSlug: 'github',
inputParams: z.object({
repo: z.string().describe('Repository name'),
}),
execute: async (input, connectionConfig) => {
const response = await fetch(`https://api.github.com/repos/${input.repo}`, {
headers: {
Authorization: `Bearer ${connectionConfig.val?.access_token}`,
},
});
const data = await response.json();
return {
data: data,
error: response.ok ? null : 'API request failed',
successful: response.ok,
};
},
});
```
## Input Validation with Zod
Define and validate parameters using Zod:
```typescript
inputParams: z.object({
// Required string
name: z.string().describe('User name'),
// Optional with default
count: z.number().optional().default(10).describe('Number of items'),
// With validation
email: z.string().email().describe('Email address'),
// Enum
status: z.enum(['active', 'inactive']).describe('Status'),
// Array
tags: z.array(z.string()).describe('Tags'),
// Nested object
metadata: z.object({
key: z.string(),
value: z.string(),
}).optional().describe('Metadata'),
})
```
**Always use `.describe()`** - helps AI understand parameter purpose.
## Headers and Query Parameters
Add headers and query params via `parameters` array:
```typescript
execute: async (input, connectionConfig, executeToolRequest) => {
return await executeToolRequest({
endpoint: '/search/repositories',
method: 'GET',
parameters: [
// Query parameters
{
name: 'q',
value: input.query,
in: 'query', // ?q=value
},
// Headers
{
name: 'Accept',
value: 'application/vnd.github.v3+json',
in: 'header',
},
],
});
}
```
## Executing Custom Tools
```typescript
// Standalone tool
await composio.tools.execute('CALCULATE_SQUARE', {
userId: 'default',
arguments: { number: 5 },
});
// Toolkit-based tool (uses userId to find account)
await composio.tools.execute('GITHUB_STAR_REPOSITORY', {
userId: 'user_123',
arguments: { repository: 'composio' },
});
// With explicit connected account
await composio.tools.execute('GITHUB_STAR_REPOSITORY', {
userId: 'user_123',
connectedAccountId: 'conn_abc123',
arguments: { repository: 'composio' },
});
```
## Error Handling
Always return structured responses:
```typescript
execute: async (input) => {
try {
const result = performOperation(input);
return {
data: result,
error: null,
successful: true,
};
} catch (error) {
return {
data: null,
error: error.message,
successful: false,
};
}
}
```
## Key Points
- **Naming:** Use `TOOLKIT_ACTION_DESCRIPTION` format for slugs
- **Prefer executeToolRequest:** Handles auth and baseURL automatically
- **Describe parameters:** AI agents need clear descriptions
- **Not persisted:** Custom tools exist in memory only, recreate on restart
- **Single toolkit scope:** executeToolRequest only works within same toolkit