mcp-use
Version:
Opinionated MCP Framework for TypeScript (@modelcontextprotocol/sdk compatible) - Build MCP Agents, Clients and Servers with support for ChatGPT Apps, Code Mode, OAuth, Notifications, Sampling, Observability and more.
952 lines (943 loc) • 29.7 kB
JavaScript
import {
BaseConnector
} from "./chunk-IL42COOI.js";
import {
Tel
} from "./chunk-43HAMRQH.js";
import {
logger
} from "./chunk-U7F22OTV.js";
import {
__name
} from "./chunk-3GQAWCBQ.js";
// src/session.ts
var MCPSession = class {
static {
__name(this, "MCPSession");
}
connector;
autoConnect;
constructor(connector, autoConnect = true) {
this.connector = connector;
this.autoConnect = autoConnect;
}
async connect() {
await this.connector.connect();
}
async disconnect() {
await this.connector.disconnect();
}
async initialize() {
if (!this.isConnected && this.autoConnect) {
await this.connect();
}
await this.connector.initialize();
}
get isConnected() {
return this.connector && this.connector.isClientConnected;
}
/**
* Register an event handler for session events
*
* @param event - The event type to listen for
* @param handler - The handler function to call when the event occurs
*
* @example
* ```typescript
* session.on("notification", async (notification) => {
* console.log(`Received: ${notification.method}`, notification.params);
*
* if (notification.method === "notifications/tools/list_changed") {
* // Refresh tools list
* }
* });
* ```
*/
on(event, handler) {
if (event === "notification") {
this.connector.onNotification(handler);
}
}
/**
* Set roots and notify the server.
* Roots represent directories or files that the client has access to.
*
* @param roots - Array of Root objects with `uri` (must start with "file://") and optional `name`
*
* @example
* ```typescript
* await session.setRoots([
* { uri: "file:///home/user/project", name: "My Project" },
* { uri: "file:///home/user/data" }
* ]);
* ```
*/
async setRoots(roots) {
return this.connector.setRoots(roots);
}
/**
* Get the current roots.
*/
getRoots() {
return this.connector.getRoots();
}
/**
* Get the cached list of tools from the server.
*
* @returns Array of available tools
*
* @example
* ```typescript
* const tools = session.tools;
* console.log(`Available tools: ${tools.map(t => t.name).join(", ")}`);
* ```
*/
get tools() {
return this.connector.tools;
}
/**
* List all available tools from the MCP server.
* This method fetches fresh tools from the server, unlike the `tools` getter which returns cached tools.
*
* @param options - Optional request options
* @returns Array of available tools
*
* @example
* ```typescript
* const tools = await session.listTools();
* console.log(`Available tools: ${tools.map(t => t.name).join(", ")}`);
* ```
*/
async listTools(options) {
return this.connector.listTools(options);
}
/**
* Get the server capabilities advertised during initialization.
*
* @returns Server capabilities object
*/
get serverCapabilities() {
return this.connector.serverCapabilities;
}
/**
* Get the server information (name and version).
*
* @returns Server info object or null if not available
*/
get serverInfo() {
return this.connector.serverInfo;
}
/**
* Call a tool on the server.
*
* @param name - Name of the tool to call
* @param args - Arguments to pass to the tool (defaults to empty object)
* @param options - Optional request options (timeout, progress handlers, etc.)
* @returns Result from the tool execution
*
* @example
* ```typescript
* const result = await session.callTool("add", { a: 5, b: 3 });
* console.log(`Result: ${result.content[0].text}`);
* ```
*/
async callTool(name, args = {}, options) {
return this.connector.callTool(name, args, options);
}
/**
* List resources from the server with optional pagination.
*
* @param cursor - Optional cursor for pagination
* @param options - Request options
* @returns Resource list with optional nextCursor for pagination
*
* @example
* ```typescript
* const result = await session.listResources();
* console.log(`Found ${result.resources.length} resources`);
* ```
*/
async listResources(cursor, options) {
return this.connector.listResources(cursor, options);
}
/**
* List all resources from the server, automatically handling pagination.
*
* @param options - Request options
* @returns Complete list of all resources
*
* @example
* ```typescript
* const result = await session.listAllResources();
* console.log(`Total resources: ${result.resources.length}`);
* ```
*/
async listAllResources(options) {
return this.connector.listAllResources(options);
}
/**
* List resource templates from the server.
*
* @param options - Request options
* @returns List of available resource templates
*
* @example
* ```typescript
* const result = await session.listResourceTemplates();
* console.log(`Available templates: ${result.resourceTemplates.length}`);
* ```
*/
async listResourceTemplates(options) {
return this.connector.listResourceTemplates(options);
}
/**
* Read a resource by URI.
*
* @param uri - URI of the resource to read
* @param options - Request options
* @returns Resource content
*
* @example
* ```typescript
* const resource = await session.readResource("file:///path/to/file.txt");
* console.log(resource.contents);
* ```
*/
async readResource(uri, options) {
return this.connector.readResource(uri, options);
}
/**
* Subscribe to resource updates.
*
* @param uri - URI of the resource to subscribe to
* @param options - Request options
*
* @example
* ```typescript
* await session.subscribeToResource("file:///path/to/file.txt");
* // Now you'll receive notifications when this resource changes
* ```
*/
async subscribeToResource(uri, options) {
return this.connector.subscribeToResource(uri, options);
}
/**
* Unsubscribe from resource updates.
*
* @param uri - URI of the resource to unsubscribe from
* @param options - Request options
*
* @example
* ```typescript
* await session.unsubscribeFromResource("file:///path/to/file.txt");
* ```
*/
async unsubscribeFromResource(uri, options) {
return this.connector.unsubscribeFromResource(uri, options);
}
/**
* List available prompts from the server.
*
* @returns List of available prompts
*
* @example
* ```typescript
* const result = await session.listPrompts();
* console.log(`Available prompts: ${result.prompts.length}`);
* ```
*/
async listPrompts() {
return this.connector.listPrompts();
}
/**
* Get a specific prompt with arguments.
*
* @param name - Name of the prompt to get
* @param args - Arguments for the prompt
* @returns Prompt result
*
* @example
* ```typescript
* const prompt = await session.getPrompt("greeting", { name: "Alice" });
* console.log(prompt.messages);
* ```
*/
async getPrompt(name, args) {
return this.connector.getPrompt(name, args);
}
/**
* Send a raw request through the client.
*
* @param method - MCP method name
* @param params - Request parameters
* @param options - Request options
* @returns Response from the server
*
* @example
* ```typescript
* const result = await session.request("custom/method", { key: "value" });
* ```
*/
async request(method, params = null, options) {
return this.connector.request(method, params, options);
}
};
// src/client/base.ts
var BaseMCPClient = class {
static {
__name(this, "BaseMCPClient");
}
config = {};
sessions = {};
activeSessions = [];
constructor(config) {
if (config) {
this.config = config;
}
}
static fromDict(_cfg) {
throw new Error("fromDict must be implemented by concrete class");
}
addServer(name, serverConfig) {
this.config.mcpServers = this.config.mcpServers || {};
this.config.mcpServers[name] = serverConfig;
Tel.getInstance().trackClientAddServer(name, serverConfig);
}
removeServer(name) {
if (this.config.mcpServers?.[name]) {
delete this.config.mcpServers[name];
this.activeSessions = this.activeSessions.filter((n) => n !== name);
Tel.getInstance().trackClientRemoveServer(name);
}
}
getServerNames() {
return Object.keys(this.config.mcpServers ?? {});
}
getServerConfig(name) {
return this.config.mcpServers?.[name];
}
getConfig() {
return this.config ?? {};
}
async createSession(serverName, autoInitialize = true) {
const servers = this.config.mcpServers ?? {};
if (Object.keys(servers).length === 0) {
logger.warn("No MCP servers defined in config");
}
if (!servers[serverName]) {
throw new Error(`Server '${serverName}' not found in config`);
}
const connector = this.createConnectorFromConfig(servers[serverName]);
const session = new MCPSession(connector);
if (autoInitialize) {
await session.initialize();
}
this.sessions[serverName] = session;
if (!this.activeSessions.includes(serverName)) {
this.activeSessions.push(serverName);
}
return session;
}
async createAllSessions(autoInitialize = true) {
const servers = this.config.mcpServers ?? {};
if (Object.keys(servers).length === 0) {
logger.warn("No MCP servers defined in config");
}
for (const name of Object.keys(servers)) {
await this.createSession(name, autoInitialize);
}
return this.sessions;
}
getSession(serverName) {
const session = this.sessions[serverName];
if (!session) {
return null;
}
return session;
}
requireSession(serverName) {
const session = this.sessions[serverName];
if (!session) {
throw new Error(
`Session '${serverName}' not found. Available sessions: ${this.activeSessions.join(", ") || "none"}`
);
}
return session;
}
getAllActiveSessions() {
return Object.fromEntries(
this.activeSessions.map((n) => [n, this.sessions[n]])
);
}
async closeSession(serverName) {
const session = this.sessions[serverName];
if (!session) {
logger.warn(
`No session exists for server ${serverName}, nothing to close`
);
return;
}
try {
logger.debug(`Closing session for server ${serverName}`);
await session.disconnect();
} catch (e) {
logger.error(`Error closing session for server '${serverName}': ${e}`);
} finally {
delete this.sessions[serverName];
this.activeSessions = this.activeSessions.filter((n) => n !== serverName);
}
}
async closeAllSessions() {
const serverNames = Object.keys(this.sessions);
const errors = [];
for (const serverName of serverNames) {
try {
logger.debug(`Closing session for server ${serverName}`);
await this.closeSession(serverName);
} catch (e) {
const errorMsg = `Failed to close session for server '${serverName}': ${e}`;
logger.error(errorMsg);
errors.push(errorMsg);
}
}
if (errors.length) {
logger.error(
`Encountered ${errors.length} errors while closing sessions`
);
} else {
logger.debug("All sessions closed successfully");
}
}
};
// src/connectors/http.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import {
StreamableHTTPClientTransport,
StreamableHTTPError
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
// src/task_managers/sse.ts
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
// src/task_managers/base.ts
var ConnectionManager = class {
static {
__name(this, "ConnectionManager");
}
_readyPromise;
_readyResolver;
_donePromise;
_doneResolver;
_exception = null;
_connection = null;
_task = null;
_abortController = null;
constructor() {
this.reset();
}
/**
* Start the connection manager and establish a connection.
*
* @returns The established connection.
* @throws If the connection cannot be established.
*/
async start() {
this.reset();
logger.debug(`Starting ${this.constructor.name}`);
this._task = this.connectionTask();
await this._readyPromise;
if (this._exception) {
throw this._exception;
}
if (this._connection === null) {
throw new Error("Connection was not established");
}
return this._connection;
}
/**
* Stop the connection manager and close the connection.
*/
async stop() {
if (this._task && this._abortController) {
logger.debug(`Cancelling ${this.constructor.name} task`);
this._abortController.abort();
try {
await this._task;
} catch (e) {
if (e instanceof Error && e.name === "AbortError") {
logger.debug(`${this.constructor.name} task aborted successfully`);
} else {
logger.warn(`Error stopping ${this.constructor.name} task: ${e}`);
}
}
}
await this._donePromise;
logger.debug(`${this.constructor.name} task completed`);
}
/**
* Reset all internal state.
*/
reset() {
this._readyPromise = new Promise((res) => this._readyResolver = res);
this._donePromise = new Promise((res) => this._doneResolver = res);
this._exception = null;
this._connection = null;
this._task = null;
this._abortController = new AbortController();
}
/**
* The background task responsible for establishing and maintaining the
* connection until it is cancelled.
*/
async connectionTask() {
logger.debug(`Running ${this.constructor.name} task`);
try {
this._connection = await this.establishConnection();
logger.debug(`${this.constructor.name} connected successfully`);
this._readyResolver();
await this.waitForAbort();
} catch (err) {
this._exception = err;
logger.error(`Error in ${this.constructor.name} task: ${err}`);
this._readyResolver();
} finally {
if (this._connection !== null) {
try {
await this.closeConnection(this._connection);
} catch (closeErr) {
logger.warn(
`Error closing connection in ${this.constructor.name}: ${closeErr}`
);
}
this._connection = null;
}
this._doneResolver();
}
}
/**
* Helper that returns a promise which resolves when the abort signal fires.
*/
async waitForAbort() {
return new Promise((_resolve, _reject) => {
if (!this._abortController) {
return;
}
const signal = this._abortController.signal;
if (signal.aborted) {
_resolve();
return;
}
const onAbort = /* @__PURE__ */ __name(() => {
signal.removeEventListener("abort", onAbort);
_resolve();
}, "onAbort");
signal.addEventListener("abort", onAbort);
});
}
};
// src/task_managers/sse.ts
var SseConnectionManager = class extends ConnectionManager {
static {
__name(this, "SseConnectionManager");
}
url;
opts;
_transport = null;
reinitializing = false;
/**
* Create an SSE connection manager.
*
* @param url The SSE endpoint URL.
* @param opts Optional transport options (auth, headers, etc.).
*/
constructor(url, opts) {
super();
this.url = typeof url === "string" ? new URL(url) : url;
this.opts = opts;
}
/**
* Spawn a new `SSEClientTransport` and wrap it with 404 handling.
* Per MCP spec, clients MUST re-initialize when receiving 404 for stale sessions.
*/
async establishConnection() {
const transport = new SSEClientTransport(this.url, this.opts);
const originalSend = transport.send.bind(transport);
transport.send = async (message) => {
const sendMessage = /* @__PURE__ */ __name(async (msg) => {
if (Array.isArray(msg)) {
for (const singleMsg of msg) {
await originalSend(singleMsg);
}
} else {
await originalSend(msg);
}
}, "sendMessage");
try {
await sendMessage(message);
} catch (error) {
if (error?.code === 404 && transport.sessionId && !this.reinitializing) {
logger.warn(
`[SSE] Session not found (404), re-initializing per MCP spec...`
);
this.reinitializing = true;
try {
transport.sessionId = void 0;
await this.reinitialize(transport);
logger.info(`[SSE] Re-initialization successful, retrying request`);
await sendMessage(message);
} finally {
this.reinitializing = false;
}
} else {
throw error;
}
}
};
this._transport = transport;
logger.debug(`${this.constructor.name} connected successfully`);
return transport;
}
/**
* Re-initialize the transport with a new session
* This is called when the server returns 404 for a stale session
*/
async reinitialize(transport) {
logger.debug(`[SSE] Re-initialization triggered`);
}
/**
* Close the underlying transport and clean up resources.
*/
async closeConnection(_connection) {
if (this._transport) {
try {
await this._transport.close();
} catch (e) {
logger.warn(`Error closing SSE transport: ${e}`);
} finally {
this._transport = null;
}
}
}
};
// src/connectors/http.ts
var HttpConnector = class extends BaseConnector {
static {
__name(this, "HttpConnector");
}
baseUrl;
headers;
timeout;
sseReadTimeout;
clientInfo;
preferSse;
disableSseFallback;
gatewayUrl;
serverId;
transportType = null;
streamableTransport = null;
constructor(baseUrl, opts = {}) {
super(opts);
const originalUrl = baseUrl.replace(/\/$/, "");
this.gatewayUrl = opts.gatewayUrl;
this.serverId = opts.serverId;
if (this.gatewayUrl) {
this.baseUrl = this.gatewayUrl.replace(/\/$/, "");
this.headers = { ...opts.headers ?? {} };
this.headers["X-Target-URL"] = originalUrl;
if (this.serverId) {
this.headers["X-Server-Id"] = this.serverId;
}
} else {
this.baseUrl = originalUrl;
this.headers = { ...opts.headers ?? {} };
}
if (opts.authToken) {
this.headers.Authorization = `Bearer ${opts.authToken}`;
}
this.timeout = opts.timeout ?? 1e4;
this.sseReadTimeout = opts.sseReadTimeout ?? 3e5;
this.clientInfo = opts.clientInfo ?? {
name: "http-connector",
version: "1.0.0"
};
this.preferSse = opts.preferSse ?? false;
this.disableSseFallback = opts.disableSseFallback ?? false;
}
/** Establish connection to the MCP implementation via HTTP (streamable or SSE). */
async connect() {
if (this.connected) {
logger.debug("Already connected to MCP implementation");
return;
}
const baseUrl = this.baseUrl;
if (this.preferSse) {
logger.debug(`Connecting to MCP implementation via HTTP/SSE: ${baseUrl}`);
await this.connectWithSse(baseUrl);
return;
}
logger.debug(`Connecting to MCP implementation via HTTP: ${baseUrl}`);
try {
logger.info("\u{1F504} Attempting streamable HTTP transport...");
await this.connectWithStreamableHttp(baseUrl);
logger.info("\u2705 Successfully connected via streamable HTTP");
} catch (err) {
console.log("error in http connector connect", err);
let fallbackReason = "Unknown error";
let is401Error = false;
let httpStatusCode;
let streamableErr = null;
if (err instanceof StreamableHTTPError) {
streamableErr = err;
} else if (err instanceof Error && err.cause instanceof StreamableHTTPError) {
streamableErr = err.cause;
}
if (streamableErr) {
is401Error = streamableErr.code === 401;
httpStatusCode = streamableErr.code;
console.log("Captured HTTP status code:", httpStatusCode);
if (streamableErr.code === 400 && streamableErr.message.includes("Missing session ID")) {
fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
} else if (streamableErr.code === 404 || streamableErr.code === 405) {
fallbackReason = `Server returned ${streamableErr.code} - server likely doesn't support streamable HTTP`;
logger.debug(fallbackReason);
} else {
fallbackReason = `Server returned ${streamableErr.code}: ${streamableErr.message}`;
logger.debug(fallbackReason);
}
} else if (err instanceof Error) {
const errorStr = err.toString();
const errorMsg = err.message || "";
is401Error = errorStr.includes("401") || errorMsg.includes("Unauthorized");
if (errorStr.includes("Missing session ID") || errorStr.includes("Bad Request: Missing session ID") || errorMsg.includes("FastMCP session ID error")) {
fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
} else if (errorStr.includes("405 Method Not Allowed") || errorStr.includes("404 Not Found")) {
fallbackReason = "Server doesn't support streamable HTTP (405/404)";
logger.debug(fallbackReason);
} else {
fallbackReason = `Streamable HTTP failed: ${err.message}`;
logger.debug(fallbackReason);
}
}
if (is401Error) {
logger.info("Authentication required - skipping SSE fallback");
await this.cleanupResources();
const authError = new Error("Authentication required");
authError.code = 401;
throw authError;
}
logger.info("\u{1F504} Falling back to SSE transport...");
try {
await this.connectWithSse(baseUrl);
} catch (sseErr) {
console.error(`Failed to connect with both transports:`);
console.error(` Streamable HTTP: ${fallbackReason}`);
console.error(` SSE: ${sseErr}`);
await this.cleanupResources();
const sseIs401 = sseErr?.message?.includes("401") || sseErr?.message?.includes("Unauthorized");
if (sseIs401) {
const authError = new Error("Authentication required");
authError.code = 401;
throw authError;
}
const finalError = new Error(
`Could not connect to server with any available transport. Streamable HTTP: ${fallbackReason}`
);
if (httpStatusCode !== void 0) {
Object.defineProperty(finalError, "code", {
value: httpStatusCode,
writable: false,
enumerable: true,
configurable: true
});
logger.debug(
`Preserving HTTP status code ${httpStatusCode} in error for proxy fallback detection`
);
}
throw finalError;
}
}
}
async connectWithStreamableHttp(baseUrl) {
try {
console.log(`[HttpConnector] Connecting with Streamable HTTP:`);
console.log(` Base URL: ${baseUrl}`);
console.log(` Original URL: ${this.baseUrl}`);
console.log(` Gateway URL: ${this.gatewayUrl || "none"}`);
console.log(
` Auth Provider URL: ${this.opts.authProvider?.serverUrl || "none"}`
);
console.log(` Headers: ${JSON.stringify(this.headers)}`);
const streamableTransport = new StreamableHTTPClientTransport(
new URL(baseUrl),
{
authProvider: this.opts.authProvider,
// ← Pass OAuth provider to SDK
requestInit: {
headers: this.headers
},
// Pass through reconnection options
reconnectionOptions: {
maxReconnectionDelay: 3e4,
initialReconnectionDelay: 1e3,
reconnectionDelayGrowFactor: 1.5,
maxRetries: 2
// Disable automatic reconnection - let higher-level logic handle it
}
// Don't pass sessionId - let the SDK generate it automatically during connect()
}
);
let transport = streamableTransport;
if (this.opts.wrapTransport) {
const serverId = this.baseUrl;
transport = this.opts.wrapTransport(
transport,
serverId
);
}
const clientOptions = {
...this.opts.clientOptions || {},
capabilities: {
...this.opts.clientOptions?.capabilities || {},
roots: { listChanged: true },
// Always advertise roots capability
// Add sampling capability if callback is provided
...this.opts.samplingCallback ? { sampling: {} } : {},
// Add elicitation capability if callback is provided
...this.opts.elicitationCallback ? { elicitation: { form: {}, url: {} } } : {}
}
};
logger.debug(
`Creating Client with capabilities:`,
JSON.stringify(clientOptions.capabilities, null, 2)
);
this.client = new Client(this.clientInfo, clientOptions);
this.setupRootsHandler();
logger.debug("Roots handler registered before connect");
try {
await this.client.connect(transport, {
timeout: this.timeout
});
const sessionId = streamableTransport.sessionId;
if (sessionId) {
logger.debug(`Session ID obtained: ${sessionId}`);
} else {
logger.warn(
"Session ID not available after connect - this may cause issues with SSE stream"
);
}
} catch (connectErr) {
if (connectErr instanceof Error) {
const errMsg = connectErr.message || connectErr.toString();
if (errMsg.includes("Missing session ID") || errMsg.includes("Bad Request: Missing session ID") || errMsg.includes("Mcp-Session-Id header is required")) {
const wrappedError = new Error(
`Session ID error: ${errMsg}. The SDK should automatically extract session ID from initialize response.`
);
wrappedError.cause = connectErr;
throw wrappedError;
}
}
throw connectErr;
}
this.streamableTransport = streamableTransport;
this.connectionManager = {
stop: /* @__PURE__ */ __name(async () => {
if (this.streamableTransport) {
try {
await this.streamableTransport.terminateSession();
await this.streamableTransport.close();
} catch (e) {
logger.warn(`Error closing Streamable HTTP transport: ${e}`);
} finally {
this.streamableTransport = null;
}
}
}, "stop")
};
this.connected = true;
this.transportType = "streamable-http";
this.setupNotificationHandler();
this.setupSamplingHandler();
this.setupElicitationHandler();
logger.debug(
`Successfully connected to MCP implementation via streamable HTTP: ${baseUrl}`
);
this.trackConnectorInit({
serverUrl: this.baseUrl,
publicIdentifier: `${this.baseUrl} (streamable-http)`
});
} catch (err) {
await this.cleanupResources();
throw err;
}
}
async connectWithSse(baseUrl) {
try {
this.connectionManager = new SseConnectionManager(baseUrl, {
authProvider: this.opts.authProvider,
// ← Pass OAuth provider to SDK (same as streamable HTTP)
requestInit: {
headers: this.headers
}
});
let transport = await this.connectionManager.start();
if (this.opts.wrapTransport) {
const serverId = this.baseUrl;
transport = this.opts.wrapTransport(transport, serverId);
}
const clientOptions = {
...this.opts.clientOptions || {},
capabilities: {
...this.opts.clientOptions?.capabilities || {},
roots: { listChanged: true },
// Always advertise roots capability
// Add sampling capability if callback is provided
...this.opts.samplingCallback ? { sampling: {} } : {},
// Add elicitation capability if callback is provided
...this.opts.elicitationCallback ? { elicitation: { form: {}, url: {} } } : {}
}
};
logger.debug(
`Creating Client with capabilities (SSE):`,
JSON.stringify(clientOptions.capabilities, null, 2)
);
this.client = new Client(this.clientInfo, clientOptions);
this.setupRootsHandler();
logger.debug("Roots handler registered before connect (SSE)");
await this.client.connect(transport);
this.connected = true;
this.transportType = "sse";
this.setupNotificationHandler();
this.setupSamplingHandler();
this.setupElicitationHandler();
logger.debug(
`Successfully connected to MCP implementation via HTTP/SSE: ${baseUrl}`
);
this.trackConnectorInit({
serverUrl: this.baseUrl,
publicIdentifier: `${this.baseUrl} (sse)`
});
} catch (err) {
await this.cleanupResources();
throw err;
}
}
get publicIdentifier() {
return {
type: "http",
url: this.baseUrl,
transport: this.transportType || "unknown"
};
}
/**
* Get the transport type being used (streamable-http or sse)
*/
getTransportType() {
return this.transportType;
}
};
export {
MCPSession,
BaseMCPClient,
ConnectionManager,
HttpConnector
};