mcp-proxy
Version:
A TypeScript SSE proxy for MCP servers that use stdio transport.
246 lines (245 loc) • 7.89 kB
text/typescript
import http, { IncomingMessage } from "http";
import { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { JSONRPCMessage, ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
import { StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
//#region src/authentication.d.ts
interface AuthConfig {
apiKey?: string;
oauth?: {
error?: string;
error_description?: string;
error_uri?: string;
protectedResource?: {
resource?: string;
};
realm?: string;
scope?: string;
};
}
declare class AuthenticationMiddleware {
private config;
constructor(config?: AuthConfig);
getScopeChallengeResponse(requiredScopes: string[], errorDescription?: string, requestId?: unknown): {
body: string;
headers: Record<string, string>;
statusCode: number;
};
getUnauthorizedResponse(options?: {
error?: string;
error_description?: string;
error_uri?: string;
scope?: string;
}): {
body: string;
headers: Record<string, string>;
};
validateRequest(req: IncomingMessage): boolean;
}
//#endregion
//#region src/InMemoryEventStore.d.ts
interface InMemoryEventStoreOptions {
/**
* Maximum number of events retained across all streams before the oldest
* are evicted (FIFO). Bounds memory use; older reconnect attempts beyond
* this window fall back to a fresh (non-resumed) stream instead of a replay.
*
* @default 1000
*/
maxEvents?: number;
}
/**
* Simple in-memory implementation of the EventStore interface for resumability
* This is primarily intended for examples and testing, not for production use
* where a persistent storage solution would be more appropriate.
*/
declare class InMemoryEventStore implements EventStore {
/**
* The number of events currently retained (for observability/testing).
*/
get size(): number;
private events;
private lastTimestamp;
private lastTimestampCounter;
private readonly maxEvents;
constructor(options?: InMemoryEventStoreOptions);
/**
* Replays events that occurred after a specific event ID
* Implements EventStore.replayEventsAfter
*/
replayEventsAfter(lastEventId: string, {
send
}: {
send: (eventId: string, message: JSONRPCMessage) => Promise<void>;
}): Promise<string>;
/**
* Stores an event with a generated event ID
* Implements EventStore.storeEvent
*/
storeEvent(streamId: string, message: JSONRPCMessage): Promise<string>;
/**
* Generates a monotonic unique event ID in
* `${streamId}_${timestamp}_${counter}_${random}` format.
*/
private generateEventId;
/**
* Extracts the stream ID from an event ID
*/
private getStreamIdFromEventId;
}
//#endregion
//#region src/proxyServer.d.ts
declare const proxyServer: ({
client,
requestTimeout,
server,
serverCapabilities
}: {
client: Client;
requestTimeout?: number;
server: Server;
serverCapabilities: ServerCapabilities;
}) => Promise<void>;
//#endregion
//#region src/startHTTPServer.d.ts
/**
* `false` disables the resumability event store entirely (no replay-on-
* reconnect, no retained state). Omitted/`undefined` creates a fresh,
* bounded `InMemoryEventStore` per session - see `eventStoreMaxEvents`.
* Pass an `EventStore` instance to use a shared or custom-backed store.
*/
type EventStoreOption = EventStore | false;
interface CorsOptions {
allowedHeaders?: string | string[];
credentials?: boolean;
exposedHeaders?: string[];
maxAge?: number;
methods?: string[];
origin?: ((origin: string) => boolean) | string | string[];
}
type SSEServer = {
close: () => Promise<void>;
};
type ServerLike = {
close: Server["close"];
connect: Server["connect"];
};
/**
* Caps how many bytes of a request body the stream endpoint buffers before it
* gives up. `false` (or `0` from the CLI) disables the cap entirely, restoring
* unbounded buffering - only do that behind a gateway that already limits body
* size. Omitted/`undefined` uses `DEFAULT_MAX_BODY_SIZE`.
*/
type MaxBodySizeOption = false | number;
declare const startHTTPServer: <T extends ServerLike>({
apiKey,
authenticate,
cors,
createServer,
enableJsonResponse,
eventStore,
eventStoreMaxEvents,
host,
keepAliveTimeout,
maxBodySize,
oauth,
onClose,
onConnect,
onUnhandledRequest,
port,
sseEndpoint,
sslCa,
sslCert,
sslKey,
stateless,
streamEndpoint
}: {
apiKey?: string;
authenticate?: (request: http.IncomingMessage) => Promise<unknown>;
cors?: boolean | CorsOptions;
createServer: (request: http.IncomingMessage) => Promise<T>;
enableJsonResponse?: boolean;
/**
* Event store for the streamable HTTP transport's resumability support.
* Pass `false` to disable resumability entirely (recommended for
* request/response-only deployments that don't need replay-on-reconnect).
* Omit to get a fresh, bounded `InMemoryEventStore` per session - see
* `eventStoreMaxEvents`. Pass an `EventStore` instance to bring your own
* (e.g. a persistent, cross-process store); it will be shared across all
* sessions handled by this server.
*/
eventStore?: EventStoreOption;
/**
* Caps how many events the auto-created per-session `InMemoryEventStore`
* retains (oldest evicted first) before it's overridden by an explicit
* `eventStore`. Bounds memory for long-lived sessions. Default: 1000.
*/
eventStoreMaxEvents?: number;
host?: string;
keepAliveTimeout?: number;
/**
* Caps how many bytes of a request body the stream endpoint buffers,
* bounding the memory a single request can consume. A request over the cap
* is answered with `413 Payload Too Large` and the connection is closed.
* Default: 10485760 (10 MiB). Pass `false` to disable the cap entirely
* (unbounded buffering - only safe behind a gateway that already limits body
* size). Does not apply to the SSE endpoint, whose POST bodies are read by
* the MCP SDK.
*/
maxBodySize?: MaxBodySizeOption;
oauth?: AuthConfig["oauth"];
onClose?: (server: T) => Promise<void>;
onConnect?: (server: T) => Promise<void>;
onUnhandledRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
port: number;
sseEndpoint?: null | string;
sslCa?: null | string;
sslCert?: null | string;
sslKey?: null | string;
stateless?: boolean;
streamEndpoint?: null | string;
}) => Promise<SSEServer>;
//#endregion
//#region src/startStdioServer.d.ts
declare enum ServerType {
HTTPStream = "HTTPStream",
SSE = "SSE",
}
declare const startStdioServer: ({
initStdioServer,
initStreamClient,
serverType,
transportOptions,
url
}: {
initStdioServer?: () => Promise<Server>;
initStreamClient?: () => Promise<Client>;
serverType: ServerType;
transportOptions?: SSEClientTransportOptions | StreamableHTTPClientTransportOptions;
url: string;
}) => Promise<Server>;
//#endregion
//#region src/tapTransport.d.ts
type TransportEvent = {
error: Error;
type: "onerror";
} | {
message: JSONRPCMessage;
type: "onmessage";
} | {
message: JSONRPCMessage;
type: "send";
} | {
type: "close";
} | {
type: "onclose";
} | {
type: "start";
};
declare const tapTransport: (transport: Transport, eventHandler: (event: TransportEvent) => void) => Transport;
//#endregion
export { type AuthConfig, AuthenticationMiddleware, type CorsOptions, type EventStoreOption, InMemoryEventStore, type InMemoryEventStoreOptions, ServerType, proxyServer, startHTTPServer, startStdioServer, tapTransport };
//# sourceMappingURL=index.d.mts.map