Announcing CIMD support for MCP Client registration
Learn more

Fellow MCP vs Fellow API for AI Agents (2026)

Srinivas Karre
Founding Engineer

TL;DR

  • Fellow MCP exposes 18 tools: semantic meeting search, summaries, transcripts, action items, and agenda editing. The Developer API has no search, but owns webhooks, recording uploads, and action item completion.
  • MCP authenticates with OAuth and Dynamic Client Registration. The Developer API accepts only per-user API keys in the X-API-KEY header; Fellow offers no OAuth path to REST.
  • Multi-tenant API agents must collect raw user keys or hold an Enterprise Super Admin key that reads the whole workspace. MCP gives a per-user grant, but you still own storage, refresh, and revocation.
  • Use MCP for interactive, cross-tool meeting-context agents. Use the API for event-driven pipelines, bulk export, and action item state changes.
  • Scalekit vaults and refreshes each user's Fellow credential, scopes tools through Virtual MCP servers, and logs every tool call.

Same meeting data, two very different surfaces

Your agent needs meeting context from Fellow: what was decided in last Tuesday's pipeline review, which action items are overdue, what the customer said about pricing twenty minutes into the call. Fellow ships a hosted Model Context Protocol (MCP) server and a REST Developer API. They reach the same meeting data through very different doors. One is OAuth-only and built for natural-language retrieval. The other is API-key-only and built for moving data. The path you pick decides how users onboard, how much a leaked credential exposes, and whether your agent can react when a meeting ends. Here is how to choose.

What Fellow MCP and Fellow API actually are

Both surfaces enforce the same access rule: the caller sees only what the authenticating Fellow user can already see in the app. Almost everything else about them differs.

The Fellow MCP server

Fellow runs an official, remote MCP server on its own domain, maintained by Fellow and live since late 2025. It is also a verified connector in Claude's directory. Clients authenticate with OAuth, and MCP clients that support dynamic discovery register themselves, so there is no client ID or secret to provision.

Two admin gates sit in front of it. A workspace admin must first enable MCP connections in Workspace Settings. The admin then controls each tool individually, for example allowing read tools while keeping agenda editing off. Admins can see and delete every active MCP connection. Fellow documents all of this in its Help Center article on the MCP server.

The Fellow Developer API

The Developer API is a REST interface served from each workspace's own subdomain under /api/v1. It exposes notes, recordings with transcripts and AI notes, action items, agenda writes in Fellow Markdown, recording uploads, and webhooks. It is available on paid plans and must be switched on by a workspace admin.

Auth is API keys only. Each user generates a key in their own settings, and the key carries that user's full access. Enterprise workspaces can also issue Super Admin keys that override privacy controls and can impersonate any user through the X-On-Behalf-Of header. Limits are 3 requests per second and 10,000 requests per day per key. Fellow documents this in its Developer API reference.

Comparing them where it matters for agents

Four dimensions change how a Fellow agent behaves in production: what it can do, how it authenticates, what you operate, and which workloads each path fits.

What your agent can actually do

The table maps the capabilities Fellow agents use most. MCP tool names are Fellow's own; API rows reference the Developer API resources.

Capability
Fellow MCP
Fellow Developer API
Semantic search over summaries and transcripts
Yes, search_meetings
No search endpoint; list notes by title, date, channel, or attendee
AI meeting summaries
Yes, get_meeting_summary
Yes, ai_notes on a recording
Transcripts
Yes, get_meeting_transcript, in time windows for long meetings
Yes, full speaker-segmented transcript on a recording
Read action items
Yes, including a manager's team items
Yes, list and retrieve
Complete or archive action items
Not exposed
Yes
Read and write agendas
Yes, four agenda tools, admin-toggled
Yes, prepend, append, find-and-replace, replace
Agenda templates
Yes, seven template tools
Not exposed
Channels
List and get details
Filter notes by channel only
Upload recordings
Not exposed
Yes, from a URL
Delete notes and recordings
Not exposed
Super Admin keys only
Event subscriptions
Not exposed
Webhooks for four event types
Workspace-wide reads
No, always the connected user
Super Admin keys only

Where MCP is stronger

