Created
March 2, 2026 22:52
-
-
Save odysseus0/96a5d63bb675129943b0b59cb81e2da6 to your computer and use it in GitHub Desktop.
Repro: GMAIL_BATCH_MODIFY_MESSAGES fabricates modified_count (Composio bug)
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
| /** | |
| * Repro: GMAIL_BATCH_MODIFY_MESSAGES fabricates modified_count | |
| * | |
| * Gmail's batchModify returns 204 No Content — no count. Composio synthesizes | |
| * modified_count by echoing back len(messageIds), even for nonexistent IDs. | |
| * | |
| * Requires: COMPOSIO_API_KEY in env, Gmail OAuth (opens browser) | |
| * Run: bun run scripts/repro-batch-modify-bug.ts | |
| */ | |
| import { Composio } from '@composio/core' | |
| const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY! }) | |
| const userId = `repro-${Date.now()}` | |
| async function mcpCall(mcp: { url: string; headers: Record<string, string> }, tool: string, args: Record<string, unknown>) { | |
| const res = await fetch(mcp.url, { | |
| method: 'POST', | |
| headers: { ...mcp.headers, 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' }, | |
| body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', params: { name: tool, arguments: args }, id: 1 }), | |
| }) | |
| const text = await res.text() | |
| for (const line of text.split('\n')) { | |
| if (!line.startsWith('data: ')) continue | |
| const parsed = JSON.parse(line.slice(6)) | |
| if (parsed?.result) return JSON.parse(parsed.result.content[0].text) | |
| } | |
| } | |
| // 1. Connect Gmail | |
| const session = await composio.create(userId) | |
| const auth = await session.authorize('gmail') | |
| console.log(`Complete OAuth: ${auth.redirectUrl}`) | |
| await Bun.spawn(['open', auth.redirectUrl]).exited | |
| const conn = await auth.waitForConnection(120_000) | |
| console.log(`Connected: ${conn.id}\n`) | |
| const { mcp } = await composio.create(userId, { toolkits: ['gmail'] }) | |
| // 2. Get 2 real message IDs | |
| const fetchResult = await mcpCall(mcp, 'COMPOSIO_MULTI_EXECUTE_TOOL', { | |
| tools: [{ tool_slug: 'GMAIL_FETCH_EMAILS', arguments: { query: 'in:inbox newer_than:7d', max_results: 2 } }], | |
| }) | |
| const messages = fetchResult.data.results[0].response.data.messages | |
| const realIds = messages.map((m: { messageId: string }) => m.messageId) | |
| console.log(`Real IDs: ${realIds.join(', ')}`) | |
| // 3. Batch modify with 2 real + 2 fake IDs | |
| const fakeIds = ['1000000000000001', '1000000000000002'] | |
| const allIds = [...realIds, ...fakeIds] | |
| console.log(`Sending batch modify with ${allIds.length} IDs (${realIds.length} real, ${fakeIds.length} fake)\n`) | |
| const batchResult = await mcpCall(mcp, 'COMPOSIO_MULTI_EXECUTE_TOOL', { | |
| tools: [{ tool_slug: 'GMAIL_BATCH_MODIFY_MESSAGES', arguments: { messageIds: allIds, addLabelIds: ['STARRED'] } }], | |
| }) | |
| console.log('Result:', JSON.stringify(batchResult.data.results[0].response.data, null, 2)) | |
| console.log(`\nExpected modified_count: ${realIds.length} (only real messages exist)`) | |
| console.log(`Actual modified_count: ${batchResult.data.results[0].response.data.modified_count}`) | |
| // Cleanup | |
| await mcpCall(mcp, 'COMPOSIO_MULTI_EXECUTE_TOOL', { | |
| tools: [{ tool_slug: 'GMAIL_BATCH_MODIFY_MESSAGES', arguments: { messageIds: realIds, removeLabelIds: ['STARRED'] } }], | |
| }) | |
| await composio.connectedAccounts.delete(conn.id) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment