feat(mcp): publish canonical server.json (#1510)

## Description

Headroom can launch its MCP server, but did not publish a canonical
`server.json` that registries and MCP hosts can consume directly. This
PR adds a shared descriptor builder, commits a root `server.json`,
parity-tests that artifact against the builder and existing runtime
spec, and updates docs so registry authors do not need to reconstruct
`headroom mcp serve` from prose.

Closes #929.

## Type of Change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Added a shared `server_json.py` descriptor builder for Headroom MCP
publication metadata.
- Published a canonical root `server.json` and parity-tested it against
the builder.
- Encoded the publishable uvx contract as `headroom-ai[mcp]` plus
`headroom mcp serve`.
- Updated README and MCP docs to point registry authors at the canonical
descriptor.
- Added the README ownership marker used by MCP Registry verification.
- Kept existing registrars and `headroom mcp install` behavior
unchanged.

## Testing

- [x] Unit tests pass
- [x] Linting passes
- [x] Type checking passes
- [x] New tests added for new functionality when applicable
- [x] Manual testing performed

### Test Output

```text
Focused registry/server-json tests and reviewer approval were completed on this PR before the governance body cleanup. The current body update is documentation-only metadata for PR governance.
```

## Real Behavior Proof

- Environment: Headroom development checkout with MCP test dependencies.
- Exact command / steps: Inspected the generated `server.json` contract
and parity coverage against the descriptor builder and runtime MCP spec.
- Observed result: The committed descriptor matches the builder/runtime
contract and advertises the intended `headroom-ai[mcp]` / `headroom mcp
serve` launch path.
- Not tested: live publication to third-party registries

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Additional Notes

This body was normalized by a maintainer after approval so the
governance parser reflects the already-reviewed PR state.
This commit is contained in:
Rod Boev 2026-07-14 13:25:29 -04:00 committed by GitHub
parent d7283387ac
commit e9e9cd55b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 268 additions and 0 deletions

View file

@ -20,6 +20,8 @@
<a href="https://headroom-docs.vercel.app/docs"><img src="https://img.shields.io/badge/docs-online-blue.svg" alt="Docs"></a>
</p>
<!-- mcp-name: io.github.headroomlabs-ai/headroom -->
<p align="center">
<a href="https://headroom-docs.vercel.app/docs">Docs</a> ·
<a href="#get-started-60-seconds">Install</a> ·
@ -238,6 +240,7 @@ shows an **Output Tokens Saved** card next to input compression, labelled
Any OpenAI-compatible client works via `headroom proxy`. MCP-native: `headroom mcp install`.
Undo durable wrapping with `headroom unwrap <tool>` (supports: `claude`, `copilot`, `codex`, `opencode`, `openclaw`).
Registry authors can use the canonical [`server.json`](server.json) in the repo root instead of reconstructing the `headroom mcp serve` contract from prose.
### GitHub Copilot CLI subscription mode

View file

@ -121,6 +121,8 @@ headroom mcp serve --debug
For MCP hosts that let you configure a local stdio server, point them at `headroom mcp serve`. If you also run the proxy, pass the proxy URL explicitly so retrieval and stats come from the intended proxy instance.
If you are publishing or consuming Headroom through an MCP registry, use the canonical descriptor at `https://github.com/headroomlabs-ai/headroom/blob/main/server.json`. It captures the package form for `headroom-ai[mcp]` and the current `headroom mcp serve` launch contract in one file.
```json
{
"mcpServers": {

View file

@ -26,6 +26,7 @@ from .install import (
install_everywhere,
)
from .opencode import OpencodeRegistrar
from .server_json import build_server_json, render_server_json
__all__ = [
"DEFAULT_PROXY_URL",
@ -39,9 +40,11 @@ __all__ = [
"any_succeeded",
"build_headroom_spec",
"build_serena_spec",
"build_server_json",
"build_tokensave_spec",
"format_result",
"format_results",
"get_all_registrars",
"install_everywhere",
"render_server_json",
]

View file

@ -0,0 +1,128 @@
"""Canonical MCP publication metadata for the Headroom server."""
from __future__ import annotations
import json
from dataclasses import dataclass
from pathlib import Path
try:
import tomllib
except ModuleNotFoundError: # pragma: no cover
import tomli as tomllib # type: ignore[no-redef]
from .install import build_headroom_spec
SCHEMA_URL = "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json"
SERVER_NAME = "io.github.headroomlabs-ai/headroom"
SERVER_TITLE = "Headroom"
SERVER_DESCRIPTION = (
"Headroom MCP server for compression, retrieval, and stats in MCP-compatible hosts."
)
PYPI_OWNERSHIP_MARKER = f"<!-- mcp-name: {SERVER_NAME} -->"
WEBSITE_URL = "https://headroomlabs-ai.github.io/headroom/"
REPOSITORY_URL = "https://github.com/headroomlabs-ai/headroom"
REPOSITORY_ID = "1129940957"
PYPI_REGISTRY_URL = "https://pypi.org"
@dataclass(frozen=True)
class ProjectMetadata:
"""Package metadata needed for the published MCP descriptor."""
package_name: str
version: str
def _project_root() -> Path:
return Path(__file__).resolve().parents[2]
def load_project_metadata(pyproject_path: Path | None = None) -> ProjectMetadata:
"""Load the publishable package metadata from ``pyproject.toml``."""
path = pyproject_path or (_project_root() / "pyproject.toml")
data = tomllib.loads(path.read_text(encoding="utf-8"))
project = data["project"]
return ProjectMetadata(
package_name=project["name"],
version=project["version"],
)
def _build_runtime_contract() -> tuple[str, tuple[str, str]]:
"""Return the canonical CLI entrypoint plus the MCP subcommand tail."""
spec = build_headroom_spec()
if spec.name != "headroom":
raise ValueError(f"unexpected MCP server name: {spec.name}")
runtime_tail = spec.args[-2:]
if runtime_tail != ("mcp", "serve"):
raise ValueError(f"unexpected MCP launch tail: {spec.args}")
return spec.name, (runtime_tail[0], runtime_tail[1])
def _build_mcp_package_spec(metadata: ProjectMetadata) -> str:
"""Return the PyPI requirement needed to launch ``headroom mcp serve``."""
return f"{metadata.package_name}[mcp]"
def build_server_json(metadata: ProjectMetadata | None = None) -> dict[str, object]:
"""Build the canonical ``server.json`` payload for Headroom."""
metadata = metadata or load_project_metadata()
command_name, runtime_tail = _build_runtime_contract()
package_spec = _build_mcp_package_spec(metadata)
return {
"$schema": SCHEMA_URL,
"name": SERVER_NAME,
"description": SERVER_DESCRIPTION,
"title": SERVER_TITLE,
"websiteUrl": WEBSITE_URL,
"repository": {
"url": REPOSITORY_URL,
"source": "github",
"id": REPOSITORY_ID,
},
"version": metadata.version,
"packages": [
{
"registryType": "pypi",
"registryBaseUrl": PYPI_REGISTRY_URL,
"identifier": metadata.package_name,
"version": metadata.version,
"runtimeHint": "uvx",
# Current uvx needs --from when the package name and script name differ.
"runtimeArguments": [
{
"type": "named",
"name": "--from",
"value": package_spec,
}
],
"transport": {
"type": "stdio",
},
"packageArguments": [
{
"type": "positional",
"value": command_name,
},
*(
{
"type": "positional",
"value": value,
}
for value in runtime_tail
),
],
}
],
}
def render_server_json(metadata: ProjectMetadata | None = None) -> str:
"""Render the canonical ``server.json`` payload with stable formatting."""
return json.dumps(build_server_json(metadata), indent=2) + "\n"

46
server.json Normal file
View file

@ -0,0 +1,46 @@
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.headroomlabs-ai/headroom",
"description": "Headroom MCP server for compression, retrieval, and stats in MCP-compatible hosts.",
"title": "Headroom",
"websiteUrl": "https://headroomlabs-ai.github.io/headroom/",
"repository": {
"url": "https://github.com/headroomlabs-ai/headroom",
"source": "github",
"id": "1129940957"
},
"version": "0.27.0",
"packages": [
{
"registryType": "pypi",
"registryBaseUrl": "https://pypi.org",
"identifier": "headroom-ai",
"version": "0.27.0",
"runtimeHint": "uvx",
"runtimeArguments": [
{
"type": "named",
"name": "--from",
"value": "headroom-ai[mcp]"
}
],
"transport": {
"type": "stdio"
},
"packageArguments": [
{
"type": "positional",
"value": "headroom"
},
{
"type": "positional",
"value": "mcp"
},
{
"type": "positional",
"value": "serve"
}
]
}
]
}

View file

@ -0,0 +1,86 @@
"""Tests for the canonical MCP server.json descriptor."""
from __future__ import annotations
import json
from pathlib import Path
from headroom.mcp_registry import build_server_json, render_server_json
from headroom.mcp_registry.install import build_headroom_spec
from headroom.mcp_registry.server_json import (
PYPI_OWNERSHIP_MARKER,
REPOSITORY_ID,
REPOSITORY_URL,
SCHEMA_URL,
SERVER_DESCRIPTION,
SERVER_NAME,
WEBSITE_URL,
_build_mcp_package_spec,
load_project_metadata,
)
PROJECT_ROOT = Path(__file__).resolve().parents[2]
def test_build_server_json_uses_project_metadata() -> None:
metadata = load_project_metadata()
descriptor = build_server_json(metadata)
assert descriptor["$schema"] == SCHEMA_URL
assert descriptor["name"] == SERVER_NAME
assert descriptor["description"] == SERVER_DESCRIPTION
assert descriptor["version"] == metadata.version
assert descriptor["websiteUrl"] == WEBSITE_URL
assert descriptor["repository"] == {
"url": REPOSITORY_URL,
"source": "github",
"id": REPOSITORY_ID,
}
package = descriptor["packages"][0]
assert package["registryType"] == "pypi"
assert package["registryBaseUrl"] == "https://pypi.org"
assert package["identifier"] == metadata.package_name
assert package["version"] == metadata.version
assert package["runtimeArguments"] == [
{
"type": "named",
"name": "--from",
"value": _build_mcp_package_spec(metadata),
}
]
def test_build_server_json_matches_runtime_contract() -> None:
descriptor = build_server_json()
runtime = build_headroom_spec()
package = descriptor["packages"][0]
assert package["runtimeHint"] == "uvx"
assert package["runtimeArguments"] == [
{
"type": "named",
"name": "--from",
"value": _build_mcp_package_spec(load_project_metadata()),
}
]
assert [arg["value"] for arg in package["packageArguments"]] == [
runtime.name,
*runtime.args[-2:],
]
assert package["transport"] == {"type": "stdio"}
def test_root_server_json_matches_builder() -> None:
artifact = PROJECT_ROOT / "server.json"
assert artifact.read_text(encoding="utf-8") == render_server_json()
assert json.loads(artifact.read_text(encoding="utf-8")) == build_server_json()
def test_docs_point_to_canonical_server_json() -> None:
readme = (PROJECT_ROOT / "README.md").read_text(encoding="utf-8")
mcp_docs = (PROJECT_ROOT / "docs/content/docs/mcp.mdx").read_text(encoding="utf-8")
assert PYPI_OWNERSHIP_MARKER in readme
assert "`server.json`" in readme
assert "https://github.com/headroomlabs-ai/headroom/blob/main/server.json" in mcp_docs