Documentation Index Fetch the complete documentation index at: https://mintlify.com/composiohq/composio/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Modifiers are powerful hooks that allow you to intercept and transform tool operations at different stages. You can use modifiers to customize request parameters, transform responses, add logging, implement custom caching, and more.
Types of Modifiers
Composio supports three types of modifiers:
Schema Modifiers - Transform tool schemas before they’re exposed
Before Execute Modifiers - Modify parameters before tool execution
After Execute Modifiers - Transform responses after tool execution
Schema Modifiers
Schema modifiers allow you to customize tool metadata, parameters, and descriptions before tools are used.
Basic Schema Modification
import { Composio } from 'composio-core' ;
const composio = new Composio ({
apiKey: process . env . COMPOSIO_API_KEY
});
const tools = await composio . tools . getRawComposioTools (
{
toolkits: [ 'github' ]
},
{
modifySchema : ({ toolSlug , toolkitSlug , schema }) => {
// Customize the schema
return {
... schema ,
name: `Custom ${ schema . name } ` ,
description: `Enhanced: ${ schema . description } ` ,
tags: [ ... ( schema . tags || []), 'customized' ]
};
}
}
);
const tools = await composio . tools . getRawComposioTools (
{ tools: [ 'GITHUB_GET_REPOS' ] },
{
modifySchema : ({ schema , toolSlug }) => {
return {
... schema ,
// Add custom metadata
metadata: {
organization: 'acme-corp' ,
department: 'engineering' ,
customId: `tool_ ${ toolSlug . toLowerCase () } `
},
// Enhance descriptions for better AI understanding
description: ` ${ schema . description } . This tool is maintained by the engineering team.` ,
// Add usage examples
examples: [
{
description: 'Get public repositories' ,
arguments: { owner: 'composio' , type: 'public' }
}
]
};
}
}
);
const tools = await composio . tools . getRawComposioTools (
{ tools: [ 'HACKERNEWS_GET_USER' ] },
{
modifySchema : ({ schema }) => {
return {
... schema ,
inputParameters: {
... schema . inputParameters ,
properties: {
... schema . inputParameters . properties ,
userId: {
... schema . inputParameters . properties ?. userId ,
description: 'HackerNews username (2-15 characters)' ,
minLength: 2 ,
maxLength: 15 ,
pattern: '^[a-zA-Z0-9_-]+$'
},
// Add new optional parameters
includeSubmissions: {
type: 'boolean' ,
description: 'Include user submissions in response' ,
default: false
}
},
required: [ ... ( schema . inputParameters . required || [])]
}
};
}
}
);
const tools = await composio . tools . getRawComposioTools (
{ toolkits: [ 'github' , 'slack' ] },
{
modifySchema : ({ schema , toolSlug , toolkitSlug }) => {
// Apply toolkit-specific modifications
if ( toolkitSlug === 'github' ) {
return {
... schema ,
name: `GitHub: ${ schema . name } ` ,
tags: [ ... ( schema . tags || []), 'version-control' , 'github' ]
};
}
if ( toolkitSlug === 'slack' ) {
return {
... schema ,
name: `Slack: ${ schema . name } ` ,
tags: [ ... ( schema . tags || []), 'communication' , 'slack' ]
};
}
// Apply tool-specific modifications
if ( toolSlug === 'GITHUB_CREATE_ISSUE' ) {
return {
... schema ,
description: ` ${ schema . description } - Automatically adds organization labels` ,
metadata: {
autoLabels: [ 'bug' , 'internal' ]
}
};
}
return schema ;
}
}
);
Before Execute Modifiers
Before execute modifiers intercept and modify parameters before a tool is executed.
Basic Parameter Modification
const result = await composio . tools . execute (
'GITHUB_GET_REPOS' ,
{
userId: 'user_123' ,
version: '20250909_00' ,
arguments: {
owner: 'composio'
}
},
{
beforeExecute : async ({ toolSlug , toolkitSlug , params }) => {
console . log ( `Executing ${ toolSlug } from ${ toolkitSlug } ` );
// Add tracking metadata
return {
... params ,
arguments: {
... params . arguments ,
per_page: 100 // Always fetch max results
}
};
}
}
);
const beforeExecute = async ({ params , toolSlug }) => {
return {
... params ,
customAuthParams: {
... params . customAuthParams ,
headers: {
... params . customAuthParams ?. headers ,
'X-Request-ID' : generateRequestId (),
'X-Tool-Slug' : toolSlug ,
'X-Timestamp' : new Date (). toISOString ()
}
}
};
};
const result = await composio . tools . execute (
'GITHUB_GET_REPOS' ,
{ userId: 'user_123' , version: '20250909_00' , arguments: { owner: 'composio' } },
{ beforeExecute }
);
const beforeExecute = async ({ toolSlug , params }) => {
switch ( toolSlug ) {
case 'GITHUB_SEARCH_REPOS' :
// Enhance search with additional filters
return {
... params ,
arguments: {
... params . arguments ,
q: ` ${ params . arguments . query } language:typescript stars:>100` ,
sort: 'stars' ,
order: 'desc'
}
};
case 'SLACK_SEND_MESSAGE' :
// Add default channel if not specified
return {
... params ,
arguments: {
channel: '#general' ,
... params . arguments
}
};
default :
return params ;
}
};
const beforeExecute = async ({ toolSlug , params }) => {
// Validate and sanitize inputs
if ( toolSlug === 'GITHUB_CREATE_ISSUE' ) {
const { title , body } = params . arguments ;
if ( ! title || title . trim (). length === 0 ) {
throw new Error ( 'Issue title cannot be empty' );
}
if ( title . length > 200 ) {
throw new Error ( 'Issue title too long (max 200 characters)' );
}
return {
... params ,
arguments: {
... params . arguments ,
title: title . trim (),
body: body ?. trim () || 'No description provided'
}
};
}
return params ;
};
After Execute Modifiers
After execute modifiers transform responses after a tool has executed.
const result = await composio . tools . execute (
'GITHUB_LIST_REPOS' ,
{
userId: 'user_123' ,
version: '20250909_00' ,
arguments: { owner: 'composio' }
},
{
afterExecute : async ({ result , toolSlug }) => {
if ( ! result . successful ) {
return result ;
}
// Transform response data
return {
... result ,
data: {
repositories: result . data . items . map ( repo => ({
name: repo . name ,
url: repo . html_url ,
stars: repo . stargazers_count ,
language: repo . language
})),
count: result . data . items . length ,
executedAt: new Date (). toISOString ()
}
};
}
}
);
Error Enhancement
const afterExecute = async ({ result , toolSlug , toolkitSlug }) => {
if ( ! result . successful ) {
// Enhance error with additional context
return {
... result ,
error: {
... result . error ,
toolSlug ,
toolkitSlug ,
timestamp: new Date (). toISOString (),
retryable: isRetryableError ( result . error ),
suggestedAction: getSuggestedAction ( result . error )
}
};
}
return result ;
};
function isRetryableError ( error ) {
const retryableCodes = [ 'RATE_LIMIT' , 'TIMEOUT' , 'SERVICE_UNAVAILABLE' ];
return retryableCodes . includes ( error ?. code );
}
function getSuggestedAction ( error ) {
switch ( error ?. code ) {
case 'RATE_LIMIT' :
return 'Wait before retrying or use a different account' ;
case 'AUTHENTICATION_FAILED' :
return 'Refresh the connected account credentials' ;
case 'TIMEOUT' :
return 'Retry the operation or reduce the request size' ;
default :
return 'Check the error details and retry if appropriate' ;
}
}
Response Filtering and Enrichment
const afterExecute = async ({ result , toolSlug }) => {
if ( ! result . successful || toolSlug !== 'GITHUB_LIST_REPOS' ) {
return result ;
}
const repos = result . data . items ;
// Filter and enrich repositories
const enrichedRepos = repos
. filter ( repo => ! repo . archived && ! repo . disabled )
. map ( repo => ({
... repo ,
// Add computed fields
isPopular: repo . stargazers_count > 1000 ,
lastUpdated: new Date ( repo . updated_at ). toLocaleDateString (),
mainBranch: repo . default_branch || 'main' ,
// Add health score
healthScore: calculateRepoHealth ( repo )
}));
return {
... result ,
data: {
repositories: enrichedRepos ,
stats: {
total: enrichedRepos . length ,
popular: enrichedRepos . filter ( r => r . isPopular ). length ,
languages: [ ... new Set ( enrichedRepos . map ( r => r . language ))]
}
}
};
};
function calculateRepoHealth ( repo ) {
let score = 0 ;
if ( repo . has_issues ) score += 20 ;
if ( repo . has_wiki ) score += 20 ;
if ( repo . description ) score += 20 ;
if ( repo . license ) score += 20 ;
if ( repo . topics ?. length > 0 ) score += 20 ;
return score ;
}
Logging and Monitoring
const afterExecute = async ({ result , toolSlug , toolkitSlug }) => {
// Log execution metrics
const metrics = {
toolSlug ,
toolkitSlug ,
successful: result . successful ,
timestamp: new Date (). toISOString (),
logId: result . logId
};
if ( result . successful ) {
console . log ( '[Tool Success]' , metrics );
// Send to analytics
await analytics . track ( 'tool_execution_success' , metrics );
} else {
console . error ( '[Tool Error]' , { ... metrics , error: result . error });
// Send to error tracking
await errorTracking . captureError ( result . error , metrics );
}
return result ;
};
Combining Modifiers
You can use multiple modifiers together for powerful customization:
const tools = await composio . tools . get (
'user_123' ,
{ toolkits: [ 'github' ] },
{
// Schema modification
modifySchema : ({ schema , toolSlug }) => ({
... schema ,
name: `Custom ${ schema . name } ` ,
metadata: { customId: toolSlug }
}),
// Before execution
beforeExecute : async ({ params , toolSlug }) => {
console . log ( `Executing: ${ toolSlug } ` );
return {
... params ,
allowTracing: true // Enable tracing for all tools
};
},
// After execution
afterExecute : async ({ result , toolSlug }) => {
console . log ( `Completed: ${ toolSlug } ` );
return result ;
}
}
);
For tool router sessions, use session-specific modifiers:
const sessionTools = await composio . toolRouter . getTools ( sessionId , {
modifySchema : ({ schema , toolSlug }) => ({
... schema ,
description: `[Session Tool] ${ schema . description } `
}),
beforeExecute : async ({ toolSlug , sessionId , params }) => {
console . log ( `Executing ${ toolSlug } in session ${ sessionId } ` );
return {
... params ,
sessionMetadata: {
sessionId ,
timestamp: Date . now ()
}
};
},
afterExecute : async ({ result , toolSlug , sessionId }) => {
console . log ( `Completed ${ toolSlug } in session ${ sessionId } ` );
return {
... result ,
sessionInfo: {
sessionId ,
completedAt: new Date (). toISOString ()
}
};
}
});
Best Practices
Keep Modifiers Pure Avoid side effects in modifiers. Return new objects instead of mutating existing ones.
Handle Errors Gracefully Wrap modifier logic in try-catch blocks to prevent breaking tool execution.
Use Type Safety Leverage TypeScript types to ensure modifier functions match expected signatures.
Performance Matters Keep modifiers lightweight. Heavy operations should be async and properly awaited.
Common Use Cases
Caching Responses
const cache = new Map ();
const cacheModifiers = {
beforeExecute : async ({ toolSlug , params }) => {
const cacheKey = ` ${ toolSlug } : ${ JSON . stringify ( params . arguments ) } ` ;
if ( cache . has ( cacheKey )) {
console . log ( 'Cache hit:' , cacheKey );
}
return params ;
},
afterExecute : async ({ result , toolSlug , params }) => {
if ( result . successful ) {
const cacheKey = ` ${ toolSlug } : ${ JSON . stringify ( params . arguments ) } ` ;
cache . set ( cacheKey , result . data );
}
return result ;
}
};
Rate Limiting
const rateLimiter = new Map ();
const rateLimitModifier = async ({ toolSlug , params }) => {
const now = Date . now ();
const lastCall = rateLimiter . get ( toolSlug ) || 0 ;
const minInterval = 1000 ; // 1 second between calls
if ( now - lastCall < minInterval ) {
const waitTime = minInterval - ( now - lastCall );
await new Promise ( resolve => setTimeout ( resolve , waitTime ));
}
rateLimiter . set ( toolSlug , Date . now ());
return params ;
};
Request/Response Logging
const loggingModifiers = {
beforeExecute : async ({ toolSlug , params }) => {
console . log ( `[ ${ new Date (). toISOString () } ] Executing ${ toolSlug } ` , {
arguments: params . arguments ,
userId: params . userId
});
return params ;
},
afterExecute : async ({ result , toolSlug }) => {
console . log ( `[ ${ new Date (). toISOString () } ] Completed ${ toolSlug } ` , {
successful: result . successful ,
dataSize: JSON . stringify ( result . data || {}). length
});
return result ;
}
};
Limitations
Modifiers must be synchronous or return Promises
Throwing errors in modifiers will prevent tool execution
Schema modifiers don’t validate the modified schema
Modifiers are not persisted and must be provided with each operation
Next Steps
Tool Execution Apply modifiers to tool execution
Custom Tools Use modifiers with custom tools
File Handling Learn about automatic file upload/download modifiers
Error Handling Handle errors in modifiers