|
| 1 | +# Dynamic Tool Loading |
| 2 | + |
| 3 | +## The Problem |
| 4 | + |
| 5 | +The SonarQube MCP Server needs to perform several initialization steps before tools can be used: |
| 6 | +- Check SonarQube server version compatibility |
| 7 | +- Download and synchronize analyzer plugins (can take a minute) |
| 8 | +- Initialize the SonarLint backend with those analyzers |
| 9 | +- Load and register all tools |
| 10 | + |
| 11 | +In a traditional approach, the server blocks during all these steps, making users wait a long time before any functionality is available. The slow operations (plugin downloads, backend initialization) prevent even simple tools from being used. |
| 12 | + |
| 13 | +## The Solution |
| 14 | + |
| 15 | +The server starts immediately and loads tools in two phases: |
| 16 | + |
| 17 | +**Phase 1: Load Backend-Independent Tools First** |
| 18 | +- These tools only need the REST API to function |
| 19 | +- No analyzers or backend initialization required |
| 20 | +- Most of the functionality becomes available very quickly |
| 21 | + |
| 22 | +**Phase 2: Load Backend-Dependent Tools After Heavy Initialization** |
| 23 | +- These tools require the SonarLint backend (code analysis) |
| 24 | +- Loaded after plugin synchronization and backend setup complete |
| 25 | +- Only a small portion of total functionality |
| 26 | + |
| 27 | +This approach provides the majority of tools almost immediately while the slow initialization happens in the background. Users can start working right away instead of staring at a loading screen. |
| 28 | + |
| 29 | +## Architecture Flow |
| 30 | + |
| 31 | +``` |
| 32 | +Server Startup (< 1 second) |
| 33 | + │ |
| 34 | + ├─ Create basic services |
| 35 | + ├─ Setup transport (HTTP/stdio) |
| 36 | + └─ Start MCP server (0 tools initially) |
| 37 | + │ |
| 38 | + ▼ |
| 39 | +Background Thread Starts |
| 40 | + │ |
| 41 | + ├─ Check SonarQube version |
| 42 | + │ |
| 43 | + ▼ |
| 44 | +┌──────────────────────────────────────┐ |
| 45 | +│ Phase 1: Independent Tools │ |
| 46 | +│ │ |
| 47 | +│ ✅ Platform-specific tools │ |
| 48 | +│ ✅ Common tools │ |
| 49 | +│ │ |
| 50 | +│ → Tools registered │ |
| 51 | +│ → Client notification sent │ |
| 52 | +│ → Users can start working! │ |
| 53 | +└──────────────────────────────────────┘ |
| 54 | + │ |
| 55 | + ▼ |
| 56 | +Heavy Operations (takes time) |
| 57 | + │ |
| 58 | + ├─ Synchronize analyzer plugins |
| 59 | + ├─ Download missing analyzers |
| 60 | + ├─ Initialize SonarLint backend |
| 61 | + └─ Load analyzers |
| 62 | + │ |
| 63 | + ▼ |
| 64 | +┌──────────────────────────────────────┐ |
| 65 | +│ Phase 2: Dependent Tools │ |
| 66 | +│ │ |
| 67 | +│ ✅ Analysis tools │ |
| 68 | +│ ✅ Backend-dependent tools │ |
| 69 | +│ │ |
| 70 | +│ → Tools registered │ |
| 71 | +│ → Client notification sent │ |
| 72 | +│ → Full functionality available │ |
| 73 | +└──────────────────────────────────────┘ |
| 74 | + │ |
| 75 | + ▼ |
| 76 | +Initialization Complete |
| 77 | +``` |
| 78 | + |
| 79 | +## Client Notifications |
| 80 | + |
| 81 | +The MCP protocol's `tools/list_changed` notification mechanism ensures clients stay synchronized: |
| 82 | + |
| 83 | +- **Client connects before Phase 1:** Receives initial empty list, then notification when tools are added |
| 84 | +- **Client connects after Phase 1:** Receives tools immediately during handshake |
| 85 | +- **Client working during Phase 2:** Receives notification when analysis tools become available |
| 86 | + |
| 87 | +The server attempts to notify connected clients after each phase. If no client is connected yet, notifications are silently ignored - the client will receive the full tool list during its initialization handshake. |
| 88 | + |
| 89 | +## Tool Classification |
| 90 | + |
| 91 | +When adding a new tool, determine which phase it belongs to: |
| 92 | + |
| 93 | +**Backend-Independent (Phase 1)** - Add to `loadBackendIndependentTools()` |
| 94 | +- Only requires `ServerApi` (REST API wrapper) |
| 95 | +- No code analysis needed |
| 96 | +- Examples: REST API wrappers, data retrieval, configuration management |
| 97 | + |
| 98 | +**Backend-Dependent (Phase 2)** - Add to `loadBackendDependentTools()` |
| 99 | +- Requires SonarLint backend |
| 100 | +- Needs analyzer plugins |
| 101 | +- Examples: any tool performing code analysis or requiring language analyzers |
| 102 | + |
| 103 | +## Error Handling |
| 104 | + |
| 105 | +The architecture provides graceful degradation: |
| 106 | + |
| 107 | +- **Phase 1 fails:** No tools available (same as traditional approach) |
| 108 | +- **Phase 2 fails:** Independent tools continue working, only backend-dependent features become unavailable |
| 109 | +- **Notification fails:** Silently ignored, client receives tools during handshake |
| 110 | + |
| 111 | +Even if plugin downloads fail or the backend cannot initialize, users can still access the majority of functionality. |
| 112 | + |
| 113 | +## Testing |
| 114 | + |
| 115 | +The test harness calls `server.waitForInitialization()` to ensure both phases complete before tests run. This guarantees tests always see the complete tool set and aren't affected by timing variations. |
| 116 | + |
| 117 | +Additionally, the harness uses Awaitility to wait for the client transport to be ready, eliminating race conditions where tool notifications might fail because the client hasn't fully established its connection yet. |
| 118 | + |
| 119 | +## Benefits |
| 120 | + |
| 121 | +- **Faster time-to-first-tool:** Users can start working almost immediately |
| 122 | +- **Better user experience:** No long waiting period for basic functionality |
| 123 | +- **Fault tolerance:** Backend failures don't break everything |
| 124 | +- **Progressive enhancement:** Functionality appears as it becomes ready |
| 125 | +- **Protocol compliant:** Uses standard MCP notification mechanism |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +**Related Files:** |
| 130 | +- `src/main/java/org/sonarsource/sonarqube/mcp/SonarQubeMcpServer.java` - Main implementation |
| 131 | +- `src/main/java/org/sonarsource/sonarqube/mcp/tools/ToolExecutor.java` - Waits for initialization |
| 132 | +- `src/test/java/org/sonarsource/sonarqube/mcp/harness/SonarQubeMcpServerTestHarness.java` - Test synchronization |
| 133 | + |
0 commit comments