Skip to content

Icons

GitLab MCP Server assigns 50 unique domain icons to all 1085 self-managed Enterprise/Premium tools (1091 on GitLab.com Enterprise/Premium with Orbit), 34 base meta-tools (51 self-managed Enterprise, 52 GitLab.com Enterprise), 45 resources, and 37 prompts. Each icon is stored as three base64 data URIs — a scalable SVG (Sizes: ["any"]) plus light/dark 16×16 lossless WebP fallbacks (Sizes: ["16x16"]) for clients that reject SVG — and helps MCP clients render recognizable UI elements for each GitLab domain. They are optional presentation metadata, not a negotiated MCP protocol capability: a client that cannot render any of the formats simply ignores them, and tool behavior is unchanged.

  • 16×16 viewport — minimal size optimized for tool lists and sidebars
  • currentColor fill on the SVG entry — icons adapt to light and dark themes automatically
  • Theme-paired WebP fallback — a near-black and a near-white 16×16 WebP, tagged light/dark, for clients that reject SVG
  • Inline data URIs — embedded in the binary, zero network requests
  • One icon per domain — related tools share the same icon for visual grouping

All 50 domain icons at 32px display size, grouped by domain category. The Name column shows the Go variable used in code. Icons are attached per catalog group, a group being a meta-tool such as gitlab_branch or gitlab_issue: the individual tools projected from its actions and the gitlab://tools entries on the dynamic surface inherit the group’s icon, so a group name below stands for every tool it aggregates. Resources and prompts pick their icon directly. Twelve icons (IconAlert, IconBot, IconContainer, IconDiscussion, IconEpic, IconImport, IconIntegration, IconLink, IconNotify, IconSchedule, IconTodo, IconUpload) currently have no consumer on any served surface: the domains they were drawn for route under a broader group and take that group’s icon.

PreviewNameCatalog groups
BranchIconBranchgitlab_branch
CommitIconCommitnone (resource commit and prompt audit_commit_hygiene only; commit actions route under gitlab_repository)
TagIconTaggitlab_tag
ReleaseIconReleasegitlab_release
FileIconFilegitlab_repository
PreviewNameCatalog groups
IssueIconIssuegitlab_issue
LabelIconLabelnone (label resources and the label_distribution prompt; label actions route under gitlab_project and gitlab_group)
MilestoneIconMilestonenone (milestone resources and prompts; milestone actions route under gitlab_project and gitlab_group)
BoardIconBoardnone (resource board; board actions route under gitlab_project and gitlab_group)
LinkIconLinknone (issue and release links route under gitlab_issue and gitlab_release)
TodoIconTodonone (todo actions route under gitlab_user)
EpicIconEpicnone (epic actions route under gitlab_group)
PreviewNameCatalog groups
MRIconMRgitlab_merge_request, gitlab_mr_review
DiscussionIconDiscussion

none (discussion and note actions route under their parent’s group: gitlab_issue, gitlab_mr_review, gitlab_repository, gitlab_group, gitlab_snippet)

PreviewNameCatalog groups
PipelineIconPipelinegitlab_pipeline
JobIconJobgitlab_job
ScheduleIconSchedulenone (pipeline schedules route under gitlab_pipeline, freeze periods under gitlab_environment)
VariableIconVariablegitlab_ci_variable
RunnerIconRunnergitlab_runner
PreviewNameCatalog groups
EnvironmentIconEnvironmentgitlab_environment
DeployIconDeploynone (resource deployment; deployment actions route under gitlab_environment)
InfraIconInfragitlab_geo, gitlab_storage_move
PreviewNameCatalog groups
ProjectIconProjectgitlab_project, gitlab_project_alias, and the standalone gitlab_discover_project
GroupIconGroupgitlab_group, gitlab_group_scim
UserIconUsergitlab_user, gitlab_enterprise_user
QueueIconQueuegitlab_merge_train
BotIconBotnone (service accounts route under gitlab_group and gitlab_project)
PreviewNameCatalog groups
PackageIconPackagegitlab_package, gitlab_dependency, gitlab_model_registry
ContainerIconContainernone (the container registry routes under gitlab_package)
PreviewNameCatalog groups
SearchIconSearchgitlab_search, and gitlab_find_action on the dynamic surface
AnalyticsIconAnalyticsgitlab_dora_metrics, gitlab_orbit
PreviewNameCatalog groups
SecurityIconSecuritygitlab_security_attribute, gitlab_security_category, gitlab_security_finding, gitlab_security_scan_profile
TokenIconTokengitlab_access
KeyIconKeynone (resource deploy_key; key actions route under gitlab_access, gitlab_user and gitlab_group)
ShieldIconShieldgitlab_attestation, gitlab_external_status_check
VulnerabilityIconVulnerabilitygitlab_vulnerability
ComplianceIconCompliancegitlab_compliance_policy
PreviewNameCatalog groups
WikiIconWikigitlab_wiki, and the five workflow guide resources
SnippetIconSnippetgitlab_snippet
PreviewNameCatalog groups
ConfigIconConfig

