Documentation
¶
Overview ¶
Package toolutil provides shared utilities for MCP tool handler sub-packages. It contains error handling, pagination, logging, text processing, Markdown formatting helpers, and meta-tool infrastructure used across all domain sub-packages under internal/tools/.
The package centralizes cross-cutting behavior for:
- MCP response construction, annotations, icons, embedded resources, and Markdown formatter registration.
- GitLab API support types for pagination, GraphQL cursors, access levels, diff positions, file validation, and flexible string-or-integer IDs.
- Operational helpers for structured errors, not-found results, logging, rate limiting, polling, destructive-action confirmation, and identity resolution.
- Schema middleware that hardens generated tool schemas and enriches common pagination parameters with numeric bounds.
This package must never import from domain sub-packages to prevent circular dependencies. The dependency direction is: domain sub-packages -> toolutil.
Common Entry Points ¶
Handlers usually use WrapErr, WrapErrWithMessage, or WrapErrWithHint for errors; PaginationInput and PaginationOutput for paginated endpoints; StringOrInt for GitLab IDs that may be numeric or path-based; and RegisterMarkdown or RegisterMarkdownResult to publish type-specific Markdown renderers.
Destructive tools use ConfirmAction when they need an MCP elicitation step, and list/detail outputs commonly embed HintableOutput so meta-tools can add next-step guidance.
Markdown and Structured Output ¶
Domain packages register Markdown renderers with RegisterMarkdown and RegisterMarkdownResult. The registry lets handlers return typed structured output for MCP clients while still producing compact human-readable Markdown. A result about one GitLab object is written by Card; a collection is a table, and the shared helpers MarkdownTableHeader and MarkdownTableRow keep its formatting consistent across domains.
Dependency Direction ¶
The package dependency shape is intentionally one-way:
internal/tools/{domain}
|
v
toolutil
|
v
MCP SDK and GitLab client primitives
Index ¶
- Constants
- Variables
- func AccessLevelDescription(level gl.AccessLevelValue) string
- func ActionDispatchOutputSchema() map[string]any
- func ActionTimeout() time.Duration
- func AddMetaTool(server *mcp.Server, name, desc string, routes ActionMap, icons []mcp.Icon, ...)
- func AddReadOnlyMetaTool(server *mcp.Server, name, desc string, routes ActionMap, icons []mcp.Icon, ...)
- func AdjustPagination(p *PaginationOutput, itemCount int)
- func AnnotationsForContentKind(kind string) *mcp.Annotations
- func AppendHints(md string, hints ...string) string
- func ApplyActionMeta(options *ActionSpecOptions, meta ActionMetaEntry)
- func ApplyListOptions(opts *gl.ListOptions, page PaginationInput, keyset KeysetPaginationInput)
- func ApplyWorkItemWidgets(opts *gl.UpdateWorkItemOptions, in WorkItemWidgetInputs) (gl.RequestOptionFunc, error)
- func AssigneeFilter(field string, value StringOrInt) (*gl.AssigneeIDValue, error)
- func AttachArgumentLimits(server *mcp.Server, maxDepth int)
- func AttachRateLimit(server *mcp.Server, limiter *RateLimiter)
- func AttachRateLimitFunc(server *mcp.Server, resolve func(context.Context) *RateLimiter)
- func BoolEmoji(v bool) string
- func BoolPtr(b bool) *bool
- func BuildMetaToolSchema(routes ActionMap, mode string) map[string]any
- func BuildPipelineInputs(raw map[string]any) (gl.PipelineInputsOption, error)
- func BuildTargetURL(projectWebURL, targetType string, targetIID int64) string
- func CancelledResult(message string) *mcp.CallToolResult
- func CanonicalImportArchivePath(path string) (string, error)
- func CanonicalLocalDirPath(path string) (string, error)
- func CanonicalLocalFilePath(path string) (string, error)
- func CapturedThread[T any](op string, d *gl.Discussion, capture *gitlabclient.ResponseCapture) (T, error)
- func CapturedThreadNote[T any](op string, n *gl.Note, capture *gitlabclient.ResponseCapture) (T, error)
- func CapturedThreads[T any](op string, ds []*gl.Discussion, capture *gitlabclient.ResponseCapture) ([]T, error)
- func CatalogListingRPS(rps float64) float64
- func CellOr(value, absent string) string
- func ClampPollInterval(v int) int
- func ClampPollTimeout(v int) int
- func ClassifyError(err error) string
- func ClassifyHTTPStatus(code int) string
- func ClearableDate(field string, value *string) (*gl.ISOTime, error)
- func ClearableDateInputs(inputs ...ClearableDateInput) error
- func CloneMetaSchemaRoutes(routes map[string]ActionMap) map[string]ActionMap
- func CloneSchemaMap(schema map[string]any) map[string]any
- func CompileToolSchemas(tool *mcp.Tool, cacheKey string)
- func ComputeSHA256(path string) (string, error)
- func ComputeSHA256Reader(r io.Reader) (string, error)
- func ConfirmAction(ctx context.Context, req *mcp.CallToolRequest, message string) (*mcp.CallToolResult, error)
- func ConfirmDestructiveAction(ctx context.Context, req *mcp.CallToolRequest, params map[string]any, ...) (*mcp.CallToolResult, error)
- func ContainsAny(err error, substrs ...string) bool
- func ContextWithRequest(ctx context.Context, req *mcp.CallToolRequest) context.Context
- func CopyReadOnlyMetaAnnotations() *mcp.ToolAnnotations
- func DecodeEncodedPathIdentifier(value string) (string, bool)
- func DefuseHintsHeading(s string) string
- func DeriveAnnotations(routes ActionMap) *mcp.ToolAnnotations
- func DeriveSchema(schema any, transform string, derive func() any) any
- func DetectRichContent(body string) string
- func DiscussionThreadOutputsFromGitLab[T any](discussions []*gl.Discussion, extras []DiscussionExtra) []T
- func DynamicConfirmationDescription(confirmationSkipped bool) string
- func EditJSONBody(edit BodyEdit) gl.RequestOptionFunc
- func EmbedCanonicalResource(result *mcp.CallToolResult, template string, params map[string]any, value any)
- func EmbedResource(result *mcp.CallToolResult, uri, mimeType, text string)
- func EmbedResourceJSON(result *mcp.CallToolResult, uri string, value any)
- func EmbeddedResourcesEnabled() bool
- func EmptyMessage(resource string) string
- func EnableEmbeddedResources(enabled bool)
- func EnrichPaginationConstraints(server *mcp.Server)
- func ErrFieldRequired(field string) error
- func ErrInvalidEnum(field, value string, validValues []string) error
- func ErrRequiredInt64(operation, field string) error
- func ErrRequiredString(operation, field string) error
- func ErrorResult(message string) *mcp.CallToolResult
- func ErrorResultAnnotated(md string, ann *mcp.Annotations) *mcp.CallToolResult
- func ErrorResultMarkdown(domain, action string, err error) *mcp.CallToolResult
- func EscapeConsentValue(s string) string
- func EscapeMdHeading(s string) string
- func EscapeMdLinkDestination(url string) string
- func EscapeMdLinkLabel(s string) string
- func EscapeMdTableCell(s string) string
- func ExceedsJSONDepth(raw []byte, limit int) bool
- func ExecGraphQLDestroyNote(ctx context.Context, gql gl.GraphQLInterface, op, hint, query, noteGID string) error
- func ExpandResourceURI(template string, params map[string]any) (string, bool)
- func ExplicitConfirmFromRequest(req *mcp.CallToolRequest) bool
- func ExtractGitLabMessage(err error) string
- func ExtractHints(md string) []string
- func FieldTiers(rt reflect.Type) map[string]string
- func FineGrainedErrorNote(ctx context.Context, actionID string, err error) error
- func FineGrainedNotes(ctx context.Context, actionID string, callResult *mcp.CallToolResult, ...) *mcp.CallToolResult
- func FineGrainedRefusal(ctx context.Context, req *mcp.CallToolRequest, ...) *mcp.CallToolResult
- func FinishToolResult(callResult *mcp.CallToolResult, result any, route ActionRoute, ...) (finished *mcp.CallToolResult, structured any)
- func FormatCICDVariableCollectionMarkdown[T any](variables []T, pagination PaginationOutput, ...) string
- func FormatCICDVariableDetailMarkdown(v CICDVariableMarkdown, title string, includeEnvironmentScope bool, ...) string
- func FormatDiscussionMarkdown(discussion DiscussionMarkdown, hints ...string) string
- func FormatGID(typeName string, id int64) string
- func FormatGraphQLForwardPagination(p GraphQLForwardPaginationOutput, shown int) string
- func FormatGraphQLPagination(p GraphQLPaginationOutput, shown int) string
- func FormatISOTimePtr(t *gl.ISOTime) string
- func FormatLabelListMarkdownFunc[T any](labels []T, pagination PaginationOutput, opts LabelMarkdownOptions, ...) string
- func FormatLabelMarkdown(label LabelMarkdown, opts LabelMarkdownOptions) string
- func FormatNoteListMarkdown(notes []NoteMarkdown, pagination PaginationOutput, ...) string
- func FormatNoteMarkdown(note NoteMarkdown, opts NoteMarkdownOptions) string
- func FormatQuickActionsOnlyMarkdown(title string, status QuickActionsStatusOutput, hints ...string) string
- func FormatRESTDiscussionListMarkdown[T any](discussions []T, pagination PaginationOutput, ...) string
- func FormatSafeModePreviewMarkdown(p SafeModePreview) string
- func FormatStorageMoveCollectionMarkdown[T any](moves []T, pagination PaginationOutput, convert func(T) StorageMoveMarkdown, ...) string
- func FormatStorageMoveDetailMarkdown(move StorageMoveMarkdown, title string, hints ...string) string
- func FormatTarget(targetType string, targetIID int64, targetTitle, targetURL string) string
- func FormatTemplateAttributeListMarkdown(items []TemplateAttributeListMarkdownItem, ...) string
- func FormatTemplateContentMarkdown(title, name, language, content string, hints ...string) string
- func FormatTemplateDetailMarkdown(detail TemplateDetailMarkdown) string
- func FormatTime(s string) string
- func FormatTimePtr(t *time.Time) string
- func FormatTimeValue(t time.Time) string
- func GraphQLMutationError(operation string, payloadErrors []string) error
- func GraphQLNullPropagated(messages []string) bool
- func GraphQLNumber(s string) int64
- func GraphQLPartialPage(operation string, responseErrors []GraphQLError, kept, nulled int) (string, error)
- func GraphQLTopLevelError(operation string, responseErrors []GraphQLError) error
- func HasRegisteredMarkdownFormatter(v any) bool
- func HintAction(actionID, purpose string) string
- func IdentityToContext(ctx context.Context, id UserIdentity) context.Context
- func ImageMIMEType(filename string) string
- func IndividualToolFromActionSpec(spec ActionSpec, opts IndividualToolProjectionOptions) (*mcp.Tool, error)
- func IndividualToolFromSpecs(specs []ActionSpec, individualName string, ...) (*mcp.Tool, error)
- func InputRequiredResultFromError(err error) (*mcp.CallToolResult, bool)
- func InstanceWebURL(apiBase *url.URL) *url.URL
- func Int64UserIDFilter(field string, value StringOrInt) (*int64, gl.RequestOptionFunc, error)
- func InternalError(err error) error
- func InvalidParams(err error) error
- func IsBinaryFile(filename string) bool
- func IsDeleteToolName(name string) bool
- func IsDownloadTooLarge(err error) bool
- func IsHTTPStatus(err error, code int) bool
- func IsImageFile(filename string) bool
- func IsInternalInspection(ctx context.Context) bool
- func IsNotFound(err error) bool
- func IsPermissionRefusal(err error) bool
- func IsReadToolName(name string) bool
- func IsTerminalStatus(status string) bool
- func IsYOLOMode() bool
- func IssueStateEmoji(state string) string
- func KeysetContinuation(keyset KeysetPaginationInput) gl.RequestOptionFunc
- func LabelForRevision(req mcp.Request, result *mcp.CallToolResult) *mcp.CallToolResult
- func LinkableDestination(url string) bool
- func ListHints(hints ...string) []string
- func ListRegisteredTools(ctx context.Context, server *mcp.Server, clientName string) ([]*mcp.Tool, error)
- func LocalFilesystemAccessAllowed() bool
- func LockdownInputSchemas(server *mcp.Server)
- func LogMarkupRawOffset(s string, n int) int
- func LogToolCallAll(ctx context.Context, req *mcp.CallToolRequest, tool string, start time.Time, ...)
- func LogToolRefusal(ctx context.Context, req *mcp.CallToolRequest, tool, reason string)
- func LookupMetaActionSchema(routes map[string]ActionMap, tool, action string) (map[string]any, bool)
- func MRStateEmoji(state string) string
- func MakeMetaHandler(toolName string, routes ActionMap, formatResult FormatResultFunc) ...
- func MarkdownCodeFence(content string) string
- func MarkdownFencedBlock(language, content string) string
- func MarkdownForResult(result any) *mcp.CallToolResult
- func MarkdownFormatterCount() int
- func MarkdownRegistrationProblems() []string
- func MarkdownTableHeader(columns ...string) string
- func MarkdownTableRow(cells ...string) string
- func MdCodeSpan(s string) string
- func MdCodeSpanCell(s string) string
- func MdTitleLink(title, url string) string
- func MdUserHandle(username string) string
- func MdUserLink(username, webURL string) string
- func MergeRequestDiffsForPositionCheck(ctx context.Context, client *gitlabclient.Client, projectID string, ...) ([]*gl.MergeRequestDiff, bool)
- func MergeVariables(sources ...map[string]any) map[string]any
- func MetaActionSchema(route ActionRoute) map[string]any
- func MetaParamSchemaMode() string
- func MetaSchemaURI(tool, action string) string
- func MetaToolDescriptionPrefix(toolName string, routes ActionMap) string
- func MetaToolOutputSchema() map[string]any
- func MetaToolSchema(routes ActionMap) map[string]any
- func MustIndividualToolFromSpecs(specs []ActionSpec, individualName string, ...) *mcp.Tool
- func NodesAnswered[N, I any](nodes []*N, convert func(N) I) (items []I, nulled int)
- func NormalizeActionAlias(action string, routes ActionMap) string
- func NormalizeActionAliasForParams(toolName, action string, params map[string]any, routes ActionMap) string
- func NormalizeParamAliasesForSchema(params, schema map[string]any) map[string]any
- func NormalizeResultMarkdown(md string) string
- func NormalizeText(s string) string
- func NotFoundResult(resource, identifier string, hints ...string) *mcp.CallToolResult
- func NoteIDFromGID(gid string) (id int64, resourceEvent bool)
- func NullWhenZero[T comparable](value *T, key string, edit *BodyEdit) *T
- func OpenAndValidateFile(path string, maxSize int64) (*os.File, os.FileInfo, error)
- func OpenFileOrBase64Source(op, filePath, contentBase64 string) (reader io.Reader, size int64, cleanup func(), err error)
- func PaginationRequestOption(page PaginationInput) gl.RequestOptionFunc
- func ParamText(value any) string
- func ParameterGuidanceIdentity(guidance map[string]ParameterGuidance) string
- func ParseDateField(field, value string) (*gl.ISOTime, error)
- func ParseDateFieldString(field, value string) (*string, error)
- func ParseDateInputs(inputs ...DateInput) error
- func ParseGID(gid string) (typeName string, id int64, err error)
- func ParseMetaSchemaURI(uri string) (tool, action string)
- func ParseTimeField(field, value string) (*time.Time, error)
- func ParseTimeInputs(inputs ...TimeInput) error
- func PartialPageHint(account string) string
- func PathBelowInstanceWeb(apiBase *url.URL, remote string) (string, bool)
- func PipelineInputsSchema[T any](property string) map[string]any
- func PipelineStatusEmoji(status string) string
- func PlainPath(value string) string
- func PopulateHints(result *mcp.CallToolResult, setter HintSetter)
- func ProgressReportInterval(total int64) int64
- func RFC3339(t time.Time) string
- func RFC3339Ptr(t *time.Time) string
- func ReadDownload(op string, r io.Reader, hint string) ([]byte, error)
- func ReadFileOrBase64(op, filePath, contentBase64 string) (*bytes.Reader, error)
- func RedactURL(u *url.URL) string
- func RedactURLToOrigin(raw string) string
- func RegisterMarkdown[T any](fn func(T) string)
- func RegisterMarkdownAnnotated[T any](fn func(T) string, ann *mcp.Annotations)
- func RegisterMarkdownPair[A, B any](first func(A) string, second func(B) string)
- func RegisterMarkdownResult[T any](fn func(T) *mcp.CallToolResult)
- func RegisterMarkdownTriple[A, B, C any](first func(A) string, second func(B) string, third func(C) string)
- func RegisterSurfaceToolFromSpec(server *mcp.Server, spec ActionSpec, opts SurfaceToolRegisterOptions)
- func RegisteredMarkdownFormatterName(t reflect.Type) string
- func RegisteredMarkdownTypeNames() []string
- func RegisteredMarkdownTypes() []reflect.Type
- func RequestFromContext(ctx context.Context) *mcp.CallToolRequest
- func RequireProjectItem(ctx context.Context, operation string, projectID StringOrInt, field string, ...) error
- func ResolveNextSteps(text string, resolve ActionCallResolver) string
- func ResolveProjectWebURLs(ctx context.Context, projects gl.ProjectsServiceInterface, projectIDs []int64) map[int64]string
- func ResourceTemplateVariables(template string) ([]string, error)
- func ReviewerFilter(field string, value StringOrInt) (*gl.ReviewerIDValue, error)
- func RichContentHint(features, webURL string) string
- func SafeModePreviewResult(p SafeModePreview) *mcp.CallToolResult
- func SanitizeError(err error) error
- func SchemaForRoute[R any]() map[string]any
- func SchemaPropertyTakes(name string, value, property any) bool
- func SchemaShared(schema any) bool
- func SetActionTimeout(d time.Duration)
- func SetLocalFilesystemAccess(allowed bool)
- func SetMetaParamSchemaMode(mode string)
- func SetMetaParamSchemaModeScoped(mode string) func()
- func SetUploadConfig(maxFileSize int64)
- func SeverityBadge(severity string) string
- func ShareSchema(schema any)
- func SharedSchemaIdentity(schema any) (string, bool)
- func StripControlBytes(s string) string
- func StripLogMarkup(s string) string
- func StripMetaToolDescriptionPrefix(description string) string
- func SuccessResult(markdown string) *mcp.CallToolResult
- func TimestampParam(name string, at *time.Time) gl.RequestOptionFunc
- func TitleFromName(name string) string
- func ToolResultAnnotated(md string, ann *mcp.Annotations) *mcp.CallToolResult
- func ToolResultWithImage(md string, ann *mcp.Annotations, imageData []byte, mimeType string) *mcp.CallToolResult
- func ToolResultWithMarkdown(md string) *mcp.CallToolResult
- func TypeIdentity(rt reflect.Type) string
- func UnattributedRequestError() error
- func UnattributedRequestErrorFor(ctx context.Context) error
- func UnconfirmedWrite(client *gitlabclient.Client, resp *gl.Response, operation, object string, ...) error
- func UnmarshalParams[T any](params map[string]any) (T, error)
- func ValidActionsString(routes ActionMap) string
- func ValidateDiffPosition(diffLines []DiffLine, newLine, oldLine int) error
- func ValidatePackageFileName(filename string) error
- func ValidatePackageName(name string) error
- func ValidateRateLimit(rps float64, burst int) error
- func ValidateRouteBinding(route ActionRoute) error
- func WithActionDeadline(ctx context.Context) (context.Context, context.CancelFunc)
- func WithHints[O any](result *mcp.CallToolResult, out O, err error) (*mcp.CallToolResult, O, error)
- func WithInternalInspection(ctx context.Context) context.Context
- func WrapErr(operation string, err error) error
- func WrapErrWithHint(operation string, err error, hint string) error
- func WrapErrWithMessage(operation string, err error) error
- func WrapErrWithStatusHint(operation string, err error, code int, hint string) error
- func WrapGFMBody(body string) string
- func WriteAnsweredWithoutObject(resp *gl.Response, err error) bool
- func WriteDownloadOutputFile(path string, write func(io.Writer) error) (_ int64, err error)
- func WriteGraphQLPagination(b *strings.Builder, p GraphQLPaginationOutput, shown int)
- func WriteHints(b *strings.Builder, hints ...string)
- func WriteHookSecretKeys(b *strings.Builder, urlVariableKeys, customHeaderKeys []string)
- func WriteListFooter(b *strings.Builder, p PaginationOutput, linked bool, hints ...string)
- func WriteListHeading(b *strings.Builder, title string, shown int, p PaginationOutput)
- func WriteListSummary(b *strings.Builder, shown int, p PaginationOutput)
- func WritePagination(b *strings.Builder, p PaginationOutput)
- type AISettingsOutput
- type AccessRequesterExtra
- type ActionAliasSpec
- type ActionCallResolver
- type ActionFunc
- func SafeModeActionFunc(name string) ActionFunc
- func WrapAction[T, R any](client *gitlabclient.Client, ...) ActionFunc
- func WrapActionWithRequest[T, R any](client *gitlabclient.Client, ...) ActionFunc
- func WrapVoidAction[T any](client *gitlabclient.Client, ...) ActionFunc
- func WrapVoidActionWithRequest[T any](client *gitlabclient.Client, ...) ActionFunc
- type ActionMap
- type ActionMetaEntry
- type ActionRoute
- func CloneActionRoute(route ActionRoute) ActionRoute
- func DestructiveAction[T, R any](client *gitlabclient.Client, ...) ActionRoute
- func DestructiveActionWithRequest[T, R any](client *gitlabclient.Client, ...) ActionRoute
- func DestructiveFunc[T, R any](fn func(ctx context.Context, input T) (R, error)) ActionRoute
- func DestructiveRoute(fn ActionFunc) ActionRoute
- func DestructiveVoidAction[T any](client *gitlabclient.Client, ...) ActionRoute
- func DestructiveVoidActionWithRequest[T any](client *gitlabclient.Client, ...) ActionRoute
- func Route(fn ActionFunc) ActionRoute
- func RouteAction[T, R any](client *gitlabclient.Client, ...) ActionRoute
- func RouteActionWithRequest[T, R any](client *gitlabclient.Client, ...) ActionRoute
- func RouteFunc[T, R any](fn func(ctx context.Context, input T) (R, error)) ActionRoute
- func RouteRequestFunc[T, R any](fn func(ctx context.Context, req *mcp.CallToolRequest, input T) (R, error)) ActionRoute
- func RouteVoidAction[T any](client *gitlabclient.Client, ...) ActionRoute
- func (route ActionRoute) BindTo(client *gitlabclient.Client) ActionRoute
- func (route ActionRoute) WithAliases(aliases ...string) ActionRoute
- func (route ActionRoute) WithBoundHandler(client *gitlabclient.Client, bind func(*gitlabclient.Client) ActionFunc) ActionRoute
- func (route ActionRoute) WithParameterGuidance(guidance map[string]ParameterGuidance) ActionRoute
- func (route ActionRoute) WithRelatedActions(related ...string) ActionRoute
- func (route ActionRoute) WithTags(tags ...string) ActionRoute
- func (route ActionRoute) WithUsage(usage string) ActionRoute
- func (route ActionRoute) WrapHandler(wrap func(next ActionFunc) ActionFunc) ActionRoute
- func (route ActionRoute) WrapNotFound(notFound func(params map[string]any) any) ActionRoute
- type ActionSpec
- func CloneActionSpec(spec ActionSpec) ActionSpec
- func CloneActionSpecs(specs []ActionSpec) []ActionSpec
- func FillScopeParameterGuidance(specs []ActionSpec) []ActionSpec
- func FillScopeParameterGuidanceSingle(spec ActionSpec) ActionSpec
- func NewActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
- func NewAdditiveActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
- func NewCreateActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
- func NewDeleteActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
- func NewReadActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
- func NewUpdateActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
- type ActionSpecOptions
- type ApprovedMemberExtra
- type ApproverIDsFilter
- type AssetLinkOutput
- type AssetSourceOutput
- type AssetsOutput
- type AuthorOutput
- type BasicGroupDetailsOutput
- type BasicUserOutput
- type BillableMemberExtra
- type BoardExtra
- type BoardLabelDetailsOutput
- type BoardLabelOutput
- type BoardListAssigneeOutput
- type BoardListExtra
- type BoardUserOutput
- type BodyEdit
- type BridgeExtra
- type BridgeProjectOutput
- type CICDVariableFlags
- type CICDVariableMarkdown
- type CIMinutesUsageOutput
- type CIVariableExtra
- type Card
- func (c *Card) Bool(label string, v bool)
- func (c *Card) BoolPtr(label string, v *bool)
- func (c *Card) Code(label, value string)
- func (c *Card) Count(label string, v int64)
- func (c *Card) End(hints ...string)
- func (c *Card) Fence(title, lang, body string)
- func (c *Card) Field(label, value string)
- func (c *Card) FieldOr(label, value, absent string)
- func (c *Card) Flag(emoji, label string, on bool)
- func (c *Card) Int(label string, v int64)
- func (c *Card) Link(label, text, url string)
- func (c *Card) Markdown(label, composed string)
- func (c *Card) Note(prose string)
- func (c *Card) Secret(label, secret string)
- func (c *Card) Section(title string) *Card
- func (c *Card) Sub(label string) *Card
- func (c *Card) Table(title string, columns ...string) *CardTable
- func (c *Card) Text(label, body string)
- func (c *Card) Time(label, rfc3339 string)
- func (c *Card) URL(url string)
- func (c *Card) Warn(label string, on bool)
- type CardTable
- type ClearableDateInput
- type CodedError
- type CommitCommentExtra
- type CommitOutput
- type CompatibilityPolicy
- type CustomAttributeOutput
- type DateInput
- type DeleteOutput
- type DependencyExtra
- type DeployKeyExtra
- type DeployKeyProjectOutput
- type DeploymentApprovalOutput
- type DeploymentApprovalRuleOutput
- type DeploymentApprovalSummaryOutput
- type DeploymentExtra
- type DetailedError
- type DiffExtra
- type DiffLine
- type DiffOutput
- type DiffRefsOutput
- type DiscussionExtra
- type DiscussionMarkdown
- type DiscussionNoteMarkdown
- type DiscussionRenderer
- func (r DiscussionRenderer) FormatDiscussion(discussion DiscussionMarkdown) string
- func (r DiscussionRenderer) FormatGraphQLForwardList(discussions []DiscussionMarkdown, pagination GraphQLForwardPaginationOutput) string
- func (r DiscussionRenderer) FormatNote(note NoteMarkdown) string
- func (r DiscussionRenderer) FormatRESTList(discussions []DiscussionMarkdown, pagination PaginationOutput) string
- type DiscussionThreadNoteOutput
- type DiscussionThreadOutput
- type DuoAccessRuleNamespaceOutput
- type DuoNamespaceAccessRuleOutput
- type EpicOutput
- type EvidenceOutput
- type FeatureDefinitionExtra
- type FeatureExtra
- type FeatureFlagUserListExtra
- type FormatResultFunc
- type GraphQLClusterAgentRef
- type GraphQLCursor
- type GraphQLCursorPaginationInput
- type GraphQLError
- type GraphQLForwardPaginationOutput
- type GraphQLNoteMutation
- type GraphQLNoteMutationResult
- type GraphQLPageInfo
- type GraphQLPaginationInput
- type GraphQLPaginationOutput
- type GraphQLRawForwardPageInfo
- type GraphQLRawPageInfo
- type GraphQLUserCoreRef
- type GraphQLVulnerabilityLink
- type GraphQLVulnerabilityLocation
- type GraphQLVulnerabilityTokenStatus
- type GraphQLVulnerableDependency
- type GraphQLVulnerableKubernetesResource
- type GraphQLVulnerablePackage
- type GroupDetailExtra
- type GroupExtra
- type GroupHookExtra
- type HintSetter
- type HintableOutput
- type ImpersonationTokenExtra
- type IndividualToolAnnotationOverrides
- type IndividualToolProjectionOptions
- type IndividualToolSpec
- type InputSchemaOverride
- func FilterOverridesForSchema(schema map[string]any, overrides []InputSchemaOverride) []InputSchemaOverride
- func SchemaAnyOfRequired(propertyNames ...string) InputSchemaOverride
- func SchemaApproverIDsOverride(propertyPath string) InputSchemaOverride
- func SchemaEnumOverride(propertyPath string, values ...string) InputSchemaOverride
- func SchemaFormatOverride(propertyPath, format string) InputSchemaOverride
- func SchemaPropertyOverride(propertyPath string, values map[string]any) InputSchemaOverride
- func SchemaRootOverride(values map[string]any) InputSchemaOverride
- type InstanceUserExtra
- type InvitationExtra
- type IssueBasicExtra
- type IssueExtra
- type IssueLinksOutput
- type IssueUserOutput
- func NewIssueUserOutputFromBasicUser(u gl.BasicUser) IssueUserOutput
- func NewIssueUserOutputFromEpicAuthor(u *gl.EpicAuthor) *IssueUserOutput
- func NewIssueUserOutputFromIssueAssignee(a *gl.IssueAssignee) *IssueUserOutput
- func NewIssueUserOutputFromIssueAuthor(u *gl.IssueAuthor) *IssueUserOutput
- func NewIssueUserOutputFromIssueCloser(u *gl.IssueCloser) *IssueUserOutput
- func NewIssueUserOutputFromPointer(u *gl.BasicUser) *IssueUserOutput
- func NewIssueUserOutputsFromIssueAssignees(as []*gl.IssueAssignee) []*IssueUserOutput
- type IterationOutput
- type JSONDepthScanner
- type JobMeasures
- type KeysetPaginationInput
- type LabelDetailsOutput
- type LabelExtra
- type LabelMarkdown
- type LabelMarkdownOptions
- type LastPipelineOutput
- type LinePositionOutput
- type LineRangeOutput
- type LineType
- type LinksOutput
- type LintExtra
- type LintJobOutput
- type MRMilestoneOutput
- type MemberExtra
- type MemberRoleOutput
- type MemberUserOutput
- type MergeRequestDiffExtra
- type MergeRequestExtra
- type MergeRequestOutput
- type MergeRequestUserOutput
- type MetaSchemaActionEntry
- type MetaSchemaDiscoveryIndex
- type MetaSchemaIndex
- type MetaSchemaIndexEntry
- type MetaSchemaRegistry
- type MetaSchemaToolEntry
- type MetaToolInput
- type MilestoneExtra
- type MilestoneIssueStatsOutput
- type MilestoneOutput
- type NamespaceBasicOutput
- type NamespaceExtra
- type NoteExtra
- type NoteListMarkdownOptions
- type NoteMarkdown
- type NoteMarkdownFlags
- type NoteMarkdownOptions
- type NoteOutput
- type NotePositionOutput
- type NoteUserExtra
- type NoteUserOutput
- type OnceMap
- type PackageExtra
- type PackagePipelineExtra
- type PackagePipelineOutput
- type PackageTagOutput
- type PackageVersionExtra
- type PackageVersionOutput
- type PagesCertificateExpirationOutput
- type PagesDomainExtra
- type PaginationInput
- type PaginationOutput
- type ParamAliasExplanation
- type ParamValidationError
- type ParameterAliasSpec
- type ParameterGuidance
- type PersonalTokenOutput
- type PipelineDetailedStatusIllustrationOutput
- type PipelineDetailedStatusOutput
- type PipelineExtra
- type PipelineInfoOutput
- type PipelineMeasures
- type PipelineOutput
- type PipelineTriggerExtra
- type ProgressReader
- type ProgressWriter
- type ProjectApprovalRuleExtra
- type ProjectExtra
- type ProtectedBranchExtra
- type QuickActionsStatusOutput
- type RateLimiter
- type ReferencesOutput
- type RegistryRepositoryExtra
- type ResourceMilestoneEventExtra
- type ResourceStateEventExtra
- type ResourceTokenExtra
- type ResourceURIParamsProvider
- type RunnerExtra
- type RunnerManagerExtra
- type SAMLIdentityOutput
- type SCIMIdentityOutput
- type SSHCertificateExtra
- type SafeModePreview
- type ServiceAccountExtra
- type StorageMoveEntityMarkdown
- type StorageMoveExtra
- type StorageMoveMarkdown
- type StringOrInt
- type SuggestionOutput
- type SurfaceToolRegisterOptions
- type TaskCompletionStatusOutput
- type TemplateAttributeListMarkdownItem
- type TemplateAttributeListMarkdownOptions
- type TemplateDetailMarkdown
- type TemplateMarkdown
- type TemplateRenderer
- type TimeInput
- type TimeStatsOutput
- type TodoExtra
- type TokenExtra
- type TokenGranularScopeOutput
- type ToolError
- type UploadConfig
- type UserBasicExtra
- type UserBasicOutput
- type UserCoreRefOutput
- type UserExtra
- type UserIdentity
- type UserRefOutput
- type VoidOutput
- type VulnerabilityLinkOutput
- type VulnerabilityLocationOutput
- type VulnerabilityTokenStatusOutput
- type VulnerableDependencyOutput
- type VulnerableKubernetesResourceOutput
- type WikiExtra
- type WorkItemWidgetInputs
Constants ¶
const ( ActionSpecContentList = "list" ActionSpecContentDetail = "detail" ActionSpecContentMutate = "mutate" ActionSpecContentAssistant = "assistant" ActionSpecContentImage = "image" ActionSpecNotFoundNone = "none" ActionSpecNotFoundResult = "not_found_result" ActionSpecNotFoundPropagate = "propagate_error" ActionSpecEmbeddedNone = "none" ActionSpecEmbeddedOptional = "optional" ActionSpecEmbeddedAlways = "always" ActionSpecRichStandard = "standard" ActionSpecRichImage = "image" ActionSpecRichResourceLink = "resource_link" ActionSpecRichMixed = "mixed" )
const ( // ApproverAny matches merge requests with at least one approver. ApproverAny = "Any" // ApproverNone matches merge requests with no approvers. ApproverNone = "None" )
Approver filter literals accepted in place of a list of user IDs.
const ( DefaultMaxFileSize = config.DefaultMaxFileSize // ImportArchiveAllowlistEnv names extra directories allowed for local // GitLab project/group import archives, separated by the OS path-list separator. ImportArchiveAllowlistEnv = "GITLAB_MCP_ALLOWED_IMPORT_DIRS" // UploadDirAllowlistEnv names extra directories a tool may READ a local // file from (every file_path input), separated by the OS path-list // separator. The working directory and the OS temp directory are always // allowed. UploadDirAllowlistEnv = "GITLAB_MCP_ALLOWED_UPLOAD_DIRS" // DownloadDirAllowlistEnv names extra directories a tool may WRITE a // downloaded file into (output_path), separated by the OS path-list // separator. The working directory and the OS temp directory are always // allowed. DownloadDirAllowlistEnv = "GITLAB_MCP_ALLOWED_DOWNLOAD_DIRS" )
DefaultMaxFileSize re-exports the upload size limit from config as the single source of truth for tool utilities.
const ( GraphQLDefaultFirst = 20 GraphQLMaxFirst = 100 )
GraphQL pagination defaults.
const ( RefusalSafeMode = "safe_mode" RefusalNeedsConfirmation = "needs_confirmation" RefusalInvalidParams = "invalid_params" RefusalUnknownAction = "unknown_action" RefusalRateLimited = "rate_limited" // RefusalFineGrained is an action a fine-grained personal access token // may not run (issue 952), declined before anything of it ran. RefusalFineGrained = "fine_grained" )
Reasons a call was declined before its handler ran, recorded as the reason attribute of LogToolRefusal.
They are a closed set on purpose: an operator computing a refusal rate needs to group by something, and free text does not group.
const ( TblRowID = "| ID | %d |\n" TblRowStatus = "| Status | %s |\n" TblRowCreatedAt = "| Created At | %s |\n" TblRowUpdatedAt = "| Updated At | %s |\n" TblRowHasFailures = "| Has Failures | %v |\n" )
Table row format constants for common detail-table fields. Each pairs with TblFieldValue and is superseded by the Card rows of the same name: Card.Int for the ID, Card.Field for the status, Card.Time for the two timestamps, and Card.Warn for a failure flag, which marks the condition with the warning sign rather than rendering the boolean as "true".
const ( TblSep1Col = "| --- |\n" TblSep2Col = "| --- | --- |\n" TblSep3Col = "| --- | --- | --- |\n" TblSep4Col = "| --- | --- | --- | --- |\n" TblSep5Col = "| --- | --- | --- | --- | --- |\n" TblSep6Col = "| --- | --- | --- | --- | --- | --- |\n" TblSep7Col = "| --- | --- | --- | --- | --- | --- | --- |\n" TblSep8Col = "| --- | --- | --- | --- | --- | --- | --- | --- |\n" TblSep9Col = "| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" TblSep10Col = "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" FmtRow1Str = "| %s |\n" FmtRow2Str = "| %s | %s |\n" FmtRow3Str = "| %s | %s | %s |\n" FmtRow4Str = "| %s | %s | %s | %s |\n" FmtRow5Str = "| %s | %s | %s | %s | %s |\n" FmtRow6Str = "| %s | %s | %s | %s | %s | %s |\n" FmtRow7Str = "| %s | %s | %s | %s | %s | %s | %s |\n" FmtRow8Str = "| %s | %s | %s | %s | %s | %s | %s | %s |\n" FmtRow9Str = "| %s | %s | %s | %s | %s | %s | %s | %s | %s |\n" FmtRow10Str = "| %s | %s | %s | %s | %s | %s | %s | %s | %s | %s |\n" )
Markdown format constants for repeated table separators and row patterns.
The card rows and the heading formats that used to sit here are gone: Card writes both, through one writer that escapes the value at the write and keeps the heading level of the block it is nested in. A format string cannot do either, which is why a caller holding one wrote the escaping itself or forgot to.
const ( EmojiDraft = "\U0001F4DD" // 📝 EmojiWarning = "⚠️" // ⚠️ EmojiConfidential = "\U0001F512" // 🔒 EmojiArchived = "\U0001F4E6" // 📦 EmojiStar = "⭐" // ⭐ EmojiSuccess = "✅" // ✅ EmojiCross = "❌" // ❌ EmojiRefresh = "\U0001F504" // 🔄 EmojiFile = "\U0001F4C4" // 📄 EmojiFolder = "\U0001F4C1" // 📁 EmojiCalendar = "\U0001F4C5" // 📅 EmojiUpArrow = "⬆️" // ⬆️ EmojiDownArrow = "⬇️" // ⬇️ EmojiInfo = "ℹ️" // ℹ️ EmojiQuestion = "❓" // ❓ EmojiLink = "\U0001F517" // 🔗 EmojiUser = "\U0001F464" // 👤 EmojiGroup = "\U0001F465" // 👥 EmojiPipeline = "\U0001F6A7" // 🚧 EmojiMergeRequest = "\U0001F5C3" // 🗃️ EmojiIssue = "\U0001F4A1" // 💡 EmojiRed = "\U0001F534" // 🔴 EmojiOrange = "\U0001F7E0" // 🟠 EmojiYellow = "\U0001F7E1" // 🟡 EmojiGreen = "\U0001F7E2" // 🟢 EmojiProhibited = "\U0001F6AB" // 🚫 EmojiWhiteCircle = "⚪" // ⚪ EmojiParty = "\U0001F389" // 🎉 EmojiPurple = "\U0001F7E3" // 🟣 EmojiBlue = "\U0001F535" // 🔵 EmojiStop = "⛔" // ⛔ EmojiSkip = "⏭️" // ⏭️ EmojiNew = "\U0001F195" // 🆕 EmojiHand = "✋" // ✋ )
Contextual emoji constants for consistent visual indicators across formatters.
EmojiWarning is the negative-polarity flag Card.Warn writes: a condition such as revoked, locked, expired or has failures is marked with it and never with the tick BoolEmoji gives a true, which on such a label reads as success.
const ( MetaParamSchemaOpaque = "opaque" MetaParamSchemaCompact = "compact" MetaParamSchemaFull = "full" )
Meta-tool param schema mode constants. Mirrors the values accepted by the META_PARAM_SCHEMA env var and --meta-param-schema CLI flag in package config. Duplicated here to avoid an import cycle (config → toolutil → mcp).
const ( PollMinInterval = 5 PollMaxInterval = 60 PollDefaultInterval = 10 PollMinTimeout = 1 PollMaxTimeout = 3600 PollDefaultTimeout = 300 )
Polling bounds and defaults (all values in seconds) for tools that wait on a GitLab resource to reach a terminal state (pipelines, jobs, deployments).
const BoardListMetricLabel = "Limit Metric"
BoardListMetricLabel is what a board column's limit_metric is shown under: the row of the column's own card and the column of every table of columns, project and group boards alike, so the two packages name it in the same words (issue 1256). The cell beside it is CellOr with a dash, since a column with no limit, and every column on a Free instance, has no metric.
const DateFormatISO = "2006-01-02"
DateFormatISO is the date-only layout GitLab uses for a due date or a start date. The literal-Z timestamp layout that sat beside it is gone: it stamped whatever wall clock a value carried with a zone it may not have been in, and RFC3339 and RFC3339Ptr convert to UTC before they write.
const DefaultMaxArgumentDepth = 64
DefaultMaxArgumentDepth is the nesting ceiling applied to a tools/call arguments object.
No schema this server registers nests anywhere near it — the deepest is a few objects inside a few arrays — so it is a ceiling on shapes nothing legitimate produces rather than a budget a caller could plausibly spend.
const DestinationRefusedMessage = "this server refused to connect to that address, so the request never left the process"
DestinationRefusedMessage is what a caller is told when this server declined to open the connection an action needed.
It says the request never left the process, because that is the fact every other reading of the symptom gets wrong: nothing was sent, nothing answered, and no credential was disclosed to the address named in the cause. Retrying changes nothing, which is why the sentence does not suggest it and the hint names a flag instead.
const ErrMsgContextCanceled = "context canceled"
ErrMsgContextCanceled is the operation label passed to WrapErr when a tool handler detects context cancellation before calling the GitLab API.
const GraphQLQuickActionsStatusSelection = "quickActionsStatus { commandNames commandsOnly errorMessages messages }"
GraphQLQuickActionsStatusSelection is the selection every createNote and updateNote document sends beside its note, spelled once so that a document cannot ask for part of it: GitLab's account of the quick actions the body carried, which is the only place a caller learns whether "/label ~bug" was applied, refused, or was the whole of a body GitLab therefore kept no note for. The documents concatenate it as a constant, so the schema and shape gates judge the string GitLab receives.
const HintPreserveLinks = "" /* 127-byte string literal not displayed */
HintPreserveLinks reminds the LLM to keep the clickable [text](url) markdown links when presenting list results to the user.
const MaxDownloadBytes = 32 << 20
MaxDownloadBytes is the largest body a download action returns in one MCP response: a project or group export archive, a dependency list export, a job artifact read as a whole.
It bounds what the download becomes rather than what it costs to read: the body is buffered, base64-encoded a third larger again, and copied into a JSON-RPC message, so one call carries several times the body itself and nothing else caps that chain. The client-wide response ceiling caps the read alone (RQB-009), and a caller can start as many downloads as it likes.
A body above the ceiling is refused rather than truncated: a prefix of an archive cannot be imported or opened, and a prefix of a JSON document is not valid JSON, so a partial body answers the request wrongly rather than partially.
const MetaSchemaIndexURI = "gitlab://schema/meta/"
MetaSchemaIndexURI is the static URI returning the full meta-tool action catalog.
const MetaSchemaTemplateURI = "gitlab://schema/meta/{tool}/{action}"
MetaSchemaTemplateURI is the URI template for per-action params schemas.
const (
RateLimitRefusalPrefix = "rate limit exceeded for "
)
RateLimitRefusalPrefix and rateLimitRetrySuffix are how every refusal this limiter writes begins and ends, whichever of the two wire shapes carries it.
The prefix is exported because a refused tools/call arrives as a successful JSON-RPC result whose only distinguishing mark is this text: there is no code and no _meta on that shape, so anything that has to tell a refusal from a handler failure matches the wording. cmd/bench_resources' fairness scenario does exactly that, and reading the constant rather than a copy of the sentence makes a change of wording a compile-visible edit here instead of a silent reclassification of every refusal as a failure there.
const RedactedPlaceholder = "[redacted]"
RedactedPlaceholder is what a formatter writes instead of a value it will not echo, so that a reader sees something was withheld rather than that a tool had nothing to report. It is the one spelling every such site uses, so a reader who has met it once recognizes it everywhere.
const RedactedSecretValue = "REDACTED"
RedactedSecretValue is the standard placeholder for sensitive values that must not be exposed through structured output or Markdown renderers.
const SafeModeHint = "Set GITLAB_MCP_SAFE_MODE=false to execute this operation"
SafeModeHint is the operator-facing hint attached to every safe-mode preview.
const StepFormattingResponse = "Formatting response..."
StepFormattingResponse is a standard progress step label for response formatting.
const TblFieldValue = "| Field | Value |\n| --- | --- |\n"
TblFieldValue is the "| Field | Value |" detail table header and separator.
It is the table form of a one-object card, which Card supersedes: a card row is a list item, and a table is for a collection of objects that share columns. The constant stays until every formatter that opens a card as a table has moved; a new formatter starts a card with NewCard.
const UnattributedRequestMessage = "this request could not be attributed to a credential and was not sent to GitLab; retry, and report it if it persists"
UnattributedRequestMessage is what a caller is told when this server could not decide which credential a request belongs to.
It says what happened and asks for a report, because nothing the caller sent is wrong and nothing they can change will help: on a server shared by a configuration shape every handler resolves the caller's client from the request, and a handler that resolved none has run without one. The wording matches the refusal the subscription path already gives for the same cause, so an operator meeting both sees one fact rather than two symptoms.
What it deliberately does not say is "not found" or "your token lacks access". Both were what a caller used to see, and both send someone to check permissions that are perfectly fine.
It is one literal rather than a concatenation, as are the two descriptions of a 401 below: a constant has no statement to cover, so the mutation gate reports every operator of a concatenation as a mutant nothing can reach.
const UserCoreRefSelection = `
username
name
webUrl
`
UserCoreRefSelection selects a person GitLab names on a vulnerability or a finding: enough to say who it was and to link to them. The person's own fields are the users domain's to publish.
const VulnerabilityLinkSelection = `
name
url
`
VulnerabilityLinkSelection selects a reference the security report attached to a vulnerability or a finding: an advisory, a write-up, a fix.
const VulnerabilityLocationSelection = `
... on VulnerabilityLocationSast {
file
startLine
endLine
blobPath
vulnerableClass
vulnerableMethod
}
... on VulnerabilityLocationSecretDetection {
file
startLine
endLine
blobPath
vulnerableClass
vulnerableMethod
}
... on VulnerabilityLocationCoverageFuzzing {
file
startLine
endLine
blobPath
vulnerableClass
vulnerableMethod
crashAddress
crashType
stacktraceSnippet
}
... on VulnerabilityLocationDast {
hostname
path
requestMethod
param
}
... on VulnerabilityLocationDependencyScanning {
file
blobPath
dependency {` + vulnerableDependencySelection + `}
}
... on VulnerabilityLocationContainerScanning {
image
operatingSystem
containerRepositoryUrl
dependency {` + vulnerableDependencySelection + `}
}
... on VulnerabilityLocationClusterImageScanning {
image
operatingSystem
dependency {` + vulnerableDependencySelection + `}
kubernetesResource {
kind
name
namespace
containerName
clusterId
agent {
name
}
}
}
... on VulnerabilityLocationGeneric {
description
}
`
VulnerabilityLocationSelection selects a location through every member of GitLab's VulnerabilityLocation union. A union carries no fields of its own, and a member the document does not name comes back as an empty object, so a member left out here is a scan type whose findings carry no location at all.
const VulnerabilityTokenStatusSelection = `
status
lastVerifiedAt
createdAt
updatedAt
`
VulnerabilityTokenStatusSelection selects what GitLab last learned about the secret a secret detection finding exposed. The record's global id is left out: no action of this server takes it.
Variables ¶
var ( ReadAnnotations = &mcp.ToolAnnotations{ ReadOnlyHint: true, DestructiveHint: new(false), IdempotentHint: true, OpenWorldHint: new(true), } CreateAnnotations = &mcp.ToolAnnotations{ DestructiveHint: new(false), OpenWorldHint: new(true), } UpdateAnnotations = &mcp.ToolAnnotations{ DestructiveHint: new(false), IdempotentHint: true, OpenWorldHint: new(true), } DeleteAnnotations = &mcp.ToolAnnotations{ DestructiveHint: new(true), IdempotentHint: true, OpenWorldHint: new(true), } // NonDestructiveMetaAnnotations are for meta-tools that include // create/update operations but no delete actions. NonDestructiveMetaAnnotations = &mcp.ToolAnnotations{ DestructiveHint: new(false), OpenWorldHint: new(true), } // MetaAnnotations are annotations for meta-tools that combine read/write/delete. // Since a single meta-tool may include destructive actions, annotations reflect // the most cautious combination. MetaAnnotations = &mcp.ToolAnnotations{ DestructiveHint: new(true), OpenWorldHint: new(true), } // ReadOnlyMetaAnnotations are for meta-tools with only list/get/search actions. ReadOnlyMetaAnnotations = &mcp.ToolAnnotations{ ReadOnlyHint: true, DestructiveHint: new(false), IdempotentHint: true, OpenWorldHint: new(true), } )
Tool annotation presets for different operation categories. Each preset configures MCP hints that help LLMs understand whether a tool is read-only, destructive, or idempotent.
var ( // ContentUser marks a block for the user's display rather than the // model's reasoning: the image block of an upload or a visualization, // which a text-only model cannot read and a person is shown. // [ToolResultWithImage] and [FinishToolResult] put it on every image // block; the text beside it stays with the assistant. ContentUser = &mcp.Annotations{ Audience: []mcp.Role{"user"}, Priority: 0.8, } // ContentAssistant marks content primarily for LLM processing (search, raw data). ContentAssistant = &mcp.Annotations{ Audience: []mcp.Role{"assistant"}, Priority: 0.7, } )
Content annotation presets for the content blocks of a tool result. These guide MCP clients on who the content is intended for and its importance.
var ( // ContentList marks list/search results for LLM processing with lower priority. ContentList = &mcp.Annotations{ Audience: []mcp.Role{"assistant"}, Priority: 0.4, } // ContentDetail marks single-entity details for LLM processing with medium priority. ContentDetail = &mcp.Annotations{ Audience: []mcp.Role{"assistant"}, Priority: 0.6, } // ContentMutate marks create/update/delete results for LLM processing with high priority. ContentMutate = &mcp.Annotations{ Audience: []mcp.Role{"assistant"}, Priority: 0.8, } )
Operation-based content annotation presets. All use audience ["assistant"] so the Markdown content is available to the LLM for reasoning, while StructuredContent (JSON) serves programmatic clients. This avoids redundant display when clients show both Content and StructuredContent.
var ( // ResourceList marks list-shaped resources, readable by user and model. ResourceList = &mcp.Annotations{ Audience: []mcp.Role{"user", "assistant"}, Priority: 0.4, } // ResourceDetail marks single-entity resources, readable by user and model. ResourceDetail = &mcp.Annotations{ Audience: []mcp.Role{"user", "assistant"}, Priority: 0.6, } // ResourceMachineList and ResourceMachineDetail are for machine-facing // resources — call shapes, entry IDs, input schemas. Their intended // customer is the assistant deciding what to call (and programmatic // clients); the raw JSON has near-zero display value for a person, so // this is the one resource family that drops the "user" role. Every // other resource keeps both roles per the rationale above — and keeping // the split real is what makes the audience field carry information at // all: a value uniform across every resource tells a consumer nothing. ResourceMachineList = &mcp.Annotations{ Audience: []mcp.Role{"assistant"}, Priority: 0.4, } ResourceMachineDetail = &mcp.Annotations{ Audience: []mcp.Role{"assistant"}, Priority: 0.6, } )
Resource-facing annotation presets.
These mirror the priorities of the operation presets above but add the "user" role, and the difference is deliberate. The assistant-only audience exists to stop a client rendering a tool result twice, once from Content and once from StructuredContent. A resource has no such duality: it is content a person asks for by URI — a workflow guide, a project's issues — so telling the client not to show it to that person describes it wrongly.
var ( IconBranch = icon("branch", svgBranch) IconCommit = icon("commit", svgCommit) IconIssue = icon("issue", svgIssue) IconMR = icon("mr", svgMR) IconPipeline = icon("pipeline", svgPipeline) IconJob = icon("job", svgJob) IconRelease = icon("release", svgRelease) IconTag = icon("tag", svgTag) IconProject = icon("project", svgProject) IconGroup = icon("group", svgGroup) IconUser = icon("user", svgUser) IconWiki = icon("wiki", svgWiki) IconFile = icon("file", svgFile) IconPackage = icon("package", svgPackage) IconSearch = icon("search", svgSearch) IconLabel = icon("label", svgLabel) IconMilestone = icon("milestone", svgMilestone) IconEnvironment = icon("environment", svgEnvironment) IconDeploy = icon("deploy", svgDeploy) IconSchedule = icon("schedule", svgSchedule) IconVariable = icon("variable", svgVariable) IconRunner = icon("runner", svgRunner) IconTodo = icon("todo", svgTodo) IconHealth = icon("health", svgHealth) IconUpload = icon("upload", svgUpload) IconBoard = icon("board", svgBoard) IconSnippet = icon("snippet", svgSnippet) IconToken = icon("token", svgToken) IconIntegration = icon("integration", svgIntegration) IconNotify = icon("notify", svgNotify) IconServer = icon("server", svgServer) IconSecurity = icon("security", svgSecurity) IconConfig = icon("config", svgConfig) IconAnalytics = icon("analytics", svgAnalytics) IconKey = icon("key", svgKey) IconLink = icon("link", svgLink) IconDiscussion = icon("discussion", svgDiscussion) IconEvent = icon("event", svgEvent) IconContainer = icon("container", svgContainer) IconImport = icon("import", svgImport) IconAlert = icon("alert", svgAlert) IconTemplate = icon("template", svgTemplate) IconInfra = icon("infra", svgInfra) IconEpic = icon("epic", svgEpic) IconShield = icon("shield", svgShield) IconAudit = icon("audit", svgAudit) IconQueue = icon("queue", svgQueue) IconBot = icon("bot", svgBot) IconVulnerability = icon("vulnerability", svgVulnerability) IconCompliance = icon("compliance", svgCompliance) IconAchievement = icon("achievement", svgAchievement) IconBrand = icon("brand", svgBrand) )
Domain icons — each returns a three-element []mcp.Icon (SVG + light/dark WebP fallback) ready for the Icons field.
var ErrInvalidRateLimit = errors.New("invalid rate limit configuration")
ErrInvalidRateLimit is returned by ValidateRateLimit when the parameters are inconsistent (e.g. burst < 1 with rps > 0).
var ReadOnlyNameSuffixes = []string{
"_list", "_lists", "_get", "_search",
"_latest", "_blame", "_raw", "_diff", "_refs",
"_statuses", "_signature", "_languages", "_statistics",
}
ReadOnlyNameSuffixes lists tool-name suffixes that imply a read-only operation. Shared between annotation derivation and surface-quality audits so both agree on what "looks read-only".
Functions ¶
func AccessLevelDescription ¶
func AccessLevelDescription(level gl.AccessLevelValue) string
AccessLevelDescription maps a GitLab access level to its human-readable label. A level the table does not name is rendered with its number, "Level 35", because the number is what GitLab sent and the one thing a reader can act on; "Unknown" told them nothing, which is why two packages kept a copy of this table with the numeric fallback of their own.
func ActionDispatchOutputSchema ¶
ActionDispatchOutputSchema returns a permissive JSON Schema for tools whose exact structured result depends on the selected catalog action.
The pagination properties are the JSON names of PaginationOutput, the block a list read over REST carries, and a test reads them off the struct. They are not marked required, and the object stays open, because a list read over GraphQL carries its cursor block (GraphQLPaginationOutput) under the same key, and the SDK validates every result against this schema.
func ActionTimeout ¶
ActionTimeout reports the deadline every action runs under, 0 for none.
func AddMetaTool ¶
func AddMetaTool(server *mcp.Server, name, desc string, routes ActionMap, icons []mcp.Icon, formatResult FormatResultFunc)
AddMetaTool registers an action-dispatched meta-tool with route-derived annotations. Use it for meta-tools that may include mutating or destructive actions; if any route is destructive, the tool receives DestructiveHint=true.
func AddReadOnlyMetaTool ¶
func AddReadOnlyMetaTool(server *mcp.Server, name, desc string, routes ActionMap, icons []mcp.Icon, formatResult FormatResultFunc)
AddReadOnlyMetaTool registers an action-dispatched meta-tool whose actions are all read-only list/get/search-style operations.
func AdjustPagination ¶
func AdjustPagination(p *PaginationOutput, itemCount int)
AdjustPagination completes the pagination of a list GitLab answers without the X-Total and X-Total-Pages headers, which is what its search API does. It numbers an unnumbered page 1 and derives HasMore from the next page, and it fills the two totals only on a lone page (no next page, no previous one, page 1 at most), the one page whose rows are the whole result. Anywhere else it leaves them at 0, the value PaginationOutput documents for a total GitLab did not send: search redacts the rows a caller may not read after it pages, so neither this page's row count nor the next page number counts the result, and an earlier page need not have been full. The Markdown then says "shown, more available" instead of a count nothing measured.
A keyset page is left as PaginationFromResponse described it: it has no page number or total to infer from, and its successor is its next_page_token rather than a page.
func AnnotationsForContentKind ¶ added in v3.1.0
func AnnotationsForContentKind(kind string) *mcp.Annotations
AnnotationsForContentKind maps an action's declared content kind (ActionSpecContentList, ActionSpecContentDetail, ActionSpecContentMutate, ActionSpecContentAssistant, ActionSpecContentImage) to the content annotation its text block carries, in one place, so the kind a spec declares and the annotation the dispatcher serves cannot disagree. An image action's text block is for the assistant like any other; the image block itself carries ContentUser. An empty or unknown kind is the assistant default.
func AppendHints ¶ added in v3.2.0
AppendHints returns md with hints added to the server's guidance section: as further bullets of the section it already carries, wherever WriteHints put it, or as a section of their own when it carries none. Blank hints are dropped, and with none left md is returned as it came.
A section is extended rather than a second one written, because ExtractHints reads one section and only one: a leading section is read in preference to anything after the body, and a closing one must end the response, so a second section would either be ignored or hide the first.
func ApplyActionMeta ¶
func ApplyActionMeta(options *ActionSpecOptions, meta ActionMetaEntry)
ApplyActionMeta overlays the non-zero fields of meta onto options. Aliases and RelatedActions are copied defensively so callers may share a metadata table across actions without aliasing the underlying slices. It is a no-op for a zero-value entry, so a missing metadata row leaves the option defaults untouched.
func ApplyListOptions ¶
func ApplyListOptions(opts *gl.ListOptions, page PaginationInput, keyset KeysetPaginationInput)
ApplyListOptions copies offset (PaginationInput) and keyset (KeysetPaginationInput) parameters onto a gl.ListOptions, setting only the values the caller supplied. Pass a zero KeysetPaginationInput for endpoints that only support offset pagination.
A page_token that is a next_page_token a previous response published is not copied: it is a link carrying the listing's parameters, which KeysetContinuation makes the query of the request, and sent whole as page_token it would reach GitLab as a parameter of its own, which GitLab copies into the next link it writes. Only a raw GitLab page token goes on PageToken.
func ApplyWorkItemWidgets ¶ added in v3.3.0
func ApplyWorkItemWidgets(opts *gl.UpdateWorkItemOptions, in WorkItemWidgetInputs) (gl.RequestOptionFunc, error)
ApplyWorkItemWidgets copies the inputs that set a value onto opts, refusing a negative reference and a date that does not parse, and returns the request option that sends each removal the inputs ask for, or nil when they ask for none.
The option exists because client-go's UpdateWorkItemOptions cannot express a removal (register row 119): it wraps any reference, 0 included, into a global ID, so 0 reached GitLab as gid://gitlab/Milestone/0 and was refused as "No object found", and its widget fields are omitted when unset, so an empty date or health status sent nothing and the update answered success with the value still in place. GitLab removes each of these when the widget's field is null, which the option writes into the input client-go built, beside whatever else the update sets.
func AssigneeFilter ¶ added in v3.3.0
func AssigneeFilter(field string, value StringOrInt) (*gl.AssigneeIDValue, error)
AssigneeFilter converts an assignee_id filter into the value client-go's list options send, on the terms of [parseUserIDFilter]. It returns nil for a filter left out, so callers can assign the result unconditionally.
func AttachArgumentLimits ¶
AttachArgumentLimits registers a receiving middleware that refuses a tools/call whose arguments nest deeper than maxDepth, before the SDK decodes them.
Why this is not the HTTP body cap ¶
The SDK hands tools/call arguments to middleware as raw bytes and unmarshals them into a map[string]any only when it applies the tool's schema. That decoder (github.com/segmentio/encoding/json, through the SDK's internal json package) has no maximum-nesting guard, where the standard library refuses past 10000, and it is quadratic in nesting depth: measured on v0.5.4, an 18 KB value nested 9000 deep costs over a second of CPU, and a 4 MiB body admits depth in the millions. The decode runs ahead of additionalProperties validation and ahead of the handler, so read-only mode, safe mode and the tool's own logic are all downstream of the burn, and the per-second rate limiter counts requests rather than cycles.
A body-size cap alone only narrows the window: 256 KiB still admits depth 128000. The bound that closes it is on the shape, and it belongs in a receiving middleware rather than in the HTTP front door so that stdio — the transport with no body cap at all — is covered by the same check.
The scan is a single linear pass over bytes the SDK has already framed, so the guard costs about a microsecond on an ordinary call. A non-positive maxDepth disables it; a nil server is a no-op.
func AttachRateLimit ¶
func AttachRateLimit(server *mcp.Server, limiter *RateLimiter)
AttachRateLimit registers a receiving middleware that gates every method that reaches GitLab with the caller's credential, plus `completion/complete` and `tools/list`, when their buckets are empty.
`tools/call`, `resources/read`, `resources/subscribe`, `subscriptions/listen` and `prompts/get` draw on one bucket: each is a request to GitLab on the caller's behalf, and a limit that metered tool calls alone left the other doors open to the same upstream. They are refused differently because they fail differently. A refused tool call is reported as an MCP tool error result (IsError: true) rather than a JSON-RPC error, so the model receives a structured, retryable diagnostic and the agent loop can back off, carrying the resultType its revision requires (LabelForRevision). A refused resource or prompt request is a JSON-RPC error carrying the code that mirrors HTTP 429, since those results have no error flag of their own. A refused completion returns an empty completion instead: the documented contract for this surface is that autocomplete is never blocked, and an error in a completion popup is worse than no suggestions.
`tools/list` draws on a third bucket, refilled a tenth as fast as the tool-call one and holding the same burst (see [catalogDivisor]), and it is metered for a reason none of the others share: it reaches no upstream, it spends the processor of a process many tenants share. On the individual surface one listing marshals about 3.2 MB and is the majority of that surface's processor time, so a client listing in a loop takes the processor its co-tenants are waiting for while the shared bucket, which counts requests to GitLab, sees nothing at all. Keeping the two apart is also what preserves the property the old exemption provided: draining the tool-call bucket never refuses a client's discovery. It is refused like the flagless methods above, with the code that mirrors HTTP 429, because ListToolsResult carries no error flag either. One token is one JSON-RPC request, so a catalog split over several pages costs one per page; the server keeps its whole catalog in one page, for a different reason recorded where PageSize is set. A listing is charged first to a fourth bucket, the one the whole process shares and counts in tools listed ([processCatalog]), because the entry's multiplies by however many credentials a caller holds; that one refuses with the same code and the same words, and only the log line it writes says which refused.
Every other method (initialize, resources/list, prompts/list) bypasses the limiter: they reach no upstream and cost little to answer, and metering something cheap buys nothing and costs a concept. Which bucket each method draws on, and so which draws on none, is tenancy.MeterFor's answer (register rows RTC-001 to RTC-004); the buckets and the refusals stay here. If limiter is nil, this function is a no-op.
func AttachRateLimitFunc ¶
func AttachRateLimitFunc(server *mcp.Server, resolve func(context.Context) *RateLimiter)
AttachRateLimitFunc is AttachRateLimit for a server whose bucket depends on the request.
It exists because one server now answers for every credential of a configuration shape, while the limit is per credential: a bucket captured at registration would be one budget shared by every tenant, so the noisiest of them would refuse everybody else's calls. The resolver reads the bucket the request's own pool entry owns, and returning nil means this request is not limited, which is what an unbound request on a shape server and the stdio default both want. A listing is also charged to the bucket the whole process shares ([processCatalog]), wherever the entry's own listing bucket charges it.
func BuildMetaToolSchema ¶
BuildMetaToolSchema returns the input schema for a meta-tool given the chosen mode. Unknown modes silently fall back to MetaParamSchemaOpaque so that callers cannot break the tools/list payload by passing a typo.
- opaque: legacy {action, params:any} envelope (default).
- full: one if/then per action under allOf, with the full params schema MetaActionSchema serves.
- compact: the same conditionals, with descriptions and $defs stripped.
func BuildPipelineInputs ¶
func BuildPipelineInputs(raw map[string]any) (gl.PipelineInputsOption, error)
BuildPipelineInputs converts a JSON-decoded inputs map into the SDK's type-safe gl.PipelineInputsOption. JSON numbers decode to float64 and JSON arrays to []any; both are normalized to the SDK's supported value types (string, float64, bool, []string). An unsupported value type returns an error.
Shared by pipeline creation and manual-job play, which take the same spec:inputs value shapes.
func BuildTargetURL ¶
BuildTargetURL constructs a GitLab web URL for a target resource. Returns "" when the project web URL is empty, the IID is zero, or the target type has no known URL segment.
Supported target types: Issue, MergeRequest, Milestone.
func CancelledResult ¶
func CancelledResult(message string) *mcp.CallToolResult
CancelledResult returns an error tool result indicating the user canceled.
The operation was declined, so it produced none of the output its schema describes. A tool that declares an outputSchema MUST return structured results conforming to it, and a cancellation conforms to nothing, so this is reported as an error result rather than as a success carrying only prose, in the envelope every refusal travels in.
func CanonicalImportArchivePath ¶
CanonicalImportArchivePath validates a local GitLab export archive path and returns the canonical path resolved through symlinks. Archives must be regular .tar.gz files under the current working directory, the OS temporary directory, or a directory listed in GITLAB_MCP_ALLOWED_IMPORT_DIRS.
func CanonicalLocalDirPath ¶
CanonicalLocalDirPath resolves a caller-supplied path to an existing local directory and returns it canonicalized, subject to the same roots and the same HTTP refusal as CanonicalLocalFilePath.
func CanonicalLocalFilePath ¶
CanonicalLocalFilePath resolves a caller-supplied path to an existing local file and returns it canonicalized, provided the resolved path lies under the working directory, the OS temporary directory, or a directory listed in GITLAB_MCP_ALLOWED_UPLOAD_DIRS. It refuses every path when the server is reached over HTTP.
func CapturedThread ¶
func CapturedThread[T any](op string, d *gl.Discussion, capture *gitlabclient.ResponseCapture) (T, error)
CapturedThread converts one discussion with what its captured answer carries beside the SDK's decode, or reports under op the answer the type cannot hold. It is the whole tail of a handler that returns one thread, and answers in T, the thread type the handler's own package declares over DiscussionThreadOutput so the Markdown registry finds that package's formatter (see convertShape for what T must be).
func CapturedThreadNote ¶
func CapturedThreadNote[T any](op string, n *gl.Note, capture *gitlabclient.ResponseCapture) (T, error)
CapturedThreadNote converts one note of a thread the same way, answering in T, the note type the handler's package declares over DiscussionThreadNoteOutput.
func CapturedThreads ¶
func CapturedThreads[T any](op string, ds []*gl.Discussion, capture *gitlabclient.ResponseCapture) ([]T, error)
CapturedThreads does the same for a list of discussions, pairing each with its own extra by position.
func CatalogListingRPS ¶
CatalogListingRPS is the refill rate a listing draws on when the tool-call bucket is configured at rps.
Exported so the entrypoint can announce both figures at startup: the listing rate is the other number an operator can meet in a refusal, and the divisor that produces it belongs here rather than copied into a log statement. The burst is the configured one, unchanged; [catalogDivisor] records why.
func CellOr ¶ added in v3.3.0
CellOr renders a table cell the way Card.FieldOr renders a row: value through EscapeMdTableCell, or absent in its place when value is empty, for a column whose absence is the answer, such as the dash where a board column has no limit metric. absent is the server's own text and is written as given. It serves a CardTable row and a MarkdownTableRow alike.
func ClampPollInterval ¶
ClampPollInterval constrains a polling interval to [PollMinInterval, PollMaxInterval], returning PollDefaultInterval when the value is below the minimum.
func ClampPollTimeout ¶
ClampPollTimeout constrains a polling timeout to [PollMinTimeout, PollMaxTimeout], returning PollDefaultTimeout when the value is below the minimum.
func ClassifyError ¶
ClassifyError inspects the error chain and returns a short, human-friendly diagnostic message explaining what went wrong at a high level.
func ClassifyHTTPStatus ¶
ClassifyHTTPStatus returns a semantic description for common HTTP status codes. It describes the status alone, so a 401 gets the sentence true of both of its causes; ClassifyError, which holds the whole response, can say which one.
func ClearableDate ¶ added in v3.3.0
ClearableDate reads one optional date input of an update, where a field left out and a field set to an empty string mean different things: nil leaves the stored date alone, and an empty string removes it. An empty string returns a zero gl.ISOTime, which client-go encodes as JSON null, the value GitLab clears a date with. Anything else is read on the terms of ParseDateField.
func ClearableDateInputs ¶ added in v3.3.0
func ClearableDateInputs(inputs ...ClearableDateInput) error
ClearableDateInputs reads each update date into its destination on the terms of ClearableDate, refusing the call on the first value that does not parse, before any later one is read.
func CloneMetaSchemaRoutes ¶
CloneMetaSchemaRoutes returns a snapshot of the route maps: the two map levels are new, so later insertions and deletions in routes do not reach the snapshot, and every route in it is a CloneActionRoute copy.
The schemas are not copied. A route's InputSchema, OutputSchema and ParameterGuidance are frozen and shared by every consumer in the process, which is what lets a catalog cached per configuration serve every server without a copy per server; the copies this function used to make were half of the heap at a hundred pooled credentials. A consumer that must change a schema derives its own through DeriveSchema.
func CloneSchemaMap ¶
CloneSchemaMap returns a deep copy of a JSON Schema map, for a caller that has a reason to mutate one: every schema reachable from a shared catalog is shared with every server in the process and must be copied before it is changed.
func CompileToolSchemas ¶
CompileToolSchemas replaces a projected tool's map-based input and output schemas with process-cached *jsonschema.Schema equivalents. Passing the SDK a stable schema pointer instead of a map removes the per-registration JSON remarshal inside mcp.AddTool and lets a shared mcp.SchemaCache (keyed by pointer identity) skip schema resolution on subsequent registrations — the dominant cost when the HTTP server pool builds a new MCP server per token.
The conversion is a faithful roundtrip: the maps are produced by marshaling jsonschema output, and jsonschema.Schema preserves non-standard keys such as x_destructive. On any marshal error the original map is kept, preserving current behavior. An empty cacheKey disables compilation, so callers that cannot guarantee a content-stable key keep the map path.
func ComputeSHA256 ¶
ComputeSHA256 computes the SHA-256 checksum of a file at the given path and returns the lowercase hex-encoded hash string.
func ComputeSHA256Reader ¶
ComputeSHA256Reader computes the SHA-256 checksum from an arbitrary io.Reader and returns the lowercase hex-encoded hash string.
func ConfirmAction ¶
func ConfirmAction(ctx context.Context, req *mcp.CallToolRequest, message string) (*mcp.CallToolResult, error)
ConfirmAction uses MCP elicitation to ask the user for confirmation before a destructive action. Returns nil if the user confirmed or elicitation is unsupported (fallback: action proceeds — destructive callers must go through ConfirmDestructiveAction, which fails closed in that case). Returns an error tool result if the user declined or canceled. On sessions negotiated at protocol >= 2026-07-28 the confirmation travels as a multi round-trip input request (SEP-2322): the first pass returns an input-required result and the client retries the call with the user's answer attached.
func ConfirmDestructiveAction ¶
func ConfirmDestructiveAction(ctx context.Context, req *mcp.CallToolRequest, params map[string]any, message string) (*mcp.CallToolResult, error)
ConfirmDestructiveAction checks whether a destructive action should proceed. The confirmation flow is:
- GITLAB_MCP_YOLO_MODE / AUTOPILOT env var → skip confirmation entirely
- Explicit "confirm": true in params → skip confirmation
- MCP elicitation supported → ask user interactively
- Elicitation unsupported and no confirm param → fail closed: return an error result prompting the caller to re-send with confirm: true
Returns nil, nil if the action should proceed. Returns a non-nil *mcp.CallToolResult if the action was canceled or requires explicit confirmation.
The error return is for protocol faults rather than tool outcomes: a requestState this server did not issue, one carrying a version it does not know, or an inputResponses value of the wrong type. "Protocol errors (malformed JSON, invalid schema, internal server errors) SHOULD return a JSON-RPC error response with an appropriate error code and message", and none of those three is something the model wrote or can correct — putting them in a tool result asks it to fix a field its client mangled. Everything a caller can act on (declined, cancelled, a client that cannot prompt) stays in the result, which is where MCP wants tool-level failure.
func ContainsAny ¶
ContainsAny returns true if err.Error() contains any of the substrings.
func ContextWithRequest ¶
ContextWithRequest returns a derived context carrying the MCP request.
func CopyReadOnlyMetaAnnotations ¶
func CopyReadOnlyMetaAnnotations() *mcp.ToolAnnotations
CopyReadOnlyMetaAnnotations returns a copy of ReadOnlyMetaAnnotations so callers never alias the shared singleton.
Note on ToolAnnotations.Title: it is deliberately left unset here and in DeriveAnnotations. Since MCP 2025-06-18 a Tool carries its own top-level Title, and the display-name precedence is Title → Annotations.Title → Name, so setting both only duplicates the string in every tools/list response. The individual-tool projection has always relied on the top-level Title alone; the meta surface now matches it.
func DecodeEncodedPathIdentifier ¶ added in v3.3.0
DecodeEncodedPathIdentifier reports the percent-decoding of a namespaced path an LLM sent URL-encoded ("group%2Fproject"), and false for anything else. Only a value carrying %2F is decoded, so a successful decode always differs from the input and always carries the "/" the caller is after: '%' is not a hex digit, so no earlier escape can absorb the '%' of a %2F, and PathUnescape either turns it into "/" or fails on the whole value.
It is exported for the handlers whose path input is not one of the identifiers above: a repository file path, a wiki slug, a submodule path. Those are decoded in their own handlers rather than by name here, because "file_path" also names a local filesystem path on every upload action.
func DefuseHintsHeading ¶
DefuseHintsHeading rewrites every copy of the server's guidance heading in s so it can no longer pass for one. Text with no such heading is returned unchanged.
func DeriveAnnotations ¶
func DeriveAnnotations(routes ActionMap) *mcp.ToolAnnotations
DeriveAnnotations computes tool-level MCP annotations from the route map. If any route is destructive, returns a copy of MetaAnnotations (DestructiveHint: true). If all routes are non-destructive, returns a copy of NonDestructiveMetaAnnotations. Each call returns a fresh copy to avoid aliasing the shared singletons.
func DeriveSchema ¶
DeriveSchema returns transform applied to schema, where derive builds the result and transform names the rewrite it performs. The name must identify the rewrite completely: two calls with the same name on the same schema are assumed to want the same output, so any parameter the rewrite depends on belongs in the name.
For a schema registered with ShareSchema the result is built once per process, registered as shared in turn, and returned to every later caller; derive must therefore never mutate its input. For any other schema the result is built privately on every call, exactly as before this memo existed. A transform reapplied to its own output returns that output.
func DetectRichContent ¶
DetectRichContent scans a GFM body for non-portable features that may not render correctly outside GitLab (mermaid diagrams, math blocks, raw HTML). Returns a comma-separated list of detected features or an empty string.
func DiscussionThreadOutputsFromGitLab ¶
func DiscussionThreadOutputsFromGitLab[T any](discussions []*gl.Discussion, extras []DiscussionExtra) []T
DiscussionThreadOutputsFromGitLab converts a list of discussions with the extras the captured list answer holds for each, by position, into T, the thread type the caller's package declares over DiscussionThreadOutput (see convertShape).
func DynamicConfirmationDescription ¶ added in v3.2.0
DynamicConfirmationDescription is the text of the x_confirmation marker a destructive action's input schema carries on the dynamic surface, in the schema gitlab_find_action returns and in the one gitlab://tools/{id} serves, which find links every result to. Both readers take it from here, so the two cannot say different things.
confirmationSkipped is the switch the dynamic execute gate reads (IsYOLOMode, issue 1166), and it chooses the text: while the gate asks for confirm, the model is sent to the user for approval first, and once the switch skips the gate the marker says so, since an approval nobody gives would stall the unattended run the switch exists for.
func EditJSONBody ¶ added in v3.3.0
func EditJSONBody(edit BodyEdit) gl.RequestOptionFunc
EditJSONBody returns the request option that applies edit to the body of a write client-go has encoded, for the values an update has to send that its option struct cannot express: a null that clears a field whose pointer is tagged omitempty, an empty list a slice tagged omitempty leaves out, and the removal of a key a field without omitempty writes as null on every call. An edit with nothing in it returns no option, so the request goes out exactly as client-go built it.
It is meant for the POST, PUT and PATCH routes, whose options client-go writes as a JSON object. A body that is not one, or cannot be read, fails the request rather than sending it without the edit it was asked to carry.
func EmbedCanonicalResource ¶
func EmbedCanonicalResource(result *mcp.CallToolResult, template string, params map[string]any, value any)
EmbedCanonicalResource appends the canonical resource of a successful action result: the template is the one the action's spec declares, expanded from the parameters the call carried, and the payload is the JSON form of the output. It is a no-op without a template, on an error result, when a variable is missing, or when embedding is disabled, so every dispatcher can call it unconditionally after formatting.
FinishToolResult calls it last, after the hints have been set on the output, so the JSON the block carries and the structured output of the same result are one serialization: an embed run before the hints carried a body that lacked the next_steps the structured output had.
An output that implements ResourceURIParamsProvider names its own identity: its values are laid over the call's, in a copy, so the URI names the object returned rather than the reference the call resolved.
func EmbedResource ¶
func EmbedResource(result *mcp.CallToolResult, uri, mimeType, text string)
EmbedResource appends an EmbeddedResource content block to result that references the canonical MCP resource URI for the entity returned by the tool. mimeType should typically be "application/json" with text containing a compact JSON serialization of the entity (or empty if the resource is addressable but the body is large).
When result is nil, the URI is empty, or the global toggle is disabled, EmbedResource is a no-op. No further URI validation is performed; callers are responsible for passing well-formed URIs that match an MCP resource template registered with the server.
The block is annotated ResourceMachineDetail: it is the machine-facing payload of one entity, for the assistant deciding what to call next and for a programmatic client, with no display value for a person, which is the one resource family that drops the user role.
func EmbedResourceJSON ¶
func EmbedResourceJSON(result *mcp.CallToolResult, uri string, value any)
EmbedResourceJSON marshals value as JSON and embeds the result with MIME type application/json. Marshaling errors are dropped silently — the tool result is still returned with text and StructuredContent so the LLM has a usable response.
A nil result and an empty URI are decided by EmbedResource, which is the one place a block is appended and already refuses both; repeating the two checks here only meant that no test could tell either copy from the other. The toggle is checked here as well, and that one earns its place: it is what keeps a deployment with embedded resources off from marshaling every entity it is never going to embed.
func EmbeddedResourcesEnabled ¶
func EmbeddedResourcesEnabled() bool
EmbeddedResourcesEnabled reports the current state of the global toggle. Exposed for tests; production code should call EmbedResource directly.
func EmptyMessage ¶ added in v3.1.0
EmptyMessage is the one sentence an empty list renders, "No <resource> found." with its newline, the whole response of a list with nothing in it. The resource is a plural noun the formatter names ("merge requests", "protected branches"). It replaces the heading rather than sitting under it: a heading counting zero above a sentence saying so said it twice.
func EnableEmbeddedResources ¶
func EnableEmbeddedResources(enabled bool)
EnableEmbeddedResources toggles the global EmbedResource behavior. When false, EmbedResource is a no-op, preserving the legacy two-block (text + structuredContent) tool result shape.
func EnrichPaginationConstraints ¶
EnrichPaginationConstraints registers a receiving middleware that walks every tools/list response and injects JSON Schema numeric constraints on the standard pagination property names so LLM clients see the bounds directly in tools/list rather than only through prose in the description.
The middleware operates per property name:
- `page` gets `minimum: 1`
- `per_page` gets `minimum: 1` and `maximum: 100`
Existing constraints are preserved: if a schema already declares `minimum` or `maximum` on these properties the middleware leaves them untouched so domain-specific overrides remain authoritative. Only nodes whose `type` is `integer` or `number` (or unset, defaulting to integer per the Go SDK's int-typed pagination input) are modified, so unrelated properties named `page` on a custom schema cannot be silently mutated.
The transformation runs after LockdownInputSchemas so it sees the same fully-populated schema set every list/tools response carries.
Concurrency. As with LockdownInputSchemas, the mutation runs through [onFirstToolsList], whose `sync.Once` guard prevents concurrent `tools/list` calls from racing on the shared *Tool.InputSchema maps.
Sharing. Like the lockdown, this derives the enriched schema instead of changing the one in place (see DeriveSchema): the lockdown's output is shared across servers, and so is this one.
func ErrFieldRequired ¶
ErrFieldRequired returns a validation error indicating that a required field is missing or empty. It produces the message "<field> is required", which is the standard validation pattern used across all tool handlers.
func ErrInvalidEnum ¶
ErrInvalidEnum returns a validation error indicating that a field value is not one of the allowed options. The error message lists the valid values to guide LLMs toward correct parameter usage.
func ErrRequiredInt64 ¶
ErrRequiredInt64 returns a formatted error when a required int64 field is missing or has its zero value. This catches silent deserialization failures in meta-tool dispatch, where a misnamed JSON parameter (e.g. "mr_iid" instead of "merge_request_iid") is silently ignored and the field defaults to 0.
func ErrRequiredString ¶
ErrRequiredString returns a formatted error when a required string field is missing or empty. Like ErrRequiredInt64, this guides LLMs to use the exact parameter name when silent deserialization failures occur.
func ErrorResult ¶
func ErrorResult(message string) *mcp.CallToolResult
ErrorResult builds a standard error mcp.CallToolResult with IsError set, through ErrorResultAnnotated with the refusal annotation. The message is the server's own sentence, written as prose.
func ErrorResultAnnotated ¶ added in v3.1.0
func ErrorResultAnnotated(md string, ann *mcp.Annotations) *mcp.CallToolResult
ErrorResultAnnotated builds the one error envelope every refusal this server writes travels in: IsError set, the Markdown as the one text block through NormalizeResultMarkdown, and the annotation given, or ContentMutate for nil, since a refusal is something the model has to act on. The five hand-built literals it replaced carried no annotation and skipped the normalization every success got.
func ErrorResultMarkdown ¶
func ErrorResultMarkdown(domain, action string, err error) *mcp.CallToolResult
ErrorResultMarkdown creates an MCP tool error result carrying the DetailedError card for err, through ErrorResultAnnotated: it is the one path a handler has to a rich error result, and it travels in the same envelope as every other refusal.
func EscapeConsentValue ¶
EscapeConsentValue renders caller-controlled text for a consent dialog.
"MCP servers requesting elicitation SHOULD NOT include URLs intended to be clickable in any field of a form mode elicitation request." The dialog is where a person decides whether to allow an action, so what it shows has to be the server's own words plus data that cannot pass for them. Interpolating raw values did not meet that: a project path of
demo/proj\n\n**SECURITY NOTICE** Verify at https://evil.example/verify
reached the user as bold text and a link inside the question they were being asked, all of it attacker-supplied by way of the model.
Three things happen here. Line breaks collapse, so a value cannot add paragraphs to the dialog's structure. URL schemes are defanged to https[:]//, which stays legible while leaving nothing for a client to linkify. And the result is fenced in backticks, so emphasis, headings and link syntax inside it are shown rather than rendered — using a fence longer than any run of backticks in the value, the same rule Markdown itself uses for nesting code.
This is escaping, not sanitizing: the text still reaches the user, and should, since a project path they cannot read is no use to them deciding. What it can no longer do is impersonate the server asking the question.
func EscapeMdHeading ¶
EscapeMdHeading sanitizes a user-controlled string that will be interpolated into a Markdown heading (e.g. `## Project: {name}`). It strips leading '#' characters that could promote/demote the heading level and collapses newlines into spaces so the heading stays on one line.
The opening angle bracket and the opening square bracket are escaped for the reason EscapeMdTableCell gives, and in the same entity form: a heading is the other place a formatter puts a GitLab-authored name, all 44 call sites interpolate one value into a heading the server wrote, and the same name should not turn into a live tag or a live link in a heading after being neutralized in a cell. A heading therefore carries no link: a card's address is its URL row.
func EscapeMdLinkDestination ¶
EscapeMdLinkDestination renders url as the destination of a Markdown link. It contains the delimiters and judges nothing: whether the value may be a destination at all is LinkableDestination's question, asked by the callers that decide to write a link. The whitespace around the value is dropped first, as LinkableDestination drops it before judging, so the address written is the one that was judged: encoded instead, a leading space made " https://host/x" the relative destination "%20https://host/x". A space inside the value is still encoded.
func EscapeMdLinkLabel ¶
EscapeMdLinkLabel renders s as the visible text of a Markdown link.
A label is not a cell: escaping the pipe keeps the row intact and does nothing about the bracket. An issue titled
Fix login](http://attacker.invalid/x)
closed the label after "Fix login" and opened a destination of its own, so a reader saw the issue's title linking to a host that is not GitLab — on this server's own instruction, since HintPreserveLinks tells the model to keep the clickable links so the user can navigate to GitLab.
func EscapeMdTableCell ¶
EscapeMdTableCell escapes characters in s that would break a Markdown table row. Pipes are replaced with the HTML entity | and newlines/carriage-returns are replaced with a space so the cell stays on a single row. Control characters are dropped by StripControlBytes.
The opening angle bracket is an entity too, for the reason [mdLinkLabelEscaper] gives: a renderer takes raw HTML ahead of whatever surrounds it, so a title of
<a href="http://attacker.invalid/x">Fix login</a>
arrives as a working link to a host that is not GitLab. MdTitleLink closed that for the link case and returns this escaper's output untouched when the item has no URL, which is the ordinary shape for something with no page of its own, and around 266 formatters call this one directly.
An entity rather than the backslash the label escaper uses, because MdTitleLink pipes a cell through that escaper: a backslash arriving there is escaped again into a visible one, where < passes through untouched and renders as the character it always was.
The opening square bracket is an entity as well, [, because a value may be a link only when this server wrote the link around it. A title of
[Fix login](http://attacker.invalid/x)
passed through here as it was, so a cell or a list value the server meant as text rendered as a working link to a host that is not GitLab, and HintPreserveLinks then told the model to keep it clickable. The one consequence worth knowing is that a finished link does not survive this function: a link is built by MdTitleLink from its raw halves and is never escaped afterwards, and a cell that holds one is written as it is.
Only '<' and '[' are escaped, since everything a renderer obeys instead of reading opens with one of them (a tag, a comment, an autolink, a link, an image). '>' and ']' are left alone because callers compose cells of their own: a merge request's branch cell reads "feature/fix -> develop", and entity-encoding the arrow the server itself wrote damages output that no untrusted text ever touched.
The result is idempotent: nothing this function writes is anything it rewrites, so a value escaped twice renders once, which is what lets a caller that still escapes by hand pass its value to a helper that escapes again.
func ExceedsJSONDepth ¶
ExceedsJSONDepth reports whether raw nests containers deeper than limit.
Convenience wrapper over JSONDepthScanner for a value that is already whole in memory.
func ExecGraphQLDestroyNote ¶
func ExecGraphQLDestroyNote(ctx context.Context, gql gl.GraphQLInterface, op, hint, query, noteGID string) error
ExecGraphQLDestroyNote runs the destroyNote work item mutation, applying the shared error conventions: transport errors are wrapped with op + hint, a refusal GitLab answers at the top level is reported as that refusal, and the first mutation payload error becomes "op: message".
The document selects the errors alone. The payload type offers a note and a quick actions status as well, and Mutations::Notes::Destroy answers with neither: its resolver returns the errors and nothing else, so both are null on every response.
The top-level check is what keeps a refused delete from reading as a successful one. A note that is gone or that the token may not delete is refused with HTTP 200, a top-level error and a null payload, which client-go does not turn into an error, and a null payload carries no payload error either.
func ExpandResourceURI ¶
ExpandResourceURI fills a canonical resource URI template from an action's parameters. Each {name} is replaced by the value of the parameter of that name escaped as RFC 6570 simple expansion escapes it ([escapeSimpleExpansion]), so a project given as "group/project" lands in the URI as group%2Fproject and a scoped label priority::high as priority%3A%3Ahigh, which is the form the resource templates accept and the resource handlers decode once; {+name} keeps the slashes of a path-valued parameter, escaping each segment. It reports false when the template is empty or any variable is absent or empty, so a result whose identifier the caller never supplied gets no resource block instead of a URI with a hole in it.
func ExplicitConfirmFromRequest ¶
func ExplicitConfirmFromRequest(req *mcp.CallToolRequest) bool
ExplicitConfirmFromRequest reports whether the caller set the reserved confirm key on this tool call.
The key never reaches a typed action input: [stripReservedKeys] removes it before strict unmarshalling so it cannot trip the unknown-field rejection. A handler that needs to know whether the caller confirmed — because its destructiveness depends on runtime state and cannot be declared on the route — must therefore read it from the raw arguments, which this does for both call shapes: flat on individual tools, and nested under params on the dispatcher surfaces.
func ExtractGitLabMessage ¶
ExtractGitLabMessage extracts the specific error message from the GitLab response err carries, found over REST and GraphQL alike by [gitLabResponseOf]. Returns empty string if not found, if the message only repeats the HTTP status text (e.g. "405 Method Not Allowed"), or if it is an unparsed upstream response body rather than a GitLab message.
The unparsed-body drop belongs to [boundedGitLabMessage], which every path out of here returns through, so it is not tested a second time on the way in: a copy of that test could only agree with the one below it.
What survives is flattened onto one line and truncated to [maxGitLabMessageLen] bytes. GitLab's messages routinely quote input an attacker chose (a branch name, a path, a title), so the span has to stay a span: with its newlines intact it could add structure to the error text a model reads.
func ExtractHints ¶
ExtractHints parses the "💡 Next steps" section from a Markdown tool response and returns the individual hint strings. Returns nil when the section is absent.
Only a section in a position the server could have written it is read, and there are exactly two: WriteHints is called either on an empty builder, so the section opens the response, or after the body, so it closes it. The leading form is a defect the migration to one card vocabulary retires, not an idiom, and the number of formatters still using it only shrinks; it is read here because a response that has it carries no other section. A section anywhere in between is content that happens to look like guidance, a README, a job log, a project description, and content does not get to speak as the server. This used to be the first match anywhere in the response, which is what let a file's contents fill next_steps.
func FieldTiers ¶
FieldTiers returns a map from top-level JSON field name to the minimum licensing tier declared via the `tier:"premium"` / `tier:"ultimate"` struct tag on the input/output type rt. Fields without a tier tag (the common case, Free) are omitted. Anonymous embedded structs are flattened, mirroring the JSON field promotion used by [secretJSONFieldNames]. The result drives per-instance-tier schema pruning so Free/Premium instances never see fields that require a higher tier.
func FineGrainedErrorNote ¶ added in v3.2.0
FineGrainedErrorNote is FineGrainedNotes for a handler that reported an answer as a Go error: the error comes back carrying a note after its own message, and still unwraps to the error it was.
Two answers get one. GitLab's missing-permissions refusal of a permission the session's grant does hold, at the boundary type the refusal names, gets the authority's HeldElsewhereNote: GitLab's sentence is the same whether the grant lacks the permission or holds it for other projects, and only the session knows which, so the note says what is missing is this project or group in the grant, or its user's membership there (issue 1336). A not-found error, which is how most GraphQL handlers report a null, gets the NullNote.
func FineGrainedNotes ¶ added in v3.2.0
func FineGrainedNotes(ctx context.Context, actionID string, callResult *mcp.CallToolResult, result any) *mcp.CallToolResult
FineGrainedNotes adds to an answer a fine-grained session was served the next steps that say what GitLab did to it without saying so, and returns it. A classic session's answer is returned as it came.
Three notes, each worded by the authority (internal/finegrained) so every surface words them alike: the parts of the answer GitLab leaves empty for this credential; on a not-found answer of an action that reads GraphQL, that GitLab answers null for an object the token cannot see; and on an empty list of an action whose answer is a GraphQL list or connection, that GitLab leaves out the items the token cannot see. They are written into the answer's own next steps, so the tail every dispatcher applies afterwards (FinishToolResult) carries them into next_steps as well.
A safe-mode preview gets none: it is the answer to a call nothing sent to GitLab, so there is no answer of GitLab's for a note to describe. The meta and dynamic surfaces reach here with one, since safe mode replaces a write's route there (SafeModeActionFunc); the individual surface answers with its preview before it would.
func FineGrainedRefusal ¶ added in v3.2.0
func FineGrainedRefusal(ctx context.Context, req *mcp.CallToolRequest, callName, actionID, prefix string) *mcp.CallToolResult
FineGrainedRefusal is the one check every dispatcher makes before it does anything on an action's behalf: it answers the call with the reason when a fine-grained session may not run the action, and nil when it may.
It reads the authority of the client bound to the request (gitlabclient.AuthorityFrom), which only a fine-grained credential's client carries, so a classic session returns at once and pays nothing. What it decides comes from the authority's Decide and what it says from its WithheldText (internal/finegrained), so every surface refuses in the same words, which name the action by its canonical ID and never a tool.
The dispatchers call it before their own work: in the meta handler right after the action is resolved and its arguments validated, before a destructive action is confirmed and before the route's safe-mode preview; in the individual handler before safe mode; in dynamic execute right after the action is resolved; and in the standalone handler first. A withheld write is therefore never offered for confirmation nor previewed as something this token could do. The receiving middleware answers most calls before the SDK validates their arguments, and this is the defense behind it.
prefix is prepended to the refusal: the dynamic surface names its own tool there, as every refusal it writes does, and every other caller passes "". callName is what the refusal is logged under. The refusal is logged at INFO with the reason class RefusalFineGrained, and the action and its cause at DEBUG, never the token or anything of its grant.
func FinishToolResult ¶ added in v3.1.0
func FinishToolResult(callResult *mcp.CallToolResult, result any, route ActionRoute, params map[string]any) (finished *mcp.CallToolResult, structured any)
FinishToolResult is the one tail every dispatcher applies to a formatted result, so an envelope rule cannot be added to two dispatchers of three again. In order: a nil result, which a formatter returning "" produces, becomes the JSON rendering rather than reaching the client with no content block; an error result is returned as it is, with no structured output, since the action produced none of what its schema describes; every text block is annotated with the action's declared content kind and every image block for the user; the next-step hints the Markdown carries are set on the typed output when its type declares them; and the canonical resource is embedded last, so the JSON it carries agrees with the structured output, hints included.
The hints are set through the output's own HintSetter, never spliced into its JSON: the individual surface validates the structured output against the schema the type declares, and a next_steps key on a type that declares none would fail that validation, so a type without HintableOutput carries its hints in the Markdown alone.
func FormatCICDVariableCollectionMarkdown ¶
func FormatCICDVariableCollectionMarkdown[T any](variables []T, pagination PaginationOutput, convert func(T) CICDVariableMarkdown, title, emptyMessage string, includeEnvironmentScope bool, hints ...string) string
FormatCICDVariableCollectionMarkdown maps package-specific CI/CD variable outputs and renders them as a shared Markdown list.
func FormatCICDVariableDetailMarkdown ¶
func FormatCICDVariableDetailMarkdown(v CICDVariableMarkdown, title string, includeEnvironmentScope bool, hints ...string) string
FormatCICDVariableDetailMarkdown renders a CI/CD variable as the card the project, group and instance variables share, closed with the next steps the caller names. The hints are the caller's because the actions that update and delete a variable differ by scope (ci_variable.update, ci_variable.group_update, ci_variable.instance_update), and a card shared by all three cannot name one of them for the other two.
func FormatDiscussionMarkdown ¶
func FormatDiscussionMarkdown(discussion DiscussionMarkdown, hints ...string) string
FormatDiscussionMarkdown renders a single discussion thread as the card of one thread: the heading the card writer escapes, the thread's notes quoted under their authors, and the hints last. The notes are a collection the reader reads rather than a table of columns, so they keep the shape [writeDiscussionNotes] gives them.
The thread id reaches this renderer as a field of the result rather than as a literal the server wrote, and a heading is the one line where a value that is not a digest would open a tag, so the card's heading escaper is what contains it.
func FormatGID ¶
FormatGID builds a GitLab Global ID string from a type name and numeric ID.
FormatGID("Vulnerability", 42) → "gid://gitlab/Vulnerability/42"
func FormatGraphQLForwardPagination ¶
func FormatGraphQLForwardPagination(p GraphQLForwardPaginationOutput, shown int) string
FormatGraphQLForwardPagination renders the pagination metadata of a forward-only connection, which never names a previous page.
func FormatGraphQLPagination ¶
func FormatGraphQLPagination(p GraphQLPaginationOutput, shown int) string
FormatGraphQLPagination renders cursor-based pagination metadata as a Markdown summary line, suitable for appending to list tool responses.
func FormatISOTimePtr ¶
FormatISOTimePtr renders an optional *gl.ISOTime as YYYY-MM-DD, or "" when nil.
func FormatLabelListMarkdownFunc ¶
func FormatLabelListMarkdownFunc[T any](labels []T, pagination PaginationOutput, opts LabelMarkdownOptions, convert func(T) LabelMarkdown) string
FormatLabelListMarkdownFunc renders labels after mapping domain-specific outputs to the shared Markdown view.
func FormatLabelMarkdown ¶
func FormatLabelMarkdown(label LabelMarkdown, opts LabelMarkdownOptions) string
FormatLabelMarkdown renders a project or group label as a card: the identity rows, the description as the card's long text, the priority when GitLab sent one, the two flags as glyphs, and the counters when GitLab sent them, zeros included, since a label a listing counted and nothing uses is an answer of its own.
func FormatNoteListMarkdown ¶
func FormatNoteListMarkdown(notes []NoteMarkdown, pagination PaginationOutput, opts NoteListMarkdownOptions) string
FormatNoteListMarkdown renders a list of GitLab notes as a Markdown table. The table carries no link, so the footer carries no instruction to keep them.
func FormatNoteMarkdown ¶
func FormatNoteMarkdown(note NoteMarkdown, opts NoteMarkdownOptions) string
FormatNoteMarkdown renders a single GitLab note as a card: the author as a handle, the time, the flags that hold, the resolution state when the domain has one, the body as the card's long text, quoted under its label when it spans more than one line, and what the quick actions in the body did when a mutation reported it.
A note GitLab synthesizes from a resource event has no ID, so its card is headed by its global ID and says why no note action takes it.
func FormatQuickActionsOnlyMarkdown ¶ added in v3.1.0
func FormatQuickActionsOnlyMarkdown(title string, status QuickActionsStatusOutput, hints ...string) string
FormatQuickActionsOnlyMarkdown renders the answer to a note mutation whose body held nothing but quick actions: GitLab ran them and kept no note, so there is no note to show, only what the commands did. title names the kind of note the caller asked for ("Epic Note").
func FormatRESTDiscussionListMarkdown ¶
func FormatRESTDiscussionListMarkdown[T any](discussions []T, pagination PaginationOutput, convert func(T) DiscussionMarkdown, title, emptyMessage string, hints ...string) string
FormatRESTDiscussionListMarkdown maps REST discussion outputs and renders them with offset pagination metadata.
func FormatSafeModePreviewMarkdown ¶ added in v3.1.0
func FormatSafeModePreviewMarkdown(p SafeModePreview) string
FormatSafeModePreviewMarkdown renders a preview as a card, the way every other refusal is rendered: the intercepted tool in the heading, the status and mode rows, the tool as a code span, the would-be arguments in a JSON fence, and the operator's hint last. It is the registered formatter for SafeModePreview, so the dispatcher surfaces render the preview through it, and SafeModePreviewResult is what the individual surface answers with.
The hint is escaped, unlike every other hint this server writes. WriteHints writes a hint as it is given because a hint is the server's own sentence composed at the call site, and one of them, HintPreserveLinks, is a link; here the hint is a field of a struct the formatter is handed, so it is data on the same terms as the status and the mode. Unescaped it could end its own bullet and open a heading, a list item or a second guidance section below the card, which took the real hint out of next_steps with it.
func FormatStorageMoveCollectionMarkdown ¶
func FormatStorageMoveCollectionMarkdown[T any](moves []T, pagination PaginationOutput, convert func(T) StorageMoveMarkdown, title, emptyMessage, entityColumn string) string
FormatStorageMoveCollectionMarkdown maps package-specific storage moves and renders them as a shared Markdown list.
func FormatStorageMoveDetailMarkdown ¶
func FormatStorageMoveDetailMarkdown(move StorageMoveMarkdown, title string, hints ...string) string
FormatStorageMoveDetailMarkdown renders one repository storage move as a card: the identity and state rows, the two storages, the creation time in the display form, and the moved entity as a link with its ID.
func FormatTarget ¶
FormatTarget builds a Markdown table cell for a typed target resource. When targetURL is non-empty, the result is a clickable link like [Issue #42](url). When empty, the label is returned as plain text. Returns "" if there is nothing to display.
The title reaches MdTitleLink raw. It used to be escaped here and again inside the link helper, which was harmless only while the escaper left every character it wrote alone; the rule now is that a link is built from its raw halves once, and escaping a value that is about to be linked is the shape that turns the link back into text.
func FormatTemplateAttributeListMarkdown ¶
func FormatTemplateAttributeListMarkdown(items []TemplateAttributeListMarkdownItem, opts TemplateAttributeListMarkdownOptions) string
FormatTemplateAttributeListMarkdown renders a template list as a table, in the two-column and the three-column shape alike, so the two families share one heading, one empty form and one footer. Templates carry no link, so the footer carries no instruction to keep them.
func FormatTemplateContentMarkdown ¶
FormatTemplateContentMarkdown renders a GitLab template body as a card whose only content is the body inside a fenced code block, through Card.Fence, which sizes the fence past the longest backtick run in the body. On an instance with custom file templates the set is served out of a designated project, so the name is a repository file name somebody chose, and the heading is escaped as every card heading is.
func FormatTemplateDetailMarkdown ¶
func FormatTemplateDetailMarkdown(detail TemplateDetailMarkdown) string
FormatTemplateDetailMarkdown renders a template detail page as a card: the identity rows, the description as the card's long text, the three lists joined on their rows, and the content in a fence under its heading when the caller named one. The project-template endpoints also serve issue and merge-request description templates, whose name is a file in .gitlab/issue_templates named by whoever pushed it, so the heading is escaped as every card heading is.
func FormatTime ¶
FormatTime converts an RFC3339 timestamp string to a human-readable format ("2 Jan 2006 15:04 UTC"), falling back to the date-only layout and then to the string it was given.
Every one of this function's callers writes the result straight into Markdown, so the fallback runs it through EscapeMdTableCell rather than returning it as it arrived. A value that reaches the fallback is by definition not a timestamp, which leaves only two ways to get there: a field this server formatted itself and a field carrying whatever GitLab put in it. Returning the second verbatim put a pipe, a newline and a '<' into a table cell from 155 call sites, and telling each of those call sites to escape a timestamp would have taught the next reader that a date needs escaping. Escaping here costs nothing that renders: neither layout's output contains any character the escaper touches.
func FormatTimePtr ¶
FormatTimePtr renders an optional *time.Time as RFC 3339, or "" when nil.
It is superseded by RFC3339Ptr, which it is an alias of: the name says display and the output is the wire form, which is how a table came to show RFC 3339 where every other one shows FormatTime's layout. New code calls RFC3339Ptr, or FormatTimeValue for a value a reader sees, and the staticcheck deprecation marker goes on here once the forty-odd callers have moved, since the marker fails the lint gate on every one of them until then.
func FormatTimeValue ¶ added in v3.1.0
FormatTimeValue renders t in the display form, or "" for a zero time, which is what a field GitLab did not send decodes to and would otherwise read as the year one.
func GraphQLMutationError ¶
GraphQLMutationError formats mutation payload errors, if any.
func GraphQLNullPropagated ¶ added in v3.3.0
GraphQLNullPropagated reports whether the top-level errors of a GraphQL answer are all the ones a null below a non-null position writes, which is how an object GitLab denied answers where its position cannot be null. A set holding any other message, a refusal among them, is not one, and neither is an empty set: an answer that came back null with no error is the caller's to see.
func GraphQLNumber ¶ added in v3.1.0
GraphQLNumber reads a number GitLab's GraphQL API spells as a String: the iid of an issue or a merge request. A value that is not a number, which the schema allows, reads as zero rather than an error, since the object it sits on is worth reporting whether or not that one value parsed.
func GraphQLPartialPage ¶ added in v3.3.0
func GraphQLPartialPage(operation string, responseErrors []GraphQLError, kept, nulled int) (string, error)
GraphQLPartialPage reads what GitLab said beside a page of a connection it answered (issue 1298). GitLab nulls a node or a field it could not resolve where it sits and explains the null in errors[], keeping the rest of the answer, so errors[] beside a connection that resolved mean the page was answered in part, and a node answered null with no error is one left out all the same. kept is how many nodes the page still holds, and nulled how many it answered null.
It returns the account a page's card names, empty for a page answered whole, and an error for a page with no node left: read as a page, that answer would say the list is empty, which is what nobody knows. With errors[] the error is GraphQLTopLevelError's, so a refusal it carries is read as one.
func GraphQLTopLevelError ¶
func GraphQLTopLevelError(operation string, responseErrors []GraphQLError) error
GraphQLTopLevelError formats top-level GraphQL response errors, if any.
The error it returns keeps each message apart as well as joined into its text, because a message is read whole: GitLab refuses a GraphQL mutation a fine-grained token lacks the permission for with one errors[] entry whose message is exactly the sentence its authorization service wrote, and one of those sentences, "404 Not Found", means something only as a whole entry ([fineGrainedRefusalOf]).
func HasRegisteredMarkdownFormatter ¶
HasRegisteredMarkdownFormatter reports whether a Markdown formatter has been registered for the given Go type. Accepts either a reflect.Type (the canonical lookup path, matching spec.Route.OutputType) or a value of any kind (used by tests). Pointer types are dereferenced to their element type for the registry lookup, exactly as MarkdownForResult dereferences a pointer value. Returns false for nil, for the special "interface" reflect.Type returned by reflect.TypeOf on a nil/untyped interface, and for unregistered types.
This is the authoritative source for "is there a Markdown formatter for this output type?" queries from external tools (e.g. cmd/audit_discovery_completeness uses it to gate the `missing_next_steps` check).
func HintAction ¶ added in v3.1.0
HintAction composes a next-step hint that names an action by its canonical catalog ID, "Use action 'issue.update' to change this issue". The dynamic surface executes that ID as it is written. The meta and individual surfaces do not accept it: a meta tool's action argument takes the action's name within that tool, and an individual tool is named by its own declared name, which no rule derives from the ID (issue 1325). Those two surfaces register their dispatchers' formatters through WithResolvedNextSteps, which rewrites the hint into the call they accept before the result leaves the server, so a hint is written once, here, in the one form every surface can be resolved from.
func IdentityToContext ¶
func IdentityToContext(ctx context.Context, id UserIdentity) context.Context
IdentityToContext stores a UserIdentity in the context. Used at startup in stdio mode to make the identity available to all tool handlers.
func ImageMIMEType ¶
ImageMIMEType returns the MIME type for image file extensions. Returns an empty string for non-image files.
func IndividualToolFromActionSpec ¶
func IndividualToolFromActionSpec(spec ActionSpec, opts IndividualToolProjectionOptions) (*mcp.Tool, error)
IndividualToolFromActionSpec projects canonical action metadata into an MCP tool definition for the individual-tool surface.
func IndividualToolFromSpecs ¶
func IndividualToolFromSpecs(specs []ActionSpec, individualName string, opts IndividualToolProjectionOptions) (*mcp.Tool, error)
IndividualToolFromSpecs projects the spec that owns an individual tool name.
func InputRequiredResultFromError ¶
func InputRequiredResultFromError(err error) (*mcp.CallToolResult, bool)
InputRequiredResultFromError extracts the input-required tool result from a handler error produced by elicitation.Flow.PendingError. Surface dispatchers call this before logging so a pending multi round-trip exchange is returned to the client instead of being reported as a handler failure.
func InstanceWebURL ¶ added in v3.3.0
InstanceWebURL returns the web URL of the instance whose REST API is at apiBase: the API base with its trailing "api/v4/" removed, so an instance served under a relative URL root keeps the root (https://example.com/gitlab/api/v4/ gives https://example.com/gitlab/). The path always ends in "/". The userinfo, query and fragment an operator may have configured are left out, since the result is compared with URLs and never sent anywhere. apiBase itself is not changed.
func Int64UserIDFilter ¶ added in v3.3.0
func Int64UserIDFilter(field string, value StringOrInt) (*int64, gl.RequestOptionFunc, error)
Int64UserIDFilter reads a user ID filter for an option struct that types it *int64 and so cannot carry None or Any, which is how client-go's issue statistics options declare assignee_id (upstream register row 125). A user ID comes back as the pointer for the option. None and Any come back as a request option that writes the literal into the query under field, the option struct's field left nil. A filter left out returns neither, and a refusal returns only the error.
func InternalError ¶
InternalError marks an error as JSON-RPC -32603: an upstream call failed or a response would not decode, and there is nothing the caller can change.
func InvalidParams ¶
InvalidParams marks an error as JSON-RPC -32602: the caller sent something missing, empty or unparseable, and has to change it.
func IsBinaryFile ¶
IsBinaryFile returns true when the filename has a known binary extension that is not an image. Returns false for text and image files.
func IsDeleteToolName ¶
IsDeleteToolName reports whether name contains or ends with "delete".
func IsDownloadTooLarge ¶ added in v3.3.0
IsDownloadTooLarge reports whether err is a response ceiling refusing a body (gitlabclient.ErrResponseTooLarge): the client-wide one, or the one an action set on its request with gitlabclient.WithResponseLimit. A handler asks it of an SDK call's error so that a body the read stopped gets the same way out as one the action's own ceiling refused, rather than a bare "unexpected error".
func IsHTTPStatus ¶
IsHTTPStatus reports whether GitLab answered err with the given HTTP status code, over REST or over GraphQL. Useful for handling specific API responses like 404 (feature not available on CE) or 403 (insufficient permissions).
The status is read the way ClassifyError reads it, so the two never disagree about one error: a GraphQL refusal is looked through, and a 404 is recognized by client-go's sentinel, which carries no response.
func IsImageFile ¶
IsImageFile returns true when the filename has an image extension. Comparison is case-insensitive. Returns false for empty strings.
func IsInternalInspection ¶ added in v3.2.0
IsInternalInspection reports whether ctx carries WithInternalInspection's mark.
It is exported for the layers that narrow what a fine-grained session is listed (issue 952): the tools/list filter, the find filter and the gitlab://tools filter all hand the server's own listings the whole surface, since registration counts, narrows and snapshots what it registered, and on stdio the credential those listings would otherwise be filtered for is the process's own.
func IsNotFound ¶
IsNotFound reports whether err represents a 404 Not Found, either via a structured GitLab ErrorResponse status code or via a plain-text error message from client-go (which may contain "404 Not Found" as text).
func IsPermissionRefusal ¶ added in v3.1.0
IsPermissionRefusal reports whether GitLab answered err with a refusal that can be a valid credential lacking a permission: a REST 401 or 403 whose body carries no RFC 6750 error code and does not refuse the account itself, which gitlabclient.RefusalMayBePermission decides. It is what a handler keys a hint naming a role, a license or an owner on, where IsHTTPStatus with 403 used to be.
The 401 is the reason it exists. At the routes entry 55 of docs/development/upstream-bugs.md lists, GitLab refuses a missing permission with 401, so a hint scoped to 403 was never shown there. A hint scoped to 401 alone is wrong the other way: ClassifyError describes a 401 that GitLab said was about the credential as the token itself being refused, and a role suggestion after that verdict contradicts it. This predicate is false for those answers, for every other 401 or 403 the API guard writes a code on (a DPoP refusal or a restricted language server client, which ClassifyError does not describe as the token being refused, are among them), for its 403 about a missing token scope, and for its 403 refusing an account the API will not serve (blocked, deactivated, the Terms of Service not accepted and the like), so the hint follows only a description that leaves a permission refusal open. gitlabclient.RefusalMayBePermission holds the full rule.
A handler whose route answers a 403 that means something else, a license or an archived project rather than a role, pairs it with IsHTTPStatus to tell the two apart. The 403 is kept here because it is what these routes would answer if GitLab retired entry 55, and then a handler that keys one hint on this predicate alone needs no change. The handlers that pair it with a status do, since each reads the status as the cause: the security settings routes, the three external status check merge request routes, the fork link and the merge train add. Those are the ones to revisit when entry 55 is retired.
func IsReadToolName ¶
IsReadToolName reports whether name ends with a read-only suffix.
func IsTerminalStatus ¶
IsTerminalStatus reports whether a CI/CD status represents a finished state.
func IsYOLOMode ¶
func IsYOLOMode() bool
IsYOLOMode returns true when destructive action confirmation should be skipped entirely. It reads GITLAB_MCP_YOLO_MODE (or its old spelling YOLO_MODE, warned about at startup like every other renamed setting) and falls back to AUTOPILOT, a convention other agent tooling sets. Any truthy value (1, true, yes — case-insensitive) enables the mode.
func IssueStateEmoji ¶
IssueStateEmoji returns the Markdown emoji for an issue state.
func KeysetContinuation ¶ added in v3.3.0
func KeysetContinuation(keyset KeysetPaginationInput) gl.RequestOptionFunc
KeysetContinuation returns the request option every handler of a list GitLab pages by keyset passes on its SDK call, last, after gl.WithContext. When page_token is a next_page_token a previous response published, the next link's query becomes the whole query of the request through gl.WithKeysetPaginationParameters, so each endpoint is sent the parameter it pages by (id_before or id_after, cursor, page_token or last) and the rest of the listing's arguments exactly as GitLab read them and wrote them into the link. Whatever else the call sends in its query is dropped: GitLab writes every parameter of the listing into its link, an Array one such as labels or topic in Rack's bracket form (labels[]=a), while client-go sends the same filter comma-separated under the bare name, and Rack refuses with 400 a query that carries both. Any other page_token is a raw GitLab page token, which ApplyListOptions has already put on the request, and an empty one is the first page: neither changes it.
A continuation that carries a credential or sudo is refused rather than applied, because passing it on would let a page_token authenticate, or act as another user, where no argument of the action can.
func LabelForRevision ¶ added in v3.2.0
func LabelForRevision(req mcp.Request, result *mcp.CallToolResult) *mcp.CallToolResult
LabelForRevision returns result, which a receiving middleware answers the tools/call req with in place of the SDK's tool dispatcher, carrying the resultType req's revision requires.
Revision 2026-07-28 requires resultType on every result. go-sdk v1.8.0 sets it on a tools/call result only inside its own tool dispatcher, because such a result can also be input_required, and labels after the middleware chain returns only the result types that can never be input_required. So a tool result a middleware makes, which is what a refusal is, went to a 2026-07-28 client without the field while every call the dispatcher served carried "complete". The SDK keeps the field unexported behind an unexported setter, and its own decoding is the one public way to set it: the result is written with every field it has and its resultType, and read back through mcp.CallToolResult.UnmarshalJSON.
The resultType is chosen the way the SDK's dispatcher chooses it, by whether the result carries InputRequests: "input_required" when it does, even an empty map, which the SDK reads as load shedding, and "complete" when it does not. Labeled "complete", a result asking for input would go out as {"resultType":"complete","inputRequests":...}, and a client that trusts the label drops the requests; left as it was, it would go out with no label, breaking the requirement this exists to meet.
A request of an earlier revision gets result as it is, which is what the SDK sends such a client from its own dispatcher (see [namesResultTypeRevision] for where the two tests differ), and so does a nil result, which has nothing to label. A result that cannot make the round trip is handed back unlabeled too, the answer the SDK would have sent: no refusal this server makes is one, since each is a text block and an error flag.
It is the workaround for row 66 of docs/development/upstream-bugs.md, which go-sdk fixed after v1.8.0 in e40f35d, the merge of its pull request 1226. TestSDK_MiddlewareToolResult_GoesOutUnlabeled fails on the bump that brings the fix and says what to delete.
func LinkableDestination ¶ added in v3.1.0
LinkableDestination reports whether url may be written as the destination of a link or an image: an absolute http or https address, and nothing else.
The escaper above contains the delimiters, so a value cannot end the link it is in, and it says nothing about the scheme. A javascript, data or vbscript destination is a whole link that a client renders live, and it is the client's renderer that decides what a click does: goldmark drops such a destination on its own, and a chat surface that hands the href through does not. Every address this server links is one GitLab built from its own origin, so the allow list costs nothing legitimate. The check runs on the value with its control bytes gone, because "java\x00script:" is the escaped destination "javascript:" and would pass a check made on the bytes as sent.
Plain http is admitted on purpose: a self-managed instance served without TLS builds every address it sends on that scheme, and refusing it would unlink every row such an instance answers with. The scheme is read as the text before the first "://" and compared whole, so "xhttp" and "httpx" are refused, and an address is only linked when something follows the separator. A value with no separator at all has nothing following one, which is the same refusal.
func ListHints ¶
ListHints prepends HintPreserveLinks to list-result next-step hints, once: an empty hint or a copy of HintPreserveLinks among the arguments is dropped, so a caller that already names it does not get it twice. The hints it returns are for the section a list formatter writes after its table. Writing that section first, on an empty builder, is a defect rather than an idiom: the guidance then opens the response, ahead of the data it is about, and any hint the formatter appends afterwards can never reach next_steps, since ExtractHints reads a section only at one end of the response.
func ListRegisteredTools ¶
func ListRegisteredTools(ctx context.Context, server *mcp.Server, clientName string) ([]*mcp.Tool, error)
ListRegisteredTools lists tools registered on a server through an ephemeral in-memory MCP client session.
func LocalFilesystemAccessAllowed ¶
func LocalFilesystemAccessAllowed() bool
LocalFilesystemAccessAllowed reports whether caller-supplied local paths are honored in this process.
func LockdownInputSchemas ¶
LockdownInputSchemas registers a receiving middleware that rewrites tools/list responses so every tool's inputSchema declares `additionalProperties: false` at the root and on any nested object schema reachable through "properties", "items", "anyOf", "oneOf", or "allOf". It also strips jsonschema tag metadata such as ",required" from property descriptions after the SDK generates schemas. Schemas converted from SDK types are stored back as map[string]any values, so callers inspecting tools after this middleware runs should not expect the original concrete schema type.
Background. The MCP specification (2025-11-25 §server/tools) requires inputSchema to be a valid JSON Schema object but does not mandate `additionalProperties`. JSON Schema 2020-12 default semantics treat an unspecified `additionalProperties` as `true`, which silently accepts unknown fields. When an LLM mistypes an argument name (e.g. "projetc_id" instead of "project_id"), the server forwards an empty value to the handler, which then fails with a confusing "missing parameter" error rather than the actionable "unknown property" diagnostic the LLM needs to self-correct.
Schemas that already declare `additionalProperties` (true or false) at a given level are left untouched, so meta-tool router branches that intentionally permit unknown fields for forward compatibility remain intact.
Concurrency. The MCP Go SDK does not expose a public API to enumerate registered tools at startup, so the transformation runs inside a `tools/list` middleware via [onFirstToolsList], which guards the mutation with a `sync.Once`.
Sharing. The schema a tool carries at that point is never changed in place: a compiled schema is shared by every server in the process through the compile cache, and a map may be shared through a catalog. The locked down form is derived from it, once per process for a shared schema (see DeriveSchema), so a thousand pooled servers list one set of maps.
func LogMarkupRawOffset ¶ added in v3.3.0
LogMarkupRawOffset returns where, in the log s as written, the byte that StripLogMarkup keeps at offset n stands, n running from 0 to the length of the stripped text, whose end is the end of s: s from that offset strips to the stripped text from n. It maps n back through the two passes StripLogMarkup makes, the escape sequences of the text the section markers were removed from and then the markers of s, since a marker inside a sequence leaves a sequence only the second pass sees.
func LogToolCallAll ¶
func LogToolCallAll(ctx context.Context, req *mcp.CallToolRequest, tool string, start time.Time, result any, err error)
LogToolCallAll logs a tool call to stderr (slog). It is the standard logging function for all tool handlers. When the request contains authenticated user identity (any mode), it includes the user in the log output for audit trail purposes.
result is what the handler returned, taken so the record can say whether the call actually succeeded. A handler that reports failure in its result rather than as a Go error, which NotFoundResult does in every get handler that uses it, used to be logged as "tool call completed" with nothing distinguishing it from a call that worked. An operator could not compute an error rate from this stream, because the stream did not contain one.
func LogToolRefusal ¶
func LogToolRefusal(ctx context.Context, req *mcp.CallToolRequest, tool, reason string)
LogToolRefusal records a call the server declined to run.
These paths returned an error result to the model and wrote nothing at all: safe-mode previews, unknown actions, missing parameters and unconfirmed destructive actions each returned before reaching LogToolCallAll. They are refusals rather than failures, so they are INFO, and they are exactly the events an operator most wants to count: a deployment refusing every third call because its clients have not learned the parameter shape looks identical to a healthy one from the logs.
It is the second half of the observability item ADR-0011 already accepted: "add observability for find query, selected action, validation failure, policy block, and destructive confirmation events". Only the destructive one was delivered.
func LookupMetaActionSchema ¶
func LookupMetaActionSchema(routes map[string]ActionMap, tool, action string) (map[string]any, bool)
LookupMetaActionSchema returns the per-action params schema for a tool/action pair.
func MRStateEmoji ¶
MRStateEmoji returns the Markdown emoji for a merge request state.
func MakeMetaHandler ¶
func MakeMetaHandler(toolName string, routes ActionMap, formatResult FormatResultFunc) func(ctx context.Context, req *mcp.CallToolRequest, input MetaToolInput) (*mcp.CallToolResult, any, error)
MakeMetaHandler creates a generic MCP tool handler that dispatches to action routes. The formatResult function converts the action result into an MCP response. If formatResult is nil, a default JSON formatter is used.
Destructive actions (delete, remove, revoke, unprotect, etc.) are automatically intercepted with a user confirmation prompt via MCP elicitation before execution. Confirmation can be bypassed with GITLAB_MCP_YOLO_MODE/AUTOPILOT env vars or by passing "confirm": true in the action params.
func MarkdownCodeFence ¶
MarkdownCodeFence returns a backtick fence long enough to contain content: three backticks, or one more than the longest run inside it.
A fixed three-backtick fence is closed by any content that contains one, and the content here is a repository file, a job log, a snippet or a diff, written by whoever can push a branch or run a pipeline. Everything after the run they wrote renders as live Markdown at the top level of the response: headings, links, and the server's own guidance section. Sizing the fence to the body is the rule Markdown itself uses for nesting code, and it is why this helper exists rather than a literal.
func MarkdownFencedBlock ¶
MarkdownFencedBlock renders content as a complete fenced code block: a fence sized by MarkdownCodeFence, the info string, the body, and the matching closing fence on its own line. Pass an empty language for a bare fence.
Prefer it over writing a fence by hand wherever the body is GitLab-authored.
func MarkdownForResult ¶
func MarkdownForResult(result any) *mcp.CallToolResult
MarkdownForResult resolves a tool output to its Markdown mcp.CallToolResult. A nil input renders the success confirmation; a value of an unregistered type, or a typed nil pointer, renders nil for the dispatcher to fall back on. A pointer to a registered type is dereferenced first, which is the same question HasRegisteredMarkdownFormatter answers: the two used to disagree, so a handler returning a pointer had a formatter the coverage test could see and the runtime never called.
func MarkdownFormatterCount ¶
func MarkdownFormatterCount() int
MarkdownFormatterCount returns the number of registered Markdown formatters (string + result variants). Useful for sanity checks in tests and tools that need to know "are formatters even loaded?".
func MarkdownRegistrationProblems ¶ added in v3.1.0
func MarkdownRegistrationProblems() []string
MarkdownRegistrationProblems returns every registration the registry refused or could only half honor, sorted, for a test to assert there are none.
func MarkdownTableHeader ¶
MarkdownTableHeader returns a Markdown table header followed by a standard separator row for the supplied column labels.
func MarkdownTableRow ¶
MarkdownTableRow returns a Markdown table row for the supplied cell values.
func MdCodeSpan ¶ added in v3.1.0
MdCodeSpan renders a GitLab-authored value as an inline code span, for a value the reader has to copy exactly: a token, a variable key, a path, a SHA, a command.
A code span is its own containment, so nothing inside it is Markdown and no entity is written: "|" and "<" render literally inside one, which is what the sites that filled a span with EscapeMdTableCell were showing. What a value can still do is end the span with a backtick run of its own, so the fence is one backtick longer than the longest run inside, the rule Markdown itself uses for nesting code. Line breaks collapse to a space, since a span ends with its line, and control bytes are dropped. A value that begins or ends with a backtick is padded with a space on both sides, which CommonMark strips again. An empty value renders as nothing rather than as two bare backticks, which read as a span that never closes.
func MdCodeSpanCell ¶ added in v3.1.0
MdCodeSpanCell renders a value as MdCodeSpan does, for a table cell: the pipe is backslash-escaped, which GFM honors inside a code span in a table and which is the one escape that works there, since the entity a cell would otherwise use renders literally inside the span.
func MdTitleLink ¶
MdTitleLink returns the title as a Markdown link if url is non-empty, otherwise returns the escaped title. Suitable for table cells. Both halves are escaped, so neither the title nor the URL can end the link they are in, and a destination that is not an http or https address is not linked at all: the address is written in a code span, after the title when the title says something else, so the reader keeps what GitLab sent and nothing in it is live. A websocket endpoint or a wiki attachment's relative path lands here as well as a hostile value, and a code span is the honest rendering of an address a client could not have opened anyway.
func MdUserHandle ¶ added in v3.1.0
MdUserHandle renders a username as the "@handle" GitLab shows, escaped for a card row or a cell, or nothing for an empty name, so a card never shows a bare "@".
func MdUserLink ¶ added in v3.1.0
MdUserLink renders a username as its handle linked to the user's profile, through MdTitleLink, the escaped handle alone when the profile URL is empty, and nothing for an empty name.
func MergeRequestDiffsForPositionCheck ¶ added in v3.1.0
func MergeRequestDiffsForPositionCheck(ctx context.Context, client *gitlabclient.Client, projectID string, mrIID int64) ([]*gl.MergeRequestDiff, bool)
MergeRequestDiffsForPositionCheck lists a merge request's diffs for a position pre-check and reports whether it could.
The pre-check exists to hand a model an actionable message before GitLab answers a line outside the diff with a bare 400 or 500, and it is advisory: when the listing itself fails the second result is false and the caller skips the check rather than refusing the write. What is skipped is the message, never the judgement, because the write that follows goes to the same instance with the same credential and GitLab answers it on its own terms. A 401, 403 or 404 on the listing is the refusal the write is about to get, with the right status; a cancelled context ends the write the same way; and a transient failure on the listing leaves the write judged by GitLab alone, which is exactly what a caller had before the pre-check existed. The cause is logged at debug so that a listing which fails every time can be seen without the write having to fail.
func MergeVariables ¶
MergeVariables merges multiple variable maps into a single map. Later maps override earlier ones for duplicate keys.
func MetaActionSchema ¶
func MetaActionSchema(route ActionRoute) map[string]any
MetaActionSchema returns the params schema served for one meta-tool action: the route's input schema with the destructive confirmation property and the parameter guidance added, or a permissive placeholder when the route captured no schema. For a route of a shared catalog the result is built once per process and shared; the caller must not mutate it.
func MetaParamSchemaMode ¶
func MetaParamSchemaMode() string
MetaParamSchemaMode reports the meta-tool input schema strategy currently selected by SetMetaParamSchemaMode: "opaque", "compact" or "full". Callers that memoize a registered surface must key on it, because the same registration produces different input schemas under each mode.
func MetaSchemaURI ¶
MetaSchemaURI returns the resource URI for a tool/action schema.
func MetaToolDescriptionPrefix ¶
MetaToolDescriptionPrefix builds a fixed-format header that should be prepended to a meta-tool's user-supplied description. The header gives LLMs a literal JSON usage example based on a representative action and points at the gitlab://tools resource for per-action params schemas. Returns an empty string when routes is empty so callers degrade gracefully rather than emit a malformed header.
func MetaToolOutputSchema ¶
MetaToolOutputSchema returns the shared action-dispatch output schema used by meta-tools.
func MetaToolSchema ¶
MetaToolSchema builds a JSON Schema for a meta-tool with the action field constrained to an enum of valid action names extracted from the routes map. Setting this as Tool.InputSchema ensures the LLM sees the exact list of valid actions in the schema, enabling first-try action selection.
The strategy used (opaque, compact, full) is read from the package-level mode set via SetMetaParamSchemaMode. Default is opaque. Callers that always want the opaque envelope regardless of global configuration should invoke BuildMetaToolSchema directly.
The opaque envelope is small and is compiled and cached by name downstream (see CompileToolSchemas). The compact and full envelopes embed every action's params schema and are served as maps, so they are shared as maps, one per distinct routes map (see [metaEnvelopeFor]).
func MustIndividualToolFromSpecs ¶
func MustIndividualToolFromSpecs(specs []ActionSpec, individualName string, opts IndividualToolProjectionOptions) *mcp.Tool
MustIndividualToolFromSpecs projects an individual tool or panics on invalid registration metadata. Use it from catalog-backed startup paths where a missing spec is a programming error.
func NodesAnswered ¶ added in v3.3.0
NodesAnswered converts the nodes of a connection page in order, leaving out each node GitLab answered null, and returns how many it left out for GraphQLPartialPage to account for (issue 1298). A connection decodes its nodes as pointers for this: decoded as values, a null node read as an item of zero values. No node at all is an empty page, never a nil one.
func NormalizeActionAlias ¶
NormalizeActionAlias returns the action a historical spelling stands for on the meta-tool the call was sent to, and the spelling itself when the tool claims none.
The aliases are the ones the group's own routes carry (ActionRoute.CompatibilityAliases), projected by the catalog from each action's compatibility policy, which is `internal/tools/actioncompat` and so the one table the dynamic surface resolves against too. There used to be a second table in this file, keyed by legacy spelling and valued by canonical ID; a route map is keyed by bare action names, so every dotted value in it could never be found and about ninety of its ninety-three entries were unreachable. `milestone.get` was in that table and resolved on no surface.
An action the tool routes is returned as it is. An alias is another spelling of one action, and a name the tool already routes is an action of its own: rewriting it runs a different handler than the one the caller named. That is what group_board_list did on a licensed instance, where epic_board_list also exists on gitlab_group and the alias turned a request for the group's issue boards into its epic boards.
An alias two of the group's own actions claim is left alone rather than resolved to whichever the map iteration reached first. The catalog refuses such a group, so this only guards a route map assembled by hand.
func NormalizeActionAliasForParams ¶
func NormalizeActionAliasForParams(toolName, action string, params map[string]any, routes ActionMap) string
NormalizeActionAliasForParams returns the canonical action name for aliases that depend on the submitted parameter shape.
func NormalizeParamAliasesForSchema ¶
NormalizeParamAliasesForSchema applies the same compatibility aliases used by UnmarshalParams, driven by a JSON Schema properties map instead of a Go struct type. The dynamic surface normalises with it before dispatch, and FinishToolResult, through embedParams, expands the embedded resource URI from its output, so both see the parameters as the handler's UnmarshalParams saw them.
func NormalizeResultMarkdown ¶ added in v3.1.0
NormalizeResultMarkdown is the one normalization every rendered response passes through on its way into a content block: trailing spaces and tabs dropped from each line, and control bytes dropped everywhere, since a formatter that writes a GitLab field straight into its builder would otherwise deliver an escape sequence to whatever prints the text. It is in one place so the constructor a formatter happens to be registered through cannot change the rendering. See StripControlBytes.
func NormalizeText ¶
NormalizeText replaces literal escape sequences with real characters. MCP clients may send text with literal backslash-n instead of real newlines when the JSON transport double-escapes the input.
Replacement order matters to avoid cascading conversions:
- `\\` -> `\` (double-escaped backslash first, so `\\n` becomes `\` + literal n, not a newline)
- `\r\n` -> `\n` (CRLF before individual CR/LF to avoid double-replacement)
- `\r` -> `\n` (standalone carriage return)
- `\n` -> newline (the most common case)
- `\t` -> tab
func NotFoundResult ¶
func NotFoundResult(resource, identifier string, hints ...string) *mcp.CallToolResult
NotFoundResult creates an informational MCP tool result for resources that do not exist or are not accessible. Instead of returning a Go error (which would be logged as ERROR and produce an opaque error message for the LLM), this returns a card with actionable next steps.
The result has IsError=true to signal the tool could not fulfill the request, but the content is rich and helpful: the LLM can act on the suggestions. It is annotated for a detail, since it answers a get.
Both halves of the sentence are contained here, each by the escaper its slot takes: the identifier because it is text the caller typed and a get handler echoes it as it arrived, and the label because it is not always the constant this once claimed it was. Two packages read it off the result instead, and they disagreed about whose job the escaping was: badges escaped it at its own call site and orbit passed it through raw, which is the shape a rule stated in a comment rather than in code always ends up in. The heading takes the same label through the card writer, which applies the heading escaper.
Use this in register.go handler closures when IsHTTPStatus(err, 404) is true for "get" operations. Pass nil error back to the SDK so the call is logged at INFO level instead of ERROR.
func NoteIDFromGID ¶ added in v3.3.0
NoteIDFromGID reads a note's numeric ID out of the global ID GitLab answered it with, and reports whether the note is one GitLab synthesizes from a resource event: a label, state, milestone, iteration or weight change GitLab's NoteType answers with a global ID ending in the hexadecimal discussion ID (gid://gitlab/WeightNote/0f3c...), which carries no number. Such a note has ID 0 and no note action can address it; a value that is not a GitLab global ID at all is neither a number nor an event.
func NullWhenZero ¶ added in v3.3.0
func NullWhenZero[T comparable](value *T, key string, edit *BodyEdit) *T
NullWhenZero reads one optional update field that GitLab clears only with null, for an option struct whose pointer is tagged omitempty and so cannot carry one. A field left out (nil) returns nil and edits nothing. A field given its zero value, "" for a text or 0 for an ID reference, which is how a caller asks to clear it, records key as null in edit and returns nil, so the option struct leaves the key to the edit. Any other value is returned as given, for the option struct to send.
func OpenAndValidateFile ¶
OpenAndValidateFile opens a caller-supplied local file for reading after confining it to the allowed upload directories, resolving it through every symlink on the way, and validating that what the resolved path names is a regular file of at most maxSize bytes. Returns the open file handle and its FileInfo.
The containment is the point: file_path names a path on the machine the server runs on, and the caller — a model following an instruction that may have come from an issue description or a job log — is not the person whose files these are. See CanonicalLocalFilePath for the roots.
func OpenFileOrBase64Source ¶
func OpenFileOrBase64Source(op, filePath, contentBase64 string) (reader io.Reader, size int64, cleanup func(), err error)
OpenFileOrBase64Source resolves the mutually-exclusive file_path / content_base64 upload input pair into a streaming reader plus the source size and a cleanup func the caller must defer. file_path is opened and validated against the configured upload size limit and streamed without buffering; content_base64 is decoded in memory. All errors are prefixed with op, matching the per-tool error convention.
func PaginationRequestOption ¶ added in v3.1.0
func PaginationRequestOption(page PaginationInput) gl.RequestOptionFunc
PaginationRequestOption carries a caller's page and per_page to a client-go method that takes no options struct to put them in. client-go models several of GitLab's paginated lists that way (a user's GPG keys and emails, system hooks, project aliases, a commit's merge requests), and without this such a method can only ever ask for GitLab's first page.
A value the caller left at zero is not sent, so GitLab applies its own default, exactly as ApplyListOptions leaves a zero out of a gl.ListOptions. The option runs after client-go has encoded the request's own query, so it adds to that query rather than replacing it.
func ParamText ¶ added in v3.1.0
ParamText renders a tool argument's value the way the caller wrote it, for the identifier a not-found result names. A route wrapper reads the arguments as they were decoded, before anything coerced them, so a JSON number arrives as a float64, and %v prints one of a million or more in exponent form: a package_id of 31234567 was named "3.1234567e+07" on the dynamic, meta and individual surfaces alike. A whole number is printed as the integer it is, and anything else as fmt prints it.
It renders a value and never looks up a name: which key the value is read from is ActionRoute.WrapNotFound's concern, which hands its builder the arguments with the documented aliases already resolved.
func ParameterGuidanceIdentity ¶
func ParameterGuidanceIdentity(guidance map[string]ParameterGuidance) string
ParameterGuidanceIdentity names a guidance map by its content, for a transform key whose output depends on the guidance as well as on the schema. Nil and empty maps name the same thing, since a transform treats them alike.
By content and not by address, which was the other way to close the same hole. DeriveSchema memoizes on the identity of the input schema, and every typed route's input schema is shared, so a route from a catalog nobody retained still writes a memo entry that lives for the process. An address in that key would be the address of a guidance map that does not: once the catalog is collected the allocator may hand the address out again, and the next map at it would be served the previous route's guidance. Pinning the guidance of every catalog marked shared would close it for the routes somebody remembered to pin and leave the rest keyed on a transient address; a digest cannot be forgotten, holds nothing alive, and lets two routes that spell the same guidance share one derivation.
The cost is a hash of a few short strings per lookup, against the schema clone the memo saves.
func ParseDateField ¶ added in v3.3.0
ParseDateField reads one date-only input, naming the field it came from when the value does not parse.
An RFC 3339 value is read for the calendar date it is written in, not the date it falls on in UTC: an expiry of 2026-12-31T23:00:00-05:00 names the last day of 2026, and moving it to the first of 2027 would grant a day the caller did not ask for. That is the case issue 1308 found dropped outright, for an SSH key and an invitation that then never expired.
func ParseDateFieldString ¶ added in v3.3.0
ParseDateFieldString reads one date-only input on the terms of ParseDateField and returns it as YYYY-MM-DD, for the client-go options that carry a date as a plain string, the member expiries among them. Such a field used to forward whatever the caller wrote, so a value GitLab could not read was sent and refused only after the request, if at all.
func ParseDateInputs ¶ added in v3.3.0
ParseDateInputs parses each date-only input into its destination, on the terms of ParseTimeInputs.
func ParseGID ¶
ParseGID extracts the type name and numeric ID from a GitLab Global ID. It returns an error if the format is invalid.
ParseGID("gid://gitlab/Vulnerability/42") → ("Vulnerability", 42, nil)
func ParseMetaSchemaURI ¶
ParseMetaSchemaURI extracts the tool and action segments from a schema URI.
func ParseTimeField ¶ added in v3.3.0
ParseTimeField reads one timestamp input, naming the field it came from when the value does not parse.
It replaces a parser that answered nil for a value it could not read, which every one of its 84 callers took for an absent input: a list went out with no date filter and answered more than was asked, and a note asked to be backdated was dated now, each reported as a success (issue 1308). A value that is not a timestamp is the caller's mistake, and only the caller can correct it, so it is refused.
func ParseTimeInputs ¶ added in v3.3.0
ParseTimeInputs parses each input into its destination, leaving the destination of an empty one alone, and refuses the call on the first value that does not parse, before any later one is read.
func PartialPageHint ¶ added in v3.3.0
PartialPageHint is the next step a list card adds when GitLab answered its page in part, naming the account GraphQLPartialPage gave, and empty for a page answered whole. The account carries GitLab's own messages, so it is escaped as the single-line list value it becomes.
func PathBelowInstanceWeb ¶ added in v3.3.0
PathBelowInstanceWeb reports the path of remote below the instance's web URL, without the leading slash, when remote is an http or https URL on the instance's scheme, host and port whose path lies under the instance's relative URL root; ok is false for anything else, the root itself included.
It is the rule GitLab's own SubmoduleHelper applies with Gitlab.config.gitlab.url: a prefix match on the web URL, relative root included. It stops at http and https because an SSH clone URL never carries the relative root (GitLab builds it from gitlab_shell.ssh_path_prefix), so stripping the root from one would mangle a top-level group that happens to share the root's name. The host is compared without regard to case and a port left out is the scheme's default, which is how a browser or git reads the two.
func PipelineInputsSchema ¶
PipelineInputsSchema builds the JSON Schema for the typed input T and constrains the named map property's values to exactly the shapes BuildPipelineInputs accepts (string, number, boolean, array of strings). The generic map[string]any field alone would advertise additionalProperties:true, promising value shapes the converter rejects.
func PipelineStatusEmoji ¶
PipelineStatusEmoji returns the Markdown emoji for a pipeline status.
func PlainPath ¶ added in v3.3.0
PlainPath returns a repository path as GitLab names it: value decoded once when it carries a URL-encoded "/" (DecodeEncodedPathIdentifier), and value unchanged otherwise. client-go escapes a path segment itself, so a path the caller encoded and the handler passed through reached GitLab escaped twice.
func PopulateHints ¶
func PopulateHints(result *mcp.CallToolResult, setter HintSetter)
PopulateHints extracts next-step hints from the Markdown content of a CallToolResult and sets them on the output struct. It is a no-op when result is nil, contains no TextContent, or has no hints section.
func ProgressReportInterval ¶
ProgressReportInterval returns the byte interval between progress reports. It is the smaller of 1 MB or 5% of total, with a minimum of 64 KB.
func RFC3339 ¶ added in v3.1.0
RFC3339 renders t in the wire form, RFC 3339 in UTC, or "" for a zero time.
func RFC3339Ptr ¶ added in v3.1.0
RFC3339Ptr renders an optional time in the wire form, or "" when nil.
func ReadDownload ¶ added in v3.3.0
ReadDownload reads a download's body and returns it when it holds at most MaxDownloadBytes, reading one byte past the ceiling and no more to tell. A longer body is refused with hint, the way out the action names (usually the GitLab route to download from directly); so is a read a response ceiling stopped (IsDownloadTooLarge), and any other failed read is reported as one.
func ReadFileOrBase64 ¶
ReadFileOrBase64 resolves the mutually-exclusive file_path / content_base64 upload input pair into an in-memory bytes.Reader, for endpoints that need a seekable or length-aware body. All errors are prefixed with op.
func RedactURL ¶ added in v3.1.0
RedactURL renders u with the three parts a secret can hide in removed: the userinfo, the query and the fragment. What survives is the scheme, the host and the path, which is what identifies the destination.
A URL reaches a model through this server whenever a tool reports where it is talking to, and each of the three dropped parts is somewhere a credential is routinely written: a proxy login in the userinfo, a signed token in the query, a fragment carried over from a browser. None of them identifies the destination, so none of them is worth the risk of echoing.
The path survives because for an instance URL it names the installation (a GitLab served under a subpath) rather than the caller, and because client-go builds its own error text from the same three parts, which leaves the two agreeing instead of this being the one place that says more. Use RedactURLToOrigin for a destination whose path is itself the secret.
func RedactURLToOrigin ¶ added in v3.1.0
RedactURLToOrigin parses raw and renders its origin alone: the scheme and the host. It drops what RedactURL drops and the path with it, replacing a path, query or fragment that was there with "/..." so a reader can see that something was withheld.
The path goes because for a webhook, an integration endpoint or any other callback URL the path is where the secret lives: a Slack or Teams hook is a public host with an unguessable path, and truncating such a URL to a fixed number of characters leaks the front of it, userinfo included. The host is what an auditor reads these values for, and the host is all that is left.
Text that does not parse as a URL, or that names no scheme and host, is reported as RedactedPlaceholder rather than echoed: a value that is not a URL is a value nothing here has judged.
func RegisterMarkdown ¶
RegisterMarkdown registers a Markdown string formatter for type T. Subsequent calls to MarkdownForResult with a value of type T, or a pointer to one, will invoke fn and wrap the returned string in a mcp.CallToolResult annotated for the assistant.
func RegisterMarkdownAnnotated ¶ added in v3.1.0
func RegisterMarkdownAnnotated[T any](fn func(T) string, ann *mcp.Annotations)
RegisterMarkdownAnnotated registers a Markdown string formatter for type T whose results carry ann, one of the ContentList, ContentDetail and ContentMutate presets; nil means ContentAssistant. The dispatchers override it with the annotation the action's declared content kind resolves to, so a formatter reached through the catalog is annotated by the spec and a formatter reached any other way by this.
A second registration for a type is refused and recorded: the first one stays, since a registration that silently replaced another is how one output type came to render another action's heading. An interface type is refused and recorded too, because its registration lands under a key no concrete value ever looks up.
func RegisterMarkdownPair ¶
RegisterMarkdownPair registers two Markdown string formatters.
func RegisterMarkdownResult ¶
func RegisterMarkdownResult[T any](fn func(T) *mcp.CallToolResult)
RegisterMarkdownResult registers a result formatter for type T. Use this for types that need custom mcp.CallToolResult construction (e.g. uploads with image content). A second registration for a type, or a registration for an interface type, is refused and recorded on the same terms as RegisterMarkdownAnnotated.
func RegisterMarkdownTriple ¶
func RegisterMarkdownTriple[A, B, C any](first func(A) string, second func(B) string, third func(C) string)
RegisterMarkdownTriple registers three Markdown string formatters.
func RegisterSurfaceToolFromSpec ¶
func RegisterSurfaceToolFromSpec(server *mcp.Server, spec ActionSpec, opts SurfaceToolRegisterOptions)
RegisterSurfaceToolFromSpec registers one visible MCP tool by projecting an ActionSpec and executing its route handler directly.
func RegisteredMarkdownFormatterName ¶ added in v3.1.0
RegisteredMarkdownFormatterName returns the name of the formatter registered for t, the served one where two were registered, or "" when none is.
func RegisteredMarkdownTypeNames ¶
func RegisteredMarkdownTypeNames() []string
RegisteredMarkdownTypeNames returns the type names of all registered Markdown formatters (both string and result variants). Used by validation tests to verify sub-packages self-register their formatters.
func RegisteredMarkdownTypes ¶ added in v3.1.0
RegisteredMarkdownTypes returns every type a Markdown formatter is registered for, string and result variants alike, sorted by name so a scan over them is deterministic. It is the seam the runtime structural gate drives: a fixture can be built for a type where only a name could not.
func RequestFromContext ¶
func RequestFromContext(ctx context.Context) *mcp.CallToolRequest
RequestFromContext extracts the MCP request from a context, or nil if absent.
func RequireProjectItem ¶ added in v3.3.0
func RequireProjectItem(ctx context.Context, operation string, projectID StringOrInt, field string, id int64) error
RequireProjectItem refuses, under operation, what a handler acting on one item of a project is handed when it names none: a context that has already ended, no project_id, or no positive id under the parameter field names. It sends nothing. The issue and snippet discussion handlers each start with these three checks, in this order and in these words.
func ResolveNextSteps ¶ added in v3.3.0
func ResolveNextSteps(text string, resolve ActionCallResolver) string
ResolveNextSteps rewrites by resolve every canonical ID the guidance section of text names, leaving an ID resolve does not answer as it was.
Only the section the server wrote is rewritten, the one ExtractHints reads. A result carries GitLab's bytes as well, a repository file, an issue body, a job log, and a call spelled in one of them is that content: rewriting it would hand the caller text that is not what GitLab holds.
func ResolveProjectWebURLs ¶
func ResolveProjectWebURLs(ctx context.Context, projects gl.ProjectsServiceInterface, projectIDs []int64) map[int64]string
ResolveProjectWebURLs fetches the web URL for each unique project ID. Failures are silently ignored — missing URLs simply produce no links.
func ResourceTemplateVariables ¶
ResourceTemplateVariables returns the names of the {variables} in a URI template, in order of appearance, without the RFC 6570 "+" reserved-expansion prefix. A brace that never closes, or a pair with nothing between them, is an error rather than the end of the scan: ExpandResourceURI answers such a template by embedding nothing, and a declaration that validation had let through would fail that quietly on every call.
func ReviewerFilter ¶ added in v3.3.0
func ReviewerFilter(field string, value StringOrInt) (*gl.ReviewerIDValue, error)
ReviewerFilter converts a reviewer_id filter into the value client-go's merge request list options send, on the terms of [parseUserIDFilter]. It returns nil for a filter left out.
func RichContentHint ¶
RichContentHint returns an informational note directing users to the GitLab web URL for full rendering when non-portable GFM features are detected. Returns an empty string when features or webURL is empty.
func SafeModePreviewResult ¶ added in v3.1.0
func SafeModePreviewResult(p SafeModePreview) *mcp.CallToolResult
SafeModePreviewResult renders a preview as the error result the individual surface answers an intercepted call with: the tool did not run, so it produced none of the output its schema describes, and the specification is unconditional about what a declared schema obliges.
func SanitizeError ¶
SanitizeError returns err with a rendering that reflects no upstream response body, leaving the chain intact for errors.As and errors.Is.
The four wrapping helpers in this file already do this to what they wrap, but a handler is free to wrap a client-go error itself with %w, and a good many do, so for those actions nothing between the handler and the SDK ever bounded the body. Calling this at the dispatchers, where every action's error passes on its way to the model and to the "tool call failed" log line, makes the wrapping helpers defense in depth rather than the only gate.
The same holds for what a fine-grained refusal means, which the wrapping helpers say through ClassifyError: an error that carries one and reaches a dispatcher without that description, as a GraphQL refusal a handler returns straight from GraphQLTopLevelError does, gets it here.
func SchemaForRoute ¶
SchemaForRoute returns the cached output schema for type R. Exported for use by gen_llms and audit tools.
func SchemaPropertyTakes ¶ added in v3.3.0
SchemaPropertyTakes reports whether a parameter whose schema is property takes value sent under name: the property declares no type, which leaves nothing to judge it by; or it declares the value's JSON type; or the dispatchers coerce the value into a type it declares, as they do a number written as text into an integer or a number and a whole number into the string of an ID parameter.
The dynamic surface asks it before offering an unknown parameter's nearest valid name, so that following the advice never moves a value to where the schema refuses it: job_id is three edits from job, and job takes a job's name where job_id carried its number (issue 1333).
func SchemaShared ¶
SchemaShared reports whether schema was registered with ShareSchema, or produced by DeriveSchema from a registered schema.
func SetActionTimeout ¶
SetActionTimeout sets the deadline every action runs under. Zero or a negative value disables it.
func SetLocalFilesystemAccess ¶
func SetLocalFilesystemAccess(allowed bool)
SetLocalFilesystemAccess overrides the transport inference for this process: pass false to refuse every caller-supplied local path, true to allow the allow-listed roots. Call it before any tool handler runs.
The default is inferred from the process arguments rather than configured, so a deployment that never heard of this policy still gets the right answer: an operator who forgets a flag would otherwise be the one running the exposed server. An explicit call always wins over the inference.
func SetMetaParamSchemaMode ¶
func SetMetaParamSchemaMode(mode string)
SetMetaParamSchemaMode selects the meta-tool input schema strategy used by MetaToolSchema. Accepts "opaque" (default), "compact", or "full". Any other value is coerced to opaque so that misconfiguration cannot break the tools/list payload. Must be called before meta-tools are registered; later calls only affect schemas built after the call returns.
func SetMetaParamSchemaModeScoped ¶
func SetMetaParamSchemaModeScoped(mode string) func()
SetMetaParamSchemaModeScoped selects the meta-tool input schema strategy and returns a restore function for tests that temporarily override the global mode.
func SetUploadConfig ¶
func SetUploadConfig(maxFileSize int64)
SetUploadConfig overrides the default upload thresholds. Call before RegisterAll to propagate values into tool handler closures.
func SeverityBadge ¶ added in v3.1.0
SeverityBadge renders a vulnerability or finding severity as its color glyph and the level in capitals, the one badge the security domains share. A level the table does not know is rendered escaped, since it is a value GitLab sent.
func ShareSchema ¶
func ShareSchema(schema any)
ShareSchema registers a schema as process-lived so that transforms of it are memoized by DeriveSchema. Registering keeps the schema reachable for the rest of the process, which is the point: only a schema that can never be collected has an address that can serve as its identity.
Register what the process builds once and serves from then on: the reflected type schemas, the compiled tool schemas, and the routes of a catalog cached per configuration. Never register a per-server map.
func SharedSchemaIdentity ¶
SharedSchemaIdentity returns a string naming a registered schema's identity, for callers that build a composite cache key out of several schemas. It reports false for a schema that is not shared, because the address of a map that can be collected names nothing durable.
func StripControlBytes ¶
StripControlBytes removes the C0 and C1 control ranges and DEL from s, keeping the three controls Markdown actually uses: tab, newline and carriage return.
Everything this package renders is GitLab-authored: an issue title, a file, a job log, a note somebody left on a merge request. None of it is written by the caller, and a good deal of it is written by whoever can open an issue or push a branch, which on a public project is anybody. It reaches the model as text and frequently reaches a person's terminal unchanged, where an escape sequence is not text but an instruction: ESC[2J clears the screen, ESC]0;…BEL renames the window. Nothing downstream filters them, so they are dropped here, at the point the text becomes part of a response.
Dropping rather than escaping is deliberate. A rendered escape would still have to be un-rendered by something to be read, and the sequences that matter carry no information a reader loses by not seeing them: what survives of ESC[2J is "[2J", which says plainly that the content tried something.
This covers the text channel and not structuredContent, and that is a decision rather than an oversight. structuredContent is marshaled by the SDK from the typed value each handler returns, so there is no funnel in this package to apply it at: closing it means either a sweep over every output struct in the 175 handler packages, or a result middleware in the server that walks each value reflectively on the way out. The second is the tempting one and it is the wrong trade. encoding/json already escapes every byte in these ranges, so what reaches a client is the six characters of a \u001b sequence, which no terminal acts on: a viewer would have to parse the JSON and print a string field raw to be affected, and such a client is showing the text channel too, which is sanitized here. Paying a reflective walk of every result for that is not worth it. If the shape of the risk changes, the place to add it is one middleware at the server boundary, not this function.
func StripLogMarkup ¶ added in v3.3.0
StripLogMarkup removes the terminal markup GitLab Runner writes into a CI job log, the ANSI control sequences and the section markers, and leaves the text a person reads. StripControlBytes drops only the ESC byte of a sequence, so a log went out as "[0K[36;1mPreparing" beside "section_start:1791747389:prepare_executor", and the structured channel carried the raw bytes. A lone ESC that starts no sequence is left for StripControlBytes to drop where text becomes Markdown.
func StripMetaToolDescriptionPrefix ¶
StripMetaToolDescriptionPrefix removes the generated meta-tool usage header added by MetaToolDescriptionPrefix while preserving standalone descriptions that happen to start with an example.
func SuccessResult ¶
func SuccessResult(markdown string) *mcp.CallToolResult
SuccessResult builds a success mcp.CallToolResult carrying the Markdown as its one text block, annotated for the assistant. Empty Markdown yields nil, which the dispatchers render as JSON through FinishToolResult; it is how a nil action result and a result with no formatter are answered.
func TimestampParam ¶ added in v3.3.0
func TimestampParam(name string, at *time.Time) gl.RequestOptionFunc
TimestampParam returns the request option that sends a timestamp parameter as the instant the caller gave, for a parameter GitLab declares DateTime that a client-go option struct carries as a date (upstream register row 126). Such a field encodes as YYYY-MM-DD, which GitLab reads as midnight UTC: an SSH key asked to expire at 23:00 expired 23 hours early, and a list bounded at 18:00 was bounded at the start of its day.
client-go writes its options into the JSON body of a POST, PUT or PATCH and into the query of any other method, and the option writes the timestamp under name in the same place, in RFC 3339 with the offset it was given in, replacing whatever was there. The caller leaves the option struct's own field unset, so this is the one writer of the parameter. A nil timestamp returns no option, and the request goes out as client-go built it.
func TitleFromName ¶
TitleFromName generates a human-readable UI title from a snake_case MCP tool name by stripping the "gitlab_" prefix and converting to Title Case.
TitleFromName("gitlab_list_projects") // returns "List Projects"
TitleFromName("my_open_mrs") // returns "My Open MRs"
func ToolResultAnnotated ¶
func ToolResultAnnotated(md string, ann *mcp.Annotations) *mcp.CallToolResult
ToolResultAnnotated wraps a Markdown string into a CallToolResult with content annotations that guide MCP clients on audience and priority. Pass nil annotations to get the same behavior as ToolResultWithMarkdown.
func ToolResultWithImage ¶
func ToolResultWithImage(md string, ann *mcp.Annotations, imageData []byte, mimeType string) *mcp.CallToolResult
ToolResultWithImage creates a CallToolResult containing both a text description (metadata) and an ImageContent block with the raw image bytes. Multimodal LLMs can "see" the image; text-only LLMs get the metadata. The text block carries ann, or the assistant default, and the image block carries ContentUser: the image is for a person's display, the text for the model's reasoning.
func ToolResultWithMarkdown ¶
func ToolResultWithMarkdown(md string) *mcp.CallToolResult
ToolResultWithMarkdown wraps a Markdown string into a CallToolResult with a single TextContent entry annotated for assistant-only audience. This prevents MCP clients (e.g. VS Code) from displaying raw Markdown inline: the LLM processes it and presents formatted output to the user. The text passes through NormalizeResultMarkdown, as every rendered response does.
func TypeIdentity ¶
TypeIdentity names a reflected type unambiguously, package path included, since two packages may declare input types with the same name. It is the form a transform name embeds when the transform depends on a type.
func UnattributedRequestError ¶
func UnattributedRequestError() error
UnattributedRequestError is UnattributedRequestMessage as a JSON-RPC internal error, for the surfaces that answer with an error value rather than a classified string.
Internal rather than invalid-request for the reason the message gives: the request was well formed and this server failed to route it.
func UnattributedRequestErrorFor ¶
UnattributedRequestErrorFor is UnattributedRequestError for a request that is still live, and the reason it ended for one that is not.
Not every unattributed request is a wiring defect, which is what the message says it is. A POST the client abandoned takes its carrier with it, and the carrier is where the credential is read from, so the binding finds nothing and the handler resolves the credential-less client: a legitimate cause, answered with a sentence asking the caller to report a bug. The tools path never had this problem, because ClassifyError checks cancellation first and that check is what it hits.
Consulting the context is what tells them apart. A cancelled request is over and nobody is reading the answer, so what matters is only that it is not blamed on the wiring in a log an operator does read.
One cause it cannot distinguish, and does not claim to: the pool evicting the entry between the gate resolving it and the gate looking up its state. The request is alive and unattributable, and the honest thing to tell that caller is exactly what the message already says, since retrying rebuilds the entry.
func UnconfirmedWrite ¶ added in v3.3.0
func UnconfirmedWrite(client *gitlabclient.Client, resp *gl.Response, operation, object string, err, wrapped error) error
UnconfirmedWrite returns the error a GraphQL write's handler answers with, given what its client-go call returned: wrapped, the handler's own error, except for a write GitLab ran and answered without the object it returns (WriteAnsweredWithoutObject) on a fine-grained session, which is told the write was probably committed. The decision of which session that is belongs to the client's authority, which only a fine-grained credential's client carries, so a classic token's write is never described as probably committed.
func UnmarshalParams ¶
UnmarshalParams re-serializes params map to JSON and deserializes into T. LLMs frequently send numeric values as JSON strings (e.g. "17" instead of 17). When standard unmarshalling fails, this function retries after coercing string values that look like integers or floats into actual numbers.
Unknown keys in params (i.e. fields that do not exist on T) are rejected with an actionable error so that LLMs receive a clear diagnostic when they mistype a parameter name (e.g. "iid" instead of "snippet_id"). This mirrors the JSON Schema lockdown applied to tools/list responses (see LockdownInputSchemas) and the MCP guidance to surface validation errors as recoverable tool results so the model can self-correct. Meta-protocol keys (see [reservedParamKeys]) are stripped before unmarshalling.
func ValidActionsString ¶
ValidActionsString returns a sorted, comma-separated list of action names.
func ValidateDiffPosition ¶
ValidateDiffPosition checks whether a (newLine, oldLine) combination corresponds to a valid commentable position in the parsed diff lines.
Rules enforced (per GitLab API):
- new_line only → line must be an added (+) line
- old_line only → line must be a removed (-) line
- both set → line must be an unchanged context line
Returns nil when the position is valid, or a descriptive error explaining exactly why the position is invalid and what the caller should do instead.
func ValidatePackageFileName ¶
ValidatePackageFileName validates a filename for GitLab generic package upload. Filenames must not be empty, must not contain spaces, and must not start with a tilde or at-sign.
func ValidatePackageName ¶
ValidatePackageName validates a GitLab generic package name against allowed characters. Names must start with a letter or digit and may contain A-Z a-z 0-9 . _ - + ~ / @.
func ValidateRateLimit ¶
ValidateRateLimit reports whether the given rps/burst pair forms a well-defined limiter configuration. Used by the server entrypoint to fail fast on bad CLI input rather than silently disabling the limiter.
func ValidateRouteBinding ¶
func ValidateRouteBinding(route ActionRoute) error
ValidateRouteBinding reports a route whose handler was replaced without its binder being replaced with it. Such a route serves the replacement on the server it was built for and the constructor's plain handler on every server a shared catalog rebinds it to, which is a defect no test of the domain package can see, since the package tests the route as it built it. The check is exact: the handler in place must be the very closure the binder installed, compared by identity rather than by the code it runs, because the compiler gives an inlined copy of a function literal a closure of its own.
A route with no binder at all passes here, because this compares the two halves against each other and there is nothing to compare. Whether a route is allowed to have no binder is a question about where the route lives, and is answered where that is known: the catalog requires one of every action but the dynamic controllers, which close over a registry rather than a client.
func WithActionDeadline ¶
WithActionDeadline derives the context an action runs under: the caller's, bounded by the configured deadline when there is one. The cancel function is always safe to call.
Exported for the one model-facing handler that is not a catalog action and so passes through none of the WrapAction functions: the dynamic surface's gitlab_find_action, whose cost is the catalog rather than a GitLab call and which was therefore the only registered tool with no deadline at all.
func WithHints ¶
func WithHints[O any](result *mcp.CallToolResult, out O, err error) (*mcp.CallToolResult, O, error)
WithHints extracts hints from a CallToolResult and populates them on the typed output struct, returning all three handler values in one call. This avoids evaluation-order ambiguity in multi-value return statements.
For value Out types (the common case), &out is used internally to satisfy the HintSetter pointer receiver. For pointer Out types (*T), the pointer itself implements HintSetter. If neither case applies, WithHints is a no-op.
return toolutil.WithHints(toolutil.ToolResultWithMarkdown(md), out, err)
func WithInternalInspection ¶
WithInternalInspection marks ctx as belonging to a session the server opens against itself, so what it asks for is not charged to a caller's bucket.
The server lists its own tools several times while it starts: to drop the excluded ones, to learn which survived the read-only and safe-mode passes, to count them and build the gitlab://tools manifest from one listing, and to write the server card. Those requests travel the same receiving middlewares a client's do, so metering tools/list charged them to the deployment's own bucket, and on a server started with --rate-limit-burst=1 the second one was refused and the tool manifest resource failed to build. Pass this to the server's mcp.Server.Connect: the handler context descends from that one, and no header or parameter a caller controls reaches it.
Only the catalog bucket consults the mark, because only listings are asked for this way. A method that reached GitLab would be spending the credential whether the server or a client asked for it, and should still be charged.
func WrapErr ¶
WrapErr classifies the error, enriches it with a semantic message, and wraps it with the operation name. All tool handlers funnel through here so connectivity and auth problems are reported consistently.
func WrapErrWithHint ¶
WrapErrWithHint works like WrapErrWithMessage but appends an actionable hint that tells the LLM what to do next, after the classification and GitLab's message and before the cause:
"branchDelete: conflict: the resource already exists or there is a state conflict ({message: Cannot delete: protected branch}). Suggestion: use branch.unprotect first, then retry deletion: DELETE https://gitlab.example.com/api/v4/projects/1/repository/branches/main: 409 {message: Cannot delete: protected branch}"
The hint should be a concise suggestion starting with a verb (e.g., "use branch.list to verify the branch name"), and names an action by its canonical ID rather than by a tool name, which only one surface of three registers; cmd/audit_action_ids fails on one.
The hint is left out where it cannot be right: for a request this server never sent or declined to send, and for a refusal of a fine-grained token's grant, whose description already says what is missing.
func WrapErrWithMessage ¶
WrapErrWithMessage works like WrapErr but also includes the specific GitLab error message (from ErrorResponse.Message) when available. The composition is the operation, the classification, GitLab's message in parentheses, and the sanitized cause, which carries the request line, the status and that same message once more:
"fileCreate: bad request: check your input parameters ({message: A file with this name already exists}): POST https://gitlab.example.com/api/v4/projects/1/repository/files/README.md: 400 {message: A file with this name already exists}"
Use WrapErrWithMessage for mutating operations where the specific GitLab error detail helps the LLM understand what went wrong. Use WrapErr for read-only operations where the generic classification suffices.
func WrapErrWithStatusHint ¶
WrapErrWithStatusHint returns WrapErrWithHint(operation, err, hint) when err matches the given HTTP status code, otherwise falls back to WrapErrWithMessage(operation, err). It compresses the common pattern:
if toolutil.IsHTTPStatus(err, 404) {
return ..., toolutil.WrapErrWithHint(op, err, hint)
}
return ..., toolutil.WrapErrWithMessage(op, err)
into a single call. For handlers that need different hints per status, use a switch over IsHTTPStatus checks; this helper covers the dominant single- status case.
func WrapGFMBody ¶
WrapGFMBody wraps user-generated GFM content in a Markdown blockquote to prevent heading hierarchy conflicts and structural breaks in the formatted output. Empty bodies return an empty string.
The quote is the containment: every line of the body is a line of a quote, so the body cannot add a heading, a list item or a section to the document it sits in. Two things happen before it. Control characters are dropped, and the server's own guidance heading is defused, so a body carrying that heading is shown as the text it is rather than parsed back out as the server's suggestions. See DefuseHintsHeading.
func WriteAnsweredWithoutObject ¶ added in v3.3.0
WriteAnsweredWithoutObject reports whether err, what a client-go GraphQL write returned with resp, is GitLab having run the write and answered without the object it returns: the object answered null, which client-go reports as its not-found or its empty-response sentinel, or nulled below a non-null position, which it reports as the top-level errors that null writes.
Only an answer GitLab gave with HTTP 200 qualifies. client-go reports a 404 of the GraphQL endpoint itself with the same not-found sentinel, and that request ran nothing; an error with no response is no answer of GitLab's to read at all. Every write this is asked about is one request, so the answer is the write's own and never a lookup's made before it.
func WriteDownloadOutputFile ¶ added in v3.1.0
WriteDownloadOutputFile writes a download to the destination a caller named, and only once write has produced all of it. It returns the number of bytes written.
The destination is resolved and confined by [canonicalDownloadOutputPath] before anything is created, so one outside the allowed roots leaves no directory behind either. It is resolved again once its directory exists: the first pass could only vouch for the ancestors that were there at the time, and a symlink planted under a directory this call just made would otherwise decide where the bytes land.
write receives a temporary file in the destination's directory, never the destination. When write returns nil the file is synced, closed and renamed over the destination, and on Unix the directory is synced after the rename; when write returns an error, or the file cannot be put in place, the temporary file is removed and the error returned, so the destination holds exactly what it held before, or is still absent. That is the point of the indirection: a download cut off by an error answer, a dropped connection or a cancelled call used to leave an empty or truncated file at the destination, which the next reader took for the download. The directories made on the way stay, since they are empty and a retry needs them. A process killed outright leaves its temporary file, recognizable by [partialDownloadPrefix], and still never a partial destination.
The rename is also what closes the race [canonicalDownloadOutputPath] cannot: it resolves a symlink already at the destination and confines what that link names, but a local principal who can write in an allowed root could plant one after the check. A rename replaces the directory entry at the destination and does not follow it, so a symlink planted there is replaced rather than written through, on every platform. On Unix the replacement is one rename(2) and atomic. On Windows it is MoveFileEx with MOVEFILE_REPLACE_EXISTING, which Go does not promise to be atomic and which fails while another process holds the destination open without sharing its deletion, a running program for one, or when the destination is marked read-only; it then keeps its previous content and the error says so.
A replaced destination is a new file, and a hard link to the old file keeps the old content. On Unix the new file is readable and writable by the owner alone (created with mode 0600), like every file this creates, whatever the file it replaces allowed; on Windows it takes the access the directory grants to new files, not the old file's own ACL, since Go creates it with no security descriptor of its own and the rename carries the one it inherited. On Unix, whether it may be replaced at all is decided by the directory's permissions, as for any rename, rather than by the old file's own.
func WriteGraphQLPagination ¶ added in v3.1.0
func WriteGraphQLPagination(b *strings.Builder, p GraphQLPaginationOutput, shown int)
WriteGraphQLPagination writes the cursor summary of a GraphQL list after a blank line, the one way a cursor-paginated list ends: the line used to be written four ways across ten packages, one of them with no blank line in front of it, which glued it to the last row of the table.
func WriteHints ¶
WriteHints appends the server's guidance section to the Markdown builder: a horizontal rule, the "💡 Next steps" heading, and one bullet per hint. Each hint is a short string describing a related action the model can take (e.g. "Use action 'package.delete' to remove this package"). If no hints are provided, no section is written.
The section separates itself from whatever precedes it. The rule is written after exactly one blank line, whatever the builder ended with. A formatter that stopped mid-line used to get "text\n---", which CommonMark reads as a setext heading: its last line became an H2, the rule vanished into it, and nothing downstream could find the section, so its hints never reached next_steps. Trailing newlines are trimmed first so the blank line is one and not three. On an empty builder the section opens the response, after the one newline [leadingHintsBlock] accepts, rather than at the very first byte, where a "---" reads as YAML front matter to a renderer that looks for it.
Whether or not there are hints, any guidance heading already in the builder is defused first. This is the one place every formatter passes through on its way to a response, the raw file, job trace, snippet and discussion renderers that embed GitLab bytes verbatim included, so doing it here covers them all without each renderer having to remember, and covers the ones added later.
A hint is written as it is given, control bytes aside: it is the server's own sentence, and a GitLab-authored value inside one has to be escaped by the formatter that composed it. The escaping gate follows this write back through every caller, and at L1-08 every hint a domain formatter passes classifies safe; the ten arguments that do not are this package's own plumbing of those hints through an options struct, a renderer constructor, a method receiver or a registered formatter's output type, which the classifier does not follow to the literal the domain wrote. The write is therefore declared below rather than judged, and the declaration retires when the classifier follows those shapes: it fails the gate as stale the moment nothing needs it.
A blank hint is dropped, and a call whose every hint is blank writes no section at all. A hint reaches a formatter as a field of its result as often as it does as a literal, and a result built from a zero value carries those fields empty: written out, each became a bare "- " and the first of them, landing directly under the sentence above it, turned that sentence into a setext heading. Guidance with no text in it is not guidance, so the shape that damages the document is the one that gets dropped rather than each caller having to remember to check.
func WriteHookSecretKeys ¶ added in v3.1.0
WriteHookSecretKeys writes the tables of a webhook's URL variables and custom headers, keys only, with every value redacted: both are secrets GitLab itself never sends back, and the key is what a reader needs to know which ones are set. Either list may be empty, in which case its table is not written.
func WriteListFooter ¶ added in v3.1.0
func WriteListFooter(b *strings.Builder, p PaginationOutput, linked bool, hints ...string)
WriteListFooter closes a list result: the pagination footer through WritePagination, then the guidance section. When linked is true the table carried a link column and HintPreserveLinks leads the hints, as ListHints arranges; when it is false the hint is dropped even if the caller passed it, since an instruction to keep the links of a table that has none is noise the model has to read past.
func WriteListHeading ¶ added in v3.1.0
func WriteListHeading(b *strings.Builder, title string, shown int, p PaginationOutput)
WriteListHeading writes the H2 a list result opens with, "## Title (N)", and the summary line WriteListSummary adds for a multi-page result.
N is what the response can vouch for: the total GitLab sent when it sent one, the count shown with "more available" when the page has a successor but no total (keyset pagination and search send none), the count shown with its page number on a later page that has no successor and no total, and the count shown otherwise. A heading that printed the page length under a larger total, a zero total above a table of rows, or the rows of a last page as if they were the whole list, was the commonest way a list misled its reader.
func WriteListSummary ¶
func WriteListSummary(b *strings.Builder, shown int, p PaginationOutput)
WriteListSummary appends a brief "Showing N of M results (page X of Y)" line between the heading and the table body. It is a no-op when there is only a single page, because the heading count already conveys everything, and it names no total when GitLab sent none rather than saying "of 0".
func WritePagination ¶
func WritePagination(b *strings.Builder, p PaginationOutput)
WritePagination appends the pagination footer after a blank line, whatever the builder ends with, so the footer opens a paragraph of its own rather than continuing the last table row or list item as a lazy line. A pagination that says nothing (no page, no total, no page size) writes nothing, since "Page 0 of 0 | 0 items total" is an answer GitLab never gave.
Types ¶
type AISettingsOutput ¶ added in v3.1.0
type AISettingsOutput struct {
DuoAgentPlatformEnabled bool `json:"duo_agent_platform_enabled"`
DuoWorkflowMCPEnabled bool `json:"duo_workflow_mcp_enabled"`
FoundationalAgentsDefaultEnabled bool `json:"foundational_agents_default_enabled"`
AICatalogRestrictedToGroupHierarchy bool `json:"ai_catalog_restricted_to_group_hierarchy"`
AIUsageDataCollectionEnabled bool `json:"ai_usage_data_collection_enabled"`
IncludeRecommendedAllowed bool `json:"include_recommended_allowed"`
AllowAllUnixSockets bool `json:"allow_all_unix_sockets"`
AllowProjectExtension bool `json:"allow_project_extension"`
PromptInjectionProtectionLevel string `json:"prompt_injection_protection_level,omitempty"`
MinimumAccessLevelExecute string `json:"minimum_access_level_execute,omitempty"`
MinimumAccessLevelExecuteAsync string `json:"minimum_access_level_execute_async,omitempty"`
MinimumAccessLevelManage string `json:"minimum_access_level_manage,omitempty"`
MinimumAccessLevelEnableOnProjects string `json:"minimum_access_level_enable_on_projects,omitempty"`
}
AISettingsOutput is the ai_settings object ee/lib/api/entities/ai_settings.rb renders. It lives here rather than in a domain package because two entities expose it: GroupDetail, and the application setting.
The four minimum-access-level keys are gated on the dap_group_customizable_permissions feature flag, so each is omitted rather than sent empty when the flag is off.
type AccessRequesterExtra ¶ added in v3.1.0
type AccessRequesterExtra struct {
Locked bool `json:"locked"`
PublicEmail string `json:"public_email"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
}
AccessRequesterExtra is what lib/api/entities/access_requester.rb sends on a pending access request that client-go's AccessRequest does not carry.
It is deliberately not MemberExtra, and this is the whole point of the shape. The entity does not inherit Member: it exposes `user` merged from UserBasic and `requested_at`, and nothing else, which the live record in docs/development/gitlab-api-live.json states as those two fields. A pending request is not a membership, so it carries no access level, no expiry, no role, no membership state and no SAML or SCIM identity, and the four keys here are the UserBasic ones client-go leaves out. This shape used to embed MemberExtra on the strength of a comment saying the entity inherits Member, which it does not, and ten membership keys were published on a response that has never carried one of them.
The same UserBasic exposes avatar_path and custom_attributes, and neither is read: both wait on a presenter option, and no access-request route declares only_path or with_custom_attributes, so GitLab has never sent either here.
func CapturedAccessRequester ¶ added in v3.1.0
func CapturedAccessRequester(capture *gitlabclient.ResponseCapture) (AccessRequesterExtra, error)
CapturedAccessRequester reads them off the captured answer to a request that returned one pending access request.
func CapturedAccessRequesters ¶ added in v3.1.0
func CapturedAccessRequesters(capture *gitlabclient.ResponseCapture, decoded int) ([]AccessRequesterExtra, error)
CapturedAccessRequesters reads the same off a list answer, one extra per request in order, the count held to what the SDK decoded.
type ActionAliasSpec ¶
type ActionAliasSpec struct {
Alias string
Target string
Source string
Searchable bool
Deprecated bool
RemovalVersion string
Reason string
}
ActionAliasSpec describes a compatibility alias that resolves to a canonical action owned by an ActionSpec.
type ActionCallResolver ¶ added in v3.3.0
ActionCallResolver answers how the serving surface calls the action a canonical ID names, as the words that follow "Use" in a next step, and whether that surface serves the action at all.
type ActionFunc ¶
ActionFunc is a handler that receives raw params and returns a result or error.
func SafeModeActionFunc ¶
func SafeModeActionFunc(name string) ActionFunc
SafeModeActionFunc returns an ActionFunc that returns a SafeModePreview for name instead of executing anything. It is used to neutralize mutating catalog actions at registration time, so dispatcher surfaces (meta-tools and the dynamic execute tool) preview each mutating action individually while their read-only actions keep executing.
func WrapAction ¶
func WrapAction[T, R any](client *gitlabclient.Client, fn func(ctx context.Context, client *gitlabclient.Client, input T) (R, error)) ActionFunc
WrapAction wraps a typed handler (input T -> output R) into a generic ActionFunc.
The client the handler runs under is the one bound to the request context, falling back to the one this route was built for. That is the seam a server shared by several credentials needs: its routes are bound to the unbound client, which refuses every request, and the per-request binding is what makes a call reach the caller's own instance. On stdio, and everywhere a catalog is built for one real client, no context carries a binding and the captured client is returned unchanged.
func WrapActionWithRequest ¶
func WrapActionWithRequest[T, R any](client *gitlabclient.Client, fn func(ctx context.Context, req *mcp.CallToolRequest, client *gitlabclient.Client, input T) (R, error)) ActionFunc
WrapActionWithRequest wraps a handler that also requires the MCP request (e.g., for progress tracking). The request is extracted from context via RequestFromContext; if absent, nil is passed.
func WrapVoidAction ¶
func WrapVoidAction[T any](client *gitlabclient.Client, fn func(ctx context.Context, client *gitlabclient.Client, input T) error) ActionFunc
WrapVoidAction wraps a typed handler that returns only error.
It carries the same deadline as WrapAction, and for a stronger reason: an action that returns nothing is a delete, a cancel or a retry, which is exactly the call an operator wants bounded. The deadline was missing here while the setting's own documentation said every action passed through it.
func WrapVoidActionWithRequest ¶
func WrapVoidActionWithRequest[T any](client *gitlabclient.Client, fn func(ctx context.Context, req *mcp.CallToolRequest, client *gitlabclient.Client, input T) error) ActionFunc
WrapVoidActionWithRequest wraps a void handler that also requires the MCP request. The request is extracted from context via RequestFromContext; if absent, nil is passed.
type ActionMap ¶
type ActionMap map[string]ActionRoute
ActionMap maps action names to their route definitions (handler + metadata).
func ActionSpecsToMap ¶
func ActionSpecsToMap(specs []ActionSpec) ActionMap
ActionSpecsToMap converts canonical action specs to a legacy ActionMap.
func ActionSpecsToMapWithError ¶
func ActionSpecsToMapWithError(specs []ActionSpec) (ActionMap, error)
ActionSpecsToMapWithError converts canonical action specs to a legacy ActionMap.
type ActionMetaEntry ¶
type ActionMetaEntry struct {
// Usage replaces the generic action Usage line when non-empty.
Usage string
// Aliases replaces the default tool-name alias list when non-empty.
Aliases []string
// Related replaces RelatedActions when non-empty.
Related []string
// Guidance replaces ParameterGuidance when non-empty.
Guidance map[string]ParameterGuidance
// Description sets the individual-tool description when non-empty.
Description string
}
ActionMetaEntry holds the per-action discovery metadata that domains overlay on top of their shared ActionSpecOptions defaults: a non-generic Usage line, distinctive natural-language Aliases, canonical RelatedActions, per-parameter Guidance, and the "Returns: … See also: …" individual-tool Description (1:1 audit R-META). Every GitLab domain that carries an action metadata table uses this single type instead of a domain-local struct, and applies it with ApplyActionMeta, so the overlay logic lives in one place.
Zero-value fields are skipped by ApplyActionMeta; only fields a domain actually populates override the option defaults.
type ActionRoute ¶
type ActionRoute struct {
Handler ActionFunc
// Bind builds the handler for a client. Nil means the handler depends on
// no client and is shared as it is.
Bind func(*gitlabclient.Client) ActionFunc
Destructive bool
InputType reflect.Type
OutputType reflect.Type
InputSchema map[string]any
OutputSchema map[string]any
ParameterGuidance map[string]ParameterGuidance
Aliases []string
// CompatibilityAliases are the historical spellings that resolve to this
// action, lowercased, taken from the action's compatibility policy by the
// catalog. They travel on the route because the dispatcher sees the route
// and not the spec: [NormalizeActionAlias] answers out of the group's own
// route map, and a table kept beside that map cannot know which of its
// entries the group owns. Aliases above is the search list, which is a
// different question and is never resolved as a call.
CompatibilityAliases []string
Tags []string
Usage string
RelatedActions []string
// EmbeddedResource is the canonical resource URI template the dispatchers
// expand from the call's parameters and embed in a successful result. The
// catalog sets it from the action's spec when the spec's policy embeds.
EmbeddedResource string
// ContentKind is the content kind the action's spec declares, which
// [FinishToolResult] resolves to the annotation every text block of the
// result carries through [AnnotationsForContentKind]. The catalog sets it
// from the spec, so the declared kind and the served annotation are one
// value.
ContentKind string
// ActionID is the canonical catalog ID of the action this route runs,
// such as issue.list. The catalog sets it, and so does the projection of a
// standalone utility, for the same reason the fields above travel here:
// the dispatchers see the route and not the catalog, and what a
// fine-grained session may run is decided by that ID ([FineGrainedRefusal],
// [FineGrainedNotes]). Empty on a route no catalog projected, which
// nothing decides.
ActionID string
// contains filtered or unexported fields
}
ActionRoute pairs an action handler with metadata about its behavior. Used by meta-tools to carry per-route destructive classification without string parsing. OutputSchema holds the JSON Schema for the action's typed output. InputSchema holds the JSON Schema for the action's typed params (nil for routes constructed via the untyped Route and DestructiveRoute constructors).
A route is two things with different lifetimes. Its metadata (the schemas, the guidance, the aliases) depends on nothing but the action, and a catalog cached per configuration shares it between every server in the process; the schema maps are frozen, and a consumer that needs to change one copies it first (see DeriveSchema). Its Handler is bound to one GitLab client, and so to one credential. Bind is what joins the two: the RouteAction family sets it to rebuild the handler for any client, and ActionRoute.BindTo pairs the shared metadata with a handler for the client at hand. A route with no Bind has a handler that captured no client and is served as is.
Replacing Handler directly breaks that pairing silently: the shared catalog would rebind to the constructor's plain handler and drop whatever the replacement added. ActionRoute.WrapHandler and ActionRoute.WithBoundHandler change the handler and its binder together, and ValidateRouteBinding refuses a route whose two halves disagree.
func CloneActionRoute ¶
func CloneActionRoute(route ActionRoute) ActionRoute
CloneActionRoute returns a copy of the route that owns its string slices and shares everything else. The handler, the types and the frozen schema and guidance maps describe the same action and are not copied; the Aliases, Tags and RelatedActions slices are, so a caller may append to them without reaching the original.
func DestructiveAction ¶
func DestructiveAction[T, R any](client *gitlabclient.Client, fn func(ctx context.Context, client *gitlabclient.Client, input T) (R, error)) ActionRoute
DestructiveAction wraps a typed function as a destructive ActionRoute and attaches input/output schemas.
func DestructiveActionWithRequest ¶
func DestructiveActionWithRequest[T, R any](client *gitlabclient.Client, fn func(ctx context.Context, req *mcp.CallToolRequest, client *gitlabclient.Client, input T) (R, error)) ActionRoute
DestructiveActionWithRequest wraps a typed function that needs the MCP request as a destructive ActionRoute and attaches input/output schemas.
func DestructiveFunc ¶
func DestructiveFunc[T, R any](fn func(ctx context.Context, input T) (R, error)) ActionRoute
DestructiveFunc wraps a typed function as a destructive ActionRoute without a GitLab client dependency and attaches input and output schemas.
func DestructiveRoute ¶
func DestructiveRoute(fn ActionFunc) ActionRoute
DestructiveRoute creates a destructive ActionRoute without an output schema.
func DestructiveVoidAction ¶
func DestructiveVoidAction[T any](client *gitlabclient.Client, fn func(ctx context.Context, client *gitlabclient.Client, input T) error) ActionRoute
DestructiveVoidAction wraps a typed void function as a destructive ActionRoute. The handler returns a typed DeleteOutput confirmation so meta-tool routes expose structured output instead of nil content.
func DestructiveVoidActionWithRequest ¶
func DestructiveVoidActionWithRequest[T any](client *gitlabclient.Client, fn func(ctx context.Context, req *mcp.CallToolRequest, client *gitlabclient.Client, input T) error) ActionRoute
DestructiveVoidActionWithRequest wraps a request-aware void function as a destructive ActionRoute with typed DeleteOutput confirmation, reusing WrapVoidActionWithRequest so the request-extraction logic is not duplicated.
func Route ¶
func Route(fn ActionFunc) ActionRoute
Route creates a non-destructive ActionRoute without an output schema.
func RouteAction ¶
func RouteAction[T, R any](client *gitlabclient.Client, fn func(ctx context.Context, client *gitlabclient.Client, input T) (R, error)) ActionRoute
RouteAction wraps a typed function as a non-destructive ActionRoute and attaches the JSON Schema for the input type T and output type R.
func RouteActionWithRequest ¶
func RouteActionWithRequest[T, R any](client *gitlabclient.Client, fn func(ctx context.Context, req *mcp.CallToolRequest, client *gitlabclient.Client, input T) (R, error)) ActionRoute
RouteActionWithRequest wraps a typed function that needs the MCP request as a non-destructive ActionRoute and attaches input/output schemas.
func RouteFunc ¶
func RouteFunc[T, R any](fn func(ctx context.Context, input T) (R, error)) ActionRoute
RouteFunc wraps a typed function as a non-destructive ActionRoute without a GitLab client dependency and attaches input and output schemas.
func RouteRequestFunc ¶
func RouteRequestFunc[T, R any](fn func(ctx context.Context, req *mcp.CallToolRequest, input T) (R, error)) ActionRoute
RouteRequestFunc wraps a typed request-aware function as a non-destructive ActionRoute without a GitLab client dependency and attaches schemas.
func RouteVoidAction ¶
func RouteVoidAction[T any](client *gitlabclient.Client, fn func(ctx context.Context, client *gitlabclient.Client, input T) error) ActionRoute
RouteVoidAction wraps a typed void function as a non-destructive ActionRoute. The handler returns a typed VoidOutput confirmation so meta-tool routes expose structured output instead of nil content.
func (ActionRoute) BindTo ¶
func (route ActionRoute) BindTo(client *gitlabclient.Client) ActionRoute
BindTo returns the route with its handler rebuilt for client. A route whose Bind is nil captured no client and is returned unchanged. The metadata is shared with the receiver, not copied: it is the same action.
func (ActionRoute) WithAliases ¶
func (route ActionRoute) WithAliases(aliases ...string) ActionRoute
WithAliases returns a copy of route with additional search aliases.
func (ActionRoute) WithBoundHandler ¶
func (route ActionRoute) WithBoundHandler(client *gitlabclient.Client, bind func(*gitlabclient.Client) ActionFunc) ActionRoute
WithBoundHandler returns the route with a handler that bind builds for client, and with bind recorded so a shared catalog can build the same handler for any other client. Use it where the replacement handler needs the client itself rather than only the handler it replaces.
func (ActionRoute) WithParameterGuidance ¶
func (route ActionRoute) WithParameterGuidance(guidance map[string]ParameterGuidance) ActionRoute
WithParameterGuidance returns a copy of route with merged parameter guidance.
func (ActionRoute) WithRelatedActions ¶
func (route ActionRoute) WithRelatedActions(related ...string) ActionRoute
WithRelatedActions returns a copy of route with related canonical action IDs.
The parameter is named related because cmd/audit_action_ids follows a parameter out to its callers only under a name beginning with related, and so judges the IDs where each caller writes them.
func (ActionRoute) WithTags ¶
func (route ActionRoute) WithTags(tags ...string) ActionRoute
WithTags returns a copy of route with additional search tags.
func (ActionRoute) WithUsage ¶
func (route ActionRoute) WithUsage(usage string) ActionRoute
WithUsage returns a copy of route with a short model-facing usage hint.
func (ActionRoute) WrapHandler ¶
func (route ActionRoute) WrapHandler(wrap func(next ActionFunc) ActionFunc) ActionRoute
WrapHandler returns the route with wrap applied to its handler, now and on every later rebinding. Use it where a domain decorates the constructor's handler, such as turning a 404 into a structured not-found result: wrap receives the handler to decorate and returns the decorated one.
func (ActionRoute) WrapNotFound ¶ added in v3.1.0
func (route ActionRoute) WrapNotFound(notFound func(params map[string]any) any) ActionRoute
WrapNotFound returns the route with GitLab's 404 answered by the value notFound builds from the call's arguments, in place of the error, now and on every later rebinding. The value is a typed not-found output whose formatter renders NotFoundResult.
notFound reads the arguments as the handler read them: with the documented parameter aliases resolved against the route's input type, which is what UnmarshalParams does before any handler runs. The meta surface hands a route the arguments as the caller spelled them and resolves the aliases only inside that call, on a copy, so a builder reading the map it was handed named a project given as project_path, or a milestone given as iid, as "<nil>", although the request GitLab refused was for exactly that project. The map notFound receives is a copy wherever anything was resolved, so the handler's own arguments are never changed.
type ActionSpec ¶
type ActionSpec struct {
Name string
Route ActionRoute
Aliases []string
Tags []string
Usage string
RelatedActions []string
Compatibility CompatibilityPolicy
ParameterGuidance map[string]ParameterGuidance
InputSchemaOverrides []InputSchemaOverride
ReadOnly bool
Destructive bool
Idempotent bool
OpenWorld bool
Edition string
GitLabDotComOnly bool
OwnerPackage string
IndividualTool IndividualToolSpec
ContentKind string
NotFoundPolicy string
EmbeddedResourcePolicy string
// EmbeddedResource is the canonical gitlab:// URI template of the entity a
// get-style action returns, expanded from the call's parameters and
// embedded in the result under the embedded-resource policy.
EmbeddedResource string
RichResultPolicy string
SchemaValidationNotes []string
RuntimeValidationNotes []string
}
ActionSpec is the canonical metadata contract for one GitLab action.
func CloneActionSpec ¶
func CloneActionSpec(spec ActionSpec) ActionSpec
CloneActionSpec returns a defensive copy of spec and all mutable metadata it owns.
func CloneActionSpecs ¶
func CloneActionSpecs(specs []ActionSpec) []ActionSpec
CloneActionSpecs returns defensive copies of specs in their original order.
func FillScopeParameterGuidance ¶
func FillScopeParameterGuidance(specs []ActionSpec) []ActionSpec
FillScopeParameterGuidance returns a copy of specs with default ParameterGuidance entries added for every scope-suggestive parameter (project_id, group_id, user_id, instance_id, namespace_id, milestone_id, epic_id, ref, branch, tag, sha, path, iid) present in each spec's input schema that lacks an explicit guidance entry. The defaults use the canonical SemanticRole for each scope name and the standard "Project ID or URL-encoded path…" / "Branch or tag name…" ValueSource text.
Packages call this from their ActionSpecs() function before returning so that the discovery auditor's missing_parameter_guidance check is satisfied without each action having to hand-write guidance for every common scope parameter. Explicit guidance entries on a spec are preserved unchanged (they take precedence over the defaults).
func FillScopeParameterGuidanceSingle ¶
func FillScopeParameterGuidanceSingle(spec ActionSpec) ActionSpec
FillScopeParameterGuidanceSingle is the single-spec form of FillScopeParameterGuidance. The central tier filter calls this after pruning tier-restricted fields from the input schema so that the default guidance set never references a field the filter has stripped. Existing guidance entries whose parameter has been removed from the schema are dropped, while guidance for scope-suggestive parameters still present in the schema is added when missing. Explicit guidance entries written by package authors are preserved unchanged.
func NewActionSpec ¶
func NewActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
NewActionSpec creates a defensive canonical action specification.
The route's input schema is not changed in place: the overrides and the canonical enums, formats and ranges are applied to a derived schema, built once per process for a shared input (see DeriveSchema) and privately otherwise. The transform is idempotent, so a spec cloned through this constructor keeps the schema it already had.
func NewAdditiveActionSpec ¶
func NewAdditiveActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
NewAdditiveActionSpec creates a mutating action whose repetition changes the result, so idempotentHint stays false.
It exists because "update" is the wrong shape for some endpoints that look like one. Logging spent time is the example: GitLab's add_spent_time is additive, so two calls of "1h" leave two hours logged, and an idempotentHint of true would invite a model to retry a call whose outcome it could not observe — leaving the user's timesheet wrong in a way nobody notices for a while.
The hint is advisory, which is exactly why it has to be accurate: nothing enforces it, so its only effect is on what a model decides to do.
func NewCreateActionSpec ¶
func NewCreateActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
NewCreateActionSpec creates a mutating, non-idempotent action specification.
func NewDeleteActionSpec ¶
func NewDeleteActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
NewDeleteActionSpec creates a destructive, idempotent action specification.
func NewReadActionSpec ¶
func NewReadActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
NewReadActionSpec creates a read-only, idempotent action specification.
func NewUpdateActionSpec ¶
func NewUpdateActionSpec(name string, route ActionRoute, opts ActionSpecOptions) ActionSpec
NewUpdateActionSpec creates a mutating, idempotent action specification.
func (ActionSpec) Validate ¶
func (spec ActionSpec) Validate() error
Validate verifies invariants that must hold before projecting a spec.
func (ActionSpec) WithEmbeddedResource ¶
func (spec ActionSpec) WithEmbeddedResource(template string) ActionSpec
WithEmbeddedResource returns the spec with its canonical resource declared: the given gitlab:// URI template, expanded from the call's parameters, is embedded in every successful result. Declared at the spec site so the action's owner package states which resource a get returns, and the catalog validator holds the template to the action's parameters.
type ActionSpecOptions ¶
type ActionSpecOptions struct {
Aliases []string
Tags []string
Usage string
RelatedActions []string
Compatibility CompatibilityPolicy
ParameterGuidance map[string]ParameterGuidance
InputSchemaOverrides []InputSchemaOverride
ReadOnly bool
Destructive bool
Idempotent bool
OpenWorld bool
Edition string
GitLabDotComOnly bool
OwnerPackage string
IndividualTool IndividualToolSpec
ContentKind string
NotFoundPolicy string
EmbeddedResourcePolicy string
EmbeddedResource string
RichResultPolicy string
SchemaValidationNotes []string
RuntimeValidationNotes []string
}
ActionSpecOptions contains optional metadata for NewActionSpec.
type ApprovedMemberExtra ¶ added in v3.1.0
type ApprovedMemberExtra struct {
MemberExtra
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
CreatedBy *MemberUserOutput `json:"created_by"`
// ExpiresAt is a date and not a timestamp, which is how GitLab spells a
// membership expiry, so it is read as the string it arrives as.
ExpiresAt string `json:"expires_at"`
Email string `json:"email"`
MemberRole *MemberRoleOutput `json:"member_role"`
}
ApprovedMemberExtra is what lib/api/entities/member.rb sends on the member an approved access request became, beyond what client-go's AccessRequest carries. Approving is the one route of that family GitLab answers with Member rather than AccessRequester, so it is the one that has a membership to describe: everything MemberExtra reads arrives here, and these are the keys beside them. The two URLs are unconditional; the address, the creator and the custom role each wait on their own condition, and the expiry is sent on every member.
Three keys the entity can send are deliberately not read: is_using_seat, avatar_path and custom_attributes wait on presenter options the caller has to ask for, and no access-request route declares show_seat_info, only_path or with_custom_attributes, so GitLab never sends them here. The first is absent from this shape and the other two arrive through the embed unread.
func CapturedApprovedMember ¶ added in v3.1.0
func CapturedApprovedMember(capture *gitlabclient.ResponseCapture) (ApprovedMemberExtra, error)
CapturedApprovedMember reads them off the captured answer to approving one access request.
type ApproverIDsFilter ¶
type ApproverIDsFilter []StringOrInt
ApproverIDsFilter mirrors GitLab's approver_ids and approved_by_ids merge request filters, which accept either a list of numeric user IDs or exactly one of the literals "Any" and "None".
Elements are StringOrInt, so both JSON numbers and JSON strings unmarshal cleanly. Actions exposing this filter must widen the published item type with SchemaApproverIDsOverride, otherwise input validation rejects the literals before the handler ever sees them.
func (ApproverIDsFilter) ApproverIDsValue ¶
func (f ApproverIDsFilter) ApproverIDsValue() (*gl.ApproverIDsValue, error)
ApproverIDsValue converts the filter into the SDK value expected by the merge request list options. It returns nil for an empty filter, so callers can assign the result unconditionally.
The literals are only meaningful on their own: GitLab has no notion of "None plus these IDs", so mixing them with user IDs is rejected rather than silently dropped, which would return a differently-filtered result set than the caller asked for.
type AssetLinkOutput ¶
type AssetLinkOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
DirectAssetURL string `json:"direct_asset_url,omitempty"`
External bool `json:"external"`
LinkType string `json:"link_type"`
}
AssetLinkOutput mirrors gl.ReleaseLink (the per-link object inside release.assets.links). The DirectAssetURL field is omitempty because the official assets-by-link endpoint only populates it for package-link types.
type AssetSourceOutput ¶
AssetSourceOutput mirrors gl.ReleaseAssetsSource (the per-source object inside release.assets.sources). Format is a short string (e.g. "package", "image", "url", "other").
type AssetsOutput ¶
type AssetsOutput struct {
Count int64 `json:"count"`
Sources []AssetSourceOutput `json:"sources,omitempty"`
Links []AssetLinkOutput `json:"links,omitempty"`
EvidenceFilePath string `json:"evidence_file_path,omitempty"`
}
AssetsOutput mirrors gl.ReleaseAssets (the count / sources / links / evidence_file_path object).
func NewAssetsOutput ¶
func NewAssetsOutput(a gl.ReleaseAssets) *AssetsOutput
NewAssetsOutput converts a gl.ReleaseAssets into the canonical-key assets object, returning nil when the SDK value has every field zero.
type AuthorOutput ¶
type AuthorOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
State string `json:"state,omitempty"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
}
AuthorOutput mirrors gl.BasicUser for the release author (per the documented release-by-tag response; the author object is the compact BasicUser shape, not the full User shape).
func NewAuthorOutputFromBasicUser ¶
func NewAuthorOutputFromBasicUser(u gl.BasicUser) AuthorOutput
NewAuthorOutputFromBasicUser converts a gl.BasicUser into the canonical-key author object.
type BasicGroupDetailsOutput ¶
type BasicGroupDetailsOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
WebURL string `json:"web_url,omitempty"`
}
BasicGroupDetailsOutput is the group reference GitLab renders on a board, which carries three keys and not a whole group.
type BasicUserOutput ¶
type BasicUserOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
CreatedAt string `json:"created_at,omitempty"`
}
BasicUserOutput mirrors gl.BasicUser (the user shape returned in many pipeline / merge-train / deployment-MR sub-objects: head_pipeline.user, merge_train.pipeline.user, etc.).
func NewBasicUserOutput ¶
func NewBasicUserOutput(u *gl.BasicUser) *BasicUserOutput
NewBasicUserOutput converts a *gl.BasicUser into the canonical-key basic user object, returning nil when the SDK value is nil.
func NewBasicUserOutputs ¶
func NewBasicUserOutputs(users []*gl.BasicUser) []*BasicUserOutput
NewBasicUserOutputs converts a []*gl.BasicUser slice, skipping nil elements and returning nil for an empty input.
type BillableMemberExtra ¶
type BillableMemberExtra struct {
Locked bool `json:"locked"`
PublicEmail string `json:"public_email"`
}
BillableMemberExtra is what ee/lib/api/entities/billable_member.rb sends on a billable member that client-go's BillableGroupMember does not carry: whether the account is locked and the address the user publishes, both of them from the UserBasic the entity inherits and both on every member.
The same UserBasic exposes avatar_path and custom_attributes, and neither is read: both wait on a presenter option, and the billable members route declares neither only_path nor with_custom_attributes, so GitLab has never sent either on this response.
It is deliberately not MemberExtra. A billable member is a user who counts against the seat total and not a membership record, so it carries no access level, no expiry and no role. The endpoint's own desc annotates Entities::Member while its handler presents this entity, which is why the audit reads nine membership keys against a response that has never carried one of them.
func CapturedBillableMembers ¶
func CapturedBillableMembers(capture *gitlabclient.ResponseCapture, decoded int) ([]BillableMemberExtra, error)
CapturedBillableMembers reads them off the captured answer to a list of billable members, one extra per member in order, the count held to what the SDK decoded.
type BoardExtra ¶
type BoardExtra struct {
Group *BasicGroupDetailsOutput `json:"group"`
Lists []BoardListExtra `json:"lists"`
Weight *int64 `json:"weight"`
}
BoardExtra is that reference on the board, which GitLab exposes under no condition and leaves null on a board that belongs to a project, what each of the board's lists carries beside client-go's BoardList, in the order GitLab sent the lists, and the weight the board is scoped to as GitLab sent it: null for a board with no weight scope, which client-go's IssueBoard decodes as int64 and so reads as a board scoped to weight 0 (register row 116 of docs/development/upstream-bugs.md).
func CapturedBoard ¶
func CapturedBoard(capture *gitlabclient.ResponseCapture) (BoardExtra, error)
CapturedBoard reads it off the captured answer to a request for one board.
func CapturedBoards ¶
func CapturedBoards(capture *gitlabclient.ResponseCapture, decoded int) ([]BoardExtra, error)
CapturedBoards reads the same off a list answer, one extra per board in order, the count held to what the SDK decoded.
type BoardLabelDetailsOutput ¶
type BoardLabelDetailsOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
Description string `json:"description"`
}
BoardLabelDetailsOutput is the documented board `labels[]` entry. The documented update-board responses show only id, name, color, and description for each label; the SDK label types' remaining fields are not part of the documented board labels shape.
func NewBoardLabelDetailsOutputs ¶
func NewBoardLabelDetailsOutputs(details []*gl.LabelDetails) []*BoardLabelDetailsOutput
NewBoardLabelDetailsOutputs converts a slice of gl.LabelDetails into the documented board label subset, skipping nil elements and returning nil when no labels remain (consistent with NewCustomAttributeOutputs, so callers never serialize an empty array for an effectively-empty input).
type BoardLabelOutput ¶
type BoardLabelOutput struct {
Name string `json:"name"`
Color string `json:"color"`
Description string `json:"description"`
}
BoardLabelOutput is the documented board-list label object. Every documented board-list response shows the list's `label` object with only name, color, and description; gl.Label's remaining fields are not part of the documented board-list label shape.
func NewBoardLabelOutput ¶
func NewBoardLabelOutput(l *gl.Label) *BoardLabelOutput
NewBoardLabelOutput converts a gl.Label into the documented board-list label subset, returning nil when the SDK value is nil.
type BoardListAssigneeOutput ¶
type BoardListAssigneeOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
Username string `json:"username"`
}
BoardListAssigneeOutput is the compact assignee object on a board list (Premium/Ultimate assignee list type), matching gl.BoardListAssignee. The documented response examples cover only label and milestone lists, so this premium list-type sub-object has no fuller documented shape to trim against.
func NewBoardListAssigneeOutput ¶
func NewBoardListAssigneeOutput(a *gl.BoardListAssignee) *BoardListAssigneeOutput
NewBoardListAssigneeOutput converts a gl.BoardListAssignee into the shared output shape, returning nil when the SDK value is nil.
type BoardListExtra ¶
type BoardListExtra struct {
LimitMetric string `json:"limit_metric"`
}
BoardListExtra is the metric a board list limits its work in progress by, which GitLab exposes only on a list whose board has work-in-progress limits available, so it is empty everywhere else.
func CapturedBoardList ¶
func CapturedBoardList(capture *gitlabclient.ResponseCapture) (BoardListExtra, error)
CapturedBoardList reads it off the captured answer to a request for one board list.
func CapturedBoardLists ¶
func CapturedBoardLists(capture *gitlabclient.ResponseCapture, decoded int) ([]BoardListExtra, error)
CapturedBoardLists reads the same off a list answer, one extra per board list in order, the count held to what the SDK decoded.
type BoardUserOutput ¶
type BoardUserOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
}
BoardUserOutput is the documented 6-field board assignee object shared by the project boards and group boards APIs (doc/api/boards.md and doc/api/group_boards.md). The documented update-board responses show only id, name, username, state, avatar_url, and web_url; gl.BasicUser's created_at is not part of the documented board assignee shape.
func NewBoardUserOutput ¶
func NewBoardUserOutput(u *gl.BasicUser) *BoardUserOutput
NewBoardUserOutput converts a gl.BasicUser into the documented board assignee subset, returning nil when the SDK value is nil.
type BodyEdit ¶ added in v3.3.0
type BodyEdit struct {
// Set writes each key with its value, replacing whatever client-go put
// there. A nil value writes null, which is how GitLab is told to remove a
// value an option struct tagged omitempty cannot send.
Set map[string]any
// Drop removes each key, for a field an option struct writes on every
// call although the caller left it out.
Drop []string
}
BodyEdit says what EditJSONBody writes into the JSON object client-go encoded as a request body, and what it removes from it.
type BridgeExtra ¶
type BridgeExtra struct {
JobMeasures
Project *BridgeProjectOutput `json:"project"`
}
BridgeExtra is that object on a bridge job, exposed under no condition, and the bridge's measures as GitLab sent them.
func CapturedBridges ¶
func CapturedBridges(capture *gitlabclient.ResponseCapture, decoded int) ([]BridgeExtra, error)
CapturedBridges reads it off the captured answer to a list of bridges, one extra per bridge in order, the count held to what the SDK decoded.
type BridgeProjectOutput ¶
type BridgeProjectOutput struct {
CIJobTokenScopeEnabled bool `json:"ci_job_token_scope_enabled"`
}
BridgeProjectOutput is the project object GitLab renders on a job, which carries one key: whether the job token can reach outside this project.
type CICDVariableFlags ¶
CICDVariableFlags groups boolean CI/CD variable attributes for Markdown view-model construction.
type CICDVariableMarkdown ¶
type CICDVariableMarkdown struct {
Key string
Value string
VariableType string
Protected bool
Masked bool
Hidden bool
Raw bool
EnvironmentScope string
Description string
}
CICDVariableMarkdown carries the common fields rendered by GitLab CI/CD variable tools at project, group, and instance scopes.
func NewCICDVariableMarkdown ¶
func NewCICDVariableMarkdown(key, value, variableType string, flags CICDVariableFlags, environmentScope, description string) CICDVariableMarkdown
NewCICDVariableMarkdown builds a shared Markdown view model for CI/CD variables without forcing tool packages to duplicate composite literals.
type CIMinutesUsageOutput ¶ added in v3.1.0
type CIMinutesUsageOutput struct {
TotalMinutesUsed int64 `json:"total_minutes_used"`
MonthlyMinutesUsed int64 `json:"monthly_minutes_used"`
PurchasedMinutesUsed int64 `json:"purchased_minutes_used"`
}
CIMinutesUsageOutput is the compute-minute usage ee/lib/api/entities/ci/minutes/usage.rb renders for a namespace in the current billing period: the total, and the part of it drawn from the monthly quota and from purchased minutes. ee/lib/ee/api/entities/namespace.rb sends it only for a top-level namespace whose caller holds admin_ci_minutes, which is its owner or an administrator, and GitLab sends it from 19.4 (namespaces.md). All three are whole minutes, as Ci::Minutes::Usage computes them.
type CIVariableExtra ¶
type CIVariableExtra struct {
EnvironmentScope string `json:"environment_scope"`
Hidden bool `json:"hidden"`
}
CIVariableExtra is what GitLab's CI variable entity sends that the instance variable struct does not carry: the environments the value applies to, and whether the value is hidden from every reader once set. Both are exposed only where the variable's own model answers to them.
func CapturedCIVariable ¶
func CapturedCIVariable(capture *gitlabclient.ResponseCapture) (CIVariableExtra, error)
CapturedCIVariable reads them off the captured answer to a request for one variable.
func CapturedCIVariables ¶
func CapturedCIVariables(capture *gitlabclient.ResponseCapture, decoded int) ([]CIVariableExtra, error)
CapturedCIVariables reads the same off a list answer, one extra per variable in order, the count held to what the SDK decoded.
type Card ¶ added in v3.1.0
type Card struct {
// contains filtered or unexported fields
}
Card is the one shape a tool result about a single GitLab object takes, and the only writer of its rows: the H2 heading the formatter composes, then one "- **Label**: value" item per field, then the object's long text, nested objects and nested collections, then the next-step hints last. A Markdown table is for a collection of objects that share columns and never for the fields of one object, so no method here writes a card row as a table row; the one table a Card writes is the nested collection Card.Table opens, under a heading of its own.
The list shape is the one that survives whatever is written around it. A list item is a complete block wherever it lands, so a formatter can extend a card another formatter started, and a card can follow a quote, a fence or a table. A table row is a row only while nothing but rows has been written since its header, and anywhere else it is a line of literal pipes, which is how two cards in this tree came to print "| Status | merged |" as text while their tests passed on the substring.
A Card streams into the caller's builder rather than buffering a document, so a formatter keeps the seams it has today: a fenced body, a rich-content note, a second formatter adding rows. What keeps that composable is the mark. The card records the builder's length after each of its own writes, and when anything else has been written since, the next row starts after a blank line, so it opens a list of its own instead of continuing a foreign block, and a table or a quote written in between ends where it should.
Every value is escaped at the write that renders it, by the helper the value's shape needs, and a caller passes GitLab text as it arrived: the cell escaper for an inline value, MdCodeSpan for a code span, MdTitleLink for a link, WrapGFMBody for prose. The escaping is idempotent, so a caller that still escapes by hand renders unchanged. An empty, nil, blank or zero value writes nothing where the zero is an absence: the card shows what GitLab sent, and never a label with nothing after it.
func NewCard ¶ added in v3.1.0
NewCard starts a card in b. A non-empty heading is written as an H2 through EscapeMdHeading; the formatter composes it, and the whole composition is escaped, so it must not begin with '#'. An empty heading writes none, for a card that continues under a heading the caller wrote, or a card a shared renderer starts on behalf of the formatter that owns the heading.
func (*Card) Bool ¶ added in v3.1.0
Bool writes the flag as BoolEmoji renders it, a tick or a cross. It is for a flag whose true reads as a good thing; a negative one, such as revoked, locked or expired, is written by Card.Warn.
func (*Card) BoolPtr ¶ added in v3.1.0
BoolPtr writes the flag when GitLab sent it and nothing for nil.
func (*Card) Code ¶ added in v3.1.0
Code writes the value as a code span through MdCodeSpan, for a value the reader has to copy exactly: a key, a path, a SHA, a command. Nothing inside the span is Markdown and no entity is written. A blank value writes nothing.
func (*Card) Count ¶ added in v3.1.0
Count writes a number only when it is not zero, for a limit or a count whose zero means GitLab did not say.
func (*Card) End ¶ added in v3.1.0
End writes the next-step hints through WriteHints and is always the last write of a card: a row written after it lands below the guidance section, where ExtractHints no longer finds one. A secret the card showed adds its hint ahead of the caller's, with the label escaped like the row that showed it, since the hint is a list item too. It is called once, on the card NewCard started; on a nested card it writes the same section.
func (*Card) Fence ¶ added in v3.1.0
Fence writes a body inside a fenced code block sized by MarkdownFencedBlock, under a heading one level below the card's own when the title is not blank: a file, a template, a job log, a diff. An empty body writes nothing, heading included.
func (*Card) Field ¶ added in v3.1.0
Field writes "- **label**: value" with both halves through the cell escaper and the guidance heading defused. A blank value writes nothing, which is how every optional field is written: the card shows what GitLab sent.
func (*Card) FieldOr ¶ added in v3.1.0
FieldOr writes value like Card.Field, or absent in its place when the value is blank, for a field whose absence is the answer: "never" for an expiry, "unlimited" for a quota, "-" for a slot. A blank absent writes nothing.
func (*Card) Flag ¶ added in v3.1.0
Flag writes "- <emoji> **label**" when on is true and nothing otherwise, for a condition worth stating only when it holds: confidential, archived, a draft. An empty emoji writes the label alone.
func (*Card) Int ¶ added in v3.1.0
Int writes a number that is an answer at zero: an ID, or a count GitLab always sends.
func (*Card) Link ¶ added in v3.1.0
Link writes MdTitleLink of text and url: a link when url is set, the escaped text alone when it is not, and nothing when both are blank. A blank text with a url links the url itself, so a link never has an empty label.
func (*Card) Markdown ¶ added in v3.1.0
Markdown writes a value the formatter composed from parts that are already escaped, such as a link built by MdTitleLink beside a status word, or a code span beside a count. It is the one row whose value is written as given, which is why the escaping gate judges the call site rather than this method: the write here is a plain concatenation with no hole of its own, and a value that reaches it raw is the formatter's finding, at the line that composed it.
func (*Card) Note ¶ added in v3.1.0
Note writes a paragraph of the server's own prose after the rows and sections and before the hints: a rendering note, a confirmation sentence. It is one line, with control bytes dropped and line breaks collapsed, so it can add no structure; prose GitLab wrote belongs in Card.Text, which quotes it. A blank note writes nothing.
func (*Card) Secret ¶ added in v3.1.0
Secret writes a value that is shown once, as Card.Code does, and records it so that Card.End adds the hint to store it, since a token or a secret GitLab returns on creation cannot be retrieved again. A blank value writes nothing and adds no hint.
func (*Card) Section ¶ added in v3.1.0
Section writes a heading one level below the card's own, through EscapeMdHeading, and returns the card that writes the section's rows: the shape of a large nested object, a pipeline inside a merge request, a commit inside a release. Its rows start at column zero under the heading, and a section of a section goes one level deeper, to H6 at most. A blank title opens no heading and the returned card continues the same list. The fields of the object itself are written before its sections, since a row written after one lands under the section's heading.
func (*Card) Sub ¶ added in v3.1.0
Sub writes a label-only row and returns the card that writes the rows nested under it, indented two spaces further: the shape of a small nested object, an author or a milestone, whose fields belong beside the parent's. The nested rows continue the parent's list, so a parent row written after them follows directly.
func (*Card) Table ¶ added in v3.1.0
Table writes a nested collection's heading, one level below the card's own and omitted when the title is blank, then the table header for columns, and returns the table that takes the rows. The table is written at column zero whatever the card's depth, and the card's next row starts after a blank line, so the table ends where its rows end. A table with no columns writes only the heading.
func (*Card) Text ¶ added in v3.1.0
Text writes prose a person typed into GitLab: a description, a note body, a commit message. A one-line body stays on the field's line, through the cell escaper with the guidance heading defused. A longer one becomes a blockquote under the label through WrapGFMBody, indented so the quote belongs to the item, and nothing in it can add a field, a heading or a list item to the card. Trailing line breaks are dropped first, so a one-line body that ends in a newline stays inline. A blank body writes nothing.
func (*Card) Time ¶ added in v3.1.0
Time writes an RFC 3339 timestamp in the display form FormatTime gives, or nothing when it is empty. A time.Time is passed as RFC3339Ptr of it.
func (*Card) URL ¶ added in v3.1.0
URL writes the "URL" row, the address linked to itself, or nothing when it is empty.
type CardTable ¶ added in v3.1.0
type CardTable struct {
// contains filtered or unexported fields
}
CardTable is a nested collection inside a card: the table Card.Table opened, taking one row at a time.
func (*CardTable) Row ¶ added in v3.1.0
Row writes one row of the collection. The cells are written as given, so the caller renders each one: EscapeMdTableCell for text, MdTitleLink for a link, MdCodeSpanCell for a code span, FormatTime for a timestamp. A link is a finished construct the cell escaper would turn back into text, which is why this is the one write in a card the caller escapes for, on the same terms as MarkdownTableRow, and why the escaping gate judges the call site rather than this method. A row with no cells writes nothing.
type ClearableDateInput ¶ added in v3.3.0
ClearableDateInput names one optional date input of an update and where its value goes.
type CodedError ¶
type CodedError struct {
// contains filtered or unexported fields
}
CodedError carries a JSON-RPC code alongside the original error.
It exists because the go-sdk derives the wire code from the error value: its toWireError leaves Code at 0 unless the error is, or wraps, a *jsonrpc.Error. An unclassified error therefore reaches the client as code 0, which is not a JSON-RPC error code at all, and a client cannot tell "you sent the wrong arguments" from "the upstream is down" — the two failures it would act on differently.
Unwrap lists the cause before the code, so errors.As finds the innermost code first. That ordering is deliberate in both directions: a handler that wraps an argument failure in its own context still reports the argument code, and a cause that already carries a specific code (a not-found, say) keeps it rather than being flattened into the generic one.
func (*CodedError) Error ¶
func (e *CodedError) Error() string
Error renders the cause alone: the code is for the transport, not the reader.
func (*CodedError) Unwrap ¶
func (e *CodedError) Unwrap() []error
Unwrap exposes the cause first and the code second, in the order the type documentation explains.
type CommitCommentExtra ¶
CommitCommentExtra is when a commit comment was written, which GitLab's commit note entity exposes under no condition.
func CapturedCommitComment ¶
func CapturedCommitComment(capture *gitlabclient.ResponseCapture) (CommitCommentExtra, error)
CapturedCommitComment reads it off the captured answer to a request that returned one comment.
func CapturedCommitComments ¶
func CapturedCommitComments(capture *gitlabclient.ResponseCapture, decoded int) ([]CommitCommentExtra, error)
CapturedCommitComments reads the same off a list answer, one extra per comment in order, the count held to what the SDK decoded.
type CommitOutput ¶
type CommitOutput struct {
ID string `json:"id"`
ShortID string `json:"short_id"`
Title string `json:"title"`
AuthorName string `json:"author_name"`
AuthorEmail string `json:"author_email"`
AuthoredDate string `json:"authored_date,omitempty"`
CommitterName string `json:"committer_name"`
CommitterEmail string `json:"committer_email"`
CommittedDate string `json:"committed_date,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
Message string `json:"message"`
ParentIDs []string `json:"parent_ids,omitempty"`
}
CommitOutput is the documented reference subset of the commit object surfaced on a release (per doc/api/releases/_index.md). The documented release commit JSON surfaces id, short_id, title, created_at, parent_ids, message, author_name, author_email, authored_date, committer_name, committer_email, committed_date. SDK-only commit fields (stats, status, project_id, web_url) are not part of the documented project-release commit subset and are intentionally omitted from this base type. The group-releases endpoint adds web_url; consumers that need it embed this type and add the extra field.
func NewCommitOutput ¶
func NewCommitOutput(c gl.Commit) *CommitOutput
NewCommitOutput converts a gl.Commit into the canonical-key commit object, returning nil when the commit ID is empty (no commit associated with the release). Timestamps are formatted as RFC 3339 strings via FormatTimePtr.
type CompatibilityPolicy ¶
type CompatibilityPolicy struct {
ActionAliases []ActionAliasSpec
ParameterAliases []ParameterAliasSpec
}
CompatibilityPolicy carries compatibility aliases and their ownership policy.
func CloneCompatibilityPolicy ¶
func CloneCompatibilityPolicy(policy CompatibilityPolicy) CompatibilityPolicy
CloneCompatibilityPolicy returns a defensive copy of compatibility metadata.
type CustomAttributeOutput ¶
CustomAttributeOutput mirrors gl.CustomAttribute, a key/value custom attribute attached to a user.
func NewCustomAttributeOutputs ¶
func NewCustomAttributeOutputs(attrs []*gl.CustomAttribute) []CustomAttributeOutput
NewCustomAttributeOutputs converts a []*gl.CustomAttribute slice, skipping nil elements and returning nil when no attributes remain.
type DateInput ¶ added in v3.3.0
DateInput names one date-only input and where its parsed value goes.
type DeleteOutput ¶
type DeleteOutput struct {
HintableOutput
Status string `json:"status"`
Message string `json:"message"`
}
DeleteOutput is a confirmation message returned by destructive tool handlers (delete, unprotect, unapprove) so the LLM receives explicit feedback instead of empty content when the operation succeeds.
func DeleteResult ¶
func DeleteResult(resource string) (*mcp.CallToolResult, DeleteOutput, error)
DeleteResult builds a DeleteOutput and its Markdown representation for a successful destructive operation. The resource parameter describes what was affected (e.g., "project 42", "branch feature/x"), and reaches the Markdown through the inline escaper a card row uses, since the branch, package or file name inside it is the caller's and GitLab's rather than the server's: unescaped, a name carrying a line break wrote structure of its own under the confirmation, and one carrying a tag reached the page as raw HTML. The structured message keeps the sentence as it reads.
type DependencyExtra ¶
type DependencyExtra struct {
Malware *bool `json:"malware"`
}
DependencyExtra is whether a dependency is known malware, which GitLab exposes to a caller allowed to read the project's vulnerabilities and only while the instance has the feature enabled.
The pointer carries the third state: true is a detection, false is a package the scan cleared, and absent is a scan that did not run. Read into a bool the last two would both arrive as false.
func CapturedDependencies ¶
func CapturedDependencies(capture *gitlabclient.ResponseCapture, decoded int) ([]DependencyExtra, error)
CapturedDependencies reads it off the captured answer to a list of dependencies, one extra per dependency in order, the count held to what the SDK decoded.
type DeployKeyExtra ¶
type DeployKeyExtra struct {
ProjectsWithWriteAccess []DeployKeyProjectOutput `json:"projects_with_write_access"`
ProjectsWithReadonlyAccess []DeployKeyProjectOutput `json:"projects_with_readonly_access"`
}
DeployKeyExtra is the projects a deploy key can write to or only read from, which GitLab sends when the request asks for them. client-go models the pair on InstanceDeployKey and not on ProjectDeployKey, so this is read only for the project-scoped handlers; the last use and the usage type this shape also carried retired with client-go v3.12.0, which added both to both structs.
func CapturedDeployKey ¶
func CapturedDeployKey(capture *gitlabclient.ResponseCapture) (DeployKeyExtra, error)
CapturedDeployKey reads them off the captured answer to a request for one deploy key.
func CapturedDeployKeys ¶
func CapturedDeployKeys(capture *gitlabclient.ResponseCapture, decoded int) ([]DeployKeyExtra, error)
CapturedDeployKeys reads the same off a list answer, one extra per key in order, the count held to what the SDK decoded.
type DeployKeyProjectOutput ¶
type DeployKeyProjectOutput struct {
ID int64 `json:"id"`
Description string `json:"description"`
Name string `json:"name"`
NameWithNamespace string `json:"name_with_namespace"`
Path string `json:"path"`
PathWithNamespace string `json:"path_with_namespace"`
CreatedAt *time.Time `json:"created_at"`
}
DeployKeyProjectOutput is one project a deploy key reaches, rendered as the project identity entity: the naming and the creation date, without the settings a full project carries.
type DeploymentApprovalOutput ¶
type DeploymentApprovalOutput struct {
User *UserBasicOutput `json:"user,omitempty"`
Status string `json:"status"`
CreatedAt *time.Time `json:"created_at"`
Comment string `json:"comment,omitempty"`
}
DeploymentApprovalOutput is one approval or rejection recorded against a deployment, as lib/api/entities/deployments/approval.rb renders it.
type DeploymentApprovalRuleOutput ¶
type DeploymentApprovalRuleOutput struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id,omitempty"`
GroupID int64 `json:"group_id,omitempty"`
AccessLevel int64 `json:"access_level,omitempty"`
AccessLevelDescription string `json:"access_level_description,omitempty"`
RequiredApprovals int64 `json:"required_approvals"`
GroupInheritanceType int64 `json:"group_inheritance_type,omitempty"`
DeploymentApprovals []DeploymentApprovalOutput `json:"deployment_approvals,omitempty"`
}
DeploymentApprovalRuleOutput is one rule of the approval summary: who may approve, how many approvals it needs, and what has been recorded against it.
type DeploymentApprovalSummaryOutput ¶
type DeploymentApprovalSummaryOutput struct {
Rules []DeploymentApprovalRuleOutput `json:"rules,omitempty"`
}
DeploymentApprovalSummaryOutput is the rules a deployment must satisfy before it may run.
type DeploymentExtra ¶
type DeploymentExtra struct {
Approvals []DeploymentApprovalOutput `json:"approvals"`
ApprovalSummary *DeploymentApprovalSummaryOutput `json:"approval_summary"`
PendingApprovalCount int64 `json:"pending_approval_count"`
}
DeploymentExtra is what GitLab's extended deployment entity sends that client-go's Deployment does not carry: the approvals recorded so far, how many are still outstanding, and the rules they are counted against. All three are exposed under no condition, so a deployment that needs no approval carries them empty rather than not at all.
func CapturedDeployment ¶
func CapturedDeployment(capture *gitlabclient.ResponseCapture) (DeploymentExtra, error)
CapturedDeployment reads them off the captured answer to a request for one deployment.
func CapturedDeployments ¶
func CapturedDeployments(capture *gitlabclient.ResponseCapture, decoded int) ([]DeploymentExtra, error)
CapturedDeployments reads the same off a list answer, one extra per deployment in order, the count held to what the SDK decoded.
type DetailedError ¶
type DetailedError struct {
Domain string `json:"domain"`
Action string `json:"action"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
GitLabStatus int `json:"gitlab_status,omitempty"`
RequestID string `json:"request_id,omitempty"`
}
DetailedError represents a rich, structured error with domain context for diagnostic output. It extends ToolError with additional fields useful for automated issue creation and Markdown error reporting.
func NewDetailedError ¶
func NewDetailedError(domain, action string, err error) *DetailedError
NewDetailedError creates a DetailedError from a GitLab API error, extracting HTTP status and request ID when available.
func (*DetailedError) Error ¶
func (e *DetailedError) Error() string
Error returns a concise representation: "domain/action: message".
func (*DetailedError) Markdown ¶
func (e *DetailedError) Markdown() string
Markdown renders the error as a card suitable for an MCP tool result, with every field the error carries as a row. The domain and action are the handler's own, compiled in; the message and the details carry GitLab's words, and the card escapes them on their rows, where they used to be written raw on four consecutive bullet-less lines that rendered as one run-on paragraph. The request ID is a code span, so a reader can copy it.
The status row carries the code and its reason phrase and no reading of them, because the message already is the reading of the whole response. A second classification of the status alone used to sit beside it, and for a 401 the two disagreed on one card: the message said GitLab had rejected the token itself, and two rows down the status said the token might be fine and the refusal a missing permission.
type DiffExtra ¶ added in v3.1.0
type DiffExtra struct {
Collapsed bool `json:"collapsed"`
TooLarge bool `json:"too_large"`
GeneratedFile bool `json:"generated_file"`
}
DiffExtra is what Entities::Diff sends on a file diff that client-go's Diff does not model: whether GitLab collapsed the diff or left it out as too large, which is why a diff can arrive empty, and whether the file is generated. All three are exposed with no condition. The gap is recorded in docs/development/upstream-bugs.md.
func CapturedCompareDiffs ¶ added in v3.1.0
func CapturedCompareDiffs(capture *gitlabclient.ResponseCapture, decoded int) ([]DiffExtra, error)
CapturedCompareDiffs reads the same off a captured comparison, whose file diffs sit under diffs beside the commits.
func CapturedDiffs ¶ added in v3.1.0
func CapturedDiffs(capture *gitlabclient.ResponseCapture, decoded int) ([]DiffExtra, error)
CapturedDiffs reads the flags off a captured list of file diffs, one per diff in order, the count held to what the SDK decoded.
type DiffLine ¶
type DiffLine struct {
OldLine int // Line number in old file (0 for added lines)
NewLine int // Line number in new file (0 for removed lines)
Type LineType // Whether the line was added, removed, or unchanged
}
DiffLine represents a single line in a parsed unified diff with its old/new line numbers and type (added, removed, or context).
func ParseDiffLines ¶
ParseDiffLines parses a unified diff string and returns metadata for each line, including old/new line numbers and whether it is added, removed, or context. Only lines inside @@ hunk headers are returned.
type DiffOutput ¶
type DiffOutput struct {
OldPath string `json:"old_path"`
NewPath string `json:"new_path"`
AMode string `json:"a_mode,omitempty"`
BMode string `json:"b_mode,omitempty"`
Diff string `json:"diff"`
NewFile bool `json:"new_file"`
RenamedFile bool `json:"renamed_file"`
DeletedFile bool `json:"deleted_file"`
GeneratedFile bool `json:"generated_file"`
Collapsed bool `json:"collapsed,omitempty"`
TooLarge bool `json:"too_large,omitempty"`
}
DiffOutput represents a single file diff from the GitLab API. It is used by both commit diff and repository compare operations.
The last three flags are what lib/api/entities/diff.rb sends beside the eight client-go's Diff models, read from the captured response (see DiffExtra); they are spelled and tagged the way a merge request's file diff publishes them.
func DiffToOutput ¶
func DiffToOutput(d *gl.Diff, extra DiffExtra) DiffOutput
DiffToOutput converts a GitLab API gl.Diff to the MCP tool output format, with the flags the captured response carried beside it.
func (DiffOutput) Status ¶ added in v3.1.0
func (d DiffOutput) Status() string
Status names what happened to the file in the diff, and says so when GitLab sent no diff text for it, having collapsed it or found it too large, or marked the file generated: "modified (too large)" tells a reader why the diff is empty where "modified" alone reads as a change with nothing in it.
type DiffRefsOutput ¶
type DiffRefsOutput struct {
BaseSHA string `json:"base_sha"`
HeadSHA string `json:"head_sha"`
StartSHA string `json:"start_sha"`
}
DiffRefsOutput mirrors the diff_refs object on a merge request, carrying the base, head, and start SHAs of the diff. It is byte-identical across the mergerequests and deploymentmergerequests domains; the canonical copy lives here so both packages can alias it without introducing import cycles (ADR-0004).
type DiscussionExtra ¶
type DiscussionExtra struct {
Resolvable bool `json:"resolvable"`
Resolved bool `json:"resolved"`
Notes []NoteExtra `json:"notes"`
}
DiscussionExtra is what GitLab's Discussion entity sends that client-go's Discussion does not model, the thread's own resolution state, read from the captured response beside the notes' own extras (see CapturedDiscussion).
func CapturedDiscussion ¶
func CapturedDiscussion(capture *gitlabclient.ResponseCapture) (DiscussionExtra, error)
CapturedDiscussion reads, from the captured answer of a call that returned one discussion, what GitLab sends that client-go's Discussion and its notes do not model.
func CapturedDiscussions ¶
func CapturedDiscussions(capture *gitlabclient.ResponseCapture, decoded int) ([]DiscussionExtra, error)
CapturedDiscussions reads the same from the captured answer of a call that returned a list of discussions, one extra per discussion in the list's order, the count held to what the SDK decoded as CapturedNotes holds it.
type DiscussionMarkdown ¶
type DiscussionMarkdown struct {
ID string
Notes []NoteMarkdown
// QuickActions is what the mutation that opened the thread reported about
// the quick actions its first note carried, nil otherwise.
QuickActions *QuickActionsStatusOutput
}
DiscussionMarkdown carries common discussion fields for Markdown responses.
func DiscussionMarkdowns ¶
func DiscussionMarkdowns[T any](discussions []T, convert func(T) DiscussionMarkdown) []DiscussionMarkdown
DiscussionMarkdowns maps package-specific discussion outputs to the shared discussion Markdown view model.
func DiscussionThreadOutputMarkdowns ¶
func DiscussionThreadOutputMarkdowns(discussions []DiscussionThreadOutput) []DiscussionMarkdown
DiscussionThreadOutputMarkdowns maps thread outputs to Markdown view models.
func NewDiscussionMarkdown ¶
func NewDiscussionMarkdown(id string, notes []NoteMarkdown) DiscussionMarkdown
NewDiscussionMarkdown builds a shared Markdown view model for discussion threads.
type DiscussionNoteMarkdown ¶
type DiscussionNoteMarkdown = NoteMarkdown
DiscussionNoteMarkdown is the note of a discussion thread, which is a GitLab note like any other and is rendered by the same view model: the two used to be separate types with renderers that disagreed on the heading, the author's spelling and the body's label, and the discussion one carried no resolution state, which kept the merge request and commit discussion packages from adopting it at all.
type DiscussionRenderer ¶
type DiscussionRenderer struct {
ListTitle string
EmptyMessage string
ListHints []string
DiscussionHints []string
NoteHints []string
}
DiscussionRenderer stores stable labels and hints for a discussion family so package formatters can avoid repeating identical rendering glue.
func NewDiscussionRenderer ¶
func NewDiscussionRenderer(listTitle, emptyMessage, listHint, discussionHint, noteHint string) DiscussionRenderer
NewDiscussionRenderer builds a renderer for discussion tool families that use one hint for each list, discussion, and note view.
func (DiscussionRenderer) FormatDiscussion ¶
func (r DiscussionRenderer) FormatDiscussion(discussion DiscussionMarkdown) string
FormatDiscussion renders a single discussion using the renderer hints.
func (DiscussionRenderer) FormatGraphQLForwardList ¶
func (r DiscussionRenderer) FormatGraphQLForwardList(discussions []DiscussionMarkdown, pagination GraphQLForwardPaginationOutput) string
FormatGraphQLForwardList renders GraphQL discussion threads from a connection that only pages forward, so the summary line names no previous page.
func (DiscussionRenderer) FormatNote ¶
func (r DiscussionRenderer) FormatNote(note NoteMarkdown) string
FormatNote renders a single discussion note using the renderer hints, as the note card every note tool renders.
func (DiscussionRenderer) FormatRESTList ¶
func (r DiscussionRenderer) FormatRESTList(discussions []DiscussionMarkdown, pagination PaginationOutput) string
FormatRESTList renders REST discussion threads with offset pagination.
type DiscussionThreadNoteOutput ¶
type DiscussionThreadNoteOutput struct {
HintableOutput
ID int64 `json:"id"`
Body string `json:"body"`
Author *NoteUserOutput `json:"author,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at,omitempty"`
Resolved bool `json:"resolved"`
Resolvable bool `json:"resolvable"`
ResolvedAt string `json:"resolved_at,omitempty"`
ResolvedBy *NoteUserOutput `json:"resolved_by,omitempty"`
System bool `json:"system"`
Internal bool `json:"internal"`
Confidential bool `json:"confidential"`
Type string `json:"type,omitempty"`
NoteableType string `json:"noteable_type,omitempty"`
NoteableID int64 `json:"noteable_id,omitempty"`
NoteableIID int64 `json:"noteable_iid,omitempty"`
CommitID string `json:"commit_id,omitempty"`
Position *NotePositionOutput `json:"position,omitempty"`
ProjectID int64 `json:"project_id,omitempty"`
Imported bool `json:"imported"`
ImportedFrom string `json:"imported_from,omitempty"`
CommandsChanges map[string]any `json:"commands_changes,omitempty"`
Suggestions []SuggestionOutput `json:"suggestions,omitempty"`
}
DiscussionThreadNoteOutput mirrors every field of GitLab's Note entity as it appears within a REST discussion thread (mrdiscussions, commitdiscussions, issuediscussions, snippetdiscussions). The shape differs from NoteOutput in omitempty semantics: UpdatedAt carries omitempty (absent on notes created without an update), while Resolved and Resolvable are always emitted (never omitted) because thread resolution state is always meaningful. Field order and JSON tags are locked.
func DiscussionThreadNoteOutputFromGitLab ¶
func DiscussionThreadNoteOutputFromGitLab(n *gl.Note, extra NoteExtra) DiscussionThreadNoteOutput
DiscussionThreadNoteOutputFromGitLab converts a gl.Note within a discussion thread, and what the captured response adds to it, into the thread note shape. A nil note converts to the zero value, since the SDK returns []*gl.Note and an element may be nil.
func (DiscussionThreadNoteOutput) AuthorUsername ¶ added in v3.1.0
func (n DiscussionThreadNoteOutput) AuthorUsername() string
AuthorUsername returns the author's username, or "" when the note carries no author object.
func (DiscussionThreadNoteOutput) MarkdownNote ¶
func (n DiscussionThreadNoteOutput) MarkdownNote() NoteMarkdown
MarkdownNote returns the shared Markdown view model for a thread note: the author by username, and the flags and resolution state the note carries, which the view model used to leave out and the discussion card could not show.
func (DiscussionThreadNoteOutput) ResolvedByUsername ¶ added in v3.1.0
func (n DiscussionThreadNoteOutput) ResolvedByUsername() string
ResolvedByUsername returns the resolver's username, or "" when nobody has resolved the note.
type DiscussionThreadOutput ¶
type DiscussionThreadOutput struct {
HintableOutput
ID string `json:"id"`
IndividualNote bool `json:"individual_note"`
Resolvable bool `json:"resolvable"`
Resolved bool `json:"resolved,omitempty"`
Notes []*DiscussionThreadNoteOutput `json:"notes"`
}
DiscussionThreadOutput mirrors GitLab's Discussion entity with its full note payloads as returned by the REST Discussions API. Notes are pointer elements because the SDK returns []*gl.Note and individual notes may be nil in edge cases. `resolvable` and `resolved` are the thread's own, which client-go does not model; `resolved` is sent only for a resolvable thread.
func DiscussionThreadOutputFromGitLab ¶
func DiscussionThreadOutputFromGitLab(d *gl.Discussion, extra DiscussionExtra) DiscussionThreadOutput
DiscussionThreadOutputFromGitLab converts a gl.Discussion and what the captured response adds to it into the thread shape, every note included. A note the capture holds no extra for, which cannot happen for an answer the SDK decoded, converts with none; a nil discussion converts to the zero value.
func (DiscussionThreadOutput) MarkdownDiscussion ¶
func (d DiscussionThreadOutput) MarkdownDiscussion() DiscussionMarkdown
MarkdownDiscussion returns the shared Markdown view model for a thread.
type DuoAccessRuleNamespaceOutput ¶ added in v3.1.0
type DuoAccessRuleNamespaceOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
FullPath string `json:"full_path"`
}
DuoAccessRuleNamespaceOutput is the namespace a Duo access rule is inherited from, absent on a rule the namespace set itself.
type DuoNamespaceAccessRuleOutput ¶ added in v3.1.0
type DuoNamespaceAccessRuleOutput struct {
ThroughNamespace *DuoAccessRuleNamespaceOutput `json:"through_namespace"`
Features []string `json:"features"`
}
DuoNamespaceAccessRuleOutput is one entry of the duo_namespace_access_rules array ee/lib/ai/feature_access_rule_transformer.rb builds. Like AISettingsOutput it is shared: the application-setting entity exposes the same array.
type EpicOutput ¶
type EpicOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
GroupID int64 `json:"group_id"`
ParentID int64 `json:"parent_id"`
Title string `json:"title"`
Description string `json:"description"`
State string `json:"state"`
Confidential bool `json:"confidential"`
WebURL string `json:"web_url"`
URL string `json:"url"`
Author *IssueUserOutput `json:"author,omitempty"`
Labels []string `json:"labels,omitempty"`
Upvotes int64 `json:"upvotes,omitempty"`
Downvotes int64 `json:"downvotes,omitempty"`
UserNotesCount int64 `json:"user_notes_count,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"`
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"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ClosedAt string `json:"closed_at,omitempty"`
}
EpicOutput mirrors gl.Epic (the nested epic object surfaced on issue.epic). The nested EpicAuthorOutput is the same IssueUserOutput shape used elsewhere in the issues package.
func NewEpicOutput ¶
func NewEpicOutput(e *gl.Epic) *EpicOutput
NewEpicOutput converts a gl.Epic pointer into the canonical epic object, returning nil for a nil source.
type EvidenceOutput ¶
type EvidenceOutput struct {
SHA string `json:"sha,omitempty"`
Filepath string `json:"filepath,omitempty"`
CollectedAt string `json:"collected_at,omitempty"`
}
EvidenceOutput mirrors gl.ReleaseEvidence (the per-evidence object surfaced on the release's evidences array).
func NewEvidenceOutputs ¶
func NewEvidenceOutputs(evs []*gl.ReleaseEvidence) []*EvidenceOutput
NewEvidenceOutputs converts a slice of gl.ReleaseEvidence, skipping nil elements and returning nil for an empty input.
type FeatureDefinitionExtra ¶
type FeatureDefinitionExtra struct {
FeatureIssueURL string `json:"feature_issue_url"`
IntendedToRolloutBy string `json:"intended_to_rollout_by"`
}
FeatureDefinitionExtra is where a feature flag's definition points a reader: the issue that tracks it and the milestone it is meant to roll out by. The definition entity exposes both under no condition.
func CapturedFeatureDefinitions ¶
func CapturedFeatureDefinitions(capture *gitlabclient.ResponseCapture, decoded int) ([]FeatureDefinitionExtra, error)
CapturedFeatureDefinitions reads them off the captured answer to a list of definitions, one extra per definition in order, the count held to what the SDK decoded.
type FeatureExtra ¶
type FeatureExtra struct {
Definition FeatureDefinitionExtra `json:"definition"`
}
FeatureExtra reaches the same two keys where a feature carries its definition under a key of its own, which is the shape the feature list answers with.
func CapturedFeature ¶
func CapturedFeature(capture *gitlabclient.ResponseCapture) (FeatureExtra, error)
CapturedFeature reads them off the captured answer to a request that set one feature flag.
func CapturedFeatures ¶
func CapturedFeatures(capture *gitlabclient.ResponseCapture, decoded int) ([]FeatureExtra, error)
CapturedFeatures reads them off the captured answer to a list of features, one extra per feature in order, the count held to what the SDK decoded.
type FeatureFlagUserListExtra ¶
type FeatureFlagUserListExtra struct {
Path string `json:"path"`
EditPath string `json:"edit_path"`
}
FeatureFlagUserListExtra is where a user list lives in GitLab's own web interface, both exposed under no condition.
func CapturedFeatureFlagUserList ¶
func CapturedFeatureFlagUserList(capture *gitlabclient.ResponseCapture) (FeatureFlagUserListExtra, error)
CapturedFeatureFlagUserList reads them off the captured answer to a request for one user list.
func CapturedFeatureFlagUserLists ¶
func CapturedFeatureFlagUserLists(capture *gitlabclient.ResponseCapture, decoded int) ([]FeatureFlagUserListExtra, error)
CapturedFeatureFlagUserLists reads the same off a list answer, one extra per user list in order, the count held to what the SDK decoded.
type FormatResultFunc ¶
type FormatResultFunc func(any) *mcp.CallToolResult
FormatResultFunc converts an action result into an MCP call tool result.
func WithResolvedNextSteps ¶ added in v3.3.0
func WithResolvedNextSteps(format FormatResultFunc, resolve ActionCallResolver) FormatResultFunc
WithResolvedNextSteps returns format with every canonical ID the next steps of its result name rewritten by resolve: "Use action 'issue.update' to change this issue" becomes "Use gitlab_issue with action 'update' to ..." on the meta surface and "Use gitlab_issue_update to ..." on the individual one (issue 1325), and an ID a step quotes in backticks becomes the same call. Because FinishToolResult reads the next_steps of the structured output from that text, both carry the rewritten call. A step naming an action the surface does not serve is left as it was written, and a nil resolve returns format unchanged, which is what the dynamic surface, whose tool takes the canonical ID, registers.
type GraphQLClusterAgentRef ¶ added in v3.1.0
type GraphQLClusterAgentRef struct {
Name string `json:"name"`
}
GraphQLClusterAgentRef names the cluster agent that performed a cluster image scan. The agent's own fields are the cluster agent actions' to publish.
type GraphQLCursor ¶
GraphQLCursor is one resolved cursor request. At most one of First and Last is set, which is what makes the direction the caller asked for the direction they get.
type GraphQLCursorPaginationInput ¶
type GraphQLCursorPaginationInput struct {
GraphQLPaginationInput
Last *int `` /* 141-byte string literal not displayed */
Before string `` /* 172-byte string literal not displayed */
}
GraphQLCursorPaginationInput adds backward pagination to GraphQLPaginationInput, for the connections GitLab lets a caller walk in both directions. Embed it only when the operation declares all four variables: GraphQLCursorPaginationInput.Variables refuses a document that does not.
func (GraphQLCursorPaginationInput) Resolve ¶
func (p GraphQLCursorPaginationInput) Resolve() (GraphQLCursor, error)
Resolve turns the four requested parameters into the pair GitLab accepts.
The cursor picks the direction and the count only sizes the page. A before cursor sent beside first is not backward pagination on any GitLab connection: the keyset ones answer it with the head of the list and the array-backed ones intersect first with last, so a caller following start_cursor back through a list would loop on the page they were already on. Sending both counts is refused rather than reinterpreted, because GitLab refuses it too and there is no reading of the pair that is not a guess.
func (GraphQLCursorPaginationInput) Variables ¶
func (p GraphQLCursorPaginationInput) Variables(document string) (map[string]any, error)
Variables returns the variable map for document, refusing a document that declares fewer variables than this input can send. See GraphQLPaginationInput.Variables for why the document is a parameter.
type GraphQLError ¶
type GraphQLError struct {
Message string `json:"message"`
}
GraphQLError is one top-level GraphQL error returned in a successful HTTP response body.
type GraphQLForwardPaginationOutput ¶
type GraphQLForwardPaginationOutput struct {
HasNextPage bool `json:"has_next_page"`
EndCursor string `json:"end_cursor,omitempty"`
}
GraphQLForwardPaginationOutput is the pagination metadata of a connection that only pages forward: whether another page follows, and the cursor that asks for it.
It exists so that a tool taking GraphQLPaginationInput cannot report a previous page. GitLab's keyset connections fill in hasPreviousPage and startCursor from their second page on whether or not the field accepts before, so a forward-only tool reporting the whole of pageInfo hands a model a cursor and no parameter to spend it on, which reads as a capability the tool withdrew rather than as the dead end it is.
func ForwardPageInfoToOutput ¶
func ForwardPageInfoToOutput(pi GraphQLRawForwardPageInfo) GraphQLForwardPaginationOutput
ForwardPageInfoToOutput converts the forward half of pageInfo, as a forward-only document selects it, to the forward-only output struct.
type GraphQLNoteMutation ¶
type GraphQLNoteMutation struct {
// Op is the operation name used in wrapped errors (e.g. "epicNoteCreate").
Op string
// Hint is the corrective hint attached to transport errors.
Hint string
// PayloadKey is the mutation payload key in the response data object
// ("createNote" or "updateNote").
PayloadKey string
// Query is the GraphQL mutation document. It selects
// [GraphQLQuickActionsStatusSelection] beside the note.
Query string
// Variables holds the mutation variables.
Variables map[string]any
// Authority is the fine-grained authority of the client that sends the
// mutation, nil for any other credential. Only an updateNote answered
// with neither note nor status reads it: see [ExecGraphQLNoteMutation].
Authority *finegrained.Authority
}
GraphQLNoteMutation describes one work item note mutation call (createNote/updateNote) so the shared executor can apply the error conventions used by every GraphQL note domain (epic notes, epic discussions): transport errors are wrapped with the operation name and corrective hint, a refusal GitLab answers at the top level is reported as that refusal, the first mutation payload error becomes "op: message", and a missing note node is reported for what GitLab means by it.
type GraphQLNoteMutationResult ¶ added in v3.1.0
type GraphQLNoteMutationResult[N any] struct { // Note is the note the mutation created or edited. It is nil when the // body held only quick actions: createNote then runs them and keeps no // note, and QuickActions says what they did. Note *N // QuickActions is GitLab's account of the quick actions the body // carried, nil when it carried none. QuickActions *QuickActionsStatusOutput }
GraphQLNoteMutationResult is what a note mutation answered with.
func ExecGraphQLNoteMutation ¶
func ExecGraphQLNoteMutation[N any](ctx context.Context, gql gl.GraphQLInterface, m GraphQLNoteMutation) (GraphQLNoteMutationResult[N], error)
ExecGraphQLNoteMutation runs a work item note mutation and returns the mutated note node decoded as N with the quick actions status beside it, applying the shared error conventions described on GraphQLNoteMutation.
A payload without a note is not one condition. A body holding only quick actions makes createNote run them and keep no note, and it says so in the status, which is a result and not a failure. The same body makes updateNote delete the note it was asked to edit and answer with neither note nor status (Mutations::Notes::Update::Base in GitLab), which is reported as an error that says what happened, since the call cannot answer with the edited note it promised and a caller told only that no note came back would read the commands as not applied. A fine-grained session gets that same payload for an edit GitLab made: Work Item: Update grants update_note and not read_note, the edited note is checked against the token only after the edit ran, and the status declares no fine-grained permission, so such a session never sees one. It is told the edit probably committed, with the deletion as the other reading, since both ran (issue 1103). A refusal GitLab answers at the top level leaves the payload null, and is checked first so that neither reading is ever given to a refusal.
A payload error is a refusal, with one exception: a note GitLab kept whose every error is a quick action failure the status reports too. createNote saves the note before it applies the commands and adds a command's failure to the note's errors afterwards, so reporting that as a refusal would hide a note that exists and invite the caller to post it twice. A body of commands alone whose update failed answers the same error and status with no note, and stays a refusal: GitLab ran nothing and kept nothing.
type GraphQLPageInfo ¶
type GraphQLPageInfo struct {
HasNextPage bool `json:"has_next_page"`
HasPreviousPage bool `json:"has_previous_page"`
EndCursor string `json:"end_cursor,omitempty"`
StartCursor string `json:"start_cursor,omitempty"`
}
GraphQLPageInfo holds cursor-based pagination metadata returned by GraphQL connection responses. It maps directly to GitLab's PageInfo type.
type GraphQLPaginationInput ¶
type GraphQLPaginationInput struct {
First *int `json:"first,omitempty" jsonschema:"Number of items to return (default 20, max 100)"`
After string `json:"after,omitempty" jsonschema:"Cursor for forward pagination (from previous response end_cursor)"`
}
GraphQLPaginationInput holds forward cursor pagination parameters for a GraphQL list query: first and after, and nothing else.
Forward only is the safe default rather than a limitation of the helper. A connection paginates backwards only where GitLab's own field accepts before and last, and several fields this server queries refuse them: Project's branchRules and the work item notes widget's discussions both answer those arguments with argumentNotAccepted. What the two do with the backward half of pageInfo differs, which is why the promise is withdrawn in the output as well as in the input: branchRules reports no previous page and no start cursor, while the discussions widget is keyset-paginated and reports both from its second page on, offering a cursor no argument could ever spend. A domain whose field does accept the pair embeds GraphQLCursorPaginationInput, which is the only type that can put it on the wire, and reports the whole of pageInfo through GraphQLPaginationOutput; a forward-only domain reports GraphQLForwardPaginationOutput instead.
func (GraphQLPaginationInput) EffectiveFirst ¶
func (p GraphQLPaginationInput) EffectiveFirst() int
EffectiveFirst returns the requested page size, clamped to [1, GraphQLMaxFirst] with GraphQLDefaultFirst as fallback.
func (GraphQLPaginationInput) Variables ¶
func (p GraphQLPaginationInput) Variables(document string) (map[string]any, error)
Variables returns the variable map for document, which must be the operation the caller is about to execute.
The document is a parameter because a variable an operation does not declare is ignored rather than rejected: a page request carrying one is answered with somebody else's page and no error. Taking the document here is what makes that impossible, since the only way to obtain the map is to name the operation it is going to.
type GraphQLPaginationOutput ¶
type GraphQLPaginationOutput struct {
HasNextPage bool `json:"has_next_page"`
HasPreviousPage bool `json:"has_previous_page"`
EndCursor string `json:"end_cursor,omitempty"`
StartCursor string `json:"start_cursor,omitempty"`
}
GraphQLPaginationOutput holds pagination metadata for GraphQL list tool responses, presented in a consistent format for LLM consumers.
func PageInfoToOutput ¶
func PageInfoToOutput(pi GraphQLRawPageInfo) GraphQLPaginationOutput
PageInfoToOutput converts a raw GraphQL PageInfo response struct (with camelCase JSON keys from the API) to the snake_case output struct.
type GraphQLRawForwardPageInfo ¶
type GraphQLRawForwardPageInfo struct {
HasNextPage bool `json:"hasNextPage"`
EndCursor string `json:"endCursor"`
}
GraphQLRawForwardPageInfo is the half of pageInfo a forward-only document selects. A forward-only tool decodes into this rather than into GraphQLRawPageInfo, so its decoder declares exactly what its document asks for: a decoder carrying the backward half beside a document that never selects it holds two fields that are always empty, which is what make check-graphql-shapes refuses.
type GraphQLRawPageInfo ¶
type GraphQLRawPageInfo struct {
HasNextPage bool `json:"hasNextPage"`
HasPreviousPage bool `json:"hasPreviousPage"`
EndCursor string `json:"endCursor"`
StartCursor string `json:"startCursor"`
}
GraphQLRawPageInfo matches the camelCase JSON shape returned by the GitLab GraphQL API before conversion to our snake_case output.
type GraphQLUserCoreRef ¶ added in v3.1.0
type GraphQLUserCoreRef struct {
Username string `json:"username"`
Name string `json:"name"`
WebURL string `json:"webUrl"`
}
GraphQLUserCoreRef decodes the UserCore reference UserCoreRefSelection selects.
func (*GraphQLUserCoreRef) Output ¶ added in v3.1.0
func (u *GraphQLUserCoreRef) Output() *UserCoreRefOutput
Output converts a decoded person, and answers nil for nobody.
type GraphQLVulnerabilityLink ¶ added in v3.1.0
GraphQLVulnerabilityLink decodes the VulnerabilityLink VulnerabilityLinkSelection selects.
type GraphQLVulnerabilityLocation ¶ added in v3.1.0
type GraphQLVulnerabilityLocation struct {
File string `json:"file"`
Path string `json:"path"`
Image string `json:"image"`
StartLine string `json:"startLine"`
EndLine string `json:"endLine"`
BlobPath string `json:"blobPath"`
VulnerableClass string `json:"vulnerableClass"`
VulnerableMethod string `json:"vulnerableMethod"`
CrashAddress string `json:"crashAddress"`
CrashType string `json:"crashType"`
StacktraceSnippet string `json:"stacktraceSnippet"`
Hostname string `json:"hostname"`
RequestMethod string `json:"requestMethod"`
Param string `json:"param"`
OperatingSystem string `json:"operatingSystem"`
ContainerRepositoryURL string `json:"containerRepositoryUrl"`
Description string `json:"description"`
Dependency *GraphQLVulnerableDependency `json:"dependency"`
KubernetesResource *GraphQLVulnerableKubernetesResource `json:"kubernetesResource"`
}
GraphQLVulnerabilityLocation decodes the VulnerabilityLocation union. The members spell overlapping fields alike (file, startLine, dependency), so one flat struct holds whichever member GitLab sent, and a field no member of that kind carries is simply left empty.
GitLab types startLine and endLine as String on every member, and a live instance sends them quoted; an int here made the whole response fail to decode.
func (*GraphQLVulnerabilityLocation) Output ¶ added in v3.1.0
func (l *GraphQLVulnerabilityLocation) Output() *VulnerabilityLocationOutput
Output converts a decoded location, and answers nil for a location GitLab did not send. A location it did send is converted whatever it carries, which is how both packages read one before.
type GraphQLVulnerabilityTokenStatus ¶ added in v3.1.0
type GraphQLVulnerabilityTokenStatus struct {
Status string `json:"status"`
LastVerifiedAt string `json:"lastVerifiedAt"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
GraphQLVulnerabilityTokenStatus decodes the VulnerabilityFindingTokenStatus VulnerabilityTokenStatusSelection selects.
func (*GraphQLVulnerabilityTokenStatus) Output ¶ added in v3.1.0
func (s *GraphQLVulnerabilityTokenStatus) Output() *VulnerabilityTokenStatusOutput
Output converts a decoded token status, and answers nil for none, which is what GitLab sends for every finding that exposed no token.
type GraphQLVulnerableDependency ¶ added in v3.1.0
type GraphQLVulnerableDependency struct {
Version string `json:"version"`
Package *GraphQLVulnerablePackage `json:"package"`
}
GraphQLVulnerableDependency decodes the package a dependency or container location names, and the version of it that is vulnerable.
type GraphQLVulnerableKubernetesResource ¶ added in v3.1.0
type GraphQLVulnerableKubernetesResource struct {
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
ContainerName string `json:"containerName"`
ClusterID string `json:"clusterId"`
Agent *GraphQLClusterAgentRef `json:"agent"`
}
GraphQLVulnerableKubernetesResource decodes the workload a cluster image scan found running the vulnerable image.
type GraphQLVulnerablePackage ¶ added in v3.1.0
GraphQLVulnerablePackage decodes the package half of a vulnerable dependency.
type GroupDetailExtra ¶ added in v3.1.0
type GroupDetailExtra struct {
GroupExtra
RequireSHAForMerge *bool `json:"require_sha_for_merge"`
LockRequireSHAForMerge *bool `json:"lock_require_sha_for_merge"`
StepUpAuthRequiredOAuthProvider string `json:"step_up_auth_required_oauth_provider"`
ServiceAccessTokensExpirationEnforced *bool `json:"service_access_tokens_expiration_enforced"`
AISettings *AISettingsOutput `json:"ai_settings"`
UniqueProjectDownloadLimit *int64 `json:"unique_project_download_limit"`
UniqueProjectDownloadLimitIntervalSecs *int64 `json:"unique_project_download_limit_interval_in_seconds"`
UniqueProjectDownloadLimitAllowlist []string `json:"unique_project_download_limit_allowlist"`
UniqueProjectDownloadLimitAlertlist []int64 `json:"unique_project_download_limit_alertlist"`
AutoBanUserOnExcessiveProjectsDownload *bool `json:"auto_ban_user_on_excessive_projects_download"`
}
GroupDetailExtra is GroupExtra plus what only lib/api/entities/group_detail.rb adds, which is what a request for one group renders. A list row never carries these, which is why they are a type of their own rather than more fields on the one above.
The two SHA settings are exposed unconditionally from GitLab 19.5: whether the group's merge requests need a valid commit SHA on the merge route, and whether that setting is enforced on every descendant group (groups.md). They are pointers so an older instance's answer, which carries neither, reads as absent rather than off.
func CapturedGroup ¶ added in v3.1.0
func CapturedGroup(capture *gitlabclient.ResponseCapture) (GroupDetailExtra, error)
CapturedGroup reads, off the captured answer to a request for one group, everything only that shape carries.
type GroupExtra ¶ added in v3.1.0
type GroupExtra struct {
ShowDiffPreviewInEmail bool `json:"show_diff_preview_in_email"`
ResourceAccessTokenNotifyInherited *bool `json:"resource_access_token_notify_inherited"`
LockResourceAccessTokenNotifyInherited bool `json:"lock_resource_access_token_notify_inherited"`
DuoCoreFeaturesEnabled *bool `json:"duo_core_features_enabled"`
AutoDuoCodeReviewEnabled *bool `json:"auto_duo_code_review_enabled"`
DuoCodeReviewDecisionsEnabled *bool `json:"duo_code_review_decisions_enabled"`
WebBasedCommitSigningEnabled *bool `json:"web_based_commit_signing_enabled"`
AllowPersonalSnippets *bool `json:"allow_personal_snippets"`
BuiltInProjectTemplatesEnabled *bool `json:"built_in_project_templates_enabled"`
LockBuiltInProjectTemplatesEnabled *bool `json:"lock_built_in_project_templates_enabled"`
DuoNamespaceAccessRules []DuoNamespaceAccessRuleOutput `json:"duo_namespace_access_rules"`
}
GroupExtra is what lib/api/entities/group.rb and its EE prepend send on a group that client-go's Group does not carry, read from the captured response (ADR-0021). Every route that renders a group renders this entity, so these are the fields a list row carries too.
The pointers are GitLab's own conditions rather than this server's caution: each is exposed behind a licensed feature, a permission or a feature flag, so an absent key and a false one are different answers. The three plain values are exposed unconditionally on the CE entity.
func CapturedGroups ¶ added in v3.1.0
func CapturedGroups(capture *gitlabclient.ResponseCapture, decoded int) ([]GroupExtra, error)
CapturedGroups reads the list-shaped fields off a list answer, one extra per group in order, the count held to what the SDK decoded.
type GroupHookExtra ¶ added in v3.1.0
type GroupHookExtra struct {
RepositoryUpdateEvents bool `json:"repository_update_events"`
DuoFlowCallbackEnabled *bool `json:"duo_flow_callback_enabled"`
}
GroupHookExtra is what ee/lib/api/entities/group_hook.rb sends that client-go's GroupHook does not model. The SDK carries repository_update_events on the project hook and on the system hook and not on this one; the entity sends it on every group hook, so a plain value is the honest shape. duo_flow_callback_enabled is sent on every group hook from GitLab 19.4 (group_webhooks.md), so it is a pointer: an older instance sends no key, and that is not a hook with the setting off.
func CapturedGroupHook ¶ added in v3.1.0
func CapturedGroupHook(capture *gitlabclient.ResponseCapture) (GroupHookExtra, error)
CapturedGroupHook reads that key off the answer to a request for one hook.
func CapturedGroupHooks ¶ added in v3.1.0
func CapturedGroupHooks(capture *gitlabclient.ResponseCapture, decoded int) ([]GroupHookExtra, error)
CapturedGroupHooks reads the same off a list answer, one extra per hook in order.
type HintSetter ¶
type HintSetter interface {
// SetNextSteps stores the extracted next-step hints on the output struct.
SetNextSteps(hints []string)
}
HintSetter is implemented by any Output struct that embeds HintableOutput. PopulateHints uses this interface to set extracted hints on the output.
type HintableOutput ¶
type HintableOutput struct {
NextSteps []string `json:"next_steps,omitempty"`
}
HintableOutput is an embeddable struct that adds a next_steps field to any Output type. Embed it as the FIRST field of an Output struct so that next_steps appears first in the serialized JSON, giving LLMs immediate guidance before reading the rest of the payload.
type Output struct {
toolutil.HintableOutput
Name string `json:"name"`
}
func (*HintableOutput) SetNextSteps ¶
func (h *HintableOutput) SetNextSteps(hints []string)
SetNextSteps stores the given hints in the NextSteps field.
type ImpersonationTokenExtra ¶
type ImpersonationTokenExtra struct {
TokenExtra
Impersonation bool `json:"impersonation"`
Description string `json:"description"`
UserID int64 `json:"user_id"`
}
ImpersonationTokenExtra is what lib/api/entities/impersonation_token.rb sends beyond TokenExtra that client-go's ImpersonationToken does not carry. Its impersonation flag is its own; description and user_id come from the personal access token entity it inherits, which the SDK models on PersonalAccessToken and not on ImpersonationToken.
func CapturedImpersonationToken ¶
func CapturedImpersonationToken(capture *gitlabclient.ResponseCapture) (ImpersonationTokenExtra, error)
CapturedImpersonationToken reads, off the captured answer to a request for one impersonation token, the fields client-go's ImpersonationToken does not model.
func CapturedImpersonationTokens ¶
func CapturedImpersonationTokens(capture *gitlabclient.ResponseCapture, decoded int) ([]ImpersonationTokenExtra, error)
CapturedImpersonationTokens reads the same off a list answer, one extra per token in order, the count held to what the SDK decoded.
type IndividualToolAnnotationOverrides ¶
type IndividualToolAnnotationOverrides struct {
ReadOnly *bool
Destructive *bool
Idempotent *bool
OpenWorld *bool
}
IndividualToolAnnotationOverrides carries compatibility overrides for historical individual-tool annotations that intentionally differ from the canonical action semantics.
func (IndividualToolAnnotationOverrides) NarrowingOnly ¶
func (o IndividualToolAnnotationOverrides) NarrowingOnly(readOnly, destructive, idempotent bool) IndividualToolAnnotationOverrides
NarrowingOnly returns the overrides with every claim removed that would make a mutating, destructive or non-repeatable action look safer than the action itself is.
readOnlyHint and idempotentHint both say "this call is safe to make, and safe to repeat", and both are consulted by things that act on them: --read-only removes what is not read-only, safe mode previews what is not, and a gateway may auto-allow readOnlyHint:true without asking anyone. An override that raises either one is therefore not a presentation choice, it is a silent widening of the operator's own controls.
destructiveHint false says the opposite of what the server does with a destructive action. MCP defines it as "the tool performs only additive updates", and the individual handler demands confirm for exactly the actions whose route is destructive, so a client deciding from the hint whether to ask its user would send a merge, a ban or a forced mirror push without asking and meet the server's own confirmation instead. Twelve actions declared it on the individual surface alone, on the reading that a reversible action is not terminal, until issue 1335.
Overrides that narrow are untouched, and are the reason this type exists: an action that is not destructive may declare destructiveHint true, and an update that is not repeatable may declare idempotentHint false.
The case this was written for is system_hook_test, a mutating create that declared readOnlyHint true on the individual surface alone because the test event changes nothing on the instance. It changes no GitLab state, but it makes GitLab deliver an event to the hook's configured URL, so it is not a read, and one action classified read-only on one surface and mutating on the other two cannot be right on more than one of them.
type IndividualToolProjectionOptions ¶
IndividualToolProjectionOptions contains surface-level metadata that is shared by an individual tool projection but not owned by the action spec itself.
type IndividualToolSpec ¶
type IndividualToolSpec struct {
Name string
Title string
Description string
AnnotationOverrides IndividualToolAnnotationOverrides
}
IndividualToolSpec carries compatibility metadata for the individual-tool surface.
func CloneIndividualToolSpec ¶
func CloneIndividualToolSpec(spec IndividualToolSpec) IndividualToolSpec
CloneIndividualToolSpec returns a defensive copy of individual-tool metadata.
type InputSchemaOverride ¶
InputSchemaOverride describes a deterministic JSON Schema patch for an action input schema. PropertyPath is a dot-separated input property path; an empty path applies Values at the schema root. Array properties automatically traverse through their items schema for nested paths.
func FilterOverridesForSchema ¶
func FilterOverridesForSchema(schema map[string]any, overrides []InputSchemaOverride) []InputSchemaOverride
FilterOverridesForSchema returns the subset of overrides whose property path still resolves against schema (an empty path targets the root and is always kept). It is used after tier pruning removes higher-tier properties: an override that targeted a now-removed field (e.g. an ultimate-only parameter pruned for a Free instance) is dropped so it does not fail [validateInputSchemaOverrides] during catalog assembly. The enum/patch values were already applied to the property before pruning, so dropping the override here only keeps the override list consistent with the pruned schema.
func SchemaAnyOfRequired ¶
func SchemaAnyOfRequired(propertyNames ...string) InputSchemaOverride
SchemaAnyOfRequired returns a root override that requires at least one of the supplied property names to be present.
func SchemaApproverIDsOverride ¶
func SchemaApproverIDsOverride(propertyPath string) InputSchemaOverride
SchemaApproverIDsOverride widens the array item type of an ApproverIDsFilter parameter so both numeric user IDs and the "Any"/"None" literals pass input validation. Without it the reflected schema advertises strings only, which would reject the numeric IDs every existing caller sends.
func SchemaEnumOverride ¶
func SchemaEnumOverride(propertyPath string, values ...string) InputSchemaOverride
SchemaEnumOverride returns an input-schema override that constrains the string parameter at propertyPath to a fixed set of enum values. It collapses the repeated `SchemaPropertyOverride(path, map[string]any{"enum": []any{...}})` block used across domains into a single call so the fixed-vocabulary constraint is expressed once per action without structural duplication.
func SchemaFormatOverride ¶
func SchemaFormatOverride(propertyPath, format string) InputSchemaOverride
SchemaFormatOverride returns an input-schema override that sets the JSON Schema `format` of the string parameter at propertyPath (e.g. "date" for a YYYY-MM-DD field or "uri" for a URL field).
func SchemaPropertyOverride ¶
func SchemaPropertyOverride(propertyPath string, values map[string]any) InputSchemaOverride
SchemaPropertyOverride returns an input-schema override for a property path.
func SchemaRootOverride ¶
func SchemaRootOverride(values map[string]any) InputSchemaOverride
SchemaRootOverride returns an input-schema override applied at the schema root.
type InstanceUserExtra ¶
type InstanceUserExtra struct {
UserExtra
BioHTML string `json:"bio_html"`
EnterpriseGroupID *int64 `json:"enterprise_group_id"`
EnterpriseGroupAssociatedAt *time.Time `json:"enterprise_group_associated_at"`
ProvisionedByGroupID *int64 `json:"provisioned_by_group_id"`
ProvisionedByProjectID *int64 `json:"provisioned_by_project_id"`
UnconfirmedEmail string `json:"unconfirmed_email"`
}
InstanceUserExtra is UserExtra plus the six keys only the instance-wide user routes ever send, which is what separates internal/tools/users from the three group-scoped packages sharing the smaller shape: those serve GET /groups/:id/{enterprise_users,provisioned_users,saml_users}, and GitLab presents every one of them with UserPublic.
bio_html comes from lib/api/entities/users/bio_html.rb, which only UserProfile includes, so of the routes here it is on GET /users/:id alone. The three license-gated keys come from ee/lib/ee/api/entities/user_with_admin.rb, which only POST /users and PUT /users/:id present. provisioned_by_project_id comes from lib/api/entities/user_with_admin.rb itself, with no license, and so reaches an administrator on every route presenting that entity or one inheriting it; users.md dates it to GitLab 19.3. unconfirmed_email is not a user key at all: lib/api/entities/service_account.rb sends it, on the six-key object POST /service_accounts answers with, when the account has an address change waiting to be confirmed.
The three identifiers are pointers for the reason the counts are: a license that does not carry the feature, or a release that predates the key, sends no key, and group 0 is not that. provisioned_by_project_id is also sent as null for a user no project provisioned, which reads the same way.
func CapturedInstanceUser ¶
func CapturedInstanceUser(capture *gitlabclient.ResponseCapture) (InstanceUserExtra, error)
CapturedInstanceUser reads, off the captured answer to a request for one user on an instance-wide route, everything CapturedUser reads and the six keys beside it.
func CapturedInstanceUsers ¶
func CapturedInstanceUsers(capture *gitlabclient.ResponseCapture, decoded int) ([]InstanceUserExtra, error)
CapturedInstanceUsers reads the same off a list answer, one extra per user in order, the count held to what the SDK decoded.
type InvitationExtra ¶
type InvitationExtra struct {
InviteToken string `json:"invite_token"`
}
InvitationExtra is the token lib/api/entities/invitation.rb exposes under no condition and client-go's PendingInvite does not model. It is what the invitation URL a recipient follows is built from, so a caller allowed to list a group's pending invitations can reissue one without the mail.
func CapturedPendingInvites ¶
func CapturedPendingInvites(capture *gitlabclient.ResponseCapture, decoded int) ([]InvitationExtra, error)
CapturedPendingInvites reads it off the captured answer to a list of pending invitations, one extra per invitation in order, the count held to what the SDK decoded.
type IssueBasicExtra ¶ added in v3.1.0
type IssueBasicExtra struct {
BlockingIssuesCount *int64 `json:"blocking_issues_count"`
StartDate string `json:"start_date"`
// Type is the issue type upcased. GitLab exposes the same attribute twice,
// as issue_type through the plain expose and as type through one carrying
// format_with: :upcase, so this is read rather than derived here: the
// spelling is GitLab's decision, not ours.
Type string `json:"type"`
}
IssueBasicExtra is what lib/api/entities/issue_basic.rb sends on an issue that client-go's Issue does not carry, read from the captured response (ADR-0021). Every route that renders an issue renders this entity, so these three are on a search hit and on a merge request's closing issue too.
The count is a pointer because ee/lib/ee/api/entities/issue_basic.rb gates it on the blocked-issues licensed feature, and zero blockers is not the same answer as an instance that does not count them.
func CapturedIssueBasic ¶ added in v3.1.0
func CapturedIssueBasic(capture *gitlabclient.ResponseCapture) (IssueBasicExtra, error)
CapturedIssueBasic reads, off the captured answer to a request that renders the basic issue entity, the fields client-go's Issue does not model.
func CapturedIssueBasics ¶ added in v3.1.0
func CapturedIssueBasics(capture *gitlabclient.ResponseCapture, decoded int) ([]IssueBasicExtra, error)
CapturedIssueBasics reads the same off a list answer, one extra per issue in order, the count held to what the SDK decoded.
type IssueExtra ¶ added in v3.1.0
type IssueExtra struct {
IssueBasicExtra
EpicIID *int64 `json:"epic_iid"`
HasTasks *bool `json:"has_tasks"`
Imported *bool `json:"imported"`
ImportedFrom string `json:"imported_from"`
Severity string `json:"severity"`
TaskStatus string `json:"task_status"`
}
IssueExtra is IssueBasicExtra plus what only lib/api/entities/issue.rb adds, which is what the fifteen routes of the issues API render. A search hit and a merge request's closing issue never carry these.
has_tasks and imported are pointers for the reason the count above is: the entity sends them on every issue it renders, and a nil here means the body came from somewhere that renders less, which is a different answer from false.
func CapturedIssue ¶ added in v3.1.0
func CapturedIssue(capture *gitlabclient.ResponseCapture) (IssueExtra, error)
CapturedIssue reads everything CapturedIssueBasic reads and the six keys the full issue entity adds.
func CapturedIssues ¶ added in v3.1.0
func CapturedIssues(capture *gitlabclient.ResponseCapture, decoded int) ([]IssueExtra, error)
CapturedIssues reads the same off a list answer, one extra per issue.
type IssueLinksOutput ¶
type IssueLinksOutput struct {
Self string `json:"self"`
Notes string `json:"notes"`
AwardEmoji string `json:"award_emoji"`
Project string `json:"project"`
}
IssueLinksOutput mirrors gl.Links (the _links object on an issue). Field set is intentionally smaller than the release _links object in toolutil.LinksOutput — issues expose only self / notes / award_emoji / project URLs.
func NewIssueLinksOutput ¶
func NewIssueLinksOutput(l *gl.IssueLinks) *IssueLinksOutput
NewIssueLinksOutput converts a gl.IssueLinks value into the canonical issue-links object, returning nil for a nil source.
type IssueUserOutput ¶
type IssueUserOutput struct {
ID int64 `json:"id"`
State string `json:"state"`
WebURL string `json:"web_url"`
Name string `json:"name"`
AvatarURL string `json:"avatar_url"`
Username string `json:"username"`
}
IssueUserOutput is the documented 6-field user object that appears inside issue resources (author / assignees[] / closed_by) and inside the nested epic.author pointer. The fields mirror the per-resource JSON shape documented for issues (the full BasicUser shape omits `state` on this resource, so IssueUserOutput intentionally omits it too — see the package-local shapes.go history for the audit notes).
func NewIssueUserOutputFromBasicUser ¶
func NewIssueUserOutputFromBasicUser(u gl.BasicUser) IssueUserOutput
NewIssueUserOutputFromBasicUser converts a gl.BasicUser into the canonical issue-user shape, populating only the documented fields.
func NewIssueUserOutputFromEpicAuthor ¶
func NewIssueUserOutputFromEpicAuthor(u *gl.EpicAuthor) *IssueUserOutput
NewIssueUserOutputFromEpicAuthor converts a *gl.EpicAuthor pointer into the canonical issue-user shape. gl.EpicAuthor has the same 6 field layout as gl.BasicUser so the conversion is structural — the type distinction in the SDK is purely for documentation.
func NewIssueUserOutputFromIssueAssignee ¶
func NewIssueUserOutputFromIssueAssignee(a *gl.IssueAssignee) *IssueUserOutput
NewIssueUserOutputFromIssueAssignee converts a single *gl.IssueAssignee pointer into the canonical issue-user shape (used for the deprecated singular Assignee field on gl.Issue).
func NewIssueUserOutputFromIssueAuthor ¶
func NewIssueUserOutputFromIssueAuthor(u *gl.IssueAuthor) *IssueUserOutput
NewIssueUserOutputFromIssueAuthor converts a *gl.IssueAuthor pointer into the canonical issue-user shape. gl.IssueAuthor is the dedicated author type on gl.Issue (distinct from gl.BasicUser so the documented "issues have an author" reference is preserved) — fields are the same 6 we surface elsewhere.
func NewIssueUserOutputFromIssueCloser ¶
func NewIssueUserOutputFromIssueCloser(u *gl.IssueCloser) *IssueUserOutput
NewIssueUserOutputFromIssueCloser converts a *gl.IssueCloser pointer into the canonical issue-user shape. Same 6-field layout as the other issue-user types.
func NewIssueUserOutputFromPointer ¶
func NewIssueUserOutputFromPointer(u *gl.BasicUser) *IssueUserOutput
NewIssueUserOutputFromPointer converts a *gl.BasicUser, returning nil for a nil source so call sites can pass through SDK pointers without an extra nil check.
func NewIssueUserOutputsFromIssueAssignees ¶
func NewIssueUserOutputsFromIssueAssignees(as []*gl.IssueAssignee) []*IssueUserOutput
NewIssueUserOutputsFromIssueAssignees converts a slice of gl.IssueAssignee, skipping nil entries and returning nil for an empty / all-nil input.
type IterationOutput ¶
type IterationOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
Sequence int64 `json:"sequence"`
GroupID int64 `json:"group_id"`
Title string `json:"title"`
Description string `json:"description"`
State int64 `json:"state"`
WebURL string `json:"web_url"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
StartDate string `json:"start_date,omitempty"`
DueDate string `json:"due_date,omitempty"`
}
IterationOutput mirrors gl.Iteration (the iteration object surfaced on issue.iteration). State is an int per the SDK type (an enum of 0=upcoming, 1=current, 2=closed).
func NewIterationOutput ¶
func NewIterationOutput(it *gl.Iteration) *IterationOutput
NewIterationOutput converts a gl.Iteration pointer into the canonical iteration object, returning nil for a nil source.
func NewIterationOutputFromGroupIteration ¶
func NewIterationOutputFromGroupIteration(it *gl.GroupIteration) *IterationOutput
NewIterationOutputFromGroupIteration converts a gl.GroupIteration pointer (the type surfaced on gl.Issue.Iteration) into the canonical iteration object. gl.GroupIteration has the same 11 fields as gl.Iteration but with a separate type identity for documentation.
func NewIterationOutputFromProjectIteration ¶
func NewIterationOutputFromProjectIteration(it *gl.ProjectIteration) *IterationOutput
NewIterationOutputFromProjectIteration converts a gl.ProjectIteration pointer (the type surfaced on board lists) into the canonical iteration object. gl.ProjectIteration has the same field layout as gl.Iteration but with a separate type identity for documentation.
type JSONDepthScanner ¶
type JSONDepthScanner struct {
// contains filtered or unexported fields
}
JSONDepthScanner measures the nesting depth of a JSON value one chunk at a time, so a body can be judged as it streams rather than after it is buffered.
It counts brackets and braces outside string literals, which is all that nesting depth is, and deliberately does not validate the JSON: a scanner that also parsed would be a second, divergent implementation of a decoder this server already runs twice. Miscounting a malformed document is harmless because the decoder behind it refuses that document anyway, and the count is never an undercount for the shape this exists to stop.
The zero value is not usable; construct one with NewJSONDepthScanner.
func NewJSONDepthScanner ¶
func NewJSONDepthScanner(limit int) *JSONDepthScanner
NewJSONDepthScanner returns a scanner that trips once nesting passes limit.
func (*JSONDepthScanner) Exceeded ¶
func (s *JSONDepthScanner) Exceeded() bool
Exceeded reports whether any chunk scanned so far passed the limit.
func (*JSONDepthScanner) Scan ¶
func (s *JSONDepthScanner) Scan(chunk []byte) bool
Scan folds the next chunk of bytes into the measurement and reports whether the limit has been passed. State carries across calls, so the chunk boundaries an io.Reader happens to produce do not change the answer.
type JobMeasures ¶ added in v3.3.0
type JobMeasures struct {
Duration *float64 `json:"duration"`
QueuedDuration *float64 `json:"queued_duration"`
Coverage *float64 `json:"coverage"`
}
JobMeasures is how long a job ran and queued and the coverage it reported, as GitLab sends them: null for a job that never started, or that reported no coverage, which client-go's Job and Bridge decode as float64 and so read as 0. A pointer keeps both meanings, so a job measured at 0 seconds or at 0% coverage keeps its 0. Read from the captured response (ADR-0021); recorded as register row 116 of docs/development/upstream-bugs.md.
func CapturedJob ¶ added in v3.3.0
func CapturedJob(capture *gitlabclient.ResponseCapture) (JobMeasures, error)
CapturedJob reads a job's measures off the captured answer to a request that returned one job.
func CapturedJobs ¶ added in v3.3.0
func CapturedJobs(capture *gitlabclient.ResponseCapture, decoded int) ([]JobMeasures, error)
CapturedJobs reads the same off a list answer, one per job in order, the count held to what the SDK decoded.
type KeysetPaginationInput ¶
type KeysetPaginationInput struct {
Pagination string `` /* 264-byte string literal not displayed */
PageToken string `` /* 352-byte string literal not displayed */
}
KeysetPaginationInput holds GitLab keyset-pagination parameters for list endpoints that support keyset pagination in addition to offset pagination. Embed it alongside PaginationInput on a list input, wire it with ApplyListOptions, and pass KeysetContinuation on the SDK call so the next_page_token a keyset page publishes can be followed. Keyset pagination is more efficient than offset pagination for deep pages of large, ordered result sets.
type LabelDetailsOutput ¶
type LabelDetailsOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
Description string `json:"description"`
DescriptionHTML string `json:"description_html"`
TextColor string `json:"text_color"`
}
LabelDetailsOutput mirrors gl.LabelDetails (the inline label details surfaced on labels array inside issue and MR resources).
func NewLabelDetailsOutputs ¶
func NewLabelDetailsOutputs(details []*gl.LabelDetails) []*LabelDetailsOutput
NewLabelDetailsOutputs converts a slice of gl.LabelDetails, skipping nil entries and returning nil for an empty / all-nil input.
type LabelExtra ¶
type LabelExtra struct {
DescriptionHTML string `json:"description_html"`
}
LabelExtra is what lib/api/entities/label.rb sends on a label that client-go's Label and GroupLabel do not carry: the description rendered as HTML, sent on every label.
func CapturedLabel ¶
func CapturedLabel(capture *gitlabclient.ResponseCapture) (LabelExtra, error)
CapturedLabel reads, off the captured answer to a request for one label, the field client-go's label structs do not model.
func CapturedLabels ¶
func CapturedLabels(capture *gitlabclient.ResponseCapture, decoded int) ([]LabelExtra, error)
CapturedLabels reads the same off a list answer, one extra per label in order, the count held to what the SDK decoded.
type LabelMarkdown ¶
type LabelMarkdown struct {
ID int64
Name string
Color string
Description string
CountsSent bool
OpenIssuesCount int64
ClosedIssuesCount int64
OpenMergeRequestsCount int64
Priority int64
PrioritySpecified bool
IsProjectLabel bool
Subscribed bool
Archived bool
}
LabelMarkdown holds the common fields rendered for project and group labels.
Archived is here because GitLab archives a label rather than deleting it, and a card that says nothing about it shows an archived label exactly as it shows a live one: the label carried the flag on its output type and no renderer read it.
CountsSent says whether GitLab sent the three usage counts beside it. lib/api/entities/label.rb exposes them only under options[:with_counts], which GitLab sets from the with_counts parameter of the two label listings and on no other route, so a label from any other answer carries none, and rendering its zeros would read as a label nothing uses (issue 1174).
type LabelMarkdownOptions ¶
type LabelMarkdownOptions struct {
DetailTitle string
ListTitle string
EmptyListText string
DetailHints []string
ListHints []string
}
LabelMarkdownOptions controls label detail and list Markdown copy. The description is rendered one way for every caller, as a card row escaped through the card, so there is no switch for it: the option that let one scope escape and the other not made one entity answer the same question two ways.
type LastPipelineOutput ¶
type LastPipelineOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid,omitempty"`
ProjectID int64 `json:"project_id,omitempty"`
Status string `json:"status,omitempty"`
Source string `json:"source,omitempty"`
Ref string `json:"ref,omitempty"`
SHA string `json:"sha,omitempty"`
Name string `json:"name,omitempty"`
WebURL string `json:"web_url,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
LastPipelineOutput mirrors gl.PipelineInfo, the pipeline summary embedded in a commit payload as last_pipeline (commits, branch head commits).
func NewLastPipelineOutput ¶
func NewLastPipelineOutput(p *gl.PipelineInfo) *LastPipelineOutput
NewLastPipelineOutput maps gl.PipelineInfo to *LastPipelineOutput, or nil when the commit has no associated pipeline.
type LinePositionOutput ¶
type LinePositionOutput struct {
LineCode string `json:"line_code,omitempty"`
Type string `json:"type,omitempty"`
OldLine int64 `json:"old_line,omitempty"`
NewLine int64 `json:"new_line,omitempty"`
}
LinePositionOutput mirrors gl.LinePosition: one endpoint (start or end) of a multi-line diff note position.
func NewLinePositionOutput ¶
func NewLinePositionOutput(p *gl.LinePosition) *LinePositionOutput
NewLinePositionOutput converts a *gl.LinePosition into the canonical-key line-position object, returning nil when the SDK value is nil.
type LineRangeOutput ¶
type LineRangeOutput struct {
Start *LinePositionOutput `json:"start,omitempty"`
End *LinePositionOutput `json:"end,omitempty"`
}
LineRangeOutput mirrors gl.LineRange (the start/end of a multi-line diff note position). The full *LinePositionOutput objects are surfaced on the canonical `start` / `end` keys (mirroring gl.LineRange.StartRange / gl.LineRange.EndRange, whose JSON tags are `start` / `end`).
func NewLineRangeOutput ¶
func NewLineRangeOutput(lr *gl.LineRange) *LineRangeOutput
NewLineRangeOutput converts a *gl.LineRange into the line-range object, returning nil when absent (or when both endpoints are nil).
type LineType ¶
type LineType int
LineType classifies a line within a unified diff hunk.
const ( // LineContext identifies an unchanged line present in both old and new file versions. LineContext LineType = iota // Unchanged line (present in both old and new file) // LineAdded identifies a line present only in the new file version. LineAdded // Added line (present only in new file) // LineRemoved identifies a line present only in the old file version. LineRemoved // Removed line (present only in old file) )
type LinksOutput ¶
type LinksOutput struct {
Self string `json:"self,omitempty"`
EditURL string `json:"edit_url,omitempty"`
OpenedIssuesURL string `json:"opened_issues_url,omitempty"`
OpenedMergeRequestsURL string `json:"opened_merge_requests_url,omitempty"`
MergedMergeRequestsURL string `json:"merged_merge_requests_url,omitempty"`
ClosedIssuesURL string `json:"closed_issues_url,omitempty"`
ClosedMergeRequestsURL string `json:"closed_merge_requests_url,omitempty"`
}
LinksOutput mirrors gl.ReleaseLinks (the _links object on a release). Per the documented release-by-tag response GitLab returns URL strings for self, edit, opened_issues, opened_merge_requests, merged_merge_requests, and closed_merge_requests. The JSON tags follow the SDK's snake_case pluralisation (opened_issues_url, etc.) which differs from the older opened_issues / merged_issues / closed_issues names used in some docs.
func NewLinksOutput ¶
func NewLinksOutput(l gl.ReleaseLinks) *LinksOutput
NewLinksOutput converts a gl.ReleaseLinks into the canonical-key links object, returning nil when the SDK value has every URL field empty.
type LintExtra ¶
type LintExtra struct {
Jobs []LintJobOutput `json:"jobs"`
}
LintExtra is what lib/api/entities/ci/lint/result.rb sends that client-go's ProjectLintResult does not carry.
func CapturedLint ¶
func CapturedLint(capture *gitlabclient.ResponseCapture) (LintExtra, error)
CapturedLint reads, off the captured answer to a lint request, the jobs array client-go does not model.
type LintJobOutput ¶
type LintJobOutput struct {
Name string `json:"name"`
Stage string `json:"stage"`
BeforeScript []string `json:"before_script"`
Script []string `json:"script"`
AfterScript []string `json:"after_script"`
TagList []string `json:"tag_list"`
Only any `json:"only,omitempty"`
Except any `json:"except,omitempty"`
Environment any `json:"environment,omitempty"`
When string `json:"when"`
AllowFailure bool `json:"allow_failure"`
Needs any `json:"needs,omitempty"`
}
LintJobOutput is one job of the jobs array a CI lint answer carries when include_jobs is set, built by lib/gitlab/ci/lint.rb from the processed configuration; client-go's ProjectLintResult does not carry the array. A static check carries only, except and needs as the configuration spelled them, so those three keep the JSON shape GitLab sent, and environment is the name on a pipeline simulation and the configuration on a static check.
type MRMilestoneOutput ¶
type MRMilestoneOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
GroupID int64 `json:"group_id"`
ProjectID int64 `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
State string `json:"state"`
WebURL string `json:"web_url"`
StartDate string `json:"start_date,omitempty"`
DueDate string `json:"due_date,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
Expired *bool `json:"expired,omitempty"`
}
MRMilestoneOutput mirrors gl.Milestone as surfaced inside merge request and issue resources. Distinct from toolutil.MilestoneOutput (the release-milestone shape) — see file-level comment for the rationale and field-level deltas.
func NewMRMilestoneOutputs ¶
func NewMRMilestoneOutputs(ms []*gl.Milestone) []*MRMilestoneOutput
NewMRMilestoneOutputs converts a slice of gl.Milestone, skipping nil entries and returning nil for an empty / all-nil input.
type MemberExtra ¶
type MemberExtra struct {
Locked bool `json:"locked"`
PublicEmail string `json:"public_email"`
MembershipState string `json:"membership_state"`
TwoFactorEnabled *bool `json:"two_factor_enabled"`
GroupSAMLIdentity *SAMLIdentityOutput `json:"group_saml_identity"`
GroupSCIMIdentity *SCIMIdentityOutput `json:"group_scim_identity"`
Override *bool `json:"override"`
// AvatarPath is the avatar as a path on the instance rather than a full
// URL, and CustomAttributes the attributes an administrator who asked for
// them receives. Both come from the user object merged into the member.
AvatarPath string `json:"avatar_path"`
CustomAttributes []CustomAttributeOutput `json:"custom_attributes"`
}
MemberExtra is what lib/api/entities/member.rb sends on a member that client-go's GroupMember and ProjectMember do not carry, read from the captured response beside the SDK's own decode (ADR-0021). locked is on every member and membership_state on every member of an Enterprise instance; the rest are sent when their condition holds, so each is a pointer that stays nil otherwise. public_email and group_saml_identity are on GroupMember, and only a project member reads them from here.
func CapturedMember ¶
func CapturedMember(capture *gitlabclient.ResponseCapture) (MemberExtra, error)
CapturedMember reads, off the captured answer to a request for one member, the fields client-go's member structs do not model. A body that decoded for the SDK and does not for MemberExtra is reported, since the fault is then in the type naming the fields.
func CapturedMembers ¶
func CapturedMembers(capture *gitlabclient.ResponseCapture, decoded int) ([]MemberExtra, error)
CapturedMembers reads the same off a list answer, one extra per member in order, the count held to what the SDK decoded as CapturedNotes holds it.
type MemberRoleOutput ¶
type MemberRoleOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
GroupID int64 `json:"group_id"`
BaseAccessLevel int `json:"base_access_level"`
AdminCICDVariables bool `json:"admin_cicd_variables,omitempty"`
AdminComplianceFramework bool `json:"admin_compliance_framework,omitempty"`
AdminGroupMembers bool `json:"admin_group_member,omitempty"`
AdminMergeRequests bool `json:"admin_merge_request,omitempty"`
AdminPushRules bool `json:"admin_push_rules,omitempty"`
AdminTerraformState bool `json:"admin_terraform_state,omitempty"`
AdminVulnerability bool `json:"admin_vulnerability,omitempty"`
AdminWebHook bool `json:"admin_web_hook,omitempty"`
ArchiveProject bool `json:"archive_project,omitempty"`
ManageDeployTokens bool `json:"manage_deploy_tokens,omitempty"`
ManageGroupAccessTokens bool `json:"manage_group_access_tokens,omitempty"`
ManageMergeRequestSettings bool `json:"manage_merge_request_settings,omitempty"`
ManageProjectAccessTokens bool `json:"manage_project_access_tokens,omitempty"`
ManageSecurityPolicyLink bool `json:"manage_security_policy_link,omitempty"`
ReadCode bool `json:"read_code,omitempty"`
ReadRunners bool `json:"read_runners,omitempty"`
ReadDependency bool `json:"read_dependency,omitempty"`
ReadVulnerability bool `json:"read_vulnerability,omitempty"`
RemoveGroup bool `json:"remove_group,omitempty"`
RemoveProject bool `json:"remove_project,omitempty"`
}
MemberRoleOutput mirrors gl.MemberRole (the member_role object). Custom member roles are an Enterprise (Premium/Ultimate) feature; the object is nil on instances or members without a custom role. All permission flags are surfaced for 1:1 SDK fidelity.
func NewMemberRoleOutput ¶
func NewMemberRoleOutput(r *gl.MemberRole) *MemberRoleOutput
NewMemberRoleOutput mirrors a gl.MemberRole into the shared output shape, returning nil when the SDK value is nil.
type MemberUserOutput ¶
type MemberUserOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
State string `json:"state"`
AvatarURL string `json:"avatar_url,omitempty"`
WebURL string `json:"web_url,omitempty"`
}
MemberUserOutput mirrors gl.MemberCreatedBy (the created_by object).
func NewMemberUserOutput ¶
func NewMemberUserOutput(u *gl.MemberCreatedBy) *MemberUserOutput
NewMemberUserOutput mirrors a gl.MemberCreatedBy into the shared output shape, returning nil when the SDK value is nil.
type MergeRequestDiffExtra ¶
type MergeRequestDiffExtra struct {
PatchIDSHA string `json:"patch_id_sha"`
}
MergeRequestDiffExtra is the patch id of a merge request version, which identifies the change independently of the commits carrying it. The diff entity exposes it under no condition.
func CapturedMergeRequestDiff ¶
func CapturedMergeRequestDiff(capture *gitlabclient.ResponseCapture) (MergeRequestDiffExtra, error)
CapturedMergeRequestDiff reads it off the captured answer to a request for one version.
func CapturedMergeRequestDiffs ¶
func CapturedMergeRequestDiffs(capture *gitlabclient.ResponseCapture, decoded int) ([]MergeRequestDiffExtra, error)
CapturedMergeRequestDiffs reads the same off a list answer, one extra per version in order, the count held to what the SDK decoded.
type MergeRequestExtra ¶
type MergeRequestExtra struct {
ApprovalsBeforeMerge *int64 `json:"approvals_before_merge"`
MergeStatus string `json:"merge_status"`
Reference string `json:"reference"`
WorkInProgress bool `json:"work_in_progress"`
TitleHTML string `json:"title_html"`
DescriptionHTML string `json:"description_html"`
HeadPipeline *PipelineMeasures `json:"head_pipeline"`
}
MergeRequestExtra is what lib/api/entities/merge_request_basic.rb sends that client-go's BasicMergeRequest does not carry.
Four of the six are the older spelling of something the entity also sends under a newer name, and GitLab still sends both: merge_status beside detailed_merge_status, reference beside references, work_in_progress beside draft, and approvals_before_merge beside the approval rules API. Deprecated is not absent, and a caller reading a merge request through this server should see what GitLab put on the wire.
title_html and description_html are the exception: the entity exposes them under the render_html presenter option, and of every route GitLab mounts only GET /projects/:id/merge_requests/:merge_request_iid declares render_html as a request parameter. They arrive on that one response, when the caller asked for them, and on no other, which is why the output shape publishes them omitempty and why a type serving only the other routes leaves them out altogether.
approvals_before_merge is a pointer because GitLab sends null where no approval count applies, which a bare int64 would flatten into zero, a number that means something else.
head_pipeline is read for its durations alone, which client-go decodes as int64 and so reads null as 0 (PipelineMeasures); it is nil when GitLab sent no head pipeline.
func CapturedMergeRequest ¶
func CapturedMergeRequest(capture *gitlabclient.ResponseCapture) (MergeRequestExtra, error)
CapturedMergeRequest reads them off the captured answer to a request that returned one merge request.
func CapturedMergeRequests ¶
func CapturedMergeRequests(capture *gitlabclient.ResponseCapture, decoded int) ([]MergeRequestExtra, error)
CapturedMergeRequests reads the same off a list answer, one extra per merge request in order, the count held to what the SDK decoded.
type MergeRequestOutput ¶
type MergeRequestOutput struct {
HintableOutput
ID int64 `json:"id"`
IID int64 `json:"iid"`
ProjectID int64 `json:"project_id"`
SourceProjectID int64 `json:"source_project_id,omitempty"`
TargetProjectID int64 `json:"target_project_id,omitempty"`
Title string `json:"title"`
Description string `json:"description"`
State string `json:"state"`
Imported bool `json:"imported,omitempty"`
ImportedFrom string `json:"imported_from,omitempty"`
SourceBranch string `json:"source_branch"`
TargetBranch string `json:"target_branch"`
WebURL string `json:"web_url"`
DetailedMergeStatus string `json:"detailed_merge_status,omitempty"`
Draft bool `json:"draft"`
WorkInProgress bool `json:"work_in_progress,omitempty"`
HasConflicts bool `json:"has_conflicts"`
BlockingDiscussionsResolved bool `json:"blocking_discussions_resolved"`
Squash bool `json:"squash,omitempty"`
SquashOnMerge bool `json:"squash_on_merge,omitempty"`
MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds,omitempty"`
ShouldRemoveSourceBranch bool `json:"should_remove_source_branch,omitempty"`
AllowMaintainerToPush bool `json:"allow_maintainer_to_push,omitempty"`
DiscussionLocked bool `json:"discussion_locked"`
RebaseInProgress bool `json:"rebase_in_progress,omitempty"`
Author *BasicUserOutput `json:"author,omitempty"`
Assignee *BasicUserOutput `json:"assignee,omitempty"`
MergeUser *BasicUserOutput `json:"merge_user,omitempty"`
MergedBy *BasicUserOutput `json:"merged_by,omitempty"`
ClosedBy *BasicUserOutput `json:"closed_by,omitempty"`
Assignees []*BasicUserOutput `json:"assignees"`
Reviewers []*BasicUserOutput `json:"reviewers"`
Labels []string `json:"labels"`
LabelDetails []*LabelDetailsOutput `json:"label_details,omitempty"`
Milestone *MRMilestoneOutput `json:"milestone,omitempty"`
References *ReferencesOutput `json:"references,omitempty"`
SHA string `json:"sha,omitempty"`
MergeCommitSHA string `json:"merge_commit_sha,omitempty"`
MergeError string `json:"merge_error,omitempty"`
ChangesCount string `json:"changes_count,omitempty"`
DivergedCommitsCount int64 `json:"diverged_commits_count,omitempty"`
Upvotes int64 `json:"upvotes,omitempty"`
Downvotes int64 `json:"downvotes,omitempty"`
SquashCommitSHA string `json:"squash_commit_sha,omitempty"`
ForceRemoveSourceBranch bool `json:"force_remove_source_branch,omitempty"`
AllowCollaboration bool `json:"allow_collaboration,omitempty"`
MergeAfter string `json:"merge_after,omitempty"`
TaskCompletionStatus *TaskCompletionStatusOutput `json:"task_completion_status,omitempty"`
TimeStats *TimeStatsOutput `json:"time_stats,omitempty"`
Subscribed bool `json:"subscribed,omitempty"`
FirstContribution bool `json:"first_contribution,omitempty"`
User *MergeRequestUserOutput `json:"user,omitempty"`
DiffRefs *DiffRefsOutput `json:"diff_refs,omitempty"`
Pipeline *PipelineInfoOutput `json:"pipeline,omitempty"`
HeadPipeline *PipelineOutput `json:"head_pipeline,omitempty"`
LatestBuildStartedAt string `json:"latest_build_started_at,omitempty"`
LatestBuildFinishedAt string `json:"latest_build_finished_at,omitempty"`
FirstDeployedToProductionAt string `json:"first_deployed_to_production_at,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
MergedAt string `json:"merged_at,omitempty"`
ClosedAt string `json:"closed_at,omitempty"`
PreparedAt string `json:"prepared_at,omitempty"`
UserNotesCount int64 `json:"user_notes_count,omitempty"`
// The keys GitLab's merge request entity sends that client-go's structs do
// not model, read from the captured response beside the SDK's own decode
// (ADR-0021) and described on [MergeRequestExtra]. approvals_before_merge
// carries the tier the sibling shapes already give it, since the count only
// exists where merge request approvals are licensed.
ApprovalsBeforeMerge *int64 `json:"approvals_before_merge,omitempty" tier:"premium"`
MergeStatus string `json:"merge_status,omitempty"`
Reference string `json:"reference,omitempty"`
TitleHTML string `json:"title_html,omitempty"`
DescriptionHTML string `json:"description_html,omitempty"`
}
MergeRequestOutput mirrors gl.MergeRequest (the full merge-request payload returned by MR list/get/create/update endpoints). It is the canonical Output shape shared by the mergerequests and deploymentmergerequests domains via type aliases, eliminating the ~80-line duplicated struct block identified by SonarCloud while avoiding cross-package import cycles (ADR-0004).
HintableOutput is embedded first so next_steps appears at the top of the serialized JSON, giving LLMs workflow guidance before parsing the payload.
func (*MergeRequestOutput) ApplyExtra ¶
func (o *MergeRequestOutput) ApplyExtra(extra MergeRequestExtra)
ApplyExtra fills the keys read off the captured response.
It is a method rather than six assignments at each call site because the merge request converters are reached from four packages, and a key added to MergeRequestExtra that one of them forgot would be a schema field nothing ever fills.
work_in_progress is filled from here even though client-go models it, because it models it on MergeRequest alone: a list decodes into BasicMergeRequest, which does not carry it, so before this the key was published on every list and set on none of them. The capture reads the same bytes the SDK decoded, so the two answers cannot disagree where both exist.
type MergeRequestUserOutput ¶
type MergeRequestUserOutput struct {
CanMerge bool `json:"can_merge"`
}
MergeRequestUserOutput mirrors gl.MergeRequestUser (the user object inside MR.user_notes_count type contexts — only the CanMerge flag is part of the documented subset).
func NewMergeRequestUserOutput ¶
func NewMergeRequestUserOutput(u *gl.MergeRequestUser) *MergeRequestUserOutput
NewMergeRequestUserOutput converts a gl.MergeRequestUser pointer into the canonical-key MR user object, returning nil for a nil source.
type MetaSchemaActionEntry ¶
type MetaSchemaActionEntry struct {
Action string `json:"action"`
SchemaURI string `json:"schema_uri"`
Destructive bool `json:"destructive"`
}
MetaSchemaActionEntry describes one meta-tool action in the tool-call index.
type MetaSchemaDiscoveryIndex ¶
type MetaSchemaDiscoveryIndex struct {
URITemplate string `json:"uri_template"`
ToolCount int `json:"tool_count"`
ActionCount int `json:"action_count"`
Tools []MetaSchemaToolEntry `json:"tools"`
}
MetaSchemaDiscoveryIndex is a model-controlled schema discovery payload.
func BuildMetaSchemaDiscoveryIndex ¶
func BuildMetaSchemaDiscoveryIndex(routes map[string]ActionMap) MetaSchemaDiscoveryIndex
BuildMetaSchemaDiscoveryIndex builds the richer tool-call schema index payload.
func BuildMetaSchemaDiscoveryIndexForTool ¶
func BuildMetaSchemaDiscoveryIndexForTool(routes map[string]ActionMap, tool string) (MetaSchemaDiscoveryIndex, bool)
BuildMetaSchemaDiscoveryIndexForTool builds the tool-call index for one meta-tool.
type MetaSchemaIndex ¶
type MetaSchemaIndex struct {
URITemplate string `json:"uri_template"`
Tools []MetaSchemaIndexEntry `json:"tools"`
}
MetaSchemaIndex is the payload returned by the schema index resource.
func BuildMetaSchemaIndex ¶
func BuildMetaSchemaIndex(routes map[string]ActionMap) MetaSchemaIndex
BuildMetaSchemaIndex builds the resource-compatible schema index payload.
type MetaSchemaIndexEntry ¶
MetaSchemaIndexEntry is a single tool entry in the resource index payload.
type MetaSchemaRegistry ¶
type MetaSchemaRegistry struct {
// contains filtered or unexported fields
}
MetaSchemaRegistry stores the visible meta-tool route snapshot used by model-controlled schema discovery actions.
func NewMetaSchemaRegistry ¶
func NewMetaSchemaRegistry(routes map[string]ActionMap) *MetaSchemaRegistry
NewMetaSchemaRegistry creates a registry initialized with a route snapshot.
func (*MetaSchemaRegistry) Routes ¶
func (r *MetaSchemaRegistry) Routes() map[string]ActionMap
Routes returns a snapshot of the registry contents, taken with CloneMetaSchemaRoutes: later SetRoutes calls do not reach it, and the schemas in it are shared and must not be mutated.
func (*MetaSchemaRegistry) SetRoutes ¶
func (r *MetaSchemaRegistry) SetRoutes(routes map[string]ActionMap)
SetRoutes replaces the registry contents with a snapshot of routes, taken with CloneMetaSchemaRoutes: the maps are the registry's own, the schemas inside them are shared with the caller and frozen.
type MetaSchemaToolEntry ¶
type MetaSchemaToolEntry struct {
Tool string `json:"tool"`
ActionCount int `json:"action_count"`
Actions []MetaSchemaActionEntry `json:"actions"`
}
MetaSchemaToolEntry describes one meta-tool in the tool-call index.
type MetaToolInput ¶
type MetaToolInput struct {
Action string `json:"action" jsonschema:"Action to perform. See the tool description for available actions and their parameters."`
Params map[string]any `` /* 147-byte string literal not displayed */
}
MetaToolInput is the common input for all meta-tools. The LLM sends an action name and a params object; the dispatcher routes to the underlying handler function and deserializes params into the action-specific input struct.
type MilestoneExtra ¶
type MilestoneExtra struct {
ProjectID int64 `json:"project_id"`
}
MilestoneExtra is what GitLab's milestone entity sends that a group milestone struct does not carry: the project a project-scoped milestone belongs to, exposed only when there is one, so a group milestone leaves it zero. A group's milestone list asked for include_descendants returns the milestones of the group's projects as well, which carry it. client-go's GroupMilestone decodes the milestone's own page, the other key this used to read, as of v3.26.0.
func CapturedMilestone ¶
func CapturedMilestone(capture *gitlabclient.ResponseCapture) (MilestoneExtra, error)
CapturedMilestone reads it off the captured answer to a request for one milestone.
func CapturedMilestones ¶
func CapturedMilestones(capture *gitlabclient.ResponseCapture, decoded int) ([]MilestoneExtra, error)
CapturedMilestones reads the same off a list answer, one extra per milestone in order, the count held to what the SDK decoded.
type MilestoneIssueStatsOutput ¶
type MilestoneIssueStatsOutput struct {
Total int64 `json:"total,omitempty"`
Closed int64 `json:"closed,omitempty"`
}
MilestoneIssueStatsOutput mirrors gl.ReleaseMilestoneIssueStats (the total / closed counts object surfaced under each associated milestone on a release).
func NewMilestoneIssueStatsOutput ¶
func NewMilestoneIssueStatsOutput(s *gl.ReleaseMilestoneIssueStats) *MilestoneIssueStatsOutput
NewMilestoneIssueStatsOutput converts a gl.ReleaseMilestoneIssueStats into the canonical-key stats object.
type MilestoneOutput ¶
type MilestoneOutput struct {
ID int64 `json:"id,omitempty"`
IID int64 `json:"iid,omitempty"`
ProjectID int64 `json:"project_id,omitempty"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
State string `json:"state,omitempty"`
WebURL string `json:"web_url,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
StartDate string `json:"start_date,omitempty"`
DueDate string `json:"due_date,omitempty"`
IssueStats *MilestoneIssueStatsOutput `json:"issue_stats,omitempty"`
}
MilestoneOutput mirrors gl.ReleaseMilestone (the milestone object surfaced on the release's milestones array). Timestamps are surfaced as RFC 3339 strings via toolutil.FormatTimePtr / FormatISOTimePtr.
func NewMilestoneOutputs ¶
func NewMilestoneOutputs(ms []*gl.ReleaseMilestone) []*MilestoneOutput
NewMilestoneOutputs converts a slice of gl.ReleaseMilestone, skipping nil elements and returning nil for an empty input. Timestamps are formatted as RFC 3339 (or YYYY-MM-DD for ISOTime).
type NamespaceBasicOutput ¶
type NamespaceBasicOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
Path string `json:"path"`
Kind string `json:"kind"`
FullPath string `json:"full_path"`
ParentID int64 `json:"parent_id,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
WebURL string `json:"web_url,omitempty"`
}
NamespaceBasicOutput mirrors lib/api/entities/namespace_basic.rb, the group object GitLab renders on a todo raised in a group rather than a project, which client-go decodes into a ProjectNamespace.
type NamespaceExtra ¶
type NamespaceExtra struct {
AdditionalPurchasedStorageSize *int64 `json:"additional_purchased_storage_size"`
CIMinutesUsage *CIMinutesUsageOutput `json:"ci_minutes_usage"`
}
NamespaceExtra is the three limits GitLab's namespace entity sends to a caller who may change them, and the compute-minute usage it sends to one who may read it. client-go v3.12.0 carries the three limits, and it carries them as plain int64, where a null and a limit of zero are the same value: a null shared_runners_minutes_limit is how GitLab says there is no limit at all, so reading these from the SDK would publish "no limit" as "limited to zero minutes". The other five fields this shape used to read retired with that release, their SDK types carrying what GitLab sends. client-go models no usage at all.
func CapturedNamespace ¶
func CapturedNamespace(capture *gitlabclient.ResponseCapture) (NamespaceExtra, error)
CapturedNamespace reads them off the captured answer to a request for one namespace.
func CapturedNamespaces ¶
func CapturedNamespaces(capture *gitlabclient.ResponseCapture, decoded int) ([]NamespaceExtra, error)
CapturedNamespaces reads the same off a list answer, one extra per namespace in order, the count held to what the SDK decoded.
type NoteExtra ¶
type NoteExtra struct {
Confidential bool `json:"confidential"`
Imported bool `json:"imported"`
ImportedFrom string `json:"imported_from"`
CommandsChanges map[string]any `json:"commands_changes"`
Suggestions []SuggestionOutput `json:"suggestions"`
Author NoteUserExtra `json:"author"`
ResolvedBy NoteUserExtra `json:"resolved_by"`
}
NoteExtra is what GitLab's Note entity sends that client-go's Note does not model, read from the captured response beside the SDK's own decoding (see CapturedNote). `confidential` is here too: client-go carries it as a deprecated field some converters read and others mirror from `internal`, and reading GitLab's own value ends that difference.
func CapturedNote ¶
func CapturedNote(capture *gitlabclient.ResponseCapture) (NoteExtra, error)
CapturedNote reads, from the captured answer of a call that returned one note, what GitLab sends that client-go's Note does not model.
func CapturedNotes ¶
func CapturedNotes(capture *gitlabclient.ResponseCapture, decoded int) ([]NoteExtra, error)
CapturedNotes reads the same from the captured answer of a call that returned a list of notes, one extra per note in the list's order. The count is held to what the SDK decoded: the two read the same bytes, so a difference is a fault in this reader and not in GitLab's answer.
type NoteListMarkdownOptions ¶
type NoteListMarkdownOptions struct {
Title string
EmptyMessage string
IncludeInternal bool
Hints []string
}
NoteListMarkdownOptions configures shared note list rendering.
type NoteMarkdown ¶
type NoteMarkdown struct {
ID int64
Body string
Author string
CreatedAt string
System bool
Internal bool
Resolvable bool
Resolved bool
ResolvedBy string
// QuickActions is what a note mutation reported about the quick actions
// the body carried, nil for a note read back or a body that carried none.
QuickActions *QuickActionsStatusOutput
// EventGID is the global ID of a note GitLab synthesizes from a resource
// event (a label, state, milestone, iteration or weight change), which
// has no numeric ID: such a note is named by it, and "" names the note by
// ID as every other note is.
EventGID string
}
NoteMarkdown carries the fields every GitLab note renders with, whether it stands alone or sits in a discussion thread: issue, merge request, snippet, epic and commit notes alike.
func NewDiscussionNoteMarkdown ¶
func NewDiscussionNoteMarkdown(id int64, body, author, createdAt string) NoteMarkdown
NewDiscussionNoteMarkdown builds the note view model from the four fields a discussion note always carries.
func NewNoteMarkdown ¶
func NewNoteMarkdown(id int64, body, author, createdAt string, flags NoteMarkdownFlags, resolvedBy string) NoteMarkdown
NewNoteMarkdown builds a shared Markdown view model for GitLab notes.
func NoteMarkdowns ¶
func NoteMarkdowns[T any](notes []T, convert func(T) NoteMarkdown) []NoteMarkdown
NoteMarkdowns maps package-specific note outputs to the shared note Markdown view model.
type NoteMarkdownFlags ¶
NoteMarkdownFlags groups boolean note attributes for Markdown view-model construction.
type NoteMarkdownOptions ¶
type NoteMarkdownOptions struct {
Title string
IncludeInternal bool
IncludeResolvable bool
Hints []string
}
NoteMarkdownOptions configures shared note detail rendering.
type NoteOutput ¶
type NoteOutput struct {
HintableOutput
ID int64 `json:"id"`
Body string `json:"body"`
Author *NoteUserOutput `json:"author,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
System bool `json:"system"`
Internal bool `json:"internal"`
Resolvable bool `json:"resolvable,omitempty"`
Resolved bool `json:"resolved,omitempty"`
ResolvedAt string `json:"resolved_at,omitempty"`
ResolvedBy *NoteUserOutput `json:"resolved_by,omitempty"`
NoteableType string `json:"noteable_type,omitempty"`
NoteableID int64 `json:"noteable_id,omitempty"`
NoteableIID int64 `json:"noteable_iid,omitempty"`
CommitID string `json:"commit_id,omitempty"`
Type string `json:"type,omitempty"`
Position *NotePositionOutput `json:"position,omitempty"`
ProjectID int64 `json:"project_id,omitempty"`
Confidential bool `json:"confidential"`
Imported bool `json:"imported"`
ImportedFrom string `json:"imported_from,omitempty"`
CommandsChanges map[string]any `json:"commands_changes,omitempty"`
Suggestions []SuggestionOutput `json:"suggestions,omitempty"`
}
NoteOutput mirrors every field of GitLab's Note entity as the REST Notes API sends it (notes on merge requests, issues, snippets, etc.): the fields client-go's Note models, and the ones only the captured response carries. It is the canonical shared output shape for standalone note tools (mrnotes, issuenotes). Field order and JSON tags are locked; any change is a breaking wire-format change.
`attachment`, `title`, `file_name` and `expires_at`, which client-go's Note still carries, are not here: GitLab's entity does not expose them, and the R-PATH type grain reported them as fields no operation sends.
func NoteOutputFromGitLab ¶
func NoteOutputFromGitLab(n *gl.Note, extra NoteExtra) NoteOutput
NoteOutputFromGitLab converts a gl.Note and what the captured response adds to it into the canonical NoteOutput shape used by standalone note tools (mrnotes, issuenotes). Timestamps are formatted as RFC 3339 strings.
func (NoteOutput) AuthorUsername ¶ added in v3.1.0
func (n NoteOutput) AuthorUsername() string
AuthorUsername returns the author's username, or "" when the note carries no author object. It is the one nil guard every note formatter used to copy.
func (NoteOutput) ResolvedByUsername ¶ added in v3.1.0
func (n NoteOutput) ResolvedByUsername() string
ResolvedByUsername returns the resolver's username, or "" when nobody has resolved the note.
type NotePositionOutput ¶
type NotePositionOutput struct {
BaseSHA string `json:"base_sha,omitempty"`
StartSHA string `json:"start_sha,omitempty"`
HeadSHA string `json:"head_sha,omitempty"`
PositionType string `json:"position_type,omitempty"`
NewPath string `json:"new_path,omitempty"`
NewLine int64 `json:"new_line,omitempty"`
OldPath string `json:"old_path,omitempty"`
OldLine int64 `json:"old_line,omitempty"`
LineRange *LineRangeOutput `json:"line_range,omitempty"`
}
NotePositionOutput mirrors gl.NotePosition: the diff position of a note that is attached to a specific line of a file.
func NewNotePositionOutput ¶
func NewNotePositionOutput(p *gl.NotePosition) *NotePositionOutput
NewNotePositionOutput converts a *gl.NotePosition into the position object, returning nil when the note has no diff position.
type NoteUserExtra ¶
NoteUserExtra is what a note's author or resolver carries that client-go's NoteAuthor and NoteResolvedBy do not model, read from the captured response.
type NoteUserOutput ¶
type NoteUserOutput = UserBasicOutput
NoteUserOutput mirrors GitLab's UserBasic entity, which is what a note's author and resolver are rendered with: the identity fields, the account state and lock, and the two URLs. It carries no email, since UserBasic exposes `public_email` and never `email`; client-go's NoteAuthor carries an `email` GitLab does not send, and `public_email` and `locked` it does not model, which is why the two shared values come from the captured response (see NoteUserExtra).
func NewNoteUserOutputFromAuthor ¶
func NewNoteUserOutputFromAuthor(a gl.NoteAuthor, extra NoteUserExtra) *NoteUserOutput
NewNoteUserOutputFromAuthor converts a gl.NoteAuthor value into the additive author object. The author is always present on a note, so this returns a pointer to a populated value (never nil) to keep the canonical `author` key stable.
func NewNoteUserOutputFromResolvedBy ¶
func NewNoteUserOutputFromResolvedBy(r gl.NoteResolvedBy, extra NoteUserExtra) *NoteUserOutput
NewNoteUserOutputFromResolvedBy converts a gl.NoteResolvedBy value into the resolved-by object, returning nil when no user has resolved the note (zero ID + empty username).
type OnceMap ¶
type OnceMap[K comparable, V any] struct { // contains filtered or unexported fields }
OnceMap memoizes one value per key and builds each value exactly once, however many callers ask for it at the same moment.
The caches this replaces were all written as load, build, store-if-absent, which is correct and wasteful in the one situation they exist for: the HTTP pool starting a server per credential, where every one of them misses the same cold key, every one builds a full copy of a registry shape, a manifest snapshot or a reflected schema, and all but one are dropped having cost what the survivor cost. The entry carries the sync.Once, so the callers that lose the race wait for the winner's build instead of racing it.
The zero value is ready to use. The mutex is held only while the entry is looked up, never while a value is built, so builds for different keys still run in parallel.
func (*OnceMap[K, V]) Load ¶
func (m *OnceMap[K, V]) Load(key K, build func() V) V
Load returns the value memoized for key, calling build to produce it the first time the key is asked for. Concurrent callers for one key wait for that single build; callers for different keys build in parallel.
A build that returns a zero value is memoized like any other, so a deterministic failure is not retried on every call. A build that panics memoizes nothing: the panic reaches its own caller unchanged and the key is left to be built again, which is what the load, build, store-if-absent caches this replaced did. sync.Once does not do that by itself, since it marks itself done however its function ends, so the spent slot is dropped by the next caller to find it holding nothing. Without that the key would be poisoned for the life of the process: the zero value served to everyone, no rebuild, and OnceMap.Peek reporting that nobody ever built it.
type PackageExtra ¶
type PackageExtra struct {
Pipeline *PackagePipelineExtra `json:"pipeline"`
Versions []PackageVersionExtra `json:"versions"`
}
PackageExtra is what GitLab's package entity sends on a package that client-go's Package does not carry, on every route that renders one. Since v3.14.0 client-go models the package's creator, its Conan recipe name and its other versions with their tags; what it leaves out is on the pipelines: the one that last built the package, and the one that built each of its other versions, which only a request for one package is sent. Each of them is short the keys PackagePipelineExtra names.
The entity's pipelines key is not read. lib/api/entities/package.rb renders it as the constant EMPTY_PIPELINES whatever the package, which is how GitLab deprecated it in 16.1, so there is nothing on it to complete and no count to hold a capture to.
func CapturedPackage ¶ added in v3.1.0
func CapturedPackage(capture *gitlabclient.ResponseCapture, decodedVersions int) (PackageExtra, error)
CapturedPackage reads them off the captured answer to a request for one package, one extra per other version in order, the count held to the number of other versions the SDK decoded: the extras are paired with what client-go decoded by position, and a count that differed would put one version's pipeline keys on its neighbor.
func CapturedPackages ¶
func CapturedPackages(capture *gitlabclient.ResponseCapture, decoded int) ([]PackageExtra, error)
CapturedPackages reads the same off a page of packages, one extra per package in order, the count held to what the SDK decoded. A page carries no other versions, so the package's own pipeline is all an extra completes and the package's position is all it is paired by.
type PackagePipelineExtra ¶ added in v3.1.0
type PackagePipelineExtra struct {
IID int64 `json:"iid"`
ProjectID int64 `json:"project_id"`
Source string `json:"source"`
User UserBasicExtra `json:"user"`
}
PackagePipelineExtra is what lib/api/entities/package/pipeline.rb sends on a package's pipeline that client-go's PackagePipeline does not carry: the pipeline's iid, its project and its source, three of the eleven keys the entity exposes, and the two keys of the UserBasic who ran it that client-go's BasicUser leaves out, the zero value for a pipeline no user ran.
type PackagePipelineOutput ¶
type PackagePipelineOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
ProjectID int64 `json:"project_id"`
SHA string `json:"sha"`
Ref string `json:"ref"`
Status string `json:"status"`
Source string `json:"source"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
WebURL string `json:"web_url"`
User *UserBasicOutput `json:"user"`
}
PackagePipelineOutput is a pipeline lib/api/entities/package/pipeline.rb renders on a package, sent to a caller allowed to read it: the one that last built the package, or the one that built one of its other versions. client-go's PackagePipeline carries eight of its keys; iid, project_id and source, and the user's locked and public_email, come from PackagePipelineExtra. The timestamps are RFC 3339 to the second, like the package's own.
type PackageTagOutput ¶
type PackageTagOutput struct {
ID int64 `json:"id"`
PackageID int64 `json:"package_id"`
Name string `json:"name"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
PackageTagOutput is one tag pointing at a package, every key of it read from client-go's PackageTag: the one shape a tag is published in, on a package a listing or a request for one package returns and on each of its other versions alike. The timestamps are RFC 3339 to the second, as every timestamp this server publishes as a string is.
type PackageVersionExtra ¶ added in v3.1.0
type PackageVersionExtra struct {
Pipeline *PackagePipelineExtra `json:"pipeline"`
}
PackageVersionExtra is the part of one other version of a package that client-go's PackageVersion does not carry, all of it on its pipeline.
type PackageVersionOutput ¶
type PackageVersionOutput struct {
ID int64 `json:"id"`
Version string `json:"version"`
CreatedAt string `json:"created_at,omitempty"`
Tags []PackageTagOutput `json:"tags,omitempty"`
Pipeline *PackagePipelineOutput `json:"pipeline"`
}
PackageVersionOutput is one other version of the same package, with the tags pointing at it and the pipeline that built it, which GitLab sends only when one package is asked for and never on a page of them. It is published in this shape rather than decoded into it: client-go's PackageVersion carries the version, when it was published, its tags and eight keys of the pipeline that built it, and PackageExtra supplies the other five. Its tags and its timestamp are published as the package's own are.
type PagesCertificateExpirationOutput ¶
type PagesCertificateExpirationOutput struct {
Expired bool `json:"expired"`
Expiration *time.Time `json:"expiration"`
}
PagesCertificateExpirationOutput is when a Pages domain's certificate stops being valid, and whether it already has.
type PagesDomainExtra ¶
type PagesDomainExtra struct {
CertificateExpiration *PagesCertificateExpirationOutput `json:"certificate_expiration"`
}
PagesDomainExtra is that object on the domain, which GitLab exposes only on a domain that has a certificate at all.
func CapturedPagesDomain ¶
func CapturedPagesDomain(capture *gitlabclient.ResponseCapture) (PagesDomainExtra, error)
CapturedPagesDomain reads it off the captured answer to a request for one domain.
func CapturedPagesDomains ¶
func CapturedPagesDomains(capture *gitlabclient.ResponseCapture, decoded int) ([]PagesDomainExtra, error)
CapturedPagesDomains reads the same off a list answer, one extra per domain in order, the count held to what the SDK decoded.
type PaginationInput ¶
type PaginationInput struct {
Page int `` /* 156-byte string literal not displayed */
PerPage int `` /* 156-byte string literal not displayed */
}
PaginationInput holds common pagination query parameters for list endpoints. Constraints (page>=1, per_page in [1,100]) are also enforced at the JSON Schema level by EnrichPaginationConstraints so LLM clients see the bounds directly in tools/list responses.
type PaginationOutput ¶
type PaginationOutput struct {
Page int64 `json:"page"`
PerPage int64 `json:"per_page"`
TotalItems int64 `json:"total_items"`
TotalPages int64 `json:"total_pages"`
NextPage int64 `json:"next_page"`
PrevPage int64 `json:"prev_page"`
HasMore bool `json:"has_more"`
// NextPageToken is the continuation of a page GitLab served by keyset
// pagination: the next link of its Link header, verbatim, which is exactly
// what [gl.WithKeysetPaginationParameters] takes. A keyset page carries no
// X-Next-Page, so NextPage stays 0 on it and this is the one way to ask
// for its successor, by passing it back as page_token. It is empty on an
// offset page and on the last keyset page.
NextPageToken string `json:"next_page_token,omitempty"`
}
PaginationOutput holds pagination metadata extracted from GitLab API responses. Fields map to GitLab's X-Page, X-Per-Page, X-Total, X-Total-Pages, X-Next-Page, X-Prev-Page headers, and NextPageToken to the next link of the Link header on a keyset page. HasMore is a derived convenience flag (NextPage > 0, or a NextPageToken) so LLM clients can decide whether to paginate without inspecting either.
func PaginationFromResponse ¶
func PaginationFromResponse(resp *gl.Response) PaginationOutput
PaginationFromResponse extracts pagination metadata from a GitLab API response: the offset headers as client-go parsed them, and the keyset continuation [keysetNextPageToken] reads from the Link header.
type ParamAliasExplanation ¶
type ParamAliasExplanation struct {
Alias string `json:"alias"`
Canonical string `json:"canonical"`
Source string `json:"source"`
Notes string `json:"notes,omitempty"`
}
ParamAliasExplanation describes a compatibility parameter normalization. It intentionally records parameter names only, never parameter values.
func NormalizeParamAliasesForSchemaWithExplanation ¶
func NormalizeParamAliasesForSchemaWithExplanation(params, schema map[string]any) (map[string]any, []ParamAliasExplanation)
NormalizeParamAliasesForSchemaWithExplanation returns the normalized params and name-only metadata describing compatibility aliases that were applied.
type ParamValidationError ¶
type ParamValidationError struct {
Err error
}
ParamValidationError marks parameter decoding failures that should be surfaced as recoverable tool errors instead of protocol errors.
func (*ParamValidationError) Error ¶
func (e *ParamValidationError) Error() string
Error returns the underlying validation error message.
func (*ParamValidationError) Unwrap ¶
func (e *ParamValidationError) Unwrap() error
Unwrap returns the underlying validation error.
type ParameterAliasSpec ¶
type ParameterAliasSpec struct {
Alias string
Target string
Source string
Searchable bool
Deprecated bool
RemovalVersion string
Reason string
}
ParameterAliasSpec describes a compatibility alias for one action parameter.
type ParameterGuidance ¶
type ParameterGuidance struct {
SemanticRole string `json:"semantic_role,omitempty"`
ValueSource string `json:"value_source,omitempty"`
CommonConfusions []string `json:"common_confusions,omitempty"`
ExampleBinding string `json:"example_binding,omitempty"`
}
ParameterGuidance carries compact model-facing hints for parameters that are easy to confuse across similar GitLab actions.
func DiscussionIDParamGuidance ¶
func DiscussionIDParamGuidance(valueSource string) ParameterGuidance
DiscussionIDParamGuidance returns the canonical parameter guidance for the discussion_id parameter used by discussion-scoped actions across the discussion domains (MR, issue, snippet, commit, epic). valueSource is the domain-specific description of where the thread id comes from; the semantic role, example binding, and thread-vs-note confusion note are identical for every domain.
func DiscussionNoteIDParamGuidance ¶
func DiscussionNoteIDParamGuidance(valueSource string) ParameterGuidance
DiscussionNoteIDParamGuidance returns the canonical parameter guidance for the note_id parameter of discussion note update/delete actions. valueSource is the domain-specific description of where the note id comes from; the semantic role, example binding, and note-vs-thread confusion note are identical for every discussion domain.
type PersonalTokenOutput ¶
type PersonalTokenOutput struct {
ID int64 `json:"id"`
Name string `json:"name"`
Active bool `json:"active"`
Token string `json:"token,omitempty"`
Scopes []string `json:"scopes"`
Granular bool `json:"granular"`
GranularScopes []TokenGranularScopeOutput `json:"granular_scopes,omitempty"`
Revoked bool `json:"revoked"`
Description string `json:"description,omitempty"`
UserID int64 `json:"user_id"`
CreatedAt string `json:"created_at,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"`
LastUsedAt string `json:"last_used_at,omitempty"`
LastUsedIPs []string `json:"last_used_ips,omitempty"`
}
PersonalTokenOutput mirrors gl.PersonalAccessToken as surfaced by the impersonation-token and current-user PAT tools: identity, scopes, and RFC3339/ISO-date lifecycle timestamps. The token value is only present on creation responses. It also carries what lib/api/entities/personal_access_token.rb sends and the SDK struct does not model, read from the captured response (ADR-0021): whether the token is granular, its granular scopes when it is and the endpoint asked for them, and the addresses it was last used from while the instance exposes them.
func NewPersonalTokenOutput ¶
func NewPersonalTokenOutput(t *gl.PersonalAccessToken, extra TokenExtra) PersonalTokenOutput
NewPersonalTokenOutput converts a gl.PersonalAccessToken into the shared output shape, formatting created/last-used as RFC3339 and expiry as an ISO date, and takes the fields the capture read beside the SDK.
type PipelineDetailedStatusIllustrationOutput ¶
type PipelineDetailedStatusIllustrationOutput struct {
Image string `json:"image"`
}
PipelineDetailedStatusIllustrationOutput mirrors gl.DetailedStatus.Illustration (the icon image sub-object).
func NewPipelineDetailedStatusIllustrationOutput ¶
func NewPipelineDetailedStatusIllustrationOutput(image string) *PipelineDetailedStatusIllustrationOutput
NewPipelineDetailedStatusIllustrationOutput is reserved for SDK versions that surface Illustration as a pointer; currently the SDK exposes it as a value type, so NewPipelineDetailedStatusOutput handles the conversion inline. Kept for forward compatibility.
type PipelineDetailedStatusOutput ¶
type PipelineDetailedStatusOutput struct {
Icon string `json:"icon"`
Text string `json:"text"`
Label string `json:"label"`
Group string `json:"group"`
Tooltip string `json:"tooltip"`
HasDetails bool `json:"has_details"`
DetailsPath string `json:"details_path"`
Illustration *PipelineDetailedStatusIllustrationOutput `json:"illustration,omitempty"`
Favicon string `json:"favicon"`
}
PipelineDetailedStatusOutput mirrors gl.DetailedStatus (the icon / text / label / group / tooltip / details_path / illustration / favicon object that GitLab returns inside gl.Pipeline.DetailedStatus).
func NewPipelineDetailedStatusOutput ¶
func NewPipelineDetailedStatusOutput(s *gl.DetailedStatus) *PipelineDetailedStatusOutput
NewPipelineDetailedStatusOutput converts a *gl.DetailedStatus into the canonical-key detailed-status object, returning nil when the SDK value is nil. The SDK's Illustration field is a non-pointer gl.DetailedStatusIllustration value; we surface the nested image only when populated, keeping the canonical nil-on-empty contract.
type PipelineExtra ¶
type PipelineExtra struct {
PipelineMeasures
Archived bool `json:"archived"`
}
PipelineExtra is what lib/api/entities/ci/pipeline.rb sends on a pipeline that client-go's Pipeline does not carry: whether it is archived, sent on every pipeline rendered whole, and the two durations as GitLab measured them.
func CapturedPipeline ¶
func CapturedPipeline(capture *gitlabclient.ResponseCapture) (PipelineExtra, error)
CapturedPipeline reads, off the captured answer to a request for one pipeline, the field client-go's Pipeline does not model.
type PipelineInfoOutput ¶
type PipelineInfoOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
ProjectID int64 `json:"project_id"`
Status string `json:"status"`
Source string `json:"source"`
Ref string `json:"ref"`
SHA string `json:"sha"`
Name string `json:"name"`
WebURL string `json:"web_url"`
UpdatedAt string `json:"updated_at,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
PipelineInfoOutput mirrors gl.PipelineInfo (the compact pipeline summary surfaced on a merge request's head_pipeline / pipeline fields).
func NewPipelineInfoOutput ¶
func NewPipelineInfoOutput(p *gl.PipelineInfo) *PipelineInfoOutput
NewPipelineInfoOutput converts a gl.PipelineInfo pointer into the canonical-key pipeline info object, returning nil for a nil source.
type PipelineMeasures ¶ added in v3.3.0
type PipelineMeasures struct {
Duration *int64 `json:"duration"`
QueuedDuration *int64 `json:"queued_duration"`
}
PipelineMeasures is how long a pipeline ran and queued, as GitLab sends them: null for a pipeline that has not run (created, skipped, waiting on a manual job), which client-go's Pipeline and PipelineInfo decode as int64 and so read as 0, a pipeline that ran for no time at all. Read from the captured response (ADR-0021); recorded as register row 116 of docs/development/upstream-bugs.md.
type PipelineOutput ¶
type PipelineOutput struct {
ID int64 `json:"id"`
IID int64 `json:"iid"`
ProjectID int64 `json:"project_id"`
Status string `json:"status"`
Source string `json:"source"`
Ref string `json:"ref"`
Name string `json:"name"`
SHA string `json:"sha"`
BeforeSHA string `json:"before_sha"`
Tag bool `json:"tag"`
YamlErrors string `json:"yaml_errors"`
User *BasicUserOutput `json:"user,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
StartedAt string `json:"started_at,omitempty"`
FinishedAt string `json:"finished_at,omitempty"`
CommittedAt string `json:"committed_at,omitempty"`
Duration *int64 `json:"duration,omitempty"`
QueuedDuration *int64 `json:"queued_duration,omitempty"`
Coverage string `json:"coverage"`
WebURL string `json:"web_url"`
DetailedStatus *PipelineDetailedStatusOutput `json:"detailed_status,omitempty"`
}
PipelineOutput mirrors gl.Pipeline (the per-pipeline summary object returned by head_pipeline, merge_train.pipeline, deployment.merge_request .head_pipeline, etc.). It surfaces every field of the SDK struct that the documented GitLab API returns (1:1 audit policy) plus the nested user and detailed_status sub-objects on their canonical keys.
func NewPipelineOutput ¶
func NewPipelineOutput(p *gl.Pipeline, measures *PipelineMeasures) *PipelineOutput
NewPipelineOutput converts a *gl.Pipeline into the canonical-key pipeline object, returning nil when the SDK value is nil. Timestamps are surfaced as RFC 3339 strings via toolutil.FormatTimePtr.
The durations come from measures, what the captured answer carried for this pipeline, because client-go decodes them as int64 and reads the null of a pipeline that has not run as 0. A nil measures, a pipeline the capture held no object for, leaves both out rather than publishing the SDK's zeros.
type PipelineTriggerExtra ¶
PipelineTriggerExtra is when a trigger token stops working, exposed under no condition and null on a token that never expires.
func CapturedPipelineTrigger ¶
func CapturedPipelineTrigger(capture *gitlabclient.ResponseCapture) (PipelineTriggerExtra, error)
CapturedPipelineTrigger reads it off the captured answer to a request for one trigger.
func CapturedPipelineTriggers ¶
func CapturedPipelineTriggers(capture *gitlabclient.ResponseCapture, decoded int) ([]PipelineTriggerExtra, error)
CapturedPipelineTriggers reads the same off a list answer, one extra per trigger in order, the count held to what the SDK decoded.
type ProgressReader ¶
type ProgressReader struct {
// contains filtered or unexported fields
}
ProgressReader wraps an io.Reader and reports progress to an MCP progress tracker as bytes are read. Safe to use with a zero-value/inactive tracker.
func NewProgressReader ¶
func NewProgressReader(ctx context.Context, r io.Reader, total int64, tracker progress.Tracker) *ProgressReader
NewProgressReader creates a ProgressReader that reports on the content being read. If the tracker is inactive, the wrapper still works but skips notifications.
The message says read rather than uploaded, and that is the whole point of it. Nothing here measures a transfer: client-go's UploadRequest copies the whole multipart body into a buffer before it builds the request, and retryablehttp reads a plain io.Reader body to the end for the same reason, so every frame this emits has already fired by the time anything reaches the wire. A message that said "Uploaded" asserted something that had not happened, reached its total before the request existed, and left the phase the caller actually waits on reporting nothing.
func (*ProgressReader) BytesRead ¶
func (pr *ProgressReader) BytesRead() int64
BytesRead returns the total number of bytes read so far.
type ProgressWriter ¶
type ProgressWriter struct {
// contains filtered or unexported fields
}
ProgressWriter wraps an io.Writer and reports progress to an MCP progress tracker as bytes are written (used for downloads to disk).
func NewProgressWriter ¶
func NewProgressWriter(ctx context.Context, w io.Writer, total int64, tracker progress.Tracker) *ProgressWriter
NewProgressWriter creates a ProgressWriter that reports download progress.
A total of zero means the size is not known, which is the ordinary case for a streamed package download: client-go's writer form does not surface the content length. The wire field is omitted for it, which is what the specification asks; the message has to leave it out too, or every frame of that download reads "Downloaded 65536 / 0 bytes".
func (*ProgressWriter) BytesWritten ¶
func (pw *ProgressWriter) BytesWritten() int64
BytesWritten returns the total number of bytes written so far.
type ProjectApprovalRuleExtra ¶ added in v3.1.0
type ProjectApprovalRuleExtra struct {
CoverageMinimumThreshold *int64 `json:"coverage_minimum_threshold"`
}
ProjectApprovalRuleExtra is what ee/lib/api/entities/project_approval_rule.rb sends that client-go's ProjectApprovalRule does not model. The threshold is exposed only on a rule whose report_type is code_coverage, so nil here means "not a coverage rule" rather than a threshold of zero.
func CapturedProjectApprovalRule ¶ added in v3.1.0
func CapturedProjectApprovalRule(capture *gitlabclient.ResponseCapture) (ProjectApprovalRuleExtra, error)
CapturedProjectApprovalRule reads that key off the answer to a request for one rule.
func CapturedProjectApprovalRules ¶ added in v3.1.0
func CapturedProjectApprovalRules(capture *gitlabclient.ResponseCapture, decoded int) ([]ProjectApprovalRuleExtra, error)
CapturedProjectApprovalRules reads the same off a list answer, one extra per rule in order.
type ProjectExtra ¶ added in v3.1.0
type ProjectExtra struct {
DescriptionHTML *string `json:"description_html"`
RepositoryObjectFormat *string `json:"repository_object_format"`
ShowDiffPreviewInEmail *bool `json:"show_diff_preview_in_email"`
WarnAboutPotentiallyUnwantedCharacters *bool `json:"warn_about_potentially_unwanted_characters"`
SecretPushProtectionEnabled *bool `json:"secret_push_protection_enabled"`
WebBasedCommitSigningEnabled *bool `json:"web_based_commit_signing_enabled"`
MaxPipelinesPerMergeTrain *int64 `json:"max_pipelines_per_merge_train"`
DuoRemoteFlowsEnabled *bool `json:"duo_remote_flows_enabled"`
DuoFoundationalFlowsEnabled *bool `json:"duo_foundational_flows_enabled"`
OnlyAllowMergeIfAllStatusChecksPassed *bool `json:"only_allow_merge_if_all_status_checks_passed"`
DuoSastFPDetectionEnabled *bool `json:"duo_sast_fp_detection_enabled"`
DuoSastVRWorkflowEnabled *bool `json:"duo_sast_vr_workflow_enabled"`
DuoSecretDetectionFPEnabled *bool `json:"duo_secret_detection_fp_enabled"`
DuoDependencyBumpBreakingChanges *bool `json:"duo_dependency_bump_breaking_changes_enabled"`
SecurityPolicyPipelineMustSucceed *bool `json:"security_policy_pipeline_must_succeed"`
SPPRepositoryPipelineAccess *bool `json:"spp_repository_pipeline_access"`
// CISkipBranchPipelinesForMRs is one of the CI/CD settings
// lib/api/entities/project.rb sends only to a caller holding admin_project
// on the project, and GitLab sends it from 19.4 (projects.md).
CISkipBranchPipelinesForMRs *bool `json:"ci_skip_branch_pipelines_for_mrs"`
// DuoCodeReviewDecisionsEnabled is whether GitLab Duo may approve a merge
// request or ask for changes on it after reviewing it, which
// ee/lib/ee/api/entities/project.rb sends from 19.5 only while the
// duo_code_review_decisions feature flag is on for the project and its
// Duo code review is available (Ultimate, projects.md).
DuoCodeReviewDecisionsEnabled *bool `json:"duo_code_review_decisions_enabled"`
}
ProjectExtra is what lib/api/entities/project.rb and its EE override send that client-go's Project does not model. Every field is a pointer because none of them is unconditional in practice: the first four are absent whenever the caller asked for the simple form, which renders BasicProjectDetails instead, and the rest are gated on a licensed feature, on an ability, or on GitLab.com.
The tiers here are read from the licensed feature each lambda names rather than from the audit's resolved tier, because gen_api_live captures a condition as source text over a line range and the text of every expose in ee/lib/ee/api/entities/project.rb runs on into the next one. Two fields are mis-tiered by that bleed: max_pipelines_per_merge_train gates on merge_trains (Premium) and reads as Ultimate from its neighbour's external_status_checks, and duo_foundational_flows_enabled gates on ai_workflows (Premium) and reads as Ultimate from ai_features.
func CapturedProject ¶ added in v3.1.0
func CapturedProject(capture *gitlabclient.ResponseCapture) (ProjectExtra, error)
CapturedProject reads, off the captured answer to a request that renders the full project entity, the keys client-go's Project does not model.
func CapturedProjects ¶ added in v3.1.0
func CapturedProjects(capture *gitlabclient.ResponseCapture, decoded int) ([]ProjectExtra, error)
CapturedProjects reads the same off a list answer, one extra per project in order, the count held to what the SDK decoded.
type ProtectedBranchExtra ¶
type ProtectedBranchExtra struct {
Inherited bool `json:"inherited"`
}
ProtectedBranchExtra is whether a protected branch rule was inherited from the group rather than declared on the project, exposed under no condition.
func CapturedProtectedBranch ¶
func CapturedProtectedBranch(capture *gitlabclient.ResponseCapture) (ProtectedBranchExtra, error)
CapturedProtectedBranch reads it off the captured answer to a request for one protected branch.
func CapturedProtectedBranches ¶
func CapturedProtectedBranches(capture *gitlabclient.ResponseCapture, decoded int) ([]ProtectedBranchExtra, error)
CapturedProtectedBranches reads the same off a list answer, one extra per branch in order, the count held to what the SDK decoded.
type QuickActionsStatusOutput ¶ added in v3.1.0
type QuickActionsStatusOutput struct {
CommandNames []string `json:"command_names,omitempty"`
CommandsOnly bool `json:"commands_only"`
Messages []string `json:"messages,omitempty"`
ErrorMessages []string `json:"error_messages,omitempty"`
}
QuickActionsStatusOutput mirrors GitLab's QuickActionsStatus, what a note mutation reports about the quick actions its body carried: the commands it recognized, whether the body held nothing else (in which case GitLab keeps no note), what the commands did, and what they failed to do. A nil pointer on an output is a body that carried none: GitLab answers a status for every note on an item that supports quick actions, an empty one when the body named no command, and the decoder reads the empty one as none.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter enforces a token-bucket rate limit on the methods that cost a deployment something: those reaching GitLab with the caller's credential, plus `completion/complete` and `tools/list`, each on the bucket AttachRateLimit describes. A zero RPS disables the limiter (the constructor returns nil and the resulting middleware is a no-op).
Limits are advisory; the primary defense remains GitLab's own per-token rate limits. The local limiter exists to soften bursts (typical LLM retry-loop with a flaky tool can fire dozens of identical calls per second) and to give operators a single knob they can tighten when they see 429s in practice. HTTP mode enables it by default (10 rps, burst 40); stdio leaves it off, since there the bucket would be global to one client.
The limiter shares a single bucket across the server. In HTTP mode each server instance from the pool gets its own RateLimiter, so the limit is effectively per token and GitLab URL. In stdio mode the bucket is global to the single process. The one exception is the listing bucket the whole process shares beside every entry's (see [processCatalog]).
rate.Limiter is safe for concurrent use by design, so RateLimiter does not need additional synchronization of its own for the requests it counts one at a time; takeMu is for the one bucket that counts a listing by its size.
func NewRateLimiter ¶
func NewRateLimiter(rps float64, burst int) *RateLimiter
NewRateLimiter builds a RateLimiter with the given rate (requests per second) and burst (maximum concurrent tokens in the bucket). Returns nil if rps <= 0, which the middleware treats as "disabled". Burst is clamped to a minimum of 1 when rps > 0 to avoid an unusable zero-burst limiter.
type ReferencesOutput ¶
type ReferencesOutput struct {
Short string `json:"short"`
Relative string `json:"relative"`
Full string `json:"full"`
}
ReferencesOutput mirrors gl.References (the short / relative / full reference triplet surfaced inside issue and MR resources).
func NewReferencesOutput ¶
func NewReferencesOutput(r *gl.IssueReferences) *ReferencesOutput
NewReferencesOutput converts a gl.IssueReferences value into the canonical-key references object, returning nil when the source is the zero value.
type RegistryRepositoryExtra ¶
type RegistryRepositoryExtra struct {
Size int64 `json:"size"`
DeleteAPIPath string `json:"delete_api_path"`
}
RegistryRepositoryExtra is what GitLab's container registry repository entity sends beside the name and the path: the size, when the caller asked for it, and the path a caller allowed to administer images deletes through.
func CapturedRegistryRepositories ¶
func CapturedRegistryRepositories(capture *gitlabclient.ResponseCapture, decoded int) ([]RegistryRepositoryExtra, error)
CapturedRegistryRepositories reads the same off a list answer, one extra per repository in order, the count held to what the SDK decoded.
func CapturedRegistryRepository ¶
func CapturedRegistryRepository(capture *gitlabclient.ResponseCapture) (RegistryRepositoryExtra, error)
CapturedRegistryRepository reads them off the captured answer to a request for one repository.
type ResourceMilestoneEventExtra ¶
type ResourceMilestoneEventExtra struct {
State string `json:"state"`
}
ResourceMilestoneEventExtra is the issue or merge request's own state at the moment the milestone changed, which the milestone event entity exposes under no condition.
func CapturedResourceMilestoneEvent ¶
func CapturedResourceMilestoneEvent(capture *gitlabclient.ResponseCapture) (ResourceMilestoneEventExtra, error)
CapturedResourceMilestoneEvent reads it off the captured answer to a request for one event.
func CapturedResourceMilestoneEvents ¶
func CapturedResourceMilestoneEvents(capture *gitlabclient.ResponseCapture, decoded int) ([]ResourceMilestoneEventExtra, error)
CapturedResourceMilestoneEvents reads the same off a list answer, one extra per event in order, the count held to what the SDK decoded.
type ResourceStateEventExtra ¶
type ResourceStateEventExtra struct {
SourceCommit string `json:"source_commit"`
SourceMergeRequestID int64 `json:"source_merge_request_id"`
}
ResourceStateEventExtra is what GitLab's resource state event entity sends that client-go's StateEvent does not carry: the commit that closed the issue, and the merge request that did, each empty when something else did.
func CapturedResourceStateEvent ¶
func CapturedResourceStateEvent(capture *gitlabclient.ResponseCapture) (ResourceStateEventExtra, error)
CapturedResourceStateEvent reads them off the captured answer to a request for one event.
func CapturedResourceStateEvents ¶
func CapturedResourceStateEvents(capture *gitlabclient.ResponseCapture, decoded int) ([]ResourceStateEventExtra, error)
CapturedResourceStateEvents reads the same off a list answer, one extra per event in order, the count held to what the SDK decoded.
type ResourceTokenExtra ¶
type ResourceTokenExtra struct {
TokenExtra
ResourceType string `json:"resource_type"`
ResourceID int64 `json:"resource_id"`
}
ResourceTokenExtra is what lib/api/entities/resource_access_token.rb sends beyond TokenExtra on a project or group access token: which kind of resource the token belongs to and its id, the second absent when the bot user has no namespace.
func CapturedResourceToken ¶
func CapturedResourceToken(capture *gitlabclient.ResponseCapture) (ResourceTokenExtra, error)
CapturedResourceToken reads, off the captured answer to a request for one project or group access token, the fields the SDK's resource access token does not model.
func CapturedResourceTokens ¶
func CapturedResourceTokens(capture *gitlabclient.ResponseCapture, decoded int) ([]ResourceTokenExtra, error)
CapturedResourceTokens reads the same off a list answer, one extra per token in order, the count held to what the SDK decoded.
type ResourceURIParamsProvider ¶ added in v3.3.0
type ResourceURIParamsProvider interface {
// ResourceURIParams returns the template variables the output fills,
// or nil when it has none to offer.
ResourceURIParams() map[string]any
}
ResourceURIParamsProvider is an action output that names the canonical resource it is a snapshot of more exactly than the call that produced it can. A get that takes a reference resolving to an object (a commit asked for by a branch name) is the case: the call names the reference, the output names the object, and only the object's URI keeps reading the snapshot it was embedded with.
type RunnerExtra ¶
type RunnerExtra struct {
CreatedAt *time.Time `json:"created_at"`
CreatedBy *UserBasicOutput `json:"created_by"`
JobExecutionStatus string `json:"job_execution_status"`
TokenExpiresAt *time.Time `json:"token_expires_at"`
}
RunnerExtra is what lib/api/entities/ci/runner.rb sends on a runner that client-go's Runner and RunnerDetails do not carry: when it was created, who created it, sent to a caller allowed to read that user, and the job execution status. TokenExpiresAt is what lib/api/entities/ci/runner_details.rb adds from GitLab 19.5 for a caller allowed to read the runner's sensitive data, so a list row never carries it and RunnerDetails does not model it.
func CapturedRunner ¶
func CapturedRunner(capture *gitlabclient.ResponseCapture) (RunnerExtra, error)
CapturedRunner reads, off the captured answer to a request for one runner, the fields client-go's runner structs do not model.
func CapturedRunners ¶
func CapturedRunners(capture *gitlabclient.ResponseCapture, decoded int) ([]RunnerExtra, error)
CapturedRunners reads the same off a list answer, one extra per runner in order, the count held to what the SDK decoded.
type RunnerManagerExtra ¶
type RunnerManagerExtra struct {
JobExecutionStatus string `json:"job_execution_status"`
}
RunnerManagerExtra is what the manager is doing now, exposed under no condition on the runner manager entity. It is the same key RunnerExtra carries on the runner itself and a different object: a runner's status is the aggregate of its managers'.
func CapturedRunnerManagers ¶
func CapturedRunnerManagers(capture *gitlabclient.ResponseCapture, decoded int) ([]RunnerManagerExtra, error)
CapturedRunnerManagers reads it off the captured answer to a list of managers, one extra per manager in order, the count held to what the SDK decoded.
type SAMLIdentityOutput ¶
type SAMLIdentityOutput struct {
ExternUID string `json:"extern_uid"`
Provider string `json:"provider"`
SAMLProviderID int64 `json:"saml_provider_id"`
}
SAMLIdentityOutput mirrors gl.GroupMemberSAMLIdentity (the group_saml_identity object).
func NewSAMLIdentityOutput ¶
func NewSAMLIdentityOutput(s *gl.GroupMemberSAMLIdentity) *SAMLIdentityOutput
NewSAMLIdentityOutput mirrors a gl.GroupMemberSAMLIdentity into the shared output shape, returning nil when the SDK value is nil.
type SCIMIdentityOutput ¶
type SCIMIdentityOutput struct {
ExternUID string `json:"extern_uid"`
GroupID int64 `json:"group_id"`
Active bool `json:"active"`
}
SCIMIdentityOutput mirrors the group_scim_identity object, ee/lib/api/entities/scim_identity.rb, which client-go does not model: the SCIM identity a member holds in an SSO-enabled group, sent to the group's owners.
type SSHCertificateExtra ¶ added in v3.3.0
type SSHCertificateExtra struct {
Fingerprint string `json:"fingerprint"`
}
SSHCertificateExtra is what lib/api/entities/ssh_certificate.rb sends on a group SSH certificate that client-go's GroupSSHCertificate does not carry: from GitLab 19.5, the SHA256 fingerprint of the certificate authority's public key, written with its SHA256: prefix, on every certificate.
func CapturedSSHCertificate ¶ added in v3.3.0
func CapturedSSHCertificate(capture *gitlabclient.ResponseCapture) (SSHCertificateExtra, error)
CapturedSSHCertificate reads it off the captured answer to a request for one certificate.
func CapturedSSHCertificates ¶ added in v3.3.0
func CapturedSSHCertificates(capture *gitlabclient.ResponseCapture, decoded int) ([]SSHCertificateExtra, error)
CapturedSSHCertificates reads the same off a list answer, one extra per certificate in order, the count held to what the SDK decoded.
type SafeModePreview ¶
type SafeModePreview struct {
Status string `json:"status"`
Mode string `json:"mode"`
Tool string `json:"tool"`
Params json.RawMessage `json:"params"`
Hint string `json:"hint"`
}
SafeModePreview is the structured response returned when a mutating operation is intercepted by Safe Mode. Status is always "blocked", Mode is "safe", Tool names the intercepted tool or canonical action, Params mirrors the would-be call arguments, and Hint tells the operator how to disable safe mode.
func NewSafeModePreview ¶
func NewSafeModePreview(name string, params any) SafeModePreview
NewSafeModePreview builds a preview for name, marshaling params defensively: when params cannot be marshaled the preview still reports the blocked operation with a null params payload rather than failing the call.
func ParseSafeModePreview ¶ added in v3.1.0
func ParseSafeModePreview(text string) (SafeModePreview, bool)
ParseSafeModePreview reads a preview back out of the card FormatSafeModePreviewMarkdown wrote, and reports whether the text is one: a card whose status row says blocked and whose mode row says safe. It is the one reader a test or an evaluator needs, so that none of them parses the card by hand.
type ServiceAccountExtra ¶
type ServiceAccountExtra struct {
PublicEmail string `json:"public_email"`
// External is a pointer so an older instance's answer, which carries no
// such key, reads as absent rather than as an internal account.
External *bool `json:"external"`
}
ServiceAccountExtra is what GitLab's service account entity sends beside the name and username that client-go's account structs do not all model: the public email always, the unconfirmed one while a change of address is waiting to be confirmed, and from 19.5 whether the account is external.
The public email is read here only for the project endpoint: client-go's ProjectServiceAccount models the unconfirmed address and not the public one, while GroupServiceAccount gained both in v3.12.0 and the group package reads them from the SDK. The same GitLab entity, modeled twice upstream and unevenly. Neither struct models external, so both packages read it here.
func CapturedServiceAccount ¶
func CapturedServiceAccount(capture *gitlabclient.ResponseCapture) (ServiceAccountExtra, error)
CapturedServiceAccount reads them off the captured answer to a request for one service account.
func CapturedServiceAccounts ¶
func CapturedServiceAccounts(capture *gitlabclient.ResponseCapture, decoded int) ([]ServiceAccountExtra, error)
CapturedServiceAccounts reads the same off a list answer, one extra per account in order, the count held to what the SDK decoded.
type StorageMoveEntityMarkdown ¶
StorageMoveEntityMarkdown carries the optional GitLab resource associated with a repository storage move.
func NewStorageMoveEntityMarkdown ¶
func NewStorageMoveEntityMarkdown(label, name, url string, id int64) *StorageMoveEntityMarkdown
NewStorageMoveEntityMarkdown builds the optional entity view model for a repository storage move.
type StorageMoveExtra ¶
type StorageMoveExtra struct {
ErrorMessage string `json:"error_message"`
}
StorageMoveExtra is why a repository storage move failed, exposed under no condition by the group, project and snippet storage move entities alike and empty on a move that did not fail.
func CapturedStorageMove ¶
func CapturedStorageMove(capture *gitlabclient.ResponseCapture) (StorageMoveExtra, error)
CapturedStorageMove reads it off the captured answer to a request for one storage move.
func CapturedStorageMoves ¶
func CapturedStorageMoves(capture *gitlabclient.ResponseCapture, decoded int) ([]StorageMoveExtra, error)
CapturedStorageMoves reads the same off a list answer, one extra per move in order, the count held to what the SDK decoded.
type StorageMoveMarkdown ¶
type StorageMoveMarkdown struct {
ID int64
State string
SourceStorageName string
DestinationStorageName string
CreatedAt time.Time
Entity *StorageMoveEntityMarkdown
}
StorageMoveMarkdown carries the common fields rendered by repository storage move tools at group, snippet, and other resource scopes.
func NewStorageMoveMarkdown ¶
func NewStorageMoveMarkdown(id int64, state, sourceStorageName, destinationStorageName string, createdAt time.Time, entity *StorageMoveEntityMarkdown) StorageMoveMarkdown
NewStorageMoveMarkdown builds a shared repository storage move Markdown view model without forcing tool packages to duplicate composite literals.
type StringOrInt ¶
type StringOrInt string
StringOrInt is a string type that accepts both JSON strings and JSON numbers during unmarshalling. It always stores the value as a string internally. This is needed because LLMs frequently send numeric IDs (e.g. 405) as JSON numbers rather than strings, even when the schema declares "type": "string".
func (StringOrInt) Int64 ¶
func (s StringOrInt) Int64() (int64, error)
Int64 parses the stored string as a base-10 integer and returns it. Returns 0 and an error if the value is empty or not a valid integer.
func (StringOrInt) MarshalJSON ¶
func (s StringOrInt) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler to always output a JSON string.
func (StringOrInt) String ¶
func (s StringOrInt) String() string
String returns the underlying string value.
func (*StringOrInt) UnmarshalJSON ¶
func (s *StringOrInt) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler to accept both JSON strings (e.g. "405", "group/project") and JSON numbers (e.g. 405, 42.0).
type SuggestionOutput ¶
type SuggestionOutput struct {
ID int64 `json:"id"`
FromLine int64 `json:"from_line"`
ToLine int64 `json:"to_line"`
Appliable bool `json:"appliable"`
Applied bool `json:"applied"`
FromContent string `json:"from_content"`
ToContent string `json:"to_content"`
}
SuggestionOutput mirrors GitLab's Suggestion entity, the code suggestion a merge request diff note carries.
type SurfaceToolRegisterOptions ¶
type SurfaceToolRegisterOptions struct {
Description string
Icons []mcp.Icon
FormatResult FormatResultFunc
}
SurfaceToolRegisterOptions controls how an ActionSpec is exposed as a standalone visible MCP tool.
type TaskCompletionStatusOutput ¶
type TaskCompletionStatusOutput struct {
Count int64 `json:"count"`
CompletedCount int64 `json:"completed_count"`
}
TaskCompletionStatusOutput mirrors gl.TaskCompletionStatus (the task-count object surfaced on merge requests).
func NewTaskCompletionStatusOutput ¶
func NewTaskCompletionStatusOutput(t *gl.TasksCompletionStatus) *TaskCompletionStatusOutput
NewTaskCompletionStatusOutput converts a gl.TasksCompletionStatus pointer into the canonical-key task status object, returning nil for a nil source.
type TemplateAttributeListMarkdownItem ¶
TemplateAttributeListMarkdownItem carries common list-row fields for template-style Markdown tables, with an optional third attribute column.
type TemplateAttributeListMarkdownOptions ¶
type TemplateAttributeListMarkdownOptions struct {
Title string
EmptyMessage string
AttributeHeader string
Pagination PaginationOutput
Hints []string
}
TemplateAttributeListMarkdownOptions configures template-style list rendering. An empty AttributeHeader renders the two-column Key/Name table the plain template families use; a named one adds the third column.
type TemplateDetailMarkdown ¶
type TemplateDetailMarkdown struct {
Title string
Key string
Nickname string
Popular bool
Description string
Permissions []string
Conditions []string
Limitations []string
Content string
ContentHeading string
Hints []string
}
TemplateDetailMarkdown carries common fields for template-style detail pages. Every family renders its fields the same way, as card rows: the switch that let the license family write bullet-less label lines is gone, since consecutive lines of that shape rendered as one run-on paragraph.
type TemplateMarkdown ¶
TemplateMarkdown carries common fields rendered by template list tools.
type TemplateRenderer ¶
type TemplateRenderer struct {
ListTitle string
EmptyMessage string
ListHint string
DetailTitle string
Language string
DetailHint string
}
TemplateRenderer stores the stable labels and hints for a GitLab template family so package formatters can avoid repeating identical rendering glue.
func NewTemplateRenderer ¶
func NewTemplateRenderer(listTitle, emptyMessage, listHint, detailTitle, language, detailHint string) TemplateRenderer
NewTemplateRenderer builds a renderer for a GitLab template family.
func (TemplateRenderer) FormatContent ¶
func (r TemplateRenderer) FormatContent(name, content string) string
FormatContent renders a single GitLab template body with the renderer configuration.
func (TemplateRenderer) FormatList ¶
func (r TemplateRenderer) FormatList(templates []TemplateMarkdown, pagination PaginationOutput) string
FormatList renders a GitLab template list with the renderer configuration, as the two-column form of FormatTemplateAttributeListMarkdown: one table renderer serves the families with an attribute column and the ones without, so they cannot disagree on the heading, the empty form or the footer.
type TimeInput ¶ added in v3.3.0
TimeInput names one timestamp input and where its parsed value goes.
type TimeStatsOutput ¶
type TimeStatsOutput struct {
HumanTimeEstimate string `json:"human_time_estimate"`
HumanTotalTimeSpent string `json:"human_total_time_spent"`
TimeEstimate int64 `json:"time_estimate"`
TotalTimeSpent int64 `json:"total_time_spent"`
}
TimeStatsOutput mirrors gl.TimeStats (the time-tracking sub-object on merge requests and issues). It is the pure nested form — no next_steps — used when time_stats appears as a sub-field of another output type rather than as the standalone return value of a time-tracking handler. The standalone handlers (SetTimeEstimate, AddSpentTime, etc.) compose this with HintableOutput to add next_steps at the top level of their response.
func NewTimeStatsOutput ¶
func NewTimeStatsOutput(t *gl.TimeStats) *TimeStatsOutput
NewTimeStatsOutput converts a gl.TimeStats pointer to the canonical pure TimeStatsOutput, returning nil when the source is nil.
type TodoExtra ¶
type TodoExtra struct {
Author UserBasicExtra `json:"author"`
}
TodoExtra is what GitLab's todo entity sends that client-go's Todo does not carry: the two keys of its author's UserBasic that client-go's BasicUser leaves out, which UserBasicFrom completes the author with. client-go's Todo decodes the todo's own updated_at and group as of v3.20.0.
func CapturedTodo ¶
func CapturedTodo(capture *gitlabclient.ResponseCapture) (TodoExtra, error)
CapturedTodo reads it off the captured answer to a request that returned one todo.
type TokenExtra ¶
type TokenExtra struct {
Granular bool `json:"granular"`
GranularScopes []TokenGranularScopeOutput `json:"granular_scopes"`
LastUsedIPs []string `json:"last_used_ips"`
}
TokenExtra is what lib/api/entities/personal_access_token.rb and the entities inheriting it send on a token that client-go's PersonalAccessToken does not carry: granular on every token, granular_scopes when the token is granular and the endpoint asked for them, and last_used_ips while the instance has the feature flag on.
func CapturedToken ¶
func CapturedToken(capture *gitlabclient.ResponseCapture) (TokenExtra, error)
CapturedToken reads, off the captured answer to a request for one token, the fields client-go's PersonalAccessToken does not model.
func CapturedTokens ¶
func CapturedTokens(capture *gitlabclient.ResponseCapture, decoded int) ([]TokenExtra, error)
CapturedTokens reads the same off a list answer, one extra per token in order, the count held to what the SDK decoded.
type TokenGranularScopeOutput ¶
type TokenGranularScopeOutput struct {
Access string `json:"access"`
Permissions []string `json:"permissions"`
ProjectID int64 `json:"project_id,omitempty"`
GroupID int64 `json:"group_id,omitempty"`
}
TokenGranularScopeOutput mirrors lib/api/entities/personal_access_token_granular_scope.rb, one entry of the granular_scopes array a granular token carries. project_id is set only when the scope's namespace is a project and group_id only when it is a group, so each is absent rather than zero on the other kind.
type ToolError ¶
type ToolError struct {
Tool string `json:"tool"`
Message string `json:"message"`
StatusCode int `json:"status_code,omitempty"`
}
ToolError represents a structured error from a tool handler.
type UploadConfig ¶
type UploadConfig struct {
MaxFileSize int64
}
UploadConfig holds runtime-configurable upload parameters. Initialized with package defaults; use SetUploadConfig to override from environment config.
func GetUploadConfig ¶
func GetUploadConfig() UploadConfig
GetUploadConfig returns the current upload configuration (for testing).
type UserBasicExtra ¶ added in v3.1.0
UserBasicExtra is what lib/api/entities/user_basic.rb sends that client-go's ProjectUser does not model, and neither does its BasicUser, which is what a to-do's author and the user who ran a package's pipeline decode into. Both are unconditional on that entity, so a plain value is the honest shape here.
func CapturedNestedUserBasics ¶ added in v3.1.0
func CapturedNestedUserBasics(capture *gitlabclient.ResponseCapture, decoded int) ([]UserBasicExtra, error)
CapturedNestedUserBasics reads the same two keys off a list answer whose rows nest the user rather than being one, one extra per row in order.
func CapturedUserBasics ¶ added in v3.1.0
func CapturedUserBasics(capture *gitlabclient.ResponseCapture, decoded int) ([]UserBasicExtra, error)
CapturedUserBasics reads, off the captured answer to a request that renders a page of the basic user entity, the two keys the SDK does not model, one extra per user in order.
type UserBasicOutput ¶
type UserBasicOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
PublicEmail string `json:"public_email,omitempty"`
Name string `json:"name"`
State string `json:"state,omitempty"`
Locked bool `json:"locked"`
AvatarURL string `json:"avatar_url,omitempty"`
WebURL string `json:"web_url,omitempty"`
}
UserBasicOutput mirrors lib/api/entities/user_basic.rb, the user object GitLab renders on a note's author and resolver and on a runner's creator.
func UserBasicFrom ¶ added in v3.1.0
func UserBasicFrom(u *gl.BasicUser, extra UserBasicExtra) *UserBasicOutput
UserBasicFrom converts a user client-go decodes into BasicUser to the whole of lib/api/entities/user_basic.rb, with the two keys BasicUser does not model taken from what the captured answer carried for the same user. The seventh key BasicUser decodes, created_at, is not one UserBasic sends, so it is not published. It returns nil for a user the answer did not carry; the extra read off a null or absent user is the zero value, so nothing is lost.
type UserCoreRefOutput ¶ added in v3.1.0
type UserCoreRefOutput struct {
Username string `json:"username"`
Name string `json:"name,omitempty"`
WebURL string `json:"web_url,omitempty"`
}
UserCoreRefOutput names a person GitLab recorded against an object, such as who confirmed, dismissed or resolved a vulnerability.
type UserExtra ¶
type UserExtra struct {
CommitEmail string `json:"commit_email"`
Discord string `json:"discord"`
GitHub string `json:"github"`
LocalTime string `json:"local_time"`
PreferredLanguage string `json:"preferred_language"`
Pronouns string `json:"pronouns"`
WorkInformation string `json:"work_information"`
Followers *int64 `json:"followers"`
Following *int64 `json:"following"`
IsFollowed *bool `json:"is_followed"`
}
UserExtra is what lib/api/entities/user_public.rb, and the User it inherits, send on a user that client-go's User does not carry. It is the set every route presenting UserPublic answers with, which is what the group-scoped user lists (enterprise users, provisioned users, SAML users) and the instance-wide ones alike present, so the four packages publishing a user share it.
Seven of the ten are exposed under no condition. The three counts are gated on Ability.allowed?(current_user, :read_user_profile, user) together with following_users_allowed, so the same endpoint sends them to a caller who may read the profile and to nobody else. Each is a pointer because zero followers and a profile this caller may not read are different answers, and a bare number would tell them apart from nothing.
func CapturedUser ¶
func CapturedUser(capture *gitlabclient.ResponseCapture) (UserExtra, error)
CapturedUser reads, off the captured answer to a request for one user, the fields client-go's User does not model.
func CapturedUsers ¶
func CapturedUsers(capture *gitlabclient.ResponseCapture, decoded int) ([]UserExtra, error)
CapturedUsers reads the same off a list answer, one extra per user in order, the count held to what the SDK decoded.
type UserIdentity ¶
type UserIdentity struct {
UserID string
Username string
// Instance is the GitLab instance the identity was resolved against, when
// the caller knows it.
//
// A GitLab user id is unique within an instance and means nothing across
// them, so a deployment publishing several through --gitlab-url can log two
// different people as user_id 7. ADR-0008 fixes this struct as
// {UserID, Username} and motivates it expressly for audit logging, without
// recording that limit; an audit trail whose subject is ambiguous is the one
// place it matters.
//
// Empty where there is nothing to say: stdio resolves one identity at
// startup against one instance, and a deployment that pins --gitlab-url has
// only the one. The field is filled where it is already known rather than
// resolved again: the HTTP gate computes the instance per request to pick a
// pool entry and used to throw it away.
Instance string
}
UserIdentity holds the authenticated user's identity. Populated from OAuth TokenInfo (HTTP modes) or from the startup-resolved identity stored in context (stdio mode).
func IdentityFromContext ¶
func IdentityFromContext(ctx context.Context) UserIdentity
IdentityFromContext retrieves the UserIdentity stored in the context. Returns a zero-value UserIdentity if none was stored.
func ResolveIdentity ¶
func ResolveIdentity(ctx context.Context, req *mcp.CallToolRequest) UserIdentity
ResolveIdentity returns the authenticated user's identity by checking two sources in priority order:
- req.Extra.TokenInfo (populated by SDK in HTTP modes via RequireBearerToken)
- Context-stored identity (populated at startup in stdio mode)
Returns a zero-value UserIdentity if neither source has identity.
func (UserIdentity) IsAuthenticated ¶
func (u UserIdentity) IsAuthenticated() bool
IsAuthenticated returns true if the identity contains a non-empty UserID.
type UserRefOutput ¶
type UserRefOutput struct {
ID int64 `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
State string `json:"state"`
AvatarURL string `json:"avatar_url,omitempty"`
WebURL string `json:"web_url,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
UserRefOutput mirrors gl.BasicUser as surfaced on user resources (the created_by object): identity fields always present, avatar/web URL and created_at omitted when empty. It differs from BasicUserOutput (pipeline sub-objects), whose avatar_url and web_url are always serialized.
func NewUserRefOutput ¶
func NewUserRefOutput(u *gl.BasicUser) *UserRefOutput
NewUserRefOutput converts a *gl.BasicUser into the user-resource reference shape, returning nil when the SDK value is nil.
type VoidOutput ¶
type VoidOutput struct {
HintableOutput
Status string `json:"status"`
Message string `json:"message"`
}
VoidOutput is a confirmation message returned by tool handlers that perform an action without returning domain data (e.g., set header, start mirroring).
func VoidResult ¶
func VoidResult(message string) (*mcp.CallToolResult, VoidOutput, error)
VoidResult builds a VoidOutput and its Markdown representation for a successful void operation. The message describes what happened, and reaches the Markdown through the same escaper DeleteResult uses and for the same reason.
type VulnerabilityLinkOutput ¶ added in v3.1.0
VulnerabilityLinkOutput is one reference the report attached. The name is optional in the report and the address is not.
func VulnerabilityLinkOutputs ¶ added in v3.1.0
func VulnerabilityLinkOutputs(links []GraphQLVulnerabilityLink) []VulnerabilityLinkOutput
VulnerabilityLinkOutputs converts the report's references, and answers nil for none so the field is left out rather than written as an empty list.
type VulnerabilityLocationOutput ¶ added in v3.1.0
type VulnerabilityLocationOutput struct {
File string `json:"file,omitempty"`
StartLine int `json:"start_line,omitempty"`
EndLine int `json:"end_line,omitempty"`
BlobPath string `json:"blob_path,omitempty"`
VulnerableClass string `json:"vulnerable_class,omitempty"`
VulnerableMethod string `json:"vulnerable_method,omitempty"`
CrashAddress string `json:"crash_address,omitempty"`
CrashType string `json:"crash_type,omitempty"`
StacktraceSnippet string `json:"stacktrace_snippet,omitempty"`
Hostname string `json:"hostname,omitempty"`
RequestMethod string `json:"request_method,omitempty"`
Param string `json:"param,omitempty"`
OperatingSystem string `json:"operating_system,omitempty"`
ContainerRepositoryURL string `json:"container_repository_url,omitempty"`
Description string `json:"description,omitempty"`
Dependency *VulnerableDependencyOutput `json:"dependency,omitempty"`
KubernetesResource *VulnerableKubernetesResourceOutput `json:"kubernetes_resource,omitempty"`
}
VulnerabilityLocationOutput is where a scanner found a vulnerability, in the terms its scan type uses: a file and a line range for SAST, secret detection and coverage fuzzing, a request for DAST, a package and a version for dependency and container scanning, a workload for a cluster image scan, and free prose for a generic scanner.
File is the subject every scan type has one of: the file, and for a location that names none, the DAST request path or the container image, which is how it read before the other members were decoded and what a caller that reads File alone still finds.
type VulnerabilityTokenStatusOutput ¶ added in v3.1.0
type VulnerabilityTokenStatusOutput struct {
Status string `json:"status"`
LastVerifiedAt string `json:"last_verified_at,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
VulnerabilityTokenStatusOutput says whether a leaked secret still works: ACTIVE, INACTIVE or UNKNOWN, as GitLab last verified it with the service that issued it. It is what decides whether a secret detection finding is an incident or a clean-up.
type VulnerableDependencyOutput ¶ added in v3.1.0
type VulnerableDependencyOutput struct {
PackageName string `json:"package_name,omitempty"`
PackagePath string `json:"package_path,omitempty"`
Version string `json:"version,omitempty"`
}
VulnerableDependencyOutput is the vulnerable package and its version.
type VulnerableKubernetesResourceOutput ¶ added in v3.1.0
type VulnerableKubernetesResourceOutput struct {
Kind string `json:"kind,omitempty"`
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
ContainerName string `json:"container_name,omitempty"`
AgentName string `json:"agent_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
}
VulnerableKubernetesResourceOutput is the workload a cluster image scan found running the vulnerable image, and the agent that scanned it.
type WikiExtra ¶
type WikiExtra struct {
WikiPageMetaID int64 `json:"wiki_page_meta_id"`
FrontMatter map[string]any `json:"front_matter"`
}
WikiExtra is what GitLab's wiki page entity sends beside the title and the content: the identifier of the page's metadata record, and the YAML front matter parsed out of the page, which is a map of whatever keys the author wrote. Both are exposed under no condition, and the front matter is empty on a page that has none.
func CapturedWiki ¶
func CapturedWiki(capture *gitlabclient.ResponseCapture) (WikiExtra, error)
CapturedWiki reads them off the captured answer to a request for one page.
func CapturedWikis ¶
func CapturedWikis(capture *gitlabclient.ResponseCapture, decoded int) ([]WikiExtra, error)
CapturedWikis reads the same off a list answer, one extra per page in order, the count held to what the SDK decoded.
type WorkItemWidgetInputs ¶ added in v3.3.0
type WorkItemWidgetInputs struct {
MilestoneID *int64
ParentID *int64
IterationID *int64
StartDate *string
DueDate *string
HealthStatus *string
}
WorkItemWidgetInputs are the inputs of a work item update that set a value GitLab can also be asked to remove: the milestone, parent and iteration a work item references, its start and due dates, and its health status.
Each follows the clearing convention every update here follows (issue 1303): nil leaves the value alone, 0 removes a reference, "" removes a date or the health status, and anything else sets it.
Source Files
¶
- access_level.go
- action_meta.go
- action_schema_override.go
- action_spec.go
- action_spec_individual.go
- action_timeout.go
- annotations.go
- approver_ids.go
- board_markdown.go
- board_shapes.go
- brandmark_gen.go
- card.go
- confirm.go
- constants.go
- diff.go
- diff_position.go
- discussion_guidance.go
- doc.go
- download_ceiling.go
- download_output.go
- download_output_unix.go
- embed.go
- embed_template.go
- errors.go
- file_source.go
- file_utils.go
- file_utils_unix.go
- fine_grained.go
- graphql.go
- graphql_note_mutation.go
- hints.go
- icons.go
- identity.go
- instance_url.go
- issue_shapes.go
- json_body.go
- label_markdown.go
- logging.go
- markdown.go
- md_registry.go
- member_capture.go
- member_shapes.go
- meta_schema.go
- meta_tool.go
- mr_shapes.go
- names.go
- not_found.go
- note_capture.go
- note_shapes.go
- once_map.go
- output.go
- pagination.go
- pipeline_inputs.go
- pipeline_shapes.go
- polling.go
- rate_limit.go
- release_shapes.go
- result_type.go
- rpc_code.go
- safe_mode.go
- schema_compile.go
- schema_lockdown.go
- schema_middleware.go
- schema_pagination.go
- schema_share.go
- schema_type_overrides.go
- sent_shapes.go
- string_or_int.go
- surface_tool.go
- template_markdown.go
- text.go
- time_helpers.go
- tool_listing.go
- unconfirmed_write.go
- url_redact.go
- user_id_filter.go
- user_shapes.go
- vulnerability_shapes.go
- work_item_widgets.go