-
Notifications
You must be signed in to change notification settings - Fork 9
feat: add Mercury MCP Integration Security & Architecture Guide #203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| # Mercury MCP Integration — Security & Architecture Guide | ||
|
|
||
| ## 0. Structural diagram | ||
|
|
||
|  | ||
|
|
||
| ## 1. End-to-End Architecture | ||
|
|
||
| ``` | ||
| ┌─────────────────────────────────────────────────────────────────────┐ | ||
| │ SUPERCODE USER │ | ||
| │ (CLI or Web dashboard) │ | ||
| └────────────────────────────┬────────────────────────────────────────┘ | ||
| │ Authenticated session | ||
| ▼ | ||
| ┌─────────────────────────────────────────────────────────────────────┐ | ||
| │ MCP MANAGER (McpManager) │ | ||
| │ • HTTP/SSE transport to Composio MCP gateway │ | ||
| │ • HTTP/SSE transport to MergeDev connector gateway │ | ||
| │ • Prefixes tools as mcp_composio_* and mcp_mergedev_* │ | ||
| │ • All tool invocations gated by PermissionManager.check() │ | ||
| └────────────────────────────┬────────────────────────────────────────┘ | ||
| │ | ||
| │ Parallel gateways | ||
| │ | ||
| ┌──────────────┴──────────────┐ | ||
| │ │ | ||
| ▼ ▼ | ||
| ┌──────────────────────────────┐ ┌──────────────────────────────┐ | ||
| │ COMPOSIO GATEWAY │ │ MERGEDEV GATEWAY │ | ||
| │ │ │ │ | ||
| │ • OAuth flow & session │ │ • Connector governance │ | ||
| │ • Token storage/refresh │ │ • Policy enforcement │ | ||
| │ • Connected account mgmt │ │ • Per-call audit logs │ | ||
| │ • Mercury API proxy │ │ • Rate limiting │ | ||
| │ │ │ • Scoped tool access │ | ||
| └──────────────┬───────────────┘ └──────────────┬───────────────┘ | ||
| │ │ | ||
| └────────────────┬─────────────────┘ | ||
| │ | ||
| ▼ | ||
| ┌─────────────────────────────────────────────────────────────────────┐ | ||
| │ MERCURY API │ | ||
| │ Read-only: Balances · Transactions · Vendors │ | ||
| └─────────────────────────────────────────────────────────────────────┘ | ||
| ``` | ||
|
|
||
| ### How the Parallel Flow Works | ||
|
|
||
| ``` | ||
| Mercury MCP tool call (e.g., mcp_composio_mercury_get_balance) | ||
| │ | ||
| ▼ | ||
| McpManager routes the call to BOTH gateways independently: | ||
| │ | ||
| ├──→ Composio validates session + OAuth token | ||
| │ → Proxies request to Mercury API → returns result | ||
| │ | ||
| └──→ MergeDev validates policy + enforces governance | ||
| → Logs the call to audit trail → passes or blocks | ||
| ``` | ||
|
|
||
| For a Mercury operation to succeed, **both gateways must authorize it**. Composio provides the authenticated session and OAuth token — without this, Mercury rejects the call entirely. MergeDev enforces connector-level policy, rate limits, and per-call audit logging — without this, the call is blocked at the governance layer. | ||
|
|
||
| ### Key Architectural Invariants | ||
|
|
||
| - **No component calls Mercury directly.** Every Mercury API request passes through the Composio MCP gateway, which handles OAuth authentication and proxies the request. Supercode never holds Mercury OAuth tokens. | ||
| - **MergeDev is the governance layer — not a proxy.** MergeDev enforces policy, logs invocations, and can block operations, but the actual Mercury API call flows through Composio's session. MergeDev and Composio operate independently. | ||
| - **Both must pass.** A call blocked by MergeDev never reaches Mercury, even if Composio returns a valid session. A call with an invalid Composio session fails at the auth layer regardless of MergeDev's policy decision. | ||
| - **All tools are read-only.** Only balance queries, transaction history, and vendor listing are exposed. No payment initiation or vendor modification. Mercury enforces this at the OAuth scope level. | ||
|
|
||
| --- | ||
|
|
||
| ## 2. OAuth Flow (via Composio) | ||
|
|
||
| ``` | ||
| User clicks "Connect Mercury" in Supercode | ||
| │ | ||
| ▼ | ||
| ComposioSessionManager.connectApp("mercury", userId) | ||
| → Composio SDK: toolkits.authorize(userId, "mercury") | ||
| → Returns redirectUrl (Composio-hosted OAuth consent page) | ||
| │ | ||
| ▼ | ||
| Browser opens → Mercury OAuth consent screen (read-only scopes) | ||
| │ | ||
| ▼ | ||
| User authorizes → Mercury sends callback to Composio | ||
| │ | ||
| ▼ | ||
| Composio polls: waitForConnection() until account status = ACTIVE | ||
| │ | ||
| ▼ | ||
| Session re-created with connected account: | ||
| composio.sessions.create(userId, { | ||
| mcp: true, | ||
| connectedAccounts: { mercury: connectedAccountId } | ||
| }) | ||
| → Returns { url, headers, sessionId } | ||
| │ | ||
| ▼ | ||
| McpManager.reconnectServer("composio", { url, headers }) | ||
| McpManager.reconnectServer("mergedev", { url, headers }) | ||
| → Both Mercury MCP connections now active in Supercode | ||
| ``` | ||
|
|
||
| ### Tool Naming | ||
|
|
||
| - `mcp_composio_mercury_get_balance` — actual Mercury API call via Composio | ||
| - `mcp_mergedev_mercury_get_balance` — governance check via MergeDev (policy + audit) | ||
|
|
||
| The agent calls the composio-prefixed tool to execute the operation. The mergedev-prefixed governance runs in parallel to authorize, log, and enforce policy. | ||
|
|
||
| --- | ||
|
|
||
| ## 3. Permission Enforcement | ||
|
|
||
| Every Mercury MCP tool invocation flows through the PermissionManager before reaching either gateway: | ||
|
|
||
| ``` | ||
| Agent calls mcp_composio_mercury_get_balance | ||
| │ | ||
| ▼ | ||
| PermissionManager.check(toolName, args) | ||
| ├── 1. Session-level override? (allow / deny / ask) | ||
| ├── 2. DEFAULT_RULES evaluated (wildcard match, last rule wins) | ||
| ├── 3. Agent ruleset merged (per-agent permission isolation) | ||
| ├── 4. Saved user grants ("always for session") applied | ||
| └── 5. If "ask" → interactive user prompt (once / always / reject) | ||
| │ | ||
| ▼ | ||
| If allowed → McpManager forwards to both gateways: | ||
| ├──→ Composio: validate session → proxy to Mercury → return result | ||
| └──→ MergeDev: validate policy → log audit → pass or block | ||
| If denied → Returns { success: false, cancelled: true, reason: "..." } | ||
| ``` | ||
|
|
||
| ### Agent Rulesets Applied to Mercury Tools | ||
|
|
||
| | Agent | Mercury Tool Access | | ||
| |-------|-------------------| | ||
| | build | Allowed (tools are inherently read-only at API level) | | ||
| | plan | Allowed (read operations in permission set) | | ||
| | explore | Allowed (read operations in permission set) | | ||
| | general | Allowed | | ||
|
|
||
| All agents can invoke Mercury read tools because the PermissionManager's DEFAULT_RULES allow unknown tools with a prompt, and agent rulesets permit read operations. The critical security boundary is at the OAuth scope level — Mercury enforces read-only regardless of what the agent attempts. | ||
|
|
||
| ### Subagent Isolation | ||
|
|
||
| When a parent agent spawns a subagent, the parent's DENY rules are automatically re-appended after the child's rules. This prevents a restricted parent from being bypassed by spawning a permissive subagent. | ||
|
|
||
| --- | ||
|
|
||
| ## 4. Data Handling & Retention | ||
|
|
||
| ### What Supercode Persists (Mercury-Related) | ||
|
|
||
| **Nothing.** The Supercode PostgreSQL schema contains no Mercury-specific tables. No balances, transactions, vendor info, or OAuth tokens are stored in Supercode's database. | ||
|
|
||
|
Comment on lines
+157
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the document and related mentions of Mercury retention/persistence.
git ls-files | rg 'supercode-mercury\.md|mercury|chat history|provider payload|trace|log|retention|redact|response'
# Show the relevant section of the target file with line numbers.
if git ls-files | rg -q '^supercode-mercury\.md$'; then
nl -ba supercode-mercury.md | sed -n '140,280p'
fi
# Search for persistence/retention language across the repo.
rg -n --hidden --glob '!**/.git/**' \
-e 'persist(ed|s|ence)|stored|storage|retention|redact|redaction|trace|traces|log(s|ged|ging)?|chat history|conversation message|provider payload|Mercury' .Repository: yashdev9274/supercli Length of output: 934 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Read the target document section with portable line numbering.
sed -n '140,280p' supercode-mercury.md | cat -n
printf '\n--- MATCHES ---\n'
# Search for Mercury-related persistence/retention wording in the repository.
rg -n --hidden --glob '!**/.git/**' \
-e 'persist(ed|s|ence)|stored|storage|retention|redact|redaction|trace|traces|log(s|ged|ging)?|chat history|conversation message|provider payload|Mercury' supercode-mercury.md .Repository: yashdev9274/supercli Length of output: 50377 Clarify the Mercury retention boundary. Supercode does persist Mercury-derived data in 🤖 Prompt for AI Agents |
||
| ### What Composio Persists | ||
|
|
||
| | Data | Location | Retention | | ||
| |------|----------|-----------| | ||
| | Mercury OAuth tokens (access + refresh, read-only scoped) | Composio infrastructure | Until user revokes or token expires | | ||
| | Connected account metadata (account ID, status, provider) | Composio infrastructure | Until disconnected | | ||
| | Active MCP session state | Composio infrastructure | Per session lifecycle | | ||
|
Comment on lines
+161
to
+167
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the relevant code paths and map their structure first.
git ls-files | rg '^(apps/supercode-cli/server/src/index\.ts|.*connectedAccounts.*|.*mcp.*|.*account.*)$'
echo
echo "== Outline of apps/supercode-cli/server/src/index.ts =="
ast-grep outline apps/supercode-cli/server/src/index.ts --view expanded || true
echo
echo "== Search for connectedAccounts usage =="
rg -n "connectedAccounts\.list|connectedAccounts|ACTIVE account|Mercury" apps/supercode-cli/server/src/index.ts apps/supercode-cli/server/src || true
echo
echo "== Search for user/tenant scoping around connected accounts =="
rg -n "tenant|userId|accountId|workspace|project|session" apps/supercode-cli/server/src || true
echo
echo "== Search for tests or helpers around connected accounts =="
rg -n "connectedAccounts|Mercury|ACTIVE" . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || trueRepository: yashdev9274/supercli Length of output: 39526 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== apps/supercode-cli/server/src/index.ts (around Composio session proxy) =="
sed -n '1080,1155p' apps/supercode-cli/server/src/index.ts | cat -n
echo
echo "== apps/supercode-cli/server/src/mcp/composio.ts (session/account handling) =="
sed -n '1,280p' apps/supercode-cli/server/src/mcp/composio.ts | cat -n
echo
echo "== Search for any user-scoped filtering passed to connectedAccounts.list() =="
rg -n "connectedAccounts\.list\(\{[^}]*user|connectedAccounts\.list\(\{[^}]*account|connectedAccounts\.list\(\{[^}]*tenant|connectedAccounts\.list\(" apps/supercode-cli/server/srcRepository: yashdev9274/supercli Length of output: 11851 🌐 Web query:
💡 Result: In Composio, connected accounts and sessions are managed within the scope of a specific project, as projects serve as the primary isolation boundary for all resources, including connected accounts, authentication configurations, and API keys [1][2]. Connected Accounts To retrieve connected accounts, use the connectedAccounts.list method [3]. This method supports filtering by various criteria, including user IDs, via an optional query object [3][4][5]. Example (TypeScript): // List all accounts for a specific user const userAccounts = await composio.connectedAccounts.list({ userIds: ['user_123'] }); Example (Python): # List all accounts for a user accounts = composio.connected_accounts.list( user_ids=['user_123']) The available filter parameters for connectedAccounts.list include user_ids, toolkit_slugs, statuses, auth_config_ids, and connected_account_ids [4]. Sessions To initialize a session, use the composio.create(user_id) method, which acts as the entry point for all integrations [6][7]. While the documentation refers to sessions.create (often associated with the underlying API or internal session management), the recommended implementation for users is invoking create on the Composio SDK instance to establish a context for a specific user [6][7]. Once a session is created, it can access toolkits and connected accounts authorized for that project [8]. Project-wide access is implicitly handled by the API key associated with the project; resources in one project are strictly isolated from others [1]. Citations:
Scope Composio connected accounts to the authenticated user
🤖 Prompt for AI Agents |
||
|
|
||
| ### What MergeDev Persists | ||
|
|
||
| | Data | Location | Retention | | ||
| |------|----------|-----------| | ||
| | Connector invocation logs | MergeDev infrastructure | Per MergeDev retention policy | | ||
| | Policy decisions (allowed/blocked) | MergeDev infrastructure | Per MergeDev retention policy | | ||
| | Audit trail (operation, user, timestamp) | MergeDev infrastructure | Per MergeDev retention policy | | ||
|
|
||
| ### Local Storage | ||
|
|
||
| - No Mercury data is written to local filesystem | ||
| - `~/.config/supercode/cli-config.json` stores only session IDs (no credentials) | ||
| - `~/.better-auth/token.json` stores Supercode's own auth token (GitHub OAuth, not Mercury) | ||
|
|
||
| ### Data Use Policy | ||
|
|
||
| - **No Mercury data is used for model training.** Supercode does not feed any Mercury-originated data into LLM training pipelines. | ||
| - **No cross-account analytics.** Usage data (LLM token counts) is tracked per-account only and never shared. | ||
| - **No third-party data sharing.** Mercury data stays within the user's session. | ||
| - MergeDev audit logs contain Mercury operation metadata (operation type, timestamp) but not sensitive financial data like account numbers or transaction amounts. | ||
|
|
||
| --- | ||
|
|
||
| ## 5. Scopes & Capabilities | ||
|
|
||
| | Capability | Read-Only | Available To | | ||
| |------------|-----------|-------------| | ||
| | Query account balances | ✓ | Paid, Startup, Founder | | ||
| | View transaction history | ✓ | Paid, Startup, Founder | | ||
| | List vendors | ✓ | Paid, Startup, Founder | | ||
| | Initiate payments | ✗ | Not available | | ||
| | Create/edit vendors | ✗ | Not available | | ||
| | Modify account settings | ✗ | Not available | | ||
|
|
||
| The integration is **strictly read-only across all tiers**. Mercury enforces this at the OAuth scope level. Even a compromised Composio session cannot execute write operations against Mercury. MergeDev's policy layer provides an additional safeguard by explicitly blocking write operations at the governance level. | ||
|
|
||
| --- | ||
|
|
||
| ## 6. Audit & Revocation | ||
|
|
||
| ### Audit | ||
|
|
||
| | Audit Surface | What's Captured | Location | | ||
| |---------------|----------------|----------| | ||
| | Composio session creation | Per-user session with connected account IDs | Composio + Supercode server | | ||
| | Connected account status | ACTIVE vs disconnected | Composio `connectedAccounts.list()` | | ||
| | Mercury API invocations | Operation type, timestamp, user, result | MergeDev audit logs | | ||
| | Policy violations | Blocked calls, reason, user | MergeDev audit logs | | ||
| | LLM usage | Provider, model, token counts, cost | Supercode `usage_event` table | | ||
| | Chat history | Conversation messages with tool arguments | Supercode `conversation` + `message` tables | | ||
|
|
||
| MergeDev provides the dedicated per-call audit trail for Mercury operations, addressing the gap of individual tool invocation logging. | ||
|
|
||
| ### Revocation | ||
|
|
||
| **From Supercode:** | ||
| 1. User opens `/mcp` TUI → views connected apps and connector status | ||
| 2. Disconnects Mercury → `composioSessionManager.resetSession()` | ||
| 3. Future Mercury tool calls fail at the Composio session layer | ||
| 4. Since access is read-only, revocation risk is limited to loss of balance visibility | ||
|
Comment on lines
+224
to
+228
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift Do not describe
Document the real revocation path and implement remote account/session teardown if local disconnect is intended to revoke access. 🤖 Prompt for AI Agents |
||
|
|
||
| **From Mercury (direct):** | ||
| 1. User visits Mercury's authorized applications page | ||
| 2. Revokes Supercode's access | ||
| 3. Composio-managed OAuth token immediately invalidates | ||
| 4. MergeDev connector becomes non-functional | ||
|
|
||
| **From MergeDev (admin):** | ||
| 1. Admin can revoke connector access at the MergeDev dashboard | ||
| 2. Blocks all Mercury operations at the governance layer | ||
| 3. Retains audit trail for compliance review | ||
|
|
||
| --- | ||
|
|
||
| ## 7. Security Summary | ||
|
|
||
| ### Strengths | ||
|
|
||
| 1. OAuth tokens never touch Supercode — stored and managed entirely by Composio | ||
| 2. No Mercury data persisted in Supercode's database or local files | ||
| 3. Dual-gate authorization — both Composio (auth/session) and MergeDev (policy/audit) must pass | ||
| 4. Read-only by design — Mercury enforces at OAuth scope level, MergeDev enforces at policy level | ||
|
Comment on lines
+247
to
+250
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n## Files mentioning createSessionFromServer / composio session\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/.git/**' 'createSessionFromServer|/api/composio/session|apiKey|Composio\(' .
printf '\n## Candidate files in repository root related to supercode-mercury\n'
git ls-files | rg 'supercode-mercury\.md|composio|mercury'Repository: yashdev9274/supercli Length of output: 15374 🏁 Script executed: #!/bin/bash
set -euo pipefail
# If the files are found, inspect the relevant ranges with line numbers.
for f in $(git ls-files | rg 'supercode-mercury\.md|composio|mercury'); do
echo "## $f"
wc -l "$f"
doneRepository: yashdev9274/supercli Length of output: 306 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the relevant document section around the cited lines.
f=$(git ls-files | rg '^supercode-mercury\.md$')
echo "## $f"
cat -n "$f" | sed -n '230,265p'Repository: yashdev9274/supercli Length of output: 1839 🌐 Web query:
💡 Result: In Composio, the session creation process (via Citations:
🌐 Web query:
💡 Result: To create a Composio session from your server, you initialize the Composio client with your API key and then create a session scoped to a specific user ID [1][2][3]. Steps to create a session: 1. Initialize the Composio client using your API key. 2. Call the create method to establish a session for a user. TypeScript Example: import { Composio } from ' Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "## apps/supercode-cli/server/src/mcp/composio.ts"
cat -n apps/supercode-cli/server/src/mcp/composio.ts | sed -n '1,180p'
echo
echo "## apps/supercode-cli/server/src/index.ts around /api/composio/session"
cat -n apps/supercode-cli/server/src/index.ts | sed -n '1070,1125p'Repository: yashdev9274/supercli Length of output: 8628 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the changelog / docs references around the session flow.
echo "## CHANGELOG.md around the session entry"
cat -n CHANGELOG.md | sed -n '1,30p'
echo
echo "## apps/web/app/(pages)/changelog/page.tsx around the session entry"
cat -n 'apps/web/app/(pages)/changelog/page.tsx' | sed -n '30,55p'Repository: yashdev9274/supercli Length of output: 4152 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "## apps/supercode-cli/server/src/mcp/composio.ts"
cat -n apps/supercode-cli/server/src/mcp/composio.ts | sed -n '1,180p'
echo
echo "## apps/supercode-cli/server/src/index.ts around /api/composio/session"
cat -n apps/supercode-cli/server/src/index.ts | sed -n '1080,1120p'Repository: yashdev9274/supercli Length of output: 8038 Do not return 🤖 Prompt for AI Agents |
||
| 5. Three-layer permission enforcement — agent rulesets → permission manager → user approval | ||
| 6. Subagent isolation — parent's DENY always overrides child's ALLOW | ||
| 7. Authenticated API surface — every server endpoint validates Better-Auth bearer tokens | ||
| 8. MergeDev audit trail — per-call logging of Mercury operations for compliance | ||
|
|
||
| ### Known Gaps (Acknowledged) | ||
|
|
||
| 1. No per-user rate limiting on MCP tool calls (MergeDev policies can address this) | ||
| 2. No tool output sanitization — raw Mercury responses returned to the AI model (read-only data only) | ||
| 3. Stale session cleanup — no cron job to prune expired Composio sessions from Supercode references | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 241
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 419
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 506
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 26901
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 16163
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 5520
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 6633
Document the real gating path supercode-mercury.md:16-21, 48-63, 118-135 —
McpManagerexposes separatemcp_composio_*andmcp_mergedev_*tools, and the CLI only prefersmcp_composio_*for execution; there is no wrapper that forces both gateways on every Mercury call. Update the flow/invariants to match that behavior, or add the missing enforcement if dual authorization is required.🤖 Prompt for AI Agents