22 Commits

Author SHA1 Message Date
90f19dfc0f fix(post-update): clear Claude plugin cache on update
The Claude plugin cache at ~/.claude/plugins/cache/leo-claude-mktplace/
holds versioned copies of .mcp.json files. When we update .mcp.json
to use run.sh instead of direct python paths, the cache retains old
versions, causing MCP servers to fail.

Now post-update.sh clears this cache, forcing Claude to read fresh
configs from the installed marketplace on next session start.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 14:55:42 -05:00
54bb347ee1 Merge pull request 'fix(gitea-mcp): add fix/* and other branch patterns to permissions' (#219) from fix/branch-permission-patterns into development
Reviewed-on: #219
2026-01-27 19:42:02 +00:00
51bcc26ea9 fix(gitea-mcp): add fix/* and other branch patterns to permissions
The branch permission check was only allowing feat/, feature/, and dev/
prefixes for write operations. This blocked PR creation from fix/*
branches, forcing fallback to direct API calls.

Added patterns:
- fix/, bugfix/, hotfix/ - for bug fixes
- chore/, refactor/ - for maintenance
- docs/, test/ - for documentation and tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 14:41:18 -05:00
dbb6d46fa4 Merge pull request 'fix(mcp): use wrapper scripts instead of venv symlinks' (#217) from fix/mcp-venv-wrapper-scripts into development
Reviewed-on: #217
2026-01-27 19:38:31 +00:00
e7050e2ad8 fix(mcp): use wrapper scripts instead of venv symlinks
Replace direct python path in .mcp.json with run.sh wrapper scripts
that automatically locate the venv in cache or local directory.

Problem: .venv symlinks are gitignored, causing them to be wiped on
every git operation. The SessionStart hook should recreate them but
this was unreliable, leading to repeated MCP server failures.

Solution: run.sh scripts that:
- First check ~/.cache/claude-mcp-venvs/leo-claude-mktplace/{server}/.venv
- Fallback to local .venv if exists
- Exit with helpful error if neither found

This eliminates dependency on symlinks entirely - the scripts are
tracked in git and will always be present after clone/pull/update.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 14:36:05 -05:00
35380594b4 Merge pull request 'feat(cmdb-assistant): add data quality validation v1.1.0' (#215) from feat/cmdb-assistant-data-quality into development
Reviewed-on: #215
2026-01-27 17:34:19 +00:00
0055c9ecf2 feat(gitea-mcp): add create_pull_request tool
Add missing create_pull_request tool to Gitea MCP server. This completes
the PR lifecycle - previously only had list/get/review/comment tools.

- Add create_pull_request to GiteaClient
- Add async wrapper to PullRequestTools with branch permissions
- Register tool in server.py with proper schema
- Parameters: title, body, head, base, labels (optional)
- Branch-aware security: only allowed on development/feature branches

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 12:30:48 -05:00
a74a048898 feat(cmdb-assistant): add data quality validation v1.1.0
Add validation hooks, best practices skill, and new commands to enforce
NetBox data quality standards:

Hooks:
- SessionStart: Test NetBox connectivity, report data quality issues
- PreToolUse: Validate VM/device parameters before create/update

New Commands:
- /cmdb-audit: Data quality analysis (vms, devices, naming, roles)
- /cmdb-register: Register current machine with running applications
- /cmdb-sync: Sync machine state with NetBox, detect drift

Best Practices Skill:
- Dependency order (regions -> sites -> devices -> VMs)
- Site/tenant/platform assignment requirements
- Naming conventions enforcement
- Role consolidation guidance

Updated agent with validation requirements, dependency order checks,
naming convention warnings, and duplicate prevention.

Marketplace: 5.0.0 -> 5.1.0
Plugin: 1.0.0 -> 1.1.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 12:27:23 -05:00
7492cfad66 Merge pull request 'fix(post-update): use venv-repair for instant symlink restoration' (#213) from fix/post-update-venv-repair into development
Reviewed-on: #213
2026-01-27 16:56:52 +00:00
59db9ea0b0 fix(post-update): use venv-repair for instant symlink restoration
Replace the old approach (create venvs in marketplace directory) with
the new venv-repair.sh approach (symlinks to external cache).

This ensures post-update.sh:
- Instantly restores symlinks if cache exists
- Only does full pip install on first run
- Works correctly after marketplace updates

Flow after this fix:
  Update marketplace → post-update.sh → venv-repair → Session works

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 11:55:43 -05:00
a21199d3db Merge pull request 'fix(mcp): persistent venv cache survives marketplace updates' (#211) from fix/persistent-venv-cache into development
Reviewed-on: #211
2026-01-27 16:49:19 +00:00
1abda1ca0f fix(mcp): persistent venv cache survives marketplace updates
Problem:
- Venvs in marketplace directory got deleted on every update
- Users had to manually run setup.sh and wait for full pip install
- This caused MCP servers to fail until manually fixed

Solution:
- Store venvs in external cache (~/.cache/claude-mcp-venvs/)
- Auto-repair symlinks via SessionStart hook (instant operation)
- Only run pip install on first use or when requirements change

Architecture:
  Cache (runtime) → Marketplaces → External venv cache

  The chain of symlinks ensures all three locations work:
  1. ~/.claude/plugins/cache/.../mcp-servers/* (runtime)
  2. ~/.claude/plugins/marketplaces/.../mcp-servers/* (install)
  3. ~/.cache/claude-mcp-venvs/* (persistent venvs)

Performance:
- First install: ~2-3 min (unchanged)
- After marketplace update: 0.03 sec (was 2-3 min)

Files:
- scripts/venv-repair.sh: Fast symlink restoration for hooks
- scripts/setup-venvs.sh: Full setup with external cache
- plugins/projman/hooks/startup-check.sh: Auto-repair on session start
- .gitignore: Ignore .venv symlinks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 11:48:13 -05:00
bbb822db16 Merge pull request 'fix(post-update): create missing venvs instead of just warning' (#209) from fix/post-update-create-venvs into development
Reviewed-on: #209
2026-01-27 15:51:39 +00:00
08e1dcb1f5 fix(post-update): create missing venvs instead of just warning
Previously post-update.sh would only warn when venvs were missing,
requiring a separate setup.sh run. Now it automatically creates
missing venvs and installs dependencies including editable packages.

Also added viz-platform and contract-validator to the update list.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 10:41:04 -05:00
1b029d97b8 Merge pull request 'fix/data-platform-filter-index' (#207) from fix/data-platform-filter-index into development
Reviewed-on: #207
2026-01-26 22:27:19 +00:00
4ed3ed7e14 fix(data-platform): reset index after filter to prevent extra column
The filter tool was adding an __index_level_0__ column to results
because pandas query() preserves the original index, which gets
converted to a column when storing the DataFrame.

Added .reset_index(drop=True) after query() to drop the preserved
index and create a clean sequential index.

Fixes #203

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 17:25:37 -05:00
c5232bd7bf Merge pull request 'development' (#202) from development into main
Reviewed-on: #202
2026-01-26 21:49:08 +00:00
f9e23fd6eb Merge pull request 'fix/setup-editable-install' (#201) from fix/setup-editable-install into development
Reviewed-on: #201
2026-01-26 21:48:50 +00:00
457ed9c9ff fix(setup): install MCP packages in editable mode for viz-platform and contract-validator
The setup script only installed requirements.txt dependencies but not the
local package itself, causing MCP servers to fail with ModuleNotFoundError.

Changes:
- Add `pip install -e .` for servers with pyproject.toml
- Add viz-platform and contract-validator to MCP server setup
- Add symlink verification for new plugins
- Update version banner to v5.0.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 16:44:11 -05:00
dadb4d3576 Merge pull request 'Merge development into main: contract-validator setup docs' (#200) from development into main 2026-01-26 20:57:23 +00:00
ba771f100f Merge pull request 'docs: add contract-validator initial-setup and update version tags' (#199) from docs/contract-validator-setup into development 2026-01-26 20:57:09 +00:00
2b9cb5defd docs: add contract-validator initial-setup and update version tags
- Add /initial-setup command for contract-validator plugin
- Add contract-validator section to README.md (NEW in v5.0.0)
- Update data-platform version tag: *NEW* -> *NEW in v4.0.0*
- Update viz-platform version tag: *NEW* -> *NEW in v4.0.0*
- Add Contract Validator MCP Server section to README.md
- Add contract-validator to test commands table and repo structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 15:55:17 -05:00
38 changed files with 2458 additions and 97 deletions

View File

@@ -6,7 +6,7 @@
},
"metadata": {
"description": "Project management plugins with Gitea and NetBox integrations",
"version": "5.0.0"
"version": "5.1.0"
},
"plugins": [
{
@@ -75,8 +75,8 @@
},
{
"name": "cmdb-assistant",
"version": "1.0.0",
"description": "NetBox CMDB integration for infrastructure management",
"version": "1.1.0",
"description": "NetBox CMDB integration with data quality validation and machine registration",
"source": "./plugins/cmdb-assistant",
"author": {
"name": "Leo Miranda",
@@ -86,7 +86,7 @@
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"mcpServers": ["./.mcp.json"],
"category": "infrastructure",
"tags": ["cmdb", "netbox", "dcim", "ipam"],
"tags": ["cmdb", "netbox", "dcim", "ipam", "data-quality", "validation"],
"license": "MIT"
},
{

2
.gitignore vendored
View File

@@ -31,6 +31,8 @@ venv/
ENV/
env/
.venv/
.venv
**/.venv
# PyCharm
.idea/

View File

@@ -4,6 +4,51 @@ All notable changes to the Leo Claude Marketplace will be documented in this fil
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
#### Gitea MCP Server - create_pull_request Tool
- **`create_pull_request`**: Create new pull requests via MCP
- Parameters: title, body, head (source branch), base (target branch), labels
- Branch-aware security: only allowed on development/feature branches
- Completes the PR lifecycle (was previously missing - only had list/get/review/comment)
#### cmdb-assistant v1.1.0 - Data Quality Validation
- **SessionStart Hook**: Tests NetBox API connectivity at session start
- Warns if VMs exist without site assignment
- Warns if devices exist without platform
- Non-blocking: displays warning, doesn't prevent work
- **PreToolUse Hook**: Validates input parameters before VM/device operations
- Warns about missing site, tenant, platform
- Non-blocking: suggests best practices without blocking
- **`/cmdb-audit` Command**: Comprehensive data quality analysis
- Scopes: all, vms, devices, naming, roles
- Identifies Critical/High/Medium/Low issues
- Provides prioritized remediation recommendations
- **`/cmdb-register` Command**: Register current machine into NetBox
- Discovers system info: hostname, platform, hardware, network interfaces
- Discovers running apps: Docker containers, systemd services
- Creates device with interfaces, IPs, and sets primary IP
- Creates cluster and VMs for Docker containers
- **`/cmdb-sync` Command**: Sync machine state with NetBox
- Compares current state with NetBox record
- Shows diff of changes (interfaces, IPs, containers)
- Updates with user confirmation
- Supports --full and --dry-run flags
- **NetBox Best Practices Skill**: Reference documentation
- Dependency order for object creation
- Naming conventions (`{role}-{site}-{number}`, `{env}-{app}-{number}`)
- Role consolidation guidance
- Site/tenant/platform assignment requirements
- **Agent Enhancement**: Updated cmdb-assistant agent with validation requirements
- Proactive suggestions for missing fields
- Naming convention checks
- Dependency order enforcement
- Duplicate prevention
---
## [5.0.0] - 2026-01-26
### Added

View File

@@ -1,4 +1,4 @@
# Leo Claude Marketplace - v5.0.0
# Leo Claude Marketplace - v5.1.0
A collection of Claude Code plugins for project management, infrastructure automation, and development workflows.
@@ -53,6 +53,19 @@ Analyze, optimize, and create CLAUDE.md configuration files for Claude Code proj
**Commands:** `/config-analyze`, `/config-optimize`, `/config-init`
#### [contract-validator](./plugins/contract-validator/README.md) *NEW in v5.0.0*
**Cross-Plugin Compatibility Validation**
Validate plugin marketplaces for command conflicts, tool overlaps, and broken agent references.
- Interface parsing from plugin README.md files
- Agent extraction from CLAUDE.md definitions
- Pairwise compatibility checks between all plugins
- Data flow validation for agent sequences
- Markdown or JSON reports with actionable suggestions
**Commands:** `/validate-contracts`, `/check-agent`, `/list-interfaces`, `/initial-setup`
### Productivity
#### [clarity-assist](./plugins/clarity-assist/README.md) *NEW in v3.0.0*
@@ -98,7 +111,7 @@ Full CRUD operations for network infrastructure management directly from Claude
### Data Engineering
#### [data-platform](./plugins/data-platform/README.md) *NEW*
#### [data-platform](./plugins/data-platform/README.md) *NEW in v4.0.0*
**pandas, PostgreSQL/PostGIS, and dbt Integration**
Comprehensive data engineering toolkit with persistent DataFrame storage.
@@ -113,7 +126,7 @@ Comprehensive data engineering toolkit with persistent DataFrame storage.
### Visualization
#### [viz-platform](./plugins/viz-platform/README.md) *NEW*
#### [viz-platform](./plugins/viz-platform/README.md) *NEW in v4.0.0*
**Dash Mantine Components Validation and Theming**
Visualization toolkit with version-locked component validation and design token theming.
@@ -157,7 +170,7 @@ Comprehensive NetBox REST API integration for infrastructure management.
| Virtualization | Clusters, VMs, Interfaces |
| Extras | Tags, Custom Fields, Audit Log |
### Data Platform MCP Server (shared) *NEW*
### Data Platform MCP Server (shared) *NEW in v4.0.0*
pandas, PostgreSQL/PostGIS, and dbt integration for data engineering.
@@ -168,7 +181,7 @@ pandas, PostgreSQL/PostGIS, and dbt integration for data engineering.
| PostGIS | `st_tables`, `st_geometry_type`, `st_srid`, `st_extent` |
| dbt | `dbt_parse`, `dbt_run`, `dbt_test`, `dbt_build`, `dbt_compile`, `dbt_ls`, `dbt_docs_generate`, `dbt_lineage` |
### Viz Platform MCP Server (shared) *NEW*
### Viz Platform MCP Server (shared) *NEW in v4.0.0*
Dash Mantine Components validation and visualization tools.
@@ -180,6 +193,16 @@ Dash Mantine Components validation and visualization tools.
| Theme | `theme_create`, `theme_extend`, `theme_validate`, `theme_export_css`, `theme_list`, `theme_activate` |
| Page | `page_create`, `page_add_navbar`, `page_set_auth`, `page_list`, `page_get_app_config` |
### Contract Validator MCP Server (shared) *NEW in v5.0.0*
Cross-plugin compatibility validation tools.
| Category | Tools |
|----------|-------|
| Parse | `parse_plugin_interface`, `parse_claude_md_agents` |
| Validation | `validate_compatibility`, `validate_agent_refs`, `validate_data_flow` |
| Report | `generate_compatibility_report`, `list_issues` |
## Installation
### Prerequisites
@@ -278,6 +301,7 @@ After installing plugins, the `/plugin` command may show `(no content)` - this i
| cmdb-assistant | `/cmdb-assistant:cmdb-search` |
| data-platform | `/data-platform:ingest` |
| viz-platform | `/viz-platform:chart` |
| contract-validator | `/contract-validator:validate-contracts` |
## Repository Structure
@@ -289,14 +313,16 @@ leo-claude-mktplace/
│ ├── gitea/ # Gitea MCP (issues, PRs, wiki)
│ ├── netbox/ # NetBox MCP (CMDB)
│ ├── data-platform/ # Data engineering (pandas, PostgreSQL, dbt)
── viz-platform/ # Visualization (DMC, Plotly, theming)
── viz-platform/ # Visualization (DMC, Plotly, theming)
│ └── contract-validator/ # Cross-plugin validation (v5.0.0)
├── plugins/ # All plugins
│ ├── projman/ # Sprint management
│ ├── git-flow/ # Git workflow automation
│ ├── pr-review/ # PR review
│ ├── clarity-assist/ # Prompt optimization
│ ├── data-platform/ # Data engineering
│ ├── viz-platform/ # Visualization (NEW)
│ ├── viz-platform/ # Visualization
│ ├── contract-validator/ # Cross-plugin validation (NEW)
│ ├── claude-config-maintainer/ # CLAUDE.md optimization
│ ├── cmdb-assistant/ # NetBox CMDB integration
│ ├── doc-guardian/ # Documentation drift detection

View File

@@ -0,0 +1,17 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_VENV="$HOME/.cache/claude-mcp-venvs/leo-claude-mktplace/contract-validator/.venv"
LOCAL_VENV="$SCRIPT_DIR/.venv"
if [[ -f "$CACHE_VENV/bin/python" ]]; then
PYTHON="$CACHE_VENV/bin/python"
elif [[ -f "$LOCAL_VENV/bin/python" ]]; then
PYTHON="$LOCAL_VENV/bin/python"
else
echo "ERROR: No venv found. Run: ./scripts/setup-venvs.sh" >&2
exit 1
fi
cd "$SCRIPT_DIR"
export PYTHONPATH="$SCRIPT_DIR"
exec "$PYTHON" -m mcp_server.server "$@"

View File

@@ -330,7 +330,7 @@ class PandasTools:
return {'error': f'DataFrame not found: {data_ref}'}
try:
filtered = df.query(condition)
filtered = df.query(condition).reset_index(drop=True)
result_name = name or f"{data_ref}_filtered"
return self._check_and_store(
filtered,

View File

@@ -0,0 +1,17 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_VENV="$HOME/.cache/claude-mcp-venvs/leo-claude-mktplace/data-platform/.venv"
LOCAL_VENV="$SCRIPT_DIR/.venv"
if [[ -f "$CACHE_VENV/bin/python" ]]; then
PYTHON="$CACHE_VENV/bin/python"
elif [[ -f "$LOCAL_VENV/bin/python" ]]; then
PYTHON="$LOCAL_VENV/bin/python"
else
echo "ERROR: No venv found. Run: ./scripts/setup-venvs.sh" >&2
exit 1
fi
cd "$SCRIPT_DIR"
export PYTHONPATH="$SCRIPT_DIR"
exec "$PYTHON" -m mcp_server.server "$@"

View File

@@ -787,3 +787,42 @@ class GiteaClient:
response = self.session.post(url, json=data)
response.raise_for_status()
return response.json()
def create_pull_request(
self,
title: str,
body: str,
head: str,
base: str,
labels: Optional[List[str]] = None,
repo: Optional[str] = None
) -> Dict:
"""
Create a new pull request.
Args:
title: PR title
body: PR description/body
head: Source branch name (the branch with changes)
base: Target branch name (the branch to merge into)
labels: Optional list of label names
repo: Repository in 'owner/repo' format
Returns:
Created pull request dictionary
"""
owner, target_repo = self._parse_repo(repo)
url = f"{self.base_url}/repos/{owner}/{target_repo}/pulls"
data = {
'title': title,
'body': body,
'head': head,
'base': base
}
if labels:
label_ids = self._resolve_label_ids(labels, owner, target_repo)
data['labels'] = label_ids
logger.info(f"Creating PR '{title}' in {owner}/{target_repo}: {head} -> {base}")
response = self.session.post(url, json=data)
response.raise_for_status()
return response.json()

View File

@@ -844,6 +844,41 @@ class GiteaMCPServer:
},
"required": ["pr_number", "body"]
}
),
Tool(
name="create_pull_request",
description="Create a new pull request",
inputSchema={
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "PR title"
},
"body": {
"type": "string",
"description": "PR description/body"
},
"head": {
"type": "string",
"description": "Source branch name (the branch with changes)"
},
"base": {
"type": "string",
"description": "Target branch name (the branch to merge into)"
},
"labels": {
"type": "array",
"items": {"type": "string"},
"description": "Optional list of label names"
},
"repo": {
"type": "string",
"description": "Repository name (owner/repo format)"
}
},
"required": ["title", "body", "head", "base"]
}
)
]
@@ -959,6 +994,8 @@ class GiteaMCPServer:
result = await self.pr_tools.create_pr_review(**arguments)
elif name == "add_pr_comment":
result = await self.pr_tools.add_pr_comment(**arguments)
elif name == "create_pull_request":
result = await self.pr_tools.create_pull_request(**arguments)
else:
raise ValueError(f"Unknown tool: {name}")

View File

@@ -66,7 +66,13 @@ class IssueTools:
return operation in ['list_issues', 'get_issue', 'get_labels', 'create_issue']
# Development branches (full access)
if branch in ['development', 'develop'] or branch.startswith(('feat/', 'feature/', 'dev/')):
# Include all common feature/fix branch patterns
dev_prefixes = (
'feat/', 'feature/', 'dev/',
'fix/', 'bugfix/', 'hotfix/',
'chore/', 'refactor/', 'docs/', 'test/'
)
if branch in ['development', 'develop'] or branch.startswith(dev_prefixes):
return True
# Unknown branch - be restrictive

View File

@@ -69,7 +69,13 @@ class PullRequestTools:
return operation in read_ops + ['add_pr_comment']
# Development branches (full access)
if branch in ['development', 'develop'] or branch.startswith(('feat/', 'feature/', 'dev/')):
# Include all common feature/fix branch patterns
dev_prefixes = (
'feat/', 'feature/', 'dev/',
'fix/', 'bugfix/', 'hotfix/',
'chore/', 'refactor/', 'docs/', 'test/'
)
if branch in ['development', 'develop'] or branch.startswith(dev_prefixes):
return True
# Unknown branch - be restrictive
@@ -272,3 +278,42 @@ class PullRequestTools:
None,
lambda: self.gitea.add_pr_comment(pr_number, body, repo)
)
async def create_pull_request(
self,
title: str,
body: str,
head: str,
base: str,
labels: Optional[List[str]] = None,
repo: Optional[str] = None
) -> Dict:
"""
Create a new pull request (async wrapper with branch check).
Args:
title: PR title
body: PR description/body
head: Source branch name (the branch with changes)
base: Target branch name (the branch to merge into)
labels: Optional list of label names
repo: Override configured repo (for PMO multi-repo)
Returns:
Created pull request dictionary
Raises:
PermissionError: If operation not allowed on current branch
"""
if not self._check_branch_permissions('create_pull_request'):
branch = self._get_current_branch()
raise PermissionError(
f"Cannot create PR on branch '{branch}'. "
f"Switch to a development or feature branch to create PRs."
)
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self.gitea.create_pull_request(title, body, head, base, labels, repo)
)

17
mcp-servers/gitea/run.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_VENV="$HOME/.cache/claude-mcp-venvs/leo-claude-mktplace/gitea/.venv"
LOCAL_VENV="$SCRIPT_DIR/.venv"
if [[ -f "$CACHE_VENV/bin/python" ]]; then
PYTHON="$CACHE_VENV/bin/python"
elif [[ -f "$LOCAL_VENV/bin/python" ]]; then
PYTHON="$LOCAL_VENV/bin/python"
else
echo "ERROR: No venv found. Run: ./scripts/setup-venvs.sh" >&2
exit 1
fi
cd "$SCRIPT_DIR"
export PYTHONPATH="$SCRIPT_DIR"
exec "$PYTHON" -m mcp_server.server "$@"

