purchase-mcp-server
Version:
Purchase and budget management server handling requisitions, purchase orders, expenses, budgets, and vendor management with ERP access for data extraction
151 lines • 5.8 kB
JavaScript
/**
* MCP Server - Standardized Implementation
* Multi-tenant server with YAML-based configuration
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { ToolHandler } from "./tools/index.js";
import { config } from "./config/index.js";
import { logger, initializeConnection as initializeTypesenseConnection, closeConnection as closeTypesenseConnection, setConfig } from "syia-mcp-utils";
import { toolDefinitions } from "./tools/schema.js";
import { promptList, getPrompt } from "./prompts/index.js";
import { resourceList, readResource } from "./resources/index.js";
// TODO: Replace with syia-mcp-utils import when imoCache is moved there
import { initializeImoCache } from "./utils/imoCache.js";
// Get version dynamically from package.json
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
export { logger };
// Global server instance and transport for access during shutdown
let server = null;
let transport = null;
// Shared error handler for requests
function handleRequestError(operation, error, context) {
logger.error(`Error in ${operation}`, { error, context });
throw error;
}
// Graceful shutdown handler
async function handleShutdown(signal) {
logger.info(`Received ${signal}. Shutting down server...`);
try {
// Close all database connections
await closeTypesenseConnection();
// Close transport if it exists
if (transport) {
try {
await transport.close();
logger.info("Transport closed successfully");
}
catch (error) {
logger.error("Error closing transport:", error);
}
}
logger.info("Server shutdown complete");
process.exit(0);
}
catch (error) {
logger.error("Error during shutdown:", error);
process.exit(1);
}
}
// Setup graceful shutdown handlers
process.on('SIGINT', () => handleShutdown('SIGINT'));
process.on('SIGTERM', () => handleShutdown('SIGTERM'));
process.on('uncaughtException', (error) => {
logger.error("Uncaught exception:", error);
handleShutdown('uncaughtException');
});
process.on('unhandledRejection', (reason) => {
logger.error("Unhandled rejection:", reason);
handleShutdown('unhandledRejection');
});
async function initializeServices() {
setConfig(config);
initializeTypesenseConnection();
// Initialize IMO cache (fetch and save to file)
await initializeImoCache(config.companyName);
}
async function main() {
logger.info(`Starting MCP server v${packageJson.version} in ${config.environment} environment`);
try {
await initializeServices();
}
catch (error) {
logger.error("Failed to initialize services:", error);
process.exit(1);
}
// Create server instance
server = new Server({
name: packageJson.name,
version: packageJson.version
}, {
capabilities: {
resources: { read: true, list: true, templates: true },
tools: { list: true, call: true },
prompts: { list: true, get: true }
}
});
const toolHandler = new ToolHandler(server);
// Resource handlers
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return { resources: resourceList };
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
try {
const content = await readResource(request.params.uri);
return {
contents: [{
uri: request.params.uri,
mimeType: "application/json",
text: content
}]
};
}
catch (error) {
handleRequestError("ReadResource", error, request.params.uri);
}
});
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
return { templates: [] };
});
// Tools handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: toolDefinitions };
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const result = await toolHandler.handleCallTool(request.params.name, request.params.arguments || {});
return { content: result };
}
catch (error) {
handleRequestError("CallTool", error, request.params.name);
}
});
// Prompt handlers
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return { prompts: promptList };
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
try {
const result = getPrompt(request.params.name, request.params.arguments);
return result;
}
catch (error) {
handleRequestError("GetPrompt", error, request.params.name);
}
});
// Connect transport and start server
transport = new StdioServerTransport();
await server.connect(transport);
logger.info("Server started successfully");
}
main().catch((error) => {
logger.error("Fatal error during server startup:", error);
process.exit(1);
});
//# sourceMappingURL=index.js.map