Ir al contenido

Iconos

GitLab MCP Server asigna 51 iconos de dominio a las 1088 herramientas de una instancia Ultimate autoalojada (1094 en GitLab.com Ultimate, con Orbit), 34 meta-herramientas base (51 en Ultimate autoalojado, 52 en GitLab.com Ultimate), 45 recursos y 37 prompts, y un icono 52.º, la marca, al propio servidor. Cada icono se almacena como tres data URIs base64, un SVG escalable (Sizes: ["any"]) seguido de variantes WebP clara y oscura de 16×16 sin pérdida (Sizes: ["16x16"]) para clientes que rechazan SVG, y ayuda a los clientes MCP a mostrar elementos de interfaz reconocibles para cada dominio de GitLab. Son metadatos de presentación opcionales, no una capacidad de protocolo MCP negociada: un cliente que no puede renderizar ninguno de los formatos simplemente los ignora, y el comportamiento de las herramientas no cambia.

¿Cómo se diseñan los iconos de GitLab MCP Server?

Sección titulada «¿Cómo se diseñan los iconos de GitLab MCP Server?»
  • Viewport 16×16 para los 51 iconos de dominio, un tamaño mínimo adecuado para listas de herramientas y barras laterales (la marca conserva un viewBox de 24×24)
  • currentColor en la entrada SVG, así que un icono toma el color de texto del cliente y se adapta a los temas claro y oscuro sin declarar ninguno
  • Variante WebP por tema: un WebP casi negro y otro casi blanco, de 16×16, etiquetados light y dark, para clientes que rechazan SVG
  • SVG pequeños y planos: 14 de los 51 iconos de dominio son un único <path>, el resto usa como mucho siete elementos, todos ellos path, circle, ellipse, line, polyline o rect, y el marcado de ningún icono llega a 700 bytes
  • Autocontenidos: sin <script>, sin gestores de eventos, sin style, sin texto, sin referencias externas y sin imágenes incrustadas, así que dibujar un icono no hace que el cliente descargue ni ejecute nada
  • Data URIs en línea, embebidos en el binario, así que mostrar un icono no cuesta ninguna petición de red
  • Un icono por grupo del catálogo: todas las herramientas de un grupo comparten el icono del grupo, y los grupos relacionados comparten uno (gitlab_project y gitlab_project_alias, por ejemplo)

La especificación MCP (revisión 2025-11-25) fija los tipos de imagen que tiene que aceptar un cliente que renderiza iconos:

Tipo MIMESoporte del clienteNotas
image/pngMUSTCompatibilidad universal
image/jpeg (y image/jpg)MUSTCompatibilidad universal
image/svg+xmlSHOULDEscalable, requiere precauciones de seguridad; la primera entrada de este servidor
image/webpSHOULDFormato moderno y eficiente; las dos variantes de este servidor

Cada icono que envía este servidor es un array de tres entradas, siempre en el mismo orden:

EntradamimeTypesizesthemeColor
icons[0]image/svg+xml["any"](ninguno)currentColor
icons[1]image/webp["16x16"]lightcasi negro #1A1A1A
icons[2]image/webp["16x16"]darkcasi blanco #FAFAFA

El orden es un contrato estable, fijado por las pruebas unitarias del servidor: primero el SVG, luego el WebP claro y luego el oscuro. Un cliente debería elegir de todos modos por mimeType y theme, pero uno que lea por posición puede contar con él. SVG y WebP son solo de nivel SHOULD, así que un cliente que solo renderiza PNG y JPEG, el mínimo de nivel MUST, no renderiza ninguno de estos iconos.

