MCP tool surface changed
If intentional, update tests/mcpjam/__snapshots__/tool-surface.json in this PR.
--- tests/mcpjam/__snapshots__/tool-surface.json 2026-05-18 09:27:29.902210481 +0000
+++ current-surface.json 2026-05-18 09:27:44.543300757 +0000
@@ -175,7 +175,7 @@
"name": "COMPOSIO_REMOTE_BASH_TOOL"
},
{
- "description": "\n Process **REMOTE FILES** or script BULK TOOL EXECUTIONS using Python code IN A REMOTE SANDBOX. If you can see the data in chat, DON'T USE THIS TOOL.\n**ONLY** use this when processing **data stored in a remote file** or when scripting bulk tool executions.\n\nDO NOT USE\n- When the complete response is already inline/in-memory, or you only need quick parsing, summarization, or basic math.\n\nUSE IF\n- To parse/analyze tool outputs saved by COMPOSIO_MULTI_EXECUTE_TOOL to a remote file in the sandbox or to script multi-tool chains there.\n- For bulk or repeated executions of known Composio tools (e.g., add a label to 100 emails).\n- To call APIs via proxy_execute when no Composio tool exists for that API.\n\n\nOUTPUTS\n- Returns a compact result or, if too long, artifacts under `/mnt/files/.composio/output` (cloud-backed FUSE mount, persisted across sandbox restarts).\n\nIMPORTANT CODING RULES:\n 1. Stepwise Execution: Split work into small steps. Save intermediate outputs to `/mnt/files/` (cloud-backed, persisted across failures/timeouts) or variables. Call COMPOSIO_REMOTE_WORKBENCH again for the next step.\n 2. Notebook Persistence: This is a persistent Jupyter notebook cell: variables, functions, imports, and in-memory state persist across executions. Helper functions are preloaded.\n 3. Top-level cells: Do not use `return`; Jupyter only allows it inside functions. For final values, end with `output` or `print(output)`, not `return output`.\n 4. Parallelism & Timeout (CRITICAL): There is a **hard 3-minute (180s) execution limit** per cell. Always prioritize PARALLEL execution using ThreadPoolExecutor for bulk operations - e.g., call run_composio_tool or invoke_llm across rows. If data is large, split it into smaller batches across cells.\n 5. Checkpoints: Save checkpoints to `/mnt/files/` so that long runs can be resumed from the last completed step, even after a timeout or sandbox restart.\n 6. Schema Safety: Never assume the response schema for run_composio_tool if not known already from previous tools. To inspect schema, either run a simple request **outside** the workbench or use invoke_llm helper.\n 7. LLM Helpers: Always use invoke_llm helper for summary, analysis, or field extraction on results; prefer it for much better results over ad hoc filtering.\n 8. Avoid Meta Loops: Do not use run_composio_tool to call COMPOSIO_* meta tools. Only use it for app tools.\n 9. Pagination: Use when data spans multiple pages. Continue fetching pages with the returned next_page_token or cursor until none remains. Parallelize page fetches when the tool supports page_number.\n 10. No Hardcoding: Never hardcode data. Load it from files or tool responses, iterating to construct intermediate or final inputs/outputs.\n 11. To share files with the user: copy final outputs to /mnt/files/ (cloud-backed mount — do NOT use it as a working directory for heavy I/O like ffmpeg; process in /tmp/ first, then copy). Call get_mount_file_url to generate a shareable download URL. Never expose raw sandbox paths to the user. Prefer to download useful artifacts after task is complete.\n\n\nENV & HELPERS:\n- Home directory: `/home/user`.\n- NOTE: Helper functions already initialized in the workbench - DO NOT import or redeclare them:\n - \n`run_composio_tool(tool_slug: str, arguments: dict, *, account: str = None) -> tuple[Dict[str, Any], str]`: Execute a known Composio **app** tool. Do not invent names; match the tool input schema. Use for loops/parallel/bulk calls. Pass account ID or alias to select a specific account.\n i) run_composio_tool returns JSON with top-level \"data\". Parse carefully—structure may be nested.\n \n - \n`invoke_llm(query: str) -> tuple[str, str]`: Invoke an LLM for semantic tasks. Pass MAX 200k characters.\n i) NOTE Prompting guidance: When building prompts for invoke_llm, prefer f-strings (or concatenation) so literal braces stay intact. If using str.format, escape braces by doubling them ({{ }}).\n ii) Define the exact JSON schema you want and batch items into smaller groups to stay within token limit.\n\n - `proxy_execute(method, endpoint, toolkit, query_params=None, body=None, headers=None) -> tuple[Any, str]`: Call a toolkit API directly when no Composio tool exists. Only one toolkit can be invoked with proxy_execute per workbench call\n - `web_search(query: str) -> tuple[str, str]`: Search the web for information.\n - `smart_file_extract(sandbox_file_path: str, show_preview: bool = True) -> tuple[str, str]`: Extracts text from files in the sandbox (e.g., PDF, image).\n - `get_mount_file_url(file_path: str) -> tuple[str, str]`: Generate a shareable download URL for a file in /mnt/files/. This directory is backed by remote cloud storage — do NOT write directly to it during heavy I/O (ffmpeg, large transforms, etc.). Instead, work in /tmp/ or /home/user/ and copy final outputs to /mnt/files/ when done. Then call get_mount_file_url to get a shareable link.\n - Workbench comes with comprehensive Image Processing (PIL/Pillow, OpenCV, scikit-image), PyTorch ML libraries, Document and Report handling tools (pandoc, python-docx, pdfplumber, reportlab), and standard Data Analysis tools (pandas, numpy, matplotlib) for advanced visual, analytical, and AI tasks.\n All helper functions return a tuple (result, error). Always check error before using result.\n\n## Python Helper Functions for LLM Scripting\n\n\n### run_composio_tool\nExecutes a known Composio tool via backend API. Do NOT call COMPOSIO_* meta tools to avoid cycles.\n\n def run_composio_tool(tool_slug: str, arguments: Dict[str, Any], *, account: str = None) -> tuple[Dict[str, Any], str]\n # Returns: (tool_response_dict, error_message)\n # Success: ({\"data\": {actual_data}}, \"\") - Note the top-level data\n # Error: ({}, \"error_message\") or (response_data, \"error_message\")\n\n # Default account (single account or first match):\n result, error = run_composio_tool(\"GMAIL_FETCH_EMAILS\", {\"max_results\": 1, \"user_id\": \"me\"})\n if error:\n print(\"GMAIL_FETCH_EMAILS error:\", error)\n else:\n email_data = result.get(\"data\", {})\n print(\"Fetched:\", email_data)\n\n # Specific account (account ID or alias):\n result, error = run_composio_tool(\"GMAIL_SEND_EMAIL\", {\"to\": \"x@y.com\", \"body\": \"hi\"}, account=\"coup_hurricane_dal_analytical\")\n \n\n\n### invoke_llm\nCalls LLM for reasoning, analysis, and semantic tasks. Pass MAX 200k characters.\n\n def invoke_llm(query: str) -> tuple[str, str]\n # Returns: (llm_response, error_message)\n\n # Example: analyze tool response with LLM\n tool_resp, err = run_composio_tool(\"GMAIL_FETCH_EMAILS\", {\"max_results\": 5, \"user_id\": \"me\"})\n if not err:\n parsed = tool_resp.get(\"data\", {})\n resp, err2 = invoke_llm(f\"Summarize these emails: {parsed}\")\n if not err2:\n print(resp)\n # TIP: batch prompts to reduce LLM calls.\n \n\n\n### proxy_execute\nDirect API call to a connected toolkit service.\n\n def proxy_execute(\n method: Literal[\"GET\",\"POST\",\"PUT\",\"DELETE\",\"PATCH\"],\n endpoint: str,\n toolkit: str,\n query_params: Optional[Dict[str, str]] = None,\n body: Optional[object] = None,\n headers: Optional[Dict[str, str]] = None,\n ) -> tuple[Any, str]\n # Returns: (response_data, error_message)\n\n # Example: GET request with query parameters\n query_params = {\"q\": \"is:unread\", \"maxResults\": \"10\"}\n data, error = proxy_execute(\"GET\", \"/gmail/v1/users/me/messages\", \"gmail\", query_params=query_params)\n if not error:\n print(\"Success:\", data)\n\n\n### web_search\nSearches the web via Exa AI.\n\n def web_search(query: str) -> tuple[str, str]\n # Returns: (search_results_text, error_message)\n\n results, error = web_search(\"latest developments in AI\")\n if not error:\n print(\"Results:\", results)\n\n\n### get_mount_file_url\nGenerates a shareable download URL for a file in /mnt/files/.\n\n**IMPORTANT**: /mnt/files/ is a FUSE mount backed by remote cloud storage (not local disk).\n- Do NOT use it as a working directory for heavy I/O (ffmpeg, image processing, large file transforms).\n- Do all processing in /tmp/ or /home/user/, then copy the final output to /mnt/files/.\n- Use this for files you want the user to be able to download.\n\n def get_mount_file_url(file_path: str) -> tuple[str, str]\n # Returns: (download_url, error_message)\n # Success: (\"https://...\", \"\")\n # Error: (\"\", \"error message\")\n\n # Example: process in /tmp, copy to mount, then get URL\n import shutil\n # ... do heavy processing, write result to /tmp/output.mp4 ...\n shutil.copy(\"/tmp/output.mp4\", \"/mnt/files/output.mp4\")\n url, error = get_mount_file_url(\"output.mp4\")\n if error:\n print(\"Error:\", error)\n else:\n print(\"Download URL:\", url)\n \n\n## Best Practices\n\n\n### Error-first pattern and Defensive parsing (print keys while narrowing)\n res, err = run_composio_tool(\"GMAIL_FETCH_EMAILS\", {\"max_results\": 5})\n if err:\n print(\"error:\", err)\n elif isinstance(res, dict):\n print(\"res keys:\", list(res.keys()))\n data = res.get(\"data\") or {}\n print(\"data keys:\", list(data.keys()))\n msgs = data.get(\"messages\") or []\n print(\"messages count:\", len(msgs))\n for m in msgs:\n print(\"subject:\", m.get(\"subject\", \"<missing>\"))\n\n### Parallelize within the 3-minute cell timeout\nAdjust concurrency so all tasks finish within 3 minutes.\n\n import concurrent.futures\n\n MAX_CONCURRENCY = 10 # Adjust as needed\n\n def process_one(item):\n result, error = run_composio_tool(\"GMAIL_SEND_EMAIL\", item)\n if error:\n return {\"status\": \"failed\", \"error\": error}\n return {\"status\": \"ok\", \"data\": result}\n\n with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_CONCURRENCY) as ex:\n results = list(ex.map(process_one, items))\n \n\n ",
+ "description": "\n Process **REMOTE FILES** or script BULK TOOL EXECUTIONS using Python code IN A REMOTE SANDBOX. If you can see the data in chat, DON'T USE THIS TOOL.\n**ONLY** use this when processing **data stored in a remote file** or when scripting bulk tool executions.\n\nDO NOT USE\n- When the complete response is already inline/in-memory, or you only need quick parsing, summarization, or basic math.\n\nUSE IF\n- To parse/analyze tool outputs saved by COMPOSIO_MULTI_EXECUTE_TOOL to a remote file in the sandbox or to script multi-tool chains there.\n- For bulk or repeated executions of known Composio tools (e.g., add a label to 100 emails).\n- To call APIs via proxy_execute when no Composio tool exists for that API.\n\n\nOUTPUTS\n- Returns a compact result or, if too long, artifacts under `/mnt/files/.composio/output` (cloud-backed FUSE mount, persisted across sandbox restarts).\n\nIMPORTANT CODING RULES:\n 1. Stepwise Execution: Split work into small steps. Save intermediate outputs to `/mnt/files/` (cloud-backed, persisted across failures/timeouts) or variables. Call COMPOSIO_REMOTE_WORKBENCH again for the next step.\n 2. Notebook Persistence: This is a persistent Jupyter notebook cell: variables, functions, imports, and in-memory state persist across executions. Helper functions are preloaded.\n 3. Top-level cells: Do not use `return`; Jupyter only allows it inside functions. For final values, end with `output` or `print(output)`, not `return output`.\n 4. Parallelism & Timeout (CRITICAL): There is a **hard 3-minute (180s) execution limit** per cell. Always prioritize PARALLEL execution using ThreadPoolExecutor for bulk operations - e.g., call run_composio_tool or invoke_llm across rows. If data is large, split it into smaller batches across cells.\n 5. Checkpoints: Save checkpoints to `/mnt/files/` so that long runs can be resumed from the last completed step, even after a timeout or sandbox restart.\n 6. Schema Safety: Never assume the response schema for run_composio_tool if not known already from previous tools. To inspect schema, either run a simple request **outside** the workbench or use invoke_llm helper.\n 7. LLM Helpers: Always use invoke_llm helper for summary, analysis, or field extraction on results; prefer it for much better results over ad hoc filtering.\n 8. Avoid Meta Loops: Do not use run_composio_tool to call COMPOSIO_* meta tools. Only use it for app tools.\n 9. Pagination: Use when data spans multiple pages. Continue fetching pages with the returned next_page_token or cursor until none remains. Parallelize page fetches when the tool supports page_number.\n 10. No Hardcoding: Never hardcode data. Load it from files or tool responses, iterating to construct intermediate or final inputs/outputs.\n 11. To share files with the user: copy final outputs to /mnt/files/ (cloud-backed mount — do NOT use it as a working directory for heavy I/O like ffmpeg; process in /tmp/ first, then copy). Call get_mount_file_url to generate a shareable download URL. Never expose raw sandbox paths to the user. Prefer to download useful artifacts after task is complete.\n\n\nENV & HELPERS:\n- Home directory: `/home/user`.\n- NOTE: Helper functions already initialized in the workbench - DO NOT import or redeclare them:\n - \n`run_composio_tool(tool_slug: str, arguments: dict, *, account: str = None) -> tuple[Dict[str, Any], str]`: Execute a known Composio **app** tool. Do not invent names; match the tool input schema. Use for loops/parallel/bulk calls. Pass account ID or alias to select a specific account.\n i) run_composio_tool returns JSON with top-level \"data\". Parse carefully—structure may be nested.\n \n - \n`invoke_llm(query: str) -> tuple[str, str]`: Invoke an LLM for semantic tasks. Pass MAX 200k characters.\n i) NOTE Prompting guidance: When building prompts for invoke_llm, prefer f-strings (or concatenation) so literal braces stay intact. If using str.format, escape braces by doubling them ({{ }}).\n ii) Define the exact JSON schema you want and batch items into smaller groups to stay within token limit.\n\n - `proxy_execute(method, endpoint, toolkit, query_params=None, body=None, headers=None) -> tuple[Any, str]`: Call a toolkit API directly when no Composio tool exists. Only one toolkit can be invoked with proxy_execute per workbench call\n - `web_search(query: str) -> tuple[str, str]`: Search the web for information.\n - `smart_file_extract(sandbox_file_path: str, show_preview: bool = True) -> tuple[str, str]`: Extracts text from files in the sandbox (e.g., PDF, image).\n - `get_mount_file_url(file_path: str) -> tuple[str, str]`: Generate a shareable download URL for a file in /mnt/files/. See helper docs below for /mnt/files/ heavy-I/O caveats.\n All helper functions return a tuple (result, error). Always check error before using result.\n\n## Python Helper Functions for LLM Scripting\n\n\n### run_composio_tool\nExecutes a known Composio tool via backend API. Do NOT call COMPOSIO_* meta tools to avoid cycles.\n\n def run_composio_tool(tool_slug: str, arguments: Dict[str, Any], *, account: str = None) -> tuple[Dict[str, Any], str]\n # Returns: (tool_response_dict, error_message)\n # Success: ({\"data\": {actual_data}}, \"\") - Note the top-level data\n # Error: ({}, \"error_message\") or (response_data, \"error_message\")\n\n # Default account (single account or first match):\n result, error = run_composio_tool(\"GMAIL_FETCH_EMAILS\", {\"max_results\": 1, \"user_id\": \"me\"})\n if error:\n print(\"GMAIL_FETCH_EMAILS error:\", error)\n else:\n email_data = result.get(\"data\", {})\n print(\"Fetched:\", email_data)\n\n # Specific account (account ID or alias):\n result, error = run_composio_tool(\"GMAIL_SEND_EMAIL\", {\"to\": \"x@y.com\", \"body\": \"hi\"}, account=\"coup_hurricane_dal_analytical\")\n \n\n\n### invoke_llm\nCalls LLM for reasoning, analysis, and semantic tasks. Pass MAX 200k characters.\n\n # Returns: (llm_response, error_message)\n\n # Example: analyze tool response with LLM\n tool_resp, err = run_composio_tool(\"GMAIL_FETCH_EMAILS\", {\"max_results\": 5, \"user_id\": \"me\"})\n if not err:\n parsed = tool_resp.get(\"data\", {})\n resp, err2 = invoke_llm(f\"Summarize these emails: {parsed}\")\n if not err2:\n print(resp)\n # TIP: batch prompts to reduce LLM calls.\n \n\n\n### proxy_execute\nDirect API call to a connected toolkit service.\n\n def proxy_execute(\n method: Literal[\"GET\",\"POST\",\"PUT\",\"DELETE\",\"PATCH\"],\n endpoint: str,\n toolkit: str,\n query_params: Optional[Dict[str, str]] = None,\n body: Optional[object] = None,\n headers: Optional[Dict[str, str]] = None,\n ) -> tuple[Any, str]\n # Returns: (response_data, error_message)\n\n # Example: GET request with query parameters\n query_params = {\"q\": \"is:unread\", \"maxResults\": \"10\"}\n data, error = proxy_execute(\"GET\", \"/gmail/v1/users/me/messages\", \"gmail\", query_params=query_params)\n if not error:\n print(\"Success:\", data)\n\n\n### web_search\nSearches the web via Exa AI.\n\n # Returns: (search_results_text, error_message)\n\n results, error = web_search(\"latest developments in AI\")\n if not error:\n print(\"Results:\", results)\n\n\n### get_mount_file_url\nGenerates a shareable download URL for a file in /mnt/files/.\n\n**IMPORTANT**: /mnt/files/ is a FUSE mount backed by remote cloud storage (not local disk).\n- Do NOT use it as a working directory for heavy I/O (ffmpeg, image processing, large file transforms).\n- Do all processing in /tmp/ or /home/user/, then copy the final output to /mnt/files/.\n- Use this for files you want the user to be able to download.\n\n # Returns: (download_url, error_message)\n # Success: (\"https://...\", \"\")\n # Error: (\"\", \"error message\")\n\n # Example: process in /tmp, copy to mount, then get URL\n import shutil\n # ... do heavy processing, write result to /tmp/output.mp4 ...\n shutil.copy(\"/tmp/output.mp4\", \"/mnt/files/output.mp4\")\n url, error = get_mount_file_url(\"output.mp4\")\n if error:\n print(\"Error:\", error)\n else:\n print(\"Download URL:\", url)\n \n\n## Best Practices\n\n\n### Error-first pattern and Defensive parsing (print keys while narrowing)\n res, err = run_composio_tool(\"GMAIL_FETCH_EMAILS\", {\"max_results\": 5})\n if err:\n print(\"error:\", err)\n elif isinstance(res, dict):\n print(\"res keys:\", list(res.keys()))\n data = res.get(\"data\") or {}\n print(\"data keys:\", list(data.keys()))\n msgs = data.get(\"messages\") or []\n print(\"messages count:\", len(msgs))\n for m in msgs:\n print(\"subject:\", m.get(\"subject\", \"<missing>\"))\n\n### Parallelize within the 3-minute cell timeout\nAdjust concurrency so all tasks finish within 3 minutes.\n\n import concurrent.futures\n\n MAX_CONCURRENCY = 10 # Adjust as needed\n\n def process_one(item):\n result, error = run_composio_tool(\"GMAIL_SEND_EMAIL\", item)\n if error:\n return {\"status\": \"failed\", \"error\": error}\n return {\"status\": \"ok\", \"data\": result}\n\n with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_CONCURRENCY) as ex:\n results = list(ex.map(process_one, items))\n \n\n ",
"inputSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
@@ -209,7 +209,7 @@
"name": "COMPOSIO_REMOTE_WORKBENCH"
},
{
- "description": "\n Tool Server Info: Composio connects 500+ apps—Slack, GitHub, Notion, Google Workspace (Gmail, Sheets, Drive, Calendar), Microsoft (Outlook, Teams), X/Twitter, Figma, Web Search / Deep research, Browser tool (scrape URLs, browser automation), Meta apps (Instagram, Meta Ads), TikTok, AI tools like Nano Banana & Veo3, and more—for seamless cross-app automation.\n Use this tool to discover relevant tools plus the recommended plan and common pitfalls for reliable execution.\n Always call this tool first whenever a user mentions or implies an external app, service, or workflow—never say \"I don't have access to X/Y app\" before calling it.\n\nUsage guidelines:\n - Use this tool whenever kicking off a task. Re-run it when you need additional tools/plans due to missing details, errors, or a changed use case.\n - If the user pivots to a different use case in same chat, you MUST call this tool again with the new use case and generate a new session_id.\n - Specify the use_case with a normalized description of the problem, query, or task. Be clear and precise. Queries can be simple single-app actions or multiple linked queries for complex cross-app workflows.\n - Pass known_fields along with use_case as a string of key–value hints (for example, \"channel_name: general\") to help the search resolve missing details such as IDs.\n \n- User has manually connected the apps: agent_mail, googledocs. Prefer these apps when intent is unclear.\n\nSplitting guidelines (Important):\n 1. Atomic queries: 1 query = 1 tool call. Include hidden prerequisites (e.g., add \"get Linear issue\" before \"update Linear issue\").\n 2. Include app names: If user names a toolkit, include it in every sub query so intent stays scoped (e.g., \"fetch Gmail emails\", \"reply to Gmail email\").\n 3. English input: Translate non-English prompts while preserving intent and identifiers.\n\n Example:\n User query: \"send an email to John welcoming him and create a meeting invite for tomorrow\"\n Search call: queries: [\n {use_case: \"send an email to someone\", known_fields: \"recipient_name: John\"},\n {use_case: \"create a meeting invite\", known_fields: \"meeting_date: tomorrow\"}\n ]\n\nPlan review checklist (Important):\n - The response includes a detailed execution plan and common pitfalls. You MUST review this plan carefully, adapt it to your current context, and generate your own final step-by-step plan before execution. Execute the steps in order to ensure reliable and accurate execution. Skipping or ignoring required steps can lead to unexpected failures.\n - Check the plan and pitfalls for input parameter nuances (required fields, IDs, formats, limits). Before executing any tool, you MUST review its COMPLETE input schema and provide STRICTLY schema-compliant arguments to avoid invalid-input errors.\n - Determine whether pagination is needed; if a response returns a pagination token and completeness is implied, paginate until exhaustion and do not return partial results.\n\nResponse:\n - Tools & Input Schemas: The response lists toolkits (apps) and tools suitable for the task, along with their tool_slug, description, input schema / schemaRef, and related tools for prerequisites, alternatives, or next steps.\n - NOTE: Tools with schemaRef instead of input_schema require you to call COMPOSIO_GET_TOOL_SCHEMAS first to load their full input_schema before use.\n - Connection Info: If a toolkit has an active connection, the response includes it along with any available current user information. If no active connection exists, you MUST initiate a new connection via COMPOSIO_MANAGE_CONNECTIONS with the correct toolkit name. DO NOT execute any toolkit tool without an ACTIVE connection.\n - Time Info: The response includes the current UTC time for reference. You can reference UTC time from the response if needed.\n \n - The tools returned to you through this are to be called via COMPOSIO_MULTI_EXECUTE_TOOL. Ensure each tool execution specifies the correct tool_slug and arguments exactly as defined by the tool's input schema.\n \nSESSION: ALWAYS set this parameter, first for any workflow. Pass session: {generate_id: true} for new workflows OR session: {id: \"EXISTING_ID\"} to continue. ALWAYS use the returned session_id in ALL subsequent meta tool calls.\n ",
+ "description": "\n Tool Server Info: Composio connects 500+ apps—Slack, GitHub, Notion, Google Workspace (Gmail, Sheets, Drive, Calendar), Microsoft (Outlook, Teams), X/Twitter, Figma, Web Search / Deep research, Browser tool (scrape URLs, browser automation), Meta apps (Instagram, Meta Ads), TikTok, and more—for seamless cross-app automation.\n Use this tool to discover relevant tools plus the recommended plan and common pitfalls for reliable execution.\n Always call this tool first whenever a user mentions or implies an external app, service, or workflow—never say \"I don't have access to X/Y app\" before calling it.\n\nUsage guidelines:\n - Use this tool whenever kicking off a task. Re-run it when you need additional tools/plans due to missing details, errors, or a changed use case.\n - If the user pivots to a different use case in same chat, you MUST call this tool again with the new use case and generate a new session_id.\n - Specify the use_case with a normalized description of the problem, query, or task. Be clear and precise. Queries can be simple single-app actions or multiple linked queries for complex cross-app workflows.\n - Pass known_fields along with use_case as a string of key–value hints (for example, \"channel_name: general\") to help the search resolve missing details such as IDs.\n \n- User has manually connected the apps: agent_mail, gmail, googledocs. Prefer these apps when intent is unclear.\n\nSplitting guidelines (Important):\n 1. Atomic queries: 1 query = 1 tool call. Include hidden prerequisites (e.g., add \"get Linear issue\" before \"update Linear issue\").\n 2. Include app names: If user names a toolkit, include it in every sub query so intent stays scoped (e.g., \"fetch Gmail emails\", \"reply to Gmail email\").\n 3. English input: Translate non-English prompts while preserving intent and identifiers.\n\n Example:\n User query: \"send an email to John welcoming him and create a meeting invite for tomorrow\"\n Search call: queries: [\n {use_case: \"send an email to someone\", known_fields: \"recipient_name: John\"},\n {use_case: \"create a meeting invite\", known_fields: \"meeting_date: tomorrow\"}\n ]\n\nPlan review checklist (Important):\n - The response includes a detailed execution plan and common pitfalls. You MUST review this plan carefully, adapt it to your current context, and generate your own final step-by-step plan before execution. Execute the steps in order to ensure reliable and accurate execution. Skipping or ignoring required steps can lead to unexpected failures.\n - Check the plan and pitfalls for input parameter nuances (required fields, IDs, formats, limits). Before executing any tool, you MUST review its COMPLETE input schema and provide STRICTLY schema-compliant arguments to avoid invalid-input errors.\n - Determine whether pagination is needed; if a response returns a pagination token and completeness is implied, paginate until exhaustion and do not return partial results.\n\nResponse:\n - Tools & Input Schemas: The response lists toolkits (apps) and tools suitable for the task, along with their tool_slug, description, input schema / schemaRef, and related tools for prerequisites, alternatives, or next steps.\n - NOTE: Tools with schemaRef instead of input_schema require you to call COMPOSIO_GET_TOOL_SCHEMAS first to load their full input_schema before use.\n - Connection Info: If a toolkit has an active connection, the response includes it along with any available current user information. If no active connection exists, you MUST initiate a new connection via COMPOSIO_MANAGE_CONNECTIONS with the correct toolkit name. DO NOT execute any toolkit tool without an ACTIVE connection.\n - Time Info: The response includes the current UTC time for reference. You can reference UTC time from the response if needed.\n \n - The tools returned to you through this are to be called via COMPOSIO_MULTI_EXECUTE_TOOL. Ensure each tool execution specifies the correct tool_slug and arguments exactly as defined by the tool's input schema.\n \nSESSION: ALWAYS set this parameter, first for any workflow. Pass session: {generate_id: true} for new workflows OR session: {id: \"EXISTING_ID\"} to continue. ALWAYS use the returned session_id in ALL subsequent meta tool calls.\n ",
"inputSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,