initial setup: planning documents updated

This commit is contained in:
2025-11-06 08:29:41 -05:00
parent 817cc3d7bf
commit eebfd0e2f9
8 changed files with 4500 additions and 1709 deletions

View File

@@ -0,0 +1,325 @@
# DEFINITIVE ARCHITECTURE - FINAL CORRECT VERSION
## ⚠️ THIS IS THE ONLY CORRECT STRUCTURE ⚠️
If you see ANY other structure in ANY other document, **THIS ONE IS CORRECT**.
---
## Repository Structure (FINAL)
```
your-gitea/hyperhivelabs/claude-plugins/
├── .claude-plugin/
│ └── marketplace.json
├── mcp-servers/ # ← SHARED BY BOTH PLUGINS
│ ├── gitea/
│ │ ├── .venv/
│ │ ├── requirements.txt
│ │ ├── mcp_server/
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── config.py
│ │ │ ├── gitea_client.py
│ │ │ └── tools/
│ │ │ ├── __init__.py
│ │ │ ├── issues.py
│ │ │ └── labels.py
│ │ └── tests/
│ │ ├── test_config.py
│ │ ├── test_gitea_client.py
│ │ └── test_tools.py
│ └── wikijs/
│ ├── .venv/
│ ├── requirements.txt
│ ├── mcp_server/
│ │ ├── __init__.py
│ │ ├── server.py
│ │ ├── config.py
│ │ ├── wikijs_client.py
│ │ └── tools/
│ │ ├── __init__.py
│ │ ├── pages.py
│ │ ├── lessons_learned.py
│ │ └── documentation.py
│ └── tests/
│ ├── test_config.py
│ ├── test_wikijs_client.py
│ └── test_tools.py
├── projman/ # ← PROJECT PLUGIN
│ ├── .claude-plugin/
│ │ └── plugin.json
│ ├── .mcp.json # Points to ../mcp-servers/
│ ├── commands/
│ │ ├── sprint-plan.md
│ │ ├── sprint-start.md
│ │ ├── sprint-status.md
│ │ ├── sprint-close.md
│ │ └── labels-sync.md
│ ├── agents/
│ │ ├── planner.md
│ │ ├── orchestrator.md
│ │ └── executor.md
│ ├── skills/
│ │ └── label-taxonomy/
│ │ └── labels-reference.md
│ ├── README.md
│ └── CONFIGURATION.md
└── projman-pmo/ # ← PMO PLUGIN
├── .claude-plugin/
│ └── plugin.json
├── .mcp.json # Points to ../mcp-servers/
├── commands/
│ ├── pmo-status.md
│ ├── pmo-priorities.md
│ ├── pmo-dependencies.md
│ └── pmo-schedule.md
├── agents/
│ └── pmo-coordinator.md
└── README.md
```
---
## Key Points
### 1. MCP Servers Are SHARED
- Location: `mcp-servers/` at repository root
- NOT inside `projman/` or `projman-pmo/`
- Built ONCE, used by BOTH plugins
### 2. Plugins Reference MCP Servers
- Both plugins use `.mcp.json` to point to `../mcp-servers/`
- No MCP code inside plugin directories
- Only commands, agents, and skills in plugin directories
### 3. Mode Detection
- MCP servers detect mode based on environment variables
- Project mode: When `GITEA_REPO` and `WIKIJS_PROJECT` present
- Company mode: When those variables absent (PMO)
---
## Configuration Files
### projman/.mcp.json
```json
{
"mcpServers": {
"gitea-projman": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"GITEA_API_URL": "${GITEA_API_URL}",
"GITEA_API_TOKEN": "${GITEA_API_TOKEN}",
"GITEA_OWNER": "${GITEA_OWNER}",
"GITEA_REPO": "${GITEA_REPO}"
}
},
"wikijs-projman": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"WIKIJS_API_URL": "${WIKIJS_API_URL}",
"WIKIJS_API_TOKEN": "${WIKIJS_API_TOKEN}",
"WIKIJS_BASE_PATH": "${WIKIJS_BASE_PATH}",
"WIKIJS_PROJECT": "${WIKIJS_PROJECT}"
}
}
}
}
```
### projman-pmo/.mcp.json
```json
{
"mcpServers": {
"gitea-pmo": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"GITEA_API_URL": "${GITEA_API_URL}",
"GITEA_API_TOKEN": "${GITEA_API_TOKEN}",
"GITEA_OWNER": "${GITEA_OWNER}"
}
},
"wikijs-pmo": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"WIKIJS_API_URL": "${WIKIJS_API_URL}",
"WIKIJS_API_TOKEN": "${WIKIJS_API_TOKEN}",
"WIKIJS_BASE_PATH": "${WIKIJS_BASE_PATH}"
}
}
}
}
```
**Critical:** Both plugins point to `../mcp-servers/` using relative paths.
---
## Setup Instructions
### 1. System Configuration
```bash
# Create config directory
mkdir -p ~/.config/claude
# Gitea config
cat > ~/.config/claude/gitea.env << EOF
GITEA_API_URL=https://gitea.hyperhivelabs.com/api/v1
GITEA_API_TOKEN=your_token
GITEA_OWNER=hyperhivelabs
EOF
# Wiki.js config
cat > ~/.config/claude/wikijs.env << EOF
WIKIJS_API_URL=https://wiki.hyperhivelabs.com/graphql
WIKIJS_API_TOKEN=your_token
WIKIJS_BASE_PATH=/hyper-hive-labs
EOF
# Secure files
chmod 600 ~/.config/claude/*.env
```
### 2. Project Configuration
```bash
# In each project root
cat > .env << EOF
GITEA_REPO=cuisineflow
WIKIJS_PROJECT=projects/cuisineflow
EOF
# Add to .gitignore
echo ".env" >> .gitignore
```
### 3. Install MCP Servers (ONE TIME)
```bash
# Gitea MCP Server
cd /path/to/claude-plugins/mcp-servers/gitea
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Wiki.js MCP Server
cd /path/to/claude-plugins/mcp-servers/wikijs
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
---
## What Makes This Work
### Mode Detection in config.py
```python
# mcp-servers/gitea/mcp_server/config.py
def load(self):
# ... load configs ...
self.repo = os.getenv('GITEA_REPO') # Optional
if self.repo:
self.mode = 'project'
logger.info(f"Running in project mode: {self.repo}")
else:
self.mode = 'company'
logger.info("Running in company-wide mode (PMO)")
return {
'api_url': self.api_url,
'api_token': self.api_token,
'owner': self.owner,
'repo': self.repo,
'mode': self.mode
}
```
### Same MCP Code, Different Behavior
The SAME MCP server code runs differently based on environment variables:
**When projman calls it:**
- Has `GITEA_REPO` → operates on single repository
- Has `WIKIJS_PROJECT` → operates on single project path
**When projman-pmo calls it:**
- No `GITEA_REPO` → operates on all repositories
- No `WIKIJS_PROJECT` → operates on entire company namespace
---
## Visual Path Flow
### projman Plugin Flow
```
projman/.mcp.json
↓ (cwd: ../mcp-servers/gitea)
mcp-servers/gitea/mcp_server/server.py
↓ (loads config)
mcp-servers/gitea/mcp_server/config.py
↓ (detects GITEA_REPO present)
→ PROJECT MODE
```
### projman-pmo Plugin Flow
```
projman-pmo/.mcp.json
↓ (cwd: ../mcp-servers/gitea)
mcp-servers/gitea/mcp_server/server.py
↓ (loads config)
mcp-servers/gitea/mcp_server/config.py
↓ (detects NO GITEA_REPO)
→ COMPANY MODE
```
---
## File Paths Quick Reference
### Gitea MCP Server Files
- Config loader: `mcp-servers/gitea/mcp_server/config.py`
- API client: `mcp-servers/gitea/mcp_server/gitea_client.py`
- Server entry: `mcp-servers/gitea/mcp_server/server.py`
- Issue tools: `mcp-servers/gitea/mcp_server/tools/issues.py`
- Label tools: `mcp-servers/gitea/mcp_server/tools/labels.py`
### Wiki.js MCP Server Files
- Config loader: `mcp-servers/wikijs/mcp_server/config.py`
- API client: `mcp-servers/wikijs/mcp_server/wikijs_client.py`
- Server entry: `mcp-servers/wikijs/mcp_server/server.py`
- Page tools: `mcp-servers/wikijs/mcp_server/tools/pages.py`
- Lessons tools: `mcp-servers/wikijs/mcp_server/tools/lessons_learned.py`
### Plugin Files
- projman config: `projman/.mcp.json`
- projman-pmo config: `projman-pmo/.mcp.json`
---
## This Is The Truth
**If ANY other document shows MCP servers inside plugin directories, that document is WRONG.**
**THIS document shows the CORRECT, FINAL architecture.**
Use this as your reference. Period.

View File

@@ -0,0 +1,241 @@
# ProjMan Implementation - Document Index
All documentation for building the projman and projman-pmo plugins.
---
## ⚠️ START HERE - CORRECT ARCHITECTURE
### [CORRECT-ARCHITECTURE.md](./CORRECT-ARCHITECTURE.md)
**⚠️ THIS IS THE DEFINITIVE REFERENCE ⚠️**
**Use when:** You need to verify the correct repository structure
**Contains:**
- THE ONLY CORRECT repository structure
- MCP servers are SHARED at root level (`mcp-servers/` directory)
- Configuration file examples
- Setup instructions
- Path references
- Mode detection implementation
**If any other document conflicts with this, THIS ONE IS CORRECT.**
---
## 📚 Core Implementation Documents
### [projman-implementation-plan-updated.md](./projman-implementation-plan-updated.md)
**Purpose:** Complete, detailed implementation plan
**Use when:** Actually building the plugins (your main reference)
**Contains:**
- 12 detailed implementation phases
- Configuration architecture
- Complete code examples
- Success criteria per phase
- Testing strategies
- No timelines - work at your pace
- **Length:** Comprehensive (2000+ lines)
---
## 🐍 Python-Specific Guides
### [projman-python-quickstart.md](./projman-python-quickstart.md)
**Purpose:** Python-specific implementation guide
**Use when:** Setting up Python environment, writing code
**Contains:**
- Python project structure
- Virtual environment setup
- Requirements.txt examples
- Configuration loader code
- Modular code patterns
- Testing with pytest
- Debugging tips
---
## 🏗️ Architecture Documentation
### [two-mcp-architecture-guide.md](./two-mcp-architecture-guide.md)
**Purpose:** Deep dive into two-MCP-server architecture
**Use when:** Understanding the MCP server design
**Contains:**
- Wiki.js structure at `/hyper-hive-labs`
- Complete Gitea MCP server code
- Complete Wiki.js MCP server code (GraphQL)
- Configuration examples
- Mode detection implementation
- Setup instructions
- Migration guidance
---
## 🎯 How to Use These Documents
### Phase 1: Planning & Setup
1. Read **CORRECT-ARCHITECTURE.md** to understand the definitive repository structure
2. Review **projman-implementation-plan-updated.md** Phase 1 for setup overview
3. Set up your Gitea and Wiki.js instances
4. Create system-level configuration files
### Phase 2: Starting Implementation
1. Open **projman-implementation-plan-updated.md** (your main reference for all 12 phases)
2. Start with Phase 1.1a (Gitea MCP Server)
3. Reference **projman-python-quickstart.md** for Python patterns and virtual environment setup
4. Reference **two-mcp-architecture-guide.md** for detailed MCP server code examples
### Phase 3: During Development
1. **Main reference:** projman-implementation-plan-updated.md (follow phase by phase)
2. **Structure reference:** CORRECT-ARCHITECTURE.md (when in doubt about paths)
3. **Code patterns:** projman-python-quickstart.md
4. **Architecture deep dive:** two-mcp-architecture-guide.md
### Phase 4: Troubleshooting
1. Check **CORRECT-ARCHITECTURE.md** for definitive path references
2. Review configuration examples in **two-mcp-architecture-guide.md**
3. Check Python-specific debugging in **projman-python-quickstart.md**
4. Verify setup instructions in **projman-implementation-plan-updated.md** Phase 1.3
---
## 📖 Document Relationships
```
CORRECT-ARCHITECTURE.md (definitive structure)
↓ (referenced by)
├── projman-implementation-plan-updated.md (main implementation guide)
│ ↓ (uses Python patterns from)
│ ├── projman-python-quickstart.md
│ ↓ (references architecture from)
│ └── two-mcp-architecture-guide.md
└── DOCUMENT-INDEX.md (this file - navigation)
```
---
## 🎨 Quick Reference by Topic
### Repository Structure
- **Definitive reference:** CORRECT-ARCHITECTURE.md (lines 9-80)
- **Key point:** MCP servers are SHARED at `mcp-servers/` directory (not inside plugins)
### Configuration
- **Setup instructions:** CORRECT-ARCHITECTURE.md (lines 172-229)
- **Implementation details:** projman-implementation-plan-updated.md (Phase 1.3)
- **Python code examples:** projman-python-quickstart.md (lines 140-214)
- **Config loader:** two-mcp-architecture-guide.md (lines 281-358)
### MCP Servers
- **Architecture overview:** CORRECT-ARCHITECTURE.md (Key Points section)
- **Gitea MCP:** projman-implementation-plan-updated.md (Phase 1.1a)
- **Wiki.js MCP:** projman-implementation-plan-updated.md (Phase 1.1b)
- **Complete implementation:** two-mcp-architecture-guide.md (lines 277-680)
### Wiki.js Structure
- **Full structure:** two-mcp-architecture-guide.md (lines 13-70)
- **Path resolution:** projman-implementation-plan-updated.md (lines 110-115)
- **Integration:** projman-implementation-plan-updated.md (Phase 4.1)
### Python Patterns
- **Setup & dependencies:** projman-python-quickstart.md (lines 15-111)
- **Modular code structure:** projman-python-quickstart.md (lines 511-575)
- **Virtual environment:** projman-python-quickstart.md (lines 579-616)
### Sprint Workflow
- **Commands:** projman-implementation-plan-updated.md (Phase 2)
- **Agents:** projman-implementation-plan-updated.md (Phase 3)
- **Lessons Learned:** projman-implementation-plan-updated.md (Phase 4)
### PMO Plugin
- **Requirements:** projman-implementation-plan-updated.md (Phase 9)
- **Implementation:** projman-implementation-plan-updated.md (Phase 10-11)
- **Multi-project methods:** two-mcp-architecture-guide.md (lines 639-679)
---
## 🚀 Suggested Reading Order
### First Time (Understanding the Project)
1. **CORRECT-ARCHITECTURE.md** (15 minutes)
- Understand the definitive repository structure
- See MCP server placement (shared at root)
- Review configuration approach
2. **projman-python-quickstart.md** (30 minutes)
- Understand Python setup
- See code patterns
- Virtual environment setup
3. **projman-implementation-plan-updated.md** (2-3 hours)
- Read Phase 1 in detail
- Skim Phases 2-12 to understand the flow
- This is your main implementation guide
4. **two-mcp-architecture-guide.md** (1 hour)
- Deep dive into MCP server architecture
- Complete code examples
- Wiki.js structure and integration
### During Implementation
- Keep **projman-implementation-plan-updated.md** open (your main reference)
- Reference **CORRECT-ARCHITECTURE.md** when unsure about paths
- Use **projman-python-quickstart.md** for Python-specific code
- Use **two-mcp-architecture-guide.md** for detailed MCP implementation
### When You Need Quick Answers
- **"What's the correct repository structure?"** → CORRECT-ARCHITECTURE.md
- **"How do I set up Python?"** → projman-python-quickstart.md
- **"How does configuration work?"** → CORRECT-ARCHITECTURE.md or two-mcp-architecture-guide.md
- **"What's the full MCP server code?"** → two-mcp-architecture-guide.md
- **"What do I build in Phase X?"** → projman-implementation-plan-updated.md
---
## 📊 Document Statistics
| Document | Lines | Focus | Primary Use |
|----------|-------|-------|-------------|
| CORRECT-ARCHITECTURE.md | 325 | Definitive Structure | Reference (paths, config) |
| projman-implementation-plan-updated.md | 2081 | Complete Implementation | Main guide (building) |
| projman-python-quickstart.md | 727 | Python Patterns | Code patterns & setup |
| two-mcp-architecture-guide.md | 941 | Architecture Deep Dive | MCP implementation |
**Total:** ~4,074 lines of comprehensive documentation
---
## ✅ Pre-Implementation Checklist
Before starting Phase 1, verify you have:
- [ ] Read CORRECT-ARCHITECTURE.md (understand structure)
- [ ] Understand the two-MCP-server architecture (Gitea + Wiki.js)
- [ ] Understand shared MCP codebase at `mcp-servers/` (not in plugin dirs)
- [ ] Understand Wiki.js structure at `/hyper-hive-labs`
- [ ] Understand hybrid configuration (system + project levels)
- [ ] Python 3.11+ installed
- [ ] Access to Gitea instance
- [ ] Access to Wiki.js instance
- [ ] API tokens for both services
---
## 🎯 Key Architectural Decisions
These are the final decisions documented across all files:
1. **Two MCP Servers** - Separate Gitea and Wiki.js servers for better maintainability
2. **Shared MCP Codebase** - Located at `mcp-servers/` (root level), used by both plugins
3. **Python Implementation** - MCP servers written in Python 3.11+
4. **Hybrid Configuration** - System-level tokens + project-level paths
5. **Wiki.js for Lessons** - Superior to Git-based Wiki for documentation and search
6. **Mode Detection** - MCP servers detect project vs company-wide mode via environment variables
7. **Build Order** - projman first (Phases 1-8), then projman-pmo (Phases 9-12)
---
## 🎉 You're Ready!
You have everything you need to build the projman and projman-pmo plugins. All architectural decisions are finalized and documented.
**Start here:** [projman-implementation-plan-updated.md](./projman-implementation-plan-updated.md) - Phase 1.1a
Good luck with the build!

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,729 @@
# ProjMan Plugins - Python Quick Start
This guide provides Python-specific setup and development information for the projman and projman-pmo plugins.
> **⚠️ IMPORTANT:** For the definitive repository structure, refer to [CORRECT-ARCHITECTURE.md](./CORRECT-ARCHITECTURE.md). This guide shows Python-specific patterns and setup.
---
## Technology Stack
- **MCP Server:** Python 3.11+
- **Commands:** Markdown files
- **Agents:** Markdown files
- **Dependencies:** pip with requirements.txt
- **Virtual Environment:** .venv (per plugin)
---
## Initial Setup
### 1. System Requirements
```bash
# Python 3.11 or higher
python --version
# pip (latest)
pip --version
# git
git --version
```
### 2. System-Level Configuration
```bash
# Create config directory
mkdir -p ~/.config/claude
# Create gitea.env with your credentials
cat > ~/.config/claude/gitea.env << EOF
GITEA_API_URL=https://gitea.hyperhivelabs.com/api/v1
GITEA_API_TOKEN=your_token_here
GITEA_OWNER=hyperhivelabs
EOF
# Secure the file
chmod 600 ~/.config/claude/gitea.env
```
### 3. Project-Level Configuration
```bash
# In each repository root
echo "GITEA_REPO=cuisineflow" > .env
# Add to .gitignore
echo ".env" >> .gitignore
```
---
## MCP Server Structure
```
hyperhivelabs/claude-plugins/
├── mcp-servers/ # SHARED by both plugins
│ ├── gitea/
│ │ ├── .venv/
│ │ ├── requirements.txt
│ │ ├── mcp_server/
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── config.py
│ │ │ ├── gitea_client.py
│ │ │ └── tools/
│ │ └── tests/
│ └── wikijs/
│ ├── .venv/
│ ├── requirements.txt
│ ├── mcp_server/
│ │ ├── __init__.py
│ │ ├── server.py
│ │ ├── config.py
│ │ └── wikijs_client.py
│ └── tests/
├── projman/
│ ├── .mcp.json # Points to ../mcp-servers/
│ ├── commands/
│ └── agents/
└── projman-pmo/
├── .mcp.json # Points to ../mcp-servers/
└── commands/
```
---
## Dependencies (requirements.txt)
```txt
# anthropic-sdk==0.18.0 # MCP SDK
anthropic-sdk>=0.18.0
# python-dotenv==1.0.0 # Environment variable loading
python-dotenv>=1.0.0
# requests==2.31.0 # HTTP client for Gitea API
requests>=2.31.0
# pydantic==2.5.0 # Data validation
pydantic>=2.5.0
# pytest==7.4.3 # Testing framework
pytest>=7.4.3
# pytest-asyncio==0.23.0 # Async testing support
pytest-asyncio>=0.23.0
```
**Note:** Following your coding preferences, library versions are specified with comments showing the exact version being used.
---
## Development Workflow
### Initial MCP Server Setup
```bash
# Navigate to MCP servers directory
cd /path/to/claude-plugins/mcp-servers/gitea
# Create virtual environment
python -m venv .venv
# Activate virtual environment
source .venv/bin/activate # Linux/Mac
# or
.venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Verify installation
python -c "import anthropic; print('SDK installed')"
```
### Configuration Loader (config.py)
```python
# mcp-servers/gitea/mcp_server/config.py
from pathlib import Path
from dotenv import load_dotenv
import os
from typing import Dict, Optional
class Config:
"""Hybrid configuration loader for projman plugins"""
def __init__(self):
self.api_url: Optional[str] = None
self.api_token: Optional[str] = None
self.owner: Optional[str] = None
self.repo: Optional[str] = None
def load(self) -> Dict[str, str]:
"""
Load configuration from system and project levels.
Project-level configuration overrides system-level.
"""
# Load system config
system_config = Path.home() / '.config' / 'claude' / 'gitea.env'
if system_config.exists():
load_dotenv(system_config)
else:
raise FileNotFoundError(
f"System config not found: {system_config}\n"
"Create it with: mkdir -p ~/.config/claude && "
"cat > ~/.config/claude/gitea.env"
)
# Load project config (overrides system)
project_config = Path.cwd() / '.env'
if project_config.exists():
load_dotenv(project_config, override=True)
# Extract values
self.api_url = os.getenv('GITEA_API_URL')
self.api_token = os.getenv('GITEA_API_TOKEN')
self.owner = os.getenv('GITEA_OWNER')
self.repo = os.getenv('GITEA_REPO') # Optional for PMO
# Validate required variables
self._validate()
return {
'api_url': self.api_url,
'api_token': self.api_token,
'owner': self.owner,
'repo': self.repo
}
def _validate(self) -> None:
"""Validate that required configuration is present"""
required = {
'GITEA_API_URL': self.api_url,
'GITEA_API_TOKEN': self.api_token,
'GITEA_OWNER': self.owner
}
missing = [key for key, value in required.items() if not value]
if missing:
raise ValueError(
f"Missing required configuration: {', '.join(missing)}\n"
"Check your ~/.config/claude/gitea.env file"
)
# Usage
config = Config()
config_dict = config.load()
```
### Gitea API Client (gitea_client.py)
```python
# mcp-servers/gitea/mcp_server/gitea_client.py
import requests
from typing import List, Dict, Optional
from .config import Config
class GiteaClient:
"""Client for interacting with Gitea API"""
def __init__(self):
config = Config()
config_dict = config.load()
self.base_url = config_dict['api_url']
self.token = config_dict['api_token']
self.owner = config_dict['owner']
self.repo = config_dict.get('repo') # Optional
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'token {self.token}',
'Content-Type': 'application/json'
})
def list_issues(
self,
state: str = 'open',
labels: Optional[List[str]] = None,
repo: Optional[str] = None
) -> List[Dict]:
"""
List issues from Gitea repository.
Args:
state: Issue state (open, closed, all)
labels: Filter by labels
repo: Override configured repo (for PMO multi-repo)
"""
target_repo = repo or self.repo
if not target_repo:
raise ValueError("Repository not specified")
url = f"{self.base_url}/repos/{self.owner}/{target_repo}/issues"
params = {'state': state}
if labels:
params['labels'] = ','.join(labels)
response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()
def create_issue(
self,
title: str,
body: str,
labels: Optional[List[str]] = None,
repo: Optional[str] = None
) -> Dict:
"""Create a new issue in Gitea"""
target_repo = repo or self.repo
if not target_repo:
raise ValueError("Repository not specified")
url = f"{self.base_url}/repos/{self.owner}/{target_repo}/issues"
data = {
'title': title,
'body': body
}
if labels:
data['labels'] = labels
response = self.session.post(url, json=data)
response.raise_for_status()
return response.json()
def get_labels(
self,
repo: Optional[str] = None
) -> List[Dict]:
"""Get all labels from repository"""
target_repo = repo or self.repo
if not target_repo:
raise ValueError("Repository not specified")
url = f"{self.base_url}/repos/{self.owner}/{target_repo}/labels"
response = self.session.get(url)
response.raise_for_status()
return response.json()
```
### MCP Server Entry Point (server.py)
```python
# mcp-servers/gitea/mcp_server/server.py
from anthropic import Anthropic
from .gitea_client import GiteaClient
from .tools import IssueTools, LabelTools, WikiTools
class ProjManMCPServer:
"""Main MCP server for projman plugin"""
def __init__(self):
self.gitea = GiteaClient()
self.issue_tools = IssueTools(self.gitea)
self.label_tools = LabelTools(self.gitea)
self.wiki_tools = WikiTools(self.gitea)
def register_tools(self):
"""Register all available MCP tools"""
return [
# Issue tools
self.issue_tools.list_issues,
self.issue_tools.get_issue,
self.issue_tools.create_issue,
self.issue_tools.update_issue,
self.issue_tools.add_comment,
# Label tools
self.label_tools.get_labels,
self.label_tools.suggest_labels,
# Wiki tools
self.wiki_tools.search_wiki,
self.wiki_tools.get_wiki_page,
self.wiki_tools.create_wiki_page
]
if __name__ == '__main__':
server = ProjManMCPServer()
# MCP server startup logic here
```
---
## Testing
### Unit Tests
```python
# tests/test_config.py
import pytest
from pathlib import Path
from mcp_server.config import Config
def test_load_system_config(tmp_path):
"""Test loading system-level configuration"""
# Create mock system config
config_dir = tmp_path / '.config' / 'claude'
config_dir.mkdir(parents=True)
config_file = config_dir / 'gitea.env'
config_file.write_text(
"GITEA_API_URL=https://test.com/api/v1\n"
"GITEA_API_TOKEN=test_token\n"
"GITEA_OWNER=test_owner\n"
)
# Test config loading
config = Config()
# ... test assertions
def test_project_config_override(tmp_path):
"""Test that project config overrides system config"""
# ... test implementation
def test_missing_required_config():
"""Test error handling for missing configuration"""
with pytest.raises(ValueError):
config = Config()
config.load()
```
### Integration Tests
```python
# tests/test_gitea_client.py
import pytest
from mcp_server.gitea_client import GiteaClient
@pytest.fixture
def gitea_client():
"""Fixture providing configured Gitea client"""
return GiteaClient()
def test_list_issues(gitea_client):
"""Test listing issues from Gitea"""
issues = gitea_client.list_issues(state='open')
assert isinstance(issues, list)
def test_create_issue(gitea_client):
"""Test creating an issue in Gitea"""
issue = gitea_client.create_issue(
title="Test Issue",
body="Test body",
labels=["Type/Bug"]
)
assert issue['title'] == "Test Issue"
assert "Type/Bug" in [label['name'] for label in issue['labels']]
```
### Running Tests
```bash
# Activate virtual environment
source .venv/bin/activate
# Run all tests
pytest
# Run with coverage
pytest --cov=mcp_server --cov-report=html
# Run specific test file
pytest tests/test_config.py
# Run with verbose output
pytest -v
```
---
## .mcp.json Configuration
### projman (Repository-Specific)
```json
{
"mcpServers": {
"gitea-projman": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"GITEA_API_URL": "${GITEA_API_URL}",
"GITEA_API_TOKEN": "${GITEA_API_TOKEN}",
"GITEA_OWNER": "${GITEA_OWNER}",
"GITEA_REPO": "${GITEA_REPO}"
}
},
"wikijs-projman": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"WIKIJS_API_URL": "${WIKIJS_API_URL}",
"WIKIJS_API_TOKEN": "${WIKIJS_API_TOKEN}",
"WIKIJS_BASE_PATH": "${WIKIJS_BASE_PATH}",
"WIKIJS_PROJECT": "${WIKIJS_PROJECT}"
}
}
}
}
```
### projman-pmo (Multi-Project)
```json
{
"mcpServers": {
"gitea-pmo": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"GITEA_API_URL": "${GITEA_API_URL}",
"GITEA_API_TOKEN": "${GITEA_API_TOKEN}",
"GITEA_OWNER": "${GITEA_OWNER}"
}
},
"wikijs-pmo": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"WIKIJS_API_URL": "${WIKIJS_API_URL}",
"WIKIJS_API_TOKEN": "${WIKIJS_API_TOKEN}",
"WIKIJS_BASE_PATH": "${WIKIJS_BASE_PATH}"
}
}
}
}
```
**Note:** Both plugins reference `../mcp-servers/` (shared location). PMO doesn't use `GITEA_REPO` since it operates across all repositories.
---
## Modular Code Structure (Following Your Preferences)
### Single Responsibility Functions
```python
def validate_configuration(config: Dict[str, str]) -> None:
"""
Validate that all required configuration values are present.
Raises ValueError if any required values are missing.
"""
required_keys = ['api_url', 'api_token', 'owner']
missing = [key for key in required_keys if not config.get(key)]
if missing:
raise ValueError(f"Missing configuration: {', '.join(missing)}")
def load_system_config() -> Dict[str, str]:
"""
Load configuration from system-level gitea.env file.
Returns dictionary of configuration values.
"""
config_path = Path.home() / '.config' / 'claude' / 'gitea.env'
if not config_path.exists():
raise FileNotFoundError(f"System config not found: {config_path}")
load_dotenv(config_path)
return {
'api_url': os.getenv('GITEA_API_URL'),
'api_token': os.getenv('GITEA_API_TOKEN'),
'owner': os.getenv('GITEA_OWNER')
}
def load_project_config() -> Dict[str, Optional[str]]:
"""
Load project-specific configuration from local .env file.
Returns dictionary with 'repo' key, value may be None if not configured.
"""
project_env = Path.cwd() / '.env'
if project_env.exists():
load_dotenv(project_env, override=True)
return {
'repo': os.getenv('GITEA_REPO')
}
def merge_configurations(system: Dict, project: Dict) -> Dict[str, str]:
"""
Merge system and project configurations.
Project values override system values where present.
"""
merged = system.copy()
merged.update({k: v for k, v in project.items() if v is not None})
return merged
def main():
"""Main entry point that orchestrates configuration loading"""
system_config = load_system_config()
project_config = load_project_config()
final_config = merge_configurations(system_config, project_config)
validate_configuration(final_config)
return final_config
```
---
## Virtual Environment Management
### Creation
```bash
# In plugin mcp-server directory
python -m venv .venv
```
### Activation
```bash
# Linux/Mac
source .venv/bin/activate
# Windows
.venv\Scripts\activate
```
### Deactivation
```bash
deactivate
```
### Cleanup & Rebuild
```bash
# Remove old virtual environment
rm -rf .venv
# Create fresh virtual environment
python -m venv .venv
# Activate and reinstall
source .venv/bin/activate
pip install -r requirements.txt
```
---
## Debugging
### Enable Debug Logging
```python
# Add to server.py
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
```
### Common Issues
**Issue:** Module not found
```bash
# Solution: Ensure PYTHONPATH is set in .mcp.json
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/mcp-server"
}
```
**Issue:** Configuration not loading
```bash
# Solution: Check file permissions
chmod 600 ~/.config/claude/gitea.env
# Verify file exists
cat ~/.config/claude/gitea.env
```
**Issue:** API authentication failing
```bash
# Solution: Test token manually
curl -H "Authorization: token YOUR_TOKEN" \
https://your-gitea.com/api/v1/user
```
---
## Performance Optimization
### Caching with functools
```python
from functools import lru_cache
@lru_cache(maxsize=128)
def get_labels_cached(repo: str) -> List[Dict]:
"""Cached label retrieval to reduce API calls"""
return self.gitea.get_labels(repo)
```
### Async Operations
```python
import asyncio
import aiohttp
async def fetch_multiple_repos(repos: List[str]) -> List[Dict]:
"""Fetch data from multiple repositories concurrently"""
async with aiohttp.ClientSession() as session:
tasks = [fetch_repo_data(session, repo) for repo in repos]
return await asyncio.gather(*tasks)
```
---
## Next Steps
1. **Set up system configuration** as shown above
2. **Create project configuration** in your first repository
3. **Navigate to Phase 1.1** of the implementation plan
4. **Build the MCP server** following the structure above
5. **Write tests** as you implement each component
6. **Test with real Gitea instance** early and often
---
## Key Differences from Node.js Approach
| Aspect | Node.js | Python (Your Choice) |
|--------|---------|---------------------|
| Dependencies | package.json | requirements.txt |
| Package Manager | npm/yarn | pip |
| Isolation | node_modules | .venv |
| Module System | ES6 imports | Python imports |
| Async | async/await | async/await |
| Type Checking | TypeScript | Type hints + Pydantic |
| Testing | Jest | pytest |
---
## Resources
- **Anthropic MCP SDK (Python):** https://github.com/anthropics/anthropic-sdk-python
- **Python Requests:** https://docs.python-requests.org/
- **Pydantic:** https://docs.pydantic.dev/
- **pytest:** https://docs.pytest.org/
- **Gitea API Docs:** https://docs.gitea.com/api/
---
Ready to build! 🚀

View File

@@ -0,0 +1,944 @@
# Two MCP Server Architecture - Implementation Guide
## Overview
The projman plugin now uses **two separate MCP servers**:
1. **Gitea MCP Server** - Issues, labels, repository management
2. **Wiki.js MCP Server** - Documentation, lessons learned, knowledge base
This separation provides better maintainability, independent configuration, and leverages Wiki.js's superior documentation features.
> **⚠️ IMPORTANT:** For the definitive repository structure and configuration paths, refer to [CORRECT-ARCHITECTURE.md](./CORRECT-ARCHITECTURE.md). This guide provides detailed implementation examples and architectural deep-dive.
---
## Wiki.js Structure at Hyper Hive Labs
### Company-Wide Organization
```
Wiki.js Instance: https://wiki.hyperhivelabs.com
└── /hyper-hive-labs/ # Base path for all HHL content
├── projects/ # Project-specific documentation
│ ├── cuisineflow/
│ │ ├── lessons-learned/
│ │ │ ├── sprints/
│ │ │ │ ├── sprint-01-auth.md
│ │ │ │ ├── sprint-02-api.md
│ │ │ │ └── ...
│ │ │ ├── patterns/
│ │ │ │ ├── service-extraction.md
│ │ │ │ └── database-migration.md
│ │ │ └── INDEX.md
│ │ └── documentation/
│ │ ├── architecture/
│ │ ├── api/
│ │ └── deployment/
│ ├── cuisineflow-site/
│ │ ├── lessons-learned/
│ │ └── documentation/
│ ├── intuit-engine/
│ │ ├── lessons-learned/
│ │ └── documentation/
│ └── hhl-site/
│ ├── lessons-learned/
│ └── documentation/
├── company/ # Company-wide documentation
│ ├── processes/
│ │ ├── onboarding.md
│ │ ├── deployment.md
│ │ └── code-review.md
│ ├── standards/
│ │ ├── python-style-guide.md
│ │ ├── api-design.md
│ │ └── security.md
│ └── tools/
│ ├── gitea-guide.md
│ ├── wikijs-guide.md
│ └── claude-plugins.md
└── shared/ # Cross-project resources
├── architecture-patterns/
│ ├── microservices.md
│ ├── api-gateway.md
│ └── database-per-service.md
├── best-practices/
│ ├── error-handling.md
│ ├── logging.md
│ └── testing.md
└── tech-stack/
├── python-ecosystem.md
├── docker.md
└── ci-cd.md
```
---
## Configuration Architecture
### System-Level Configuration
**File: `~/.config/claude/gitea.env`**
```bash
GITEA_API_URL=https://gitea.hyperhivelabs.com/api/v1
GITEA_API_TOKEN=your_gitea_token_here
GITEA_OWNER=hyperhivelabs
```
**File: `~/.config/claude/wikijs.env`**
```bash
WIKIJS_API_URL=https://wiki.hyperhivelabs.com/graphql
WIKIJS_API_TOKEN=your_wikijs_token_here
WIKIJS_BASE_PATH=/hyper-hive-labs
```
**Why separate files?**
- Different services, different authentication
- Can update one without affecting the other
- Clear separation of concerns
- Easier to revoke/rotate tokens per service
### Project-Level Configuration
**File: `project-root/.env`**
```bash
# Gitea repository name
GITEA_REPO=cuisineflow
# Wiki.js project path (relative to /hyper-hive-labs)
WIKIJS_PROJECT=projects/cuisineflow
```
**Path Resolution:**
- Full Wiki.js path = `{WIKIJS_BASE_PATH}/{WIKIJS_PROJECT}`
- For cuisineflow: `/hyper-hive-labs/projects/cuisineflow`
- For intuit-engine: `/hyper-hive-labs/projects/intuit-engine`
### PMO Configuration (No Project Scope)
**PMO operates at company level:**
- **Gitea**: No `GITEA_REPO` → accesses all repos
- **Wiki.js**: No `WIKIJS_PROJECT` → accesses entire `/hyper-hive-labs` namespace
---
## Plugin Structure
### Repository Structure (CORRECT)
```
hyperhivelabs/claude-plugins/
├── mcp-servers/ # SHARED by both plugins
│ ├── gitea/
│ │ ├── .venv/
│ │ ├── requirements.txt
│ │ │ # anthropic-sdk>=0.18.0
│ │ │ # python-dotenv>=1.0.0
│ │ │ # requests>=2.31.0
│ │ │ # pydantic>=2.5.0
│ │ ├── .env.example
│ │ ├── mcp_server/
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── config.py
│ │ │ ├── gitea_client.py
│ │ │ └── tools/
│ │ │ ├── issues.py
│ │ │ └── labels.py
│ │ └── tests/
│ │ ├── test_config.py
│ │ ├── test_gitea_client.py
│ │ └── test_tools.py
│ └── wikijs/
│ ├── .venv/
│ ├── requirements.txt
│ │ # anthropic-sdk>=0.18.0
│ │ # python-dotenv>=1.0.0
│ │ # gql>=3.4.0
│ │ # aiohttp>=3.9.0
│ │ # pydantic>=2.5.0
│ ├── .env.example
│ ├── mcp_server/
│ │ ├── __init__.py
│ │ ├── server.py
│ │ ├── config.py
│ │ ├── wikijs_client.py
│ │ └── tools/
│ │ ├── pages.py
│ │ ├── lessons_learned.py
│ │ └── documentation.py
│ └── tests/
│ ├── test_config.py
│ ├── test_wikijs_client.py
│ └── test_tools.py
├── projman/ # Project plugin
│ ├── .claude-plugin/
│ │ └── plugin.json
│ ├── .mcp.json # Points to ../mcp-servers/
│ ├── commands/
│ │ ├── sprint-plan.md
│ │ ├── sprint-start.md
│ │ ├── sprint-status.md
│ │ ├── sprint-close.md
│ │ └── labels-sync.md
│ ├── agents/
│ │ ├── planner.md
│ │ ├── orchestrator.md
│ │ └── executor.md
│ ├── skills/
│ │ └── label-taxonomy/
│ │ └── labels-reference.md
│ ├── README.md
│ └── CONFIGURATION.md
└── projman-pmo/ # PMO plugin
├── .claude-plugin/
│ └── plugin.json
├── .mcp.json # Points to ../mcp-servers/
├── commands/
│ ├── pmo-status.md
│ ├── pmo-priorities.md
│ ├── pmo-dependencies.md
│ └── pmo-schedule.md
├── agents/
│ └── pmo-coordinator.md
└── README.md
```
---
## MCP Configuration Files
### projman .mcp.json (Project-Scoped)
```json
{
"mcpServers": {
"gitea-projman": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"GITEA_API_URL": "${GITEA_API_URL}",
"GITEA_API_TOKEN": "${GITEA_API_TOKEN}",
"GITEA_OWNER": "${GITEA_OWNER}",
"GITEA_REPO": "${GITEA_REPO}"
}
},
"wikijs-projman": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"WIKIJS_API_URL": "${WIKIJS_API_URL}",
"WIKIJS_API_TOKEN": "${WIKIJS_API_TOKEN}",
"WIKIJS_BASE_PATH": "${WIKIJS_BASE_PATH}",
"WIKIJS_PROJECT": "${WIKIJS_PROJECT}"
}
}
}
}
```
### projman-pmo .mcp.json (Company-Wide)
```json
{
"mcpServers": {
"gitea-pmo": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/gitea",
"GITEA_API_URL": "${GITEA_API_URL}",
"GITEA_API_TOKEN": "${GITEA_API_TOKEN}",
"GITEA_OWNER": "${GITEA_OWNER}"
}
},
"wikijs-pmo": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/../mcp-servers/wikijs",
"WIKIJS_API_URL": "${WIKIJS_API_URL}",
"WIKIJS_API_TOKEN": "${WIKIJS_API_TOKEN}",
"WIKIJS_BASE_PATH": "${WIKIJS_BASE_PATH}"
}
}
}
}
```
**Critical Notes:**
- Both plugins reference `../mcp-servers/` (shared location at repository root)
- **projman**: Includes `GITEA_REPO` and `WIKIJS_PROJECT` for project-scoped operations
- **projman-pmo**: Omits project-specific variables for company-wide operations
---
## Wiki.js MCP Server Implementation
### Configuration Loader
```python
# mcp-wikijs/mcp_server/config.py
from pathlib import Path
from dotenv import load_dotenv
import os
from typing import Dict, Optional
class WikiJSConfig:
"""Hybrid configuration loader for Wiki.js"""
def __init__(self):
self.api_url: Optional[str] = None
self.api_token: Optional[str] = None
self.base_path: Optional[str] = None
self.project_path: Optional[str] = None
self.full_path: Optional[str] = None
def load(self) -> Dict[str, str]:
"""
Load Wiki.js configuration from system and project levels.
Composes full path from base_path + project_path.
"""
# Load system config
system_config = Path.home() / '.config' / 'claude' / 'wikijs.env'
if system_config.exists():
load_dotenv(system_config)
else:
raise FileNotFoundError(
f"System config not found: {system_config}\n"
"Create it with: cat > ~/.config/claude/wikijs.env"
)
# Load project config (if exists, optional for PMO)
project_config = Path.cwd() / '.env'
if project_config.exists():
load_dotenv(project_config, override=True)
# Extract values
self.api_url = os.getenv('WIKIJS_API_URL')
self.api_token = os.getenv('WIKIJS_API_TOKEN')
self.base_path = os.getenv('WIKIJS_BASE_PATH') # /hyper-hive-labs
self.project_path = os.getenv('WIKIJS_PROJECT') # projects/cuisineflow (optional)
# Compose full path
if self.project_path:
self.full_path = f"{self.base_path}/{self.project_path}"
else:
# PMO mode - entire company namespace
self.full_path = self.base_path
# Validate required variables
self._validate()
return {
'api_url': self.api_url,
'api_token': self.api_token,
'base_path': self.base_path,
'project_path': self.project_path,
'full_path': self.full_path
}
def _validate(self) -> None:
"""Validate that required configuration is present"""
required = {
'WIKIJS_API_URL': self.api_url,
'WIKIJS_API_TOKEN': self.api_token,
'WIKIJS_BASE_PATH': self.base_path
}
missing = [key for key, value in required.items() if not value]
if missing:
raise ValueError(
f"Missing required configuration: {', '.join(missing)}\n"
"Check your ~/.config/claude/wikijs.env file"
)
```
### GraphQL Client
```python
# mcp-wikijs/mcp_server/wikijs_client.py
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
from typing import List, Dict, Optional
from .config import WikiJSConfig
class WikiJSClient:
"""Client for interacting with Wiki.js GraphQL API"""
def __init__(self):
config = WikiJSConfig()
config_dict = config.load()
self.api_url = config_dict['api_url']
self.api_token = config_dict['api_token']
self.base_path = config_dict['base_path']
self.project_path = config_dict.get('project_path')
self.full_path = config_dict['full_path']
# Set up GraphQL client
transport = AIOHTTPTransport(
url=self.api_url,
headers={'Authorization': f'Bearer {self.api_token}'}
)
self.client = Client(
transport=transport,
fetch_schema_from_transport=True
)
async def search_pages(
self,
query: str,
path: Optional[str] = None,
tags: Optional[List[str]] = None
) -> List[Dict]:
"""
Search pages in Wiki.js within a specific path.
Args:
query: Search query string
path: Optional path to search within (defaults to full_path)
tags: Optional list of tags to filter by
"""
search_path = path or self.full_path
gql_query = gql("""
query SearchPages($query: String!, $path: String) {
pages {
search(query: $query, path: $path) {
results {
id
path
title
description
tags
updatedAt
}
}
}
}
""")
result = await self.client.execute(
gql_query,
variable_values={'query': query, 'path': search_path}
)
pages = result['pages']['search']['results']
# Filter by tags if specified
if tags:
pages = [
p for p in pages
if any(tag in p['tags'] for tag in tags)
]
return pages
async def get_page(self, path: str) -> Dict:
"""Fetch a specific page by path"""
gql_query = gql("""
query GetPage($path: String!) {
pages {
single(path: $path) {
id
path
title
description
content
tags
createdAt
updatedAt
}
}
}
""")
result = await self.client.execute(
gql_query,
variable_values={'path': path}
)
return result['pages']['single']
async def create_page(
self,
path: str,
title: str,
content: str,
tags: List[str],
description: str = ""
) -> Dict:
"""
Create a new page in Wiki.js.
Args:
path: Full path for the page (e.g., /hyper-hive-labs/projects/cuisineflow/lessons-learned/sprints/sprint-01)
title: Page title
content: Page content (markdown)
tags: List of tags
description: Optional description
"""
gql_mutation = gql("""
mutation CreatePage(
$path: String!,
$title: String!,
$content: String!,
$tags: [String]!,
$description: String
) {
pages {
create(
path: $path,
title: $title,
content: $content,
tags: $tags,
description: $description,
isPublished: true,
editor: "markdown"
) {
responseResult {
succeeded
errorCode
message
}
page {
id
path
title
}
}
}
}
""")
result = await self.client.execute(
gql_mutation,
variable_values={
'path': path,
'title': title,
'content': content,
'tags': tags,
'description': description
}
)
return result['pages']['create']
async def update_page(
self,
page_id: int,
content: str,
tags: Optional[List[str]] = None
) -> Dict:
"""Update existing page"""
variables = {
'id': page_id,
'content': content
}
if tags is not None:
variables['tags'] = tags
gql_mutation = gql("""
mutation UpdatePage(
$id: Int!,
$content: String!,
$tags: [String]
) {
pages {
update(
id: $id,
content: $content,
tags: $tags
) {
responseResult {
succeeded
errorCode
message
}
}
}
}
""")
result = await self.client.execute(gql_mutation, variable_values=variables)
return result['pages']['update']
async def list_pages(self, path: str) -> List[Dict]:
"""List all pages within a path"""
gql_query = gql("""
query ListPages($path: String!) {
pages {
list(path: $path, orderBy: TITLE) {
id
path
title
description
tags
updatedAt
}
}
}
""")
result = await self.client.execute(
gql_query,
variable_values={'path': path}
)
return result['pages']['list']
# Lessons Learned Specific Methods
async def create_lesson(
self,
sprint_name: str,
lesson_content: str,
tags: List[str]
) -> Dict:
"""
Create a lessons learned document for a sprint.
Args:
sprint_name: Sprint identifier (e.g., "sprint-16-intuit-engine")
lesson_content: Full lesson markdown content
tags: Tags for categorization
"""
# Compose path within project's lessons-learned/sprints/
lesson_path = f"{self.full_path}/lessons-learned/sprints/{sprint_name}"
title = f"Sprint {sprint_name.split('-')[1]}: {' '.join(sprint_name.split('-')[2:]).title()}"
return await self.create_page(
path=lesson_path,
title=title,
content=lesson_content,
tags=tags,
description=f"Lessons learned from {sprint_name}"
)
async def search_lessons(
self,
query: str,
tags: Optional[List[str]] = None
) -> List[Dict]:
"""
Search lessons learned within the current project.
Args:
query: Search keywords
tags: Optional tags to filter by
"""
lessons_path = f"{self.full_path}/lessons-learned"
return await self.search_pages(
query=query,
path=lessons_path,
tags=tags
)
# PMO Multi-Project Methods
async def search_all_projects(
self,
query: str,
tags: Optional[List[str]] = None
) -> Dict[str, List[Dict]]:
"""
Search lessons across all projects (PMO mode).
Returns results grouped by project.
"""
all_projects_path = f"{self.base_path}/projects"
results = await self.search_pages(
query=query,
path=all_projects_path,
tags=tags
)
# Group by project
by_project = {}
for result in results:
# Extract project name from path
# e.g., "/hyper-hive-labs/projects/cuisineflow/..." -> "cuisineflow"
path_parts = result['path'].split('/')
if len(path_parts) >= 4:
project = path_parts[3]
if project not in by_project:
by_project[project] = []
by_project[project].append(result)
return by_project
async def get_shared_docs(self, category: str) -> List[Dict]:
"""
Access company-wide shared documentation.
Args:
category: Category within shared/ (e.g., "architecture-patterns", "best-practices")
"""
shared_path = f"{self.base_path}/shared/{category}"
return await self.list_pages(path=shared_path)
```
---
## MCP Tools Structure
### Gitea MCP Tools
```python
# mcp-gitea/mcp_server/tools/issues.py
class IssueTools:
def __init__(self, gitea_client):
self.gitea = gitea_client
async def list_issues(self, state='open', labels=None):
"""List issues in current repository"""
return await self.gitea.list_issues(state=state, labels=labels)
async def get_issue(self, issue_number):
"""Get specific issue details"""
return await self.gitea.get_issue(issue_number)
async def create_issue(self, title, body, labels=None):
"""Create new issue"""
return await self.gitea.create_issue(title, body, labels)
# ... other issue tools
# mcp-gitea/mcp_server/tools/labels.py
class LabelTools:
def __init__(self, gitea_client):
self.gitea = gitea_client
async def get_labels(self):
"""Get all labels from repository"""
return await self.gitea.get_labels()
async def suggest_labels(self, context):
"""Suggest appropriate labels based on context"""
# Label suggestion logic using taxonomy
pass
```
### Wiki.js MCP Tools
```python
# mcp-wikijs/mcp_server/tools/pages.py
class PageTools:
def __init__(self, wikijs_client):
self.wikijs = wikijs_client
async def search_pages(self, query, path=None, tags=None):
"""Search Wiki.js pages"""
return await self.wikijs.search_pages(query, path, tags)
async def get_page(self, path):
"""Get specific page"""
return await self.wikijs.get_page(path)
async def create_page(self, path, title, content, tags):
"""Create new page"""
return await self.wikijs.create_page(path, title, content, tags)
# ... other page tools
# mcp-wikijs/mcp_server/tools/lessons_learned.py
class LessonsLearnedTools:
def __init__(self, wikijs_client):
self.wikijs = wikijs_client
async def create_lesson(self, sprint_name, content, tags):
"""Create lessons learned document"""
return await self.wikijs.create_lesson(sprint_name, content, tags)
async def search_lessons(self, query, tags=None):
"""Search past lessons"""
return await self.wikijs.search_lessons(query, tags)
async def search_all_projects(self, query, tags=None):
"""Search lessons across all projects (PMO)"""
return await self.wikijs.search_all_projects(query, tags)
```
---
## Setup Instructions
### 1. System Configuration
```bash
# Create config directory
mkdir -p ~/.config/claude
# Create Gitea config
cat > ~/.config/claude/gitea.env << EOF
GITEA_API_URL=https://gitea.hyperhivelabs.com/api/v1
GITEA_API_TOKEN=your_gitea_token
GITEA_OWNER=hyperhivelabs
EOF
# Create Wiki.js config
cat > ~/.config/claude/wikijs.env << EOF
WIKIJS_API_URL=https://wiki.hyperhivelabs.com/graphql
WIKIJS_API_TOKEN=your_wikijs_token
WIKIJS_BASE_PATH=/hyper-hive-labs
EOF
# Secure config files
chmod 600 ~/.config/claude/*.env
```
### 2. Project Configuration
```bash
# In each project root
cat > .env << EOF
GITEA_REPO=cuisineflow
WIKIJS_PROJECT=projects/cuisineflow
EOF
# Add to .gitignore
echo ".env" >> .gitignore
```
### 3. Install MCP Servers
```bash
# Gitea MCP Server
cd /path/to/claude-plugins/mcp-servers/gitea
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Wiki.js MCP Server
cd /path/to/claude-plugins/mcp-servers/wikijs
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
### 4. Initialize Wiki.js Structure
Create the base structure in Wiki.js web interface:
1. Navigate to https://wiki.hyperhivelabs.com
2. Create `/hyper-hive-labs` page
3. Create `/hyper-hive-labs/projects` page
4. Create `/hyper-hive-labs/company` page
5. Create `/hyper-hive-labs/shared` page
Or use the Wiki.js API:
```python
# One-time setup script
import asyncio
from wikijs_client import WikiJSClient
async def initialize_wiki_structure():
client = WikiJSClient()
# Create base pages
await client.create_page(
path="/hyper-hive-labs",
title="Hyper Hive Labs",
content="# Hyper Hive Labs Documentation",
tags=["company"]
)
await client.create_page(
path="/hyper-hive-labs/projects",
title="Projects",
content="# Project Documentation",
tags=["projects"]
)
# ... create other base pages
asyncio.run(initialize_wiki_structure())
```
---
## Benefits of This Architecture
### 1. Separation of Concerns
- **Gitea MCP**: Project tracking, issues, labels
- **Wiki.js MCP**: Knowledge management, documentation
### 2. Independent Configuration
- Update Gitea credentials without affecting Wiki.js
- Different token expiration policies
- Independent service availability
### 3. Better Documentation Features
- Wiki.js rich editor
- Built-in search and indexing
- Tag system
- Version history
- Access control
- Web-based review and editing
### 4. Company-Wide Knowledge Base
- Shared documentation accessible to all projects
- Cross-project lesson learning
- Best practices repository
- Onboarding materials
- Technical standards
### 5. Scalability
- Add new projects easily
- Grow company documentation organically
- PMO has visibility across everything
- Individual projects stay focused
---
## Migration from Single MCP
If you have existing Wiki content in Git:
```python
# Migration script
import asyncio
from wikijs_client import WikiJSClient
from pathlib import Path
async def migrate_lessons_to_wikijs():
"""Migrate existing lessons learned from Git to Wiki.js"""
client = WikiJSClient()
# Read existing markdown files
lessons_dir = Path("wiki/lessons-learned/sprints")
for lesson_file in lessons_dir.glob("*.md"):
content = lesson_file.read_text()
sprint_name = lesson_file.stem
# Extract tags from content (e.g., from frontmatter or hashtags)
tags = extract_tags(content)
# Create in Wiki.js
await client.create_lesson(
sprint_name=sprint_name,
lesson_content=content,
tags=tags
)
print(f"Migrated: {sprint_name}")
asyncio.run(migrate_lessons_to_wikijs())
```
---
## Next Steps
1. **Set up Wiki.js instance** if not already done
2. **Create base structure** in Wiki.js
3. **Implement both MCP servers** (Phase 1.1a and 1.1b)
4. **Test configuration** with both services
5. **Migrate existing lessons** (if applicable)
6. **Start using with next sprint**
The two-MCP-server architecture provides a solid foundation for both project-level and company-wide knowledge management!