Announcing CIMD support for MCP Client registration
Learn more

MCP vs API for Integrating Webex to AI Agents

Cisco hosts official Webex MCP servers for Meetings and Messaging, plus a Suite server still in beta, and none of them works until each customer's admin enables it in Control Hub. The Webex REST API covers what the MCP servers leave out: events, invitees, participants, Adaptive Cards and the directory. It also offers four kinds of token with very different reach. This guide compares both for a meeting and messaging agent and shows how to run per-user grants across many customer orgs.

TL;DR

  • Cisco hosts official Webex MCP servers at https://mcp.webexapis.com/mcp/: Webex Meetings (8 tools) and Webex Messaging (24 tools), both publicly available since May 2026, plus a Webex Suite server (25 tools across Meetings, Messaging, Calling, and Vidcast) that Cisco still labels Beta. None of them works until an org admin enables it in Control Hub, and none of them receives events: the Messaging server can create a webhook, but your agent cannot hear it fire.
  • The MCP servers take a Webex OAuth 2.0 bearer token that must carry the spark:mcp scope plus the resource scopes each tool needs. The REST API at https://webexapis.com/v1 accepts four kinds of token: personal access tokens that expire 12 hours after sign-in, bot tokens that cannot act for a user, integration tokens from the Authorization Code flow, and service app tokens for a machine account a Full Admin authorizes.
  • Webex does not publish numeric rate limits. Its guidance is that limits vary by resource and change as needed, and a 429 carries a Retry-After header in seconds. Webhook payloads carry IDs, not message text, and a webhook that fails 100 deliveries within five minutes is disabled and stays disabled.
  • MCP is the faster path for a person asking Claude, Cursor, or Copilot Studio to schedule a meeting, find a message, or pull last week's recordings. The API is the path for meeting-recap pipelines driven by meetingTranscripts webhooks, Adaptive Card workflows, bots, and directory lookups.
  • Scalekit's webex connector exposes 49 tools over the Webex REST API (31 read, 18 write, 0 destructive) with per-user OAuth 2.0 through your own Webex integration. Scalekit stores and refreshes each user's tokens, so the MCP vs API decision doesn't change your auth infrastructure.

The decision your Webex agent forces first

Your agent lives where your customers already talk. It needs to turn a finished Webex meeting into a recap in the team's space, schedule a follow-up with the right invitees, find the message where someone agreed to a deadline, or react when a person submits an Adaptive Card. Cisco now hosts official Model Context Protocol (MCP) servers for Webex Meetings and Webex Messaging, and a Suite server that adds Calling and Vidcast. Webex also has the REST API that every bot and integration has used for years. They overlap on messages, spaces, meetings, recordings, and transcripts. They diverge on who has to switch them on, what counts as a credential, and whether your agent can react to anything at all. Here's how to pick.

What Webex MCP and Webex API actually are

Webex MCP

Webex's MCP servers are remote endpoints Cisco hosts under https://mcp.webexapis.com/mcp/. Nothing runs locally. Webex's What's New page records the rollout: the Meetings and Messaging servers and a Vidcast server became publicly available in May 2026, Cisco Workspaces in June, the Meetings server was published on Claude in July, Webex Connect in August, the Suite server became available in Codex in September, and the Contact Center server became publicly available in October.

Three servers matter for a collaboration agent:

  • Webex Meetings MCP at https://mcp.webexapis.com/mcp/webex-meeting (docs), with 8 tools: webex-list-meetings, webex-create-meeting, webex-update-meeting, webex-delete-meeting, webex-get-meeting-status, webex-get-meeting-summary, webex-list-recordings, and webex-list-transcripts.
  • Webex Messaging MCP at https://mcp.webexapis.com/mcp/webex-messaging (docs), with 24 tools across messages, spaces, memberships, webhooks, files, and threads, such as webex-create-message, webex-search-messages, webex-create-space, webex-add-membership, webex-create-webhook, webex-upload-file, and webex-create-thread-reply.
  • Webex Suite MCP at https://mcp.webexapis.com/mcp/webex-suite (docs), labeled Beta, with 25 tools. It folds create, update, and delete into tools such as webex-manage-meeting and webex-manage-message, and adds Calling (webex-call-control, webex-get-call-insights, webex-search-calls) and Vidcast (vidcast-get-video-transcript and four more).

Auth on all three is an OAuth 2.0 bearer token from issuer https://webexapis.com. The client obtains a Webex OAuth token and sends it as Authorization: Bearer, and the server calls the Webex REST API as that user. The token must include spark:mcp, which Webex lists as required for the MCP connection, plus resource scopes such as meeting:schedules_write or spark:messages_read. Webex lists Amazon Quick, Claude Code, Claude Desktop, Codex, Copilot Studio, Cursor, Gemini CLI, and VS Code as supported clients. Every server page carries the same prerequisite: the server "must be enabled by your organization's admin in Webex Control Hub before it can be used."

Before the hosted servers, the WebexSamples GitHub organization published a local server, webex-messaging-mcp-server (MIT, 52 tools). It runs over stdio or Streamable HTTP and reads a bearer token from WEBEX_PUBLIC_WORKSPACE_API_KEY. If that is a personal access token, it lasts 12 hours, which makes it a prototyping tool rather than a production path.

Webex API

The Webex REST API at https://webexapis.com/v1 covers messaging (messages, rooms, memberships, teams, room tabs, attachment actions for Adaptive Cards), people and organizations, meetings (meetings, invitees, participants, templates, summaries, recordings, transcripts), webhooks, and Webex Calling and admin APIs. Every request carries a bearer token. Which token is the real decision:

  • Personal access token: copied from the developer portal and, per Webex, it expires "12 hours after you sign in to the Developer Portal." Webex recommends against using it in production.
  • Bot token: a bot is its own Webex identity with a token shown once. Bots "do not, however, perform actions within Webex on behalf of a Webex user," and in group spaces a bot only sees messages where it is @mentioned.
  • Integration token: an integration runs the OAuth 2.0 Authorization Code flow at https://webexapis.com/v1/authorize and exchanges the code at https://webexapis.com/v1/access_token. Webex's authentication guide, as published on its government developer portal, shows expires_in of 1209600 seconds (14 days) and refresh_token_expires_in of 7776000 seconds (90 days), and says each refresh renews the refresh token's lifetime. The commercial authentication guide tells you to use the values the service returns rather than assume them. Each Webex user can own at most 20 integrations, and the client secret is shown once.
  • Service app token: a service app acts through a machine account in the customer's org. Only a Full Admin can authorize it in Control Hub, and tokens come from POST /v1/applications/{applicationId}/token called with a separate integration token that holds spark:applications_token.

Comparing them where it matters for agents

What your agent can actually do

Capability
Webex MCP (Meetings + Messaging)
Webex API (webexapis.com/v1)
Send, edit, and reply in threads
Yes
Yes
Search messages across spaces
Yes
Limited: List messages per space
Delete messages and spaces
Yes
Yes: REST delete endpoints; Scalekit ships no destructive tools
Spaces, memberships, teams
Yes: Spaces and memberships; no team tools
Yes
Schedule, update, cancel meetings
Yes
Yes
Invitees and participants
Limited: No dedicated invitee or participant tool among the 8
Yes
AI meeting summaries
Yes: Ended meetings with Webex AI Assistant
Yes
Recordings and transcripts
Limited
Yes
People directory
No: No people tool on either server
Yes
Adaptive Card submissions
No
Yes
React to new messages, meetings, transcripts
No: Can create webhooks, cannot receive them
Yes: Webhooks for messages, meetings, transcripts, recordings, card actions and more
Calling and Vidcast
Limited: Suite server only, Beta
Yes: Webex Calling APIs; Vidcast not in Scalekit's connector

Cisco split the MCP surface the way a person thinks about Webex: "my meetings" and "my messages." The Meetings server is deliberately small. It lists, creates, changes, and deletes meetings and reads summaries, recordings, and transcripts, but it has no tool to add one invitee or see who actually joined. The Messaging server is broad and includes destructive tools, so the same session that answers "what did Priya say about the launch" can delete the space it found the answer in.

The API's distinctive advantage is everything that happens without a person asking. Adaptive Card submissions arrive as attachmentActions. A finished transcript arrives as a meetingTranscripts webhook. Directory lookups go through /people. None of those has an MCP equivalent today.

What the MCP path makes easier

"Find the thread where the design review got moved and reschedule it for Thursday with the same people" on the raw API means listing the user's spaces, listing messages in each likely space because GET /messages works one space at a time, reading the thread, finding the meeting series, listing its invitees, and sending an update with the right time zone. Each response lands in the model's context.

On MCP, the model calls webex-search-messages, webex-get-thread, webex-list-meetings, and webex-update-meeting, and Webex owns the schemas. Message search alone saves the agent from guessing which of a user's spaces to page through. Writing the schema is the hard part. Not the API call.

The tradeoff is that Cisco and the customer's admin own the surface. Control Hub lets admins enable or disable individual tools, flags schema changes as signature drift, and caches each server's tool list for up to one hour. The Suite server is Beta and bundles operations into manage tools, so the same action has two names depending on which server a user connected. An agent tuned against one server's tool names can break when an admin moves the org to the other.

The auth path each one puts you on

On the MCP path, the user's OAuth token is the credential, and the org's admin is the gate. In Control Hub, under Apps > Agentic Apps, an admin sets each server to "Blocked for all users," "Allowed for all users," or "Limited access" for selected groups, and can switch off individual tools (provisioning guide). That is a real control most vendors' MCP servers lack. It is also a dependency: until a customer's admin acts, your agent has nothing to connect to. The token itself needs spark:mcp on top of the resource scopes, so an existing integration grant without it will not work against the MCP servers. What the user can't do, the agent can't do: the server calls the REST API as the signed-in user.

On the direct API path, you pick the identity. A personal access token is a 12-hour shortcut. A bot token is the bot, not a user: it cannot read meeting data on a person's behalf and only sees @mentions in group spaces, though Webex warns that this pattern "does not protect other messages in the space from being accessed by a bot in all flows." An integration token is the user, constrained by the scopes you requested and the user's own permissions. A service app token is a machine account with org-wide reach, authorized once by a Full Admin; deauthorizing it deletes the machine account and invalidates its tokens.

Meeting scopes have a sharp edge. Several meeting endpoints accept hostEmail, which lets a site admin with admin-level meeting scopes act on any host's meetings. Scalekit's connector exposes that as host_email on meeting tools, and it only works for site admins. Connect one admin's account for a whole customer, and every user's request runs with that admin's reach.

Both paths still need per-user isolation

In a multi-tenant B2B agent, MCP's OAuth flow gives you a token per user and the direct API gives you a token per user. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose.

Webex makes the boundary concrete because messaging permissions follow space membership. A user's token reads the spaces that user belongs to. A compliance officer's token reads far more. Wire one broad credential into an assistant used by a whole customer and any user can ask about a space they are not in, and Webex will answer for the credential's owner.

Recommended reading: OAuth vs API keys for AI agents

What you own in production

On the MCP path, Cisco hosts the servers, owns the tool schemas, and enforces each user's permissions and the admin's tool switches. You own each user's OAuth session if your product is the MCP client, including a re-consent when you need a scope the grant lacks. You own the admin conversation at every customer. You own the gaps: no events, no invitee or participant tools, no directory. Webex's MCP docs publish no rate limits, so budget as if the REST API's limits apply.

On the API path, you own schemas, pagination, retries, and the token lifecycle. Webex's rate limiting guidance gives no numbers: limits "will vary depending on the calls being made" and are "subject to change as needed." A 429 arrives with Retry-After in seconds, and Webex advises pausing after the first one because you are "probably pretty close to failing on every server." Build backoff on Retry-After, not a fixed budget.

Webhooks are how a headless Webex agent learns anything happened. Payloads carry identifiers, not content: a message notification names the message, and your app fetches it with an authorized request. When you set a secret, each delivery carries X-Spark-Signature, a hex HMAC-SHA1 of the raw body, per Hookdeck's Webex webhook guide. The same guide notes two production traps: a webhook is disabled after 100 failed deliveries within five minutes and is not re-enabled automatically, and the meetings resource can lag its webhook by up to 10 minutes, so a first read may return 404. Org-wide webhooks use ownedBy: org and need admin or compliance scopes.

When to use MCP, when to use the API

Use Webex MCP when:

  • A manager in Claude Desktop or Copilot Studio asks "what did we decide in yesterday's pricing review" and webex-list-meetings plus webex-get-meeting-summary answer it with their own meeting access.
  • A user in Cursor or VS Code wants to search their spaces with webex-search-messages and post a status update to a thread, without anyone building an integration.
  • An IT team wants agent access to Webex that its admin can block, limit to groups, or trim tool by tool in Control Hub.
  • You are evaluating a Webex assistant for your own org, where you control the admin settings and each person signs in with their own account.

Use the Webex API when:

  • A meetingTranscripts webhook fires and a pipeline downloads the transcript, drafts a recap, and posts it to the team's space before anyone asks.
  • Your agent runs on Adaptive Cards: it posts a card with webex_message_create, and a person's submission arrives as an attachmentActions event that webex_attachment_action_get reads.
  • The agent needs invitees, participants, transcript snippets with speaker timing, or directory lookups, none of which the MCP servers expose.
  • You serve many customer orgs and cannot wait for each admin to enable MCP servers, or you need a bot identity, a service app, or an org-wide webhook.

The credential problem that exists on both paths

Both paths hand you a Webex credential per user. Neither hands you a vault. A multi-tenant meeting assistant serving 60 customer orgs with 25 users each holds 1,500 Webex grants. On the integration path, each is an access token Webex's guide shows lasting 14 days and a refresh token whose 90-day lifetime renews on every refresh, so an unused grant dies quietly after three months and a used one never ages out. On the MCP path, the same 1,500 grants also need spark:mcp, and each customer's admin can switch the server off underneath them. Every one must be stored encrypted, kept out of logs and LLM context, mapped to exactly one user at one org, and revoked when that user leaves.

The tempting shortcut is a service app or one site admin's grant per customer. Fewer secrets, larger blast radius: a single token reads every host's meetings and acts as an admin for every user who asks your agent anything. Shared credentials are a single-user solution. They do not survive a second user.

Scalekit's webex connector runs the OAuth flow per user, stores each user's tokens in the token vault, and refreshes them, so the MCP vs API decision doesn't change your auth infrastructure. Your agent passes an identifier; credentials never touch the agent runtime or the LLM context.

Recommended reading: how to handle token refresh for AI agents

How to connect a Webex agent with Scalekit

Scalekit's webex connector is an API-path connector built by Scalekit with 49 tools: 31 read-only, 18 write, and none destructive. It covers messages and direct messages, spaces, space tabs, memberships, teams and team memberships, people, meetings, meeting sites, templates, invitees, participants, summaries, recordings, transcripts, and Adaptive Card attachment actions. It authenticates with OAuth 2.0 through your own Webex integration, and it does not include webhook management. The core set for a meeting-recap assistant:

Tool
What it does
webex_me_get
Returns the connected user's Webex profile; call it first
webex_meetings_list
Lists the user's meetings
webex_meeting_get
Returns one meeting series, scheduled meeting, or instance by meeting_id
webex_meeting_summary_get
Returns the AI-generated summary of an ended meeting
webex_meeting_transcripts_list
Lists transcripts of the user's ended meetings by time range or meeting
webex_meeting_transcript_download
Downloads a full transcript as WebVTT or plain text
webex_rooms_list
Lists the spaces the user belongs to
webex_message_create
Sends text, Markdown, a file, or a card to a space or a person

Prerequisites and the connection name

In the Scalekit dashboard, go to AgentKit > Connections, create a Webex connection, and copy its redirect URI. In the Webex developer portal, create an integration, add that redirect URI, select the scopes your tools need, and enter the integration's Client ID and Client Secret on the Scalekit connection. The name you give the connection is the connection_name your code passes, and the string must match the dashboard exactly. This is the most common integration error. Copy SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET from Developers > API Credentials into .env. See the connector docs.

pip install scalekit-sdk-python python-dotenv langchain-openai requests

Authorize each user once

get_or_create_connected_account returns the user's connected account. If it is not ACTIVE, get_authorization_link returns the Webex sign-in link the user opens once to approve your integration. Then webex_me_get confirms which Webex identity this grant belongs to, so a user who signed in with a personal account instead of their work account is caught before the agent reads anything.

import os from dotenv import load_dotenv from scalekit import ScalekitClient 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"], ) actions = scalekit_client.actions CONNECTION_NAME = "webex" # must match the connection name in AgentKit > Connections identifier = "user_123" # your app's stable ID for this Webex user account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=identifier, ) if account.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=identifier, ) print("Authorize Webex:", link.link) input("Press Enter after signing in to Webex...") account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=identifier, ) if account.connected_account.status != "ACTIVE": raise RuntimeError(f"Webex is {account.connected_account.status}, not ACTIVE") me = actions.execute_tool( tool_name="webex_me_get", tool_input={}, connection_name=CONNECTION_NAME, identifier=identifier, ) print(me.execution_id, me.data)

Retrieve the Webex tools this user can call