17
mcp-servers/netbox/run.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_VENV="$HOME/.cache/claude-mcp-venvs/leo-claude-mktplace/netbox/.venv"
LOCAL_VENV="$SCRIPT_DIR/.venv"
if [[ -f "$CACHE_VENV/bin/python" ]]; then
PYTHON="$CACHE_VENV/bin/python"
elif [[ -f "$LOCAL_VENV/bin/python" ]]; then
PYTHON="$LOCAL_VENV/bin/python"
else
echo "ERROR: No venv found. Run: ./scripts/setup-venvs.sh" >&2
exit 1
fi
cd "$SCRIPT_DIR"
export PYTHONPATH="$SCRIPT_DIR"
exec "$PYTHON" -m mcp_server.server "$@"

17
mcp-servers/viz-platform/run.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_VENV="$HOME/.cache/claude-mcp-venvs/leo-claude-mktplace/viz-platform/.venv"
LOCAL_VENV="$SCRIPT_DIR/.venv"
if [[ -f "$CACHE_VENV/bin/python" ]]; then
PYTHON="$CACHE_VENV/bin/python"
elif [[ -f "$LOCAL_VENV/bin/python" ]]; then
PYTHON="$LOCAL_VENV/bin/python"
else
echo "ERROR: No venv found. Run: ./scripts/setup-venvs.sh" >&2
exit 1
fi
cd "$SCRIPT_DIR"
export PYTHONPATH="$SCRIPT_DIR"
exec "$PYTHON" -m mcp_server.server "$@"

View File

