Skip to content

Icons

GitLab MCP Server assigns 51 unique domain icons to all 1088 tools of a self-managed Ultimate instance (1094 on GitLab.com Ultimate, with Orbit), 34 base meta-tools (51 on self-managed Ultimate, 52 on GitLab.com Ultimate), 45 resources, and 37 prompts, and a 52nd icon, the brand mark, to the server itself. Each icon is stored as three base64 data URIs, a scalable SVG (Sizes: ["any"]) followed by light and 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 for the 51 domain icons, a minimal size suited to tool lists and sidebars (the brand mark keeps a 24×24 viewBox)
  • currentColor fill on the SVG entry, so an icon takes the client’s text color and adapts to light and dark themes without declaring one
  • Theme-paired WebP fallback: a near-black and a near-white 16×16 WebP, tagged light and dark, for clients that reject SVG
  • Small, flat SVGs: 14 of the 51 domain icons are a single <path>, the rest use at most seven elements, all of them path, circle, ellipse, line, polyline or rect, and no icon’s markup reaches 700 bytes
  • Self-contained: no <script>, no event handlers, no style, no text, no external references and no embedded images, so drawing an icon makes the client fetch or run nothing
  • Inline data URIs, embedded in the binary, so showing an icon costs no network request
  • One icon per catalog group: every tool of a group shares the group’s icon, and related groups share one (gitlab_project and gitlab_project_alias, for example)

The MCP specification (revision 2025-11-25) sets the image types a client that renders icons has to accept:

MIME typeClient supportNotes
image/pngMUSTUniversal compatibility
image/jpeg (and image/jpg)MUSTUniversal compatibility
image/svg+xmlSHOULDScalable, needs security precautions; this server’s first entry
image/webpSHOULDModern, efficient format; this server’s two fallbacks

Every icon this server sends is an array of three entries, always in the same order:

EntrymimeTypesizesthemeColor
icons[0]image/svg+xml["any"](none)currentColor
icons[1]image/webp["16x16"]lightnear-black #1A1A1A
icons[2]image/webp["16x16"]darknear-white #FAFAFA

The order is a stable contract, pinned by the server’s unit tests: the SVG comes first, then the light WebP, then the dark one. A client should still choose by mimeType and theme, but one that reads by position can rely on it. SVG and WebP are only SHOULD-level, so a client that renders PNG and JPEG alone, the MUST-level minimum, renders none of these icons.

Every entry is an inline data: URI carrying base64, never a link a client has to fetch. Base64 rather than raw markup because SVG carries characters (<, >, ", #, spaces) that are not valid in an unencoded data URI, and because the specification describes an embedded icon as a data: URI with base64-encoded image data:

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIC4uLj4uLi48L3N2Zz4=
data:image/webp;base64,UklGRiYAAABXRUJQVlA4...

The SVG entry fills with currentColor, so it takes the text color around it and serves every theme without declaring one. A raster image cannot do that, so the two WebP entries are drawn once per theme: near-black #1A1A1A for a light background and near-white #FAFAFA for a dark one, rather than one saturated color that would clash with both. They are 16×16 (the specification keeps sizes: ["any"] for scalable formats such as SVG) and lossless. When the fallbacks were added (#295), lossless WebP measured about 32% smaller than optimized PNG across the icon set at that size; JPEG was ruled out for having no alpha channel, and GIF because its 256-color palette with one-bit alpha bands antialiased edges.

All 51 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 inherit the group’s icon, so a group name below stands for every tool it aggregates. The dynamic surface lists only two tools, gitlab_find_action (IconSearch) and gitlab_execute_action (IconServer), and neither its search results nor the gitlab://tools manifest carries an icon per action. Resources and prompts pick their icon directly; the table after the gallery lists them. 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
AchievementIconAchievementgitlab_achievement
QueueIconQueuegitlab_merge_train
BotIconBotnone (service accounts route under gitlab_group, gitlab_project and gitlab_user)
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 (GitLab.com only)
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, which no group the catalog builds reaches: a unit test holds every group, at every tier on a self-managed instance and on GitLab.com, to an entry of its own
TemplateIconTemplategitlab_template, gitlab_ci_catalog
PreviewNameCatalog groups
NotifyIconNotifynone (notification settings route under gitlab_user)
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)

