MCP Base
by jsmiff
- server
// Generic MCP Server using Ollama for embeddings and text generation
import {
McpServer,
ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { z } from "zod";
import express from "express";
import dotenv from "dotenv";
import cors from "cors";
// Import the Ollama services
import OllamaEmbeddingsService from "./utils/ollama_embedding.js";
import OllamaTextGenerationService from "./utils/ollama_text_generation.js";
// Conditionally import Supabase
let createClient;
try {
({ createClient } = await import("@supabase/supabase-js"));
} catch (error) {
console.log("Supabase SDK not installed, running without database support");
}
// Import sample components
import { registerSampleTool } from "./tools/sample-tool.js";
import { registerSamplePrompts } from "./prompts/sample-prompt.js";
import { registerSampleResources } from "./resources/sample-resource.js";
console.log("Loading environment variables...");
// Load environment variables
dotenv.config();
// Configuration
const config = {
port: process.env.PORT || 3000,
supabaseUrl: process.env.SUPABASE_URL,
supabaseKey: process.env.SUPABASE_SERVICE_KEY,
ollamaUrl: process.env.OLLAMA_URL || "http://localhost:11434",
embedModel: process.env.OLLAMA_EMBED_MODEL || "nomic-embed-text",
llmModel: process.env.OLLAMA_LLM_MODEL || "llama3",
serverMode: process.env.SERVER_MODE || "http", // 'stdio' or 'http'
};
// Check if Supabase is available and configured
const supabaseEnabled = !!(createClient && config.supabaseUrl && config.supabaseKey);
if (!supabaseEnabled) {
console.log("Supabase integration is disabled. Running without database support.");
}
// Initialize the Ollama services
console.log("Initializing Ollama services...");
const embeddings = new OllamaEmbeddingsService({
baseUrl: config.ollamaUrl,
model: config.embedModel,
});
const textGenerator = new OllamaTextGenerationService({
baseUrl: config.ollamaUrl,
model: config.llmModel,
});
// Initialize Supabase if enabled
let supabase = null;
if (supabaseEnabled) {
console.log("Connecting to Supabase...");
supabase = createClient(config.supabaseUrl, config.supabaseKey);
} else {
console.log("Skipping Supabase initialization");
}
// Initialize the database (if needed)
async function initializeDatabase() {
try {
console.log("Checking Ollama availability...");
if (!(await embeddings.isAvailable())) {
throw new Error(
"Ollama service is not available. Make sure Ollama is running."
);
}
console.log("Pulling embedding model if needed...");
await embeddings.pullModelIfNeeded();
console.log("Pulling LLM model if needed...");
await textGenerator.pullModelIfNeeded();
// Add your database initialization code here
// For example:
// await initializeCollections();
console.log("Database initialization complete");
} catch (error) {
console.error("Error initializing database:", error);
throw error;
}
}
// Create the MCP server
console.log("Creating MCP server...");
const server = new McpServer({
name: "MCP Server Base",
version: "1.0.0",
});
// ------------ RESOURCES ------------
// Register resources
registerSampleResources(server, supabase);
// ------------ TOOLS ------------
// Register tools
registerSampleTool(server, textGenerator, embeddings, supabase);
// ------------ PROMPTS ------------
// Register prompts
registerSamplePrompts(server, supabase);
// Main function to start the server
async function startServer() {
try {
console.log("Initializing database...");
await initializeDatabase();
console.log("Determining transport mode...");
if (config.serverMode === "stdio") {
console.log("Starting server with stdio transport...");
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("MCP Server started with stdio transport");
} else {
console.log("Starting server with HTTP/SSE transport...");
const app = express();
app.use(cors());
// Use a single transport for simplicity
let transport;
// Simple SSE endpoint
app.get("/sse", async (req, res) => {
transport = new SSEServerTransport("/message", res);
await server.connect(transport);
});
app.post("/message", async (req, res) => {
await transport.handlePostMessage(req, res);
});
// Health check endpoint
app.get("/health", async (req, res) => {
try {
console.log("Performing health check...");
const ollamaAvailable = await embeddings.isAvailable();
let dbAvailable = false;
if (supabaseEnabled) {
try {
// Try connecting to Supabase
const { data, error } = await supabase.from("health_check").select("count");
dbAvailable = !error;
} catch (e) {
console.error("Supabase health check error:", e);
}
}
res.json({
status: "ok",
services: {
ollama: ollamaAvailable ? "available" : "unavailable",
database: supabaseEnabled ? (dbAvailable ? "available" : "unavailable") : "disabled",
},
models: {
embedding: config.embedModel,
llm: config.llmModel,
}
});
} catch (error) {
console.error("Error during health check:", error);
res.status(500).json({
status: "error",
message: error.message,
});
}
});
// Documentation endpoint
app.get("/", (req, res) => {
console.log("Serving documentation...");
res.send(`
<html>
<head>
<title>MCP Server Base</title>
<style>
body { font-family: sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #0ea5e9; }
pre { background: #f5f5f5; padding: 10px; border-radius: 4px; overflow: auto; }
.endpoint { background: #eee; padding: 10px; margin: 10px 0; border-radius: 4px; }
</style>
</head>
<body>
<h1>MCP Server Base</h1>
<p>This server provides a Model Context Provider (MCP) interface.</p>
<h2>Endpoints</h2>
<div class="endpoint">
<h3>SSE Connection</h3>
<code>GET /sse</code>
<p>Connect to the server using Server-Sent Events (SSE)</p>
</div>
<div class="endpoint">
<h3>Message</h3>
<code>POST /message</code>
<p>Send messages to the server</p>
</div>
<div class="endpoint">
<h3>Health Check</h3>
<code>GET /health</code>
<p>Check server health and service status</p>
</div>
<h2>Available Resources</h2>
<p>No resources defined yet.</p>
<h2>Available Tools</h2>
<p>No tools defined yet.</p>
<h2>Available Prompts</h2>
<p>No prompts defined yet.</p>
</body>
</html>
`);
});
// Start HTTP server
app.listen(config.port, () => {
console.log(`MCP Server running on http://localhost:${config.port}`);
console.log(`- SSE endpoint: http://localhost:${config.port}/sse`);
console.log(
`- Message endpoint: http://localhost:${config.port}/message`
);
console.log(`- Health check: http://localhost:${config.port}/health`);
});
}
} catch (error) {
console.error("Error starting server:", error);
process.exit(1);
}
}
// Start the server
console.log("Starting server...");
startServer().catch((error) => {
console.error("Error starting server:", error);
process.exit(1);
});
export default server;