Description
Workflow tools that accept a complex request parameter (like review_urgent_order_requirements, forecasts_get_for_products, etc.) fail with a Pydantic validation error because the MCP layer is passing the request parameter as a JSON string instead of a dictionary.
Error
1 validation error for call[review_urgent_order_requirements]
request
Input should be a valid dictionary or instance of ReviewUrgentOrdersRequest [type=model_type, input_value='{"days_threshold": 30}', input_type=str]
Affected Tools
All workflow tools that use the nested request pattern:
review_urgent_order_requirements
forecasts_get_for_products
forecasts_update_and_monitor
generate_purchase_orders_from_urgent_items
create_supplier_with_products
configure_product
update_forecast_settings
products_configure_lifecycle
- Potentially others
Root Cause
The MCP tool registration is receiving the request parameter as a JSON string, but the Pydantic validation expects a dictionary or model instance. This is likely happening in the tool wrapper/adapter layer between FastMCP and our tool implementations.
Reproduction
# Via MCP
mcp__stocktrim-dev__review_urgent_order_requirements(
request={"days_threshold": 30}
)
# Error: Input should be a valid dictionary...
Expected Behavior
The MCP layer should deserialize the JSON parameter into a dictionary before passing it to the Pydantic model, or the tool should accept a string and deserialize it internally.
Investigation Needed
- Check how FastMCP handles complex nested parameters
- Review the tool wrapper implementation in
tools/workflows/
- Determine if this is a FastMCP issue or our implementation issue
- Check ADR 002 (tool-interface-pattern) for parameter flattening guidance
Proposed Solutions
Option 1: Pre-deserialize in tool wrapper
@mcp.tool()
async def review_urgent_order_requirements(
request: str, # Accept as string
context: Context | None = None,
) -> str:
# Deserialize manually
request_dict = json.loads(request) if isinstance(request, str) else request
request_obj = ReviewUrgentOrdersRequest(**request_dict)
return await _review_urgent_order_requirements(request_obj, context)
Option 2: Flatten parameters per ADR 002
Follow the parameter flattening pattern and avoid nested request objects:
@mcp.tool()
async def review_urgent_order_requirements(
days_threshold: int = 30,
location_codes: list[str] | None = None,
category: str | None = None,
supplier_codes: list[str] | None = None,
context: Context | None = None,
) -> str:
request = ReviewUrgentOrdersRequest(
days_threshold=days_threshold,
location_codes=location_codes,
category=category,
supplier_codes=supplier_codes,
)
return await _review_urgent_order_requirements(request, context)
Impact
- Severity: HIGH - Blocks all workflow tools (the most valuable MCP features)
- Affected Tools: ~8 workflow tools
- Workaround: None - these tools are completely non-functional
- Users Affected: Anyone trying to use high-level workflow operations
Priority
This should be P0 as it blocks the most important and advertised features of the MCP server.
Related Files
stocktrim_mcp_server/src/stocktrim_mcp_server/tools/workflows/*.py
docs/architecture/decisions/002-tool-interface-pattern.md
Description
Workflow tools that accept a complex
requestparameter (likereview_urgent_order_requirements,forecasts_get_for_products, etc.) fail with a Pydantic validation error because the MCP layer is passing the request parameter as a JSON string instead of a dictionary.Error
Affected Tools
All workflow tools that use the nested request pattern:
review_urgent_order_requirementsforecasts_get_for_productsforecasts_update_and_monitorgenerate_purchase_orders_from_urgent_itemscreate_supplier_with_productsconfigure_productupdate_forecast_settingsproducts_configure_lifecycleRoot Cause
The MCP tool registration is receiving the
requestparameter as a JSON string, but the Pydantic validation expects a dictionary or model instance. This is likely happening in the tool wrapper/adapter layer between FastMCP and our tool implementations.Reproduction
Expected Behavior
The MCP layer should deserialize the JSON parameter into a dictionary before passing it to the Pydantic model, or the tool should accept a string and deserialize it internally.
Investigation Needed
tools/workflows/Proposed Solutions
Option 1: Pre-deserialize in tool wrapper
Option 2: Flatten parameters per ADR 002
Follow the parameter flattening pattern and avoid nested request objects:
Impact
Priority
This should be P0 as it blocks the most important and advertised features of the MCP server.
Related Files
stocktrim_mcp_server/src/stocktrim_mcp_server/tools/workflows/*.pydocs/architecture/decisions/002-tool-interface-pattern.md