# Tool Modifiers in Composio: How to Transform Tool Inputs and Outputs Before Execution

> Discover Composio tool modifiers to transform tool inputs and outputs before execution. Customize function calls and streamline your workflow with this powerful SDK feature.

- Repository: [Composio/composio](https://github.com/composiohq/composio)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Tool modifiers are middleware-style functions in the Composio SDK that intercept tool calls at three specific points—before execution, after execution, and during schema generation—to transform inputs, reshape outputs, and customize metadata without modifying underlying tool implementations.**

The Composio SDK provides a pluggable architecture for LLM-invoked tools, enabling developers to inject custom logic at critical execution stages. According to the ComposioHQ/composio source code, these modifiers allow you to adjust authentication headers, truncate large payloads, or reshape tool schemas before they reach the LLM, all through a consistent type-safe interface defined in [`ts/packages/core/src/types/modifiers.types.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/types/modifiers.types.ts).

## Types of Tool Modifiers

The SDK defines three distinct modifier types, each triggering at a different lifecycle phase. All three are defined in the core TypeScript types file and receive the `toolSlug` and `toolkitSlug` parameters, enabling conditional logic that targets specific tools while leaving others untouched.

### Before-Execution Modifier

The **before-execution modifier** (`beforeExecuteModifier`) runs immediately before a tool is sent to the Composio API. This hook receives the `ToolExecuteParams` object and allows you to adjust or enrich arguments, inject authentication headers, or add contextual data.

Use this modifier when you need to override LLM-generated parameters, enforce business rules, or inject runtime secrets that should not be exposed in the tool schema.

### After-Execution Modifier

The **after-execution modifier** (`afterExecuteModifier`) executes right after the API returns a `ToolExecuteResponse`. This transformation layer enables you to truncate large payloads, map fields to a different structure, inject derived information, or filter sensitive data before returning results to the LLM.

### Schema Modifier

The **schema modifier** (`TransformToolSchemaModifier`) fires when a tool's schema is first fetched via `tools.get()`. This modifier changes the tool's description, input parameters, or metadata before exposure to the LLM. Common use cases include versioning tools, redacting internal fields, or appending organization-specific context to tool descriptions.

## Implementation by Provider Type

The wiring of modifiers differs based on whether you use non-agentic or agentic providers.

For **non-agentic providers** (plain chat completions), pass modifiers directly to `composio.tools.execute()` as an options object. For **agentic providers** (such as Vercel, Mastra, or CrewAI), pass modifiers to `composio.tools.get()`, where they are applied internally during the provider's execution step.

## Code Examples

### TypeScript: Transforming Inputs and Outputs with `execute()`

When working with chat-completion providers, supply `beforeExecute` and `afterExecute` callbacks to the `execute()` method. The following example limits HackerNews results and logs the transformation:

```typescript
import { Composio } from "@composio/core";

const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });
const userId = "default";

const result = await composio.tools.execute(
  "HACKERNEWS_GET_LATEST_POSTS",
  {
    userId,
    arguments: { size: 10 }
  },
  {
    beforeExecute: ({ toolSlug, toolkitSlug, params }) => {
      if (toolSlug === "HACKERNEWS_GET_LATEST_POSTS") {
        params.arguments.size = 1;
      }
      console.log(`[before] ${toolSlug} args:`, params.arguments);
      return params;
    },

    afterExecute: ({ toolSlug, toolkitSlug, result }) => {
      if (toolSlug === "HACKERNEWS_GET_LATEST_POSTS") {
        const first = result.data.items?.[0];
        return { ...result, data: { items: first ? [first] : [] } };
      }
      return result;
    },
  },
);

```

Both modifiers must return the modified object (`params` for before-execution, `result` for after-execution) to propagate changes through the execution chain.

### Python: Decorator-Based Modifiers

The Python SDK offers decorator-based registration via `@before_execute` and `@after_execute`. These decorators accept a `tools` list to filter which tool slugs trigger the modifier:

```python
from composio import Composio, before_execute, after_execute
from composio.types import ToolExecuteParams, ToolExecutionResponse

composio = Composio()
user_id = "default"

@before_execute(tools=["HACKERNEWS_GET_LATEST_POSTS"])
def limit_posts(tool: str, toolkit: str, params: ToolExecuteParams) -> ToolExecuteParams:
    params["arguments"]["size"] = 1
    return params

