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.
7.5 KiB
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.
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
- 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.
- Formatting inline – The formatting logic is simple string concatenation. No need for a separate formatter class.
- 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):
def get_capacity_details(capacity_id: int | str) -> str:
New internal calls added:
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)
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_detailswith 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
- Tag:
-
Property 2 test: Generate random non-empty descriptions (text strategy), random lists of
CapacityReferenceRowdicts with non-emptyprojectsandpartner_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
- Tag:
-
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
- Tag:
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