actioncatalog

package
v3.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package actioncatalog provides the canonical GitLab action catalog shared by catalog-backed MCP tool surfaces.

The catalog is the intermediate action core between typed GitLab handlers and the public meta and dynamic tool surfaces. It stores executable actions as deterministic groups, preserving route metadata such as input schemas, output schemas, destructive flags, action aliases, tags, icons, descriptions, and formatter hooks.

Action IDs use the stable domain.action form derived from the backing meta-tool name and action name. For example, gitlab_project/create becomes project.create, and gitlab_merge_request/list becomes merge_request.list. Dynamic mode uses these IDs directly, while meta-tools expose the same routes through action dispatch inside domain tools.

This package is not the registry for individual MCP tools. Individual tools are still registered directly by internal/tools.RegisterAll for compatibility. Meta-tools and dynamic tools consume this catalog through adapters such as internal/tools.RegisterMetaCatalog and internal/tools/dynamic.NewRegistryFromCatalog.

Invariants

Catalog construction must be deterministic. Groups preserve explicit action order for user-facing descriptions, cloning avoids mutable alias/schema sharing between surfaces, and validation rejects duplicate action IDs or ambiguous aliases before tools are registered.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DomainFromToolName

func DomainFromToolName(toolName string) string

DomainFromToolName returns the canonical dynamic domain for a meta-tool name.

func ToActionMaps

func ToActionMaps(catalog *Catalog) map[string]toolutil.ActionMap

ToActionMaps returns legacy route maps for compatibility with existing schema resources, audits, and registration paths.

Types

type Action

type Action struct {
	ID                     ActionID
	ToolName               string
	Domain                 string
	Name                   string
	Route                  toolutil.ActionRoute
	SchemaURI              string
	Aliases                []string
	Tags                   []string
	Usage                  string
	RelatedActions         []string
	Compatibility          toolutil.CompatibilityPolicy
	ReadOnly               bool
	Edition                string
	GitLabDotComOnly       bool
	OwnerPackage           string
	IndividualTool         toolutil.IndividualToolSpec
	ContentKind            string
	NotFoundPolicy         string
	EmbeddedResourcePolicy string
	EmbeddedResource       string
	RichResultPolicy       string
	SchemaValidationNotes  []string
	RuntimeValidationNotes []string
	SpecBacked             bool
	Destructive            bool
	Idempotent             bool
	OpenWorld              bool
}

Action describes one executable GitLab action in the canonical catalog.

func ActionsFromSpecs

func ActionsFromSpecs(specs []toolutil.ActionSpec) ([]Action, error)

ActionsFromSpecs projects canonical action specs into catalog actions.

type ActionID

type ActionID string

ActionID is the stable dynamic identifier for one GitLab action.

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog stores deterministic groups and action lookup indexes. A Catalog is intended to be mutated during single-threaded initialization and then shared read-only; concurrent mutation is not supported.

What a catalog shares

Every accessor returns copies of the Group and Action values, so a caller may set fields and append to slices without reaching the catalog. What the copies do not include is the route metadata: the InputSchema, OutputSchema and ParameterGuidance maps of every route are frozen and shared, between the copies, the catalog, and every other catalog derived from it by filtering or binding. The accessors used to deep-copy them, and in the HTTP pool, where a catalog is consulted several times for each of a thousand credentials, those copies were half of the process's heap. A consumer that must change a schema derives its own through toolutil.DeriveSchema.

Shared and bound catalogs

A catalog built once per configuration and cached for the process is marked with Catalog.MarkShared; its handlers are bound to no credential. Each server gets Catalog.BindTo of it: the same groups and actions, with the handlers rebuilt for that server's client. A bound catalog remembers the shared one it came from as its origin, and consumers that build something client-independent from a catalog (the dynamic search index, the tool manifest, the telemetry identifier) key it on Catalog.SharedOrigin so every server of one configuration shares one.

func FromActionMaps

func FromActionMaps(routes map[string]toolutil.ActionMap) *Catalog

FromActionMaps converts legacy route maps into a canonical catalog.

func FromActionMapsWithError

func FromActionMapsWithError(routes map[string]toolutil.ActionMap) (*Catalog, error)

FromActionMapsWithError converts legacy route maps into a canonical catalog and reports invalid groups instead of panicking.

func NewCatalog

func NewCatalog() *Catalog