@@ -1,7 +1,7 @@
{
"name": "cmdb-assistant",
"version": "1.0.0",
"description": "NetBox CMDB integration for infrastructure management - query, create, update, and manage network devices, IP addresses, sites, and more",
"version": "1.1.0",
"description": "NetBox CMDB integration with data quality validation - query, create, update, and manage network devices, IP addresses, sites, and more with best practices enforcement",
"author": {
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
@@ -15,7 +15,9 @@
"infrastructure",
"network",
"ipam",
"dcim"
"dcim",
"data-quality",
"validation"
],
"commands": ["./commands/"],
"mcpServers": ["./.mcp.json"]

View File

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

View File

@@ -2,6 +2,14 @@
A Claude Code plugin for NetBox CMDB integration - query, create, update, and manage your network infrastructure directly from Claude Code.
## What's New in v1.1.0
- **Data Quality Validation**: Hooks for SessionStart and PreToolUse that check data quality and warn about missing fields
- **Best Practices Skill**: Reference documentation for NetBox patterns (naming conventions, dependency order, role management)
- **`/cmdb-audit`**: Analyze data quality across VMs, devices, naming conventions, and roles
- **`/cmdb-register`**: Register the current machine into NetBox with all running applications (Docker containers, systemd services)
- **`/cmdb-sync`**: Synchronize existing machine state with NetBox (detect drift, update with confirmation)
## Features
- **Full CRUD Operations**: Create, read, update, and delete across all NetBox modules
@@ -9,6 +17,9 @@ A Claude Code plugin for NetBox CMDB integration - query, create, update, and ma
- **IP Management**: Allocate IPs, manage prefixes, track VLANs
- **Infrastructure Documentation**: Document servers, network devices, and connections
- **Audit Trail**: Review changes and maintain infrastructure history
- **Data Quality Validation**: Proactive checks for missing site, tenant, platform assignments
- **Machine Registration**: Auto-discover and register servers with running applications
- **Drift Detection**: Sync machine state and detect changes over time
## Installation
@@ -40,10 +51,14 @@ Add to your Claude Code plugins or marketplace configuration.
| Command | Description |
|---------|-------------|
| `/initial-setup` | Interactive setup wizard for NetBox MCP server |
| `/cmdb-search <query>` | Search for devices, IPs, sites, or any CMDB object |
| `/cmdb-device <action>` | Manage network devices (list, create, update, delete) |
| `/cmdb-ip <action>` | Manage IP addresses and prefixes |
| `/cmdb-site <action>` | Manage sites and locations |
| `/cmdb-audit [scope]` | Data quality analysis (all, vms, devices, naming, roles) |
| `/cmdb-register` | Register current machine into NetBox with running apps |
| `/cmdb-sync` | Sync machine state with NetBox (detect drift, update) |
## Agent
@@ -103,6 +118,15 @@ This plugin provides access to the full NetBox API:
- **Wireless**: WLANs, Wireless Links
- **Extras**: Tags, Custom Fields, Journal Entries, Audit Log
## Hooks
| Event | Purpose |
|-------|---------|
| `SessionStart` | Test NetBox connectivity, report data quality issues |
| `PreToolUse` | Validate VM/device parameters before create/update |
Hooks are **non-blocking** - they emit warnings but never prevent operations.
## Architecture
```
@@ -115,13 +139,23 @@ cmdb-assistant/
│ ├── cmdb-search.md # Search command
│ ├── cmdb-device.md # Device management
│ ├── cmdb-ip.md # IP management
── cmdb-site.md # Site management
── cmdb-site.md # Site management
│ ├── cmdb-audit.md # Data quality audit (NEW)
│ ├── cmdb-register.md # Machine registration (NEW)
│ └── cmdb-sync.md # Machine sync (NEW)
├── hooks/
│ ├── hooks.json # Hook configuration
│ ├── startup-check.sh # SessionStart validation
│ └── validate-input.sh # PreToolUse validation
├── skills/
│ └── netbox-patterns/
│ └── SKILL.md # NetBox best practices reference
├── agents/
│ └── cmdb-assistant.md # Main assistant agent
└── README.md
```
The plugin uses the shared NetBox MCP server at `../mcp-servers/netbox/`.
The plugin uses the shared NetBox MCP server at `mcp-servers/netbox/`.
## Configuration

View File

@@ -76,3 +76,132 @@ When presenting data:
- Suggest corrective actions
- For permission errors, note what access is needed
- For validation errors, explain required fields/formats
## Data Quality Validation
**IMPORTANT:** Load the `netbox-patterns` skill for best practice reference.
Before ANY create or update operation, validate against NetBox best practices:
### VM Operations
**Required checks before `virt_create_vm` or `virt_update_vm`:**
1. **Cluster/Site Assignment** - VMs must have either cluster or site
2. **Tenant Assignment** - Recommend if not provided
3. **Platform Assignment** - Recommend for OS tracking
4. **Naming Convention** - Check against `{env}-{app}-{number}` pattern
5. **Role Assignment** - Recommend appropriate role
**If user provides no site/tenant, ASK:**
> "This VM has no site or tenant assigned. NetBox best practices recommend:
> - **Site**: For location-based queries and power budgeting
> - **Tenant**: For resource isolation and ownership tracking
>
> Would you like me to:
> 1. Assign to an existing site/tenant (list available)
> 2. Create new site/tenant first
> 3. Proceed without (not recommended for production use)"
### Device Operations
**Required checks before `dcim_create_device` or `dcim_update_device`:**
1. **Site is REQUIRED** - Fail without it
2. **Platform Assignment** - Recommend for OS tracking
3. **Naming Convention** - Check against `{role}-{location}-{number}` pattern
4. **Role Assignment** - Ensure appropriate role selected
5. **After Creation** - Offer to set primary IP
### Cluster Operations
**Required checks before `virt_create_cluster`:**
1. **Site Scope** - Recommend assigning to site
2. **Cluster Type** - Ensure appropriate type selected
3. **Device Association** - Recommend linking to host device
### Role Management
**Before creating a new device role:**
1. List existing roles with `dcim_list_device_roles`
2. Check if a more general role already exists
3. Recommend role consolidation if >10 specific roles exist
**Example guidance:**
> "You're creating role 'nginx-web-server'. An existing 'web-server' role exists.
> Consider using 'web-server' and tracking nginx via the platform field instead.
> This reduces role fragmentation and improves maintainability."
## Dependency Order Enforcement
When creating multiple objects, follow this order:
```
1. Regions → Sites → Locations → Racks
2. Tenant Groups → Tenants
3. Manufacturers → Device Types
4. Device Roles, Platforms
5. Devices (with site, role, type)
6. Clusters (with type, optional site)
7. VMs (with cluster)
8. Interfaces → IP Addresses → Primary IP assignment
```
**CRITICAL Rules:**
- NEVER create a VM before its cluster exists
- NEVER create a device before its site exists
- NEVER create an interface before its device exists
- NEVER create an IP before its interface exists (if assigning)
## Naming Convention Enforcement
When user provides a name, check against patterns:
| Object Type | Pattern | Example |
|-------------|---------|---------|
| Device | `{role}-{site}-{number}` | `web-dc1-01` |
| VM | `{env}-{app}-{number}` or `{prefix}_{service}` | `prod-api-01` |
| Cluster | `{site}-{type}` | `dc1-vmware`, `home-docker` |
| Prefix | Include purpose in description | "Production /24 for web tier" |
**If name doesn't match patterns, warn:**
> "The name 'HotServ' doesn't follow naming conventions.
> Suggested: `prod-hotserv-01` or `hotserv-cloud-01`.
> Consistent naming improves searchability and automation compatibility.
> Proceed with original name? [Y/n]"
## Duplicate Prevention
Before creating objects, always check for existing duplicates:
```
# Before creating device
dcim_list_devices name=<proposed-name>
# Before creating VM
virt_list_vms name=<proposed-name>
# Before creating prefix
ipam_list_prefixes prefix=<proposed-prefix>
```
If duplicate found, inform user and suggest update instead of create.
## Available Commands
Users can invoke these commands for structured workflows:
| Command | Purpose |
|---------|---------|
| `/cmdb-search <query>` | Search across all CMDB objects |
| `/cmdb-device <action>` | Device CRUD operations |
| `/cmdb-ip <action>` | IP address and prefix management |
| `/cmdb-site <action>` | Site and location management |
| `/cmdb-audit [scope]` | Data quality analysis |
| `/cmdb-register` | Register current machine |
| `/cmdb-sync` | Sync machine state with NetBox |

View File

@@ -0,0 +1,195 @@
---
description: Audit NetBox data quality and identify consistency issues
---
# CMDB Data Quality Audit
Analyze NetBox data for quality issues and best practice violations.
## Usage
```
/cmdb-audit [scope]
```
**Scopes:**
- `all` (default) - Full audit across all categories
- `vms` - Virtual machines only
- `devices` - Physical devices only
- `naming` - Naming convention analysis
- `roles` - Role fragmentation analysis
## Instructions
You are a data quality auditor for NetBox. Your job is to identify consistency issues and best practice violations.
**IMPORTANT:** Load the `netbox-patterns` skill for best practice reference.
### Phase 1: Data Collection
Run these MCP tool calls to gather data for analysis:
```
1. virt_list_vms (no filters - get all)
2. dcim_list_devices (no filters - get all)
3. virt_list_clusters (no filters)
4. dcim_list_sites
5. tenancy_list_tenants
6. dcim_list_device_roles
7. dcim_list_platforms
```
Store the results for analysis.
### Phase 2: Quality Checks
Analyze collected data for these issues by severity:
#### CRITICAL Issues (must fix immediately)
| Check | Detection |
|-------|-----------|
| VMs without cluster | `cluster` field is null AND `site` field is null |
| Devices without site | `site` field is null |
| Active devices without primary IP | `status=active` AND `primary_ip4` is null AND `primary_ip6` is null |
#### HIGH Issues (should fix soon)
| Check | Detection |
|-------|-----------|
| VMs without site | VM has no site (neither direct nor via cluster.site) |
| VMs without tenant | `tenant` field is null |
| Devices without platform | `platform` field is null |
| Clusters not scoped to site | `site` field is null on cluster |
| VMs without role | `role` field is null |
#### MEDIUM Issues (plan to address)
| Check | Detection |
|-------|-----------|
| Inconsistent naming | Names don't match patterns: devices=`{role}-{site}-{num}`, VMs=`{env}-{app}-{num}` |
| Role fragmentation | More than 10 device roles with <3 assignments each |
| Missing tags on production | Active resources without any tags |
| Mixed naming separators | Some names use `_`, others use `-` |
#### LOW Issues (informational)
| Check | Detection |
|-------|-----------|
| Docker containers as VMs | Cluster type is "Docker Compose" - document this modeling choice |
| VMs without description | `description` field is empty |
| Sites without physical address | `physical_address` is empty |
| Devices without serial | `serial` field is empty |
### Phase 3: Naming Convention Analysis
For naming scope, analyze patterns:
1. **Extract naming patterns** from existing objects
2. **Identify dominant patterns** (most common conventions)
3. **Flag outliers** that don't match dominant patterns
4. **Suggest standardization** based on best practices
**Expected Patterns:**
- Devices: `{role}-{location}-{number}` (e.g., `web-dc1-01`)
- VMs: `{prefix}_{service}` or `{env}-{app}-{number}` (e.g., `prod-api-01`)
- Clusters: `{site}-{type}` (e.g., `home-docker`)
### Phase 4: Role Analysis
For roles scope, analyze fragmentation:
1. **List all device roles** with assignment counts
2. **Identify single-use roles** (only 1 device/VM)
3. **Identify similar roles** that could be consolidated
4. **Suggest consolidation** based on patterns
**Red Flags:**
- More than 15 highly specific roles
- Roles with technology in name (use platform instead)
- Roles that duplicate functionality
### Phase 5: Report Generation
Present findings in this structure:
```markdown
## CMDB Data Quality Audit Report
**Generated:** [timestamp]
**Scope:** [scope parameter]
### Summary
| Metric | Count |
|--------|-------|
| Total VMs | X |
| Total Devices | Y |
| Total Clusters | Z |
| **Total Issues** | **N** |
| Severity | Count |
|----------|-------|
| Critical | A |
| High | B |
| Medium | C |
| Low | D |
### Critical Issues
[List each with specific object names and IDs]
**Example:**
- VM `HotServ` (ID: 1) - No cluster or site assignment
- Device `server-01` (ID: 5) - No site assignment
### High Issues
[List each with specific object names]
### Medium Issues
[Grouped by category with counts]
### Recommendations
1. **[Most impactful fix]** - affects N objects
2. **[Second priority]** - affects M objects
...
### Quick Fixes
Commands to fix common issues:
```
# Assign site to VM
virt_update_vm id=X site=Y
# Assign platform to device
dcim_update_device id=X platform=Y
```
### Next Steps
- Run `/cmdb-register` to properly register new machines
- Use `/cmdb-sync` to update existing registrations
- Consider bulk updates via NetBox web UI for >10 items
```
## Scope-Specific Instructions
### For `vms` scope:
Focus only on Virtual Machine checks. Skip device and role analysis.
### For `devices` scope:
Focus only on Device checks. Skip VM and cluster analysis.
### For `naming` scope:
Focus on naming convention analysis across all objects. Generate detailed pattern report.
### For `roles` scope:
Focus on role fragmentation analysis. Generate consolidation recommendations.
## User Request
$ARGUMENTS

View File

@@ -0,0 +1,322 @@
---
description: Register the current machine into NetBox with all running applications
---
# CMDB Machine Registration
Register the current machine into NetBox, including hardware info, network interfaces, and running applications (Docker containers, services).
## Usage
```
/cmdb-register [--site <site-name>] [--tenant <tenant-name>] [--role <role-name>]
```
**Options:**
- `--site <name>`: Site to assign (will prompt if not provided)
- `--tenant <name>`: Tenant for resource isolation (optional)
- `--role <name>`: Device role (default: auto-detect based on services)
## Instructions
You are registering the current machine into NetBox. This is a multi-phase process that discovers local system information and creates corresponding NetBox objects.
**IMPORTANT:** Load the `netbox-patterns` skill for best practice reference.
### Phase 1: System Discovery (via Bash)
Gather system information using these commands:
#### 1.1 Basic Device Info
```bash
# Hostname
hostname
# OS/Platform info
cat /etc/os-release 2>/dev/null || uname -a
# Hardware model (varies by system)
# Raspberry Pi:
cat /proc/device-tree/model 2>/dev/null || echo "Unknown"
# x86 systems:
cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown"
# Serial number
# Raspberry Pi:
cat /proc/device-tree/serial-number 2>/dev/null || cat /proc/cpuinfo | grep Serial | cut -d: -f2 | tr -d ' ' 2>/dev/null
# x86 systems:
cat /sys/class/dmi/id/product_serial 2>/dev/null || echo "Unknown"
# CPU info
nproc
# Memory (MB)
free -m | awk '/Mem:/ {print $2}'
# Disk (GB, root filesystem)
df -BG / | awk 'NR==2 {print $2}' | tr -d 'G'
```
#### 1.2 Network Interfaces
```bash
# Get interfaces with IPs (JSON format)
ip -j addr show 2>/dev/null || ip addr show
# Get default gateway interface
ip route | grep default | awk '{print $5}' | head -1
# Get MAC addresses
ip -j link show 2>/dev/null || ip link show
```
#### 1.3 Running Applications
```bash
# Docker containers (if docker available)
docker ps --format '{"name":"{{.Names}}","image":"{{.Image}}","status":"{{.Status}}","ports":"{{.Ports}}"}' 2>/dev/null || echo "Docker not available"
# Docker Compose projects (check common locations)
find ~/apps /home/*/apps -name "docker-compose.yml" -o -name "docker-compose.yaml" 2>/dev/null | head -20
# Systemd services (running)
systemctl list-units --type=service --state=running --no-pager --plain 2>/dev/null | grep -v "^UNIT" | head -30
```
### Phase 2: Pre-Registration Checks (via MCP)
Before creating objects, verify prerequisites:
#### 2.1 Check if Device Already Exists
```
dcim_list_devices name=<hostname>
```
**If device exists:**
- Inform user and suggest `/cmdb-sync` instead
- Ask if they want to proceed with re-registration (will update existing)
#### 2.2 Verify/Create Site
If `--site` provided:
```
dcim_list_sites name=<site-name>
```
If site doesn't exist, ask user if they want to create it.
If no site provided, list available sites and ask user to choose:
```
dcim_list_sites
```
#### 2.3 Verify/Create Platform
Based on OS detected, check if platform exists:
```
dcim_list_platforms name=<platform-name>
```
**Platform naming:**
- `Raspberry Pi OS (Bookworm)` for Raspberry Pi
- `Ubuntu 24.04 LTS` for Ubuntu
- `Debian 12` for Debian
- Use format: `{OS Name} {Version}`
If platform doesn't exist, create it:
```
dcim_create_platform name=<platform-name> slug=<slug>
```
#### 2.4 Verify/Create Device Role
Based on detected services:
- If Docker containers found → `Docker Host`
- If only basic services → `Server`
- If specific role specified → Use that
```
dcim_list_device_roles name=<role-name>
```
### Phase 3: Device Registration (via MCP)
#### 3.1 Get/Create Manufacturer and Device Type
For Raspberry Pi:
```
dcim_list_manufacturers name="Raspberry Pi Foundation"
dcim_list_device_types manufacturer_id=X model="Raspberry Pi 4 Model B"
```
Create if not exists.
For generic x86:
```
dcim_list_manufacturers name=<detected-manufacturer>
```
#### 3.2 Create Device
```
dcim_create_device
name=<hostname>
device_type=<device_type_id>
role=<role_id>
site=<site_id>
platform=<platform_id>
tenant=<tenant_id> # if provided
serial=<serial>
description="Registered via cmdb-assistant"
```
#### 3.3 Create Interfaces
For each network interface discovered:
```
dcim_create_interface
device=<device_id>
name=<interface_name> # eth0, wlan0, tailscale0, etc.
type=<type> # 1000base-t, virtual, other
mac_address=<mac>
enabled=true
```
**Interface type mapping:**
- `eth*`, `enp*``1000base-t`
- `wlan*``ieee802.11ax` (or appropriate wifi type)
- `tailscale*`, `docker*`, `br-*``virtual`
- `lo` → skip (loopback)
#### 3.4 Create IP Addresses
For each IP on each interface:
```
ipam_create_ip_address
address=<ip/prefix> # e.g., "192.168.1.100/24"
assigned_object_type="dcim.interface"
assigned_object_id=<interface_id>
status="active"
description="Discovered via cmdb-register"
```
#### 3.5 Set Primary IP
Identify primary IP (interface with default route):
```
dcim_update_device
id=<device_id>
primary_ip4=<primary_ip_id>
```
### Phase 4: Container Registration (via MCP)
If Docker containers were discovered:
#### 4.1 Create/Get Cluster Type
```
virt_list_cluster_types name="Docker Compose"
```
Create if not exists:
```
virt_create_cluster_type name="Docker Compose" slug="docker-compose"
```
#### 4.2 Create Cluster
For each Docker Compose project directory found:
```
virt_create_cluster
name=<project-name> # e.g., "apps-hotport"
type=<cluster_type_id>
site=<site_id>
description="Docker Compose stack on <hostname>"
```
#### 4.3 Create VMs for Containers
For each running container:
```
virt_create_vm
name=<container_name>
cluster=<cluster_id>
site=<site_id>
role=<role_id> # Map container function to role
status="active"
vcpus=<cpu_shares> # Default 1.0 if unknown
memory=<memory_mb> # Default 256 if unknown
disk=<disk_gb> # Default 5 if unknown
description=<container purpose>
comments=<image, ports, volumes info>
```
**Container role mapping:**
- `*caddy*`, `*nginx*`, `*traefik*` → "Reverse Proxy"
- `*db*`, `*postgres*`, `*mysql*`, `*redis*` → "Database"
- `*webui*`, `*frontend*` → "Web Application"
- Others → Infer from image name or use generic "Container"
### Phase 5: Documentation
#### 5.1 Add Journal Entry
```
extras_create_journal_entry
assigned_object_type="dcim.device"
assigned_object_id=<device_id>
comments="Device registered via /cmdb-register command\n\nDiscovered:\n- X network interfaces\n- Y IP addresses\n- Z Docker containers"
```
### Phase 6: Summary Report
Present registration summary:
```markdown
## Machine Registration Complete
### Device Created
- **Name:** <hostname>
- **Site:** <site>
- **Platform:** <platform>
- **Role:** <role>
- **ID:** <device_id>
- **URL:** https://netbox.example.com/dcim/devices/<id>/
### Network Interfaces
| Interface | Type | MAC | IP Address |
|-----------|------|-----|------------|
| eth0 | 1000base-t | aa:bb:cc:dd:ee:ff | 192.168.1.100/24 |
| tailscale0 | virtual | - | 100.x.x.x/32 |
### Primary IP: 192.168.1.100
### Docker Containers Registered (if applicable)
**Cluster:** <cluster_name> (ID: <cluster_id>)
| Container | Role | vCPUs | Memory | Status |
|-----------|------|-------|--------|--------|
| media_jellyfin | Media Server | 2.0 | 2048MB | Active |
| media_sonarr | Media Management | 1.0 | 512MB | Active |
### Next Steps
- Run `/cmdb-sync` periodically to keep data current
- Run `/cmdb-audit` to check data quality
- Add tags for classification (env:*, team:*, etc.)
```
## Error Handling
- **Device already exists:** Suggest `/cmdb-sync` or ask to proceed
- **Site not found:** List available sites, offer to create new
- **Docker not available:** Skip container registration, note in summary
- **Permission denied:** Note which operations failed, suggest fixes
## User Request
$ARGUMENTS

View File

@@ -0,0 +1,336 @@
---
description: Synchronize current machine state with existing NetBox record
---
# CMDB Machine Sync
Update an existing NetBox device record with the current machine state. Compares local system information with NetBox and applies changes.
## Usage
```
/cmdb-sync [--full] [--dry-run]
```
**Options:**
- `--full`: Force refresh all fields, even unchanged ones
- `--dry-run`: Show what would change without applying updates
## Instructions
You are synchronizing the current machine's state with its NetBox record. This involves comparing current system state with stored data and updating differences.
**IMPORTANT:** Load the `netbox-patterns` skill for best practice reference.
### Phase 1: Device Lookup (via MCP)
First, find the existing device record:
```bash
# Get current hostname
hostname
```
```
dcim_list_devices name=<hostname>
```
**If device not found:**
- Inform user: "Device '<hostname>' not found in NetBox"
- Suggest: "Run `/cmdb-register` to register this machine first"
- Exit sync
**If device found:**
- Store device ID and all current field values
- Fetch interfaces: `dcim_list_interfaces device_id=<device_id>`
- Fetch IPs: `ipam_list_ip_addresses device_id=<device_id>`
Also check for associated clusters/VMs:
```
virt_list_clusters # Look for cluster associated with this device
virt_list_vms cluster=<cluster_id> # If cluster found
```
### Phase 2: Current State Discovery (via Bash)
Gather current system information (same as `/cmdb-register`):
```bash
# Device info
hostname
cat /etc/os-release 2>/dev/null || uname -a
nproc
free -m | awk '/Mem:/ {print $2}'
df -BG / | awk 'NR==2 {print $2}' | tr -d 'G'
# Network interfaces with IPs
ip -j addr show 2>/dev/null || ip addr show
# Docker containers
docker ps --format '{"name":"{{.Names}}","image":"{{.Image}}","status":"{{.Status}}"}' 2>/dev/null || echo "[]"
```
### Phase 3: Comparison
Compare discovered state with NetBox record:
#### 3.1 Device Attributes
| Field | Compare |
|-------|---------|
| Platform | OS version changed? |
| Status | Still active? |
| Serial | Match? |
| Description | Keep existing |
#### 3.2 Network Interfaces
| Change Type | Detection |
|-------------|-----------|
| New interface | Interface exists locally but not in NetBox |
| Removed interface | Interface in NetBox but not locally |
| Changed MAC | MAC address different |
| Interface type | Type mismatch |
#### 3.3 IP Addresses
| Change Type | Detection |
|-------------|-----------|
| New IP | IP exists locally but not in NetBox |
| Removed IP | IP in NetBox but not locally (on this device) |
| Primary IP changed | Default route interface changed |
#### 3.4 Docker Containers
| Change Type | Detection |
|-------------|-----------|
| New container | Container running locally but no VM in cluster |
| Stopped container | VM exists but container not running |
| Resource change | vCPUs/memory different (if trackable) |
### Phase 4: Diff Report
Present changes to user:
```markdown
## Sync Diff Report
**Device:** <hostname> (ID: <device_id>)
**NetBox URL:** https://netbox.example.com/dcim/devices/<id>/
### Device Attributes
| Field | NetBox Value | Current Value | Action |
|-------|--------------|---------------|--------|
| Platform | Ubuntu 22.04 | Ubuntu 24.04 | UPDATE |
| Status | active | active | - |
### Network Interfaces
#### New Interfaces (will create)
| Interface | Type | MAC | IPs |
|-----------|------|-----|-----|
| tailscale0 | virtual | - | 100.x.x.x/32 |
#### Removed Interfaces (will mark offline)
| Interface | Type | Reason |
|-----------|------|--------|
| eth1 | 1000base-t | Not found locally |
#### Changed Interfaces
| Interface | Field | Old | New |
|-----------|-------|-----|-----|
| eth0 | mac_address | aa:bb:cc:00:00:00 | aa:bb:cc:11:11:11 |
### IP Addresses
#### New IPs (will create)
- 192.168.1.150/24 on eth0
#### Removed IPs (will unassign)
- 192.168.1.100/24 from eth0
### Docker Containers
#### New Containers (will create VMs)
| Container | Image | Role |
|-----------|-------|------|
| media_lidarr | linuxserver/lidarr | Media Management |
#### Stopped Containers (will mark offline)
| Container | Last Status |
|-----------|-------------|
| media_bazarr | Exited |
### Summary
- **Updates:** X
- **Creates:** Y
- **Removals/Offline:** Z
```
### Phase 5: User Confirmation
If not `--dry-run`:
```
The following changes will be applied:
- Update device platform to "Ubuntu 24.04"
- Create interface "tailscale0"
- Create IP "100.x.x.x/32" on tailscale0
- Create VM "media_lidarr" in cluster
- Mark VM "media_bazarr" as offline
Proceed with sync? [Y/n]
```
**Use AskUserQuestion** to get confirmation.
### Phase 6: Apply Updates (via MCP)
Only if user confirms (or `--full` specified):
#### 6.1 Device Updates
```
dcim_update_device
id=<device_id>
platform=<new_platform_id>
# ... other changed fields
```
#### 6.2 Interface Updates
**For new interfaces:**
```
dcim_create_interface
device=<device_id>
name=<interface_name>
type=<type>
mac_address=<mac>
enabled=true
```
**For removed interfaces:**
```
dcim_update_interface
id=<interface_id>
enabled=false
description="Marked offline by cmdb-sync - interface no longer present"
```
**For changed interfaces:**
```
dcim_update_interface
id=<interface_id>
mac_address=<new_mac>
```
#### 6.3 IP Address Updates
**For new IPs:**
```
ipam_create_ip_address
address=<ip/prefix>
assigned_object_type="dcim.interface"
assigned_object_id=<interface_id>
status="active"
```
**For removed IPs:**
```
ipam_update_ip_address
id=<ip_id>
assigned_object_type=null
assigned_object_id=null
description="Unassigned by cmdb-sync"
```
#### 6.4 Primary IP Update
If primary IP changed:
```
dcim_update_device
id=<device_id>
primary_ip4=<new_primary_ip_id>
```
#### 6.5 Container/VM Updates
**For new containers:**
```
virt_create_vm
name=<container_name>
cluster=<cluster_id>
status="active"
# ... other fields
```
**For stopped containers:**
```
virt_update_vm
id=<vm_id>
status="offline"
description="Container stopped - detected by cmdb-sync"
```
### Phase 7: Journal Entry
Document the sync:
```
extras_create_journal_entry
assigned_object_type="dcim.device"
assigned_object_id=<device_id>
comments="Device synced via /cmdb-sync command\n\nChanges applied:\n- <list of changes>"
```
### Phase 8: Summary Report
```markdown
## Sync Complete
**Device:** <hostname>
**Sync Time:** <timestamp>
### Changes Applied
- Updated platform: Ubuntu 22.04 → Ubuntu 24.04
- Created interface: tailscale0 (ID: X)
- Created IP: 100.x.x.x/32 (ID: Y)
- Created VM: media_lidarr (ID: Z)
- Marked VM offline: media_bazarr (ID: W)
### Current State
- **Interfaces:** 4 (3 active, 1 offline)
- **IP Addresses:** 5
- **Containers/VMs:** 8 (7 active, 1 offline)
### Next Sync
Run `/cmdb-sync` again after:
- Adding/removing Docker containers
- Changing network configuration
- OS upgrades
```
## Dry Run Mode
If `--dry-run` specified:
- Complete Phase 1-4 (lookup, discovery, compare, diff report)
- Skip Phase 5-8 (no confirmation, no updates, no journal)
- End with: "Dry run complete. No changes applied. Run without --dry-run to apply."
## Full Sync Mode
If `--full` specified:
- Skip user confirmation
- Update all fields even if unchanged (force refresh)
- Useful for ensuring NetBox matches current state exactly
## Error Handling
- **Device not found:** Suggest `/cmdb-register`
- **Permission denied on updates:** Note which failed, continue with others
- **Cluster not found:** Offer to create or skip container sync
- **API errors:** Log error, continue with remaining updates
## User Request
$ARGUMENTS

View File

@@ -0,0 +1,21 @@
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/startup-check.sh"
}
],
"PreToolUse": [
{
"matcher": "mcp__plugin_cmdb-assistant_netbox__virt_create|mcp__plugin_cmdb-assistant_netbox__virt_update|mcp__plugin_cmdb-assistant_netbox__dcim_create|mcp__plugin_cmdb-assistant_netbox__dcim_update",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate-input.sh"
}
]
}
]
}
}

