{"version":3,"sources":["../../src/cli/entrypoint-runtime.ts"],"sourcesContent":["/**\n * Entrypoint bootstrap — spawned by the CLI (`run`, `inspect`, `list`, `call`,\n * `dev inspector`) instead of the user's server file directly.\n *\n * This mirrors Python FastMCP's `FileSystemSource` entrypoint resolution\n * (`fastmcp/utilities/mcp_server_config/v1/sources/filesystem.py`): import the\n * user's file, resolve a named export (or a factory function that returns a\n * server), and start it. Keeping this in a spawned subprocess (rather than\n * importing the user's file in the CLI's own process) preserves two things the\n * existing CLI already relies on: `tsx` transpilation for TypeScript files, and\n * process isolation so a crashing user server doesn't take down the CLI.\n *\n * Contract (set by the parent CLI process via environment variables):\n *   - FASTMCP_ENTRYPOINT_FILE: absolute path to the user's server file (required)\n *   - FASTMCP_ENTRYPOINT_EXPORT: export name to resolve. Omitted when the user\n *     didn't specify one, in which case a conventional name is auto-detected.\n *\n * Resolution order when FASTMCP_ENTRYPOINT_EXPORT is not set:\n *   `default`, `mcp`, `server`, `app` — the first of these that is already a\n *   FastMCP instance wins. Auto-detection deliberately does NOT call factory\n *   functions (mirroring Python FastMCP's `_find_server_object`, which only\n *   resolves factories for an explicit entrypoint) — invoking an arbitrary\n *   exported function as a side effect of guessing a name would be surprising,\n *   and a name match that isn't a FastMCP instance is skipped in favor of the\n *   next candidate rather than committing to it. Factory functions are only\n *   resolved when the export name is given explicitly (`file:export` or\n *   `--export`).\n *\n * Backwards compatibility: if no explicit export was requested and none of the\n * conventional names resolve to a valid entrypoint, this script assumes the\n * file already started its own server via top-level side effects (the\n * historical fastmcp-ts contract, e.g. `server.run()` at module scope) and\n * exits without error. An explicit export request that can't be resolved is\n * always a hard error.\n */\n\nimport { pathToFileURL } from 'node:url'\n\nconst AUTO_DETECT_NAMES = ['default', 'mcp', 'server', 'app']\n\ninterface RunnableFastMCP {\n  run(): Promise<void>\n  isRunning: boolean\n  address: { host: string; port: number; path: string } | null\n}\n\nfunction isFastMCPInstance(value: unknown): value is RunnableFastMCP {\n  if (!value || typeof value !== 'object') return false\n  const obj = value as Record<string, unknown>\n  return (\n    typeof obj['run'] === 'function' &&\n    typeof obj['tool'] === 'function' &&\n    typeof obj['resource'] === 'function' &&\n    typeof obj['prompt'] === 'function' &&\n    typeof obj['connect'] === 'function'\n  )\n}\n\nfunction describe(value: unknown): string {\n  if (value === null) return 'null'\n  if (Array.isArray(value)) return 'an array'\n  return `a ${typeof value}`\n}\n\nfunction fail(message: string): never {\n  console.error(`fastmcp: ${message}`)\n  process.exit(1)\n}\n\nasync function resolveFactory(\n  value: unknown,\n  exportName: string,\n  filePath: string,\n): Promise<unknown> {\n  if (typeof value !== 'function') return value\n  try {\n    return await (value as () => unknown)()\n  } catch (err) {\n    fail(\n      `Failed to call entrypoint factory \"${exportName}\" in ${filePath}: ` +\n        (err instanceof Error ? err.message : String(err)),\n    )\n  }\n}\n\nasync function main(): Promise<void> {\n  const filePath = process.env['FASTMCP_ENTRYPOINT_FILE']\n  if (!filePath) {\n    fail('internal error: FASTMCP_ENTRYPOINT_FILE was not set for the entrypoint bootstrap')\n  }\n\n  const explicitExport = process.env['FASTMCP_ENTRYPOINT_EXPORT']\n\n  let mod: Record<string, unknown>\n  try {\n    mod = (await import(pathToFileURL(filePath).href)) as Record<string, unknown>\n  } catch (err) {\n    fail(\n      `Failed to load entrypoint file \"${filePath}\": ` +\n        (err instanceof Error ? (err.stack ?? err.message) : String(err)),\n    )\n  }\n\n  let resolved: RunnableFastMCP | undefined\n\n  if (explicitExport) {\n    if (!(explicitExport in mod)) {\n      const available = Object.keys(mod).filter((k) => k !== '__esModule')\n      fail(\n        `Entrypoint export \"${explicitExport}\" not found in ${filePath}.` +\n          (available.length > 0\n            ? ` Available exports: ${available.join(', ')}.`\n            : ' The file has no exports.'),\n      )\n    }\n    const target = await resolveFactory(mod[explicitExport], explicitExport, filePath)\n    if (!isFastMCPInstance(target)) {\n      fail(\n        `Entrypoint export \"${explicitExport}\" in ${filePath} is not a FastMCP server ` +\n          `(or a function that returns one). Got ${describe(target)}.`,\n      )\n    }\n    resolved = target\n  } else {\n    for (const name of AUTO_DETECT_NAMES) {\n      if (!(name in mod)) continue\n      const candidate = mod[name]\n      // Only a direct FastMCP instance is auto-detected — a name match that\n      // isn't one (including a function, which would require calling it) is\n      // skipped in favor of the next conventional name rather than erroring\n      // or invoking it speculatively.\n      if (isFastMCPInstance(candidate)) {\n        resolved = candidate\n        break\n      }\n    }\n    if (resolved === undefined) {\n      // No conventional export resolved to a FastMCP instance. Assume this is\n      // a legacy self-running file (it started its own server via top-level\n      // side effects when imported above) and exit quietly.\n      return\n    }\n  }\n\n  if (resolved.isRunning) {\n    // The file already started this server itself (e.g. a top-level\n    // `server.run()` call) before we got a chance to. Don't start it twice.\n    return\n  }\n\n  await resolved.run()\n\n  // FastMCP.run() now logs its own 'listening on <url>' line (framework lifecycle logs), which the CLI's run command sniffs on stderr; no manual line needed here.\n}\n\nmain().catch((err) => {\n  console.error(err)\n  process.exit(1)\n})\n"],"mappings":";;;;AAoCA,sBAA8B;AAE9B,IAAM,oBAAoB,CAAC,WAAW,OAAO,UAAU,KAAK;AAQ5D,SAAS,kBAAkB,OAA0C;AACnE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,KAAK,MAAM,cACtB,OAAO,IAAI,MAAM,MAAM,cACvB,OAAO,IAAI,UAAU,MAAM,cAC3B,OAAO,IAAI,QAAQ,MAAM,cACzB,OAAO,IAAI,SAAS,MAAM;AAE9B;AAEA,SAAS,SAAS,OAAwB;AACxC,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,SAAO,KAAK,OAAO,KAAK;AAC1B;AAEA,SAAS,KAAK,SAAwB;AACpC,UAAQ,MAAM,YAAY,OAAO,EAAE;AACnC,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,eACb,OACA,YACA,UACkB;AAClB,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,MAAI;AACF,WAAO,MAAO,MAAwB;AAAA,EACxC,SAAS,KAAK;AACZ;AAAA,MACE,sCAAsC,UAAU,QAAQ,QAAQ,QAC7D,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACpD;AAAA,EACF;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,WAAW,QAAQ,IAAI,yBAAyB;AACtD,MAAI,CAAC,UAAU;AACb,SAAK,kFAAkF;AAAA,EACzF;AAEA,QAAM,iBAAiB,QAAQ,IAAI,2BAA2B;AAE9D,MAAI;AACJ,MAAI;AACF,UAAO,MAAM,WAAO,+BAAc,QAAQ,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ;AAAA,MACE,mCAAmC,QAAQ,SACxC,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG;AAAA,IACnE;AAAA,EACF;AAEA,MAAI;AAEJ,MAAI,gBAAgB;AAClB,QAAI,EAAE,kBAAkB,MAAM;AAC5B,YAAM,YAAY,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,MAAM,MAAM,YAAY;AACnE;AAAA,QACE,sBAAsB,cAAc,kBAAkB,QAAQ,OAC3D,UAAU,SAAS,IAChB,uBAAuB,UAAU,KAAK,IAAI,CAAC,MAC3C;AAAA,MACR;AAAA,IACF;AACA,UAAM,SAAS,MAAM,eAAe,IAAI,cAAc,GAAG,gBAAgB,QAAQ;AACjF,QAAI,CAAC,kBAAkB,MAAM,GAAG;AAC9B;AAAA,QACE,sBAAsB,cAAc,QAAQ,QAAQ,kEACT,SAAS,MAAM,CAAC;AAAA,MAC7D;AAAA,IACF;AACA,eAAW;AAAA,EACb,OAAO;AACL,eAAW,QAAQ,mBAAmB;AACpC,UAAI,EAAE,QAAQ,KAAM;AACpB,YAAM,YAAY,IAAI,IAAI;AAK1B,UAAI,kBAAkB,SAAS,GAAG;AAChC,mBAAW;AACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,aAAa,QAAW;AAI1B;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,WAAW;AAGtB;AAAA,EACF;AAEA,QAAM,SAAS,IAAI;AAGrB;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}