Rhema MCP Daemon Usage Guide
Table of Contents
Installation
Prerequisites
-
Rust 1.70+ (for building from source)
-
Git repository with Rhema context files
-
Optional: Redis for distributed caching
Building from Source
# Clone the repository
git clone https://github.com/fugue-ai/rhema.git
cd rhema
# Build the project
cargo build --release
# Install globally
cargo install --path .Using Pre-built Binaries
Download the latest release from the GitHub releases page .
Quick Start
1. Initialize Rhema Context
First, ensure you have a Rhema-enabled repository:
# Initialize Rhema in your repository
rhema init --scope-type service --scope-name my-service
# This creates the basic structure:
# .rhema/
# ├── scopes/
# │ └── my-service/
# │ ├── scope.yaml
# │ ├── knowledge.yaml
# │ ├── todos.yaml
# │ ├── decisions.yaml
# │ └── patterns.yaml
2. Start the Daemon
# Start with default configuration
rhema daemon start
# Start with custom configuration
rhema daemon start --host 0.0.0.0 --port 8080 --auth --api-key "your-secret-key"3. Verify the Daemon
# Check health
curl http://localhost:8080/health
# List scopes
curl http://localhost:8080/scopes
# Execute a query
curl -X POST http://localhost:8080/query \
-H "Content-Type: application/json" \
-d '{"query": "SELECT * FROM scopes"}'Deployment Options
Local Development
# Simple local development setup
rhema daemon start --host 127.0.0.1 --port 8080
# With file watching for development
rhema daemon start --watch --watch-dirs ".rhema,config"Docker Deployment
Create a Dockerfile:
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/rhema /usr/local/bin/rhema
WORKDIR /app
EXPOSE 8080
CMD ["rhema", "daemon", "start", "--host", "0.0.0.0", "--port", "8080"]Create docker-compose.yml:
version: '3.8'
services:
rhema-mcp:
build: .
ports:
- "8080:8080"
volumes:
- .:/app
- /tmp/rhema-mcp.sock:/tmp/rhema-mcp.sock
environment:
- Rhema_API_KEY=your-secret-key
- Rhema_REDIS_URL=redis://redis:6379
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"Run with Docker Compose:
docker-compose up -dKubernetes Deployment
Create k8s-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: rhema-mcp
labels:
app: rhema-mcp
spec:
replicas: 3
selector:
matchLabels:
app: rhema-mcp
template:
metadata:
labels:
app: rhema-mcp
spec:
containers:
- name: rhema-mcp
image: rhema-mcp:latest
ports:
- containerPort: 8080
env:
- name: Rhema_API_KEY
valueFrom:
secretKeyRef:
name: rhema-secrets
key: api-key
- name: Rhema_REDIS_URL
value: "redis://rhema-redis:6379"
volumeMounts:
- name: rhema-config
mountPath: /app/.rhema
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: rhema-config
configMap:
name: rhema-config
---
apiVersion: v1
kind: Service
metadata:
name: rhema-mcp-service
spec:
selector:
app: rhema-mcp
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancerDeploy to Kubernetes:
kubectl apply -f k8s-deployment.yamlSystemd Service
Create /etc/systemd/system/rhema-mcp.service:
[Unit]
Description=Rhema MCP Daemon
After=network.target
[Service]
Type=simple
User=rhema
Group=rhema
WorkingDirectory=/opt/rhema
ExecStart=/usr/local/bin/rhema daemon start --config /etc/rhema/rhema-mcp.yaml
Restart=always
RestartSec=10
Environment=Rhema_API_KEY=your-secret-key
[Install]
WantedBy=multi-user.targetEnable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable rhema-mcp
sudo systemctl start rhema-mcp
sudo systemctl status rhema-mcpIntegration Examples
Python Integration
import requests
import json
from typing import Dict, Any, Optional
class RhemaClient:
def __init__(self, base_url: str = "http://localhost:8080", api_key: Optional[str] = None):
self.base_url = base_url
self.session = requests.Session()
if api_key:
self.session.headers.update({'Authorization': f'Bearer {api_key}'})
self.session.headers.update({'Content-Type': 'application/json'})
def health(self) -> Dict[str, Any]:
"""Get daemon health status."""
response = self.session.get(f'{self.base_url}/health')
response.raise_for_status()
return response.json()
def list_scopes(self) -> list:
"""List all available scopes."""
response = self.session.get(f'{self.base_url}/scopes')
response.raise_for_status()
return response.json()
def get_scope(self, scope_id: str) -> Optional[Dict[str, Any]]:
"""Get details for a specific scope."""
response = self.session.get(f'{self.base_url}/scopes/\{scope_id\}')
if response.status_code == 404:
return None
response.raise_for_status()
return response.json()
def execute_query(self, query: str, parameters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Execute a CQL query."""
data = {'query': query}
if parameters:
data['parameters'] = parameters
response = self.session.post(f'{self.base_url}/query', json=data)
response.raise_for_status()
return response.json()
def get_knowledge(self, scope_id: str) -> Optional[Dict[str, Any]]:
"""Get knowledge base for a scope."""
response = self.session.get(f'{self.base_url}/scopes/\{scope_id\}/knowledge')
if response.status_code == 404:
return None
response.raise_for_status()
return response.json()
# Usage example
client = RhemaClient('http://localhost:8080', 'your-api-key')
# Check health
health = client.health()
print(f"Daemon status: {health['status']}")
# List scopes
scopes = client.list_scopes()
for scope in scopes:
print(f"Scope: {scope['path']}")
# Execute query
result = client.execute_query("SELECT * FROM scopes WHERE type = 'service'")
print(f"Found {len(result['results'])} service scopes")
# Get knowledge
knowledge = client.get_knowledge('my-service')
if knowledge:
print(f"Knowledge base: {knowledge['title']}")JavaScript/Node.js Integration
class RhemaClient {
constructor(baseUrl = 'http://localhost:8080', apiKey = null) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async request(endpoint, options = {}) {
const headers = {
'Content-Type': 'application/json',
...options.headers
};
if (this.apiKey) {
headers['Authorization'] = `Bearer ${this.apiKey}`;
}
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
async health() {
return this.request('/health');
}
async listScopes() {
return this.request('/scopes');
}
async getScope(scopeId) {
return this.request(`/scopes/${scopeId}`);
}
async executeQuery(query, parameters = {}) {
return this.request('/query', {
method: 'POST',
body: JSON.stringify({ query, parameters })
});
}
async getKnowledge(scopeId) {
return this.request(`/scopes/${scopeId}/knowledge`);
}
async getTodos(scopeId) {
return this.request(`/scopes/${scopeId}/todos`);
}
async getDecisions(scopeId) {
return this.request(`/scopes/${scopeId}/decisions`);
}
}
// Usage example
const client = new RhemaClient('http://localhost:8080', 'your-api-key');
async function main() {
try {
// Check health
const health = await client.health();
console.log(`Daemon status: ${health.status}`);
// List scopes
const scopes = await client.listScopes();
console.log('Available scopes:', scopes.map(s => s.path));
// Execute query
const result = await client.executeQuery(
"SELECT * FROM scopes WHERE type = 'service'"
);
console.log(`Found ${result.results.length} service scopes`);
// Get knowledge for a scope
const knowledge = await client.getKnowledge('my-service');
if (knowledge) {
console.log(`Knowledge base: ${knowledge.title}`);
}
} catch (error) {
console.error('Error:', error.message);
}
}
main();WebSocket Integration
class RhemaWebSocketClient {
constructor(url = 'ws://localhost:8081/ws', apiKey = null) {
this.url = url;
this.apiKey = apiKey;
this.ws = null;
this.messageId = 0;
this.pendingRequests = new Map();
}
connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket connected');
resolve();
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
reject(error);
};
this.ws.onclose = () => {
console.log('WebSocket disconnected');
};
});
}
handleMessage(message) {
if (message.id && this.pendingRequests.has(message.id)) {
const { resolve, reject } = this.pendingRequests.get(message.id);
this.pendingRequests.delete(message.id);
if (message.error) {
reject(new Error(message.error.message));
} else {
resolve(message.result);
}
} else if (message.method === 'resources/changed') {
// Handle notifications
this.onResourceChanged?.(message.params);
}
}
async sendRequest(method, params = {}) {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket not connected');
}
const id = ++this.messageId;
const request = {
jsonrpc: '2.0',
id,
method,
params
};
return new Promise((resolve, reject) => {
this.pendingRequests.set(id, { resolve, reject });
this.ws.send(JSON.stringify(request));
});
}
async subscribe(uri) {
return this.sendRequest('resources/subscribe', { uri });
}
async unsubscribe(subscriptionId) {
return this.sendRequest('resources/unsubscribe', { subscription_id: subscriptionId });
}
async health() {
return this.sendRequest('system/health');
}
async executeQuery(query, parameters = {}) {
return this.sendRequest('query/execute', { query, parameters });
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// Usage example
const wsClient = new RhemaWebSocketClient('ws://localhost:8081/ws', 'your-api-key');
// Handle resource changes
wsClient.onResourceChanged = (params) => {
console.log(`Resource changed: (params)`);
};
async function main() {
try {
await wsClient.connect();
// Subscribe to resource changes
const subscription = await wsClient.subscribe('rhema://scopes/my-service');
console.log('Subscribed:', subscription.subscription_id);
// Execute query
const result = await wsClient.executeQuery("SELECT * FROM scopes");
console.log('Query result:', result);
// Keep connection alive
setInterval(async () => {
const health = await wsClient.health();
console.log('Health:', health.status);
}, 30000);
} catch (error) {
console.error('Error:', error.message);
}
}
main();Unix Socket Integration
#!/bin/bash
# Unix socket client example
SOCKET_PATH="/tmp/rhema-mcp.sock"
# Function to send JSON-RPC request
send_request() {
local method="$1"
local params="$2"
local id="$3"
local request=$(cat <<EOF
{"jsonrpc": "2.0", "id": $id, "method": "$method", "params": $params}
EOF
)
echo "$request" | nc -U "$SOCKET_PATH"
}
# Health check
echo "Checking health..."
send_request "system/health" "{}" 1
# List scopes
echo "Listing scopes..."
send_request "resources/list" '{"uri": "rhema://scopes"}' 2
# Execute query
echo "Executing query..."
send_request "query/execute" '{"query": "SELECT * FROM scopes"}' 3Monitoring and Maintenance
Health Monitoring
# Basic health check
curl -f http://localhost:8080/health || exit 1
# Detailed health information
curl http://localhost:8080/health | jq '.'
# Check specific metrics
curl http://localhost:8080/health | jq '.memory_usage'
curl http://localhost:8080/health | jq '.cache_hit_rate'Log Monitoring
# Follow logs
tail -f /var/log/rhema-mcp.log
# Search for errors
grep ERROR /var/log/rhema-mcp.log
# Monitor connection count
watch -n 5 'curl -s http://localhost:8080/health | jq ".connections"'Performance Monitoring
# Check memory usage
curl http://localhost:8080/health | jq '.memory_usage'
# Monitor cache performance
curl http://localhost:8080/stats | jq '.cache_stats'
# Check query performance
curl http://localhost:8080/stats | jq '.query_stats'Automated Monitoring Script
#!/bin/bash
# Monitoring script for Rhema MCP Daemon
DAEMON_URL="http://localhost:8080"
ALERT_EMAIL="admin@example.com"
# Check if daemon is running
check_health() {
local response=$(curl -s -w "%{http_code}" "$DAEMON_URL/health" -o /tmp/health.json)
local status_code="${response: -3}"
if [ "$status_code" != "200" ]; then
echo "ERROR: Daemon health check failed with status $status_code"
return 1
fi
local status=$(jq -r '.status' /tmp/health.json)
if [ "$status" != "healthy" ]; then
echo "ERROR: Daemon status is $status"
return 1
fi
echo "OK: Daemon is healthy"
return 0
}
# Check memory usage
check_memory() {
local used=$(jq -r '.memory_usage.used' /tmp/health.json)
local total=$(jq -r '.memory_usage.total' /tmp/health.json)
local percentage=$((used * 100 / total))
if [ "$percentage" -gt 80 ]; then
echo "WARNING: High memory usage: ${percentage}%"
return 1
fi
echo "OK: Memory usage: ${percentage}%"
return 0
}
# Check cache performance
check_cache() {
local hit_rate=$(jq -r '.cache_hit_rate' /tmp/health.json)
local percentage=$(echo "$hit_rate * 100" | bc -l | cut -d. -f1)
if [ "$percentage" -lt 80 ]; then
echo "WARNING: Low cache hit rate: ${percentage}%"
return 1
fi
echo "OK: Cache hit rate: ${percentage}%"
return 0
}
# Main monitoring function
main() {
local errors=0
if ! check_health; then
((errors++))
fi
if ! check_memory; then
((errors++))
fi
if ! check_cache; then
((errors++))
fi
if [ "$errors" -gt 0 ]; then
echo "ALERT: $errors issues detected with Rhema MCP Daemon"
# Send alert email
echo "Rhema MCP Daemon monitoring alert" | mail -s "Daemon Alert" "$ALERT_EMAIL"
exit 1
fi
echo "All checks passed"
}
mainTroubleshooting
Common Issues
Daemon Won’t Start
# Check if port is already in use
sudo lsof -i :8080
# Check if Unix socket file exists
ls -la /tmp/rhema-mcp.sock
# Check permissions
sudo chown rhema:rhema /tmp/rhema-mcp.sock
sudo chmod 660 /tmp/rhema-mcp.sockAuthentication Errors
# Check API key configuration
grep api_key /etc/rhema/rhema-mcp.yaml
# Test with curl
curl -H "Authorization: Bearer your-api-key" http://localhost:8080/healthRedis Connection Issues
# Test Redis connectivity
redis-cli ping
# Check Redis URL
echo $Rhema_REDIS_URL
# Test connection with redis-cli
redis-cli -u "redis://localhost:6379" pingFile Watching Issues
# Check inotify limits (Linux)
cat /proc/sys/fs/inotify/max_user_watches
# Increase limits
echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches
# Check file permissions
ls -la .rhema/Debug Mode
Enable debug logging for troubleshooting:
# Start with debug logging
rhema daemon start --log-level debug
# Or modify config file
sed -i 's/level: "info"/level: "debug"/' rhema-mcp.yamlLog Analysis
# Find errors
grep -i error /var/log/rhema-mcp.log
# Find warnings
grep -i warn /var/log/rhema-mcp.log
# Monitor real-time
tail -f /var/log/rhema-mcp.log | grep -E "(ERROR|WARN)"
# Analyze performance
grep "execution_time" /var/log/rhema-mcp.log | awk '{sum+=$NF; count++} END {print "Average:", sum/count}'Best Practices
Security
-
Always use authentication in production
-
Use strong, randomly generated API keys
-
Restrict CORS origins to your application domains
-
Use HTTPS in production environments
-
Regularly rotate API keys and JWT secrets
-
Monitor access logs for suspicious activity
Performance
-
Use Redis for distributed caching
-
Configure appropriate cache TTL values
-
Monitor memory usage and cache hit rates
-
Use connection pooling for database connections
-
Implement rate limiting for API endpoints
-
Use WebSocket for real-time updates
Reliability
-
Implement health checks and monitoring
-
Use systemd or similar for process management
-
Set up log rotation and archival
-
Implement graceful shutdown handling
-
Use load balancers for high availability
-
Regularly backup configuration and data
Development
-
Use version control for configuration files
-
Implement automated testing for API endpoints
-
Use environment-specific configurations
-
Document API changes and breaking changes
-
Implement proper error handling in clients
-
Use structured logging for better debugging
Deployment
-
Use containerization for consistent deployments
-
Implement blue-green deployments for zero downtime
-
Use configuration management tools
-
Set up automated monitoring and alerting
-
Implement proper backup and recovery procedures
-
Use infrastructure as code for deployment automation