veas
Version:
Veas CLI - Command-line interface for Veas platform
236 lines • 8.52 kB
JavaScript
import { AuthManager } from '../auth/auth-manager.js';
import { logger } from '../utils/logger.js';
export class MCPClient {
static instance;
authManager;
baseUrl;
connected = false;
constructor(baseUrl) {
this.authManager = AuthManager.getInstance();
this.baseUrl = baseUrl || process.env.VEAS_API_URL || 'http://localhost:3000';
}
static getInstance() {
if (!MCPClient.instance) {
MCPClient.instance = new MCPClient();
}
return MCPClient.instance;
}
async initialize() {
const credentials = await this.authManager.getCredentials();
const token = credentials?.patToken || credentials?.token || credentials?.accessToken;
if (!token) {
throw new Error('Not authenticated. Please run "veas login" first.');
}
const response = await fetch(`${this.baseUrl}/api/mcp-manual`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: {
name: 'veas-cli',
version: '1.0.0',
},
},
id: 'init',
}),
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('Unauthorized. Please check your authentication.');
}
throw new Error(`Initialization failed: ${response.statusText}`);
}
const result = (await response.json());
if (result?.error) {
throw new Error(result.error.message);
}
this.connected = true;
return result?.result;
}
async listTools() {
const credentials = await this.authManager.getCredentials();
const token = credentials?.patToken || credentials?.token || credentials?.accessToken;
if (!token) {
throw new Error('Authentication token not found. Please run "veas login" first.');
}
const response = await fetch(`${this.baseUrl}/api/mcp/mcp`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tools/list',
params: {},
id: 'list-tools',
}),
});
if (!response.ok) {
throw new Error(`Failed to list tools: ${response.statusText}`);
}
const result = await response.json();
if (result?.error) {
throw new Error(result.error.message);
}
return result?.result;
}
async request(method, params, headers) {
const credentials = await this.authManager.getCredentials();
const token = credentials?.patToken || credentials?.token || credentials?.accessToken;
const response = await fetch(`${this.baseUrl}/api/mcp/mcp`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
...headers,
},
body: JSON.stringify({
jsonrpc: '2.0',
method,
params,
id: Date.now().toString(),
}),
});
return response.json();
}
disconnect() {
this.connected = false;
}
isConnected() {
return this.connected;
}
async callTool(toolName, params) {
const credentials = await this.authManager.getCredentials();
const token = credentials?.patToken || credentials?.token || credentials?.accessToken;
if (!token) {
throw new Error('Authentication token not found. Please run "veas login" first.');
}
const url = `${this.baseUrl}/api/mcp-manual`;
const requestBody = {
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: toolName,
arguments: params,
},
id: Date.now().toString(),
};
logger.debug(`Calling MCP tool: ${toolName}`);
logger.debug(`API URL: ${url}`);
logger.debug(`Using token: ${token ? 'Yes' : 'No'} (length: ${token?.length || 0})`);
logger.debug(`Token type: ${credentials?.patToken ? 'PAT' : credentials?.token ? 'Session' : 'None'}`);
try {
const response = await fetch(url, {
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();
if (!response.ok) {
logger.error(`HTTP ${response.status}: ${responseText}`);
throw new Error(`HTTP ${response.status}: ${responseText}`);
}
let result;
try {
result = JSON.parse(responseText);
}
catch (_e) {
throw new Error(`Failed to parse response: ${responseText}`);
}
if (result.error) {
logger.debug(`MCP error: ${JSON.stringify(result.error)}`);
throw new Error(result.error.message || 'Unknown error');
}
logger.debug(`MCP response result:`, JSON.stringify(result.result, null, 2));
return result.result;
}
catch (error) {
logger.error(`MCP call failed: ${error}`);
if (error instanceof Error && error.name === 'AbortError') {
throw new Error('Request timed out after 30 seconds');
}
throw error;
}
}
async callToolSafe(toolName, params) {
try {
const result = await this.callTool(toolName, params);
return {
success: true,
data: result,
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
async listToolsWithResult() {
const credentials = await this.authManager.getCredentials();
const token = credentials?.patToken || credentials?.token;
if (!token) {
throw new Error('Authentication token not found. Please run "veas login" first.');
}
const url = `${process.env.VEAS_API_URL || 'https://veas.app'}/api/mcp-simple`;
const requestBody = {
jsonrpc: '2.0',
method: 'tools/list',
id: '1',
};
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
'X-MCP-Token': token,
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const errorText = await response.text();
return {
success: false,
error: `HTTP ${response.status}: ${errorText}`,
};
}
const result = await response.json();
if (result.error) {
return {
success: false,
error: result.error.message || 'Unknown error',
};
}
return {
success: true,
data: result.result,
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
}
export async function callMCPTool(toolName, params) {
const client = MCPClient.getInstance();
return client.callTool(toolName, params);
}
//# sourceMappingURL=mcp-client.js.map