Skip to main content
Glama

Redmine MCP Server

by yonaka15
index.ts4.7 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; import { redmineClient } from "../lib/client/index.js"; import config from "../lib/config.js"; import * as tools from "../tools/index.js"; import { HandlerContext } from "./types.js"; import { createIssuesHandlers } from "./issues.js"; import { createProjectsHandlers } from "./projects.js"; import { createTimeEntriesHandlers } from "./time_entries.js"; import { createUserHandlers } from "./users.js"; import { formatAllowedStatuses } from "../formatters/projects.js"; // Import the new formatter // Create handler context const context: HandlerContext = { client: redmineClient, config: config, logger: { info: console.log, error: console.error, warn: console.warn, debug: console.debug, }, }; // Create resource handlers const issuesHandlers = createIssuesHandlers(context); // Pass the formatter function to createProjectsHandlers const projectsHandlers = createProjectsHandlers(context, formatAllowedStatuses); const timeEntriesHandlers = createTimeEntriesHandlers(context); const usersHandlers = createUserHandlers(context); // Create handler map const handlers = { ...issuesHandlers, ...projectsHandlers, ...timeEntriesHandlers, ...usersHandlers, }; // Available tools list // The PROJECT_LIST_STATUSES_TOOL will be added in the tools/index.ts modification step const TOOLS: Tool[] = [ // Issue-related tools tools.ISSUE_LIST_TOOL, tools.ISSUE_GET_TOOL, tools.ISSUE_CREATE_TOOL, tools.ISSUE_UPDATE_TOOL, tools.ISSUE_DELETE_TOOL, tools.ISSUE_ADD_WATCHER_TOOL, tools.ISSUE_REMOVE_WATCHER_TOOL, // Project-related tools tools.PROJECT_LIST_TOOL, tools.PROJECT_SHOW_TOOL, tools.PROJECT_CREATE_TOOL, tools.PROJECT_UPDATE_TOOL, tools.PROJECT_ARCHIVE_TOOL, tools.PROJECT_UNARCHIVE_TOOL, tools.PROJECT_DELETE_TOOL, tools.PROJECT_LIST_STATUSES_TOOL, // New tool for listing project statuses // Time entry tools tools.TIME_ENTRY_LIST_TOOL, tools.TIME_ENTRY_SHOW_TOOL, tools.TIME_ENTRY_CREATE_FOR_ISSUE_TOOL, tools.TIME_ENTRY_CREATE_FOR_PROJECT_TOOL, tools.TIME_ENTRY_UPDATE_TOOL, tools.TIME_ENTRY_DELETE_TOOL, // User-related tools tools.USER_LIST_TOOL, tools.USER_SHOW_TOOL, tools.USER_CREATE_TOOL, tools.USER_UPDATE_TOOL, tools.USER_DELETE_TOOL, ]; // Initialize server const server = new Server( { name: config.server.name, version: config.server.version, }, { capabilities: { tools: {}, }, } ); // Tools list handler server.setRequestHandler(ListToolsRequestSchema, async () => ({ // Dynamically build the list of tools from the tools module at runtime // This ensures that any tool added to tools/index.ts is automatically included. tools: Object.values(tools).filter(t => typeof t === 'object' && t !== null && 'name' in t && 'description' in t && 'inputSchema' in t) as Tool[], })); // Tool execution handler server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; if (!args || typeof args !== "object") { // Even if args is null or undefined, pass it as an empty object for handlers that might not expect args. // Specific handlers should validate their own arguments. } // Execute handler // Ensure args is always an object, even if empty. const handlerArgs = args || {}; if (name in handlers) { return await handlers[name as keyof typeof handlers](handlerArgs); } // Unknown tool return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true, }; } catch (error) { // Log error details console.error("Error in request handler:"); if (error instanceof Error) { console.error("Error type:", error.constructor.name); console.error("Error message:", error.message); console.error("Stack trace:", error.stack); if ("cause" in error && error.cause) { console.error("Error cause:", error.cause); } } else { console.error("Unknown error:", String(error)); } // Return simple error message to user return { content: [ { type: "text", text: error instanceof Error ? error.message : String(error), }, ], isError: true, }; } }); // Start server async function runServer() { const transport = new StdioServerTransport(); await server.connect(transport); } // Exports export { server, runServer };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yonaka15/mcp-server-redmine'

If you have feedback or need assistance with the MCP directory API, please join our Discord server