This is not a catalog load. The agent asks for the tools this user's connected account is authorized to call, then narrows them to the eight a read-only meeting assistant needs. webex_message_create, webex_meeting_update, and webex_membership_create never reach the model, so it cannot post to a customer-facing space or move a board meeting while it answers a question about last week.

tools = actions.langchain.get_tools( identifier=identifier, connection_names=[CONNECTION_NAME], tool_names=[ "webex_me_get", "webex_meetings_list", "webex_meeting_get", "webex_meeting_summary_get", "webex_meeting_transcripts_list", "webex_meeting_transcript_download", "webex_rooms_list", "webex_messages_list", ], page_size=100, ) tool_map = {t.name: t for t in tools} print(sorted(tool_map))

Run the agent loop

The loop runs until the model stops requesting tools. Every call executes with this user's Webex grant, so the model sees only the meetings the user hosted or can access and the spaces they belong to.

from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ SystemMessage( "You help a Webex user catch up on meetings. Call webex_me_get first to learn " "the user's name and time zone. Use webex_meetings_list and webex_meeting_get " "to find meetings, webex_meeting_summary_get for ended meetings that have an AI " "summary, and webex_meeting_transcripts_list with " "webex_meeting_transcript_download in txt format when no summary exists. Use " "webex_rooms_list and webex_messages_list to find follow-up discussion in " "spaces. Quote decisions with the speaker's name. Never claim you sent or " "changed anything." ), HumanMessage( "What did we decide in last week's meetings about the Q1 launch date, and has " "anyone raised a concern about it in our spaces since?" ), ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: tool = tool_map.get(tc["name"]) if tool: result = tool.invoke(tc["args"]) else: result = f"Unknown tool: {tc['name']}" messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Deterministic calls with execute_tool

For pipelines, skip the open-ended loop. execute_tool runs one tool with a fixed input and returns data plus an execution_id you can log. This handler runs when a Webex webhook reports meetingTranscripts created. It verifies X-Spark-Signature, reads the meeting and the transcript with the host's own grant, asks a model for a recap, and posts it to the team's space with webex_message_create. The identifier comes from the webhook's createdBy, the person who registered it, so every read and the post run as that user, not as an admin.

import hashlib import hmac import json import time from langchain_openai import ChatOpenAI def verify_webex_signature(raw_body: bytes, signature: str, secret: str) -> bool: """Webex signs the raw body with HMAC-SHA1; X-Spark-Signature is hex.""" expected = hmac.new(secret.encode(), raw_body, hashlib.sha1).hexdigest() return hmac.compare_digest(expected, signature.strip().lower()) # Map the Webex person who created the webhook to your app's identifier WEBEX_PERSON_TO_IDENTIFIER = {"Y2lzY29zcGFyazovL3VzL1BFT1BMRS9leGFtcGxl": "user_123"} RECAP_ROOM_BY_IDENTIFIER = {"user_123": os.environ["WEBEX_RECAP_ROOM_ID"]} recap_llm = ChatOpenAI(model="gpt-4o") def get_meeting_with_retry(meeting_id: str, user_id: str, attempts: int = 5): """The meetings resource can lag its webhook, so retry before giving up.""" last_error = None for attempt in range(attempts): try: return actions.execute_tool( tool_name="webex_meeting_get", tool_input={"meeting_id": meeting_id}, connection_name=CONNECTION_NAME, identifier=user_id, ) except Exception as exc: # surfaced as an error while the resource is not readable yet last_error = exc time.sleep(30 * (attempt + 1)) raise RuntimeError(f"Meeting {meeting_id} not readable yet") from last_error def handle_webex_webhook(raw_body: bytes, signature: str) -> None: secret = os.environ["WEBEX_WEBHOOK_SECRET"] # set when the webhook was created, stored per user if not verify_webex_signature(raw_body, signature, secret): raise PermissionError("Invalid X-Spark-Signature") event = json.loads(raw_body) if event.get("resource") != "meetingTranscripts" or event.get("event") != "created": return user_id = WEBEX_PERSON_TO_IDENTIFIER[event["createdBy"]] transcript_id = event["data"]["id"] meeting_id = event["data"]["meetingId"] meeting = get_meeting_with_retry(meeting_id, user_id) print(meeting.execution_id, meeting_id) title = meeting.data.get("title", "Webex meeting") if isinstance(meeting.data, dict) else "Webex meeting" transcript = actions.execute_tool( tool_name="webex_meeting_transcript_download", tool_input={"transcript_id": transcript_id, "format": "txt"}, connection_name=CONNECTION_NAME, identifier=user_id, ) print(transcript.execution_id, transcript_id) recap = recap_llm.invoke( "Write a Webex recap in Markdown with three sections: Decisions, Action items " "(owner and due date), and Open questions. Use only what the transcript says.\n\n" f"Meeting: {title}\n\nTranscript:\n{transcript.data}" ).content posted = actions.execute_tool( tool_name="webex_message_create", tool_input={ "room_id": RECAP_ROOM_BY_IDENTIFIER[user_id], "markdown": f"**Recap: {title}**\n\n{recap}", }, connection_name=CONNECTION_NAME, identifier=user_id, ) print(posted.execution_id, title)

Webex disables a webhook after 100 failed deliveries within five minutes and does not turn it back on, so acknowledge with a 2xx immediately and run this handler from a queue. Make it idempotent on transcript_id, because a retried delivery should not post a second recap. Long transcripts can exceed a model's context window; chunk them, or prefer webex_meeting_summary_get when the customer has Webex AI Assistant.

Reaching the Webex REST API

Webhook registration is the part that stays in your own code: Scalekit's webex connector has no webhook tools, and the hosted MCP servers can create a webhook but have nowhere to deliver it. Register once per user with that user's token, which Scalekit can send on the connected account's behalf through proxied API calls, or with a token from your own integration as shown here. The token needs the scopes for the resource you subscribe to.

import os import time import requests WEBEX_API = "https://webexapis.com/v1" token = os.environ["WEBEX_USER_ACCESS_TOKEN"] # this user's integration token, never a shared admin token def webex_post(path: str, body: dict, max_attempts: int = 5) -> dict: """Webex publishes no fixed limits; back off on the Retry-After it returns with 429.""" for _ in range(max_attempts): resp = requests.post( f"{WEBEX_API}{path}", json=body, headers={"Authorization": f"Bearer {token}"}, timeout=30, ) if resp.status_code == 429: time.sleep(int(resp.headers.get("Retry-After", "60"))) continue resp.raise_for_status() return resp.json() raise RuntimeError(f"Webex kept returning 429 for {path}") webhook = webex_post( "/webhooks", { "name": "meeting-recap-agent", "targetUrl": "https://agent.example.com/webhooks/webex", "resource": "meetingTranscripts", "event": "created", "secret": os.environ["WEBEX_WEBHOOK_SECRET"], }, ) print(webhook["id"], webhook["status"])

Store the webhook id per user. If your endpoint is down long enough for Webex to mark it inactive, set status back to active through the update webhook endpoint once the endpoint is healthy; Webex will not do it for you.

Multi-tool, multi-tenant Webex agents on a virtual MCP server

A meeting agent rarely stops at Webex. Plenty of companies meet in Webex and work in Slack, and the recap belongs where the work happens. Load all 49 webex tools and the model holds webex_message_create, webex_meeting_update, webex_membership_create, and webex_team_create while answering "what did we decide." At roughly 200 tokens each, 49 tools is about 9,800 tokens before the agent does any work. The fix is not better prompting. It is surface reduction. A Scalekit virtual MCP server declares exactly which tools an agent role can see across connections, and session tokens keep each run bound to one user's connected accounts. Docs: set up a virtual MCP server.

Create the server once per agent role

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = actions.mcp.create_config( name="webex-meeting-recap", description="Reads a user's Webex meetings, summaries, and transcripts and posts a recap to Slack", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="webex", tools=[ "webex_me_get", "webex_meetings_list", "webex_meeting_summary_get", "webex_meeting_transcripts_list", "webex_meeting_transcript_download", ], ), McpConfigConnectionToolMapping( connection_name="slack", tools=["slack_send_message"], ), ], ) print(vmcp.config.id, vmcp.config.mcp_server_url)

Mint a session token per user, per run

One server definition serves every user at every customer. Before each run, check that the user's accounts are active and mint a short-lived session token scoped to them. Any MCP client that speaks Streamable HTTP can connect; this example uses langchain-mcp-adapters (pip install langchain-mcp-adapters) and passes the token as a Bearer header.

