DABYTE DATA DESK · standards
A descriptor is not a server: what MCP registries validate before they list you
Answer. Registries validate the paperwork, not the endpoint. The Official MCP Registry checks your server.json against a schema and checks that you control the namespace, but it never opens a connection to the URL you list: its remote-transport check treats the URL as a string (internal/validators/validators.go, read 2026-08-09). You can see the result in the registry's own data — the record ai.alpic.test/test-mcp-server is status active and isLatest true, its listed remote is https://test.alpic.ai/, and that hostname returns Non-existent domain. The live call arrives later, from catalogs and clients, and what it should look like changed recently: protocol revision 2026-07-28 removed the initialize handshake and the Mcp-Session-Id header, so a cold tools/list is now the specified shape rather than a shortcut. We probed 41 streamable-http endpoints listed in the registry on 2026-08-09 with exactly that call: 20 returned a tools array, 12 returned HTTP 401, 4 returned HTTP 503, 4 refused for a missing session, and 1 did not resolve.
Who reported it, and when we saw it
Key facts
- The current protocol revision is 2026-07-28, with 2025-11-25 and 2025-06-18 behind it. Its changelog lists as major changes: "Make MCP stateless: remove the initialize/notifications/initialized handshake" and "Remove protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport." Checked at modelcontextprotocol.io/specification/versioning on 2026-08-09.
- The registry lists endpoints it has never called. The record ai.alpic.test/test-mcp-server is status active, isLatest true, remote https://test.alpic.ai/; nslookup test.alpic.ai 1.1.1.1 returns Non-existent domain (both checked 2026-08-09). The relevant code is validateRemoteTransport in internal/validators/validators.go: it accepts only streamable-http or sse, requires a URL, rejects localhost and undefined template variables, imports no HTTP client, and contains no occurrence of tools/list.
- Of 41 unique streamable-http remote URLs taken from registry.modelcontextprotocol.io/v0/servers?limit=100 and probed with a bare tools/list on 2026-08-09: 20 returned a tools array, 12 returned HTTP 401, 4 returned HTTP 503, 4 refused for a missing session, and 1 did not resolve.
- Our own endpoint fails the current revision: POST server/discover to https://dabyte.ai/mcp returns HTTP 200 with {"error":{"code":-32601,"message":"method not found: server/discover"}} (checked 2026-08-09), where revision 2026-07-28 says servers MUST implement that method and MUST return HTTP 404 with -32601 for one they do not.
- The server.json schema dated 2025-12-11 sets description maxLength 100, title maxLength 100, name maxLength 200 and minLength 3 with pattern ^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$, and version maxLength 255; required fields are name, description and version. Downloaded on 2026-08-09.
- /.well-known/mcp.json is a convention, not a ratified part of MCP. The discovery proposal is SEP-2127, an open pull request that moved to in-review on 2026-08-07, and it proposes a different path: .well-known/mcp/server-cards.json.
- Glama validates two files that share the name glama.json against two incompatible schemas: /.well-known/glama.json on the domain requires maintainers as objects carrying an email (connector.json), while glama.json in a repository root requires maintainers as GitHub username strings (server.json). Both schemas require the field. Fetched 2026-08-09.
A descriptor is a file; a server is something that answers
A descriptor is a static JSON file. It can list tools, input schemas and descriptions, and a web server can hand it out with no code behind it. A server is a live endpoint that accepts an HTTP POST carrying a JSON-RPC 2.0 body and answers methods such as tools/list and tools/call. Under the current protocol revision, 2026-07-28, the server MUST provide a single HTTP endpoint path that supports POST; the separate GET stream endpoint that earlier revisions defined was removed in that revision (checked 2026-08-09). The conventional descriptor path deserves a caveat, because the title of this article leans on it. /.well-known/mcp.json is a convention, not a ratified part of MCP: no published revision defines it, and the 2026-07-28 changelog adopts no well-known discovery mechanism. The live proposal is SEP-2127, an open pull request that moved to in-review on 2026-08-07, and it proposes a different path, .well-known/mcp/server-cards.json. Our own descriptor sits at /.well-known/mcp.json and declares "schema_version": "2025-06-18", which is a protocol revision identifier rather than a descriptor schema version. Treat the shape as a convention, ours included, and not as a standard. The distinction survives the caveat, because it was never about the path. A descriptor at any path is the part that looks finished: it carries the tool names, so it reads like proof that the tools exist. It is not. Compare curl -s https://dabyte.ai/.well-known/mcp.json with a POST of a JSON-RPC body to https://dabyte.ai/mcp — two separate outputs of the same build, and only the second one answers a call. The ordering rule follows: stand up the endpoint, prove it answers, and only then emit the descriptor and file the registry record. Doing it the other way round means the failure surfaces in someone else's catalog rather than in your terminal.
What the Official Registry actually validates
The registry publishes its extra rules in docs/reference/server-json/official-registry-requirements.md. They cover four things: proof that you control the namespace you are publishing into, proof that you own any referenced package, an allowlist of package registry base URLs, and a restriction on which _meta keys survive publication. Nothing in that document concerns the behaviour of a remote endpoint. The remote-servers guide adds only that a remote server has to be publicly reachable at the URL you give. Enforcement of that sentence is thinner than it sounds. Read internal/validators/validators.go in modelcontextprotocol/registry: validateRemoteTransport switches on the transport type, accepts only streamable-http or sse, requires the URL to be non-empty, runs IsValidRemoteURL, which rejects malformed URLs and localhost, and runs IsValidTemplatedURL to catch template variables that no variables block defines. The file imports no HTTP client and contains no occurrence of the string tools/list, fetched from raw.githubusercontent.com on 2026-08-09. Reading one Go function is an inference, so here is the registry's own data instead. The record ai.alpic.test/test-mcp-server is status active and isLatest true, and the streamable-http remote it lists is https://test.alpic.ai/. That hostname does not resolve: nslookup test.alpic.ai 1.1.1.1 returns Non-existent domain, and curl against it fails at DNS (checked 2026-08-09). A record can be live in the registry while the endpoint behind it has never existed, and the registry will not tell you. Verify your own with curl "https://registry.modelcontextprotocol.io/v0/servers?search=<name>" for the record, and a separate POST to the endpoint for the reality.
The first request is not initialize any more
For most of MCP's life the answer to what a client sends first was initialize. Revision 2026-07-28 changed that. Its changelog lists among the major changes "Make MCP stateless: remove the initialize/notifications/initialized handshake" and "Remove protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport." The same revision adds server/discover, which servers MUST implement, and specifies that a server which does not implement a requested RPC method MUST respond with HTTP 404 and JSON-RPC error -32601. Revision 2025-11-25 sits between that and 2025-06-18, the revision under which the client SHOULD NOT send requests other than pings before the server has responded to initialize. So a cold tools/list is now the specified shape rather than a crawler cutting corners, and the HTTP 400 missing-session rule that earlier revisions defined is no longer part of the protocol (all checked 2026-08-09). What is actually deployed is a different question, and it is measurable. Taking the first 100 records from registry.modelcontextprotocol.io/v0/servers, keeping only isLatest entries with a streamable-http remote and no template variables in the URL, gives 41 unique endpoints. Posting {"jsonrpc":"2.0","id":1,"method":"tools/list"} to each on 2026-08-09 produced 20 tools arrays, 12 HTTP 401 responses, 4 HTTP 503 responses, 4 refusals for a missing session, and 1 that did not resolve — the alpic record above. The four refusals are servers implementing a superseded revision: api.agentic-news.ai/mcp answered HTTP 400 with code -32000 and a message about no valid session ID or initialization request; agentberg.ai/mcp and mcp.aivonic.ai answered HTTP 400 with code -32600 and "Bad Request: Missing session ID"; mcp.abmeter.ai answered HTTP 200 while the JSON-RPC body carried error -32600 saying the Mcp-Session-Id header is required. That last shape is the one to watch, because a health check that reads the HTTP status and stops records mcp.abmeter.ai as up while any consumer that reads the body gets an error. We fail a version of the same check, so here is the curl that catches us. POST {"jsonrpc":"2.0","id":1,"method":"server/discover"} to https://dabyte.ai/mcp and you get HTTP 200 with {"error":{"code":-32601,"message":"method not found: server/discover"}} (checked 2026-08-09). Under 2026-07-28 that method is mandatory and an unimplemented method must return HTTP 404 with -32601, so our endpoint is wrong in two ways at once: it does not implement server/discover, and it wraps the error in a 200 exactly as mcp.abmeter.ai does. Our initialize response still advertises protocolVersion 2025-06-18. Answering a cold tools/list is the part that keeps us listed; it is not the same as having migrated, and the measurement above is not a claim that we are on the right side of it.
One hundred characters, and the other schema limits
The description field in server.json has maxLength 100. That number is not in the tutorial prose; it is in the schema, and the way to see it is to download https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json and read definitions.ServerDetail.properties. The same block sets title to 100, version to 255, and name to a minimum of 3 and a maximum of 200 characters matching ^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$, which means exactly one slash and no spaces. Only name, description and version are required. A hundred characters is shorter than most descriptions written for a landing page. The two versions of our dabyte record run 95 and 88 characters; our dablock record runs 93 and 99, the second one a character away from rejection. Count them yourself in the API response from curl "https://registry.modelcontextprotocol.io/v0/servers?search=dabyte". A first draft lifted from marketing copy will typically run several times over and fail validation at submission rather than at review. Optional fields are not cosmetic. Version 1.0.0 of ai.dabyte/visibility-index carries $schema, name, description, version, remotes and repository — the three required fields plus two more — and no title, websiteUrl or icons; downstream catalog cards rendered the bare slug. Version 1.0.1 added exactly those three. There is no edit in place: a correction is a new version, and both remain visible in the API with isLatest distinguishing them.
Your login method names you, and the name does not move
The authentication page states the binding plainly. Authenticate with GitHub OAuth and your server name MUST be of the form io.github.username/* or io.github.orgname/*. Authenticate against a domain, by DNS TXT record or by hosted file, and your name MUST be the reverse-DNS form of that domain. The quickstart's troubleshooting table lists the error you get when the two disagree: permission is refused because the authentication method does not match the namespace format (checked 2026-08-09). There is no rename operation, and the consequence travels downstream, because catalogs build their URLs from the registry name. A Glama connector lives at /mcp/connectors/<registry-namespace>/<slug>, so a namespace chosen for convenience on day one becomes a permanent URL. You can see the shape that GitHub authentication produces without touching our records: io.github.pulse-digital-dev/mcp-ai-visibility-index. For anything published on behalf of a domain the domain path is usually the one you want, and it has to be decided before the first successful publish rather than after.
base64, hex, and a proof file that has to survive your deploys
HTTP domain authentication works by hosting a file at /.well-known/mcp-registry-auth containing a single line of the form v=MCPv1; k=ed25519; p=<key>. Two encodings appear in two separate code blocks on the same documentation page, separated by the ECDSA P-384, Google KMS and Azure Key Vault variants, and mixing them is an easy way to fail login. The public key that goes in the file comes from openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64, so it is base64 of 32 raw bytes and ends in padding. The private key handed to mcp-publisher login http comes from openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n', which yields hex. Check with curl -s https://<domain>/.well-known/mcp-registry-auth and confirm the p value looks like base64, not hex. Our own public failure was one layer up from the encoding. On 2026-08-06 that file returned 404 on both dabyte.ai and dablock.ai, although our working notes recorded it as restored and both registry records had already published. Our notes offer two candidate causes for that specific event and settle on neither: the file never reached the persist directory, or it was overwritten again. The general mechanism they do document is that the site generator replaces files it does not own, which had already cost us an IndexNow key. Nothing broke at the time, because the CLI session was still authenticated; the loss became visible only when a record needed updating. The fix is structural rather than careful. Ownership proofs are credentials, so they should not be committed to the repository, but they also cannot be hand-placed on a host that rebuilds its own document root. We moved them into an overlay directory applied after the copy step. Both paths now return 200, which anyone can check with the curl above; the 404 on 2026-08-06 is our record of the day, not something a reader can verify after the fact.
Generic names are already taken, and each catalog checks something different
Slugs are first-come. The name ai-visibility-index on PulseMCP is held by an unrelated project, and the official registry is the easier place to confirm it: curl "https://registry.modelcontextprotocol.io/v0/servers?search=visibility-index" returns io.github.pulse-digital-dev/mcp-ai-visibility-index with publishedAt 2026-06-16T08:24:54Z and isLatest true (checked 2026-08-09). PulseMCP labels 16 June 2026 as "Released On" rather than as its listing date. Check occupancy before a descriptive name enters your positioning. Note that pulsemcp.com returns HTTP 403 to a plain curl and 200 to a browser user agent (checked 2026-08-09), so a 403 from a script is not evidence that a page or a form is gone. Beyond the official registry, validation stops being uniform. Glama distinguishes a Server listing, built from a GitHub repository, from a Connector listing, built from a hosted endpoint. Both are claimed with a file named glama.json, validated against two incompatible schemas: /.well-known/glama.json on the domain must declare maintainers as objects containing an email that matches your Glama account, per connector.json, while glama.json in a repository root must declare maintainers as plain GitHub username strings, per server.json. Both schemas require the field, so a file in the wrong shape still parses as JSON and simply fails to prove ownership. The asymmetry extends to artefacts. Server badges live under /mcp/servers/<owner>/<repo>/badge and connector badges under /mcp/connectors/<namespace>/<slug>/badge: on 2026-08-09, glama.ai/mcp/servers/cisco-open/network-sketcher/badge returned 200 and glama.ai/mcp/connectors/dev.jam.mcp/jam/badge returned 404. Glama's FAQ states that only healthy connectors are indexed for search and that unhealthy connectors stay in a pending state until they become reachable, which puts the live endpoint back at the centre. Whatever the registry accepted, the catalogs decide by calling.
Questions this answers
Do I need an npm package to list a remote MCP server?
No. The quickstart is written around a TypeScript server published to npm and adds an mcpName field to package.json, which is why remote publishers often assume a package is mandatory. The remote-servers page shows a server.json with a remotes array and no packages array at all, and the schema requires only name, description and version. Both of our published servers use remotes only; you can see the full documents in the response from curl "https://registry.modelcontextprotocol.io/v0/servers?search=dabyte".
My server requires authentication. Will catalogs mark it as broken?
Requiring auth is normal: 12 of the 41 endpoints we probed on 2026-08-09 returned HTTP 401 to an unauthenticated tools/list, although one of those twelve returned 401 with a missing_session body rather than an auth error, which is a fifth flavour of the same session problem. What matters is that the transport-level answer matches reality. Returning HTTP 401 with a clear body is unambiguous to a crawler. Returning HTTP 200 with a JSON-RPC error inside, as one probed endpoint did and as our own endpoint does for server/discover, means status-code health checks record you as healthy while every real consumer sees a failure. If some of your tools are read-only, exposing tools/list without credentials also lets catalogs render your tool schemas.
The docs show /v0.1/servers but I have seen /v0/servers. Which one is live?
On 2026-08-09 both returned identical payloads for our records — two versions of ai.dabyte/visibility-index in each case — so either works today. If one path returns an empty result set, try the other before concluding your submission failed. The registry is in preview and its maintainers warn about breaking changes and data resets, so treat the API path as something to re-check rather than hard-code once.
Can I rename a server after publishing?
There is no rename. The name is bound to the authentication method you used, so changing namespace means publishing under a new name; the old record stays in the registry as the latest version of its own name rather than being superseded, because isLatest is scoped per server name. Our two versions are not an example of this — they are a metadata correction under one name, described above. Downstream URLs derived from the namespace, such as a Glama connector path, inherit whatever you chose first.
Machine access
/api/articles.json— every piece we published, with the measurement each one rests on/api/aiv.json— the AI Visibility Index the numbers above come from- How the index is measured