## Summary
The `composio dev auth-configs create` command fails when custom OAuth
credentials are spread at the top level of `auth_config`. The API
expects those values under `auth_config.credentials`.
Fixes#4058
## Changes
- Nests custom credentials under `auth_config.credentials`.
- Merges parsed `--scopes` into the same credentials object.
- Preserves `credentials: undefined` when neither credentials nor scopes
are supplied.
- Captures the typed repository request in the CLI test layer and
asserts the exact custom OAuth payload.
- Records the user-visible fix in the CLI changelog.
- Merges the current `next` branch without conflicts.
## Verification
- Reproduced on the old implementation: 1 of 8 focused tests failed
because `client_id` remained at `auth_config.client_id`.
- Verified the fix: 8 of 8 focused auth-config create tests passed.
- Full CLI package suite: 120 test files passed; 1,188 tests passed; 1
skipped.
- `pnpm typecheck`: 14 tasks passed across the 19 TypeScript packages in
scope.
- `pnpm run lint:packages`: passed with existing warnings only.
- Prettier check on all four changed files: passed.
The fresh-worktree CLI suite currently needs a temporary local Vitest
alias for the pre-existing `#ssrf_guard` resolver drift. That alias was
used only to run the suite and is not part of this PR.
## Release notes
`@composio/cli` is excluded from Changesets, so this PR updates
`ts/packages/cli/CHANGELOG.md` directly.
---------
Co-authored-by: jkomyno <alberto@composio.dev>
## Summary
Automated sync of backend data into the docs site. Triggered by:
`schedule`.
## What changed
- **Toolkit catalog** (`docs/public/data/toolkits.json`,
`toolkits-list.json`) — refreshed list of available toolkits, auth
schemes, and tools from the backend API
- **OpenAPI specs** (`docs/public/openapi.json`,
`docs/public/openapi-v3.json`, `docs/public/openapi-webhooks.json`) —
latest v3.1 and v3.0 API specifications plus the webhook-events spec,
fetched from production
- **API reference pages** (`docs/content/reference/api-reference/`,
`docs/content/reference/v3/api-reference/`) — regenerated index pages
for both API versions
- **Meta tools reference** (`docs/public/data/meta-tools.json`,
`docs/content/toolkits/meta-tools/*.mdx`) — updated meta tool schemas
and reference docs
---------
Co-authored-by: Sushmithamallesh <19796925+Sushmithamallesh@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
## Summary
- Reverts the quickstart portion of #4099, which replaced the framework
picker with a Python-only `uv` flow and left TypeScript readers without
a quickstart path (a gap the review on that PR also flagged).
- Restores the `QuickstartFlow` picker: OpenAI Agents, Claude Agent SDK,
and Vercel AI SDK, each with Python and TypeScript tabs and
`PackageInstall` steps.
- Keeps the parts of #4099 that made the page better:
- Sharper intro and the meta-tools explanation (sessions load app
schemas only when the agent needs them).
- Agent instructions that handle Connect Links and confirm destructive
actions, applied to all six code samples. The Claude Agent SDK
TypeScript sample previously shipped with no system prompt.
- "What just happened?" section with dual-language session persistence
notes (`session.session_id` / `session.sessionId`, `composio.use()`).
- Production callout for replacing `user_123` with a stable user ID.
- Logs API pointer and the "Adapt the example" cards.
- Deletes `docs/tests/static/quickstart.test.ts`, which #4099 added to
pin the Python-only structure.
## Validation
- All three TypeScript code blocks pass strict type-checking with the
docs compiler settings (same check Twoslash runs at build).
- Page renders correctly on the local dev server with all three
frameworks and both language tabs.
- Prose follows the good-docs-writing style guide (no em-dashes, "you"
voice, concept-first openings).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reverts the quickstart portion of #4099, which replaced the framework
picker (OpenAI Agents, Claude Agent SDK, Vercel AI SDK with Python and
TypeScript tabs) with a Python-only uv flow, leaving TypeScript readers
without a quickstart path.
Keeps the parts of #4099 that improved the page:
- Sharper intro and meta-tools explanation
- Agent instructions that handle Connect Links and confirm destructive
actions, applied to all six code samples (the Claude Agent SDK
TypeScript sample previously had no system prompt)
- "What just happened?" section, dual-language session persistence notes
- Production callout for replacing user_123 with a stable user ID
- Logs API pointer and "Adapt the example" cards
Deletes tests/static/quickstart.test.ts, which was added by #4099 to pin
the Python-only structure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a Skills page under a new **Skills** section in the docs nav.
## What it covers
A skill is an execution playbook for one concrete task — the tools it
needs, the order to call them in, and the pitfalls that make it fail.
The page leads with why that saves work (an agent rediscovering a tool
sequence and its failure modes pays for it in tokens and retries), then
covers what a skill contains and how to browse the catalog.
## Notes for review
- **No cURL examples.** Skills are dashboard-only today — there is no
public API. That cuts against the repo's usual preference for
machine-readable API docs, so the page calls the absence out explicitly
rather than leaving a reader hunting for an endpoint. Happy to revisit
when an API lands.
- **A screenshot is coming in a follow-up.** The page reads fine without
one, but the browsing section would benefit.
- Branched off `next` and targets `next`, per `docs/CLAUDE.md`.
- Nav placement is a new `---Skills---` section. Open to it living under
an existing section instead.
## Verification
- `bun run scripts/validate-links.ts` → 0 errors
- `bun run build` → exit 0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Applied the repo's good-docs-writing skill. The page broke its two hardest
rules: em-dashes are banned in this style and the page was built on them
(the sibling triggers page has zero), and a page with no example asked the
reader to take the whole mechanism on trust.
It now shows the search response carrying a plan, so the shape is concrete
before the prose explains it. The field names are checked against the
schema in composio_actions/utils/schemas.ts: recommended_plan_steps and
known_pitfalls are string arrays, difficulty is a string, and all three are
optional, present only when a skill covers the use case. The page says so,
since anything else would have readers destructure fields that may be absent.
Also cut the filler the audit checklist flags and made every definition a
sentence rather than a dash fragment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## Summary
- Reframes the docs Welcome page around two intents: build with Composio
or use Composio from an existing agent.
- Replaces the framework picker with a general Python quickstart and
routes other frameworks to focused provider guides.
- Adds a shared Agent plugins page for Codex and Claude Code, with
CLI-first setup and explicit MCP alternatives.
- Simplifies the sidebar into Get Started, Core concepts, Guides, Direct
execution (legacy), and Migration and security.
- Keeps the Welcome page readable through its `.md` endpoint and
clarifies intent routing in `llms.txt`.
## Review order
1. Welcome hero and the two intent cards.
2. Quickstart copy and runnable example.
3. Agent plugins, CLI, and MCP routing.
4. Sidebar grouping and progressive disclosure.
5. Agent-readable Welcome Markdown and regression tests.
## Validation
- `bun run test` — 208 passing
- `bun run types:check`
- `bun run lint:links` — 0 errors
- `bun run lint` — passes with pre-existing warnings only
- Dashboard badge styles compared against the live product UI
## Deliberately out of scope
- Docs eval harnesses or eval result files
- Search ranking, agent-index generation, or agent prompt changes
- Deleting pre-existing unused hero or quickstart components
- Unrelated lint warnings and docs content cleanup
This replaces #4085 with a one-commit, docs-only review surface. The
earlier PR accumulated implementation experiments and bot commentary
that made the intended change difficult to evaluate.
## Summary
- clarify that the SDK fetching examples default to `latest`
- distinguish REST v3 pinned defaults from REST v3.1 latest defaults
- replace the blanket production warning with output-consumer guidance
## Root cause
The legacy fetching guide used an unqualified `API` statement next to
SDK examples. That conflated SDK behavior with two REST API versions and
contradicted the current API reference.
No SDK package changes or changeset are required.
## Summary
- publish the first-party `composio` skill from the SDK repository
- route first by product (Composio For You or Composio Platform), then
by the requested job
- preserve progressive disclosure, credential safety, and a strict
evidence boundary
- explain exactly what the CLI, raw MCP, OpenAI/Codex plugin, and Claude
Code plugin make discoverable
## Starting point
This keeps Palash's generic router design and Soumya's canonical-link
and cross-language corrections from temporary-repository PRs
[#1](https://github.com/composio-temp/composio-skill/pull/1)
([Glen](https://app.tryglen.com/composio-temp/composio-skill/pull/1))
and [#2](https://github.com/composio-temp/composio-skill/pull/2)
([Glen](https://app.tryglen.com/composio-temp/composio-skill/pull/2)).
The maintained copy now lives with Composio's SDK, CLI, and docs source
instead of the temporary evaluation repository.
## Installation and discovery
Project-scoped, with agent selection when needed:
```bash
npx skills add ComposioHQ/composio --skill composio
```
Global, non-interactive install for the detected agent:
```bash
npx skills add ComposioHQ/composio --skill composio --global --yes
```
This public skill is installed explicitly. A raw MCP connection exposes
tools but does not install a skill. The CLI-owned `composio-cli` skill
and the OpenAI plugin-owned `composio-runtime` skill remain separate
because they own current CLI operation and hosted-versus-local runtime
routing, respectively. The Claude Code plugin does not bundle another
skill.
## Dashboard handoff
Dashboard Getting Started is one context handled by the generic Platform
route, not the skill's identity. When the developer already has the Step
1 `COMPOSIO_API_KEY`, the skill uses that existing project key, never
creates or replaces it, avoids `composio dev init`, and inspects the
real agent path and user identity before proposing code.
## Forward evaluation
Four blind requests were run through fresh agents:
1. Personal Cursor access to Gmail and Google Calendar passed the For
You/MCP route and avoided Platform credentials.
2. First-time Python setup initially overexposed advanced options; after
progressive-disclosure rules, the replay stayed on the minimum
SDK/session/auth path.
3. A GitHub 401 with a working project key remained a narrow
provider-auth debugging task and did not restart onboarding.
4. A TypeScript dashboard handoff initially invented repository facts;
after the evidence boundary, the replay preserved the key and requested
the actual framework and entrypoint.
These are manual routing and response-quality evaluations, not reusable
eval infrastructure. A live provider/OAuth run remains part of the
separate dashboard E2E gate.
## Validation
- skill-creator validator: passed
- canonical repository validator: 18 skills passed before this
documentation-only follow-up
- routing smoke test: 18/18 probes passed
- isolated local `skills add --copy`: passed
- current head CI: pending after the discovery clarification
- no SDK package, backend, CLI, database, or release-metadata changes
## Summary
- Repairs runnable TypeScript and Python examples for current backend
requirements, including authenticated MCP endpoints, current transports,
valid tool identifiers, provider limits, and resource uniqueness.
- Replaces placeholder resource IDs with explicit `COMPOSIO_EXAMPLES_*`
configuration and makes failed examples exit loudly.
- Adds `scripts/examples-provision.mjs` as an idempotent provisioning
check for a disposable examples project.
## Scope
- This PR no longer changes the Python SDK runtime or generated-client
dependency.
- Python remains pinned to the published `composio-client==1.43.0` in
`pyproject.toml`, `setup.py`, and `uv.lock`.
- The owned 2.x client integration is deferred until that client
completes its release guarantees and is explicitly published.
- The scheduled live workflow and manifest remain deferred until their
runner is tracked.
## Verification
- `make chk`
- `make tst` (`927 passed, 33 skipped`)
- Hosted checks rerun against commit `12691aca7`.
This PR:
- records the pending CLI entries under `0.3.3`
- restores the missing `0.3.2` boundary from its published source tag
- keeps `Unreleased` available for changes after the stable promotion
- verifies the release metadata with `pnpm validate:changesets` and
`pnpm test:release-workflow`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) 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.
# Releases
## @composio/core@0.16.0
### Minor Changes
- 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`.
```ts
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.
### Patch Changes
- a2f6b96: Add `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.
- c625edc: Reuse fetched tool schemas when provider-wrapped tools
execute to avoid a redundant retrieval request.
- Updated dependencies [5e57815]
- @composio/json-schema-to-zod@0.3.0
## @composio/json-schema-to-zod@0.3.0
### Minor Changes
- 5e57815: Keep the content of free-form object arguments, and apply
dynamic key rules only where they belong.
An object schema with no named properties, written as `{ "type":
"object" }` or `{ "properties": {} }`, used to become a strict empty
object. The only value that passed was `{}`. Any real payload was
rejected. Such an object is now open, so arbitrary content is accepted
and preserved. This works at the root, nested inside another object, and
inside array items.
Dynamic keys are now routed to the schemas that actually claim them.
Every matching `patternProperties` entry validates its key. A
schema-valued `additionalProperties` applies only to keys that no
declared property and no pattern matches. Before this change a key could
pass by satisfying an unrelated pattern instead of its own.
**What no longer works**
Converting a property-less object no longer gives you a schema that
rejects everything.
```ts
const schema = jsonSchemaToZod({ type: 'object' });
schema.parse({ anything: 1 });
// before: threw, because the result was z.object({}).strict()
// now: returns { anything: 1 }
```
**What to do instead**
If you want a property-less object to stay closed, say so in the JSON
Schema:
```ts
const schema = jsonSchemaToZod({ type: 'object', additionalProperties:
false });
schema.parse({ anything: 1 }); // throws, as before
```
Objects that name at least one property are unchanged. They still reject
unknown keys when `additionalProperties` is omitted.
## @composio/claude-agent-sdk@0.11.0
### Minor Changes
- 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.
```ts
// 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:
```json
{
"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/google@0.10.2
### Patch Changes
- a2f6b96: Add missing object types to nested Google GenAI tool schemas
so Gemini accepts function declarations with property maps. This release
requires `@composio/core >=0.16.0 <1.0.0` for the shared schema
normalizer.
## @composio/slim@0.16.0
### Patch Changes
- Updated dependencies [5e57815]
- @composio/json-schema-to-zod@0.3.0
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR:
- builds on top of https://github.com/ComposioHQ/composio/pull/4094
- bumps `composio` and all 12 provider distributions from `0.18.2` to
`0.19.0`
- keeps `python/composio/__version__.py` and `uv.lock` aligned with
package metadata
- documents recursive provider argument-presence preservation in the
existing cross-SDK changelog
- passes the release-workflow guard, Ruff, mypy, provider type
inference, and 1,167 Python tests
- builds and validates 26 wheel/sdist artifacts with Twine
- must be retargeted to `next` after #4094 merges
This PR:
- replaces the removed `/docs/tools-and-toolkits.md` health probe with
`/docs/how-composio-works.md`
- restores the scheduled docs health check after the tools page moved in
the sessions-first rewrite
- verifies the canonical endpoint returns `200 text/markdown`
- keeps the release stack unchanged
This PR:
- builds on #4081's cross-SDK JSON Schema conversion contract
- closes#4092 and supersedes the narrower CrewAI-only proposal in #4093
- validates and serializes CrewAI, LangChain, and LangGraph arguments
without renaming aliases, padding omitted optionals with `None`,
dropping explicit nulls, declared defaults, or dynamic extras
- forces framework validation for pattern-only and other zero-field
Pydantic schemas before tool execution
- returns structured CrewAI validation failures
The final correctness pass extends presence tracking recursively. It
combines each selected Pydantic model's `model_fields_set` with metadata
for JSON Schema-declared defaults, including nested objects, arrays,
maps, `anyOf`/`oneOf`, overlapping `patternProperties`, and
schema-valued `additionalProperties`.
It preserves literal `$ref`-shaped instance defaults and keeps dynamic
attributes and `model_extra` in their previous plain dict/list form.
Repeated dynamic-key materialization is presence-safe and does not turn
omitted nullable fields into explicit nulls.
## Verification
- Provider serialization and schema converter: 158 passed
- Provider, neighboring provider, and parser suites: 219 passed
- Full `nox -s chk`: Ruff and mypy passed
- Claude Fable 5 high adversarial gate: 359 tests plus union, reference,
overlapping-pattern, alias, reserved-key, recursion, and wide/deep
payload probes; `VERDICT: SHIP`
- `git diff --check` passed
Python package versioning remains part of the later Python release
workflow; this PR does not require a TypeScript Changeset.
Closes#4092.
This PR:
- fixes#4023, where Python provider Pydantic models silently reduced
property-less object arguments such as
`METABASE_POST_API_CARD.dataset_query` to `{}`
- fixes#4064, where the Zod and Effect converters rejected the same
free-form object content
- fixes#4022, where Google Gemini rejected nested property-bearing
schemas that omitted `type: "object"`
- supersedes https://github.com/ComposioHQ/composio/pull/4037 with
complete Python handling for open, closed, pattern-constrained, and
schema-valued dynamic keys
- supersedes https://github.com/ComposioHQ/composio/pull/4065 with one
cross-SDK contract covering Zod, Effect, Python, CLI validation, and
provider boundaries
- supersedes https://github.com/ComposioHQ/composio/pull/4077 by
incorporating its complete-schema Claude Agent SDK registration and root
constraint handling
- supersedes https://github.com/ComposioHQ/composio/pull/4047 with its
corrected Google-only normalization, including property-map and
reserved-key safety
- keeps https://github.com/ComposioHQ/composio/pull/4094 as a stacked
Python-provider follow-up because framework serialization and CrewAI
error handling are downstream of schema conversion
- includes minor Changesets for `@composio/core`,
`@composio/json-schema-to-zod`, and `@composio/claude-agent-sdk`; the
fixed release group also bumps `@composio/slim`, and `@composio/google`
carries a patch Changeset
A tool argument declared as a bare `{ "type": "object" }` could be
rejected, reduced to `{}`, or lose its root constraints depending on the
SDK boundary. This PR makes TypeScript and Python agree on free-form
objects and dynamic keys, and protects that agreement with a shared
corpus.
## Relationship to other work
### Superseded PRs
- #4037 fixes the narrow Python property-less-object reproduction by
allowing Pydantic extras, but does not enforce `patternProperties`,
schema-valued `additionalProperties`, or closed-object rejection. This
PR preserves the reported Metabase payload while enforcing the complete
object contract.
- #4065 fixes property-less objects in the Zod and Effect converters.
This PR incorporates that behavior, expands the dynamic-key semantics,
and proves parity with Python and the provider-facing paths.
- #4077 preserves root constraints through `ToolSchema.parse` and the
Claude Agent SDK. This PR incorporates that work and tests it against
the same cross-SDK corpus.
- #4047 targets #4022. This PR incorporates its Google schema
normalization with a structure-aware walker that preserves property
maps, instance values, and reserved keys.
### Follow-up and adjacent PRs
- #4094 is intentionally separate and stacked on this PR. This PR
defines which arguments a JSON Schema accepts and what
Pydantic/Zod/Effect preserve. #4094 handles the later framework boundary
where CrewAI, LangChain, and LangGraph serialize an already-validated
model, restoring aliases, distinguishing absent values from explicit
nulls and defaults, preserving dynamic extras, and returning structured
CrewAI validation failures.
- #4093 is the narrower CrewAI-only proposal for #4092. #4094
incorporates that fix and broadens it to the two other providers that
use the same generated Pydantic models, so #4093 is not duplicated here.
Keeping #4094 separate leaves this PR focused on the shared cross-SDK
conversion contract. It also isolates provider-specific framework hooks
and Python error-shape behavior, which have a different compatibility
and test surface and do not require TypeScript Changesets.
## What changed
- Property-less object schemas are open by default. Their content is
accepted and preserved at the root, in nested objects, and in array
items. An explicit `additionalProperties: false` still closes them.
- Objects that declare `properties` remain strict when
`additionalProperties` is omitted. This is Composio's established
tool-input behavior.
- `ToolSchema.parse` now accepts bare object roots, keeps root
`patternProperties`, accepts schema-valued `additionalProperties`, and
exposes optional `properties` in its public type.
- The Zod and Effect converters apply every matching `patternProperties`
schema. A schema-valued `additionalProperties` applies only to keys not
claimed by a declared property or pattern.
- Python validates dynamic values with Draft 7 JSON Schema before
Pydantic materializes defaults. This avoids coercive or incomplete
Pydantic representations changing whether input is accepted.
- Python supports local JSON Pointer references in dynamic-key schemas.
Unresolved, external, and anchored references fail during model
construction instead of silently weakening validation.
- Accepted Python dynamic keys remain available through both
`model_dump()` and attribute access, which is required by agentic
providers such as LangChain.
- Gemini now exposes free-form object arguments as `Dict[str, Any]` in
the generated callable signature.
- `@composio/google` adds missing object types before emitting Google
GenAI declarations while preserving property maps and instance-valued
schema annotations.
- The Claude Agent SDK provider registers the complete object schema
instead of a raw property map, so root rules reach MCP validation.
- The Effect converter reports dynamic-key schema defects while
compiling the schema, rather than as input errors blaming the arguments.
`@cfworker/json-schema` compiles `patternProperties` keys and resolves
`$ref` lazily, so the defect previously reached the caller as an input
validation failure. The scope matches Python's: `patternProperties`
keys, and references inside a `patternProperties` or schema-valued
`additionalProperties` subschema, followed transitively. References
elsewhere stay with the interpreter, so a dangling reference in a branch
validation never visits keeps working.
## Behavior changes
Three existing fail-open paths now reject invalid input:
1. Python agentic providers reject unknown keys when an object declares
properties and omits `additionalProperties`, instead of dropping those
keys. This also covers a property-less object that sets
`additionalProperties: false`, which Python previously left to
Pydantic's default `extra="ignore"`.
2. The Claude Agent SDK provider rejects undeclared arguments through
MCP validation instead of stripping them and running the tool.
3. The CLI rejects a tool schema whose `patternProperties` key is not a
valid regular expression, or whose dynamic-key subschema carries an
unresolvable reference, when it compiles the schema. This can fail a
call that previously succeeded: the interpreter surfaced such a defect
only if validation descended into that part of the schema, so a payload
that stayed out of the affected branch ran against a schema that could
not fully validate it.
Gemini is affected differently: it uses `json_schema_to_pydantic_type`
to build a callable signature, but does not use `json_schema_to_model`,
so the agentic-provider unknown-key behavior does not apply.
## Cross-SDK contract
The TypeScript and Python fixtures contain the same 24 cases and are
required to remain byte-identical:
```text
ts/packages/core/test/fixtures/json-schema-conversion/object-cases.json
python/tests/fixtures/json-schema-conversion/object-cases.json
```
The corpus covers open and closed objects, overlapping patterns,
schema-valued additional properties, empty schemas, enums, nested strict
containers, false schemas, type unions, local references, JSON integer
semantics, default materialization, and intentional Composio strictness.
Each case has one shared acceptance result. Converter-specific expected
outputs are separate because Zod and Pydantic materialize defaults while
Effect validates without transforming output. Any acceptance difference
requires an explicit divergence reason.
The decoding rules and the cross-language invariants have one
definition. `@composio/core` owns the loader at
`ts/packages/core/test/fixtures/json-schema-conversion/corpus.ts`, and
the Zod, Effect, and CLI suites re-export it rather than each carrying a
copy. It reads the corpus through `resolveJsonModule`, which is already
enabled in every consumer, so no test project needs `@types/node` to
load a fixture.
## Releases and documentation
- Three explicit minor Changesets cover `@composio/core`,
`@composio/json-schema-to-zod`, and `@composio/claude-agent-sdk`.
- `@composio/core` and `@composio/slim` are a fixed Changesets group, so
the core Changeset automatically schedules the matching minor release of
`@composio/slim`; no duplicate slim Changeset is required.
- `@composio/google` has a patch Changeset and requires `@composio/core
>=0.16.0 <1.0.0`, preventing installation with older Core versions that
lack the shared normalizer.
- `@composio/json-schema-to-effect-schema` is private. The CLI and CLI
local tools are excluded from Changesets, so the CLI's user-facing fix
is recorded in `ts/packages/cli/CHANGELOG.md`.
- Python does not use Changesets. It now declares `jsonschema>=4.23.0`
directly in both package metadata sources, and the generated `uv.lock`
is refreshed with the repository-pinned uv version.
- The docs changelog distinguishes Python agentic-provider behavior,
Gemini signature behavior, and Claude Agent SDK registration behavior.
## Verification
Local verification after the final implementation:
- Python: `nox -s tst` (`1002 passed, 34 skipped`) and `nox -s chk`
- Gemini provider: `32 passed` with the provider installed editable
- TypeScript: core `1088`, Zod `118`, Effect `65`, Claude Agent SDK
`24`, Google `14`, and focused CLI validation `9`
- Affected TypeScript typechecks
- `pnpm validate:changesets` and Changesets status against the PR exact
`next` base
- Detached Changesets versioning and a strict-peer packed consumer:
Core/Slim `0.16.0`, Google `0.10.2`, Zod `0.3.0`, and Claude Agent SDK
`0.11.0`
- Python sdist and wheel build, with both artifacts passing `twine
check`
- Prettier, Ruff formatting, `uv lock --check`, byte-identical fixture
comparison, and `git diff --check`
Closes#4022, #4023, and #4064.
The catalog is gated to selected orgs, so pointing every reader at a Skills
tab most of them cannot see would send them looking for a nav item that
isn't there. The capability itself is not gated — skills already reach every
org through the search response — so the rest of the page stands on its own.
The section was also stale on its own terms: it advertised app chips showing
per-toolkit counts, which read the catalog-stats endpoint that has since been
removed, and described browse ordering as "maturity / most-refined" after the
list was changed to say most used.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This PR:
- closes#4102
- disables Git 2.54 automatic maintenance for the docs data update job
while actions/checkout lacks the upstream fix
- prevents background maintenance from racing the shallow fetch of
docs/auto-update-data before create-pull-request pushes it
- keeps the change limited to the affected workflow
- verifies the workflow with actionlint v1.7.12
This PR:
- builds on top of https://github.com/ComposioHQ/composio/pull/4055
- resolves a tool's toolkit against what the CLI already knows — the
slugs baked in at build time
(https://github.com/ComposioHQ/composio/pull/4055) plus the ones this
machine has learned since — so the common case costs one small local
file read and no network. Every tool execution needed the toolkit, and
the catalog was ~800 KB and ~2 s of the wall time of a command that then
does one small API call
- resolves that local knowledge once per run: `Effect.cached` on the
catalog service means one file read, one parse, and one lookup set per
process, where a single `composio execute` resolves a toolkit four times
over (bootstrap telemetry, the execute context, the connection check,
the execution itself) and `--parallel` multiplies that by the number of
tools
- falls through to the catalog only for a slug that matches nothing
known, which is what a toolkit released after the binary looks like, and
records what it finds in `known-toolkit-slugs.json` — so it costs a
fetch once rather than every run. The recording forks at most once per
run: every miss in a run merges the same memoized fetch, so later forks
could only rewrite an identical file
- refreshes learned slugs in the background once they are older than a
week, which bounds how long a brand-new toolkit can be mistaken for a
shorter one sharing its prefix
- gives that background refresh ten seconds, then abandons it. A
successful command ends when the event loop drains, not on an explicit
`process.exit`, so an unbounded background fetch would hold the exiting
`composio` process open until the network answered — visible to `&&`
chains, `$(...)`, and CI step timing. An abandoned refresh costs a fetch
on a later run, nothing more
- keeps `FORCE_USE_CACHE` semantics intact: the learned file is derived
data, not saved API responses, so it is read unconditionally. That
distinction is documented in the module and in the README
- cannot fail a command — a missing, unreadable, or corrupt file means
"nothing learned yet", and an unreachable catalog still degrades to the
first-underscore guess
- writes cache files through a temp file and a rename, so an interrupted
run leaves the previous cache intact instead of a truncated
`toolkits.json` the next run reads back as a parse failure
- drops the `toolkit` field from the cached tool model, which nothing
reads once resolution goes through the catalog service
- measured on a logged-in binary: `composio execute
GOOGLE_ANALYTICS_RUN_REPORT --dry-run` goes from ~8.0-8.6 s before the
stack, to ~3.4-4.3 s after
https://github.com/ComposioHQ/composio/pull/4054, to ~1.7-2.0 s here —
startup and the tool-schema fetch only
- no visible command surface changed, so no VHS recording
## Summary
Resolution now reads like this, with the catalog reached only on a
genuine miss:
```mermaid
graph TD
A[tool slug] --> B{session meta tool?}
B -->|yes| C[no toolkit]
B -->|no| D[baked slugs + learned slugs]
D --> E{longest prefix match?}
E -->|yes| F[toolkit slug]
E -->|no| G[fetch catalog]
G -->|match| H[toolkit slug, slug remembered]
G -->|unreachable| I[first-underscore guess]
```
Twelve tests cover the branches: baked hit and learned hit with zero
fetches, first run seeding the file, a miss that fetches and persists,
an unreachable catalog degrading to the guess, a corrupt file tolerated,
a stale file refreshing in the background, a fresh file not refreshing,
meta tools resolving to nothing without a fetch, the same run answering
from its memo instead of re-reading the file, and a second recording in
one run never reaching the file.
This PR:
- builds on top of https://github.com/ComposioHQ/composio/pull/4054
- checks in `src/generated/toolkit-slugs.ts` — the 1070 toolkit slugs
the CLI knows without asking the API. Resolving a tool slug to its
toolkit needs the catalog, but the catalog is ~800 KB of metadata to
learn ~11 KB of slugs
- adds `scripts/generate-toolkit-slugs.ts` (`pnpm --filter @composio/cli
generate:toolkit-slugs`), which paginates the production catalog, keeps
`native` toolkits, sorts and dedupes. It authenticates with
`COMPOSIO_API_KEY` (CI) or `COMPOSIO_USER_API_KEY` + `COMPOSIO_ORG_ID`
(a logged-in developer); both paths verified against production
- gates the write: fewer than 1000 slugs, a malformed slug, or a missing
staple (`github`, `gmail`, `google_analytics`) aborts before touching
the file, so a bad or wrong-scoped credential fails loudly instead of
overwriting a good list with a partial catalog
- adds `.github/workflows/cli.update-toolkit-slugs.yml`, a weekly cron
that opens a refresh PR when the catalog moves. `workflow_dispatch` only
works once this lands on the default branch
- warns during binary builds when the baked list is older than two
weeks, and never fails on it — a stale list is slow, never wrong,
because the backend does not remove toolkits
- adds a guard test: sorted, unique, lowercase-slug-shaped, staples
present, no network
- nothing reads the list yet;
https://github.com/ComposioHQ/composio/pull/4055 wires it into
resolution
- internal only — no changelog entry, no visible command surface, no VHS
recording
## Context
On the cron secret: `docs-update-data.yml` documents
`secrets.COMPOSIO_API_KEY` as staging-scoped and uses its own
`secrets.COMPOSIO_DOCS_API_KEY` for production reads. This workflow uses
the shared secret as decided, and the sanity gate is what makes that
safe to try — a wrong-scoped key 401s, writes nothing, and opens no PR.
The alternative is named in a comment next to the `env:` block, one line
to swap if the first run fails.
## Follow-up folded in
The toolkit-slug shape now has one definition: `TOOLKIT_SLUG_PATTERN`
lives in `src/models/toolkits.ts` and is shared by the schema, the
generation script's sanity gate, and the generated-output guard test.
This PR:
- builds on top of https://github.com/ComposioHQ/composio/pull/4053
- memoizes the toolkit catalog for the lifetime of the cached repository
layer with `Effect.cached`. A single command asks for the full list
several times — toolkit resolution, validation, telemetry — and the file
cache only answers when `FORCE_USE_CACHE` is set, so the default path
paid for the same ~800 KB two-page download up to four times
- fixes a doubling underneath it: `createCachedEffect` wrapped its whole
body — cache lookup and the underlying request alike — in one `catchAll`
that fell back to re-running the request, so a request that failed on
its own was immediately sent again and failed the same way. Cache
concerns now degrade to a cache miss on their own, and the request runs
exactly once with its failure passed straight through
- `Effect.cached` memoizes the outcome rather than only the success, so
a failed fetch stays failed for the process. That is the right trade for
a one-shot CLI, where retrying a broken network per call only multiplies
the wait before the same error surfaces
- adds a counting-repository test stub and four tests: one fetch for N
sequential callers, one for N concurrent callers, one attempt for a
failing fetch, and a `FORCE_USE_CACHE` hit served with zero fetches
(that last one passes against the old implementation too, so it pins
existing behavior rather than blessing the rewrite)
- measured on a logged-in binary: `composio execute
GOOGLE_ANALYTICS_RUN_REPORT --dry-run` goes from ~8.0-8.6 s to ~3.4-4.3
s
- no visible command surface changed, so no VHS recording
This PR:
- closes https://github.com/ComposioHQ/composio/issues/4049
- resolves a tool's toolkit by matching the tool slug against the known
toolkit list, longest underscore-bounded prefix first, instead of
splitting on the first underscore — `GOOGLE_ANALYTICS_RUN_REPORT`
belongs to `google_analytics`, not `google`, and the old split picked
the wrong connected account
- routes every call site (execute, listen, tools-executor, file uploads,
error overrides, telemetry) through one Effect resolver,
`toolkitFromToolSlug`, so they cannot disagree
- keeps the first-underscore split as `guessToolkitFromToolSlug`, used
only when the toolkit list cannot be loaded
- fixes a second miss that the old split hid: `COMPOSIO_SEARCH_TOOLS` is
a session meta tool, but `composio_search` is a real linkable toolkit,
so matching against the real catalog made a failed meta call tell users
to link an app that had nothing to do with it. The meta tool slugs move
out of `tools-executor.ts` into `src/utils/meta-tool-slugs.ts`, and
resolution answers "no toolkit" for them before any prefix matching
- no visible command surface changed, so no VHS recording
## Context
This is correct but not yet fast: resolution still reads the toolkit
catalog, which is ~800 KB over two pages. Three stacked PRs follow that
make the common case cost no network at all.
Every tool execution had to know which toolkit the tool belongs to, and
the only reliable source was the toolkit catalog — ~800 KB and ~2 s of
the wall time of a command that then does one small API call.
Match against what the CLI already knows instead: the slugs baked in at
build time, plus the ones this machine has learned since, kept in
`known-toolkit-slugs.json`. A slug that matches nothing known still
falls through to the catalog — that is what a toolkit released after the
binary looks like — and what it finds is remembered, so it costs a fetch
once rather than every run. Learned slugs older than a week trigger a
background refresh; that bounds how long a brand-new toolkit can be
mistaken for a shorter one sharing its prefix.
The file is derived data rather than saved API responses, so it is read
regardless of FORCE_USE_CACHE, which keeps its meaning. Nothing here can
fail a command: a missing, unreadable, or corrupt file means "nothing
learned yet", and an unreachable catalog still degrades to the
first-underscore guess.
Cache writes also move to a temp file and a rename, so an interrupted
run leaves the previous cache intact instead of a truncated file that
the next run reads back as a parse failure.
Resolving a tool slug to its toolkit needs the catalog, but the catalog
is ~800 KB of metadata to learn ~11 KB of slugs. Check the slugs in as
generated source so the CLI starts out knowing them.
Adds the generator (paginates the production catalog, keeps native
toolkits, sorts and dedupes), the checked-in list, a guard test, and a
weekly workflow that opens a refresh PR when the catalog moves. The
generator refuses to write a list that is short, malformed, or missing
staple toolkits, so a bad or wrong-scoped credential aborts before
overwriting a good list. The binary build warns when the list is older
than two weeks, and never fails on it.
Nothing reads the list yet.
A single command can ask for the full toolkit list several times —
toolkit resolution, validation, telemetry — and the file cache only
answers when FORCE_USE_CACHE is set, so the default path paid for the
same ~800 KB two-page download up to four times.
Memoize the lookup for the lifetime of the cached repository layer. The
first caller pays and the rest await the same result. Effect.cached
memoizes the outcome rather than only the success, so a failed fetch
stays failed for the process: the right trade for a one-shot CLI, where
retrying a broken network per call only multiplies the wait before the
same error surfaces.
That surfaced a second doubling underneath. The caching helper wrapped
its whole body — cache lookup and the underlying request alike — in one
catchAll that fell back to re-running the request, so a request that
failed on its own was immediately sent again and failed the same way.
Cache concerns now degrade to a miss on their own, and the request runs
exactly once with its failure passed straight through.
Tool slugs carry no delimiter between the toolkit and the action, so
splitting on the first underscore resolved GOOGLE_ANALYTICS_RUN_REPORT to
`google` instead of `google_analytics` and picked the wrong connected
account.
Match the tool slug against the known toolkit list instead, longest
underscore-bounded prefix first, and keep the old split as the fallback
for when that list is unavailable. Routed through a new Effect resolver
so every call site (execute, listen, tools-executor, file uploads, error
overrides, telemetry) shares one answer.
Matching against the real catalog exposes a second miss the old split
hid: `COMPOSIO_SEARCH_TOOLS` is a session meta tool, but `composio_search`
is a real linkable toolkit, so a failed meta call told users to link an
app that had nothing to do with it. The meta tool slugs move out of
tools-executor into src/utils/meta-tool-slugs.ts, and resolution answers
'no toolkit' for them before any prefix matching.
Fixes https://github.com/ComposioHQ/composio/issues/4049
Reuse fetched raw tool schemas for provider-wrapped execution while preserving modifier isolation, runtime validation, and unknown-slug fallback behavior.
Refresh the Python 3.12.13 mise lock entries required by the Audit freshness gate.
Co-authored-by: breezeFur <voidexl@outlook.com>