From 7a373db157f42f98f66c0bbc89dc05b4f0c9bd9b Mon Sep 17 00:00:00 2001 From: Brenno Ferrari Date: Wed, 25 Mar 2026 08:37:56 +0100 Subject: [PATCH] feat: add schema_version to index.json and pack metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add forward-compatible schema versioning to all generated registry files: - index.json now uses envelope format: {"schema_version": 1, "packs": {…}} - Each packs/{name}.json includes a top-level schema_version field - generate.py emits REGISTRY_SCHEMA_VERSION (currently 1) in both formats This enables older weave clients to detect and reject registry formats they cannot safely parse, displaying a clear "please upgrade" message. Companion to PackWeave/weave#237. --- index.json | 297 +++++++++++++++++---------------- packs/brave-search.json | 1 + packs/docs-writer.json | 1 + packs/fetch.json | 1 + packs/filesystem.json | 1 + packs/git-tools.json | 1 + packs/github.json | 1 + packs/memory.json | 1 + packs/postgres.json | 1 + packs/python-dev.json | 1 + packs/rust-dev.json | 1 + packs/sequential-thinking.json | 1 + packs/sqlite.json | 1 + packs/web-dev.json | 1 + scripts/generate.py | 29 +++- 15 files changed, 184 insertions(+), 155 deletions(-) diff --git a/index.json b/index.json index 01f988c..7ba2ead 100644 --- a/index.json +++ b/index.json @@ -1,150 +1,153 @@ { - "brave-search": { - "name": "brave-search", - "description": "Web and local search via the Brave Search API through MCP", - "keywords": [ - "search", - "brave", - "web", - "mcp" - ], - "latest_version": "0.1.0" - }, - "docs-writer": { - "name": "docs-writer", - "description": "System prompt and slash commands for technical writing: READMEs, API docs, and changelogs", - "keywords": [ - "documentation", - "writing", - "readme", - "technical-writing" - ], - "latest_version": "0.1.0" - }, - "fetch": { - "name": "fetch", - "description": "HTTP fetch tool via MCP — retrieve web pages and API responses as markdown or raw text", - "keywords": [ - "fetch", - "http", - "web", - "api", - "mcp" - ], - "latest_version": "0.1.0" - }, - "filesystem": { - "name": "filesystem", - "description": "Read and write local files via the MCP filesystem server", - "keywords": [ - "filesystem", - "files", - "mcp", - "read", - "write" - ], - "latest_version": "0.1.0" - }, - "git-tools": { - "name": "git-tools", - "description": "Git operations via MCP plus a structured code review slash command", - "keywords": [ - "git", - "version-control", - "code-review", - "mcp" - ], - "latest_version": "0.1.0" - }, - "github": { - "name": "github", - "description": "GitHub repos, issues, pull requests, and code search via MCP", - "keywords": [ - "github", - "git", - "issues", - "pull-requests", - "mcp" - ], - "latest_version": "0.1.0" - }, - "memory": { - "name": "memory", - "description": "Persistent key-value memory store across sessions via MCP knowledge graph", - "keywords": [ - "memory", - "knowledge-graph", - "persistence", - "mcp" - ], - "latest_version": "0.1.0" - }, - "postgres": { - "name": "postgres", - "description": "PostgreSQL database query access via MCP — read-only schema inspection and SQL execution", - "keywords": [ - "postgres", - "postgresql", - "database", - "sql", - "mcp" - ], - "latest_version": "0.1.0" - }, - "python-dev": { - "name": "python-dev", - "description": "System prompt and slash commands for Python development: best practices, typing, and tooling", - "keywords": [ - "python", - "development", - "typing", - "best-practices" - ], - "latest_version": "0.1.0" - }, - "rust-dev": { - "name": "rust-dev", - "description": "System prompt and slash commands for Rust development: clippy, error handling, and idiomatic patterns", - "keywords": [ - "rust", - "cargo", - "clippy", - "development" - ], - "latest_version": "0.1.0" - }, - "sequential-thinking": { - "name": "sequential-thinking", - "description": "Structured chain-of-thought reasoning via MCP — break down complex problems step by step", - "keywords": [ - "reasoning", - "chain-of-thought", - "thinking", - "mcp" - ], - "latest_version": "0.1.0" - }, - "sqlite": { - "name": "sqlite", - "description": "SQLite database access via MCP — read, write, and analyze local SQLite databases", - "keywords": [ - "sqlite", - "database", - "sql", - "mcp" - ], - "latest_version": "0.1.0" - }, - "web-dev": { - "name": "web-dev", - "description": "Browser automation via Puppeteer MCP plus a web development system prompt", - "keywords": [ - "web", - "browser", - "puppeteer", - "automation", - "mcp" - ], - "latest_version": "0.1.0" + "schema_version": 1, + "packs": { + "brave-search": { + "name": "brave-search", + "description": "Web and local search via the Brave Search API through MCP", + "keywords": [ + "search", + "brave", + "web", + "mcp" + ], + "latest_version": "0.1.0" + }, + "docs-writer": { + "name": "docs-writer", + "description": "System prompt and slash commands for technical writing: READMEs, API docs, and changelogs", + "keywords": [ + "documentation", + "writing", + "readme", + "technical-writing" + ], + "latest_version": "0.1.0" + }, + "fetch": { + "name": "fetch", + "description": "HTTP fetch tool via MCP — retrieve web pages and API responses as markdown or raw text", + "keywords": [ + "fetch", + "http", + "web", + "api", + "mcp" + ], + "latest_version": "0.1.0" + }, + "filesystem": { + "name": "filesystem", + "description": "Read and write local files via the MCP filesystem server", + "keywords": [ + "filesystem", + "files", + "mcp", + "read", + "write" + ], + "latest_version": "0.1.0" + }, + "git-tools": { + "name": "git-tools", + "description": "Git operations via MCP plus a structured code review slash command", + "keywords": [ + "git", + "version-control", + "code-review", + "mcp" + ], + "latest_version": "0.1.0" + }, + "github": { + "name": "github", + "description": "GitHub repos, issues, pull requests, and code search via MCP", + "keywords": [ + "github", + "git", + "issues", + "pull-requests", + "mcp" + ], + "latest_version": "0.1.0" + }, + "memory": { + "name": "memory", + "description": "Persistent key-value memory store across sessions via MCP knowledge graph", + "keywords": [ + "memory", + "knowledge-graph", + "persistence", + "mcp" + ], + "latest_version": "0.1.0" + }, + "postgres": { + "name": "postgres", + "description": "PostgreSQL database query access via MCP — read-only schema inspection and SQL execution", + "keywords": [ + "postgres", + "postgresql", + "database", + "sql", + "mcp" + ], + "latest_version": "0.1.0" + }, + "python-dev": { + "name": "python-dev", + "description": "System prompt and slash commands for Python development: best practices, typing, and tooling", + "keywords": [ + "python", + "development", + "typing", + "best-practices" + ], + "latest_version": "0.1.0" + }, + "rust-dev": { + "name": "rust-dev", + "description": "System prompt and slash commands for Rust development: clippy, error handling, and idiomatic patterns", + "keywords": [ + "rust", + "cargo", + "clippy", + "development" + ], + "latest_version": "0.1.0" + }, + "sequential-thinking": { + "name": "sequential-thinking", + "description": "Structured chain-of-thought reasoning via MCP — break down complex problems step by step", + "keywords": [ + "reasoning", + "chain-of-thought", + "thinking", + "mcp" + ], + "latest_version": "0.1.0" + }, + "sqlite": { + "name": "sqlite", + "description": "SQLite database access via MCP — read, write, and analyze local SQLite databases", + "keywords": [ + "sqlite", + "database", + "sql", + "mcp" + ], + "latest_version": "0.1.0" + }, + "web-dev": { + "name": "web-dev", + "description": "Browser automation via Puppeteer MCP plus a web development system prompt", + "keywords": [ + "web", + "browser", + "puppeteer", + "automation", + "mcp" + ], + "latest_version": "0.1.0" + } } } diff --git a/packs/brave-search.json b/packs/brave-search.json index 7409db4..946ab58 100644 --- a/packs/brave-search.json +++ b/packs/brave-search.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "brave-search", "description": "Web and local search via the Brave Search API through MCP", "authors": [ diff --git a/packs/docs-writer.json b/packs/docs-writer.json index c9f4748..89cacdb 100644 --- a/packs/docs-writer.json +++ b/packs/docs-writer.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "docs-writer", "description": "System prompt and slash commands for technical writing: READMEs, API docs, and changelogs", "authors": [ diff --git a/packs/fetch.json b/packs/fetch.json index a128d25..db3a6bf 100644 --- a/packs/fetch.json +++ b/packs/fetch.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "fetch", "description": "HTTP fetch tool via MCP — retrieve web pages and API responses as markdown or raw text", "authors": [ diff --git a/packs/filesystem.json b/packs/filesystem.json index 598e7bb..6cf0fd6 100644 --- a/packs/filesystem.json +++ b/packs/filesystem.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "filesystem", "description": "Read and write local files via the MCP filesystem server", "authors": [ diff --git a/packs/git-tools.json b/packs/git-tools.json index 6f20edb..34fae4a 100644 --- a/packs/git-tools.json +++ b/packs/git-tools.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "git-tools", "description": "Git operations via MCP plus a structured code review slash command", "authors": [ diff --git a/packs/github.json b/packs/github.json index 5b8ef60..2ba714f 100644 --- a/packs/github.json +++ b/packs/github.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "github", "description": "GitHub repos, issues, pull requests, and code search via MCP", "authors": [ diff --git a/packs/memory.json b/packs/memory.json index f98d670..3434ae4 100644 --- a/packs/memory.json +++ b/packs/memory.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "memory", "description": "Persistent key-value memory store across sessions via MCP knowledge graph", "authors": [ diff --git a/packs/postgres.json b/packs/postgres.json index a17daef..167c377 100644 --- a/packs/postgres.json +++ b/packs/postgres.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "postgres", "description": "PostgreSQL database query access via MCP — read-only schema inspection and SQL execution", "authors": [ diff --git a/packs/python-dev.json b/packs/python-dev.json index 5305ef9..6e5d943 100644 --- a/packs/python-dev.json +++ b/packs/python-dev.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "python-dev", "description": "System prompt and slash commands for Python development: best practices, typing, and tooling", "authors": [ diff --git a/packs/rust-dev.json b/packs/rust-dev.json index 5463504..2e5a5d5 100644 --- a/packs/rust-dev.json +++ b/packs/rust-dev.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "rust-dev", "description": "System prompt and slash commands for Rust development: clippy, error handling, and idiomatic patterns", "authors": [ diff --git a/packs/sequential-thinking.json b/packs/sequential-thinking.json index d550768..4106e06 100644 --- a/packs/sequential-thinking.json +++ b/packs/sequential-thinking.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "sequential-thinking", "description": "Structured chain-of-thought reasoning via MCP — break down complex problems step by step", "authors": [ diff --git a/packs/sqlite.json b/packs/sqlite.json index 268f2f3..24050ca 100644 --- a/packs/sqlite.json +++ b/packs/sqlite.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "sqlite", "description": "SQLite database access via MCP — read, write, and analyze local SQLite databases", "authors": [ diff --git a/packs/web-dev.json b/packs/web-dev.json index bac46e7..11ca3d4 100644 --- a/packs/web-dev.json +++ b/packs/web-dev.json @@ -1,4 +1,5 @@ { + "schema_version": 1, "name": "web-dev", "description": "Browser automation via Puppeteer MCP plus a web development system prompt", "authors": [ diff --git a/scripts/generate.py b/scripts/generate.py index fd5bed7..ab2856a 100644 --- a/scripts/generate.py +++ b/scripts/generate.py @@ -6,7 +6,7 @@ python3 scripts/generate.py For each pack under src/{name}/, this script: - 1. Reads all files and builds an inline `files` map (relative path → content). + 1. Reads all files and builds an inline `files` map (relative path -> content). 2. Parses pack.toml to extract metadata (version, description, authors, etc.). 3. Writes packs/{name}.json — creating it or updating the version entry in place. @@ -19,6 +19,12 @@ import sys from pathlib import Path +# Schema version for all generated registry files (index.json, packs/*.json). +# Bump this when the registry format changes in a way that older clients cannot +# safely ignore. Clients compare this against their own supported version and +# surface a clear "please upgrade weave" error when the registry is newer. +REGISTRY_SCHEMA_VERSION = 1 + def parse_pack_toml(text: str) -> dict: """Extract scalar fields from the [pack] section of a pack.toml. @@ -64,7 +70,7 @@ def semver_key(version_str: str) -> tuple[int, ...]: def build_files_map(pack_src_dir: Path) -> dict[str, str]: - """Walk src/{name}/ and return a flat map of relative-path → file content.""" + """Walk src/{name}/ and return a flat map of relative-path -> file content.""" files: dict[str, str] = {} for file_path in sorted(pack_src_dir.rglob("*")): if not file_path.is_file(): @@ -136,6 +142,7 @@ def process_pack(src_dir: Path, packs_dir: Path, pack_name: str) -> dict: versions.sort(key=lambda v: semver_key(v["version"]), reverse=True) pack_metadata = { + "schema_version": REGISTRY_SCHEMA_VERSION, "name": meta["name"] or pack_name, "description": meta["description"] or "", "authors": meta["authors"], @@ -150,7 +157,7 @@ def process_pack(src_dir: Path, packs_dir: Path, pack_name: str) -> dict: f.write("\n") latest = max(versions, key=lambda v: semver_key(v["version"]))["version"] - print(f" {pack_name} → {version} (latest: {latest})") + print(f" {pack_name} -> {version} (latest: {latest})") return { "name": pack_metadata["name"], @@ -161,8 +168,11 @@ def process_pack(src_dir: Path, packs_dir: Path, pack_name: str) -> dict: def regenerate_index(packs_dir: Path) -> dict: - """Build index.json from all packs/*.json files.""" - index: dict[str, dict] = {} + """Build index.json from all packs/*.json files. + + Returns the full envelope: ``{"schema_version": N, "packs": {…}}``. + """ + packs: dict[str, dict] = {} for pack_json in sorted(packs_dir.glob("*.json")): with open(pack_json, encoding="utf-8") as f: meta = json.load(f) @@ -171,13 +181,16 @@ def regenerate_index(packs_dir: Path) -> dict: if not versions: continue latest = max(versions, key=lambda v: semver_key(v["version"]))["version"] - index[name] = { + packs[name] = { "name": meta.get("name", name), "description": meta.get("description", ""), "keywords": meta.get("keywords", []), "latest_version": latest, } - return index + return { + "schema_version": REGISTRY_SCHEMA_VERSION, + "packs": packs, + } def main() -> None: @@ -201,7 +214,7 @@ def main() -> None: with open(index_path, "w", encoding="utf-8") as f: json.dump(index, f, indent=2, ensure_ascii=False) f.write("\n") - print(f" index.json — {len(index)} pack(s)") + print(f" index.json — {len(index['packs'])} pack(s), schema_version={index['schema_version']}") print("\nDone.")