MCP
This document explains how MCP works internally in ConvEngine, based strictly on the real implementation of:
com.github.salilvnair.convengine.engine.steps.McpToolStep
MCP is used when:
- An intent requires external data
- That data must be fetched safely
- The engine must reason in multiple steps
- SQL or tools must be DB-driven, not hardcoded
Example conversationβ
What is the status of my move for connection USPSC003BA100SA277CON1388
The status of your move for connection USPSC003BA100SA277CON1388 is MOVED.
MCP execution timelineβ
McpToolStep runs after intent resolution and before rules / response.
McpToolRegistry loads enabled tools from ce_mcp_tool.
If no tools exist, MCP exits early.
Any stale context_json.mcp.finalAnswer or observations are removed.
If this step is skipped, previous answers WILL leak into new requests.
Planner decides whether to CALL_TOOL or ANSWER.
Tool code + arguments are returned by the planner and audited.
SQL template from ce_mcp_db_tool is executed via
McpDbExecutor.
Tool result is appended to context_json.mcp.observations.
Final answer is written to context_json.mcp.finalAnswer.
Core MCP loop (real code)β
MCP context structureβ
After execution, MCP writes into ce_conversation.context_json:
{
"mcp": {
"observations": [
{
"toolCode": "postgres.move_status",
"json": "[{ \"status\": \"MOVED\" }]"
}
],
"finalAnswer": "The status of your move is MOVED."
}
}
MCP does NOT return the API response directly.
It prepares data for rules and response resolution.
How McpPlanner decides which tool to callβ
McpPlanner is the only component that decides:
- Whether MCP should continue
- Whether to CALL_TOOL or ANSWER
- Which tool to call
- Which arguments to pass
McpToolStep never chooses a tool.
It blindly executes whatever McpPlanner returns.
Why postgres.move_status is chosenβ
Given:
- Intent = MOVE_CONNECTIONS
- Question contains a connection id
- No existing answer in context
- Tool metadata describing move-status lookup
The planner emits a CALL_TOOL plan.
Core MCP loop (real code)β
What happens after MCP?β
β’ RulesStep may short-circuit if $.mcp.finalAnswer exists
β’ Or state may be updated via ce_rule
β’ ResponseResolutionStep selects ce_response
β’ Response may be EXACT or DERIVED
Why MCP is safe by designβ
No dynamic SQL from LLM
All tools are DB-configured
Param schema validated
Max rows enforced
Failures degrade gracefully
Every step is audited
Final takeawayβ
MCP is controlled, deterministic, DB-driven, and safe.
Debugging tipβ
If you ever see:
Second request returns previous answer
Check these in order:
- MCP_CONTEXT_CLEARED audit exists
- SQL tool params extracted correctly
- derivation_hint does not blindly reuse old finalAnswer
Final takeawayβ
MCP in ConvEngine is not βagents doing magicβ.
It is:
β’ Controlled
β’ Deterministic
β’ Auditable
β’ DB-driven
β’ Safe