NewCatalog creates an empty action catalog.

func (*Catalog) Action

func (c *Catalog) Action(id ActionID) (Action, bool)

Action returns a copy of one action by canonical ID, sharing the route schemas as described on Catalog.

func (*Catalog) ActionMaps

func (c *Catalog) ActionMaps() map[string]toolutil.ActionMap

ActionMaps returns a legacy route snapshot keyed by tool and action. The maps are the caller's; the routes share their schemas with the catalog.

func (*Catalog) Actions

func (c *Catalog) Actions() []Action

Actions returns copies of all actions sorted by canonical ID, sharing the route schemas as described on Catalog.

func (*Catalog) AddAction

func (c *Catalog) AddAction(toolName string, action Action, groupOptions ...GroupOptions) error

AddAction adds one action to an existing or newly-created group. When the group does not exist, callers may provide GroupOptions so the synthesized group carries the same metadata as a normal catalog group.

func (*Catalog) AddGroup

func (c *Catalog) AddGroup(group Group) error

AddGroup adds a complete group to the catalog.

func (*Catalog) BindTo

func (c *Catalog) BindTo(client *gitlabclient.Client) *Catalog

BindTo returns this catalog with every handler rebuilt for client, through toolutil.ActionRoute.BindTo. The groups, the actions and the route metadata are the same; only the handlers are new, which is the whole per-credential cost of a surface built from a shared catalog. The result remembers this catalog's Catalog.SharedOrigin as its own.

func (*Catalog) Clone

func (c *Catalog) Clone() *Catalog

Clone returns a copy of the catalog that may be added to and filtered without reaching the original, sharing the route schemas as described on Catalog. The copy is a new catalog with no shared origin: what is added to it afterwards makes it a different action set.

func (*Catalog) CountActions

func (c *Catalog) CountActions() int

CountActions returns the number of actions in the catalog.

func (*Catalog) CountGroups

func (c *Catalog) CountGroups() int

CountGroups returns the number of groups in the catalog.

func (*Catalog) ExcludedActionIDs

func (c *Catalog) ExcludedActionIDs(excludeTools []string) []string

ExcludedActionIDs returns the canonical action IDs excludeTools removes from this catalog, sorted.

The tool surface is not the only request path to a GitLab object: a resource template returns the same data through the same credential, and its registration knows nothing about tool names. Resolving the operator's entries — group name, individual tool name or action ID alike — to canonical IDs here is what lets that other surface apply the same narrowing from one table keyed by one kind of name, instead of repeating this matcher.

func (*Catalog) Filter

func (c *Catalog) Filter(opts FilterOptions) *Catalog

Filter applies all catalog-level filters in a deterministic order.

func (*Catalog) FilterAllowedToolNames

func (c *Catalog) FilterAllowedToolNames(toolNames []string) *Catalog

FilterAllowedToolNames returns a cloned catalog with only explicitly allowed tools.

func (*Catalog) FilterExcludedToolNames

func (c *Catalog) FilterExcludedToolNames(excludeTools []string) (filtered *Catalog, unmatched []string)

FilterExcludedToolNames returns a cloned catalog without the tools and actions named by excludeTools, together with the entries that matched nothing in this catalog.

An entry is matched against three names, because three names reach the same action depending on which surface is registered:

  • a group's meta-tool name ("gitlab_issue"), which removes the group and every action in it;
  • an action's individual tool name ("gitlab_issue_delete"), which removes that one action;
  • the canonical action ID ("issue.delete"), which the dynamic surface takes directly, likewise.

Only the group name matched before. The default surface is dynamic, where the two visible tools reach every action by canonical ID, so an operator who hardened a deployment with EXCLUDE_TOOLS=gitlab_issue_delete got a server that still executed it and a startup log that said nothing was excluded.

Aliases are deliberately not matched. Alias resolution is fuzzy by design, and an exclusion that removes more than the operator named is a different defect rather than a fix for this one.

A group whose every action was excluded is dropped: a dispatcher that can only refuse is worse than no tool at all. A group that was already empty is left alone so catalog validation can still report it.

func (*Catalog) FilterExcludedTools

func (c *Catalog) FilterExcludedTools(excludeTools []string) *Catalog

FilterExcludedTools returns a cloned catalog without the tools and actions named by excludeTools.

