2025-08-26 12:32:21 -07:00
/ * *
* BrainyMCPService
*
* This class provides a unified service for accessing Brainy data and augmentations
* through the Model Control Protocol ( MCP ) . It integrates the BrainyMCPAdapter and
* MCPAugmentationToolset classes and provides WebSocket and REST server implementations
* for external model access .
* /
import { v4 as uuidv4 } from '../universal/uuid.js'
2025-09-11 16:23:32 -07:00
import { BrainyInterface } from '../types/brainyDataInterface.js'
2025-08-26 12:32:21 -07:00
import {
MCPRequest ,
MCPResponse ,
MCPDataAccessRequest ,
MCPToolExecutionRequest ,
MCPSystemInfoRequest ,
MCPAuthenticationRequest ,
MCPRequestType ,
MCPServiceOptions ,
MCP_VERSION ,
MCPTool
} from '../types/mcpTypes.js'
import { BrainyMCPAdapter } from './brainyMCPAdapter.js'
import { MCPAugmentationToolset } from './mcpAugmentationToolset.js'
import { isBrowser , isNode } from '../utils/environment.js'
export class BrainyMCPService {
private dataAdapter : BrainyMCPAdapter
private toolset : MCPAugmentationToolset
private options : MCPServiceOptions
private authTokens : Map < string , { userId : string ; expires : number } >
private rateLimits : Map < string , { count : number ; resetTime : number } >
/ * *
* Creates a new BrainyMCPService
2025-09-11 16:23:32 -07:00
* @param brainyData The Brainy instance to wrap
2025-08-26 12:32:21 -07:00
* @param options Configuration options for the service
* /
constructor (
2025-09-11 16:23:32 -07:00
brainyData : BrainyInterface ,
2025-08-26 12:32:21 -07:00
options : MCPServiceOptions = { }
) {
this . dataAdapter = new BrainyMCPAdapter ( brainyData )
this . toolset = new MCPAugmentationToolset ( )
this . options = options
this . authTokens = new Map ( )
this . rateLimits = new Map ( )
}
/ * *
* Handles an MCP request
* @param request The MCP request
* @returns An MCP response
* /
async handleRequest ( request : MCPRequest ) : Promise < MCPResponse > {
try {
switch ( request . type ) {
case MCPRequestType . DATA_ACCESS :
return await this . dataAdapter . handleRequest (
request as MCPDataAccessRequest
)
case MCPRequestType . TOOL_EXECUTION :
return await this . toolset . handleRequest (
request as MCPToolExecutionRequest
)
case MCPRequestType . SYSTEM_INFO :
return await this . handleSystemInfoRequest (
request as MCPSystemInfoRequest
)
case MCPRequestType . AUTHENTICATION :
return await this . handleAuthenticationRequest (
request as MCPAuthenticationRequest
)
default :
return this . createErrorResponse (
request . requestId ,
'UNSUPPORTED_REQUEST_TYPE' ,
` Request type ${ request . type } is not supported `
)
}
} catch ( error ) {
return this . createErrorResponse (
request . requestId ,
'INTERNAL_ERROR' ,
error instanceof Error ? error.message : String ( error )
)
}
}
/ * *
* Handles a system info request
* @param request The MCP request
* @returns An MCP response
* /
private async handleSystemInfoRequest (
request : MCPSystemInfoRequest
) : Promise < MCPResponse > {
try {
switch ( request . infoType ) {
case 'status' :
return this . createSuccessResponse ( request . requestId , {
status : 'active' ,
version : MCP_VERSION ,
environment : isBrowser ( ) ? 'browser' : isNode ( ) ? 'node' : 'unknown'
} )
case 'availableTools' :
const tools : MCPTool [ ] = await this . toolset . getAvailableTools ( )
return this . createSuccessResponse ( request . requestId , tools )
case 'version' :
return this . createSuccessResponse ( request . requestId , {
version : MCP_VERSION
} )
default :
return this . createErrorResponse (
request . requestId ,
'UNSUPPORTED_INFO_TYPE' ,
` Info type ${ request . infoType } is not supported `
)
}
} catch ( error ) {
return this . createErrorResponse (
request . requestId ,
'INTERNAL_ERROR' ,
error instanceof Error ? error.message : String ( error )
)
}
}
/ * *
* Handles an authentication request
* @param request The MCP request
* @returns An MCP response
* /
private async handleAuthenticationRequest (
request : MCPAuthenticationRequest
) : Promise < MCPResponse > {
try {
if ( ! this . options . enableAuth ) {
return this . createSuccessResponse ( request . requestId , {
authenticated : true ,
message : 'Authentication is not enabled'
} )
}
const { credentials } = request
// Check API key authentication
if (
credentials . apiKey &&
this . options . apiKeys ? . includes ( credentials . apiKey )
) {
const token = this . generateAuthToken ( 'api-user' )
return this . createSuccessResponse ( request . requestId , {
authenticated : true ,
token
} )
}
2025-09-11 16:23:32 -07:00
// Authentication must be implemented by the user
throw new Error (
'Authentication not configured. Please implement custom authentication handler by extending BrainyMCPService and overriding authenticateUser()'
)
2025-08-26 12:32:21 -07:00
return this . createErrorResponse (
request . requestId ,
'INVALID_CREDENTIALS' ,
'Invalid credentials'
)
} catch ( error ) {
return this . createErrorResponse (
request . requestId ,
'INTERNAL_ERROR' ,
error instanceof Error ? error.message : String ( error )
)
}
}
/ * *
* Checks if a request is valid
* @param request The request to check
* @returns Whether the request is valid
* /
private isValidRequest ( request : any ) : boolean {
return (
request &&
typeof request === 'object' &&
request . type &&
request . requestId &&
request . version
)
}
/ * *
* Checks if a request is authenticated
* @param request The request to check
* @returns Whether the request is authenticated
* /
private isAuthenticated ( request : MCPRequest ) : boolean {
if ( ! this . options . enableAuth ) {
return true
}
return request . authToken ? this . isValidToken ( request . authToken ) : false
}
/ * *
* Checks if a token is valid
* @param token The token to check
* @returns Whether the token is valid
* /
private isValidToken ( token : string ) : boolean {
const tokenInfo = this . authTokens . get ( token )
if ( ! tokenInfo ) {
return false
}
if ( tokenInfo . expires < Date . now ( ) ) {
this . authTokens . delete ( token )
return false
}
return true
}
/ * *
* Generates an authentication token
* @param userId The user ID to associate with the token
* @returns The generated token
* /
private generateAuthToken ( userId : string ) : string {
const token = uuidv4 ( )
const expires = Date . now ( ) + 24 * 60 * 60 * 1000 // 24 hours
this . authTokens . set ( token , { userId , expires } )
return token
}
/ * *
* Checks if a client has exceeded the rate limit
* @param clientId The client ID to check
* @returns Whether the client is within the rate limit
* /
private checkRateLimit ( clientId : string ) : boolean {
if ( ! this . options . rateLimit ) {
return true
}
const now = Date . now ( )
const limit = this . rateLimits . get ( clientId )
if ( ! limit ) {
this . rateLimits . set ( clientId , {
count : 1 ,
resetTime : now + this . options . rateLimit . windowMs
} )
return true
}
if ( limit . resetTime < now ) {
limit . count = 1
limit . resetTime = now + this . options . rateLimit . windowMs
return true
}
if ( limit . count >= this . options . rateLimit . maxRequests ) {
return false
}
limit . count ++
return true
}
/ * *
* Creates a success response
* @param requestId The request ID
* @param data The response data
* @returns An MCP response
* /
private createSuccessResponse ( requestId : string , data : any ) : MCPResponse {
return {
success : true ,
requestId ,
version : MCP_VERSION ,
data
}
}
/ * *
* Creates an error response
* @param requestId The request ID
* @param code The error code
* @param message The error message
* @param details Optional error details
* @returns An MCP response
* /
private createErrorResponse (
requestId : string ,
code : string ,
message : string ,
details? : any
) : MCPResponse {
return {
success : false ,
requestId ,
version : MCP_VERSION ,
error : {
code ,
message ,
details
}
}
}
/ * *
* Creates a new request ID
* @returns A new UUID
* /
generateRequestId ( ) : string {
return uuidv4 ( )
}
/ * *
* Handles an MCP request directly ( for in - process models )
* @param request The MCP request
* @returns An MCP response
* /
async handleMCPRequest ( request : MCPRequest ) : Promise < MCPResponse > {
return await this . handleRequest ( request )
}
}