Resources and prompts name their icon directly instead of inheriting one from a catalog group. This table lists, by the name the server registers, every icon that at least one of the 45 resources or 37 prompts carries; the other 27 domain icons are used by none.

IconResourcesPrompts
IconAnalyticsnonemerge_velocity, project_activity_report, weekly_team_recap
IconBoardboardnone
IconBranchbranch, project_branchesbranch_mr_summary, compare_branches
IconCommitcommitaudit_commit_hygiene
IconConfigfeature_flag, tool_manifest, tool_detailnone
IconDeploydeploymentnone
IconEnvironmentenvironmentnone
IconFilefile_blobnone
IconGroupgroup, groupsteam_overview
IconHealthnoneproject_health_check
IconIssueissue, project_issuesmy_issues, stale_items_report, unassigned_items
IconJobjob, pipeline_jobsnone
IconKeydeploy_keynone
IconLabellabel, group_label, project_labelslabel_distribution
IconMRmerge_request, merge_request_notes, merge_request_discussionsgroup_mr_dashboard, mr_description_quality, mr_discussion_health, mr_risk_assessment, my_open_mrs, my_pending_reviews, review_mr, suggest_mr_reviewers, summarize_mr_changes, summarize_open_mrs
IconMilestonemilestone, group_milestone, project_milestonesgroup_milestone_progress, milestone_progress
IconPipelinepipeline, latest_pipelinesummarize_pipeline_status
IconProjectproject, group_projectsnone
IconReleaserelease, project_releasesgenerate_release_notes, release_cadence, release_readiness
IconSecuritynoneaudit_project_full, audit_project_workflow
IconSnippetsnippet, project_snippetnone
IconTagtag, project_tagsnone
IconUsercurrent_user, group_members, project_membersdaily_standup, my_activity_summary, project_contributors, reviewer_workload, team_member_workload, user_activity_report, user_stats
IconWikiwiki_page, and the five workflow guides served under gitlab://guides/none
Brand mark

IconBrand identifies the server rather than a GitLab domain. It is the 52nd icon, attached to the server’s own identity (Implementation.Icons) and to no tool, resource or prompt. The mark is the “fan-out”: a source node projecting three branch arcs, each ending in a node, which reads as a git graph and as the server’s design, one action catalog reaching the dynamic, meta and individual tool surfaces. It is original artwork for this project and the same mark as the site’s logo and favicon, all drawn from one geometry. It replaced first IconServer, which is also the icon of gitlab_execute_action, and then a GitLab tanuki, which is GitLab’s trademark.

A client receives it in two places:

  • In the initialize response, as serverInfo.icons.
  • In every result on protocol revision 2026-07-28 or later (SEP-2575), where a client need not send initialize first: the server identifies itself in the result’s _meta, under io.modelcontextprotocol/serverInfo, and that entry is the whole Implementation, its three icon entries included. Only an empty result, such as the answer to a ping, goes without it.

That second path is why the mark stays a handful of primitives, three arcs and four circles. Two details differ from the domain icons: it keeps a 24×24 viewBox, because it is scaled from the shared brand geometry rather than drawn at glyph size, and like them its SVG entry uses currentColor with no theme, followed by the same light and dark WebP pair for clients that reject SVG.

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, cross-checked against what each client actually does rather than against the specification’s SHOULD-level wording.

