/**
* postgres-mcp - MCP Server Wrapper
*
* Wraps the MCP SDK server with database adapter integration,
* tool filtering, logging capabilities, and graceful shutdown support.
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import type { DatabaseAdapter } from "../adapters/DatabaseAdapter.js";
import type { ToolFilterConfig } from "../types/index.js";
import { parseToolFilter } from "../filtering/ToolFilter.js";
import { logger } from "../utils/logger.js";
import { SERVER_INSTRUCTIONS } from "../constants/ServerInstructions.js";
export interface ServerConfig {
name: string;
version: string;
adapter: DatabaseAdapter;
toolFilter?: string | undefined;
}
/**
* PostgreSQL MCP Server
*/
export class PostgresMcpServer {
private mcpServer: McpServer;
private adapter: DatabaseAdapter;
private filterConfig: ToolFilterConfig;
private transport: StdioServerTransport | null = null;
constructor(config: ServerConfig) {
this.adapter = config.adapter;
this.filterConfig = parseToolFilter(config.toolFilter);
// Create MCP server with logging capability enabled and server instructions
this.mcpServer = new McpServer(
{
name: config.name,
version: config.version,
},
{
capabilities: {
logging: {},
},
instructions: SERVER_INSTRUCTIONS,
},
);
// Connect the logger to the underlying MCP server for protocol logging
// The McpServer.server property exposes the low-level Server instance
logger.setMcpServer(this.mcpServer.server);
logger.setLoggerName(config.name);
logger.info("MCP Server initialized", {
name: config.name,
version: config.version,
toolFilter: config.toolFilter ?? "none",
capabilities: ["logging"],
});
}
/**
* Register all tools, resources, and prompts
*/
private registerComponents(): void {
// Register tools (with filtering)
this.adapter.registerTools(this.mcpServer, this.filterConfig.enabledTools);
// Register resources
this.adapter.registerResources(this.mcpServer);
// Register prompts
this.adapter.registerPrompts(this.mcpServer);
const toolCount = this.filterConfig.enabledTools.size;
const resourceCount = this.adapter.getResourceDefinitions().length;
const promptCount = this.adapter.getPromptDefinitions().length;
logger.info("Components registered", {
tools: toolCount,
resources: resourceCount,
prompts: promptCount,
});
}
/**
* Start the server with stdio transport
*/
async start(): Promise<void> {
// Register all components
this.registerComponents();
// Create and connect transport
this.transport = new StdioServerTransport();
await this.mcpServer.connect(this.transport);
logger.info("MCP Server started with stdio transport");
}
/**
* Gracefully stop the server
*/
async stop(): Promise<void> {
logger.info("Stopping MCP Server...");
try {
await this.mcpServer.close();
logger.info("MCP Server stopped");
} catch (error) {
logger.error("Error stopping server", {
error: error instanceof Error ? error.message : "Unknown error",
});
}
}
/**
* Get the underlying MCP server instance
*/
getMcpServer(): McpServer {
return this.mcpServer;
}
/**
* Get the database adapter
*/
getAdapter(): DatabaseAdapter {
return this.adapter;
}
/**
* Get filter configuration
*/
getFilterConfig(): ToolFilterConfig {
return this.filterConfig;
}
}