Spotted while fixing the provider-dependency failures in #4221.
## Problem
`python/examples/tool_router/tool_router_mcp.py` guarded a missing
credential like this:
```python
api_key = os.environ.get("COMPOSIO_API_KEY")
if not api_key:
print("Error: COMPOSIO_API_KEY environment variable not set")
print("Please set it using: export COMPOSIO_API_KEY='your_api_key'")
return
```
`return` exits the coroutine normally, so `asyncio.run(main())`
completes and the process exits **0**. The live-examples harness scores
entries by exit code, so a run that did nothing at all was recorded
**green** — the worst kind of failure for a canary, since it reports
success while testing nothing.
## Fix
Raise instead, using the `require_env` helper the sibling examples
already use (`examples/tool_router/preload.py`):
```python
def require_env(name: str) -> str:
value = os.environ.get(name)
if not value:
raise RuntimeError(f"Set {name} before running this example.")
return value
```
## Verification
Replaying the harness's own invocation with the variable unset:
```
$ unset COMPOSIO_API_KEY
$ uv run --project python --with 'openai-agents>=0.19' \
--with ./python/providers/openai_agents --with ./python \
python python/examples/tool_router/tool_router_mcp.py
before: exit=0 # silently "passes"
after: exit=1 # RuntimeError: Set COMPOSIO_API_KEY before running this example.
```
`ruff check`, `ruff format --check`, and `nox -s chk_examples` all pass.
## Scope
I checked whether other examples share the pattern.
`examples/tool_router/authorize.py` prints the same message, but calls
`exit(1)` at module level rather than returning from a coroutine, so it
already fails correctly — left alone. This was the only instance.
Consumer product and the backend-provided default redirect URI use the
v1 callback (api/v1/auth-apps/add), but the developer docs instructed the
v3.1 URL (api/v3.1/toolkits/auth/callback). Users copied the wrong URL
into their OAuth app allow lists. Align all instructional docs + the
Python example with v1.
The migration guide (migration-guide/new-sdk.mdx) is left unchanged: it
documents the v1->v3 history and flipping it would make it contradictory.
Fixes UXE-261.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## 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 is **part 1 of 3** splitting
https://github.com/ComposioHQ/composio/pull/3505 to make the removal of
the old 2025 custom tools easier to review. It carries the **Python**
slice.
- removes the legacy `composio.tools.custom_tool` registry path:
`core.models.custom_tools`, `ExecuteRequestFn`, the old fallback
execution wiring, the security tests, and the example
- preserves the 2026 tool-router APIs: `composio.experimental.tool()`,
`composio.experimental.Toolkit`, inline custom-tool execution,
preload/attach/use flows, and `session.custom_tools()`
- bumps the Python workspace and all provider packages `0.13.1` →
`0.14.0`
## Notes
- This slice is a byte-identical subset of #3505 — the three split
branches recombine to that PR's exact tree. See #3505 for the original
local verification logs (`uv run --frozen nox -s chk`, targeted pytest);
CI re-runs per PR.
## Summary
Security-hardening for automatic file upload in `@composio/core` (patch
release per changeset).
### Changes
- **Default denylist** for local paths before auto-upload /
`files.upload`: blocks common credential directories (e.g. `.ssh`,
`.aws`) and credential-like filenames (e.g. `.env`, default SSH private
keys). Resolves symlinks when the path exists.
- **Config:** `sensitiveFileUploadProtection`,
`fileUploadPathDenySegments` on `Composio`.
- **`beforeFileUpload`** hook (e.g. with `composio.tools.get` /
`tools.execute`): rewrite path, return `false` to abort, or throw.
- **Errors:** `ComposioSensitiveFilePathBlockedError`,
`ComposioFileUploadAbortedError`; file modifier errors exported from
`@composio/core` errors entry.
- **Changeset:** patch bump for `@composio/core`.
### Notes
- URLs and `File` blobs are not subject to the path denylist
(unchanged).
- Opt out of path checks only if required:
`sensitiveFileUploadProtection: false`.
### Tests
- `pnpm test` in `ts/packages/core` (799 tests) passed locally before
commit.
Made with [Cursor](https://cursor.com)
- Fix async validation bypass for single-param tools: check
asyncio.iscoroutinefunction(fn) before wrapping in _infer_tool
- Remove inline # slug/name comments from example (jkomyno review)
- Add test for async single-param rejection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single blank lines between definitions, noqa for E302/E303
since this is an example file not a library module.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 scenarios: standalone, toolkit, mixed local, remote, Gmail proxy
(CREATE_DRAFT via ctx.proxy_execute), mixed all paths in one turn.
manage_connections=True, user_id=default throughout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full agent loop using OpenAI Agents SDK with custom tools routing.
Verifies COMPOSIO_SEARCH_TOOLS discovers custom tools and
COMPOSIO_MULTI_EXECUTE_TOOL routes them in-process.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- P1: Preserve SessionExecuteResponse model for remote execute (don't
convert to dict — backward compatible for callers using attribute access)
- P2: Serialize Pydantic models in SessionContextImpl.execute remote path
via _serialize_arguments() before sending to backend
- P2: Apply before_execute/after_execute modifiers around intercepted
COMPOSIO_MULTI_EXECUTE_TOOL calls in custom tools routing path
- P2: Surface backend batch errors in mixed local/remote multi-execute
(include remote batch error message instead of just tool failure count)
- Reject async execute handlers at factory level (asyncio.iscoroutinefunction)
- Add custom_tools_v2.py e2e example (equivalent of TS custom-tools.ts)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Changed the "dev" script to run build snippets before starting the documentation server.
- Added a new "dev:all" task in turbo.json for better dependency management.
- Updated installation commands in documentation to reflect the new version 0.8.0 for various Composio packages.
- Adjusted authentication snippets and examples for clarity and accuracy.