docs: annotate cli and runtime functions
This commit is contained in:
parent
86003eb431
commit
0772c260ad
10
src/cli.ts
10
src/cli.ts
@ -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);
|
||||
|
||||
10
src/oauth.ts
10
src/oauth.ts
@ -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,
|
||||
|
||||
@ -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 & {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user