refactor: bundle MCP servers inside plugins for cache compatibility

Claude Code only caches the plugin directory when installed from a
marketplace, not parent directories. This broke the shared mcp-servers/
architecture because relative paths like ../../mcp-servers/ resolved
to non-existent locations in the cache.

Changes:
- Move gitea and wikijs MCP servers into plugins/projman/mcp-servers/
- Move netbox MCP server into plugins/cmdb-assistant/mcp-servers/
- Update .mcp.json files to use ${CLAUDE_PLUGIN_ROOT}/mcp-servers/
- Update setup.sh to handle new bundled structure
- Add netbox.env config template to setup.sh
- Update CLAUDE.md and CANONICAL-PATHS.md documentation

This ensures plugins work correctly when installed and cached.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-15 17:23:02 -05:00
parent c73e4c4794
commit d84425cbb0
50 changed files with 158 additions and 112 deletions

132
CLAUDE.md
View File

@@ -161,10 +161,11 @@ Both plugins use **two shared MCP servers** at repository root level (`mcp-serve
- `tag_lesson` - Add tags to lessons learned - `tag_lesson` - Add tags to lessons learned
**Key Architecture Points:** **Key Architecture Points:**
- MCP servers are **shared** by both plugins at `mcp-servers/gitea` and `mcp-servers/wikijs` - MCP servers are **bundled inside each plugin** at `plugins/{plugin}/mcp-servers/`
- This ensures plugins work when cached by Claude Code (only plugin directory is cached)
- Each MCP server detects its mode (project-scoped vs company-wide) based on environment variables - Each MCP server detects its mode (project-scoped vs company-wide) based on environment variables
- Configuration uses hybrid approach (system-level + project-level) - Configuration uses hybrid approach (system-level + project-level)
- All plugins reference `../../mcp-servers/` in their `.mcp.json` files (from `plugins/*/`) - All plugins reference `${CLAUDE_PLUGIN_ROOT}/mcp-servers/` in their `.mcp.json` files
## Branch-Aware Security Model ## Branch-Aware Security Model
@@ -261,76 +262,73 @@ See [docs/reference-material/projman-implementation-plan.md](docs/reference-mate
bandit/support-claude-mktplace/ bandit/support-claude-mktplace/
├── .claude-plugin/ ├── .claude-plugin/
│ └── marketplace.json │ └── marketplace.json
├── mcp-servers/ # ← SHARED BY ALL PLUGINS ├── plugins/ # ← ALL PLUGINS (with bundled MCP servers)
│ ├── gitea/ │ ├── projman/ # ← PROJECT PLUGIN
│ │ ├── .venv/ │ │ ├── .claude-plugin/
│ │ ├── requirements.txt # Python dependencies │ │ │ └── plugin.json
│ │ ├── mcp_server/ │ │ ├── .mcp.json # Points to ${CLAUDE_PLUGIN_ROOT}/mcp-servers/
│ │ │ ├── __init__.py │ │ ├── mcp-servers/ # ← MCP servers BUNDLED IN plugin
│ │ │ ├── server.py │ │ │ ├── gitea/
│ │ │ ├── config.py # Mode detection (project/company) │ │ │ │ ├── .venv/
│ │ │ ├── gitea_client.py │ │ │ │ ├── requirements.txt
│ │ │ └── tools/ │ │ │ │ ├── mcp_server/
│ │ │ ── issues.py │ │ │ ── tests/
│ │ │ └── labels.py │ │ │ └── wikijs/
│ │ └── tests/ │ │ │ ├── .venv/
└── wikijs/ │ │ ├── requirements.txt
├── .venv/ │ │ ├── mcp_server/
├── requirements.txt # Python + GraphQL dependencies │ └── tests/
├── mcp_server/ ├── commands/
│ ├── __init__.py │ ├── sprint-plan.md
│ ├── server.py │ ├── sprint-start.md
│ ├── config.py # Mode detection (project/company) │ ├── sprint-status.md
│ ├── wikijs_client.py # GraphQL client │ ├── sprint-close.md
│ └── tools/ │ └── labels-sync.md
├── pages.py ├── agents/
├── lessons_learned.py │ │ ├── planner.md
── documentation.py │ │ ── orchestrator.md
└── tests/ │ │ └── executor.md
└── plugins/ # ← ALL PLUGINS ├── skills/
├── projman/ # ← PROJECT PLUGIN │ │ └── label-taxonomy/
│ ├── .claude-plugin/ │ │ └── labels-reference.md
│ │ ── plugin.json │ │ ── README.md
── .mcp.json # Points to ../../mcp-servers/ ── CONFIGURATION.md
│ ├── commands/ │ ├── projman-pmo/ # ← PMO PLUGIN
│ │ ├── sprint-plan.md │ │ ├── .claude-plugin/
│ │ ── sprint-start.md │ │ ── plugin.json
│ │ ├── sprint-status.md │ │ ├── .mcp.json
│ │ ├── sprint-close.md │ │ ├── commands/
│ │ ── labels-sync.md │ │ ── agents/
── agents/ │ │── pmo-coordinator.md
│ │ ── planner.md │ │ ── README.md
│ │ ├── orchestrator.md │ ├── cmdb-assistant/ # ← CMDB PLUGIN
│ │ ── executor.md │ │ ── .claude-plugin/
── skills/ │ │── plugin.json
└── label-taxonomy/ │ │ ├── .mcp.json # Points to ${CLAUDE_PLUGIN_ROOT}/mcp-servers/
│ └── labels-reference.md ├── mcp-servers/ # ← MCP servers BUNDLED IN plugin
── README.md │ │── netbox/
└── CONFIGURATION.md │ │ ├── .venv/
├── projman-pmo/ # ← PMO PLUGIN │ │ ├── requirements.txt
├── .claude-plugin/ │ │ └── mcp_server/
│ │ ── plugin.json │ │ ── commands/
── .mcp.json # Points to ../../mcp-servers/ ── agents/
── commands/ ── project-hygiene/ # ← CLEANUP PLUGIN
│ ├── pmo-status.md └── ...
├── pmo-priorities.md ├── scripts/ # Setup and maintenance scripts
│ │ ├── pmo-dependencies.md │ ├── setup.sh
│ └── pmo-schedule.md │ └── post-update.sh
│ ├── agents/ └── docs/
│ │ └── pmo-coordinator.md
│ └── README.md
└── project-hygiene/ # ← CLEANUP PLUGIN
└── ...
``` ```
### Key Design Decisions ### Key Design Decisions
**Two MCP Servers (Shared Architecture):** **MCP Servers (Bundled in Plugins):**
- **Gitea MCP**: Issues, labels, repository management - **Gitea MCP**: Issues, labels, repository management (bundled in projman)
- **Wiki.js MCP**: Documentation, lessons learned, knowledge base - **Wiki.js MCP**: Documentation, lessons learned, knowledge base (bundled in projman)
- Servers are **shared** between both plugins at repository root - **NetBox MCP**: Infrastructure management (bundled in cmdb-assistant)
- Servers are **bundled inside each plugin** that needs them
- This ensures plugins work when cached by Claude Code
- Mode detection based on environment variables (project vs company-wide) - Mode detection based on environment variables (project vs company-wide)
- Benefits: Single source of truth, fix bugs once, professional architecture
**Python Implementation:** **Python Implementation:**
- Python chosen over Node.js for MCP servers - Python chosen over Node.js for MCP servers

View File

@@ -2,7 +2,7 @@
**This file defines ALL valid paths in this repository. No exceptions. No inference. No assumptions.** **This file defines ALL valid paths in this repository. No exceptions. No inference. No assumptions.**
Last Updated: 2025-12-12 Last Updated: 2025-12-15
--- ---
@@ -19,15 +19,23 @@ support-claude-mktplace/
│ ├── references/ # Reference specifications │ ├── references/ # Reference specifications
│ └── workflows/ # Workflow documentation │ └── workflows/ # Workflow documentation
├── hooks/ # Shared hooks (if any) ├── hooks/ # Shared hooks (if any)
├── mcp-servers/ # Shared MCP servers (AT ROOT) ├── plugins/ # ALL plugins with bundled MCP servers
│ ├── gitea/
│ ├── wikijs/
│ └── netbox/
├── plugins/ # ALL plugins (INSIDE plugins/)
│ ├── projman/ │ ├── projman/
│ │ ├── .claude-plugin/
│ │ ├── mcp-servers/ # MCP servers bundled IN plugin
│ │ │ ├── gitea/
│ │ │ └── wikijs/
│ │ ├── commands/
│ │ ├── agents/
│ │ └── skills/
│ ├── projman-pmo/ │ ├── projman-pmo/
│ ├── project-hygiene/ │ ├── project-hygiene/
│ └── cmdb-assistant/ │ └── cmdb-assistant/
│ ├── .claude-plugin/
│ ├── mcp-servers/ # MCP servers bundled IN plugin
│ │ └── netbox/
│ ├── commands/
│ └── agents/
├── scripts/ # Setup and maintenance scripts ├── scripts/ # Setup and maintenance scripts
├── CLAUDE.md ├── CLAUDE.md
├── README.md ├── README.md
@@ -50,19 +58,21 @@ support-claude-mktplace/
| Plugin agents | `plugins/{plugin-name}/agents/` | `plugins/projman/agents/` | | Plugin agents | `plugins/{plugin-name}/agents/` | `plugins/projman/agents/` |
| Plugin .mcp.json | `plugins/{plugin-name}/.mcp.json` | `plugins/projman/.mcp.json` | | Plugin .mcp.json | `plugins/{plugin-name}/.mcp.json` | `plugins/projman/.mcp.json` |
### MCP Server Paths ### MCP Server Paths (Bundled in Plugins)
MCP servers are now **bundled inside each plugin** to ensure they work when plugins are cached.
| Context | Pattern | Example | | Context | Pattern | Example |
|---------|---------|---------| |---------|---------|---------|
| MCP server location | `mcp-servers/{server-name}/` | `mcp-servers/gitea/` | | MCP server location | `plugins/{plugin}/mcp-servers/{server}/` | `plugins/projman/mcp-servers/gitea/` |
| MCP server code | `mcp-servers/{server-name}/mcp_server/` | `mcp-servers/gitea/mcp_server/` | | MCP server code | `plugins/{plugin}/mcp-servers/{server}/mcp_server/` | `plugins/projman/mcp-servers/gitea/mcp_server/` |
| MCP venv | `mcp-servers/{server-name}/.venv/` | `mcp-servers/gitea/.venv/` | | MCP venv | `plugins/{plugin}/mcp-servers/{server}/.venv/` | `plugins/projman/mcp-servers/gitea/.venv/` |
### Relative Path Patterns (CRITICAL) ### Relative Path Patterns (CRITICAL)
| From | To | Pattern | | From | To | Pattern |
|------|----|---------| |------|----|---------|
| Plugin .mcp.json | MCP server | `${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/{server}` | | Plugin .mcp.json | Bundled MCP server | `${CLAUDE_PLUGIN_ROOT}/mcp-servers/{server}` |
| marketplace.json | Plugin | `./plugins/{plugin-name}` | | marketplace.json | Plugin | `./plugins/{plugin-name}` |
### Documentation Paths ### Documentation Paths
@@ -92,16 +102,13 @@ support-claude-mktplace/
### Relative Path Calculation ### Relative Path Calculation
From `plugins/projman/.mcp.json` to `mcp-servers/gitea/`: From `plugins/projman/.mcp.json` to bundled `mcp-servers/gitea/`:
``` ```
plugins/projman/.mcp.json plugins/projman/.mcp.json
↑ go up to plugins/projman/ (../) → MCP servers are IN the plugin at mcp-servers/
↑ go up to plugins/ (../)
↑ go up to root/ (../)
→ go down to mcp-servers/gitea/ (mcp-servers/gitea/)
Result: ../../mcp-servers/gitea/ Result: mcp-servers/gitea/
With variable: ${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/gitea/ With variable: ${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea/
``` ```
From `.claude-plugin/marketplace.json` to `plugins/projman/`: From `.claude-plugin/marketplace.json` to `plugins/projman/`:
@@ -120,9 +127,18 @@ Result: ./plugins/projman
| Wrong | Why | Correct | | Wrong | Why | Correct |
|-------|-----|---------| |-------|-----|---------|
| `projman/` at root | Plugins go in `plugins/` | `plugins/projman/` | | `projman/` at root | Plugins go in `plugins/` | `plugins/projman/` |
| `../mcp-servers/` from plugin | Missing one level | `../../mcp-servers/` | | `mcp-servers/` at root | MCP servers are bundled in plugins | `plugins/{plugin}/mcp-servers/` |
| `../../mcp-servers/` from plugin | Old pattern, doesn't work with caching | `${CLAUDE_PLUGIN_ROOT}/mcp-servers/` |
| `./../../../plugins/projman` in marketplace | Wrong (old nested structure) | `./plugins/projman` | | `./../../../plugins/projman` in marketplace | Wrong (old nested structure) | `./plugins/projman` |
| Creating `docs/CORRECT-ARCHITECTURE.md` | This file replaces it | Use `docs/CANONICAL-PATHS.md` |
---
## Architecture Note
MCP servers are bundled inside each plugin (not shared at root) because:
- Claude Code caches only the plugin directory when installed
- Relative paths to parent directories break in the cache
- Each plugin must be self-contained to work properly
--- ---
@@ -130,4 +146,5 @@ Result: ./plugins/projman
| Date | Change | By | | Date | Change | By |
|------|--------|-----| |------|--------|-----|
| 2025-12-15 | Restructured: MCP servers now bundled in plugins | Claude Code |
| 2025-12-12 | Initial creation | Claude Code | | 2025-12-12 | Initial creation | Claude Code |

View File

@@ -1,11 +1,11 @@
{ {
"mcpServers": { "mcpServers": {
"netbox": { "netbox": {
"command": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/netbox/.venv/bin/python", "command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/netbox/.venv/bin/python",
"args": ["-m", "mcp_server.server"], "args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/netbox", "cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/netbox",
"env": { "env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/netbox" "PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/netbox"
} }
} }
} }

View File

@@ -1,19 +1,19 @@
{ {
"mcpServers": { "mcpServers": {
"gitea": { "gitea": {
"command": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/gitea/.venv/bin/python", "command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea/.venv/bin/python",
"args": ["-m", "mcp_server.server"], "args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/gitea", "cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea",
"env": { "env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/gitea" "PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea"
} }
}, },
"wikijs": { "wikijs": {
"command": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/wikijs/.venv/bin/python", "command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/wikijs/.venv/bin/python",
"args": ["-m", "mcp_server.server"], "args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/wikijs", "cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/wikijs",
"env": { "env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../../mcp-servers/wikijs" "PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/wikijs"
} }
} }
} }

View File

@@ -5,12 +5,12 @@
# Usage: ./scripts/setup.sh # Usage: ./scripts/setup.sh
# #
# This script: # This script:
# 1. Creates Python virtual environments for MCP servers # 1. Creates Python virtual environments for MCP servers (inside each plugin)
# 2. Installs dependencies # 2. Installs dependencies
# 3. Creates config file templates (if missing) # 3. Creates config file templates (if missing)
# 4. Validates existing configuration # 4. Validates existing configuration
# 5. Checks/creates Wiki.js directory structure # 5. Checks/creates Wiki.js directory structure
# 6. Syncs Gitea labels (creates/updates, respects custom labels) # 6. Validates label reference file
# 7. Reports remaining manual steps # 7. Reports remaining manual steps
# #
@@ -40,11 +40,13 @@ log_error() { echo -e "${RED}[ERROR]${NC} $1"; FAILED+=("$1"); }
log_todo() { echo -e "${YELLOW}[TODO]${NC} $1"; MANUAL_TODO+=("$1"); } log_todo() { echo -e "${YELLOW}[TODO]${NC} $1"; MANUAL_TODO+=("$1"); }
# --- Section 1: Python Environments --- # --- Section 1: Python Environments ---
setup_python_env() { # MCP servers are now inside each plugin
local server_name="$1" setup_plugin_mcp() {
local server_path="$REPO_ROOT/mcp-servers/$server_name" local plugin_name="$1"
local server_name="$2"
local server_path="$REPO_ROOT/plugins/$plugin_name/mcp-servers/$server_name"
log_info "Setting up $server_name MCP server..." log_info "Setting up $server_name MCP server (plugin: $plugin_name)..."
if [[ ! -d "$server_path" ]]; then if [[ ! -d "$server_path" ]]; then
log_error "$server_name directory not found at $server_path" log_error "$server_name directory not found at $server_path"
@@ -55,10 +57,10 @@ setup_python_env() {
# Check if venv exists # Check if venv exists
if [[ -d ".venv" ]]; then if [[ -d ".venv" ]]; then
log_skip "$server_name venv already exists" log_skip "$plugin_name/$server_name venv already exists"
else else
python3 -m venv .venv python3 -m venv .venv
log_success "$server_name venv created" log_success "$plugin_name/$server_name venv created"
fi fi
# Install/update dependencies # Install/update dependencies
@@ -67,9 +69,9 @@ setup_python_env() {
pip install -q --upgrade pip pip install -q --upgrade pip
pip install -q -r requirements.txt pip install -q -r requirements.txt
deactivate deactivate
log_success "$server_name dependencies installed" log_success "$plugin_name/$server_name dependencies installed"
else else
log_error "$server_name requirements.txt not found" log_error "$plugin_name/$server_name requirements.txt not found"
fi fi
cd "$REPO_ROOT" cd "$REPO_ROOT"
@@ -117,6 +119,22 @@ EOF
log_success "wikijs.env template created" log_success "wikijs.env template created"
log_todo "Edit ~/.config/claude/wikijs.env with your Wiki.js credentials" log_todo "Edit ~/.config/claude/wikijs.env with your Wiki.js credentials"
fi fi
# NetBox config
if [[ -f "$config_dir/netbox.env" ]]; then
log_skip "netbox.env already exists"
else
cat > "$config_dir/netbox.env" << 'EOF'
# NetBox API Configuration
# Update these values with your NetBox instance details
NETBOX_API_URL=https://netbox.example.com/api
NETBOX_API_TOKEN=your_netbox_token_here
EOF
chmod 600 "$config_dir/netbox.env"
log_success "netbox.env template created"
log_todo "Edit ~/.config/claude/netbox.env with your NetBox credentials"
fi
} }
# --- Section 3: Validate Configuration --- # --- Section 3: Validate Configuration ---
@@ -128,7 +146,7 @@ validate_config() {
# Check Gitea config has real values # Check Gitea config has real values
if [[ -f "$config_dir/gitea.env" ]]; then if [[ -f "$config_dir/gitea.env" ]]; then
source "$config_dir/gitea.env" source "$config_dir/gitea.env"
if [[ "$GITEA_API_TOKEN" == "your_gitea_token_here" ]] || [[ -z "$GITEA_API_TOKEN" ]]; then if [[ "${GITEA_API_TOKEN:-}" == "your_gitea_token_here" ]] || [[ -z "${GITEA_API_TOKEN:-}" ]]; then
log_todo "Update GITEA_API_TOKEN in ~/.config/claude/gitea.env" log_todo "Update GITEA_API_TOKEN in ~/.config/claude/gitea.env"
else else
log_success "Gitea configuration appears valid" log_success "Gitea configuration appears valid"
@@ -138,12 +156,22 @@ validate_config() {
# Check Wiki.js config has real values # Check Wiki.js config has real values
if [[ -f "$config_dir/wikijs.env" ]]; then if [[ -f "$config_dir/wikijs.env" ]]; then
source "$config_dir/wikijs.env" source "$config_dir/wikijs.env"
if [[ "$WIKIJS_API_TOKEN" == "your_wikijs_jwt_token_here" ]] || [[ -z "$WIKIJS_API_TOKEN" ]]; then if [[ "${WIKIJS_API_TOKEN:-}" == "your_wikijs_jwt_token_here" ]] || [[ -z "${WIKIJS_API_TOKEN:-}" ]]; then
log_todo "Update WIKIJS_API_TOKEN in ~/.config/claude/wikijs.env" log_todo "Update WIKIJS_API_TOKEN in ~/.config/claude/wikijs.env"
else else
log_success "Wiki.js configuration appears valid" log_success "Wiki.js configuration appears valid"
fi fi
fi fi
# Check NetBox config has real values
if [[ -f "$config_dir/netbox.env" ]]; then
source "$config_dir/netbox.env"
if [[ "${NETBOX_API_TOKEN:-}" == "your_netbox_token_here" ]] || [[ -z "${NETBOX_API_TOKEN:-}" ]]; then
log_todo "Update NETBOX_API_TOKEN in ~/.config/claude/netbox.env"
else
log_success "NetBox configuration appears valid"
fi
fi
} }
# --- Section 4: Wiki.js Directory Structure --- # --- Section 4: Wiki.js Directory Structure ---
@@ -156,7 +184,7 @@ setup_wikijs_structure() {
local config_dir="$HOME/.config/claude" local config_dir="$HOME/.config/claude"
if [[ -f "$config_dir/wikijs.env" ]]; then if [[ -f "$config_dir/wikijs.env" ]]; then
source "$config_dir/wikijs.env" source "$config_dir/wikijs.env"
if [[ "$WIKIJS_API_TOKEN" != "your_wikijs_jwt_token_here" ]] && [[ -n "$WIKIJS_API_TOKEN" ]]; then if [[ "${WIKIJS_API_TOKEN:-}" != "your_wikijs_jwt_token_here" ]] && [[ -n "${WIKIJS_API_TOKEN:-}" ]]; then
log_info "Wiki.js credentials found - directory structure can be verified after first use" log_info "Wiki.js credentials found - directory structure can be verified after first use"
log_success "Wiki.js setup deferred to first use" log_success "Wiki.js setup deferred to first use"
else else
@@ -238,9 +266,12 @@ main() {
echo "==============================================" echo "=============================================="
echo "" echo ""
# Python environments # Python environments for projman plugin
setup_python_env "gitea" setup_plugin_mcp "projman" "gitea"
setup_python_env "wikijs" setup_plugin_mcp "projman" "wikijs"
# Python environment for cmdb-assistant plugin
setup_plugin_mcp "cmdb-assistant" "netbox"
# Configuration # Configuration
setup_config_templates setup_config_templates