Mindset v3 / Deploy in your app / MCP Server Integration
View as MarkdownTools MCP Server Integration Guide
Guide on implementing and registering your Tools MCP server with the Mindset AI platform.
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:
- You implement an MCP server that validates API keys and provides tools
- You register your server URL and API key with Mindset AI
- Mindset AI sends requests to your server with the API key and user context
- Your server validates both and processes the requests
Critical Decision: Use an MCP Library
Recommended: Use FastMCP or the Official MCP SDK
We strongly recommend using an established MCP library:
Python (Recommended):
- FastMCP v2.10+ - Pythonic, production-ready, handles all protocol complexity
- Official MCP Python SDK
TypeScript/JavaScript:
Why This Matters: Common Integration Issues
Manual JSON-RPC/MCP implementations can encounter protocol compliance issues:
- Tool responses are missing required
result/errorfield - Notification handling is incorrect → Protocol violations
- Missing MCP-specific fields → Incomplete implementation
- Inconsistent error codes → Poor error handling
These issues arise from the complexity of correctly implementing all JSON-RPC 2.0 and MCP specification requirements, including edge cases like notification handling and ID type preservation.
With FastMCP/SDK: Protocol compliance is handled automatically, eliminating these common pitfalls.
Manual Implementation Requirements
If you choose to implement without a library (not recommended), you must thoroughly familiarize yourself with both the JSON-RPC 2.0 and MCP specifications, and ensure your server is fully compliant with both standards:
- JSON-RPC 2.0 Specification
- Required response fields (
resultXORerror) - Notification handling (requests without
id) - Error codes and structures
- ID type preservation
- Required response fields (
- Model Context Protocol Specification
- Tool discovery (
tools/list) - Tool execution (
tools/call) - Response content structures
- Optional MCP fields (
isError,structuredContent)
- Tool discovery (
- Validation Requirements
- You must pass all tests in the "Testing Your Implementation" section below
- You must handle all edge cases that libraries handle automatically
- You must stay current with spec updates
Important: Manual implementation requires careful attention to protocol details. Missing or incorrectly formatted fields will cause agent integration to fail. We strongly recommend using an MCP library unless you have specific requirements that prevent it.
When Manual Implementation Might Make Sense
Consider manual implementation only if:
- You have an existing JSON-RPC infrastructure to integrate with
- You need specific protocol customizations that libraries don't support
- You have deep JSON-RPC/MCP expertise on your team
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
Registration vs Runtime Authentication
Registration endpoints must only validate API keys
Your MCP server will be called in two different contexts:
1. Registration Context (No Agent Session)
When customers register your MCP server with Mindset AI, the platform calls these endpoints to validate the server:
initialize- Establish connection and capabilitiesnotifications/initialized- Confirm initializationtools/list- Discover available tools
During registration, no agent session exists yet. This means that the following headers cannot be used for authentication purposes during registration:
x-session-tagsx-user-id
Your server should not require session-specific validation (like checking for keys in x-session-tags) for these endpoints. If you do, registration will fail because no session data exists during registration. These endpoints should only validate the API key in the Authorization header.
2. Runtime Context (if the AgentSessions API is used to create Agent sessions)
When agents call your tools during conversations, the platform calls:
tools/call- Execute a specific tool
During runtime, if you are using the AgentSessions API, an agent session exists. This means:
x-session-tagswill contain session-specific tagsx-user-idwill identify the current userx-app-uidwill identify the application
Your server can validate session-specific data (permissions, context keys in x-session-tags, etc.) for tools/call requests.
User Context and Permissions
Mindset AI includes the following lowercase headers in every request:
Headers Provided by Mindset AI
- x-user-id: Identifies the end user on whose behalf the action is being taken
- x-app-uid: The application identifier
- x-session-tags: Contains the exact JSON array of tags provided when creating the agent session via the AgentSessions API. These are arbitrary strings that you define for your own filtering/segmentation needs (e.g., ["department:sales", "SALES", "region:north", "Q4-2024", "premium_access"]). If you don't need filtering, you can provide an empty array or omit tags when creating the session.
- x-human-uid: Internal Mindset AI identifier for the user (optional, for informational/logging purposes only)
For Embed customers: The x-user-id value will correspond with the user identifier sent by your system to ours to register the user initially. The x-session-tags will contain the exact tags array you provided when creating the agent session, allowing you to apply custom filtering, access control, or business logic based on these tags.
Important Distinction: Current User vs. Target Users
The x-user-id header identifies the CURRENT user - the person making the request. However, your tools may need to operate on OTHER users:
- Regular tools: Act on behalf of the current user (e.g., "get my settings", "update my profile")
- Use x-user-id header to identify who is making the request
- Never accept user_id as a parameter for these operations
- Admin tools: Allow the current user to manage other users (e.g., "create user John", "add user Alice to group")
- Use x-user-id header to identify the ADMIN performing the action
- Accept user_id/email as parameters to identify the TARGET users being managed
- Verify the current user has admin permissions before allowing these operations
The recommended approach for handling user context:
- Validate the user ID against your user database
- For admin tools, verify the x-user-id has appropriate permissions
- Use x-session-tags for additional filtering or access control
- Map to your internal user ID for operations
- Apply user-specific permissions and access controls
- Log actions for audit purposes
This separation between system-level authentication (via API key) and user-level authorization (via header) provides flexibility in how you manage user permissions while maintaining security.
Implementation Requirements
Your MCP server should:
- Implement JSON-RPC 2.0 and MCP protocols: Your server must be fully compliant with the JSON-RPC 2.0 specification and Model Context Protocol specification. We recommend using FastMCP or an official MCP SDK, which handle all protocol requirements automatically.
- Validate API Keys: Check the Bearer token in the Authorization header against your configured API key
- Enforce User Permissions: Use the
x-user-idheader to identify users and apply appropriate access controls - Return Proper Responses: Follow JSON-RPC 2.0 response structure (every response must have either
resultorerrorfield) - Follow Tool Naming Convention: Tool names must match the pattern
^[a-zA-Z0-9_-]+(only letters, numbers, underscores, and hyphens). Tool names with dots, spaces, or special characters will cause errors with OpenAI and other LLM providers. For example, usesearch_for_userinstead ofsearch.for.user.
You may implement your server using any language or framework that can:
- Validate Bearer tokens (API keys)
- Implement HTTP endpoints according to the JSON-RPC 2.0 and MCP specifications
- Parse the
x-user-idand optionally thex-human-uidheaders from requests - Return JSON-RPC 2.0 compliant responses
Example Python Implementation with FastMCP
FastMCP is the recommended framework for building MCP servers in Python. It dramatically simplifies development by handling all the protocol complexities while providing production-ready features like authentication, middleware support, and deployment tools. Learn more at gofastmcp.com.
1. Setting Up 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.
Tool Description Length: Tool descriptions should be at least 5 words. Descriptions shorter than 5 words may not provide enough context for LLMs to understand when and how to use the tool effectively. For example, "Search for users" (3 words) is too short, but "Search for users by name or email" (7 words) provides clear context.
Parameter names should be self-explanatory - the LLM uses these names to understand what data to provide. For example, use search_query not q, include_completed not 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
Always include a status field - Use "success", "error", or "partial" to help the LLM understand the outcome.
Use message for human-readable outcomes - Brief, actionable information the LLM can relay to the user. Avoid including IDs or technical identifiers in messages.
Use data for structured results - Machine-readable data the LLM needs to process further. This is where identifiers should live when needed.
Include error details in the data field for errors - When returning an error status, include API/system error details in the data field so the agent has feedback and can adapt subsequent attempts. For example, include rate limit information, validation errors, or specific fields that failed.
Be cautious with internal identifiers - Avoid exposing internal database keys or system internals that could confuse users or create security risks. However, you may need to include public-facing identifiers (like course IDs, document IDs, or order numbers) in the data field when they're necessary for subsequent operations or user reference. Keep IDs out of the message field - use natural language there instead.
Important: Tool Return Value vs Wire Format
If using FastMCP/SDK: Your tool functions return plain data structures (objects/dictionaries). The library automatically wraps these in the MCP-required content field before sending on the wire. You don't need to handle this wrapping yourself.
If implementing manually: You must wrap your response in the MCP content structure yourself. The examples below show what your tool function returns when using an MCP library. For manual implementations, see the "Expected Response" section in "Testing Your Implementation" for the complete wire format with the content field.
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
# Include error details in data field so agent can adapt
return {
"status": "error",
"message": f"Invalid filter parameters: {str(e)}",
"data": {
"error_type": "validation_error",
"error_details": str(e)
}
}
except PermissionError:
# Clear message about access restrictions
# Include what permission was needed
return {
"status": "error",
"message": "Unable to access course enrollment data",
"data": {
"error_type": "permission_denied",
"required_permission": "read_courses"
}
}
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": {
"error_type": "internal_error",
"retry_after": 60 # Suggest retry delay in seconds
}
}
# 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
These tests verify JSON-RPC 2.0 and MCP protocol compliance. All servers (library-based or manual) must pass these tests before registration with Mindset.
- If using FastMCP/SDK: These tests verify your tool logic works correctly (protocol compliance is handled automatically)
- If implementing manually: These tests are essential - they catch protocol violations that break agent integration
The MCP Handshake Test Sequence
When connecting, the Mindset agent performs this exact sequence:
- Initialize - Establish connection and exchange capabilities
- Notification - Confirm initialization complete (no response expected)
- List Tools - Get available tools
- Call Tool - Execute a tool
All four must succeed or integration will fail.
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"}2. 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):
If using FastMCP (SSE format with automatic MCP wrapping):
data: {"jsonrpc":"2.0","result":{"content":[{"type":"text","text":"{\"status\":\"success\",\"message\":\"Found 3 enrolled courses\",\"data\":{\"courses\":[{\"course_id\":\"CS101\",\"title\":\"Introduction to Python\",\"progress\":75,\"status\":\"active\"}],\"total\":3}}"}],"isError":false},"id":"2"}Note: Your tool function returns {"status":"success","message":"...","data":{...}} and FastMCP automatically wraps it in the MCP-required content field. Manual implementations must handle this wrapping explicitly.
3. 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"}4. 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")
breakProduction 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