MCP Server
Bubbles exposes a Model Context Protocol (MCP) server so AI assistants can read your teams, bubbles, transcripts, and comments — and send the notetaker to record a meeting — directly. It is a remote, streamable-HTTP MCP server protected by OAuth 2.1 (the same authorization server described in Authentication).
Connecting takes no setup and no token to paste: your assistant adds the URL, the OAuth handshake runs automatically, and Bubbles opens in your browser so you can sign in — or create an account — the same way you sign in to Bubbles itself. You land back in the conversation with your assistant bound to your real Bubbles account.
Endpoint
https://api-tom.usebubbles.com/mcpThe server advertises its OAuth requirements at
https://api-tom.usebubbles.com/.well-known/oauth-protected-resource/mcp. A compliant MCP client discovers the
authorization server automatically, registers itself (dynamic client registration), and completes a
PKCE authorization — which opens Bubbles in your browser. There you log in to an existing account or
create a new one, with Google, Microsoft, or an emailed code, exactly as on the Bubbles web app. If
you are new, you finish setting up your workspace and calendar there too. The connection is then bound
to your account, and the tools that read your data work.
Connector URLs
Add whichever matches how you want to start. They expose the identical tools and both keep your data locked until a Bubbles account is bound; they differ only in where you sign in.
| Connector | URL | What happens when you add it |
|---|---|---|
| Default | https://api-tom.usebubbles.com/mcp | Opens Bubbles in your browser to sign in or sign up — with Google, Microsoft, or an emailed code — and binds this connection to that account. Recommended for most users. |
| Seamless | https://api-tom.usebubbles.com/mcp-seamless | Connects instantly with no browser step — an anonymous session — and you sign in later, in plain language, inside the conversation (below). Use it when you would rather not leave the chat. |
Connect
In Settings → Connectors → Add custom connector, give it a name and set the server URL to your Bubbles MCP endpoint:
https://api-tom.usebubbles.com/mcpClaude opens Bubbles in your browser to sign in or create an account. Once you are done, the Bubbles tools appear in the composer’s tool menu and work on your account right away.
ChatGPT currently needs Developer mode to add a custom MCP connector. In Settings → Connectors → Advanced, turn on Developer mode, then choose Add custom connector and set the server URL to your Bubbles MCP endpoint:
https://api-tom.usebubbles.com/mcpChatGPT opens Bubbles in your browser to sign in or create an account. Once you are done, the tools that read your data start working. See the ChatGPT App page for the full walkthrough.
Add the server from your terminal:
claude mcp add --transport http bubbles "https://api-tom.usebubbles.com/mcp"On first use Claude Code opens Bubbles in your browser to sign in or create an account. Once you are done, the tools that read your data start working.
Add the server to ~/.cursor/mcp.json (or a project .cursor/mcp.json):
{ "mcpServers": { "bubbles": { "url": "https://api-tom.usebubbles.com/mcp" } }}Reload MCP servers in Cursor’s settings; it opens Bubbles in your browser to sign in or create an account, then lists the Bubbles tools ready to use.
After you sign in
Your assistant picks the thread back up by calling bubbles_whats_next, which tells it whether
anything still needs setting up and what it can do for you now. If you already have a workspace it
will not ask you to make another — it just gets to work.
Advanced: sign in inside the conversation
If you would rather not leave the chat — or your client cannot open a browser — add the seamless connector instead:
https://api-tom.usebubbles.com/mcp-seamlessIt connects as an anonymous session: the tools are listed, but the read tools return a short “the
user is not signed in yet” message with no data and join_meeting will not dispatch, so nothing
about any account is exposed until you sign in. Signing in then happens entirely in the chat, with no
password and no token to copy:
- Ask the assistant to sign you in (for example, “Sign me in to Bubbles.”). It calls
bubbles_signup_start(orbubbles_login_start— they are the same unified flow) with your email, and Bubbles emails you a sign-in code. The same flow logs in existing users and creates an account for new ones, so you never have to say which you are. - Read the code back to the assistant. It calls
bubbles_signup_verifywith only the code — the connection already knows which sign-in is in progress, so there is no session token to pass around. This finishes signing you in. - If you are new, the assistant offers to set up a workspace:
bubbles_signup_workspaceslooks for an existing workspace your email domain can join, andbubbles_signup_workspacecreates a new one (or joins/enables domain auto-join). A newly created workspace may include a free Pro trial.
Once you are signed in, the read tools return your data and join_meeting works — on the same
connection, in the same conversation.
Tools
Beyond the sign-in tools above, the connector registers seven data tools — six read-only tools
(the same read surface as the REST API, sharing one read core so results are consistent) plus one
write tool, join_meeting. Each tool’s annotations are truthful: the six read tools are marked
read-only, and join_meeting is marked as not read-only and open-world (it reaches an external
meeting platform), so a client can gate it accordingly.
| Tool | Title | Type | Does |
|---|---|---|---|
list_bubbles | List bubbles | read | List the caller’s bubbles (paginated; supports the same filters as GET /v1/bubbles). |
search_bubbles | Search bubbles | read | Search the caller’s bubbles by keyword (title, author, and meeting notes), ranked by relevance. |
get_bubble | Get bubble | read | Fetch a single bubble by id. |
get_bubble_transcript | Get bubble transcript | read | Fetch a bubble’s diarized transcript (chunked: default 50k, max 120k chars per call). |
get_bubble_comments | Get bubble comments | read | List a bubble’s comments (paginated). |
list_teams | List teams | read | List the caller’s teams, plans, roles, and channels. |
join_meeting | Join & record a meeting | write | Send the notetaker to join and record a meeting by link (requires the meetings:join scope). |
Schemas
list_bubbles— inputs:team_id?,type?(meeting_recording|screen_recording|custom_image),created_after?,created_before?,limit(1–100, default 20),cursor?. Returns{ bubbles[], has_more, next_cursor? }.search_bubbles— inputs:query(required — matched against a bubble’s title, its author’s name, and its AI-generated meeting notes/summary, so a keyword that appears only inside the notes still finds the bubble; title matches rank highest, then author, then notes), plus the same optional filters aslist_bubbles(team_id?,type?,created_after?,created_before?,limit,cursor?). Returns the same{ bubbles[], has_more, next_cursor? }shape, ranked by relevance. Use it when the user names a topic, project, person, meeting, or something discussed in a meeting; only bubbles the user can access are ever returned.get_bubble— input:bubble_id. Returns the full bubble object (title, url, author, participants, media, summary, action items, …).get_bubble_transcript— inputs:bubble_id,format(text|json, defaulttext),offset_chars(default 0),max_chars(1,000–120,000, default 50,000). Returns{ transcript, offset_chars, returned_chars, total_chars, has_more, next_offset? }.get_bubble_comments— input:bubble_id. Returns{ bubble_id, comments[] }.list_teams— no inputs. Returns your teams, plans, roles, and channels.join_meeting(write) — inputs:meeting_url(a Google Meet, Microsoft Teams, Zoom, Webex, or GoTo link — any other link is rejected and no notetaker is sent),team_id(fromlist_teams— the workspace to record into). Sends the Bubbles notetaker to join and record the meeting and returns the dispatched meeting-join resource{ id, type: "meeting_join", status: "dispatched", meeting_url, team_id, created_at }. Requires themeetings:joinscope (personal access tokens carry it implicitly; an OAuth connection carries it too).
Large transcripts are chunked
get_bubble_transcript returns at most 50,000 characters by default and never more than
120,000 in a single call. When a transcript is longer, the response sets has_more: true and
returns a next_offset; call again with offset_chars: next_offset to fetch the next chunk. Chunk
boundaries snap back to the last newline so a speaker turn is never split mid-block. total_chars
tells you the full length up front.