@after_execute(tools=["HACKERNEWS_GET_USER"])
def extract_karma(
    tool: str, toolkit: str, response: ToolExecutionResponse
) -> ToolExecutionResponse:
    return {
        **response,
        "data": {"karma": response["data"]["karma"]},
    }

tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_LATEST_POSTS")

result = composio.provider.handle_tool_calls(
    response=llm_response,
    user_id=user_id,
    modifiers=[limit_posts, extract_karma],
)

```

Pass the registered modifier functions to `handle_tool_calls()` or similar provider-specific execution methods to apply them.

### Schema Modification: Customizing Tool Definitions

Use the `modifySchema` option in `tools.get()` to transform how a tool appears to the LLM before schema generation:

```typescript
import { Composio } from "@composio/core";

const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });

const tools = await composio.tools.get(
  "default",
  { tools: ["GITHUB_GET_REPOS"] },
  {
    modifySchema: ({ schema, toolSlug, toolkitSlug }) => {
      if (toolSlug === "GITHUB_GET_REPOS") {
        schema.name = `Acme Corp – ${schema.name}`;
        schema.metadata = { ...schema.metadata, internal: true };
      }
      return schema;
    },
  },
);

```

This approach is particularly useful for multi-tenant applications where the same underlying tool requires different descriptions or metadata for different customers.

## Key Source Files

The implementation and type definitions for tool modifiers reside in the following locations within the ComposioHQ/composio repository:

- [`ts/packages/core/src/types/modifiers.types.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/types/modifiers.types.ts) – Core TypeScript definitions for `beforeExecuteModifier`, `afterExecuteModifier`, and `TransformToolSchemaModifier`
- `fern/pages/src/tools-and-triggers/before-execution-modifiers.mdx` – Documentation for pre-execution transformations
- `fern/pages/src/tools-and-triggers/after-execution-modifiers.mdx` – Documentation for post-execution transformations
- `docs/content/docs/tools-direct/modify-tool-behavior/before-execution-modifiers.mdx` – Updated documentation location for before-execution modifiers
- `docs/content/docs/tools-direct/modify-tool-behavior/after-execution-modifiers.mdx` – Updated documentation location for after-execution modifiers

## Summary

- **Tool modifiers** provide middleware-style hooks at three lifecycle stages: before execution, after execution, and during schema generation.
- The `beforeExecuteModifier` transforms `ToolExecuteParams` to adjust arguments or inject authentication before API calls.
- The `afterExecuteModifier` reshapes `ToolExecuteResponse` objects to filter, truncate, or enrich returned data.
- The `TransformToolSchemaModifier` customizes tool metadata and descriptions before exposure to LLMs.
- TypeScript implementations use callback options in `composio.tools.execute()` or `composio.tools.get()`.
- Python implementations use `@before_execute` and `@after_execute` decorators registered with specific tool slugs.
- All modifier types receive `toolSlug` and `toolkitSlug` parameters, enabling selective application to specific tools while bypassing others.

## Frequently Asked Questions

### What is the difference between before-execution and after-execution modifiers?

**Before-execution modifiers** run immediately before the tool call is sent to the Composio API, allowing you to modify input arguments, inject headers, or enforce validation rules. **After-execution modifiers** run immediately after receiving the API response, enabling you to transform, filter, or truncate the output data before it returns to the LLM. Both receive the tool and toolkit slugs as parameters for conditional logic.

### Can I apply modifiers to only specific tools in my toolkit?

Yes. All modifier functions receive `toolSlug` and `toolkitSlug` parameters. You can write conditional logic inside the modifier body to return unmodified data when these identifiers do not match your target tools. In Python, the `@before_execute` and `@after_execute` decorators accept a `tools` list that automatically filters which slugs trigger the modifier.

### How do schema modifiers affect tool calling behavior?

Schema modifiers alter the tool's definition—such as its name, description, and input parameters—before the LLM receives the schema. This affects how the LLM understands and invokes the tool, allowing you to hide internal fields, add organizational context, or version tool descriptions without changing the underlying API implementation. The transformation occurs during the `tools.get()` call.

### Do modifiers work with all LLM providers in the Composio SDK?

Modifiers work across both non-agentic providers (standard chat completions) and agentic providers (Vercel, Mastra, CrewAI). For non-agentic workflows, pass modifiers directly to `composio.tools.execute()`. For agentic frameworks, pass them to `composio.tools.get()`, where the SDK internally applies them during the provider's execution step.