@stackable-labs/mcp-app-extension 0.24.0 → 1.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/dist/index.js CHANGED
@@ -1,9 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { STANDALONE_CLIENT, MCP_AUTH_FILE, getToken, STANDALONE_CLIENT_AUTH_FILE } from './chunk-DCPV7HMV.js';
3
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
- import { IDENTITY_EVENT, ACTIVITY_EVENT, SURFACE_TARGET, PERMISSIONS, CAPABILITY_PERMISSION_MAP, ALLOWED_ICONS, UI_TAGS, UI_TAG_ATTRIBUTES, tagToComponentName } from '@stackable-labs/sdk-extension-contracts';
4
+ import { IDENTITY_EVENT, ACTIVITY_EVENT, SURFACE_TARGET, TEMPLATE_FLAVORS, PERMISSIONS, CAPABILITY_PERMISSION_MAP, EVENT_HOOK_PERMISSION_MAP, ALLOWED_ICONS, UI_TAGS, UI_TAG_ATTRIBUTES, tagToComponentName } from '@stackable-labs/sdk-extension-contracts';
5
+ import fs4, { readFile, mkdir, writeFile, constants } from 'fs/promises';
6
+ import path, { join } from 'path';
7
+ import os, { homedir } from 'os';
6
8
  import { z } from 'zod';
9
+ import { createServer } from 'http';
10
+ import process6 from 'process';
11
+ import { Buffer as Buffer$1 } from 'buffer';
12
+ import { fileURLToPath } from 'url';
13
+ import { promisify } from 'util';
14
+ import childProcess, { execFile } from 'child_process';
15
+ import fs3 from 'fs';
7
16
 
8
17
  // ../../sdk/extension/ai-docs/src/generated/template-content.ts
9
18
  var PATTERN_SECTIONS = [
@@ -1572,10 +1581,10 @@ var generatePatterns = () => {
1572
1581
  description: "Code patterns: store navigation, API wrapper, surface composition",
1573
1582
  globs: ["packages/extension/src/**/*.tsx", "packages/extension/src/**/*.ts"]
1574
1583
  });
1575
- const sections = PATTERN_SECTIONS.map(({ path, title, code }) => `## ${title}
1584
+ const sections = PATTERN_SECTIONS.map(({ path: path2, title, code }) => `## ${title}
1576
1585
 
1577
1586
  \`\`\`tsx
1578
- // src/${path}
1587
+ // src/${path2}
1579
1588
  ${code}
1580
1589
  \`\`\``).join("\n\n");
1581
1590
  const body = `# Code Patterns
@@ -1595,10 +1604,10 @@ var generateRecipes = () => {
1595
1604
  description: "Reference code and recipes for common extension development tasks",
1596
1605
  globs: ["packages/extension/src/**/*.tsx", "packages/extension/src/**/*.ts", "packages/extension/public/manifest.json"]
1597
1606
  });
1598
- const references = RECIPE_SECTIONS.map(({ path, title, code }) => `## Reference: ${title}
1607
+ const references = RECIPE_SECTIONS.map(({ path: path2, title, code }) => `## Reference: ${title}
1599
1608
 
1600
1609
  \`\`\`tsx
1601
- // src/${path}
1610
+ // src/${path2}
1602
1611
  ${code}
1603
1612
  \`\`\``).join("\n\n");
1604
1613
  const body = `# Recipes
@@ -1738,8 +1747,6 @@ export const Header = () => {
1738
1747
  - **Use ScrollArea** \u2014 wrap content surfaces in \`<ui.ScrollArea>\` for overflow handling
1739
1748
  `;
1740
1749
  };
1741
-
1742
- // ../../sdk/extension/ai-docs/src/cli-commands.ts
1743
1750
  var DLX = "pnpm --config.dlx-cache-max-age=0 dlx";
1744
1751
  var CLI = {
1745
1752
  /** Scaffold a new extension project */
@@ -1768,11 +1775,14 @@ var CLI = {
1768
1775
  mcp: `${DLX} @stackable-labs/cli-app-extension@latest ai mcp`
1769
1776
  }
1770
1777
  };
1771
- var TEMPLATE_FLAVORS = [
1772
- { name: "minimal", label: "Minimal", description: "Bare minimum \u2014 single surface, hello-world component" },
1773
- { name: "starter", label: "Starter", description: "Common patterns \u2014 store, api helpers, menu (default)" },
1774
- { name: "kitchen-sink", label: "Kitchen Sink", description: "Everything \u2014 every component, capability, surface, and hook" }
1775
- ];
1778
+ var TEMPLATE_FLAVOR_META = {
1779
+ minimal: { label: "Minimal", description: "Bare minimum \u2014 single surface, hello-world component" },
1780
+ starter: { label: "Starter", description: "Common patterns \u2014 store, api helpers, menu (default)" },
1781
+ "kitchen-sink": { label: "Kitchen Sink", description: "Everything \u2014 every component, capability, surface, and hook" }
1782
+ };
1783
+ var TEMPLATE_FLAVOR_DETAILS = TEMPLATE_FLAVORS.map(
1784
+ (name) => ({ name, ...TEMPLATE_FLAVOR_META[name] })
1785
+ );
1776
1786
 
1777
1787
  // ../../sdk/extension/ai-docs/src/generators/quick-start.ts
1778
1788
  var generateQuickStart = () => {
@@ -1964,7 +1974,7 @@ var generateCliReference = () => {
1964
1974
  description: "CLI commands for creating, developing, and deploying Stackable extensions",
1965
1975
  globs: ["packages/extension/src/**/*.tsx", "packages/extension/src/**/*.ts"]
1966
1976
  });
1967
- const templateRows = TEMPLATE_FLAVORS.map((t) => `| \`${t.name}\` | ${t.description} |`).join("\n");
1977
+ const templateRows = TEMPLATE_FLAVOR_DETAILS.map((t) => `| \`${t.name}\` | ${t.description} |`).join("\n");
1968
1978
  return `${fm}
1969
1979
 
1970
1980
  # CLI Reference
@@ -3382,6 +3392,7 @@ var generateValidateExtensionCommand = () => {
3382
3392
  description: "Validate this extension for common errors before deploying (manifest, permissions, surfaces, imports)",
3383
3393
  targets: ["*"]
3384
3394
  });
3395
+ const eventHookBullets = Object.keys(EVENT_HOOK_PERMISSION_MAP).map((hook) => `- \`${hook}\` \u2192 needs \`${EVENT_HOOK_PERMISSION_MAP[hook]}\` permission (also check manifest \`events\` array has matching entries)`).join("\n");
3385
3396
  return `${fm}
3386
3397
 
3387
3398
  # Validate Extension
@@ -3417,9 +3428,7 @@ Scan all \`.tsx\` files in \`packages/extension/src/\` for capability usage:
3417
3428
  - \`capabilities.actions.toast\` \u2192 needs \`actions:toast\` permission
3418
3429
  - \`capabilities.actions.invoke\` \u2192 needs \`actions:invoke\` permission
3419
3430
  - \`useExtendIdentity\` \u2192 needs \`extend:identity\` permission
3420
- - \`useIdentityEvent\` \u2192 needs \`events:identity\` permission (also check manifest \`events\` array has matching entries)
3421
- - \`useMessagingEvent\` \u2192 needs \`events:messaging\` permission (also check manifest \`events\` array has matching entries)
3422
- - \`useActivityEvent\` \u2192 needs \`events:activity\` permission (also check manifest \`events\` array has matching entries)
3431
+ ${eventHookBullets}
3423
3432
 
3424
3433
  Report:
3425
3434
  - **Missing permissions:** capabilities used in code but not declared in manifest
@@ -4433,6 +4442,108 @@ pair(EXAMPLE_SNIPPETS, EXAMPLE_SNIPPETS2);
4433
4442
  pair(CAPABILITY_SNIPPETS, CAPABILITY_SNIPPETS2);
4434
4443
  pair(EVENT_SNIPPETS, EVENT_SNIPPETS2);
4435
4444
 
4445
+ // ../../lib/contracts/src/custom.ts
4446
+ var CUSTOM_ROLE = {
4447
+ SUPER_ADMIN: "super_admin"
4448
+ };
4449
+ new Set(Object.values(CUSTOM_ROLE));
4450
+
4451
+ // ../../lib/utils-js/src/crypto.ts
4452
+ var getCrypto = () => {
4453
+ if (typeof globalThis !== "undefined" && globalThis.crypto) {
4454
+ return globalThis.crypto;
4455
+ }
4456
+ throw new Error("Web Crypto API not available \u2014 requires Node.js 22+ or a modern browser");
4457
+ };
4458
+ var getSubtle = () => {
4459
+ const crypto = getCrypto();
4460
+ if (crypto.subtle) {
4461
+ return crypto.subtle;
4462
+ }
4463
+ throw new Error("SubtleCrypto not available \u2014 requires a secure context (HTTPS) or Node.js 22+");
4464
+ };
4465
+ var encodeBytes = (bytes, encoding) => {
4466
+ if (encoding === "hex") {
4467
+ return [...bytes].map((b2) => b2.toString(16).padStart(2, "0")).join("");
4468
+ }
4469
+ const base64 = btoa(String.fromCharCode(...bytes));
4470
+ if (encoding === "base64url") {
4471
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4472
+ }
4473
+ return base64;
4474
+ };
4475
+ var getRandomBytes = (length, encoding = "base64") => {
4476
+ const bytes = new Uint8Array(length);
4477
+ getCrypto().getRandomValues(bytes);
4478
+ return encodeBytes(bytes, encoding);
4479
+ };
4480
+ var getDigest = async (input, encoding = "hex") => {
4481
+ const digest = await getSubtle().digest(
4482
+ "SHA-256",
4483
+ new TextEncoder().encode(input)
4484
+ );
4485
+ return encodeBytes(new Uint8Array(digest), encoding);
4486
+ };
4487
+ var getNonce = () => getRandomBytes(16, "base64url");
4488
+ var getVerifier = () => getRandomBytes(32, "base64url");
4489
+
4490
+ // ../../lib/utils-auth/src/constants.ts
4491
+ var STANDALONE_CLIENT_DATA = {
4492
+ CLI: { name: "@stackable-labs/cli-app-extension", authFile: "cli-auth.json" },
4493
+ MCP: { name: "@stackable-labs/mcp-app-extension", authFile: "mcp-auth.json" }
4494
+ };
4495
+ var STANDALONE_CLIENT = Object.fromEntries(
4496
+ Object.entries(STANDALONE_CLIENT_DATA).map(([k, v]) => [k, v.name])
4497
+ );
4498
+ var STANDALONE_CLIENT_AUTH_FILE = Object.fromEntries(
4499
+ Object.entries(STANDALONE_CLIENT_DATA).map(([k, v]) => [k, v.authFile])
4500
+ );
4501
+ Object.values(STANDALONE_CLIENT);
4502
+
4503
+ // ../../lib/utils-auth/src/index.ts
4504
+ var deriveClientId = async (clientName) => (await getDigest(clientName)).slice(0, 32);
4505
+ var AUTH_DIR = join(homedir(), ".stackable");
4506
+ join(AUTH_DIR, STANDALONE_CLIENT_AUTH_FILE.CLI);
4507
+ var MCP_AUTH_FILE = join(AUTH_DIR, STANDALONE_CLIENT_AUTH_FILE.MCP);
4508
+ var resolveAuthFile = (filename) => join(AUTH_DIR, filename ?? STANDALONE_CLIENT_AUTH_FILE.CLI);
4509
+ var readAuthState = async (filename) => {
4510
+ try {
4511
+ const content = await readFile(resolveAuthFile(filename), "utf8");
4512
+ return JSON.parse(content);
4513
+ } catch {
4514
+ return null;
4515
+ }
4516
+ };
4517
+ var writeAuthState = async (state, filename) => {
4518
+ await mkdir(AUTH_DIR, { recursive: true, mode: 448 });
4519
+ await writeFile(resolveAuthFile(filename), JSON.stringify(state, null, 2), { mode: 384 });
4520
+ };
4521
+ var decodeJwtPayload = (token) => {
4522
+ try {
4523
+ const [, payload] = token.split(".");
4524
+ if (!payload) {
4525
+ return null;
4526
+ }
4527
+ const json = Buffer.from(payload, "base64url").toString("utf8");
4528
+ return JSON.parse(json);
4529
+ } catch {
4530
+ return null;
4531
+ }
4532
+ };
4533
+ var getToken = async (filename) => {
4534
+ const state = await readAuthState(filename);
4535
+ if (!state) {
4536
+ throw new Error("Not authenticated. Run `stackable-app-extension auth login` first.");
4537
+ }
4538
+ const payload = decodeJwtPayload(state.token);
4539
+ if (payload?.exp && typeof payload.exp === "number") {
4540
+ if (Date.now() >= payload.exp * 1e3) {
4541
+ throw new Error("Session expired. Run `stackable-app-extension auth login` to re-authenticate.");
4542
+ }
4543
+ }
4544
+ return state.token;
4545
+ };
4546
+
4436
4547
  // package.json
4437
4548
  var package_default = {
4438
4549
  version: "0.0.0"};
@@ -4456,19 +4567,23 @@ var createMcpServer = (options = {}) => {
4456
4567
  return await getToken(STANDALONE_CLIENT_AUTH_FILE.MCP);
4457
4568
  } catch {
4458
4569
  }
4459
- const { performOAuthFlow } = await import('./auth-WHLYYSCQ.js');
4570
+ if (!options.performOAuthFlow) {
4571
+ throw new Error(
4572
+ "No auth token available. Provide authContext (server) or performOAuthFlow (CLI)."
4573
+ );
4574
+ }
4460
4575
  const MCP_API_BASE_URL = process.env.MCP_API_BASE_URL ?? DEFAULT_MCP_API_BASE_URL;
4461
- return performOAuthFlow(`${MCP_API_BASE_URL}/.well-known/oauth-authorization-server`);
4576
+ return options.performOAuthFlow(`${MCP_API_BASE_URL}/.well-known/oauth-authorization-server`);
4462
4577
  };
4463
4578
  const authHeaders = (token) => ({
4464
4579
  authorization: `Bearer ${token}`,
4465
4580
  "content-type": "application/json",
4466
4581
  "x-client-name": MCP_CLIENT_NAME
4467
4582
  });
4468
- const callApi = async (path) => {
4583
+ const callApi = async (path2) => {
4469
4584
  const token = await getAuthToken();
4470
4585
  const ADMIN_API_BASE_URL = process.env.ADMIN_API_BASE_URL ?? DEFAULT_ADMIN_API_URL;
4471
- const url = `${ADMIN_API_BASE_URL}${path}`;
4586
+ const url = `${ADMIN_API_BASE_URL}${path2}`;
4472
4587
  let res;
4473
4588
  try {
4474
4589
  res = await fetch(url, { headers: authHeaders(token) });
@@ -4595,12 +4710,7 @@ ${errors.map((e) => `- ${e}`).join("\n")}`);
4595
4710
  usedPermissions.add(permission);
4596
4711
  }
4597
4712
  }
4598
- const eventHookMap = {
4599
- useIdentityEvent: "events:identity",
4600
- useMessagingEvent: "events:messaging",
4601
- useActivityEvent: "events:activity"
4602
- };
4603
- for (const [hook, permission] of Object.entries(eventHookMap)) {
4713
+ for (const [hook, permission] of Object.entries(EVENT_HOOK_PERMISSION_MAP)) {
4604
4714
  if (allSource.includes(hook)) {
4605
4715
  usedPermissions.add(permission);
4606
4716
  }
@@ -4639,8 +4749,623 @@ ${issues.map((i) => `- ${i}`).join("\n")}`);
4639
4749
  }, async ({ appId }) => withAuth(() => callApi(`/app-extension/${appId}/instances`)));
4640
4750
  return server2;
4641
4751
  };
4752
+ var isDockerCached;
4753
+ function hasDockerEnv() {
4754
+ try {
4755
+ fs3.statSync("/.dockerenv");
4756
+ return true;
4757
+ } catch {
4758
+ return false;
4759
+ }
4760
+ }
4761
+ function hasDockerCGroup() {
4762
+ try {
4763
+ return fs3.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
4764
+ } catch {
4765
+ return false;
4766
+ }
4767
+ }
4768
+ function isDocker() {
4769
+ if (isDockerCached === void 0) {
4770
+ isDockerCached = hasDockerEnv() || hasDockerCGroup();
4771
+ }
4772
+ return isDockerCached;
4773
+ }
4774
+
4775
+ // ../../../node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
4776
+ var cachedResult;
4777
+ var hasContainerEnv = () => {
4778
+ try {
4779
+ fs3.statSync("/run/.containerenv");
4780
+ return true;
4781
+ } catch {
4782
+ return false;
4783
+ }
4784
+ };
4785
+ function isInsideContainer() {
4786
+ if (cachedResult === void 0) {
4787
+ cachedResult = hasContainerEnv() || isDocker();
4788
+ }
4789
+ return cachedResult;
4790
+ }
4791
+
4792
+ // ../../../node_modules/.pnpm/is-wsl@3.1.1/node_modules/is-wsl/index.js
4793
+ var isWsl = () => {
4794
+ if (process6.platform !== "linux") {
4795
+ return false;
4796
+ }
4797
+ if (os.release().toLowerCase().includes("microsoft")) {
4798
+ if (isInsideContainer()) {
4799
+ return false;
4800
+ }
4801
+ return true;
4802
+ }
4803
+ try {
4804
+ if (fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
4805
+ return !isInsideContainer();
4806
+ }
4807
+ } catch {
4808
+ }
4809
+ if (fs3.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs3.existsSync("/run/WSL")) {
4810
+ return !isInsideContainer();
4811
+ }
4812
+ return false;
4813
+ };
4814
+ var is_wsl_default = process6.env.__IS_WSL_TEST__ ? isWsl : isWsl();
4815
+
4816
+ // ../../../node_modules/.pnpm/wsl-utils@0.1.0/node_modules/wsl-utils/index.js
4817
+ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
4818
+ const defaultMountPoint = "/mnt/";
4819
+ let mountPoint;
4820
+ return async function() {
4821
+ if (mountPoint) {
4822
+ return mountPoint;
4823
+ }
4824
+ const configFilePath = "/etc/wsl.conf";
4825
+ let isConfigFileExists = false;
4826
+ try {
4827
+ await fs4.access(configFilePath, constants.F_OK);
4828
+ isConfigFileExists = true;
4829
+ } catch {
4830
+ }
4831
+ if (!isConfigFileExists) {
4832
+ return defaultMountPoint;
4833
+ }
4834
+ const configContent = await fs4.readFile(configFilePath, { encoding: "utf8" });
4835
+ const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
4836
+ if (!configMountPoint) {
4837
+ return defaultMountPoint;
4838
+ }
4839
+ mountPoint = configMountPoint.groups.mountPoint.trim();
4840
+ mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
4841
+ return mountPoint;
4842
+ };
4843
+ })();
4844
+ var powerShellPathFromWsl = async () => {
4845
+ const mountPoint = await wslDrivesMountPoint();
4846
+ return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
4847
+ };
4848
+ var powerShellPath = async () => {
4849
+ if (is_wsl_default) {
4850
+ return powerShellPathFromWsl();
4851
+ }
4852
+ return `${process6.env.SYSTEMROOT || process6.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
4853
+ };
4854
+
4855
+ // ../../../node_modules/.pnpm/define-lazy-prop@3.0.0/node_modules/define-lazy-prop/index.js
4856
+ function defineLazyProperty(object, propertyName, valueGetter) {
4857
+ const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
4858
+ Object.defineProperty(object, propertyName, {
4859
+ configurable: true,
4860
+ enumerable: true,
4861
+ get() {
4862
+ const result = valueGetter();
4863
+ define(result);
4864
+ return result;
4865
+ },
4866
+ set(value) {
4867
+ define(value);
4868
+ }
4869
+ });
4870
+ return object;
4871
+ }
4872
+ var execFileAsync = promisify(execFile);
4873
+ async function defaultBrowserId() {
4874
+ if (process6.platform !== "darwin") {
4875
+ throw new Error("macOS only");
4876
+ }
4877
+ const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
4878
+ const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
4879
+ const browserId = match?.groups.id ?? "com.apple.Safari";
4880
+ if (browserId === "com.apple.safari") {
4881
+ return "com.apple.Safari";
4882
+ }
4883
+ return browserId;
4884
+ }
4885
+ var execFileAsync2 = promisify(execFile);
4886
+ async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
4887
+ if (process6.platform !== "darwin") {
4888
+ throw new Error("macOS only");
4889
+ }
4890
+ const outputArguments = humanReadableOutput ? [] : ["-ss"];
4891
+ const execOptions = {};
4892
+ if (signal) {
4893
+ execOptions.signal = signal;
4894
+ }
4895
+ const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
4896
+ return stdout.trim();
4897
+ }
4898
+
4899
+ // ../../../node_modules/.pnpm/bundle-name@4.1.0/node_modules/bundle-name/index.js
4900
+ async function bundleName(bundleId) {
4901
+ return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
4902
+ tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
4903
+ }
4904
+ var execFileAsync3 = promisify(execFile);
4905
+ var windowsBrowserProgIds = {
4906
+ MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
4907
+ // The missing `L` is correct.
4908
+ MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
4909
+ MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
4910
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
4911
+ ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
4912
+ ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
4913
+ ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
4914
+ ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
4915
+ BraveHTML: { name: "Brave", id: "com.brave.Browser" },
4916
+ BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
4917
+ BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
4918
+ BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
4919
+ FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
4920
+ OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
4921
+ VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
4922
+ "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
4923
+ };
4924
+ new Map(Object.entries(windowsBrowserProgIds));
4925
+ var UnknownBrowserError = class extends Error {
4926
+ };
4927
+ async function defaultBrowser(_execFileAsync = execFileAsync3) {
4928
+ const { stdout } = await _execFileAsync("reg", [
4929
+ "QUERY",
4930
+ " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
4931
+ "/v",
4932
+ "ProgId"
4933
+ ]);
4934
+ const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
4935
+ if (!match) {
4936
+ throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
4937
+ }
4938
+ const { id } = match.groups;
4939
+ const dotIndex = id.lastIndexOf(".");
4940
+ const hyphenIndex = id.lastIndexOf("-");
4941
+ const baseIdByDot = dotIndex === -1 ? void 0 : id.slice(0, dotIndex);
4942
+ const baseIdByHyphen = hyphenIndex === -1 ? void 0 : id.slice(0, hyphenIndex);
4943
+ return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? { name: id, id };
4944
+ }
4945
+
4946
+ // ../../../node_modules/.pnpm/default-browser@5.5.0/node_modules/default-browser/index.js
4947
+ var execFileAsync4 = promisify(execFile);
4948
+ var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
4949
+ async function defaultBrowser2() {
4950
+ if (process6.platform === "darwin") {
4951
+ const id = await defaultBrowserId();
4952
+ const name = await bundleName(id);
4953
+ return { name, id };
4954
+ }
4955
+ if (process6.platform === "linux") {
4956
+ const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
4957
+ const id = stdout.trim();
4958
+ const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
4959
+ return { name, id };
4960
+ }
4961
+ if (process6.platform === "win32") {
4962
+ return defaultBrowser();
4963
+ }
4964
+ throw new Error("Only macOS, Linux, and Windows are supported");
4965
+ }
4966
+
4967
+ // ../../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
4968
+ var execFile5 = promisify(childProcess.execFile);
4969
+ var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
4970
+ var localXdgOpenPath = path.join(__dirname$1, "xdg-open");
4971
+ var { platform, arch } = process6;
4972
+ async function getWindowsDefaultBrowserFromWsl() {
4973
+ const powershellPath = await powerShellPath();
4974
+ const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
4975
+ const encodedCommand = Buffer$1.from(rawCommand, "utf16le").toString("base64");
4976
+ const { stdout } = await execFile5(
4977
+ powershellPath,
4978
+ [
4979
+ "-NoProfile",
4980
+ "-NonInteractive",
4981
+ "-ExecutionPolicy",
4982
+ "Bypass",
4983
+ "-EncodedCommand",
4984
+ encodedCommand
4985
+ ],
4986
+ { encoding: "utf8" }
4987
+ );
4988
+ const progId = stdout.trim();
4989
+ const browserMap = {
4990
+ ChromeHTML: "com.google.chrome",
4991
+ BraveHTML: "com.brave.Browser",
4992
+ MSEdgeHTM: "com.microsoft.edge",
4993
+ FirefoxURL: "org.mozilla.firefox"
4994
+ };
4995
+ return browserMap[progId] ? { id: browserMap[progId] } : {};
4996
+ }
4997
+ var pTryEach = async (array, mapper) => {
4998
+ let latestError;
4999
+ for (const item of array) {
5000
+ try {
5001
+ return await mapper(item);
5002
+ } catch (error) {
5003
+ latestError = error;
5004
+ }
5005
+ }
5006
+ throw latestError;
5007
+ };
5008
+ var baseOpen = async (options) => {
5009
+ options = {
5010
+ wait: false,
5011
+ background: false,
5012
+ newInstance: false,
5013
+ allowNonzeroExitCode: false,
5014
+ ...options
5015
+ };
5016
+ if (Array.isArray(options.app)) {
5017
+ return pTryEach(options.app, (singleApp) => baseOpen({
5018
+ ...options,
5019
+ app: singleApp
5020
+ }));
5021
+ }
5022
+ let { name: app, arguments: appArguments = [] } = options.app ?? {};
5023
+ appArguments = [...appArguments];
5024
+ if (Array.isArray(app)) {
5025
+ return pTryEach(app, (appName) => baseOpen({
5026
+ ...options,
5027
+ app: {
5028
+ name: appName,
5029
+ arguments: appArguments
5030
+ }
5031
+ }));
5032
+ }
5033
+ if (app === "browser" || app === "browserPrivate") {
5034
+ const ids = {
5035
+ "com.google.chrome": "chrome",
5036
+ "google-chrome.desktop": "chrome",
5037
+ "com.brave.Browser": "brave",
5038
+ "org.mozilla.firefox": "firefox",
5039
+ "firefox.desktop": "firefox",
5040
+ "com.microsoft.msedge": "edge",
5041
+ "com.microsoft.edge": "edge",
5042
+ "com.microsoft.edgemac": "edge",
5043
+ "microsoft-edge.desktop": "edge"
5044
+ };
5045
+ const flags = {
5046
+ chrome: "--incognito",
5047
+ brave: "--incognito",
5048
+ firefox: "--private-window",
5049
+ edge: "--inPrivate"
5050
+ };
5051
+ const browser = is_wsl_default ? await getWindowsDefaultBrowserFromWsl() : await defaultBrowser2();
5052
+ if (browser.id in ids) {
5053
+ const browserName = ids[browser.id];
5054
+ if (app === "browserPrivate") {
5055
+ appArguments.push(flags[browserName]);
5056
+ }
5057
+ return baseOpen({
5058
+ ...options,
5059
+ app: {
5060
+ name: apps[browserName],
5061
+ arguments: appArguments
5062
+ }
5063
+ });
5064
+ }
5065
+ throw new Error(`${browser.name} is not supported as a default browser`);
5066
+ }
5067
+ let command;
5068
+ const cliArguments = [];
5069
+ const childProcessOptions = {};
5070
+ if (platform === "darwin") {
5071
+ command = "open";
5072
+ if (options.wait) {
5073
+ cliArguments.push("--wait-apps");
5074
+ }
5075
+ if (options.background) {
5076
+ cliArguments.push("--background");
5077
+ }
5078
+ if (options.newInstance) {
5079
+ cliArguments.push("--new");
5080
+ }
5081
+ if (app) {
5082
+ cliArguments.push("-a", app);
5083
+ }
5084
+ } else if (platform === "win32" || is_wsl_default && !isInsideContainer() && !app) {
5085
+ command = await powerShellPath();
5086
+ cliArguments.push(
5087
+ "-NoProfile",
5088
+ "-NonInteractive",
5089
+ "-ExecutionPolicy",
5090
+ "Bypass",
5091
+ "-EncodedCommand"
5092
+ );
5093
+ if (!is_wsl_default) {
5094
+ childProcessOptions.windowsVerbatimArguments = true;
5095
+ }
5096
+ const encodedArguments = ["Start"];
5097
+ if (options.wait) {
5098
+ encodedArguments.push("-Wait");
5099
+ }
5100
+ if (app) {
5101
+ encodedArguments.push(`"\`"${app}\`""`);
5102
+ if (options.target) {
5103
+ appArguments.push(options.target);
5104
+ }
5105
+ } else if (options.target) {
5106
+ encodedArguments.push(`"${options.target}"`);
5107
+ }
5108
+ if (appArguments.length > 0) {
5109
+ appArguments = appArguments.map((argument) => `"\`"${argument}\`""`);
5110
+ encodedArguments.push("-ArgumentList", appArguments.join(","));
5111
+ }
5112
+ options.target = Buffer$1.from(encodedArguments.join(" "), "utf16le").toString("base64");
5113
+ } else {
5114
+ if (app) {
5115
+ command = app;
5116
+ } else {
5117
+ const isBundled = !__dirname$1 || __dirname$1 === "/";
5118
+ let exeLocalXdgOpen = false;
5119
+ try {
5120
+ await fs4.access(localXdgOpenPath, constants.X_OK);
5121
+ exeLocalXdgOpen = true;
5122
+ } catch {
5123
+ }
5124
+ const useSystemXdgOpen = process6.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
5125
+ command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
5126
+ }
5127
+ if (appArguments.length > 0) {
5128
+ cliArguments.push(...appArguments);
5129
+ }
5130
+ if (!options.wait) {
5131
+ childProcessOptions.stdio = "ignore";
5132
+ childProcessOptions.detached = true;
5133
+ }
5134
+ }
5135
+ if (platform === "darwin" && appArguments.length > 0) {
5136
+ cliArguments.push("--args", ...appArguments);
5137
+ }
5138
+ if (options.target) {
5139
+ cliArguments.push(options.target);
5140
+ }
5141
+ const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
5142
+ if (options.wait) {
5143
+ return new Promise((resolve, reject) => {
5144
+ subprocess.once("error", reject);
5145
+ subprocess.once("close", (exitCode) => {
5146
+ if (!options.allowNonzeroExitCode && exitCode > 0) {
5147
+ reject(new Error(`Exited with code ${exitCode}`));
5148
+ return;
5149
+ }
5150
+ resolve(subprocess);
5151
+ });
5152
+ });
5153
+ }
5154
+ subprocess.unref();
5155
+ return subprocess;
5156
+ };
5157
+ var open = (target, options) => {
5158
+ if (typeof target !== "string") {
5159
+ throw new TypeError("Expected a `target`");
5160
+ }
5161
+ return baseOpen({
5162
+ ...options,
5163
+ target
5164
+ });
5165
+ };
5166
+ function detectArchBinary(binary) {
5167
+ if (typeof binary === "string" || Array.isArray(binary)) {
5168
+ return binary;
5169
+ }
5170
+ const { [arch]: archBinary } = binary;
5171
+ if (!archBinary) {
5172
+ throw new Error(`${arch} is not supported`);
5173
+ }
5174
+ return archBinary;
5175
+ }
5176
+ function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
5177
+ if (wsl && is_wsl_default) {
5178
+ return detectArchBinary(wsl);
5179
+ }
5180
+ if (!platformBinary) {
5181
+ throw new Error(`${platform} is not supported`);
5182
+ }
5183
+ return detectArchBinary(platformBinary);
5184
+ }
5185
+ var apps = {};
5186
+ defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
5187
+ darwin: "google chrome",
5188
+ win32: "chrome",
5189
+ linux: ["google-chrome", "google-chrome-stable", "chromium"]
5190
+ }, {
5191
+ wsl: {
5192
+ ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
5193
+ x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
5194
+ }
5195
+ }));
5196
+ defineLazyProperty(apps, "brave", () => detectPlatformBinary({
5197
+ darwin: "brave browser",
5198
+ win32: "brave",
5199
+ linux: ["brave-browser", "brave"]
5200
+ }, {
5201
+ wsl: {
5202
+ ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
5203
+ x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
5204
+ }
5205
+ }));
5206
+ defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
5207
+ darwin: "firefox",
5208
+ win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
5209
+ linux: "firefox"
5210
+ }, {
5211
+ wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
5212
+ }));
5213
+ defineLazyProperty(apps, "edge", () => detectPlatformBinary({
5214
+ darwin: "microsoft edge",
5215
+ win32: "msedge",
5216
+ linux: ["microsoft-edge", "microsoft-edge-dev"]
5217
+ }, {
5218
+ wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
5219
+ }));
5220
+ defineLazyProperty(apps, "browser", () => "browser");
5221
+ defineLazyProperty(apps, "browserPrivate", () => "browserPrivate");
5222
+ var open_default = open;
5223
+
5224
+ // src/auth.ts
5225
+ var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
5226
+ var generatePKCE = async () => {
5227
+ const codeVerifier = getVerifier();
5228
+ const codeChallenge = await getDigest(codeVerifier, "base64url");
5229
+ return { codeVerifier, codeChallenge };
5230
+ };
5231
+ var fetchDiscovery = async (discoveryUrl) => {
5232
+ const res = await fetch(discoveryUrl);
5233
+ if (!res.ok) {
5234
+ throw new Error(`Failed to fetch OAuth discovery: ${res.status} ${res.statusText}`);
5235
+ }
5236
+ return res.json();
5237
+ };
5238
+ var registerClient = async (registrationEndpoint) => {
5239
+ const res = await fetch(registrationEndpoint, {
5240
+ method: "POST",
5241
+ headers: { "content-type": "application/json" },
5242
+ body: JSON.stringify({
5243
+ client_name: STANDALONE_CLIENT.MCP,
5244
+ redirect_uris: ["http://127.0.0.1/callback"]
5245
+ })
5246
+ });
5247
+ if (!res.ok) {
5248
+ throw new Error(`Client registration failed: ${res.status}`);
5249
+ }
5250
+ const data = await res.json();
5251
+ return data.client_id;
5252
+ };
5253
+ var exchangeCodeForToken = async (tokenEndpoint, code, codeVerifier, clientId, redirectUri) => {
5254
+ const res = await fetch(tokenEndpoint, {
5255
+ method: "POST",
5256
+ headers: { "content-type": "application/x-www-form-urlencoded" },
5257
+ body: new URLSearchParams({
5258
+ grant_type: "authorization_code",
5259
+ code,
5260
+ code_verifier: codeVerifier,
5261
+ client_id: clientId,
5262
+ redirect_uri: redirectUri
5263
+ }).toString()
5264
+ });
5265
+ if (!res.ok) {
5266
+ const error = await res.json().catch(() => ({}));
5267
+ throw new Error(error.error_description ?? error.error ?? `Token exchange failed: ${res.status}`);
5268
+ }
5269
+ return res.json();
5270
+ };
5271
+ var decodeJwtPayload2 = (token) => {
5272
+ const [, payload] = token.split(".");
5273
+ if (!payload) {
5274
+ throw new Error("Invalid JWT format");
5275
+ }
5276
+ return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
5277
+ };
5278
+ var performOAuthFlow = async (discoveryUrl) => {
5279
+ const discovery = await fetchDiscovery(discoveryUrl);
5280
+ let clientId = await deriveClientId(STANDALONE_CLIENT.MCP);
5281
+ if (discovery.registration_endpoint) {
5282
+ clientId = await registerClient(discovery.registration_endpoint);
5283
+ }
5284
+ const { codeVerifier, codeChallenge } = await generatePKCE();
5285
+ const state = getNonce();
5286
+ let resolveCode;
5287
+ let rejectCode;
5288
+ const codePromise = new Promise((resolve, reject) => {
5289
+ resolveCode = resolve;
5290
+ rejectCode = reject;
5291
+ });
5292
+ let server2;
5293
+ server2 = createServer((req, res) => {
5294
+ const url = new URL(req.url, "http://localhost");
5295
+ if (url.pathname === "/callback") {
5296
+ const error = url.searchParams.get("error");
5297
+ if (error) {
5298
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
5299
+ res.end("<html><body><h2>Authentication failed</h2><p>You can close this tab.</p></body></html>");
5300
+ rejectCode(new Error(url.searchParams.get("error_description") ?? error));
5301
+ return;
5302
+ }
5303
+ const code2 = url.searchParams.get("code");
5304
+ const returnedState = url.searchParams.get("state");
5305
+ if (!code2) {
5306
+ res.writeHead(400, { "content-type": "text/plain" });
5307
+ res.end("Missing authorization code");
5308
+ return;
5309
+ }
5310
+ if (returnedState !== state) {
5311
+ res.writeHead(400, { "content-type": "text/plain" });
5312
+ res.end("State mismatch");
5313
+ rejectCode(new Error("OAuth state mismatch \u2014 possible CSRF attack"));
5314
+ return;
5315
+ }
5316
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
5317
+ res.end("<html><body><h2>Authenticated</h2><p>You can return to your terminal.</p></body></html>");
5318
+ resolveCode(code2);
5319
+ } else {
5320
+ res.writeHead(404);
5321
+ res.end();
5322
+ }
5323
+ });
5324
+ await new Promise((r) => server2.listen(0, "127.0.0.1", r));
5325
+ const port = server2.address().port;
5326
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
5327
+ const authUrl = new URL(discovery.authorization_endpoint);
5328
+ authUrl.searchParams.set("client_id", clientId);
5329
+ authUrl.searchParams.set("redirect_uri", redirectUri);
5330
+ authUrl.searchParams.set("code_challenge", codeChallenge);
5331
+ authUrl.searchParams.set("code_challenge_method", "S256");
5332
+ authUrl.searchParams.set("state", state);
5333
+ authUrl.searchParams.set("response_type", "code");
5334
+ await open_default(authUrl.toString());
5335
+ const timeout = setTimeout(() => {
5336
+ server2.close();
5337
+ rejectCode(new Error("Authentication timed out. Please try again."));
5338
+ }, LOGIN_TIMEOUT_MS);
5339
+ let code;
5340
+ try {
5341
+ code = await codePromise;
5342
+ } catch (err) {
5343
+ clearTimeout(timeout);
5344
+ server2.close();
5345
+ throw err;
5346
+ }
5347
+ clearTimeout(timeout);
5348
+ server2.close();
5349
+ const tokenResponse = await exchangeCodeForToken(
5350
+ discovery.token_endpoint,
5351
+ code,
5352
+ codeVerifier,
5353
+ clientId,
5354
+ redirectUri
5355
+ );
5356
+ const payload = decodeJwtPayload2(tokenResponse.access_token);
5357
+ await writeAuthState(
5358
+ {
5359
+ token: tokenResponse.access_token,
5360
+ orgId: payload.orgId,
5361
+ userId: payload.sub
5362
+ },
5363
+ STANDALONE_CLIENT_AUTH_FILE.MCP
5364
+ );
5365
+ return tokenResponse.access_token;
5366
+ };
4642
5367
 
4643
5368
  // src/index.ts
4644
- var server = createMcpServer();
5369
+ var server = createMcpServer({ performOAuthFlow });
4645
5370
  var transport = new StdioServerTransport();
4646
5371
  await server.connect(transport);