Skip to main content
Glama
GrpcClientFactory.js5.81 kB
import * as grpc from "@grpc/grpc-js"; import { TypedEvent } from "../../Common/EventHandler.js"; import { obj2String } from "../../Common/Json.js"; import { Logger } from "../../Common/Logger.js"; class GrpcClient { address; serviceName; proto; option; isClosed = false; client; _closed = new TypedEvent(); get closed() { return this._closed; } constructor(address, serviceName, proto, option) { this.address = address; this.serviceName = serviceName; this.proto = proto; this.option = option; if (!this.checkProto(serviceName, proto)) { throw new Error(`Not contains service: ${serviceName} ${this.getProtoInformation(proto)}`); } this.client = this.createClient(address, serviceName, proto, option); } callAsync(method, request, headers, options, token) { if (this.isClosed) { throw new Error("Client closed."); } if (!this.checkClient(method)) { throw new Error(`Not contains method: ${method} ${this.getProtoInformation()}`); } const callInfo = this.proto.protocolBuffer.packageName + "." + this.serviceName + "/" + method; Logger.debug("GrpcClient call -> " + callInfo); Logger.debug("GrpcClient request -> " + obj2String(request)); Logger.debug("GrpcClient request header -> " + obj2String(headers)); const methodFunc = this.client[method]; return new Promise((resolve, reject) => { let timerId; try { const call = methodFunc.call(this.client, request, this.createMetaData(headers, options), (error, value) => { let response; if (error) { response = { code: error.code, details: error.details, }; } else { response = value; } Logger.debug("GrpcClient response -> " + obj2String(response)); clearInterval(timerId); resolve(response); }); if (token) { timerId = setInterval(() => { if (token.isCancellationRequested) { clearInterval(timerId); call.cancel(); const message = "GrpcClient cancel request -> " + callInfo; Logger.debug(message); reject(new Error(message)); } }, 100); } } catch (e) { const message = `GrpcClient call failed. address: ${this.address} service: ${this.serviceName} ${this.getProtoInformation()} \r\n ${e}`; Logger.warn(message); Logger.warn(e); reject(new Error(message)); } }); } close() { if (this.isClosed) { return; } this.isClosed = true; try { this.client.close(); } catch (e) { Logger.warn(e); } finally { this._closed.emit(undefined); } } checkProto(serviceName, proto) { if (!Object.keys(proto.packageDefinition).includes(proto.protocolBuffer.packageName + "." + serviceName)) { return false; } return true; } checkClient(method) { if (typeof this.client[method] !== "function") { return false; } return true; } getProtoInformation(proto) { const p = proto ?? this.proto; return `(path: ${p.protocolBuffer.metadata.protoPath}, name: ${p.protocolBuffer.metadata.protoFileName})`; } createClient(address, serviceName, proto, option) { const serviceDefinition = proto.packageDefinition[proto.protocolBuffer.packageName + "." + serviceName]; const clientConstructor = grpc.makeClientConstructor(serviceDefinition, serviceName); return new clientConstructor(address, this.createCredentials(option), this.createOptions(address, option)); } createMetaData(headers, options) { const metadata = new grpc.Metadata(); if (headers) { const metaHeaders = new grpc.Metadata(); Object.keys(headers).forEach((k) => metaHeaders.add(k, headers[k])); metadata.merge(metaHeaders); } if (options?.deadlineMsec) { metadata.set("deadline", new Date().setMilliseconds(new Date().getMilliseconds() + options?.deadlineMsec).toString()); } return metadata; } createCredentials(option) { if (option?.SSL) { return grpc.credentials.createSsl(); } else { return grpc.credentials.createInsecure(); } } createOptions(address, option) { let options = {}; const httpProxy = process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY; if (httpProxy) { options = { ...options, "grpc.enable_http_proxy": 0, // "grpc.http_proxy": httpProxy, }; } // if (option?.SSL) { // options = { // ...options, // "grpc.ssl_target_name_override": address, // "grpc.default_authority": address, // }; // } return options; } } class GrpcClientFactory { create(address, serviceName, proto, option) { return new GrpcClient(address, serviceName, proto, option); } } export const grpcClientFactory = new GrpcClientFactory();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Yuki4-dev/grpc-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server