View File

@@ -0,0 +1,66 @@
#!/bin/bash
# cmdb-assistant SessionStart hook
# Tests NetBox API connectivity and checks for data quality issues
# All output MUST have [cmdb-assistant] prefix
# Non-blocking: always exits 0
set -euo pipefail
PREFIX="[cmdb-assistant]"
# Load NetBox configuration
NETBOX_CONFIG="$HOME/.config/claude/netbox.env"
if [[ ! -f "$NETBOX_CONFIG" ]]; then
echo "$PREFIX NetBox not configured - run /cmdb-assistant:initial-setup"
exit 0
fi
# Source config
source "$NETBOX_CONFIG"
# Validate required variables
if [[ -z "${NETBOX_API_URL:-}" ]] || [[ -z "${NETBOX_API_TOKEN:-}" ]]; then
echo "$PREFIX Missing NETBOX_API_URL or NETBOX_API_TOKEN in config"
exit 0
fi
# Quick API connectivity test (5s timeout)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -m 5 \
-H "Authorization: Token $NETBOX_API_TOKEN" \
-H "Accept: application/json" \
"${NETBOX_API_URL}/" 2>/dev/null || echo "000")
if [[ "$HTTP_CODE" == "000" ]]; then
echo "$PREFIX NetBox API unreachable (timeout/connection error)"
exit 0
elif [[ "$HTTP_CODE" != "200" ]]; then
echo "$PREFIX NetBox API returned HTTP $HTTP_CODE - check credentials"
exit 0
fi
# Check for VMs without site assignment (data quality)
VMS_RESPONSE=$(curl -s -m 5 \
-H "Authorization: Token $NETBOX_API_TOKEN" \
-H "Accept: application/json" \
"${NETBOX_API_URL}/virtualization/virtual-machines/?site__isnull=true&limit=1" 2>/dev/null || echo '{"count":0}')
VMS_NO_SITE=$(echo "$VMS_RESPONSE" | grep -o '"count":[0-9]*' | cut -d: -f2 || echo "0")
if [[ "$VMS_NO_SITE" -gt 0 ]]; then
echo "$PREFIX $VMS_NO_SITE VMs without site assignment - run /cmdb-audit for details"
fi
# Check for devices without platform
DEVICES_RESPONSE=$(curl -s -m 5 \
-H "Authorization: Token $NETBOX_API_TOKEN" \
-H "Accept: application/json" \
"${NETBOX_API_URL}/dcim/devices/?platform__isnull=true&limit=1" 2>/dev/null || echo '{"count":0}')
DEVICES_NO_PLATFORM=$(echo "$DEVICES_RESPONSE" | grep -o '"count":[0-9]*' | cut -d: -f2 || echo "0")
if [[ "$DEVICES_NO_PLATFORM" -gt 0 ]]; then
echo "$PREFIX $DEVICES_NO_PLATFORM devices without platform - consider updating"
fi
exit 0

View File

