Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/mcp-server/scripts/publish-mcp-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,10 @@ if (npmVersion) {
// Repository link — optional, but if present it must be publicly readable
// ---------------------------------------------------------------------------
//
// The field is deliberately absent from server.json; see repositoryLink.ts for
// why, and for the verdict rules this check applies. It exists because the
// private monorepo URL shipped once already, in 0.2.0.
// server.json points at ShiplightAI/shiplight-cli, which is public; see
// repositoryLink.ts for the verdict rules this check applies. The check exists
// because a private monorepo URL shipped once already, in 0.2.0, and the field
// then stayed absent through 0.2.3 rather than ship a link nobody could open.
step('Repository link');
const repoUrl = manifest.repository?.url;
let repoStatus = '';
Expand Down
16 changes: 7 additions & 9 deletions apps/mcp-server/scripts/repositoryLink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,15 @@ describe('server.json', () => {
readFileSync(join(dirname(dirname(fileURLToPath(import.meta.url))), 'server.json'), 'utf8'),
) as { repository?: { url?: string } };

it('does not link a repository readers cannot open', () => {
it('links the public repository that holds this server', () => {
// Checked without a network call so it holds in any environment: the URL
// itself is the defect, whatever GitHub happens to answer today. The field
// is optional in the registry schema and stays omitted while this
// repository is not public — see the note in
// scripts/__tests__/server-json-registry-manifest.test.ts for how to add it
// back when that changes.
// itself is what matters here, whatever GitHub happens to answer today. The
// publish preflight does fetch it anonymously and blocks on a 4xx, which is
// what catches a regression to a private or wrong repo.
assert.equal(
manifest.repository,
undefined,
'server.json declares a repository, but this repo is not public — the link would 404 for registry readers.',
manifest.repository?.url,
'https://github.com/ShiplightAI/shiplight-cli',
'server.json must link the public repo that holds this server, so registry readers can reach its source.',
);
});
});
15 changes: 9 additions & 6 deletions apps/mcp-server/scripts/repositoryLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ const TRANSIENT_4XX = new Set(['408', '425', '429']);
/**
* Decide what an anonymous fetch of `repository.url` means for a release.
*
* `repository` is optional in the registry schema, and ours is deliberately
* absent: the server's source lives in a private monorepo, and a link nobody
* can open is worse than no link at all. Pointing it at some other public
* Shiplight repo is not a fix either — the field exists so reviewers can read
* *this server's* code, so a wrong repo misleads exactly the people it is meant
* to serve.
* `repository` is optional in the registry schema. Ours points at
* ShiplightAI/shiplight-cli, which holds this server's source and is public.
*
* It was absent for 0.2.1 through 0.2.3, when that source sat in a private
* monorepo and a link nobody could open was worse than no link at all. That
* reasoning still governs the verdicts below: a link is only worth publishing
* if an anonymous reader can follow it to *this server's* code, so a private
* repo and a wrong-but-public one are equally useless to the reviewers the
* field exists to serve.
*
* Only a 4xx blocks, and only a 4xx that means "you cannot see this". A 5xx or
* an unreachable host is trouble at GitHub's end or on the runner, and must not
Expand Down
5 changes: 5 additions & 0 deletions apps/mcp-server/server.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"name": "ai.shiplight/shiplight",
"title": "Shiplight",
"description": "An MCP server that provides browser automation",
"repository": {
"url": "https://github.com/ShiplightAI/shiplight-cli",
"source": "github",
"subfolder": "apps/mcp-server"
},
"version": "0.2.3",
"websiteUrl": "https://www.shiplight.ai",
"packages": [
Expand Down
30 changes: 15 additions & 15 deletions scripts/__tests__/server-json-registry-manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
* the schema was the whole contract cost the 0.2.1 registry publish, after npm
* had already been written and could not be taken back.
*
* `repository`, by contrast, really is optional and is omitted on purpose: the
* source is private, and a link nobody outside the org can open is worse than
* no link. See the repository tests below.
* `repository`, by contrast, really is optional. It was omitted through 0.2.3,
* when the source was private and a link nobody outside the org could open was
* worse than no link; it now points at the public repo. See the repository
* tests below.
*/
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
Expand Down Expand Up @@ -104,26 +105,25 @@ describe('server.json tracks the published MCP package', () => {
assert.match(manifest.websiteUrl ?? '', /^https:\/\//);
});

test('does not advertise a repository readers cannot open', () => {
test('advertises the public repository that holds this server', () => {
// The field is optional in the schema — only name, description and version
// are required — and 0.2.0 shipped one pointing at a repository that 404'd
// for everyone outside the org, so every reader of the registry entry got a
// broken link. It stays omitted while this repository is not public.
// broken link. It stayed omitted through 0.2.3 for that reason.
//
// When ShiplightAI/shiplight-cli goes public, add it back:
// "repository": { "url": "https://github.com/ShiplightAI/shiplight-cli",
// "source": "github", "subfolder": "apps/mcp-server" }
// and invert this assertion. The publish workflow's manifest validator
// checks the URL is anonymously reachable, so it will confirm the change.
// ShiplightAI/shiplight-cli is public now and holds this server's source,
// so the link is worth publishing. subfolder points at it inside the
// monorepo. The publish preflight fetches the URL anonymously and blocks on
// a 4xx, so a regression to a private or wrong repo fails the release.
//
// apps/mcp-server/scripts/repositoryLink.test.ts asserts the same thing in
// the mcp-server lane, so a developer running that package's tests sees it
// without waiting for this one.
assert.equal(
manifest.repository,
undefined,
'server.json declares a repository, but this repo is not public — the link would 404 for registry readers',
);
assert.deepEqual(manifest.repository, {
url: 'https://github.com/ShiplightAI/shiplight-cli',
source: 'github',
subfolder: 'apps/mcp-server',
});
});

test('if a repository is declared at all, it is a well-formed public GitHub link', () => {
Expand Down
Loading