#!/bin/bash # Check if provider name is provided if [ -z "$1" ]; then echo "Please provide a provider name" echo "Usage: ./scripts/create-provider.sh [--agentic] [--output-dir ]" exit 1 fi PROVIDER_NAME=$1 shift # Default output directory OUTPUT_DIR="python/providers" IS_AGENTIC=false # Parse optional arguments while [[ $# -gt 0 ]]; do case $1 in --agentic) IS_AGENTIC=true shift ;; --output-dir) OUTPUT_DIR="$2" shift 2 ;; *) echo "Unknown option: $1" echo "Usage: ./scripts/create-provider.sh [--agentic] [--output-dir ]" exit 1 ;; esac done PROVIDER_PATH="$OUTPUT_DIR/$PROVIDER_NAME" # Convert to title case (e.g., myai -> Myai) TITLE_CASE_PROVIDER_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${PROVIDER_NAME:0:1})${PROVIDER_NAME:1}" PACKAGE_NAME="composio_$PROVIDER_NAME" # Check if provider directory already exists if [ -d "$PROVIDER_PATH" ]; then echo "❌ Provider '$PROVIDER_NAME' already exists in $PROVIDER_PATH" exit 1 fi # Create directory structure mkdir -p "$PROVIDER_PATH/$PACKAGE_NAME" # Create pyproject.toml cat > "$PROVIDER_PATH/pyproject.toml" << EOL [project] name = "$PACKAGE_NAME" version = "0.1.0" description = "${IS_AGENTIC:+Agentic }Provider for ${TITLE_CASE_PROVIDER_NAME} in the Composio SDK" readme = "README.md" requires-python = ">=3.10,<4" authors = [ { name = "Composio", email = "tech@composio.dev" } ] classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", ] dependencies = [ "composio", ] [project.urls] Homepage = "https://github.com/ComposioHQ/composio" EOL # Create setup.py for backwards compatibility cat > "$PROVIDER_PATH/setup.py" << EOL from setuptools import setup, find_packages setup( name="$PACKAGE_NAME", version="0.1.0", packages=find_packages(), install_requires=[ "composio>=0.1.0", ], python_requires=">=3.10,<4", author="Your Name", author_email="you@example.com", description="${IS_AGENTIC:+Agentic }Provider for ${TITLE_CASE_PROVIDER_NAME} in the Composio SDK", long_description=open("README.md").read(), long_description_content_type="text/markdown", url="https://github.com/composio/composio", classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ], ) EOL # Create __init__.py cat > "$PROVIDER_PATH/$PACKAGE_NAME/__init__.py" << EOL """ ${TITLE_CASE_PROVIDER_NAME} Provider for composio SDK. """ from .provider import ${TITLE_CASE_PROVIDER_NAME}Provider __all__ = ["${TITLE_CASE_PROVIDER_NAME}Provider"] EOL # Create provider.py if [ "$IS_AGENTIC" = true ]; then cat > "$PROVIDER_PATH/$PACKAGE_NAME/provider.py" << EOL """ ${TITLE_CASE_PROVIDER_NAME} Provider implementation. """ from __future__ import annotations import typing as t from composio.core.provider import AgenticProvider from composio.core.provider.agentic import AgenticProviderExecuteFn from composio.types import Tool # Define your tool format class ${TITLE_CASE_PROVIDER_NAME}Tool: """${TITLE_CASE_PROVIDER_NAME} tool format""" def __init__(self, name: str, description: str, execute: t.Callable, schema: dict): self.name = name self.description = description self.execute = execute self.schema = schema class ${TITLE_CASE_PROVIDER_NAME}Provider( AgenticProvider[${TITLE_CASE_PROVIDER_NAME}Tool, t.List[${TITLE_CASE_PROVIDER_NAME}Tool]], name="${PROVIDER_NAME}" ): """ Composio toolset for ${TITLE_CASE_PROVIDER_NAME} framework. """ def wrap_tool( self, tool: Tool, execute_tool: AgenticProviderExecuteFn ) -> ${TITLE_CASE_PROVIDER_NAME}Tool: """Wrap a tool in the ${PROVIDER_NAME} format.""" def execute_wrapper(**kwargs) -> t.Dict: result = execute_tool(tool.slug, kwargs) if not result.get("successful", False): raise Exception(result.get("error", "Tool execution failed")) return result.get("data", {}) return ${TITLE_CASE_PROVIDER_NAME}Tool( name=tool.slug, description=tool.description or "", execute=execute_wrapper, schema=tool.input_parameters ) def wrap_tools( self, tools: t.Sequence[Tool], execute_tool: AgenticProviderExecuteFn ) -> t.List[${TITLE_CASE_PROVIDER_NAME}Tool]: """ Get composio tools wrapped as a list of ${TITLE_CASE_PROVIDER_NAME} tools. """ return [self.wrap_tool(tool, execute_tool) for tool in tools] EOL else cat > "$PROVIDER_PATH/$PACKAGE_NAME/provider.py" << EOL """ ${TITLE_CASE_PROVIDER_NAME} Provider implementation. """ from __future__ import annotations import typing as t from composio.core.provider import NonAgenticProvider from composio.types import Tool, Modifiers, ToolExecutionResponse # Define your tool format class ${TITLE_CASE_PROVIDER_NAME}Tool: """${TITLE_CASE_PROVIDER_NAME} tool format""" def __init__(self, name: str, description: str, parameters: dict): self.name = name self.description = description self.parameters = parameters # Define your tool collection format ${TITLE_CASE_PROVIDER_NAME}ToolCollection: t.TypeAlias = t.List[${TITLE_CASE_PROVIDER_NAME}Tool] class ${TITLE_CASE_PROVIDER_NAME}Provider( NonAgenticProvider[${TITLE_CASE_PROVIDER_NAME}Tool, ${TITLE_CASE_PROVIDER_NAME}ToolCollection], name="${PROVIDER_NAME}" ): """ Composio toolset for ${TITLE_CASE_PROVIDER_NAME} platform. """ def wrap_tool(self, tool: Tool) -> ${TITLE_CASE_PROVIDER_NAME}Tool: """Transform a single tool to platform format""" return ${TITLE_CASE_PROVIDER_NAME}Tool( name=tool.slug, description=tool.description or "", parameters={ "type": "object", "properties": tool.input_parameters.get("properties", {}), "required": tool.input_parameters.get("required", []) } ) def wrap_tools(self, tools: t.Sequence[Tool]) -> ${TITLE_CASE_PROVIDER_NAME}ToolCollection: """Transform a collection of tools""" return [self.wrap_tool(tool) for tool in tools] def execute_tool_call( self, user_id: str, tool_call: dict, modifiers: t.Optional[Modifiers] = None ) -> ToolExecutionResponse: """ Execute a tool call. :param user_id: User ID to use for executing function calls. :param tool_call: Tool call metadata containing 'name' and 'arguments'. :param modifiers: Modifiers to use for executing function calls. :return: Object containing output data from the tool call. """ return self.execute_tool( slug=tool_call["name"], arguments=tool_call["arguments"], modifiers=modifiers, user_id=user_id ) def handle_tool_calls( self, user_id: str, response: t.Union[dict, t.Any], modifiers: t.Optional[Modifiers] = None ) -> t.List[ToolExecutionResponse]: """ Handle tool calls from ${TITLE_CASE_PROVIDER_NAME} response. :param response: Response object from ${TITLE_CASE_PROVIDER_NAME} :param user_id: User ID to use for executing function calls. :param modifiers: Modifiers to use for executing function calls. :return: A list of output objects from the tool calls. """ # TODO: Implement based on ${TITLE_CASE_PROVIDER_NAME}'s response format # Example: # outputs = [] # for tool_call in response.tool_calls: # outputs.append( # self.execute_tool_call( # user_id=user_id, # tool_call=tool_call, # modifiers=modifiers, # ) # ) # return outputs raise NotImplementedError("Tool call handling not implemented yet") EOL fi # Create py.typed marker for PEP 561 touch "$PROVIDER_PATH/$PACKAGE_NAME/py.typed" # Create demo script if [ "$IS_AGENTIC" = true ]; then cat > "$PROVIDER_PATH/${PROVIDER_NAME}_demo.py" << EOL """ ${TITLE_CASE_PROVIDER_NAME} demo. """ import asyncio # from ${PROVIDER_NAME}_framework import Agent, Runner # Import your agent framework from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider from composio import Composio # Initialize Composio toolset composio = Composio(provider=${TITLE_CASE_PROVIDER_NAME}Provider()) # Get all the tools tools = composio.tools.get( user_id="default", tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"], ) # TODO: Create an agent with the tools # agent = Agent( # name="GitHub Agent", # instructions="You are a helpful assistant that helps users with GitHub tasks.", # tools=tools, # ) # Run the agent async def main(): # TODO: Implement your agent execution logic # result = await Runner.run( # starting_agent=agent, # input=( # "Star the repository composiohq/composio on GitHub. If done " # "successfully, respond with 'Action executed successfully'" # ), # ) # print(result.final_output) print("Implement your ${TITLE_CASE_PROVIDER_NAME} agent logic here!") print(f"Got {len(tools)} tools:") for tool in tools: print(f" - {tool.name}: {tool.description}") asyncio.run(main()) EOL else cat > "$PROVIDER_PATH/${PROVIDER_NAME}_demo.py" << EOL """ ${TITLE_CASE_PROVIDER_NAME} demo. """ from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider # from ${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME} # Import your AI client from composio import Composio # Initialize tools. # ${PROVIDER_NAME}_client = ${TITLE_CASE_PROVIDER_NAME}() # Initialize your AI client composio = Composio(provider=${TITLE_CASE_PROVIDER_NAME}Provider()) # Define task. task = "Star a repo composiohq/composio on GitHub" # Get GitHub tools that are pre-configured tools = composio.tools.get(user_id="default", toolkits=["GITHUB"]) # TODO: Get response from the LLM # response = ${PROVIDER_NAME}_client.chat.completions.create( # model="your-model", # tools=tools, # messages=[ # {"role": "system", "content": "You are a helpful assistant."}, # {"role": "user", "content": task}, # ], # ) # print(response) # TODO: Execute the function calls. # result = composio.provider.handle_tool_calls(response=response, user_id="default") # print(result) print(f"Got {len(tools)} tools for the task: {task}") for tool in tools: print(f" - {tool.name}: {tool.description}") print("\nImplement your ${TITLE_CASE_PROVIDER_NAME} integration here!") EOL fi # Create README.md cat > "$PROVIDER_PATH/README.md" << EOL # $PACKAGE_NAME ${IS_AGENTIC:+Agentic }Provider for ${TITLE_CASE_PROVIDER_NAME} in the Composio SDK. ## Features ${IS_AGENTIC:+- **Full Tool Execution**: Execute tools with proper parameter handling - **Agent Support**: Create agents with wrapped tools - **Type Safety**: Full type annotations for better IDE support}${!IS_AGENTIC:+- **Tool Transformation**: Convert Composio tools to ${TITLE_CASE_PROVIDER_NAME} format - **Tool Execution**: Execute tools with proper parameter handling - **Type Safety**: Full type annotations for better IDE support} ## Installation \`\`\`bash pip install $PACKAGE_NAME # or uv add $PACKAGE_NAME \`\`\` ## Quick Start \`\`\`python from composio import Composio from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider # Initialize Composio with ${TITLE_CASE_PROVIDER_NAME} provider composio = Composio( api_key="your-composio-api-key", provider=${TITLE_CASE_PROVIDER_NAME}Provider() ) # Get available tools tools = composio.tools.get( user_id="default", toolkits=["github", "gmail"] ) # Use tools with ${TITLE_CASE_PROVIDER_NAME} # TODO: Add your usage example here \`\`\` ## Usage Examples ### Basic Example \`\`\`python from composio import Composio from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider # Initialize provider provider = ${TITLE_CASE_PROVIDER_NAME}Provider() # Initialize Composio composio = Composio( api_key="your-api-key", provider=provider ) # Get tools tools = composio.tools.get( user_id="default", toolkits=["github"] ) ${IS_AGENTIC:+# Use the tools with your agent framework # TODO: Implement your agent logic here for tool in tools: print(f"Tool: {tool.name} - {tool.description}")}${!IS_AGENTIC:+# TODO: Get response from your AI platform (example) # response = ${PROVIDER_NAME}_client.chat.completions.create( # model="your-model", # tools=tools, # messages=[ # {"role": "system", "content": "You are a helpful assistant."}, # {"role": "user", "content": "Star the composiohq/composio repository"}, # ], # ) # # # Execute the function calls # result = composio.provider.handle_tool_calls(response=response, user_id="default") # print(result)} \`\`\` ## API Reference ### ${TITLE_CASE_PROVIDER_NAME}Provider Class The \`${TITLE_CASE_PROVIDER_NAME}Provider\` class extends \`${IS_AGENTIC:+AgenticProvider}${!IS_AGENTIC:+NonAgenticProvider}\` and provides ${PROVIDER_NAME}-specific functionality. #### Methods ##### \`wrap_tool(tool: Tool${IS_AGENTIC:+, execute_tool: AgenticProviderExecuteFn}) -> ${TITLE_CASE_PROVIDER_NAME}Tool\` Wraps a tool in the ${PROVIDER_NAME} format. \`\`\`python tool = provider.wrap_tool(composio_tool${IS_AGENTIC:+, execute_tool}) \`\`\` ##### \`wrap_tools(tools: Sequence[Tool]${IS_AGENTIC:+, execute_tool: AgenticProviderExecuteFn}) -> ${IS_AGENTIC:+${TITLE_CASE_PROVIDER_NAME}Toolkit}${!IS_AGENTIC:+${TITLE_CASE_PROVIDER_NAME}ToolCollection}\` Wraps multiple tools in the ${PROVIDER_NAME} format. \`\`\`python tools = provider.wrap_tools(composio_tools${IS_AGENTIC:+, execute_tool}) \`\`\` ${!IS_AGENTIC:+##### \`execute_tool_call(user_id: str, tool_call: dict, modifiers: Optional[Modifiers] = None) -> ToolExecutionResponse\` Executes a tool call. \`\`\`python result = provider.execute_tool_call( user_id="default", tool_call={"name": "TOOL_NAME", "arguments": {...}} ) \`\`\`} ## Development 1. Clone the repository 2. Install dependencies: \`uv sync\` 3. Make your changes 4. Run tests: \`pytest\` 5. Format code: \`ruff format\` 6. Lint code: \`ruff check\` ## Contributing We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for more details. ## License Apache License 2.0 ## Support For support, please visit our [Documentation](https://docs.composio.dev) or join our [Discord Community](https://discord.gg/composio). EOL # Make the script executable chmod +x "$PROVIDER_PATH/${PROVIDER_NAME}_demo.py" echo "✨ Created new ${IS_AGENTIC:+agentic }provider at $PROVIDER_PATH" echo "✨ Provider structure:" echo " 📁 $PROVIDER_PATH/" echo " ├── 📄 README.md" echo " ├── 📄 pyproject.toml" echo " ├── 📄 setup.py" echo " ├── 📄 ${PROVIDER_NAME}_demo.py" echo " └── 📁 $PACKAGE_NAME/" echo " ├── 📄 __init__.py" echo " ├── 📄 provider.py" echo " └── 📄 py.typed" echo "" echo "🚀 Next steps:" echo " 1. cd $PROVIDER_PATH" echo " 2. Update provider.py with your implementation" echo " 3. Install in development mode: uv pip install -e ." echo " 4. Test your provider: python ${PROVIDER_NAME}_demo.py"