Sanctum Core Module: Model Context Protocol Server
SMCP is a powerful, plugin-based Model Context Protocol (MCP) server for the Sanctum Letta AI framework. This server provides seamless integration between AI clients and external tools through a robust plugin architecture. As a Sanctum Core Module, it represents the official, production-ready implementation maintained by the Sanctum team.
SMCP has been completely rewritten to use the base MCP library instead of FastMCP for full compatibility with Letta's SSE client:
- β Bidirectional SSE: Proper clientβserver communication
- β Letta Compatible: Tools appear in both test and attached modes
- β Production Ready: Robust error handling and graceful shutdown
- β Plugin System: Dynamic discovery and execution
Why the change? FastMCP's SSE implementation is unidirectional (serverβclient only), which breaks compatibility with Letta's bidirectional SSE client requirements.
- Plugin Architecture: Easy-to-write plugins for any external service or tool
- MCP Protocol Compliant: Full support for the Model Context Protocol specification
- SSE Transport: Bidirectional server-sent events for efficient communication with Letta
- JSON-RPC 2.0: Standardized request/response handling
- Auto-Discovery: Automatic plugin detection and tool registration
- Health Monitoring: Built-in health checks and status reporting
- Production Ready: Comprehensive error handling and logging
New to SMCP? Start with our comprehensive documentation:
- π Getting Started Guide - Complete setup in 5 minutes
- π Plugin Development - Build your first plugin
- π Examples - Copy-paste working code
- π¨ Troubleshooting - Solve any problem quickly
- Python 3.8 or higher
- pip package manager
Option 1: Master Sanctum Installer (Recommended) The master Sanctum installer will automatically deploy SMCP to the correct location within your Sanctum environment with all necessary configurations.
Option 2: Standalone Repository SMCP can also function as a standalone repository for development, testing, or custom deployments.
-
Create virtual environment
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install dependencies
pip install -r requirements.txt
-
Run the server
python smcp/mcp_server.py
The server will start on http://localhost:8000 by default with localhost + Docker container access for development environments.
By default, the server binds to all interfaces (0.0.0.0) to allow connections from both the local machine and Docker containers running on the same host. This is ideal for development environments where Docker containers need to communicate with the MCP server.
For localhost-only access (more restrictive):
python smcp/mcp_server.py --host 127.0.0.1To allow external connections (use with caution):
python smcp/mcp_server.py --allow-externalCustom port:
python smcp/mcp_server.py --port 9000Custom host binding:
python smcp/mcp_server.py --host 0.0.0.0 --port 8000When deployed via the master Sanctum installer, SMCP is automatically:
- Installed to the correct location within your Sanctum environment
- Configured with appropriate environment variables
- Integrated with the Sanctum plugin management system
- Set up with proper networking and security configurations
Note: The following configuration options apply to standalone deployments. When using the master installer, these are handled automatically.
| Variable | Default | Description |
|---|---|---|
MCP_PORT |
8000 |
Port for the MCP server |
MCP_PLUGINS_DIR |
smcp/plugins/ |
Directory containing plugins |
MCP_HOST |
0.0.0.0 |
Host to bind to (default: all interfaces for Docker compatibility) |
# Default: localhost + Docker containers
python smcp/mcp_server.py
# Custom port
export MCP_PORT=9000
python smcp/mcp_server.py
# Localhost-only (more restrictive)
python smcp/mcp_server.py --host 127.0.0.1
# Custom plugins directory
export MCP_PLUGINS_DIR=/path/to/custom/plugins
python smcp/mcp_server.pyNote: When deployed via the master Sanctum installer, plugins are automatically discovered and managed. The following applies to standalone deployments and custom plugin development.
For comprehensive plugin development documentation, see docs/dev/plugin-development-guide.md.
Each plugin should follow this directory structure:
plugins/
βββ your_plugin/
β βββ __init__.py
β βββ cli.py # Main plugin interface
β βββ README.md # Plugin documentation
The server supports symbolic links for flexible plugin deployment. You can centralize plugins in a designated location and use symlinks for discovery:
# Central plugin repository
/opt/sanctum/plugins/
βββ botfather/
βββ devops/
βββ custom-plugin/
# MCP server plugin directory with symlinks
smcp/plugins/
βββ botfather -> /opt/sanctum/plugins/botfather
βββ devops -> /opt/sanctum/plugins/devops
βββ custom-plugin -> /opt/sanctum/plugins/custom-plugin
- Separation of Concerns: Keep MCP server code separate from plugin implementations
- Centralized Management: Manage plugins in a designated repository
- Dynamic Loading: Add/remove plugins by creating/removing symlinks
- Version Control: Maintain plugins in separate repositories
- Deployment Flexibility: Deploy plugins independently of the MCP server
You can override the plugin directory using the MCP_PLUGINS_DIR environment variable:
# Use custom plugin directory
export MCP_PLUGINS_DIR=/opt/sanctum/plugins
python smcp/mcp_server.py-
Create plugin directory
mkdir -p smcp/plugins/my_plugin
-
Create the CLI interface (
smcp/plugins/my_plugin/cli.py)#!/usr/bin/env python3 """ My Plugin CLI A sample plugin for the Sanctum Letta MCP Server. """ import argparse import json import sys def main(): parser = argparse.ArgumentParser(description="My Plugin CLI") subparsers = parser.add_subparsers(dest="command", help="Available commands") # Add your command cmd_parser = subparsers.add_parser("my-command", help="Execute my command") cmd_parser.add_argument("--param", required=True, help="Required parameter") cmd_parser.add_argument("--optional", default="default", help="Optional parameter") args = parser.parse_args() if args.command == "my-command": result = execute_my_command(args.param, args.optional) print(json.dumps(result)) else: parser.print_help() sys.exit(1) def execute_my_command(param, optional): """Execute the main command logic.""" # Your plugin logic here return { "status": "success", "param": param, "optional": optional, "message": "Command executed successfully" } if __name__ == "__main__": main()
-
Make it executable
chmod +x smcp/plugins/my_plugin/cli.py
-
Test your plugin
python smcp/plugins/my_plugin/cli.py my-command --param "test" --optional "value"
- Command Structure: Use descriptive command names with hyphens
- Parameter Validation: Always validate required parameters
- Error Handling: Return meaningful error messages
- JSON Output: Return structured JSON for easy parsing
- Documentation: Include help text for all commands and parameters
- botfather: Telegram Bot API integration
- devops: Deployment and infrastructure management
- SSE Endpoint:
GET /sse- Server-sent events for real-time communication - Message Endpoint:
POST /messages/- JSON-RPC 2.0 message handling
- Connection: Client establishes SSE connection
- Initialization: Client sends
initializerequest - Capability Exchange: Server responds with available tools
- Tool Execution: Client can call registered tools
- Event Streaming: Server sends events via SSE
import httpx
import json
async def connect_to_mcp():
base_url = "http://localhost:8000"
# Initialize connection
init_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {"tools": {}, "resources": {}, "prompts": {}},
"clientInfo": {"name": "my-client", "version": "1.0.0"}
}
}
async with httpx.AsyncClient() as client:
response = await client.post(f"{base_url}/messages/", json=init_request)
data = response.json()
# List available tools
tools_request = {
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
response = await client.post(f"{base_url}/messages/", json=tools_request)
tools = response.json()["result"]["tools"]
# Call a tool
call_request = {
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "health",
"arguments": {}
}
}
response = await client.post(f"{base_url}/messages/", json=call_request)
result = response.json()["result"]
return result# Run all tests
python -m pytest tests/ -v
# Run specific test categories
python -m pytest tests/unit/ -v
python -m pytest tests/integration/ -v
python -m pytest tests/e2e/ -v
# Run with coverage
python -m pytest tests/ --cov=smcp --cov-report=html- Unit Tests: Core functionality and plugin system
- Integration Tests: MCP protocol and endpoint testing
- E2E Tests: Complete workflow validation
The server provides a built-in health check tool:
curl -X POST http://localhost:8000/messages/ \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"health","arguments":{}}}'Logs are written to stdout and, by default, to mcp.log with rotation. Configure behavior via environment variables:
MCP_LOG_LEVEL(defaultINFO)MCP_LOG_JSON(settruefor JSON logs)MCP_LOG_FILE(defaultmcp.log)MCP_LOG_ROTATION(size,time, ornone)MCP_DISABLE_FILE_LOG(settrueto disable file logging)
See docs/api-reference.md for the full matrix.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Install development dependencies
pip install -r requirements-dev.txt
# Run linting
flake8 smcp/ tests/
# Run type checking
mypy smcp/
# Run tests with coverage
python -m pytest tests/ --cov=smcp --cov-report=html- π Getting Started Guide - NEW USERS START HERE! Complete setup and first steps
- π Plugin Development Guide - Create and deploy custom plugins
- π Examples Guide - Practical examples and code samples for all use cases
- π Deployment Guide - Production deployment with systemd, Docker, and reverse proxy
- π§ Configuration Guide - Complete API documentation and configuration options
- ποΈ MCP Reference Architecture - High-level architecture overview
- π Letta MCP Connection Guide - Connect Letta clients to SMCP
- π¨ Troubleshooting Guide - Common issues and solutions for all problems
- π Monitoring & Health Checks - Production monitoring setup
- π Project Plan - Internal project planning and decisions
This project uses dual licensing:
- Code: Licensed under the GNU Affero General Public License v3.0 (AGPLv3) - see the LICENSE file for details.
- Documentation & Data: Licensed under the Creative Commons Attribution-ShareAlike 4.0 International License (CC-BY-SA 4.0) - see the LICENSE-DOCS file for details.
Important: AGPLv3 is a copyleft license that requires any derivative works to also be open source. If you modify and distribute this software, you must make your source code available under the same license.
- Model Context Protocol for the protocol specification
- FastMCP for the server framework
- The Sanctum team for the AI framework integration
- The Letta team for the kernel for SanctumOS
For support, questions, or contributions:
- Author: Mark Rizzn Hopkins
- Repository: https://github.com/sanctumos/smcp
- Issues: https://github.com/sanctumos/smcp/issues
Part of the Sanctum Suite - A comprehensive AI framework for modern applications.