Entries that name nothing on this surface are logged at WARN rather than refused: one configuration is routinely reused across Free, Premium and Ultimate instances, and a lower-tier catalog legitimately lacks tools the same file names. The warning is deliberately operator-facing only — the excluded actions must stay out of the client-facing withheld list, or the dynamic registry would name back the very actions the operator removed.

Standalone utility tools (gitlab_discover_project, gitlab_interactive_*) are not in this catalog; they are filtered where they are added, so the warning names them too. Use Catalog.FilterExcludedToolNames when the caller can account for those before reporting.

func (*Catalog) FilterReadOnlyActions

func (c *Catalog) FilterReadOnlyActions() *Catalog

FilterReadOnlyActions returns a cloned catalog containing only read-only actions, filtered at action granularity rather than group granularity.

A group that mixes reads and writes keeps its read-only actions instead of disappearing entirely, which is what read-only mode promises: mutating operations are removed, reads keep working. Groups left without a single read-only action are dropped, and every surviving group is marked ReadOnly so derived tool annotations agree with the actions the group actually exposes (otherwise read-only tool pruning would remove the very tools this filter just built).

func (*Catalog) FilterReadOnlyGroups

func (c *Catalog) FilterReadOnlyGroups() *Catalog

FilterReadOnlyGroups returns a cloned catalog containing only read-only groups.

func (*Catalog) Group

func (c *Catalog) Group(toolName string) (Group, bool)

Group returns a copy of one group by tool name, sharing the route schemas as described on Catalog.

func (*Catalog) Groups

func (c *Catalog) Groups() []Group

Groups returns copies of all groups sorted by tool name, sharing the route schemas as described on Catalog.

func (*Catalog) MarkShared

func (c *Catalog) MarkShared()

MarkShared records that this catalog is the process-lived copy of its configuration and registers every route schema in it with toolutil.ShareSchema, so the transforms every server derives from them are built once. Call it only from a cache that keeps the catalog for the rest of the process: a registered schema is never collected, and a catalog's identity may key other caches from then on.

func (*Catalog) SharedOrigin

func (c *Catalog) SharedOrigin() *Catalog

SharedOrigin returns the shared catalog this one is, or was bound from, and nil for a catalog that is neither. It is the identity to key a client-independent derivation on: every server of one configuration binds the same shared catalog, and a catalog nobody shared has no identity worth caching under.

func (*Catalog) Validate

func (c *Catalog) Validate() error

Validate verifies that the catalog has a consistent, executable action index.

func (*Catalog) WithSafeModePreviews

func (c *Catalog) WithSafeModePreviews() *Catalog

WithSafeModePreviews returns a cloned catalog in which every mutating action is replaced by a handler returning a toolutil.SafeModePreview naming that action, leaving read-only actions untouched.

Safe Mode is a per-action policy, but dispatcher surfaces (meta-tools and the dynamic execute tool) expose one tool covering many actions, so intercepting at the tool level would block that tool's reads too. Rewriting the catalog instead keeps the interception exactly as granular as the policy: reads execute, writes preview, and the preview names the action rather than the dispatcher. Destructive is cleared on rewritten actions because nothing is executed, so there is nothing to confirm.

type CatalogGroupSpec

type CatalogGroupSpec struct {
	ToolName               string
	Title                  string
	Description            string
	ReadOnly               bool
	Icons                  []mcp.Icon
	BaseDomain             string
	EnterpriseOnly         bool
	GitLabDotComOnly       bool
	CapabilityRequirements []string
	FormatResult           toolutil.FormatResultFunc
	Actions                []toolutil.ActionSpec
	OwnerPackage           string
	SurfaceKind            SurfaceKind
}

CatalogGroupSpec is the canonical metadata contract for one catalog group.

func CloneCatalogGroupSpec

func CloneCatalogGroupSpec(spec CatalogGroupSpec) CatalogGroupSpec

CloneCatalogGroupSpec returns a defensive copy of group metadata.

func (CatalogGroupSpec) GroupOptions

func (spec CatalogGroupSpec) GroupOptions() GroupOptions

GroupOptions returns catalog group options projected from the group spec.

func (CatalogGroupSpec) Validate

func (spec CatalogGroupSpec) Validate() error

Validate verifies group-level catalog invariants before runtime projection.

type FilterOptions

