Skip to content

Dynamic toolset

The dynamic toolset is the low-token mode for GitLab MCP Server. It keeps the complete GitLab action catalog available, but shows your AI client only three public tools:

ToolWhat it does
gitlab_search_toolsFinds the right GitLab action from natural language, domains, verbs, aliases, and typo-tolerant matching
gitlab_describe_toolsReturns the exact parameters, examples, and safety metadata for one or more actions
gitlab_execute_toolExecutes the selected action after validating the action ID and parameters

Meta-tools remain the default today because they are the most broadly compatible surface. Dynamic mode is the low-token alternative.

Large MCP servers can spend a lot of context on tool discovery before the user asks anything. GitLab MCP Server can expose up to 1011 individual operations, and even the default meta-tool catalog advertises 32 to 48 domain tools.

Dynamic mode exposes that catalog through search, describe, and execute tools. The model discovers only what it needs for the current task.

flowchart LR
    A[tools/list] --> B[3 public dynamic tools]
    B --> C[Search action catalog]
    C --> D[Describe selected actions]
    D --> E[Execute one canonical action]
    E --> F[GitLab REST v4 or GraphQL]

This usually adds one or two discovery calls per task, but it keeps the initial MCP tool context very small. The catalog is shared with meta-tools, so dynamic mode reuses the same schemas, destructive-action safeguards, read-only filtering, safe-mode previews, token-scope filtering, and result formatting.

Add TOOL_SURFACE=dynamic to your server environment:

{
"servers": {
"gitlab": {
"type": "stdio",
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx",
"TOOL_SURFACE": "dynamic"
}
}
}
}
Terminal window
gitlab-mcp-server --http \
--gitlab-url=https://gitlab.com \
--tool-surface=dynamic

For the smallest startup context, also use the minimal capability surface:

Terminal window
gitlab-mcp-server --http \
--gitlab-url=https://gitlab.com \
--tool-surface=dynamic \
--capability-surface=minimal

Dynamic mode works best when the assistant follows a simple rhythm: search, describe, execute.

sequenceDiagram
    participant User
    participant AI as AI Assistant
    participant Search as gitlab_search_tools
    participant Describe as gitlab_describe_tools
    participant Execute as gitlab_execute_tool
    participant GitLab

    User->>AI: List open merge requests authored by me
    AI->>Search: merge request list open authored by me
    Search-->>AI: merge_request.list
    AI->>Describe: merge_request.list
    Describe-->>AI: Params schema and examples
    AI->>Execute: merge_request.list with validated params
    Execute->>GitLab: API request
    GitLab-->>Execute: GitLab data
    Execute-->>AI: Markdown and structured JSON
    AI-->>User: Formatted answer

Each dynamic tool returns a normal MCP tool result: Markdown in content, JSON data in structuredContent, and isError on the result envelope when the server returns repair guidance. gitlab_execute_tool does not use a special GitLab path. It dispatches to the same underlying action handler used by meta-tools, so schemas, policy checks, safe-mode previews, destructive confirmations, and result formatting stay consistent.

CallWhat the assistant receivesHow the assistant should use it
gitlab_search_toolsA ranked list of canonical action IDs with the backing meta-tool, domain, action, schema URI, destructive flag, required params, usage hints, related actions, optional explanations, and scorePick the best domain.action candidate and describe it before execution
gitlab_describe_toolsExact input_schema, required params, safety metadata, usage hints, related actions, optional output_schema, schema URI, and an executable gitlab_execute_tool exampleBuild params from the schema and avoid guessed field names
gitlab_execute_toolThe existing action response from the backing handler, usually Markdown plus structured JSONUse the returned data to answer the user, or repair from isError: true feedback

Search and describe are deliberately cheap compared with advertising every GitLab operation in tools/list. The model pays for detailed schemas only when it needs a specific action.

gitlab_search_tools is more than a substring search. It indexes canonical IDs, split ID words, backing meta-tool names, domains, action names, aliases, tags, required params, optional params, schema property names, schema enum values, compact schema descriptions, and internal backend metadata.

The ranking pipeline:

  1. Normalizes the query by lower-casing it and splitting spaces, dots, underscores, and hyphens.
  2. Removes common stopwords such as the, to, with, and please.
  3. Expands synonyms such as mr → merge request, secret → CI variable/token, show → get, and remove → delete. Backend words such as github pr or jira ticket normalize to GitLab merge-request or issue concepts without exposing non-GitLab action IDs.
  4. Scores exact canonical IDs first, then aliases, tags, domain/action names, required params, schema enum values, schema fields, and broader metadata matches.
  5. Runs fuzzy typo recovery only when lexical search returns no matches or only low-confidence matches.
  6. Searches long prompts in three- to six-term windows so multi-step prompts can surface multiple relevant actions.

Fuzzy recovery is bounded on purpose: it allows up to two edit mistakes for tokens of at least three characters and suppresses weak typo matches for destructive actions. That helps with prompts like merje requesy list, while short terms such as mr still rely on aliases and synonyms instead of loose typo matching.

Search accepts explain: true when the assistant needs deterministic scoring reasons. The default response stays compact. Enabling explain does not change ranking; it only adds reasoning metadata. No-match searches return a small suggestions list, and curated workflows may return related_actions, such as repository.compare before analyze.release_notes.

First, search for the action:

{
"tool": "gitlab_search_tools",
"arguments": {
"query": "merge request list open authored by me project",
"limit": 5
}
}

Then describe the selected action:

{
"tool": "gitlab_describe_tools",
"arguments": {
"actions": ["merge_request.list"]
}
}

Finally, execute it:

{
"tool": "gitlab_execute_tool",
"arguments": {
"action": "merge_request.list",
"params": {
"project_id": "my-group/my-project",
"state": "opened",
"scope": "created_by_me",
"per_page": 20
}
}
}

The assistant should execute the canonical action ID returned by search or describe. Aliases are useful for discovery, but canonical IDs are the stable execution contract.

Dynamic mode is built to be repairable. If a call returns isError: true, the assistant should use the message as feedback and retry the right step.

FailureRecovery
Search query is emptyRetry search with a domain, resource, verb, and useful filters
Action ID is unknownSearch again or use the suggested canonical IDs from the error message
Ambiguous aliasPick one listed domain.action ID and describe it
Params are rejectedDescribe the action and rebuild params from input_schema
Destructive action is blockedAsk the user for explicit approval before retrying with top-level confirm: true

Dynamic mode reuses the same safety model as meta-tools. Destructive actions still require explicit confirmation unless your deployment has intentionally disabled prompts with YOLO_MODE or AUTOPILOT.

{
"tool": "gitlab_execute_tool",
"arguments": {
"action": "project.delete",
"params": {
"project_id": "my-group/my-project"
}
}
}

Without confirmation, the server returns an error result instead of deleting the project. To run the action intentionally, pass confirm: true at the top level of the gitlab_execute_tool arguments:

{
"tool": "gitlab_execute_tool",
"arguments": {
"action": "project.delete",
"confirm": true,
"params": {
"project_id": "my-group/my-project"
}
}
}

Dynamic execution validates parameters before dispatch. Unknown fields, including unsupported security-sensitive fields such as masked or protected on pipeline schedule variables, are rejected with repair guidance instead of being silently removed.

For safer deployments, use GITLAB_READ_ONLY=true to remove mutating actions or GITLAB_SAFE_MODE=true to preview mutations without applying them.

QuestionMeta-toolsDynamic toolset
What is visible in tools/list?32 to 48 domain tools3 public discovery/execution tools
How does the model choose?Pick a domain tool and actionSearch the canonical action catalog, then describe the action
Where are schemas found?Tool schema or gitlab://schema/meta/... resourcesgitlab_describe_tools
Best current useDefault compatibilityLow-token clients and compact action discovery
RollbackAlready defaultUnset TOOL_SURFACE or set TOOL_SURFACE=meta
SymptomWhat to do
You only see three toolsThat is expected in dynamic mode. Ask the assistant to search and describe actions before execution
Search returns broad resultsInclude domain, resource, action, and filters, for example merge request list open authored by me
Execute rejects an actionSearch again and use the canonical domain.action ID from the result
Execute rejects parametersDescribe the action and retry with the exact field names and types
Resources and prompts still use contextAdd CAPABILITY_SURFACE=minimal or --capability-surface=minimal