Skip to content

MCP Integration ​

Model Context Protocol (MCP) integration for the Oktivo platform. Connects AI agents to external tools (Google Calendar, HubSpot CRM, custom MCP servers) during live calls.

How It Works ​

  1. Superadmin enables mcp_integrations feature for an Organization
  2. Admin assigns the feature to a Customer
  3. Customer connects MCP integrations (global or per-agent) via the UI
  4. On call dispatch, backend resolves active integrations → decrypts credentials → embeds mcp_servers[] in LiveKit metadata
  5. Agent Server creates MCPServerHTTP instances → passes to AgentSession → LLM can invoke MCP tools during conversation

Step-by-Step UI Testing ​

1. Enable Feature (Superadmin) ​

  1. Login as superadmin
  2. Go to Organizations page
  3. Click edit on the target org
  4. In the Features grid, check mcp integrations
  5. Save

What happens: mcp_integrations is added to org.plan.features[] in MongoDB.

2. Assign Feature to Customer (Admin) ​

  1. Login as admin of that org
  2. Go to Customers page
  3. Click edit on the target customer
  4. In the Features list, check mcp integrations
  5. Save

What happens: mcp_integrations is added to user.features[] in MongoDB. The customer can now access MCP endpoints.

3. Connect an Integration (Customer) ​

  1. Login as the customer
  2. Click MCP Integrations in the sidebar (plug icon)
  3. You see the Available Integrations catalog:
    • Google Calendar
    • HubSpot CRM
    • Generic MCP Server
  4. Click Connect on any card
  5. Fill in credentials (e.g., Composio API key)
  6. Select scope: Global (all agents) or Agent-specific (one agent)
  7. Click Test Connection backend resolves the URL and attempts an MCP handshake (10s timeout)
  8. If test passes, click Connect

What happens:

  • POST /api/v2/mcp/integrations is called
  • Backend validates catalogId, encrypts credentials (AES-256-GCM), stores McpIntegration document
  • Integration appears in Active Integrations section with masked credentials

4. Verify in a Call ​

  1. Go to Agents → open any agent → click Test Call
  2. The call dispatches to LiveKit

What happens behind the scenes:

  • calling.service.ts / agent.controller.ts calls resolveMcpServersForDispatch(customerUID, agentId)
  • Queries all active global + agent-scoped integrations
  • Decrypts credentials, resolves serverUrl templates (e.g., {{api_key}} → actual key)
  • Embeds mcp_servers[] in dispatch metadata
  • Agent Server parses metadata → creates mcp.MCPServerHTTP instances → passes to AgentSession
  • LLM can now invoke tools exposed by the MCP server during the conversation

Quick Local Test ​

  1. Run a test MCP server:
    bash
    npx -y supergateway --stdio "npx -y @modelcontextprotocol/server-everything" --port 8888
  2. In MCP Integrations page, create Generic MCP Server:
    • Server URL: http://host.docker.internal:8888/sse (use host.docker.internal for Docker)
    • Auth Token: leave empty
  3. Click Test Connection → green success
  4. Click Connect
  5. Make a test call → check Agent Server logs for Created MCP server: Generic MCP Server
  6. During the call, say "use the echo tool to repeat hello"
  7. Check supergateway terminal for incoming connections

5. Delete an Integration ​

  1. In MCP Integrations page, click the trash icon on any active integration
  2. Confirm deletion

What happens: Soft-delete sets deletedAt timestamp. Integration no longer appears in lists or dispatch resolution.

6. Agent Deletion Cascade ​

When you delete an agent, all its agent-scoped MCP integrations are automatically soft-deleted.

7. Agent Cloning ​

When you clone an agent, MCP integrations are NOT cloned (credentials are sensitive). You must re-configure them on the cloned agent.

Architecture ​

Outbound/Test Call Flow ​

Inbound Call Flow ​

Feature Gate ​

Same two-layer pattern as dynamic_tools:

