Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .github/workflows/build_mcp_server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ on:
paths:
- ".github/image/Dockerfile"
- ".github/workflows/build_mcp_server.yml"
- "catalog/**"
- "mcp-server/**"
- "skills/**"

jobs:
build:
Expand Down Expand Up @@ -42,6 +44,9 @@ jobs:
with:
toolchain: stable

- name: Generate skill catalog
run: node catalog/scripts/generate-skill-catalog.mjs
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Run cargo build
run: cargo build --target ${{ matrix.target }} ${{ matrix.args }}
working-directory: mcp-server
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/check_catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ on:
- main
paths:
- ".github/workflows/check_catalog.yml"
- "catalog/**"
- "frontends/catalog/**"
- "skills/**"
pull_request:
branches:
- main
paths:
- ".github/workflows/check_catalog.yml"
- "catalog/**"
- "frontends/catalog/**"
- "skills/**"

jobs:
check:
Expand All @@ -39,6 +43,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Verify generated skill catalog
working-directory: .
run: node catalog/scripts/generate-skill-catalog.mjs

- name: Run typecheck
run: pnpm run typecheck

Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/check_mcp_server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ on:
- main
paths:
- ".github/workflows/check_mcp_server.yml"
- "catalog/**"
- "mcp-server/**"
- "skills/**"
pull_request:
branches:
- main
paths:
- ".github/workflows/check_mcp_server.yml"
- "catalog/**"
- "mcp-server/**"
- "skills/**"

jobs:
lint:
Expand All @@ -24,5 +28,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

- name: Generate skill catalog
working-directory: .
run: node catalog/scripts/generate-skill-catalog.mjs

- name: Clippy check lints
run: cargo clippy -- -D warnings
8 changes: 8 additions & 0 deletions .github/workflows/test_mcp_server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ on:
- main
paths:
- ".github/workflows/test_mcp_server.yml"
- "catalog/**"
- "mcp-server/**"
- "skills/**"
pull_request:
branches:
- main
paths:
- ".github/workflows/test_mcp_server.yml"
- "catalog/**"
- "mcp-server/**"
- "skills/**"

jobs:
lint:
Expand All @@ -24,5 +28,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

- name: Generate skill catalog
working-directory: .
run: node catalog/scripts/generate-skill-catalog.mjs

- name: Test
run: cargo test
84 changes: 71 additions & 13 deletions catalog/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# Extension Catalog
# MCP Catalogs

This directory contains the Supernode MCP extension catalog.
This directory contains the catalog documents consumed by the Supernode MCP server.

`extension-catalog.json` is the catalog document consumed by the MCP server. It is also the payload that should be published as the official OCI catalog artifact.
- `extension-catalog.json`: installable extension contracts and chart references.
- `skill-catalog.manifest.json`: editable metadata for operational skill guides.
- `skill-catalog.json`: generated, gitignored skill catalog payload with embedded markdown content.

## Contract
Run this after editing `skill-catalog.manifest.json` or `../skills/*.md`:

The catalog document uses this top-level shape:
```sh
node catalog/scripts/generate-skill-catalog.mjs
```

`skill-catalog.json` is intentionally not committed. Generate it locally before building MCP from source or publishing the skill catalog artifact.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
## Extension Catalog Contract

`extension-catalog.json` uses this top-level shape:

```json
{
Expand All @@ -28,52 +38,94 @@ Each extension entry describes the MCP-facing extension contract:
- `outputs`: user-facing endpoints provided by workloads of this extension.
- `chart`: OCI Helm chart reference used by MCP install and upgrade operations.

## Skill Catalog Contract

`skill-catalog.json` uses this top-level shape:

```json
{
"schemaVersion": "supernode.skillCatalog/v1",
"skills": []
}
```

Each skill entry describes one operational guide:

- `id`: canonical URI-safe skill ID used by `supernode://skills/{skillId}`.
- `title` and `description`: human-readable metadata for agents and users.
- `tags`: discovery labels.
- `extensions`: related extension IDs, when applicable.
- `tools`: MCP tools used by the guide.
- `content`: markdown guide content embedded from `../skills/*.md`.

Edit `skill-catalog.manifest.json` rather than `skill-catalog.json` directly. The manifest stores the same metadata plus `contentPath`; the generator embeds the referenced markdown into the publishable JSON document. Because `skill-catalog.json` is generated and gitignored, changes to skill metadata or markdown are published only after regenerating it.

## Trusted Sources

By default, MCP only trusts official Supernode OCI references:

- Catalog artifacts must be fetched from `oci.supernode.store`.
- Extension charts must be under `oci://oci.supernode.store/extensions/{extensionId}`.

This is intentional. The catalog influences what agents recommend and what MCP can install or execute for metrics collection. Treat it as a supply-chain input, not a cosmetic document.
This is intentional. Extension catalogs influence what MCP can install and execute for metrics collection. Skill catalogs are prompt-bearing operational guidance and can influence agent behavior. Treat both as supply-chain inputs, not cosmetic documents.

For local development only, MCP can be started with:

```text
MCP_EXTENSION_CATALOG_ALLOW_UNTRUSTED=true
MCP_SKILL_CATALOG_ALLOW_UNTRUSTED=true
```

Do not enable this in production. An untrusted catalog can point MCP at untrusted charts, mislead agents through descriptions and schemas, or define unsafe metrics collection metadata.
Do not enable these in production.

## Publishing

The official catalog should be published as a standalone OCI artifact containing `extension-catalog.json` as a JSON layer.
The official catalogs should be published as standalone OCI artifacts containing a single JSON layer. Use the publish script rather than invoking `oras` directly; it regenerates `skill-catalog.json` immediately before pushing so the OCI artifact matches the current manifest and markdown.

Recommended layer media type:
Recommended extension catalog layer media type:

```text
application/vnd.supernode.extension-catalog.v1+json
```

Example using `oras`:
Recommended skill catalog layer media type:

```text
application/vnd.supernode.skill-catalog.v1+json
```

Publish both catalogs with:

```sh
CATALOG_TAG=0.1.0 catalog/scripts/publish-catalogs.sh
```

The script runs the equivalent of:

```sh
node catalog/scripts/generate-skill-catalog.mjs

oras push \
oci.supernode.store/extension-catalog:0.1.0 \
extension-catalog.json:application/vnd.supernode.extension-catalog.v1+json
oci.supernode.store/extension-catalog:${CATALOG_TAG} \
catalog/extension-catalog.json:application/vnd.supernode.extension-catalog.v1+json

oras push \
oci.supernode.store/skill-catalog:${CATALOG_TAG} \
catalog/skill-catalog.json:application/vnd.supernode.skill-catalog.v1+json
```

Production deployments should prefer digest-pinned catalog references when practical:

```text
oci://oci.supernode.store/extension-catalog@sha256:<digest>
oci://oci.supernode.store/skill-catalog@sha256:<digest>
```

Tag references are supported and convenient for development or release channels, but digest references are safer because they are immutable.

## Validation

MCP validates the catalog at load time:
MCP validates extension catalogs at load time:

- `schemaVersion` must be `supernode.extensionCatalog/v1`.
- extension IDs must be unique and non-empty.
Expand All @@ -83,4 +135,10 @@ MCP validates the catalog at load time:
- chart references must be OCI references.
- by default, chart references must point to `oci://oci.supernode.store/extensions/{extensionId}`.

MCP validates skill catalogs at load time:

- `schemaVersion` must be `supernode.skillCatalog/v1`.
- skill IDs must be unique, non-empty, and URI-safe.
- `title`, `description`, and `content` must be non-empty.

Longer term, official catalog artifacts should also be signed and verified before MCP accepts them.
40 changes: 40 additions & 0 deletions catalog/scripts/generate-skill-catalog.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { readFileSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const scriptDir = dirname(fileURLToPath(import.meta.url));
const catalogDir = resolve(scriptDir, "..");
const manifestPath = resolve(catalogDir, "skill-catalog.manifest.json");
const outputPath = resolve(catalogDir, "skill-catalog.json");

const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));

if (manifest.schemaVersion !== "supernode.skillCatalogManifest/v1") {
throw new Error(
`unsupported skill catalog manifest schema: ${manifest.schemaVersion}`,
);
}

const seen = new Set();
const skills = manifest.skills.map(({ contentPath, ...skill }) => {
if (!skill.id || seen.has(skill.id)) {
throw new Error(`invalid or duplicate skill id: ${skill.id}`);
}
seen.add(skill.id);

if (!contentPath) {
throw new Error(`missing contentPath for skill: ${skill.id}`);
}

return {
...skill,
content: readFileSync(resolve(catalogDir, contentPath), "utf8"),
};
});

const catalog = {
schemaVersion: "supernode.skillCatalog/v1",
skills,
};

writeFileSync(outputPath, `${JSON.stringify(catalog, null, 2)}\n`);
16 changes: 16 additions & 0 deletions catalog/scripts/publish-catalogs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
REGISTRY="${REGISTRY:-oci.supernode.store}"
CATALOG_TAG="${CATALOG_TAG:-0.1.0}"

node "${ROOT_DIR}/catalog/scripts/generate-skill-catalog.mjs"

oras push \
"${REGISTRY}/extension-catalog:${CATALOG_TAG}" \
"${ROOT_DIR}/catalog/extension-catalog.json:application/vnd.supernode.extension-catalog.v1+json"

oras push \
"${REGISTRY}/skill-catalog:${CATALOG_TAG}" \
"${ROOT_DIR}/catalog/skill-catalog.json:application/vnd.supernode.skill-catalog.v1+json"
Loading
Loading