Our new SDK is in beta
Our new SDK is in beta
Last week, we rolled out the preview of new SDKs for Composio's Client. This is a significant upgrade for us, as we have largely rethought our platform — improving its speed, reliability, and developer experience. These changes are primarily available through our new python
and typescript
SDKs.
Composio has two core elements: our clients — which includes our SDKs, our API, and our managed cloud platform, where we execute user’s tools.
Our cloud platform has undergone a major upgrade as we've scaled our business significantly in recent months. These new clients fully leverage those platform improvements along with the much improved developer experience.
Versioning
Versioning can be a bit confusing so to get it out of the way, here’s a small note:
This is a major release with many breaking changes.
We call it a v3
SDK based on our v3
APIs, but our SDKs have still not reached a 1.0
release. While we are confident in these SDKs, we are not calling them a 1.0
release yet as we want to iterate on these contracts for a bit longer before locking them in place.
Upgrading
Most of this post will discuss how we approached designing the new SDKs, what you can expect today, and what's coming in the weeks ahead. For a direct migration guide, please visit https://v3.docs.composio.dev/docs/migration
If you just want to quickly try out the SDKs, you can start by installing them here:
pip install -U composio npm
Both the packages have been renamed:
Python:
composio-core
→composio
TypeScript:
composio-core
→@composio/core
Design Principles
I want to walk through the design principles behind our new sdk and where we're heading in the future. While explaining these principles, I'll also address many of the new changes you'll find in the sdk.
Clarity > Simplicity
Below we talk about how to obfuscate concepts to provide magical experience while being clear this is what we were attempting to do with our previous naming. We fell short here, we did a bad job capturing the essence of a concept and sided with simplicity over clarity — programmers always joke about how hard naming is and it was particularly challenging for us We'll talk a lot about exposing magic here, but let's start with naming—as we all know, naming is hard, and it was particularly challenging for us.
There are a few examples I'll highlight:
Integration
We were overloaded with a bunch of questions from our users (and possibly you!) like;
“What is an integration? Does
GitHub
integration mean the app itself or the specific instance one configures”?“What is the difference between a connection, integration, app?”
“Is a tool the same thing as an integration?”
These questions indicated we had huge name collisions as well as improper explanation throughout our product for important concepts.
So where did we end up? While it's definitely not perfect (we believe there's still plenty of room for improvement and would love your feedback), here's our current terminology:
Previous Term | Current Term | Definition |
---|---|---|
Actions | Tools | Individual operations or capabilities that can be performed by an LLM agent |
Apps | Toolkits | A collection of tools grouped under a single application |
Integration | Auth Config | Configuration containing developer credentials and application-level settings such as scopes and API endpoints. Scoped to a toolkit. |
Connection | Connected accounts | User-linked accounts associated with a toolkit |
Entity ID | User ID | The identifier of the user performing the action (UUID or email) |
Toolsets | Providers | LLM or agent framework that can be used with Composio to create agents |
This nomenclature update presents itself through our namespacing
. The namespacing provides clarity about the action you're taking. This is also great for language models, which we'll circle back to later.
This leads into an important point we want to address!
Avoiding Hidden Magic
Composio can feel magical at times, but we don't want it to feel too "magical." Instead, we aim to make your interactions more explicit. There are many examples of this approach in the SDK—let me call out two:
Multiple connected accounts
Previously, you could create multiple connected accounts associated with an entity_id
for an auth config, and while executing, we would "magically" choose the latest one which lead to unexpected behaviour for users.
Now, you have to explicitly allow that behavior and then choose the specific connected account you want to use via a https://v3.docs.composio.dev/docs/modifiers/before-execution
const connection = await composio.connectedAccounts.initiate('user_123', 'auth_config_123', { allowMultiple: true, });
Global objects
In previous SDKs, configuration like workspaces, processors, metadata, and even entity IDs could be specified at the global ComposioToolSet
level. These configurations attached to the “lifecycle” of the toolset itself, not to individual tool executions—leading to implicit, persistent side effects across calls.
toolset = ComposioToolSet( workspace_config=WorkspaceType.Host(), metadata={ App.CODE_ANALYSIS_TOOL: { "dir_to_index_path": repo_path, "create_index": False } }, processors={ "pre": { App.GITHUB: pop_thought_from_request, }, "schema": { App.GITHUB: add_thought_to_request, }, "post": { Action.GITHUB_CREATE_AN_ISSUE_COMMENT: github_pulls_create_review }, }, )
However, one could also override these configurations at any point while using the toolset
object.
toolset.execute_action( action=Action.CODE_ANALYSIS_TOOL_CREATE_CODE_MAP, params={}, metadata={ App.CODE_ANALYSIS_TOOL: { "dir_to_index_path": repo_path, "create_index": False } }, )
The "lifecycle" and side effects caused lots of confusion and introduced problematic behavior in our users' codebases.
Now the tool processors and user_id
(previously entity_id
) are scoped to a single getTool
or execute
call.
@before_execute(tools=["HACKERNEWS_GET_LATEST_POSTS"]) def before_execute_modifier( tool: str, toolkit: str, params: ToolExecuteParams, ) -> ToolExecuteParams: params["arguments"]["size"] = 1 return params # Get tools tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_LATEST_POSTS") # Get response from the LLM response = openai_client.chat.completions.create( model="gpt-4o-mini", tools=tools, messages=[{"role": "user", "content": "Fetch latest posts from hackernews"}], ) print(response) # Execute the function calls. result = composio.provider.handle_tool_calls( response=response, user_id="hey@example.com", modifiers=[ before_execute_modifier, ], )
While reducing hidden magic, we've also focused on creating clear paths that allow users to gradually adopt more complex features.
Obfuscating complexity, with graduation in mind
Our product and the ability to use our product has many layers of complexity, for many users we want to obfuscate this complexity but this comes at the cost of being bad "magical" for user further along the graduation curve. We want to russian nesting doll our complexity
Hence, we built clear graduation paths in our product and hid the complexity needed for each level without hindering the next level.
// before const entity = await toolset.getEntity(userIdInMyApp); // Get Entity object console.log(`Initiating GitHub connection for entity: ${entity.id}`); // Initiate connection using the app's Integration and the user's Entity ID const connectionRequest = await entity.initiateConnection({ appName: appToConnect });
This code would check if you had an auth config (previously called an integration), then create a connection, and finally allow you to connect. When executing this code, it wasn't obvious which auth config you were using or which connected account was being referenced.
This flow was the primary way for all classes of users, from prototyping to single use auth configs to programmatic auth configs
Now we have a clear graduation path
// new (magic) const connection = await composio.toolkits.authorize(userId, 'LINEAR');
This simplified flow deliberately hides complexity for users in the prototyping phase, but avoids cramming everything into a “magic” function. Instead, it nudges developers toward more explicit, production-ready paths as they advance.
// new (non magic) const linearAuthConfigId = 'ac_dqYN9oElNVlg'; const userId = '1111-2222-3333'; // Initiate the OAuth connection request const connRequest = await composio.connectedAccounts.initiate(userId, linearAuthConfigId);
Here the user explicitly creates an auth config and then uses it to initiate a connected account. This is the new base flow for users who want to build with agents on our platform, where they likely only need one auth config for one app https://v3.docs.composio.dev/docs/authenticating-tools
The above flow is designed for building with static auth configs, but we also have users who want to create auth configs on the fly for their customers. For those users, we provide another flow https://v3.docs.composio.dev/docs/programmatic-auth-configs
Being Less Laissez-Faire
Devtools must strike a balance between being too dogmatic (which makes building painful for developers) and being too laissez-faire (which creates confusion). We felt we were falling into the latter camp and wanted to become more opinionated.
While we still support various ways of using our product, we now enforce more rules and provide stricter workflows. Previously, we had too many ways to accomplish the same thing.
One clear example is the enforcement of user_ids
(previously entity_ids
). This concept encapsulates several different things and was difficult to communicate, so we previously allowed workarounds or skipping it altogether. Now, we require its use and will iteratively improve how we communicate this complexity.
to explain briefly: user_id
is an identifier you can use to identify your users however you want—it can be their email, their ID, or even their team's or org's ID. It is, simply put, your user's ID.
You'll notice this approach in many other places where previously flexible flows are now more constrained, with a single canonical path for each operation.
Building with Language Models in Mind
One of the key principles we are keeping in mind is building for language models. These models largely wrote our providers (which means you can create your own providers quickly). Features like namespacing and a cleaner, more type-safe api significantly help in this area. We think quite a bit about how we can surface information to a language model to allow it to write composio code better.
We're also optimising our documentation for language models by including docs in our packages and shipping CLAUDE.md
in our cli
Path Forward
There's much more to our improvements beyond what I've mentioned from ensuring parity between both SDKs, component-specific libraries, more extensible design, and better type safety. I wanted to focus primarily on the motivations behind these changes.
Expect a rapid pace of iteration on the product in the coming months. As we gather customer feedback and formalize these contracts, we'll move quickly toward a locked-in contract for our 1.0
release.
If you want to try the sdk today, head to https://v3.docs.composio.dev/docs/welcome. We'd love your feedback in our discord here.
Last week, we rolled out the preview of new SDKs for Composio's Client. This is a significant upgrade for us, as we have largely rethought our platform — improving its speed, reliability, and developer experience. These changes are primarily available through our new python
and typescript
SDKs.
Composio has two core elements: our clients — which includes our SDKs, our API, and our managed cloud platform, where we execute user’s tools.
Our cloud platform has undergone a major upgrade as we've scaled our business significantly in recent months. These new clients fully leverage those platform improvements along with the much improved developer experience.
Versioning
Versioning can be a bit confusing so to get it out of the way, here’s a small note:
This is a major release with many breaking changes.
We call it a v3
SDK based on our v3
APIs, but our SDKs have still not reached a 1.0
release. While we are confident in these SDKs, we are not calling them a 1.0
release yet as we want to iterate on these contracts for a bit longer before locking them in place.
Upgrading
Most of this post will discuss how we approached designing the new SDKs, what you can expect today, and what's coming in the weeks ahead. For a direct migration guide, please visit https://v3.docs.composio.dev/docs/migration
If you just want to quickly try out the SDKs, you can start by installing them here:
pip install -U composio npm
Both the packages have been renamed:
Python:
composio-core
→composio
TypeScript:
composio-core
→@composio/core
Design Principles
I want to walk through the design principles behind our new sdk and where we're heading in the future. While explaining these principles, I'll also address many of the new changes you'll find in the sdk.
Clarity > Simplicity
Below we talk about how to obfuscate concepts to provide magical experience while being clear this is what we were attempting to do with our previous naming. We fell short here, we did a bad job capturing the essence of a concept and sided with simplicity over clarity — programmers always joke about how hard naming is and it was particularly challenging for us We'll talk a lot about exposing magic here, but let's start with naming—as we all know, naming is hard, and it was particularly challenging for us.
There are a few examples I'll highlight:
Integration
We were overloaded with a bunch of questions from our users (and possibly you!) like;
“What is an integration? Does
GitHub
integration mean the app itself or the specific instance one configures”?“What is the difference between a connection, integration, app?”
“Is a tool the same thing as an integration?”
These questions indicated we had huge name collisions as well as improper explanation throughout our product for important concepts.
So where did we end up? While it's definitely not perfect (we believe there's still plenty of room for improvement and would love your feedback), here's our current terminology:
Previous Term | Current Term | Definition |
---|---|---|
Actions | Tools | Individual operations or capabilities that can be performed by an LLM agent |
Apps | Toolkits | A collection of tools grouped under a single application |
Integration | Auth Config | Configuration containing developer credentials and application-level settings such as scopes and API endpoints. Scoped to a toolkit. |
Connection | Connected accounts | User-linked accounts associated with a toolkit |
Entity ID | User ID | The identifier of the user performing the action (UUID or email) |
Toolsets | Providers | LLM or agent framework that can be used with Composio to create agents |
This nomenclature update presents itself through our namespacing
. The namespacing provides clarity about the action you're taking. This is also great for language models, which we'll circle back to later.
This leads into an important point we want to address!
Avoiding Hidden Magic
Composio can feel magical at times, but we don't want it to feel too "magical." Instead, we aim to make your interactions more explicit. There are many examples of this approach in the SDK—let me call out two:
Multiple connected accounts
Previously, you could create multiple connected accounts associated with an entity_id
for an auth config, and while executing, we would "magically" choose the latest one which lead to unexpected behaviour for users.
Now, you have to explicitly allow that behavior and then choose the specific connected account you want to use via a https://v3.docs.composio.dev/docs/modifiers/before-execution
const connection = await composio.connectedAccounts.initiate('user_123', 'auth_config_123', { allowMultiple: true, });
Global objects
In previous SDKs, configuration like workspaces, processors, metadata, and even entity IDs could be specified at the global ComposioToolSet
level. These configurations attached to the “lifecycle” of the toolset itself, not to individual tool executions—leading to implicit, persistent side effects across calls.
toolset = ComposioToolSet( workspace_config=WorkspaceType.Host(), metadata={ App.CODE_ANALYSIS_TOOL: { "dir_to_index_path": repo_path, "create_index": False } }, processors={ "pre": { App.GITHUB: pop_thought_from_request, }, "schema": { App.GITHUB: add_thought_to_request, }, "post": { Action.GITHUB_CREATE_AN_ISSUE_COMMENT: github_pulls_create_review }, }, )
However, one could also override these configurations at any point while using the toolset
object.
toolset.execute_action( action=Action.CODE_ANALYSIS_TOOL_CREATE_CODE_MAP, params={}, metadata={ App.CODE_ANALYSIS_TOOL: { "dir_to_index_path": repo_path, "create_index": False } }, )
The "lifecycle" and side effects caused lots of confusion and introduced problematic behavior in our users' codebases.
Now the tool processors and user_id
(previously entity_id
) are scoped to a single getTool
or execute
call.
@before_execute(tools=["HACKERNEWS_GET_LATEST_POSTS"]) def before_execute_modifier( tool: str, toolkit: str, params: ToolExecuteParams, ) -> ToolExecuteParams: params["arguments"]["size"] = 1 return params # Get tools tools = composio.tools.get(user_id=user_id, slug="HACKERNEWS_GET_LATEST_POSTS") # Get response from the LLM response = openai_client.chat.completions.create( model="gpt-4o-mini", tools=tools, messages=[{"role": "user", "content": "Fetch latest posts from hackernews"}], ) print(response) # Execute the function calls. result = composio.provider.handle_tool_calls( response=response, user_id="hey@example.com", modifiers=[ before_execute_modifier, ], )
While reducing hidden magic, we've also focused on creating clear paths that allow users to gradually adopt more complex features.
Obfuscating complexity, with graduation in mind
Our product and the ability to use our product has many layers of complexity, for many users we want to obfuscate this complexity but this comes at the cost of being bad "magical" for user further along the graduation curve. We want to russian nesting doll our complexity
Hence, we built clear graduation paths in our product and hid the complexity needed for each level without hindering the next level.
// before const entity = await toolset.getEntity(userIdInMyApp); // Get Entity object console.log(`Initiating GitHub connection for entity: ${entity.id}`); // Initiate connection using the app's Integration and the user's Entity ID const connectionRequest = await entity.initiateConnection({ appName: appToConnect });
This code would check if you had an auth config (previously called an integration), then create a connection, and finally allow you to connect. When executing this code, it wasn't obvious which auth config you were using or which connected account was being referenced.
This flow was the primary way for all classes of users, from prototyping to single use auth configs to programmatic auth configs
Now we have a clear graduation path
// new (magic) const connection = await composio.toolkits.authorize(userId, 'LINEAR');
This simplified flow deliberately hides complexity for users in the prototyping phase, but avoids cramming everything into a “magic” function. Instead, it nudges developers toward more explicit, production-ready paths as they advance.
// new (non magic) const linearAuthConfigId = 'ac_dqYN9oElNVlg'; const userId = '1111-2222-3333'; // Initiate the OAuth connection request const connRequest = await composio.connectedAccounts.initiate(userId, linearAuthConfigId);
Here the user explicitly creates an auth config and then uses it to initiate a connected account. This is the new base flow for users who want to build with agents on our platform, where they likely only need one auth config for one app https://v3.docs.composio.dev/docs/authenticating-tools
The above flow is designed for building with static auth configs, but we also have users who want to create auth configs on the fly for their customers. For those users, we provide another flow https://v3.docs.composio.dev/docs/programmatic-auth-configs
Being Less Laissez-Faire
Devtools must strike a balance between being too dogmatic (which makes building painful for developers) and being too laissez-faire (which creates confusion). We felt we were falling into the latter camp and wanted to become more opinionated.
While we still support various ways of using our product, we now enforce more rules and provide stricter workflows. Previously, we had too many ways to accomplish the same thing.
One clear example is the enforcement of user_ids
(previously entity_ids
). This concept encapsulates several different things and was difficult to communicate, so we previously allowed workarounds or skipping it altogether. Now, we require its use and will iteratively improve how we communicate this complexity.
to explain briefly: user_id
is an identifier you can use to identify your users however you want—it can be their email, their ID, or even their team's or org's ID. It is, simply put, your user's ID.
You'll notice this approach in many other places where previously flexible flows are now more constrained, with a single canonical path for each operation.
Building with Language Models in Mind
One of the key principles we are keeping in mind is building for language models. These models largely wrote our providers (which means you can create your own providers quickly). Features like namespacing and a cleaner, more type-safe api significantly help in this area. We think quite a bit about how we can surface information to a language model to allow it to write composio code better.
We're also optimising our documentation for language models by including docs in our packages and shipping CLAUDE.md
in our cli
Path Forward
There's much more to our improvements beyond what I've mentioned from ensuring parity between both SDKs, component-specific libraries, more extensible design, and better type safety. I wanted to focus primarily on the motivations behind these changes.
Expect a rapid pace of iteration on the product in the coming months. As we gather customer feedback and formalize these contracts, we'll move quickly toward a locked-in contract for our 1.0
release.
If you want to try the sdk today, head to https://v3.docs.composio.dev/docs/welcome. We'd love your feedback in our discord here.
MCP Webinar
We’re hosting first ever MCP webinar where we will discuss MCP security, Tool Authentication, Best practices for building and deploying MCP agents, and answer your questions. So, please join us on July 17, 2025. It'll be fun.


MCP Webinar
We’re hosting first ever MCP webinar where we will discuss MCP security, Tool Authentication, Best practices for building and deploying MCP agents, and answer your questions. So, please join us on July 17, 2025. It'll be fun.


MCP Webinar
We’re hosting first ever MCP webinar where we will discuss MCP security, Tool Authentication, Best practices for building and deploying MCP agents, and answer your questions. So, please join us on July 17, 2025. It'll be fun.

Recommended Blogs
Recommended Blogs
composio, sdk, preview, rlease
Stay updated.

Stay updated.