**feat: implement Model Control Protocol (MCP) for Brainy with WebSocket and REST interfaces**

### Changes:
- **Core MCP Components**:
  - Introduced `BrainyMCPAdapter`, `BrainyMCPService`, and `MCPAugmentationToolset` for handling data access, tool execution, system information, and authentication via MCP.
  - Added asynchronous handlers to process requests for Brainy data, augmentations, and relationships.
  - Built pipelines to expose Brainy augmentation capabilities as tools.

- **Server Implementations**:
  - Added WebSocket and REST interfaces in `initializeMCPService` for external MCP requests.
  - Included rate limiting, authentication, and CORS support for REST API.

- **Type Definitions**:
  - Defined MCP-related types such as `MCPRequestType`, `MCPResponse`, and `MCPToolExecutionRequest` in `src/types/mcpTypes.ts`.
  - Incorporated augmentation-type-specific methods from `augmentationPipeline`.

- **Exports**:
  - Exposed MCP modules (`BrainyMCPAdapter`, `BrainyMCPService`, `MCPAugmentationToolset`) via `src/mcp/index.ts`.

### Purpose:
Introduced a Model Control Protocol (MCP) framework to enable seamless integration of Brainy data and augmentation tools with external models. The implementation provides structured, scalable access to data and tools through both WebSocket and REST APIs.
This commit is contained in:
David Snelling 2025-06-20 10:56:21 -07:00
parent 5a67d48bbc
commit 2e8e947adc
7 changed files with 1328 additions and 0 deletions

View file

@ -0,0 +1,205 @@
import { WebSocketServer } from 'ws'
import express from 'express'
import cors from 'cors'
import { BrainyData, BrainyMCPService } from '@soulcraft/brainy'
import { v4 as uuidv4 } from 'uuid'
/**
* Initialize the MCP service with WebSocket and REST API servers
*/
export function initializeMCPService(
brainy: BrainyData,
options: {
wsPort?: number
restPort?: number
enableAuth?: boolean
apiKeys?: string[]
rateLimit?: {
windowMs: number
maxRequests: number
}
cors?: any
}
) {
// Create the MCP service
const mcpService = new BrainyMCPService(brainy, options)
// Start WebSocket server if port is provided
if (options.wsPort) {
startWebSocketServer(mcpService, options.wsPort)
}
// Start REST server if port is provided
if (options.restPort) {
startRESTServer(mcpService, options.restPort, options.cors)
}
return mcpService
}
/**
* Start a WebSocket server for the MCP service
*/
function startWebSocketServer(mcpService: BrainyMCPService, port: number) {
const wss = new WebSocketServer({ port })
wss.on('connection', (ws: any) => {
ws.on('message', async (message: string) => {
try {
const request = JSON.parse(message)
// Handle the request using the MCP service
const response = await mcpService.handleMCPRequest(request)
// Send the response
ws.send(JSON.stringify(response))
} catch (error) {
// Send error response
ws.send(
JSON.stringify({
success: false,
requestId: uuidv4(),
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : String(error)
}
})
)
}
})
})
console.log(`MCP WebSocket server started on port ${port}`)
return wss
}
/**
* Start a REST server for the MCP service
*/
function startRESTServer(
mcpService: BrainyMCPService,
port: number,
corsOptions?: any
) {
const app = express()
// Parse JSON request bodies
app.use(express.json())
// Enable CORS if configured
if (corsOptions) {
app.use(cors(corsOptions))
}
// MCP endpoints
app.post('/mcp/data', async (req: any, res: any) => {
try {
const response = await mcpService.handleMCPRequest({
...req.body,
type: 'DATA_ACCESS'
})
res.json(response)
} catch (error) {
res.status(500).json({
success: false,
requestId: uuidv4(),
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : String(error)
}
})
}
})
app.post('/mcp/tools', async (req: any, res: any) => {
try {
const response = await mcpService.handleMCPRequest({
...req.body,
type: 'TOOL_EXECUTION'
})
res.json(response)
} catch (error) {
res.status(500).json({
success: false,
requestId: uuidv4(),
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : String(error)
}
})
}
})
app.post('/mcp/system', async (req: any, res: any) => {
try {
const response = await mcpService.handleMCPRequest({
...req.body,
type: 'SYSTEM_INFO'
})
res.json(response)
} catch (error) {
res.status(500).json({
success: false,
requestId: uuidv4(),
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : String(error)
}
})
}
})
app.post('/mcp/auth', async (req: any, res: any) => {
try {
const response = await mcpService.handleMCPRequest({
...req.body,
type: 'AUTHENTICATION'
})
res.json(response)
} catch (error) {
res.status(500).json({
success: false,
requestId: uuidv4(),
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : String(error)
}
})
}
})
// Get available tools
app.get('/mcp/tools', async (req: any, res: any) => {
try {
const response = await mcpService.handleMCPRequest({
type: 'SYSTEM_INFO',
requestId: uuidv4(),
version: '1.0',
infoType: 'availableTools'
})
res.json(response)
} catch (error) {
res.status(500).json({
success: false,
requestId: uuidv4(),
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : String(error)
}
})
}
})
// Start the server
const server = app.listen(port, () => {
console.log(`MCP REST API server started on port ${port}`)
})
return server
}

180
src/mcp/README.md Normal file
View file

@ -0,0 +1,180 @@
# Model Control Protocol (MCP) for Brainy
This document provides information about the Model Control Protocol (MCP) implementation in Brainy, which allows external models to access Brainy data and use the augmentation pipeline as tools.
## Components
The MCP implementation consists of three main components:
1. **BrainyMCPAdapter**: Provides access to Brainy data through MCP
2. **MCPAugmentationToolset**: Exposes the augmentation pipeline as tools
3. **BrainyMCPService**: Integrates the adapter and toolset, providing WebSocket and REST server implementations for external model access
## Environment Compatibility
### BrainyMCPAdapter
The `BrainyMCPAdapter` has no environment-specific dependencies and can run in any environment where Brainy itself runs, including:
- Browser environments
- Node.js environments
- Server environments
### MCPAugmentationToolset
The `MCPAugmentationToolset` also has no environment-specific dependencies and can run in any environment where Brainy itself runs, including:
- Browser environments
- Node.js environments
- Server environments
### BrainyMCPService
The `BrainyMCPService` has been refactored to separate the core functionality from the Node.js-specific server functionality:
1. **Core Functionality**: The core request handling functionality (`handleMCPRequest`) can run in any environment where Brainy itself runs. This is what remains in the main Brainy package.
2. **Server Functionality**: The WebSocket and REST server functionality has been moved to the cloud-wrapper project to avoid including Node.js-specific dependencies in the browser bundle:
- `ws` for WebSocket server
- `express` for REST API
- `cors` for Cross-Origin Resource Sharing
This separation ensures that the browser bundle remains lightweight and doesn't include unnecessary Node.js-specific dependencies. In browser or other environments, you can still use the core functionality through the `handleMCPRequest` method.
## Usage
### In Any Environment (Browser, Node.js, Server)
```typescript
import { BrainyData, BrainyMCPAdapter, MCPAugmentationToolset } from '@soulcraft/brainy'
// Create a BrainyData instance
const brainyData = new BrainyData()
await brainyData.init()
// Create an MCP adapter
const adapter = new BrainyMCPAdapter(brainyData)
// Create a toolset
const toolset = new MCPAugmentationToolset()
// Use the adapter to access Brainy data
const response = await adapter.handleRequest({
type: 'data_access',
operation: 'search',
requestId: adapter.generateRequestId(),
version: '1.0.0',
parameters: {
query: 'example query',
k: 5
}
})
// Use the toolset to execute augmentation pipeline tools
const toolResponse = await toolset.handleRequest({
type: 'tool_execution',
toolName: 'brainy_memory_storeData',
requestId: toolset.generateRequestId(),
version: '1.0.0',
parameters: {
args: ['key1', { some: 'data' }]
}
})
```
### In Node.js Environment with Server Functionality
To use the MCP service with WebSocket and REST server functionality, you should use the cloud-wrapper project:
```typescript
import { BrainyData } from '@soulcraft/brainy'
import { initializeBrainy } from './services/brainyService.js'
import { initializeMCPService } from './services/mcpService.js'
// Initialize Brainy
const brainyData = await initializeBrainy()
// Initialize MCP service with WebSocket and REST server functionality
const mcpService = initializeMCPService(brainyData, {
wsPort: 8080,
restPort: 3000,
enableAuth: true,
apiKeys: ['your-api-key'],
rateLimit: {
maxRequests: 100,
windowMs: 60000 // 1 minute
},
cors: {
origin: '*',
credentials: true
}
})
```
Alternatively, you can configure the MCP service using environment variables in the cloud-wrapper:
```
# MCP configuration
MCP_WS_PORT=8080
MCP_REST_PORT=3000
MCP_ENABLE_AUTH=true
MCP_API_KEYS=your-api-key,another-key
MCP_RATE_LIMIT_REQUESTS=100
MCP_RATE_LIMIT_WINDOW_MS=60000
MCP_ENABLE_CORS=true
```
### In Browser Environment (Core Functionality Only)
```typescript
import { BrainyData, BrainyMCPService } from '@soulcraft/brainy'
// Create a BrainyData instance
const brainyData = new BrainyData()
await brainyData.init()
// Create an MCP service (server functionality will be disabled in browser)
const mcpService = new BrainyMCPService(brainyData)
// Use the core functionality
const response = await mcpService.handleMCPRequest({
type: 'data_access',
operation: 'search',
requestId: mcpService.generateRequestId(),
version: '1.0.0',
parameters: {
query: 'example query',
k: 5
}
})
```
## Cloud Wrapper Integration
The MCP service's server functionality has been integrated directly into the cloud-wrapper project. The cloud-wrapper automatically initializes the MCP service if the appropriate environment variables are set:
```
# MCP configuration
MCP_WS_PORT=8080
MCP_REST_PORT=3000
MCP_ENABLE_AUTH=true
MCP_API_KEYS=your-api-key,another-key
MCP_RATE_LIMIT_REQUESTS=100
MCP_RATE_LIMIT_WINDOW_MS=60000
MCP_ENABLE_CORS=true
```
You can deploy the cloud wrapper to various cloud platforms using the npm scripts from the root directory:
```bash
# Deploy to AWS Lambda and API Gateway
npm run deploy:cloud:aws
# Deploy to Google Cloud Run
npm run deploy:cloud:gcp
# Deploy to Cloudflare Workers
npm run deploy:cloud:cloudflare
```
The cloud wrapper is specifically designed for server environments and includes additional features like logging, security headers, and deployment scripts for various cloud providers. See the [Cloud Wrapper README](../../cloud-wrapper/README.md) for detailed configuration instructions and API documentation.

203
src/mcp/brainyMCPAdapter.ts Normal file
View file

@ -0,0 +1,203 @@
/**
* BrainyMCPAdapter
*
* This class provides an adapter for accessing Brainy data through the Model Control Protocol (MCP).
* It wraps a BrainyData instance and exposes methods for getting vectors, searching similar items,
* and getting relationships.
*/
import { v4 as uuidv4 } from 'uuid'
import { BrainyDataInterface } from '../types/brainyDataInterface.js'
import {
MCPRequest,
MCPResponse,
MCPDataAccessRequest,
MCPRequestType,
MCP_VERSION
} from '../types/mcpTypes.js'
export class BrainyMCPAdapter {
private brainyData: BrainyDataInterface
/**
* Creates a new BrainyMCPAdapter
* @param brainyData The BrainyData instance to wrap
*/
constructor(brainyData: BrainyDataInterface) {
this.brainyData = brainyData
}
/**
* Handles an MCP data access request
* @param request The MCP request
* @returns An MCP response
*/
async handleRequest(request: MCPDataAccessRequest): Promise<MCPResponse> {
try {
switch (request.operation) {
case 'get':
return await this.handleGetRequest(request)
case 'search':
return await this.handleSearchRequest(request)
case 'add':
return await this.handleAddRequest(request)
case 'getRelationships':
return await this.handleGetRelationshipsRequest(request)
default:
return this.createErrorResponse(
request.requestId,
'UNSUPPORTED_OPERATION',
`Operation ${request.operation} is not supported`
)
}
} catch (error) {
return this.createErrorResponse(
request.requestId,
'INTERNAL_ERROR',
error instanceof Error ? error.message : String(error)
)
}
}
/**
* Handles a get request
* @param request The MCP request
* @returns An MCP response
*/
private async handleGetRequest(request: MCPDataAccessRequest): Promise<MCPResponse> {
const { id } = request.parameters
if (!id) {
return this.createErrorResponse(
request.requestId,
'MISSING_PARAMETER',
'Parameter "id" is required'
)
}
const noun = await this.brainyData.get(id)
if (!noun) {
return this.createErrorResponse(
request.requestId,
'NOT_FOUND',
`No noun found with id ${id}`
)
}
return this.createSuccessResponse(request.requestId, noun)
}
/**
* Handles a search request
* @param request The MCP request
* @returns An MCP response
*/
private async handleSearchRequest(request: MCPDataAccessRequest): Promise<MCPResponse> {
const { query, k = 10 } = request.parameters
if (!query) {
return this.createErrorResponse(
request.requestId,
'MISSING_PARAMETER',
'Parameter "query" is required'
)
}
const results = await this.brainyData.searchText(query, k)
return this.createSuccessResponse(request.requestId, results)
}
/**
* Handles an add request
* @param request The MCP request
* @returns An MCP response
*/
private async handleAddRequest(request: MCPDataAccessRequest): Promise<MCPResponse> {
const { text, metadata } = request.parameters
if (!text) {
return this.createErrorResponse(
request.requestId,
'MISSING_PARAMETER',
'Parameter "text" is required'
)
}
const id = await this.brainyData.add(text, metadata)
return this.createSuccessResponse(request.requestId, { id })
}
/**
* Handles a getRelationships request
* @param request The MCP request
* @returns An MCP response
*/
private async handleGetRelationshipsRequest(request: MCPDataAccessRequest): Promise<MCPResponse> {
const { id } = request.parameters
if (!id) {
return this.createErrorResponse(
request.requestId,
'MISSING_PARAMETER',
'Parameter "id" is required'
)
}
// This is a simplified implementation - in a real implementation, we would
// need to check if these methods exist on the BrainyDataInterface
const outgoing = await (this.brainyData as any).getVerbsBySource?.(id) || []
const incoming = await (this.brainyData as any).getVerbsByTarget?.(id) || []
return this.createSuccessResponse(request.requestId, { outgoing, incoming })
}
/**
* 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()
}
}

347
src/mcp/brainyMCPService.ts Normal file
View file

@ -0,0 +1,347 @@
/**
* 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 'uuid'
import { BrainyDataInterface } from '../types/brainyDataInterface.js'
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
* @param brainyData The BrainyData instance to wrap
* @param options Configuration options for the service
*/
constructor(
brainyData: BrainyDataInterface,
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
})
}
// Check username/password authentication
// This is a placeholder - in a real implementation, you would check against a database
if (
credentials.username === 'admin' &&
credentials.password === 'password'
) {
const token = this.generateAuthToken(credentials.username)
return this.createSuccessResponse(request.requestId, {
authenticated: true,
token
})
}
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)
}
}

