Skip to content

Instantly share code, notes, and snippets.

@mharris717
Last active June 16, 2026 23:02
Show Gist options
  • Select an option

  • Save mharris717/f0cf51557b03386a47338305f2aeb66c to your computer and use it in GitHub Desktop.

Select an option

Save mharris717/f0cf51557b03386a47338305f2aeb66c to your computer and use it in GitHub Desktop.
Rbots MCP tool discovery and call flow

Rbots MCP Tool Discovery And Call Flow

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.

1. High-Level Overview

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;
Loading

2. Tool Discovery Detail

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;
Loading

3. Read-Only Tool Call Detail

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;
Loading

4. Approval-Required Tool Call Detail

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;
Loading

5. Entity Diagram: Core MCP

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;
Loading

6. Entity Diagram: Flat MCP And Knowledge

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;
Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment