Color legend used across diagrams:
- Blue:
core/platform/mcp/ - Purple:
flat/tools/mcp/ - Green: other
flat/ - Yellow: knowledge-search / MCP knowledge-indexing code
- Gray: external systems or non-MCP infrastructure
McpCatalog is folded into McpClient in the diagrams because it mostly adds provider aggregation/resolution wiring for this path. Assume rippling_gateway is the active MCP provider.
flowchart TD
U[User request] --> Agent["<b>Flat agent</b><br/>chooses next step"]
Agent --> Discover["<b>Discover tools</b><br/>knowledge_search"]
Discover --> ReadDoc["<b>Read MCP doc</b><br/>schema + call snippet"]
ReadDoc --> Agent
Agent --> Call["<b>Call MCP tool</b><br/>execute_python + call_tool"]
Call --> Gate{Requires approval?}
Gate -->|No| Direct["<b>Direct call</b><br/>read-only path"]
Gate -->|Yes| HITL["<b>HITL action</b><br/>external_mcp target"]
Direct --> Result[Tool result]
HITL --> Result
Result --> Agent
classDef coreMcp fill:#dbeafe,stroke:#2563eb,color:#111827;
classDef flatMcp fill:#ede9fe,stroke:#7c3aed,color:#111827;
classDef otherFlat fill:#dcfce7,stroke:#16a34a,color:#111827;
classDef knowledge fill:#fef3c7,stroke:#d97706,color:#111827;
classDef other fill:#f3f4f6,stroke:#6b7280,color:#111827;
class Agent,Call,HITL otherFlat;
class Discover,ReadDoc knowledge;
class Direct flatMcp;
class U,Gate,Result other;
flowchart TD
U[User asks for external app data/action] --> Agent["<b>Flat agent</b><br/>needs external capability"]
Agent --> KS["<b>knowledge_search</b><br/>entry point for discovery"]
KS --> GMTD["<b>get_mcp_tool_documents</b><br/>MCP leg of search"]
GMTD --> GMC["<b>get_mcp_client</b><br/>current session + options"]
GMC --> MC["<b>McpClient</b><br/>lists and resolves tools"]
MC --> PolicyClient["<b>RmainPluginSettingsClient</b><br/>loads plugin inventory"]
PolicyClient --> Rmain["<b>ListUserPlugins</b><br/>rmain runtime config API"]
Rmain --> Policy["<b>PluginPolicy</b><br/>enablement wrapper"]
Policy --> Plugins["<b>RmainUserPlugins</b><br/>enabled gateway plugins"]
MC --> Provider["<b>RipplingGatewayMcpProvider</b><br/>provider implementation"]
Provider --> Token["<b>GatewayTokenClient</b><br/>client credentials token"]
Token --> GatewayToken["<b>MCP Gateway token endpoint</b><br/>returns bearer token"]
Provider --> Servers["<b>RipplingGatewayServer</b><br/>one per plugin"]
Plugins --> Servers
GatewayToken --> Servers
Servers --> Collection["<b>RipplingGatewayToolCollection</b><br/>session-scoped collection"]
Collection --> Cache["<b>StaleWhileRefreshCache</b><br/>fresh list or fallback"]
Cache --> Warmup["<b>list_gateway_tools_with_warmup</b><br/>cold connector retries"]
Warmup --> Session["<b>mcp_session</b><br/>streamable HTTP session"]
Session --> Gateway["<b>MCP Gateway proxy</b><br/>connector route"]
Gateway --> Upstream["<b>Upstream MCP server</b><br/>connector-owned"]
Upstream --> RawTools["<b>MCP Tool definitions</b><br/>raw mcp.types.Tool list"]
RawTools --> BuildTool["<b>McpTool objects</b><br/>qualified name + invoke"]
BuildTool --> LiveRepo["<b>McpLiveToolRepository</b><br/>per-user dynamic index"]
LiveRepo --> LiveIndex["<b>_McpLiveIndex</b><br/>embedding index"]
LiveIndex --> Matched["<b>McpTool matches</b><br/>top semantic results"]
GMTD --> OverlayRepo["<b>McpOverlayRepository</b><br/>static YAML query docs"]
OverlayRepo --> ToolDocs["<b>McpToolDoc</b><br/>overlays and enrichments"]
Matched --> Stored["<b>StoredSearchDocument</b><br/>agent-readable MCP docs"]
ToolDocs --> Stored
Stored --> Persist["<b>persist_mcp_tool_documents</b><br/>thread VFS"]
Persist --> Fetch["<b>fetch_document_and_analyse</b><br/>read selected doc"]
Fetch --> Agent
classDef coreMcp fill:#dbeafe,stroke:#2563eb,color:#111827;
classDef flatMcp fill:#ede9fe,stroke:#7c3aed,color:#111827;
classDef otherFlat fill:#dcfce7,stroke:#16a34a,color:#111827;
classDef knowledge fill:#fef3c7,stroke:#d97706,color:#111827;
classDef other fill:#f3f4f6,stroke:#6b7280,color:#111827;
class MC,Provider,Token,Servers,Collection,Cache,Warmup,Session,BuildTool,PolicyClient,Policy,Plugins coreMcp;
class KS,GMTD,LiveRepo,LiveIndex,OverlayRepo,ToolDocs,Stored,Persist,Fetch knowledge;
class Agent otherFlat;
class U,Rmain,GatewayToken,Gateway,Upstream,RawTools other;
This is the normal path when the discovered MCP tool is read-only by annotations. Yes from Tool found? is the straight-down natural path.
flowchart TD
Agent["<b>Flat agent</b><br/>has qualified tool name"] --> ExecPy["<b>execute_python</b><br/>runs Python sandbox"]
ExecPy --> Sandbox["<b>Sandbox call_tool</b><br/>stdio protocol request"]
Sandbox --> HandleTool["<b>_handle_tool_request</b><br/>host-side bridge"]
HandleTool --> GTC["<b>get_tool_client</b><br/>build composite client"]
GTC --> Composite["<b>CompositeToolClient</b><br/>backend + local + MCP"]
Composite --> ExternalClient["<b>ExternalMcpToolClient</b><br/>routes MCP names"]
ExternalClient --> HasTool["<b>has_tool</b><br/>parse McpToolName"]
HasTool --> Callable["<b>ExternalMcpCallableTool</b><br/>one qualified name"]
Callable --> GetTool["<b>McpClient.get_tool</b><br/>resolve actual tool"]
GetTool --> Found{Tool found?}
Found -->|Yes| Approval{Requires approval?}
Found -->|No| NotFound["<b>tool_not_found observation</b><br/>search once, then deflect"]
Approval -->|No| Invoke["<b>McpTool.invoke</b><br/>provider callback"]
Approval -->|Yes| Bounce["<b>FlatToolRetriableError</b><br/>use execute_action"]
Invoke --> OpenSession["<b>RipplingGatewayServer.session</b><br/>opens MCP stream"]
OpenSession --> ClientSession[ClientSession]
ClientSession --> Call["<b>session.call_tool</b><br/>raw name + args"]
Call --> Serialize["<b>serialize result</b><br/>JSON/text/content"]
Serialize --> McpResult["<b>McpToolResult</b><br/>value or failure"]
McpResult --> Observation["<b>_ExternalMcpObservation</b><br/>flat observation"]
Observation --> Agent
classDef coreMcp fill:#dbeafe,stroke:#2563eb,color:#111827;
classDef flatMcp fill:#ede9fe,stroke:#7c3aed,color:#111827;
classDef otherFlat fill:#dcfce7,stroke:#16a34a,color:#111827;
classDef knowledge fill:#fef3c7,stroke:#d97706,color:#111827;
classDef other fill:#f3f4f6,stroke:#6b7280,color:#111827;
class GetTool,Invoke,OpenSession,ClientSession,Serialize,McpResult coreMcp;
class ExternalClient,Callable,Observation,Bounce flatMcp;
class Agent,ExecPy,Sandbox,HandleTool,GTC,Composite otherFlat;
class NotFound knowledge;
class HasTool,Found,Approval,Call other;
This is the write/destructive/unknown-annotation path.
flowchart TD
Agent["<b>Flat agent</b><br/>direct call was approval-gated"] --> Prep["<b>Build Arrow row</b><br/>one external action request"]
Prep --> Row["<b>ExternalMcpActionRow</b><br/>tool_name, arguments_json, reason"]
Row --> ExecuteAction["<b>execute_action</b><br/>generic action tool"]
ExecuteAction --> Factory["<b>HitlFactory</b><br/>selects HITL class"]
Factory --> HITL["<b>ExternalMcpActionsHITL</b><br/>MCP approval target"]
HITL --> Load["<b>load_external_mcp_action</b><br/>shared loader"]
Load --> Rows["<b>load_action_rows</b><br/>read Arrow IPC table"]
Rows --> Validate["<b>Validate row</b><br/>Pydantic model"]
Validate --> Args["<b>Parse arguments_json</b><br/>must be JSON object"]
Args --> Lookup["<b>McpClient.get_tool</b><br/>verify tool exists"]
Lookup --> Found{Tool found?}
Found -->|Yes| ApprovalCheck{requires_approval is true?}
Found -->|No| Missing["<b>FlatToolRetriableError</b><br/>tool not enabled"]
ApprovalCheck -->|Yes| Preview["<b>ExternalMcpActionRowPreview</b><br/>user-facing preview"]
ApprovalCheck -->|No| DirectHint["<b>FlatToolRetriableError</b><br/>call directly from execute_python"]
Preview --> ActionInput["<b>ActionInputEvent</b><br/>Approve or Reject"]
ActionInput --> Stop["<b>FlatToolResponseSignal</b><br/>should_stop true"]
Stop --> User["<b>User approves</b><br/>next turn resumes"]
User --> Execute["<b>ExternalMcpActionsHITL.execute</b><br/>reload row and execute"]
Execute --> ExecuteTool["<b>McpClient.execute_tool</b><br/>resolve + invoke + display"]
ExecuteTool --> Invoke["<b>McpTool.invoke</b><br/>provider callback"]
Invoke --> Gateway["<b>MCP Gateway call</b><br/>session.call_tool"]
Gateway --> Result["<b>McpToolResult</b><br/>display string"]
Result --> Agent
classDef coreMcp fill:#dbeafe,stroke:#2563eb,color:#111827;
classDef flatMcp fill:#ede9fe,stroke:#7c3aed,color:#111827;
classDef otherFlat fill:#dcfce7,stroke:#16a34a,color:#111827;
classDef other fill:#f3f4f6,stroke:#6b7280,color:#111827;
class Lookup,ExecuteTool,Invoke,Result coreMcp;
class Row,HITL,Load,Validate,Args,Preview,Execute flatMcp;
class Agent,Prep,ExecuteAction,Factory,Rows,ActionInput,Stop otherFlat;
class Missing,DirectHint,User,Gateway,Found,ApprovalCheck other;
flowchart LR
Client["<b>McpClient</b><br/>facade"] --> Context["<b>McpProviderContext</b><br/>session/options"]
Client --> Provider["<b>RipplingGatewayMcpProvider</b><br/>provider"]
Provider -.implements.-> ProviderProtocol["<b>McpProvider</b><br/>protocol"]
Provider --> Collection["<b>RipplingGatewayToolCollection</b><br/>collection"]
Collection -.implements.-> CollectionProtocol["<b>McpToolCollection</b><br/>protocol"]
Collection --> Tool["<b>McpTool</b><br/>metadata + invoke"]
Tool --> Name["<b>McpToolName</b><br/>qualified parser"]
Tool --> Result["<b>McpToolResult</b><br/>value/failure"]
Provider --> Server["<b>RipplingGatewayServer</b><br/>server/session"]
Provider --> Token["<b>GatewayTokenClient</b><br/>token cache"]
Provider --> Cache["<b>StaleWhileRefreshCache</b><br/>list cache"]
Provider --> Policy["<b>PluginPolicy</b><br/>enablement"]
Policy --> Inventory["<b>RmainUserPlugins</b><br/>inventory"]
Inventory --> Plugin["<b>RmainUserPlugin</b><br/>one plugin"]
Plugin --> ServerInfo["<b>RmainGatewayServerInfo</b><br/>gateway metadata"]
classDef coreMcp fill:#dbeafe,stroke:#2563eb,color:#111827;
class Client,Context,Provider,ProviderProtocol,Collection,CollectionProtocol,Tool,Name,Result,Server,Token,Cache,Policy,Inventory,Plugin,ServerInfo coreMcp;
flowchart LR
Composite["<b>CompositeToolClient</b><br/>multi-client lookup"] --> ExtClient["<b>ExternalMcpToolClient</b><br/>MCP adapter"]
ExtClient --> Callable["<b>ExternalMcpCallableTool</b><br/>one tool name"]
Callable --> Observation["<b>_ExternalMcpObservation</b><br/>result wrapper"]
ExecuteAction["<b>execute_action</b><br/>action tool"] --> Factory["<b>HitlFactory</b><br/>target router"]
Factory --> Hitl["<b>ExternalMcpActionsHITL</b><br/>external_mcp handler"]
Hitl --> Row["<b>ExternalMcpActionRow</b><br/>tool + args"]
Row --> Preview["<b>ExternalMcpActionRowPreview</b><br/>preview formatter"]
KS["<b>knowledge_search</b><br/>search tool"] --> Adapter["<b>get_mcp_tool_documents</b><br/>MCP adapter"]
Adapter --> LiveRepo["<b>McpLiveToolRepository</b><br/>dynamic index"]
LiveRepo --> LiveIndex["<b>_McpLiveIndex</b><br/>tool embeddings"]
Adapter --> OverlayRepo["<b>McpOverlayRepository</b><br/>YAML index"]
OverlayRepo --> ToolDoc["<b>McpToolDoc</b><br/>YAML model"]
Adapter --> Stored["<b>StoredSearchDocument</b><br/>persisted doc"]
classDef flatMcp fill:#ede9fe,stroke:#7c3aed,color:#111827;
classDef otherFlat fill:#dcfce7,stroke:#16a34a,color:#111827;
classDef knowledge fill:#fef3c7,stroke:#d97706,color:#111827;
class ExtClient,Callable,Observation,Hitl,Row,Preview flatMcp;
class Composite,ExecuteAction,Factory otherFlat;
class KS,Adapter,LiveRepo,LiveIndex,OverlayRepo,ToolDoc,Stored knowledge;