Model Context Protocol (MCP) AI Integration

Status: Production Ready
Version: 1.0.0
Last Updated: December 2024

Overview

The Model Context Protocol (MCP) integration enables AI assistants like Claude (via Cursor IDE) to access WarmlyYours content through semantic search. This allows AI to find relevant product information, FAQs, installation guides, images, and more using natural language queries.

MCP is an open standard developed by Anthropic for connecting AI models to external tools and data sources. Learn more at modelcontextprotocol.io.

Architecture

┌─────────────────┐     HTTPS/SSE      ┌──────────────────┐
│   AI Client     │◄──────────────────►│   MCP Server     │
│  (Cursor/Claude)│                    │  (FastMCP gem)   │
└─────────────────┘                    └────────┬─────────┘
                                                │
                                       ┌────────▼─────────┐
                                       │   MCP Tools      │
                                       │  - semantic_search│
                                       │  - find_faqs     │
                                       │  - find_images   │
                                       │  - find_publications│
                                       │  - find_showcases│
                                       │  - find_reviews  │
                                       └────────┬─────────┘
                                                │
                                       ┌────────▼─────────┐
                                       │  pgvector DB     │
                                       │  (Embeddings)    │
                                       └──────────────────┘

Available Tools

Tool Description
semantic_search Search all content with natural language queries
find_faqs Find FAQ articles by topic or product line
find_images Find product images and installation photos
find_publications Find PDFs, manuals, and datasheets
find_showcases Find customer installation galleries
find_reviews Find customer reviews from Reviews.io

Endpoints

MCP is only accessible via the API subdomain (api.warmlyyours.com).

Endpoint Method Description
https://api.warmlyyours.com/mcp/sse GET Server-Sent Events for real-time communication
https://api.warmlyyours.com/mcp/messages POST JSON-RPC message handler for tool calls

Note: Requests to other subdomains (www, crm) will be rejected even with valid tokens.

Authentication

Requirements

  1. Employee Account - Only employees can access MCP
  2. mcp_access Role - Must have the mcp_access role (admins have this by default)
  3. API Token - A valid, non-expired API token

Token Generation

Via Web UI

  1. Navigate to CRM → Employees → [Employee Name]
  2. Click the System tab
  3. In the API Tokens panel, click Create Token
  4. Enter a name (e.g., "Cursor MCP") and expiration
  5. Copy the token immediately - it won't be shown again!

Via Rails Console

# Find the employee
employee = Employee.find_by(email: 'your.email@warmlyyours.com')

# Check if they have MCP access
employee..can_access_mcp?
# => true (if admin or has mcp_access role)

# Generate a token
token = employee..api_authentications.create!(
  name: 'Cursor MCP',
  expires_in: '3 months'
)

puts "Token: #{token.api_authentication_token}"
# Token: abc123xyz...

Token Management

  • View tokens: /employees/:id/api_tokens
  • Revoke tokens: Click "Revoke" button on any active token
  • Auto-revocation: All tokens are automatically revoked if the account is disabled

Token Expiration Options

Duration Use Case
1 hour Testing/debugging
1 day Temporary access
1 week Short-term projects
1 month Regular use
3 months Recommended for daily use
6 months Long-term development
1 year Extended access
Never Permanent (use sparingly)

Client Configuration

Cursor IDE

Create or edit ~/.cursor/mcp.json:

{
  "mcpServers": {
    "heatwave": {
      "url": "https://api.warmlyyours.com/mcp/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_TOKEN_HERE"
      }
    }
  }
}

After saving, restart Cursor. The MCP server will appear in the available tools.

Claude Desktop (if supported)

{
  "mcpServers": {
    "heatwave": {
      "command": "curl",
      "args": [
        "-H", "Authorization: Bearer YOUR_API_TOKEN_HERE",
        "https://api.warmlyyours.com/mcp/sse"
      ]
    }
  }
}

Local Development

For development, use api.warmlyyours.me:3000:

{
  "mcpServers": {
    "heatwave-dev": {
      "url": "https://api.warmlyyours.me:3000/mcp/sse",
      "headers": {
        "Authorization": "Bearer YOUR_DEV_TOKEN"
      }
    }
  }
}

