Files
Orchestrator/bahn/teamlandkarte-mcp/.kiro/specs/capacity-details-enrichment/design.md
T
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

164 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design Document: Capacity Details Enrichment
## Overview
This feature enriches the existing `get_capacity_details` MCP tool to display additional information about a capacity: its description, references (partner + projects), and certifications. The data is already available in the database and exposed through existing `DBClient` methods (`get_capacity_description`, `get_capacity_references`, `get_capacity_certificates`). The change is purely in the tool's output formatting layer.
The tool currently returns a Markdown table with basic capacity fields (ID, Owner, Role, Competences, Availability) followed by a "Next steps" section. After this enhancement, it will include three additional sections between the table and the next steps: Beschreibung, Referenzen, and Zertifizierungen.
## Architecture
The change is localized to the `get_capacity_details` tool function inside `build_server()` in `src/teamlandkarte_mcp/mcp_server.py`. No new modules, classes, or external dependencies are needed.
```mermaid
sequenceDiagram
participant Client as MCP Client
participant Tool as get_capacity_details
participant DB as DBClient
Client->>Tool: call(capacity_id)
Tool->>DB: get_capacity_by_id(capacity_id)
DB-->>Tool: Capacity | None
Tool->>DB: get_capacity_description(capacity_id)
DB-->>Tool: str | None
Tool->>DB: get_capacity_references(capacity_id)
DB-->>Tool: list[CapacityReferenceRow]
Tool->>DB: get_capacity_certificates(capacity_id)
DB-->>Tool: list[str]
Tool-->>Client: Formatted Markdown string
```
### Design Decisions
1. **Sequential DB calls** The three additional queries are simple key lookups on indexed views. Parallelizing them would add complexity (async conversion of the tool) for negligible latency gain. Keep the tool synchronous.
2. **Formatting inline** The formatting logic is simple string concatenation. No need for a separate formatter class.
3. **Empty-state handling** Each section shows a "(keine)" placeholder when data is absent, keeping the output structure predictable for LLM consumers.
## Components and Interfaces
### Modified Component: `get_capacity_details` tool
**Current signature** (unchanged):
```python
def get_capacity_details(capacity_id: int | str) -> str:
```
**New internal calls added:**
```python
description: str | None = db_client.get_capacity_description(capacity_id)
references: list[CapacityReferenceRow] = db_client.get_capacity_references(capacity_id)
certificates: list[str] = db_client.get_capacity_certificates(capacity_id)
```
**Output format** (Markdown string):
```
| ID | Owner | Role | Competences | Availability |
| ... |
## Beschreibung
<description text or "(keine)">
## Referenzen
- **Partner A**: Projekt X, Projekt Y
- Projekt Z (no partner)
*or* Referenzen: (keine)
## Zertifizierungen
- Zertifikat 1
- Zertifikat 2
*or* Zertifizierungen: (keine)
## Next steps
Call find_matching_tasks(capacity_id=...) to see matching open tasks.
```
### Existing Interfaces Used (no changes)
| Method | Returns | Source |
|--------|---------|--------|
| `DBClient.get_capacity_description(capacity_id)` | `str \| None` | `teamlandkarte_v_capacities_latest.description` |
| `DBClient.get_capacity_references(capacity_id)` | `list[CapacityReferenceRow]` | `teamlandkarte_v_capacity_references_latest` joined with partners |
| `DBClient.get_capacity_certificates(capacity_id)` | `list[str]` | `teamlandkarte_v_capacity_certificates_latest.description` |
## Data Models
### CapacityReferenceRow (existing, unchanged)
```python
class CapacityReferenceRow(TypedDict):
partner_name: str # May be empty string when partner_id is NULL
projects: str # Project text from the references view
```
No new data models are introduced.
## Correctness Properties
*A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
### Property 1: All enrichment data is fetched for any capacity
*For any* valid capacity ID that resolves to an existing capacity, the tool SHALL invoke `get_capacity_description`, `get_capacity_references`, and `get_capacity_certificates` with that capacity ID.
**Validates: Requirements 1.1, 2.1, 3.1**
### Property 2: Non-empty data appears in output
*For any* capacity with a non-empty description, a non-empty list of references, and a non-empty list of certificates, the formatted output SHALL contain the description text, every reference's projects text, and every certificate string.
**Validates: Requirements 1.2, 2.2, 3.2**
### Property 3: Section ordering is fixed
*For any* capacity (regardless of which fields are empty or populated), the output string SHALL contain the section markers in the order: capacity table first, then "Beschreibung", then "Referenzen", then "Zertifizierungen", then "Next steps" — and each section SHALL be separated by at least one blank line.
**Validates: Requirements 4.1, 4.2**
## Error Handling
| Scenario | Behaviour |
|----------|-----------|
| `get_capacity_by_id` returns `None` | Return `"Capacity not found: {capacity_id}"` (existing behaviour, unchanged) |
| `get_capacity_description` returns `None` | Display `"Beschreibung: (keine)"` |
| `get_capacity_references` returns `[]` | Display `"Referenzen: (keine)"` |
| `get_capacity_certificates` returns `[]` | Display `"Zertifizierungen: (keine)"` |
| `get_capacity_references` returns a row with empty `partner_name` | Display only the `projects` text (no bold partner prefix) |
| Any DB call raises an exception | Let it propagate (existing error handling in the MCP framework catches and reports it) |
## Testing Strategy
### Unit Tests
- Test `get_capacity_details` with a mock DB client returning known data for all three new fields → verify output contains expected sections and content.
- Test empty-state: description=None, references=[], certificates=[] → verify "(keine)" placeholders appear.
- Test edge case: reference with empty `partner_name` → verify only projects text is shown.
- Test that capacity-not-found still returns the error message unchanged.
### Property-Based Tests
Library: **Hypothesis** (Python)
Each property test runs a minimum of 100 iterations.
- **Property 1 test**: Generate random capacity IDs and mock DB responses. Verify all three DB methods are called with the correct ID.
- Tag: `Feature: capacity-details-enrichment, Property 1: All enrichment data is fetched for any capacity`
- **Property 2 test**: Generate random non-empty descriptions (text strategy), random lists of `CapacityReferenceRow` dicts with non-empty `projects` and `partner_name`, and random lists of non-empty certificate strings. Call the formatting logic and assert all generated data appears in the output.
- Tag: `Feature: capacity-details-enrichment, Property 2: Non-empty data appears in output`
- **Property 3 test**: Generate random combinations of present/absent data (description: str|None, references: list of 0-5 items, certificates: list of 0-5 items). Call the formatting logic and assert section headers appear in the correct order with blank-line separation.
- Tag: `Feature: capacity-details-enrichment, Property 3: Section ordering is fixed`
### Test Configuration
- Property-based testing library: `hypothesis` (already available in the project's test dependencies)
- Minimum iterations: 100 per property (`@settings(max_examples=100)`)
- Each test tagged with a comment referencing the design property