/** * 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 GetTreeSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), sha: z.string().describe('Git tree SHA or commit SHA'), }); 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'), }); const ListMilestonesSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const GetMilestoneSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), milestoneId: z.number().describe('Milestone ID'), }); const CreateMilestoneSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), title: z.string().describe('Milestone title'), description: z.string().optional().describe('Milestone description'), due_on: z.string().optional().describe('Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'), }); const UpdateMilestoneSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), milestoneId: z.number().describe('Milestone ID'), title: z.string().optional().describe('Milestone title'), description: z.string().optional().describe('Milestone description'), due_on: z.string().optional().describe('Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'), state: z.enum(['open', 'closed']).optional().describe('Milestone state'), }); const DeleteMilestoneSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), milestoneId: z.number().describe('Milestone ID'), }); const CreateOrganizationSchema = z.object({ username: z.string().describe('Organization username (alphanumeric, dashes, underscores)'), full_name: z.string().optional().describe('Organization full name'), description: z.string().optional().describe('Organization description'), website: z.string().optional().describe('Organization website'), location: z.string().optional().describe('Organization location'), }); const ListPublicOrganizationsSchema = z.object({ username: z.string().describe('Username to get public organizations for'), }); const GetOrganizationSchema = z.object({ orgname: z.string().describe('Organization name'), }); const UpdateOrganizationSchema = z.object({ orgname: z.string().describe('Organization name'), full_name: z.string().optional().describe('Organization full name'), description: z.string().optional().describe('Organization description'), website: z.string().optional().describe('Organization website'), location: z.string().optional().describe('Organization location'), }); const AddOrganizationMemberSchema = z.object({ orgname: z.string().describe('Organization name'), username: z.string().describe('Username to add as member'), role: z.enum(['admin', 'member']).describe('Member role (admin or member)'), }); const ListOrganizationTeamsSchema = z.object({ orgname: z.string().describe('Organization name'), }); const CreateTeamSchema = z.object({ orgname: z.string().describe('Organization name'), name: z.string().describe('Team name (max 30 characters, alphanumeric with dashes and dots)'), description: z.string().optional().describe('Team description (max 255 characters)'), permission: z.enum(['read', 'write', 'admin']).optional().describe('Team permission level (default: read)'), }); const GetTeamSchema = z.object({ teamId: z.number().describe('Team ID'), }); const AddTeamMemberSchema = z.object({ teamId: z.number().describe('Team ID'), username: z.string().describe('Username to add to the team'), }); const RemoveTeamMemberSchema = z.object({ teamId: z.number().describe('Team ID'), username: z.string().describe('Username to remove from the team'), }); const ListReleasesSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const ListCollaboratorsSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const CheckCollaboratorSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), username: z.string().describe('Username to check for collaborator status'), }); const AddCollaboratorSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), username: z.string().describe('Username to add as collaborator'), permission: z.enum(['read', 'write', 'admin']).optional().describe('Permission level (default: write)'), }); const RemoveCollaboratorSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), username: z.string().describe('Username to remove as collaborator'), }); const AddUserEmailsSchema = z.object({ emails: z.array(z.string()).describe('Array of email addresses to add'), }); const DeleteUserEmailsSchema = z.object({ emails: z.array(z.string()).describe('Array of email addresses to delete'), }); const ListUserKeysSchema = z.object({ username: z.string().describe('Username to list public keys for'), }); const GetPublicKeySchema = z.object({ keyId: z.number().describe('Public key ID'), }); const CreatePublicKeySchema = z.object({ title: z.string().describe('Key title/name for identification'), key: z.string().describe('The public key content (SSH public key format)'), }); const DeletePublicKeySchema = z.object({ keyId: z.number().describe('Public key ID to delete'), }); const ListHooksSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const CreateHookSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), type: z.enum(['gogs', 'slack']).describe('Webhook type (gogs or slack)'), config: z.object({ url: z.string().describe('URL to send webhook payloads to'), content_type: z.enum(['json', 'form']).describe('Content type for payload (json or form)'), secret: z.string().optional().describe('Optional secret for webhook validation'), channel: z.string().optional().describe('Slack channel (for slack webhooks)'), username: z.string().optional().describe('Slack username (for slack webhooks)'), icon_url: z.string().optional().describe('Slack icon URL (for slack webhooks)'), color: z.string().optional().describe('Slack color (for slack webhooks)'), }).describe('Webhook configuration'), events: z.array(z.enum(['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release'])) .optional() .describe('Events that trigger the webhook (default: ["push"])'), active: z.boolean().optional().describe('Whether the webhook is active (default: false)'), }); const UpdateHookSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), hookId: z.number().describe('Webhook ID to update'), config: z.object({ url: z.string().describe('URL to send webhook payloads to'), content_type: z.enum(['json', 'form']).describe('Content type for payload (json or form)'), secret: z.string().optional().describe('Optional secret for webhook validation'), channel: z.string().optional().describe('Slack channel (for slack webhooks)'), username: z.string().optional().describe('Slack username (for slack webhooks)'), icon_url: z.string().optional().describe('Slack icon URL (for slack webhooks)'), color: z.string().optional().describe('Slack color (for slack webhooks)'), }).describe('Webhook configuration'), events: z.array(z.enum(['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release'])) .optional() .describe('Events that trigger the webhook'), active: z.boolean().optional().describe('Whether the webhook is active'), }); const DeleteHookSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), hookId: z.number().describe('Webhook ID to delete'), }); const ListFollowersSchema = z.object({ username: z.string().describe('Username to list followers for'), }); const ListFollowingSchema = z.object({ username: z.string().describe('Username to list following for'), }); const CheckFollowingSchema = z.object({ username: z.string().describe('Username to check if authenticated user is following'), }); const FollowUserSchema = z.object({ username: z.string().describe('Username to follow'), }); const UnfollowUserSchema = z.object({ username: z.string().describe('Username to unfollow'), }); const ListDeployKeysSchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), }); const GetDeployKeySchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), keyId: z.number().describe('Deploy key ID'), }); const CreateDeployKeySchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), title: z.string().describe('Deploy key title/name for identification'), key: z.string().describe('The public key content (SSH public key format)'), read_only: z.boolean().optional().describe('Whether the key is read-only (default: true)'), }); const DeleteDeployKeySchema = z.object({ owner: z.string().describe('Repository owner username'), repo: z.string().describe('Repository name'), keyId: z.number().describe('Deploy key ID to delete'), }); const RenderMarkdownSchema = z.object({ text: z.string().describe('Markdown text to render'), mode: z.enum(['gfm', 'markdown']).optional().describe('Rendering mode: "gfm" for GitHub Flavored Markdown (default) or "markdown" for standard Markdown'), context: z.string().optional().describe('Repository context in the format "owner/repo" for repository-aware rendering (e.g., for issue references)'), }); const RenderMarkdownRawSchema = z.object({ text: z.string().describe('Raw markdown text to render to HTML'), }); /** * 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: 'get_tree', description: 'Get a git tree by SHA - provides low-level access to repository structure at a specific commit or tree SHA', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, sha: { type: 'string', description: 'Git tree SHA or commit SHA', }, }, required: ['owner', 'repo', 'sha'], }, }, { 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'], }, }, { name: 'list_milestones', description: 'List all milestones 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_milestone', description: 'Get a specific milestone by ID', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, milestoneId: { type: 'number', description: 'Milestone ID', }, }, required: ['owner', 'repo', 'milestoneId'], }, }, { name: 'create_milestone', description: 'Create a new milestone in a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, title: { type: 'string', description: 'Milestone title', }, description: { type: 'string', description: 'Milestone description', }, due_on: { type: 'string', description: 'Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)', }, }, required: ['owner', 'repo', 'title'], }, }, { name: 'update_milestone', description: 'Update an existing milestone', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, milestoneId: { type: 'number', description: 'Milestone ID', }, title: { type: 'string', description: 'Milestone title', }, description: { type: 'string', description: 'Milestone description', }, due_on: { type: 'string', description: 'Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)', }, state: { type: 'string', enum: ['open', 'closed'], description: 'Milestone state', }, }, required: ['owner', 'repo', 'milestoneId'], }, }, { name: 'delete_milestone', description: 'Delete a milestone from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, milestoneId: { type: 'number', description: 'Milestone ID', }, }, required: ['owner', 'repo', 'milestoneId'], }, }, { name: 'list_user_organizations', description: 'List organizations for the authenticated user', inputSchema: { type: 'object', properties: {}, }, }, { name: 'create_organization', description: 'Create a new organization', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Organization username (alphanumeric, dashes, underscores)', }, full_name: { type: 'string', description: 'Organization full name', }, description: { type: 'string', description: 'Organization description', }, website: { type: 'string', description: 'Organization website', }, location: { type: 'string', description: 'Organization location', }, }, required: ['username'], }, }, { name: 'list_public_organizations', description: 'List public organizations for a specific user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to get public organizations for', }, }, required: ['username'], }, }, { name: 'get_organization', description: 'Get information about a specific organization', inputSchema: { type: 'object', properties: { orgname: { type: 'string', description: 'Organization name', }, }, required: ['orgname'], }, }, { name: 'update_organization', description: 'Update an organization (requires owner permissions)', inputSchema: { type: 'object', properties: { orgname: { type: 'string', description: 'Organization name', }, full_name: { type: 'string', description: 'Organization full name', }, description: { type: 'string', description: 'Organization description', }, website: { type: 'string', description: 'Organization website', }, location: { type: 'string', description: 'Organization location', }, }, required: ['orgname'], }, }, { name: 'add_organization_member', description: 'Add or update organization membership (requires owner permissions)', inputSchema: { type: 'object', properties: { orgname: { type: 'string', description: 'Organization name', }, username: { type: 'string', description: 'Username to add as member', }, role: { type: 'string', enum: ['admin', 'member'], description: 'Member role (admin or member)', }, }, required: ['orgname', 'username', 'role'], }, }, { name: 'list_organization_teams', description: 'List teams in an organization', inputSchema: { type: 'object', properties: { orgname: { type: 'string', description: 'Organization name', }, }, required: ['orgname'], }, }, { name: 'create_team', description: 'Create a new team in an organization (requires admin permissions)', inputSchema: { type: 'object', properties: { orgname: { type: 'string', description: 'Organization name', }, name: { type: 'string', description: 'Team name (max 30 characters, alphanumeric with dashes and dots)', }, description: { type: 'string', description: 'Team description (max 255 characters)', }, permission: { type: 'string', enum: ['read', 'write', 'admin'], description: 'Team permission level (default: read)', }, }, required: ['orgname', 'name'], }, }, { name: 'get_team', description: 'Get information about a specific team', inputSchema: { type: 'object', properties: { teamId: { type: 'number', description: 'Team ID', }, }, required: ['teamId'], }, }, { name: 'add_team_member', description: 'Add a user to a team (requires admin permissions)', inputSchema: { type: 'object', properties: { teamId: { type: 'number', description: 'Team ID', }, username: { type: 'string', description: 'Username to add to the team', }, }, required: ['teamId', 'username'], }, }, { name: 'remove_team_member', description: 'Remove a user from a team (requires admin permissions)', inputSchema: { type: 'object', properties: { teamId: { type: 'number', description: 'Team ID', }, username: { type: 'string', description: 'Username to remove from the team', }, }, required: ['teamId', 'username'], }, }, { name: 'list_releases', description: 'List all releases for a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'list_collaborators', description: 'List all collaborators for a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'check_collaborator', description: 'Check if a user is a collaborator on a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, username: { type: 'string', description: 'Username to check for collaborator status', }, }, required: ['owner', 'repo', 'username'], }, }, { name: 'add_collaborator', description: 'Add a collaborator to a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, username: { type: 'string', description: 'Username to add as collaborator', }, permission: { type: 'string', enum: ['read', 'write', 'admin'], description: 'Permission level (default: write)', }, }, required: ['owner', 'repo', 'username'], }, }, { name: 'remove_collaborator', description: 'Remove a collaborator from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, username: { type: 'string', description: 'Username to remove as collaborator', }, }, required: ['owner', 'repo', 'username'], }, }, { name: 'list_user_emails', description: 'List email addresses for the authenticated user', inputSchema: { type: 'object', properties: {}, }, }, { name: 'add_user_emails', description: 'Add email address(es) to the authenticated user account', inputSchema: { type: 'object', properties: { emails: { type: 'array', items: { type: 'string', }, description: 'Array of email addresses to add', }, }, required: ['emails'], }, }, { name: 'delete_user_emails', description: 'Delete email address(es) from the authenticated user account', inputSchema: { type: 'object', properties: { emails: { type: 'array', items: { type: 'string', }, description: 'Array of email addresses to delete', }, }, required: ['emails'], }, }, { name: 'list_user_keys', description: 'List public SSH keys for a specific user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to list public keys for', }, }, required: ['username'], }, }, { name: 'list_my_keys', description: 'List public SSH keys for the authenticated user', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_public_key', description: 'Get details of a specific public SSH key by ID', inputSchema: { type: 'object', properties: { keyId: { type: 'number', description: 'Public key ID', }, }, required: ['keyId'], }, }, { name: 'create_public_key', description: 'Create a new public SSH key for the authenticated user', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Key title/name for identification', }, key: { type: 'string', description: 'The public key content (SSH public key format)', }, }, required: ['title', 'key'], }, }, { name: 'delete_public_key', description: 'Delete a public SSH key by ID', inputSchema: { type: 'object', properties: { keyId: { type: 'number', description: 'Public key ID to delete', }, }, required: ['keyId'], }, }, { name: 'list_hooks', description: 'List all webhooks for a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'create_hook', description: 'Create a new webhook for a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, type: { type: 'string', enum: ['gogs', 'slack'], description: 'Webhook type (gogs or slack)', }, config: { type: 'object', properties: { url: { type: 'string', description: 'URL to send webhook payloads to', }, content_type: { type: 'string', enum: ['json', 'form'], description: 'Content type for payload (json or form)', }, secret: { type: 'string', description: 'Optional secret for webhook validation', }, channel: { type: 'string', description: 'Slack channel (for slack webhooks)', }, username: { type: 'string', description: 'Slack username (for slack webhooks)', }, icon_url: { type: 'string', description: 'Slack icon URL (for slack webhooks)', }, color: { type: 'string', description: 'Slack color (for slack webhooks)', }, }, required: ['url', 'content_type'], description: 'Webhook configuration', }, events: { type: 'array', items: { type: 'string', enum: ['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release'], }, description: 'Events that trigger the webhook (default: ["push"])', }, active: { type: 'boolean', description: 'Whether the webhook is active (default: false)', }, }, required: ['owner', 'repo', 'type', 'config'], }, }, { name: 'update_hook', description: 'Update an existing webhook', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, hookId: { type: 'number', description: 'Webhook ID to update', }, config: { type: 'object', properties: { url: { type: 'string', description: 'URL to send webhook payloads to', }, content_type: { type: 'string', enum: ['json', 'form'], description: 'Content type for payload (json or form)', }, secret: { type: 'string', description: 'Optional secret for webhook validation', }, channel: { type: 'string', description: 'Slack channel (for slack webhooks)', }, username: { type: 'string', description: 'Slack username (for slack webhooks)', }, icon_url: { type: 'string', description: 'Slack icon URL (for slack webhooks)', }, color: { type: 'string', description: 'Slack color (for slack webhooks)', }, }, required: ['url', 'content_type'], description: 'Webhook configuration', }, events: { type: 'array', items: { type: 'string', enum: ['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release'], }, description: 'Events that trigger the webhook', }, active: { type: 'boolean', description: 'Whether the webhook is active', }, }, required: ['owner', 'repo', 'hookId', 'config'], }, }, { name: 'delete_hook', description: 'Delete a webhook from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, hookId: { type: 'number', description: 'Webhook ID to delete', }, }, required: ['owner', 'repo', 'hookId'], }, }, { name: 'list_followers', description: 'List followers of a specific user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to list followers for', }, }, required: ['username'], }, }, { name: 'list_following', description: 'List users that a specific user is following', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to list following for', }, }, required: ['username'], }, }, { name: 'check_following', description: 'Check if the authenticated user is following a target user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to check if authenticated user is following', }, }, required: ['username'], }, }, { name: 'follow_user', description: 'Follow a user (authenticated user follows the target user)', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to follow', }, }, required: ['username'], }, }, { name: 'unfollow_user', description: 'Unfollow a user (authenticated user unfollows the target user)', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username to unfollow', }, }, required: ['username'], }, }, { name: 'list_deploy_keys', description: 'List all deploy keys for a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, }, required: ['owner', 'repo'], }, }, { name: 'get_deploy_key', description: 'Get details of a specific deploy key', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, keyId: { type: 'number', description: 'Deploy key ID', }, }, required: ['owner', 'repo', 'keyId'], }, }, { name: 'create_deploy_key', description: 'Add a new deploy key to a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, title: { type: 'string', description: 'Deploy key title/name for identification', }, key: { type: 'string', description: 'The public key content (SSH public key format)', }, read_only: { type: 'boolean', description: 'Whether the key is read-only (default: true)', }, }, required: ['owner', 'repo', 'title', 'key'], }, }, { name: 'delete_deploy_key', description: 'Remove a deploy key from a repository', inputSchema: { type: 'object', properties: { owner: { type: 'string', description: 'Repository owner username', }, repo: { type: 'string', description: 'Repository name', }, keyId: { type: 'number', description: 'Deploy key ID to delete', }, }, required: ['owner', 'repo', 'keyId'], }, }, { name: 'render_markdown', description: 'Render markdown text to HTML with support for GitHub Flavored Markdown (GFM) and repository context', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'Markdown text to render', }, mode: { type: 'string', enum: ['gfm', 'markdown'], description: 'Rendering mode: "gfm" for GitHub Flavored Markdown (default) or "markdown" for standard Markdown', }, context: { type: 'string', description: 'Repository context in the format "owner/repo" for repository-aware rendering (e.g., for issue references)', }, }, required: ['text'], }, }, { name: 'render_markdown_raw', description: 'Render raw markdown text to HTML (simpler endpoint, plain text input)', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'Raw markdown text to render to HTML', }, }, required: ['text'], }, }, ], }; }); // 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 'get_tree': { const { owner, repo, sha } = GetTreeSchema.parse(args); const tree = await gogsClient.getTree(owner, repo, sha); return { content: [ { type: 'text', text: JSON.stringify(tree, 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}`, }, ], }; } case 'list_milestones': { const { owner, repo } = ListMilestonesSchema.parse(args); const milestones = await gogsClient.listMilestones(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(milestones, null, 2), }, ], }; } case 'get_milestone': { const { owner, repo, milestoneId } = GetMilestoneSchema.parse(args); const milestone = await gogsClient.getMilestone(owner, repo, milestoneId); return { content: [ { type: 'text', text: JSON.stringify(milestone, null, 2), }, ], }; } case 'create_milestone': { const { owner, repo, title, description, due_on } = CreateMilestoneSchema.parse(args); const milestone = await gogsClient.createMilestone(owner, repo, { title, description, due_on, }); return { content: [ { type: 'text', text: JSON.stringify(milestone, null, 2), }, ], }; } case 'update_milestone': { const { owner, repo, milestoneId, title, description, due_on, state } = UpdateMilestoneSchema.parse(args); const milestone = await gogsClient.updateMilestone(owner, repo, milestoneId, { title, description, due_on, state, }); return { content: [ { type: 'text', text: JSON.stringify(milestone, null, 2), }, ], }; } case 'delete_milestone': { const { owner, repo, milestoneId } = DeleteMilestoneSchema.parse(args); await gogsClient.deleteMilestone(owner, repo, milestoneId); return { content: [ { type: 'text', text: `Milestone ${milestoneId} deleted successfully from ${owner}/${repo}`, }, ], }; } case 'list_user_organizations': { const organizations = await gogsClient.listUserOrganizations(); return { content: [ { type: 'text', text: JSON.stringify(organizations, null, 2), }, ], }; } case 'create_organization': { const { username, full_name, description, website, location } = CreateOrganizationSchema.parse(args); const organization = await gogsClient.createOrganization({ username, full_name, description, website, location, }); return { content: [ { type: 'text', text: JSON.stringify(organization, null, 2), }, ], }; } case 'list_public_organizations': { const { username } = ListPublicOrganizationsSchema.parse(args); const organizations = await gogsClient.listPublicOrganizations(username); return { content: [ { type: 'text', text: JSON.stringify(organizations, null, 2), }, ], }; } case 'get_organization': { const { orgname } = GetOrganizationSchema.parse(args); const organization = await gogsClient.getOrganization(orgname); return { content: [ { type: 'text', text: JSON.stringify(organization, null, 2), }, ], }; } case 'update_organization': { const { orgname, full_name, description, website, location } = UpdateOrganizationSchema.parse(args); const organization = await gogsClient.updateOrganization(orgname, { full_name, description, website, location, }); return { content: [ { type: 'text', text: JSON.stringify(organization, null, 2), }, ], }; } case 'add_organization_member': { const { orgname, username, role } = AddOrganizationMemberSchema.parse(args); await gogsClient.addOrganizationMember(orgname, username, role); return { content: [ { type: 'text', text: `User ${username} added to organization ${orgname} with role ${role}`, }, ], }; } case 'list_organization_teams': { const { orgname } = ListOrganizationTeamsSchema.parse(args); const teams = await gogsClient.listOrganizationTeams(orgname); return { content: [ { type: 'text', text: JSON.stringify(teams, null, 2), }, ], }; } case 'create_team': { const { orgname, name, description, permission } = CreateTeamSchema.parse(args); const team = await gogsClient.createTeam(orgname, { name, description, permission, }); return { content: [ { type: 'text', text: JSON.stringify(team, null, 2), }, ], }; } case 'get_team': { const { teamId } = GetTeamSchema.parse(args); const team = await gogsClient.getTeam(teamId); return { content: [ { type: 'text', text: JSON.stringify(team, null, 2), }, ], }; } case 'add_team_member': { const { teamId, username } = AddTeamMemberSchema.parse(args); await gogsClient.addTeamMember(teamId, username); return { content: [ { type: 'text', text: `User ${username} added to team ${teamId} successfully`, }, ], }; } case 'remove_team_member': { const { teamId, username } = RemoveTeamMemberSchema.parse(args); await gogsClient.removeTeamMember(teamId, username); return { content: [ { type: 'text', text: `User ${username} removed from team ${teamId} successfully`, }, ], }; } case 'list_releases': { const { owner, repo } = ListReleasesSchema.parse(args); const releases = await gogsClient.listReleases(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(releases, null, 2), }, ], }; } case 'list_collaborators': { const { owner, repo } = ListCollaboratorsSchema.parse(args); const collaborators = await gogsClient.listCollaborators(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(collaborators, null, 2), }, ], }; } case 'check_collaborator': { const { owner, repo, username } = CheckCollaboratorSchema.parse(args); const isCollaborator = await gogsClient.checkCollaborator(owner, repo, username); return { content: [ { type: 'text', text: JSON.stringify({ isCollaborator, username, repository: `${owner}/${repo}` }, null, 2), }, ], }; } case 'add_collaborator': { const { owner, repo, username, permission } = AddCollaboratorSchema.parse(args); await gogsClient.addCollaborator(owner, repo, username, permission); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully added ${username} as collaborator to ${owner}/${repo}`, permission: permission || 'write' }, null, 2), }, ], }; } case 'remove_collaborator': { const { owner, repo, username } = RemoveCollaboratorSchema.parse(args); await gogsClient.removeCollaborator(owner, repo, username); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully removed ${username} as collaborator from ${owner}/${repo}` }, null, 2), }, ], }; } case 'list_user_emails': { const emails = await gogsClient.listUserEmails(); return { content: [ { type: 'text', text: JSON.stringify(emails, null, 2), }, ], }; } case 'add_user_emails': { const { emails } = AddUserEmailsSchema.parse(args); const addedEmails = await gogsClient.addUserEmails(emails); return { content: [ { type: 'text', text: JSON.stringify(addedEmails, null, 2), }, ], }; } case 'delete_user_emails': { const { emails } = DeleteUserEmailsSchema.parse(args); await gogsClient.deleteUserEmails(emails); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully deleted ${emails.length} email address(es)`, emails }, null, 2), }, ], }; } case 'list_user_keys': { const { username } = ListUserKeysSchema.parse(args); const keys = await gogsClient.listUserKeys(username); return { content: [ { type: 'text', text: JSON.stringify(keys, null, 2), }, ], }; } case 'list_my_keys': { const keys = await gogsClient.listMyKeys(); return { content: [ { type: 'text', text: JSON.stringify(keys, null, 2), }, ], }; } case 'get_public_key': { const { keyId } = GetPublicKeySchema.parse(args); const key = await gogsClient.getPublicKey(keyId); return { content: [ { type: 'text', text: JSON.stringify(key, null, 2), }, ], }; } case 'create_public_key': { const { title, key } = CreatePublicKeySchema.parse(args); const newKey = await gogsClient.createPublicKey({ title, key }); return { content: [ { type: 'text', text: JSON.stringify(newKey, null, 2), }, ], }; } case 'delete_public_key': { const { keyId } = DeletePublicKeySchema.parse(args); await gogsClient.deletePublicKey(keyId); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully deleted public key with ID ${keyId}`, keyId }, null, 2), }, ], }; } case 'list_hooks': { const { owner, repo } = ListHooksSchema.parse(args); const hooks = await gogsClient.listHooks(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(hooks, null, 2), }, ], }; } case 'create_hook': { const { owner, repo, type, config, events, active } = CreateHookSchema.parse(args); const hook = await gogsClient.createHook(owner, repo, { type, config, events, active, }); return { content: [ { type: 'text', text: JSON.stringify(hook, null, 2), }, ], }; } case 'update_hook': { const { owner, repo, hookId, config, events, active } = UpdateHookSchema.parse(args); const hook = await gogsClient.updateHook(owner, repo, hookId, { config, events, active, }); return { content: [ { type: 'text', text: JSON.stringify(hook, null, 2), }, ], }; } case 'delete_hook': { const { owner, repo, hookId } = DeleteHookSchema.parse(args); await gogsClient.deleteHook(owner, repo, hookId); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully deleted webhook ${hookId} from ${owner}/${repo}`, hookId, repository: `${owner}/${repo}` }, null, 2), }, ], }; } case 'list_followers': { const { username } = ListFollowersSchema.parse(args); const followers = await gogsClient.listFollowers(username); return { content: [ { type: 'text', text: JSON.stringify(followers, null, 2), }, ], }; } case 'list_following': { const { username } = ListFollowingSchema.parse(args); const following = await gogsClient.listFollowing(username); return { content: [ { type: 'text', text: JSON.stringify(following, null, 2), }, ], }; } case 'check_following': { const { username } = CheckFollowingSchema.parse(args); const isFollowing = await gogsClient.checkFollowing(username); return { content: [ { type: 'text', text: JSON.stringify({ isFollowing, username, message: isFollowing ? `You are following ${username}` : `You are not following ${username}` }, null, 2), }, ], }; } case 'follow_user': { const { username } = FollowUserSchema.parse(args); await gogsClient.followUser(username); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully followed user ${username}`, username }, null, 2), }, ], }; } case 'unfollow_user': { const { username } = UnfollowUserSchema.parse(args); await gogsClient.unfollowUser(username); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully unfollowed user ${username}`, username }, null, 2), }, ], }; } case 'list_deploy_keys': { const { owner, repo } = ListDeployKeysSchema.parse(args); const keys = await gogsClient.listDeployKeys(owner, repo); return { content: [ { type: 'text', text: JSON.stringify(keys, null, 2), }, ], }; } case 'get_deploy_key': { const { owner, repo, keyId } = GetDeployKeySchema.parse(args); const key = await gogsClient.getDeployKey(owner, repo, keyId); return { content: [ { type: 'text', text: JSON.stringify(key, null, 2), }, ], }; } case 'create_deploy_key': { const { owner, repo, title, key, read_only } = CreateDeployKeySchema.parse(args); const deployKey = await gogsClient.createDeployKey(owner, repo, { title, key, read_only, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Deploy key created successfully', key: deployKey }, null, 2), }, ], }; } case 'delete_deploy_key': { const { owner, repo, keyId } = DeleteDeployKeySchema.parse(args); await gogsClient.deleteDeployKey(owner, repo, keyId); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully deleted deploy key ${keyId}`, keyId }, null, 2), }, ], }; } case 'render_markdown': { const { text, mode, context } = RenderMarkdownSchema.parse(args); const html = await gogsClient.renderMarkdown({ text, mode, context, }); return { content: [ { type: 'text', text: html, }, ], }; } case 'render_markdown_raw': { const { text } = RenderMarkdownRawSchema.parse(args); const html = await gogsClient.renderMarkdownRaw(text); return { content: [ { type: 'text', text: html, }, ], }; } 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; }