import asyncio from datetime import timedelta from langchain_mcp_adapters.client import MultiServerMCPClient config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url user_id = "user_123" accounts = actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=user_id, include_auth_link=True, ) not_ready = [ a for a in accounts.connected_accounts if str(a.connected_account_status).upper() != "ACTIVE" ] for a in not_ready: print(f"{a.connection_name} is {a.connected_account_status}: {a.authentication_link}") if not_ready: raise RuntimeError("Connect the accounts above before starting this run") session_token = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=user_id, expiry=timedelta(minutes=30), ).token async def load_tools(): client = MultiServerMCPClient( { "scalekit": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {session_token}"}, } } ) return await client.get_tools() mcp_tools = asyncio.run(load_tools()) print([t.name for t in mcp_tools])

The agent sees six tools instead of 49 plus everything Slack exposes. The endpoint is static; the identity is per user. Control Hub can trim tools on Cisco's own servers too, but that switch belongs to each customer's admin and applies to every agent in their org; the virtual server picks individual tools per agent role, and you own the definition.

Observability: every Webex action tied to a user

Every execute_tool call returns an execution_id, and every execution is attributed to a connected account, which maps to one user at one customer. When a customer asks which agent run posted a recap into the wrong space or rescheduled the board meeting, the answer is a lookup, not an investigation. Webex records its side too: a message created through a user's integration token appears in the space as that user, and Control Hub logs an AdminAudit event when a Full Admin authorizes a service app.

Attribution is where the identity choice shows. A bot posts as the bot, so the space shows the bot and your log is the only record of which user asked for it. A service app acts as a machine account for the whole org. A site admin's grant with host_email changes other hosts' meetings under the admin's name. Per-user integration grants keep Webex's own history and your execution log pointing at the same person.

Recommended reading: audit trails for agent auth

Which one to build against

If your agent is a person's assistant inside Claude, Cursor, Copilot Studio, or Codex, scheduling meetings, searching messages, and reading summaries, Cisco's hosted Webex MCP servers are a good fit, provided each customer's admin has enabled them in Control Hub; stay on the Meetings and Messaging servers until the Suite server leaves Beta. If your agent runs unattended, reacts to transcripts, messages, or Adaptive Card submissions, needs invitees, participants, or the directory, or serves many customer orgs whose admins you do not control, build against the Webex REST API with per-user integration grants. Either way, you hold a Webex credential per user that can read their meetings and post in their spaces, and that's what needs production-grade infrastructure.

FAQs

Does Webex have an official MCP server?

Yes. Cisco hosts Webex Meetings and Webex Messaging MCP servers, both publicly available since May 2026, plus a Webex Suite server that adds Calling and Vidcast and is still labeled beta. Every server must be enabled by the organization's admin in Webex Control Hub before anyone can use it.

How does Webex MCP authentication work?

The servers take a Webex OAuth bearer token that must carry the MCP scope plus the resource scopes each tool needs, and they call the REST API as the signed-in user. Admins can block a server, allow it for everyone, limit it to groups or switch off individual tools in Control Hub.

What token types does the Webex REST API accept?

Personal access tokens that expire 12 hours after sign-in, bot tokens that act as the bot and cannot act for a user, integration tokens from the authorization code flow that act as the user, and service app tokens for a machine account that a Full Admin authorizes for the whole org.

What can the Webex API do that the MCP servers cannot?

The API delivers webhooks for messages, meetings, transcripts, recordings and Adaptive Card submissions, and it exposes meeting invitees, participants, transcript snippets and the people directory. The Messaging MCP server can create a webhook but cannot receive one, so a meeting-recap pipeline belongs on the API.

How do Webex webhooks behave in production?

Payloads carry identifiers, not content, so your app fetches each message or transcript with an authorized call. A secret adds an HMAC signature header. A webhook is disabled after 100 failed deliveries within five minutes and is not re-enabled automatically, and the meetings resource can lag its webhook by several minutes.

How does Scalekit connect an AI agent to Webex?

Scalekit's Webex connector runs per-user OAuth through your own Webex integration, stores each user's tokens in a vault and refreshes them. It exposes 49 tools, 31 read and 18 write with none destructive, covering messages, spaces, meetings, transcripts and Adaptive Card actions, served per agent role through a virtual MCP server.

Build your Webex agent with Scalekit

Browse the Scalekit Webex connector and the Webex connector docs with the full tool list. See Scalekit pricing, or talk to us.

No items found.
On this page
Share this article
Scalekit
Find us on ProductHunt

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.