@modeltoolsprotocol/mtpcli 0.2.2 → 0.2.3

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.
Files changed (2) hide show
  1. package/dist/index.js +1219 -228
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7341,28 +7341,6 @@ var init_search2 = __esm(() => {
7341
7341
  });
7342
7342
 
7343
7343
  // src/auth.ts
7344
- var exports_auth = {};
7345
- __export(exports_auth, {
7346
- storeToken: () => storeToken,
7347
- runToken: () => runToken,
7348
- runStatus: () => runStatus,
7349
- runRefresh: () => runRefresh,
7350
- runLogout: () => runLogout,
7351
- runLogin: () => runLogin,
7352
- runEnv: () => runEnv,
7353
- oauth2Login: () => oauth2Login,
7354
- login: () => login,
7355
- loadToken: () => loadToken,
7356
- isTokenExpired: () => isTokenExpired,
7357
- getProvider: () => getProvider,
7358
- getEnvExport: () => getEnvExport,
7359
- getEnvDict: () => getEnvDict,
7360
- getAuthConfig: () => getAuthConfig,
7361
- ensureValidToken: () => ensureValidToken,
7362
- deleteToken: () => deleteToken,
7363
- authStatus: () => authStatus,
7364
- apiKeyLogin: () => apiKeyLogin
7365
- });
7366
7344
  import { createHash, randomBytes } from "node:crypto";
7367
7345
  import {
7368
7346
  chmodSync,
@@ -7372,10 +7350,8 @@ import {
7372
7350
  unlinkSync,
7373
7351
  writeFileSync as writeFileSync2
7374
7352
  } from "node:fs";
7375
- import { createServer } from "node:http";
7376
7353
  import { homedir as homedir2 } from "node:os";
7377
7354
  import { dirname, join as join2 } from "node:path";
7378
- import { createInterface } from "node:readline";
7379
7355
  function tokensDir(baseDir) {
7380
7356
  return join2(baseDir ?? join2(homedir2(), ".mtpcli"), "tokens");
7381
7357
  }
@@ -7395,14 +7371,526 @@ function loadToken(toolName, providerId, baseDir) {
7395
7371
  return null;
7396
7372
  return JSON.parse(readFileSync2(path2, "utf-8"));
7397
7373
  }
7374
+ function isTokenExpired(token) {
7375
+ if (!token.expires_at)
7376
+ return false;
7377
+ const exp = new Date(token.expires_at);
7378
+ const buffer = 5 * 60 * 1000;
7379
+ return exp.getTime() - buffer < Date.now();
7380
+ }
7381
+ function generatePkce() {
7382
+ const bytes = randomBytes(64);
7383
+ const verifier = bytes.toString("base64url").slice(0, 128);
7384
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
7385
+ return { verifier, challenge };
7386
+ }
7387
+ async function exchangeCode(tokenUrl, code, clientId, redirectUri, codeVerifier, resource) {
7388
+ const params = new URLSearchParams({
7389
+ grant_type: "authorization_code",
7390
+ code,
7391
+ client_id: clientId,
7392
+ redirect_uri: redirectUri
7393
+ });
7394
+ if (codeVerifier)
7395
+ params.set("code_verifier", codeVerifier);
7396
+ if (resource)
7397
+ params.set("resource", resource);
7398
+ const resp = await fetch(tokenUrl, {
7399
+ method: "POST",
7400
+ headers: {
7401
+ "Content-Type": "application/x-www-form-urlencoded",
7402
+ Accept: "application/json"
7403
+ },
7404
+ body: params.toString()
7405
+ });
7406
+ const text = await resp.text();
7407
+ return parseTokenResponse(text);
7408
+ }
7409
+ async function refreshAccessToken(tokenUrl, refreshToken, clientId, resource) {
7410
+ const params = new URLSearchParams({
7411
+ grant_type: "refresh_token",
7412
+ refresh_token: refreshToken,
7413
+ client_id: clientId
7414
+ });
7415
+ if (resource)
7416
+ params.set("resource", resource);
7417
+ const resp = await fetch(tokenUrl, {
7418
+ method: "POST",
7419
+ headers: {
7420
+ "Content-Type": "application/x-www-form-urlencoded",
7421
+ Accept: "application/json"
7422
+ },
7423
+ body: params.toString()
7424
+ });
7425
+ const text = await resp.text();
7426
+ return parseTokenResponse(text);
7427
+ }
7428
+ function parseTokenResponse(text) {
7429
+ let val;
7430
+ try {
7431
+ val = JSON.parse(text);
7432
+ } catch {
7433
+ val = Object.fromEntries(new URLSearchParams(text));
7434
+ }
7435
+ const accessToken = val.access_token;
7436
+ if (!accessToken)
7437
+ throw new Error("no access_token in response");
7438
+ const expiresIn = typeof val.expires_in === "number" ? val.expires_in : typeof val.expires_in === "string" ? parseInt(val.expires_in, 10) || undefined : undefined;
7439
+ const expiresAt = expiresIn ? new Date(Date.now() + expiresIn * 1000).toISOString() : undefined;
7440
+ const scope = val.scope;
7441
+ const scopes = scope ? scope.split(/\s+/) : undefined;
7442
+ return {
7443
+ access_token: accessToken,
7444
+ token_type: val.token_type ?? undefined,
7445
+ refresh_token: val.refresh_token ?? undefined,
7446
+ expires_at: expiresAt,
7447
+ scopes,
7448
+ provider_id: "",
7449
+ created_at: new Date().toISOString()
7450
+ };
7451
+ }
7452
+ async function ensureValidToken(toolName, auth) {
7453
+ const provider = auth.providers[0];
7454
+ if (!provider)
7455
+ return null;
7456
+ const token = loadToken(toolName, provider.id);
7457
+ if (!token)
7458
+ return null;
7459
+ if (isTokenExpired(token)) {
7460
+ if (token.refresh_token && provider.tokenUrl && provider.clientId) {
7461
+ try {
7462
+ const newToken = await refreshAccessToken(provider.tokenUrl, token.refresh_token, provider.clientId);
7463
+ if (!newToken.refresh_token) {
7464
+ newToken.refresh_token = token.refresh_token;
7465
+ }
7466
+ newToken.provider_id = provider.id;
7467
+ storeToken(toolName, provider.id, newToken);
7468
+ return newToken.access_token;
7469
+ } catch (e) {
7470
+ process.stderr.write(`warning: token refresh failed: ${e instanceof Error ? e.message : e}
7471
+ `);
7472
+ return null;
7473
+ }
7474
+ }
7475
+ return null;
7476
+ }
7477
+ return token.access_token;
7478
+ }
7479
+ async function getEnvDict(toolName, auth) {
7480
+ const token = await ensureValidToken(toolName, auth);
7481
+ if (!token)
7482
+ return {};
7483
+ return { [auth.envVar]: token };
7484
+ }
7485
+ var init_auth = __esm(() => {
7486
+ init_search2();
7487
+ });
7488
+
7489
+ // src/mcp-oauth.ts
7490
+ var exports_mcp_oauth = {};
7491
+ __export(exports_mcp_oauth, {
7492
+ storeClientRegistration: () => storeClientRegistration,
7493
+ serverStorageKey: () => serverStorageKey,
7494
+ registerClient: () => registerClient,
7495
+ promptForClientId: () => promptForClientId,
7496
+ parseWwwAuthenticate: () => parseWwwAuthenticate,
7497
+ mcpOAuthFlow: () => mcpOAuthFlow,
7498
+ loadOrRefreshMcpToken: () => loadOrRefreshMcpToken,
7499
+ loadClientRegistration: () => loadClientRegistration,
7500
+ fetchResourceMetadata: () => fetchResourceMetadata,
7501
+ fetchAuthServerMetadata: () => fetchAuthServerMetadata
7502
+ });
7503
+ import { randomBytes as randomBytes2 } from "node:crypto";
7504
+ import {
7505
+ existsSync as existsSync3,
7506
+ mkdirSync as mkdirSync3,
7507
+ readFileSync as readFileSync3,
7508
+ writeFileSync as writeFileSync3,
7509
+ chmodSync as chmodSync2
7510
+ } from "node:fs";
7511
+ import { createServer } from "node:http";
7512
+ import { homedir as homedir3 } from "node:os";
7513
+ import { dirname as dirname2, join as join3 } from "node:path";
7514
+ import { createInterface } from "node:readline";
7515
+ function serverStorageKey(url) {
7516
+ const u = new URL(url);
7517
+ return `mcp_${u.host}`;
7518
+ }
7519
+ function parseWwwAuthenticate(header) {
7520
+ const result = {};
7521
+ const params = header.replace(/^Bearer\s+/i, "");
7522
+ const re = /(\w+)=(?:"([^"]*)"|(\S+))/g;
7523
+ let match;
7524
+ while ((match = re.exec(params)) !== null) {
7525
+ const key = match[1];
7526
+ const val = match[2] ?? match[3];
7527
+ if (key === "resource_metadata")
7528
+ result.resourceMetadata = val;
7529
+ if (key === "scope")
7530
+ result.scope = val;
7531
+ }
7532
+ return result;
7533
+ }
7534
+ async function fetchResourceMetadata(url) {
7535
+ const resp = await fetch(url);
7536
+ if (!resp.ok) {
7537
+ throw new Error(`failed to fetch resource metadata from ${url}: ${resp.status}`);
7538
+ }
7539
+ return resp.json();
7540
+ }
7541
+ async function fetchAuthServerMetadata(serverUrl, wwwAuth) {
7542
+ if (wwwAuth) {
7543
+ const parsed = parseWwwAuthenticate(wwwAuth);
7544
+ if (parsed.resourceMetadata) {
7545
+ try {
7546
+ const resource = await fetchResourceMetadata(parsed.resourceMetadata);
7547
+ if (resource.authorization_servers?.length) {
7548
+ const asUrl = resource.authorization_servers[0];
7549
+ const wellKnown = `${asUrl}/.well-known/oauth-authorization-server`;
7550
+ const resp = await fetch(wellKnown);
7551
+ if (resp.ok) {
7552
+ return resp.json();
7553
+ }
7554
+ }
7555
+ } catch {}
7556
+ }
7557
+ }
7558
+ const origin = new URL(serverUrl).origin;
7559
+ const oauthUrl = `${origin}/.well-known/oauth-authorization-server`;
7560
+ try {
7561
+ const resp = await fetch(oauthUrl);
7562
+ if (resp.ok) {
7563
+ return resp.json();
7564
+ }
7565
+ } catch {}
7566
+ const oidcUrl = `${origin}/.well-known/openid-configuration`;
7567
+ try {
7568
+ const resp = await fetch(oidcUrl);
7569
+ if (resp.ok) {
7570
+ return resp.json();
7571
+ }
7572
+ } catch {}
7573
+ throw new Error(`could not discover OAuth authorization server for ${serverUrl}`);
7574
+ }
7575
+ async function registerClient(metadata, redirectUri) {
7576
+ if (!metadata.registration_endpoint) {
7577
+ throw new Error("no registration_endpoint in auth server metadata");
7578
+ }
7579
+ const body = {
7580
+ client_name: "mtpcli",
7581
+ redirect_uris: [redirectUri],
7582
+ grant_types: ["authorization_code"],
7583
+ response_types: ["code"],
7584
+ token_endpoint_auth_method: "none"
7585
+ };
7586
+ const resp = await fetch(metadata.registration_endpoint, {
7587
+ method: "POST",
7588
+ headers: {
7589
+ "Content-Type": "application/json",
7590
+ Accept: "application/json"
7591
+ },
7592
+ body: JSON.stringify(body)
7593
+ });
7594
+ if (!resp.ok) {
7595
+ const text = await resp.text();
7596
+ throw new Error(`dynamic client registration failed (${resp.status}): ${text}`);
7597
+ }
7598
+ const result = await resp.json();
7599
+ const clientId = result.client_id;
7600
+ if (!clientId)
7601
+ throw new Error("no client_id in registration response");
7602
+ return {
7603
+ clientId,
7604
+ clientSecret: result.client_secret,
7605
+ redirectUri,
7606
+ registeredAt: Date.now()
7607
+ };
7608
+ }
7609
+ function promptForClientId(serverUrl) {
7610
+ return new Promise((resolve, reject) => {
7611
+ process.stderr.write(`
7612
+ No dynamic registration available for ${serverUrl}.
7613
+ ` + `You need to register a client manually and provide the client_id.
7614
+ ` + `Enter client_id: `);
7615
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
7616
+ rl.question("", (answer) => {
7617
+ rl.close();
7618
+ const trimmed = answer.trim();
7619
+ if (!trimmed) {
7620
+ reject(new Error("no client_id provided"));
7621
+ return;
7622
+ }
7623
+ resolve(trimmed);
7624
+ });
7625
+ });
7626
+ }
7627
+ function clientStorePath() {
7628
+ return join3(homedir3(), ".mtpcli", "mcp-oauth-clients.json");
7629
+ }
7630
+ function loadClientStore() {
7631
+ const path2 = clientStorePath();
7632
+ if (!existsSync3(path2))
7633
+ return {};
7634
+ try {
7635
+ return JSON.parse(readFileSync3(path2, "utf-8"));
7636
+ } catch {
7637
+ return {};
7638
+ }
7639
+ }
7640
+ function saveClientStore(store) {
7641
+ const path2 = clientStorePath();
7642
+ mkdirSync3(dirname2(path2), { recursive: true });
7643
+ writeFileSync3(path2, JSON.stringify(store, null, 2));
7644
+ chmodSync2(path2, 384);
7645
+ }
7646
+ function loadClientRegistration(origin) {
7647
+ const store = loadClientStore();
7648
+ return store[origin] ?? null;
7649
+ }
7650
+ function storeClientRegistration(origin, client, metadata) {
7651
+ const store = loadClientStore();
7652
+ store[origin] = { client, metadata };
7653
+ saveClientStore(store);
7654
+ }
7655
+ function startCallbackServer() {
7656
+ return new Promise((resolve, reject) => {
7657
+ let codeResolve;
7658
+ let codeReject;
7659
+ const codePromise = new Promise((res, rej) => {
7660
+ codeResolve = res;
7661
+ codeReject = rej;
7662
+ });
7663
+ const server = createServer((req, res) => {
7664
+ const url = new URL(req.url ?? "/", `http://127.0.0.1`);
7665
+ const code = url.searchParams.get("code");
7666
+ const error = url.searchParams.get("error");
7667
+ if (code) {
7668
+ const html = "<html><body><h2>Authentication successful!</h2>" + "<p>You can close this tab and return to your terminal.</p>" + "<script>window.close()</script></body></html>";
7669
+ res.writeHead(200, {
7670
+ "Content-Type": "text/html",
7671
+ "Content-Length": Buffer.byteLength(html).toString()
7672
+ });
7673
+ res.end(html);
7674
+ server.close();
7675
+ codeResolve(code);
7676
+ return;
7677
+ }
7678
+ if (error) {
7679
+ const desc = url.searchParams.get("error_description") ?? "Unknown error";
7680
+ const html = `<html><body><h2>Authentication failed</h2><p>${desc}</p></body></html>`;
7681
+ res.writeHead(400, {
7682
+ "Content-Type": "text/html",
7683
+ "Content-Length": Buffer.byteLength(html).toString()
7684
+ });
7685
+ res.end(html);
7686
+ server.close();
7687
+ codeReject(new Error(`OAuth error: ${error} - ${desc}`));
7688
+ return;
7689
+ }
7690
+ res.writeHead(404);
7691
+ res.end();
7692
+ });
7693
+ server.listen(0, "127.0.0.1", () => {
7694
+ const addr = server.address();
7695
+ if (!addr || typeof addr === "string") {
7696
+ reject(new Error("failed to get callback server address"));
7697
+ return;
7698
+ }
7699
+ resolve({
7700
+ port: addr.port,
7701
+ waitForCode: () => {
7702
+ const timer = setTimeout(() => {
7703
+ server.close();
7704
+ codeReject(new Error("OAuth callback timed out (120s)"));
7705
+ }, 120000);
7706
+ return codePromise.finally(() => clearTimeout(timer));
7707
+ },
7708
+ server
7709
+ });
7710
+ });
7711
+ server.on("error", (e) => {
7712
+ reject(new Error(`failed to bind callback server: ${e.message}`));
7713
+ });
7714
+ });
7715
+ }
7716
+ async function mcpOAuthFlow(serverUrl, wwwAuth, clientId) {
7717
+ const origin = new URL(serverUrl).origin;
7718
+ const storageKey = serverStorageKey(serverUrl);
7719
+ const wwwParsed = parseWwwAuthenticate(wwwAuth);
7720
+ process.stderr.write(`Discovering OAuth authorization server...
7721
+ `);
7722
+ const metadata = await fetchAuthServerMetadata(serverUrl, wwwAuth);
7723
+ const challengeMethods = metadata.code_challenge_methods_supported;
7724
+ if (challengeMethods && !challengeMethods.includes("S256")) {
7725
+ throw new Error("authorization server does not support S256 PKCE (required)");
7726
+ }
7727
+ const callback = await startCallbackServer();
7728
+ const redirectUri = `http://127.0.0.1:${callback.port}/callback`;
7729
+ let resolvedClientId = clientId;
7730
+ let registration;
7731
+ if (!resolvedClientId) {
7732
+ const cached = loadClientRegistration(origin);
7733
+ if (cached) {
7734
+ resolvedClientId = cached.client.clientId;
7735
+ registration = cached.client;
7736
+ process.stderr.write(`Using cached client registration for ${origin}
7737
+ `);
7738
+ }
7739
+ }
7740
+ if (!resolvedClientId) {
7741
+ if (metadata.registration_endpoint) {
7742
+ process.stderr.write(`Registering client dynamically...
7743
+ `);
7744
+ registration = await registerClient(metadata, redirectUri);
7745
+ resolvedClientId = registration.clientId;
7746
+ storeClientRegistration(origin, registration, metadata);
7747
+ } else {
7748
+ callback.server.close();
7749
+ resolvedClientId = await promptForClientId(serverUrl);
7750
+ const newCallback = await startCallbackServer();
7751
+ return doOAuthLogin(metadata, resolvedClientId, `http://127.0.0.1:${newCallback.port}/callback`, newCallback, wwwParsed.scope, storageKey, origin);
7752
+ }
7753
+ }
7754
+ return doOAuthLogin(metadata, resolvedClientId, redirectUri, callback, wwwParsed.scope, storageKey, origin);
7755
+ }
7756
+ async function doOAuthLogin(metadata, clientId, redirectUri, callback, scope, storageKey, origin) {
7757
+ const state = randomBytes2(32).toString("base64url");
7758
+ const pkce = generatePkce();
7759
+ const scopes = scope ?? metadata.scopes_supported?.join(" ") ?? undefined;
7760
+ const params = new URLSearchParams({
7761
+ client_id: clientId,
7762
+ redirect_uri: redirectUri,
7763
+ response_type: "code",
7764
+ state,
7765
+ code_challenge: pkce.challenge,
7766
+ code_challenge_method: "S256"
7767
+ });
7768
+ if (scopes)
7769
+ params.set("scope", scopes);
7770
+ const authUrl = `${metadata.authorization_endpoint}?${params.toString()}`;
7771
+ process.stderr.write(`Opening browser for OAuth login...
7772
+ `);
7773
+ process.stderr.write(`If the browser doesn't open, visit:
7774
+ ${authUrl}
7775
+
7776
+ `);
7777
+ open_default(authUrl).catch(() => {});
7778
+ process.stderr.write(`Waiting for authentication callback on port ${callback.port}...
7779
+ `);
7780
+ const code = await callback.waitForCode();
7781
+ process.stderr.write(`Exchanging authorization code for tokens...
7782
+ `);
7783
+ const token = await exchangeCode(metadata.token_endpoint, code, clientId, redirectUri, pkce.verifier);
7784
+ token.provider_id = "mcp-oauth";
7785
+ storeToken(storageKey, "mcp-oauth", token);
7786
+ storeClientRegistration(origin, {
7787
+ clientId,
7788
+ redirectUri,
7789
+ registeredAt: Date.now()
7790
+ }, metadata);
7791
+ process.stderr.write(`Authenticated with ${origin}
7792
+ `);
7793
+ return token.access_token;
7794
+ }
7795
+ async function loadOrRefreshMcpToken(serverUrl) {
7796
+ const storageKey = serverStorageKey(serverUrl);
7797
+ const token = loadToken(storageKey, "mcp-oauth");
7798
+ if (!token)
7799
+ return null;
7800
+ if (!isTokenExpired(token)) {
7801
+ return token.access_token;
7802
+ }
7803
+ if (token.refresh_token) {
7804
+ const origin = new URL(serverUrl).origin;
7805
+ const cached = loadClientRegistration(origin);
7806
+ if (cached) {
7807
+ try {
7808
+ const newToken = await refreshAccessToken(cached.metadata.token_endpoint, token.refresh_token, cached.client.clientId);
7809
+ if (!newToken.refresh_token) {
7810
+ newToken.refresh_token = token.refresh_token;
7811
+ }
7812
+ newToken.provider_id = "mcp-oauth";
7813
+ storeToken(storageKey, "mcp-oauth", newToken);
7814
+ return newToken.access_token;
7815
+ } catch (e) {
7816
+ process.stderr.write(`warning: token refresh failed: ${e instanceof Error ? e.message : e}
7817
+ `);
7818
+ }
7819
+ }
7820
+ }
7821
+ return null;
7822
+ }
7823
+ var init_mcp_oauth = __esm(() => {
7824
+ init_open();
7825
+ init_auth();
7826
+ });
7827
+
7828
+ // src/auth.ts
7829
+ var exports_auth = {};
7830
+ __export(exports_auth, {
7831
+ storeToken: () => storeToken2,
7832
+ runToken: () => runToken,
7833
+ runStatus: () => runStatus,
7834
+ runRefresh: () => runRefresh,
7835
+ runLogout: () => runLogout,
7836
+ runLogin: () => runLogin,
7837
+ runEnv: () => runEnv,
7838
+ refreshAccessToken: () => refreshAccessToken2,
7839
+ oauth2Login: () => oauth2Login,
7840
+ login: () => login,
7841
+ loadToken: () => loadToken2,
7842
+ isTokenExpired: () => isTokenExpired2,
7843
+ getProvider: () => getProvider,
7844
+ getEnvExport: () => getEnvExport,
7845
+ getEnvDict: () => getEnvDict2,
7846
+ getAuthConfig: () => getAuthConfig,
7847
+ generatePkce: () => generatePkce2,
7848
+ exchangeCode: () => exchangeCode2,
7849
+ ensureValidToken: () => ensureValidToken2,
7850
+ deleteToken: () => deleteToken,
7851
+ authStatus: () => authStatus,
7852
+ apiKeyLogin: () => apiKeyLogin
7853
+ });
7854
+ import { createHash as createHash2, randomBytes as randomBytes3 } from "node:crypto";
7855
+ import {
7856
+ chmodSync as chmodSync3,
7857
+ existsSync as existsSync4,
7858
+ mkdirSync as mkdirSync4,
7859
+ readFileSync as readFileSync4,
7860
+ unlinkSync as unlinkSync2,
7861
+ writeFileSync as writeFileSync4
7862
+ } from "node:fs";
7863
+ import { createServer as createServer2 } from "node:http";
7864
+ import { homedir as homedir4 } from "node:os";
7865
+ import { dirname as dirname3, join as join4 } from "node:path";
7866
+ import { createInterface as createInterface2 } from "node:readline";
7867
+ function tokensDir2(baseDir) {
7868
+ return join4(baseDir ?? join4(homedir4(), ".mtpcli"), "tokens");
7869
+ }
7870
+ function tokenPath2(toolName, providerId, baseDir) {
7871
+ return join4(tokensDir2(baseDir), toolName, `${providerId}.json`);
7872
+ }
7873
+ function storeToken2(toolName, providerId, token, baseDir) {
7874
+ const path2 = tokenPath2(toolName, providerId, baseDir);
7875
+ mkdirSync4(dirname3(path2), { recursive: true });
7876
+ writeFileSync4(path2, JSON.stringify(token, null, 2));
7877
+ chmodSync3(path2, 384);
7878
+ return path2;
7879
+ }
7880
+ function loadToken2(toolName, providerId, baseDir) {
7881
+ const path2 = tokenPath2(toolName, providerId, baseDir);
7882
+ if (!existsSync4(path2))
7883
+ return null;
7884
+ return JSON.parse(readFileSync4(path2, "utf-8"));
7885
+ }
7398
7886
  function deleteToken(toolName, providerId, baseDir) {
7399
- const path2 = tokenPath(toolName, providerId, baseDir);
7400
- if (!existsSync2(path2))
7887
+ const path2 = tokenPath2(toolName, providerId, baseDir);
7888
+ if (!existsSync4(path2))
7401
7889
  return false;
7402
- unlinkSync(path2);
7890
+ unlinkSync2(path2);
7403
7891
  return true;
7404
7892
  }
7405
- function isTokenExpired(token) {
7893
+ function isTokenExpired2(token) {
7406
7894
  if (!token.expires_at)
7407
7895
  return false;
7408
7896
  const exp = new Date(token.expires_at);
@@ -7418,10 +7906,10 @@ function getProvider(auth, providerId) {
7418
7906
  return auth.providers.find((p) => p.id === providerId);
7419
7907
  return auth.providers[0];
7420
7908
  }
7421
- function generatePkce() {
7422
- const bytes = randomBytes(64);
7909
+ function generatePkce2() {
7910
+ const bytes = randomBytes3(64);
7423
7911
  const verifier = bytes.toString("base64url").slice(0, 128);
7424
- const challenge = createHash("sha256").update(verifier).digest("base64url");
7912
+ const challenge = createHash2("sha256").update(verifier).digest("base64url");
7425
7913
  return { verifier, challenge };
7426
7914
  }
7427
7915
  function waitForCallback(port, timeoutSecs) {
@@ -7430,7 +7918,7 @@ function waitForCallback(port, timeoutSecs) {
7430
7918
  server.close();
7431
7919
  reject(new Error("OAuth callback timed out"));
7432
7920
  }, timeoutSecs * 1000);
7433
- const server = createServer((req, res) => {
7921
+ const server = createServer2((req, res) => {
7434
7922
  const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
7435
7923
  const code = url.searchParams.get("code");
7436
7924
  const error = url.searchParams.get("error");
@@ -7469,7 +7957,7 @@ function waitForCallback(port, timeoutSecs) {
7469
7957
  });
7470
7958
  });
7471
7959
  }
7472
- async function exchangeCode(tokenUrl, code, clientId, redirectUri, codeVerifier) {
7960
+ async function exchangeCode2(tokenUrl, code, clientId, redirectUri, codeVerifier, resource) {
7473
7961
  const params = new URLSearchParams({
7474
7962
  grant_type: "authorization_code",
7475
7963
  code,
@@ -7478,6 +7966,8 @@ async function exchangeCode(tokenUrl, code, clientId, redirectUri, codeVerifier)
7478
7966
  });
7479
7967
  if (codeVerifier)
7480
7968
  params.set("code_verifier", codeVerifier);
7969
+ if (resource)
7970
+ params.set("resource", resource);
7481
7971
  const resp = await fetch(tokenUrl, {
7482
7972
  method: "POST",
7483
7973
  headers: {
@@ -7487,14 +7977,16 @@ async function exchangeCode(tokenUrl, code, clientId, redirectUri, codeVerifier)
7487
7977
  body: params.toString()
7488
7978
  });
7489
7979
  const text = await resp.text();
7490
- return parseTokenResponse(text);
7980
+ return parseTokenResponse2(text);
7491
7981
  }
7492
- async function refreshAccessToken(tokenUrl, refreshToken, clientId) {
7982
+ async function refreshAccessToken2(tokenUrl, refreshToken, clientId, resource) {
7493
7983
  const params = new URLSearchParams({
7494
7984
  grant_type: "refresh_token",
7495
7985
  refresh_token: refreshToken,
7496
7986
  client_id: clientId
7497
7987
  });
7988
+ if (resource)
7989
+ params.set("resource", resource);
7498
7990
  const resp = await fetch(tokenUrl, {
7499
7991
  method: "POST",
7500
7992
  headers: {
@@ -7504,9 +7996,9 @@ async function refreshAccessToken(tokenUrl, refreshToken, clientId) {
7504
7996
  body: params.toString()
7505
7997
  });
7506
7998
  const text = await resp.text();
7507
- return parseTokenResponse(text);
7999
+ return parseTokenResponse2(text);
7508
8000
  }
7509
- function parseTokenResponse(text) {
8001
+ function parseTokenResponse2(text) {
7510
8002
  let val;
7511
8003
  try {
7512
8004
  val = JSON.parse(text);
@@ -7538,7 +8030,7 @@ async function oauth2Login(toolName, provider, port) {
7538
8030
  if (!provider.clientId)
7539
8031
  throw new Error("provider missing clientId");
7540
8032
  const redirectUri = `http://127.0.0.1:${port}/callback`;
7541
- const state = randomBytes(32).toString("base64url");
8033
+ const state = randomBytes3(32).toString("base64url");
7542
8034
  const params = new URLSearchParams({
7543
8035
  client_id: provider.clientId,
7544
8036
  redirect_uri: redirectUri,
@@ -7550,7 +8042,7 @@ async function oauth2Login(toolName, provider, port) {
7550
8042
  }
7551
8043
  let codeVerifier;
7552
8044
  if (provider.type === "oauth2-pkce") {
7553
- const pkce = generatePkce();
8045
+ const pkce = generatePkce2();
7554
8046
  codeVerifier = pkce.verifier;
7555
8047
  params.set("code_challenge", pkce.challenge);
7556
8048
  params.set("code_challenge_method", "S256");
@@ -7573,9 +8065,9 @@ ${fullUrl}
7573
8065
  throw new Error("no authorization code received (timed out?)");
7574
8066
  process.stderr.write(`Exchanging authorization code for tokens...
7575
8067
  `);
7576
- const token = await exchangeCode(provider.tokenUrl, result.code, provider.clientId, redirectUri, codeVerifier);
8068
+ const token = await exchangeCode2(provider.tokenUrl, result.code, provider.clientId, redirectUri, codeVerifier);
7577
8069
  token.provider_id = provider.id;
7578
- storeToken(toolName, provider.id, token);
8070
+ storeToken2(toolName, provider.id, token);
7579
8071
  process.stderr.write(`Authenticated with ${display}
7580
8072
  `);
7581
8073
  return token;
@@ -7596,7 +8088,7 @@ function apiKeyLogin(toolName, provider, tokenValue) {
7596
8088
  provider_id: provider.id,
7597
8089
  created_at: new Date().toISOString()
7598
8090
  };
7599
- storeToken(toolName, provider.id, token);
8091
+ storeToken2(toolName, provider.id, token);
7600
8092
  const display = provider.displayName ?? provider.id;
7601
8093
  process.stderr.write(`Token stored for ${display}
7602
8094
  `);
@@ -7615,7 +8107,7 @@ function apiKeyLogin(toolName, provider, tokenValue) {
7615
8107
  `);
7616
8108
  }
7617
8109
  process.stderr.write("Enter your token/API key: ");
7618
- const rl = createInterface({ input: process.stdin, output: process.stderr });
8110
+ const rl = createInterface2({ input: process.stdin, output: process.stderr });
7619
8111
  rl.question("", (answer) => {
7620
8112
  rl.close();
7621
8113
  finish(answer.trim());
@@ -7636,22 +8128,22 @@ async function login(toolName, provider, token, port = 8914) {
7636
8128
  throw new Error(`unknown auth type: ${provider.type}`);
7637
8129
  }
7638
8130
  }
7639
- async function ensureValidToken(toolName, auth) {
8131
+ async function ensureValidToken2(toolName, auth) {
7640
8132
  const provider = auth.providers[0];
7641
8133
  if (!provider)
7642
8134
  return null;
7643
- const token = loadToken(toolName, provider.id);
8135
+ const token = loadToken2(toolName, provider.id);
7644
8136
  if (!token)
7645
8137
  return null;
7646
- if (isTokenExpired(token)) {
8138
+ if (isTokenExpired2(token)) {
7647
8139
  if (token.refresh_token && provider.tokenUrl && provider.clientId) {
7648
8140
  try {
7649
- const newToken = await refreshAccessToken(provider.tokenUrl, token.refresh_token, provider.clientId);
8141
+ const newToken = await refreshAccessToken2(provider.tokenUrl, token.refresh_token, provider.clientId);
7650
8142
  if (!newToken.refresh_token) {
7651
8143
  newToken.refresh_token = token.refresh_token;
7652
8144
  }
7653
8145
  newToken.provider_id = provider.id;
7654
- storeToken(toolName, provider.id, newToken);
8146
+ storeToken2(toolName, provider.id, newToken);
7655
8147
  return newToken.access_token;
7656
8148
  } catch (e) {
7657
8149
  process.stderr.write(`warning: token refresh failed: ${e instanceof Error ? e.message : e}
@@ -7663,14 +8155,14 @@ async function ensureValidToken(toolName, auth) {
7663
8155
  }
7664
8156
  return token.access_token;
7665
8157
  }
7666
- async function getEnvDict(toolName, auth) {
7667
- const token = await ensureValidToken(toolName, auth);
8158
+ async function getEnvDict2(toolName, auth) {
8159
+ const token = await ensureValidToken2(toolName, auth);
7668
8160
  if (!token)
7669
8161
  return {};
7670
8162
  return { [auth.envVar]: token };
7671
8163
  }
7672
8164
  async function getEnvExport(toolName, auth) {
7673
- const env = await getEnvDict(toolName, auth);
8165
+ const env = await getEnvDict2(toolName, auth);
7674
8166
  if (Object.keys(env).length === 0) {
7675
8167
  return "# No token available. Run: mtpcli auth login <tool>";
7676
8168
  }
@@ -7683,7 +8175,7 @@ async function getEnvExport(toolName, auth) {
7683
8175
  async function authStatus(toolName, auth) {
7684
8176
  const providers = [];
7685
8177
  for (const provider of auth.providers) {
7686
- const token = loadToken(toolName, provider.id);
8178
+ const token = loadToken2(toolName, provider.id);
7687
8179
  const status = {
7688
8180
  id: provider.id,
7689
8181
  type: provider.type,
@@ -7691,7 +8183,7 @@ async function authStatus(toolName, auth) {
7691
8183
  authenticated: token !== null
7692
8184
  };
7693
8185
  if (token) {
7694
- status.expired = isTokenExpired(token);
8186
+ status.expired = isTokenExpired2(token);
7695
8187
  status.has_refresh = !!token.refresh_token;
7696
8188
  if (token.expires_at)
7697
8189
  status.expires_at = token.expires_at;
@@ -7744,161 +8236,46 @@ async function runToken(toolName) {
7744
8236
  const auth = await getAuthConfig(toolName);
7745
8237
  if (!auth)
7746
8238
  throw new Error(`tool '${toolName}' does not declare auth`);
7747
- const t = await ensureValidToken(toolName, auth);
8239
+ const t = await ensureValidToken2(toolName, auth);
7748
8240
  if (!t)
7749
8241
  throw new Error(`no valid token for '${toolName}'. Run: mtpcli auth login ${toolName}`);
7750
8242
  console.log(t);
7751
8243
  }
7752
- async function runEnv(toolName) {
7753
- const auth = await getAuthConfig(toolName);
7754
- if (!auth)
7755
- throw new Error(`tool '${toolName}' does not declare auth`);
7756
- console.log(await getEnvExport(toolName, auth));
7757
- }
7758
- async function runRefresh(toolName) {
7759
- const authConfig = await getAuthConfig(toolName);
7760
- if (!authConfig)
7761
- throw new Error(`tool '${toolName}' does not declare auth`);
7762
- const provider = authConfig.providers[0];
7763
- if (!provider)
7764
- throw new Error("no auth provider configured");
7765
- const token = loadToken(toolName, provider.id);
7766
- if (!token) {
7767
- throw new Error(`no stored token for '${toolName}'. Run: mtpcli auth login ${toolName}`);
7768
- }
7769
- if (!token.refresh_token) {
7770
- throw new Error(`token for '${toolName}' has no refresh_token (provider type: ${provider.type})`);
7771
- }
7772
- if (!provider.tokenUrl || !provider.clientId) {
7773
- throw new Error("provider missing tokenUrl or clientId for refresh");
7774
- }
7775
- const newToken = await refreshAccessToken(provider.tokenUrl, token.refresh_token, provider.clientId);
7776
- if (!newToken.refresh_token) {
7777
- newToken.refresh_token = token.refresh_token;
7778
- }
7779
- newToken.provider_id = provider.id;
7780
- storeToken(toolName, provider.id, newToken);
7781
- const display = provider.displayName ?? provider.id;
7782
- process.stderr.write(`Token refreshed for ${display}
7783
- `);
7784
- }
7785
- var init_auth = __esm(() => {
7786
- init_open();
7787
- init_search2();
7788
- });
7789
-
7790
- // src/auth.ts
7791
- import {
7792
- chmodSync as chmodSync2,
7793
- existsSync as existsSync3,
7794
- mkdirSync as mkdirSync3,
7795
- readFileSync as readFileSync3,
7796
- unlinkSync as unlinkSync2,
7797
- writeFileSync as writeFileSync3
7798
- } from "node:fs";
7799
- import { homedir as homedir3 } from "node:os";
7800
- import { dirname as dirname2, join as join3 } from "node:path";
7801
- function tokensDir2(baseDir) {
7802
- return join3(baseDir ?? join3(homedir3(), ".mtpcli"), "tokens");
7803
- }
7804
- function tokenPath2(toolName, providerId, baseDir) {
7805
- return join3(tokensDir2(baseDir), toolName, `${providerId}.json`);
7806
- }
7807
- function storeToken2(toolName, providerId, token, baseDir) {
7808
- const path2 = tokenPath2(toolName, providerId, baseDir);
7809
- mkdirSync3(dirname2(path2), { recursive: true });
7810
- writeFileSync3(path2, JSON.stringify(token, null, 2));
7811
- chmodSync2(path2, 384);
7812
- return path2;
7813
- }
7814
- function loadToken2(toolName, providerId, baseDir) {
7815
- const path2 = tokenPath2(toolName, providerId, baseDir);
7816
- if (!existsSync3(path2))
7817
- return null;
7818
- return JSON.parse(readFileSync3(path2, "utf-8"));
7819
- }
7820
- function isTokenExpired2(token) {
7821
- if (!token.expires_at)
7822
- return false;
7823
- const exp = new Date(token.expires_at);
7824
- const buffer = 5 * 60 * 1000;
7825
- return exp.getTime() - buffer < Date.now();
7826
- }
7827
- async function refreshAccessToken2(tokenUrl, refreshToken, clientId) {
7828
- const params = new URLSearchParams({
7829
- grant_type: "refresh_token",
7830
- refresh_token: refreshToken,
7831
- client_id: clientId
7832
- });
7833
- const resp = await fetch(tokenUrl, {
7834
- method: "POST",
7835
- headers: {
7836
- "Content-Type": "application/x-www-form-urlencoded",
7837
- Accept: "application/json"
7838
- },
7839
- body: params.toString()
7840
- });
7841
- const text = await resp.text();
7842
- return parseTokenResponse2(text);
7843
- }
7844
- function parseTokenResponse2(text) {
7845
- let val;
7846
- try {
7847
- val = JSON.parse(text);
7848
- } catch {
7849
- val = Object.fromEntries(new URLSearchParams(text));
7850
- }
7851
- const accessToken = val.access_token;
7852
- if (!accessToken)
7853
- throw new Error("no access_token in response");
7854
- const expiresIn = typeof val.expires_in === "number" ? val.expires_in : typeof val.expires_in === "string" ? parseInt(val.expires_in, 10) || undefined : undefined;
7855
- const expiresAt = expiresIn ? new Date(Date.now() + expiresIn * 1000).toISOString() : undefined;
7856
- const scope = val.scope;
7857
- const scopes = scope ? scope.split(/\s+/) : undefined;
7858
- return {
7859
- access_token: accessToken,
7860
- token_type: val.token_type ?? undefined,
7861
- refresh_token: val.refresh_token ?? undefined,
7862
- expires_at: expiresAt,
7863
- scopes,
7864
- provider_id: "",
7865
- created_at: new Date().toISOString()
7866
- };
7867
- }
7868
- async function ensureValidToken2(toolName, auth) {
7869
- const provider = auth.providers[0];
7870
- if (!provider)
7871
- return null;
7872
- const token = loadToken2(toolName, provider.id);
7873
- if (!token)
7874
- return null;
7875
- if (isTokenExpired2(token)) {
7876
- if (token.refresh_token && provider.tokenUrl && provider.clientId) {
7877
- try {
7878
- const newToken = await refreshAccessToken2(provider.tokenUrl, token.refresh_token, provider.clientId);
7879
- if (!newToken.refresh_token) {
7880
- newToken.refresh_token = token.refresh_token;
7881
- }
7882
- newToken.provider_id = provider.id;
7883
- storeToken2(toolName, provider.id, newToken);
7884
- return newToken.access_token;
7885
- } catch (e) {
7886
- process.stderr.write(`warning: token refresh failed: ${e instanceof Error ? e.message : e}
7887
- `);
7888
- return null;
7889
- }
7890
- }
7891
- return null;
7892
- }
7893
- return token.access_token;
7894
- }
7895
- async function getEnvDict2(toolName, auth) {
7896
- const token = await ensureValidToken2(toolName, auth);
7897
- if (!token)
7898
- return {};
7899
- return { [auth.envVar]: token };
8244
+ async function runEnv(toolName) {
8245
+ const auth = await getAuthConfig(toolName);
8246
+ if (!auth)
8247
+ throw new Error(`tool '${toolName}' does not declare auth`);
8248
+ console.log(await getEnvExport(toolName, auth));
8249
+ }
8250
+ async function runRefresh(toolName) {
8251
+ const authConfig = await getAuthConfig(toolName);
8252
+ if (!authConfig)
8253
+ throw new Error(`tool '${toolName}' does not declare auth`);
8254
+ const provider = authConfig.providers[0];
8255
+ if (!provider)
8256
+ throw new Error("no auth provider configured");
8257
+ const token = loadToken2(toolName, provider.id);
8258
+ if (!token) {
8259
+ throw new Error(`no stored token for '${toolName}'. Run: mtpcli auth login ${toolName}`);
8260
+ }
8261
+ if (!token.refresh_token) {
8262
+ throw new Error(`token for '${toolName}' has no refresh_token (provider type: ${provider.type})`);
8263
+ }
8264
+ if (!provider.tokenUrl || !provider.clientId) {
8265
+ throw new Error("provider missing tokenUrl or clientId for refresh");
8266
+ }
8267
+ const newToken = await refreshAccessToken2(provider.tokenUrl, token.refresh_token, provider.clientId);
8268
+ if (!newToken.refresh_token) {
8269
+ newToken.refresh_token = token.refresh_token;
8270
+ }
8271
+ newToken.provider_id = provider.id;
8272
+ storeToken2(toolName, provider.id, newToken);
8273
+ const display = provider.displayName ?? provider.id;
8274
+ process.stderr.write(`Token refreshed for ${display}
8275
+ `);
7900
8276
  }
7901
8277
  var init_auth2 = __esm(() => {
8278
+ init_open();
7902
8279
  init_search2();
7903
8280
  });
7904
8281
 
@@ -7963,7 +8340,7 @@ __export(exports_serve, {
7963
8340
  argToJsonSchema: () => argToJsonSchema
7964
8341
  });
7965
8342
  import { execFile as execFile9, spawn } from "node:child_process";
7966
- import { createInterface as createInterface2 } from "node:readline";
8343
+ import { createInterface as createInterface3 } from "node:readline";
7967
8344
  import { promisify as promisify9 } from "node:util";
7968
8345
  function argToJsonSchema(arg) {
7969
8346
  const schema = {};
@@ -8139,7 +8516,7 @@ async function handleRequest(state, req) {
8139
8516
  let authEnv;
8140
8517
  const authConfig = state.authConfigs.get(entry.cliTool);
8141
8518
  if (authConfig) {
8142
- const env = await getEnvDict2(entry.cliTool, authConfig);
8519
+ const env = await getEnvDict(entry.cliTool, authConfig);
8143
8520
  if (Object.keys(env).length > 0)
8144
8521
  authEnv = env;
8145
8522
  }
@@ -8186,7 +8563,7 @@ async function run(toolNames) {
8186
8563
  schemas: allSchemas,
8187
8564
  authConfigs: allAuth
8188
8565
  };
8189
- const rl = createInterface2({ input: process.stdin, crlfDelay: Infinity });
8566
+ const rl = createInterface3({ input: process.stdin, crlfDelay: Infinity });
8190
8567
  for await (const line of rl) {
8191
8568
  const trimmed = line.trim();
8192
8569
  if (!trimmed)
@@ -8209,24 +8586,365 @@ async function run(toolNames) {
8209
8586
  }
8210
8587
  var execFileAsync7, VERSION = "0.1.0";
8211
8588
  var init_serve = __esm(() => {
8212
- init_auth2();
8589
+ init_auth();
8213
8590
  init_mcp();
8214
8591
  init_models();
8215
8592
  init_search2();
8216
8593
  execFileAsync7 = promisify9(execFile9);
8217
8594
  });
8218
8595
 
8596
+ // src/mcp-oauth.ts
8597
+ var exports_mcp_oauth2 = {};
8598
+ __export(exports_mcp_oauth2, {
8599
+ storeClientRegistration: () => storeClientRegistration2,
8600
+ serverStorageKey: () => serverStorageKey2,
8601
+ registerClient: () => registerClient2,
8602
+ promptForClientId: () => promptForClientId2,
8603
+ parseWwwAuthenticate: () => parseWwwAuthenticate2,
8604
+ mcpOAuthFlow: () => mcpOAuthFlow2,
8605
+ loadOrRefreshMcpToken: () => loadOrRefreshMcpToken2,
8606
+ loadClientRegistration: () => loadClientRegistration2,
8607
+ fetchResourceMetadata: () => fetchResourceMetadata2,
8608
+ fetchAuthServerMetadata: () => fetchAuthServerMetadata2
8609
+ });
8610
+ import { randomBytes as randomBytes4 } from "node:crypto";
8611
+ import {
8612
+ existsSync as existsSync5,
8613
+ mkdirSync as mkdirSync5,
8614
+ readFileSync as readFileSync5,
8615
+ writeFileSync as writeFileSync5,
8616
+ chmodSync as chmodSync4
8617
+ } from "node:fs";
8618
+ import { createServer as createServer3 } from "node:http";
8619
+ import { homedir as homedir5 } from "node:os";
8620
+ import { dirname as dirname4, join as join5 } from "node:path";
8621
+ import { createInterface as createInterface4 } from "node:readline";
8622
+ function serverStorageKey2(url) {
8623
+ const u = new URL(url);
8624
+ return `mcp_${u.host}`;
8625
+ }
8626
+ function parseWwwAuthenticate2(header) {
8627
+ const result = {};
8628
+ const params = header.replace(/^Bearer\s+/i, "");
8629
+ const re = /(\w+)=(?:"([^"]*)"|(\S+))/g;
8630
+ let match;
8631
+ while ((match = re.exec(params)) !== null) {
8632
+ const key = match[1];
8633
+ const val = match[2] ?? match[3];
8634
+ if (key === "resource_metadata")
8635
+ result.resourceMetadata = val;
8636
+ if (key === "scope")
8637
+ result.scope = val;
8638
+ }
8639
+ return result;
8640
+ }
8641
+ async function fetchResourceMetadata2(url) {
8642
+ const resp = await fetch(url);
8643
+ if (!resp.ok) {
8644
+ throw new Error(`failed to fetch resource metadata from ${url}: ${resp.status}`);
8645
+ }
8646
+ return resp.json();
8647
+ }
8648
+ async function fetchAuthServerMetadata2(serverUrl, wwwAuth) {
8649
+ if (wwwAuth) {
8650
+ const parsed = parseWwwAuthenticate2(wwwAuth);
8651
+ if (parsed.resourceMetadata) {
8652
+ try {
8653
+ const resource = await fetchResourceMetadata2(parsed.resourceMetadata);
8654
+ if (resource.authorization_servers?.length) {
8655
+ const asUrl = resource.authorization_servers[0];
8656
+ const wellKnown = `${asUrl}/.well-known/oauth-authorization-server`;
8657
+ const resp = await fetch(wellKnown);
8658
+ if (resp.ok) {
8659
+ return resp.json();
8660
+ }
8661
+ }
8662
+ } catch {}
8663
+ }
8664
+ }
8665
+ const origin = new URL(serverUrl).origin;
8666
+ const oauthUrl = `${origin}/.well-known/oauth-authorization-server`;
8667
+ try {
8668
+ const resp = await fetch(oauthUrl);
8669
+ if (resp.ok) {
8670
+ return resp.json();
8671
+ }
8672
+ } catch {}
8673
+ const oidcUrl = `${origin}/.well-known/openid-configuration`;
8674
+ try {
8675
+ const resp = await fetch(oidcUrl);
8676
+ if (resp.ok) {
8677
+ return resp.json();
8678
+ }
8679
+ } catch {}
8680
+ throw new Error(`could not discover OAuth authorization server for ${serverUrl}`);
8681
+ }
8682
+ async function registerClient2(metadata, redirectUri) {
8683
+ if (!metadata.registration_endpoint) {
8684
+ throw new Error("no registration_endpoint in auth server metadata");
8685
+ }
8686
+ const body = {
8687
+ client_name: "mtpcli",
8688
+ redirect_uris: [redirectUri],
8689
+ grant_types: ["authorization_code"],
8690
+ response_types: ["code"],
8691
+ token_endpoint_auth_method: "none"
8692
+ };
8693
+ const resp = await fetch(metadata.registration_endpoint, {
8694
+ method: "POST",
8695
+ headers: {
8696
+ "Content-Type": "application/json",
8697
+ Accept: "application/json"
8698
+ },
8699
+ body: JSON.stringify(body)
8700
+ });
8701
+ if (!resp.ok) {
8702
+ const text = await resp.text();
8703
+ throw new Error(`dynamic client registration failed (${resp.status}): ${text}`);
8704
+ }
8705
+ const result = await resp.json();
8706
+ const clientId = result.client_id;
8707
+ if (!clientId)
8708
+ throw new Error("no client_id in registration response");
8709
+ return {
8710
+ clientId,
8711
+ clientSecret: result.client_secret,
8712
+ redirectUri,
8713
+ registeredAt: Date.now()
8714
+ };
8715
+ }
8716
+ function promptForClientId2(serverUrl) {
8717
+ return new Promise((resolve, reject) => {
8718
+ process.stderr.write(`
8719
+ No dynamic registration available for ${serverUrl}.
8720
+ ` + `You need to register a client manually and provide the client_id.
8721
+ ` + `Enter client_id: `);
8722
+ const rl = createInterface4({ input: process.stdin, output: process.stderr });
8723
+ rl.question("", (answer) => {
8724
+ rl.close();
8725
+ const trimmed = answer.trim();
8726
+ if (!trimmed) {
8727
+ reject(new Error("no client_id provided"));
8728
+ return;
8729
+ }
8730
+ resolve(trimmed);
8731
+ });
8732
+ });
8733
+ }
8734
+ function clientStorePath2() {
8735
+ return join5(homedir5(), ".mtpcli", "mcp-oauth-clients.json");
8736
+ }
8737
+ function loadClientStore2() {
8738
+ const path2 = clientStorePath2();
8739
+ if (!existsSync5(path2))
8740
+ return {};
8741
+ try {
8742
+ return JSON.parse(readFileSync5(path2, "utf-8"));
8743
+ } catch {
8744
+ return {};
8745
+ }
8746
+ }
8747
+ function saveClientStore2(store) {
8748
+ const path2 = clientStorePath2();
8749
+ mkdirSync5(dirname4(path2), { recursive: true });
8750
+ writeFileSync5(path2, JSON.stringify(store, null, 2));
8751
+ chmodSync4(path2, 384);
8752
+ }
8753
+ function loadClientRegistration2(origin) {
8754
+ const store = loadClientStore2();
8755
+ return store[origin] ?? null;
8756
+ }
8757
+ function storeClientRegistration2(origin, client, metadata) {
8758
+ const store = loadClientStore2();
8759
+ store[origin] = { client, metadata };
8760
+ saveClientStore2(store);
8761
+ }
8762
+ function startCallbackServer2() {
8763
+ return new Promise((resolve, reject) => {
8764
+ let codeResolve;
8765
+ let codeReject;
8766
+ const codePromise = new Promise((res, rej) => {
8767
+ codeResolve = res;
8768
+ codeReject = rej;
8769
+ });
8770
+ const server = createServer3((req, res) => {
8771
+ const url = new URL(req.url ?? "/", `http://127.0.0.1`);
8772
+ const code = url.searchParams.get("code");
8773
+ const error = url.searchParams.get("error");
8774
+ if (code) {
8775
+ const html = "<html><body><h2>Authentication successful!</h2>" + "<p>You can close this tab and return to your terminal.</p>" + "<script>window.close()</script></body></html>";
8776
+ res.writeHead(200, {
8777
+ "Content-Type": "text/html",
8778
+ "Content-Length": Buffer.byteLength(html).toString()
8779
+ });
8780
+ res.end(html);
8781
+ server.close();
8782
+ codeResolve(code);
8783
+ return;
8784
+ }
8785
+ if (error) {
8786
+ const desc = url.searchParams.get("error_description") ?? "Unknown error";
8787
+ const html = `<html><body><h2>Authentication failed</h2><p>${desc}</p></body></html>`;
8788
+ res.writeHead(400, {
8789
+ "Content-Type": "text/html",
8790
+ "Content-Length": Buffer.byteLength(html).toString()
8791
+ });
8792
+ res.end(html);
8793
+ server.close();
8794
+ codeReject(new Error(`OAuth error: ${error} - ${desc}`));
8795
+ return;
8796
+ }
8797
+ res.writeHead(404);
8798
+ res.end();
8799
+ });
8800
+ server.listen(0, "127.0.0.1", () => {
8801
+ const addr = server.address();
8802
+ if (!addr || typeof addr === "string") {
8803
+ reject(new Error("failed to get callback server address"));
8804
+ return;
8805
+ }
8806
+ resolve({
8807
+ port: addr.port,
8808
+ waitForCode: () => {
8809
+ const timer = setTimeout(() => {
8810
+ server.close();
8811
+ codeReject(new Error("OAuth callback timed out (120s)"));
8812
+ }, 120000);
8813
+ return codePromise.finally(() => clearTimeout(timer));
8814
+ },
8815
+ server
8816
+ });
8817
+ });
8818
+ server.on("error", (e) => {
8819
+ reject(new Error(`failed to bind callback server: ${e.message}`));
8820
+ });
8821
+ });
8822
+ }
8823
+ async function mcpOAuthFlow2(serverUrl, wwwAuth, clientId) {
8824
+ const origin = new URL(serverUrl).origin;
8825
+ const storageKey = serverStorageKey2(serverUrl);
8826
+ const wwwParsed = parseWwwAuthenticate2(wwwAuth);
8827
+ process.stderr.write(`Discovering OAuth authorization server...
8828
+ `);
8829
+ const metadata = await fetchAuthServerMetadata2(serverUrl, wwwAuth);
8830
+ const challengeMethods = metadata.code_challenge_methods_supported;
8831
+ if (challengeMethods && !challengeMethods.includes("S256")) {
8832
+ throw new Error("authorization server does not support S256 PKCE (required)");
8833
+ }
8834
+ const callback = await startCallbackServer2();
8835
+ const redirectUri = `http://127.0.0.1:${callback.port}/callback`;
8836
+ let resolvedClientId = clientId;
8837
+ let registration;
8838
+ if (!resolvedClientId) {
8839
+ const cached = loadClientRegistration2(origin);
8840
+ if (cached) {
8841
+ resolvedClientId = cached.client.clientId;
8842
+ registration = cached.client;
8843
+ process.stderr.write(`Using cached client registration for ${origin}
8844
+ `);
8845
+ }
8846
+ }
8847
+ if (!resolvedClientId) {
8848
+ if (metadata.registration_endpoint) {
8849
+ process.stderr.write(`Registering client dynamically...
8850
+ `);
8851
+ registration = await registerClient2(metadata, redirectUri);
8852
+ resolvedClientId = registration.clientId;
8853
+ storeClientRegistration2(origin, registration, metadata);
8854
+ } else {
8855
+ callback.server.close();
8856
+ resolvedClientId = await promptForClientId2(serverUrl);
8857
+ const newCallback = await startCallbackServer2();
8858
+ return doOAuthLogin2(metadata, resolvedClientId, `http://127.0.0.1:${newCallback.port}/callback`, newCallback, wwwParsed.scope, storageKey, origin);
8859
+ }
8860
+ }
8861
+ return doOAuthLogin2(metadata, resolvedClientId, redirectUri, callback, wwwParsed.scope, storageKey, origin);
8862
+ }
8863
+ async function doOAuthLogin2(metadata, clientId, redirectUri, callback, scope, storageKey, origin) {
8864
+ const state = randomBytes4(32).toString("base64url");
8865
+ const pkce = generatePkce();
8866
+ const scopes = scope ?? metadata.scopes_supported?.join(" ") ?? undefined;
8867
+ const params = new URLSearchParams({
8868
+ client_id: clientId,
8869
+ redirect_uri: redirectUri,
8870
+ response_type: "code",
8871
+ state,
8872
+ code_challenge: pkce.challenge,
8873
+ code_challenge_method: "S256"
8874
+ });
8875
+ if (scopes)
8876
+ params.set("scope", scopes);
8877
+ const authUrl = `${metadata.authorization_endpoint}?${params.toString()}`;
8878
+ process.stderr.write(`Opening browser for OAuth login...
8879
+ `);
8880
+ process.stderr.write(`If the browser doesn't open, visit:
8881
+ ${authUrl}
8882
+
8883
+ `);
8884
+ open_default(authUrl).catch(() => {});
8885
+ process.stderr.write(`Waiting for authentication callback on port ${callback.port}...
8886
+ `);
8887
+ const code = await callback.waitForCode();
8888
+ process.stderr.write(`Exchanging authorization code for tokens...
8889
+ `);
8890
+ const token = await exchangeCode(metadata.token_endpoint, code, clientId, redirectUri, pkce.verifier);
8891
+ token.provider_id = "mcp-oauth";
8892
+ storeToken(storageKey, "mcp-oauth", token);
8893
+ storeClientRegistration2(origin, {
8894
+ clientId,
8895
+ redirectUri,
8896
+ registeredAt: Date.now()
8897
+ }, metadata);
8898
+ process.stderr.write(`Authenticated with ${origin}
8899
+ `);
8900
+ return token.access_token;
8901
+ }
8902
+ async function loadOrRefreshMcpToken2(serverUrl) {
8903
+ const storageKey = serverStorageKey2(serverUrl);
8904
+ const token = loadToken(storageKey, "mcp-oauth");
8905
+ if (!token)
8906
+ return null;
8907
+ if (!isTokenExpired(token)) {
8908
+ return token.access_token;
8909
+ }
8910
+ if (token.refresh_token) {
8911
+ const origin = new URL(serverUrl).origin;
8912
+ const cached = loadClientRegistration2(origin);
8913
+ if (cached) {
8914
+ try {
8915
+ const newToken = await refreshAccessToken(cached.metadata.token_endpoint, token.refresh_token, cached.client.clientId);
8916
+ if (!newToken.refresh_token) {
8917
+ newToken.refresh_token = token.refresh_token;
8918
+ }
8919
+ newToken.provider_id = "mcp-oauth";
8920
+ storeToken(storageKey, "mcp-oauth", newToken);
8921
+ return newToken.access_token;
8922
+ } catch (e) {
8923
+ process.stderr.write(`warning: token refresh failed: ${e instanceof Error ? e.message : e}
8924
+ `);
8925
+ }
8926
+ }
8927
+ }
8928
+ return null;
8929
+ }
8930
+ var init_mcp_oauth2 = __esm(() => {
8931
+ init_open();
8932
+ init_auth();
8933
+ });
8934
+
8219
8935
  // src/wrap.ts
8220
8936
  var exports_wrap = {};
8221
8937
  __export(exports_wrap, {
8222
8938
  run: () => run2,
8223
8939
  parseToolArgs: () => parseToolArgs,
8940
+ parseHeaders: () => parseHeaders,
8224
8941
  mcpToolToCommand: () => mcpToolToCommand,
8225
8942
  jsonSchemaToArg: () => jsonSchemaToArg,
8226
- McpClient: () => McpClient
8943
+ McpClient: () => McpClient,
8944
+ HttpMcpClient: () => HttpMcpClient
8227
8945
  });
8228
8946
  import { spawn as spawn2 } from "node:child_process";
8229
- import { createInterface as createInterface3 } from "node:readline";
8947
+ import { createInterface as createInterface5 } from "node:readline";
8230
8948
  function jsonSchemaToArg(name, prop, required) {
8231
8949
  let argType = "string";
8232
8950
  let values;
@@ -8378,7 +9096,7 @@ class McpClient {
8378
9096
  if (!child.stdout || !child.stdin) {
8379
9097
  throw new Error("failed to start MCP server");
8380
9098
  }
8381
- const rl = createInterface3({ input: child.stdout, crlfDelay: Infinity });
9099
+ const rl = createInterface5({ input: child.stdout, crlfDelay: Infinity });
8382
9100
  const client = new McpClient(child, rl);
8383
9101
  client.sendRequest("initialize", {
8384
9102
  protocolVersion: "2024-11-05",
@@ -8417,11 +9135,219 @@ class McpClient {
8417
9135
  this.child.kill();
8418
9136
  }
8419
9137
  }
8420
- async function run2(serverCmd, describeMode, toolName, toolArgs = []) {
8421
- const client = await McpClient.start(serverCmd);
9138
+
9139
+ class HttpMcpClient {
9140
+ url;
9141
+ sessionId;
9142
+ protocolVersion;
9143
+ extraHeaders;
9144
+ nextId = 0;
9145
+ accessToken;
9146
+ clientId;
9147
+ oauthAttempted = false;
9148
+ constructor(url, extraHeaders, clientId) {
9149
+ this.url = url;
9150
+ this.extraHeaders = extraHeaders;
9151
+ this.clientId = clientId;
9152
+ }
9153
+ static async start(url, extraHeaders = {}, clientId) {
9154
+ const client = new HttpMcpClient(url, extraHeaders, clientId);
9155
+ if (!extraHeaders["Authorization"] && !extraHeaders["authorization"]) {
9156
+ const { loadOrRefreshMcpToken: loadOrRefreshMcpToken3 } = await Promise.resolve().then(() => (init_mcp_oauth2(), exports_mcp_oauth2));
9157
+ const token = await loadOrRefreshMcpToken3(url);
9158
+ if (token) {
9159
+ client.accessToken = token;
9160
+ }
9161
+ }
9162
+ const initResult = await client.sendRequest("initialize", {
9163
+ protocolVersion: "2025-11-25",
9164
+ capabilities: {},
9165
+ clientInfo: { name: "mtpcli-wrap", version: VERSION2 }
9166
+ });
9167
+ client.protocolVersion = initResult.protocolVersion ?? "2025-11-25";
9168
+ await client.sendNotification("notifications/initialized", {});
9169
+ return client;
9170
+ }
9171
+ buildHeaders() {
9172
+ const headers = {
9173
+ "Content-Type": "application/json",
9174
+ Accept: "application/json, text/event-stream"
9175
+ };
9176
+ if (this.accessToken && !this.extraHeaders["Authorization"] && !this.extraHeaders["authorization"]) {
9177
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
9178
+ }
9179
+ Object.assign(headers, this.extraHeaders);
9180
+ if (this.sessionId) {
9181
+ headers["MCP-Session-Id"] = this.sessionId;
9182
+ }
9183
+ if (this.protocolVersion) {
9184
+ headers["MCP-Protocol-Version"] = this.protocolVersion;
9185
+ }
9186
+ return headers;
9187
+ }
9188
+ parseJsonRpcResponse(json) {
9189
+ if (json.error) {
9190
+ const err = json.error;
9191
+ throw new Error(`MCP error: ${err.message} (${err.code})`);
9192
+ }
9193
+ return json.result ?? {};
9194
+ }
9195
+ async readSseResponse(resp, requestId) {
9196
+ const sid = resp.headers.get("mcp-session-id");
9197
+ if (sid)
9198
+ this.sessionId = sid;
9199
+ const text = await resp.text();
9200
+ const events = text.split(/\n\n+/);
9201
+ for (const event of events) {
9202
+ const dataLines = [];
9203
+ for (const line of event.split(`
9204
+ `)) {
9205
+ if (line.startsWith("data:")) {
9206
+ dataLines.push(line.slice(5).trimStart());
9207
+ }
9208
+ }
9209
+ if (dataLines.length === 0)
9210
+ continue;
9211
+ let json;
9212
+ try {
9213
+ json = JSON.parse(dataLines.join(`
9214
+ `));
9215
+ } catch {
9216
+ continue;
9217
+ }
9218
+ if (json.id === requestId) {
9219
+ return this.parseJsonRpcResponse(json);
9220
+ }
9221
+ }
9222
+ throw new Error("SSE stream ended without a response matching request ID " + requestId);
9223
+ }
9224
+ async handleOAuth(resp) {
9225
+ if (this.oauthAttempted) {
9226
+ throw new Error(`HTTP 401 from ${this.url} (OAuth already attempted, not retrying)`);
9227
+ }
9228
+ this.oauthAttempted = true;
9229
+ const wwwAuth = resp.headers.get("www-authenticate") ?? "";
9230
+ const { mcpOAuthFlow: mcpOAuthFlow3 } = await Promise.resolve().then(() => (init_mcp_oauth2(), exports_mcp_oauth2));
9231
+ this.accessToken = await mcpOAuthFlow3(this.url, wwwAuth, this.clientId);
9232
+ }
9233
+ async sendRequest(method, params) {
9234
+ this.nextId++;
9235
+ const body = {
9236
+ jsonrpc: "2.0",
9237
+ id: this.nextId,
9238
+ method,
9239
+ params
9240
+ };
9241
+ const resp = await fetch(this.url, {
9242
+ method: "POST",
9243
+ headers: this.buildHeaders(),
9244
+ body: JSON.stringify(body)
9245
+ });
9246
+ if (resp.status === 401) {
9247
+ await this.handleOAuth(resp);
9248
+ const retryResp = await fetch(this.url, {
9249
+ method: "POST",
9250
+ headers: this.buildHeaders(),
9251
+ body: JSON.stringify(body)
9252
+ });
9253
+ if (!retryResp.ok) {
9254
+ throw new Error(`HTTP ${retryResp.status} ${retryResp.statusText} from ${this.url}`);
9255
+ }
9256
+ const ct2 = retryResp.headers.get("content-type") ?? "";
9257
+ if (ct2.includes("text/event-stream")) {
9258
+ return this.readSseResponse(retryResp, this.nextId);
9259
+ }
9260
+ const sid2 = retryResp.headers.get("mcp-session-id");
9261
+ if (sid2)
9262
+ this.sessionId = sid2;
9263
+ const json2 = await retryResp.json();
9264
+ return this.parseJsonRpcResponse(json2);
9265
+ }
9266
+ if (!resp.ok) {
9267
+ throw new Error(`HTTP ${resp.status} ${resp.statusText} from ${this.url}`);
9268
+ }
9269
+ const ct = resp.headers.get("content-type") ?? "";
9270
+ if (ct.includes("text/event-stream")) {
9271
+ return this.readSseResponse(resp, this.nextId);
9272
+ }
9273
+ const sid = resp.headers.get("mcp-session-id");
9274
+ if (sid) {
9275
+ this.sessionId = sid;
9276
+ }
9277
+ const json = await resp.json();
9278
+ return this.parseJsonRpcResponse(json);
9279
+ }
9280
+ async sendNotification(method, params) {
9281
+ const body = {
9282
+ jsonrpc: "2.0",
9283
+ method,
9284
+ params
9285
+ };
9286
+ const resp = await fetch(this.url, {
9287
+ method: "POST",
9288
+ headers: this.buildHeaders(),
9289
+ body: JSON.stringify(body)
9290
+ });
9291
+ if (resp.status === 401) {
9292
+ await this.handleOAuth(resp);
9293
+ const retryResp = await fetch(this.url, {
9294
+ method: "POST",
9295
+ headers: this.buildHeaders(),
9296
+ body: JSON.stringify(body)
9297
+ });
9298
+ if (!retryResp.ok) {
9299
+ throw new Error(`HTTP ${retryResp.status} ${retryResp.statusText} from ${this.url}`);
9300
+ }
9301
+ const sid2 = retryResp.headers.get("mcp-session-id");
9302
+ if (sid2)
9303
+ this.sessionId = sid2;
9304
+ return;
9305
+ }
9306
+ if (!resp.ok) {
9307
+ throw new Error(`HTTP ${resp.status} ${resp.statusText} from ${this.url}`);
9308
+ }
9309
+ const sid = resp.headers.get("mcp-session-id");
9310
+ if (sid) {
9311
+ this.sessionId = sid;
9312
+ }
9313
+ }
9314
+ async listTools() {
9315
+ const result = await this.sendRequest("tools/list", {});
9316
+ return result.tools ?? [];
9317
+ }
9318
+ async callTool(name, arguments_) {
9319
+ return this.sendRequest("tools/call", { name, arguments: arguments_ });
9320
+ }
9321
+ stop() {}
9322
+ }
9323
+ function parseHeaders(raw) {
9324
+ const headers = {};
9325
+ for (const h of raw) {
9326
+ const idx = h.indexOf(":");
9327
+ if (idx === -1) {
9328
+ throw new Error(`invalid header (missing ':'): ${h}`);
9329
+ }
9330
+ headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
9331
+ }
9332
+ return headers;
9333
+ }
9334
+ async function run2(serverCmd, serverUrl, describeMode, toolName, toolArgs = [], rawHeaders = [], clientId) {
9335
+ let client;
9336
+ let serverName;
9337
+ if (serverUrl) {
9338
+ const headers = parseHeaders(rawHeaders);
9339
+ client = await HttpMcpClient.start(serverUrl, headers, clientId);
9340
+ try {
9341
+ serverName = new URL(serverUrl).hostname;
9342
+ } catch {
9343
+ serverName = serverUrl;
9344
+ }
9345
+ } else {
9346
+ client = await McpClient.start(serverCmd);
9347
+ serverName = serverCmd.split("/").pop()?.split(/\s/)[0] ?? serverCmd;
9348
+ }
8422
9349
  try {
8423
9350
  const mcpTools = await client.listTools();
8424
- const serverName = serverCmd.split("/").pop()?.split(/\s/)[0] ?? serverCmd;
8425
9351
  const commands = mcpTools.map(mcpToolToCommand);
8426
9352
  const schema = {
8427
9353
  name: serverName,
@@ -9208,7 +10134,7 @@ function cleanJson(obj) {
9208
10134
  }
9209
10135
 
9210
10136
  // src/index.ts
9211
- var VERSION3 = "0.2.2";
10137
+ var VERSION3 = "0.2.3";
9212
10138
  function selfDescribe() {
9213
10139
  const schema = {
9214
10140
  name: "mtpcli",
@@ -9266,8 +10192,7 @@ function selfDescribe() {
9266
10192
  {
9267
10193
  name: "tool",
9268
10194
  type: "string",
9269
- required: true,
9270
- description: "Tool name"
10195
+ description: "Tool name (required unless --url is used)"
9271
10196
  },
9272
10197
  {
9273
10198
  name: "--provider",
@@ -9278,6 +10203,16 @@ function selfDescribe() {
9278
10203
  name: "--token",
9279
10204
  type: "string",
9280
10205
  description: "API key or bearer token"
10206
+ },
10207
+ {
10208
+ name: "--url",
10209
+ type: "string",
10210
+ description: "HTTP MCP server URL (triggers OAuth discovery and login)"
10211
+ },
10212
+ {
10213
+ name: "--client-id",
10214
+ type: "string",
10215
+ description: "Pre-registered OAuth client ID (for --url)"
9281
10216
  }
9282
10217
  ],
9283
10218
  examples: [
@@ -9285,6 +10220,10 @@ function selfDescribe() {
9285
10220
  {
9286
10221
  description: "API key login",
9287
10222
  command: "mtpcli auth login my-tool --token sk-xxx"
10223
+ },
10224
+ {
10225
+ description: "Login to HTTP MCP server",
10226
+ command: "mtpcli auth login --url https://mcp.example.com/v1/mcp"
9288
10227
  }
9289
10228
  ]
9290
10229
  },
@@ -9382,8 +10321,22 @@ function selfDescribe() {
9382
10321
  {
9383
10322
  name: "--server",
9384
10323
  type: "string",
9385
- required: true,
9386
- description: "MCP server command"
10324
+ description: "MCP server command (stdio transport)"
10325
+ },
10326
+ {
10327
+ name: "--url",
10328
+ type: "string",
10329
+ description: "MCP server URL (Streamable HTTP transport). Mutually exclusive with --server."
10330
+ },
10331
+ {
10332
+ name: "--header",
10333
+ type: "array",
10334
+ description: "HTTP header(s) for --url, e.g. 'Authorization: Bearer tok'. Repeatable."
10335
+ },
10336
+ {
10337
+ name: "--client-id",
10338
+ type: "string",
10339
+ description: "Pre-registered OAuth client ID (for servers without dynamic registration)"
9387
10340
  },
9388
10341
  {
9389
10342
  name: "--describe",
@@ -9398,12 +10351,20 @@ function selfDescribe() {
9398
10351
  ],
9399
10352
  examples: [
9400
10353
  {
9401
- description: "Describe an MCP server",
10354
+ description: "Describe a stdio MCP server",
9402
10355
  command: 'mtpcli wrap --server "npx @mcp/server-github" --describe'
9403
10356
  },
9404
10357
  {
9405
- description: "Call a tool",
10358
+ description: "Call a tool on a stdio server",
9406
10359
  command: 'mtpcli wrap --server "npx @mcp/server-github" search_repos -- --query mtpcli'
10360
+ },
10361
+ {
10362
+ description: "Describe an HTTP MCP server",
10363
+ command: "mtpcli wrap --url http://localhost:3000/mcp --describe"
10364
+ },
10365
+ {
10366
+ description: "Call a tool on an HTTP server",
10367
+ command: "mtpcli wrap --url http://localhost:3000/mcp search_repos -- --query mtpcli"
9407
10368
  }
9408
10369
  ]
9409
10370
  },
@@ -9517,28 +10478,38 @@ program2.command("search").description("Search across --describe-compatible tool
9517
10478
  }
9518
10479
  });
9519
10480
  var authCmd = program2.command("auth").description("Manage authentication for tools");
9520
- authCmd.command("login").description("Log in to a tool").argument("<tool>", "Tool name").option("--provider <id>", "Specific provider ID").option("--token <value>", "API key / bearer token (skip OAuth flow)").action(async (tool, opts) => {
9521
- const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
10481
+ authCmd.command("login").description("Log in to a tool").argument("[tool]", "Tool name").option("--provider <id>", "Specific provider ID").option("--token <value>", "API key / bearer token (skip OAuth flow)").option("--url <url>", "HTTP MCP server URL (triggers OAuth discovery)").option("--client-id <id>", "Pre-registered OAuth client ID (for --url)").action(async (tool, opts) => {
10482
+ if (opts.url) {
10483
+ const { mcpOAuthFlow: mcpOAuthFlow3 } = await Promise.resolve().then(() => (init_mcp_oauth(), exports_mcp_oauth));
10484
+ await mcpOAuthFlow3(opts.url, "", opts.clientId);
10485
+ return;
10486
+ }
10487
+ if (!tool) {
10488
+ process.stderr.write(`error: tool name is required (or use --url)
10489
+ `);
10490
+ process.exit(1);
10491
+ }
10492
+ const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_auth2(), exports_auth));
9522
10493
  await runLogin2(tool, opts.provider, opts.token);
9523
10494
  });
9524
10495
  authCmd.command("logout").description("Log out from a tool").argument("<tool>", "Tool name").action(async (tool) => {
9525
- const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
10496
+ const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_auth2(), exports_auth));
9526
10497
  await runLogout2(tool);
9527
10498
  });
9528
10499
  authCmd.command("status").description("Show auth status for a tool").argument("<tool>", "Tool name").action(async (tool) => {
9529
- const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
10500
+ const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_auth2(), exports_auth));
9530
10501
  await runStatus2(tool);
9531
10502
  });
9532
10503
  authCmd.command("token").description("Print the access token for a tool").argument("<tool>", "Tool name").action(async (tool) => {
9533
- const { runToken: runToken2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
10504
+ const { runToken: runToken2 } = await Promise.resolve().then(() => (init_auth2(), exports_auth));
9534
10505
  await runToken2(tool);
9535
10506
  });
9536
10507
  authCmd.command("env").description("Print shell export statement for eval").argument("<tool>", "Tool name").action(async (tool) => {
9537
- const { runEnv: runEnv2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
10508
+ const { runEnv: runEnv2 } = await Promise.resolve().then(() => (init_auth2(), exports_auth));
9538
10509
  await runEnv2(tool);
9539
10510
  });
9540
10511
  authCmd.command("refresh").description("Force-refresh the stored OAuth token for a tool").argument("<tool>", "Tool name").action(async (tool) => {
9541
- const { runRefresh: runRefresh2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
10512
+ const { runRefresh: runRefresh2 } = await Promise.resolve().then(() => (init_auth2(), exports_auth));
9542
10513
  await runRefresh2(tool);
9543
10514
  });
9544
10515
  program2.command("serve").description("Serve CLI tools as an MCP server (cli2mcp bridge)").requiredOption("--tool <names...>", "Tool(s) to serve").addHelpText("after", `
@@ -9580,9 +10551,29 @@ Multiple tools can be combined into a single MCP server:
9580
10551
  const { run: run5 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
9581
10552
  await run5(opts.tool);
9582
10553
  });
9583
- program2.command("wrap").description("Wrap an MCP server as a CLI tool (mcp2cli bridge)").requiredOption("--server <cmd>", "MCP server command to run").option("--describe", "Output --describe JSON instead of invoking", false).argument("[tool_name]", "Tool name to invoke").argument("[args...]", "Arguments for the tool (after --)").action(async (toolName, args, opts) => {
10554
+ program2.command("wrap").description("Wrap an MCP server as a CLI tool (mcp2cli bridge)").option("--server <cmd>", "MCP server command to run (stdio transport)").option("--url <url>", "MCP server URL (Streamable HTTP transport)").option("-H, --header <header...>", "HTTP header(s) for --url (e.g. 'Authorization: Bearer tok')").option("--client-id <id>", "Pre-registered OAuth client ID (for --url)").option("--describe", "Output --describe JSON instead of invoking", false).argument("[tool_name]", "Tool name to invoke").argument("[args...]", "Arguments for the tool (after --)").action(async (toolName, args, opts) => {
10555
+ if (opts.server && opts.url) {
10556
+ process.stderr.write(`error: --server and --url are mutually exclusive
10557
+ `);
10558
+ process.exit(1);
10559
+ }
10560
+ if (!opts.server && !opts.url) {
10561
+ process.stderr.write(`error: one of --server or --url is required
10562
+ `);
10563
+ process.exit(1);
10564
+ }
10565
+ if (opts.header && !opts.url) {
10566
+ process.stderr.write(`error: --header requires --url
10567
+ `);
10568
+ process.exit(1);
10569
+ }
10570
+ if (opts.clientId && !opts.url) {
10571
+ process.stderr.write(`error: --client-id requires --url
10572
+ `);
10573
+ process.exit(1);
10574
+ }
9584
10575
  const { run: run5 } = await Promise.resolve().then(() => (init_wrap(), exports_wrap));
9585
- await run5(opts.server, opts.describe, toolName, args);
10576
+ await run5(opts.server, opts.url, opts.describe, toolName, args, opts.header ?? [], opts.clientId);
9586
10577
  });
9587
10578
  program2.command("validate").description("Validate a tool's --describe output against the MTP spec").argument("[tool]", "Tool name to validate").option("--json", "Output as JSON", false).option("--stdin", "Read JSON from stdin", false).option("--skip-help", "Skip --help cross-reference", false).action(async (tool, opts) => {
9588
10579
  const { run: run5 } = await Promise.resolve().then(() => (init_validate(), exports_validate));