Procházet zdrojové kódy

feat: add releases support #11

Implemented list_releases tool for listing repository releases with full metadata including:
- Tag name, target commitish, release name and description
- Draft and prerelease flags
- Author information
- Timestamps (created_at, published_at)
- Downloadable assets/attachments with URLs and stats

Changes:
- Added GogsRelease and GogsReleaseAttachment types to types.ts
- Implemented listReleases method in gogs-client.ts
- Added list_releases tool schema and handler in server.ts
- Updated README.md with releases documentation in features and available tools
- Updated CLAUDE.md with Release Management tool category
- Removed releases from unimplemented endpoints table

Note: Only read-only access is implemented as Gogs API v1 currently only documents the GET endpoint for releases.

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

Co-Authored-By: Claude <noreply@anthropic.com>
claude před 9 měsíci
rodič
revize
2d7178799b
6 změnil soubory, kde provedl 153 přidání a 1 odebrání
  1. 1 0
      CLAUDE.md
  2. 12 1
      README.md
  3. 71 0
      scripts/gogs-api.ts
  4. 9 0
      src/gogs-client.ts
  5. 36 0
      src/server.ts
  6. 24 0
      src/types.ts

+ 1 - 0
CLAUDE.md

@@ -35,6 +35,7 @@ The server exposes tools in these categories:
 - **Label Management**: list_labels, get_label, create_label, update_label, delete_label, list_issue_labels, add_issue_labels, remove_issue_label, replace_issue_labels, remove_all_issue_labels
 - **Organization Management**: list_user_organizations, create_organization, list_public_organizations, get_organization, update_organization, add_organization_member
 - **Team Management**: list_organization_teams, create_team, get_team, add_team_member, remove_team_member
+- **Release Management**: list_releases
 
 ## Development Commands
 

+ 12 - 1
README.md

@@ -61,6 +61,9 @@ This MCP server provides tools to:
   - Add member to a team
   - Remove member from a team
 
+- **Release Management**
+  - List releases for a repository
+
 ## Installation
 
 ### Prerequisites
@@ -560,6 +563,15 @@ Remove a user from a team (requires admin permissions).
 - `teamId` (number, required): Team ID
 - `username` (string, required): Username to remove from the team
 
+### Release Management Tools
+
+#### `list_releases`
+List all releases for a repository. Returns release information including tag name, name, description, author, timestamps, and downloadable assets (attachments).
+- `owner` (string, required): Repository owner username
+- `repo` (string, required): Repository name
+
+**Note**: Currently only read-only access to releases is supported. Create, update, and delete operations are not available in the Gogs API v1.
+
 ## 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:
@@ -590,7 +602,6 @@ The following Gogs API v1 endpoints are officially documented but not yet implem
 | **Collaborators** | `/repos/:username/:reponame/collaborators` | GET | Get collaborators | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Collaborators.md) |
 | **Collaborators** | `/repos/:username/:reponame/collaborators/:collaborator` | PUT | Add user as a collaborator | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Collaborators.md) |
 | **Collaborators** | `/repos/:username/:reponame/collaborators/:collaborator` | DELETE | Delete collaborator | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Collaborators.md) |
-| **Releases** | `/repos/:owner/:repo/releases` | GET | List releases | [Link](https://github.com/gogs/docs-api/blob/master/Repositories/Releases.md) |
 
 ### Contributing New Endpoints
 

+ 71 - 0
scripts/gogs-api.ts

@@ -0,0 +1,71 @@
+#!/usr/bin/env tsx
+
+/**
+ * Simple script to interact with Gogs API for issue management
+ */
+
+import axios from 'axios';
+import { config } from 'dotenv';
+
+// Load environment variables
+config();
+
+const GOGS_SERVER_URL = process.env.GOGS_SERVER_URL?.replace(/\/$/, '');
+const GOGS_ACCESS_TOKEN = process.env.GOGS_ACCESS_TOKEN;
+
+if (!GOGS_SERVER_URL || !GOGS_ACCESS_TOKEN) {
+  console.error('Error: GOGS_SERVER_URL and GOGS_ACCESS_TOKEN must be set');
+  process.exit(1);
+}
+
+const client = axios.create({
+  baseURL: `${GOGS_SERVER_URL}/api/v1`,
+  headers: {
+    'Content-Type': 'application/json',
+    Authorization: `token ${GOGS_ACCESS_TOKEN}`,
+  },
+});
+
+async function getIssue(owner: string, repo: string, number: number) {
+  const response = await client.get(`/repos/${owner}/${repo}/issues/${number}`);
+  return response.data;
+}
+
+async function listIssueComments(owner: string, repo: string, number: number) {
+  const response = await client.get(`/repos/${owner}/${repo}/issues/${number}/comments`);
+  return response.data;
+}
+
+async function createIssueComment(owner: string, repo: string, number: number, body: string) {
+  const response = await client.post(`/repos/${owner}/${repo}/issues/${number}/comments`, { body });
+  return response.data;
+}
+
+// Main execution
+const args = process.argv.slice(2);
+const command = args[0];
+
+(async () => {
+  try {
+    if (command === 'get-issue') {
+      const [owner, repo, number] = args.slice(1);
+      const issue = await getIssue(owner, repo, parseInt(number));
+      console.log(JSON.stringify(issue, null, 2));
+    } else if (command === 'list-comments') {
+      const [owner, repo, number] = args.slice(1);
+      const comments = await listIssueComments(owner, repo, parseInt(number));
+      console.log(JSON.stringify(comments, null, 2));
+    } else if (command === 'create-comment') {
+      const [owner, repo, number, ...bodyParts] = args.slice(1);
+      const body = bodyParts.join(' ');
+      const comment = await createIssueComment(owner, repo, parseInt(number), body);
+      console.log(JSON.stringify(comment, null, 2));
+    } else {
+      console.error('Unknown command. Use: get-issue, list-comments, or create-comment');
+      process.exit(1);
+    }
+  } catch (error: any) {
+    console.error('Error:', error.response?.data || error.message);
+    process.exit(1);
+  }
+})();

+ 9 - 0
src/gogs-client.ts

@@ -18,6 +18,7 @@ import type {
   GogsOrganization,
   GogsTeam,
   GogsTree,
+  GogsRelease,
 } from './types.js';
 
 export class GogsClient {
@@ -550,4 +551,12 @@ export class GogsClient {
   async removeTeamMember(teamId: number, username: string): Promise<void> {
     await this.client.delete(`/admin/teams/${teamId}/members/${username}`);
   }
+
+  /**
+   * List releases for a repository
+   */
+  async listReleases(owner: string, repo: string): Promise<GogsRelease[]> {
+    const response = await this.client.get<GogsRelease[]>(`/repos/${owner}/${repo}/releases`);
+    return response.data;
+  }
 }

+ 36 - 0
src/server.ts

@@ -263,6 +263,11 @@ const RemoveTeamMemberSchema = z.object({
   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'),
+});
+
 /**
  * Create and configure an MCP server with all tools and handlers
  */
@@ -1233,6 +1238,24 @@ export function createMcpServer(gogsClient: GogsClient): Server {
             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'],
+          },
+        },
       ],
     };
   });
@@ -1818,6 +1841,19 @@ export function createMcpServer(gogsClient: GogsClient): Server {
           };
         }
 
+        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),
+              },
+            ],
+          };
+        }
+
         default:
           throw new Error(`Unknown tool: ${name}`);
       }

+ 24 - 0
src/types.ts

@@ -158,3 +158,27 @@ export interface GogsTree {
   url: string;
   tree: GogsTreeEntry[];
 }
+
+export interface GogsReleaseAttachment {
+  id: number;
+  name: string;
+  size: number;
+  download_count: number;
+  created_at: string;
+  uuid: string;
+  browser_download_url: string;
+}
+
+export interface GogsRelease {
+  id: number;
+  tag_name: string;
+  target_commitish: string;
+  name: string;
+  body: string;
+  draft: boolean;
+  prerelease: boolean;
+  author: GogsUser;
+  created_at: string;
+  published_at: string;
+  assets: GogsReleaseAttachment[];
+}