gitlab_admin, gitlab_feature_flags, gitlab_member_role, the gitlab_interactive_* wizards, and the gitlab://tools manifest resources

ServerIconServergitlab_execute_action on the dynamic surface, and the fallback for a group missing from the icon map
TemplateIconTemplategitlab_template, gitlab_ci_catalog
PreviewNameCatalog groups
NotifyIconNotifynone (notification actions route under gitlab_user and gitlab_admin)
EventIconEventgitlab_custom_emoji
AlertIconAlertnone (alert and error-tracking actions route under gitlab_admin)
AuditIconAuditgitlab_audit_event
PreviewNameCatalog groups
IntegrationIconIntegrationnone (integration actions route under gitlab_project, system hooks under gitlab_admin)
HealthIconHealthgitlab_server (gitlab_server_status on the individual surface)
UploadIconUploadnone (upload actions route under gitlab_project and gitlab_group)
ImportIconImport

none (import and export actions route under gitlab_project, gitlab_group and gitlab_admin)

Icon rendering depends on the MCP client. The server attaches the same icon metadata in every case; whether it appears is up to the client. Icons are optional, so clients that do not render them lose nothing functional. This table reflects a primary-source (client source code and official docs) survey of 15 clients from 2026-08-24, not vendor marketing.

MCP ClientRendersNotes
VS Code (GitHub Copilot)✅ YesRejects the SVG entry (image/svg+xml not in its MIME allowlist), renders the Theme-matched WebP fallback
Cursor❓ UnclearAccepts the SVG format in validation, but server-icon rendering was staff-confirmed absent as of March 2026 (an unconfirmed hint of improvement followed in June); tool/resource/prompt icons unverified
OpenAI Codex (CLI + IDE core)❓ UnclearServer/tool icons are captured and forwarded internally, but no rendering code was found in the open-source TUI; the closed-source VS Code extension and ChatGPT desktop app were not inspectable
Windsurf / Devin Desktop❓ UnknownNo icon documentation found anywhere; closed source, no public client to inspect
JetBrains AI Assistant / Junie❌ NoSettings docs enumerate only generic action-button icons (add/remove/edit/reconnect) and a connection-status glyph — no server-supplied icon field; resources and prompts remain wholly unsupported (JUNIE-1606/1607, both open)
Claude Desktop❌ NoProtocol-level icons are not confirmed rendered anywhere; a separate, unrelated .mcpb manifest icon mechanism works for local extensions, which is not what this project ships
Claude Code❌ NoText-only interface — a generic placeholder and the raw tool name
Kiro (AWS)❌ NoPrompts/resources show a generic “MCP” protocol badge, not this project’s per-item icon
Zed❌ NoVerified in source: no icons field on any of its Implementation/Tool/Resource/Prompt types
Cline❌ NoNo icon rendering found anywhere in the client
Continue.dev❌ NoNo MCP icon rendering in any version, in any format; separate favicon setting
Goose (Block)❌ NoIts SDK carries the field; Goose’s own client never reads it
LibreChat❌ NoNo icon rendering found anywhere in the client
Open WebUI❌ NoNo icon rendering found anywhere in the client
5ire / Witsy❌ NoNo icon rendering found in either client
Custom clients (direct LLM API)DependsRenders whichever entries its own MCP client implementation honors — see Building a custom MCP client? below for a concrete selection algorithm

If you’re talking to an MCP server directly over JSON-RPC — no VS Code, Claude Desktop, or Cursor in between — icon rendering is entirely your client’s job. icons is optional, additive metadata (SEP-973, protocol revision 2025-11-25): a server that ignores it (Zed, Continue.dev, and most hand-rolled clients today) is still fully spec-compliant. Everything below is for clients that do want to render a UI.

The array shows up on four object types, wherever your client already reads their name/description:

CarrierWhere it comes from
ImplementationserverInfo.icons in the initialize response — the server’s own brand mark
Tooleach entry in tools/list
Resource / ResourceTemplateeach entry in resources/list
Prompteach entry in prompts/list

Each Icon is { src, mimeType?, sizes?, theme? }. src is a data: or https:/http: URI. Treat an absent or empty icons array as “no icon for this object” and fall back to a generic per-type glyph (wrench for tools, document for resources, etc.) — never fail the call over it.

