@lanonasis/cli
Version:
LanOnasis Enterprise CLI - Memory as a Service, API Key Management, and Infrastructure Orchestration
520 lines (519 loc) • 25.4 kB
JavaScript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { CLIConfig } from './utils/config.js';
// MCP Protocol Compliance: Redirect all console output to stderr
// This prevents stdout pollution which breaks JSON-RPC communication
const originalConsoleError = console.error;
// Silent mode for MCP protocol compliance
const isSilentMode = process.env.LANONASIS_SILENT === 'true' || process.argv.includes('--silent');
if (isSilentMode) {
// Completely silence all output except JSON-RPC
console.log = () => { };
console.error = () => { };
console.warn = () => { };
console.info = () => { };
console.debug = () => { };
}
else {
// Redirect to stderr for debugging
console.log = (...args) => originalConsoleError('[MCP-LOG]', ...args);
console.error = (...args) => originalConsoleError('[MCP-ERROR]', ...args);
console.warn = (...args) => originalConsoleError('[MCP-WARN]', ...args);
console.info = (...args) => originalConsoleError('[MCP-INFO]', ...args);
}
// Disable colors and verbose output for MCP protocol compliance
process.env.FORCE_COLOR = '0';
process.env.DEBUG = '';
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
class LanonasisMCPServer {
server;
config;
constructor() {
this.config = new CLIConfig();
this.server = new Server({
name: 'lanonasis-mcp-server',
version: '1.3.0',
}, {
capabilities: {
tools: {},
resources: {},
},
});
this.setupHandlers();
}
setupHandlers() {
// List available tools - Comprehensive MCP toolset matching legacy CLI
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// Memory Management Tools
{
name: 'create_memory',
description: 'Create a new memory entry with vector embedding',
inputSchema: {
type: 'object',
properties: {
title: { type: 'string', description: 'Memory title' },
content: { type: 'string', description: 'Memory content' },
memory_type: { type: 'string', description: 'Type of memory', enum: ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'] },
tags: { type: 'array', items: { type: 'string' }, description: 'Memory tags' },
topic_id: { type: 'string', description: 'Topic ID for organization' }
},
required: ['title', 'content']
}
},
{
name: 'search_memories',
description: 'Search through memories with semantic vector search',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
memory_type: { type: 'string', description: 'Filter by memory type' },
limit: { type: 'number', description: 'Maximum results to return', default: 10 },
threshold: { type: 'number', description: 'Similarity threshold (0.0-1.0)', default: 0.7 },
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' }
},
required: ['query']
}
},
{
name: 'get_memory',
description: 'Get a specific memory by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Memory ID' }
},
required: ['id']
}
},
{
name: 'update_memory',
description: 'Update an existing memory',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Memory ID' },
title: { type: 'string', description: 'Memory title' },
content: { type: 'string', description: 'Memory content' },
memory_type: { type: 'string', description: 'Type of memory' },
tags: { type: 'array', items: { type: 'string' }, description: 'Memory tags' }
},
required: ['id']
}
},
{
name: 'delete_memory',
description: 'Delete a memory by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Memory ID' }
},
required: ['id']
}
},
{
name: 'list_memories',
description: 'List memories with pagination and filters',
inputSchema: {
type: 'object',
properties: {
limit: { type: 'number', description: 'Number of memories to return', default: 20 },
offset: { type: 'number', description: 'Offset for pagination', default: 0 },
memory_type: { type: 'string', description: 'Filter by memory type' },
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' }
}
}
},
// API Key Management Tools
{
name: 'create_api_key',
description: 'Create a new API key',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'API key name' },
description: { type: 'string', description: 'API key description' },
project_id: { type: 'string', description: 'Project ID' },
access_level: { type: 'string', description: 'Access level', enum: ['public', 'authenticated', 'team', 'admin', 'enterprise'] },
expires_in_days: { type: 'number', description: 'Expiration in days', default: 365 }
},
required: ['name']
}
},
{
name: 'list_api_keys',
description: 'List API keys',
inputSchema: {
type: 'object',
properties: {
project_id: { type: 'string', description: 'Filter by project ID' },
active_only: { type: 'boolean', description: 'Show only active keys', default: true }
}
}
},
{
name: 'rotate_api_key',
description: 'Rotate an API key',
inputSchema: {
type: 'object',
properties: {
key_id: { type: 'string', description: 'API key ID to rotate' }
},
required: ['key_id']
}
},
{
name: 'delete_api_key',
description: 'Delete an API key',
inputSchema: {
type: 'object',
properties: {
key_id: { type: 'string', description: 'API key ID to delete' }
},
required: ['key_id']
}
},
// Project Management Tools
{
name: 'create_project',
description: 'Create a new project',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Project name' },
description: { type: 'string', description: 'Project description' },
organization_id: { type: 'string', description: 'Organization ID' }
},
required: ['name']
}
},
{
name: 'list_projects',
description: 'List projects',
inputSchema: {
type: 'object',
properties: {
organization_id: { type: 'string', description: 'Filter by organization ID' }
}
}
},
// Organization Management Tools
{
name: 'get_organization_info',
description: 'Get organization information',
inputSchema: {
type: 'object',
properties: {}
}
},
// Authentication Tools
{
name: 'get_auth_status',
description: 'Get authentication status',
inputSchema: {
type: 'object',
properties: {}
}
},
// Configuration Tools
{
name: 'get_config',
description: 'Get configuration settings',
inputSchema: {
type: 'object',
properties: {
key: { type: 'string', description: 'Specific config key to retrieve' }
}
}
},
{
name: 'set_config',
description: 'Set configuration setting',
inputSchema: {
type: 'object',
properties: {
key: { type: 'string', description: 'Configuration key' },
value: { type: 'string', description: 'Configuration value' }
},
required: ['key', 'value']
}
},
// Health and Status Tools
{
name: 'get_health_status',
description: 'Get system health status',
inputSchema: {
type: 'object',
properties: {}
}
}
]
};
});
// Handle tool calls - Comprehensive implementation
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const apiKey = process.env.LANONASIS_API_KEY;
const apiUrl = process.env.LANONASIS_API_URL || 'https://api.lanonasis.com';
if (!apiKey) {
return {
content: [
{
type: 'text',
text: 'Error: LANONASIS_API_KEY environment variable is required'
}
]
};
}
const headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'User-Agent': 'lanonasis-mcp-server/1.3.0'
};
switch (name) {
// Memory Management Tools
case 'create_memory': {
const response = await fetch(`${apiUrl}/api/v1/memory`, {
method: 'POST',
headers,
body: JSON.stringify(args)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Memory creation failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `✅ Memory created successfully:\n${JSON.stringify(result, null, 2)}`
}
]
};
}
case 'search_memories': {
const queryParams = new URLSearchParams();
if (args.query)
queryParams.append('query', String(args.query));
if (args.memory_type)
queryParams.append('memory_type', String(args.memory_type));
if (args.limit)
queryParams.append('limit', args.limit.toString());
if (args.threshold)
queryParams.append('threshold', args.threshold.toString());
if (args.tags && Array.isArray(args.tags)) {
args.tags.forEach((tag) => queryParams.append('tags', String(tag)));
}
const response = await fetch(`${apiUrl}/api/v1/memory/search?${queryParams}`, {
method: 'GET',
headers
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Memory search failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `🔍 Search results (${result.length || 0} found):\n${JSON.stringify(result, null, 2)}`
}
]
};
}
case 'get_memory': {
const response = await fetch(`${apiUrl}/api/v1/memory/${args.id}`, {
method: 'GET',
headers
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Memory retrieval failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `📄 Memory details:\n${JSON.stringify(result, null, 2)}`
}
]
};
}
case 'update_memory': {
const { id, ...updateData } = args;
const response = await fetch(`${apiUrl}/api/v1/memory/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(updateData)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Memory update failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `✏️ Memory updated successfully:\n${JSON.stringify(result, null, 2)}`
}
]
};
}
case 'delete_memory': {
const response = await fetch(`${apiUrl}/api/v1/memory/${args.id}`, {
method: 'DELETE',
headers
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Memory deletion failed: ${response.status} ${response.statusText} - ${errorText}`);
}
return {
content: [
{
type: 'text',
text: `🗑️ Memory deleted successfully (ID: ${args.id})`
}
]
};
}
case 'list_memories': {
const queryParams = new URLSearchParams();
if (args.limit)
queryParams.append('limit', args.limit.toString());
if (args.offset)
queryParams.append('offset', args.offset.toString());
if (args.memory_type)
queryParams.append('memory_type', String(args.memory_type));
if (args.tags && Array.isArray(args.tags)) {
args.tags.forEach((tag) => queryParams.append('tags', String(tag)));
}
const response = await fetch(`${apiUrl}/api/v1/memory?${queryParams}`, {
method: 'GET',
headers
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Memory listing failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `📋 Memory list (${result.length || 0} items):\n${JSON.stringify(result, null, 2)}`
}
]
};
}
// API Key Management Tools
case 'create_api_key': {
const response = await fetch(`${apiUrl}/api/v1/api-keys`, {
method: 'POST',
headers,
body: JSON.stringify(args)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API key creation failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `🔑 API key created successfully:\n${JSON.stringify(result, null, 2)}`
}
]
};
}
case 'list_api_keys': {
const queryParams = new URLSearchParams();
if (args.project_id)
queryParams.append('project_id', String(args.project_id));
if (args.active_only !== undefined)
queryParams.append('active_only', args.active_only.toString());
const response = await fetch(`${apiUrl}/api/v1/api-keys?${queryParams}`, {
method: 'GET',
headers
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API key listing failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `🔑 API keys (${result.length || 0} found):\n${JSON.stringify(result, null, 2)}`
}
]
};
}
case 'get_health_status': {
const response = await fetch(`${apiUrl}/api/v1/health`, {
method: 'GET',
headers
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Health check failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `💚 System health status:\n${JSON.stringify(result, null, 2)}`
}
]
};
}
default:
return {
content: [
{
type: 'text',
text: `❌ Unknown tool: ${name}. Available tools: create_memory, search_memories, get_memory, update_memory, delete_memory, list_memories, create_api_key, list_api_keys, get_health_status`
}
]
};
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
content: [
{
type: 'text',
text: `❌ Error: ${errorMessage}`
}
]
};
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
// Log to stderr that server is ready
console.error('[MCP-INFO] Lanonasis MCP Server started and ready');
}
}
// Start the server
const server = new LanonasisMCPServer();
server.run().catch((error) => {
console.error('[MCP-ERROR] Failed to start server:', error);
process.exit(1);
});