Four review fixes on the sidebar restore.
Folder headings do not close, so every page emitted after a folder read
as one of that folder's children. Making `authentication` a folder
mid-list swallowed `triggers` and `skills` into `### Authentication`.
Reordering meta.json would fix the output but push Authentication below
Skills in the human sidebar, which is what this PR exists to prevent —
so walkPageTree now buffers each separator section and flushes its plain
pages ahead of its folders. Legacy-separator skipping is unchanged. This
also fixes the pre-existing case where `agent-plugins`, `cli` and
`composio-connect` read as children of `#### Custom providers`.
Drop "index" from authentication/meta.json. Listing it clears
`node.index`, so fumadocs renders the folder as a chevron BUTTON plus a
duplicate child row instead of a SidebarFolderLink — the row a reader
clicks to reach /docs/authentication (1,850 views/month) expanded the
folder instead of navigating. Omitting it matches how `providers` and
`migration-guide` already render. Verified in the built DOM: an A with
href=/docs/authentication, no duplicate row, /docs/authentication.md
still emitted exactly once via node.index.
Rename the ---Security and data--- separator to ---Security---; it
duplicated the folder title directly beneath it, in llms.txt and in the
sidebar.
Point the last three agent/instructions/context.md bullets at canonical
paths rather than redirect-only /docs/authenticating-users/* ones, so
that file is internally consistent.
Extend the llms.txt section test to assert the nearest preceding heading
of any level, and to cover the sibling pages that regressed. It fails
against the old walk.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two review follow-ups:
- Revert docs/lib/standup-bot-source.json. It is an upstream source
snapshot that gets regenerated, so editing the doc URL in that code
comment only desyncs it and would be clobbered anyway. The permanent
redirect covers the stale path.
- Strengthen the agent-reachability assertion in start-routing.test.ts.
Asserting meta.json membership alone is weaker than the assertion it
replaced, which checked a string that actually shipped into llms.txt:
a break in the page-tree walk would have gone unnoticed. Import the
llms.txt route handler and assert its generated output — each of the
8 guide URLs present exactly once, the 7 auth guides under Core
concepts -> Authentication, shared-connections under Guides -> Extend
sessions, and no trailing "## Authentication guides" section. Follows
the existing tests/static/llms-openapi.test.ts pattern of importing an
app route directly, so it runs in `bun run test` with no server.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #4099 dropped 8 auth pages from meta.json. That was a net win for
machines (Loop 5 eval: 84/100 vs 77/100, tool-routing failure mode gone)
but it made the pages unreachable by clicking. PostHog, weekday-matched
and control-adjusted, shows the affected pages down 14-64pp beyond the
site-wide baseline drift.
Restore human navigation without touching the machine-facing wins:
- authentication.mdx becomes authentication/index.mdx and the 7 sibling
auth pages move into the folder. /docs/authentication keeps its URL
(fumadocs serves index.mdx at the folder route), so Core concepts
still shows one row and the top-level sidebar row count stays 20.
- shared-connections moves into extending-sessions: it is experimental
and is a session capability, not a core auth concept.
- "Migration and security" splits into "Migration" and "Security and
data" — separator headings, not clickable rows.
- Drop AUTHENTICATION_GUIDE_URLS from app/llms.txt/route.ts. The pages
are back in the page tree, so they emit automatically under Core
concepts -> Authentication; the hardcoded list would now duplicate.
- 8 permanent redirects for the old URLs (36-42% Google entry rate on
most of them), plus existing redirect destinations repointed so none
of them chain.
- ~90 inbound links rewritten across content, api-overviews, app, lib
and agent instructions. lint:links is the gate: 0 errors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This PR:
- builds on merged PR #4107
- adds `examples-manifest.json`: 93 runnable entrypoints across both
SDKs with tiers (unattended / provisioned / bounded / excluded),
per-entry timeouts, env contracts, and labeled readiness markers for
OAuth flows
- adds `harness/run.mjs`: sweeps entries against staging with per-entry
Composio traces, negative controls (garbage credentials must fail), a
`selftest`, and a baseline/candidate client swap for parity runs
- rejects every non-staging `COMPOSIO_BASE_URL` before the harness or
provisioning script can send credentials
- adds `--llm mock`: LLM examples run against a pinned
`@copilotkit/aimock` server with scripted tool-call fixtures
(`harness/llm-mock/`) — full sweeps spend zero model tokens while
Composio tool execution stays live on staging; 46/48 LLM entries pass
mocked, and the two `@openai/agents` entries are marked `llmMock:false`
- adds `examples-live.yml`: nightly gc-provision → mock sweep →
four-entry live LLM canary; dispatchable per language, client, entry
subset, and LLM mode, and bound to the `staging` GitHub environment
- accepts separate TypeScript tarball and Python wheel inputs for
candidate sweeps, restores exact pre-run workspace files on failure, and
rejects empty or partial parity comparisons
The configured `COMPOSIO_API_KEY` must belong to a dedicated, disposable
staging project. The workflow becomes dispatchable after it exists on
the default branch.
## Context
Examples historically went stale because nothing executed them. PR #4107
makes them runnable and provisionable; this PR adds the machinery that
runs them on a schedule and validates the Stainless→self-managed client
swap via baseline/candidate parity sweeps.
```mermaid
graph TD
M[examples-manifest.json] --> R[harness/run.mjs sweep]
P[examples-provision.mjs --gc] -->|COMPOSIO_EXAMPLES_* ids| R
R -->|tier 1-3 entries| E[TS + Python examples]
A[aimock fixtures] -->|--llm mock| E
E -->|live tool execution + traces| C[Composio staging]
R --> N[nightly live canary: 4 entries]
```
## Verification
- `env -u COMPOSIO_BASE_URL node harness/run.mjs selftest`
- `pnpm test:examples` against a clean tracked snapshot
- manifest integrity: 93 unique entry IDs and 93 existing files
- JavaScript, Python, JSON, YAML, and embedded Bash syntax checks
- `pnpm validate:changesets`
ComposioToolVersionRequiredError, JsonSchemaToZodError, and
JsonSchemaRefResolutionError omitted the this.name assignment their sibling
subclasses perform, so instances inherited the base class field value and
reported name 'ComposioError'. error.name is forwarded to error telemetry,
which mis-grouped these three distinct error types under the base name.
sendMetric and sendErrorLog awaited fetch() with no timeout, so a stalled
telemetry endpoint could leave an awaited SDK call pending indefinitely.
Bound both best-effort requests with an AbortController and a cleared timer
via a shared postWithTimeout helper, mirroring the npm version-check bound in
utils/version.ts. The existing catch still swallows the abort, preserving the
never-throws contract.
## 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>
Custom MCP hardening docs pass from Pelago feedback (Linear PLEN-2931,
origin PLEN-2927), verified against platform code on master.
- Auth config: state explicitly that registration does not create an
auth config; for API-key and DCR OAuth servers creating one (with
`is_enabled_for_tool_router: true`) is a required separate step. Also
fixed the PATCH example: the update body requires `"type": "custom"`
(discriminated union), the previous example would 400.
- v3 gotcha: explain the empty-tools-list symptom, `GET
/api/v3/tools?toolkit_slug=CUSTOM_...` pins a default version without
custom tools; pass `toolkit_versions=latest` or use v3.1.
- Upsert semantics refresh (tracks platform #11598): re-registering an
owned slug now updates mutable fields (name, logo) in place, identical
config is a no-op; `app_url` and `auth_schemes` stay immutable (409,
delete + re-register). Updated the registration callout, logo section,
delete section, and known gaps.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## 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.