Run every candidate icon array through three filters, in this order. Stop as soon as one candidate survives; if none do, fall back to your generic glyph.

  1. Format support. Decide up front what your renderer can display — typically raster formats your UI toolkit/image decoder handles natively (image/png, image/jpeg, image/webp, image/gif), plus image/svg+xml only if you’ve built the sanitization path below. Filter on the declared mimeType when present; when it’s absent, sniff the actual bytes (magic number for data:, or a HEAD/Content-Type check for https:) rather than guessing from the URI’s file extension — mimeType is server-supplied, untrusted metadata, not a guarantee.
  2. Theme match. If your client has a light/dark UI theme, prefer icons whose theme matches it. Critically: an icon with no theme field is theme-agnostic, not unthemed — it’s designed to render correctly regardless of host theme (e.g. an SVG using currentColor that inherits your UI’s text color). Match it in both light and dark modes rather than skipping it or treating it as a coin-flip default. Concretely: gitlab-mcp-server ships three Icon entries per icon — one SVG with sizes: ["any"] and no theme (adapts via currentColor), plus a WebP pair with sizes: ["16x16"] and explicit theme: "light" / theme: "dark" (pre-rendered near-black / near-white). A correct client either renders the SVG (it’s already theme-correct, so this step is a no-op) or, if it can’t do SVG, picks the WebP whose theme equals its current UI theme.
  3. Size match, with fallback. sizes: ["any"] means scalable/vector — it satisfies any requested size, so treat it as your first choice whenever your format filter allows SVG. For raster candidates, parse each WxH string, sort ascending, and pick the smallest one that’s still >= your target render size (this is what VS Code does) rather than the nearest by absolute difference — an oversized icon downscales cleanly; an undersized one doesn’t upscale cleanly. If nothing meets the target, take the largest available rather than failing.

Icons come from a server you may not control, so treat every field as untrusted input, per the spec’s own SHOULDs:

  • Scheme allowlist. Accept only data: and https: (treat plain http: as untrusted unless you’re deliberately supporting local/dev HTTP-transport servers). Reject everything else (file:, javascript:, etc.) outright.
  • Domain check on remote URLs. The spec says consumers SHOULD verify an https:/http: icon URL is same-domain as the client/server or otherwise trusted — don’t blindly render an <img src> pointing at an arbitrary third party. VS Code’s approach is a good concrete bar: for a server reached over HTTP transport, only accept icon URLs whose authority matches that same server’s authority; reject anything pointing elsewhere.
  • data: URIs still need validation. Verify the declared/sniffed MIME is one you actually allow, cap the decoded byte size before handing it to your image decoder (a malicious server can embed an oversized or malformed payload as a cheap DoS against your renderer), and reject on any decode error rather than best-effort passing it through.
  • Remote fetches are real network requests. An https: icon isn’t inert metadata — resolving it leaks the viewer’s IP and enables tracking/beacon patterns, and if you fetch it server-side (e.g. to proxy or cache it) that’s a user-controlled URL fetch and needs the same SSRF guards as any other: block private/link-local ranges, cap redirects, set a timeout and a byte-size ceiling.
  • SVG is the highest-risk format — the spec calls this out explicitly: SVG can embed <script> and event-handler attributes. Two acceptable paths: (a) don’t support image/svg+xml at all and drop straight to the next candidate (this is what VS Code does — simplest safe default), or (b) if you want crisp vector icons, sanitize before rendering — strip <script>, all on* attributes, <foreignObject>, and external references (e.g. with an SVG-profile DOMPurify config) before inlining the markup or handing it to your DOM/renderer. Never pass raw SVG bytes from an untrusted server straight into an inline <svg>/dangerouslySetInnerHTML-style sink.
  • Cap what you evaluate. A hostile server can return hundreds of Icon entries per object; bound how many you inspect per selection call so a malformed response can’t turn icon rendering into an amplification vector.
