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:
- Global SDK level – Set once in
new Composio({ toolkitVersions: … })and inherited by all subsequent calls. - Instance level – Override when constructing individual
ToolsorTriggersinstances directly. - Per-call level – Pass
toolkit_versionsinline totools.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
- Define global toolkit versions in the
Composioconstructor to set a baseline for all operations. - Override versions per-call using the
toolkit_versionsparameter intools.execute()for targeted testing. - Use
COMPOSIO_TOOLKIT_*environment variables to configure versions in CI/CD without code changes. - Reference
ts/packages/core/src/utils/toolkitVersion.tsfor resolution logic andts/packages/core/src/models/Tools.tsfor request injection. - Validate your configuration with the test patterns found in
ts/packages/core/test/core/versions.test.ts.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →