docs: annotate cli and runtime functions

This commit is contained in:
Peter Steinberger 2025-11-05 03:55:09 +00:00
parent 86003eb431
commit 0772c260ad
3 changed files with 32 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import { createRuntime } from "./runtime.js";
type FlagMap = Partial<Record<string, string>>;
// main parses CLI flags and dispatches to list/call commands.
async function main(): Promise<void> {
const argv = process.argv.slice(2);
if (argv.length === 0) {
@ -38,6 +39,7 @@ async function main(): Promise<void> {
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<ReturnType<typeof createRuntime>>,
args: string[],
@ -90,6 +93,7 @@ async function handleList(
}
}
// handleCall invokes a tool, prints JSON, and optionally tails logs.
async function handleCall(
runtime: Awaited<ReturnType<typeof createRuntime>>,
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);

View File

@ -21,6 +21,7 @@ interface Deferred<T> {
reject: (reason?: unknown) => void;
}
// createDeferred produces a minimal promise wrapper for async coordination.
function createDeferred<T>(): Deferred<T> {
let resolve!: (value: T) => void;
let reject!: (reason?: unknown) => void;
@ -31,6 +32,7 @@ function createDeferred<T>(): Deferred<T> {
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<T>(filePath: string): Promise<T | undefined> {
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<void> {
@ -279,6 +286,7 @@ class FileOAuthClientProvider implements OAuthClientProvider {
);
}
// waitForAuthorizationCode resolves once the local callback server captures a redirect.
async waitForAuthorizationCode(): Promise<string> {
if (!this.authorizationDeferred) {
this.authorizationDeferred = createDeferred<string>();
@ -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<void> {
if (!this.server) {
return;
@ -309,6 +318,7 @@ export async function createOAuthSession(
definition: ServerDefinition,
logger: OAuthLogger,
): Promise<OAuthSession> {
// createOAuthSession spins up a temporary callback server and file-backed provider for OAuth flows.
const { provider, close } = await FileOAuthClientProvider.create(
definition,
logger,

View File

@ -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<Runtime> {
@ -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<ListResourcesRequest["params"]> = {},
@ -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<ClientContext> {
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<void> {
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<ClientContext> {
@ -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 & {