type Icon = {
src: string;
mimeType?: string;
sizes?: string[];
theme?: "light" | "dark";
};
const SUPPORTED_MIME = new Set([
"image/png",
"image/jpeg",
"image/webp",
"image/gif",
]);
// Flip to true only once you've wired the sanitize-before-render path below.
const SUPPORTS_SANITIZED_SVG = false;
function selectIcon(
icons: Icon[] | undefined,
opts: {
uiTheme: "light" | "dark";
targetPx: number;
serverAuthority: string;
},
): Icon | undefined {
if (!icons?.length) return undefined;
const candidates = icons
// 1. scheme allowlist + domain check
.filter((i) => {
const uri = safeParseUri(i.src);
if (!uri) return false;
if (uri.scheme === "data") return true;
if (uri.scheme === "https" || uri.scheme === "http") {
return (
uri.authority.toLowerCase() === opts.serverAuthority.toLowerCase()
);
}
return false;
})
// 2. format support (sniff, don't trust the label blindly)
.filter((i) => {
const mime = i.mimeType ?? sniffMimeType(i.src);
if (mime === "image/svg+xml") return SUPPORTS_SANITIZED_SVG;
return mime !== undefined && SUPPORTED_MIME.has(mime);
});
if (!candidates.length) return undefined;
// 3. theme: exact match OR theme-agnostic (no `theme` field at all)
const themed = candidates.filter(
(i) => i.theme === undefined || i.theme === opts.uiTheme,
);
const pool = themed.length ? themed : candidates;
// 4. size: prefer scalable ("any"), else smallest raster >= target, else largest available
const scalable = pool.find((i) => i.sizes?.includes("any"));
if (scalable) return scalable;
const bySize = pool
.flatMap((i) => (i.sizes ?? []).map((s) => ({ icon: i, ...parseWxH(s) })))
.sort((a, b) => a.width - b.width);
return (
bySize.find((c) => c.width >= opts.targetPx)?.icon ??
bySize[bySize.length - 1]?.icon ??
pool[0]
);
}
// Render step (only reached for icon.mimeType === "image/svg+xml"):
// svgMarkup = fetchOrDecode(icon.src)
// safeMarkup = sanitizeSvg(svgMarkup) // strip <script>, on*, <foreignObject>, external refs
// renderInline(safeMarkup)

This gives you graceful degradation at every layer: no icons field → your generic glyph; icons present but none pass your format/scheme/domain filters → same generic glyph; a full match → the theme- and size-correct variant, rendered through a path that never trusts server-supplied bytes further than necessary.

  • Source: internal/toolutil/icons.go, generator: cmd/gen_icon_webp
  • Encoding: three base64 data URIs per icon — data:image/svg+xml;base64,... plus data:image/webp;base64,... (light) and data:image/webp;base64,... (dark). The previews on this page use URL-encoded SVG (data:image/svg+xml,%3Csvg ...) for inline browser rendering only — the registered icons in internal/toolutil/icons.go are base64.
  • Viewport: All icons use 16×16; the SVG entry uses currentColor for automatic theme adaptation, the WebP entries are pre-rasterized per theme (Theme: "light" near-black, Theme: "dark" near-white)
  • Registration: Icons are assigned through catalog action specs and group specs, then projected into meta, individual, and dynamic tool surfaces

Frequently asked questions

What are the icons in GitLab MCP Server?

GitLab MCP Server assigns 50 unique domain icons to every tool, resource, and prompt it exposes, plus a 51st brand mark on the server identity itself, so MCP clients can render recognizable UI elements for each GitLab domain. Related tools share one icon for visual grouping — for example, branches, repository, and submodules all use IconBranch. Each icon carries three entries: a scalable SVG (Sizes: ["any"], currentColor fill, adapts to any theme automatically) plus two 16×16 lossless WebP fallbacks tagged Theme: "light"/"dark", for clients that reject SVG but honor the theme hint. They are optional presentation metadata, not a negotiated MCP protocol capability.

How are GitLab MCP Server icons designed and encoded?

Each icon uses a minimal 16×16 viewport optimized for tool lists and sidebars. The primary entry is an SVG with a currentColor fill so it adapts to light and dark themes automatically; it is embedded as a base64 data URI (data:image/svg+xml;base64,...) directly in the binary, requiring zero network requests. Two more entries are pre-rasterized, lossless WebP fallbacks (data:image/webp;base64,..., 16×16, near-black for Theme: "light" and near-white for Theme: "dark") generated by cmd/gen_icon_webp for clients whose MIME allowlist excludes SVG. One icon is shared per domain so related tools group visually. Icons are registered through catalog action specs and group specs, then projected into the meta, individual, and dynamic tool surfaces. The source lives in `internal/toolutil/icons.go`, plus the generated project mark in internal/toolutil/brandmark_gen.go (emitted by cmd/gen_brand).

Which MCP clients render tool icons?

Icon rendering varies by client. VS Code with GitHub Copilot renders tool, server, and resource icons — it rejects the SVG entry (image/svg+xml is not in its MIME allowlist) but renders the theme-matched WebP fallback instead. Continue.dev does not render MCP protocol icons in any current version, in any format; it has a separate, unrelated per-server favicon setting that is sometimes mistaken for icon support. Claude Desktop does not render tool icons, and Claude Code is a text-only interface that does not display them. Because icons are optional metadata, clients that do not support icon rendering simply ignore them and tool functionality is unaffected.

Do icons affect tool functionality?

No. Icons are optional presentation metadata attached to tools, resources, and prompts. Clients that cannot render icons ignore them, and every tool behaves identically whether or not its icon is displayed. Icons exist purely to help users visually recognize GitLab domains in clients that support SVG or WebP rendering; they never change a tool's parameters, behavior, or results.