Extract tool definitions and dispatcher from server.py into tool_registry.py
to enable transport-agnostic reuse. External consumers (e.g., HTTP transport
in gitea-mcp-remote) can now import and use the Gitea MCP tools without
duplicating code.
Changes:
- Create pyproject.toml with PEP 621 compliant package manifest (hatchling)
- Create tool_registry.py with get_tool_definitions() and create_tool_dispatcher()
- Refactor server.py to use registry (1100 -> 93 lines)
- Update __init__.py with package exports and __version__
The tool_filter parameter enables selective tool exposure for remote servers.
Stdio transport behavior is unchanged - all 36 tools still work identically.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement wiki-based Request for Comments system for capturing,
reviewing, and tracking feature ideas through their lifecycle.
New commands:
- /rfc-create: Create RFC from conversation or clarified spec
- /rfc-list: List RFCs grouped by status
- /rfc-review: Submit Draft RFC for review
- /rfc-approve: Approve RFC for sprint planning
- /rfc-reject: Reject RFC with documented reason
RFC lifecycle: Draft → Review → Approved → Implementing → Implemented
Integration:
- /sprint-plan detects approved RFCs and offers selection
- /sprint-close updates RFC status on completion
- clarity-assist suggests /rfc-create for feature ideas
New MCP tool: allocate_rfc_number
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MCP library validates schema BEFORE call_tool handler runs, so
our _coerce_types function never gets a chance to convert strings.
Changed all integer fields to accept both types:
- issue_number, milestone_id, pr_number, depends_on, milestone, limit, position
This fixes: "Input validation error: '312' is not of type 'integer'"
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The _get_current_branch() method was running git commands from the
installed plugin directory instead of the user's project directory.
This caused incorrect branch detection (always seeing 'main' from
the marketplace repo instead of the user's actual branch).
Fix: Use CLAUDE_PROJECT_DIR environment variable to get the correct
project directory and pass it as cwd to subprocess.run().
Fixes#231
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
Fixes#160: update_wiki_page was renaming pages to "unnamed"
Root causes:
1. page_name wasn't URL-encoded, breaking pages with special chars like ':'
2. PATCH request was missing 'title' field, causing Gitea to use default name
Changes:
- Add URL encoding (urllib.parse.quote) to get_wiki_page, update_wiki_page, delete_wiki_page
- Add 'title': page_name to update_wiki_page payload to preserve page name
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes multiple issues from diagnostic #123:
1. create_label_smart type safety (labels.py)
- Add isinstance(result, dict) checks after API calls
- Return structured error dict if API returns unexpected type
- Prevents "list indices must be integers" crash
2. debug-report always uses curl with labels
- Remove MCP option - always use curl for marketplace issues
- Add label ID fetching step (Source/Diagnostic, Type/Bug)
- Include labels in curl POST payload
- Avoids branch protection restrictions on main branch
Fixes#123
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
create_label_smart now checks if label already exists before creating.
- Checks both org and repo labels
- Handles format variations (Type/Bug vs Type: Bug)
- Returns {skipped: true} if label already exists
- Prevents duplicate label creation errors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The suggest_labels tool accepted a repo parameter in the implementation
but didn't expose it in the MCP tool schema, causing it to always rely
on auto-detection which failed in some contexts.
Fixes#94
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The suggest_labels function now dynamically detects the label naming convention
used in the repository (slash format like Type/Bug or colon-space format like
Type: Bug) instead of hardcoding slash format.
Changes:
- Added _build_label_lookup() to parse and normalize label formats
- Added _find_label() to find actual labels from the lookup
- Updated suggest_labels() to accept optional repo parameter
- Labels are fetched first, then suggestions match actual names
- Supports Efforts/Effort normalization (handles singular/plural)
Fixes issue #73 sub-issue 3: label format mismatch
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The MCP server runs with cwd set to the plugin directory, not the
user's project directory. This caused git remote auto-detection to
fail because it was looking at the wrong directory.
Changes:
- Added _find_project_directory() method with multiple strategies:
1. CLAUDE_PROJECT_DIR environment variable
2. PWD environment variable (if it has .git or .env)
3. Current working directory (if it has .git or .env)
- Updated _detect_repo_from_git() to accept project_dir parameter
- Added 3 new tests for project directory detection
Fixes#70
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1. Repo Auto-Detection (config.py):
- Added _detect_repo_from_git() to parse git remote URL
- Supports SSH, SSH short, HTTPS, HTTP URL formats
- Falls back to git remote when GITEA_REPO not set
2. Organization Validation (gitea_client.py):
- Changed is_org_repo() to use /orgs/{owner} endpoint
- Added _is_organization() method for reliable org detection
- Fixes issue where owner.type was null in Gitea API
3. Tests:
- Added 6 tests for git URL parsing
- Added 3 tests for org detection
Fixes#64
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The get_labels tool was failing with 404 for user-owned repositories
because it always queried /api/v1/orgs/{owner}/labels, which only
works for organizations.
Changes:
- labels.py: Check is_org_repo() before fetching org labels
- gitea_client.py: Same fix in _resolve_label_ids()
- test_labels.py: Added tests for both org and user-owned repos
Fixes#61
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Claude Code only caches the plugin directory when installed from a
marketplace, not parent directories. This broke the shared mcp-servers/
architecture because relative paths like ../../mcp-servers/ resolved
to non-existent locations in the cache.
Changes:
- Move gitea and wikijs MCP servers into plugins/projman/mcp-servers/
- Move netbox MCP server into plugins/cmdb-assistant/mcp-servers/
- Update .mcp.json files to use ${CLAUDE_PLUGIN_ROOT}/mcp-servers/
- Update setup.sh to handle new bundled structure
- Add netbox.env config template to setup.sh
- Update CLAUDE.md and CANONICAL-PATHS.md documentation
This ensures plugins work correctly when installed and cached.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove separate GITEA_OWNER config, use owner/repo format everywhere
- Add _parse_repo() helper to extract owner and repo from combined string
- Update plugin.json schema: file -> source, author as object
- Remove redundant configuration section from cmdb-assistant plugin
- Simplify gitea_client.py by removing excessive docstrings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem:
- Gitea API expects label IDs (integers), not label names (strings)
- Previous implementation passed label names directly, causing 422 errors
Solution:
- Added _resolve_label_ids() method to convert names to IDs
- Fetches all labels (org + repo) and builds name->ID mapping
- Automatically resolves IDs before creating issues
Testing:
- Created test issue #4 with 4 labels (manual verification)
- Created test issue #5 with 11 labels (automated testing)
- All labels applied correctly in Gitea
Also updated:
- projman/skills/label-taxonomy/labels-reference.md with current taxonomy
- Status updated to "Synced with Gitea" (43 labels total)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>