diff --git a/fern/pages/src/tool-calling/executing-tools.mdx b/fern/pages/src/tool-calling/executing-tools.mdx
index 53777dcb1d..f6f98a6fb6 100644
--- a/fern/pages/src/tool-calling/executing-tools.mdx
+++ b/fern/pages/src/tool-calling/executing-tools.mdx
@@ -231,4 +231,114 @@ response = composio.tools.proxy(
```
-If you're interested in extending toolkits and creating custom tools, see [Custom tools](/docs/custom-tools).
\ No newline at end of file
+If you're interested in extending toolkits and creating custom tools, see [Custom tools](/docs/custom-tools).
+
+## Automatic File Handling
+
+Composio SDK includes automatic file handling for tools that work with files. When enabled (default), the SDK automatically handles file uploads and downloads during tool execution.
+
+### File Upload
+
+When a tool accepts file inputs (marked with `file_uploadable: true`), you can pass local file paths or URLs. Here's an example using Google Drive upload:
+
+
+```typescript TypeScript maxLines=60 wordWrap
+import { Composio } from '@composio/core';
+import path from 'path';
+
+const composio = new Composio({
+ apiKey: process.env.COMPOSIO_API_KEY
+});
+
+// Upload a local file to Google Drive
+const result = await composio.tools.execute('GOOGLEDRIVE_UPLOAD_FILE', {
+ userId: 'default',
+ arguments: {
+ file_to_upload: path.join(__dirname, 'document.pdf') // Local file path
+ }
+});
+
+console.log(result.data); // Contains Google Drive file details
+```
+
+```python Python maxLines=60 wordWrap
+import os
+
+from composio import Composio
+
+composio = Composio()
+
+# Upload a local file to Google Drive
+result = composio.tools.execute(
+ "GOOGLEDRIVE_UPLOAD_FILE",
+ user_id="default",
+ arguments={
+ "file_to_upload": os.path.join(os.getcwd(), "document.pdf") # Local file path
+ }
+)
+
+print(result.data) # Contains Google Drive file details
+```
+
+
+The SDK automatically:
+1. Reads the file content
+2. Uploads it to secure storage
+3. Passes the file metadata to the tool
+
+### File Download
+
+When a tool returns file outputs, the SDK automatically:
+1. Downloads the file to a local temporary directory
+2. Provides the local file path in the response
+
+
+```typescript TypeScript maxLines=60 wordWrap
+// Download a file from Google Drive
+const result = await composio.tools.execute('GOOGLEDRIVE_DOWNLOAD_FILE', {
+ userId: 'default',
+ arguments: {
+ file_id: 'your_file_id'
+ }
+});
+
+// Result includes local file path
+console.log(result.data.file.uri); // "/path/to/downloaded/file.pdf"
+```
+
+```python Python maxLines=60 wordWrap
+# Download a file from Google Drive
+result = composio.tools.execute(
+ "GOOGLEDRIVE_DOWNLOAD_FILE",
+ user_id="default",
+ arguments={
+ "file_id": "your_file_id"
+ }
+)
+
+# Result includes local file path
+print(result.data["file"]) # "/path/to/downloaded/file.pdf"
+```
+
+
+### Disabling Auto File Handling
+
+You can disable automatic file handling when initializing the Typescript SDK:
+
+
+```typescript TypeScript maxLines=60 wordWrap
+const composio = new Composio({
+ apiKey: process.env.COMPOSIO_API_KEY,
+ autoUploadDownloadFiles: false
+});
+
+// Now you need to handle files manually using composio.files API
+const fileData = await composio.files.upload({
+ filePath: path.join(__dirname, 'document.pdf'),
+ toolSlug: 'GOOGLEDRIVE_UPLOAD_FILE',
+ toolkitSlug: 'googledrive'
+});
+```
+
+
+For more details on file handling, see [Auto Upload and Download Files](/docs/advanced/auto-upload-download).
\ No newline at end of file
diff --git a/fern/pages/src/tool-calling/fetching-tools.mdx b/fern/pages/src/tool-calling/fetching-tools.mdx
index 2ea95becfc..e04ec5825a 100644
--- a/fern/pages/src/tool-calling/fetching-tools.mdx
+++ b/fern/pages/src/tool-calling/fetching-tools.mdx
@@ -22,6 +22,7 @@ When you fetch tools from a toolkit, the most important tools are returned first
Composio determines the importance of a tool based on the usage and relevance of the tool.
+
```typescript TypeScript maxLines=60 wordWrap
const tools = await composio.tools.get(
@@ -62,6 +63,37 @@ tools = composio.tools.get(
```
+**Filtering by scopes**
+
+When working with OAuth-based toolkits, you can filter tools based on their required scopes. This is useful when you want to:
+- Get tools that match specific permission levels
+- Ensure tools align with available user permissions
+- Filter tools based on their required OAuth scopes
+
+
+Scope filtering can only be used with a single toolkit at a time.
+
+
+
+```typescript TypeScript maxLines=60 wordWrap
+// Get GitHub tools that require specific scopes
+const tools = await composio.tools.get(userId, {
+ toolkits: ["GITHUB"],
+ scopes: ["repo"], // Only get tools requiring these scopes
+ limit: 10
+});
+```
+```python Python maxLines=60 wordWrap
+# Get GitHub tools that require specific scopes
+tools = composio.tools.get(
+ user_id,
+ toolkits=["GITHUB"],
+ scopes=["repo"], # Only get tools requiring these scopes
+ limit=10
+)
+```
+
+
## Filtering by tool
You may specify the list of tools to fetch by directly providing the tool names. Browse the list of tools [here](/tools) to view and inspect the tools for each toolkit.
@@ -118,11 +150,17 @@ You may also filter tools by searching for them. This is a good way to find tool
This step runs a semantic search on the tool names and descriptions and returns the most relevant tools.
-
```typescript TypeScript maxLines=60 wordWrap
const tools = await composio.tools.get(userId, {
search: "hubspot organize contacts",
});
+
+// Search within a specific toolkit
+const tools = await composio.tools.get(userId, {
+ search: "repository issues",
+ toolkits: ["GITHUB"], // Optional: limit search to specific toolkit
+ limit: 5 // Optional: limit number of results
+});
```
```python Python maxLines=60 wordWrap
@@ -130,5 +168,39 @@ tools = composio.tools.get(
user_id,
search="hubspot organize contacts",
)
+
+# Search within a specific toolkit
+tools = composio.tools.get(
+ user_id,
+ search="repository issues",
+ toolkits=["GITHUB"], # Optional: limit search to specific toolkit
+ limit=5 # Optional: limit number of results
+)
```
-
\ No newline at end of file
+
+
+## Filter Combinations
+
+When fetching tools, you must use one of these filter combinations:
+
+1. **Tools Only**: Fetch specific tools by their slugs
+ ```typescript
+ { tools: ["TOOL_1", "TOOL_2"] }
+ ```
+
+2. **Toolkits Only**: Fetch tools from specific toolkits
+ ```typescript
+ { toolkits: ["TOOLKIT_1", "TOOLKIT_2"], limit?: number }
+ ```
+
+3. **Single Toolkit with Scopes**: Fetch tools requiring specific OAuth scopes
+ ```typescript
+ { toolkits: ["GITHUB"], scopes: ["read:repo"], limit?: number }
+ ```
+
+4. **Search**: Search across all tools or within specific toolkits
+ ```typescript
+ { search: "query", toolkits?: string[], limit?: number }
+ ```
+
+These combinations are mutually exclusive - you can't mix `tools` with `search` or use `scopes` with multiple toolkits.
\ No newline at end of file
diff --git a/python/composio/core/models/connected_accounts.py b/python/composio/core/models/connected_accounts.py
index 228245901f..8407834701 100644
--- a/python/composio/core/models/connected_accounts.py
+++ b/python/composio/core/models/connected_accounts.py
@@ -227,25 +227,8 @@ def calcom_auth(
),
}
- def snowflake(
- self, options: connected_account_create_params.ConnectionStateUnionMember9Val
- ) -> connected_account_create_params.ConnectionState:
- """
- Create a new connected account using Snowflake.
- """
- return {
- "auth_scheme": "SNOWFLAKE",
- "val": t.cast(
- connected_account_create_params.ConnectionStateUnionMember9Val,
- {
- **options,
- "status": "ACTIVE",
- },
- ),
- }
-
def billcom_auth(
- self, options: connected_account_create_params.ConnectionStateUnionMember10Val
+ self, options: connected_account_create_params.ConnectionStateUnionMember9Val
) -> connected_account_create_params.ConnectionState:
"""
Create a new connected account using Bill.com auth.
@@ -253,7 +236,7 @@ def billcom_auth(
return {
"auth_scheme": "BILLCOM_AUTH",
"val": t.cast(
- connected_account_create_params.ConnectionStateUnionMember10Val,
+ connected_account_create_params.ConnectionStateUnionMember9Val,
{
**options,
"status": "ACTIVE",
@@ -262,7 +245,7 @@ def billcom_auth(
}
def basic_with_jwt(
- self, options: connected_account_create_params.ConnectionStateUnionMember11Val
+ self, options: connected_account_create_params.ConnectionStateUnionMember10Val
) -> connected_account_create_params.ConnectionState:
"""
Create a new connected account using basic auth with JWT.
@@ -270,7 +253,7 @@ def basic_with_jwt(
return {
"auth_scheme": "BASIC_WITH_JWT",
"val": t.cast(
- connected_account_create_params.ConnectionStateUnionMember11Val,
+ connected_account_create_params.ConnectionStateUnionMember10Val,
{
**options,
"status": "ACTIVE",
diff --git a/python/composio/core/models/custom_tools.py b/python/composio/core/models/custom_tools.py
index ca6df6d228..bfdad0de36 100644
--- a/python/composio/core/models/custom_tools.py
+++ b/python/composio/core/models/custom_tools.py
@@ -88,6 +88,7 @@ def __parse_info(self) -> Tool:
output_parameters={},
available_versions=[],
version="1.0.0",
+ scopes=[],
slug=self.slug,
toolkit=tool_list_response.ItemToolkit(
logo="",
diff --git a/python/composio/core/models/tools.py b/python/composio/core/models/tools.py
index 540e277fba..47cfdbff91 100644
--- a/python/composio/core/models/tools.py
+++ b/python/composio/core/models/tools.py
@@ -93,6 +93,8 @@ def get_raw_composio_tools(
tools: t.Optional[list[str]] = None,
search: t.Optional[str] = None,
toolkits: t.Optional[list[str]] = None,
+ scopes: t.Optional[t.List[str]] = None,
+ limit: t.Optional[int] = None,
) -> list[Tool]:
"""
Get a list of tool schemas based on the provided filters.
@@ -116,6 +118,8 @@ def get_raw_composio_tools(
",".join(toolkits) if toolkits else self._client.not_given
),
search=search if search else self._client.not_given,
+ scopes=scopes,
+ limit=str(limit) if limit is not None else self._client.not_given,
).items
)
return tools_list
@@ -126,13 +130,17 @@ def _get(
tools: t.Optional[list[str]] = None,
search: t.Optional[str] = None,
toolkits: t.Optional[list[str]] = None,
+ scopes: t.Optional[t.List[str]] = None,
modifiers: t.Optional[Modifiers] = None,
+ limit: t.Optional[int] = None,
):
"""Get a list of tools based on the provided filters."""
tools_list = self.get_raw_composio_tools(
tools=tools,
search=search,
toolkits=toolkits,
+ scopes=scopes,
+ limit=limit,
)
if modifiers is not None:
tools_list = [
@@ -197,6 +205,8 @@ def get(
user_id: str,
*,
toolkits: list[str],
+ scopes: t.Optional[t.List[str]] = None,
+ limit: t.Optional[int] = None,
modifiers: t.Optional[Modifiers] = None,
):
"""Get tools by toolkit slugs (Only important tools are returned)"""
@@ -218,6 +228,7 @@ def get(
*,
toolkits: list[str],
search: t.Optional[str] = None,
+ limit: t.Optional[int] = None,
modifiers: t.Optional[Modifiers] = None,
):
"""Get tool by search term and/or toolkit slugs and search term"""
@@ -230,7 +241,9 @@ def get(
tools: t.Optional[list[str]] = None,
search: t.Optional[str] = None,
toolkits: t.Optional[list[str]] = None,
+ scopes: t.Optional[t.List[str]] = None,
modifiers: t.Optional[Modifiers] = None,
+ limit: t.Optional[int] = None,
):
"""Get a tool or list of tools based on the provided arguments."""
if slug is not None:
@@ -240,7 +253,9 @@ def get(
tools=tools,
search=search,
toolkits=toolkits,
+ scopes=scopes,
modifiers=modifiers,
+ limit=limit,
)
def _wrap_execute_tool(
diff --git a/python/composio/core/models/triggers.py b/python/composio/core/models/triggers.py
index 6c8ff78c01..2c856e8eab 100644
--- a/python/composio/core/models/triggers.py
+++ b/python/composio/core/models/triggers.py
@@ -732,7 +732,7 @@ def create(
)
def _get_connected_account_for_user(self, trigger: str, user_id: str) -> str:
- toolkit = self.get_type(slug=trigger).toolkit.name
+ toolkit = self.get_type(slug=trigger).toolkit.slug
connected_accounts = self._client.connected_accounts.list(
toolkit_slugs=[toolkit]
)
diff --git a/python/pyproject.toml b/python/pyproject.toml
index 56182d9060..8fb56cb7b4 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -7,7 +7,7 @@ requires-python = ">=3.10,<4"
dependencies = [
"pysher>=1.0.8",
"pydantic>=2.6.4",
- "composio-client==1.3.0",
+ "composio-client==1.4.0",
"typing-extensions>=4.0.0",
"openai",
]
diff --git a/ts/docs/api/tools.md b/ts/docs/api/tools.md
index 6086c5a639..eee673e484 100644
--- a/ts/docs/api/tools.md
+++ b/ts/docs/api/tools.md
@@ -178,13 +178,15 @@ type ToolsOnlyParams = {
toolkits?: never; // Cannot be used with tools
limit?: never;
search?: never;
-};
+ scopes?: never;
+}
type ToolkitsOnlyParams = {
tools?: never; // Cannot be used with toolkits
toolkits: string[]; // List of toolkit slugs to filter by
limit?: number; // Limit the number of results
- search?: never; // Cannot be used with important flag,
+ search?: never; // Cannot be used with important flag
+ scopes?: string[]; // Optional list of required OAuth scopes
};
type ToolkitSearchOnlyParams = {
@@ -192,6 +194,7 @@ type ToolkitSearchOnlyParams = {
toolkits?: string[]; // Optional list of toolkit slugs to filter by
limit?: number; // Limit the number of results
search: string; // Search term
+ scopes?: string[]; // Optional list of required OAuth scopes
};
type ToolListParams = ToolsOnlyParams | ToolkitsOnlyParams | ToolkitSearchOnlyParams;
@@ -202,6 +205,29 @@ Note: The parameters are organized into three mutually exclusive combinations:
1. Using `tools` array to fetch specific tools by their slugs
2. Using `toolkits` with optional `important` flag to fetch tools from specific toolkits
3. Using `search` with optional `toolkits` to search for tools by name/description
+4. Using `scopes` can only be done with a single `toolkits` slug
+
+You can also filter tools by their scopes:
+
+```typescript
+// Get tools with specific scopes
+const scopedTools = await composio.tools.get('default', {
+ toolkits: ['github'],
+ scopes: ['read:repo', 'write:repo'], // Only get tools requiring these scopes
+});
+
+// Search tools with specific scopes
+const searchedScopedTools = await composio.tools.get('default', {
+ search: 'repository',
+ scopes: ['read:repo'], // Only get tools requiring read:repo scope
+ limit: 10,
+});
+```
+
+The `scopes` parameter allows you to:
+- Filter tools based on their required OAuth scopes
+- Get tools that match specific permission levels
+- Ensure tools align with available user permissions
Examples:
diff --git a/uv.lock b/uv.lock
index 1a1fb55df6..24e60a287e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -467,7 +467,7 @@ dev = [
[package.metadata]
requires-dist = [
- { name = "composio-client", specifier = "==1.3.0" },
+ { name = "composio-client", specifier = "==1.4.0" },
{ name = "openai" },
{ name = "pydantic", specifier = ">=2.6.4" },
{ name = "pysher", specifier = ">=1.0.8" },
@@ -504,7 +504,7 @@ requires-dist = [
[[package]]
name = "composio-client"
-version = "1.3.0"
+version = "1.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -514,9 +514,9 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/df/4f/410ae47457c65923a378da46caabf7187dcf301838e1fedc70b21cc63180/composio_client-1.3.0.tar.gz", hash = "sha256:5006ae7988201c32d4595500077b8405b7c6ea5b795c28ca5973df568b1c767e", size = 167940 }
+sdist = { url = "https://files.pythonhosted.org/packages/35/35/c6bb5ce5f6b602efad86e4777a16ded43c51d9cb7f653fc93ab03e26741b/composio_client-1.4.0.tar.gz", hash = "sha256:c5524a02d16dabcdb71ae77e5fc00016dd4a447e36c9cb9ef7ff3ae8e87686d6", size = 167300 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f4/12/aa419b5d0bdbdc68413d730f8c722187d97bd0cf534ccf5280e1194f37aa/composio_client-1.3.0-py3-none-any.whl", hash = "sha256:ff01ce07fa1a89f2bd5448858e101f1c75b4142d27f618961437f6a60a8c16e5", size = 210297 },
+ { url = "https://files.pythonhosted.org/packages/65/01/83484fbce94e5d67c13920911dc8c7a41413ef0d7baded21ec40185000aa/composio_client-1.4.0-py3-none-any.whl", hash = "sha256:81e8132b35cc9a675f4196b9126f02721d03f56125bcad9a74b720a01bfd40d0", size = 207495 },
]
[[package]]