How to Manage Toolkit Versions for Production Stability in Composio

Pin toolkit versions globally via the Composio constructor, override per-call when needed, and use environment variables for CI/CD to ensure deterministic agent behavior across releases.

The Composio SDK provides a robust versioning system that lets you lock individual toolkits (GitHub, Gmail, Slack, etc.) to specific releases, preventing unexpected breaking changes when providers update their APIs. By managing toolkit versions explicitly, you guarantee that your AI agents execute with predictable request and response schemas regardless of upstream changes. This guide walks through the configuration hierarchy, source code implementation, and production patterns used in the ComposioHQ/composio repository.

Understanding the Versioning Architecture

The SDK implements toolkit versioning through a type-safe hierarchy that resolves versions at runtime. At the core, ToolkitVersionParam in ts/packages/core/src/types/tool.types.ts defines the input shape as either a string ("latest" or a global version) or an object mapping toolkit slugs to specific versions.

During SDK initialization in ts/packages/core/src/utils/sdk.ts, this parameter is normalized into ToolkitVersions, an object that guarantees every toolkit has an explicit version entry. The resolution logic lives in ts/packages/core/src/utils/toolkitVersion.ts, where the getToolkitVersion helper extracts the effective version for a given slug, defaulting to "latest" when no explicit mapping exists.

Both Tools and Triggers models store this configuration at construction time. In ts/packages/core/src/models/Tools.ts (lines 72-87) and ts/packages/core/src/models/Triggers.ts (lines 75-85), the SDK attaches the resolved toolkitVersions to every outgoing API request, ensuring the backend receives the correct version identifier.

Configuration Hierarchy

The SDK merges version specifications across three layers, with later layers taking precedence over earlier ones:

  1. Global SDK level – Set once in new Composio({ toolkitVersions: … }) and inherited by all subsequent calls.
  2. Instance level – Override when constructing individual Tools or Triggers instances directly.
  3. Per-call level – Pass toolkit_versions inline to tools.execute() for single-use overrides.

This merge order (global → instance → call) is verified by the test suites in ts/packages/core/test/models/tools.test.ts and ts/packages/core/test/core/versions.test.ts.

Production Patterns

Pinning Versions Globally

For production stability, define a global version mapping in your SDK initialization. This approach ensures every tool execution uses the tested toolkit release unless explicitly overridden.

import { Composio } from '@composio/core';

const composio = new Composio({
  apiKey: process.env.COMPOSIO_API_KEY!,
  // Lock GitHub and Gmail to known stable versions.
  toolkitVersions: {
    github: '20251201_01',
    gmail:  '20250909_00',
    // Toolkits omitted here default to "latest".
  },
});

All API calls made through composio.tools will now use these pinned versions.

Overriding for Single Executions

When testing new toolkit releases or handling edge cases, override the version for a specific call without affecting global configuration:

const result = await composio.tools.execute('GITHUB_CREATE_REPO', {
  userId: 'user-123',
  arguments: { name: 'my-repo' },
  // Force a specific GitHub version just for this invocation.
  toolkit_versions: { github: '20251115_00' },
});

The SDK injects this inline mapping into the request body at the call site in ts/packages/core/src/models/Tools.ts (line 779).

Loading from Environment Variables

For CI/CD pipelines and containerized deployments, the SDK automatically reads version mappings from environment variables prefixed with COMPOSIO_TOOLKIT_. In ts/packages/core/src/utils/sdk.ts (lines 73-105), the constructor scans for these variables and merges them into the global configuration.

export COMPOSIO_TOOLKIT_GITHUB=20251201_01
export COMPOSIO_TOOLKIT_SLACK=20250930_02
const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY! });
// Versions are loaded from environment automatically.

This pattern enables version updates without code changes or redeployment.

Debugging Effective Versions

To verify which version resolves at runtime, use the getToolkitVersion utility:

import { getToolkitVersion } from '@composio/core/utils/toolkitVersion';

const version = getToolkitVersion('github', composio.config.toolkitVersions);
console.log('Effective GitHub toolkit version:', version);
// Output: "20251201_01"

This helper implements the fallback logic defined in ts/packages/core/src/utils/toolkitVersion.ts (lines 10-21), showing exactly which version applies after environment and configuration merging.

Why Version Pinning Matters for Production

Pinning toolkit versions delivers three critical production benefits:

  • API Stability – Toolkits evolve with new fields and changed semantics; locking a version guarantees consistent request/response shapes.
  • Predictable Billing – Newer toolkit versions may introduce paid features; explicit versioning prevents surprise costs.
  • Regression Safety – If a new version breaks a workflow, roll back by updating the version mapping rather than redeploying code.

Summary

Frequently Asked Questions

How does the SDK handle toolkit versions if I don't specify any?

When no explicit version is provided, the SDK defaults to "latest" for all toolkits. This fallback is implemented in ts/packages/core/src/utils/toolkitVersion.ts, where getToolkitVersion returns "latest" if the toolkit slug is absent from the configuration object.

Can I mix string and object formats for toolkitVersions?

Yes. The ToolkitVersionParam type accepts either a string (applying to all toolkits) or an object mapping specific slugs to versions. In ts/packages/core/src/utils/sdk.ts, the SDK normalizes string inputs into object format to ensure consistent handling across the codebase.

Do toolkit versions affect both tools and triggers?

Yes. Both the Tools model (ts/packages/core/src/models/Tools.ts) and the Triggers model (ts/packages/core/src/models/Triggers.ts) store the toolkitVersions configuration and inject them into respective API requests, ensuring consistent versioning across actions and event subscriptions.

What happens if I specify a version that doesn't exist?

The SDK forwards the version identifier to the Composio API backend. If the version is invalid, the API returns an error at request time. The SDK itself does not validate version strings locally; it only resolves which version identifier to send based on the configuration hierarchy.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →