Skip to content

Commit 46fe339

Browse files
committed
MCP-198 Immediately start up the MCP and dynamically load tools
1 parent 409f6a1 commit 46fe339

File tree

7 files changed

+495
-93
lines changed

7 files changed

+495
-93
lines changed

docs/dynamic-tool-loading.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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

Comments
 (0)