feat(composio): key-gated Composio toolkit access over MCP - #327
Open
plombeer31 wants to merge 1 commit into
Open
feat(composio): key-gated Composio toolkit access over MCP#327plombeer31 wants to merge 1 commit into
plombeer31 wants to merge 1 commit into
Conversation
Composio is a hosted catalogue of ~1500 SaaS toolkits (Gmail, Slack, Notion, Linear, Jira, ...) that also brokers each app's OAuth. This wires it in as one more MCP server rather than as a bespoke integration: a tool-router session yields a Streamable-HTTP MCP endpoint authenticated by a static `x-api-key` header, which is exactly the transport src/mcp/ already speaks. No SDK. `@composio/core` would drag a transitive dependency tree into a project that ships a single-file SEA binary, and would duplicate lifecycle, retry, approval and status machinery the MCP client already owns. The whole integration needs one HTTP call. The session model keeps the prompt cost flat: instead of ~1500 toolkits' worth of schemas, four meta-tools land in the registry (SEARCH_TOOLS, GET_TOOL_SCHEMAS, MANAGE_CONNECTIONS, MULTI_EXECUTE_TOOL) and the agent discovers real tools at runtime. Nothing is user-visible yet -- the key-entry UI is the Integrations hub in a follow-up. Set COMPOSIO_API_KEY in <stateDir>/.env to try it. Design notes: - The key is the only gate. No key (or `composio.enabled: false`) and no server is appended, so no Composio tool is ever registered and the model cannot reach one. - The key never enters config.json -- it lives in <stateDir>/.env, matching TELEGRAM_BOT_TOKEN and the web.search.*.apiKeyEnv precedent. Config carries only the switch, the env-var name, and cached session ids. - Failure is soft. Composio being down must not stop the agent from booting; it logs a warning and continues with no server. - `userId` is a minted UUID, persisted, never an email: Composio scopes connected accounts to it, so regenerating it would orphan every app the operator had already authorised. - The remote workbench is disabled explicitly -- it duplicates os.shell.run and would route the operator's shell work through a third-party sandbox. - Trust stays approval_gated. Discovery is still unprompted because mcp-tool-adapter exempts tools annotated readOnlyHint, which is how Composio tags its two discovery tools, while the two mutating tools are tagged destructive and keep hitting the approval gate. Config v50: additive `composio` block; older files inherit defaults that mount nothing. Verified against a live Composio key: server connects `up` with 4 tools registered as mcp.composio.*, the session is cached and reused on the next boot, the key does not appear in config.json, and with no key the runtime reports zero MCP servers and zero mcp.* tools.
This was referenced Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Wires Composio — a hosted catalogue of ~1500 SaaS toolkits (Gmail, Slack, Notion, Linear, Jira, …) that also brokers each app's OAuth — into the agent as one more MCP server.
A Composio tool-router session yields a Streamable-HTTP MCP endpoint authenticated by a static
x-api-keyheader, which is exactly the transportsrc/mcp/already speaks. The cold-path wiring is a singleawait resolveComposioServerConfig(...)that appends at most one entry to the server list beforeMcpManageris constructed.Nothing is user-visible yet. The key-entry UI is the Integrations hub in a follow-up PR. To try this one, put
COMPOSIO_API_KEY=…in<stateDir>/.env.Why no SDK
@composio/corewould drag a transitive dependency tree into a project that ships a single-file SEA binary, and would duplicate the lifecycle, retry, approval and status machinerysrc/mcp/already owns. The whole integration needs exactly one HTTP call.Composio's session model is also what keeps this cheap in prompt terms: rather than loading ~1500 toolkits' worth of schemas, the session exposes four meta-tools and the agent discovers real tools at runtime. Stable-prefix cost is four tools, flat, regardless of catalogue size.
readOnlyHintCOMPOSIO_SEARCH_TOOLStrueCOMPOSIO_GET_TOOL_SCHEMAStrueCOMPOSIO_MANAGE_CONNECTIONSfalse, destructiveCOMPOSIO_MULTI_EXECUTE_TOOLfalse, destructiveDesign notes
composio.enabled: false) ⇒ no server appended ⇒ no Composio tool registered ⇒ the model cannot see or call one. No second switch, no partial state.config.json. It lives in<stateDir>/.envunder the name incomposio.apiKeyEnv, matchingTELEGRAM_BOT_TOKENand theweb.search.*.apiKeyEnvprecedent. Config carries only the switch, the env-var name, and cached session ids.userIdis a minted UUID, persisted, never an email. Composio scopes connected accounts to it, so regenerating it would silently orphan every app the operator had already authorised — and an email would hand PII to a third party for no benefit.workbench: { enable: false }dropsCOMPOSIO_REMOTE_WORKBENCH/COMPOSIO_REMOTE_BASH_TOOL, which duplicateos.shell.runand would route the operator's shell work through a third-party sandbox.approval_gated. Loosening it topure_readwould un-gate the writes too. Discovery is unprompted anyway, via the existingreadOnlyHintexemption inmcp-tool-adapter.ts— so the seamlessness costs nothing in consent.Config
v50 adds an additive
composioblock (enabled,apiKeyEnv,userId,sessionId,mcpUrl). Older files inherit defaults that mount nothing. v49 was appended toSUPPORTED_INPUT_VERSIONS.Testing
npm run lintandnpm testgreen (7157 tests). 31 new unit tests across five files.Verified against a live Composio key on a scratch state dir:
upwithtoolCount: 4; registry showsmcp.composio.COMPOSIO_{SEARCH_TOOLS,GET_TOOL_SCHEMAS,MANAGE_CONNECTIONS,MULTI_EXECUTE_TOOL}config.json, reused unchanged on the next boot (no second API call)config.jsonMCP servers: [],mcp.* tools: []Annotations were read off the live endpoint, not assumed — that is where the gating table above comes from.