indusagi 0.13.6 → 0.13.8

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 (44) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +7 -1
  3. package/dist/cli.js +114 -14
  4. package/dist/index.js +1618 -51
  5. package/dist/llmgateway.js +114 -14
  6. package/dist/mcp.js +812 -3
  7. package/dist/runtime.js +114 -14
  8. package/dist/sarvam-mcp.js +960 -0
  9. package/dist/shell-app.js +116 -14
  10. package/dist/smithy.js +114 -14
  11. package/dist/swarm.js +114 -14
  12. package/dist/tracing.js +37 -0
  13. package/dist/types/connectors-sarvam/connect.d.ts +14 -0
  14. package/dist/types/connectors-sarvam/connectors-sarvam.test.d.ts +14 -0
  15. package/dist/types/connectors-sarvam/index.d.ts +14 -0
  16. package/dist/types/connectors-sarvam/toolbox.d.ts +19 -0
  17. package/dist/types/connectors-sarvam/types.d.ts +49 -0
  18. package/dist/types/connectors-zoho/connect.d.ts +22 -0
  19. package/dist/types/connectors-zoho/connectors-zoho.test.d.ts +10 -0
  20. package/dist/types/connectors-zoho/index.d.ts +13 -0
  21. package/dist/types/connectors-zoho/toolbox.d.ts +19 -0
  22. package/dist/types/connectors-zoho/types.d.ts +56 -0
  23. package/dist/types/facade/mcp-core/client.d.ts +32 -0
  24. package/dist/types/facade/mcp-core/index.d.ts +4 -0
  25. package/dist/types/facade/mcp-core/oauth/flow.d.ts +72 -0
  26. package/dist/types/facade/mcp-core/oauth/index.d.ts +18 -0
  27. package/dist/types/facade/mcp-core/oauth/oauth.test.d.ts +8 -0
  28. package/dist/types/facade/mcp-core/oauth/provider.d.ts +51 -0
  29. package/dist/types/facade/mcp-core/oauth/token-store.d.ts +31 -0
  30. package/dist/types/facade/mcp-core/oauth/types.d.ts +96 -0
  31. package/dist/types/facade/mcp-core/toolbox-bridge.d.ts +46 -0
  32. package/dist/types/facade/mcp-core/toolbox-bridge.test.d.ts +8 -0
  33. package/dist/types/index.d.ts +2 -0
  34. package/dist/types/llmgateway/contract/model-card.d.ts +1 -1
  35. package/dist/types/llmgateway/credentials/secrets.d.ts +2 -0
  36. package/dist/zoho.js +1451 -0
  37. package/package.json +11 -4
  38. package/dist/knowledge/guides/authoring-an-agent.md +0 -53
  39. package/dist/knowledge/guides/choosing-tools.md +0 -49
  40. package/dist/knowledge/guides/model-selection.md +0 -51
  41. package/dist/knowledge/guides/writing-system-prompts.md +0 -53
  42. package/dist/knowledge/index.ts +0 -19
  43. package/dist/knowledge/loader.ts +0 -200
  44. package/dist/knowledge/manifest.json +0 -29
package/dist/mcp.js CHANGED
@@ -1,8 +1,23 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+
1
13
  // src/facade/mcp-core/client.ts
2
14
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
15
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
4
16
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
5
17
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
18
+ import {
19
+ UnauthorizedError
20
+ } from "@modelcontextprotocol/sdk/client/auth.js";
6
21
  import {
7
22
  ListRootsRequestSchema,
8
23
  ElicitRequestSchema,
@@ -204,11 +219,90 @@ var MCPClient = class {
204
219
  this.log("debug", "Transport closed");
205
220
  prevOnClose?.();
206
221
  };
207
- await client.connect(transport, { timeout: DEFAULT_CONNECT_TIMEOUT });
222
+ try {
223
+ await client.connect(transport, { timeout: DEFAULT_CONNECT_TIMEOUT });
224
+ } catch (err) {
225
+ const recovered = await this.tryFinishOAuthAndReconnect(client, transport, err);
226
+ if (!recovered) {
227
+ throw this.wrapConnectError(err);
228
+ }
229
+ }
208
230
  this.serverCapabilities = client.getServerCapabilities();
209
231
  this.isConnected = true;
210
232
  this.log("info", `Connected to MCP server`);
211
233
  }
234
+ /**
235
+ * If `err` is an OAuth challenge and we have a provider + finishAuth-capable
236
+ * transport, wait for the browser code, exchange tokens, then reconnect on a
237
+ * **fresh** transport (the first transport is already `start()`ed and cannot
238
+ * be reused — see StreamableHTTPClientTransport.start).
239
+ *
240
+ * Matches the official SDK CLI example (`simpleOAuthClient`):
241
+ * finishAuth(code) → new StreamableHTTPClientTransport → client.connect again
242
+ *
243
+ * Client.connect already called `close()` on init failure, so the Client is
244
+ * free to attach a new transport.
245
+ */
246
+ async tryFinishOAuthAndReconnect(client, transport, err) {
247
+ if (!isAuthRequiredError(err)) return false;
248
+ const provider = this.options.authProvider;
249
+ if (!provider) return false;
250
+ const finishAuth = transport.finishAuth;
251
+ const waitForCode = provider.waitForAuthorizationCode;
252
+ if (typeof finishAuth !== "function" || typeof waitForCode !== "function") {
253
+ this.log(
254
+ "error",
255
+ "MCP server requires authentication but OAuth finish path is incomplete (need authProvider.waitForAuthorizationCode + transport.finishAuth)."
256
+ );
257
+ return false;
258
+ }
259
+ this.log("info", "MCP server requires OAuth \u2014 complete login in the browser\u2026");
260
+ try {
261
+ const code = await waitForCode.call(provider);
262
+ this.log("info", "OAuth authorization code received \u2014 exchanging tokens\u2026");
263
+ await finishAuth.call(transport, code);
264
+ try {
265
+ await transport.close?.();
266
+ } catch {
267
+ }
268
+ const fresh = this.createTransport(this.config);
269
+ this.transport = fresh;
270
+ const prevOnClose = fresh.onclose;
271
+ fresh.onclose = () => {
272
+ this.isConnected = false;
273
+ this.log("debug", "Transport closed");
274
+ prevOnClose?.();
275
+ };
276
+ this.log("info", "Reconnecting MCP session with OAuth tokens\u2026");
277
+ await client.connect(fresh, { timeout: DEFAULT_CONNECT_TIMEOUT });
278
+ return true;
279
+ } catch (oauthErr) {
280
+ this.log(
281
+ "error",
282
+ `OAuth finish failed: ${oauthErr instanceof Error ? oauthErr.message : String(oauthErr)}`
283
+ );
284
+ throw this.wrapConnectError(oauthErr);
285
+ }
286
+ }
287
+ /** Turn raw SDK / network errors into a clearer MCPError for callers. */
288
+ wrapConnectError(err) {
289
+ if (err instanceof MCPError) return err;
290
+ const message = err instanceof Error ? err.message : String(err);
291
+ if (isAuthRequiredError(err)) {
292
+ return new MCPError(
293
+ `Authentication required for MCP server "${this.serverName}". Pass auth: "auto" (default) to run the OAuth browser flow, or set a Bearer token via headers. Original: ${message}`,
294
+ "CONNECTION_FAILED" /* CONNECTION_FAILED */,
295
+ err,
296
+ { serverName: this.serverName, cause: err instanceof Error ? err : void 0 }
297
+ );
298
+ }
299
+ return new MCPError(
300
+ `Failed to connect to MCP server "${this.serverName}": ${message}`,
301
+ "CONNECTION_FAILED" /* CONNECTION_FAILED */,
302
+ err,
303
+ { serverName: this.serverName, cause: err instanceof Error ? err : void 0 }
304
+ );
305
+ }
212
306
  /**
213
307
  * Build the SDK transport for the configured server.
214
308
  *
@@ -230,9 +324,15 @@ var MCPClient = class {
230
324
  if ("url" in config) {
231
325
  const requestInit = config.headers ? { headers: config.headers } : void 0;
232
326
  if (config.transport === "sse") {
233
- return new SSEClientTransport(config.url, { requestInit });
327
+ return new SSEClientTransport(config.url, {
328
+ requestInit,
329
+ ...this.options.authProvider ? { authProvider: this.options.authProvider } : {}
330
+ });
234
331
  }
235
- return new StreamableHTTPClientTransport(config.url, { requestInit });
332
+ return new StreamableHTTPClientTransport(config.url, {
333
+ requestInit,
334
+ ...this.options.authProvider ? { authProvider: this.options.authProvider } : {}
335
+ });
236
336
  }
237
337
  throw new MCPError(
238
338
  "Server configuration must include either a command or a url",
@@ -604,6 +704,22 @@ function mapToSdkElicitResult(result) {
604
704
  }
605
705
  return { action: result.action };
606
706
  }
707
+ function isAuthRequiredError(err) {
708
+ if (err == null) return false;
709
+ if (err instanceof UnauthorizedError) return true;
710
+ if (typeof err === "object") {
711
+ const code = err.code;
712
+ if (code === 401 || code === "401") return true;
713
+ }
714
+ if (err instanceof Error) {
715
+ const msg = err.message;
716
+ if (/unauthorized/i.test(msg)) return true;
717
+ if (/authentication required/i.test(msg)) return true;
718
+ if (/auth required/i.test(msg)) return true;
719
+ if (err.name === "UnauthorizedError") return true;
720
+ }
721
+ return false;
722
+ }
607
723
  function sanitizeEnv(env) {
608
724
  const out = {};
609
725
  for (const [k, v] of Object.entries(env)) {
@@ -1322,6 +1438,697 @@ function createMCPToolsRecord(tools, client) {
1322
1438
  return toolRecord;
1323
1439
  }
1324
1440
 
1441
+ // src/capabilities/kernel/spec.ts
1442
+ function coerceInput(raw) {
1443
+ if (typeof raw === "string") {
1444
+ const trimmed = raw.trim();
1445
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
1446
+ try {
1447
+ return JSON.parse(trimmed);
1448
+ } catch {
1449
+ }
1450
+ }
1451
+ return raw;
1452
+ }
1453
+ if (raw === null || raw === void 0) {
1454
+ return {};
1455
+ }
1456
+ return raw;
1457
+ }
1458
+ function describeThrow(err) {
1459
+ if (err instanceof Error) return err.message;
1460
+ if (typeof err === "string") return err;
1461
+ try {
1462
+ return JSON.stringify(err);
1463
+ } catch {
1464
+ return String(err);
1465
+ }
1466
+ }
1467
+ function projectOutcome(id, result) {
1468
+ const blocks = result.content;
1469
+ let output;
1470
+ if (blocks.length === 1) {
1471
+ const only = blocks[0];
1472
+ output = only.kind === "text" ? only.text : only.value;
1473
+ } else {
1474
+ output = blocks;
1475
+ }
1476
+ return { id, output, isError: result.isError === true };
1477
+ }
1478
+ function defineTool(spec) {
1479
+ return {
1480
+ name: spec.name,
1481
+ // Facade `AgentTool` surface: top-level schema fields + an `execute` adapter
1482
+ // so the tool is usable by the high-level `Agent` as well as the kernel.
1483
+ label: spec.name,
1484
+ description: spec.description,
1485
+ parameters: spec.parameters,
1486
+ async execute(_toolCallId, params, signal) {
1487
+ const ctx = {
1488
+ cwd: process.cwd(),
1489
+ signal: signal ?? new AbortController().signal
1490
+ };
1491
+ try {
1492
+ const result = await spec.run(coerceInput(params), ctx);
1493
+ return {
1494
+ content: result.content.map(
1495
+ (block) => block.kind === "text" ? { type: "text", text: block.text } : { type: "text", text: JSON.stringify(block.value) }
1496
+ ),
1497
+ details: {},
1498
+ isError: result.isError ?? false
1499
+ };
1500
+ } catch (err) {
1501
+ return {
1502
+ content: [{ type: "text", text: `${spec.name} did not finish: ${describeThrow(err)}` }],
1503
+ details: {},
1504
+ isError: true
1505
+ };
1506
+ }
1507
+ },
1508
+ descriptor() {
1509
+ return {
1510
+ name: spec.name,
1511
+ description: spec.description,
1512
+ parameters: spec.parameters
1513
+ };
1514
+ },
1515
+ async invoke(call, ctx) {
1516
+ if (ctx.signal.aborted) {
1517
+ return {
1518
+ id: call.id,
1519
+ output: `Cancelled before ${spec.name} could begin.`,
1520
+ isError: true
1521
+ };
1522
+ }
1523
+ try {
1524
+ const input = coerceInput(call.input);
1525
+ const result = await spec.run(input, ctx);
1526
+ return projectOutcome(call.id, result);
1527
+ } catch (err) {
1528
+ return {
1529
+ id: call.id,
1530
+ output: `${spec.name} did not finish: ${describeThrow(err)}`,
1531
+ isError: true
1532
+ };
1533
+ }
1534
+ }
1535
+ };
1536
+ }
1537
+
1538
+ // src/facade/mcp-core/toolbox-bridge.ts
1539
+ var NOOP_FS_NOT_SUPPORTED = "not supported in MCP-wrapped tools";
1540
+ var noopFs = {
1541
+ readFile: () => {
1542
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1543
+ },
1544
+ writeFile: () => {
1545
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1546
+ },
1547
+ stat: () => {
1548
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1549
+ },
1550
+ readdir: () => {
1551
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1552
+ },
1553
+ mkdir: () => {
1554
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1555
+ },
1556
+ rm: () => {
1557
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1558
+ },
1559
+ exists: () => {
1560
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1561
+ }
1562
+ };
1563
+ var noopShell = {
1564
+ exec: () => {
1565
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1566
+ },
1567
+ spawn: () => {
1568
+ throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
1569
+ }
1570
+ };
1571
+ function makeToolContext(signal) {
1572
+ return {
1573
+ cwd: process.cwd(),
1574
+ fs: noopFs,
1575
+ shell: noopShell,
1576
+ signal,
1577
+ budget: { kind: "tail", maxBytes: 5e4 }
1578
+ };
1579
+ }
1580
+ function describeThrow2(err) {
1581
+ if (err instanceof Error) return err.message;
1582
+ if (typeof err === "string") return err;
1583
+ try {
1584
+ return JSON.stringify(err);
1585
+ } catch {
1586
+ return String(err);
1587
+ }
1588
+ }
1589
+ function projectMcpResult(result, logger) {
1590
+ const blocks = [];
1591
+ if (result.content && result.content.length > 0) {
1592
+ for (const block of result.content) {
1593
+ if (block.type === "text") {
1594
+ blocks.push({ kind: "text", text: block.text });
1595
+ } else if (block.type === "image") {
1596
+ const len = typeof block.data === "string" ? block.data.length : 0;
1597
+ blocks.push({ kind: "text", text: `[image ${block.mimeType}, ${len} chars base64]` });
1598
+ logger?.debug(`mcp bridge: image block collapsed to text mention (${block.mimeType})`);
1599
+ } else if (block.type === "resource") {
1600
+ blocks.push({ kind: "text", text: `[resource ${block.resource.uri}]` });
1601
+ } else {
1602
+ blocks.push({ kind: "text", text: "[unknown content block]" });
1603
+ }
1604
+ }
1605
+ }
1606
+ if (result.structuredContent !== void 0) {
1607
+ blocks.push({ kind: "json", value: result.structuredContent });
1608
+ }
1609
+ const content = blocks.length > 0 ? blocks : [{ kind: "text", text: "(no content)" }];
1610
+ return {
1611
+ content,
1612
+ ...result.isError === true ? { isError: true } : {}
1613
+ };
1614
+ }
1615
+ function makeRunner(client, remoteName, logger) {
1616
+ return async (input, ctx) => {
1617
+ if (ctx.signal.aborted) {
1618
+ return {
1619
+ content: [{ kind: "text", text: "Cancelled before tool could start." }],
1620
+ isError: true
1621
+ };
1622
+ }
1623
+ try {
1624
+ const args = input ?? {};
1625
+ const result = await client.callTool(remoteName, args);
1626
+ return projectMcpResult(result, logger);
1627
+ } catch (err) {
1628
+ const wrapped = err instanceof MCPError ? err : createServerError(describeThrow2(err), client.serverName);
1629
+ logger?.error(`mcp bridge: tool "${remoteName}" failed: ${wrapped.message}`, wrapped);
1630
+ return {
1631
+ content: [{ kind: "text", text: `MCP tool failed: ${wrapped.message}` }],
1632
+ isError: true
1633
+ };
1634
+ }
1635
+ };
1636
+ }
1637
+ async function mcpClientToToolBox(client, options = {}) {
1638
+ const prefix = options.namePrefix ?? "mcp";
1639
+ const logger = options.logger;
1640
+ const tools = await client.listTools();
1641
+ const filtered = typeof options.filter === "function" ? tools.filter((t) => options.filter(t)) : tools;
1642
+ const byName = /* @__PURE__ */ new Map();
1643
+ for (const tool of filtered) {
1644
+ const remoteName = tool.name;
1645
+ const prefixed = `${prefix}__${remoteName}`;
1646
+ const description = tool.description && tool.description.length > 0 ? tool.description : `<tool "${remoteName}" from MCP server "${client.serverName}">`;
1647
+ const parameters = tool.inputSchema;
1648
+ const def = defineTool({
1649
+ name: prefixed,
1650
+ description,
1651
+ parameters,
1652
+ run: makeRunner(client, remoteName, logger)
1653
+ });
1654
+ byName.set(prefixed, def);
1655
+ }
1656
+ const runner = {
1657
+ async run(call, signal) {
1658
+ const def = byName.get(call.name);
1659
+ if (!def) {
1660
+ return {
1661
+ id: call.id,
1662
+ output: `No tool "${call.name}" on MCP server "${client.serverName}".`,
1663
+ isError: true
1664
+ };
1665
+ }
1666
+ return def.invoke(call, makeToolContext(signal));
1667
+ }
1668
+ };
1669
+ return {
1670
+ descriptors() {
1671
+ return Array.from(byName.values()).map((d) => d.descriptor());
1672
+ },
1673
+ runner
1674
+ };
1675
+ }
1676
+
1677
+ // src/facade/mcp-core/oauth/index.ts
1678
+ var oauth_exports = {};
1679
+ __export(oauth_exports, {
1680
+ auth: () => auth,
1681
+ clearMcpOAuthState: () => clearMcpOAuthState,
1682
+ createMcpOAuthProvider: () => createMcpOAuthProvider,
1683
+ credentialsPath: () => credentialsPath,
1684
+ discoverOAuthServerInfo: () => discoverOAuthServerInfo,
1685
+ exchangeAuthorization: () => exchangeAuthorization,
1686
+ extractWWWAuthenticateParams: () => extractWWWAuthenticateParams,
1687
+ openBrowser: () => openBrowser,
1688
+ readMcpOAuthState: () => readMcpOAuthState,
1689
+ refreshAuthorization: () => refreshAuthorization,
1690
+ registerClient: () => registerClient,
1691
+ resolveCredentialsDir: () => resolveCredentialsDir,
1692
+ startAuthorization: () => startAuthorization,
1693
+ startOAuthCallbackServer: () => startOAuthCallbackServer,
1694
+ waitForAuthorizationCode: () => waitForAuthorizationCode,
1695
+ writeMcpOAuthState: () => writeMcpOAuthState
1696
+ });
1697
+
1698
+ // src/facade/mcp-core/oauth/flow.ts
1699
+ import { createServer } from "node:http";
1700
+ var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
1701
+ var SUCCESS_HTML = '<!doctype html><html><body style="font-family:system-ui;padding:2rem"><h1>Authorization complete</h1><p>You can close this tab and return to your terminal.</p></body></html>';
1702
+ var ERROR_HTML = (msg) => `<!doctype html><html><body style="font-family:system-ui;padding:2rem"><h1>Authorization failed</h1><pre>${escapeHtml(msg)}</pre></body></html>`;
1703
+ function escapeHtml(s) {
1704
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1705
+ }
1706
+ async function startOAuthCallbackServer(options) {
1707
+ const timeoutMs = options?.timeoutMs ?? CALLBACK_TIMEOUT_MS;
1708
+ const logger = options?.logger;
1709
+ let settled = false;
1710
+ let timer;
1711
+ let resolveCode;
1712
+ let rejectCode;
1713
+ const codePromise = new Promise((res, rej) => {
1714
+ resolveCode = res;
1715
+ rejectCode = rej;
1716
+ });
1717
+ codePromise.catch(() => {
1718
+ });
1719
+ let expectedState;
1720
+ const clearTimer = () => {
1721
+ if (timer !== void 0) {
1722
+ clearTimeout(timer);
1723
+ timer = void 0;
1724
+ }
1725
+ };
1726
+ const settleReject = (err) => {
1727
+ if (settled) return;
1728
+ settled = true;
1729
+ clearTimer();
1730
+ try {
1731
+ server.close();
1732
+ } catch {
1733
+ }
1734
+ rejectCode(err);
1735
+ };
1736
+ const settleResolve = (code) => {
1737
+ if (settled) return;
1738
+ settled = true;
1739
+ clearTimer();
1740
+ resolveCode(code);
1741
+ setTimeout(() => {
1742
+ try {
1743
+ server.close();
1744
+ } catch {
1745
+ }
1746
+ }, 50);
1747
+ };
1748
+ const server = createServer((req, res) => {
1749
+ if (settled) {
1750
+ res.statusCode = 410;
1751
+ res.end("already used");
1752
+ return;
1753
+ }
1754
+ handleRequest(
1755
+ req,
1756
+ res,
1757
+ expectedState,
1758
+ (code) => settleResolve(code),
1759
+ (err) => settleReject(err),
1760
+ logger
1761
+ );
1762
+ });
1763
+ await new Promise((resolveListen, rejectListen) => {
1764
+ server.once("error", rejectListen);
1765
+ server.listen(0, "127.0.0.1", () => resolveListen());
1766
+ });
1767
+ const address = server.address();
1768
+ if (!address) {
1769
+ server.close();
1770
+ throw new Error("oauth-callback: server bound to no address");
1771
+ }
1772
+ const port = address.port;
1773
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
1774
+ const close = () => {
1775
+ if (!settled) {
1776
+ settleReject(new Error("oauth-callback: closed before browser login completed"));
1777
+ } else {
1778
+ clearTimer();
1779
+ try {
1780
+ server.close();
1781
+ } catch {
1782
+ }
1783
+ }
1784
+ };
1785
+ return {
1786
+ redirectUri,
1787
+ port,
1788
+ waitForCode(state) {
1789
+ expectedState = state;
1790
+ logger?.debug(`oauth-callback: waiting for browser at ${redirectUri}`);
1791
+ if (!settled && timer === void 0) {
1792
+ timer = setTimeout(() => {
1793
+ settleReject(
1794
+ new Error(
1795
+ `oauth-callback: timed out after ${timeoutMs}ms waiting for browser login at ${redirectUri}`
1796
+ )
1797
+ );
1798
+ }, timeoutMs);
1799
+ timer.unref();
1800
+ }
1801
+ return codePromise;
1802
+ },
1803
+ close
1804
+ };
1805
+ }
1806
+ async function waitForAuthorizationCode(expectedState, options) {
1807
+ const server = await startOAuthCallbackServer(options);
1808
+ const code = await server.waitForCode(expectedState);
1809
+ return {
1810
+ code,
1811
+ redirectUri: server.redirectUri,
1812
+ port: server.port,
1813
+ close: () => server.close()
1814
+ };
1815
+ }
1816
+ function handleRequest(req, res, expectedState, onCode, onError, logger) {
1817
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
1818
+ if (url.pathname !== "/callback") {
1819
+ if (url.pathname === "/favicon.ico") {
1820
+ res.statusCode = 404;
1821
+ res.end();
1822
+ return;
1823
+ }
1824
+ res.statusCode = 404;
1825
+ res.end("not found");
1826
+ return;
1827
+ }
1828
+ const errorParam = url.searchParams.get("error");
1829
+ if (errorParam) {
1830
+ const desc = url.searchParams.get("error_description") ?? "";
1831
+ logger?.warn(`oauth-callback: server returned error ${errorParam} ${desc}`);
1832
+ res.statusCode = 400;
1833
+ res.setHeader("content-type", "text/html; charset=utf-8");
1834
+ res.end(ERROR_HTML(`${errorParam}${desc ? ": " + desc : ""}`));
1835
+ onError(new Error(`OAuth provider returned error: ${errorParam}${desc ? " \u2014 " + desc : ""}`));
1836
+ return;
1837
+ }
1838
+ const code = url.searchParams.get("code");
1839
+ const state = url.searchParams.get("state");
1840
+ if (!code) {
1841
+ res.statusCode = 400;
1842
+ res.setHeader("content-type", "text/html; charset=utf-8");
1843
+ res.end(ERROR_HTML("missing code"));
1844
+ return;
1845
+ }
1846
+ if (expectedState && state && state !== expectedState) {
1847
+ res.statusCode = 400;
1848
+ res.setHeader("content-type", "text/html; charset=utf-8");
1849
+ res.end(ERROR_HTML("state mismatch \u2014 possible CSRF"));
1850
+ onError(new Error("OAuth callback state did not match \u2014 refusing to exchange code"));
1851
+ return;
1852
+ }
1853
+ res.statusCode = 200;
1854
+ res.setHeader("content-type", "text/html; charset=utf-8");
1855
+ res.end(SUCCESS_HTML);
1856
+ logger?.debug(`oauth-callback: received authorization code (${code.length} chars)`);
1857
+ onCode(code);
1858
+ }
1859
+ async function openBrowser(url) {
1860
+ try {
1861
+ const mod = await import("node:child_process").catch(() => null);
1862
+ if (!mod) return false;
1863
+ const cmd = platformOpenCommand(url);
1864
+ if (!cmd) return false;
1865
+ const child = mod.spawn(cmd.cmd, cmd.args, { detached: true, stdio: "ignore" });
1866
+ child.unref();
1867
+ return true;
1868
+ } catch {
1869
+ return false;
1870
+ }
1871
+ }
1872
+ function platformOpenCommand(url) {
1873
+ switch (process.platform) {
1874
+ case "darwin":
1875
+ return { cmd: "open", args: [url] };
1876
+ case "win32":
1877
+ return { cmd: "rundll32", args: ["url.dll,FileProtocolHandler", url] };
1878
+ default:
1879
+ return { cmd: "xdg-open", args: [url] };
1880
+ }
1881
+ }
1882
+
1883
+ // src/facade/mcp-core/oauth/token-store.ts
1884
+ import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
1885
+ import { dirname as dirname2, join as join2 } from "node:path";
1886
+ function defaultCredentialsDir() {
1887
+ const xdg = process.env.XDG_CONFIG_HOME;
1888
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
1889
+ if (xdg && xdg.length > 0) return join2(xdg, "indusagi", "credentials", "mcp");
1890
+ if (home.length > 0) return join2(home, ".config", "indusagi", "credentials", "mcp");
1891
+ return join2(process.cwd(), ".indusagi", "credentials", "mcp");
1892
+ }
1893
+ function resolveCredentialsDir(override) {
1894
+ return override && override.length > 0 ? override : defaultCredentialsDir();
1895
+ }
1896
+ function credentialsPath(credentialsDir, id) {
1897
+ const safe = id.replace(/[^a-zA-Z0-9._-]/g, "-");
1898
+ return join2(credentialsDir, `${safe}.json`);
1899
+ }
1900
+ function readMcpOAuthState(credentialsDir, id) {
1901
+ const path = credentialsPath(credentialsDir, id);
1902
+ if (!existsSync2(path)) return void 0;
1903
+ let raw;
1904
+ try {
1905
+ raw = readFileSync2(path, "utf8");
1906
+ } catch (e) {
1907
+ return void 0;
1908
+ }
1909
+ let parsed;
1910
+ try {
1911
+ parsed = JSON.parse(raw);
1912
+ } catch (e) {
1913
+ return void 0;
1914
+ }
1915
+ if (typeof parsed !== "object" || parsed === null) return void 0;
1916
+ return { id, updatedAt: Date.now(), ...parsed };
1917
+ }
1918
+ function writeMcpOAuthState(credentialsDir, state) {
1919
+ const path = credentialsPath(credentialsDir, state.id);
1920
+ mkdirSync2(dirname2(path), { recursive: true });
1921
+ const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
1922
+ const json = JSON.stringify({ ...state, updatedAt: Date.now() }, null, 2);
1923
+ writeFileSync2(tmp, json, { mode: 384 });
1924
+ renameSync(tmp, path);
1925
+ try {
1926
+ chmodSync(path, 384);
1927
+ } catch {
1928
+ }
1929
+ }
1930
+ function clearMcpOAuthState(credentialsDir, id) {
1931
+ const path = credentialsPath(credentialsDir, id);
1932
+ try {
1933
+ const fs = __require("node:fs");
1934
+ fs.rmSync(path, { force: true });
1935
+ } catch {
1936
+ }
1937
+ }
1938
+
1939
+ // src/facade/mcp-core/oauth/provider.ts
1940
+ function randomState() {
1941
+ const bytes = new Uint8Array(16);
1942
+ crypto.getRandomValues(bytes);
1943
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1944
+ }
1945
+ function toSdkTokens(t) {
1946
+ return {
1947
+ access_token: t.access_token,
1948
+ refresh_token: t.refresh_token,
1949
+ scope: t.scope,
1950
+ token_type: t.token_type ?? "Bearer",
1951
+ expires_in: t.expires_at !== void 0 ? Math.max(0, Math.floor((t.expires_at - Date.now()) / 1e3)) : void 0
1952
+ };
1953
+ }
1954
+ function toSdkClientInfo(c) {
1955
+ return {
1956
+ client_id: c.client_id,
1957
+ client_secret: c.client_secret,
1958
+ client_id_issued_at: c.client_id_issued_at,
1959
+ client_secret_expires_at: c.client_secret_expires_at
1960
+ };
1961
+ }
1962
+ async function createMcpOAuthProvider(config) {
1963
+ const credentialsDir = resolveCredentialsDir(config.credentialsDir);
1964
+ const openBrowserEnabled = config.openBrowser ?? Boolean(process.env.CI !== "true" && process.stdout.isTTY);
1965
+ let callback;
1966
+ let redirectUrl;
1967
+ if (config.redirectUrl) {
1968
+ redirectUrl = config.redirectUrl;
1969
+ } else {
1970
+ callback = await startOAuthCallbackServer({
1971
+ logger: config.logger ? { debug: config.logger.debug, warn: config.logger.warn } : void 0
1972
+ });
1973
+ redirectUrl = callback.redirectUri;
1974
+ }
1975
+ const clientMetadata = {
1976
+ client_name: config.clientMetadata?.client_name ?? "indusagi-mcp-client",
1977
+ client_uri: config.clientMetadata?.client_uri,
1978
+ logo_uri: config.clientMetadata?.logo_uri,
1979
+ contacts: config.clientMetadata?.contacts ? [...config.clientMetadata.contacts] : void 0,
1980
+ tos_uri: config.clientMetadata?.tos_uri,
1981
+ policy_uri: config.clientMetadata?.policy_uri,
1982
+ redirect_uris: config.clientMetadata?.redirect_uris ? [...config.clientMetadata.redirect_uris] : [redirectUrl],
1983
+ token_endpoint_auth_method: config.clientMetadata?.token_endpoint_auth_method ?? "none",
1984
+ grant_types: config.clientMetadata?.grant_types ? [...config.clientMetadata.grant_types] : ["authorization_code", "refresh_token"],
1985
+ response_types: config.clientMetadata?.response_types ? [...config.clientMetadata.response_types] : ["code"],
1986
+ scope: config.clientMetadata?.scope
1987
+ };
1988
+ let lastAuthUrl;
1989
+ let lastState;
1990
+ let pendingCode;
1991
+ const provider = {
1992
+ id: config.id,
1993
+ credentialsDir,
1994
+ redirectUrl,
1995
+ get clientMetadata() {
1996
+ return clientMetadata;
1997
+ },
1998
+ state() {
1999
+ const s = randomState();
2000
+ lastState = s;
2001
+ return s;
2002
+ },
2003
+ clientInformation() {
2004
+ const state = readMcpOAuthState(credentialsDir, config.id);
2005
+ return state?.client ? toSdkClientInfo(state.client) : void 0;
2006
+ },
2007
+ saveClientInformation(client) {
2008
+ const state = readMcpOAuthState(credentialsDir, config.id) ?? {
2009
+ id: config.id,
2010
+ updatedAt: Date.now()
2011
+ };
2012
+ writeMcpOAuthState(credentialsDir, {
2013
+ ...state,
2014
+ client: {
2015
+ client_id: client.client_id,
2016
+ client_secret: client.client_secret,
2017
+ client_id_issued_at: client.client_id_issued_at,
2018
+ client_secret_expires_at: client.client_secret_expires_at
2019
+ }
2020
+ });
2021
+ },
2022
+ tokens() {
2023
+ const state = readMcpOAuthState(credentialsDir, config.id);
2024
+ return state?.tokens ? toSdkTokens(state.tokens) : void 0;
2025
+ },
2026
+ saveTokens(tokens) {
2027
+ const state = readMcpOAuthState(credentialsDir, config.id) ?? {
2028
+ id: config.id,
2029
+ updatedAt: Date.now()
2030
+ };
2031
+ writeMcpOAuthState(credentialsDir, {
2032
+ ...state,
2033
+ tokens: {
2034
+ access_token: tokens.access_token,
2035
+ refresh_token: tokens.refresh_token,
2036
+ scope: tokens.scope,
2037
+ token_type: tokens.token_type,
2038
+ expires_at: tokens.expires_in !== void 0 ? Date.now() + Math.trunc(tokens.expires_in) * 1e3 : void 0
2039
+ }
2040
+ });
2041
+ },
2042
+ async redirectToAuthorization(authorizationUrl) {
2043
+ const url = authorizationUrl.toString();
2044
+ lastAuthUrl = url;
2045
+ try {
2046
+ lastState = new URL(url).searchParams.get("state") ?? lastState;
2047
+ } catch {
2048
+ }
2049
+ const prompt = `oauth: open this URL in your browser to authorize Zoho MCP:
2050
+ ${url}`;
2051
+ config.logger?.info?.(prompt);
2052
+ config.logger?.debug?.(prompt);
2053
+ console.error(`
2054
+ [zoho oauth] Authorize in browser:
2055
+ ${url}
2056
+ `);
2057
+ let opened = false;
2058
+ if (openBrowserEnabled) opened = await openBrowser(url);
2059
+ if (!opened) {
2060
+ const tip = "oauth: could not auto-open browser. Visit the URL above manually.";
2061
+ config.logger?.info?.(tip);
2062
+ config.logger?.debug?.(tip);
2063
+ console.error(`[zoho oauth] ${tip}`);
2064
+ } else {
2065
+ config.logger?.debug?.("oauth: browser open launched");
2066
+ }
2067
+ },
2068
+ saveCodeVerifier(verifier) {
2069
+ const state = readMcpOAuthState(credentialsDir, config.id) ?? {
2070
+ id: config.id,
2071
+ updatedAt: Date.now()
2072
+ };
2073
+ writeMcpOAuthState(credentialsDir, { ...state, codeVerifier: verifier });
2074
+ },
2075
+ codeVerifier() {
2076
+ const state = readMcpOAuthState(credentialsDir, config.id);
2077
+ const verifier = state?.codeVerifier;
2078
+ if (!verifier) throw new Error("No PKCE code verifier saved");
2079
+ return verifier;
2080
+ },
2081
+ invalidateCredentials(scope) {
2082
+ if (scope === "all") {
2083
+ clearMcpOAuthState(credentialsDir, config.id);
2084
+ return;
2085
+ }
2086
+ const state = readMcpOAuthState(credentialsDir, config.id);
2087
+ if (!state) return;
2088
+ const next = { ...state };
2089
+ if (scope === "tokens" || scope === "verifier") next.tokens = void 0;
2090
+ if (scope === "verifier") next.codeVerifier = void 0;
2091
+ if (scope === "discovery") {
2092
+ next.resourceMetadata = void 0;
2093
+ next.authServerMetadata = void 0;
2094
+ }
2095
+ writeMcpOAuthState(credentialsDir, next);
2096
+ },
2097
+ async waitForAuthorizationCode() {
2098
+ if (pendingCode) return pendingCode;
2099
+ if (!callback) {
2100
+ throw new Error(
2101
+ "oauth: no local callback server (custom redirectUrl set). Pass the authorization code via finishAuth yourself."
2102
+ );
2103
+ }
2104
+ config.logger?.info("oauth: waiting for browser authorization\u2026");
2105
+ console.error("[zoho oauth] Waiting for browser authorization\u2026");
2106
+ const code = await callback.waitForCode(lastState);
2107
+ pendingCode = code;
2108
+ return code;
2109
+ },
2110
+ dispose() {
2111
+ callback?.close();
2112
+ callback = void 0;
2113
+ },
2114
+ lastAuthorizationUrl() {
2115
+ return lastAuthUrl;
2116
+ }
2117
+ };
2118
+ return provider;
2119
+ }
2120
+
2121
+ // src/facade/mcp-core/oauth/index.ts
2122
+ import {
2123
+ auth,
2124
+ discoverOAuthServerInfo,
2125
+ startAuthorization,
2126
+ exchangeAuthorization,
2127
+ refreshAuthorization,
2128
+ registerClient,
2129
+ extractWWWAuthenticateParams
2130
+ } from "@modelcontextprotocol/sdk/client/auth.js";
2131
+
1325
2132
  // src/facade/mcp-core/server.ts
1326
2133
  var MCPServer = class {
1327
2134
  constructor(options) {
@@ -1632,6 +2439,8 @@ export {
1632
2439
  isSessionError,
1633
2440
  jsonSchemaToTypeBox,
1634
2441
  loadMCPConfig,
2442
+ mcpClientToToolBox,
2443
+ oauth_exports as oauth,
1635
2444
  registerMCPToolsInRegistry,
1636
2445
  saveConfig,
1637
2446
  saveProjectConfig,