@@ -0,0 +1,79 @@
#!/bin/bash
# cmdb-assistant PreToolUse validation hook
# Validates input parameters for create/update operations
# NON-BLOCKING: Warns but allows operation to proceed (always exits 0)
set -euo pipefail
PREFIX="[cmdb-assistant]"
# Read tool input from stdin
INPUT=$(cat)
# Extract tool name from the input
# Format varies, try to find tool_name or name field
TOOL_NAME=""
if echo "$INPUT" | grep -q '"tool_name"'; then
TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/' || true)
elif echo "$INPUT" | grep -q '"name"'; then
TOOL_NAME=$(echo "$INPUT" | grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/' || true)
fi
# If we can't determine the tool, exit silently
if [[ -z "$TOOL_NAME" ]]; then
exit 0
fi
# VM creation/update validation
if echo "$TOOL_NAME" | grep -qE "virt_create_vm|virt_create_virtual_machine|virt_update_vm|virt_update_virtual_machine"; then
WARNINGS=()
# Check for missing site
if ! echo "$INPUT" | grep -qE '"site"[[:space:]]*:[[:space:]]*[0-9]'; then
WARNINGS+=("no site assigned")
fi
# Check for missing tenant
if ! echo "$INPUT" | grep -qE '"tenant"[[:space:]]*:[[:space:]]*[0-9]'; then
WARNINGS+=("no tenant assigned")
fi
# Check for missing platform
if ! echo "$INPUT" | grep -qE '"platform"[[:space:]]*:[[:space:]]*[0-9]'; then
WARNINGS+=("no platform assigned")
fi
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
echo "$PREFIX VM best practice: $(IFS=', '; echo "${WARNINGS[*]}") - consider assigning for data quality"
fi
fi
# Device creation/update validation
if echo "$TOOL_NAME" | grep -qE "dcim_create_device|dcim_update_device"; then
WARNINGS=()
# Check for missing platform
if ! echo "$INPUT" | grep -qE '"platform"[[:space:]]*:[[:space:]]*[0-9]'; then
WARNINGS+=("no platform assigned")
fi
# Check for missing tenant
if ! echo "$INPUT" | grep -qE '"tenant"[[:space:]]*:[[:space:]]*[0-9]'; then
WARNINGS+=("no tenant assigned")
fi
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
echo "$PREFIX Device best practice: $(IFS=', '; echo "${WARNINGS[*]}") - consider assigning"
fi
fi
# Cluster creation validation
if echo "$TOOL_NAME" | grep -qE "virt_create_cluster"; then
# Check for missing site scope
if ! echo "$INPUT" | grep -qE '"site"[[:space:]]*:[[:space:]]*[0-9]'; then
echo "$PREFIX Cluster best practice: no site scope - clusters should be scoped to a site"
fi
fi
# Always allow operation (non-blocking)
exit 0

View File

@@ -0,0 +1,249 @@
---
description: NetBox best practices for data quality and consistency based on official NetBox Labs guidelines
---
# NetBox Best Practices Skill
Reference documentation for proper NetBox data modeling, following official NetBox Labs guidelines.
## CRITICAL: Dependency Order
Objects must be created in this order due to foreign key dependencies. Creating objects out of order results in validation errors.
```
1. ORGANIZATION (no dependencies)
├── Tenant Groups
├── Tenants (optional: Tenant Group)
├── Regions
├── Site Groups
└── Tags
2. SITES AND LOCATIONS
├── Sites (optional: Region, Site Group, Tenant)
└── Locations (requires: Site, optional: parent Location)
3. DCIM PREREQUISITES
├── Manufacturers
├── Device Types (requires: Manufacturer)
├── Platforms
├── Device Roles
└── Rack Roles
4. RACKS
└── Racks (requires: Site, optional: Location, Rack Role, Tenant)
5. DEVICES
├── Devices (requires: Device Type, Role, Site; optional: Rack, Location)
└── Interfaces (requires: Device)
6. VIRTUALIZATION
├── Cluster Types
├── Cluster Groups
├── Clusters (requires: Cluster Type, optional: Site)
├── Virtual Machines (requires: Cluster OR Site)
└── VM Interfaces (requires: Virtual Machine)
7. IPAM
├── VRFs (optional: Tenant)
├── Prefixes (optional: VRF, Site, Tenant)
├── IP Addresses (optional: VRF, Tenant, Interface)
└── VLANs (optional: Site, Tenant)
8. CONNECTIONS (last)
└── Cables (requires: endpoints)
```
**Key Rule:** NEVER create a VM before its cluster exists. NEVER create a device before its site exists.
## HIGH: Site Assignment
**All infrastructure objects should have a site:**
| Object Type | Site Requirement |
|-------------|------------------|
| Devices | **REQUIRED** |
| Racks | **REQUIRED** |
| VMs | RECOMMENDED (via cluster or direct) |
| Clusters | RECOMMENDED |
| Prefixes | RECOMMENDED |
| VLANs | RECOMMENDED |
**Why Sites Matter:**
- Location-based queries and filtering
- Power and capacity budgeting
- Physical inventory tracking
- Compliance and audit requirements
## HIGH: Tenant Usage
Use tenants for logical resource separation:
**When to Use Tenants:**
- Multi-team environments (assign resources to teams)
- Multi-customer scenarios (MSP, hosting)
- Cost allocation requirements
- Access control boundaries
**Apply Tenants To:**
- Sites (who owns the physical location)
- Devices (who operates the hardware)
- VMs (who owns the workload)
- Prefixes (who owns the IP space)
- VLANs (who owns the network segment)
## HIGH: Platform Tracking
Platforms track OS/runtime information for automation and lifecycle management.
**Platform Examples:**
| Device Type | Platform Examples |
|-------------|-------------------|
| Servers | Ubuntu 24.04, Windows Server 2022, RHEL 9 |
| Network | Cisco IOS 17.x, Junos 23.x, Arista EOS |
| Raspberry Pi | Raspberry Pi OS (Bookworm), Ubuntu Server ARM |
| Containers | Docker Container (as runtime indicator) |
**Benefits:**
- Vulnerability tracking (CVE correlation)
- Configuration management integration
- Lifecycle management (EOL tracking)
- Automation targeting
## MEDIUM: Tag Conventions
Use tags for cross-cutting classification that spans object types.
**Recommended Tag Patterns:**
| Pattern | Purpose | Examples |
|---------|---------|----------|
| `env:*` | Environment classification | `env:production`, `env:staging`, `env:development` |
| `app:*` | Application grouping | `app:web`, `app:database`, `app:monitoring` |
| `team:*` | Ownership | `team:platform`, `team:infra`, `team:devops` |
| `backup:*` | Backup policy | `backup:daily`, `backup:weekly`, `backup:none` |
| `monitoring:*` | Monitoring level | `monitoring:critical`, `monitoring:standard` |
**Tags vs Custom Fields:**
- Tags: Cross-object classification, multiple values, filtering
- Custom Fields: Object-specific structured data, single values, reporting
## MEDIUM: Naming Conventions
Consistent naming improves searchability and automation compatibility.
**Recommended Patterns:**
| Object Type | Pattern | Examples |
|-------------|---------|----------|
| Devices | `{role}-{location}-{number}` | `web-dc1-01`, `db-cloud-02`, `fw-home-01` |
| VMs | `{env}-{app}-{number}` | `prod-api-01`, `dev-worker-03` |
| Clusters | `{site}-{type}` | `dc1-vmware`, `home-docker` |
| Prefixes | Include purpose in description | "Production web tier /24" |
| VLANs | `{site}-{function}` | `dc1-mgmt`, `home-iot` |
**Avoid:**
- Inconsistent casing (mixing `HotServ` and `hotserv`)
- Mixed separators (mixing `hhl_cluster` and `media-cluster`)
- Generic names without context (`server1`, `vm2`)
- Special characters other than hyphen
## MEDIUM: Role Consolidation
Avoid role fragmentation - use general roles with platform/tags for specificity.
**Instead of:**
```
nginx-web-server
apache-web-server
web-server-frontend
web-server-api
```
**Use:**
```
web-server (role) + platform (nginx/apache) + tags (frontend, api)
```
**Recommended Role Categories:**
| Category | Roles |
|----------|-------|
| Infrastructure | `hypervisor`, `storage-server`, `network-device`, `firewall` |
| Compute | `application-server`, `database-server`, `web-server`, `api-server` |
| Services | `container-host`, `load-balancer`, `monitoring-server`, `backup-server` |
| Development | `development-workstation`, `ci-runner`, `build-server` |
| Containers | `reverse-proxy`, `database`, `cache`, `queue`, `worker` |
## Docker Containers as VMs
NetBox's Virtualization module can model Docker containers:
**Approach:**
1. Create device for physical Docker host
2. Create cluster (type: "Docker Compose" or "Docker Swarm")
3. Associate cluster with host device
4. Create VMs for each container in the cluster
**VM Fields for Containers:**
- `name`: Container name (e.g., `media_jellyfin`)
- `role`: Container function (e.g., `Media Server`)
- `vcpus`: CPU limit/shares
- `memory`: Memory limit (MB)
- `disk`: Volume size estimate
- `description`: Container purpose
- `comments`: Image, ports, volumes, dependencies
**This is a pragmatic modeling choice** - containers aren't VMs, but the Virtualization module is the closest fit for tracking container workloads.
## Primary IP Workflow
To set a device/VM's primary IP:
1. Create interface on device/VM
2. Create IP address assigned to interface
3. Set IP as `primary_ip4` or `primary_ip6` on device/VM
**Why Primary IP Matters:**
- Used for device connectivity checks
- Displayed in device list views
- Used by automation tools (NAPALM, Ansible)
- Required for many integrations
## Data Quality Checklist
Before closing a sprint or audit:
- [ ] All VMs have site assignment (direct or via cluster)
- [ ] All VMs have tenant assignment
- [ ] All active devices have platform
- [ ] All active devices have primary IP
- [ ] Naming follows conventions
- [ ] No orphaned prefixes (allocated but unused)
- [ ] Tags applied consistently
- [ ] Clusters scoped to sites
- [ ] Roles not overly fragmented
## MCP Tool Reference
**Dependency Order for Creation:**
```
1. dcim_create_site
2. dcim_create_manufacturer
3. dcim_create_device_type
4. dcim_create_device_role
5. dcim_create_platform
6. dcim_create_device
7. virt_create_cluster_type
8. virt_create_cluster
9. virt_create_vm
10. dcim_create_interface / virt_create_vm_interface
11. ipam_create_ip_address
12. dcim_update_device (set primary_ip4)
```
**Lookup Before Create:**
Always check if object exists before creating to avoid duplicates:
```
1. dcim_list_devices name=<hostname>
2. If exists, update; if not, create
```

View File

@@ -2,9 +2,8 @@
"mcpServers": {
"contract-validator": {
"type": "stdio",
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/contract-validator/.venv/bin/python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/contract-validator"
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/contract-validator/run.sh",
"args": []
}
}
}

View File

@@ -40,6 +40,7 @@ pip install -r requirements.txt
| Command | Description |
|---------|-------------|
| `/initial-setup` | Interactive setup wizard |
| `/validate-contracts` | Full marketplace compatibility validation |
| `/check-agent` | Validate single agent definition |
| `/list-interfaces` | Show all plugin interfaces |

View File

