Building an MCP Server
Build an MCP server to connect your business tools to JackAI Nexus
Building an MCP Server for JackAI Nexus
This guide explains how to build an MCP (Model Context Protocol) server that connects to JackAI Nexus. Your MCP server exposes tools, resources, and prompts that the AI assistant can use during conversations.
Quick Start
1. Install the MCP SDK
pip install "mcp>=1.0.0"
2. Create a Server
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My Business Tools")
@mcp.tool()
def get_product_info(product_id: str) -> str:
"""Get product details by ID."""
# Your business logic here
return f"Product {product_id}: Widget Pro, $29.99"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
3. Connect to Nexus
In the JackAI Nexus dashboard:
- Go to AI Assistants > select your assistant > MCP Servers
- Click Add MCP Server
- Enter:
- Server URL:
https://your-server.com/mcp - Transport: Streamable HTTP
- Auth Token: your secret token (optional)
- Server URL:
- Click Test Connection to verify
Server Configuration
Transport
Use Streamable HTTP (recommended). Your server will listen on a single /mcp endpoint.
mcp.run(transport="streamable-http")
Default port is 8000. To change it:
mcp.settings.port = 8100
mcp.settings.host = "0.0.0.0"
Authentication
Protect your server with a Bearer token:
import os
MCP_AUTH_TOKEN = os.getenv("MCP_AUTH_TOKEN", "your-secret-token")
# The MCP SDK handles Bearer token auth automatically
# Nexus sends: Authorization: Bearer <token>
In Nexus, enter the same token in the Authentication Token field.
Allowed Hosts
If your server is behind a reverse proxy, allow the proxy's hostname:
from mcp.server.fastmcp.server import TransportSecuritySettings
mcp.settings.transport_security = TransportSecuritySettings(
enable_dns_rebinding_protection=True,
allowed_hosts=[
"localhost",
"127.0.0.1",
"mcp.your-domain.com",
],
)
Tools
Tools are functions the AI can call during conversations. They receive parameters and return text.
Basic Tool (Plain Text)
@mcp.tool()
def get_order_status(order_id: str) -> str:
"""Check the status of a customer order.
Args:
order_id: The order ID to look up
"""
# Query your database/API
return f"Order {order_id}: Shipped, arriving March 15"
Key rules:
- Tools return text — Nexus handles sending it to the customer
- The docstring becomes the tool description the AI sees
- Use type hints for parameters — they become the input schema
- Optional parameters use
Optional[str] = None
Tool with Media Response
To send files (PDFs, images, audio) to the customer, return a JSON string with a media key. Nexus detects this format and sends the attachments via the messaging platform (WhatsApp, Messenger, etc.).
Document (PDF)
import json
@mcp.tool()
def send_catalog(category: str) -> str:
"""Send a product catalog PDF to the customer.
Args:
category: Product category (e.g., 'electronics', 'furniture')
"""
pdf_url = f"https://your-server.com/catalogs/{category}.pdf"
return json.dumps({
"text": f"Here is our {category} catalog.",
"media": [{
"type": "document",
"url": pdf_url,
"filename": f"{category}-catalog.pdf",
"mime_type": "application/pdf"
}]
})
Image
@mcp.tool()
def send_product_image(product_id: str) -> str:
"""Send a product image to the customer.
Args:
product_id: The product ID
"""
image_url = f"https://your-server.com/images/{product_id}.jpg"
return json.dumps({
"text": "Here is the product image.",
"media": [{
"type": "image",
"url": image_url,
"filename": f"{product_id}.jpg",
"mime_type": "image/jpeg"
}]
})
Audio / Voice Message
@mcp.tool()
def send_audio_guide(topic: str) -> str:
"""Send a pre-recorded audio explanation.
Args:
topic: The topic to explain
"""
audio_url = f"https://your-server.com/audio/{topic}.opus"
return json.dumps({
"text": "Here is an audio explanation about the topic.",
"media": [{
"type": "audio",
"url": audio_url,
"filename": f"{topic}.opus",
"mime_type": "audio/ogg"
}]
})
Media Response Format
{
"text": "Message text shown to the customer",
"media": [
{
"type": "document | image | audio",
"url": "https://your-server.com/path/to/file",
"filename": "display-name.pdf",
"mime_type": "application/pdf"
}
]
}
| Field | Required | Description |
|---|---|---|
text | Yes | Text message sent alongside the media |
media | Yes | Array of media attachments |
media[].type | Yes | document, image, or audio |
media[].url | Yes | Public URL where Nexus can download the file |
media[].filename | Yes | Display filename for the recipient |
media[].mime_type | No | MIME type (auto-detected from URL if missing) |
Important notes:
- The
urlmust be publicly accessible — Nexus downloads the file server-side - Multiple attachments are supported in a single response
- Media is sent before the text message
- If a tool returns plain text (no
mediakey), it works as a normal text response — no changes needed - The
textfield is what the AI uses as the tool result for conversation context
Resources
Resources inject knowledge into the AI's context. They are read-only text that the AI can reference during conversations.
@mcp.resource("myapp://company-info")
def company_info() -> str:
"""Company overview, contact details, and policies."""
return """COMPANY INFO
Name: Acme Corp
Phone: +1-555-0100
Email: support@acme.com
Hours: Mon-Fri 9am-6pm
RETURN POLICY
30-day returns on all products..."""
@mcp.resource("myapp://pricing")
def pricing() -> str:
"""Current product pricing and discount tiers."""
return """PRICING
Widget Pro: $29.99
Widget Plus: $49.99
Enterprise: Contact sales
DISCOUNTS
10+ units: 10% off
50+ units: 20% off"""
Best practices:
- Use resources for static knowledge (company info, FAQs, pricing, policies)
- Keep resource content focused and concise
- The AI reads all resources as context — don't include irrelevant data
- URI scheme is arbitrary (e.g.,
myapp://,company://)
Prompts
Prompts provide behavioral guidelines for the AI. They define how the AI should interact with customers.
@mcp.prompt()
def customer_service_guidelines() -> str:
"""Role definition and behavior rules for the AI assistant."""
return """CUSTOMER SERVICE GUIDELINES
ROLE: You are a customer service agent for Acme Corp.
RULES:
1. Be polite and professional
2. Answer questions using the provided resources
3. If you don't know, say so — don't make up information
4. For complaints, collect details and escalate
5. Keep responses short and helpful"""
@mcp.prompt()
def conversation_flow() -> str:
"""Step-by-step conversation flow."""
return """CONVERSATION FLOW
STEP 1: Greet the customer
STEP 2: Identify their need
STEP 3: Provide information using tools
STEP 4: Guide to next steps
STEP 5: Escalate if needed"""
Tips:
- Prompts guide the AI's behavior — define the role, tone, and rules
- Reference tool names in prompts so the AI knows when to use them
- Keep instructions clear and specific
Complete Example
Here's a minimal but complete MCP server:
#!/usr/bin/env python
"""My Business MCP Server"""
import json
import os
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My Business Tools")
# --- Configuration ---
SITE_URL = os.getenv("SITE_URL", "https://my-business.com")
MCP_PORT = int(os.getenv("MCP_PORT", "8100"))
# --- Resources (AI context) ---
@mcp.resource("biz://company-info")
def company_info() -> str:
"""Company overview and contact information."""
return "Company: My Business Inc. Phone: +1-555-0100..."
# --- Prompts (AI behavior) ---
@mcp.prompt()
def guidelines() -> str:
"""Customer service behavior rules."""
return "You are a helpful assistant for My Business Inc..."
# --- Tools (AI actions) ---
@mcp.tool()
def get_product_info(product: str) -> str:
"""Get information about a product.
Args:
product: Product name or ID
"""
return f"Product details for {product}..."
@mcp.tool()
def send_catalog(category: str) -> str:
"""Send a product catalog PDF.
Args:
category: Product category
"""
return json.dumps({
"text": f"Here is our {category} catalog.",
"media": [{
"type": "document",
"url": f"{SITE_URL}/catalogs/{category}.pdf",
"filename": f"{category}-catalog.pdf",
"mime_type": "application/pdf"
}]
})
# --- Run ---
if __name__ == "__main__":
mcp.settings.port = MCP_PORT
mcp.settings.host = "0.0.0.0"
mcp.run(transport="streamable-http")
Deployment
Docker
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "server.py"]
# docker-compose.yml
services:
mcp_server:
build: .
ports:
- "127.0.0.1:8100:8100"
environment:
- MCP_AUTH_TOKEN=your-secret-token
- SITE_URL=https://your-domain.com
restart: always
HTTPS
Nexus connects to your server via HTTPS. Options:
- Cloudflare proxy (recommended) — free SSL, set DNS to proxied (orange cloud)
- Let's Encrypt — use certbot with nginx
- Cloudflare Origin Certificate — for Full (strict) SSL mode
Nginx reverse proxy example:
server {
listen 443 ssl;
server_name mcp.your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8100;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off;
}
}
Nexus Connection Settings
| Setting | Description | Default |
|---|---|---|
| Server URL | Your MCP server endpoint | — |
| Transport | Streamable HTTP (recommended) or SSE | Streamable HTTP |
| Auth Token | Bearer token for authentication | (optional) |
| Cache TTL | How often to re-discover tools (seconds) | 3600 |
| Timeout | Max wait for tool execution (seconds) | 30 |
| Static Parameters | Fixed values auto-injected into every tool call | (optional) |
| Custom Headers | Extra HTTP headers for the MCP server | (optional) |
| Verify SSL | Verify the server's SSL certificate | true |
Static Parameters
Use static parameters to inject context that every tool call needs (e.g., tenant ID, API keys). These are added automatically to all tool calls from Nexus.
Tool Filter
By default, all discovered tools are enabled. You can select specific tools to enable if you only want the AI to use a subset.
Troubleshooting
| Problem | Solution |
|---|---|
| Connection refused | Check server is running and port is correct |
| SSL certificate error | Set verify_ssl: false in config, or install a valid cert |
| 403 Forbidden | Check auth token matches between Nexus and server |
| Tools not discovered | Click Refresh on the MCP server card |
| Media not delivered | Ensure the url in media response is publicly accessible |
| Audio not playing on WhatsApp | Use .opus or .ogg format — WhatsApp requires OGG/Opus |