Skip to content

Commit 9dda0e2

Browse files
authored
Merge pull request #289 from deploystackio/main
prod release
2 parents c77adba + 94d722f commit 9dda0e2

File tree

4 files changed

+150
-53
lines changed

4 files changed

+150
-53
lines changed

development/satellite/hierarchical-router.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ Instead of exposing all tools directly, the satellite exposes only 2 meta-tools:
8080
- 37.5% of 200k context consumed
8181

8282
**After (Hierarchical):**
83-
- 2 meta-tools × 175 tokens = 350 tokens
84-
- 0.175% of 200k context consumed
85-
- **99.5% reduction**
83+
- 2 tools × 686 tokens = 1372 tokens consumed
84+
- Result: 0.686% of context window used
85+
- **Token Reduction: 98.3%**
8686

8787
## Meta-Tool Specifications
8888

@@ -527,8 +527,8 @@ Next time the client calls `discover_mcp_tools`, the new tools are automatically
527527
| Metric | Traditional | Hierarchical | Reduction |
528528
|--------|-------------|--------------|-----------|
529529
| Tools Exposed | 150 | 2 | 98.7% |
530-
| Tokens Consumed | 75,000 | 350 | 99.5% |
531-
| Context Available | 62.5% | 99.8% | +37.3% |
530+
| Tokens Consumed | 75,000 | 1372 | 98.2% |
531+
| Context Available | 62.5% | 99.3% | +36.8% |
532532

533533
### Search Performance
534534

@@ -541,8 +541,8 @@ Next time the client calls `discover_mcp_tools`, the new tools are automatically
541541

542542
**Claude Code Example:**
543543
- Before: 82,000 tokens (41%) consumed by MCP tools
544-
- After: 350 tokens (0.175%) consumed by meta-tools
545-
- Result: **81,650 tokens freed for actual work**
544+
- After: 1372 tokens (0.686%) consumed by meta-tools
545+
- Result: **80,628 tokens freed for actual work**
546546

547547
## Implementation Status
548548

development/satellite/oauth-authentication.mdx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,94 @@ const response = await fetch(`${backendUrl}/api/oauth2/introspect`, {
268268
- `active: false` - Token invalid, return authentication error
269269
- Team context includes: team_id, team_name, team_role, team_permissions
270270

271+
## Session Management and Security Model
272+
273+
### MCP Sessions vs OAuth Authentication
274+
275+
The satellite implements a two-layer security model that separates authentication from session management:
276+
277+
**Authentication Layer (OAuth Bearer Token):**
278+
- Primary security mechanism for all requests
279+
- Validates user identity, team membership, and permissions
280+
- Enforced by authentication middleware before session handling
281+
- Team isolation enforced at this layer via token introspection
282+
283+
**Session Layer (MCP Session ID):**
284+
- Transport-level identifier for HTTP/SSE connection routing
285+
- NOT a security credential - purely for protocol state management
286+
- Can be safely reused because security comes from Bearer token
287+
- Managed by StreamableHTTPServerTransport from MCP SDK
288+
289+
### Session Resurrection After Satellite Restart
290+
291+
When a satellite restarts (deployments, updates, crashes), MCP sessions are lost because they live in memory. The satellite implements transparent session resurrection to avoid forcing users to manually reconnect:
292+
293+
**How Session Resurrection Works:**
294+
1. Client sends request with old session ID (from before restart)
295+
2. Satellite validates Bearer token FIRST (authentication layer)
296+
3. If session ID is stale, satellite creates new Server + Transport with same session ID
297+
4. Bootstrap transport with synthetic `initialize` request
298+
5. Process actual client request normally
299+
6. Client continues without reconnection
300+
301+
**Implementation Details:**
302+
```typescript
303+
// Authentication happens FIRST (line 558 in mcp-server-wrapper.ts)
304+
const authHeader = request.headers['authorization'];
305+
const token = authHeader?.replace(/^Bearer\s+/i, '');
306+
307+
if (!token) {
308+
return reply.status(401).send({
309+
jsonrpc: '2.0',
310+
error: { code: -32001, message: 'Authentication required' },
311+
id: null
312+
});
313+
}
314+
315+
// Validate token via introspection BEFORE session handling
316+
const introspectionResult = await this.tokenIntrospectionService.validateToken(token);
317+
318+
if (!introspectionResult.valid) {
319+
return reply.status(401).send({
320+
jsonrpc: '2.0',
321+
error: { code: -32002, message: 'Invalid token' },
322+
id: null
323+
});
324+
}
325+
326+
// NOW handle session resurrection (lines 616-722)
327+
const sessionId = request.headers['mcp-session-id'];
328+
const existingTransport = this.transports.get(sessionId);
329+
330+
if (!existingTransport && sessionId) {
331+
// Create new Server + Transport with same session ID
332+
server = new Server({ name: 'deploystack-satellite', version: '1.0.0' });
333+
334+
transport = new StreamableHTTPServerTransport({
335+
sessionIdGenerator: () => sessionId, // Reuse old session ID
336+
onsessioninitialized: (restoredSessionId) => {
337+
this.transports.set(restoredSessionId, { transport, server });
338+
}
339+
});
340+
341+
await server.connect(transport);
342+
343+
// Bootstrap transport with synthetic initialize request
344+
const syntheticInitRequest = {
345+
jsonrpc: '2.0',
346+
id: 0,
347+
method: 'initialize',
348+
params: {
349+
protocolVersion: '2024-11-05',
350+
capabilities: {},
351+
clientInfo: { name: 'resurrected-session', version: '1.0.0' }
352+
}
353+
};
354+
355+
await transport.handleRequest(request.raw, mockRes, syntheticInitRequest);
356+
}
357+
```
358+
271359
## Team-Aware Tool Discovery
272360

273361
### Tool Filtering Implementation

package-lock.json

Lines changed: 53 additions & 44 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
"dependencies": {},
1313
"devDependencies": {
1414
"@semantic-release/github": "^12.0.2",
15-
"@types/node": "24.9.1",
15+
"@types/node": "24.10.1",
1616
"markdownlint-cli": "^0.45.0",
17-
"markdownlint-cli2": "^0.18.1",
17+
"markdownlint-cli2": "^0.19.0",
1818
"typescript": "5.9.3"
1919
}
2020
}

0 commit comments

Comments
 (0)