/** * MCP Server Setup * Contains all the server configuration, tool schemas, and request handlers */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { GogsClient } from './gogs-client.js'; // Define tool schemas const GetUserSchema = z.object({ username: z.string().describe('Username to get information for'), }); const SearchUsersSchema = z.object({ query: z.string().describe('Search query for users'), limit: z.number().optional().describe('Maximum number of results (default: 10)'), }); const ListUserReposSchema = z.object({ username: z.string().optional().describe('Username (omit for authenticated user)'), }); const SearchReposSchema = z.object({ query: z.string().describe('Search query for repositories'), uid: z.number().optional().describe('User ID to filter repositories'), limit: z.number().optional().describe('Maximum number of results (default: 10)'), page: z.number().optional().describe('Page number (default: 1)'), }); const GetRepoSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const CreateRepoSchema = z.object({ name: z.string().describe('Repository name'), description: z.string().optional().describe('Repository description'), private: z.boolean().optional().describe('Create as private repository (default: false)'), auto_init: z.boolean().optional().describe('Initialize with README (default: false)'), gitignores: z.string().optional().describe('Gitignore templates (e.g., "Go,VisualStudioCode")'), license: z.string().optional().describe('License template (e.g., "MIT License")'), readme: z.string().optional().describe('README template (default: "Default")'), }); const DeleteRepoSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const GetContentsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), path: z.string().describe('File or directory path'), ref: z.string().optional().describe('Branch, tag, or commit SHA (default: default branch)'), }); const GetRawContentSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), ref: z.string().describe('Branch, tag, or commit SHA'), path: z.string().describe('File path'), }); const ListBranchesSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const GetCommitsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), sha: z.string().optional().describe('Branch name or commit SHA'), page: z.number().optional().describe('Page number (default: 1)'), }); const ListIssuesSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), state: z.enum(['open', 'closed', 'all']).optional().describe('Filter by state (default: open)'), labels: z.string().optional().describe('Comma-separated list of label names'), page: z.number().optional().describe('Page number (default: 1)'), per_page: z.number().optional().describe('Results per page (default: 30)'), }); const GetIssueSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), }); const CreateIssueSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), title: z.string().describe('Issue title'), body: z.string().optional().describe('Issue description'), assignee: z.string().optional().describe('Username to assign the issue to'), milestone: z.number().optional().describe('Milestone ID'), labels: z.array(z.number()).optional().describe('Array of label IDs'), }); const UpdateIssueSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), title: z.string().optional().describe('Issue title'), body: z.string().optional().describe('Issue description'), assignee: z.string().optional().describe('Username to assign the issue to'), milestone: z.number().optional().describe('Milestone ID'), state: z.enum(['open', 'closed']).optional().describe('Issue state'), labels: z.array(z.number()).optional().describe('Array of label IDs'), }); const ListIssueCommentsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), }); const CreateIssueCommentSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), body: z.string().describe('Comment text'), }); const UpdateIssueCommentSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), commentId: z.number().describe('Comment ID'), body: z.string().describe('Updated comment text'), }); const ListLabelsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const GetLabelSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), labelId: z.number().describe('Label ID'), }); const CreateLabelSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), name: z.string().describe('Label name'), color: z.string().describe('7-character hex color code with leading # (e.g., #ff0000)'), }); const UpdateLabelSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), labelId: z.number().describe('Label ID'), name: z.string().optional().describe('Label name'), color: z.string().optional().describe('7-character hex color code with leading # (e.g., #ff0000)'), }); const DeleteLabelSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), labelId: z.number().describe('Label ID'), }); const ListIssueLabelsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), }); const AddIssueLabelsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), labels: z.array(z.number()).describe('Array of label IDs to add'), }); const RemoveIssueLabelSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), labelId: z.number().describe('Label ID to remove'), }); const ReplaceIssueLabelsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), labels: z.array(z.number()).describe('Array of label IDs to set (replaces all existing labels)'), }); const RemoveAllIssueLabelsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), number: z.number().describe('Issue number'), }); /** * Create and configure an MCP server with all tools and handlers */ export function createMcpServer(gogsClient: GogsClient): Server { const server = new Server( { name: 'gogs-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, } ); // Register tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get_current_user', description: 'Get information about the authenticated user', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_user', description: 'Get information about a specific user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to get information for', }, }, required: ['username'], }, }, { name: 'search_users', description: 'Search for users by username', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for users', }, limit: { type: 'number', description: 'Maximum number of results (default: 10)', }, }, required: ['query'], }, }, { name: 'list_user_repositories', description: 'List repositories for a user (or authenticated user if no username provided)', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username (omit for authenticated user)', }, }, }, }, { name: 'search_repositories', description: 'Search for repositories', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for repositories', }, uid: { type: 'number', description: 'User ID to filter repositories', }, limit: { type: 'number', description: 'Maximum number of results (default: 10)', }, page: { type: 'number', description: 'Page number (default: 1)', }, }, required: ['query'], }, }, { name: 'get_repository', description: 'Get information about a specific repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'create_repository', description: 'Create a new repository for the authenticated user', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Repository name', }, description: { type: 'string', description: 'Repository description', }, private: { type: 'boolean', description: 'Create as private repository (default: false)', }, auto_init: { type: 'boolean', description: 'Initialize with README (default: false)', }, gitignores: { type: 'string', description: 'Gitignore templates (e.g., "Go,VisualStudioCode")', }, license: { type: 'string', description: 'License template (e.g., "MIT License")', }, readme: { type: 'string', description: 'README template (default: "Default")', }, }, required: ['name'], }, }, { name: 'delete_repository', description: 'Delete a repository (requires admin access)', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'get_contents', description: 'Get file or directory contents from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, path: { type: 'string', description: 'File or directory path', }, ref: { type: 'string', description: 'Branch, tag, or commit SHA (default: default branch)', }, }, required: ['owner', 'repo', 'path'], }, }, { name: 'get_raw_content', description: 'Get raw file content from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, ref: { type: 'string', description: 'Branch, tag, or commit SHA', }, path: { type: 'string', description: 'File path', }, }, required: ['owner', 'repo', 'ref', 'path'], }, }, { name: 'list_branches', description: 'List all branches in a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'get_commits', description: 'Get commit history from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, sha: { type: 'string', description: 'Branch name or commit SHA', }, page: { type: 'number', description: 'Page number (default: 1)', }, }, required: ['owner', 'repo'], }, }, { name: 'list_issues', description: 'List issues in a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, state: { type: 'string', enum: ['open', 'closed', 'all'], description: 'Filter by state (default: open)', }, labels: { type: 'string', description: 'Comma-separated list of label names', }, page: { type: 'number', description: 'Page number (default: 1)', }, per_page: { type: 'number', description: 'Results per page (default: 30)', }, }, required: ['owner', 'repo'], }, }, { name: 'get_issue', description: 'Get a specific issue by number', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, }, required: ['owner', 'repo', 'number'], }, }, { name: 'create_issue', description: 'Create a new issue in a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, title: { type: 'string', description: 'Issue title', }, body: { type: 'string', description: 'Issue description', }, assignee: { type: 'string', description: 'Username to assign the issue to', }, milestone: { type: 'number', description: 'Milestone ID', }, labels: { type: 'array', items: { type: 'number', }, description: 'Array of label IDs', }, }, required: ['owner', 'repo', 'title'], }, }, { name: 'update_issue', description: 'Update an existing issue (including closing or reopening)', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, title: { type: 'string', description: 'Issue title', }, body: { type: 'string', description: 'Issue description', }, assignee: { type: 'string', description: 'Username to assign the issue to', }, milestone: { type: 'number', description: 'Milestone ID', }, state: { type: 'string', enum: ['open', 'closed'], description: 'Issue state', }, labels: { type: 'array', items: { type: 'number', }, description: 'Array of label IDs', }, }, required: ['owner', 'repo', 'number'], }, }, { name: 'list_issue_comments', description: 'List all comments on an issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, }, required: ['owner', 'repo', 'number'], }, }, { name: 'create_issue_comment', description: 'Add a comment to an issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, body: { type: 'string', description: 'Comment text', }, }, required: ['owner', 'repo', 'number', 'body'], }, }, { name: 'update_issue_comment', description: 'Edit an existing comment on an issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, commentId: { type: 'number', description: 'Comment ID', }, body: { type: 'string', description: 'Updated comment text', }, }, required: ['owner', 'repo', 'commentId', 'body'], }, }, { name: 'list_labels', description: 'List all labels in a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'get_label', description: 'Get a specific label by ID', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, labelId: { type: 'number', description: 'Label ID', }, }, required: ['owner', 'repo', 'labelId'], }, }, { name: 'create_label', description: 'Create a new label in a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, name: { type: 'string', description: 'Label name', }, color: { type: 'string', description: '7-character hex color code with leading # (e.g., #ff0000)', }, }, required: ['owner', 'repo', 'name', 'color'], }, }, { name: 'update_label', description: 'Update an existing label', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, labelId: { type: 'number', description: 'Label ID', }, name: { type: 'string', description: 'Label name', }, color: { type: 'string', description: '7-character hex color code with leading # (e.g., #ff0000)', }, }, required: ['owner', 'repo', 'labelId'], }, }, { name: 'delete_label', description: 'Delete a label from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, labelId: { type: 'number', description: 'Label ID', }, }, required: ['owner', 'repo', 'labelId'], }, }, { name: 'list_issue_labels', description: 'List all labels on a specific issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, }, required: ['owner', 'repo', 'number'], }, }, { name: 'add_issue_labels', description: 'Add labels to an issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, labels: { type: 'array', items: { type: 'number', }, description: 'Array of label IDs to add', }, }, required: ['owner', 'repo', 'number', 'labels'], }, }, { name: 'remove_issue_label', description: 'Remove a specific label from an issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, labelId: { type: 'number', description: 'Label ID to remove', }, }, required: ['owner', 'repo', 'number', 'labelId'], }, }, { name: 'replace_issue_labels', description: 'Replace all labels on an issue with a new set', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, labels: { type: 'array', items: { type: 'number', }, description: 'Array of label IDs to set (replaces all existing labels)', }, }, required: ['owner', 'repo', 'number', 'labels'], }, }, { name: 'remove_all_issue_labels', description: 'Remove all labels from an issue', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, number: { type: 'number', description: 'Issue number', }, }, required: ['owner', 'repo', 'number'], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; switch (name) { case 'get_current_user': { const user = await gogsClient.getCurrentUser(); return { content: [ { type: 'text', text: JSON.stringify(user, null, 2), }, ], }; } case 'get_user': { const { username } = GetUserSchema.parse(args); const user = await gogsClient.getUser(username); return { content: [ { type: 'text', text: JSON.stringify(user, null, 2), }, ], }; } case 'search_users': { const { query, limit } = SearchUsersSchema.parse(args); const users = await gogsClient.searchUsers(query, limit); return { content: [ { type: 'text', text: JSON.stringify(users, null, 2), }, ], }; } case 'list_user_repositories': { const { username } = ListUserReposSchema.parse(args); const repos = username ? await gogsClient.listUserRepositories(username) : await gogsClient.listMyRepositories(); return { content: [ { type: 'text', text: JSON.stringify(repos, null, 2), }, ], }; } case 'search_repositories': { const { query, uid, limit, page } = SearchReposSchema.parse(args); const repos = await gogsClient.searchRepositories(query, { uid, limit, page }); return { content: [ { type: 'text', text: JSON.stringify(repos, null, 2), }, ], }; } case 'get_repository': { const { owner, repo } = GetRepoSchema.parse(args); const repository = await gogsClient.getRepository(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(repository, null, 2), }, ], }; } case 'create_repository': { const data = CreateRepoSchema.parse(args); const repo = await gogsClient.createRepository(data); return { content: [ { type: 'text', text: JSON.stringify(repo, null, 2), }, ], }; } case 'delete_repository': { const { owner, repo } = DeleteRepoSchema.parse(args); await gogsClient.deleteRepository(owner, repo); return { content: [ { type: 'text', text: `Repository ${owner}/${repo} deleted successfully`, }, ], }; } case 'get_contents': { const { owner, repo, path, ref } = GetContentsSchema.parse(args); const contents = await gogsClient.getContents(owner, repo, path, ref); // If it's a file with base64 content, decode it if (!Array.isArray(contents) && contents.type === 'file' && contents.content) { const decodedContent = Buffer.from(contents.content, 'base64').toString('utf-8'); return { content: [ { type: 'text', text: `File: ${contents.path}\nSize: ${contents.size} bytes\nSHA: ${contents.sha}\n\nContent:\n${decodedContent}`, }, ], }; } return { content: [ { type: 'text', text: JSON.stringify(contents, null, 2), }, ], }; } case 'get_raw_content': { const { owner, repo, ref, path } = GetRawContentSchema.parse(args); const content = await gogsClient.getRawContent(owner, repo, ref, path); return { content: [ { type: 'text', text: content, }, ], }; } case 'list_branches': { const { owner, repo } = ListBranchesSchema.parse(args); const branches = await gogsClient.listBranches(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(branches, null, 2), }, ], }; } case 'get_commits': { const { owner, repo, sha, page } = GetCommitsSchema.parse(args); const commits = await gogsClient.getCommits(owner, repo, { sha, page }); return { content: [ { type: 'text', text: JSON.stringify(commits, null, 2), }, ], }; } case 'list_issues': { const { owner, repo, state, labels, page, per_page } = ListIssuesSchema.parse(args); const issues = await gogsClient.listIssues(owner, repo, { state, labels, page, per_page }); return { content: [ { type: 'text', text: JSON.stringify(issues, null, 2), }, ], }; } case 'get_issue': { const { owner, repo, number } = GetIssueSchema.parse(args); const issue = await gogsClient.getIssue(owner, repo, number); return { content: [ { type: 'text', text: JSON.stringify(issue, null, 2), }, ], }; } case 'create_issue': { const { owner, repo, title, body, assignee, milestone, labels } = CreateIssueSchema.parse(args); const issue = await gogsClient.createIssue(owner, repo, { title, body, assignee, milestone, labels, }); return { content: [ { type: 'text', text: JSON.stringify(issue, null, 2), }, ], }; } case 'update_issue': { const { owner, repo, number, title, body, assignee, milestone, state, labels } = UpdateIssueSchema.parse(args); const issue = await gogsClient.updateIssue(owner, repo, number, { title, body, assignee, milestone, state, labels, }); return { content: [ { type: 'text', text: JSON.stringify(issue, null, 2), }, ], }; } case 'list_issue_comments': { const { owner, repo, number } = ListIssueCommentsSchema.parse(args); const comments = await gogsClient.listIssueComments(owner, repo, number); return { content: [ { type: 'text', text: JSON.stringify(comments, null, 2), }, ], }; } case 'create_issue_comment': { const { owner, repo, number, body } = CreateIssueCommentSchema.parse(args); const comment = await gogsClient.createIssueComment(owner, repo, number, body); return { content: [ { type: 'text', text: JSON.stringify(comment, null, 2), }, ], }; } case 'update_issue_comment': { const { owner, repo, commentId, body } = UpdateIssueCommentSchema.parse(args); const comment = await gogsClient.updateIssueComment(owner, repo, commentId, body); return { content: [ { type: 'text', text: JSON.stringify(comment, null, 2), }, ], }; } case 'list_labels': { const { owner, repo } = ListLabelsSchema.parse(args); const labels = await gogsClient.listLabels(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(labels, null, 2), }, ], }; } case 'get_label': { const { owner, repo, labelId } = GetLabelSchema.parse(args); const label = await gogsClient.getLabel(owner, repo, labelId); return { content: [ { type: 'text', text: JSON.stringify(label, null, 2), }, ], }; } case 'create_label': { const { owner, repo, name, color } = CreateLabelSchema.parse(args); const label = await gogsClient.createLabel(owner, repo, { name, color }); return { content: [ { type: 'text', text: JSON.stringify(label, null, 2), }, ], }; } case 'update_label': { const { owner, repo, labelId, name, color } = UpdateLabelSchema.parse(args); const label = await gogsClient.updateLabel(owner, repo, labelId, { name, color }); return { content: [ { type: 'text', text: JSON.stringify(label, null, 2), }, ], }; } case 'delete_label': { const { owner, repo, labelId } = DeleteLabelSchema.parse(args); await gogsClient.deleteLabel(owner, repo, labelId); return { content: [ { type: 'text', text: `Label ${labelId} deleted successfully from ${owner}/${repo}`, }, ], }; } case 'list_issue_labels': { const { owner, repo, number } = ListIssueLabelsSchema.parse(args); const labels = await gogsClient.listIssueLabels(owner, repo, number); return { content: [ { type: 'text', text: JSON.stringify(labels, null, 2), }, ], }; } case 'add_issue_labels': { const { owner, repo, number, labels } = AddIssueLabelsSchema.parse(args); const result = await gogsClient.addIssueLabels(owner, repo, number, labels); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'remove_issue_label': { const { owner, repo, number, labelId } = RemoveIssueLabelSchema.parse(args); await gogsClient.removeIssueLabel(owner, repo, number, labelId); return { content: [ { type: 'text', text: `Label ${labelId} removed successfully from issue #${number}`, }, ], }; } case 'replace_issue_labels': { const { owner, repo, number, labels } = ReplaceIssueLabelsSchema.parse(args); const result = await gogsClient.replaceIssueLabels(owner, repo, number, labels); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'remove_all_issue_labels': { const { owner, repo, number } = RemoveAllIssueLabelsSchema.parse(args); await gogsClient.removeAllIssueLabels(owner, repo, number); return { content: [ { type: 'text', text: `All labels removed successfully from issue #${number}`, }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { if (error instanceof Error) { return { content: [ { type: 'text', text: `Error: ${error.message}`, }, ], isError: true, }; } throw error; } }); // Handle resources (optional - for browsing repository structure) server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [], }; }); server.setRequestHandler(ReadResourceRequestSchema, async (request) => { throw new Error('Resource reading not yet implemented'); }); return server; }