Skip to content

Commit 4ede0d1

Browse files
committed
LSP client fixes, notification handler, index-and-map, lsp/ and docs
- LSP: --stdio, Windows spawn, init caps, normalize DocumentSymbol/SymbolInformation - LSP notification handler for window/logMessage (DEBUG_LSP_NOTIFICATIONS=1) - MCP fallback when LSP returns no symbols; shared MCP client in symbols.ts - Symbol mapping: kind strings, LSP SymbolKind fallback in mapping.ts - lsp/: dedicated npm setup, sysml-v2-lsp, test-server.mjs - scripts: index-and-map.mjs, debug-lsp-symbols.mjs, compare MCP vs LSP - docs: MCP_INTERACTION_GUIDE, MCP_CLIENT_TEMPLATE_ANALYSIS, PLAN.md, SKILL_DESIGN_PATTERNS, TOOLS - AGENTS.md, CLAUDE.md Made-with: Cursor
1 parent ad33a57 commit 4ede0d1

25 files changed

Lines changed: 2042 additions & 54 deletions

AGENTS.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!-- gitnexus:start -->
2+
# GitNexus — Code Intelligence
3+
4+
This project is indexed by GitNexus as **codebase-sysmledgraph-new** (208 symbols, 215 relationships, 0 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
5+
6+
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
7+
8+
## Always Do
9+
10+
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
11+
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
12+
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
13+
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
14+
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
15+
16+
## When Debugging
17+
18+
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
19+
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
20+
3. `READ gitnexus://repo/codebase-sysmledgraph-new/process/{processName}` — trace the full execution flow step by step
21+
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
22+
23+
## When Refactoring
24+
25+
- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`.
26+
- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code.
27+
- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed.
28+
29+
## Never Do
30+
31+
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
32+
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
33+
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
34+
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
35+
36+
## Tools Quick Reference
37+
38+
| Tool | When to use | Command |
39+
|------|-------------|---------|
40+
| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` |
41+
| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` |
42+
| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` |
43+
| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` |
44+
| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` |
45+
| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` |
46+
47+
## Impact Risk Levels
48+
49+
| Depth | Meaning | Action |
50+
|-------|---------|--------|
51+
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
52+
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
53+
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
54+
55+
## Resources
56+
57+
| Resource | Use for |
58+
|----------|---------|
59+
| `gitnexus://repo/codebase-sysmledgraph-new/context` | Codebase overview, check index freshness |
60+
| `gitnexus://repo/codebase-sysmledgraph-new/clusters` | All functional areas |
61+
| `gitnexus://repo/codebase-sysmledgraph-new/processes` | All execution flows |
62+
| `gitnexus://repo/codebase-sysmledgraph-new/process/{name}` | Step-by-step execution trace |
63+
64+
## Self-Check Before Finishing
65+
66+
Before completing any code modification task, verify:
67+
1. `gitnexus_impact` was run for all modified symbols
68+
2. No HIGH/CRITICAL risk warnings were ignored
69+
3. `gitnexus_detect_changes()` confirms changes match expected scope
70+
4. All d=1 (WILL BREAK) dependents were updated
71+
72+
## CLI
73+
74+
- Re-index: `npx gitnexus analyze`
75+
- Check freshness: `npx gitnexus status`
76+
- Generate docs: `npx gitnexus wiki`
77+
78+
<!-- gitnexus:end -->

CLAUDE.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!-- gitnexus:start -->
2+
# GitNexus — Code Intelligence
3+
4+
This project is indexed by GitNexus as **codebase-sysmledgraph-new** (208 symbols, 215 relationships, 0 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
5+
6+
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
7+
8+
## Always Do
9+
10+
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
11+
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
12+
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
13+
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
14+
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
15+
16+
## When Debugging
17+
18+
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
19+
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
20+
3. `READ gitnexus://repo/codebase-sysmledgraph-new/process/{processName}` — trace the full execution flow step by step
21+
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
22+
23+
## When Refactoring
24+
25+
- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`.
26+
- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code.
27+
- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed.
28+
29+
## Never Do
30+
31+
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
32+
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
33+
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
34+
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
35+
36+
## Tools Quick Reference
37+
38+
| Tool | When to use | Command |
39+
|------|-------------|---------|
40+
| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` |
41+
| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` |
42+
| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` |
43+
| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` |
44+
| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` |
45+
| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` |
46+
47+
## Impact Risk Levels
48+
49+
| Depth | Meaning | Action |
50+
|-------|---------|--------|
51+
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
52+
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
53+
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
54+
55+
## Resources
56+
57+
| Resource | Use for |
58+
|----------|---------|
59+
| `gitnexus://repo/codebase-sysmledgraph-new/context` | Codebase overview, check index freshness |
60+
| `gitnexus://repo/codebase-sysmledgraph-new/clusters` | All functional areas |
61+
| `gitnexus://repo/codebase-sysmledgraph-new/processes` | All execution flows |
62+
| `gitnexus://repo/codebase-sysmledgraph-new/process/{name}` | Step-by-step execution trace |
63+
64+
## Self-Check Before Finishing
65+
66+
Before completing any code modification task, verify:
67+
1. `gitnexus_impact` was run for all modified symbols
68+
2. No HIGH/CRITICAL risk warnings were ignored
69+
3. `gitnexus_detect_changes()` confirms changes match expected scope
70+
4. All d=1 (WILL BREAK) dependents were updated
71+
72+
## CLI
73+
74+
- Re-index: `npx gitnexus analyze`
75+
- Check freshness: `npx gitnexus status`
76+
- Generate docs: `npx gitnexus wiki`
77+
78+
<!-- gitnexus:end -->
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# MCP Client Template Analysis
2+
3+
Analysis of [andrea9293/mcp-client-template](https://github.com/andrea9293/mcp-client-template) patterns, protocol, and behavior.
4+
5+
## Architecture
6+
7+
**Two-tier web app:**
8+
- **Frontend** (Vite + TypeScript): Web UI for managing servers and sending commands
9+
- **Backend** (Express + TypeScript): REST API that wraps MCP SDK client operations
10+
11+
**Data persistence:**
12+
- `data/mcp-servers.json` — Server configurations (command, type, args)
13+
- `data/auth-tokens.json` — OAuth tokens per server
14+
15+
## Protocol & Transport
16+
17+
### Transport Types Supported
18+
19+
1. **`stdio`** — Newline-delimited JSON over stdin/stdout
20+
- Uses `StdioClientTransport` from SDK
21+
- Spawns process: `{ command, args }`
22+
- Example: `{ type: 'stdio', command: 'node', args: ['server.js'] }`
23+
24+
2. **`httpstream`** — Streamable HTTP (long-polling or streaming)
25+
- Uses `StreamableHTTPClientTransport` from SDK
26+
- Command is the server URL
27+
- Example: `{ type: 'httpstream', command: 'https://mcp-server.example.com' }`
28+
29+
3. **`sse`** — Server-Sent Events
30+
- Uses `SSEClientTransport` from SDK
31+
- Command is the server URL
32+
- Example: `{ type: 'sse', command: 'https://mcp-server.example.com/events' }`
33+
34+
### MCP SDK Usage
35+
36+
```typescript
37+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
38+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
39+
40+
const client = new Client({ name: 'my-client', version: '1.0.0' });
41+
const transport = new StdioClientTransport({ command: 'node', args: ['server.js'] });
42+
await client.connect(transport); // ← initialize happens here
43+
// Then: client.request({ method: 'tools/list' }, schema)
44+
```
45+
46+
**Key point:** The SDK `Client.connect(transport)` handles the full MCP lifecycle:
47+
1. Spawns process (for stdio) or opens connection (for HTTP/SSE)
48+
2. Sends `initialize` request
49+
3. Waits for `initialize` response
50+
4. Sends `notifications/initialized`
51+
5. Client is ready for `tools/list`, `tools/call`, etc.
52+
53+
## Connection Pattern
54+
55+
**Per-request connection:**
56+
- Each `/mcp` API call creates a new `Client` + `Transport`
57+
- Connects, makes request(s), then closes
58+
- No connection pooling or reuse
59+
60+
**Why:** Simpler error handling; each request is isolated. Trade-off: slower (reconnect cost per request).
61+
62+
## OAuth2.1 Flow
63+
64+
1. **First connect attempt**`UnauthorizedError` thrown
65+
2. **Extract auth URL** from `OAuthClientProvider.redirectToAuthorization()`
66+
3. **Return 401 with `authUrl`** to frontend
67+
4. **User completes OAuth** in browser → redirects to `/oauth/callback`
68+
5. **Backend calls `transport.finishAuth(code)`**
69+
6. **Reconnect** with authenticated transport
70+
7. **Save tokens** to `auth-tokens.json` for future use
71+
72+
**Token reuse:** On subsequent connects, if tokens exist, they're loaded into the OAuth provider before connecting.
73+
74+
## API Endpoints
75+
76+
### `POST /add-server`
77+
- Adds server config to `mcp-servers.json`
78+
- Body: `{ id, command, type, args }`
79+
- Persists immediately
80+
81+
### `GET /servers`
82+
- Lists all servers from `mcp-servers.json`
83+
- Returns array of `{ name, command, type, args }`
84+
85+
### `POST /mcp`
86+
- **Body:** `{ server: string, command: string, args?: any[] }`
87+
- **Command format:** `"list"` or `"call <toolName> [json-args]"`
88+
- **Flow:**
89+
1. Load server config
90+
2. `autoDetectTransport()` → create Client + Transport
91+
3. `client.connect()` → initialize handshake
92+
4. Parse command:
93+
- `"list"``client.request({ method: 'tools/list' }, ListToolsResultSchema)`
94+
- `"call <name> [args]"``client.request({ method: 'tools/call', params: { name, arguments } }, CallToolResultSchema)`
95+
5. Return result as JSON
96+
6. Close transport
97+
98+
### `POST /oauth/callback`
99+
- Receives OAuth code from frontend
100+
- Calls `transport.finishAuth(code)`
101+
- Reconnects with authenticated transport
102+
- Saves tokens
103+
104+
## Key Patterns
105+
106+
### 1. Transport Auto-Detection
107+
108+
```typescript
109+
async function autoDetectTransport(serverConfig) {
110+
const client = new Client({ name: serverConfig.name, version: '1.0.0' });
111+
const oauthProvider = new InMemoryOAuthProvider();
112+
113+
if (type === 'stdio') {
114+
const transport = new StdioClientTransport({ command, args });
115+
await client.connect(transport);
116+
} else if (type === 'httpstream' || type === 'sse') {
117+
const TransportClass = type === 'httpstream'
118+
? StreamableHTTPClientTransport
119+
: SSEClientTransport;
120+
const transport = new TransportClass(new URL(command), { authProvider: oauthProvider });
121+
await client.connect(transport);
122+
}
123+
return { client, transport };
124+
}
125+
```
126+
127+
**Pattern:** Factory that creates the right transport based on `type`, handles OAuth for HTTP transports.
128+
129+
### 2. Request Schema Validation
130+
131+
Uses Zod schemas from SDK:
132+
- `ListToolsResultSchema` for `tools/list`
133+
- `CallToolResultSchema` for `tools/call`
134+
- `LoggingMessageNotificationSchema` for notifications
135+
136+
**Pattern:** Type-safe request/response validation via SDK schemas.
137+
138+
### 3. Error Handling
139+
140+
- **UnauthorizedError** → Return 401 with `authUrl` for OAuth flow
141+
- **Other errors** → Return 500 with error details
142+
- **Connection errors** → Caught and returned as JSON
143+
144+
**Pattern:** HTTP status codes map to MCP/auth errors; frontend handles 401 specially.
145+
146+
### 4. Notification Handling
147+
148+
```typescript
149+
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
150+
// Log notification (template doesn't do much here)
151+
});
152+
```
153+
154+
**Pattern:** Register handlers for server notifications (e.g. `logging/message`); template logs them.
155+
156+
## Comparison with Our LSP Client
157+
158+
| Aspect | MCP Template | Our LSP Client |
159+
|--------|-------------|----------------|
160+
| **SDK** | Uses `@modelcontextprotocol/sdk` Client | Custom Content-Length transport |
161+
| **Framing** | Newline-delimited (SDK default) | Content-Length (LSP-style) |
162+
| **Lifecycle** | `client.connect()` handles initialize | Manual: `initialize``initialized` → requests |
163+
| **Connection** | Per-request (new client each time) | Shared client (reused across requests) |
164+
| **Protocol** | MCP (`tools/list`, `tools/call`) | LSP (`textDocument/documentSymbol`) |
165+
| **Transport** | stdio, HTTP, SSE | stdio only (Content-Length) |
166+
| **OAuth** | Full OAuth2.1 support | None (LSP doesn't use OAuth) |
167+
168+
## Takeaways for Our Codebase
169+
170+
1. **SDK vs Custom:** We can't use SDK `StdioClientTransport` because sysml-v2-lsp uses Content-Length, not newline-delimited. Our custom transport is correct.
171+
172+
2. **Connection Reuse:** Template creates per-request connections; we reuse a shared client. Our approach is better for performance (no reconnect cost), but we need to handle errors/restarts.
173+
174+
3. **Lifecycle:** Template relies on SDK's `connect()` to do initialize; we do it manually. Both are valid—SDK abstracts it, we have explicit control.
175+
176+
4. **Error Handling:** Template maps MCP errors to HTTP status codes; we throw/return errors directly. Our approach fits our use case (CLI/scripts, not REST API).
177+
178+
5. **OAuth:** Template has full OAuth flow; we don't need it for LSP (LSP doesn't use OAuth).
179+
180+
## What We Should Adopt
181+
182+
1. **Notification handlers:** If the LSP server sends notifications (e.g. `window/logMessage`), we should register handlers instead of ignoring them.
183+
184+
2. **Request timeout:** Template doesn't show explicit timeouts, but we added 30s timeout—good practice.
185+
186+
3. **Error mapping:** Template maps `UnauthorizedError` to 401; we could map LSP errors to more specific error types.
187+
188+
4. **Connection retry:** Template doesn't retry; we could add retry logic for transient failures.
189+
190+
## What We Shouldn't Adopt
191+
192+
1. **Per-request connections:** Too slow for indexing (many files). Our shared client is better.
193+
194+
2. **REST API wrapper:** We're a CLI/library, not a web service. Direct function calls are appropriate.
195+
196+
3. **OAuth:** Not needed for LSP protocol.

0 commit comments

Comments
 (0)