Cada entrada es una URI data: en línea con base64, nunca un enlace que el cliente tenga que descargar. Base64 en lugar del marcado en crudo porque el SVG contiene caracteres (<, >, ", #, espacios) que no son válidos en una data URI sin codificar, y porque la especificación describe un icono incrustado como una URI data: con los datos de la imagen codificados en base64:

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

La entrada SVG se rellena con currentColor, así que toma el color del texto que la rodea y sirve para cualquier tema sin declarar ninguno. Una imagen rasterizada no puede hacerlo, así que las dos entradas WebP se dibujan una vez por tema: casi negro #1A1A1A para un fondo claro y casi blanco #FAFAFA para uno oscuro, en lugar de un único color saturado que chocaría con ambos. Son de 16×16 (la especificación reserva sizes: ["any"] para formatos escalables como SVG) y sin pérdida. Cuando se añadieron las variantes (#295), WebP sin pérdida midió alrededor de un 32% menos que PNG optimizado en todo el conjunto de iconos a ese tamaño; JPEG se descartó por no tener canal alfa, y GIF porque su paleta de 256 colores con alfa de un bit crea bandas en los bordes suavizados.

Los 51 iconos de dominio a 32px de tamaño, agrupados por categoría de dominio. La columna Nombre muestra la variable Go usada en el código. Los iconos se adjuntan por grupo del catálogo, siendo un grupo una meta-herramienta como gitlab_branch o gitlab_issue: las herramientas individuales proyectadas desde sus acciones heredan el icono del grupo, así que un nombre de grupo más abajo representa a todas las herramientas que agrega. La superficie dinámica solo lista dos herramientas, gitlab_find_action (IconSearch) y gitlab_execute_action (IconServer), y ni sus resultados de búsqueda ni el manifiesto gitlab://tools llevan un icono por acción. Los recursos y los prompts eligen su icono directamente; la tabla que sigue a la galería los enumera. Doce iconos (IconAlert, IconBot, IconContainer, IconDiscussion, IconEpic, IconImport, IconIntegration, IconLink, IconNotify, IconSchedule, IconTodo, IconUpload) no tienen actualmente ningún consumidor en ninguna superficie servida: los dominios para los que se dibujaron se enrutan bajo un grupo más amplio y toman el icono de ese grupo.

Vista previaNombreGrupos del catálogo
RamaIconBranchgitlab_branch
CommitIconCommitninguno (solo el recurso commit y el prompt audit_commit_hygiene; las acciones de commits se enrutan bajo gitlab_repository)
TagIconTaggitlab_tag
ReleaseIconReleasegitlab_release
ArchivoIconFilegitlab_repository
Vista previaNombreGrupos del catálogo
IssueIconIssuegitlab_issue
EtiquetaIconLabelninguno (los recursos de etiquetas y el prompt label_distribution; las acciones de etiquetas se enrutan bajo gitlab_project y gitlab_group)
MilestoneIconMilestoneninguno (recursos y prompts de milestones; las acciones de milestones se enrutan bajo gitlab_project y gitlab_group)
TableroIconBoardninguno (recurso board; las acciones de boards se enrutan bajo gitlab_project y gitlab_group)
EnlaceIconLinkninguno (los enlaces de issues y de releases se enrutan bajo gitlab_issue y gitlab_release)
Tarea pendienteIconTodoninguno (las acciones de tareas pendientes se enrutan bajo gitlab_user)
ÉpicaIconEpicninguno (las acciones de épicas se enrutan bajo gitlab_group)
Vista previaNombreGrupos del catálogo
MRIconMRgitlab_merge_request, gitlab_mr_review
DiscusiónIconDiscussionninguno (las acciones de discusiones y notas se enrutan bajo el grupo de su padre: gitlab_issue, gitlab_mr_review, gitlab_repository, gitlab_group, gitlab_snippet)
Vista previaNombreGrupos del catálogo
PipelineIconPipelinegitlab_pipeline
JobIconJobgitlab_job
ProgramaciónIconScheduleninguno (las programaciones de pipelines se enrutan bajo gitlab_pipeline, los periodos de congelación bajo gitlab_environment)
VariableIconVariablegitlab_ci_variable
RunnerIconRunnergitlab_runner
Vista previaNombreGrupos del catálogo
EntornoIconEnvironmentgitlab_environment
DespliegueIconDeployninguno (recurso deployment; las acciones de despliegues se enrutan bajo gitlab_environment)
InfraestructuraIconInfragitlab_geo, gitlab_storage_move
Vista previaNombreGrupos del catálogo
ProyectoIconProjectgitlab_project, gitlab_project_alias y la herramienta independiente gitlab_discover_project
GrupoIconGroupgitlab_group, gitlab_group_scim
UsuarioIconUsergitlab_user, gitlab_enterprise_user
LogroIconAchievementgitlab_achievement
ColaIconQueuegitlab_merge_train
BotIconBotninguno (las cuentas de servicio se enrutan bajo gitlab_group, gitlab_project y gitlab_user)
Vista previaNombreGrupos del catálogo
PaqueteIconPackagegitlab_package, gitlab_dependency, gitlab_model_registry
ContenedorIconContainerninguno (el registro de contenedores se enruta bajo gitlab_package)
Vista previaNombreGrupos del catálogo
BúsquedaIconSearchgitlab_search, y gitlab_find_action en la superficie dinámica
AnalíticaIconAnalyticsgitlab_dora_metrics, gitlab_orbit (solo GitLab.com)
Vista previaNombreGrupos del catálogo
SeguridadIconSecuritygitlab_security_attribute, gitlab_security_category, gitlab_security_finding, gitlab_security_scan_profile
TokenIconTokengitlab_access
ClaveIconKeyninguno (recurso deploy_key; las acciones de claves se enrutan bajo gitlab_access, gitlab_user y gitlab_group)
EscudoIconShieldgitlab_attestation, gitlab_external_status_check
VulnerabilidadIconVulnerabilitygitlab_vulnerability
CumplimientoIconCompliancegitlab_compliance_policy
Vista previaNombreGrupos del catálogo
WikiIconWikigitlab_wiki, y los cinco recursos de guías de flujo de trabajo
SnippetIconSnippetgitlab_snippet
Vista previaNombreGrupos del catálogo
ConfiguraciónIconConfiggitlab_admin, gitlab_feature_flags, gitlab_member_role, los asistentes gitlab_interactive_* y los recursos del manifiesto gitlab://tools
ServidorIconServergitlab_execute_action en la superficie dinámica, y el valor de reserva para un grupo ausente del mapa de iconos, al que no llega ningún grupo que construya el catálogo: una prueba unitaria exige a cada grupo, en cada nivel en una instancia autoalojada y en GitLab.com, una entrada propia
PlantillaIconTemplategitlab_template, gitlab_ci_catalog
Vista previaNombreGrupos del catálogo
NotificaciónIconNotifyninguno (la configuración de notificaciones se enruta bajo gitlab_user)
EventoIconEventgitlab_custom_emoji
AlertaIconAlertninguno (las acciones de alertas y de seguimiento de errores se enrutan bajo gitlab_admin)
AuditoríaIconAuditgitlab_audit_event
Vista previaNombreGrupos del catálogo
IntegraciónIconIntegrationninguno (las acciones de integraciones se enrutan bajo gitlab_project, los system hooks bajo gitlab_admin)
SaludIconHealthgitlab_server (gitlab_server_status en la superficie individual)
SubidaIconUploadninguno (las acciones de subidas se enrutan bajo gitlab_project y gitlab_group)
ImportaciónIconImportninguno (las acciones de importación y exportación se enrutan bajo gitlab_project, gitlab_group y gitlab_admin)

¿Qué iconos usan los recursos y los prompts?

Sección titulada «¿Qué iconos usan los recursos y los prompts?»

Los recursos y los prompts nombran su icono directamente en lugar de heredarlo de un grupo del catálogo. Esta tabla enumera, por el name con el que el servidor los registra, cada icono que lleva al menos uno de los 45 recursos o 37 prompts; los otros 27 iconos de dominio no los usa ninguno.

IconoRecursosPrompts
IconAnalyticsningunomerge_velocity, project_activity_report, weekly_team_recap
IconBoardboardninguno
IconBranchbranch, project_branchesbranch_mr_summary, compare_branches
IconCommitcommitaudit_commit_hygiene
IconConfigfeature_flag, tool_manifest, tool_detailninguno
IconDeploydeploymentninguno
IconEnvironmentenvironmentninguno
IconFilefile_blobninguno
IconGroupgroup, groupsteam_overview
IconHealthningunoproject_health_check
IconIssueissue, project_issuesmy_issues, stale_items_report, unassigned_items
IconJobjob, pipeline_jobsninguno
IconKeydeploy_keyninguno
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_projectsninguno
IconReleaserelease, project_releasesgenerate_release_notes, release_cadence, release_readiness
IconSecurityningunoaudit_project_full, audit_project_workflow
IconSnippetsnippet, project_snippetninguno
IconTagtag, project_tagsninguno
IconUsercurrent_user, group_members, project_membersdaily_standup, my_activity_summary, project_contributors, reviewer_workload, team_member_workload, user_activity_report, user_stats
IconWikiwiki_page, y las cinco guías de flujo de trabajo servidas bajo gitlab://guides/ninguno
Marca del servidor

IconBrand identifica al servidor y no a un dominio de GitLab. Es el icono 52.º, adjunto a la identidad propia del servidor (Implementation.Icons) y a ninguna herramienta, recurso o prompt. La marca es el “abanico” (fan-out): un nodo de origen que proyecta tres arcos de rama, cada uno terminado en un nodo, que se lee como un grafo de git y como el diseño del servidor, un único catálogo de acciones que llega a las superficies de herramientas dinámica, meta e individual. Es una obra original de este proyecto y la misma marca que el logotipo y el favicon del sitio, todos dibujados a partir de una sola geometría. Sustituyó primero a IconServer, que también es el icono de gitlab_execute_action, y después a un tanuki de GitLab, que es marca registrada de GitLab.

Un cliente la recibe en dos sitios:

  • En la respuesta de initialize, como serverInfo.icons.
  • En cada resultado con la revisión de protocolo 2026-07-28 o posterior (SEP-2575), en la que un cliente no necesita enviar initialize antes: el servidor se identifica en el _meta del resultado, bajo io.modelcontextprotocol/serverInfo, y esa entrada es el Implementation completo, con sus tres entradas de icono. Solo un resultado vacío, como la respuesta a un ping, va sin ella.

Esa segunda vía es la razón de que la marca se quede en un puñado de primitivas, tres arcos y cuatro círculos. Dos detalles la distinguen de los iconos de dominio: conserva un viewBox de 24×24, porque se escala desde la geometría compartida de la marca en lugar de dibujarse a tamaño de glifo, y como ellos su entrada SVG usa currentColor sin theme, seguida del mismo par WebP claro y oscuro para los clientes que rechazan SVG.

El renderizado de iconos depende del cliente MCP. El servidor adjunta los mismos metadatos de iconos en todos los casos; que aparezcan depende del cliente. Los iconos son opcionales, así que los clientes que no los renderizan no pierden nada funcional. Esta tabla refleja una investigación de fuentes primarias (código fuente del cliente y documentación oficial, no material de marketing) sobre 15 clientes, realizada el 2026-08-24 y contrastada con lo que cada cliente hace de verdad en lugar de con la redacción de nivel SHOULD de la especificación.

Cliente MCPRenderizaNotas
VS Code (GitHub Copilot)✅ SíRechaza la entrada SVG (image/svg+xml no está en su lista de MIME permitidos) pero renderiza la variante WebP que coincide con su tema
Cursor❓ InciertoAcepta el formato SVG en validación, pero el renderizado del propio icono de servidor fue confirmado ausente por el equipo en marzo de 2026 (un indicio no confirmado de mejora llegó en junio de 2026); el renderizado de iconos de herramienta, recurso y prompt sigue sin verificar en ningún sentido
OpenAI Codex (CLI + núcleo IDE)❓ InciertoLos iconos de servidor y de herramienta se capturan y reenvían por el protocolo interno de Codex, pero no se encontró en ninguna parte de la TUI de código abierto código que los vuelva a leer para renderizarlos; la extensión de VS Code y la app de escritorio de ChatGPT, de código cerrado, no se pudieron inspeccionar
Windsurf / Devin Desktop❓ DesconocidoNo se encontró documentación de iconos en ningún sitio; código cerrado, sin cliente público que inspeccionar
JetBrains AI Assistant / Junie❌ NoLos documentos de configuración solo enumeran iconos genéricos de botones de acción (añadir/eliminar/editar/reconectar) y un indicador de estado de conexión, sin campo de icono propio del servidor; los recursos y prompts siguen sin soporte alguno (JUNIE-1606/1607, ambos abiertos)
Claude Desktop❌ NoLos campos Implementation.icons y Tool.icons del protocolo no se confirman renderizados en ningún sitio. Un icono distinto de manifiesto .mcpb sí se muestra para extensiones instaladas localmente, y el paquete de Claude Desktop de este proyecto declara uno, pero es el icono del propio paquete, no los iconos del protocolo que describe esta página
Claude Code❌ NoInterfaz solo texto: un marcador genérico y el nombre bruto de la herramienta, confirmado por varias peticiones de funcionalidad abiertas que piden exactamente esto
Kiro (AWS)❌ NoLos prompts y las plantillas de recursos muestran una insignia genérica “MCP” del protocolo junto a cada entrada, no el campo icons por elemento que rellena este proyecto
Zed❌ NoVerificado en el código fuente: ninguno de sus tipos Implementation/Tool/Resource/Prompt lleva un campo icons
Cline❌ NoNo se encontró renderizado de iconos en ninguna parte del cliente (verificado en el código fuente)
Continue.dev❌ NoNo lee el campo icons de MCP en ninguna versión ni formato; tiene en su lugar un ajuste propietario no relacionado, faviconUrl
Goose (Block)❌ NoEl SDK rmcp subyacente lleva el campo; el propio código de cliente de Goose nunca lo vuelve a leer
LibreChat❌ NoNo se encontró renderizado de iconos en ninguna parte del cliente
Open WebUI❌ NoNo se encontró renderizado de iconos en ninguna parte del cliente
5ire / Witsy❌ NoNo se encontró renderizado de iconos en ninguno de los dos clientes
Clientes propios (API de LLM directa)DependeRenderiza las entradas que su propia implementación de cliente MCP admita; consulta ¿Construyes tu propio cliente MCP? más abajo para un algoritmo de selección concreto

Si hablas directamente con un servidor MCP por JSON-RPC (sin VS Code, Claude Desktop o Cursor de por medio), el renderizado de iconos es responsabilidad exclusiva de tu cliente. icons son metadatos opcionales y aditivos (SEP-973, revisión de protocolo 2025-11-25): un cliente que los ignora (Zed, Continue.dev y la mayoría de clientes propios hoy en día) sigue siendo totalmente conforme a la especificación. Todo lo que sigue es para clientes que sí quieren renderizar una interfaz.

El array aparece en cinco tipos de objeto, allí donde tu cliente ya lee su nombre/descripción:

PortadorDe dónde procede
ImplementationserverInfo.icons en la respuesta de initialize, y con el protocolo 2026-07-28 o posterior la entrada io.modelcontextprotocol/serverInfo del _meta de cada resultado: la marca propia del servidor
Toolcada entrada de tools/list
Resourcecada entrada de resources/list
ResourceTemplatecada entrada de resources/templates/list
Promptcada entrada de prompts/list

Cada Icon es { src, mimeType?, sizes?, theme? }. La especificación permite que src sea una URL HTTP o HTTPS o una URI data: con los datos de la imagen en base64, y exige a quien la consume aceptar solo https: y data: y rechazar esquemas inseguros como javascript:, file:, ftp: y ws:. Este servidor solo envía URIs data:. Trata un array icons ausente o vacío como “sin icono para este objeto” y recurre a un glifo genérico por tipo (una llave inglesa para herramientas, un documento para recursos, etc.); nunca falles la llamada por ello.

Pasa cada array de iconos candidato por tres filtros, en este orden. Detente en cuanto un candidato sobreviva; si ninguno lo hace, recurre a tu glifo genérico.

  1. Soporte de formato. Decide de antemano qué puede mostrar tu renderizador — normalmente formatos rasterizados que tu toolkit de interfaz/decodificador de imágenes maneja de forma nativa (image/png, image/jpeg, image/webp, image/gif), más image/svg+xml solo si has construido la ruta de sanitización descrita más abajo. Filtra por el mimeType declarado cuando esté presente; cuando falte, analiza los bytes reales (número mágico para data:, o una comprobación HEAD/Content-Type para https:) en lugar de adivinar por la extensión del archivo en la URI — mimeType es un metadato suministrado por el servidor, no fiable, no una garantía.
  2. Coincidencia de tema. Si tu cliente tiene un tema de interfaz claro/oscuro, prefiere los iconos cuyo theme coincida. Importante: un icono sin campo theme es agnóstico al tema, no “sin tema” — está diseñado para renderizarse correctamente sin importar el tema del anfitrión (por ejemplo, un SVG que usa currentColor y hereda el color de texto de tu interfaz). Hazlo coincidir en ambos modos, claro y oscuro, en lugar de omitirlo o tratarlo como una opción por defecto al azar. En concreto: gitlab-mcp-server envía tres entradas Icon por icono — un SVG con sizes: ["any"] y sin theme (se adapta vía currentColor), más un par WebP con sizes: ["16x16"] y theme: "light" / theme: "dark" explícitos (renderizados de antemano en casi negro / casi blanco). Un cliente correcto o bien renderiza el SVG (ya es correcto para cualquier tema, así que este paso es un no-op), o bien, si no puede con SVG, elige el WebP cuyo theme coincida con el tema actual de su interfaz.
  3. Coincidencia de tamaño, con reserva. sizes: ["any"] significa escalable/vectorial — satisface cualquier tamaño solicitado, así que trátalo como primera opción siempre que tu filtro de formato admita SVG. Para candidatos rasterizados, analiza cada cadena AnchoxAlto, ordénalas de forma ascendente y elige la más pequeña que siga siendo >= tu tamaño de renderizado objetivo (así lo hace VS Code) en lugar de la más cercana por diferencia absoluta — un icono más grande de lo necesario se reduce de forma limpia; uno más pequeño no se amplía limpiamente. Si ninguno alcanza el objetivo, toma el disponible más grande en lugar de fallar.

Los iconos proceden de un servidor que quizá no controlas, así que trata cada campo como entrada no confiable, como la especificación exige a todo consumidor:

  • Lista blanca de esquemas. Acepta solo data: y https:, como exige la especificación, y rechaza sin más cualquier otro, incluido http: sin cifrar junto con file:, javascript:, ftp: y ws:. Rechaza una redirección que cambie el esquema o salga del origen.
  • Comprobación de origen en URLs remotas. La especificación pide a los consumidores verificar que una URL de icono https: procede del mismo origen que el servidor, así que no renderices a ciegas un <img src> que apunte a un tercero arbitrario. El enfoque de VS Code es un buen listón concreto: para un servidor alcanzado por transporte HTTP, solo acepta URLs de icono cuya autoridad coincida con la de ese mismo servidor; rechaza cualquier otra. Descarga un icono sin credenciales: sin cookies y sin cabecera Authorization.
  • Las URIs data: también necesitan validación. Verifica que el MIME declarado/detectado sea uno que realmente admites, limita el tamaño en bytes decodificado antes de entregarlo a tu decodificador de imágenes (un servidor malicioso puede incrustar una carga sobredimensionada o malformada como DoS barato contra tu renderizador), y rechaza ante cualquier error de decodificación en lugar de dejarlo pasar en el mejor de los casos.
  • Las descargas remotas son peticiones de red reales. Un icono https: no es un metadato inerte — resolverlo filtra la IP del espectador y habilita patrones de seguimiento/baliza, y si tú lo descargas del lado del servidor (por ejemplo, para hacer proxy o cachearlo) eso es una descarga de URL controlada por el usuario y necesita las mismas protecciones SSRF que cualquier otra: bloquear rangos privados/link-local, limitar redirecciones, establecer un tiempo límite y un tope de tamaño en bytes.
  • SVG es el formato de mayor riesgo — la especificación lo señala explícitamente: SVG puede incrustar <script> y atributos de gestor de eventos. Dos rutas aceptables: (a) no admitir image/svg+xml en absoluto y pasar directamente al siguiente candidato (así lo hace VS Code — la opción segura más simple), o (b) si quieres iconos vectoriales nítidos, sanitiza antes de renderizar — elimina <script>, todos los atributos on*, <foreignObject> y referencias externas (por ejemplo con una configuración de DOMPurify de perfil SVG) antes de incrustar el marcado o pasarlo a tu DOM/renderizador. Nunca pases bytes SVG en crudo procedentes de un servidor no confiable directamente a un sumidero de tipo <svg> inline o dangerouslySetInnerHTML.
  • Limita lo que evalúas. Un servidor hostil puede devolver cientos de entradas Icon por objeto; acota cuántas inspeccionas por llamada de selección para que una respuesta malformada no convierta el renderizado de iconos en un vector de amplificación.
type Icon = {
src: string;
mimeType?: string;
sizes?: string[];
theme?: "light" | "dark";
};
const SUPPORTED_MIME = new Set([
"image/png",
"image/jpeg",
"image/webp",
"image/gif",
]);
// Cambia a true solo cuando hayas implementado la ruta de sanitizar-antes-de-renderizar de abajo.
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. lista blanca de esquemas + comprobación de dominio
.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. soporte de formato (analiza los bytes, no confíes ciegamente en la etiqueta)
.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. tema: coincidencia exacta O agnóstico al tema (sin campo `theme`)
const themed = candidates.filter(
(i) => i.theme === undefined || i.theme === opts.uiTheme,
);
const pool = themed.length ? themed : candidates;
// 4. tamaño: prefiere escalable ("any"), si no el rasterizado más pequeño >= objetivo, si no el mayor disponible
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]
);
}
// Paso de renderizado (solo se alcanza cuando icon.mimeType === "image/svg+xml"):
// svgMarkup = fetchOrDecode(icon.src)
// safeMarkup = sanitizeSvg(svgMarkup) // elimina <script>, on*, <foreignObject>, referencias externas
// renderInline(safeMarkup)

Esto te da una degradación gradual en cada capa: sin campo icons → tu glifo genérico; iconos presentes pero ninguno pasa tus filtros de formato/esquema/dominio → el mismo glifo genérico; una coincidencia completa → la variante correcta en tema y tamaño, renderizada a través de una ruta que nunca confía en los bytes suministrados por el servidor más de lo necesario.

  • Fuente: internal/toolutil/icons.go, generadores: cmd/gen_icon_webp para las variantes WebP y cmd/gen_brand para la marca
  • Codificación: tres data URIs base64 por icono, data:image/svg+xml;base64,... seguida de data:image/webp;base64,... (clara) y data:image/webp;base64,... (oscura). Las previsualizaciones de esta página usan SVG con codificación URL (data:image/svg+xml,%3Csvg ...) coloreado #888 solo para renderizado en línea en el navegador; los iconos registrados están en base64 y usan currentColor.
  • Viewport: los iconos de dominio usan 16×16 y la marca 24×24; la entrada SVG usa currentColor para adaptación automática al tema, las entradas WebP están prerasterizadas por tema (Theme: "light" casi negro, Theme: "dark" casi blanco)
  • Registro: cada grupo del catálogo tiene un icono (un grupo que faltara en el mapa de iconos recurriría a IconServer, y una prueba unitaria exige una entrada a cada grupo que construye el catálogo, así que ninguno lo hace); una meta-herramienta lo lleva y cada herramienta individual proyectada desde las acciones del grupo lo hereda, mientras que las dos herramientas de la superficie dinámica llevan IconSearch e IconServer, y los recursos y los prompts nombran el suyo

Quien contribuya y tenga que regenerar las variantes WebP o la marca encontrará los comandos en gen_icon_webp y gen_brand en la referencia de comandos.

Preguntas frecuentes

¿Qué son los iconos en GitLab MCP Server?

GitLab MCP Server asigna 51 iconos de dominio a cada herramienta, recurso y prompt que expone, más una marca, la 52.ª, en la identidad del propio servidor, para que los clientes MCP puedan mostrar elementos de interfaz reconocibles para cada dominio de GitLab. Los iconos se adjuntan por grupo del catálogo, así que todas las herramientas de un grupo comparten uno: las herramientas de gitlab_branch usan IconBranch, mientras que todas las de gitlab_repository (el árbol, los commits, los archivos y los submódulos) usan IconFile. Cada icono lleva tres entradas, siempre en este orden: un SVG escalable (Sizes: ["any"], relleno currentColor, se adapta a cualquier tema automáticamente) más dos variantes WebP de 16×16 sin pérdida etiquetadas Theme: "light"/"dark", para clientes que rechazan SVG pero respetan la pista de tema. Son metadatos de presentación opcionales, no una capacidad de protocolo MCP negociada.

¿Cómo se diseñan y codifican los iconos de GitLab MCP Server?

Cada icono de dominio usa un viewport mínimo de 16×16 optimizado para listas de herramientas y barras laterales. La entrada principal es un SVG con relleno currentColor que se adapta a los temas claro y oscuro automáticamente; se incrusta como data URI base64 (data:image/svg+xml;base64,...) directamente en el binario, sin peticiones de red. Otras dos entradas son variantes WebP prerasterizadas y sin pérdida (data:image/webp;base64,..., 16×16, casi negro para Theme: "light" y casi blanco para Theme: "dark") generadas por cmd/gen_icon_webp para clientes cuya lista de MIME permitidos excluye SVG. Los iconos se adjuntan por grupo del catálogo, no por acción: una meta-herramienta lleva el icono de su grupo, y cada herramienta individual proyectada desde las acciones de ese grupo lo hereda, así que las herramientas relacionadas se agrupan visualmente. La superficie dinámica solo lista gitlab_find_action (IconSearch) y gitlab_execute_action (IconServer); las acciones a las que llegan no llevan icono propio. Los recursos y los prompts nombran su propio icono. El código fuente está en `internal/toolutil/icons.go`, más la marca del proyecto generada en internal/toolutil/brandmark_gen.go (emitida por cmd/gen_brand).

