ElevenLabs MCP Backend
Production-grade backend service that connects ElevenLabs Agents to Supabase using MCP (Model Context Protocol). Enables voice agents to look up real-time order details via the lookup_order tool.
Features
- ✅ MCP Server (Streamable HTTP) - Full JSON-RPC 2.0 implementation
- ✅ Supabase Integration - Real-time order data queries
- ✅ Bearer Token Authentication - Secure API access
- ✅ Health Check Endpoint - Service monitoring
- ✅ REST Backup Endpoint - Alternative to MCP for testing
- ✅ Structured Logging - Request tracking and error handling
- ✅ Production Ready - Works on Replit, Railway, Render, Fly, Vercel
Architecture
ElevenLabs Agent → MCP Server (/mcp) → Supabase → Orders Table
The MCP server implements the following JSON-RPC 2.0 methods:
initialize- Protocol handshakeping- Health checktools/list- List available toolstools/call- Execute tools (lookup_order)
Quick Start
Prerequisites
- Node.js 18+
- Supabase account and project
- ElevenLabs account with Agents feature
1. Clone and Install
git clone <your-repo>
cd elevenlabs-mcp-backend
npm install
2. Set Up Supabase
- Go to your Supabase project dashboard
- Open the SQL Editor
- Run the SQL from
sql/schema.sqlto create theorderstable and seed data
3. Configure Environment Variables
Copy env.example to .env:
cp env.example .env
Edit .env with your values:
PORT=3000
MCP_SECRET=your-secure-random-string-here
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
Generate a secure MCP_SECRET:
openssl rand -hex 32
Get Supabase credentials:
SUPABASE_URL: Found in Project Settings → API → Project URLSUPABASE_SERVICE_ROLE_KEY: Found in Project Settings → API → service_role key (⚠️ Keep secret!)
4. Build and Run
Development:
npm run dev
Production:
npm run build
npm start
The server will start on http://localhost:3000 (or your PORT env var).
Testing
Health Check
curl http://localhost:3000/health
Expected response:
{
"ok": true,
"service": "elevenlabs-mcp-backend",
"timestamp": "2025-12-23T12:00:00.000Z"
}
MCP: tools/list
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secure-random-string-here" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list",
"params": {}
}'
Expected response:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"tools": [
{
"name": "lookup_order",
"description": "Looks up order details by order ID...",
"inputSchema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID to look up (e.g., 'TR-10001')"
}
},
"required": ["order_id"]
}
}
]
}
}
MCP: tools/call (Existing Order)
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secure-random-string-here" \
-d '{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/call",
"params": {
"name": "lookup_order",
"arguments": {
"order_id": "TR-10001"
}
}
}'
Expected response:
{
"jsonrpc": "2.0",
"id": "2",
"result": {
"content": [
{
"type": "text",
"text": "Order TR-10001 is SHIPPED. ETA: December 28, 2025. Tracking: 123456789 (Yurtiçi Kargo). Last updated: December 23, 2025, 10:30 AM."
}
],
"structured": {
"order_id": "TR-10001",
"status": "SHIPPED",
"eta": "2025-12-28",
"carrier": "Yurtiçi Kargo",
"tracking_number": "123456789",
"last_update": "2025-12-23T10:30:00Z"
}
}
}
MCP: tools/call (Missing Order)
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secure-random-string-here" \
-d '{
"jsonrpc": "2.0",
"id": "3",
"method": "tools/call",
"params": {
"name": "lookup_order",
"arguments": {
"order_id": "TR-99999"
}
}
}'
Expected response:
{
"jsonrpc": "2.0",
"id": "3",
"error": {
"code": -32004,
"message": "Order not found: TR-99999"
}
}
REST Backup Endpoint
curl -X POST http://localhost:3000/lookup-order \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secure-random-string-here" \
-d '{
"order_id": "TR-10001"
}'
Expected response:
{
"order_id": "TR-10001",
"status": "SHIPPED",
"eta": "2025-12-28",
"carrier": "Yurtiçi Kargo",
"tracking_number": "123456789",
"last_update": "2025-12-23T10:30:00Z",
"issue_flag": null,
"notes": "Out for delivery",
"formatted_text": "Order TR-10001 is SHIPPED. ETA: December 28, 2025. Tracking: 123456789 (Yurtiçi Kargo). Last updated: December 23, 2025, 10:30 AM."
}
ElevenLabs Configuration
Setting Up MCP Server in ElevenLabs
-
Deploy your backend to a public URL (e.g.,
https://your-app.railway.apporhttps://your-app.vercel.app) -
In ElevenLabs Dashboard:
- Go to Agents → Select your agent → Tools → MCP Servers
- Click Add MCP Server
-
Configure the MCP Server:
- MCP Server URL:
https://your-domain.com/mcp - Header:
Authorization: Bearer <your-MCP_SECRET> - Tool Approval Mode:
- Always Ask (recommended for production)
- No Approval (for testing/demo)
- MCP Server URL:
-
Test Connection:
- Click Test Connection in ElevenLabs
- Should see: ✅ Connection successful
- The
lookup_ordertool should appear in available tools
Important Security Notes
⚠️ Only agents with this MCP server attached can use the tool.
- Not "all agents can read the DB"
- Only agents that have this MCP server connected and permission
- Each agent must have the MCP server configured individually
Agent System Prompt Fix
If your agent prompt currently says:
"You have access only to uploaded TXT order records"
Replace it with:
"You have access to real-time order records using the lookup_order tool. When a user asks about an order, always call the lookup_order tool with the order_id. Never guess order details - always use the tool to retrieve accurate information."
Keep these guardrails:
- Never guess order details
- Always call the
lookup_ordertool when asked about orders - If the tool returns "Order not found", inform the user clearly
Deployment
Railway
- Connect your GitHub repo to Railway
- Railway auto-detects Node.js
- Add environment variables in Railway dashboard:
PORT(auto-set by Railway, but can override)MCP_SECRETSUPABASE_URLSUPABASE_SERVICE_ROLE_KEY
- Deploy!
Render
- Create new Web Service
- Connect your repo
- Build command:
npm install && npm run build - Start command:
npm start - Add environment variables
- Deploy!
Vercel
- Install Vercel CLI:
npm i -g vercel - Run
vercelin project directory - Add environment variables in Vercel dashboard
- Note: Vercel serverless may have cold starts. Consider Railway/Render for better MCP performance.
Fly.io
- Install Fly CLI
- Run
fly launch - Add secrets:
fly secrets set MCP_SECRET=... SUPABASE_URL=... SUPABASE_SERVICE_ROLE_KEY=... - Deploy:
fly deploy
Replit
- Import your GitHub repo
- Add environment variables in Secrets tab
- Run
npm install && npm run build && npm start - Replit auto-exposes the port
API Reference
Endpoints
GET /health
Health check (no auth required)
Response:
{
"ok": true,
"service": "elevenlabs-mcp-backend",
"timestamp": "2025-12-23T12:00:00.000Z"
}
POST /mcp
MCP JSON-RPC 2.0 endpoint (requires Bearer token)
Headers:
Authorization: Bearer <MCP_SECRET>Content-Type: application/json
Request Body: JSON-RPC 2.0 format
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": { ... }
}
Response: JSON-RPC 2.0 format
POST /lookup-order
REST backup endpoint (requires Bearer token)
Headers:
Authorization: Bearer <MCP_SECRET>Content-Type: application/json
Request Body:
{
"order_id": "TR-10001"
}
Response:
{
"order_id": "TR-10001",
"status": "SHIPPED",
"eta": "2025-12-28",
"carrier": "Yurtiçi Kargo",
"tracking_number": "123456789",
"last_update": "2025-12-23T10:30:00Z",
"issue_flag": null,
"notes": "Out for delivery",
"formatted_text": "..."
}
Error Codes
JSON-RPC 2.0 error codes:
-32700: Parse error-32600: Invalid request-32601: Method not found-32602: Invalid params-32603: Internal error-32004: Order not found (custom)
Database Schema
The orders table has the following structure:
CREATE TABLE orders (
id UUID PRIMARY KEY,
order_id TEXT UNIQUE NOT NULL,
status TEXT NOT NULL, -- PROCESSING, SHIPPED, DELIVERED, CANCELED, RETURNED, ON_HOLD
eta DATE,
carrier TEXT,
tracking_number TEXT,
last_update TIMESTAMPTZ DEFAULT NOW(),
issue_flag TEXT, -- e.g., "address_problem", "payment_review"
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
See sql/schema.sql for full schema and seed data.
Development
Project Structure
.
├── src/
│ ├── server.ts # Fastify server and routes
│ ├── mcp.ts # MCP JSON-RPC handler
│ ├── supabase.ts # Supabase client and queries
│ ├── auth.ts # Bearer token middleware
│ ├── logger.ts # Structured logging
│ └── types.ts # TypeScript types
├── sql/
│ └── schema.sql # Database schema and seeds
├── dist/ # Compiled JavaScript (generated)
├── package.json
├── tsconfig.json
├── env.example
└── README.md
Scripts
npm run dev- Development with hot reload (tsx watch)npm run build- Compile TypeScript to JavaScriptnpm start- Run production servernpm run type-check- Type check without building
Logging
Logs include:
- Request IDs for tracing
- Method names (MCP methods)
- Tool names
- Order IDs
- Errors (safely, no secrets exposed)
Example log:
[2025-12-23T12:00:00.000Z] [INFO] Processing MCP request {"requestId":"123-abc","method":"tools/call","id":"2"}
[2025-12-23T12:00:00.100Z] [INFO] Order found {"requestId":"123-abc","orderId":"TR-10001","status":"SHIPPED"}
Troubleshooting
"Order not found" for valid orders
- Check Supabase connection: Verify
SUPABASE_URLandSUPABASE_SERVICE_ROLE_KEY - Check table exists: Run
sql/schema.sqlin Supabase SQL Editor - Check order_id format: Must match exactly (case-sensitive)
MCP "Test Connection" fails in ElevenLabs
- Verify server is deployed and accessible
- Check
Authorizationheader format:Bearer <token>(with space) - Verify
MCP_SECRETmatches in both places - Check server logs for authentication errors
CORS errors
- Server allows all origins by default (
origin: true) - For production, restrict in
src/server.tsCORS config
Supabase connection errors
- Service role key must have full access (bypasses RLS)
- Verify URL format:
https://xxxxx.supabase.co(no trailing slash) - Check network/firewall allows outbound HTTPS
Final Checklist
After deployment, verify:
- ✅ Server running
- ✅
/healthworks - ✅
/mcptools/listworks - ✅
/mcptools/callworks - ✅ Supabase returns rows
- ✅ ElevenLabs "Test Connection" passes
- ✅ Voice agent can answer "Where is my order TR-10001?"
License
MIT
Support
For issues or questions:
- Check server logs for detailed error messages
- Verify environment variables are set correctly
- Test with curl commands above
- Check Supabase dashboard for data
Built with: Fastify, TypeScript, Supabase, MCP (Model Context Protocol)