Skip to content

Commit 11e3b59

Browse files
authored
Merge pull request #29 from Beyond-Better/staging
Client session tracking; improved types
2 parents a4b52c5 + 3aa0b7b commit 11e3b59

File tree

14 files changed

+338
-42
lines changed

14 files changed

+338
-42
lines changed

deno.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@beyondbetter/bb-mcp-server",
3-
"version": "0.1.16",
3+
"version": "0.1.17",
44
"description": "Comprehensive library for building Deno-based MCP servers",
55
"license": "MIT",
66
"copyright": "2025 - Beyond Better <charlie@beyondbetter.app>",

src/lib/server/AppServer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export class AppServer {
111111
// credentialStore: appDependencies.credentialStore,
112112
// });
113113
}
114+
114115
const resolvedDependencies: AppServerDependencies = await getAllDependencies(
115116
appDependencies,
116117
);
@@ -247,6 +248,7 @@ export class AppServer {
247248

248249
// Create HTTP server if we have the dependencies
249250
if (this.dependencies.httpServerConfig) {
251+
this._logger.info('AppServer: customEndpoints...', this.dependencies.customEndpoints);
250252
this.httpServer = new HttpServer({
251253
logger: this._logger,
252254
beyondMcpServer: this.dependencies.beyondMcpServer,
@@ -256,6 +258,7 @@ export class AppServer {
256258
workflowRegistry: this.dependencies.workflowRegistry,
257259
httpServerConfig: this.dependencies.httpServerConfig,
258260
docsEndpointHandler: this.dependencies.docsEndpointHandler,
261+
customEndpoints: this.dependencies.customEndpoints,
259262
});
260263

261264
// Start HTTP server (handles MCP via /mcp endpoint)

src/lib/server/BeyondMcpServer.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
type CreateMessageResult,
4343
type ElicitInputRequest,
4444
type ElicitInputResult,
45+
type SendNotificationRequest,
4546
type ToolDefinition,
4647
type ToolHandler,
4748
ToolHandlerMode,
@@ -233,6 +234,13 @@ export class BeyondMcpServer {
233234
return this.sdkMcpServer;
234235
}
235236

237+
/**
238+
* Get the transport manager instance
239+
*/
240+
getTransportManager(): TransportManager {
241+
return this.transportManager;
242+
}
243+
236244
/**
237245
* Execute an operation within the context of an authenticated user
238246
*/
@@ -361,18 +369,28 @@ export class BeyondMcpServer {
361369
/**
362370
* MCP SDK integration methods
363371
*/
364-
async createMessage(request: CreateMessageRequest): Promise<CreateMessageResult> {
372+
async createMessage(
373+
request: CreateMessageRequest,
374+
sessionId?: string,
375+
): Promise<CreateMessageResult> {
376+
if (!this.mcpSDKHelpers) {
377+
throw new Error('BeyondMcpServer not initialized. Call initialize() first.');
378+
}
379+
return await this.mcpSDKHelpers.createMessage(request, sessionId);
380+
}
381+
382+
async elicitInput(request: ElicitInputRequest, sessionId?: string): Promise<ElicitInputResult> {
365383
if (!this.mcpSDKHelpers) {
366384
throw new Error('BeyondMcpServer not initialized. Call initialize() first.');
367385
}
368-
return await this.mcpSDKHelpers.createMessage(request);
386+
return await this.mcpSDKHelpers.elicitInput(request, sessionId);
369387
}
370388

371-
async elicitInput(request: ElicitInputRequest): Promise<ElicitInputResult> {
389+
async sendNotification(request: SendNotificationRequest, sessionId?: string): Promise<void> {
372390
if (!this.mcpSDKHelpers) {
373391
throw new Error('BeyondMcpServer not initialized. Call initialize() first.');
374392
}
375-
return await this.mcpSDKHelpers.elicitInput(request);
393+
return await this.mcpSDKHelpers.sendNotification(request, sessionId);
376394
}
377395

378396
/**

src/lib/server/DependencyHelpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ export async function getAllDependencies(
860860
consumerDeps.oauthConsumer, // Pass OAuth consumer for third-party auth
861861
consumerDeps.thirdpartyApiClient, // Pass API client for token refresh
862862
);
863+
863864
const transportManager = overrides.transportManager || getTransportManager(
864865
configManager,
865866
logger,
@@ -881,6 +882,10 @@ export async function getAllDependencies(
881882
const docsEndpointHandler = overrides.docsEndpointHandler ||
882883
getDocsEndpointHandler(docsEndpointConfig, logger);
883884

885+
if (overrides.customEndpoints) {
886+
consumerDeps.customEndpoints = overrides.customEndpoints;
887+
}
888+
884889
// Create MCP server (either from class or default)
885890
const serverConfig = overrides.serverConfig || {
886891
name: configManager.get('SERVER_NAME', 'generic-mcp-server'),

src/lib/server/HttpServer.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { OAuthProvider } from '../auth/OAuthProvider.ts';
1919
import type { OAuthConsumer } from '../auth/OAuthConsumer.ts';
2020
import type { WorkflowRegistry } from '../workflows/WorkflowRegistry.ts';
2121
import type { DocsEndpointHandler } from './DocsEndpointHandler.ts';
22+
import type { CustomEndpoints } from './ServerTypes.ts';
2223
import { OAuthEndpoints } from './OAuthEndpoints.ts';
2324
import { BeyondMcpServer } from './BeyondMcpServer.ts';
2425
import { APIRouter } from './APIRouter.ts';
@@ -73,10 +74,7 @@ export interface HttpServerDependencies {
7374
/** Documentation endpoint handler (optional) */
7475
docsEndpointHandler?: DocsEndpointHandler | undefined;
7576
/** Custom endpoint handlers */
76-
customEndpoints?: Array<{
77-
path: string;
78-
handle(request: Request): Promise<Response>;
79-
}>;
77+
customEndpoints?: CustomEndpoints | undefined;
8078
}
8179

8280
/**
@@ -100,10 +98,7 @@ export class HttpServer {
10098
private corsHandler: CORSHandler;
10199
private errorPages: ErrorPages;
102100
private docsHandler: DocsEndpointHandler | undefined;
103-
private customEndpoints: Array<{
104-
path: string;
105-
handle(request: Request): Promise<Response>;
106-
}>;
101+
private customEndpoints: CustomEndpoints | undefined;
107102

108103
// Integration components
109104
private beyondMcpServer: BeyondMcpServer;
@@ -150,11 +145,11 @@ export class HttpServer {
150145
hostname: this.httpServerConfig.hostname,
151146
signal: this.abortController.signal,
152147
onListen: ({ port, hostname }) => {
153-
this.logger.info(`HTTP server running on http://${hostname}:${port}`);
148+
this.logger.info(`HttpServer: HTTP server running on http://${hostname}:${port}`);
154149
},
155150
}, (request: Request) => this.handleRequest(request));
156151

157-
this.server.finished.then(() => this.logger.info('HTTP server closed'));
152+
this.server.finished.then(() => this.logger.info('HttpServer: HTTP server closed'));
158153
}
159154

160155
/**
@@ -164,7 +159,7 @@ export class HttpServer {
164159
if (this.server) {
165160
this.abortController.abort('stopping server');
166161
this.server = undefined;
167-
this.logger.info('HTTP server stopped');
162+
this.logger.info('HttpServer: HTTP server stopped');
168163
}
169164
}
170165

@@ -176,7 +171,7 @@ export class HttpServer {
176171
const path = url.pathname;
177172
const method = request.method;
178173

179-
this.logger.debug(`HTTP request: ${method} ${path}`);
174+
this.logger.debug(`HttpServer: HTTP request: ${method} ${path}`);
180175

181176
try {
182177
// Handle CORS preflight
@@ -191,7 +186,7 @@ export class HttpServer {
191186
return this.corsHandler.addCORSHeaders(response);
192187
} catch (error) {
193188
this.logger.error(
194-
'HTTP request error:',
189+
'HttpServer: HTTP request error:',
195190
error instanceof Error ? error : new Error(String(error)),
196191
);
197192
return this.corsHandler.addCORSHeaders(
@@ -228,9 +223,11 @@ export class HttpServer {
228223
}
229224

230225
// Custom endpoints
231-
for (const handler of this.customEndpoints) {
232-
if (path.startsWith(handler.path)) {
233-
return await handler.handle(request);
226+
if (this.customEndpoints) {
227+
for (const handler of this.customEndpoints) {
228+
if (path.startsWith(handler.path)) {
229+
return await handler.handle(request, { beyondMcpServer: this.beyondMcpServer });
230+
}
234231
}
235232
}
236233

src/lib/server/MCPSDKHelpers.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
* - Tool schema generation helpers
99
*/
1010

11-
import { McpServer as SdkMcpServer } from 'mcp/server/mcp.js';
11+
import type { McpServer as SdkMcpServer } from 'mcp/server/mcp.js';
1212
import type { CallToolResult } from 'mcp/types.js';
1313

1414
// Import library components
15-
import { Logger } from '../utils/Logger.ts';
15+
import type { Logger } from '../utils/Logger.ts';
1616
import { toError } from '../utils/Error.ts';
1717

1818
// Import types
@@ -21,7 +21,9 @@ import type {
2121
CreateMessageResult,
2222
ElicitInputRequest,
2323
ElicitInputResult,
24+
//LoggingLevel,
2425
RegisteredTool,
26+
SendNotificationRequest,
2527
} from '../types/BeyondMcpTypes.ts';
2628

2729
/**
@@ -39,19 +41,24 @@ export class BeyondMcpSDKHelpers {
3941
/**
4042
* MCP Sampling API integration
4143
*/
42-
async createMessage(request: CreateMessageRequest): Promise<CreateMessageResult> {
44+
async createMessage(
45+
request: CreateMessageRequest,
46+
sessionId?: string,
47+
): Promise<CreateMessageResult> {
4348
this.logger.debug('MCPSDKHelpers: Creating message via MCP sampling API', {
4449
model: request.model,
4550
messageCount: request.messages.length,
4651
maxTokens: request.maxTokens,
4752
temperature: request.temperature,
53+
sessionId,
4854
});
4955

5056
try {
5157
// Cast to MCP SDK expected type
5258
const mcpRequest = {
5359
...request,
5460
maxTokens: request.maxTokens || 2000, // Ensure maxTokens is present
61+
// deno-lint-ignore no-explicit-any
5562
} as any;
5663

5764
const result = await this.sdkMcpServer.server.createMessage(mcpRequest);
@@ -60,6 +67,7 @@ export class BeyondMcpSDKHelpers {
6067
model: request.model,
6168
hasContent: !!result?.content,
6269
contentLength: result?.content ? JSON.stringify(result.content).length : 0,
70+
sessionId,
6371
});
6472

6573
// Cast result to expected type with unknown intermediate
@@ -72,27 +80,65 @@ export class BeyondMcpSDKHelpers {
7280
}
7381
}
7482

83+
/**
84+
* MCP Notification API integration
85+
* Sends a logging message notification to the client
86+
*/
87+
async sendNotification(request: SendNotificationRequest, sessionId?: string): Promise<void> {
88+
this.logger.debug('MCPSDKHelpers: Sending notification via MCP notification API', {
89+
level: request.level,
90+
logger: request.logger,
91+
hasData: !!request.data,
92+
sessionId,
93+
});
94+
95+
try {
96+
// Send notification using SDK's sendLoggingMessage
97+
await this.sdkMcpServer.sendLoggingMessage(
98+
{
99+
level: request.level,
100+
logger: request.logger,
101+
data: request.data,
102+
},
103+
sessionId,
104+
);
105+
106+
this.logger.debug('MCPSDKHelpers: Notification sent successfully', {
107+
level: request.level,
108+
logger: request.logger,
109+
});
110+
} catch (error) {
111+
this.logger.error('MCPSDKHelpers: MCP notification failed:', toError(error));
112+
throw new Error(
113+
`MCP notification failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
114+
);
115+
}
116+
}
117+
75118
/**
76119
* MCP Elicitation API integration
77120
*/
78-
async elicitInput(request: ElicitInputRequest): Promise<ElicitInputResult> {
121+
async elicitInput(request: ElicitInputRequest, sessionId?: string): Promise<ElicitInputResult> {
79122
this.logger.debug('MCPSDKHelpers: Eliciting input via MCP elicitation API', {
80123
messageLength: request.message.length,
81124
hasSchema: !!request.requestedSchema,
125+
sessionId,
82126
});
83127

84128
try {
85129
// Cast to MCP SDK expected type
86130
const mcpRequest = {
87131
...request,
132+
// deno-lint-ignore no-explicit-any
88133
requestedSchema: request.requestedSchema as any,
89134
};
90-
135+
// deno-lint-ignore no-explicit-any
91136
const result = await this.sdkMcpServer.server.elicitInput(mcpRequest as any);
92137

93138
this.logger.debug('MCPSDKHelpers: Input elicited successfully', {
94139
action: result.action,
95140
hasContent: !!result.content,
141+
sessionId,
96142
});
97143

98144
// Cast result to expected type - handle action mapping

src/lib/server/ServerTypes.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { Logger } from '../../types/library.types.ts';
1111
import type { OAuthProvider } from '../auth/OAuthProvider.ts';
1212
import type { TransportManager } from '../transport/TransportManager.ts';
1313
import type { WorkflowRegistry } from '../workflows/WorkflowRegistry.ts';
14+
import type { BeyondMcpServer } from './BeyondMcpServer.ts';
1415

1516
// Re-export core server types for convenience
1617
export type { HttpServerConfig, HttpServerDependencies } from './HttpServer.ts';
@@ -315,6 +316,16 @@ export interface EndpointRegistry {
315316
} | null;
316317
}
317318

319+
/**
320+
* Endpoint handler registry
321+
*/
322+
export interface CustomEndpoint {
323+
path: string;
324+
//handle(request: Request, dependencies: AppDependencies): Promise<Response>;
325+
handle(request: Request, dependencies: { beyondMcpServer: BeyondMcpServer }): Promise<Response>;
326+
}
327+
export type CustomEndpoints = Array<CustomEndpoint>;
328+
318329
/**
319330
* Request/response logging configuration
320331
*/

0 commit comments

Comments
 (0)