Skip to main content
Glama
crud.go12 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 label 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" ) // ListRepoLabelsParams defines the parameters for the list_repo_labels tool. // It specifies the owner and repository name to list labels from. type ListRepoLabelsParams 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"` } // ListRepoLabelsImpl implements the read-only MCP tool for listing repository labels. // This operation is safe, idempotent, and does not modify any data. It fetches // all available labels for a specified repository using the Forgejo SDK. type ListRepoLabelsImpl struct { Client *tools.Client } // Definition describes the `list_repo_labels` tool. It requires `owner` and `repo` // as parameters and is marked as a safe, read-only operation. func (ListRepoLabelsImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "list_repo_labels", Title: "List Repository Labels", Description: "List all labels available in a repository. Returns label information including name, description, color, and ID.", 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", }, }, Required: []string{"owner", "repo"}, }, } } // Handler implements the logic for listing labels. It calls the Forgejo SDK's // `ListRepoLabels` function and formats the resulting slice of labels into // a markdown list. Errors will occur if the repository is not found or // authentication fails. func (impl ListRepoLabelsImpl) Handler() mcp.ToolHandlerFor[ListRepoLabelsParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args ListRepoLabelsParams) (*mcp.CallToolResult, any, error) { p := args // Call SDK labels, _, err := impl.Client.ListRepoLabels(p.Owner, p.Repo, forgejo.ListLabelsOptions{}) if err != nil { return nil, nil, fmt.Errorf("failed to list labels: %w", err) } // Convert to our types and format var content string if len(labels) == 0 { content = "No labels found in this repository." } else { // Convert labels to our type labelList := make(types.LabelList, len(labels)) for i, label := range labels { labelList[i] = &types.Label{Label: label} } content = fmt.Sprintf("Found %d labels\n\n%s", len(labels), labelList.ToMarkdown()) } return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: content, }, }, }, nil, nil } } // CreateLabelParams defines the parameters for the create_label tool. // It includes the label's name, color, and optional description. type CreateLabelParams 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"` // Name is the name of the new label. Name string `json:"name"` // Color is the hex color code for the label (without the '#'). Color string `json:"color"` // Description is the optional markdown description of the label. Description string `json:"description,omitempty"` } // CreateLabelImpl implements the MCP tool for creating a new repository label. // This is a non-idempotent operation that creates a new label using the Forgejo SDK. type CreateLabelImpl struct { Client *tools.Client } // Definition describes the `create_label` tool. It requires `owner`, `repo`, // a `name`, and a `color`. It is not idempotent, as multiple calls with the // same name will fail once the first label is created. func (CreateLabelImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "create_label", Title: "Create Label", Description: "Create a new label in a repository. Specify the label name, description, and color.", 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", }, "name": { Type: "string", Description: "Label name", }, "color": { Type: "string", Description: "Label color (hex color code without #, e.g., 'ff0000' for red)", }, "description": { Type: "string", Description: "Optional label description", }, }, Required: []string{"owner", "repo", "name", "color"}, }, } } // Handler implements the logic for creating a label. It calls the Forgejo SDK's // `CreateLabel` function and returns the details of the newly created label. func (impl CreateLabelImpl) Handler() mcp.ToolHandlerFor[CreateLabelParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args CreateLabelParams) (*mcp.CallToolResult, any, error) { p := args // Build options for SDK call opt := forgejo.CreateLabelOption{ Name: p.Name, Color: p.Color, Description: p.Description, } // Call SDK label, _, err := impl.Client.CreateLabel(p.Owner, p.Repo, opt) if err != nil { return nil, nil, fmt.Errorf("failed to create label: %w", err) } // Convert to our type and format labelWrapper := &types.Label{Label: label} return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: labelWrapper.ToMarkdown(), }, }, }, nil, nil } } // EditLabelParams defines the parameters for the edit_label tool. // It specifies the label to edit by ID and the fields to update. type EditLabelParams 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 label to edit. ID int `json:"id"` // Name is the new name for the label. Name string `json:"name,omitempty"` // Color is the new hex color code for the label (without the '#'). Color string `json:"color,omitempty"` // Description is the new optional markdown description for the label. Description string `json:"description,omitempty"` } // EditLabelImpl implements the MCP tool for editing an existing repository label. // This is an idempotent operation that modifies a label's metadata using the // Forgejo SDK. type EditLabelImpl struct { Client *tools.Client } // Definition describes the `edit_label` tool. It requires `owner`, `repo`, and the // label `id`. It is marked as idempotent. func (EditLabelImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "edit_label", Title: "Edit Label", Description: "Edit an existing label's name, description, or color.", 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: "Label ID", }, "name": { Type: "string", Description: "New label name (optional)", }, "color": { Type: "string", Description: "New label color (hex color code without #, e.g., 'ff0000' for red) (optional)", }, "description": { Type: "string", Description: "New label description (optional)", }, }, Required: []string{"owner", "repo", "id"}, }, } } // Handler implements the logic for editing a label. It calls the Forgejo SDK's // `EditLabel` function. It will return an error if the label ID is not found. func (impl EditLabelImpl) Handler() mcp.ToolHandlerFor[EditLabelParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args EditLabelParams) (*mcp.CallToolResult, any, error) { p := args // Build options for SDK call opt := forgejo.EditLabelOption{} if p.Name != "" { opt.Name = &p.Name } if p.Color != "" { opt.Color = &p.Color } if p.Description != "" { opt.Description = &p.Description } // Call SDK label, _, err := impl.Client.EditLabel(p.Owner, p.Repo, int64(p.ID), opt) if err != nil { return nil, nil, fmt.Errorf("failed to edit label: %w", err) } // Convert to our type and format labelWrapper := &types.Label{Label: label} return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{ Text: labelWrapper.ToMarkdown(), }, }, }, nil, nil } } // DeleteLabelParams defines the parameters for the delete_label tool. // It specifies the label to be deleted by its ID. type DeleteLabelParams 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 label to delete. ID int `json:"id"` } // DeleteLabelImpl implements the destructive MCP tool for deleting a repository label. // This is an idempotent but irreversible operation that removes a label from a // repository using the Forgejo SDK. type DeleteLabelImpl struct { Client *tools.Client } // Definition describes the `delete_label` tool. It requires `owner`, `repo`, and // the label `id`. It is marked as a destructive operation to ensure clients // can warn the user before execution. func (DeleteLabelImpl) Definition() *mcp.Tool { return &mcp.Tool{ Name: "delete_label", Title: "Delete Label", Description: "Delete a label 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: "Label ID to delete", }, }, Required: []string{"owner", "repo", "id"}, }, } } // Handler implements the logic for deleting a label. It calls the Forgejo SDK's // `DeleteLabel` function. On success, it returns a simple text confirmation. // It will return an error if the label does not exist. func (impl DeleteLabelImpl) Handler() mcp.ToolHandlerFor[DeleteLabelParams, any] { return func(ctx context.Context, req *mcp.CallToolRequest, args DeleteLabelParams) (*mcp.CallToolResult, any, error) { p := args // Call SDK _, err := impl.Client.DeleteLabel(p.Owner, p.Repo, int64(p.ID)) if err != nil { return nil, nil, fmt.Errorf("failed to delete label: %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