@toolprint/hypertool-mcp
Version:
HyperTool MCP proxy server for routing requests between clients and multiple underlying MCP servers
155 lines • 4.08 kB
JavaScript
/**
* Stdio client implementation for MCP servers using official MCP SDK
*/
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { EventEmitter } from "events";
import { BaseConnection } from "./base.js";
import { createChildLogger } from "../../utils/logging.js";
const logger = createChildLogger({ module: "clients/stdio" });
/**
* Stdio client wrapper using proper MCP SDK Client
*/
export class StdioClient extends EventEmitter {
config;
client = null;
isConnected = false;
constructor(config) {
super();
this.config = config;
}
get isRunning() {
return this.isConnected && this.client !== null;
}
/**
* Start the MCP server process using SDK Client
*/
async start() {
if (this.isRunning) {
return;
}
try {
const transport = new StdioClientTransport({
command: this.config.command,
args: this.config.args,
env: this.config.env,
});
this.client = new Client({
name: "hypertool-mcp-client",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
await this.client.connect(transport);
this.isConnected = true;
}
catch (error) {
this.client = null;
throw new Error(`Failed to start stdio client: ${error.message}`);
}
}
/**
* Stop the MCP server process
*/
async stop() {
if (!this.client) {
return;
}
try {
await this.client.close();
}
catch (error) {
logger.error("Error closing stdio client:", error);
}
finally {
this.client = null;
this.isConnected = false;
}
}
/**
* List tools from the MCP server
*/
async listTools() {
if (!this.client) {
throw new Error("Client not connected");
}
return await this.client.listTools();
}
/**
* Call a tool on the MCP server
*/
async callTool(params) {
if (!this.client) {
throw new Error("Client not connected");
}
return await this.client.callTool(params);
}
/**
* Ping the server to check if it's responsive
*/
async ping() {
if (!this.isRunning) {
return false;
}
try {
await this.listTools();
return true;
}
catch {
return false;
}
}
/**
* Get the SDK client instance for advanced operations
*/
get sdkClient() {
return this.client;
}
}
/**
* Stdio connection implementation
*/
export class StdioConnection extends BaseConnection {
_client = null;
constructor(serverName, config, options = {}) {
super(serverName, config, options);
}
/**
* Connect to the stdio server
*/
async doConnect() {
this._client = new StdioClient(this.config);
// Forward client events
this._client.on("error", (error) => {
this.emit("error", this.createEvent("error", { error }));
});
this._client.on("disconnect", () => {
this.emit("disconnected", this.createEvent("disconnected"));
});
this._client.on("message", (message) => {
this.emit("message", message);
});
await this._client.start();
}
/**
* Disconnect from the stdio server
*/
async doDisconnect() {
if (this._client) {
await this._client.stop();
this._client = null;
}
}
/**
* Ping the stdio server
*/
async doPing() {
if (!this._client) {
return false;
}
return await this._client.ping();
}
}
//# sourceMappingURL=stdio.js.map