Documentation
¶
Overview ¶
Package epics implements MCP tools for GitLab epic operations.
The package wraps the GitLab Epics API:
Index ¶
- func ActionSpecs(client *gitlabclient.Client) []toolutil.ActionSpec
- func Delete(ctx context.Context, client *gitlabclient.Client, input DeleteInput) error
- func DeleteOutput(ctx context.Context, client *gitlabclient.Client, input DeleteInput) (toolutil.DeleteOutput, error)
- func FormatLinksMarkdown(out LinksOutput) string
- func FormatListMarkdown(out ListOutput) string
- func FormatOutputMarkdown(e Output) string
- type BasicUserOutput
- type ChildItem
- type CreateInput
- type CreateLinkedItems
- type DeleteInput
- type GetInput
- type GetLinksInput
- type LinkedItem
- type LinksItem
- type LinksOutput
- type ListInput
- type ListOutput
- type Output
- type ResourceLinksOutput
- type UpdateInput
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ActionSpecs ¶
func ActionSpecs(client *gitlabclient.Client) []toolutil.ActionSpec
ActionSpecs returns canonical specs for group epic actions.
func Delete ¶
func Delete(ctx context.Context, client *gitlabclient.Client, input DeleteInput) error
Delete permanently removes an epic using the Work Items API.
func DeleteOutput ¶
func DeleteOutput(ctx context.Context, client *gitlabclient.Client, input DeleteInput) (toolutil.DeleteOutput, error)
DeleteOutput deletes an epic and returns the canonical success message shape.
func FormatLinksMarkdown ¶
func FormatLinksMarkdown(out LinksOutput) string
FormatLinksMarkdown renders child epics as a Markdown table.
func FormatListMarkdown ¶
func FormatListMarkdown(out ListOutput) string
FormatListMarkdown renders a list of epics as a Markdown table.
func FormatOutputMarkdown ¶
FormatOutputMarkdown renders a single epic as a Markdown summary.
Types ¶
type BasicUserOutput ¶
type BasicUserOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
Name string `json:"name,omitempty"`
State string `json:"state,omitempty"`
Locked bool `json:"locked,omitempty"`
PublicEmail string `json:"public_email,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
WebURL string `json:"web_url,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
BasicUserOutput mirrors gl.BasicUser (and the compatible gl.EpicAuthor), the compact user object embedded on the epic author and assignees keys. Per the 1:1 audit policy (full nested objects, C-IMPORTS) the SDK sub-object is replicated here rather than imported from a sibling package to preserve the zero-import-cycle constraint. locked and public_email are on the REST author and on neither SDK type: a live GET /api/v4/groups/gitlab-org/epics on 2026-09-07 answered with all eight keys of GitLab's user entity, and gl.EpicAuthor declares six. They stay empty on the Work Items path, whose author fragment selects id, username, name, state, createdAt, avatarUrl and webUrl.
type ChildItem ¶
type ChildItem struct {
IID int64 `json:"iid" jsonschema:"Internal ID (IID) of the child epic"`
Path string `json:"path,omitempty" jsonschema:"Namespace full path of the child epic"`
}
ChildItem is a summary reference to a child epic in the hierarchy. It mirrors the shape the work items domain publishes for the same widget.
type CreateInput ¶
type CreateInput struct {
FullPath string `json:"full_path" jsonschema:"Full path of the group (e.g. my-group),required"`
Title string `json:"title" jsonschema:"Epic title,required"`
Description string `json:"description,omitempty" jsonschema:"Epic description (Markdown supported)"`
Confidential *bool `json:"confidential,omitempty" jsonschema:"Whether the epic is confidential"`
Color string `json:"color,omitempty" jsonschema:"Epic color (hex format, e.g. #FF0000)"`
StartDate string `json:"start_date,omitempty" jsonschema:"Start date (YYYY-MM-DD)"`
DueDate string `json:"due_date,omitempty" jsonschema:"Due date (YYYY-MM-DD)"`
CreatedAt string `` /* 210-byte string literal not displayed */
CreateSource string `` /* 159-byte string literal not displayed */
AssigneeIDs []int64 `json:"assignee_ids,omitempty" jsonschema:"Global IDs of assignees"`
LabelIDs []int64 `json:"label_ids,omitempty" jsonschema:"Global IDs of labels"`
MilestoneID *int64 `json:"milestone_id,omitempty" jsonschema:"Global ID of the milestone to assign to the epic"`
// Making an epic a sub-epic is the Ultimate multi-level hierarchy
// [ListInput.ParentIDs] cites.
ParentID *int64 `` /* 134-byte string literal not displayed */
LinkedItems *CreateLinkedItems `json:"linked_items,omitempty" tier:"ultimate" jsonschema:"Epics to link to the new one, all with the same link type"`
Weight *int64 `json:"weight,omitempty" tier:"premium" jsonschema:"Weight of the epic"`
HealthStatus string `json:"health_status,omitempty" tier:"ultimate" jsonschema:"Health status (onTrack/needsAttention/atRisk)"`
}
CreateInput defines parameters for creating a new epic.
type CreateLinkedItems ¶
type CreateLinkedItems struct {
WorkItemIDs []int64 `json:"work_item_ids" jsonschema:"Global IDs of epics or work items to link,required"`
LinkType string `json:"link_type" jsonschema:"Link type: BLOCKS, BLOCKED_BY, or RELATED,required"`
}
CreateLinkedItems specifies epics to link while the epic is being created. It mirrors the work items domain's shape for the same widget so one API carries one call shape on both tools.
type DeleteInput ¶
type DeleteInput struct {
FullPath string `json:"full_path" jsonschema:"Full path of the group (e.g. my-group),required"`
IID int64 `json:"epic_iid" jsonschema:"Epic IID within the group,required"`
}
DeleteInput defines parameters for deleting an epic.
type GetInput ¶
type GetInput struct {
FullPath string `json:"full_path" jsonschema:"Full path of the group (e.g. my-group),required"`
IID int64 `json:"epic_iid" jsonschema:"Epic IID within the group,required"`
}
GetInput defines parameters for getting a single epic.
type GetLinksInput ¶
type GetLinksInput struct {
FullPath string `json:"full_path" jsonschema:"Full path of the group (e.g. my-group),required"`
IID int64 `json:"epic_iid" jsonschema:"Epic IID within the group,required"`
}
GetLinksInput defines parameters for listing child epics (REST).
type LinkedItem ¶
type LinkedItem struct {
IID int64 `json:"iid"`
LinkType string `json:"link_type"`
Path string `json:"path,omitempty"`
}
LinkedItem represents a linked work item summary.
type LinksItem ¶
type LinksItem struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
GroupID int64 `json:"group_id,omitempty"`
ParentID int64 `json:"parent_id,omitempty"`
ParentIID int64 `json:"parent_iid,omitempty"`
WorkItemID int64 `json:"work_item_id,omitempty"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
State string `json:"state"`
WebURL string `json:"web_url,omitempty"`
WebEditURL string `json:"web_edit_url,omitempty"`
Author *BasicUserOutput `json:"author,omitempty"`
Labels []string `json:"labels,omitempty"`
Confidential bool `json:"confidential,omitempty"`
Color string `json:"color,omitempty"`
TextColor string `json:"text_color,omitempty"`
StartDate string `json:"start_date,omitempty"`
StartDateIsFixed bool `json:"start_date_is_fixed,omitempty"`
StartDateFixed string `json:"start_date_fixed,omitempty"`
StartDateFromMilestones string `json:"start_date_from_milestones,omitempty"`
StartDateFromInheritedSource string `json:"start_date_from_inherited_source,omitempty"`
DueDate string `json:"due_date,omitempty"`
DueDateIsFixed bool `json:"due_date_is_fixed,omitempty"`
DueDateFixed string `json:"due_date_fixed,omitempty"`
DueDateFromMilestones string `json:"due_date_from_milestones,omitempty"`
DueDateFromInheritedSource string `json:"due_date_from_inherited_source,omitempty"`
EndDate string `json:"end_date,omitempty"`
Upvotes int64 `json:"upvotes,omitempty"`
Downvotes int64 `json:"downvotes,omitempty"`
References *toolutil.ReferencesOutput `json:"references,omitempty"`
Imported bool `json:"imported,omitempty"`
ImportedFrom string `json:"imported_from,omitempty"`
Links *ResourceLinksOutput `json:"_links,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ClosedAt string `json:"closed_at,omitempty"`
}
LinksItem is the child-epic output for the GetLinks REST endpoint. Per the 1:1 audit policy it surfaces the whole REST epic: every field of gl.Epic GitLab sends, plus the twelve it sends that gl.Epic does not declare (see [epicAPI]). The author is a full nested object.
user_notes_count, url, subscribed and reference are absent for the reasons Output and [epicAPI] record.
There is no label_details either, and unlike the four above that is this endpoint rather than the entity: ee/lib/api/epic_links.rb declares GET /groups/:id/-/epics/:epic_iid/epics with id and epic_iid alone and presents the entity with no options, so the labels array is always titles here. with_labels_details belongs to the list endpoint, which fills Output's label_details; the dual-shape decoder stays because that path needs it.
type LinksOutput ¶
type LinksOutput struct {
toolutil.HintableOutput
ChildEpics []LinksItem `json:"child_epics"`
}
LinksOutput holds child epics of a parent epic (REST-backed).
func GetLinks ¶
func GetLinks(ctx context.Context, client *gitlabclient.Client, input GetLinksInput) (LinksOutput, error)
GetLinks retrieves all child epics of a parent epic. This handler uses the REST API because client-go v2 does not yet expose a GraphQL query for work item children.
type ListInput ¶
type ListInput struct {
FullPath string `json:"full_path" jsonschema:"Full path of the group (e.g. my-group or my-group/sub-group),required"`
State string `json:"state,omitempty" jsonschema:"Filter by state (opened/closed/all)"`
Search string `json:"search,omitempty" jsonschema:"Search in title and description"`
In []string `` /* 135-byte string literal not displayed */
AuthorUsername string `json:"author_username,omitempty" jsonschema:"Filter by author username"`
AuthorID *int64 `` /* 231-byte string literal not displayed */
AssigneeUsernames []string `json:"assignee_usernames,omitempty" jsonschema:"Filter by assignee usernames"`
AssigneeWildcardID string `json:"assignee_wildcard_id,omitempty" jsonschema:"Filter by assignment state rather than by user: ANY, ME, or NONE"`
IIDs []string `json:"iids,omitempty" jsonschema:"Fetch only these epic IIDs, each as a decimal string (e.g. [\"12\", \"34\"])"`
IDs []string `` /* 169-byte string literal not displayed */
// An epic under another epic is the multi-level hierarchy
// docs.gitlab.com/user/work_items/child_items/ puts at Tier: Ultimate
// under "Work with multi-level hierarchies", so a filter that selects by
// one names something no lower tier can have.
ParentIDs []string `` /* 154-byte string literal not displayed */
LabelName []string `json:"label_name,omitempty" jsonschema:"Filter by label names"`
MilestoneTitle []string `json:"milestone_title,omitempty" jsonschema:"Filter by the titles of the milestones assigned to the epic"`
MilestoneWildcardID string `` /* 136-byte string literal not displayed */
MyReactionEmoji string `` /* 131-byte string literal not displayed */
Confidential *bool `json:"confidential,omitempty" jsonschema:"Filter by confidentiality"`
Subscribed string `` /* 138-byte string literal not displayed */
HealthStatusFilter string `` /* 154-byte string literal not displayed */
Weight string `json:"weight,omitempty" tier:"premium" jsonschema:"Filter by weight, as a decimal string (e.g. \"5\")"`
WeightWildcardID string `` /* 135-byte string literal not displayed */
OrderBy string `json:"order_by,omitempty" jsonschema:"Order epics by field (created_at, updated_at, title). Defaults to created_at"`
Sort string `json:"sort,omitempty" jsonschema:"Sort order (asc or desc). Defaults to desc"`
CreatedAfter string `json:"created_after,omitempty" jsonschema:"Return epics created after date (ISO 8601, e.g. 2025-01-01T00:00:00Z)"`
CreatedBefore string `json:"created_before,omitempty" jsonschema:"Return epics created before date (ISO 8601, e.g. 2025-12-31T23:59:59Z)"`
UpdatedAfter string `json:"updated_after,omitempty" jsonschema:"Return epics updated on or after date (ISO 8601, e.g. 2025-01-01T00:00:00Z)"`
UpdatedBefore string `json:"updated_before,omitempty" jsonschema:"Return epics updated on or before date (ISO 8601, e.g. 2025-12-31T23:59:59Z)"`
ClosedAfter string `json:"closed_after,omitempty" jsonschema:"Return epics closed on or after date (ISO 8601, e.g. 2025-01-01T00:00:00Z)"`
ClosedBefore string `json:"closed_before,omitempty" jsonschema:"Return epics closed on or before date (ISO 8601, e.g. 2025-12-31T23:59:59Z)"`
DueAfter string `json:"due_after,omitempty" jsonschema:"Return epics due on or after date (ISO 8601, e.g. 2025-01-01T00:00:00Z)"`
DueBefore string `json:"due_before,omitempty" jsonschema:"Return epics due on or before date (ISO 8601, e.g. 2025-12-31T23:59:59Z)"`
WithLabelsDetails *bool `` /* 266-byte string literal not displayed */
IncludeAncestors *bool `json:"include_ancestors,omitempty" jsonschema:"Include epics from ancestor groups"`
IncludeDescendants *bool `json:"include_descendants,omitempty" jsonschema:"Include epics from descendant groups"`
toolutil.GraphQLCursorPaginationInput
toolutil.PaginationInput
toolutil.KeysetPaginationInput
}
ListInput defines parameters for listing group epics.
Two GitLab APIs answer this action and the filters decide which: a request naming only what the REST epics endpoint accepts is served by it, and anything below that only Namespace.workItems can express routes the whole request through the Work Items GraphQL query. [workItemsPathReason] is the one place that decision is made, so a filter added here and forgotten there is silently dropped rather than refused.
The page request follows the same split, and [validateListPagination] enforces it: page, per_page, pagination and page_token are the REST endpoint's, first, after, last and before are the query's, and asking for one beside a filter that routes to the other is refused rather than answered with a page nobody asked for.
type ListOutput ¶
type ListOutput struct {
toolutil.HintableOutput
Epics []Output `json:"epics"`
Pagination *toolutil.GraphQLPaginationOutput `json:"pagination,omitempty"`
OffsetPagination *toolutil.PaginationOutput `json:"offset_pagination,omitempty"`
}
ListOutput holds a page of epics plus the pagination block of whichever API answered.
The two blocks are mutually exclusive and both are omitted when empty, so the one a response carries is what tells a caller which path served it: the REST epics endpoint pages by number and reports X-Next-Page, while the Work Items query pages by cursor and reports pageInfo. Publishing one shape for both would mean answering a full REST page with has_next_page false, which is how a caller stops paging one item short of the rest of the list.
func List ¶
func List(ctx context.Context, client *gitlabclient.Client, input ListInput) (ListOutput, error)
List retrieves epics for a group using the Work Items API with type filter.
type Output ¶
type Output struct {
toolutil.HintableOutput
ID int64 `json:"id"`
IID int64 `json:"iid"`
Type string `json:"type"`
State string `json:"state"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
WebURL string `json:"web_url,omitempty"`
WebEditURL string `json:"web_edit_url,omitempty"`
GroupID int64 `json:"group_id,omitempty"`
ParentID int64 `json:"parent_id,omitempty"`
WorkItemID int64 `json:"work_item_id,omitempty"`
Author *BasicUserOutput `json:"author,omitempty"`
Assignees []*BasicUserOutput `json:"assignees,omitempty"`
Labels []string `json:"labels,omitempty"`
LabelDetails []*toolutil.LabelDetailsOutput `json:"label_details,omitempty"`
LinkedItems []LinkedItem `json:"linked_items,omitempty" tier:"ultimate"`
Children []ChildItem `json:"children,omitempty"`
Confidential bool `json:"confidential,omitempty"`
Color string `json:"color,omitempty"`
TextColor string `json:"text_color,omitempty"`
StartDate string `json:"start_date,omitempty"`
StartDateIsFixed bool `json:"start_date_is_fixed,omitempty"`
StartDateFixed string `json:"start_date_fixed,omitempty"`
StartDateFromMilestones string `json:"start_date_from_milestones,omitempty"`
StartDateFromInheritedSource string `json:"start_date_from_inherited_source,omitempty"`
DueDate string `json:"due_date,omitempty"`
DueDateIsFixed bool `json:"due_date_is_fixed,omitempty"`
DueDateFixed string `json:"due_date_fixed,omitempty"`
DueDateFromMilestones string `json:"due_date_from_milestones,omitempty"`
DueDateFromInheritedSource string `json:"due_date_from_inherited_source,omitempty"`
EndDate string `json:"end_date,omitempty"`
HealthStatus string `json:"health_status,omitempty" tier:"ultimate"`
Weight *int64 `json:"weight,omitempty" tier:"premium"`
MilestoneID *int64 `json:"milestone_id,omitempty"`
Upvotes int64 `json:"upvotes,omitempty"`
Downvotes int64 `json:"downvotes,omitempty"`
References *toolutil.ReferencesOutput `json:"references,omitempty"`
Imported bool `json:"imported,omitempty"`
ImportedFrom string `json:"imported_from,omitempty"`
Links *ResourceLinksOutput `json:"_links,omitempty"`
ParentIID int64 `json:"parent_iid,omitempty"`
ParentPath string `json:"parent_path,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ClosedAt string `json:"closed_at,omitempty"`
}
Output represents a single epic (backed by a Work Item of type Epic, or by the REST epic for the list and child-epic links endpoints). Per the 1:1 audit policy it carries the union of what each source exposes; fields absent on a given source stay at their zero value.
There is no status and no iteration_id. Both were always null: an Epic work item carries neither the STATUS nor the ITERATION widget, per the widget list gitlab.com answered on 2026-09-07 for namespace(fullPath: "gitlab-org") { workItemTypes { nodes { name widgetDefinitions { type } } } }, and neither is a field of the REST epic. There is no user_notes_count and no url either: gl.Epic declares both, and GitLab's OpenAPI record, its doc/api/epics.md example bodies and a live gitlab.com response agree that no epic endpoint sends either one.
There is no subscribed and no reference for the reason [epicAPI] records: the entity renders each one only under an option no route this package calls passes.
func Create ¶
func Create(ctx context.Context, client *gitlabclient.Client, input CreateInput) (Output, error)
Create creates a new epic using the Work Items API with the Epic type.
func Update ¶
func Update(ctx context.Context, client *gitlabclient.Client, input UpdateInput) (Output, error)
Update modifies an existing epic using the Work Items API.
type ResourceLinksOutput ¶
type ResourceLinksOutput struct {
Self string `json:"self,omitempty"`
EpicIssues string `json:"epic_issues,omitempty"`
Group string `json:"group,omitempty"`
Parent string `json:"parent,omitempty"`
}
ResourceLinksOutput mirrors the `_links` object GitLab renders on a REST epic: the API URLs of the epic itself, its issues, its group and its parent. gl.Epic declares none of it.
type UpdateInput ¶
type UpdateInput struct {
FullPath string `json:"full_path" jsonschema:"Full path of the group (e.g. my-group),required"`
IID int64 `json:"epic_iid" jsonschema:"Epic IID within the group,required"`
Title string `json:"title,omitempty" jsonschema:"Updated epic title"`
Description string `json:"description,omitempty" jsonschema:"Updated description (Markdown supported)"`
StateEvent string `json:"state_event,omitempty" jsonschema:"State event: CLOSE or REOPEN"`
// Reparenting reaches the same hierarchy creating one does, so it carries
// the tier [ListInput.ParentIDs] cites.
ParentID *int64 `json:"parent_id,omitempty" tier:"ultimate" jsonschema:"Global ID of the parent epic work item"`
Color string `json:"color,omitempty" jsonschema:"Epic color (hex format)"`
StartDate string `json:"start_date,omitempty" jsonschema:"Start date (YYYY-MM-DD)"`
DueDate string `json:"due_date,omitempty" jsonschema:"Due date (YYYY-MM-DD)"`
AddLabelIDs []int64 `json:"add_label_ids,omitempty" jsonschema:"Global IDs of labels to add"`
RemoveLabelIDs []int64 `json:"remove_label_ids,omitempty" jsonschema:"Global IDs of labels to remove"`
AssigneeIDs []int64 `json:"assignee_ids,omitempty" jsonschema:"Global IDs of assignees (empty array to remove all)"`
MilestoneID *int64 `json:"milestone_id,omitempty" jsonschema:"Global ID of the milestone to assign to the epic"`
Weight *int64 `json:"weight,omitempty" tier:"premium" jsonschema:"Weight of the epic"`
HealthStatus string `json:"health_status,omitempty" tier:"ultimate" jsonschema:"Health status (onTrack/needsAttention/atRisk)"`
}
UpdateInput defines parameters for updating an existing epic.
There is no status here. An Epic work item carries no STATUS widget, so the mutation refuses the field: the widget list gitlab.com answered on 2026-09-07 for namespace(fullPath: "gitlab-org") { workItemTypes { nodes { name widgetDefinitions { type } } } } gives Epic AI_SESSION, ASSIGNEES, AWARD_EMOJI, COLOR, CURRENT_USER_TODOS, CUSTOM_FIELDS, DESCRIPTION, HEALTH_STATUS, HIERARCHY, LABELS, LINKED_ITEMS, MILESTONE, NOTES, NOTIFICATIONS, PARTICIPANTS, START_AND_DUE_DATE, TIME_TRACKING, VERIFICATION_STATUS and WEIGHT, and neither STATUS nor ITERATION nor CRM_CONTACTS. Issue and Task carry all three, which is where the field was copied from.