my-mcp-server.ts•5.72 kB
/**
* MCP Server Wrapper
*
* This class encapsulates the MCP SDK server components and provides
* a clean interface using dependency injection patterns.
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import type {
IMCPServerWrapper,
MCPServerConfig,
IModuleLoaderService
} from "../types/index.js";
import { ModuleLoaderService } from "../services/index.js";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
/**
* Abstract base class for MCP server implementations
*/
export abstract class BaseMCPServer {
protected abstract createServer(config: MCPServerConfig): McpServer;
protected abstract createTransport(): any;
abstract initialize(): Promise<void>;
abstract connect(): Promise<void>;
abstract shutdown(): Promise<void>;
}
/**
* Concrete implementation of MCP Server with SDK adapter
*/
export class MyMCPServer extends BaseMCPServer implements IMCPServerWrapper {
private server: McpServer | null = null;
private transport: any = null;
private moduleLoader: IModuleLoaderService;
private config: MCPServerConfig;
private isInitialized = false;
private isConnected = false;
constructor(
config: MCPServerConfig,
moduleLoader?: IModuleLoaderService
) {
super();
this.config = config;
this.moduleLoader = moduleLoader || new ModuleLoaderService();
}
/**
* Create and configure the MCP server instance
*/
protected createServer(config: MCPServerConfig): McpServer {
console.log(`Creating MCP server: ${config.name} v${config.version}`);
return new McpServer({
name: config.name,
version: config.version
});
}
/**
* Create the transport layer (stdio by default)
*/
protected createTransport(): StdioServerTransport {
console.log("Creating StdioServerTransport");
return new StdioServerTransport();
}
/**
* Initialize the server and register all components
*/
async initialize(): Promise<void> {
if (this.isInitialized) {
console.log("Server already initialized");
return;
}
console.log("Initializing MCP Server...");
try {
// Create server instance
this.server = this.createServer(this.config);
// Create transport
this.transport = this.createTransport();
// Load and register all components
await this.registerAllComponents();
this.isInitialized = true;
console.log("✓ MCP Server initialization completed");
} catch (error) {
console.error("✗ Failed to initialize MCP Server:", error);
throw error;
}
}
/**
* Connect the server to the transport
*/
async connect(): Promise<void> {
if (!this.isInitialized) {
throw new Error("Server must be initialized before connecting");
}
if (this.isConnected) {
console.log("Server already connected");
return;
}
if (!this.server || !this.transport) {
throw new Error("Server or transport not properly initialized");
}
try {
console.log("Connecting MCP Server to transport...");
await this.server.connect(this.transport);
this.isConnected = true;
console.log("✓ MCP Server connected successfully");
} catch (error) {
console.error("✗ Failed to connect MCP Server:", error);
throw error;
}
}
/**
* Shutdown the server and cleanup resources
*/
async shutdown(): Promise<void> {
console.log("Shutting down MCP Server...");
try {
if (this.transport && typeof this.transport.close === 'function') {
await this.transport.close();
}
this.isConnected = false;
this.isInitialized = false;
console.log("✓ MCP Server shutdown completed");
} catch (error) {
console.error("✗ Error during server shutdown:", error);
throw error;
}
}
/**
* Register all MCP components from their respective directories
*/
private async registerAllComponents(): Promise<void> {
if (!this.server) {
throw new Error("Server not initialized");
}
const currentDir = dirname(fileURLToPath(import.meta.url));
const srcDir = join(currentDir, "..");
// Register components from each directory
const componentDirs = [
{ path: join(srcDir, "tools"), name: "tools" },
{ path: join(srcDir, "resources"), name: "resources" },
{ path: join(srcDir, "prompts"), name: "prompts" }
];
for (const { path, name } of componentDirs) {
try {
console.log(`Loading ${name} from ${path}...`);
const modules = await this.moduleLoader.loadModulesFromDirectory(path);
for (const module of modules) {
await this.moduleLoader.registerModule(module, this.server);
}
console.log(`✓ Registered ${modules.length} ${name}`);
} catch (error) {
console.error(`✗ Failed to register ${name}:`, error);
// Continue with other components even if one fails
}
}
}
/**
* Get server status information
*/
getStatus(): {
initialized: boolean;
connected: boolean;
config: MCPServerConfig;
loadedModules: Array<{ name: string; metadata?: any }>;
} {
return {
initialized: this.isInitialized,
connected: this.isConnected,
config: this.config,
loadedModules: this.moduleLoader.getLoadedModulesInfo()
};
}
/**
* Get the underlying server instance (for advanced usage)
* Note: This should be used sparingly to maintain encapsulation
*/
getServerInstance(): McpServer | null {
return this.server;
}
}