Superadmin → adds "mcp_integrations" to org.plan.features[]
Admin      → assigns "mcp_integrations" to customer.features[]
Customer   → can access /api/v2/mcp/* endpoints

Backend enforcement: requireFeatureAccess(...FEATURE_MAP.manageMcpIntegrations) middleware on all public MCP routes. Returns 403 if feature not assigned.

Superadmins bypass all feature checks. Admins can access if org has the feature.

Data Model ​

McpIntegration Collection ​

FieldTypeDescription
integrationIdStringUnique, prefix mcpi_
catalogIdStringReferences catalog entry (e.g., google-calendar)
customerUIDStringTenant isolation
orgUIDStringOrganization
scopeEnum"global" or "agent"
agentIdString/nullAgent ID when scope=agent, null when global
credentialsObjectAES-256-GCM encrypted key-value pairs
isActiveBooleanDefault true
deletedAtDate/nullSoft-delete timestamp

Indexes ​

  • { customerUID: 1, deletedAt: 1 } list by customer
  • { customerUID: 1, scope: 1, agentId: 1, deletedAt: 1 } list by customer+agent
  • { catalogId: 1, customerUID: 1, scope: 1, agentId: 1 } unique partial (deletedAt: null), prevents duplicates
  • { agentId: 1, deletedAt: 1 } cascade delete on agent deletion

Credential Handling ​

Create/Update → encryptSecret(value) per field → stored encrypted in MongoDB
Dispatch/Internal API → decryptSecret(value) per field → used to resolve serverUrl
Public API response → maskCredentials(value) → "sk-a****" (first 4 chars + ****)
Decryption failure → skip that integration, log error, continue (graceful degradation)

API Endpoints ​

Public (JWT + mcp_integrations feature gate) ​

MethodPathDescription
GET/api/v2/mcp/catalogList catalog entries
POST/api/v2/mcp/integrationsCreate integration
GET/api/v2/mcp/integrationsList active (optional ?agentId=X for global+agent)
GET/api/v2/mcp/integrations/:idGet single
PUT/api/v2/mcp/integrations/:idUpdate (re-encrypts credentials)
DELETE/api/v2/mcp/integrations/:idSoft-delete
POST/api/v2/mcp/integrations/verifyTest connection (10s timeout)

Internal (x-api-secret auth) ​

MethodPathDescription
GET/internal/mcp-servers/:agentId/:customerUIDResolved servers with decrypted credentials

File Structure ​

Client Backend ​

client_backend/src/
├── types/mcp-integration.types.ts      # Interfaces
├── config/mcp-catalog.ts               # Static catalog (Google Calendar, HubSpot, Generic)
├── models/McpIntegration.ts            # Mongoose schema + indexes
├── repositories/mcp-integration.repository.ts
├── services/mcp-integration.service.ts # Business logic
├── controllers/mcp-integration.controller.ts
├── routes/mcp-integration.routes.ts    # Public routes
└── routes/mcp-integration-internal.routes.ts # Internal routes

Agent Server ​

Agent/src/
├── schemas/metadata_schema.py          # McpServerDef + MetadataSchema.mcp_servers
└── core/
    ├── mcp_servers.py                  # MCPServerHTTP creation + fetch from API
    ├── session_builder.py              # Passes mcp_servers to AgentSession
    └── voice_assistant.py              # Loads MCP servers in __init__

Frontend ​

Integrations are configured from the unified Tools page. The standalone /mcp-integrations page was deleted in 2026-08 and its route now redirects to /tools see TOOLS-AND-LOGS.md.

app/src/
├── lib/features.ts                                    # MCP_INTEGRATIONS feature constant
├── services/mcpIntegration.service.ts                 # API client + tool call logs
├── components/features/tools/IntegrationToolForm.tsx  # Setup form (registered in TOOL_TYPE_CONFIGS)
├── components/features/tools/tool-registry.ts         # list / get / remove / setActive adapter
├── components/features/tools/logs/
│   └── IntegrationLogsSection.tsx                     # /logs → Integration Logs tab
├── components/features/McpTabContent.tsx              # Agent Editor → MCP sub-tab (overlaps /tools)
└── routes.tsx                                         # /tools route; /mcp-integrations redirects

Tool Call Logging ​

MCP tool calls go straight from the Agent Server to the external MCP server, so the backend never sees the HTTP exchange and there is no status code or header detail to record. Agent/src/core/mcp_tool_logging.py instead listens for LiveKit's function_tools_executed event and reports the tool name, the arguments the LLM supplied, the result returned and the latency to POST /internal/mcp-tool-call-logs.

Only MCP-owned tools are reported toolset ids are set to the McpIntegration id in mcp_servers.create_mcp_toolsets, which is how the backend resolves the catalog entry for display. Logging is fire-and-forget so a logging failure can never affect the conversation.

FieldNotes
integrationId / catalogIdcatalogId resolved server-side for display
toolNameAs exposed by the MCP server (e.g. create_event)
requestArguments / responseBodyJSON-stringified, truncated to 8KB
statussuccess or failed no queued state exists
callRoomIdLiveKit room, correlates with the Contact and recording

Stored in McpToolCallLog with a 30-day TTL. Read via GET /api/v2/mcp/tool-call-logs (JWT + mcp_integrations), surfaced at /logs → Integration Logs.

Troubleshooting ​

IssueCauseFix
No "MCP Integrations" in sidebarFeature not assigned to customerSuperadmin → enable for org, Admin → assign to customer
403 on MCP endpointsMissing mcp_integrations featureSame as above
409 on createDuplicate catalogId+scope+agentIdDelete existing integration first
Test Connection failsBad credentials or server URLDouble-check API key / server URL
Test Connection timeoutMCP server unreachableVerify server is running and accessible
MCP tools not in callIntegration inactive or decryption failedCheck isActive flag, check server logs for decrypt errors
Agent Server can't fetchInternal API unreachableVerify BACKEND_URL and WEBHOOK_SECRET env vars
Credentials corruptedEncryption key rotatedRe-create the integration with fresh credentials

Last updated: