bc-webclient-mcp
Version:
Model Context Protocol (MCP) server for Microsoft Dynamics 365 Business Central via WebUI protocol. Enables AI assistants to interact with BC through the web client protocol, supporting Card, List, and Document pages with full line item support and server
110 lines • 4.92 kB
JavaScript
/**
* MCP STDIO Server Entry Point
*
* This script starts the MCP server in STDIO mode for use with Claude Desktop
* and other MCP clients that communicate via JSON-RPC over stdin/stdout.
*
* Usage:
* MCP_STDIO_LOG_FILE=./logs/mcp-stdio.log node stdio-server.js
*/
import { MCPServer } from './services/mcp-server.js';
import { StdioTransport } from './services/stdio-transport.js';
import { bcConfig } from './core/config.js';
import { logger } from './core/logger.js';
import { AuditLogger } from './services/audit-logger.js';
import { CacheManager } from './services/cache-manager.js';
import { isOk } from './core/result.js';
// Import 6 core tools (consolidated from 9 for context efficiency)
import { GetPageMetadataTool } from './tools/get-page-metadata-tool.js';
import { SearchPagesTool } from './tools/search-pages-tool.js';
import { ReadPageDataTool } from './tools/read-page-data-tool.js';
import { WritePageDataTool } from './tools/write-page-data-tool.js';
import { ExecuteActionTool } from './tools/execute-action-tool.js';
import { SelectAndDrillDownTool } from './tools/select-and-drill-down-tool.js';
// Optional/Advanced tools (not in default registry, but available for opt-in)
import { CreateRecordByFieldNameTool } from './tools/create-record-by-field-name-tool.js';
// Import resources
import { buildResources } from './resources/index.js';
/**
* Main function to start the STDIO MCP server
*/
async function main() {
try {
// Get BC connection config
const { baseUrl, username, password, tenantId } = bcConfig;
if (!password) {
logger.error('BC_PASSWORD environment variable not set');
process.exit(1);
}
// Create primary BC connection
// NOTE: Tools will use ConnectionManager for session reuse.
// This initial connection is only used as a fallback/template for tools.
const connection = new (await import('./connection/bc-page-connection.js')).BCPageConnection({
baseUrl,
username,
password,
tenantId,
timeout: 30000,
});
// Don't connect immediately - let ConnectionManager handle it on-demand
// Create cache manager
const cacheManager = new CacheManager({
maxEntries: 1000,
defaultTtlMs: 300000,
cleanupIntervalMs: 60000,
enableCoalescing: true,
});
// Create MCP server
const server = new MCPServer(logger);
// Register MCP resources
for (const resource of buildResources({ logger })) {
server.registerResource(resource);
}
// Create audit logger
const auditLogger = new AuditLogger(logger, 10000);
// Register 5 Core MCP Tools (consolidated from 9 for context efficiency)
// Read-only tools
server.registerTool(new GetPageMetadataTool(connection, bcConfig));
server.registerTool(new SearchPagesTool(bcConfig, undefined, cacheManager)); // Uses ConnectionManager directly
server.registerTool(new ReadPageDataTool(connection, bcConfig)); // Now includes filtering (filter_list merged)
// Write/mutation tools (with audit logger)
server.registerTool(new WritePageDataTool(connection, bcConfig, auditLogger));
server.registerTool(new ExecuteActionTool(connection, bcConfig, auditLogger));
server.registerTool(new SelectAndDrillDownTool(connection, bcConfig, auditLogger));
// Optional/Advanced tools
server.registerTool(new CreateRecordByFieldNameTool(connection, bcConfig, auditLogger));
// server.registerTool(new CreateRecordTool(connection, bcConfig, auditLogger));
// server.registerTool(new UpdateRecordTool(connection, bcConfig, auditLogger));
// Create STDIO transport
const transport = new StdioTransport(server, { logger });
// Start transport
const startResult = await transport.start();
if (!isOk(startResult)) {
logger.error(`Failed to start STDIO transport: ${startResult.error.message}`);
process.exit(1);
}
logger.info('BC MCP STDIO Server ready');
// Keep process alive
process.on('SIGINT', async () => {
logger.info('Received SIGINT, shutting down gracefully');
await transport.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
logger.info('Received SIGTERM, shutting down gracefully');
await transport.stop();
process.exit(0);
});
}
catch (error) {
logger.error(`Fatal error starting STDIO server: ${String(error)}`);
process.exit(1);
}
}
// Start the server
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
//# sourceMappingURL=stdio-server.js.map