@goke/mcp 0.0.12 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/auth.ts CHANGED
@@ -29,8 +29,10 @@ async function openBrowser(url: string): Promise<void> {
29
29
 
30
30
  /**
31
31
  * Start the OAuth flow for an MCP server.
32
- * This is an internal function - consumers should not call this directly.
33
- * It is automatically triggered by addMcpCommands when a 401 error occurs.
32
+ *
33
+ * Used internally by addMcpCommands on 401 errors, but also available
34
+ * for CLIs that need explicit control over the auth flow (e.g. a login
35
+ * command that runs the flow in a background daemon).
34
36
  *
35
37
  * This function:
36
38
  * 1. Starts a local callback server on a random port
package/src/index.ts CHANGED
@@ -9,7 +9,6 @@
9
9
  *
10
10
  * - **Auto-discovery**: Fetches all tools from the MCP server and creates CLI commands
11
11
  * - **Caching**: Tools are cached for 1 hour to avoid reconnecting on every invocation
12
- * - **Session reuse**: MCP session IDs are cached to skip initialization handshake
13
12
  * - **Type-aware parsing**: Handles string, number, boolean, object, and array arguments
14
13
  * - **JSON schema support**: Generates CLI options from tool input schemas
15
14
  * - **OAuth support**: Automatic OAuth authentication on 401 errors (lazy auth)
@@ -48,13 +47,14 @@ import { wrapJsonSchema } from "goke";
48
47
  import yaml from "js-yaml";
49
48
  import { FileOAuthProvider } from "./oauth-provider.js";
50
49
  import { startOAuthFlow, isAuthRequiredError } from "./auth.js";
50
+ export { startOAuthFlow } from "./auth.js";
51
51
  import type { McpOAuthConfig, McpOAuthState } from "./types.js";
52
52
  export { addCliToolsToMcp, createMcpAction } from "./cli-to-mcp.js";
53
53
  export type { AddCliToolsToMcpOptions, CreateMcpActionOptions } from "./cli-to-mcp.js";
54
54
 
55
- // Public exports - only types that consumers need
55
+ // Public exports
56
56
  export type { Transport };
57
- export type { McpOAuthConfig, McpOAuthState } from "./types.js";
57
+ export type { McpOAuthConfig, McpOAuthState, StartOAuthFlowOptions, OAuthFlowResult } from "./types.js";
58
58
 
59
59
  export interface CachedMcpTools {
60
60
  tools: Array<{
@@ -63,7 +63,6 @@ export interface CachedMcpTools {
63
63
  inputSchema?: unknown;
64
64
  }>;
65
65
  timestamp: number;
66
- sessionId?: string;
67
66
  }
68
67
 
69
68
  const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
@@ -90,12 +89,15 @@ export interface AddMcpCommandsOptions {
90
89
 
91
90
  /**
92
91
  * Returns a transport to connect to the MCP server, or null if not configured.
93
- * If null is returned, no MCP tool commands will be registered.
94
- * @param sessionId - Optional session ID from cache to reuse existing session
95
- *
96
- * @deprecated Use getMcpUrl + oauth instead for simpler setup
92
+ * Use this for stdio servers or any setup `getMcpUrl` cannot express.
97
93
  */
98
- getMcpTransport?: (sessionId?: string) => Transport | null | Promise<Transport | null>;
94
+ getMcpTransport?: () => Transport | null | Promise<Transport | null>;
95
+
96
+ /**
97
+ * Argv used to decide whether to skip live discovery.
98
+ * Defaults to `process.argv.slice(2)`.
99
+ */
100
+ argv?: string[];
99
101
 
100
102
  /**
101
103
  * Extra headers for MCP HTTP requests (for example `Authorization`).
@@ -236,13 +238,26 @@ function isHelpOrMetaArgv(argv: string[]) {
236
238
  return argv.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-v");
237
239
  }
238
240
 
239
- function createTransportWithAuth(
240
- url: URL,
241
- sessionId: string | undefined,
242
- oauthState: McpOAuthState | undefined,
243
- oauth: McpOAuthConfig | undefined,
244
- headers?: Record<string, string>,
245
- ): StreamableHTTPClientTransport {
241
+ function matchesRegisteredCommand({ argv, cli }: { argv: string[]; cli: Goke }) {
242
+ const parts = argv.filter((arg) => !arg.startsWith("-"));
243
+ return cli.commands.some((cmd) => {
244
+ if (!cmd.name) return false;
245
+ const nameParts = cmd.name.split(" ");
246
+ return nameParts.every((part, i) => parts[i] === part);
247
+ });
248
+ }
249
+
250
+ function createTransportWithAuth({
251
+ url,
252
+ oauthState,
253
+ oauth,
254
+ headers,
255
+ }: {
256
+ url: URL
257
+ oauthState?: McpOAuthState
258
+ oauth?: McpOAuthConfig
259
+ headers?: Record<string, string>
260
+ }): StreamableHTTPClientTransport {
246
261
  let authProvider: FileOAuthProvider | undefined;
247
262
 
248
263
  if (oauth && oauthState?.tokens) {
@@ -261,7 +276,6 @@ function createTransportWithAuth(
261
276
 
262
277
  const hasHeaders = headers && Object.keys(headers).length > 0;
263
278
  return new StreamableHTTPClientTransport(url, {
264
- sessionId,
265
279
  authProvider,
266
280
  requestInit: hasHeaders ? { headers } : undefined,
267
281
  });
@@ -273,7 +287,6 @@ function createTransportWithAuth(
273
287
  * Adds MCP tool commands to a goke CLI instance.
274
288
  *
275
289
  * Tools are cached for 1 hour to avoid connecting on every CLI invocation.
276
- * Session ID is also cached to skip MCP initialization handshake.
277
290
  *
278
291
  * OAuth is lazy - authentication only happens when a 401 error occurs.
279
292
  * After successful auth, the operation is automatically retried.
@@ -289,11 +302,10 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
289
302
  oauth,
290
303
  loadCache,
291
304
  saveCache,
305
+ argv = process.argv.slice(2),
292
306
  } = options;
293
307
 
294
- // Helper to get transport - supports both old and new API
295
- const getTransport = async (sessionId?: string): Promise<Transport | null> => {
296
- // New API: getMcpUrl + oauth
308
+ const getTransport = async (): Promise<Transport | null> => {
297
309
  if (getMcpUrl) {
298
310
  const mcpUrl = getMcpUrl();
299
311
  if (!mcpUrl) {
@@ -303,12 +315,16 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
303
315
  const url = new URL(mcpUrl);
304
316
  const oauthState = oauth?.load();
305
317
 
306
- return createTransportWithAuth(url, sessionId, oauthState, oauth, getHeaders?.());
318
+ return createTransportWithAuth({
319
+ url,
320
+ oauthState,
321
+ oauth,
322
+ headers: getHeaders?.(),
323
+ });
307
324
  }
308
325
 
309
- // Legacy API: getMcpTransport
310
326
  if (getMcpTransport) {
311
- return getMcpTransport(sessionId);
327
+ return getMcpTransport();
312
328
  }
313
329
 
314
330
  return null;
@@ -346,14 +362,17 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
346
362
  // Try to use cached tools first (fast path - no network)
347
363
  const cachedTools = loadCache();
348
364
  const isCacheValid = cachedTools && (Date.now() - cachedTools.timestamp) < CACHE_TTL_MS;
349
- const helpOrMeta = isHelpOrMetaArgv(process.argv.slice(2));
365
+ const skipLiveDiscovery =
366
+ isHelpOrMetaArgv(argv) || matchesRegisteredCommand({ argv, cli });
350
367
 
351
368
  let tools: CachedMcpTools["tools"] | undefined;
352
- let cachedSessionId: string | undefined;
353
369
 
354
370
  if (isCacheValid && cachedTools) {
355
371
  tools = cachedTools.tools;
356
- cachedSessionId = cachedTools.sessionId;
372
+ } else if (skipLiveDiscovery) {
373
+ if (cachedTools) {
374
+ tools = cachedTools.tools;
375
+ }
357
376
  } else {
358
377
  const transport = await getTransport();
359
378
  if (transport) {
@@ -363,8 +382,6 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
363
382
  const result = await client.listTools();
364
383
  tools = result.tools;
365
384
 
366
- const sessionId = (transport as { sessionId?: string }).sessionId;
367
-
368
385
  saveCache({
369
386
  tools: tools.map((t) => ({
370
387
  name: t.name,
@@ -372,11 +389,9 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
372
389
  inputSchema: t.inputSchema,
373
390
  })),
374
391
  timestamp: Date.now(),
375
- sessionId,
376
392
  });
377
- cachedSessionId = sessionId;
378
393
  } catch (err) {
379
- const shouldAuth = isAuthRequiredError(err) && oauth && getMcpUrl && !helpOrMeta;
394
+ const shouldAuth = isAuthRequiredError(err) && oauth && getMcpUrl && !skipLiveDiscovery;
380
395
  if (shouldAuth) {
381
396
  const mcpUrl = getMcpUrl();
382
397
  if (mcpUrl) {
@@ -386,7 +401,7 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
386
401
  }
387
402
  }
388
403
  }
389
- if (!helpOrMeta) {
404
+ if (!skipLiveDiscovery) {
390
405
  console.error(`Failed to connect to MCP server: ${err instanceof Error ? err.message : err}`);
391
406
  }
392
407
  } finally {
@@ -396,7 +411,6 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
396
411
 
397
412
  if (!tools && cachedTools) {
398
413
  tools = cachedTools.tools;
399
- cachedSessionId = cachedTools.sessionId;
400
414
  }
401
415
  }
402
416
 
@@ -450,7 +464,7 @@ export async function addMcpCommands(options: AddMcpCommandsOptions): Promise<vo
450
464
  const parsedArgs = extractToolArguments(cliOptions, inputSchema);
451
465
 
452
466
  const executeWithRetry = async (isRetry = false): Promise<void> => {
453
- const transport = await getTransport(isRetry ? undefined : cachedSessionId);
467
+ const transport = await getTransport();
454
468
  if (!transport) {
455
469
  console.error("MCP transport not available. Run login command first.");
456
470
  process.exit(1);