@@ -0,0 +1,152 @@
---
description: Interactive setup wizard for contract-validator plugin - verifies MCP server and shows capabilities
---
# Contract-Validator Setup Wizard
This command sets up the contract-validator plugin for cross-plugin compatibility validation.
## Important Context
- **This command uses Bash, Read, Write, and AskUserQuestion tools** - NOT MCP tools
- **MCP tools won't work until after setup + session restart**
- **No external credentials required** - this plugin validates local files only
---
## Phase 1: Environment Validation
### Step 1.1: Check Python Version
```bash
python3 --version
```
Requires Python 3.10+. If below, stop setup and inform user:
```
Python 3.10 or higher is required. Please install it and run /initial-setup again.
```
---
## Phase 2: MCP Server Setup
### Step 2.1: Locate Contract-Validator MCP Server
```bash
# If running from installed marketplace
ls -la ~/.claude/plugins/marketplaces/leo-claude-mktplace/mcp-servers/contract-validator/ 2>/dev/null || echo "NOT_FOUND_INSTALLED"
# If running from source
ls -la ~/claude-plugins-work/mcp-servers/contract-validator/ 2>/dev/null || echo "NOT_FOUND_SOURCE"
```
Determine which path exists and use that as the MCP server path.
### Step 2.2: Check Virtual Environment
```bash
ls -la /path/to/mcp-servers/contract-validator/.venv/bin/python 2>/dev/null && echo "VENV_EXISTS" || echo "VENV_MISSING"
```
### Step 2.3: Create Virtual Environment (if missing)
```bash
cd /path/to/mcp-servers/contract-validator && python3 -m venv .venv && source .venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt && deactivate
```
**If pip install fails:**
- Show the error to the user
- Suggest: "Check your internet connection and try again."
---
## Phase 3: Validation
### Step 3.1: Verify MCP Server
```bash
cd /path/to/mcp-servers/contract-validator && .venv/bin/python -c "from mcp_server.server import ContractValidatorMCPServer; print('MCP Server OK')"
```
If this fails, check the error and report it to the user.
### Step 3.2: Summary
Display:
```
╔════════════════════════════════════════════════════════════════╗
║ CONTRACT-VALIDATOR SETUP COMPLETE ║
╠════════════════════════════════════════════════════════════════╣
║ MCP Server: ✓ Ready ║
║ Parse Tools: ✓ Available (2 tools) ║
║ Validation Tools: ✓ Available (3 tools) ║
║ Report Tools: ✓ Available (2 tools) ║
╚════════════════════════════════════════════════════════════════╝
```
### Step 3.3: Session Restart Notice
---
**Session Restart Required**
Restart your Claude Code session for MCP tools to become available.
**After restart, you can:**
- Run `/validate-contracts` to check all plugins for compatibility issues
- Run `/check-agent` to validate a single agent definition
- Run `/list-interfaces` to see all plugin commands and tools
---
## Available Tools
| Category | Tools | Description |
|----------|-------|-------------|
| Parse | `parse_plugin_interface`, `parse_claude_md_agents` | Extract interfaces from README.md and agents from CLAUDE.md |
| Validation | `validate_compatibility`, `validate_agent_refs`, `validate_data_flow` | Check conflicts, tool references, and data flows |
| Report | `generate_compatibility_report`, `list_issues` | Generate reports and filter issues |
---
## Available Commands
| Command | Description |
|---------|-------------|
| `/validate-contracts` | Full marketplace compatibility validation |
| `/check-agent` | Validate single agent definition |
| `/list-interfaces` | Show all plugin interfaces |
---
## Use Cases
### 1. Pre-Release Validation
Run `/validate-contracts` before releasing a new marketplace version to catch:
- Command name conflicts between plugins
- Missing tool references in agents
- Broken data flows
### 2. Agent Development
Run `/check-agent` when creating or modifying agents to verify:
- All referenced tools exist
- Data flows are valid
- No undeclared dependencies
### 3. Plugin Audit
Run `/list-interfaces` to get a complete view of:
- All commands across plugins
- All tools available
- Potential overlap areas
---
## No Configuration Required
This plugin doesn't require any configuration files. It reads plugin manifests and README files directly from the filesystem.
**Paths it scans:**
- Marketplace: `~/.claude/plugins/marketplaces/leo-claude-mktplace/plugins/`
- Source (if available): `~/claude-plugins-work/plugins/`

View File

@@ -2,9 +2,8 @@
"mcpServers": {
"data-platform": {
"type": "stdio",
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/data-platform/.venv/bin/python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/data-platform"
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/data-platform/run.sh",
"args": []
}
}
}

View File

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

View File

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

View File

@@ -5,13 +5,43 @@
PREFIX="[projman]"
# Check if MCP venv exists
# Calculate paths
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "$(realpath "$0")")")}"
VENV_PATH="$PLUGIN_ROOT/mcp-servers/gitea/.venv/bin/python"
# Marketplace root is 2 levels up from plugin root (plugins/projman -> .)
MARKETPLACE_ROOT="$(dirname "$(dirname "$PLUGIN_ROOT")")"
VENV_REPAIR_SCRIPT="$MARKETPLACE_ROOT/scripts/venv-repair.sh"
PLUGIN_CACHE="$HOME/.claude/plugins/cache/leo-claude-mktplace"
if [[ ! -f "$VENV_PATH" ]]; then
echo "$PREFIX MCP venvs missing - run setup.sh from installed marketplace"
exit 0
# ============================================================================
# Clear stale plugin cache (MUST run before MCP servers load)
# ============================================================================
# The cache at ~/.claude/plugins/cache/ holds versioned .mcp.json files.
# After marketplace updates, cached configs may point to old paths.
# Clearing forces Claude to read fresh configs from installed marketplace.
if [[ -d "$PLUGIN_CACHE" ]]; then
rm -rf "$PLUGIN_CACHE"
# Don't output anything - this should be silent and automatic
fi
# ============================================================================
# Auto-repair MCP venvs (runs before other checks)
# ============================================================================
if [[ -x "$VENV_REPAIR_SCRIPT" ]]; then
# Run venv repair - this creates symlinks to cached venvs
# Only outputs messages if something needed fixing
"$VENV_REPAIR_SCRIPT" 2>/dev/null || {
echo "$PREFIX MCP venv setup failed - run: cd $MARKETPLACE_ROOT && ./scripts/setup-venvs.sh"
exit 0
}
else
# Fallback: just check if venv exists
VENV_PATH="$PLUGIN_ROOT/mcp-servers/gitea/.venv/bin/python"
if [[ ! -f "$VENV_PATH" ]]; then
echo "$PREFIX MCP venvs missing - run setup.sh from installed marketplace"
exit 0
fi
fi
# Check git remote vs .env config (only if .env exists)

View File

@@ -2,9 +2,8 @@
"mcpServers": {
"viz-platform": {
"type": "stdio",
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/viz-platform/.venv/bin/python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/viz-platform"
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/viz-platform/run.sh",
"args": []
}
}
}

View File

@@ -1,17 +1,20 @@
#!/usr/bin/env bash
#
# post-update.sh - Run after pulling updates
# post-update.sh - Run after pulling updates or marketplace sync
#
# Usage: ./scripts/post-update.sh
#
# This script:
# 1. Updates Python dependencies for MCP servers
# 2. Validates configuration still works
# 3. Reports any new manual steps from CHANGELOG
# 1. Clears Claude plugin cache (forces fresh .mcp.json reads)
# 2. Restores MCP venv symlinks (instant if cache exists)
# 3. Creates venvs in external cache if missing (first run only)
# 4. Shows recent changelog updates
#
set -euo pipefail
CLAUDE_PLUGIN_CACHE="$HOME/.claude/plugins/cache/leo-claude-mktplace"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
@@ -19,42 +22,25 @@ REPO_ROOT="$(dirname "$SCRIPT_DIR")"
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
update_mcp_server() {
local server_name="$1"
local server_path="$REPO_ROOT/mcp-servers/$server_name"
log_info "Updating $server_name dependencies..."
if [[ -d "$server_path/.venv" ]] && [[ -f "$server_path/requirements.txt" ]]; then
cd "$server_path"
source .venv/bin/activate
pip install -q --upgrade pip
pip install -q -r requirements.txt
deactivate
cd "$REPO_ROOT"
log_success "$server_name dependencies updated"
else
log_warn "$server_name not fully set up - run ./scripts/setup.sh first"
fi
}
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
check_changelog() {
log_info "Checking CHANGELOG for recent updates..."
if [[ -f "$REPO_ROOT/CHANGELOG.md" ]]; then
# Show the Unreleased section
echo ""
echo "Recent changes (from CHANGELOG.md):"
echo "-----------------------------------"
sed -n '/## \[Unreleased\]/,/## \[/p' "$REPO_ROOT/CHANGELOG.md" | head -30
echo "-----------------------------------"
echo ""
local unreleased
unreleased=$(sed -n '/## \[Unreleased\]/,/## \[/p' "$REPO_ROOT/CHANGELOG.md" | grep -E '^### ' | head -1 || true)
if [[ -n "$unreleased" ]]; then
echo ""
log_info "Recent changes (from CHANGELOG.md):"
echo "-----------------------------------"
sed -n '/## \[Unreleased\]/,/## \[/p' "$REPO_ROOT/CHANGELOG.md" | head -20
echo "-----------------------------------"
fi
fi
}
@@ -64,23 +50,37 @@ main() {
echo "=============================================="
echo ""
# Shared MCP servers at repository root (v3.0.0+)
update_mcp_server "gitea"
update_mcp_server "netbox"
update_mcp_server "data-platform"
# Clear Claude plugin cache to force fresh .mcp.json reads
# This cache holds versioned copies that become stale after updates
if [[ -d "$CLAUDE_PLUGIN_CACHE" ]]; then
log_info "Clearing Claude plugin cache..."
rm -rf "$CLAUDE_PLUGIN_CACHE"
log_success "Plugin cache cleared"
fi
# Run venv-repair.sh to restore symlinks to external cache
# This is instant if cache exists, or does full setup on first run
if [[ -x "$SCRIPT_DIR/venv-repair.sh" ]]; then
log_info "Restoring MCP venv symlinks..."
if "$SCRIPT_DIR/venv-repair.sh"; then
log_success "MCP venvs ready"
else
log_error "MCP venv setup failed"
log_warn "Run: $SCRIPT_DIR/setup-venvs.sh for full setup"
exit 1
fi
else
log_error "venv-repair.sh not found at $SCRIPT_DIR"
exit 1
fi
check_changelog
echo ""
log_success "Post-update complete!"
echo ""
echo "If you see new features in the changelog that require"
echo "configuration changes, update your ~/.config/claude/*.env files."
echo "IMPORTANT: Restart Claude Code for changes to take effect."
echo "MCP servers will work immediately on next session start."
}
main "$@"
# Clear plugin cache to ensure fresh hooks are loaded
echo "Clearing plugin cache..."
rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/
echo "Cache cleared"

281
scripts/setup-venvs.sh Executable file
View File

