Files
leo-claude-mktplace/docs/references/projman-pmo/two-mcp-architecture-guide.md

28 KiB

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. 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

GITEA_API_URL=https://gitea.hyperhivelabs.com/api/v1
GITEA_API_TOKEN=your_gitea_token_here
GITEA_OWNER=hyperhivelabs

File: ~/.config/claude/wikijs.env

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

# 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)

{
  "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)

{
  "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

# 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

# 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

# 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

# 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

# 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

# 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

# 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:

# 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:

# 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!