Skip to Content
Getting StartedAI Agent Integration

AI Agent Integration

Connect AI agents to the Loop comms platform via the Model Context Protocol (MCP). The @loop/mcp-comms server exposes 59 tools for managing content, audiences, sends, analytics, and more.

MCP Transport Setup

The MCP server supports two transport modes: stdio for local development and HTTP/SSE for remote agents.

stdio Transport (Local)

Run the MCP server as a subprocess from your AI agent:

{ "mcpServers": { "loop-comms": { "command": "npx", "args": ["@loop/mcp-comms"], "env": { "LOOP_API_KEY": "lk_dev_...", "LOOP_BRAND_ID": "your-brand-uuid" } } } }

For Cursor or Claude Desktop, add this to your MCP configuration file.

HTTP/SSE Transport (Remote)

For hosted agents, connect to the SSE endpoint:

SSE endpoint: https://comms.loop.health/mcp/sse Message endpoint: https://comms.loop.health/mcp/message

Example connection with the MCP SDK:

import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; const transport = new SSEClientTransport( new URL('https://comms.loop.health/mcp/sse'), { requestInit: { headers: { 'Authorization': 'Bearer <OAUTH_TOKEN>', }, }, } ); const client = new Client({ name: 'my-agent', version: '1.0.0' }); await client.connect(transport);

Clerk OAuth Authentication

The MCP server authenticates via Clerk OAuth. AI agents need a valid OAuth token to connect.

OAuth Flow

  1. Register your agent as an OAuth application in Clerk
  2. Request a token with the comms:tools scope
  3. Include the token in the Authorization header
POST https://clerk.loop.health/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials &client_id=your_client_id &client_secret=your_client_secret &scope=comms:tools

Service Tokens

For automated agents running without user context, use a service token:

LOOP_SERVICE_TOKEN=lst_...

Service tokens are scoped to a specific brand and role.

Role Mapping

The MCP server enforces RBAC on every tool call. The authenticated user’s role determines which tools are available.

RoleAccess Level
adminAll 59 tools
marketingContent, audiences, sequences, sends, pages, shares, reports, analytics
advisorContent (read), sends (draft/preview), analytics
opsOps tools, audit, platform health, flags
educatorContent (read), pages (read), shares (read)

Role is determined from Clerk publicMetadata.adminRole. If no role is set, all tool calls are denied.

Role Constants

const ALL_ROLES = ['admin', 'marketing', 'advisor', 'ops', 'educator']; const ADMIN_ONLY = ['admin']; const MARKETING_AND_ADMIN = ['admin', 'marketing']; const OPS_AND_ADMIN = ['admin', 'ops'];

Tool Discovery

After connecting, list available tools to see what’s accessible for the current role:

const { tools } = await client.listTools(); for (const tool of tools) { console.log(`${tool.name}: ${tool.description}`); }

The server returns only the tools the authenticated role can access. An admin sees all 59 tools; a marketing user sees a filtered subset.

Tool Categories

CategoryTool CountExample Tools
Content8templates.list, templates.create, voice.get
Audiences4audiences.create_from_description, audiences.preview
Sequences5sequences.create, sequences.simulate
Sends5sends.draft, sends.schedule
Pages6pages.draft, pages.publish
Shares8share.draft, share.publish
Transactional5events.declare, events.fire_test
Reports3reports.create, reports.schedule_send
Routing3routing.propose_rule, routing.simulate
Analytics2analytics.ask, anomaly.explain
Ops6brands.list, audit.search, platform.health
SQL1sql.run
State1state.recent_changes
Pairing2session.pair, session.disconnect

Example Session

Here’s a complete session where an AI agent creates and sends a marketing email:

Agent → server: tools/list Server → agent: [59 tools listed] Agent → server: tools/call tool: templates.list input: { "channel": "email", "status": "active" } Server → agent: [3 templates returned] Agent → server: tools/call tool: audiences.create_from_description input: { "description": "Customers who purchased BPC-157 in the last 30 days", "brandId": "brand-uuid" } Server → agent: { "audienceId": "aud_abc123", "estimatedSize": 1247 } Agent → server: tools/call tool: sends.draft input: { "templateId": "tpl_xyz789", "audienceId": "aud_abc123", "subject": "Your BPC-157 protocol update" } Server → agent: { "sendId": "snd_def456", "status": "draft", "estimatedRecipients": 1247 } Agent → server: tools/call tool: sends.preview input: { "sendId": "snd_def456" } Server → agent: { "previewUrl": "https://comms.loop.health/preview/snd_def456", "html": "..." } Agent → server: tools/call tool: sends.schedule input: { "sendId": "snd_def456", "scheduledAt": "2026-05-20T10:00:00Z" } Server → agent: { "sendId": "snd_def456", "status": "scheduled", "scheduledAt": "2026-05-20T10:00:00Z" }

Next Steps