Repository navigation
Commit 31d2255
Fixes #21494
Clause-②: no (registry metadata; no accept-set or published API surface
change)
## What this adds
`packages/mcp/server.json` is one `server.json` for the official MCP
registry, written from what ships. It is metadata only. It does not
change code, any manifest or any workflow, and it adds no CI gate.
It carries **one** entry: a `streamable-http` **remote**. The host in
that URL is the user's own deployment: `https://{host}/api/v1/mcp`. It
carries **no** npm package entry.
The submission (`mcp-publisher login` and `publish` under the project's
account) stays with the maintainer, on the `Maintainer-action:` line of
#20791. So do the mcp.so, Smithery and PulseMCP listings. #20791 remains
open.
## Measured first: which entry the registry can truthfully carry
### Remote entry: yes
Measured on the **published** `@objectstack/cli@17.6.0`. The command was
`npx -y @objectstack/cli@17.6.0 start --home SCRATCH -p RANDOM --no-ui`.
It booted the empty kernel and was torn down by its recorded process
group.
| probe | answer |
|---|---|
| `GET /api/v1/discovery` | `"mcp": "/api/v1/mcp"` |
| `initialize` on `POST /api/v1/mcp`, anonymous | `401`, with
`WWW-Authenticate: Bearer realm="ObjectStack MCP",
resource_metadata=".../.well-known/oauth-protected-resource"` |
| `initialize` with `x-api-key: osk_…` (key minted by `POST
/api/v1/keys` after an email sign-up) | `200`, `serverInfo
{"name":"objectstack","version":"1.0.0"}` |
| `initialize` with `Authorization: Bearer osk_…` | `200` |
- **The path is the same on every CLI-served deployment.**
`createDispatcherPlugin` defaults `prefix` to `/api/v1`
(`packages/runtime/src/dispatcher-plugin.ts`), and `os serve` constructs
it with no prefix.
- **No hosted URL is invented.** There is no single hosted URL, so the
host is a required URL template variable. The registry documents remote
URL template variables for this case: "multi-tenant deployments where a
single server definition can support multiple endpoints".
- **The registry's semantic validator accepts it.** It accepts `{host}`
in a remote URL and requires `https` (`IsValidRemoteURL` in
`internal/validators/utils.go`, registry repo at `bf4e88cb`).
- **`x-api-key` is declared optional and secret.** On https deployments
the OAuth track is live. `plugin-auth` refuses it only on public plain
HTTP.
### npm package entry over stdio: no
- **`@objectstack/mcp` cannot be run with npx.** None of its 64
published versions declares a `bin` (npm packument). `npx -y
@objectstack/mcp@17.6.0` exits 1 with `npm error could not determine
executable to run`.
- **The stdio transport runs only as a mode of the user's app.** It
starts with `OS_MCP_STDIO_ENABLED=true OS_MCP_STDIO_API_KEY=osk_… os
start`, run in the project directory (`content/docs/ai/connect-mcp.mdx`,
`packages/cli/src/commands/start.ts`).
- **The CLI package resolves, but the registry cannot set its working
directory.** `npx -y @objectstack/cli@17.6.0 --version` does resolve a
bin, because both bins point at one file. The registry's `Package`
definition has no working-directory field. Its properties are
environmentVariables, fileSha256, identifier, packageArguments,
registryBaseUrl, registryType, runtimeArguments, runtimeHint, transport
and version.
- **Launched from a client's directory, the published CLI never serves
the user's app.** Measured: it boots the empty kernel (`Artifact: none
(empty kernel — install apps via the Console marketplace)`). The stdio
plugin then refuses the key and the boot exits 1: `OS_MCP_STDIO_API_KEY
did not resolve to a valid identity … Refusing to start stdio
(ADR-0101)`.
So an npm entry would name nothing that runs today. Whether to ship a
standalone stdio launcher is a product question, not this card's.
### Ownership marker (A3): not owed, manifest untouched
- **npm entries are checked at publish time.** The registry reads
`mcpName` from `registry.npmjs.org/{identifier}/{version}`
(`internal/validators/registries/npm.go`).
- **Remotes have no ownership check.** They need only namespace
authentication at publish.
- **No published version carries the marker.** `@objectstack/mcp@17.6.0`
has no `mcpName`.
With no npm entry, no marker is owed. So `package.json` is not edited,
and no changeset is added.
## Placement (A4)
- **What reads the file.** `mcp-publisher publish` and `mcp-publisher
validate` read `./server.json` from the working directory by default, or
the path they are given.
- **Nothing here reads it.** No step in this repository's release flow
reads a `server.json` (`git grep` found zero before this PR).
- **Where it sits.** With no measured reader, it goes in
`packages/mcp/server.json`, beside the package whose surface it
describes, and `repository.subfolder` points there.
- **How to submit.** Run `mcp-publisher publish
packages/mcp/server.json`, or run it from `packages/mcp`.
- **It does not ship.** `npm pack --dry-run` in `packages/mcp` lists
CHANGELOG.md, LICENSE, README.md and package.json, with README.md as the
positive control. `server.json` is absent, because `files` names only
`dist`, `README.md` and `CHANGELOG.md`. This is why the PR carries
`skip-changeset`.
## Validation
All runs were at HEAD `9eda36398c`. Each was a one-off local run.
Nothing is committed.
| validator | against | result |
|---|---|---|
| `mcp-publisher validate packages/mcp/server.json`. mcp-publisher 1.8.1
(commit `f52dc85`) is the latest release. | the live registry's `POST
/v0/validate`, which runs schema and semantic validation (registry
`/v0/version`: 1.8.1) | `✅ server.json is valid`, exit 0 |
| direct `POST https://registry.modelcontextprotocol.io/v0/validate` |
the same | `{"valid":true,"issues":[]}` |
| ajv 8.20.0 with ajv-formats 3.0.1, draft-07 |
`https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json`,
schema version **2025-12-11**, the registry's `CurrentSchemaVersion`
(fetched copy sha256 `3fba0959…`) | `VALID`, exit 0 |
**Negative controls.** These show that both validators can fail:
- **`{tenant}` in the URL, with only `host` declared.** mcp-publisher
exits 1 with `invalid-templated-url`. ajv says VALID, because the schema
alone cannot see this.
- **An `http://{host}/…` URL.** mcp-publisher exits 1 with
`invalid-remote-url`. ajv says VALID.
- **A 101-character description.** mcp-publisher exits 1 (422, expected
length at most 100). ajv says INVALID and exits 1.
The registry validator is the stronger of the two, and the file passes
both.
**NOT MEASURED: namespace authentication at publish.** It needs the
project's account.
## Choices the maintainer may change before submitting
- **`name`: `io.github.objectstack-ai/objectstack`.** This is the
GitHub-org namespace, and it matches `repository.url`. The registry docs
say a login must be an **Owner** of the objectstack-ai org to publish
under it. The alternative is a domain namespace,
`ai.objectstack/objectstack`, which needs a DNS TXT record on the apex.
Only this one line changes.
- **`version`: `17.6.0`.** This is the npm `latest` release, the one
whose surface this entry was written from. The registry needs a new
version on every publish. Nothing here bumps it, because the card rules
out a new gate.
- **`websiteUrl`: `https://objectstack.ai/docs/ai/connect-mcp`.** It
maps to `content/docs/ai/connect-mcp.mdx` under the docs site's `/docs`
mount (`apps/docs/lib/source.ts`, the mapping
`check-published-readme-links.mjs` reads). NOT MEASURED live: this
container's egress proxy refuses CONNECT to objectstack.ai with 403.
## Acceptance notes
- **The server's reported version disagrees with its documentation.**
The server reports `serverInfo.version` `"1.0.0"` (measured above).
`MCPServerPluginOptions.version` is documented as "Defaults to package
version." The registry calls server.json `version` the equivalent of MCP
`Implementation.version`, so today the two differ: 17.6.0 here, 1.0.0
from the server. This is reported to the seat as a finding and is not
touched here.
- **The duplicate-URL check matches the literal template string.** The
registry refuses a second server that publishes an identical remote URL
string. Here that string is `https://{host}/api/v1/mcp`. `GET
/v0/servers?search=objectstack` answered 0, and the control
`search=weather` answered 1.
## Gates
- **Derived gates.** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at `9eda36398c` named 42 commands.
All 42 ran and exited 0. The `--ran` reconciliation printed "42 derived
famil(ies) accounted for — 42 run, 0 NOT-MEASURED (a DERIVED zero …)".
Four of them needed a workspace build first (turbo, 72 of 72 tasks):
`check:dts-closure`, `check:dual-build-cjs-loads`,
`check:lean-entry-closure` and `check:sourcemap-no-sources-content`.
- **`@objectstack/mcp`.** `vitest run` passed 34 files and 382 tests.
`typecheck` exited 0.
- **Lint.** This is a proven narrowing, not a `pnpm lint` run:
1. Population, from `eslint.config.mjs`: every `files` glob names only
`{ts,tsx,mts,cts,js,jsx,mjs,cjs}`. ESLint's own verdict on this file is
"File ignored because no matching configuration was supplied."
2. Count, from `--format json`: 1 result with 0 errors and 1 warning
(that notice). So 0 files were linted.
3. Invariance: the config never enables type-aware linting. No block
sets `parserOptions.project` or `projectService`. No source file
references `server.json` (`git grep` exit 1, control exit 0). So this
diff cannot change the verdict on any other file.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 550f4cc commit 31d2255
1 file changed
Lines changed: 35 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
0 commit comments