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.
164 lines
7.5 KiB
Markdown
164 lines
7.5 KiB
Markdown
# 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
|