NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #3104 most downloaded on PyPI
SDK for integrating Composio with your applications.
Last release 5 days ago
29 Sep 2026
Ships fairly regularly
a new release about every 2 weeks
Some releases are documented
notes for 21 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
2 years old
79 releases · first in 2024
One column per month.
prepares composio==2.0.0b0 with composio-client==2.0.0rc6
This PR:
composio==2.0.0b0 with composio-client==2.0.0rc60.x release trainVerification:
uv lock --checkpnpm test:release-workflowgo run github.com/rhysd/actionlint/cmd/actionlint@v1.7.10 .github/workflows/py.release.ymlmake build-core and twine check --strict dist/*0.21.0; uv pip check and all provider imports passedAfter merge, pushing the immutable py@2.0.0b0 tag will publish only
the composio wheel and source distribution.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
e182bc8 : Remove the deprecated OpenAI Assistants API helpers from OpenAIProvider : handleAssistantMessage , waitAndHandleAssistantToolCalls , and wai…
This PR was opened by the Changesets
release GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.
instant.return_instant_charge, expose instantCharge on executioninstantAccount. Useinstant_account for explicit account selection.instantCharge in projectmetadata.instant_charge in tool log metadata.OpenAIProvider: handleAssistantMessage,waitAndHandleAssistantToolCalls, andwaitAndHandleAssistantStreamToolCalls. OpenAI shut down the AssistantsOpenAIResponsesProvider from @composio/openai with the ResponsesConnectionRequest.status in sync withwaitForConnection(). The request now reports ACTIVE once theFAILED, EXPIRED,REVOKED) when it fails, matching toJSON() and the Python SDK.ConnectionRequest.waitForConnection(). telemetry.instrument() nowcreateConnectionRequest().executeToolCallhandleResponse to execute throughoptions argument is now optional. Passing a session@composio/core 0.17.0 or later. Custom provider subclassesexecuteToolCallhandleResponse to execute throughoptions argument is now optional. Passing a session@composio/core 0.17.0 or later. Custom provider subclassesexecuteToolCallhandleResponse to execute throughoptions argument is now optional. Passing a session@composio/core 0.17.0 or later. Custom provider subclassesInclude the Python premium-charge response support merged in #4623 in the 0.24.0 changelog. The notes cover the opt-in, typed response, provider and m
Include the Python premium-charge response support merged in #4623 in
the 0.24.0 changelog. The notes cover the opt-in, typed response,
provider and mixed-execution preservation, and exported charge fields.
Validation: release-workflow checks, Prettier, and git diff checks pass.
This is a documentation-only follow-up to the release preparation.
Nothing published for this version
Release Python SDK 0.22.0
Release Python SDK 0.22.0
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
760f8d0 : Allow OpenAI and Anthropic provider tool-call helpers to execute through a supplied Tool Router session. Session meta-tools now retain their
{ error } results without changing successful payloads. Custom provider subclasses overriding executeToolCall or handleToolCalls may require updates because these methods now accept session targets.s3Url), S3 presigned uploads (new_presigned_url), Tool Router session file downloads (RemoteFile.buffer() / blob() / text() / save()) and session file uploads (upload_url) now go through the same SSRF guard that already covered user-supplied URLs, so a response naming a private, loopback, or link-local address is refused instead of fetched. Redirect hops are re-validated. Edge runtimes keep their current behavior: session file transfers are not blocked there, since a Worker cannot resolve DNS to check and its fetch does not originate inside the caller's network.@composio/json-schema-to-zod@0.3.0
5e57815: Keep free-form object roots, patternProperties, and additionalProperties when parsing a tool schema.
ToolSchema.parse used to reject a bare { "type": "object" } root, drop root patternProperties as an unknown key, and reject a root additionalProperties written as a schema instead of a boolean. Free-form roots now parse successfully, and both constraints survive parsing exactly as written. The public ToolSchema type now makes properties optional to reflect those valid property-less object schemas.
This matters downstream. Every provider reads inputParameters after parsing, so a tool that declares dynamic keys had those rules stripped before the model ever saw them.
An omitted additionalProperties still stays omitted. The parser does not invent a value, because each converter decides its own default.
What no longer works
Parsing no longer fails on a schema-valued root additionalProperties.
const tool = ToolSchema.parse({
slug: 'MY_TOOL',
inputParameters: {
type: 'object',
properties: { name: { type: 'string' } },
additionalProperties: { type: 'number' },
},
// ...
});
// before: parsing failed, because only a boolean was accepted
// now: tool.inputParameters.additionalProperties is { type: 'number' }
What to do instead
Nothing, if you only ever passed boolean values. That case is unchanged.
If your code assumed inputParameters never carries patternProperties, or that additionalProperties is always a boolean, widen that assumption. Both keywords can now appear, and additionalProperties can be a boolean or a schema object.
type: "object" to nested JSON Schema nodes that carry properties without an explicit type, so tool schemas work with strict OpenAPI 3.0 consumers like Google Gemini.e5c9ada: Refresh the OpenAI runtime dependency to version 7.
1503786: Replace the loose JSON Schema property type with a recursive, type-safe definition.
JSONSchemaProperty (re-exported from @composio/core and reachable through
Tool.input_parameters / Tool.output_parameters) is now a concrete recursive
interface instead of effectively any. Runtime behavior is unchanged, but
consumer code that indexed into it without narrowing (for example
schema.properties.foo.type or schema.default.someField) may see new type
errors: properties entries are now possibly undefined and default /
enum values are unknown. Narrow with optional chaining or explicit type
guards when upgrading.
{"api_key": "..."} — the shape error messages usually carry — was sent unredacted.ssrfSafeFetch, and the response body before throwing on !response.ok in both URL-upload call sites, instead of leaving them for the garbage collector to reclaim.isNewerVersion helper.58bc93b: Refresh dependency ranges and lockfiles across the workspace.
homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....fc17c37: Export the sensitive-file-upload denylist guard from the package root so downstream packages share one implementation: assertSafeFileUploadPath, isBlockedSensitiveFileUploadPath, and BUILTIN_FILE_UPLOAD_PATH_DENY_SEGMENTS. The guard now routes its filesystem access through the internal #platform abstraction (adding a realpathSync platform method), so it is edge/workerd-safe and the module carries no static node:* imports. Behavior on Node/Bun is unchanged.
20a4711: triggers.create now resolves the connection from user_id on the backend instead of client-side.
connectedAccounts.list() call. When connectedAccountId is omitted, the backend resolves the first active connection for the user and the trigger's toolkit (ordered by most recently created), matching tool execution.create no longer throws ComposioConnectedAccountNotFoundError for a missing or invalid connection. That case now surfaces as the backend error from the upsert call. ComposioTriggerTypeNotFoundError (invalid slug) and ValidationError (including empty userId) are still thrown client-side.user_id on upsert (ComposioHQ/platform#10932). Self-hosted deployments must be on a version that includes it.7125576: Normalize duplicate JSON Schema required entries before provider tool schemas are emitted.
58bc93b: Refresh dependency ranges and lockfiles across the workspace.
4c3a321: Disable client retries on tools.execute and tools.proxyExecute. These are non-idempotent writes, so a silent retry after a read timeout could duplicate the side effect (e.g. send the same email more than once). Both now route through a sibling client built with maxRetries: 0; reads keep the default retry behaviour.
b07fcad: Add an eve provider: EveProvider makes session.tools() return eve-native defineTools, defineComposioTools is the replay-safe step.started resolver, and (ctx, next) hooks can rewrite, deny, or transform Tool Router meta-tool calls.
Preserve successful local-tool results when the remote half of a mixed COMPOSIO_MULTI_EXECUTE_TOOL batch fails at the transport layer, so callers can see which side effects already completed before retrying.
fa933a6: Fix the homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....
2ef40ce: Treat local file paths that begin with http as paths instead of URLs, ensuring that upload allowlist and sensitive-file denylist checks still run.
e78ed31: Execute every parallel tool call in OpenAIProvider.handleToolCalls. It previously only ran the first tool call in each assistant message, so parallel tool calls (on by default) dropped the rest and left their tool_call_ids unanswered, failing the next request.
The calls are run sequentially, in the order the model returned them — here "parallel" means the model issued several calls in one turn, not that they execute concurrently — so each tool_call_id is answered exactly once and the tool messages come back in a deterministic order.
Only the first choice is handled. Tool results are fed back into a single assistant turn, so with n > 1 iterating over every choice would run each tool call once per choice and orphan the tool_call_ids from the alternative completions.
a0f37a7: Close two secret/SSRF exposure surfaces in the TypeScript SDK:
composio.files.upload(url) and automatic file upload during tool execution previously did a raw fetch() on user-supplied URLs with no guard. They now resolve the host and refuse private, loopback, link-local (incl. the 169.254.169.254 cloud-metadata endpoint), CGNAT, and reserved addresses, reject non-http(s) schemes, and follow redirects manually so each hop is re-validated (blocking a public URL that redirects into internal space). Blocked requests throw ComposioBlockedInternalUrlError. Node-only; behaviour for public URLs is unchanged.error.message / error.stack verbatim. They are now passed through a redactor that strips URL query strings, Authorization bearer/basic credentials, and secret-like key=value pairs (API keys, tokens, client secrets, passwords) before transport.820abb9: Resolve toolkit version pins case-insensitively. Version maps are keyed by normalized (lowercase) slugs, but getToolkitVersion previously looked them up with the raw slug, so a pin configured under a different casing (e.g. { GitHub: '20250101_00' } or COMPOSIO_TOOLKIT_VERSION_GITHUB) could silently fall back to 'latest'. Normalization is now centralized in a single normalizeToolkitSlug helper used symmetrically on both the write (map-building) and read (lookup) paths, so the two sides can no longer drift. This mirrors the equivalent fix in the Python SDK.
Updated dependencies [58bc93b]
Updated dependencies [fa933a6]
605a726: Add Tool Router session deletion APIs.
d17a268: Add the first-class composio.sessions.create() API while keeping composio.create() as an alias, expose the experimental shared-connection ACL
d17a268: Add the first-class composio.sessions.create() API while keeping composio.create() as an alias, expose the experimental shared-connection ACL patch helper as connectedAccounts.updateAcl() while keeping experimental.updateAcl() as an alias, and include SDK docs/source in the published package.
MCP is now opt-in. Sessions return native tools by default; the hosted MCP endpoint is only surfaced on the type when you create the session with { mcp: true }. The default create() / use() now return SessionWithoutMcp (the runtime object is unchanged — session.mcp still exists at runtime — but it is no longer in the type).
Migration: read session.mcp only after creating with { mcp: true }.
d17a268: Surface the resolved workbench config on Tool Router sessions.
Session.workbench is now populated from the API response (on create/retrieve/attach/update). It exposes the resolved workbench config, e.g. session.workbench?.enable (defaults to true server-side).This lets callers create a session with the remote workbench disabled (workbench: { enable: false }) and detect that state — the foundation for running code in a sandbox you own via the experimental @composio/experimental/workbench helpers.
sandbox for session code-execution configuration while continuing to accept the existing workbench alias.triggers.parse() to parse and optionally verify incoming webhook requests.triggers.setWebhookSubscription() to create or update the project webhook subscription from the TypeScript SDK.[0.13.0] - 2026-05-07
Highlights
Notes
@composio/*@0.7.0 was published briefly and has been deprecated on npm in favour of 0.8.0.
[0.12.0] - 2026-04-28
Changed
Migration
composio = Composio(api_key=...) # auto upload/download was on by default
composio = Composio(
api_key=...,
dangerously_allow_auto_upload_download_files=True,
file_upload_dirs=["/path/to/allowed/dir"], # optional; defaults to
~/.composio/temp
file_download_dir="/path/to/downloads", # optional
)
Notes
{ error } results without changing successful payloads. Custom provider subclasses overriding executeToolCall or handleToolCalls may require updates because these methods now accept session targets.a0bef5d: Bump @composio/client to 0.1.0-alpha.74.
dfd7a08: Add per-request cancellation to public SDK methods via a new ComposioRequestOptions ({ signal?: AbortSignal }) trailing argument, plus a typed ComposioRequestCancelledError for detecting caller-initiated aborts.
Without this, a slow tools.get or tools.execute had no way to be cancelled — a 100s search would block the calling agent indefinitely. The new shape:
try {
const tools = await composio.tools.get(
'user_1',
{ search: 'send email', limit: 50 },
{ signal: AbortSignal.timeout(5_000) }
);
} catch (err) {
if (err instanceof ComposioRequestCancelledError) {
return;
}
throw err;
}
The signal is forwarded to the underlying @composio/client fetch. Any abort error (APIUserAbortError, AbortError, or DOMException(name='AbortError')) coming back is normalized to ComposioRequestCancelledError so callers can instanceof-detect cancellation without unwrapping nested causes. Catch-and-wrap paths in tools.execute / tools.getRawComposioToolBySlug / toolkits.get re-throw the cancellation error rather than remapping it to ComposioToolExecutionError / ComposioToolNotFoundError / ComposioToolkitFetchError.
Wired through on:
get, getRawComposioTools, getRawComposioToolBySlug, getRawToolRouterSessionTools, execute, executeSessionTool, getToolsEnum, getInput, proxyExecuteget, listCategorieslist, create, get, update, delete, updateStatus, enable, disablelist, get, delete, refresh, updateStatus, enable, disable, updatelistActive, create, update, delete, enable, disable, listTypes, getType, listEnumcreate, list, get, delete, update, generatecomposio.create / composio.use, composio.toolRouter.create / .use) — long-running session-creation pathsauthorize, toolkits, search, execute, proxyExecute, updateNative tool execution is cancelled by the SDK (the underlying fetch is aborted). Custom tools are different — the SDK can't preempt user-supplied JavaScript. Two affordances are added so callers get sensible behavior anyway:
signal.aborted is true before the user's execute runs, the SDK throws ComposioRequestCancelledError and never invokes user code.AbortSignal is exposed via SessionContext.signal for Tool Router custom tools. Long-running implementations can wire ctx.signal into their own fetch (or any abortable IO) to abort mid-execution; the resulting AbortError is normalized to ComposioRequestCancelledError by the SDK.import { experimental_createTool } from '@composio/core';
const longRunningFetch = experimental_createTool('LONG_RUNNING_FETCH', {
name: 'Long-running fetch',
description: 'Fetches a URL with cooperative cancellation',
inputParams: z.object({ url: z.string() }),
execute: async (input, ctx) => {
// Pass ctx.signal into fetch so a session.execute(...) abort cancels
// the in-flight HTTP request mid-flight.
const resp = await fetch(input.url, { signal: ctx.signal });
return { result: await resp.json() };
},
});
025a657: Drop CommonJS entrypoints and publish the TypeScript SDK packages as ESM-only packages. This is a breaking change within the existing 0.x release line: consumers must use Node.js 22.22.3 or newer. CommonJS callers can only rely on Node's native require(esm) interop, and the SDK no longer ships custom CommonJS compatibility machinery or .cjs artifacts.
4b76dbf: Remove the deprecated uuid field from the auth config retrieve/list response type.
The platform has removed the deprecated V1/V2 UUID-mirror field from V3 API responses (it was a mirror of the canonical nanoid id). The SDK no longer reads or re-exposes uuid on AuthConfigRetrieveResponse (and therefore on the items of AuthConfigListResponse).
This is technically a breaking change to the SDK response type: consumers should use id instead of uuid. The expectedInputFields field is unaffected — it remains a top-level field on the API response.
552859a: Expose search and showDisabled filters on authConfigs.list().
23f9053: Replace chalk with picocolors for colored error and log output. The two render identically, but picocolors is a fraction of the size (~0.8 kB gzipped vs chalk's much larger footprint), shrinking the bundled package.
507318d: Add a provider-agnostic JSON-schema property-key sanitizer: sanitizeSchemaPropertyKeys(schema, policy), restoreOriginalKeys(value, mapping), mappingHasRenames(mapping), and the KeyMapping / KeySanitizationPolicy types.
Some providers constrain the characters and length of tool input_schema property keys and reject the whole request on a single violation. This utility rewrites offending keys to conforming aliases (recursing through properties, array items/prefixItems, the composition keywords allOf/anyOf/oneOf and not/if/then/else, plus additionalProperties/patternProperties/$defs/contains) and records a schema-shaped reverse mapping so the original parameter names can be restored before execution. The constraint is injected as a KeySanitizationPolicy, so the traversal, collision handling, prototype safety, and depth cap stay provider-agnostic. The @composio/anthropic provider now consumes it.
6a4cb54: Preserve root $defs / definitions blocks on tool parameter schemas and dereference them in the Node file modifier, so auto file upload/download detection works when file_uploadable or file_downloadable is hidden behind an internal $ref.
cbbad15: Improve Zod compatibility at the SDK schema boundary. Custom tools now convert both zod/v3 and Zod v4 schemas to JSON Schema correctly instead of degrading Zod v4 object schemas to empty schemas. @composio/core now exposes jsonSchemaToZodShape via @composio/core/utils/json-schema, and the Claude Agent SDK provider uses that core subpath instead of converting to a full Zod object and casting .shape out of it.
Updated dependencies [025a657]
Sensitive path blocking — Before auto-uploading a local file, paths are checked against a built-in denylist (segments like .ssh, .aws, etc., plus risk
.ssh, .aws, etc., plus risky filenames). Configurable via Composio(..., sensitive_file_upload_protection=..., file_upload_path_deny_segments=...).@before_file_upload modifiers — File path hooks are only via modifiers (same idea as @before_execute): decorate a function, pass it in modifiers=[...] on tools.get / tools.execute (and tool router session.tools(...)). Hooks are composed with merge_before_file_upload and run when substituting file_uploadable paths, before before_execute modifiers.before_file_upload= on Composio, get, execute, or the tool router; FileHelper no longer stores a default hook on the client—only the merged modifier hook (or None) per call.SensitiveFilePathBlockedError / FileUploadAbortedError (and related) when a path is blocked or a hook returns False.before_file_upload and glue exported from the package as needed.Version note: composio is 0.11.6 in pyproject.toml, with Python >=3.10.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
fa933a6: Fix the homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch
homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....This project publishes no release notes. Between v0.11.0 and v0.11.1 there were 242 commits, 176 of them substantive:
nodeVersion: ['current'] to echo $(.nvmrc) (#2514)…and 156 more.
ac6bbab: Register each tool with its complete schema, so root rules reach the Claude Agent SDK.
ac6bbab: Register each tool with its complete schema, so root rules reach the Claude Agent SDK.
The provider used to register a raw property shape. A raw shape is only the map of named properties, so it cannot carry any root rule. additionalProperties and patternProperties were dropped before the SDK saw them.
Two things went wrong because of that. Free-form object arguments lost their content. An argument the tool never declared was quietly removed, and the tool ran anyway.
Both are fixed. The provider now registers the whole object schema.
What no longer works
An undeclared argument no longer passes silently.
// The tool declares `to` and nothing else. The model also sends `hallucinated`.
{ to: 'someone@example.com', hallucinated: 'value' }
// before: `hallucinated` was stripped, and the tool ran with { to: '...' }
// now: the caller receives an error result, and the tool does not run
Tools with no input parameters behave the same way. They stay closed and reject every argument.
What to do instead
If the model should be allowed to send extra keys, say so in the tool schema:
{
"type": "object",
"properties": { "to": { "type": "string" } },
"additionalProperties": true
}
If an argument is one the tool really accepts, declare it in properties. Rejection is usually the better outcome, because the agent sees the error and can correct itself instead of running with a silently dropped argument.
This brings the provider in line with @composio/vercel, @composio/langchain, and @composio/llamaindex, which already registered complete schemas.
@composio/vercel. The ai peer dependency range is now ^6.0.0 || ^7.0.0 (AI SDK 5 is no longer supported; both v6 and v7 are covered by e2e compatibility tests).userId when creating trigger instances so trigger 2FA flows can verify connected account ownership.composio.tools.createCustomTool(...) in-memory registry API. Use Tool Router custom tools via experimental_createTool, experimental_createToolkit, and composio.create(..., { experimental: { customTools, customToolkits } }) instead.22a9171: Defer telemetry batch and error sends so instrumentation does not wait for telemetry network requests before returning SDK results or rethrowing SDK errors.
93b67e8: Fix automatic file upload/download substitution for file schemas that accept either a single file or a list of files.
The file modifier now selects composed-schema branches by runtime value shape, so anyOf(file, array<file>) uploads or downloads each file when the tool receives a list while preserving existing single-file behavior.
b69cef1: Tolerate dangling $ref pointers in tool schemas the Composio API ships without a matching $defs entry. Some toolkits (e.g. GMAIL_FETCH_EMAILS) emit outputParameters with "$ref": "#/$defs/FetchEmailsResponse" while never declaring a top-level $defs block. After the strict resolver shipped with the previous Mastra fix, this caused composio.tools.get(...) to throw JsonSchemaRefResolutionError upfront, making every Gmail / Slack / Google-Calendar tool unusable through MastraProvider. The SDK now degrades the unresolvable branch to a permissive object schema and surfaces a single observability warning per (toolSlug, ref) pair instead of crashing.
dereferenceJsonSchema accepts a new optional second argument { onUnresolved?: 'throw' | 'sentinel'; onReplace?: (ref, reason) => void }. Default behavior is unchanged ('throw') — first-party / custom-tool schemas with a typo'd $ref still surface as a hard error. Pass 'sentinel' to replace unresolved branches with the cycle-break sentinel ({ type: 'object', additionalProperties: true }) that the resolver already uses for $ref cycles. The replaced sentinel carries a default description hint so LLMs consuming the wrapped tool's schema get an in-band signal that the branch is opaque; a caller-provided description sibling overrides the default (Draft 2020-12 sibling-keyword merge). Safety caps (MAX_REF_CHAIN_DEPTH, MAX_NODE_DEPTH) keep throwing in both modes. New UnresolvedRefStrategy, UnresolvedRefReason, and DereferenceJsonSchemaOptions type exports.MastraProvider.wrapTool opts both inputParameters and outputParameters into 'sentinel' mode and emits one logger.warn per (toolSlug, ref) pair via the provider-scoped dedup Set. User-controlled segments in the warning (tool.slug, toolkit.slug, ref) are JSON.stringifyd to neutralize embedded newlines / ANSI escapes / control bytes that could otherwise forge log lines (CWE-117). A matching one-shot telemetry event (composio.mastra.wrapTool.danglingRef) fires next to the warn so the Composio team has aggregate visibility into which toolkits are affected; the event respects COMPOSIO_DISABLE_TELEMETRY=true.telemetry instance from @composio/core is now publicly re-exported alongside logger, so providers can emit aggregate signals without reaching into the package's internals.$defs / definitions continue to be inlined exactly as before — no regression in the type-info preservation contract introduced by the previous Mastra fix.1ba66ca: Fix tools.execute (and tools.get / tools.list) failing with a ZodError for every tool from an MCP-backed toolkit (e.g. granola_mcp, apify_mcp, tavily_mcp).
MCP toolkits don't declare an output schema, and the Composio API serializes that as output_parameters: {}. The SDK's ParametersSchema required { type: 'object', properties: {...} }, so transformToolCases rejected the response and getRawComposioToolBySlug — called by execute and the list path — threw.
The SDK now normalizes empty ({}), null, and missing input_parameters / output_parameters payloads to undefined before validation. outputParameters was already declared .optional() in the public Tool type, so this preserves the contract: "undefined means no declared schema." Tools that do declare a schema continue to be validated strictly.
No public API change; no toolkit allow-list.
ce4b213: fix(providers): normalize string tool-call arguments across all providers
Models occasionally emit tool-call arguments as a JSON string instead of an
object (most visibly with COMPOSIO_MULTI_EXECUTE_TOOL on the Vercel AI SDK),
which broke downstream validation with errors like
tool_use.input: Input should be a valid dictionary.
@composio/core now exposes a single normalizeToolArguments helper, and every
provider routes model-supplied arguments through it. Object payloads pass
through unchanged, JSON strings are parsed, empty/null payloads become {},
and anything that cannot resolve to an object throws a typed
ComposioInvalidToolArgumentsError instead of a raw SyntaxError or a silently
forwarded malformed string. This replaces the inconsistent per-provider guards
that previously existed only in vercel, cloudflare and openai-agents.
This project publishes no release notes. Between v0.10.6 and v0.11.0 there were 250 commits, 208 of them substantive:
…and 188 more.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
This project publishes no release notes. Between v0.10.5 and v0.10.6 there were 22 commits, 19 of them substantive:
This project publishes no release notes. Between v0.10.5 and v0.10.6 there were 22 commits, 19 of them substantive:
tsdown + introduce e2e tests (#2360)composio.triggers.verifyWebhooks (#2361)This project publishes no release notes. Between v0.10.4 and v0.10.5 there were 4 commits, 4 of them substantive:
This project publishes no release notes. Between v0.10.4 and v0.10.5 there were 4 commits, 4 of them substantive:
auto_upload_download_files boolean flag to Composio() (#2334)This project publishes no release notes. Between v0.10.2 and v0.10.4 there were 125 commits, 115 of them substantive:
This project publishes no release notes. Between v0.10.2 and v0.10.4 there were 125 commits, 115 of them substantive:
…and 95 more.
503b50a: Refresh runtime dependencies across the TypeScript SDK packages.
58bc93b: Refresh dependency ranges and lockfiles across the workspace.
homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....@composio/core >=0.16.0 <1.0.0 for the shared schema normalizer.This project publishes no release notes. Between v0.10.1 and v0.10.2 there were 133 commits, 114 of them substantive:
…and 94 more.
58bc93b: Refresh dependency ranges and lockfiles across the workspace.
homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....required entries before provider tool schemas are emitted.homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....required entries before provider tool schemas are emitted.homepage links in these packages' package.json. They pointed at github.com/ComposioHQ/composio/tree/main/..., but the default branch is next and no main branch exists, so every link 404'd on npm and in editor tooltips. They now point at tree/next/....@mastra/core peer dependency range.This project publishes no release notes. Between v0.10.0 and v0.10.1 there were 3 commits, 3 of them substantive:
composio generate --toolkits [toolkits] support, for both ts and py (#2252)025a657: Drop CommonJS entrypoints and publish the TypeScript SDK packages as ESM-only packages. This is a breaking change within the existing 0.x rel…
require(esm) interop, and the SDK no longer ships custom CommonJS compatibility machinery or .cjs artifacts.require(esm) interop, and the SDK no longer ships custom CommonJS compatibility machinery or .cjs artifacts.additionalProperties: true), enum/const are widened to also admit null, and required is dropped (APIs omit unset fields rather than returning null for them). All of these only widen what validates, so previously-valid output is unaffected.require(esm) interop, and the SDK no longer ships custom CommonJS compatibility machinery or .cjs artifacts.zod/v3 and Zod v4 schemas to JSON Schema correctly instead of degrading Zod v4 object schemas to empty schemas. @composio/core now exposes jsonSchemaToZodShape via @composio/core/utils/json-schema, and the Claude Agent SDK provider uses that core subpath instead of converting to a full Zod object and casting .shape out of it.require(esm) interop, and the SDK no longer ships custom CommonJS compatibility machinery or .cjs artifacts.507318d: Harden the Anthropic tool property-key sanitizer:
allOf/anyOf/oneOf, not/if/then/else), prefixItems tuples, additionalProperties/patternProperties, and $defs/definitions — not just top-level properties and array items. Previously a single illegal key under one of these would still 400 the entire request. Renames inside composition keywords are restored correctly (they fold into the value level they share).$ref keys restored. wrapTool now dereferences internal $ref/$defs (leniently — a dangling ref degrades to a permissive schema instead of throwing) before sanitizing, so keys reachable only through a reference are both made compliant and restored. Only genuinely dynamic positions (additionalProperties, patternProperties, contains) remain rewrite-only.@composio/core (sanitizeSchemaPropertyKeys / restoreOriginalKeys); this provider supplies only the Anthropic key constraint as a policy, reusing core's prototype-pollution guard and depth cap.Object.prototype member (__proto__, constructor, toString, hasOwnProperty, …) no longer throws or silently corrupts the payload sent to the backend. Object.prototype is never mutated.{1,64} length bound.debug logs when it rewrites a tool's schema keys and when it restores them at execution time, and the wrap-then-execute (same provider instance) contract for key restoration is documented.c90ae95: Sanitize tool input_schema property keys that violate Anthropic's ^[a-zA-Z0-9_.-]{1,64}$ constraint before sending tools to the Messages API, then restore the original names when the tool is executed.
A single non-conforming key previously made Anthropic reject the entire tools array with HTTP 400, taking down every other tool in the same request. This commonly happened with OneDrive's Microsoft Graph OData parameters ($top, $filter, @microsoft.graph.conflictBehavior) and with over-long flattened keys from tools such as Zoom. Offending keys are now rewritten to conforming aliases ($ → dollar_, @ → at_, any other illegal character → _, keys longer than 64 characters truncated with a deterministic suffix), recursing through nested object properties and array items schemas. The original parameter names are restored before the call reaches the Composio backend, so the model sees dollar_top while the backend still receives $top. Schemas whose keys already conform are left unchanged.
42ebff3: feat(connected-accounts): namespace SHARED-connection surface under experimental
Aligns the TypeScript SDK with the experimental wire shape used by Shared Connections. The flat accountType / aclConfigForShared options on connectedAccounts.link() and session.authorize() have moved under a single experimental block, and connectedAccounts.updateAcl() has moved off the class onto a top-level experimental_updateAcl(composio, id, opts) export — same precedent as experimental_createTool / experimental_createToolkit.
The experimental namespace is the signal that the shape may change in future releases. Pinning a SHARED connection in a session config (connectedAccounts: { gmail: [...] }) and direct execute by connectedAccountId are unchanged — only the connection-create / patch / authorize surfaces are namespaced.
Also surfaces the accountType filter on connectedAccounts.list() so SHARED connections can be listed without dropping to the raw client. The wire keeps this as a flat query param, so the SDK keeps it flat too.
@composio/client bumped from 0.1.0-alpha.71 → 0.1.0-alpha.72 so the generated typed client carries the Experimental namespaces for link.create, toolRouter.session.link, and connectedAccounts.patch.
Caller migration:
// before
await composio.connectedAccounts.link('user_id', 'auth_config_id', {
accountType: 'SHARED',
aclConfigForShared: { allowAllUsers: true },
});
await composio.connectedAccounts.updateAcl('ca_abc', { allowAllUsers: true });
await session.authorize('github', {
accountType: 'SHARED',
aclConfigForShared: { allowAllUsers: true },
});
// after
await composio.connectedAccounts.link('user_id', 'auth_config_id', {
experimental: {
accountType: 'SHARED',
aclConfigForShared: { allowAllUsers: true },
},
});
await experimental_updateAcl(composio, 'ca_abc', { allowAllUsers: true });
await session.authorize('github', {
experimental: {
accountType: 'SHARED',
aclConfigForShared: { allowAllUsers: true },
},
});
// new — list SHARED connections
const shared = await composio.connectedAccounts.list({
accountType: 'SHARED',
userIds: ['user_creator'],
});
This project publishes no release notes. Between v0.9.5-rc.1 and v0.10.0 there were 76 commits, 65 of them substantive:
…and 45 more.
Nothing published for this version
Nothing published for this version
This project publishes no release notes. Between v0.9.4 and v0.9.5-rc.1 there were 6 commits, 6 of them substantive:
This project publishes no release notes. Between v0.9.4 and v0.9.5-rc.1 there were 6 commits, 6 of them substantive:
Nothing published for this version
ce4b213: fix(providers): normalize string tool-call arguments across all providers
ce4b213: fix(providers): normalize string tool-call arguments across all providers
Models occasionally emit tool-call arguments as a JSON string instead of an
object (most visibly with COMPOSIO_MULTI_EXECUTE_TOOL on the Vercel AI SDK),
which broke downstream validation with errors like
tool_use.input: Input should be a valid dictionary.
@composio/core now exposes a single normalizeToolArguments helper, and every
provider routes model-supplied arguments through it. Object payloads pass
through unchanged, JSON strings are parsed, empty/null payloads become {},
and anything that cannot resolve to an object throws a typed
ComposioInvalidToolArgumentsError instead of a raw SyntaxError or a silently
forwarded malformed string. This replaces the inconsistent per-provider guards
that previously existed only in vercel, cloudflare and openai-agents.
b69cef1: Tolerate dangling $ref pointers in tool schemas the Composio API ships without a matching $defs entry. Some toolkits (e.g. GMAIL_FETCH_EMAILS) emit outputParameters with "$ref": "#/$defs/FetchEmailsResponse" while never declaring a top-level $defs block. After the strict resolver shipped with the previous Mastra fix, this caused composio.tools.get(...) to throw JsonSchemaRefResolutionError upfront, making every Gmail / Slack / Google-Calendar tool unusable through MastraProvider. The SDK now degrades the unresolvable branch to a permissive object schema and surfaces a single observability warning per (toolSlug, ref) pair instead of crashing.
dereferenceJsonSchema accepts a new optional second argument { onUnresolved?: 'throw' | 'sentinel'; onReplace?: (ref, reason) => void }. Default behavior is unchanged ('throw') — first-party / custom-tool schemas with a typo'd $ref still surface as a hard error. Pass 'sentinel' to replace unresolved branches with the cycle-break sentinel ({ type: 'object', additionalProperties: true }) that the resolver already uses for $ref cycles. The replaced sentinel carries a default description hint so LLMs consuming the wrapped tool's schema get an in-band signal that the branch is opaque; a caller-provided description sibling overrides the default (Draft 2020-12 sibling-keyword merge). Safety caps (MAX_REF_CHAIN_DEPTH, MAX_NODE_DEPTH) keep throwing in both modes. New UnresolvedRefStrategy, UnresolvedRefReason, and DereferenceJsonSchemaOptions type exports.MastraProvider.wrapTool opts both inputParameters and outputParameters into 'sentinel' mode and emits one logger.warn per (toolSlug, ref) pair via the provider-scoped dedup Set. User-controlled segments in the warning (tool.slug, toolkit.slug, ref) are JSON.stringifyd to neutralize embedded newlines / ANSI escapes / control bytes that could otherwise forge log lines (CWE-117). A matching one-shot telemetry event (composio.mastra.wrapTool.danglingRef) fires next to the warn so the Composio team has aggregate visibility into which toolkits are affected; the event respects COMPOSIO_DISABLE_TELEMETRY=true.telemetry instance from @composio/core is now publicly re-exported alongside logger, so providers can emit aggregate signals without reaching into the package's internals.$defs / definitions continue to be inlined exactly as before — no regression in the type-info preservation contract introduced by the previous Mastra fix.ce4b213: fix(providers): normalize string tool-call arguments across all providers
Models occasionally emit tool-call arguments as a JSON string instead of an
object (most visibly with COMPOSIO_MULTI_EXECUTE_TOOL on the Vercel AI SDK),
which broke downstream validation with errors like
tool_use.input: Input should be a valid dictionary.
@composio/core now exposes a single normalizeToolArguments helper, and every
provider routes model-supplied arguments through it. Object payloads pass
through unchanged, JSON strings are parsed, empty/null payloads become {},
and anything that cannot resolve to an object throws a typed
ComposioInvalidToolArgumentsError instead of a raw SyntaxError or a silently
forwarded malformed string. This replaces the inconsistent per-provider guards
that previously existed only in vercel, cloudflare and openai-agents.
### Patch Changes - Updated dependencies [42ebff3] - @composio/core@0.10.0
### Patch Changes - Updated dependencies [84a3a07] - Updated dependencies [c358ffa] - @composio/core@0.9.1
84a3a07: Add accountType and per-user ACL support for SHARED connected accounts.
accountType on create: composio.connectedAccounts.link(userId, authConfigId, { accountType: 'SHARED' }) creates a SHARED connection. Default remains PRIVATE. A SHARED connection can be used by other userIds, but only when the connection is explicitly pinned in a tool-router session's config and only when the requesting userId passes the connection's ACL.accountType on retrieve: get() and list() responses now include accountType ('PRIVATE' | 'SHARED').aclConfigForShared on create + retrieve: per-user ACL block — { allowAllUsers, allowedUserIds, notAllowedUserIds }. On responses the field is undefined when the caller isn't authorised to see the ACL, so callers can distinguish "I can't see the ACL" from "ACL is the default deny-by-default state".updateAcl() method (new): composio.connectedAccounts.updateAcl(nanoid, { allowAllUsers, allowedUserIds, notAllowedUserIds }) writes the ACL via PATCH. PATCH semantics — omit a field to leave it unchanged; pass an empty array to clear an allow/deny list. At least one field required. Calling on a PRIVATE connection raises ComposioAclOnlyForSharedError (400).ToolRouterSession.authorize() options gain accountType + aclConfigForShared, so a SHARED connection with an ACL can be created in one call from inside a tool-router session.ACL resolution rule (deny wins):
userId ∈ notAllowedUserIds → DENYallowAllUsers === true → ALLOWuserId ∈ allowedUserIds → ALLOWLimits: each ACL list accepts up to 1000 entries; each userId is 1..256 characters. The SDK enforces these caps at the input boundary.
New error classes:
ComposioSharedAccessDeniedError (403) — surfaces from direct connectedAccountId execution paths when the requesting user fails the ACL.ComposioAclOnlyForSharedError (400) — ACL fields sent on a PRIVATE connection.ComposioSharedConnectionNotAccessibleError (400) — tool-router session create / PATCH with a pinned SHARED connection the session user cannot use.No breaking changes. Existing link() callers without the new options get a PRIVATE connection exactly as today; existing get() / list() callers see new optional fields.
The Python SDK mirror ships in a separate PR.
c358ffa: Fix false-positive initiate() deprecation warning for custom auth configs (SEC-339 follow-up).
composio.connectedAccounts.initiate() previously emitted a one-time console.warn on every redirectable-OAuth response, regardless of whether the auth config was Composio-managed (subject to the 2026-07-03 cutover) or custom (unaffected). The wording was conditional ("If this auth config is Composio-managed…") so callers using their own OAuth apps could ignore it, but the warning still printed and caused noise in logs.
Apollo already emits the SEC-339 Deprecation / Sunset / Link rel="deprecation" headers (RFC 9745 / RFC 8594) only on the retiring branch — managed + redirectable OAuth. The SDK now reads the Deprecation header from the response (via APIPromise.withResponse()) and gates the warning on its presence. Custom auth configs and non-OAuth schemes get a clean response from the server and now stay silent in the SDK as well.
initiate() calls against custom OAuth auth configs or non-OAuth schemes (API key, bearer, basic). Managed-OAuth callers continue to get exactly one warning per process, now with revised wording that points at the response's Sunset header for the precise cutover date.initiate() returns the same ConnectionRequest shape and respects the same allowMultiple guard. ComposioLegacyConnectedAccountsEndpointRetiredError continues to surface from the 400 retired-path response.mockApiPromiseWithHeaders() in connectedAccounts.test.ts wraps a value as an APIPromise-shaped thenable so the new tests can simulate apollo's header behavior. Pre-existing initiate tests using mockResolvedValueOnce continue to pass via the SDK's defensive fallback when withResponse is absent on the mock.Python SDK gets the matching change in the same release train.
Bumped to align with @composio/core@0.9.0 for the Tool Router release train. No public-API change in this provider package.
@composio/core@0.9.0 for the Tool Router release train. No public-API change in this provider package.@composio/core@0.9.0 for the Tool Router release train. No public-API change in this provider package.cc673b6: Resolve internal JSON Schema $ref pointers (#/$defs/... and #/definitions/...) before handing tool parameters to @mastra/schema-compat. Composio tools whose schemas use $defs/definitions — legal under Draft 7 and 2020-12 — no longer trigger the AJV can't resolve reference … error, and the resolved type information from $defs survives the JSON-Schema → Zod → JSON-Schema round-trip instead of being silently degraded to a permissive anyOf.
dereferenceJsonSchema helper exported from @composio/core performs the inline expansion. It deep-clones the input, walks every applicator reflectively (so future JSON Schema keywords are covered), shallow-merges sibling keywords next to $ref per Draft 2020-12 semantics, breaks cycles with { type: 'object', additionalProperties: true } (matching the upstream guidance in mastra-ai/mastra#15341), and strips $defs/definitions once everything reachable is inlined. External (http:///https://) $ref pointers are left untouched.@composio/mastra calls the helper inside wrapTool for both inputParameters and outputParameters.@mastra/schema-compat dependency floor raised to ^1.2.9 so users automatically receive PR #15400's recursive-$ref handling.Updated dependencies [c9b6525]
Updated dependencies [cc673b6]
Updated dependencies [9f14971]
Updated dependencies [81f8027]
Updated dependencies [711a703]
Updated dependencies [bccd32b]
Updated dependencies [bccd32b]
Updated dependencies [07c9bab]
Updated dependencies [3ece424]
9f14971: Add migration tooling for the link auth migration: callers using composio.connectedAccounts.initiate() for Composio-managed auth configs on redirectable OAuth schemes (OAuth1, OAuth2, DCR_OAUTH) now get a typed error and a one-time deprecation warning ahead of the 2026-07-03 all-orgs cutover.
ComposioLegacyConnectedAccountsEndpointRetiredError, exported from @composio/core. Thrown by initiate() when the underlying POST /api/v3/connected_accounts returns a 400 indicating the retiring path. Carries the migration message and a possibleFixes block pointing at link() and the migration guide.console.warn from initiate() when the response indicates a redirectable OAuth scheme. Wording is conditional ("If this auth config is Composio-managed…") so callers using custom OAuth apps or non-OAuth schemes can ignore it without ambiguity.initiate() now flags the retirement explicitly and points at link() for the affected combination. Custom auth configs and non-OAuth schemes (API key, bearer, basic) are unaffected.No behavior change for any caller outside the Composio-managed-OAuth combination — initiate() continues to call the legacy endpoint, return the same ConnectionRequest shape, and respect the allowMultiple guard.
81f8027: Add session.update() method for partially updating session configuration after creation. Accepts the same config shape as create() and mutates the session in-place. Available in both TypeScript and Python SDKs.
711a703: Add expanded ToolRouter session controls for agent workflows. composio.create()
now creates a fresh session on each call for better isolation and observability,
while composio.use() can resume an existing session for multi-turn
conversations. Sessions can preload frequently used tools, expose custom tools
directly from session.tools(), use a direct-tools preset for agents that know
their tool set upfront, and update session config mid-session with
session.update().
07c9bab: connectedAccounts now accepts both string and string[] per toolkit.
A single string is automatically coerced to an array to match the v3.1 API wire format. Existing callers passing { gmail: "ca_xxx" } continue to work without changes. Only one account per toolkit is allowed when multi-account mode is disabled.
3ece424: Add custom tools support to composio.use().
composio.use(id, { customTools, customToolkits }): Reuse an existing session and optionally bind SDK-local custom tools for search and execution.use() now correctly passes inlineCustomToolsPayload and preloadedCustomToolSlugs to the session, enabling custom tool execution and preloading on rehydrated sessions.CustomToolsMap.tools: The map now caches the raw CustomTool[] array for future inline re-injection on v3.1 search/execute requests.c9b6525: Fix connectedAccounts TypeScript type so a single string per toolkit is actually accepted by the public API.
Previously the schema's .transform() made ToolRouterCreateSessionConfig['connectedAccounts'] resolve to Record<string, string[]> (the post-transform output), so TypeScript users still got Type 'string' is not assignable to type 'string[]' even though the runtime accepted strings. Coercion now happens inside ToolRouter.create, mirroring the Python implementation, and the public type is Record<string, string | string[]>.
cc673b6: Resolve internal JSON Schema $ref pointers (#/$defs/... and #/definitions/...) before handing tool parameters to @mastra/schema-compat. Composio tools whose schemas use $defs/definitions — legal under Draft 7 and 2020-12 — no longer trigger the AJV can't resolve reference … error, and the resolved type information from $defs survives the JSON-Schema → Zod → JSON-Schema round-trip instead of being silently degraded to a permissive anyOf.
dereferenceJsonSchema helper exported from @composio/core performs the inline expansion. It deep-clones the input, walks every applicator reflectively (so future JSON Schema keywords are covered), shallow-merges sibling keywords next to $ref per Draft 2020-12 semantics, breaks cycles with { type: 'object', additionalProperties: true } (matching the upstream guidance in mastra-ai/mastra#15341), and strips $defs/definitions once everything reachable is inlined. External (http:///https://) $ref pointers are left untouched.@composio/mastra calls the helper inside wrapTool for both inputParameters and outputParameters.@mastra/schema-compat dependency floor raised to ^1.2.9 so users automatically receive PR #15400's recursive-$ref handling.bccd32b: Expose and document the Tool Router direct-tools preset via SessionPreset.DIRECT_TOOLS, with Python parity through SESSION_PRESET_DIRECT_TOOLS. Direct-tools examples now use the constants and keep the agent prompt generic while still asserting that only direct tools are exposed.
bccd32b: Document and tighten Tool Router preload behavior for app tools, preload.tools = "all", and SDK custom tools. Custom tool and toolkit preload hints now have clearer user-facing comments, direct custom tool descriptions now only state that search is not needed beforehand, examples assert the normalized LOCAL_* tool slugs exposed by session.tools(), and composio.use(..., customTools/customToolkits) reuses the same custom preload preparation path as session creation.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →