Skip to content

Commit fa8fa42

Browse files
author
LittleCoinCoin
committed
feat: add user feedback reporting system for MCP configuration
Add comprehensive reporting system for MCP configuration operations: - Add FieldOperation model with __str__() for console output - Add ConversionReport model for operation metadata - Implement generate_conversion_report() with dynamic field derivation - Implement display_report() for formatted console output Key features: - Three operation types: UPDATED, UNSUPPORTED, UNCHANGED - Dynamic field derivation using HOST_MODEL_REGISTRY - ASCII arrow (-->) for terminal compatibility - Supports create, update, delete, migrate operations - Dry-run mode with clear preview messaging Benefits: - No hardcoded SUPPORTED_FIELDS constants - Single source of truth (Pydantic model definitions) - Clean separation from models module - User-friendly console output
1 parent 0265d48 commit fa8fa42

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

hatch/mcp_host_config/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from .host_management import (
1818
MCPHostRegistry, MCPHostStrategy, MCPHostConfigurationManager, register_host_strategy
1919
)
20+
from .reporting import (
21+
FieldOperation, ConversionReport, generate_conversion_report, display_report
22+
)
2023

2124
# Import strategies to trigger decorator registration
2225
from . import strategies
@@ -29,5 +32,7 @@
2932
'MCPServerConfigBase', 'MCPServerConfigGemini', 'MCPServerConfigVSCode',
3033
'MCPServerConfigCursor', 'MCPServerConfigClaude', 'MCPServerConfigOmni',
3134
'HOST_MODEL_REGISTRY',
35+
# User feedback reporting
36+
'FieldOperation', 'ConversionReport', 'generate_conversion_report', 'display_report',
3237
'MCPHostRegistry', 'MCPHostStrategy', 'MCPHostConfigurationManager', 'register_host_strategy'
3338
]

hatch/mcp_host_config/reporting.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
User feedback reporting system for MCP configuration operations.
3+
4+
This module provides models and functions for generating and displaying
5+
user-friendly reports about MCP configuration changes, including field-level
6+
operations and conversion summaries.
7+
"""
8+
9+
from typing import Literal, Optional, Any, List
10+
from pydantic import BaseModel, ConfigDict
11+
12+
from .models import MCPServerConfigOmni, MCPHostType, HOST_MODEL_REGISTRY
13+
14+
15+
class FieldOperation(BaseModel):
16+
"""Single field operation in a conversion.
17+
18+
Represents a single field-level change during MCP configuration conversion,
19+
including the operation type (UPDATED, UNSUPPORTED, UNCHANGED) and values.
20+
"""
21+
22+
field_name: str
23+
operation: Literal["UPDATED", "UNSUPPORTED", "UNCHANGED"]
24+
old_value: Optional[Any] = None
25+
new_value: Optional[Any] = None
26+
27+
def __str__(self) -> str:
28+
"""Return formatted string representation for console output.
29+
30+
Uses ASCII arrow (-->) for terminal compatibility instead of Unicode.
31+
"""
32+
if self.operation == "UPDATED":
33+
return f"{self.field_name}: UPDATED {repr(self.old_value)} --> {repr(self.new_value)}"
34+
elif self.operation == "UNSUPPORTED":
35+
return f"{self.field_name}: UNSUPPORTED"
36+
elif self.operation == "UNCHANGED":
37+
return f"{self.field_name}: UNCHANGED {repr(self.new_value)}"
38+
return f"{self.field_name}: {self.operation}"
39+
40+
41+
class ConversionReport(BaseModel):
42+
"""Complete conversion report for a configuration operation.
43+
44+
Contains metadata about the operation (create, update, delete, migrate)
45+
and a list of field-level operations that occurred during conversion.
46+
"""
47+
48+
model_config = ConfigDict(validate_assignment=False)
49+
50+
operation: Literal["create", "update", "delete", "migrate"]
51+
server_name: str
52+
source_host: Optional[MCPHostType] = None
53+
target_host: MCPHostType
54+
success: bool = True
55+
error_message: Optional[str] = None
56+
field_operations: List[FieldOperation] = []
57+
dry_run: bool = False
58+
59+
60+
def generate_conversion_report(
61+
operation: Literal["create", "update", "delete", "migrate"],
62+
server_name: str,
63+
target_host: MCPHostType,
64+
omni: MCPServerConfigOmni,
65+
source_host: Optional[MCPHostType] = None,
66+
old_config: Optional[MCPServerConfigOmni] = None,
67+
dry_run: bool = False
68+
) -> ConversionReport:
69+
"""Generate conversion report for a configuration operation.
70+
71+
Analyzes the conversion from Omni model to host-specific configuration,
72+
identifying which fields were updated, which are unsupported, and which
73+
remained unchanged.
74+
75+
Args:
76+
operation: Type of operation being performed
77+
server_name: Name of the server being configured
78+
target_host: Target host for the configuration (MCPHostType enum)
79+
omni: New/updated configuration (Omni model)
80+
source_host: Source host (for migrate operation, MCPHostType enum)
81+
old_config: Existing configuration (for update operation)
82+
dry_run: Whether this is a dry-run preview
83+
84+
Returns:
85+
ConversionReport with field-level operations
86+
"""
87+
# Derive supported fields dynamically from model class
88+
model_class = HOST_MODEL_REGISTRY[target_host]
89+
supported_fields = set(model_class.model_fields.keys())
90+
91+
field_operations = []
92+
set_fields = omni.model_dump(exclude_unset=True)
93+
94+
for field_name, new_value in set_fields.items():
95+
if field_name in supported_fields:
96+
# Field is supported by target host
97+
if old_config:
98+
# Update operation - check if field changed
99+
old_fields = old_config.model_dump(exclude_unset=True)
100+
if field_name in old_fields:
101+
old_value = old_fields[field_name]
102+
if old_value != new_value:
103+
# Field was modified
104+
field_operations.append(FieldOperation(
105+
field_name=field_name,
106+
operation="UPDATED",
107+
old_value=old_value,
108+
new_value=new_value
109+
))
110+
else:
111+
# Field unchanged
112+
field_operations.append(FieldOperation(
113+
field_name=field_name,
114+
operation="UNCHANGED",
115+
new_value=new_value
116+
))
117+
else:
118+
# Field was added
119+
field_operations.append(FieldOperation(
120+
field_name=field_name,
121+
operation="UPDATED",
122+
old_value=None,
123+
new_value=new_value
124+
))
125+
else:
126+
# Create operation - all fields are new
127+
field_operations.append(FieldOperation(
128+
field_name=field_name,
129+
operation="UPDATED",
130+
old_value=None,
131+
new_value=new_value
132+
))
133+
else:
134+
# Field is not supported by target host
135+
field_operations.append(FieldOperation(
136+
field_name=field_name,
137+
operation="UNSUPPORTED",
138+
new_value=new_value
139+
))
140+
141+
return ConversionReport(
142+
operation=operation,
143+
server_name=server_name,
144+
source_host=source_host,
145+
target_host=target_host,
146+
field_operations=field_operations,
147+
dry_run=dry_run
148+
)
149+
150+
151+
def display_report(report: ConversionReport) -> None:
152+
"""Display conversion report to console.
153+
154+
Prints a formatted report showing the operation performed and all
155+
field-level changes. Uses FieldOperation.__str__() for consistent
156+
formatting.
157+
158+
Args:
159+
report: ConversionReport to display
160+
"""
161+
# Header
162+
if report.dry_run:
163+
print(f"[DRY RUN] Preview of changes for server '{report.server_name}':")
164+
else:
165+
if report.operation == "create":
166+
print(f"Server '{report.server_name}' created for host '{report.target_host.value}':")
167+
elif report.operation == "update":
168+
print(f"Server '{report.server_name}' updated for host '{report.target_host.value}':")
169+
elif report.operation == "migrate":
170+
print(f"Server '{report.server_name}' migrated from '{report.source_host.value}' to '{report.target_host.value}':")
171+
elif report.operation == "delete":
172+
print(f"Server '{report.server_name}' deleted from host '{report.target_host.value}':")
173+
174+
# Field operations
175+
for field_op in report.field_operations:
176+
print(f" {field_op}")
177+
178+
# Footer
179+
if report.dry_run:
180+
print("\nNo changes were made.")
181+

0 commit comments

Comments
 (0)