Advanced Guide (v2.0.10)
This page documents the current 2.0.10 MCP runtime: scoped planner/tool selection, current rule phases, next-tool guardrails, direct-tool execution, and the guarded Semantic Query branch.
Read order
- MCP Basics
- Advanced Guide (this page)
- Semantic Query
- Semantic Query Deep Dive
- Knowledge (Semantic Query)
- HTTP Tool
- Example 1
- Example 2
- Example 3
- Example 4 - Zapper Disconnect
Phase names (current)
PRE_RESPONSE_RESOLUTIONPOST_AGENT_INTENTPOST_AGENT_MCPPOST_TOOL_EXECUTION
Compatibility note: legacy values (PIPELINE_RULES, AGENT_POST_INTENT, AGENT_POST_MCP, TOOL_POST_EXECUTION) are normalized at runtime.
Current MCP branches
MCP execution branches
| Branch | Entry point | What it does | Key guardrails |
|---|---|---|---|
| Planner MCP | McpToolStep | Runs planner loop (`CALL_TOOL` / `ANSWER`) and accumulates `context.mcp.observations`. | Next-tool guardrail, scoped `ce_mcp_tool`, scoped `ce_mcp_planner`. |
| Direct tool | ToolOrchestrationStep | Executes exactly one `tool_request` and writes `context.mcp.toolExecution`. | Scoped tool lookup, `POST_TOOL_EXECUTION` rules. |
| Semantic query | McpToolStep planner branch | Runs semantic interpret -> query -> SQL execution chain inside planner loop. | `db.semantic.interpret` must resolve canonical intent before `db.semantic.query`; SQL remains read-only. |
Exact planner MCP order (current)
- Intent and state resolve through the normal pipeline.
McpToolStepenters the planner loop.McpPlannerreturnsCALL_TOOLorANSWER.- For
CALL_TOOL, the selected tool runs and a new row is appended tocontext.mcp.observations. - The planner loops again until it returns
ANSWER. context.mcp.finalAnsweris written.POST_AGENT_MCPrules run.PRE_RESPONSE_RESOLUTIONrules run.ResponseResolutionStepstill decides the final transport payload.
For the Semantic Query branch, the same planner loop is used, but the tool order is stricter:
db.semantic.interpretdb.semantic.querypostgres.query- planner
ANSWER
Tool execution paths
Planner MCP path (McpToolStep)
- Planner returns
CALL_TOOLorANSWER. - Tool results are appended to
context.mcp.observations. - For
ANSWER, final text is written tocontext.mcp.finalAnswer. - Rules run in phase
POST_AGENT_MCP. - Response resolution runs after rule transitions.
Direct tool path (ToolOrchestrationStep)
- Request contains
tool_request. - Exactly one tool executes.
- Result stored in
inputParams.tool_resultandcontext.mcp.toolExecution. - Rules run in phase
POST_TOOL_EXECUTION. - Response resolution runs with updated state/context.
Prompt-template interaction semantics
ce_prompt_template is now part of the runtime contract, not just prompt text storage.
interaction_mode: broad turn semantics such asCOLLECT,CONFIRM,PROCESSING,FINALinteraction_contract: JSON for capabilities and expectations
Recommended shape:
{"allows":["affirm","edit","retry","reset"],"expects":["structured_input"]}
Why it matters:
CorrectionStepuses this to decide confirm/edit/retry behavior- confirmation-first MCP flows should model turn behavior here, not by naming states
CONFIRM
Guardrail blocked answer semantics
When MCP_STATUS=GUARDRAIL_BLOCKED_NEXT_TOOL:
McpToolStepwrites fallback text (McpConstants.FALLBACK_GUARDRAIL_BLOCKED) tocontext.mcp.finalAnswer.- Rules in
POST_AGENT_MCPstill execute. ce_responseremains the final response authority (commonlyDERIVEDusingcontext.mcp.finalAnswer).
For the Semantic Query path, there is one additional hard stop:
- if the planner proposes
db.semantic.querybefore a successfuldb.semantic.interpret McpToolStepblocks execution with semantic validation guardrail reason- audit and response resolution still continue through the normal MCP lifecycle
Read-only DB behavior
All MCP DB execution is now enforced as read-only.
- generic DB tools are guarded
- Semantic Query query templates are guarded
- statements such as
INSERT,UPDATE,DELETE,DROP,TRUNCATE,ALTER,CREATE,MERGE, andCALLare blocked SELECT INTO,FOR UPDATE, and multi-statement SQL are blockedce_mcp_sql_guardrailcan extend function allow/block rules without making the runtime writable
For postgres.query, an interceptor SPI runs before guardrail + execution:
PostgresQueryInterceptor(consumer extension point, ordered chain)DefaultPostgresQueryInterceptor(framework fallback, lowest precedence)- default behavior auto-corrects invalid epoch conversion on timestamp/date columns (for example
to_timestamp(ts_col/1000.0)whents_colis already timestamp/date)
MCP metadata model for rules
context.mcp.lifecycle:
phase,status,outcome,finishedblocked,error,errorMessagelastAction,lastToolCode,lastToolGroup,lastToolArgstoolExecutedfinalAnswerDetermined(true when MCP final answer is set)toolExecutionAbrupted(true when loop stops due to max-loop guard without final answer)toolExecutionAbruptionLimit(configured loop guard limit)
context.mcp.toolExecution:
phase,status,outcome,finishederror,scopeMismatch,toolExecutedtoolCode,toolGroup,meta,result,errorMessage
JSON_PATH patterns you can use in ce_rule.match_pattern
$[?(@.context.mcp.lifecycle.finished == true && @.context.mcp.lifecycle.outcome == 'BLOCKED')]
$[?(@.context.mcp.lifecycle.error == true)]
$[?(@.context.mcp.toolExecution.phase == 'POST_TOOL_EXECUTION' && @.context.mcp.toolExecution.status == 'SUCCESS')]
$[?(@.context.mcp.toolExecution.scopeMismatch == true)]
Direct tool example (POST_TOOL_EXECUTION)
User asks: Check order ORD-7017 status.
{
"tool_request": {
"tool_code": "mock.order.status",
"tool_group": "HTTP_API",
"args": { "orderId": "ORD-7017" }
}
}
Execution:
ToolOrchestrationStepexecutes one tool.- writes
tool_result+tool_status=SUCCESS - writes
context.mcp.toolExecution.* - executes
POST_TOOL_EXECUTIONrules - example: a rule sets state to
ORDER_SUBMITTED_DIAGNOSISwhen tool status is submitted
ce_mcp_tool and ce_mcp_planner scoping rules
intent_code and state_code are mandatory (no null/blank scope).
Allowed values:
intent_code: definedce_intent.intent_codeorANYorUNKNOWNstate_code: values present ince_rule.state_codeorANYorUNKNOWN
Invalid scope rows are blocked at startup by static scope validation.
Planner prompt source (ce_mcp_planner)
Planner prompt selection:
- exact
intent_code + state_code - exact
intent_code + ANY - global
ANY + ANY - legacy fallback (
ce_configMcpPlannerkeys) if planner rows are unavailable
SQL snippets
- Planner Rows
- Tool Rows
- SQL Guardrail Rows
INSERT INTO ce_mcp_planner (planner_id, intent_code, state_code, system_prompt, user_prompt, enabled)
VALUES
(1001, 'ANY', 'ANY', '...', '...', true),
(1002, 'LOAN_APPLICATION', 'ELIGIBILITY_GATE', '...', '...', true);
INSERT INTO ce_mcp_tool (tool_id, tool_code, tool_group, intent_code, state_code, enabled, description)
VALUES
(9001, 'db.semantic.interpret', 'DB', 'ANY', 'ANY', true, 'Semantic interpret'),
(9002, 'db.semantic.query', 'DB', 'ANY', 'ANY', true, 'Semantic SQL generator'),
(9003, 'postgres.query', 'DB', 'ANY', 'ANY', true, 'Read-only SQL execution');
INSERT INTO ce_mcp_sql_guardrail (rule_type, match_value, enabled, description)
VALUES
('BLOCK_FUNCTION', 'pg_sleep', true, 'Block sleep functions in MCP DB paths'),
('ALLOW_FUNCTION', 'fetch_inventory_status', true, 'Allow safe inventory lookup function');
For MCP chains, keep final user text in ce_response/ce_prompt_template and treat context.mcp.finalAnswer as a derived source, not the final transport payload.
Use Example 3 for the full confirmation-first flow and Example 4 - Zapper Disconnect for the guarded Semantic Query path.