Skip to content

Commit 31d2255

Browse files
feat(mcp): server.json for the official MCP registry — one remote entry on the user's own deployment, no npm entry (#21494) (#21530)
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

File tree

‎packages/mcp/server.json‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3+
"name": "io.github.objectstack-ai/objectstack",
4+
"title": "ObjectStack",
5+
"description": "Read and write your ObjectStack app's records and run its exposed actions, as MCP tools.",
6+
"version": "17.6.0",
7+
"websiteUrl": "https://objectstack.ai/docs/ai/connect-mcp",
8+
"repository": {
9+
"url": "https://github.com/objectstack-ai/objectstack",
10+
"source": "github",
11+
"id": "1136691870",
12+
"subfolder": "packages/mcp"
13+
},
14+
"remotes": [
15+
{
16+
"type": "streamable-http",
17+
"url": "https://{host}/api/v1/mcp",
18+
"variables": {
19+
"host": {
20+
"description": "Host name of your own ObjectStack deployment, plus :port when it is not 443. Every deployment serves MCP at /api/v1/mcp; there is no shared hosted endpoint.",
21+
"isRequired": true,
22+
"placeholder": "crm.example.com"
23+
}
24+
},
25+
"headers": [
26+
{
27+
"name": "x-api-key",
28+
"description": "Optional osk_ API key, minted on the deployment's Connect an Agent page. Leave it unset to sign in with OAuth instead.",
29+
"isRequired": false,
30+
"isSecret": true
31+
}
32+
]
33+
}
34+
]
35+
}

0 commit comments

Comments
 (0)