From 0772c260ad4561bd026408501b7d2aea84dce5b2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 5 Nov 2025 03:55:09 +0000 Subject: [PATCH] docs: annotate cli and runtime functions --- src/cli.ts | 10 ++++++++++ src/oauth.ts | 10 ++++++++++ src/runtime.ts | 12 ++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/cli.ts b/src/cli.ts index b10ca8a..3c95146 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,6 +4,7 @@ import { createRuntime } from "./runtime.js"; type FlagMap = Partial>; +// main parses CLI flags and dispatches to list/call commands. async function main(): Promise { const argv = process.argv.slice(2); if (argv.length === 0) { @@ -38,6 +39,7 @@ async function main(): Promise { process.exit(1); } +// extractFlags snacks out targeted flags (and their values) from argv in place. function extractFlags(args: string[], keys: string[]): FlagMap { const flags: FlagMap = {}; let index = 0; @@ -57,6 +59,7 @@ function extractFlags(args: string[], keys: string[]): FlagMap { return flags; } +// handleList prints configured servers and optional tool metadata. async function handleList( runtime: Awaited>, args: string[], @@ -90,6 +93,7 @@ async function handleList( } } +// handleCall invokes a tool, prints JSON, and optionally tails logs. async function handleCall( runtime: Awaited>, args: string[], @@ -138,6 +142,7 @@ async function handleCall( tailLogIfRequested(result, parsed.tailLog ?? false); } +// extractListFlags captures list-specific options such as --schema. function extractListFlags(args: string[]): { schema: boolean } { let schema = false; let index = 0; @@ -161,6 +166,7 @@ interface CallArgsParseResult { tailLog?: boolean; } +// parseCallArguments supports selectors, JSON payloads, and key=value args. function parseCallArguments(args: string[]): CallArgsParseResult { const result: CallArgsParseResult = { args: {}, tailLog: false }; let index = 0; @@ -226,6 +232,7 @@ function parseCallArguments(args: string[]): CallArgsParseResult { return result; } +// coerceValue tries to cast string tokens into JS primitives or JSON. function coerceValue(value: string): unknown { const trimmed = value.trim(); if (trimmed === "") { @@ -253,6 +260,7 @@ function coerceValue(value: string): unknown { return trimmed; } +// indent adds consistent left padding when printing nested JSON. function indent(text: string, pad: string): string { return text .split("\n") @@ -260,6 +268,7 @@ function indent(text: string, pad: string): string { .join("\n"); } +// tailLogIfRequested prints the final lines of any referenced log files. function tailLogIfRequested(result: unknown, enabled: boolean): void { if (!enabled) { return; @@ -305,6 +314,7 @@ function tailLogIfRequested(result: unknown, enabled: boolean): void { } } +// printHelp explains available commands and global flags. function printHelp(message?: string): void { if (message) { console.error(message); diff --git a/src/oauth.ts b/src/oauth.ts index c0eebe6..baf2d15 100644 --- a/src/oauth.ts +++ b/src/oauth.ts @@ -21,6 +21,7 @@ interface Deferred { reject: (reason?: unknown) => void; } +// createDeferred produces a minimal promise wrapper for async coordination. function createDeferred(): Deferred { let resolve!: (value: T) => void; let reject!: (reason?: unknown) => void; @@ -31,6 +32,7 @@ function createDeferred(): Deferred { return { promise, resolve, reject }; } +// openExternal attempts to launch the system browser cross-platform. function openExternal(url: string) { const platform = process.platform; const stdio = "ignore"; @@ -53,10 +55,12 @@ function openExternal(url: string) { } } +// ensureDirectory guarantees a directory exists before writing JSON blobs. async function ensureDirectory(dir: string) { await fs.mkdir(dir, { recursive: true }); } +// readJsonFile returns undefined for missing files instead of throwing. async function readJsonFile(filePath: string): Promise { try { const raw = await fs.readFile(filePath, "utf8"); @@ -74,6 +78,7 @@ async function writeJsonFile(filePath: string, data: unknown) { await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf8"); } +// FileOAuthClientProvider persists OAuth session artifacts to disk and captures callback redirects. class FileOAuthClientProvider implements OAuthClientProvider { private readonly tokenPath: string; private readonly clientInfoPath: string; @@ -149,6 +154,7 @@ class FileOAuthClientProvider implements OAuthClientProvider { }; } + // attachServer listens for the OAuth redirect and resolves/rejects the deferred code promise. private attachServer(server: http.Server) { this.server = server; server.on("request", async (req, res) => { @@ -257,6 +263,7 @@ class FileOAuthClientProvider implements OAuthClientProvider { return value.trim(); } + // invalidateCredentials removes cached files to force the next OAuth flow. async invalidateCredentials( scope: "all" | "client" | "tokens" | "verifier", ): Promise { @@ -279,6 +286,7 @@ class FileOAuthClientProvider implements OAuthClientProvider { ); } + // waitForAuthorizationCode resolves once the local callback server captures a redirect. async waitForAuthorizationCode(): Promise { if (!this.authorizationDeferred) { this.authorizationDeferred = createDeferred(); @@ -286,6 +294,7 @@ class FileOAuthClientProvider implements OAuthClientProvider { return this.authorizationDeferred.promise; } + // close stops the temporary callback server created for the OAuth session. async close(): Promise { if (!this.server) { return; @@ -309,6 +318,7 @@ export async function createOAuthSession( definition: ServerDefinition, logger: OAuthLogger, ): Promise { + // createOAuthSession spins up a temporary callback server and file-backed provider for OAuth flows. const { provider, close } = await FileOAuthClientProvider.create( definition, logger, diff --git a/src/runtime.ts b/src/runtime.ts index 84777be..7457335 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -77,6 +77,7 @@ interface ClientContext { readonly oauthSession?: OAuthSession; } +// createRuntime spins up a pooled MCP runtime from config JSON or provided definitions. export async function createRuntime( options: RuntimeOptions = {}, ): Promise { @@ -91,6 +92,7 @@ export async function createRuntime( return runtime; } +// callOnce connects to a server, invokes a single tool, and disposes the connection immediately. export async function callOnce(params: { server: string; toolName: string; @@ -122,14 +124,17 @@ class McpRuntime implements Runtime { }; } + // listServers returns configured names sorted alphabetically for stable CLI output. listServers(): string[] { return [...this.definitions.keys()].sort((a, b) => a.localeCompare(b)); } + // getDefinitions exposes raw server metadata to consumers such as the CLI. getDefinitions(): ServerDefinition[] { return [...this.definitions.values()]; } + // getDefinition throws when the caller requests an unknown server name. getDefinition(server: string): ServerDefinition { const definition = this.definitions.get(server); if (!definition) { @@ -138,6 +143,7 @@ class McpRuntime implements Runtime { return definition; } + // listTools queries tool metadata and optionally includes schemas when requested. async listTools( server: string, options: ListToolsOptions = {}, @@ -152,6 +158,7 @@ class McpRuntime implements Runtime { })); } + // callTool executes a tool using the args provided by the caller. async callTool( server: string, toolName: string, @@ -165,6 +172,7 @@ class McpRuntime implements Runtime { return client.callTool(params); } + // listResources delegates to the MCP resources/list method with passthrough params. async listResources( server: string, options: Partial = {}, @@ -173,6 +181,7 @@ class McpRuntime implements Runtime { return client.listResources(options as ListResourcesRequest["params"]); } + // connect lazily instantiates a client context per server and memoizes it. async connect(server: string): Promise { const normalized = server.trim(); const existing = this.clients.get(normalized); @@ -195,6 +204,7 @@ class McpRuntime implements Runtime { } } + // close tears down transports (and OAuth sessions) for a single server or all servers. async close(server?: string): Promise { if (server) { const normalized = server.trim(); @@ -219,6 +229,7 @@ class McpRuntime implements Runtime { } } + // createClient wires up transports, optional OAuth sessions, and connects the MCP client. private async createClient( definition: ServerDefinition, ): Promise { @@ -291,6 +302,7 @@ class McpRuntime implements Runtime { }); } + // connectWithAuth retries MCP connect calls while the OAuth flow progresses. private async connectWithAuth( client: Client, transport: Transport & {