← Back to MCP

Custom AI Backends with MCP

Building specialized AI services and backends that can be accessed as standard MCP servers, enabling clients to interact with custom ML models, domain-specific AI, and proprietary algorithms.

Overview

MCP allows you to wrap any AI or ML service as a standard protocol interface. Custom backends expose specialized capabilities through tools and resources, making them accessible to any MCP client.

Custom LLM Service

Python - Domain-Specific LLM Backend
from mcp.server import Server, Tool
from mcp.types import TextContent
from transformers import pipeline
import json

server = Server("domain-llm-backend")

# Load specialized models
legal_qa = pipeline(
    "question-answering",
    model="deepset/roberta-base-squad2"
)

legal_summarizer = pipeline(
    "summarization",
    model="facebook/bart-large-cnn"
)

# Legal domain context
legal_documents = {
    "contract-101": "This contract outlines the terms of service between parties...",
    "policy-201": "The refund policy states that customers have 30 days to return items..."
}

@server.tool(
    name="query_legal_documents",
    description="Query legal documents with question-answering"
)
def query_legal_documents(question: str, document_id: str = None) -> str:
    """Answer questions about legal documents"""
    try:
        if document_id and document_id in legal_documents:
            context = legal_documents[document_id]
        else:
            context = " ".join(legal_documents.values())
        
        result = legal_qa(question=question, context=context)
        
        return TextContent(text=json.dumps({
            "answer": result["answer"],
            "confidence": float(result["score"]),
            "context": result.get("context", "")[:500]
        }))
    
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

@server.tool(
    name="summarize_legal_text",
    description="Summarize legal documents"
)
def summarize_legal_text(document_id: str, length: str = "medium") -> str:
    """Summarize legal document"""
    try:
        if document_id not in legal_documents:
            return TextContent(text="Document not found")
        
        text = legal_documents[document_id]
        
        # Adjust max_length based on requested length
        max_length = {"short": 50, "medium": 100, "long": 200}[length]
        min_length = max(10, max_length // 3)
        
        result = legal_summarizer(
            text,
            max_length=max_length,
            min_length=min_length,
            do_sample=False
        )
        
        return TextContent(text=result[0]["summary_text"])
    
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

Computer Vision Backend

Python - Image Analysis Service
from mcp.server import Server
from mcp.types import TextContent
import base64
import json
from PIL import Image
from io import BytesIO
import numpy as np

server = Server("vision-backend")

class ImageAnalyzer:
    def __init__(self):
        # In practice, would load actual models
        self.object_detector = None
        self.ocr_engine = None
    
    def detect_objects(self, image_data: str):
        """Detect objects in image"""
        try:
            image = Image.open(BytesIO(base64.b64decode(image_data)))
            
            # Simulated detection
            detections = [
                {"class": "person", "confidence": 0.95, "bbox": [10, 20, 100, 150]},
                {"class": "dog", "confidence": 0.87, "bbox": [120, 50, 200, 180]},
            ]
            
            return detections
        except Exception as e:
            return {"error": str(e)}
    
    def extract_text(self, image_data: str):
        """Extract text from image (OCR)"""
        try:
            image = Image.open(BytesIO(base64.b64decode(image_data)))
            
            # Simulated OCR
            text = "Extracted text from image"
            confidence = 0.92
            
            return {"text": text, "confidence": confidence}
        except Exception as e:
            return {"error": str(e)}
    
    def classify_image(self, image_data: str):
        """Classify image into categories"""
        try:
            image = Image.open(BytesIO(base64.b64decode(image_data)))
            
            classifications = [
                {"category": "outdoor", "score": 0.85},
                {"category": "nature", "score": 0.72},
                {"category": "landscape", "score": 0.68}
            ]
            
            return classifications
        except Exception as e:
            return {"error": str(e)}

analyzer = ImageAnalyzer()

@server.tool(name="detect_objects_in_image")
def detect_objects_in_image(image_base64: str) -> str:
    """Detect objects in an image"""
    try:
        detections = analyzer.detect_objects(image_base64)
        return TextContent(text=json.dumps(detections))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

@server.tool(name="extract_text_from_image")
def extract_text_from_image(image_base64: str) -> str:
    """Extract text from image using OCR"""
    try:
        result = analyzer.extract_text(image_base64)
        return TextContent(text=json.dumps(result))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

@server.tool(name="classify_image")
def classify_image(image_base64: str) -> str:
    """Classify image by content"""
    try:
        classifications = analyzer.classify_image(image_base64)
        return TextContent(text=json.dumps(classifications))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

Time Series Forecasting Backend

Python - ML Forecasting Service
from mcp.server import Server
from mcp.types import TextContent
import json
import numpy as np
from datetime import datetime, timedelta

server = Server("forecasting-backend")

class TimeSeriesForecaster:
    def __init__(self):
        # Simulated trained model
        self.model = None
    
    def forecast(self, data_points: list, periods: int = 12, confidence: float = 0.95):
        """Generate time series forecast"""
        try:
            # Simulated ARIMA-like forecasting
            values = np.array(data_points)
            trend = np.polyfit(range(len(values)), values, 1)
            
            forecast = []
            for i in range(periods):
                future_idx = len(values) + i
                predicted = trend[0] * future_idx + trend[1]
                # Add some noise
                predicted += np.random.normal(0, np.std(values) * 0.1)
                forecast.append({
                    "period": i + 1,
                    "forecast": float(predicted),
                    "lower_bound": float(predicted - 2),
                    "upper_bound": float(predicted + 2)
                })
            
            return forecast
        except Exception as e:
            return {"error": str(e)}
    
    def detect_anomalies(self, data_points: list, sensitivity: float = 2.0):
        """Detect anomalies in time series"""
        values = np.array(data_points)
        mean = np.mean(values)
        std = np.std(values)
        
        anomalies = []
        for i, value in enumerate(values):
            z_score = abs((value - mean) / std)
            if z_score > sensitivity:
                anomalies.append({
                    "index": i,
                    "value": float(value),
                    "anomaly_score": float(z_score)
                })
        
        return anomalies

forecaster = TimeSeriesForecaster()

@server.tool(name="forecast_time_series")
def forecast_time_series(
    data_points: list,
    periods: int = 12,
    confidence: float = 0.95
) -> str:
    """Forecast future values in time series"""
    try:
        forecast = forecaster.forecast(data_points, periods, confidence)
        return TextContent(text=json.dumps(forecast))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

@server.tool(name="detect_anomalies")
def detect_anomalies(data_points: list, sensitivity: float = 2.0) -> str:
    """Detect anomalies in time series data"""
    try:
        anomalies = forecaster.detect_anomalies(data_points, sensitivity)
        return TextContent(text=json.dumps(anomalies))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

Recommendation Engine Backend

Python - Personalized Recommendations
from mcp.server import Server
from mcp.types import TextContent
import json

server = Server("recommendation-backend")

class RecommendationEngine:
    def __init__(self):
        # Simulated user-item interaction data
        self.user_profiles = {
            "user_1": {"interests": ["tech", "sports"], "avg_rating": 4.2},
            "user_2": {"interests": ["music", "art"], "avg_rating": 3.8}
        }
        
        self.items = {
            "item_1": {"category": "tech", "avg_rating": 4.5, "tags": ["AI", "Python"]},
            "item_2": {"category": "sports", "avg_rating": 4.0, "tags": ["fitness"]},
            "item_3": {"category": "music", "avg_rating": 4.3, "tags": ["jazz", "relaxing"]}
        }
    
    def get_recommendations(self, user_id: str, top_k: int = 5):
        """Get personalized recommendations for user"""
        if user_id not in self.user_profiles:
            return {"error": "User not found"}
        
        user = self.user_profiles[user_id]
        user_interests = set(user["interests"])
        
        recommendations = []
        for item_id, item in self.items.items():
            # Simple collaborative filtering
            category_match = item["category"] in user_interests
            
            score = item["avg_rating"]
            if category_match:
                score += 0.5  # Boost score for category match
            
            recommendations.append({
                "item_id": item_id,
                "category": item["category"],
                "rating": item["avg_rating"],
                "match_score": float(score),
                "reason": "Matches your interests" if category_match else "Popular item"
            })
        
        # Sort and return top K
        recommendations.sort(key=lambda x: x["match_score"], reverse=True)
        return recommendations[:top_k]
    
    def predict_user_rating(self, user_id: str, item_id: str):
        """Predict user's rating for an item"""
        if user_id not in self.user_profiles or item_id not in self.items:
            return {"error": "User or item not found"}
        
        user = self.user_profiles[user_id]
        item = self.items[item_id]
        
        # Simple prediction: average of user's and item's ratings
        predicted_rating = (user["avg_rating"] + item["avg_rating"]) / 2
        
        return {
            "predicted_rating": float(predicted_rating),
            "confidence": 0.75,
            "explanation": "Based on your preferences and item popularity"
        }

engine = RecommendationEngine()

@server.tool(name="get_recommendations")
def get_recommendations(user_id: str, top_k: int = 5) -> str:
    """Get personalized recommendations"""
    try:
        recs = engine.get_recommendations(user_id, top_k)
        return TextContent(text=json.dumps(recs))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

@server.tool(name="predict_user_rating")
def predict_user_rating(user_id: str, item_id: str) -> str:
    """Predict user's rating for item"""
    try:
        prediction = engine.predict_user_rating(user_id, item_id)
        return TextContent(text=json.dumps(prediction))
    except Exception as e:
        return TextContent(text=f"Error: {str(e)}")

Client Interaction

Python - Using Custom AI Backend
from mcp.client import Client
import asyncio
import json
import base64

async def use_custom_backend():
    # Connect to custom AI backend
    client = Client("domain-llm-backend", transport="stdio")
    
    # Use specialized legal tool
    result = await client.call_tool(
        name="query_legal_documents",
        arguments={
            "question": "What is the refund policy?",
            "document_id": "policy-201"
        }
    )
    
    answer = json.loads(result.content[0].text)
    print(f"Legal Answer: {answer['answer']}")
    print(f"Confidence: {answer['confidence']:.2%}")

async def use_vision_backend():
    # Connect to vision service
    client = Client("vision-backend", transport="stdio")
    
    # Load and encode image
    with open("image.jpg", "rb") as f:
        image_data = base64.b64encode(f.read()).decode()
    
    # Detect objects
    result = await client.call_tool(
        name="detect_objects_in_image",
        arguments={"image_base64": image_data}
    )
    
    detections = json.loads(result.content[0].text)
    for detection in detections:
        print(f"Found {detection['class']}: {detection['confidence']:.1%}")

asyncio.run(use_custom_backend())

Best Practices