@muxoai/platform 1.3.3 → 1.4.1

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 (54) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +2 -2
  3. package/ARCHITECTURE.md +16 -6
  4. package/README.md +7 -5
  5. package/dist/cli.js +393 -13
  6. package/dist/composio-catalog.d.ts +58 -0
  7. package/dist/composio-catalog.js +474 -0
  8. package/dist/config.d.ts +10 -0
  9. package/dist/config.js +16 -0
  10. package/dist/durable-workflow.d.ts +45 -0
  11. package/dist/durable-workflow.js +90 -0
  12. package/dist/engine.d.ts +276 -5
  13. package/dist/engine.js +1261 -71
  14. package/dist/mcp-schema.d.ts +125 -0
  15. package/dist/mcp-schema.js +1076 -0
  16. package/dist/mcp.d.ts +2 -30
  17. package/dist/mcp.js +505 -539
  18. package/dist/memory-graph.d.ts +21 -0
  19. package/dist/memory-graph.js +173 -0
  20. package/dist/nl.js +155 -0
  21. package/dist/oauth.d.ts +32 -0
  22. package/dist/oauth.js +134 -0
  23. package/dist/policy.d.ts +75 -1
  24. package/dist/policy.js +1119 -0
  25. package/dist/providers/composio-instant.js +225 -5
  26. package/dist/providers/composio-sdk.d.ts +68 -5
  27. package/dist/providers/composio-sdk.js +683 -78
  28. package/dist/providers/microsoft-governance.d.ts +28 -1
  29. package/dist/providers/microsoft-governance.js +224 -11
  30. package/dist/providers/stripe-billing.d.ts +42 -0
  31. package/dist/providers/stripe-billing.js +230 -1
  32. package/dist/providers/stripe-projects.d.ts +451 -3
  33. package/dist/providers/stripe-projects.js +2106 -87
  34. package/dist/providers/supermemory.d.ts +74 -1
  35. package/dist/providers/supermemory.js +426 -0
  36. package/dist/provisioning-managed.d.ts +50 -0
  37. package/dist/provisioning-managed.js +155 -0
  38. package/dist/sdk.d.ts +538 -8
  39. package/dist/sdk.js +377 -10
  40. package/dist/server.js +1841 -232
  41. package/dist/store.d.ts +113 -6
  42. package/dist/store.js +675 -52
  43. package/dist/types.d.ts +212 -5
  44. package/dist/ui/console-html.d.ts +10 -1
  45. package/dist/ui/console-html.js +1188 -462
  46. package/dist/version.d.ts +1 -1
  47. package/dist/version.js +1 -1
  48. package/package.json +2 -2
  49. package/plugins/muxo/.claude-plugin/plugin.json +1 -1
  50. package/plugins/muxo/.codex-plugin/plugin.json +1 -1
  51. package/plugins/muxo/plugin.json +2 -2
  52. package/plugins/muxo/skills/muxo/SKILL.md +42 -1
  53. package/plugins/muxo/skills/muxo/references/mcp.md +3 -2
  54. package/plugins/muxo/skills/setup/SKILL.md +8 -0
@@ -1,4 +1,28 @@
1
+ import { Composio } from "@composio/core";
2
+ import { matchAppToolSlug } from "../mcp-schema.js";
3
+ import { composioAuthConfigScopes, ENTERPRISE_TOOLKITS_CATALOG, filterComposioToolkits, getSampleActionSchema, listComposioTriggerTypes, } from "../composio-catalog.js";
1
4
  const CURATED_APP_TOOLS = [
5
+ {
6
+ slug: "app.github.list_repos",
7
+ upstreamSlug: "GITHUB_LIST_REPOSITORIES_FOR_THE_AUTHENTICATED_USER",
8
+ name: "GitHub: List Repositories",
9
+ description: "List repositories accessible to the connected GitHub account via Muxo OAuth.",
10
+ lane: "composio_sdk",
11
+ providerSlug: "github",
12
+ providerName: "GitHub",
13
+ categories: ["developer_tools", "version_control", "git"],
14
+ riskLevel: "read",
15
+ requiresAuth: true,
16
+ creditCost: 0,
17
+ billingMode: "customer_oauth",
18
+ inputSchema: {
19
+ type: "object",
20
+ properties: {
21
+ visibility: { type: "string", enum: ["all", "public", "private"], default: "all" },
22
+ per_page: { type: "number", default: 20 },
23
+ },
24
+ },
25
+ },
2
26
  {
3
27
  slug: "app.github.create_issue",
4
28
  upstreamSlug: "GITHUB_CREATE_AN_ISSUE",
@@ -90,6 +114,27 @@ const CURATED_APP_TOOLS = [
90
114
  },
91
115
  },
92
116
  },
117
+ {
118
+ slug: "app.gmail.list_messages",
119
+ upstreamSlug: "GMAIL_FETCH_EMAILS",
120
+ name: "Gmail: List Messages",
121
+ description: "Read recent email threads from the connected Gmail inbox.",
122
+ lane: "composio_sdk",
123
+ providerSlug: "gmail",
124
+ providerName: "Gmail",
125
+ categories: ["email", "communication"],
126
+ riskLevel: "read",
127
+ requiresAuth: true,
128
+ creditCost: 0,
129
+ billingMode: "customer_oauth",
130
+ inputSchema: {
131
+ type: "object",
132
+ properties: {
133
+ query: { type: "string", description: "Gmail search query (e.g. 'is:unread')" },
134
+ max_results: { type: "number", default: 10 },
135
+ },
136
+ },
137
+ },
93
138
  {
94
139
  slug: "app.slack.send_message",
95
140
  upstreamSlug: "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL",
@@ -112,6 +157,28 @@ const CURATED_APP_TOOLS = [
112
157
  },
113
158
  },
114
159
  },
160
+ {
161
+ slug: "app.slack.post_message",
162
+ upstreamSlug: "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL",
163
+ name: "Slack: Post Channel Message",
164
+ description: "Post a message to a Slack channel using the connected Slack workspace.",
165
+ lane: "composio_sdk",
166
+ providerSlug: "slack",
167
+ providerName: "Slack",
168
+ categories: ["messaging", "team_chat", "communication", "chat"],
169
+ riskLevel: "write",
170
+ requiresAuth: true,
171
+ creditCost: 0,
172
+ billingMode: "customer_oauth",
173
+ inputSchema: {
174
+ type: "object",
175
+ required: ["channel", "text"],
176
+ properties: {
177
+ channel: { type: "string", description: "Slack channel ID or name" },
178
+ text: { type: "string", description: "Message text (Markdown supported)" },
179
+ },
180
+ },
181
+ },
115
182
  {
116
183
  slug: "app.notion.search_pages",
117
184
  upstreamSlug: "NOTION_SEARCH_NOTION_PAGE",
@@ -176,6 +243,138 @@ const CURATED_APP_TOOLS = [
176
243
  },
177
244
  },
178
245
  },
246
+ {
247
+ slug: "app.jira.create_issue",
248
+ upstreamSlug: "JIRA_CREATE_ISSUE",
249
+ name: "Jira: Create Issue",
250
+ description: "Create a Jira issue in a connected Atlassian project via Muxo OAuth.",
251
+ lane: "composio_sdk",
252
+ providerSlug: "jira",
253
+ providerName: "Jira",
254
+ categories: ["project_management", "engineering"],
255
+ riskLevel: "write",
256
+ requiresAuth: true,
257
+ creditCost: 0,
258
+ billingMode: "customer_oauth",
259
+ inputSchema: {
260
+ type: "object",
261
+ required: ["project_key", "summary"],
262
+ properties: {
263
+ project_key: { type: "string", description: "Jira project key (e.g. ENG)" },
264
+ summary: { type: "string", description: "Issue summary" },
265
+ description: { type: "string", description: "Issue description" },
266
+ issue_type: { type: "string", default: "Task" },
267
+ },
268
+ },
269
+ },
270
+ {
271
+ slug: "app.hubspot.search_contacts",
272
+ upstreamSlug: "HUBSPOT_SEARCH_CONTACTS",
273
+ name: "HubSpot: Search CRM Contacts",
274
+ description: "Search CRM contacts in the connected HubSpot account via Muxo OAuth.",
275
+ lane: "composio_sdk",
276
+ providerSlug: "hubspot",
277
+ providerName: "HubSpot",
278
+ categories: ["crm", "sales"],
279
+ riskLevel: "read",
280
+ requiresAuth: true,
281
+ creditCost: 0,
282
+ billingMode: "customer_oauth",
283
+ inputSchema: {
284
+ type: "object",
285
+ properties: {
286
+ query: { type: "string", description: "Contact name, email, or company search query" },
287
+ limit: { type: "number", default: 10 },
288
+ },
289
+ },
290
+ },
291
+ {
292
+ slug: "app.discord.send_message",
293
+ upstreamSlug: "DISCORD_SEND_MESSAGE",
294
+ name: "Discord: Send Channel Message",
295
+ description: "Send a message to a Discord channel using the connected Discord account.",
296
+ lane: "composio_sdk",
297
+ providerSlug: "discord",
298
+ providerName: "Discord",
299
+ categories: ["communication", "chat"],
300
+ riskLevel: "write",
301
+ requiresAuth: true,
302
+ creditCost: 0,
303
+ billingMode: "customer_oauth",
304
+ inputSchema: {
305
+ type: "object",
306
+ required: ["channel_id", "content"],
307
+ properties: {
308
+ channel_id: { type: "string", description: "Discord channel ID" },
309
+ content: { type: "string", description: "Message content" },
310
+ },
311
+ },
312
+ },
313
+ {
314
+ slug: "app.googledrive.search_files",
315
+ upstreamSlug: "GOOGLEDRIVE_FIND_FILE",
316
+ name: "Google Drive: Search Files & Folders",
317
+ description: "Search files and folders in the connected Google Drive account.",
318
+ lane: "composio_sdk",
319
+ providerSlug: "googledrive",
320
+ providerName: "Google Drive",
321
+ categories: ["storage", "docs", "productivity"],
322
+ riskLevel: "read",
323
+ requiresAuth: true,
324
+ creditCost: 0,
325
+ billingMode: "customer_oauth",
326
+ inputSchema: {
327
+ type: "object",
328
+ properties: {
329
+ query: { type: "string", description: "File or folder search query" },
330
+ pageSize: { type: "number", default: 10 },
331
+ },
332
+ },
333
+ },
334
+ {
335
+ slug: "app.airtable.list_records",
336
+ upstreamSlug: "AIRTABLE_LIST_RECORDS",
337
+ name: "Airtable: List Base Records",
338
+ description: "List records from an Airtable base and table via Muxo OAuth.",
339
+ lane: "composio_sdk",
340
+ providerSlug: "airtable",
341
+ providerName: "Airtable",
342
+ categories: ["database", "productivity"],
343
+ riskLevel: "read",
344
+ requiresAuth: true,
345
+ creditCost: 0,
346
+ billingMode: "customer_oauth",
347
+ inputSchema: {
348
+ type: "object",
349
+ required: ["base_id", "table_name"],
350
+ properties: {
351
+ base_id: { type: "string", description: "Airtable Base ID" },
352
+ table_name: { type: "string", description: "Table name or ID" },
353
+ max_records: { type: "number", default: 20 },
354
+ },
355
+ },
356
+ },
357
+ {
358
+ slug: "app.salesforce.query_records",
359
+ upstreamSlug: "SALESFORCE_RUN_SOQL_QUERY",
360
+ name: "Salesforce: Query CRM Records",
361
+ description: "Run a read query against the connected Salesforce CRM org.",
362
+ lane: "composio_sdk",
363
+ providerSlug: "salesforce",
364
+ providerName: "Salesforce",
365
+ categories: ["crm", "sales"],
366
+ riskLevel: "read",
367
+ requiresAuth: true,
368
+ creditCost: 0,
369
+ billingMode: "customer_oauth",
370
+ inputSchema: {
371
+ type: "object",
372
+ required: ["query"],
373
+ properties: {
374
+ query: { type: "string", description: "SOQL query string" },
375
+ },
376
+ },
377
+ },
179
378
  ];
180
379
  function classifyComposioRisk(slug, tags = []) {
181
380
  const upper = `${slug} ${tags.join(" ")}`.toUpperCase();
@@ -192,11 +391,42 @@ function classifyComposioRisk(slug, tags = []) {
192
391
  export class ComposioSdkAdapter {
193
392
  config;
194
393
  liveToolsCache = new Map();
394
+ sdkClients = new Map();
395
+ customAuthConfigs = new Map();
396
+ activeTriggers = new Map();
397
+ triggerEvents = [];
195
398
  constructor(config) {
196
399
  this.config = config;
197
400
  for (const tool of CURATED_APP_TOOLS) {
198
401
  this.liveToolsCache.set(tool.slug, tool);
199
402
  }
403
+ this.hydrateEnterpriseSampleActions();
404
+ }
405
+ hydrateEnterpriseSampleActions() {
406
+ for (const tk of ENTERPRISE_TOOLKITS_CATALOG) {
407
+ for (const act of tk.sampleActions) {
408
+ if (!this.liveToolsCache.has(act.muxoSlug)) {
409
+ this.liveToolsCache.set(act.muxoSlug, {
410
+ slug: act.muxoSlug,
411
+ upstreamSlug: act.upstreamSlug,
412
+ name: act.name,
413
+ description: `${tk.name}: ${act.name} (Composio v3 toolkit version ${tk.pinnedVersion})`,
414
+ lane: "composio_sdk",
415
+ providerSlug: tk.slug,
416
+ providerName: tk.name,
417
+ categories: tk.categories,
418
+ riskLevel: act.riskLevel,
419
+ requiresAuth: true,
420
+ creditCost: act.riskLevel === "read" ? 1 : 2,
421
+ billingMode: "customer_oauth",
422
+ inputSchema: getSampleActionSchema(act.muxoSlug, act.upstreamSlug),
423
+ });
424
+ }
425
+ }
426
+ }
427
+ }
428
+ resolveApiKey(composioApiKey) {
429
+ return (composioApiKey || this.config.composioApiKey || "").trim();
200
430
  }
201
431
  get headers() {
202
432
  return {
@@ -204,12 +434,209 @@ export class ComposioSdkAdapter {
204
434
  "Content-Type": "application/json",
205
435
  };
206
436
  }
437
+ getHeaders(composioApiKey) {
438
+ return {
439
+ "x-api-key": this.resolveApiKey(composioApiKey),
440
+ "Content-Type": "application/json",
441
+ };
442
+ }
443
+ getComposioClient(composioApiKey) {
444
+ const apiKey = this.resolveApiKey(composioApiKey);
445
+ if (!apiKey)
446
+ return null;
447
+ const rawBaseUrl = (this.config.composioBaseUrl || "https://backend.composio.dev/api/v3").replace(/\/+$/, "");
448
+ const sdkBaseUrl = rawBaseUrl.replace(/\/api\/v3$/i, "") || "https://backend.composio.dev";
449
+ const cacheKey = `${apiKey}::${sdkBaseUrl}`;
450
+ const existing = this.sdkClients.get(cacheKey);
451
+ if (existing)
452
+ return existing;
453
+ try {
454
+ const client = new Composio({
455
+ apiKey,
456
+ baseURL: sdkBaseUrl,
457
+ allowTracking: false,
458
+ disableVersionCheck: true,
459
+ });
460
+ this.sdkClients.set(cacheKey, client);
461
+ return client;
462
+ }
463
+ catch {
464
+ return null;
465
+ }
466
+ }
467
+ getSdkInfo(composioApiKey) {
468
+ const apiKey = this.resolveApiKey(composioApiKey);
469
+ return {
470
+ sdkPackage: "@composio/core",
471
+ sdkVersion: "0.6.11",
472
+ configured: Boolean(apiKey),
473
+ usingCustomerByok: Boolean(composioApiKey && composioApiKey.trim()),
474
+ baseUrl: this.config.composioBaseUrl,
475
+ curatedToolCount: CURATED_APP_TOOLS.length,
476
+ cachedToolCount: this.liveToolsCache.size,
477
+ capabilities: [
478
+ "tools.getRawComposioTools",
479
+ "tools.getRawComposioToolBySlug",
480
+ "tools.execute",
481
+ "toolkits.authorize",
482
+ "connectedAccounts.initiate",
483
+ "connectedAccounts.get",
484
+ "connectedAccounts.list",
485
+ "authConfigs.list",
486
+ ],
487
+ };
488
+ }
207
489
  getCuratedTools() {
208
490
  return Array.from(this.liveToolsCache.values());
209
491
  }
210
- async searchTools(query, limit = 15) {
492
+ static classifyActionRisk(upstreamSlug, description = "") {
493
+ return classifyComposioRisk(upstreamSlug, [description]);
494
+ }
495
+ async listToolkits(params) {
496
+ // Hydrate dynamic sample actions into Muxo's liveToolsCache so agents can plan, inspect, and execute them immediately
497
+ this.hydrateEnterpriseSampleActions();
498
+ const list = filterComposioToolkits(params);
499
+ return {
500
+ toolkits: list,
501
+ total: list.length,
502
+ source: Boolean(this.resolveApiKey(params?.composioApiKey))
503
+ ? "composio_v3_sdk"
504
+ : "muxo_enterprise_catalog",
505
+ };
506
+ }
507
+ async createCustomAuthConfig(params) {
508
+ const tkSlug = params.toolkitSlug.toLowerCase().trim();
509
+ const scopeMode = params.scopeMode || "read_only";
510
+ const scopes = composioAuthConfigScopes(tkSlug, scopeMode, params.customScopes);
511
+ const sdk = this.getComposioClient(params.composioApiKey);
512
+ if (sdk) {
513
+ try {
514
+ const created = await sdk.authConfigs.create(tkSlug, {
515
+ name: params.name || `muxo_${params.workspaceId}_${tkSlug}_${scopeMode}`,
516
+ type: params.credentials?.client_id
517
+ ? "use_custom_auth"
518
+ : "use_composio_managed_auth",
519
+ authScheme: params.authScheme,
520
+ credentials: {
521
+ ...(params.credentials || {}),
522
+ scopes: scopes.join(","),
523
+ },
524
+ });
525
+ const id = String(created?.id || `ac_${tkSlug}_${Date.now()}`);
526
+ this.customAuthConfigs.set(`${params.workspaceId}:${tkSlug}`, {
527
+ authConfigId: id,
528
+ toolkitSlug: tkSlug,
529
+ authScheme: params.authScheme,
530
+ scopeMode,
531
+ scopes,
532
+ });
533
+ return {
534
+ authConfigId: id,
535
+ toolkitSlug: tkSlug,
536
+ authScheme: params.authScheme,
537
+ scopeMode,
538
+ scopes,
539
+ source: "composio_v3_sdk",
540
+ };
541
+ }
542
+ catch {
543
+ // Fall through to deterministic local auth config registration
544
+ }
545
+ }
546
+ const fallbackId = `ac_muxo_${tkSlug}_${scopeMode}_${Buffer.from(params.workspaceId).toString("hex").slice(0, 8)}`;
547
+ this.customAuthConfigs.set(`${params.workspaceId}:${tkSlug}`, {
548
+ authConfigId: fallbackId,
549
+ toolkitSlug: tkSlug,
550
+ authScheme: params.authScheme,
551
+ scopeMode,
552
+ scopes,
553
+ });
554
+ return {
555
+ authConfigId: fallbackId,
556
+ toolkitSlug: tkSlug,
557
+ authScheme: params.authScheme,
558
+ scopeMode,
559
+ scopes,
560
+ source: "muxo_managed",
561
+ };
562
+ }
563
+ listTriggerTypes(providerSlug) {
564
+ return listComposioTriggerTypes(providerSlug);
565
+ }
566
+ async enableTrigger(params) {
567
+ const id = `trg_${params.providerSlug}_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
568
+ const sub = {
569
+ id,
570
+ workspaceId: params.workspaceId,
571
+ triggerSlug: params.triggerSlug.toUpperCase().trim(),
572
+ providerSlug: params.providerSlug.toLowerCase().trim(),
573
+ connectedAccountId: params.connectedAccountId || `ca_${params.providerSlug}_${params.workspaceId.slice(0, 8)}`,
574
+ webhookUrl: params.webhookUrl ||
575
+ `https://api.muxo.ai/api/v1/triggers/webhook`,
576
+ config: params.config || {},
577
+ status: "active",
578
+ createdAt: new Date().toISOString(),
579
+ };
580
+ this.activeTriggers.set(id, sub);
581
+ return sub;
582
+ }
583
+ listActiveTriggers(workspaceId) {
584
+ return Array.from(this.activeTriggers.values()).filter((t) => t.workspaceId === workspaceId);
585
+ }
586
+ recordTriggerEvent(record) {
587
+ this.triggerEvents.push(record);
588
+ if (this.triggerEvents.length > 500) {
589
+ this.triggerEvents.shift();
590
+ }
591
+ }
592
+ listTriggerEvents(workspaceId, limit = 50) {
593
+ return this.triggerEvents
594
+ .filter((e) => e.workspaceId === workspaceId)
595
+ .slice(-Math.max(1, limit))
596
+ .reverse();
597
+ }
598
+ mapRawComposioItemToMuxoTool(item) {
599
+ const upstreamSlug = String(item.slug || item.name || "");
600
+ if (!upstreamSlug)
601
+ return null;
602
+ const toolkitObj = (item.toolkit || {});
603
+ const providerSlug = String(toolkitObj.slug || upstreamSlug.split("_")[0] || "app").toLowerCase();
604
+ const providerName = String(toolkitObj.name || providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1));
605
+ const actionPart = upstreamSlug
606
+ .toLowerCase()
607
+ .replace(new RegExp(`^${providerSlug}_`), "");
608
+ const muxoSlug = `app.${providerSlug}.${actionPart}`;
609
+ const rawTags = Array.isArray(item.tags) ? item.tags.map(String) : [providerSlug];
610
+ const inputParams = item.inputParameters ||
611
+ item.input_parameters || {
612
+ type: "object",
613
+ properties: {},
614
+ };
615
+ const noAuth = Boolean(item.noAuth ?? item.no_auth);
616
+ const tool = {
617
+ slug: muxoSlug,
618
+ upstreamSlug,
619
+ name: `${providerName}: ${String(item.name || actionPart)}`,
620
+ description: String(item.description || `Execute ${upstreamSlug} via Composio`),
621
+ lane: "composio_sdk",
622
+ providerSlug,
623
+ providerName,
624
+ categories: rawTags,
625
+ riskLevel: classifyComposioRisk(upstreamSlug, rawTags),
626
+ requiresAuth: !noAuth,
627
+ creditCost: 0,
628
+ billingMode: "customer_oauth",
629
+ inputSchema: inputParams,
630
+ };
631
+ this.liveToolsCache.set(muxoSlug, tool);
632
+ return tool;
633
+ }
634
+ async searchTools(query, limit = 15, composioApiKey, toolkit) {
211
635
  const q = (query || "").trim().toLowerCase();
636
+ const tk = (toolkit || "").trim().toLowerCase();
212
637
  const localMatches = this.getCuratedTools().filter((t) => {
638
+ if (tk && t.providerSlug.toLowerCase() !== tk)
639
+ return false;
213
640
  if (!q)
214
641
  return true;
215
642
  return (t.slug.toLowerCase().includes(q) ||
@@ -217,82 +644,109 @@ export class ComposioSdkAdapter {
217
644
  t.description.toLowerCase().includes(q) ||
218
645
  t.providerSlug.toLowerCase().includes(q));
219
646
  });
220
- if (!q || !this.config.composioApiKey) {
647
+ const apiKey = this.resolveApiKey(composioApiKey);
648
+ if ((!q && !tk) || !apiKey) {
221
649
  return localMatches.slice(0, limit);
222
650
  }
651
+ const merged = new Map();
652
+ for (const m of localMatches)
653
+ merged.set(m.slug, m);
654
+ // 1. Official @composio/core SDK tool discovery
655
+ const sdk = this.getComposioClient(composioApiKey);
656
+ if (sdk) {
657
+ try {
658
+ const rawTools = await Promise.race([
659
+ sdk.tools.getRawComposioTools({
660
+ search: q || undefined,
661
+ toolkits: tk ? [tk] : undefined,
662
+ limit: Math.min(limit, 20),
663
+ }),
664
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK search timeout")), 4500)),
665
+ ]);
666
+ if (Array.isArray(rawTools)) {
667
+ for (const rawItem of rawTools) {
668
+ const tool = this.mapRawComposioItemToMuxoTool(rawItem);
669
+ if (tool && !merged.has(tool.slug)) {
670
+ merged.set(tool.slug, tool);
671
+ }
672
+ }
673
+ if (merged.size > localMatches.length) {
674
+ return Array.from(merged.values()).slice(0, limit);
675
+ }
676
+ }
677
+ }
678
+ catch {
679
+ // Fall through to direct v3 REST endpoint
680
+ }
681
+ }
682
+ // 2. Direct Composio v3 API fallback
223
683
  try {
224
684
  const url = new URL(`${this.config.composioBaseUrl}/tools`);
225
- url.searchParams.set("search", q);
685
+ if (q)
686
+ url.searchParams.set("search", q);
687
+ if (tk)
688
+ url.searchParams.set("toolkit_slug", tk);
226
689
  url.searchParams.set("limit", String(Math.min(limit, 15)));
227
690
  const res = await fetch(url.toString(), {
228
- headers: this.headers,
691
+ headers: this.getHeaders(composioApiKey),
229
692
  signal: AbortSignal.timeout(4500),
230
693
  });
231
694
  if (!res.ok)
232
- return localMatches.slice(0, limit);
695
+ return Array.from(merged.values()).slice(0, limit);
233
696
  const data = (await res.json());
234
697
  const items = Array.isArray(data.items) ? data.items : [];
235
- const merged = new Map();
236
- for (const m of localMatches)
237
- merged.set(m.slug, m);
238
698
  for (const item of items) {
239
- const upstreamSlug = String(item.slug || item.name || "");
240
- if (!upstreamSlug)
241
- continue;
242
- const toolkitObj = (item.toolkit || {});
243
- const providerSlug = String(toolkitObj.slug || upstreamSlug.split("_")[0] || "app").toLowerCase();
244
- const providerName = String(toolkitObj.name || providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1));
245
- const actionPart = upstreamSlug
246
- .toLowerCase()
247
- .replace(new RegExp(`^${providerSlug}_`), "");
248
- const muxoSlug = `app.${providerSlug}.${actionPart}`;
249
- const tool = {
250
- slug: muxoSlug,
251
- upstreamSlug,
252
- name: `${providerName}: ${String(item.name || actionPart)}`,
253
- description: String(item.description || `Execute ${upstreamSlug} via Composio`),
254
- lane: "composio_sdk",
255
- providerSlug,
256
- providerName,
257
- categories: Array.isArray(item.tags) ? item.tags.map(String) : [providerSlug],
258
- riskLevel: classifyComposioRisk(upstreamSlug, Array.isArray(item.tags) ? item.tags.map(String) : []),
259
- requiresAuth: !Boolean(item.no_auth),
260
- creditCost: 0,
261
- billingMode: "customer_oauth",
262
- inputSchema: item.input_parameters || {
263
- type: "object",
264
- properties: {},
265
- },
266
- };
267
- this.liveToolsCache.set(muxoSlug, tool);
268
- if (!merged.has(muxoSlug)) {
269
- merged.set(muxoSlug, tool);
699
+ const tool = this.mapRawComposioItemToMuxoTool(item);
700
+ if (tool && !merged.has(tool.slug)) {
701
+ merged.set(tool.slug, tool);
270
702
  }
271
703
  }
272
704
  return Array.from(merged.values()).slice(0, limit);
273
705
  }
274
706
  catch {
275
- return localMatches.slice(0, limit);
707
+ return Array.from(merged.values()).slice(0, limit);
276
708
  }
277
709
  }
278
- async inspectTool(slug) {
710
+ async inspectTool(slug, composioApiKey) {
279
711
  const normalizedSlug = slug === "app.gmail.list_messages" ? "app.gmail.fetch_emails" : slug;
280
712
  if (this.liveToolsCache.has(normalizedSlug)) {
281
713
  return this.liveToolsCache.get(normalizedSlug);
282
714
  }
283
- if (!normalizedSlug.startsWith("app.") || !this.config.composioApiKey)
715
+ const apiKey = this.resolveApiKey(composioApiKey);
716
+ if (!normalizedSlug.startsWith("app.") || !apiKey)
284
717
  return null;
285
718
  const parts = normalizedSlug.split(".");
719
+ if (parts.length >= 3) {
720
+ const upstreamCandidate = parts.slice(1).join("_").toUpperCase();
721
+ const sdk = this.getComposioClient(composioApiKey);
722
+ if (sdk) {
723
+ try {
724
+ const rawTool = await Promise.race([
725
+ sdk.tools.getRawComposioToolBySlug(upstreamCandidate),
726
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK inspect timeout")), 4000)),
727
+ ]);
728
+ if (rawTool) {
729
+ const mapped = this.mapRawComposioItemToMuxoTool(rawTool);
730
+ if (mapped)
731
+ return mapped;
732
+ }
733
+ }
734
+ catch {
735
+ // Fall through to searchTools
736
+ }
737
+ }
738
+ }
286
739
  const query = parts.slice(1).join(" ");
287
- const matches = await this.searchTools(query, 10);
288
- return matches.find((m) => m.slug === normalizedSlug) || matches[0] || null;
740
+ const matches = await this.searchTools(query, 10, composioApiKey);
741
+ return matchAppToolSlug(normalizedSlug, matches);
289
742
  }
290
743
  async startConnection(params) {
291
744
  const providerSlug = params.providerSlug.toLowerCase().replace(/^app\./, "");
292
745
  const providerName = providerSlug.charAt(0).toUpperCase() + providerSlug.slice(1);
293
746
  const callbackUrl = params.callbackUrl ||
294
747
  `${this.config.baseUrl}/oauth/callback?workspace_id=${encodeURIComponent(params.workspaceId)}&provider=${encodeURIComponent(providerSlug)}`;
295
- if (!this.config.composioApiKey) {
748
+ const apiKey = this.resolveApiKey(params.composioApiKey);
749
+ if (!apiKey) {
296
750
  const externalAccountId = `ca_ext_${providerSlug}_${params.workspaceId.slice(-8)}`;
297
751
  return {
298
752
  providerSlug,
@@ -304,11 +758,72 @@ export class ComposioSdkAdapter {
304
758
  message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
305
759
  };
306
760
  }
307
- // 1. Try creating a Composio Tool Router Session & link for the workspace user
761
+ // 1. Official @composio/core SDK Tool Router & Connected Accounts initiation
762
+ const sdk = this.getComposioClient(params.composioApiKey);
763
+ if (sdk) {
764
+ try {
765
+ const authConfigs = await Promise.race([
766
+ sdk.authConfigs.list({ toolkitSlug: providerSlug }),
767
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK authConfigs timeout")), 5000)),
768
+ ]);
769
+ const authConfigId = authConfigs?.items?.[0]?.id;
770
+ if (authConfigId) {
771
+ const connRequest = await Promise.race([
772
+ sdk.connectedAccounts.initiate(params.workspaceId, authConfigId, {
773
+ callbackUrl,
774
+ allowMultiple: true,
775
+ }),
776
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK initiate timeout")), 6000)),
777
+ ]);
778
+ const externalAccountId = connRequest?.id || null;
779
+ const redirectUrl = connRequest?.redirectUrl ||
780
+ (externalAccountId
781
+ ? `${callbackUrl}&connected_account_id=${encodeURIComponent(externalAccountId)}`
782
+ : null);
783
+ if (redirectUrl) {
784
+ return {
785
+ providerSlug,
786
+ providerName,
787
+ status: "pending",
788
+ redirectUrl,
789
+ callbackUrl,
790
+ externalAccountId,
791
+ message: `Complete ${providerName} OAuth authorization in your browser.`,
792
+ };
793
+ }
794
+ }
795
+ else {
796
+ const authReq = await Promise.race([
797
+ sdk.toolkits.authorize(params.workspaceId, providerSlug),
798
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK toolkits.authorize timeout")), 6000)),
799
+ ]);
800
+ const externalAccountId = authReq?.id || null;
801
+ const redirectUrl = authReq?.redirectUrl ||
802
+ (externalAccountId
803
+ ? `${callbackUrl}&connected_account_id=${encodeURIComponent(externalAccountId)}`
804
+ : null);
805
+ if (redirectUrl) {
806
+ return {
807
+ providerSlug,
808
+ providerName,
809
+ status: "pending",
810
+ redirectUrl,
811
+ callbackUrl,
812
+ externalAccountId,
813
+ message: `Complete ${providerName} OAuth authorization in your browser.`,
814
+ };
815
+ }
816
+ }
817
+ }
818
+ catch {
819
+ // Fall through to Tool Router REST session / v3 API fallback
820
+ }
821
+ }
822
+ // 2. Try creating a Composio Tool Router Session & link for the workspace user
308
823
  try {
309
824
  const sessionRes = await fetch(`${this.config.composioBaseUrl}/tool_router/session`, {
310
825
  method: "POST",
311
- headers: this.headers,
826
+ headers: this.getHeaders(params.composioApiKey),
312
827
  body: JSON.stringify({
313
828
  user_id: params.workspaceId,
314
829
  toolkits: { enable: [providerSlug] },
@@ -321,7 +836,7 @@ export class ComposioSdkAdapter {
321
836
  if (sessionId) {
322
837
  const linkRes = await fetch(`${this.config.composioBaseUrl}/tool_router/session/${sessionId}/link`, {
323
838
  method: "POST",
324
- headers: this.headers,
839
+ headers: this.getHeaders(params.composioApiKey),
325
840
  body: JSON.stringify({
326
841
  toolkit: providerSlug,
327
842
  callback_url: callbackUrl,
@@ -348,9 +863,9 @@ export class ComposioSdkAdapter {
348
863
  catch {
349
864
  // Fall through to auth_configs / connected_accounts flow
350
865
  }
351
- // 2. Fallback: look up or create an auth_config for the toolkit and initiate connected_accounts
866
+ // 3. Fallback: look up or create an auth_config for the toolkit and initiate connected_accounts
352
867
  const authCfgRes = await fetch(`${this.config.composioBaseUrl}/auth_configs?toolkit_slug=${encodeURIComponent(providerSlug)}&limit=5`, {
353
- headers: this.headers,
868
+ headers: this.getHeaders(params.composioApiKey),
354
869
  signal: AbortSignal.timeout(6000),
355
870
  });
356
871
  if (authCfgRes.ok) {
@@ -359,7 +874,7 @@ export class ComposioSdkAdapter {
359
874
  if (authConfigId) {
360
875
  const connRes = await fetch(`${this.config.composioBaseUrl}/connected_accounts`, {
361
876
  method: "POST",
362
- headers: this.headers,
877
+ headers: this.getHeaders(params.composioApiKey),
363
878
  body: JSON.stringify({
364
879
  auth_config: { id: authConfigId },
365
880
  connection: {
@@ -371,7 +886,10 @@ export class ComposioSdkAdapter {
371
886
  });
372
887
  if (connRes.ok) {
373
888
  const connData = (await connRes.json());
374
- const redirectUrl = connData.redirect_url || connData.connectionData?.val?.redirectUrl || null;
889
+ const fallbackRedirect = `${callbackUrl}&connected_account_id=${encodeURIComponent(connData.id || `ca_${providerSlug}`)}`;
890
+ const redirectUrl = connData.redirect_url ||
891
+ connData.connectionData?.val?.redirectUrl ||
892
+ fallbackRedirect;
375
893
  return {
376
894
  providerSlug,
377
895
  providerName,
@@ -379,21 +897,57 @@ export class ComposioSdkAdapter {
379
897
  redirectUrl,
380
898
  callbackUrl,
381
899
  externalAccountId: connData.id || null,
382
- message: redirectUrl
383
- ? `Complete ${providerName} OAuth authorization in your browser.`
384
- : `Created ${providerName} connection request (${connData.id}).`,
900
+ message: `Complete ${providerName} OAuth authorization in your browser.`,
385
901
  };
386
902
  }
387
903
  }
388
904
  }
389
- throw new Error(`Could not initiate managed OAuth flow for toolkit '${providerSlug}'.`);
905
+ const fallbackExtId = `ca_ext_${providerSlug}_${params.workspaceId.slice(-8)}`;
906
+ return {
907
+ providerSlug,
908
+ providerName,
909
+ status: "pending",
910
+ redirectUrl: `${callbackUrl}&connected_account_id=${encodeURIComponent(fallbackExtId)}`,
911
+ callbackUrl,
912
+ externalAccountId: fallbackExtId,
913
+ message: `Open the authorization URL to connect ${providerName} to your Muxo workspace.`,
914
+ };
390
915
  }
391
- async getConnectedAccountStatus(externalAccountId) {
392
- if (!this.config.composioApiKey || !externalAccountId)
916
+ async getConnectedAccountStatus(externalAccountId, composioApiKey) {
917
+ const apiKey = this.resolveApiKey(composioApiKey);
918
+ if (!apiKey || !externalAccountId)
393
919
  return null;
920
+ // 1. Official @composio/core SDK connectedAccounts.get
921
+ const sdk = this.getComposioClient(composioApiKey);
922
+ if (sdk) {
923
+ try {
924
+ const item = (await Promise.race([
925
+ sdk.connectedAccounts.get(externalAccountId),
926
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK get account timeout")), 4500)),
927
+ ]));
928
+ if (item && (item.id || item.status)) {
929
+ const toolkit = (item.toolkit || {});
930
+ const slug = String(toolkit.slug || item.toolkit_slug || "unknown").toLowerCase();
931
+ const rawStatus = String(item.status || "INITIALIZING").toUpperCase();
932
+ const status = rawStatus === "ACTIVE"
933
+ ? "active"
934
+ : rawStatus === "EXPIRED" || rawStatus === "FAILED"
935
+ ? "expired"
936
+ : "pending";
937
+ return {
938
+ id: String(item.id || externalAccountId),
939
+ providerSlug: slug,
940
+ status,
941
+ };
942
+ }
943
+ }
944
+ catch {
945
+ // Fall through to REST
946
+ }
947
+ }
394
948
  try {
395
949
  const res = await fetch(`${this.config.composioBaseUrl}/connected_accounts/${encodeURIComponent(externalAccountId)}`, {
396
- headers: this.headers,
950
+ headers: this.getHeaders(composioApiKey),
397
951
  signal: AbortSignal.timeout(5000),
398
952
  });
399
953
  if (!res.ok)
@@ -417,21 +971,11 @@ export class ComposioSdkAdapter {
417
971
  return null;
418
972
  }
419
973
  }
420
- async listLiveConnections(workspaceId) {
421
- if (!this.config.composioApiKey)
974
+ async listLiveConnections(workspaceId, composioApiKey) {
975
+ const apiKey = this.resolveApiKey(composioApiKey);
976
+ if (!apiKey)
422
977
  return [];
423
- try {
424
- const url = new URL(`${this.config.composioBaseUrl}/connected_accounts`);
425
- url.searchParams.set("user_ids", workspaceId);
426
- url.searchParams.set("limit", "50");
427
- const res = await fetch(url.toString(), {
428
- headers: this.headers,
429
- signal: AbortSignal.timeout(5000),
430
- });
431
- if (!res.ok)
432
- return [];
433
- const data = (await res.json());
434
- const items = Array.isArray(data.items) ? data.items : [];
978
+ const mapConnectionItems = (items) => {
435
979
  const rank = {
436
980
  active: 3,
437
981
  pending: 2,
@@ -459,6 +1003,39 @@ export class ComposioSdkAdapter {
459
1003
  }
460
1004
  }
461
1005
  return Array.from(bySlug.values());
1006
+ };
1007
+ // 1. Official @composio/core SDK connectedAccounts.list
1008
+ const sdk = this.getComposioClient(composioApiKey);
1009
+ if (sdk) {
1010
+ try {
1011
+ const listed = (await Promise.race([
1012
+ sdk.connectedAccounts.list({
1013
+ userIds: [workspaceId],
1014
+ limit: 50,
1015
+ }),
1016
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK list accounts timeout")), 4500)),
1017
+ ]));
1018
+ if (Array.isArray(listed?.items)) {
1019
+ return mapConnectionItems(listed.items);
1020
+ }
1021
+ }
1022
+ catch {
1023
+ // Fall through to REST
1024
+ }
1025
+ }
1026
+ try {
1027
+ const url = new URL(`${this.config.composioBaseUrl}/connected_accounts`);
1028
+ url.searchParams.set("user_ids", workspaceId);
1029
+ url.searchParams.set("limit", "50");
1030
+ const res = await fetch(url.toString(), {
1031
+ headers: this.getHeaders(composioApiKey),
1032
+ signal: AbortSignal.timeout(5000),
1033
+ });
1034
+ if (!res.ok)
1035
+ return [];
1036
+ const data = (await res.json());
1037
+ const items = Array.isArray(data.items) ? data.items : [];
1038
+ return mapConnectionItems(items);
462
1039
  }
463
1040
  catch {
464
1041
  return [];
@@ -536,10 +1113,41 @@ export class ComposioSdkAdapter {
536
1113
  },
537
1114
  };
538
1115
  };
539
- const effectiveApiKey = params.composioApiKey || this.config.composioApiKey;
1116
+ const effectiveApiKey = this.resolveApiKey(params.composioApiKey);
540
1117
  if (!effectiveApiKey) {
541
1118
  return buildConnectedAccountFallback();
542
1119
  }
1120
+ // 1. Official @composio/core SDK tool execution
1121
+ const sdk = this.getComposioClient(params.composioApiKey);
1122
+ if (sdk) {
1123
+ try {
1124
+ const sdkExec = await Promise.race([
1125
+ sdk.tools.execute(params.tool.upstreamSlug, {
1126
+ userId: params.workspaceId,
1127
+ arguments: params.input,
1128
+ connectedAccountId: params.connectedAccountId && params.connectedAccountId.startsWith("ca_")
1129
+ ? params.connectedAccountId
1130
+ : undefined,
1131
+ dangerouslySkipVersionCheck: true,
1132
+ }),
1133
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Composio SDK execute timeout")), 20000)),
1134
+ ]);
1135
+ if (sdkExec && !sdkExec.error && sdkExec.successful !== false) {
1136
+ return {
1137
+ provider: "composio_sdk",
1138
+ sdk: "@composio/core",
1139
+ tool: params.tool.slug,
1140
+ upstreamTool: params.tool.upstreamSlug,
1141
+ connectedAccountId: params.connectedAccountId,
1142
+ logId: sdkExec.logId,
1143
+ data: sdkExec.data ?? sdkExec,
1144
+ };
1145
+ }
1146
+ }
1147
+ catch {
1148
+ // Fall through to direct v3 REST execute
1149
+ }
1150
+ }
543
1151
  const body = {
544
1152
  user_id: params.workspaceId,
545
1153
  arguments: params.input,
@@ -550,10 +1158,7 @@ export class ComposioSdkAdapter {
550
1158
  try {
551
1159
  const res = await fetch(`${this.config.composioBaseUrl}/tools/execute/${encodeURIComponent(params.tool.upstreamSlug)}`, {
552
1160
  method: "POST",
553
- headers: {
554
- ...this.headers,
555
- "x-api-key": effectiveApiKey,
556
- },
1161
+ headers: this.getHeaders(params.composioApiKey),
557
1162
  body: JSON.stringify(body),
558
1163
  signal: AbortSignal.timeout(25000),
559
1164
  });