Skip to main content
Glama
server-tools.ts7.14 kB
/** * Shared tool registration for MCP server * This module provides a unified way to register all tools across different server types */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { findCodeOwner, getPullRequestByCommit } from "./api.js"; import { investigateError, createJiraTicketWithInvestigation } from "./handlers.js"; import { loadConfig } from "./config.js"; import * as z from "zod"; /** * Register all MCP tools on the given server instance * This function is shared across stdio, HTTP, and compatible server implementations */ export function registerTools(server: McpServer): void { const config = loadConfig(); const defaultBranch = config.server.defaultBranch; /** * Tool: Find code owner */ server.registerTool( "find_code_owner", { description: "Find the code owner and commit ID for a specific file and line number using Bitbucket blame API", inputSchema: { filePath: z.string().describe("The relative path to the file in the repository"), lineNumber: z.number().describe("The line number where the error occurred"), branch: z.string().default(defaultBranch).describe(`The branch name (default: ${defaultBranch})`), }, }, async ({ filePath, lineNumber, branch }) => { const result = await findCodeOwner(filePath, lineNumber, branch); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } ); /** * Tool: Get pull request */ server.registerTool( "get_pull_request", { description: "Get pull request information by commit ID from Bitbucket", inputSchema: { commitId: z.string().describe("The commit ID to search for pull requests"), }, }, async ({ commitId }) => { const result = await getPullRequestByCommit(commitId); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } ); /** * Tool: Create JIRA ticket */ server.registerTool( "create_jira_ticket", { description: "Create a JIRA ticket with AI-generated error analysis and investigation results from investigate_error tool. " + "The ticket will include code owner information, related pull requests, and detailed error analysis.", inputSchema: { summary: z.string().describe( "AI-generated Chinese summary that concisely describes the core cause of the error, not a direct paste of error code or stack trace. " + "Example: '用户输入未校验导致空指针异常' instead of 'NullPointerException at line 123'" ), investigationData: z.union([z.string(), z.any()]).describe( "REQUIRED: Complete data returned by investigate_error tool (can be JSON string or object). " + "If previous call result is lost or incomplete, call investigate_error again to retrieve it. " + "Do NOT manually construct this data." ), assignee: z.string().describe( "JIRA assignee username, get this value from investigationData.codeOwner.name" ), errorAnalysis: z.union([z.string(), z.any()]).describe( "AI-generated Chinese error analysis (can be JSON string or object): " + '{"errorInfo": "异常类型:{ExceptionType}。堆栈跟踪:{简要堆栈路径,例如:ClassA.methodX(File.java:123) -> ClassB.methodY(File.java:456)}。", ' + '"analysis": "根本原因:{用一两句话说明导致异常的直接原因,例如:未对用户输入做空值校验、配置缺失、类型转换错误等}。该问题引入于 {需求编号}。风险:{说明该问题对系统、业务或用户体验的影响,例如:可能导致服务中断、数据丢失、流程失败等}。", ' + '"suggestions": "修复建议:{具体、可操作的修复步骤,也可以写伪代码示例,若逻辑简单,可直接给出示例代码}。"}' ), labels: z.array(z.string()).optional().default([]).describe("Labels to add to the ticket (optional)"), }, }, async ({ summary, investigationData, assignee, errorAnalysis, labels }) => { const result = await createJiraTicketWithInvestigation( summary, investigationData, assignee, errorAnalysis, labels ); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } ); /** * Tool: Investigate error */ server.registerTool( "investigate_error", { description: "Investigate an error by finding the code owner and related pull requests. Returns complete investigation data in JSON format that can be used with create_jira_ticket.", inputSchema: { filePath: z.string().describe("The relative path to the file in the repository"), lineNumber: z.number().describe("The line number where the error occurred"), branch: z.string().default(defaultBranch).describe(`The branch name (default: ${defaultBranch})`), }, }, async ({ filePath, lineNumber, branch }) => { const result = await investigateError(filePath, lineNumber, branch); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } ); /** * Tool: Track error (full workflow) */ server.registerTool( "track_error_full", { description: "Complete workflow: find code owner, get PR info, and create JIRA ticket for an error", inputSchema: { filePath: z.string().describe("The relative path to the file in the repository"), lineNumber: z.number().describe("The line number where the error occurred"), branch: z.string().default(defaultBranch).describe(`The branch name (default: ${defaultBranch})`), errorMessage: z.string().describe("The full error message"), summary: z.string().describe("The title for the JIRA ticket"), labels: z.array(z.string()).optional().default([]).describe("Labels to add to the ticket"), }, }, async ({ filePath, lineNumber, branch, errorMessage, summary, labels }) => { // Step 1: Investigate error const investigation = await investigateError( filePath, lineNumber, branch ); // Step 2: Create JIRA ticket (pass object directly, no serialization needed) const jiraTicket = await createJiraTicketWithInvestigation( summary, investigation, investigation.codeOwner.name, errorMessage, labels ); const result = { codeOwner: investigation.codeOwner, pullRequest: investigation.pullRequests, jiraTicket, }; return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } ); }

Latest Blog Posts

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/wycCome/mcp-error-tracing'

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