veas
Version:
Veas CLI - Command-line interface for Veas platform
197 lines • 7.95 kB
JavaScript
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 pc from 'picocolors';
import { AuthManager } from '../auth/auth-manager.js';
import { logger } from '../utils/logger.js';
export class DirectMCPServer {
server;
authManager;
tools;
apiUrl;
constructor(_options = {}) {
this.server = new Server({
name: 'veas-mcp-server',
version: '0.1.0',
}, {
capabilities: {
tools: {},
},
});
this.authManager = AuthManager.getInstance();
this.tools = new Map();
this.apiUrl = process.env.VEAS_API_URL || 'https://veas.app';
}
async initialize() {
const pat = process.env.VEAS_PAT;
if (!pat) {
const session = await this.authManager.getSession();
if (!session || !session.token) {
throw new Error('Not authenticated. Please run "veas login" first or set VEAS_PAT environment variable.');
}
}
logger.debug('Loading MCP tools via direct connection...');
await this.loadTools();
logger.debug('Setting up handlers...');
this.setupHandlers();
logger.debug('Direct MCP server initialized');
}
async loadTools() {
const session = await this.authManager.getSession();
const token = process.env.VEAS_PAT || session?.patToken || session?.token;
if (!token) {
throw new Error('No authentication token available');
}
const tokenType = process.env.VEAS_PAT ? 'PAT (env)' : session?.patToken ? 'PAT' : 'device';
logger.info(`Using ${tokenType} token for MCP tools`);
logger.debug(`Token preview: ${token.substring(0, 20)}...`);
try {
logger.debug('Fetching MCP tools from server...');
const response = await fetch(`${this.apiUrl}/api/mcp-simple`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
'X-MCP-Token': token,
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tools/list',
id: '1',
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status}: ${errorText}`);
}
const result = await response.json();
if (result.error) {
throw new Error(`MCP error: ${result.error.message}`);
}
if (!result.result?.tools) {
throw new Error('No tools in response');
}
const mcpTools = result.result.tools;
logger.debug(`Received ${mcpTools.length} tools via direct MCP connection`);
for (const tool of mcpTools) {
this.tools.set(tool.name, tool);
}
logger.info(pc.green(`Loaded ${this.tools.size} MCP tools via direct connection`));
}
catch (error) {
logger.error(`Failed to load MCP tools: ${error.message}`);
logger.error(`API URL: ${this.apiUrl}/api/mcp-simple`);
throw new Error(`Failed to connect to MCP server: ${error.message}`);
}
}
setupHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: Array.from(this.tools.values()),
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const tool = this.tools.get(name);
if (!tool) {
throw new Error(`Tool not found: ${name}`);
}
try {
const result = await this.executeTool(name, args);
let responseText;
if (typeof result === 'string') {
responseText = result;
}
else if (result && typeof result === 'object') {
responseText = JSON.stringify(result, null, 2);
}
else {
responseText = String(result);
}
return {
content: [{ type: 'text', text: responseText }],
};
}
catch (error) {
logger.error(`Error executing tool ${name}:`, error);
throw error;
}
});
}
async executeTool(name, args) {
const session = await this.authManager.getSession();
const token = process.env.VEAS_PAT || session?.patToken || session?.token;
if (!token) {
throw new Error('No authentication token available');
}
const tokenType = process.env.VEAS_PAT ? 'PAT (env)' : session?.patToken ? 'PAT' : 'device';
logger.debug(`Executing tool ${name} with ${tokenType} token`);
const requestBody = {
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: name,
arguments: args,
},
id: Date.now().toString(),
};
logger.debug(`Request to ${this.apiUrl}/api/mcp-simple`);
logger.debug(`Request body: ${JSON.stringify(requestBody, null, 2)}`);
try {
const response = await fetch(`${this.apiUrl}/api/mcp-simple`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
'X-MCP-Token': token,
},
body: JSON.stringify(requestBody),
signal: AbortSignal.timeout(30000),
});
const responseText = await response.text();
logger.debug(`Response status: ${response.status}`);
if (!response.ok) {
logger.error(`Response error: ${responseText}`);
throw new Error(`HTTP ${response.status}: ${responseText}`);
}
let responseData;
try {
responseData = JSON.parse(responseText);
}
catch (_e) {
logger.error(`Failed to parse response: ${responseText}`);
throw new Error('Invalid JSON response from server');
}
if (responseData.error) {
throw new Error(`MCP error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
if (!responseData.result) {
throw new Error('No result in MCP response');
}
const result = responseData.result;
if (result.content && Array.isArray(result.content) && result.content[0]?.data) {
return result.content[0].data;
}
if (result.content && Array.isArray(result.content) && result.content[0]?.text) {
try {
return JSON.parse(result.content[0].text);
}
catch {
return result.content[0].text;
}
}
return result;
}
catch (error) {
logger.error(`Tool execution failed: ${error.message}`);
throw error;
}
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
logger.debug('Direct MCP server started on stdio transport');
}
async stop() {
logger.debug('Direct MCP server stopped');
}
}
//# sourceMappingURL=direct-server.js.map