Add icon support to server.json schema (#620)

Implements icon support based on the Model Context Protocol
specification, allowing servers to specify display icons with optional
size, MIME type, and theme variants.

## Changes

- Add `Icon` type to `pkg/model/types.go` with struct tags for automatic
API validation
- Add `icons` field to `ServerJSON` in `pkg/api/v0/types.go`
- Add icon validation in `internal/validators/validators.go`:
  - Only HTTPS URLs allowed (no HTTP or data URIs)
  - Maximum URL length of 255 characters
  - Validates absolute URLs with proper scheme
- Update JSON schema and OpenAPI documentation with Icon definitions
- Add comprehensive test cases for icon validation
- Add example icon to seed.json (Airtable server)
- Change `websiteUrl` validation to HTTPS-only for consistency

## Implementation Details

The implementation uses a two-layer validation architecture:

1. **Huma framework** validates struct tags (`required`, `maxLength`,
`enum`, `pattern`) and returns 422 for violations
2. **Custom validators** handle business logic (HTTPS scheme
requirement, absolute URL requirement) and return 400 for violations

This approach leverages Huma's automatic validation while keeping
business-specific rules in the validator layer.

## Icon Field Specification

- `src` (required): HTTPS URL to icon resource, max 255 characters
- `mimeType` (optional): One of `image/png`, `image/jpeg`, `image/jpg`,
`image/svg+xml`, `image/webp`
- `sizes` (optional): Array of size strings in "WxH" format or "any" for
scalable formats
- `theme` (optional): Either "light" or "dark" to indicate intended
background

Multiple icons can be provided (e.g., for different themes or sizes).
This commit is contained in:
adam jones 2025-10-07 13:30:32 +01:00 • committed by GitHub
commit 66993e9121
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 329 additions and 4 deletions

View file

@ -457,6 +457,37 @@ components:
items:
$ref: '#/components/schemas/KeyValueInput'
Icon:
type: object
description: An optionally-sized icon that can be displayed in a user interface
required:
- src
properties:
src:
type: string
format: uri
description: "A standard URI pointing to an icon resource. Must be an HTTPS URL. Consumers SHOULD take steps to ensure URLs serving icons are from the same domain as the server or a trusted domain. Consumers SHOULD take appropriate precautions when consuming SVGs as they can contain executable JavaScript."
example: "https://example.com/icon.png"
maxLength: 255
mimeType:
type: string
description: "Optional MIME type override if the source MIME type is missing or generic. Must be one of: image/png, image/jpeg, image/jpg, image/svg+xml, image/webp."
enum: [image/png, image/jpeg, image/jpg, image/svg+xml, image/webp]
example: "image/png"
sizes:
type: array
description: "Optional array of strings that specify sizes at which the icon can be used. Each string should be in WxH format (e.g., '48x48', '96x96') or 'any' for scalable formats like SVG. If not provided, the client should assume that the icon can be used at any size."
items:
type: string
pattern: "^(\\d+x\\d+|any)$"
examples:
- ["48x48", "96x96"]
- ["any"]
theme:
type: string
description: "Optional specifier for the theme this icon is designed for. 'light' indicates the icon is designed to be used with a light background, and 'dark' indicates the icon is designed to be used with a dark background. If not provided, the client should assume the icon can be used with any theme."
enum: [light, dark]
ServerDetail:
description: Schema for a static representation of an MCP server. Used in various contexts related to discovery, installation, and configuration.
type: object
@ -488,6 +519,11 @@ components:
format: uri
description: "Optional URL to the server's homepage, documentation, or project website. This provides a central link for users to learn more about the server. Particularly useful when the server has custom installation instructions or setup requirements."
example: "https://modelcontextprotocol.io/examples"
icons:
type: array
description: "Optional set of sized icons that the client can display in a user interface. Clients that support rendering icons MUST support at least the following MIME types: image/png and image/jpeg (safe, universal compatibility). Clients SHOULD also support: image/svg+xml (scalable but requires security precautions) and image/webp (modern, efficient format)."
items:
$ref: '#/components/schemas/Icon'
$schema:
type: string
format: uri

View file

@ -376,6 +376,51 @@
}
}
},
"Icon": {
"type": "object",
"description": "An optionally-sized icon that can be displayed in a user interface.",
"required": [
"src"
],
"properties": {
"src": {
"type": "string",
"format": "uri",
"description": "A standard URI pointing to an icon resource. Must be an HTTPS URL. Consumers SHOULD take steps to ensure URLs serving icons are from the same domain as the server or a trusted domain. Consumers SHOULD take appropriate precautions when consuming SVGs as they can contain executable JavaScript.",
"example": "https://example.com/icon.png",
"maxLength": 255
},
"mimeType": {
"type": "string",
"description": "Optional MIME type override if the source MIME type is missing or generic. Must be one of: image/png, image/jpeg, image/jpg, image/svg+xml, image/webp.",
"enum": [
"image/png",
"image/jpeg",
"image/jpg",
"image/svg+xml",
"image/webp"
],
"example": "image/png"
},
"sizes": {
"type": "array",
"description": "Optional array of strings that specify sizes at which the icon can be used. Each string should be in WxH format (e.g., '48x48', '96x96') or 'any' for scalable formats like SVG. If not provided, the client should assume that the icon can be used at any size.",
"items": {
"type": "string",
"pattern": "^(\\d+x\\d+|any)$",
"examples": ["48x48", "96x96", "any"]
}
},
"theme": {
"type": "string",
"description": "Optional specifier for the theme this icon is designed for. 'light' indicates the icon is designed to be used with a light background, and 'dark' indicates the icon is designed to be used with a dark background. If not provided, the client should assume the icon can be used with any theme.",
"enum": [
"light",
"dark"
]
}
}
},
"ServerDetail": {
"description": "Schema for a static representation of an MCP server. Used in various contexts related to discovery, installation, and configuration.",
"type": "object",
@ -423,6 +468,13 @@
"description": "Optional URL to the server's homepage, documentation, or project website. This provides a central link for users to learn more about the server. Particularly useful when the server has custom installation instructions or setup requirements.",
"example": "https://modelcontextprotocol.io/examples"
},
"icons": {
"type": "array",
"description": "Optional set of sized icons that the client can display in a user interface. Clients that support rendering icons MUST support at least the following MIME types: image/png and image/jpeg (safe, universal compatibility). Clients SHOULD also support: image/svg+xml (scalable but requires security precautions) and image/webp (modern, efficient format).",
"items": {
"$ref": "#/definitions/Icon"
}
},
"$schema": {
"type": "string",
"format": "uri",