Usage Examples

Once connected, the AI assistant can call these tools naturally:

Semantic Search

User: Find information about installing floor heating under tile
AI: [Calls semantic_search with query "installing floor heating under tile"]

Find FAQs

User: What are the voltage requirements for snow melting systems?
AI: [Calls find_faqs with query "voltage requirements snow melting"]

Find Publications

User: Get me the TempZone cable installation guide
AI: [Calls find_publications with query "TempZone cable installation guide"]

Find Images

User: Show me bathroom floor heating installation photos
AI: [Calls find_images with query "bathroom floor heating installation"]

Security

Access Control

Role MCP Access
admin ✅ Automatic
mcp_access ✅ Explicit
Other roles ❌ No access

Security Features

  1. API subdomain only - MCP endpoints only respond on api.warmlyyours.com
  2. Token-based authentication - No session cookies, no CSRF concerns
  3. Expiring tokens - All tokens have configurable expiration
  4. Revocation - Tokens can be instantly revoked
  5. Account lockout - All tokens revoked when account is disabled
  6. Audit trail - Token creation and revocation are tracked
  7. Role verification - Double-checks mcp_access role on every request

Best Practices

  • Use descriptive token names (e.g., "Cursor MCP - MacBook Pro")
  • Set reasonable expiration times (3 months recommended)
  • Revoke tokens for devices you no longer use
  • Don't share tokens between team members
  • Rotate tokens periodically

Troubleshooting

"Unauthenticated request" in logs

Cause: Missing or invalid token, OR wrong subdomain
Fix:

  1. Ensure you're using api.warmlyyours.com (not crm or www)
  2. Check that your Authorization header is correct
  3. Verify the token hasn't expired or been revoked

"No tools available" in AI client

Cause: Token is valid but user lacks mcp_access role
Fix: Ask an admin to assign the mcp_access role to your account

Connection timeout

Cause: Network issues or server not running
Fix:

  1. Check if the server is accessible: curl https://api.warmlyyours.com/mcp/sse
  2. Verify VPN connection if required
  3. Check firewall settings

Token not working after creation

Cause: Token was copied incorrectly or has special characters
Fix: Generate a new token and copy it directly from the confirmation screen

"Wrong subdomain" or silent auth failure

Cause: Using crm.warmlyyours.com or www.warmlyyours.com
Fix: Change your MCP config URL to use api.warmlyyours.com

Technical Implementation

Files

File Purpose
config/initializers/fast_mcp.rb MCP server configuration
app/mcp/application_tool.rb Base class for MCP tools
app/mcp/application_resource.rb Base class for MCP resources
app/mcp/mcp_authenticator.rb Token validation and authorization
app/mcp/tools/*.rb Individual tool implementations
app/controllers/crm/api_tokens_controller.rb Token management UI
app/views/crm/api_tokens/*.erb Token management views

Dependencies

  • fast_mcp gem - MCP server implementation
  • pgvector - Vector similarity search in PostgreSQL
  • ruby_llm - LLM API integration for embeddings

Adding New Tools

  1. Create a new file in app/mcp/tools/:
# app/mcp/tools/find_something_tool.rb
module Tools
  class FindSomethingTool < ApplicationTool
    tool_name 'find_something'
    description 'Description of what this tool does'

    arguments do
      required(:query).filled(:string).description('Search query')
      optional(:limit).filled(:integer).description('Max results')
    end

    def call(query:, limit: 10)
      # Implementation
      results = SomeModel.search(query).limit(limit)
      
      {
        query: query,
        total_results: results.size,
        results: results.map { |r| format_result(r) }
      }
    end

    private

    def format_result(result)
      { id: result.id, name: result.name }
    end
  end
end
  1. The tool will be automatically registered on server restart.

Related Documentation

Changelog

v1.0.0 (December 2024)

  • Initial MCP server implementation
  • 6 content discovery tools
  • Token-based authentication
  • Employee management UI for tokens
  • Auto-revocation on account disable