Part of #21.
Goal: close the MCP-specific gaps that an external "runtime security gateway" would inspect. Build the inspection points in-product — they need access to the tool registry and result shape, which a sidecar cannot see without re-parsing config.
Tasks
Critical files
src/mcp_tool_handler.cpp, src/mcp_route_handlers.cpp, src/endpoint_config_parser.cpp, src/include/endpoint_config.hpp, new src/mcp_response_shaper.{cpp,hpp}.
Verify
New test/integration/test_mcp_rbac.py, test_mcp_dryrun.py, test_mcp_response_shaping.py. End-to-end: configure two endpoints with different allowed-roles, issue MCP calls with two JWTs, assert exactly one succeeds; issue dry-run and assert no rows in result + SQL + plan present.
Part of #21.
Goal: close the MCP-specific gaps that an external "runtime security gateway" would inspect. Build the inspection points in-product — they need access to the tool registry and result shape, which a sidecar cannot see without re-parsing config.
Tasks
W2.1 — Per-tool RBAC. Add to endpoint YAML:
Enforce in
MCPToolHandler::executeToolbefore SQL runs (src/mcp_tool_handler.cpp:15-89). Default for endpoints withauth.required: truebut no explicitallowed-roles: deny-by-default with a clear error referencing the config key. Endpoints without auth keep working unchanged (ease-of-use principle).W2.2 — Shadow / dry-run mode. Accept
_dryRun: truein tool-call arguments (and?_dryRun=1for REST). When set: run validators + template expansion + DuckDBEXPLAIN, return rendered SQL + execution plan + estimated rows, but skip materialization. This is literally the "shadow mode + query inspection" pattern external gateways pitch.W2.3 — Tool-description hygiene. At endpoint-config-load time (
src/endpoint_config_parser.cpp:256), reject or warn on descriptions containing: control characters, JSON-breakout patterns, prompt-injection phrases ("ignore previous instructions"). Strict mode opt-in viamcp.strict-descriptions: true.W2.4 — Response shaping per tool.
Enforce in
MCPToolHandler::formatResult(src/mcp_tool_handler.cpp:70-85).W2.5 — Per-tool rate limits. Extend rate-limit config to support per-tool keys:
Critical files
src/mcp_tool_handler.cpp,src/mcp_route_handlers.cpp,src/endpoint_config_parser.cpp,src/include/endpoint_config.hpp, newsrc/mcp_response_shaper.{cpp,hpp}.Verify
New
test/integration/test_mcp_rbac.py,test_mcp_dryrun.py,test_mcp_response_shaping.py. End-to-end: configure two endpoints with differentallowed-roles, issue MCP calls with two JWTs, assert exactly one succeeds; issue dry-run and assert no rows in result + SQL + plan present.