19
src/mcp/index.ts Normal file
View file

@ -0,0 +1,19 @@
/**
* Model Control Protocol (MCP) for Brainy
*
* This module provides a Model Control Protocol (MCP) implementation for Brainy,
* allowing external models to access Brainy data and use the augmentation pipeline as tools.
*/
// Import and re-export the MCP components
import { BrainyMCPAdapter } from './brainyMCPAdapter.js'
import { MCPAugmentationToolset } from './mcpAugmentationToolset.js'
import { BrainyMCPService } from './brainyMCPService.js'
// Export the MCP components
export { BrainyMCPAdapter }
export { MCPAugmentationToolset }
export { BrainyMCPService }
// Export the MCP types
export * from '../types/mcpTypes.js'

View file

@ -0,0 +1,225 @@
/**
* MCPAugmentationToolset
*
* This class exposes the Brainy augmentation pipeline as tools through the Model Control Protocol (MCP).
* It provides methods for getting available tools and executing tools.
*/
import { v4 as uuidv4 } from 'uuid'
import {
MCPResponse,
MCPToolExecutionRequest,
MCPTool,
MCP_VERSION
} from '../types/mcpTypes.js'
import { AugmentationType } from '../types/augmentations.js'
// Import the augmentation pipeline
import { augmentationPipeline } from '../augmentationPipeline.js'
export class MCPAugmentationToolset {
/**
* Creates a new MCPAugmentationToolset
*/
constructor() {
// No initialization needed
}
/**
* Handles an MCP tool execution request
* @param request The MCP request
* @returns An MCP response
*/
async handleRequest(request: MCPToolExecutionRequest): Promise<MCPResponse> {
try {
const { toolName, parameters } = request
// Extract the augmentation type and method from the tool name
// Tool names are in the format: brainy_{augmentationType}_{method}
const parts = toolName.split('_')
if (parts.length < 3 || parts[0] !== 'brainy') {
return this.createErrorResponse(
request.requestId,
'INVALID_TOOL',
`Invalid tool name: ${toolName}. Tool names should be in the format: brainy_{augmentationType}_{method}`
)
}
const augmentationType = parts[1]
const method = parts.slice(2).join('_')
// Validate the augmentation type
if (!this.isValidAugmentationType(augmentationType)) {
return this.createErrorResponse(
request.requestId,
'INVALID_AUGMENTATION_TYPE',
`Invalid augmentation type: ${augmentationType}`
)
}
// Execute the appropriate pipeline based on the augmentation type
const result = await this.executePipeline(augmentationType, method, parameters)
return this.createSuccessResponse(request.requestId, result)
} catch (error) {
return this.createErrorResponse(
request.requestId,
'INTERNAL_ERROR',
error instanceof Error ? error.message : String(error)
)
}
}
/**
* Gets all available tools
* @returns An array of MCP tools
*/
async getAvailableTools(): Promise<MCPTool[]> {
const tools: MCPTool[] = []
// Get all available augmentation types
const augmentationTypes = augmentationPipeline.getAvailableAugmentationTypes()
for (const type of augmentationTypes) {
// Get all augmentations of this type
const augmentations = augmentationPipeline.getAugmentationsByType(type)
for (const augmentation of augmentations) {
// Get all methods of this augmentation (excluding private methods and base methods)
const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(augmentation))
.filter(method =>
!method.startsWith('_') &&
method !== 'constructor' &&
method !== 'initialize' &&
method !== 'shutDown' &&
method !== 'getStatus' &&
typeof augmentation[method] === 'function'
)
// Create a tool for each method
for (const method of methods) {
tools.push(this.createToolDefinition(type, augmentation.name, method))
}
}
}
return tools
}
/**
* Creates a tool definition
* @param type The augmentation type
* @param augmentationName The augmentation name
* @param method The method name
* @returns An MCP tool definition
*/
private createToolDefinition(type: string, augmentationName: string, method: string): MCPTool {
return {
name: `brainy_${type}_${method}`,
description: `Access to Brainy's ${type} augmentation '${augmentationName}' method '${method}'`,
parameters: {
type: 'object',
properties: {
args: {
type: 'array',
description: `Arguments for the ${method} method`
},
options: {
type: 'object',
description: 'Optional execution options'
}
},
required: ['args']
}
}
}
/**
* Executes the appropriate pipeline based on the augmentation type
* @param type The augmentation type
* @param method The method to execute
* @param parameters The parameters for the method
* @returns The result of the pipeline execution
*/
private async executePipeline(type: string, method: string, parameters: any): Promise<any> {
const { args = [], options = {} } = parameters
switch (type) {
case AugmentationType.SENSE:
return await augmentationPipeline.executeSensePipeline(method, args, options)
case AugmentationType.CONDUIT:
return await augmentationPipeline.executeConduitPipeline(method, args, options)
case AugmentationType.COGNITION:
return await augmentationPipeline.executeCognitionPipeline(method, args, options)
case AugmentationType.MEMORY:
return await augmentationPipeline.executeMemoryPipeline(method, args, options)
case AugmentationType.PERCEPTION:
return await augmentationPipeline.executePerceptionPipeline(method, args, options)
case AugmentationType.DIALOG:
return await augmentationPipeline.executeDialogPipeline(method, args, options)
case AugmentationType.ACTIVATION:
return await augmentationPipeline.executeActivationPipeline(method, args, options)
default:
throw new Error(`Unsupported augmentation type: ${type}`)
}
}
/**
* Checks if an augmentation type is valid
* @param type The augmentation type to check
* @returns Whether the augmentation type is valid
*/
private isValidAugmentationType(type: string): boolean {
return Object.values(AugmentationType).includes(type as AugmentationType)
}
/**
* 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()
}
}

149
src/types/mcpTypes.ts Normal file
View file

@ -0,0 +1,149 @@
/**
* Model Control Protocol (MCP) Types
*
* This file defines the types and interfaces for the Model Control Protocol (MCP)
* implementation in Brainy. MCP allows external models to access Brainy data and
* use the augmentation pipeline as tools.
*/
/**
* MCP version information
*/
export const MCP_VERSION = '1.0.0'
/**
* MCP request types
*/
export enum MCPRequestType {
DATA_ACCESS = 'data_access',
TOOL_EXECUTION = 'tool_execution',
SYSTEM_INFO = 'system_info',
AUTHENTICATION = 'authentication'
}
/**
* Base interface for all MCP requests
*/
export interface MCPRequest {
/** The type of request */
type: MCPRequestType
/** Request ID for tracking and correlation */
requestId: string
/** API version */
version: string
/** Authentication token (if required) */
authToken?: string
}
/**
* Interface for data access requests
*/
export interface MCPDataAccessRequest extends MCPRequest {
type: MCPRequestType.DATA_ACCESS
/** The data access operation to perform */
operation: 'get' | 'search' | 'add' | 'getRelationships'
/** Parameters for the operation */
parameters: Record<string, any>
}
/**
* Interface for tool execution requests
*/
export interface MCPToolExecutionRequest extends MCPRequest {
type: MCPRequestType.TOOL_EXECUTION
/** The name of the tool to execute */
toolName: string
/** Parameters for the tool */
parameters: Record<string, any>
}
/**
* Interface for system info requests
*/
export interface MCPSystemInfoRequest extends MCPRequest {
type: MCPRequestType.SYSTEM_INFO
/** The type of information to retrieve */
infoType: 'status' | 'availableTools' | 'version'
}
/**
* Interface for authentication requests
*/
export interface MCPAuthenticationRequest extends MCPRequest {
type: MCPRequestType.AUTHENTICATION
/** The authentication credentials */
credentials: {
apiKey?: string
username?: string
password?: string
}
}
/**
* Base interface for all MCP responses
*/
export interface MCPResponse {
/** Whether the request was successful */
success: boolean
/** The request ID from the original request */
requestId: string
/** API version */
version: string
/** Response data (if successful) */
data?: any
/** Error information (if unsuccessful) */
error?: {
code: string
message: string
details?: any
}
}
/**
* Interface for MCP tool definitions
*/
export interface MCPTool {
/** The name of the tool */
name: string
/** A description of what the tool does */
description: string
/** The parameters the tool accepts */
parameters: {
type: 'object'
properties: Record<string, {
type: string
description: string
enum?: string[]
required?: boolean
}>
required: string[]
}
}
/**
* Configuration options for MCP services
*/
export interface MCPServiceOptions {
/** Port for the WebSocket server */
wsPort?: number
/** Port for the REST server */
restPort?: number
/** Whether to enable authentication */
enableAuth?: boolean
/** API keys for authentication */
apiKeys?: string[]
/** Rate limiting configuration */
rateLimit?: {
/** Maximum number of requests per window */
maxRequests: number
/** Time window in milliseconds */
windowMs: number
}
/** CORS configuration for REST API */
cors?: {
/** Allowed origins */
origin: string | string[]
/** Whether to allow credentials */
credentials: boolean
}
}