fix: move mcp_servers to metadata.json to prevent silent plugin.json rejection

Claude Code's strict schema validation silently rejects plugins with unknown
root-level fields in plugin.json (anthropics/claude-code#20409).
Moved mcp_servers to separate metadata.json files.
Updated install/uninstall/list scripts to read from new location.
Added cache clearing to switch-profile.sh.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 13:46:43 -05:00
parent 5a8c3b041f
commit de4126bf68
12 changed files with 26 additions and 14 deletions

View File

@@ -0,0 +1 @@
{"mcp_servers": ["netbox"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["contract-validator"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["data-platform"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["gitea"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["gitea"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["gitea"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["gitea"]}

View File

@@ -0,0 +1 @@
{"mcp_servers": ["viz-platform"]}

View File

@@ -116,19 +116,19 @@ validate_target() {
} }
# --- Get MCP Servers for Plugin --- # --- Get MCP Servers for Plugin ---
# Reads the mcp_servers array from plugin.json # Reads the mcp_servers array from metadata.json (separate from plugin.json to avoid schema validation issues)
# Returns newline-separated list of MCP server names, or empty if none # Returns newline-separated list of MCP server names, or empty if none
get_mcp_servers() { get_mcp_servers() {
local plugin_name="$1" local plugin_name="$1"
local plugin_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/plugin.json" local metadata_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/metadata.json"
if [[ ! -f "$plugin_json" ]]; then if [[ ! -f "$metadata_json" ]]; then
return return
fi fi
# Read mcp_servers array from plugin.json # Read mcp_servers array from metadata.json
# Returns empty if field doesn't exist or is empty # Returns empty if field doesn't exist or is empty
jq -r '.mcp_servers // [] | .[]' "$plugin_json" 2>/dev/null || true jq -r '.mcp_servers // [] | .[]' "$metadata_json" 2>/dev/null || true
} }
# --- Check if plugin has any MCP servers --- # --- Check if plugin has any MCP servers ---

View File

@@ -68,16 +68,16 @@ get_available_plugins() {
} }
# --- Get MCP Servers for Plugin --- # --- Get MCP Servers for Plugin ---
# Reads the mcp_servers array from plugin.json # Reads the mcp_servers array from metadata.json (separate from plugin.json to avoid schema validation issues)
get_mcp_servers() { get_mcp_servers() {
local plugin_name="$1" local plugin_name="$1"
local plugin_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/plugin.json" local metadata_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/metadata.json"
if [[ ! -f "$plugin_json" ]]; then if [[ ! -f "$metadata_json" ]]; then
return return
fi fi
jq -r '.mcp_servers // [] | .[]' "$plugin_json" 2>/dev/null || true jq -r '.mcp_servers // [] | .[]' "$metadata_json" 2>/dev/null || true
} }
# --- Check if plugin has any MCP servers defined --- # --- Check if plugin has any MCP servers defined ---

View File

@@ -12,7 +12,9 @@ case "${1:-lean}" in
lean) lean)
cp "$MARKETPLACE_DIR/marketplace-lean.json" "$MARKETPLACE_DIR/marketplace.json" cp "$MARKETPLACE_DIR/marketplace-lean.json" "$MARKETPLACE_DIR/marketplace.json"
cp "$ROOT_DIR/.mcp-lean.json" "$ROOT_DIR/.mcp.json" cp "$ROOT_DIR/.mcp-lean.json" "$ROOT_DIR/.mcp.json"
rm -rf ~/.claude/plugins/cache/
echo "Switched to LEAN profile (6 plugins, 1 MCP server)" echo "Switched to LEAN profile (6 plugins, 1 MCP server)"
echo "Plugin cache cleared."
echo "" echo ""
echo "Plugins: projman, git-flow, pr-review, clarity-assist, code-sentinel, doc-guardian" echo "Plugins: projman, git-flow, pr-review, clarity-assist, code-sentinel, doc-guardian"
echo "MCP: gitea only" echo "MCP: gitea only"
@@ -22,7 +24,9 @@ case "${1:-lean}" in
full) full)
cp "$MARKETPLACE_DIR/marketplace-full.json" "$MARKETPLACE_DIR/marketplace.json" cp "$MARKETPLACE_DIR/marketplace-full.json" "$MARKETPLACE_DIR/marketplace.json"
cp "$ROOT_DIR/.mcp-full.json" "$ROOT_DIR/.mcp.json" cp "$ROOT_DIR/.mcp-full.json" "$ROOT_DIR/.mcp.json"
rm -rf ~/.claude/plugins/cache/
echo "Switched to FULL profile (12 plugins, 5 MCP servers)" echo "Switched to FULL profile (12 plugins, 5 MCP servers)"
echo "Plugin cache cleared."
echo "" echo ""
echo "Plugins: all" echo "Plugins: all"
echo "MCP: gitea, netbox, data-platform, viz-platform, contract-validator" echo "MCP: gitea, netbox, data-platform, viz-platform, contract-validator"

View File

@@ -77,19 +77,19 @@ validate_target() {
} }
# --- Get MCP Servers for Plugin --- # --- Get MCP Servers for Plugin ---
# Reads the mcp_servers array from plugin.json # Reads the mcp_servers array from metadata.json (separate from plugin.json to avoid schema validation issues)
# Returns newline-separated list of MCP server names, or empty if none # Returns newline-separated list of MCP server names, or empty if none
get_mcp_servers() { get_mcp_servers() {
local plugin_name="$1" local plugin_name="$1"
local plugin_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/plugin.json" local metadata_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/metadata.json"
if [[ ! -f "$plugin_json" ]]; then if [[ ! -f "$metadata_json" ]]; then
return return
fi fi
# Read mcp_servers array from plugin.json # Read mcp_servers array from metadata.json
# Returns empty if field doesn't exist or is empty # Returns empty if field doesn't exist or is empty
jq -r '.mcp_servers // [] | .[]' "$plugin_json" 2>/dev/null || true jq -r '.mcp_servers // [] | .[]' "$metadata_json" 2>/dev/null || true
} }
# --- Remove from .mcp.json --- # --- Remove from .mcp.json ---