Skip to main content

Why Integrate Your MCP Server?

Integrating your MCP server with Mindset AI enables powerful AI-driven automation for your organization:
  • Extend AI Capabilities: Your custom tools and data sources become accessible to AI agents, enabling them to perform domain-specific tasks unique to your business.
  • Maintain Control: You retain full control over your data and business logic while leveraging Mindset AI’s Agents.
  • User-Specific Actions: AI agents can perform actions on behalf of specific users in your system, maintaining proper access controls and audit trails.
  • Seamless Integration: Once registered, your tools appear natively within the Mindset AI platform, requiring no additional integration work.

Overview

This guide describes how to implement and register your Model Context Protocol (MCP) server with the Mindset AI platform. Once integrated, your MCP server’s tools can be made available to your AI agents running on our platform.

Architecture

The integration is straightforward:
  1. You implement an MCP server that validates API keys and provides tools.
  2. You register your server URL and API key with Mindset AI.
  3. Mindset AI sends requests to your server with the API key and user context.
  4. Your server validates both and processes the requests.

Authentication

Authentication uses simple API key validation:
  • You generate a secure API key for your MCP server (this is a secret you create and control).
  • You provide this key to Mindset AI when registering your MCP server.
  • Mindset AI stores this securely and includes it in the Authorization header as a Bearer token with every request.
  • Your MCP server validates the API key before processing requests.
Important: The API key is generated by you, not by Mindset AI. Use a cryptographically secure random string (e.g., openssl rand -hex 32 or equivalent). You’ll provide this key to Mindset AI during the MCP server registration process.

User Context and Permissions

Mindset AI includes the following lowercase headers in every request:
HeaderDescription
x-user-idIdentifies the end user on whose behalf the action is being taken.
x-session-tagsJSON array of tags provided when creating the agent session via the AgentSessions API (e.g., ["department:sales","region:north"]). Useful for filtering and segmentation.
x-human-uidInternal Mindset AI identifier for the user (optional, informational only).
For Embed customers:
  • The x-user-id corresponds with the user identifier sent by your system when registering the user.
  • The x-session-tags will be the exact tags you provided when creating the agent session.

Important Distinction: Current User vs. Target Users

  • Regular Tools: Act on behalf of the current user.
    • Use x-user-id header.
    • Never accept user_id as a parameter.
  • Admin Tools: Allow the current user to manage other users.
    • Use x-user-id to identify the admin.
    • Accept user_id/email as parameters to identify target users.
    • Verify admin permissions.
  • Validate x-user-id against your user database.
  • For admin tools, verify permissions.
  • Use x-session-tags for filtering or business logic.
  • Map to your internal user ID.
  • Apply user-specific permissions.
  • Log actions for audits.
This separation between system-level authentication (API key) and user-level authorization (headers) provides both security and flexibility.

Implementation Requirements

Your MCP server should:
  1. Implement the Model Context Protocol: Follow the MCP specification for tool discovery and execution
  2. Validate API Keys: Check the Bearer token in the Authorization header against your configured API key
  3. Enforce User Permissions: Use the x-user-id header to identify users and apply appropriate access controls
  4. Return Proper Responses: Provide JSON-formatted responses according to the MCP protocol
Any language/framework is supported as long as it can :
  • Validate Bearer tokens (API keys)
  • Implement HTTP endpoints according to the MCP specification
  • Parse the x-user-id and optionally the x-human-uid from requests
  • Return JSON responses

Example Python Implementation with FastMCP

FastMCP simplifies MCP server development with authentication, middleware, and deployment support. Learn more at gofastmcp.com.

1. API Key Configuration

import os
from fastmcp import FastMCP

# Get API key from environment variable
MCP_API_KEY = os.environ.get('MCP_API_KEY')
if not MCP_API_KEY:
    raise ValueError("MCP_API_KEY environment variable is required")

VALID_API_KEYS = {MCP_API_KEY: "production_client"}

2. Minimal Authentication Middleware

from fastmcp.server.middleware import Middleware, MiddlewareContext
from fastmcp.server.dependencies import get_http_headers

class AuthenticationMiddleware(Middleware):
    """Simple API key authentication middleware."""

    async def on_message(self, context: MiddlewareContext, call_next):
        headers = get_http_headers()

		# Extract API key from Bearer token
        auth_header = headers.get('authorization', '').lower()
        api_key = None
        if auth_header.startswith('bearer '):
            api_key = headers.get('authorization', '')[7:]
		# Validate API key
        if api_key in VALID_API_KEYS:
            return await call_next(context)
        else:
            raise ValueError("Authentication failed: Invalid or missing API key")

