瀏覽代碼

feat: add team management tools #6

Implemented missing team functionality for Organizations & Teams support:
- Added createTeam() method to create new teams in organizations
- Added getTeam() method to retrieve team details by ID
- Added addTeamMember() method to add users to teams
- Added removeTeamMember() method to remove users from teams

Also included previously uncommitted createOrganization() functionality.

All new tools are exposed via MCP with proper schemas and handlers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
claude 9 月之前
父節點
當前提交
79de499b11
共有 2 個文件被更改,包括 265 次插入0 次删除
  1. 54 0
      src/gogs-client.ts
  2. 211 0
      src/server.ts

+ 54 - 0
src/gogs-client.ts

@@ -437,6 +437,20 @@ export class GogsClient {
     return response.data;
   }
 
+  /**
+   * Create a new organization
+   */
+  async createOrganization(data: {
+    username: string;
+    full_name?: string;
+    description?: string;
+    website?: string;
+    location?: string;
+  }): Promise<GogsOrganization> {
+    const response = await this.client.post<GogsOrganization>('/user/orgs', data);
+    return response.data;
+  }
+
   /**
    * List public organizations for a specific user
    */
@@ -487,4 +501,44 @@ export class GogsClient {
     const response = await this.client.get<GogsTeam[]>(`/orgs/${orgname}/teams`);
     return response.data;
   }
+
+  /**
+   * Create a new team in an organization
+   */
+  async createTeam(
+    orgname: string,
+    data: {
+      name: string;
+      description?: string;
+      permission?: 'read' | 'write' | 'admin';
+    }
+  ): Promise<GogsTeam> {
+    const response = await this.client.post<GogsTeam>(
+      `/admin/orgs/${orgname}/teams`,
+      data
+    );
+    return response.data;
+  }
+
+  /**
+   * Get a specific team by ID
+   */
+  async getTeam(teamId: number): Promise<GogsTeam> {
+    const response = await this.client.get<GogsTeam>(`/teams/${teamId}`);
+    return response.data;
+  }
+
+  /**
+   * Add a user to a team
+   */
+  async addTeamMember(teamId: number, username: string): Promise<void> {
+    await this.client.put(`/admin/teams/${teamId}/members/${username}`);
+  }
+
+  /**
+   * Remove a user from a team
+   */
+  async removeTeamMember(teamId: number, username: string): Promise<void> {
+    await this.client.delete(`/admin/teams/${teamId}/members/${username}`);
+  }
 }

+ 211 - 0
src/server.ts

@@ -202,6 +202,14 @@ const RemoveAllIssueLabelsSchema = z.object({
   number: z.number().describe('Issue number'),
 });
 
+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'),
 });
@@ -228,6 +236,27 @@ 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'),
+});
+
 /**
  * Create and configure an MCP server with all tools and handlers
  */
@@ -974,6 +1003,36 @@ export function createMcpServer(gogsClient: GogsClient): Server {
             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',
@@ -1069,6 +1128,83 @@ export function createMcpServer(gogsClient: GogsClient): Server {
             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'],
+          },
+        },
       ],
     };
   });
@@ -1496,6 +1632,25 @@ export function createMcpServer(gogsClient: GogsClient): Server {
           };
         }
 
+        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);
@@ -1566,6 +1721,62 @@ export function createMcpServer(gogsClient: GogsClient): Server {
           };
         }
 
+        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`,
+              },
+            ],
+          };
+        }
+
         default:
           throw new Error(`Unknown tool: ${name}`);
       }