The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Actions | Updated (UTC) |
|---|---|---|---|
| dashboard | Preview, Comment | May 11, 2026 9:34am |
Refresh both CLAUDE.md files in the dashboard to reflect the current state of main:
Root CLAUDE.md (207 → 125 lines):
(org)/[org]/~/connect to the new (connect)/[org]/~/connect/ group (added in f032cb8e refactor: move org, consumer, project outside of private to avoid double query).package.json scripts: pnpm check (lint + typecheck + tenant-scope audit), pnpm audit:tenant-scope, tsgo typecheck + tsc legacy fallback, dev:portless / dev:local-apollo, test:mcp-auth[:local], email:dev, and the fusionauth:* family.clients/intercom, clients/decimal, src/workflows/ (Resend), src/hooks/, src/middleware.ts, src/types/, the expanded server/clients/ (scoped-db, scoped-clickhouse, plain, ratelimit, consumer-queries, otel), server/email/, and the broader server/utils/.useDecodedPathname, tracked-over-ui, _analytics.ts prefixes (DASHBOARD_, CONSUMER_, ONBOARDING_), pages-are-thin, writes-via-Apollo, parallel-agents /ship rules, boundaries.src/app/(public)/(auth)/CLAUDE.md (131 → 46 lines):
cli/authorize/[key]/page.tsx path-based flow alongside the legacy cookie-based cli/authorize/page.tsx, and documented the composio-dash.cli_key cookie./api/dev-login) used by agent-browser tests.AUTH_ERROR_MESSAGES lookup is now lowercase-normalized (Better Auth emits both casings — OAuth state_not_found vs magic-link ATTEMPTS_EXCEEDED).(connect)/[org]/~/connect/layout.tsx.Docs-only change — verified line counts are within update-claude-docs targets:
| File | Before | After | Target |
|---|---|---|---|
CLAUDE.md | 207 | 125 | 60-150 (root) |
src/app/(public)/(auth)/CLAUDE.md | 131 | 46 | 30-70 (directory) |
Cross-checked every claim against the current tree: src/app/(connect)/[org]/~/connect/, src/app/(public)/(auth)/cli/authorize/{page.tsx,[key]/page.tsx}, src/app/api/dev-login/, src/server/clients/ contents, and package.json scripts.
Origin: cron-update-claude-docs / zen-cron-c38de98cf7a0 Triggered by: karan@composio.dev | Source: cron Session: https://zen-api-production-4c98.up.railway.app/dashboard/#/chat/zen-cron-c38de98cf7a0
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Actions | Updated (UTC) |
|---|---|---|---|
| dashboard | Preview, Comment | May 11, 2026 9:34am |
If intentional, update tests/mcpjam/__snapshots__/tool-surface.json in this PR.
--- tests/mcpjam/__snapshots__/tool-surface.json 2026-05-11 09:35:34.987124733 +0000
+++ current-surface.json 2026-05-11 09:35:47.722025981 +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,