mcp-backlog-md
Version:
An MCP (Model Context Protocol) server for the backlog.md CLI tool.
78 lines (77 loc) • 3.15 kB
JavaScript
/**
* backlogMCPServer.ts
*
* Purpose:
* - Implements the core MCP server logic for the backlog.md tool.
* - Dynamically discovers and registers all available tools from the `src/tools` directory.
* - Manages the server lifecycle and connection to the transport layer.
*
* Logic Overview:
* - The `BacklogMCPServer` class initializes an `McpServer` instance.
* - The `registerTools` method scans the tools directory, imports each tool module, and registers it with the server.
* - The `run` method orchestrates the tool registration and connects to the `StdioServerTransport`.
*
* Last Updated:
* 2025-07-25 by Cline (Model: Cline, Task: Add missing file header)
*/
import * as changeCase from 'change-case';
import { readdir } from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import pckJson from '../../package.json' with { type: 'json' };
export class BacklogMCPServer {
server;
__filename;
__dirname;
constructor() {
this.__filename = fileURLToPath(import.meta.url);
this.__dirname = path.dirname(this.__filename);
console.info('Initializing Backlog MCP Server...');
this.server = new McpServer({
name: pckJson.name,
version: pckJson.version,
title: changeCase.capitalCase(pckJson.name),
});
process.on('SIGINT', async () => {
console.info('Shutting down server...');
await this.server.close();
process.exit(0);
});
}
async registerTools() {
const toolsDir = path.join(this.__dirname, '../tools');
console.info(`Scanning for tools in ${toolsDir}`);
try {
const files = await readdir(toolsDir);
console.info(`Found ${files.length} potential tool files.`);
for (const file of files) {
if (file.endsWith('.js')) {
const toolFilePath = path.join(toolsDir, file);
const toolModule = await import(toolFilePath);
if (toolModule.default) {
const tool = toolModule.default;
const toolName = changeCase.snakeCase(tool.definition.name);
console.info(`Registering tool: ${toolName}`);
this.server.tool(toolName, tool.definition.description, tool.definition.inputSchema, tool.execute);
}
else {
console.warn(`No default export found in ${toolFilePath}`);
}
}
}
}
catch (error) {
console.error('Failed to register tools', error);
throw error;
}
}
async run() {
await this.registerTools();
const transport = new StdioServerTransport();
console.info('Connecting to transport...');
await this.server.connect(transport);
console.info('Server is running and connected.');
}
}