MCP ClientRendersNotes
VS Code (GitHub Copilot)✅ YesRejects the SVG entry (image/svg+xml not in its MIME allowlist) but renders the Theme-matched WebP fallback
Cursor❓ UnclearAccepts the SVG format in validation, but server-icon rendering itself was staff-confirmed absent as of March 2026 (an unconfirmed hint of improvement followed in June 2026); tool, resource and prompt icon rendering unverified either way
OpenAI Codex (CLI + IDE core)❓ UnclearServer and tool icons are captured and forwarded through Codex’s internal protocol, but no code that reads them back out for rendering was found anywhere in the open-source TUI; the closed-source VS Code extension and ChatGPT desktop app could not be inspected
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, with no server-supplied icon field; resources and prompts remain wholly unsupported (JUNIE-1606/1607, both open)
Claude Desktop❌ NoThe protocol-level Implementation.icons and Tool.icons fields are not confirmed rendered anywhere. A separate .mcpb manifest icon does show for locally installed extensions, and this project’s Claude Desktop bundle declares one, but that is the bundle’s own icon, not the protocol-level icons this page describes
Claude Code❌ NoText-only interface: a generic placeholder and the raw tool name, confirmed by several open feature requests asking for exactly this
Kiro (AWS)❌ NoPrompts and resource templates show a generic “MCP” protocol badge next to every entry, not the per-item icons field this project populates
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 (verified in source)
Continue.dev❌ NoDoes not read the MCP icons field in any version, in any format; has an unrelated proprietary faviconUrl setting instead
Goose (Block)❌ NoThe underlying rmcp SDK carries the field; Goose’s own client code never reads it back out
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 client 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 five object types, wherever your client already reads their name/description:

CarrierWhere it comes from
ImplementationserverInfo.icons in the initialize response, and on protocol 2026-07-28 or later the io.modelcontextprotocol/serverInfo entry of each result’s _meta: the server’s own brand mark
Tooleach entry in tools/list
Resourceeach entry in resources/list
ResourceTemplateeach entry in resources/templates/list
Prompteach entry in prompts/list

Each Icon is { src, mimeType?, sizes?, theme? }. The specification lets src be an HTTP or HTTPS URL or a data: URI with base64 image data, and it requires a consumer to accept only https: and data: and to reject unsafe schemes such as javascript:, file:, ftp: and ws:. This server only ever sends data: URIs. 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, as the specification requires of every consumer:

  • Scheme allowlist. Accept only data: and https:, as the specification requires, and reject everything else outright, plain http: included along with file:, javascript:, ftp: and ws:. Refuse a redirect that changes the scheme or leaves the origin.
  • Origin check on remote URLs. The specification asks consumers to verify that an https: icon URL comes from the same origin as the server, so 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. Fetch an icon without credentials: no cookies, no Authorization header.
  • 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") {
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, generators: cmd/gen_icon_webp for the WebP fallbacks and cmd/gen_brand for the brand mark
  • Encoding: three base64 data URIs per icon, data:image/svg+xml;base64,... followed by 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 ...) colored #888 for inline browser rendering only; the registered icons are base64 and use currentColor.
  • Viewport: the domain icons use 16×16 and the brand mark 24×24; 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: each catalog group has one icon (a group missing from the icon map would fall back to IconServer, and a unit test holds every group the catalog builds to an entry, so none does); a meta-tool carries it and every individual tool projected from the group’s actions inherits it, while the dynamic surface’s two tools carry IconSearch and IconServer, and resources and prompts name their own

Contributors regenerating the WebP fallbacks or the brand mark will find the commands under gen_icon_webp and gen_brand in the command reference.

Frequently asked questions

What are the icons in GitLab MCP Server?

GitLab MCP Server assigns 51 unique domain icons to every tool, resource, and prompt it exposes, plus a 52nd brand mark on the server identity itself, so MCP clients can render recognizable UI elements for each GitLab domain. Icons are attached per catalog group, so every tool of a group shares one: the tools of gitlab_branch use IconBranch, while every tool of gitlab_repository (the tree, commits, files, and submodules) uses IconFile. Each icon carries three entries, always in this order: 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 domain 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. Icons are attached per catalog group, not per action: a meta-tool carries its group's icon, and every individual tool projected from that group's actions inherits it, so related tools group visually. The dynamic surface lists only gitlab_find_action (IconSearch) and gitlab_execute_action (IconServer); the actions they reach carry no icon of their own. Resources and prompts name their own icon. 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.