Skip to main content
Glama
crud.go14.6 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // // Copyright © 2025 Ronmi Ren <ronmi.ren@gmail.com> package release import ( "context" "fmt" "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/raohwork/forgejo-mcp/tools" "github.com/raohwork/forgejo-mcp/types" ) // ListReleasesParams defines the parameters for the list_releases tool. // It specifies the owner and repository, with optional pagination. type ListReleasesParams struct { // Owner is the username or organization name that owns the repository. Owner string `json:"owner"` // Repo is the name of the repository. Repo string `json:"repo"` // Page is the page number for pagination. Page int `json:"page,omitempty"` // Limit is the number of releases to return per page. Limit int `json:"limit,omitempty"` } // ListReleasesImpl implements the read-only MCP tool for listing repository releases. // This is a safe, idempotent operation that uses the Forgejo SDK to fetch a // paginated list of releases. type ListReleasesImpl struct { Client *tools.Client } // Definition describes the `list_releases` tool. It requires `owner` and `repo` // and supports pagination. It is marked as a safe, read-only operation. func (ListReleasesImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "list_releases", Title: "List Releases", Description: "List all releases in a repository. Returns release information including tag name, title, description, and assets.", Annotations: &mcp.ToolAnnotations{ ReadOnlyHint: true, IdempotentHint: true, }, InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "owner": { Type: "string", Description: "Repository owner (username or organization name)", }, "repo": { Type: "string", Description: "Repository name", }, "page": { Type: "integer", Description: "Page number for pagination (optional, defaults to 1)", Minimum: tools.Float64Ptr(1), }, "limit": { Type: "integer", Description: "Number of releases per page (optional, defaults to 10, max 50)", Minimum: tools.Float64Ptr(1), Maximum: tools.Float64Ptr(50), }, }, Required: []string{"owner", "repo"}, }, } } // Handler implements the logic for listing releases. It calls the Forgejo SDK's // `ListReleases` function and formats the results into a markdown list. func (impl ListReleasesImpl) Handler() mcp.ToolHandlerFor[ListReleasesParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args ListReleasesParams) (*mcp.CallToolResult, any, error) { p := args // Build options for SDK call opt := forgejo.ListReleasesOptions{} if p.Page > 0 { opt.Page = p.Page } if p.Limit > 0 { opt.PageSize = p.Limit } // Call SDK releases, _, err := impl.Client.ListReleases(p.Owner, p.Repo, opt) if err != nil { return nil, nil, fmt.Errorf("failed to list releases: %w", err) } // Convert to our types and format var content string if len(releases) == 0 { content = "No releases found in this repository." } else { // Convert releases to our type releaseList := make(types.ReleaseList, len(releases)) for i, release := range releases { releaseList[i] = &types.Release{Release: release} } content = fmt.Sprintf("Found %d releases\n\n%s", len(releases), releaseList.ToMarkdown()) } return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: content, }, }, }, nil, nil } } // CreateReleaseParams defines the parameters for the create_release tool. // It includes all necessary details for creating a new release. type CreateReleaseParams struct { // Owner is the username or organization name that owns the repository. Owner string `json:"owner"` // Repo is the name of the repository. Repo string `json:"repo"` // TagName is the name of the git tag for the release. TagName string `json:"tag_name"` // TargetCommitish is the branch or commit SHA to create the release from. TargetCommitish string `json:"target_commitish,omitempty"` // Name is the title of the release. Name string `json:"name"` // Body is the markdown description of the release. Body string `json:"body,omitempty"` // Draft indicates whether the release is a draft. Draft bool `json:"draft,omitempty"` // Prerelease indicates whether the release is a pre-release. Prerelease bool `json:"prerelease,omitempty"` } // CreateReleaseImpl implements the MCP tool for creating a new release. // This is a non-idempotent operation that creates a new git tag and a // corresponding release object via the Forgejo SDK. type CreateReleaseImpl struct { Client *tools.Client } // Definition describes the `create_release` tool. It requires `owner`, `repo`, // `tag_name`, and a `name`. It is not idempotent, as multiple calls will fail // once the tag is created. func (CreateReleaseImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "create_release", Title: "Create Release", Description: "Create a new release in a repository. Specify tag name, title, description, and other metadata.", Annotations: &mcp.ToolAnnotations{ ReadOnlyHint: false, DestructiveHint: tools.BoolPtr(false), IdempotentHint: false, }, InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "owner": { Type: "string", Description: "Repository owner (username or organization name)", }, "repo": { Type: "string", Description: "Repository name", }, "tag_name": { Type: "string", Description: "Git tag name for this release", }, "target_commitish": { Type: "string", Description: "Target branch or commit SHA (optional, defaults to default branch)", }, "name": { Type: "string", Description: "Release title", }, "body": { Type: "string", Description: "Release description (markdown supported) (optional)", }, "draft": { Type: "boolean", Description: "Whether this is a draft release (optional, defaults to false)", }, "prerelease": { Type: "boolean", Description: "Whether this is a prerelease (optional, defaults to false)", }, }, Required: []string{"owner", "repo", "tag_name", "name"}, }, } } // Handler implements the logic for creating a release. It calls the Forgejo SDK's // `CreateRelease` function. On success, it returns details of the new release. func (impl CreateReleaseImpl) Handler() mcp.ToolHandlerFor[CreateReleaseParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args CreateReleaseParams) (*mcp.CallToolResult, any, error) { p := args // Build options for SDK call opt := forgejo.CreateReleaseOption{ TagName: p.TagName, Target: p.TargetCommitish, Title: p.Name, Note: p.Body, IsDraft: p.Draft, IsPrerelease: p.Prerelease, } // Call SDK release, _, err := impl.Client.CreateRelease(p.Owner, p.Repo, opt) if err != nil { return nil, nil, fmt.Errorf("failed to create release: %w", err) } // Convert to our type and format releaseWrapper := &types.Release{Release: release} return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: releaseWrapper.ToMarkdown(), }, }, }, nil, nil } } // EditReleaseParams defines the parameters for the edit_release tool. // It specifies the release to edit by ID and the fields to update. type EditReleaseParams struct { // Owner is the username or organization name that owns the repository. Owner string `json:"owner"` // Repo is the name of the repository. Repo string `json:"repo"` // ID is the unique identifier of the release to edit. ID int `json:"id"` // TagName is the new git tag name for the release. TagName string `json:"tag_name,omitempty"` // TargetCommitish is the new target branch or commit SHA. TargetCommitish string `json:"target_commitish,omitempty"` // Name is the new title for the release. Name string `json:"name,omitempty"` // Body is the new markdown description for the release. Body string `json:"body,omitempty"` // Draft indicates whether the release should be marked as a draft. Draft bool `json:"draft,omitempty"` // Prerelease indicates whether the release should be marked as a pre-release. Prerelease bool `json:"prerelease,omitempty"` } // EditReleaseImpl implements the MCP tool for editing an existing release. // This is an idempotent operation that modifies the metadata of a release // identified by its ID, using the Forgejo SDK. type EditReleaseImpl struct { Client *tools.Client } // Definition describes the `edit_release` tool. It requires `owner`, `repo`, and // the release `id`. It is marked as idempotent, as multiple identical calls // will result in the same state. func (EditReleaseImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "edit_release", Title: "Edit Release", Description: "Edit an existing release's title, description, or other metadata.", Annotations: &mcp.ToolAnnotations{ ReadOnlyHint: false, DestructiveHint: tools.BoolPtr(false), IdempotentHint: true, }, InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "owner": { Type: "string", Description: "Repository owner (username or organization name)", }, "repo": { Type: "string", Description: "Repository name", }, "id": { Type: "integer", Description: "Release ID", }, "tag_name": { Type: "string", Description: "New git tag name for this release (optional)", }, "target_commitish": { Type: "string", Description: "New target branch or commit SHA (optional)", }, "name": { Type: "string", Description: "New release title (optional)", }, "body": { Type: "string", Description: "New release description (markdown supported) (optional)", }, "draft": { Type: "boolean", Description: "Whether this is a draft release (optional)", }, "prerelease": { Type: "boolean", Description: "Whether this is a prerelease (optional)", }, }, Required: []string{"owner", "repo", "id"}, }, } } // Handler implements the logic for editing a release. It calls the Forgejo SDK's // `EditRelease` function. It will return an error if the release ID is not found. func (impl EditReleaseImpl) Handler() mcp.ToolHandlerFor[EditReleaseParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args EditReleaseParams) (*mcp.CallToolResult, any, error) { p := args // Build options for SDK call opt := forgejo.EditReleaseOption{} if p.TagName != "" { opt.TagName = p.TagName } if p.TargetCommitish != "" { opt.Target = p.TargetCommitish } if p.Name != "" { opt.Title = p.Name } if p.Body != "" { opt.Note = p.Body } // Note: For boolean fields, we need to check if they were explicitly set // For now, we'll pass them directly assuming they're properly handled opt.IsDraft = &p.Draft opt.IsPrerelease = &p.Prerelease // Call SDK release, _, err := impl.Client.EditRelease(p.Owner, p.Repo, int64(p.ID), opt) if err != nil { return nil, nil, fmt.Errorf("failed to edit release: %w", err) } // Convert to our type and format releaseWrapper := &types.Release{Release: release} return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: releaseWrapper.ToMarkdown(), }, }, }, nil, nil } } // DeleteReleaseParams defines the parameters for the delete_release tool. // It specifies the release to be deleted by its ID. type DeleteReleaseParams struct { // Owner is the username or organization name that owns the repository. Owner string `json:"owner"` // Repo is the name of the repository. Repo string `json:"repo"` // ID is the unique identifier of the release to delete. ID int `json:"id"` } // DeleteReleaseImpl implements the destructive MCP tool for deleting a release. // This is an idempotent but irreversible operation that removes both the release // object and its associated git tag. It uses the Forgejo SDK. type DeleteReleaseImpl struct { Client *tools.Client } // Definition describes the `delete_release` tool. It requires `owner`, `repo`, // and the release `id`. It is marked as a destructive operation to ensure // clients can warn the user before execution. func (DeleteReleaseImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "delete_release", Title: "Delete Release", Description: "Delete a release from a repository.", Annotations: &mcp.ToolAnnotations{ ReadOnlyHint: false, DestructiveHint: tools.BoolPtr(true), IdempotentHint: true, }, InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "owner": { Type: "string", Description: "Repository owner (username or organization name)", }, "repo": { Type: "string", Description: "Repository name", }, "id": { Type: "integer", Description: "Release ID to delete", }, }, Required: []string{"owner", "repo", "id"}, }, } } // Handler implements the logic for deleting a release. It calls the Forgejo SDK's // `DeleteRelease` function. On success, it returns a simple text confirmation. // It will return an error if the release does not exist. func (impl DeleteReleaseImpl) Handler() mcp.ToolHandlerFor[DeleteReleaseParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args DeleteReleaseParams) (*mcp.CallToolResult, any, error) { p := args // Call SDK _, err := impl.Client.DeleteRelease(p.Owner, p.Repo, int64(p.ID)) if err != nil { return nil, nil, fmt.Errorf("failed to delete release: %w", err) } // Return success message emptyResponse := types.EmptyResponse{} return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: emptyResponse.ToMarkdown(), }, }, }, nil, nil } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/raohwork/forgejo-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server