Преглед изворни кода

feat: add User Public Keys support #12

Implemented full support for managing SSH public keys through the MCP server:

- Added GogsPublicKey TypeScript interface in types.ts
- Implemented 5 API client methods in gogs-client.ts:
  - listUserKeys(username): List keys for a specific user
  - listMyKeys(): List authenticated user's keys
  - getPublicKey(keyId): Get key details by ID
  - createPublicKey({title, key}): Create new key
  - deletePublicKey(keyId): Delete a key
- Added 5 MCP tools with Zod schemas in server.ts:
  - list_user_keys: List keys for a user
  - list_my_keys: List authenticated user's keys
  - get_public_key: Get key by ID
  - create_public_key: Create new SSH key
  - delete_public_key: Delete SSH key
- Updated README.md with new tools documentation and examples
- Updated CLAUDE.md with Public Key Management category
- Removed implemented endpoints from "not yet implemented" table

All tools tested successfully against live Gogs API.

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

Co-Authored-By: Claude <noreply@anthropic.com>
claude пре 9 месеци
родитељ
комит
deebc5d00d
5 измењених фајлова са 257 додато и 5 уклоњено
  1. 1 0
      CLAUDE.md
  2. 50 5
      README.md
  3. 43 0
      src/gogs-client.ts
  4. 153 0
      src/server.ts
  5. 10 0
      src/types.ts

+ 1 - 0
CLAUDE.md

@@ -38,6 +38,7 @@ The server exposes tools in these categories:
 - **Team Management**: list_organization_teams, create_team, get_team, add_team_member, remove_team_member
 - **Release Management**: list_releases
 - **Collaborator Management**: list_collaborators, check_collaborator, add_collaborator, remove_collaborator
+- **Public Key Management**: list_user_keys, list_my_keys, get_public_key, create_public_key, delete_public_key
 
 ## Development Commands
 

+ 50 - 5
README.md

@@ -71,6 +71,13 @@ This MCP server provides tools to:
 - **Release Management**
   - List releases for a repository
 
+- **Public Key Management**
+  - List public SSH keys for a user
+  - List authenticated user's public SSH keys
+  - Get details of a specific public SSH key
+  - Create new public SSH key
+  - Delete public SSH key
+
 ## Installation
 
 ### Prerequisites
@@ -653,6 +660,49 @@ Remove a user's collaborator access from a repository.
 - `repo` (string, required): Repository name
 - `username` (string, required): Username to remove as collaborator
 
+### Public Key Management Tools
+
+#### `list_user_keys`
+List all public SSH keys for a specific user. Returns key information including ID, title, key content, URL, and creation timestamp.
+- `username` (string, required): Username to list public keys for
+
+#### `list_my_keys`
+List all public SSH keys for the authenticated user. Returns key information including ID, title, key content, URL, and creation timestamp.
+
+**Example response:**
+```json
+[
+  {
+    "id": 2,
+    "title": "my-laptop-key",
+    "key": "ssh-ed25519 AAAAC3Nza...",
+    "url": "https://gogs.example.com/api/v1/user/keys/2",
+    "created_at": "2025-10-28T11:09:29Z"
+  }
+]
+```
+
+#### `get_public_key`
+Get details of a specific public SSH key by ID. Returns full key information.
+- `keyId` (number, required): Public key ID
+
+#### `create_public_key`
+Create a new public SSH key for the authenticated user. Returns the created key with its assigned ID.
+- `title` (string, required): Key title/name for identification
+- `key` (string, required): The public key content in SSH public key format (e.g., "ssh-ed25519 AAAA...")
+
+**Example:**
+```json
+{
+  "title": "My Development Key",
+  "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBf... user@hostname"
+}
+```
+
+#### `delete_public_key`
+Delete a public SSH key by ID. Only the authenticated user can delete their own keys.
+- `keyId` (number, required): Public key ID to delete
+
 ## Documented but Not Yet Implemented API Endpoints
 
 The following Gogs API v1 endpoints are officially documented but not yet implemented in this MCP server. These represent potential features for future development:
@@ -667,11 +717,6 @@ The following Gogs API v1 endpoints are officially documented but not yet implem
 | **Team Administration** | `/admin/teams/:teamid/members` | GET | List all members of a team | [Link](https://github.com/gogs/docs-api/blob/master/Administration/Teams.md#list-all-members-of-a-team) |
 | **Team Administration** | `/admin/teams/:teamid/repos/:reponame` | PUT | Add or update team repository | [Link](https://github.com/gogs/docs-api/blob/master/Administration/Teams.md#add-or-update-team-repository) |
 | **Team Administration** | `/admin/teams/:teamid/repos/:reponame` | DELETE | Remove team repository | [Link](https://github.com/gogs/docs-api/blob/master/Administration/Teams.md#remove-team-repository) |
-| **Public Keys** | `/users/:username/keys` | GET | List public keys for a user | [Link](https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md) |
-| **Public Keys** | `/user/keys` | GET | List your public keys | [Link](https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md) |
-| **Public Keys** | `/user/keys/:id` | GET | Get a single public key | [Link](https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md) |
-| **Public Keys** | `/user/keys` | POST | Create a public key | [Link](https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md) |
-| **Public Keys** | `/user/keys/:id` | DELETE | Delete a public key | [Link](https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md) |
 | **Deploy Keys** | `/repos/:username/:reponame/keys` | GET | List deploy keys | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Deploy%20Keys.md) |
 | **Deploy Keys** | `/repos/:username/:reponame/keys/:id` | GET | Get a deploy key | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Deploy%20Keys.md) |
 | **Deploy Keys** | `/repos/:username/:reponame/keys` | POST | Add a new deploy key | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Deploy%20Keys.md) |

+ 43 - 0
src/gogs-client.ts

@@ -22,6 +22,7 @@ import type {
   GogsRelease,
   GogsCollaborator,
   GogsEmail,
+  GogsPublicKey,
 } from './types.js';
 
 export class GogsClient {
@@ -694,4 +695,46 @@ export class GogsClient {
   async deleteUserEmails(emails: string[]): Promise<void> {
     await this.client.delete('/user/emails', { data: { emails } });
   }
+
+  /**
+   * List public keys for a specific user
+   */
+  async listUserKeys(username: string): Promise<GogsPublicKey[]> {
+    const response = await this.client.get<GogsPublicKey[]>(`/users/${username}/keys`);
+    return response.data;
+  }
+
+  /**
+   * List public keys for the authenticated user
+   */
+  async listMyKeys(): Promise<GogsPublicKey[]> {
+    const response = await this.client.get<GogsPublicKey[]>('/user/keys');
+    return response.data;
+  }
+
+  /**
+   * Get a single public key by ID
+   */
+  async getPublicKey(keyId: number): Promise<GogsPublicKey> {
+    const response = await this.client.get<GogsPublicKey>(`/user/keys/${keyId}`);
+    return response.data;
+  }
+
+  /**
+   * Create a new public key for the authenticated user
+   */
+  async createPublicKey(data: {
+    title: string;
+    key: string;
+  }): Promise<GogsPublicKey> {
+    const response = await this.client.post<GogsPublicKey>('/user/keys', data);
+    return response.data;
+  }
+
+  /**
+   * Delete a public key by ID
+   */
+  async deletePublicKey(keyId: number): Promise<void> {
+    await this.client.delete(`/user/keys/${keyId}`);
+  }
 }

+ 153 - 0
src/server.ts

@@ -335,6 +335,23 @@ 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'),
+});
+
 /**
  * Create and configure an MCP server with all tools and handlers
  */
@@ -1585,6 +1602,74 @@ export function createMcpServer(gogsClient: GogsClient): Server {
             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'],
+          },
+        },
       ],
     };
   });
@@ -2358,6 +2443,74 @@ export function createMcpServer(gogsClient: GogsClient): Server {
           };
         }
 
+        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),
+              },
+            ],
+          };
+        }
+
         default:
           throw new Error(`Unknown tool: ${name}`);
       }

+ 10 - 0
src/types.ts

@@ -202,3 +202,13 @@ export interface GogsEmail {
   verified: boolean;
   primary: boolean;
 }
+
+export interface GogsPublicKey {
+  id: number;
+  title: string;
+  key: string;
+  url?: string;
+  created_at: string;
+  read_only?: boolean;
+  fingerprint?: string;
+}