type FilterOptions struct {
	ExcludeTools     []string
	ReadOnlyOnly     bool
	AllowedToolNames []string
}

FilterOptions describes catalog-level filtering inputs.

type Group

type Group struct {
	ToolName               string
	Title                  string
	Description            string
	Icons                  []mcp.Icon
	ReadOnly               bool
	FormatResult           toolutil.FormatResultFunc
	BaseDomain             string
	EnterpriseOnly         bool
	GitLabDotComOnly       bool
	CapabilityRequirements []string
	OwnerPackage           string
	SurfaceKind            SurfaceKind
	Actions                map[string]Action
	ActionOrder            []string
}

Group describes all actions exposed through one logical meta-tool group.

func GroupFromSpecs

func GroupFromSpecs(opts GroupOptions, specs []toolutil.ActionSpec) (Group, error)

GroupFromSpecs builds a catalog group from canonical action specs.

func NewGroup

func NewGroup(opts GroupOptions) Group

NewGroup creates an action group with initialized maps.

func (*Group) ActionMap

func (g *Group) ActionMap() toolutil.ActionMap

ActionMap returns a legacy route map for this group. The map is the caller's; the routes in it share their schemas with the group, as every accessor of this package does (see Catalog).

func (*Group) ActionsInOrder

func (g *Group) ActionsInOrder() []Action

ActionsInOrder returns group actions in deterministic action-name order.

func (*Group) SetAction

func (g *Group) SetAction(action Action)

SetAction inserts or replaces an action in the group.

type GroupOptions

type GroupOptions struct {
	ToolName               string
	Title                  string
	Description            string
	Icons                  []mcp.Icon
	ReadOnly               bool
	FormatResult           toolutil.FormatResultFunc
	BaseDomain             string
	EnterpriseOnly         bool
	GitLabDotComOnly       bool
	CapabilityRequirements []string
	OwnerPackage           string
	SurfaceKind            SurfaceKind
}

GroupOptions contains metadata for creating a catalog group.

type SurfaceKind

type SurfaceKind string

SurfaceKind classifies the runtime surface represented by a catalog group.

const (
	// SurfaceKindGitLabAction identifies ordinary GitLab API actions.
	SurfaceKindGitLabAction SurfaceKind = "gitlab-action"
	// SurfaceKindMetaGroup identifies visible domain meta-tool dispatchers.
	SurfaceKindMetaGroup SurfaceKind = "meta-group"
	// SurfaceKindDynamicController identifies Dynamic controller tools.
	SurfaceKindDynamicController SurfaceKind = "dynamic-controller"
	// SurfaceKindRuntimeUtility identifies non-GitLab runtime helper tools.
	SurfaceKindRuntimeUtility SurfaceKind = "runtime-utility"
	// SurfaceKindInteractiveUtility identifies tools that require MCP elicitation.
	SurfaceKindInteractiveUtility SurfaceKind = "interactive-utility"
)

type SurfaceToolSpec

type SurfaceToolSpec struct {
	Name                   string
	Title                  string
	Description            string
	GroupToolName          string
	BaseDomain             string
	ActionName             string
	SurfaceKind            SurfaceKind
	Route                  toolutil.ActionRoute
	Aliases                []string
	Tags                   []string
	RelatedActions         []string
	Compatibility          toolutil.CompatibilityPolicy
	Icons                  []mcp.Icon
	CapabilityRequirements []string
	FormatResult           toolutil.FormatResultFunc
	SafeModePolicy         string
	ReadOnlyPolicy         string
	OwnerPackage           string
	ReadOnly               bool
	Destructive            bool
	Idempotent             bool
	OpenWorld              bool
}

SurfaceToolSpec is the canonical metadata contract for visible MCP tools that are not ordinary GitLab API meta-tool groups.

func CloneSurfaceToolSpec

func CloneSurfaceToolSpec(spec SurfaceToolSpec) SurfaceToolSpec

CloneSurfaceToolSpec returns a defensive copy of surface tool metadata.

func (SurfaceToolSpec) ActionSpec

func (spec SurfaceToolSpec) ActionSpec() (toolutil.ActionSpec, error)

ActionSpec projects a surface tool spec into the shared ActionSpec model.

func (SurfaceToolSpec) Validate

func (spec SurfaceToolSpec) Validate() error

Validate verifies that the surface spec has enough metadata for runtime registration and catalog projection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL