diff --git a/CHANGELOG.md b/CHANGELOG.md index 2383639..057338a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased -- Public MCP server: the bare host is the endpoint too. `POST /` answers MCP like `/mcp` (someone who pastes `https://mcp.example.com` into a connector gets connected), `GET /` names the connect URL. A signed-in server keeps `/` closed. +- Public MCP server: the bare host is the endpoint too. `POST /` answers MCP like `/mcp` (someone who pastes `https://mcp.example.com` into a connector gets connected), `GET /` sends a browser to the landing page and names the connect URL to any other client. A signed-in server keeps `/` closed. ## 0.8.69 ยท 2026-11-07 diff --git a/cli/mcp-http.ts b/cli/mcp-http.ts index d20b184..8b24a3f 100644 --- a/cli/mcp-http.ts +++ b/cli/mcp-http.ts @@ -205,6 +205,8 @@ export function startHttp(o: HttpOptions): Promise<{ close: () => void; port: nu // public: the bare host is the MCP endpoint too (people paste "mcp.example.com" into a connector), and a browser // opening it gets the connect line instead of a 404 if (pub && path === '/' && req.method === 'GET') { + // a person in a browser goes to the landing page; any other client gets the connect line + if (/text\/html/.test(String(req.headers.accept ?? ''))) { res.writeHead(302, { ...cors, location: 'https://storebound.dev/' }); return res.end(); } res.writeHead(200, { ...cors, 'content-type': 'text/plain; charset=utf-8' }); return res.end(`storebound MCP server ${pkg.version} (read-only, no key)\nAdd it as a remote MCP server / custom connector with this URL. Claude Code: claude mcp add --transport http storebound \n`); } diff --git a/tests/mcp-public.test.ts b/tests/mcp-public.test.ts index 26473b2..aab684e 100644 --- a/tests/mcp-public.test.ts +++ b/tests/mcp-public.test.ts @@ -244,6 +244,9 @@ describe('http, public mode', () => { const g = await fetch(`http://127.0.0.1:${s.port}/`); expect(g.status).toBe(200); expect(await g.text()).toContain('claude mcp add --transport http storebound'); + const b = await fetch(`http://127.0.0.1:${s.port}/`, { headers: { accept: 'text/html,application/xhtml+xml' }, redirect: 'manual' }); + expect(b.status).toBe(302); + expect(b.headers.get('location')).toBe('https://storebound.dev/'); expect((await fetch(`http://127.0.0.1:${s.port}/other`)).status).toBe(404); } finally { s.close(); } });