¿Qué clientes MCP renderizan los iconos de herramientas?

El renderizado de iconos varía según el cliente. VS Code con GitHub Copilot renderiza iconos de herramientas, servidor y recursos — rechaza la entrada SVG (image/svg+xml no está en su lista de MIME permitidos) pero renderiza la variante WebP que coincide con su tema. Continue.dev no renderiza iconos del protocolo MCP en ninguna versión actual, en ningún formato; tiene un ajuste de favicon por servidor no relacionado que a veces se confunde con soporte de iconos. Claude Desktop no renderiza iconos de herramientas, y Claude Code es una interfaz solo de texto que no los muestra. Como los iconos son metadatos opcionales, los clientes que no admiten su renderizado simplemente los ignoran y la funcionalidad de las herramientas no se ve afectada.

¿Afectan los iconos a la funcionalidad de las herramientas?

No. Los iconos son metadatos de presentación opcionales adjuntos a herramientas, recursos y prompts. Los clientes que no pueden renderizar iconos los ignoran, y cada herramienta se comporta de forma idéntica se muestre o no su icono. Los iconos existen únicamente para ayudar a los usuarios a reconocer visualmente los dominios de GitLab en clientes que admiten el renderizado SVG o WebP; nunca cambian los parámetros, el comportamiento ni los resultados de una herramienta.