3. Initialize MCP Server with Middleware

# Initialize MCP server
mcp = FastMCP("your_mcp_server_name")
# Enable authentication middleware
mcp.add_middleware(AuthenticationMiddleware())

4. Implement Your Tools

Tool Docstrings and Logical Variable Names are Critical

The docstring is what the LLM uses to decide when to call your tool. Keep it concise but descriptive — explain what the tool does and when it should be used.
  • Parameter names should be self-explanatory.
    • search_query instead of q
    • include_completed instead of inc_comp
  • Clear names reduce errors and improve the LLM’s ability to call your tool correctly.
  • Always use type hints for parameters, as FastMCP uses these to validate inputs.

Return Object Guidelines

The return object should always follow a consistent, structured format so the LLM can reliably interpret results.
  • Always include a status field
    • "success", "error", or "partial"
  • Use message for human-readable outcomes
    • Keep it short, actionable, and natural language.
    • Do not expose IDs or technical details here.
  • Use data for structured, machine-readable results
    • Identifiers (course IDs, document IDs, order numbers, etc.) should go here when necessary.
    • Never expose internal database keys or system internals that may confuse users or create security risks.
Keep IDs out of the message field. Only include them inside the data field when they are required for subsequent operations or user reference. In summary:
  • Write clear, descriptive docstrings.
  • Use logical parameter names with type hints.
  • Return a structured JSON with status, message, and data.
  • Avoid exposing internal details in user-facing fields.
from typing import List, Optional


# Example 1: Information Retrieval Tool
# This tool retrieves data scoped to the user without modifying anything


@mcp.tool()
async def get_user_enrolled_courses(
    include_completed: Optional[bool] = True,
    category: Optional[str] = None
) -> dict:
    """
    Get courses the current user is enrolled in.
    Returns list of active and optionally completed courses.
    """
    # Extract current user from headers (not a parameter!)
    headers = get_http_headers()
    user_id = headers.get('x-user-id')  # This is the user making the request


    try:
        # Fetch courses for this user (internal implementation)
        courses = fetch_user_courses(user_id, include_completed, category)  # Your implementation


        if not courses:
            return {
                "status": "success",
                "message": "No courses found",
                "data": {
                    "courses": [],
                    "total": 0
                }
            }


        # Build response without exposing internal user IDs or database keys
        course_list = []
        for course in courses:
            course_list.append({
                "course_id": course["public_id"],  # Use public identifiers
                "title": course["title"],
                "progress": course["progress_percentage"],
                "status": course["status"],
                "last_accessed": course["last_accessed"]
            })


        return {
            "status": "success",
            "message": f"Found {len(course_list)} enrolled courses",
            "data": {
                "courses": course_list,
                "total": len(course_list),
                "filters_applied": {
                    "include_completed": include_completed,
                    "category": category
                }
            }
        }


    except ValueError as e:
        # User-friendly error for invalid inputs
        return {
            "status": "error",
            "message": f"Invalid filter parameters: {str(e)}",
            "data": None
        }
    except PermissionError:
        # Clear message about access restrictions
        return {
            "status": "error",
            "message": "Unable to access course enrollment data",
            "data": None
        }
    except Exception as e:
        # Generic error - log internally but don't expose details
        print(f"Course fetch error for user {user_id}: {e}")  # Internal logging only
        return {
            "status": "error",
            "message": "Unable to retrieve courses. Please try again.",
            "data": None
        }


# Example 2: Admin Tool - Managing Other Users
# This tool allows an admin to create and manage OTHER users


@mcp.tool()
async def create_user(
    user_name: str,
    email: str
) -> dict:
    """
    Admin tool: Create a new user account.
    Requires admin permissions.
    """
    headers = get_http_headers()
    admin_id = headers.get('x-user-id')  # The ADMIN performing this action


    # Verify admin permissions
    if not is_user_admin(admin_id):
        return {
            "status": "error",
            "message": "Admin permissions required"
        }


    # Create the new user (email is the TARGET user, not the admin)
    result = create_new_user(user_name, email)


    return {
        "status": "success",
        "message": f"User '{user_name}' created successfully",
        "data": {
            "user_id": result["id"],  # Return ID for subsequent operations
            "name": user_name,
            "email": email
        }
    }


# Example 3: Admin Tool - Modifying Another User's Settings


@mcp.tool()
async def update_user_settings(
    user_id: str,  # TARGET user whose settings to update
    setting_name: str,
    value: str
) -> dict:
    """
    Change settings (theme, language, notifications) for a specific user.
    Use when modifying settings for a user other than the current user.
    """
    headers = get_http_headers()
    admin_id = headers.get('x-user-id')  # The ADMIN performing this action


    # Verify admin permissions
    if not is_user_admin(admin_id):
        return {
            "status": "error",
            "message": "Admin permissions required"
        }


    valid_settings = ["theme", "language", "notifications"]


    if setting_name not in valid_settings:
        return {
            "status": "error",
            "message": f"Unknown setting '{setting_name}'. Valid options: {', '.join(valid_settings)}"
        }


    # Update the TARGET user's settings (not the admin's)
    # Note: The user_id parameter identifies a different user than the one in x-user-id header
    # This user_id would have been identified through prior conversation or tool calls
    update_user_setting(user_id, setting_name, value)  # Your implementation


    return {
        "status": "success",
        "message": f"Settings updated for user {user_id}"
    }

5. Run The Server

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    print(f"Starting MCP server on port {port}")


    mcp.run(
        transport="streamable-http",
        host="0.0.0.0",
        port=port,
        path="/mcp",
        stateless_http=True  # Stateless mode for better scalability
    )

Testing Your Implementation

  1. Test Tool Discovery
# List available tools
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer your-api-key" \
  -H "x-user-id: test_user@example.com" \
  -H "x-session-tags: [\"department:sales\",\"region:north\"]" \
  -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":"1"}'
Expected Response:
data: {"jsonrpc":"2.0","result":{"tools":[{"name":"get_user_enrolled_courses","description":"Get courses the current user is enrolled in. Returns list of active and optionally completed courses.","inputSchema":{"type":"object","properties":{"include_completed":{"type":"boolean","default":true},"category":{"type":"string"}}}}]},"id":"1"}
  1. Test Tool Execution
# Call a tool
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer your-api-key" \
  -H "x-user-id: test_user@example.com" \
  -H "x-human-uid: human_123456789" \
  -H "x-session-tags: [\"department:sales\",\"region:north\"]" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_user_enrolled_courses","arguments":{"include_completed":true}},"id":"2"}'
Expected Response (your actual data will vary):
data: {"jsonrpc":"2.0","result":{"status":"success","message":"Found 3 enrolled courses","data":{"courses":[{"course_id":"CS101","title":"Introduction to Python","progress":75,"status":"active"}],"total":3}},"id":"2"}
  1. Test Security Failures
# Test with no API key - should fail
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "X-User-Id: test_user@example.com" \
  -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":"1"}'


# Test with invalid API key - should fail
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer invalid-key-123" \
  -H "X-User-Id: test_user@example.com" \
  -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":"2"}'
Expected Response (authentication error):
data: {"jsonrpc":"2.0","error":{"code":-32603,"message":"Authentication failed: Invalid or missing API key"},"id":"2"}
  1. Minimal Python Test Script
#!/usr/bin/env python3
import requests
import json


base_url = "http://localhost:5000/mcp"
headers = {
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
    "Authorization": "Bearer your-api-key",
    "x-user-id": "user@example.com"
    # x-human-uid is optional - included by Mindset but not required for validation
}


# Test tools/list endpoint
response = requests.post(
    base_url,
    headers=headers,
    json={"jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": "1"},
    stream=True
)


# Parse SSE response
for line in response.iter_lines():
    if line and line.decode().startswith("data: "):
        data = json.loads(line.decode()[6:])
        if "result" in data:
            print(f"✅ Found {len(data['result']['tools'])} tools")
            break

Production Considerations

Deployment

  • Your MCP server requires HTTPS in production. Deploy using your preferred infrastructure - containerization, serverless platforms, or traditional hosting all work well with the MCP protocol.

Security

  • For regular user operations: Don’t accept user identification in tool parameters - rely exclusively on the x-user-id header to identify the current user
  • For admin operations: The x-user-id identifies the admin, while target users must be specified as parameters (after verifying admin permissions)
  • Consider integrating with your existing user database and RBAC systems
  • Rate limiting can help prevent abuse
  • Use your organization’s standard secret management practices
  • Plan for API key rotation policies

Operations

  • Implement logging and monitoring appropriate for your environment
  • Consider deploying behind load balancers with health checks
  • Set up alerts for repeated authentication failures
I