Skip to content

Commit 9828234

Browse files
committed
Add optional title field to server.json schema
Introduces a new optional "title" field for MCP servers to provide a clean, human-readable display name. This allows clients and subregistries to show more concise names in their UIs. Key changes: - Added title field to ServerJSON type (max 100 chars) - Added validation to reject titles ending with 'MCP Server', 'MCP server', or 'MCP' suffixes - Updated JSON schema and OpenAPI spec - Added comprehensive test coverage for title validation - Updated documentation examples to demonstrate usage Examples of valid titles: "GitHub", "Weather API", "Slack Integration" Examples of invalid titles: "GitHub MCP", "Weather MCP Server"
1 parent 843783a commit 9828234

File tree

8 files changed

+239
-26
lines changed

8 files changed

+239
-26
lines changed

docs/guides/publishing/publish-server.md

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,14 @@ This creates a `server.json` with auto-detected values. You'll see something lik
9494
```json
9595
{
9696
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
97-
"name": "io.github.yourname/your-server",
98-
"description": "A description of your MCP server",
97+
"name": "io.github.yourname/weather-data-mcp",
98+
"title": "Weather Data",
99+
"description": "Access real-time weather data and forecasts",
99100
"version": "1.0.0",
100101
"packages": [
101102
{
102103
"registryType": "npm",
103-
"identifier": "your-package-name",
104+
"identifier": "@yourname/weather-data-mcp",
104105
"version": "1.0.0",
105106
"transport": {
106107
"type": "stdio"
@@ -153,13 +154,14 @@ Add an `mcpName` field to your `package.json`:
153154
```json
154155
{
155156
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
156-
"name": "io.github.username/server-name",
157-
"description": "A server that provides npm package functionality",
157+
"name": "io.github.username/slack-integration-mcp",
158+
"title": "Slack Integration",
159+
"description": "Send messages and manage Slack workspaces",
158160
"version": "1.0.0",
159161
"packages": [
160162
{
161163
"registryType": "npm",
162-
"identifier": "your-npm-package",
164+
"identifier": "@username/slack-integration-mcp",
163165
"version": "1.0.0",
164166
"transport": {
165167
"type": "stdio"
@@ -191,13 +193,14 @@ Add it to your README.md file (which becomes the package description on PyPI). T
191193
```json
192194
{
193195
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
194-
"name": "io.github.username/server-name",
195-
"description": "A server that provides PyPI package functionality",
196+
"name": "io.github.username/database-query-mcp",
197+
"title": "Database Query",
198+
"description": "Execute SQL queries and manage database connections",
196199
"version": "1.0.0",
197200
"packages": [
198201
{
199202
"registryType": "pypi",
200-
"identifier": "your-pypi-package",
203+
"identifier": "database-query-mcp",
201204
"version": "1.0.0",
202205
"transport": {
203206
"type": "stdio"
@@ -229,13 +232,14 @@ Add a README file to your NuGet package that includes the server name. This can
229232
```json
230233
{
231234
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
232-
"name": "io.github.username/server-name",
233-
"description": "A server that provides NuGet package functionality",
235+
"name": "io.github.username/azure-devops-mcp",
236+
"title": "Azure DevOps",
237+
"description": "Manage Azure DevOps work items and pipelines",
234238
"version": "1.0.0",
235239
"packages": [
236240
{
237241
"registryType": "nuget",
238-
"identifier": "Your.NuGet.Package",
242+
"identifier": "Username.AzureDevOpsMcp",
239243
"version": "1.0.0",
240244
"transport": {
241245
"type": "stdio"
@@ -271,14 +275,15 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
271275
```json
272276
{
273277
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
274-
"name": "io.github.username/server-name",
275-
"description": "A server that provides Docker container functionality",
278+
"name": "io.github.username/kubernetes-manager-mcp",
279+
"title": "Kubernetes Manager",
280+
"description": "Deploy and manage Kubernetes resources",
276281
"version": "1.0.0",
277282
"packages": [
278283
{
279284
"registryType": "oci",
280285
"registryBaseUrl": "https://docker.io",
281-
"identifier": "yourusername/your-mcp-server",
286+
"identifier": "yourusername/kubernetes-manager-mcp",
282287
"version": "1.0.0",
283288
"transport": {
284289
"type": "stdio"
@@ -292,14 +297,15 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
292297
```json
293298
{
294299
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
295-
"name": "io.github.username/server-name",
296-
"description": "A server that provides GitHub container functionality",
300+
"name": "io.github.username/git-operations-mcp",
301+
"title": "Git Operations",
302+
"description": "Advanced Git repository management and operations",
297303
"version": "1.0.0",
298304
"packages": [
299305
{
300306
"registryType": "oci",
301307
"registryBaseUrl": "https://ghcr.io",
302-
"identifier": "yourusername/your-mcp-server",
308+
"identifier": "username/git-operations-mcp",
303309
"version": "1.0.0",
304310
"transport": {
305311
"type": "stdio"
@@ -334,13 +340,14 @@ openssl dgst -sha256 server.mcpb
334340
```json
335341
{
336342
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
337-
"name": "io.github.username/server-name",
338-
"description": "A server that provides MCPB package functionality",
343+
"name": "io.github.username/image-processor-mcp",
344+
"title": "Image Processor",
345+
"description": "Process and transform images with various filters",
339346
"version": "1.0.0",
340347
"packages": [
341348
{
342349
"registryType": "mcpb",
343-
"identifier": "https://github.com/you/your-repo/releases/download/v1.0.0/server.mcpb",
350+
"identifier": "https://github.com/username/image-processor-mcp/releases/download/v1.0.0/image-processor.mcpb",
344351
"version": "1.0.0",
345352
"fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
346353
"transport": {
@@ -379,13 +386,14 @@ Add the `remotes` field to your `server.json` (can coexist with `packages`):
379386
```json
380387
{
381388
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
382-
"name": "com.yourcompany/api-server",
383-
"description": "Cloud-hosted MCP server for API operations",
389+
"name": "com.yourcompany/acme-analytics",
390+
"title": "ACME Analytics",
391+
"description": "Real-time business intelligence and reporting platform",
384392
"version": "2.0.0",
385393
"remotes": [
386394
{
387395
"type": "streamable-http",
388-
"url": "https://mcp.yourcompany.com/http"
396+
"url": "https://mcp.yourcompany.com/mcp"
389397
}
390398
]
391399
}
@@ -400,7 +408,7 @@ You can offer multiple connection methods:
400408
"remotes": [
401409
{
402410
"type": "streamable-http",
403-
"url": "https://mcp.yourcompany.com/http"
411+
"url": "https://mcp.yourcompany.com/mcp"
404412
},
405413
{
406414
"type": "sse",
@@ -424,7 +432,7 @@ Configure headers that clients should send when connecting:
424432
"remotes": [
425433
{
426434
"type": "streamable-http",
427-
"url": "https://mcp.yourcompany.com/http",
435+
"url": "https://mcp.yourcompany.com/mcp",
428436
"headers": [
429437
{
430438
"name": "X-API-Key",

docs/reference/api/openapi.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ paths:
156156
value:
157157
name: "io.modelcontextprotocol/filesystem"
158158
description: "Node.js server implementing Model Context Protocol (MCP) for filesystem operations."
159+
title: "Filesystem"
159160
repository:
160161
url: "https://github.com/modelcontextprotocol/servers"
161162
source: "github"
@@ -177,6 +178,7 @@ paths:
177178
value:
178179
name: "com.example/demo-server"
179180
description: "Example MCP server demonstrating publisher extensions."
181+
title: "Demo Server"
180182
repository:
181183
url: "https://github.com/example/mcp-demo"
182184
source: "github"
@@ -282,6 +284,10 @@ components:
282284
type: string
283285
description: "Human-readable description of the server's functionality"
284286
example: "Node.js server implementing Model Context Protocol (MCP) for filesystem operations."
287+
title:
288+
type: string
289+
description: "Optional human-readable title or display name for the MCP server. MCP subregistries or clients MAY choose to use this for display purposes. Should be clean and concise without 'MCP Server' or 'MCP' suffixes (e.g., 'GitHub' not 'GitHub MCP Server')."
290+
example: "Filesystem"
285291
repository:
286292
$ref: '#/components/schemas/Repository'
287293
version:

docs/reference/server-json/generic-server-json.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ The official registry has some more restrictions on top of this. See the [offici
2525
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
2626
"name": "io.modelcontextprotocol.anonymous/brave-search",
2727
"description": "MCP server for Brave Search API integration",
28+
"title": "Brave Search",
2829
"websiteUrl": "https://anonymous.modelcontextprotocol.io/examples",
2930
"repository": {
3031
"url": "https://github.com/modelcontextprotocol/servers",
@@ -71,6 +72,7 @@ For MCP servers located within a subdirectory of a larger repository (monorepo s
7172
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
7273
"name": "io.modelcontextprotocol/everything",
7374
"description": "MCP server that exercises all the features of the MCP protocol",
75+
"title": "Everything",
7476
"repository": {
7577
"url": "https://github.com/modelcontextprotocol/servers",
7678
"source": "github",
@@ -152,6 +154,7 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
152154
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
153155
"name": "io.github.modelcontextprotocol/filesystem",
154156
"description": "Node.js server implementing Model Context Protocol (MCP) for filesystem operations.",
157+
"title": "Filesystem",
155158
"repository": {
156159
"url": "https://github.com/modelcontextprotocol/servers",
157160
"source": "github",
@@ -285,6 +288,7 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
285288
{
286289
"name": "io.github.example/weather-mcp",
287290
"description": "Python MCP server for weather data access",
291+
"title": "Weather",
288292
"repository": {
289293
"url": "https://github.com/example/weather-mcp",
290294
"source": "github",
@@ -498,6 +502,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
498502
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
499503
"name": "io.modelcontextprotocol.anonymous/hybrid-mcp",
500504
"description": "MCP server available as both local package and remote service",
505+
"title": "Hybrid",
501506
"repository": {
502507
"url": "https://github.com/example/hybrid-mcp",
503508
"source": "github",
@@ -582,6 +587,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
582587
{
583588
"name": "io.modelcontextprotocol/text-editor",
584589
"description": "MCP Bundle server for advanced text editing capabilities",
590+
"title": "Text Editor",
585591
"repository": {
586592
"url": "https://github.com/modelcontextprotocol/text-editor-mcpb",
587593
"source": "github"
@@ -626,6 +632,7 @@ Some CLI tools bundle an MCP server, without a standalone MCP package or a publi
626632
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
627633
"name": "io.snyk/cli-mcp",
628634
"description": "MCP server provided by the Snyk CLI",
635+
"title": "Snyk",
629636
"version": "1.1298.0",
630637
"packages": [
631638
{

docs/reference/server-json/server.schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@
5858
"minLength": 1,
5959
"maxLength": 100
6060
},
61+
"title": {
62+
"type": "string",
63+
"description": "Optional human-readable title or display name for the MCP server. MCP subregistries or clients MAY choose to use this for display purposes. Should be clean and concise without 'MCP Server' or 'MCP' suffixes (e.g., 'GitHub' not 'GitHub MCP Server').",
64+
"example": "Weather API",
65+
"minLength": 1,
66+
"maxLength": 100
67+
},
6168
"repository": {
6269
"$ref": "#/definitions/Repository",
6370
"description": "Optional repository metadata for the MCP server source code. Recommended for transparency and security inspection."

internal/validators/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ var (
2929
// Server name validation errors
3030
ErrMultipleSlashesInServerName = errors.New("server name cannot contain multiple slashes")
3131
ErrInvalidServerNameFormat = errors.New("server name format is invalid")
32+
33+
// Title validation errors
34+
ErrTitleHasMCPSuffix = errors.New("title should not end with 'MCP Server', 'MCP server', or 'MCP' suffix. For example, name your server 'CoolThing' instead of 'CoolThing MCP Server'")
3235
)
3336

3437
// RepositorySource represents valid repository sources

internal/validators/validators.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ func ValidateServerJSON(serverJSON *apiv0.ServerJSON) error {
7373
return err
7474
}
7575

76+
// Validate title if provided
77+
if err := validateTitle(serverJSON.Title); err != nil {
78+
return err
79+
}
80+
7681
// Validate all packages (basic field validation)
7782
// Detailed package validation (including registry checks) is done during publish
7883
for _, pkg := range serverJSON.Packages {
@@ -146,6 +151,35 @@ func validateWebsiteURL(websiteURL string) error {
146151
return nil
147152
}
148153

154+
func validateTitle(title string) error {
155+
// Skip validation if title is not provided (optional field)
156+
if title == "" {
157+
return nil
158+
}
159+
160+
// Check that title is not only whitespace
161+
if strings.TrimSpace(title) == "" {
162+
return fmt.Errorf("title cannot be only whitespace")
163+
}
164+
165+
// Check for MCP suffix variants (case-insensitive)
166+
titleLower := strings.ToLower(strings.TrimSpace(title))
167+
invalidSuffixes := []string{
168+
" mcp server",
169+
" mcp",
170+
"-mcp server",
171+
"-mcp",
172+
}
173+
174+
for _, suffix := range invalidSuffixes {
175+
if strings.HasSuffix(titleLower, suffix) {
176+
return fmt.Errorf("%w: found '%s' suffix", ErrTitleHasMCPSuffix, suffix)
177+
}
178+
}
179+
180+
return nil
181+
}
182+
149183
func validatePackageField(obj *model.Package) error {
150184
if !HasNoSpaces(obj.Identifier) {
151185
return ErrPackageNameHasSpaces

0 commit comments

Comments
 (0)