The MCP server is designed for retrieval by a model. search_meetings accepts a semantic query against generated summaries, a semantic query against transcript quotes, a speaker filter, participant email domains, and a user_has_calendar_event flag that separates "my meetings" from "meetings I can see." None of that exists in the REST API. An API-path agent has to list notes by date range and filter client-side, which burns requests against a 3-per-second ceiling.

Agenda work also favors MCP. edit_agenda makes targeted changes such as adding a talking point or an action item, and the template tools let an agent standardize recurring meetings.

Where the API is stronger

The API owns state changes and data movement. Marking an action item done, archiving it, ingesting an external recording, and bulk-exporting notes for a warehouse are REST-only. So is reacting to a meeting. MCP is pull-only, while the API's ai_note.generated webhook fires shortly after Fellow generates notes from a recording.

One Scalekit-specific note: the FellowAI MCP connector page currently documents seven read tools. Call list_scoped_tools for your connection to confirm which tools it exposes before you design around agenda writes.

The auth path each one puts you on

The MCP path is OAuth with Dynamic Client Registration. The user signs in to Fellow in a browser and approves the client, and the grant is bound to that user. Admins keep two kill switches: the whole MCP capability and individual tools.

The API path is a static key per user. There is no OAuth flow, no scope model beyond regular versus Super Admin, and no documented expiry. The key inherits every permission its owner has. It stops working only when someone deletes or disables it, disables the API workspace-wide, or deactivates the user in Fellow.

Both paths require per-user credential isolation in a multi-tenant agent. MCP gives you a token per user; the API gives you a key per user. Neither path solves storage, rotation, or revocation.

The Super Admin shortcut

For an API-path agent serving many users, the tempting move is one Super Admin key plus X-On-Behalf-Of. It works: each request is scoped to the impersonated user, and Fellow audit-logs every impersonation. But the key itself reads the entire workspace, overrides privacy controls, requires an Enterprise plan, and is one secret whose leak exposes every transcript in the org. Fellow positions impersonation for admin dashboards and debugging, not delegated agent access. It also works only within one workspace, so a vendor serving many customers would need a Super Admin key from each of them.

What you own on the MCP path

On the MCP path, Fellow owns the server, the tool schemas, and the search index. You own the per-user token lifecycle and one Fellow-specific dependency: admin tool toggles. A tool an admin switches off becomes unavailable across that workspace. New tools are allowed by default unless the admin changes that setting, so your agent's tool surface can shift without a deploy on your side.

What you own on the API path

On the API path, you own everything else too: cursor pagination capped at 50 results per page, 429 handling, the daily request ceiling, response shaping for the LLM, and webhook infrastructure. That means a public HTTPS endpoint, challenge verification, HMAC-SHA256 signature checks over the svix-id, svix-timestamp, and raw body, and idempotent handling across up to eight delivery attempts spread over roughly 27 hours.

Recommended Reading: Granola MCP vs Granola API for AI Agents, the same decision for another meeting-notes platform.

When to use MCP, when to use the API

The split for Fellow follows the auth split more closely than for most tools. Pick by workload, not by preference.

Use Fellow MCP when

  • Your agent answers open-ended questions over meeting history, such as "what objections did Acme raise across our last three calls," where semantic search over summaries and transcripts does the work.
  • You are building a meeting-prep or follow-up assistant in Claude, Cursor, or your own chat UI, and the user is present to complete OAuth.
  • The agent combines Fellow context with other tools, for example checking standup blockers against open Linear tickets.
  • You need natural-language agenda drafting or templating, and your customers' admins have enabled those tools.

Use the Fellow Developer API when

  • The agent is event-driven: summarize and route every new AI note the moment ai_note.generated fires.
  • You run a deterministic pipeline, such as a nightly export of notes and transcripts into a warehouse or CRM.
  • The agent must close the loop on action items, marking them done or archiving them, or ingest recordings from another system.
  • You are building an internal, single-workspace tool where the Super Admin model is acceptable to your security team.

The credential problem that exists on both paths

A Fellow agent inside a B2B product serves hundreds of users across dozens of customer workspaces. Each one has their own Fellow credential, and meeting transcripts are among the most sensitive data a company produces.

The API key paste problem

Because the REST API has no OAuth, onboarding a user on the API path means asking them to open Fellow settings, generate a key, and paste it into your product. That key has no documented expiry, carries the user's full access, and now lives in your infrastructure. At 500 users you hold 500 long-lived secrets to encrypt, isolate per tenant, and delete on offboarding. Fellow kills a key when the user is deactivated in Fellow, not when they leave your app.

The shared connection problem

MCP improves the grant, not the custody. Fellow's own setup guide warns that sharing a Microsoft Copilot agent built on one person's Fellow connection gives everyone else that person's recaps, notes, and calendar events. The same failure happens in your backend if you cache one OAuth token and reuse it across users. Every user needs their own grant, refreshed independently and revocable without touching anyone else. This mirrors the credential ownership challenge that appears across all agent tool-calling patterns.

Where Scalekit fits

Scalekit's FellowAI MCP connector runs the OAuth 2.1 flow per user, stores tokens in Scalekit's vault, and refreshes them. Your agent references a user identifier and a connection name, never a raw token. For the Developer API, a custom API-key connector puts each user's key behind the same vault. The MCP vs API choice stops being a credential infrastructure choice.

Building a Fellow agent with Scalekit

The examples below use Python and LangChain. They cover three paths: calling Fellow's MCP tools directly, exposing Fellow through a Virtual MCP server alongside Gmail, and reaching the Developer API through a custom connector. All three share the same connected-account model.

Set up the FellowAI MCP connection

In the Scalekit dashboard, open AgentKit, then Connections, then Create Connection, and select FellowAI MCP. There are no client credentials to enter because the connector uses Dynamic Client Registration. Copy the connection name: the connection_name in your code must match it exactly, or tool calls fail with a not-found error. On the Fellow side, a workspace admin must enable MCP connections before any user can authorize.

pip install scalekit-sdk-python python-dotenv langchain-openai "langchain-mcp-adapters>=0.3,<1" # .env SCALEKIT_ENVIRONMENT_URL=<your-environment-url> SCALEKIT_CLIENT_ID=<your-client-id> SCALEKIT_CLIENT_SECRET=<your-client-secret> FELLOW_CONNECTION_NAME=fellowaimcp # must match AgentKit > Connections exactly OPENAI_API_KEY=<your-openai-api-key>

Authorize the user once

Each user authorizes Fellow one time. Scalekit creates a connected account keyed to your user identifier and owns its token lifecycle from then on. In production, surface the link in your UI instead of the terminal.

import os from dotenv import load_dotenv import scalekit.client load_dotenv() scalekit_client = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), ) actions = scalekit_client.actions # Must match the connection name in AgentKit > Connections exactly FELLOW_CONNECTION = os.getenv("FELLOW_CONNECTION_NAME", "fellowaimcp") USER_ID = "user_123" # your app's unique identifier for this user def ensure_fellow_connected(identifier: str) -> None: response = actions.get_or_create_connected_account( connection_name=FELLOW_CONNECTION, identifier=identifier ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=FELLOW_CONNECTION, identifier=identifier ) print("Authorize Fellow:", link.link) input("Press Enter after authorizing...") response = actions.get_or_create_connected_account( connection_name=FELLOW_CONNECTION, identifier=identifier ) if response.connected_account.status != "ACTIVE": raise RuntimeError( f"Fellow is {response.connected_account.status}, not ACTIVE. Authorize and retry." ) ensure_fellow_connected(USER_ID)

Retrieve the tools this user can call

Before the model sees anything, fetch the tools this user's connected account is authorized to call. This is not a catalog dump. The list is bound to this user's Fellow connection, and it is the only list you should hand to the LLM.

from google.protobuf.json_format import MessageToDict scoped_response, _ = actions.tools.list_scoped_tools( identifier=USER_ID, filter={"connection_names": [FELLOW_CONNECTION]}, page_size=100, ) for scoped_tool in scoped_response.tools: definition = MessageToDict(scoped_tool.tool).get("definition", {}) print(definition.get("name")) # e.g. fellowaimcp_search_meetings

Run the agent loop

actions.langchain.get_tools returns that same user-scoped surface as native LangChain StructuredTool objects. Scalekit injects the user's Fellow token server-side at execution time, so the token never enters the prompt, the agent process, or your logs. For more on LangChain tool calling patterns and where they stop, see the dedicated guide.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage tools = actions.langchain.get_tools( identifier=USER_ID, connection_names=[FELLOW_CONNECTION], page_size=100, ) tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ SystemMessage( "You answer questions about the user's Fellow meetings. When the user asks " "about their own meetings, set user_has_calendar_event to true when searching." ), HumanMessage( "What did we decide in my product planning meetings last week, " "and which of my action items are overdue?" ), ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

For the full adapter reference, see the LangChain example in the AgentKit docs.

Scope Fellow into a Virtual MCP server

A post-call follow-up agent needs three Fellow tools and one Gmail tool, not every tool on both servers. A Virtual MCP server declares exactly that surface once per agent role and returns a static URL shared by all users. Both connections must already exist in AgentKit.

import os from dotenv import load_dotenv from scalekit import ScalekitClient from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping load_dotenv() scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) vmcp_response = scalekit_client.actions.mcp.create_config( name="fellow-followup-agent", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="fellowaimcp", # must match your dashboard connection name tools=[ "fellowaimcp_search_meetings", "fellowaimcp_get_meeting_summary", "fellowaimcp_get_action_items", ], ), McpConfigConnectionToolMapping( connection_name="gmail", # must match your dashboard connection name tools=["gmail_fetch_mails"], ), ], ) # Persist both values; every user and every session reuses them config_id = vmcp_response.config.id mcp_server_url = vmcp_response.config.mcp_server_url print(config_id, mcp_server_url)

Mint a per-user session and connect

Before each run, confirm the user's connections are active and mint a short-lived session token bound to that user. The default expiry is about an hour, and create_session_token is also the remint call. Set expiry above your expected run time.

import asyncio import os from datetime import timedelta from dotenv import load_dotenv from scalekit import ScalekitClient from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage load_dotenv() scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) config_id = os.environ["SCALEKIT_MCP_CONFIG_ID"] mcp_server_url = os.environ["SCALEKIT_MCP_SERVER_URL"] identifier = "user_123" # 1. Every connection on the server must be ACTIVE for this user accounts = scalekit_client.actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=identifier, include_auth_link=True, ) inactive = [a for a in accounts.connected_accounts if a.connected_account_status != "ACTIVE"] for account in inactive: print(f"{account.connection_name} needs auth: {account.authentication_link}") if inactive: raise SystemExit("Authorize the connections above, then rerun.") # 2. Mint a fresh, user-bound session token for this run token = scalekit_client.actions.mcp.create_session_token( mcp_config_id=config_id, identifier=identifier, expiry=timedelta(hours=1), ).token async def run() -> None: client = MultiServerMCPClient( { "scalekit": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {token}"}, } } ) tools = await client.get_tools() # only the four tools declared above tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ HumanMessage( "Summarize my last call with Acme, list the open action items, " "and find my most recent email thread with their team." ) ] while True: response = await llm.ainvoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = await tool_map[tc["name"]].ainvoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"])) asyncio.run(run())

The step-by-step reference lives in the Set up and connect a Virtual MCP server docs. Understanding when a Virtual MCP server is the right call helps you decide whether to scope tools this way from the start.

Register the Developer API as a custom connector

Scalekit has no prebuilt connector for Fellow's REST API, so you register a custom connector. The payload declares an API_KEY auth pattern, overrides the header name to Fellow's X-API-KEY, and templates the proxy URL on each user's workspace domain.

# Management token (client credentials); requires jq ACCESS_TOKEN=$(curl -s --location "$SCALEKIT_ENVIRONMENT_URL/oauth/token" \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode "client_id=$SCALEKIT_CLIENT_ID" \ --data-urlencode "client_secret=$SCALEKIT_CLIENT_SECRET" | jq -r '.access_token') curl --fail-with-body --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers" \ --header "Authorization: Bearer $ACCESS_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "display_name": "Fellow Developer API", "description": "Fellow notes, recordings, action items, and agendas", "auth_patterns": [ { "type": "API_KEY", "display_name": "Fellow API Key", "description": "Per-user key from Fellow user settings", "auth_header_key_override": "X-API-KEY", "fields": [ {"field_name": "domain", "label": "Fellow workspace domain", "input_type": "text", "hint": "yourworkspace.fellow.app", "required": true}, {"field_name": "api_key", "label": "API Key", "input_type": "password", "hint": "Fellow Developer API key", "required": true} ] } ], "proxy_url": "https://{{domain}}/api/v1", "proxy_enabled": true }'

Call the Developer API through the vault

Create a connection for the new connector in the dashboard, then store each user's key as a connected account. Scalekit vaults it and adds the header on every proxied call. Note what this does not fix: the user still has to generate and hand over the key. Inspect the response wrapper once before you parse it.

import os # Reuses `actions` from the setup block above FELLOW_API_CONNECTION = "fellow-developer-api" # must match AgentKit > Connections exactly user_supplied_fellow_key = os.environ["FELLOW_API_KEY"] # in production, from your settings UI # Run once, when the user pastes their key into your settings page actions.upsert_connected_account( connection_name=FELLOW_API_CONNECTION, identifier="user_123", credentials={"domain": "acme.fellow.app", "api_key": user_supplied_fellow_key}, ) # Deterministic pipeline step: pull last week's notes with content and attendees notes = actions.request( connection_name=FELLOW_API_CONNECTION, identifier="user_123", path="/notes", # relative to proxy_url, so this hits /api/v1/notes method="POST", json={ "filters": {"created_at_start": "2026-09-20T00:00:00Z"}, "include": {"content_markdown": True, "event_attendees": True}, "pagination": {"page_size": 50}, }, ) print(notes)

Why the Scalekit path matters for Fellow agents

Meeting data raises the bar on every production concern. These are the pieces that change what you ship.

Every tool call is attributable

AgentKit logs every downstream tool call with the user who authorized it, the agent that ran it, the tool it used, and what came back, and streams those logs to Datadog, Splunk, or any SIEM. For a Fellow agent, that answers the first question a security reviewer asks: which agent read which transcript, on whose behalf. This is the foundation of agent tool observability — knowing not just that your agent is running, but what it is actually doing. Errors are classified by source, so you can tell whether a call never reached the Fellow connector or Fellow itself failed.

One endpoint for many tools and many tenants

A Virtual MCP server is one definition per agent role, not one per user. Each run gets a short-lived session token bound to one user's connected accounts, so Fellow, Gmail, Linear, and your other connectors sit behind a single URL with no credential sharing between users. There is no MCP server to deploy, host, or maintain.

Smaller tool surfaces, better tool selection

Fellow's search_meetings alone carries 16 parameters with paragraph-length descriptions. Exposing all 18 Fellow tools to a follow-up agent that needs three spends context on every turn and gives the model more wrong choices, including agenda writes it should never make. Surface reduction is the lever. Model upgrades help. They are not the lever.

Start from a template

The meeting prep agent template and the sales call prep agent template follow the same per-user connected-account pattern a Fellow agent needs, so the auth wiring carries over. When evaluating the build-vs-buy question for this auth layer, the hidden cost of building OAuth internally for AI agents is worth understanding before you commit to rolling it yourself.

Which one to build against

If your agent reasons over meeting history with a user present, build on Fellow MCP. It has the search surface the API lacks and the only OAuth grant Fellow offers. If your agent reacts to meetings ending, moves data in bulk, or changes action item state, use the Developer API and accept that you are now custodian of raw user keys. Many production Fellow agents will use both: a webhook to trigger, MCP to reason. The credential problem is identical either way, and that is the part that needs production-grade infrastructure. Understanding how tool calling auth changes when you move from single-tenant to multi-tenant is essential before you finalize your approach.

Get help building your Fellow agent

Building a Fellow agent for multiple users or customer workspaces? Talk to us and a Scalekit engineer will help you design the auth and tool-scoping model.

Browse the Scalekit FellowAI MCP connector, read the FellowAI MCP connector docs, or explore all agent connectors.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.