@@ -0,0 +1,281 @@
#!/usr/bin/env bash
#
# setup-venvs.sh - Smart MCP server venv management with external cache
#
# This script manages Python virtual environments for MCP servers in a
# PERSISTENT location outside the marketplace directory, so they survive
# marketplace updates.
#
# Features:
# - Stores venvs in ~/.cache/claude-mcp-venvs/ (survives updates)
# - Incremental installs (only missing packages)
# - Hash-based change detection (skip if requirements unchanged)
# - Can be called from SessionStart hooks safely
#
# Usage:
# ./scripts/setup-venvs.sh # Full setup
# ./scripts/setup-venvs.sh --check # Check only, no install
# ./scripts/setup-venvs.sh --quick # Skip if hash unchanged
# ./scripts/setup-venvs.sh gitea # Setup specific server only
#
set -euo pipefail
# ============================================================================
# Configuration
# ============================================================================
# Persistent venv location (outside marketplace)
VENV_CACHE_DIR="${HOME}/.cache/claude-mcp-venvs/leo-claude-mktplace"
# Script and repo paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
# MCP servers to manage
MCP_SERVERS=(gitea netbox data-platform viz-platform contract-validator)
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Flags
CHECK_ONLY=false
QUICK_MODE=false
SPECIFIC_SERVER=""
# ============================================================================
# Argument Parsing
# ============================================================================
while [[ $# -gt 0 ]]; do
case $1 in
--check)
CHECK_ONLY=true
shift
;;
--quick)
QUICK_MODE=true
shift
;;
-h|--help)
echo "Usage: $0 [OPTIONS] [SERVER]"
echo ""
echo "Options:"
echo " --check Check venv status without installing"
echo " --quick Skip servers with unchanged requirements"
echo " -h,--help Show this help"
echo ""
echo "Servers: ${MCP_SERVERS[*]}"
exit 0
;;
*)
SPECIFIC_SERVER="$1"
shift
;;
esac
done
# ============================================================================
# Helper Functions
# ============================================================================
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
log_skip() { echo -e "${YELLOW}[SKIP]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
# Calculate hash of requirements file(s)
requirements_hash() {
local server_path="$1"
local hash_input=""
if [[ -f "$server_path/requirements.txt" ]]; then
hash_input+=$(cat "$server_path/requirements.txt")
fi
if [[ -f "$server_path/pyproject.toml" ]]; then
hash_input+=$(cat "$server_path/pyproject.toml")
fi
echo "$hash_input" | sha256sum | cut -d' ' -f1
}
# Check if requirements changed since last install
requirements_changed() {
local server_name="$1"
local server_path="$2"
local hash_file="$VENV_CACHE_DIR/$server_name/.requirements_hash"
local current_hash
current_hash=$(requirements_hash "$server_path")
if [[ -f "$hash_file" ]]; then
local stored_hash
stored_hash=$(cat "$hash_file")
if [[ "$current_hash" == "$stored_hash" ]]; then
return 1 # Not changed
fi
fi
return 0 # Changed or no hash file
}
# Save requirements hash after successful install
save_requirements_hash() {
local server_name="$1"
local server_path="$2"
local hash_file="$VENV_CACHE_DIR/$server_name/.requirements_hash"
requirements_hash "$server_path" > "$hash_file"
}
# ============================================================================
# Main Setup Function
# ============================================================================
setup_server() {
local server_name="$1"
local server_path="$REPO_ROOT/mcp-servers/$server_name"
local venv_path="$VENV_CACHE_DIR/$server_name/.venv"
# Verify server exists in repo
if [[ ! -d "$server_path" ]]; then
log_error "$server_name: source directory not found at $server_path"
return 1
fi
# Check-only mode
if [[ "$CHECK_ONLY" == true ]]; then
if [[ -f "$venv_path/bin/python" ]]; then
log_ok "$server_name: venv exists"
else
log_error "$server_name: venv MISSING"
return 1
fi
return 0
fi
# Quick mode: skip if requirements unchanged
if [[ "$QUICK_MODE" == true ]] && [[ -f "$venv_path/bin/python" ]]; then
if ! requirements_changed "$server_name" "$server_path"; then
log_skip "$server_name: requirements unchanged"
return 0
fi
fi
log_info "$server_name: setting up venv..."
# Create cache directory
mkdir -p "$VENV_CACHE_DIR/$server_name"
# Create venv if missing
if [[ ! -d "$venv_path" ]]; then
python3 -m venv "$venv_path"
log_ok "$server_name: venv created"
fi
# Activate and install
# shellcheck disable=SC1091
source "$venv_path/bin/activate"
# Upgrade pip quietly
pip install -q --upgrade pip
# Install requirements (incremental - pip handles already-installed)
if [[ -f "$server_path/requirements.txt" ]]; then
pip install -q -r "$server_path/requirements.txt"
fi
# Install local package in editable mode if pyproject.toml exists
if [[ -f "$server_path/pyproject.toml" ]]; then
pip install -q -e "$server_path"
log_ok "$server_name: package installed (editable)"
fi
deactivate
# Save hash for quick mode
save_requirements_hash "$server_name" "$server_path"
log_ok "$server_name: ready"
}
# ============================================================================
# Create Symlinks (for backward compatibility)
# ============================================================================
create_symlinks() {
log_info "Creating symlinks for backward compatibility..."
for server_name in "${MCP_SERVERS[@]}"; do
local server_path="$REPO_ROOT/mcp-servers/$server_name"
local venv_path="$VENV_CACHE_DIR/$server_name/.venv"
local link_path="$server_path/.venv"
# Skip if source doesn't exist
[[ ! -d "$server_path" ]] && continue
# Skip if venv not in cache
[[ ! -d "$venv_path" ]] && continue
# Remove existing venv or symlink
if [[ -L "$link_path" ]]; then
rm "$link_path"
elif [[ -d "$link_path" ]]; then
log_warn "$server_name: removing old venv directory (now using cache)"
rm -rf "$link_path"
fi
# Create symlink
ln -s "$venv_path" "$link_path"
log_ok "$server_name: symlink created"
done
}
# ============================================================================
# Main
# ============================================================================
main() {
echo "=============================================="
echo " MCP Server Venv Manager"
echo "=============================================="
echo "Cache: $VENV_CACHE_DIR"
echo ""
local failed=0
if [[ -n "$SPECIFIC_SERVER" ]]; then
# Setup specific server
if setup_server "$SPECIFIC_SERVER"; then
: # success
else
failed=1
fi
else
# Setup all servers
for server in "${MCP_SERVERS[@]}"; do
if ! setup_server "$server"; then
((failed++)) || true
fi
done
fi
# Create symlinks for backward compatibility
if [[ "$CHECK_ONLY" != true ]]; then
create_symlinks
fi
echo ""
if [[ $failed -eq 0 ]]; then
log_ok "All MCP servers ready"
else
log_error "$failed server(s) failed"
return 1
fi
}
main "$@"

View File

@@ -72,11 +72,16 @@ setup_shared_mcp() {
log_success "$server_name venv created"
fi
# Install/update dependencies
# Install/update dependencies and local package
if [[ -f "requirements.txt" ]]; then
source .venv/bin/activate
pip install -q --upgrade pip
pip install -q -r requirements.txt
# Install local package in editable mode (required for MCP server to work)
if [[ -f "pyproject.toml" ]]; then
pip install -q -e .
log_success "$server_name package installed (editable mode)"
fi
deactivate
log_success "$server_name dependencies installed"
else
@@ -125,6 +130,24 @@ verify_symlinks() {
log_error "data-platform symlink missing"
log_todo "Run: ln -s ../../../mcp-servers/data-platform plugins/data-platform/mcp-servers/data-platform"
fi
# Check viz-platform -> viz-platform symlink
local vizplatform_link="$REPO_ROOT/plugins/viz-platform/mcp-servers/viz-platform"
if [[ -L "$vizplatform_link" ]]; then
log_success "viz-platform symlink exists"
else
log_error "viz-platform symlink missing"
log_todo "Run: ln -s ../../../mcp-servers/viz-platform plugins/viz-platform/mcp-servers/viz-platform"
fi
# Check contract-validator -> contract-validator symlink
local contractvalidator_link="$REPO_ROOT/plugins/contract-validator/mcp-servers/contract-validator"
if [[ -L "$contractvalidator_link" ]]; then
log_success "contract-validator symlink exists"
else
log_error "contract-validator symlink missing"
log_todo "Run: ln -s ../../../mcp-servers/contract-validator plugins/contract-validator/mcp-servers/contract-validator"
fi
}
# --- Section 3: Config File Templates ---
@@ -301,7 +324,7 @@ print_report() {
# --- Main ---
main() {
echo "=============================================="
echo " Leo Claude Marketplace Setup (v3.0.0)"
echo " Leo Claude Marketplace Setup (v5.0.0)"
echo "=============================================="
echo ""
@@ -309,6 +332,8 @@ main() {
setup_shared_mcp "gitea"
setup_shared_mcp "netbox"
setup_shared_mcp "data-platform"
setup_shared_mcp "viz-platform"
setup_shared_mcp "contract-validator"
# Verify symlinks from plugins to shared MCP servers
verify_symlinks

169
scripts/venv-repair.sh Executable file
View File

@@ -0,0 +1,169 @@
#!/usr/bin/env bash
#
# venv-repair.sh - Fast MCP venv auto-repair for SessionStart hooks
#
# This script is designed to run at session start. It:
# 1. Checks if venvs exist in external cache (~/.cache/claude-mcp-venvs/)
# 2. Creates symlinks from marketplace to cache (instant operation)
# 3. Only runs pip install if cache is missing (first install)
#
# Output format: All messages prefixed with [mcp-venv] for hook display
#
# Usage:
# ./scripts/venv-repair.sh # Auto-repair (default)
# ./scripts/venv-repair.sh --silent # Silent mode (no output unless error)
#
set -euo pipefail
# ============================================================================
# Configuration
# ============================================================================
PREFIX="[mcp-venv]"
VENV_CACHE_DIR="${HOME}/.cache/claude-mcp-venvs/leo-claude-mktplace"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
# MCP servers
MCP_SERVERS=(gitea netbox data-platform viz-platform contract-validator)
# Parse args
SILENT=false
[[ "${1:-}" == "--silent" ]] && SILENT=true
log() {
[[ "$SILENT" == true ]] && return
echo "$PREFIX $1"
}
log_error() {
echo "$PREFIX ERROR: $1" >&2
}
# ============================================================================
# Check if all venvs exist in cache
# ============================================================================
cache_complete() {
for server in "${MCP_SERVERS[@]}"; do
local venv_python="$VENV_CACHE_DIR/$server/.venv/bin/python"
[[ ! -f "$venv_python" ]] && return 1
done
return 0
}
# ============================================================================
# Create symlinks from marketplace to cache
# ============================================================================
create_symlink() {
local server_name="$1"
local server_path="$REPO_ROOT/mcp-servers/$server_name"
local venv_cache="$VENV_CACHE_DIR/$server_name/.venv"
local venv_link="$server_path/.venv"
# Skip if server doesn't exist
[[ ! -d "$server_path" ]] && return 0
# Skip if cache doesn't exist
[[ ! -d "$venv_cache" ]] && return 1
# Already correct symlink?
if [[ -L "$venv_link" ]]; then
local target
target=$(readlink "$venv_link")
[[ "$target" == "$venv_cache" ]] && return 0
rm "$venv_link"
elif [[ -d "$venv_link" ]]; then
# Old venv directory exists - back it up or remove
rm -rf "$venv_link"
fi
# Create symlink
ln -s "$venv_cache" "$venv_link"
return 0
}
create_all_symlinks() {
local created=0
for server in "${MCP_SERVERS[@]}"; do
if create_symlink "$server"; then
((created++)) || true
fi
done
[[ $created -gt 0 ]] && log "Restored $created venv symlinks"
}
# ============================================================================
# Full setup (only if cache missing)
# ============================================================================
setup_server() {
local server_name="$1"
local server_path="$REPO_ROOT/mcp-servers/$server_name"
local venv_path="$VENV_CACHE_DIR/$server_name/.venv"
[[ ! -d "$server_path" ]] && return 0
mkdir -p "$VENV_CACHE_DIR/$server_name"
# Create venv
if [[ ! -d "$venv_path" ]]; then
python3 -m venv "$venv_path"
fi
# Install dependencies
# shellcheck disable=SC1091
source "$venv_path/bin/activate"
pip install -q --upgrade pip
if [[ -f "$server_path/requirements.txt" ]]; then
pip install -q -r "$server_path/requirements.txt"
fi
if [[ -f "$server_path/pyproject.toml" ]]; then
pip install -q -e "$server_path"
fi
deactivate
# Save hash for future quick checks
local hash_file="$VENV_CACHE_DIR/$server_name/.requirements_hash"
{
if [[ -f "$server_path/requirements.txt" ]]; then
cat "$server_path/requirements.txt"
fi
if [[ -f "$server_path/pyproject.toml" ]]; then
cat "$server_path/pyproject.toml"
fi
echo "" # Ensure non-empty input for sha256sum
} | sha256sum | cut -d' ' -f1 > "$hash_file"
}
full_setup() {
log "First run - setting up MCP venvs (this only happens once)..."
for server in "${MCP_SERVERS[@]}"; do
log " Setting up $server..."
setup_server "$server"
done
log "Setup complete. Future sessions will be instant."
}
# ============================================================================
# Main
# ============================================================================
main() {
# Fast path: cache exists, just ensure symlinks
if cache_complete; then
create_all_symlinks
exit 0
fi
# Slow path: need to create venvs (first install)
full_setup
create_all_symlinks
}
main "$@"

View File

@@ -23,7 +23,7 @@ if [ -d ~/.claude/plugins/cache/leo-claude-mktplace ]; then
fi
# Verify installed hooks are command type
for plugin in doc-guardian code-sentinel projman pr-review project-hygiene data-platform; do
for plugin in doc-guardian code-sentinel projman pr-review project-hygiene data-platform cmdb-assistant; do
HOOK_FILE=~/.claude/plugins/marketplaces/leo-claude-mktplace/plugins/$plugin/hooks/hooks.json
if [ -f "$HOOK_FILE" ]; then
if grep -q '"type": "command"' "$HOOK_FILE" || grep -q '"type":"command"' "$HOOK_FILE"; then