@vibe-kit/grok-cli
Version:
An open-source AI agent that brings the power of Grok directly into your terminal.
191 lines • 6.34 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.McpProtocolClient = void 0;
const events_1 = require("events");
const stdio_transport_1 = require("./transports/stdio-transport");
const sse_transport_1 = require("./transports/sse-transport");
class McpProtocolClient extends events_1.EventEmitter {
constructor(options) {
super();
this.options = options;
this.requestId = 0;
this.pendingRequests = new Map();
this.serverInfo = null;
this.tools = [];
// Determine if we should run in quiet mode (suppress stderr in interactive mode)
const isInteractiveMode = !process.argv.includes('--prompt');
const quiet = isInteractiveMode && !process.env.MCP_DEBUG;
// Create appropriate transport
if (options.transport === 'sse') {
if (!options.url) {
throw new Error('URL is required for SSE transport');
}
this.transport = new sse_transport_1.SseTransport({
url: options.url,
headers: options.headers,
env: options.env,
quiet
});
}
else {
if (!options.command) {
throw new Error('Command is required for stdio transport');
}
this.transport = new stdio_transport_1.StdioTransport({
command: options.command,
args: options.args,
env: options.env,
quiet
});
}
// Set up transport event handlers
this.transport.on('message', (message) => {
this.handleMessage(message);
});
this.transport.on('disconnect', () => {
this.emit('disconnect');
});
this.transport.on('error', (error) => {
this.emit('error', error);
});
}
async connect() {
try {
await this.transport.connect();
await this.initialize();
}
catch (error) {
throw new Error(`Failed to connect to MCP server: ${error}`);
}
}
handleMessage(message) {
if ('id' in message) {
// Response
const pending = this.pendingRequests.get(message.id);
if (pending) {
this.pendingRequests.delete(message.id);
if (message.error) {
pending.reject(new Error(`${message.error.message} (${message.error.code})`));
}
else {
pending.resolve(message.result);
}
}
}
else {
// Notification
this.emit('notification', message);
}
}
async sendRequest(method, params) {
if (!this.transport.isConnected()) {
throw new Error('MCP server not connected');
}
const id = ++this.requestId;
const request = {
jsonrpc: '2.0',
id,
method,
params
};
return new Promise((resolve, reject) => {
this.pendingRequests.set(id, { resolve, reject });
this.transport.send(request).catch(reject);
// Timeout after 60 seconds (increased for Docker container startup)
setTimeout(() => {
if (this.pendingRequests.has(id)) {
this.pendingRequests.delete(id);
reject(new Error(`Request timeout for method: ${method} (after 60s)`));
}
}, 60000);
});
}
async initialize() {
try {
// Send initialize request
const initResult = await this.sendRequest('initialize', {
protocolVersion: '2024-11-05',
capabilities: {
tools: {}
},
clientInfo: {
name: 'grok-cli',
version: '1.0.0'
}
});
this.serverInfo = {
name: initResult.serverInfo?.name || 'Unknown',
version: initResult.serverInfo?.version || 'Unknown',
protocolVersion: initResult.protocolVersion || 'Unknown'
};
// Send initialized notification
await this.sendNotification('notifications/initialized');
// List available tools
await this.listTools();
}
catch (error) {
throw new Error(`MCP initialization failed: ${error}`);
}
}
async sendNotification(method, params) {
if (!this.transport.isConnected()) {
throw new Error('MCP server not connected');
}
const notification = {
jsonrpc: '2.0',
method,
params
};
await this.transport.send(notification);
}
async listTools() {
try {
const result = await this.sendRequest('tools/list');
this.tools = result.tools || [];
return this.tools;
}
catch (error) {
console.warn('Failed to list MCP tools:', error);
return [];
}
}
async callTool(name, arguments_) {
try {
const result = await this.sendRequest('tools/call', {
name,
arguments: arguments_
});
return result;
}
catch (error) {
throw new Error(`Tool call failed for ${name}: ${error}`);
}
}
getAvailableTools() {
return [...this.tools];
}
getServerInfo() {
return this.serverInfo;
}
isConnected() {
return this.transport.isConnected();
}
async disconnect() {
// Send shutdown request if still connected
if (this.transport.isConnected()) {
try {
await this.sendRequest('shutdown');
await this.sendNotification('exit');
}
catch (error) {
// Ignore shutdown errors
}
}
await this.transport.disconnect();
this.pendingRequests.clear();
this.tools = [];
this.serverInfo = null;
}
}
exports.McpProtocolClient = McpProtocolClient;
//# sourceMappingURL=mcp-protocol-client.js.map