@muxoai/sdk 1.3.0 → 1.4.2
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/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +19 -4
- package/.codex/config.toml +9 -2
- package/ARCHITECTURE.md +16 -6
- package/README.md +69 -24
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.js +1589 -103
- package/dist/composio-catalog.d.ts +234 -0
- package/dist/composio-catalog.js +1894 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +27 -1
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +315 -6
- package/dist/engine.js +1778 -94
- package/dist/mcp-schema.d.ts +286 -0
- package/dist/mcp-schema.js +2559 -0
- package/dist/mcp.d.ts +2 -25
- package/dist/mcp.js +949 -507
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.js +214 -35
- package/dist/oauth.d.ts +32 -0
- package/dist/oauth.js +134 -0
- package/dist/policy.d.ts +190 -0
- package/dist/policy.js +1597 -0
- package/dist/providers/composio-instant.d.ts +4 -0
- package/dist/providers/composio-instant.js +890 -535
- package/dist/providers/composio-sdk.d.ts +126 -5
- package/dist/providers/composio-sdk.js +902 -79
- package/dist/providers/microsoft-governance.d.ts +89 -3
- package/dist/providers/microsoft-governance.js +673 -26
- package/dist/providers/stripe-billing.d.ts +42 -0
- package/dist/providers/stripe-billing.js +244 -5
- package/dist/providers/stripe-projects.d.ts +459 -4
- package/dist/providers/stripe-projects.js +2674 -122
- package/dist/providers/supermemory.d.ts +102 -6
- package/dist/providers/supermemory.js +609 -55
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +705 -7
- package/dist/sdk.js +488 -10
- package/dist/server.js +2646 -348
- package/dist/store.d.ts +138 -6
- package/dist/store.js +932 -58
- package/dist/types.d.ts +312 -4
- package/dist/ui/console-html.d.ts +62 -0
- package/dist/ui/console-html.js +3742 -2228
- package/dist/ui/design-tokens.d.ts +177 -0
- package/dist/ui/design-tokens.js +210 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +5 -4
- package/plugins/muxo/.app.json +1 -1
- package/plugins/muxo/.claude-plugin/plugin.json +3 -3
- package/plugins/muxo/.codex-plugin/plugin.json +19 -10
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
- package/plugins/muxo/.mcp.json +1 -33
- package/plugins/muxo/assets/logo.png +0 -0
- package/plugins/muxo/hooks/session-start.sh +16 -4
- package/plugins/muxo/hooks/user-prompt-submit.sh +14 -1
- package/plugins/muxo/mcp.json +0 -10
- package/plugins/muxo/plugin.json +65 -8
- package/plugins/muxo/skills/muxo/SKILL.md +142 -14
- package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
- package/plugins/muxo/skills/muxo/references/cli.md +9 -3
- package/plugins/muxo/skills/muxo/references/mcp.md +22 -10
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +17 -3
- package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
- package/src/catalogues/stripe-projects-catalog.json +140 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ConnectionRecord, GovernancePolicy, MemoryKnowledgeGraph, ProvisionedResource, TemporalGraphFact } from "./types.js";
|
|
2
|
+
export interface MemoryGraphInput {
|
|
3
|
+
workspace: {
|
|
4
|
+
id: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
name: string;
|
|
7
|
+
tier: string;
|
|
8
|
+
policy?: GovernancePolicy;
|
|
9
|
+
};
|
|
10
|
+
namespace: string;
|
|
11
|
+
query?: string;
|
|
12
|
+
facts: TemporalGraphFact[];
|
|
13
|
+
resources: Array<Pick<ProvisionedResource, "resourceName" | "status" | "providerName" | "serviceSlug">>;
|
|
14
|
+
activeConnections: Array<Pick<ConnectionRecord, "id" | "providerSlug" | "providerName" | "createdAt">>;
|
|
15
|
+
memoriesCount: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds the bi-temporal knowledge graph (nodes, active/historical edges, Mermaid preview)
|
|
19
|
+
* from runtime-agnostic inputs so the Node engine and the Edge Worker render identical graphs.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildMemoryKnowledgeGraph(input: MemoryGraphInput): MemoryKnowledgeGraph;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { formatHumanFriendlyExplanation } from "./policy.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the bi-temporal knowledge graph (nodes, active/historical edges, Mermaid preview)
|
|
4
|
+
* from runtime-agnostic inputs so the Node engine and the Edge Worker render identical graphs.
|
|
5
|
+
*/
|
|
6
|
+
export function buildMemoryKnowledgeGraph(input) {
|
|
7
|
+
const ws = input.workspace;
|
|
8
|
+
const query = input.query;
|
|
9
|
+
const nodeMap = new Map();
|
|
10
|
+
const rootId = `workspace:${ws.slug}`;
|
|
11
|
+
nodeMap.set(rootId, {
|
|
12
|
+
id: rootId,
|
|
13
|
+
kind: "workspace",
|
|
14
|
+
label: ws.name,
|
|
15
|
+
summary: `Workspace ${ws.name} (${ws.slug}, tier=${ws.tier})`,
|
|
16
|
+
status: "active",
|
|
17
|
+
metadata: { workspaceId: ws.id, tier: ws.tier },
|
|
18
|
+
});
|
|
19
|
+
const ensureNode = (id, status, fallbackKind = "entity", summary) => {
|
|
20
|
+
const existing = nodeMap.get(id);
|
|
21
|
+
if (existing) {
|
|
22
|
+
if (status === "active" && existing.status === "historical") {
|
|
23
|
+
existing.status = "active";
|
|
24
|
+
}
|
|
25
|
+
return existing;
|
|
26
|
+
}
|
|
27
|
+
const kind = id.startsWith("workspace:")
|
|
28
|
+
? "workspace"
|
|
29
|
+
: id.startsWith("resource:")
|
|
30
|
+
? "resource"
|
|
31
|
+
: id.startsWith("connection:")
|
|
32
|
+
? "connection"
|
|
33
|
+
: id.startsWith("tool:")
|
|
34
|
+
? "tool"
|
|
35
|
+
: id.startsWith("guardrail:")
|
|
36
|
+
? "guardrail"
|
|
37
|
+
: fallbackKind;
|
|
38
|
+
const label = id.includes(":")
|
|
39
|
+
? id.split(":").slice(1).join(":")
|
|
40
|
+
: id.length > 36
|
|
41
|
+
? `${id.slice(0, 33)}...`
|
|
42
|
+
: id;
|
|
43
|
+
const created = {
|
|
44
|
+
id,
|
|
45
|
+
kind,
|
|
46
|
+
label,
|
|
47
|
+
summary: summary || label,
|
|
48
|
+
status,
|
|
49
|
+
};
|
|
50
|
+
nodeMap.set(id, created);
|
|
51
|
+
return created;
|
|
52
|
+
};
|
|
53
|
+
const edges = input.facts.map((fact) => {
|
|
54
|
+
const status = fact.validTo === null ? "active" : "historical";
|
|
55
|
+
const objKind = fact.sourceKind === "preference"
|
|
56
|
+
? "preference"
|
|
57
|
+
: fact.sourceKind === "policy_decision"
|
|
58
|
+
? "guardrail"
|
|
59
|
+
: fact.sourceKind === "resource_topology"
|
|
60
|
+
? "resource"
|
|
61
|
+
: "entity";
|
|
62
|
+
ensureNode(fact.subject, status, "entity", fact.subject);
|
|
63
|
+
ensureNode(fact.object, status, objKind, fact.summary);
|
|
64
|
+
return {
|
|
65
|
+
id: fact.id,
|
|
66
|
+
source: fact.subject,
|
|
67
|
+
target: fact.object,
|
|
68
|
+
subject: fact.subject,
|
|
69
|
+
object: fact.object,
|
|
70
|
+
predicate: fact.predicate,
|
|
71
|
+
summary: fact.summary,
|
|
72
|
+
confidence: fact.confidence,
|
|
73
|
+
status,
|
|
74
|
+
validFrom: fact.validFrom,
|
|
75
|
+
validTo: fact.validTo,
|
|
76
|
+
supersededById: fact.supersededById ?? null,
|
|
77
|
+
sourceKind: fact.sourceKind,
|
|
78
|
+
sourceReferenceId: fact.sourceReferenceId ?? null,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
for (const res of input.resources) {
|
|
82
|
+
ensureNode(`resource:${res.resourceName}`, res.status === "active" ? "active" : "historical", "resource", `${res.resourceName} (${res.providerName} / ${res.serviceSlug})`);
|
|
83
|
+
}
|
|
84
|
+
for (const conn of input.activeConnections) {
|
|
85
|
+
const connId = `connection:${conn.providerSlug}`;
|
|
86
|
+
ensureNode(connId, "active", "connection", `Connected OAuth App: ${conn.providerName} (${conn.providerSlug})`);
|
|
87
|
+
if (!edges.some((e) => e.source === rootId && e.target === connId)) {
|
|
88
|
+
edges.push({
|
|
89
|
+
id: `edge_conn_${conn.id}`,
|
|
90
|
+
source: rootId,
|
|
91
|
+
target: connId,
|
|
92
|
+
predicate: "CONNECTED_TO",
|
|
93
|
+
summary: `${ws.name} has active OAuth connection to ${conn.providerName}`,
|
|
94
|
+
confidence: 1.0,
|
|
95
|
+
status: "active",
|
|
96
|
+
validFrom: conn.createdAt,
|
|
97
|
+
validTo: null,
|
|
98
|
+
supersededById: null,
|
|
99
|
+
sourceKind: "system",
|
|
100
|
+
sourceReferenceId: conn.id,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const activeEdges = edges.filter((e) => e.status === "active");
|
|
105
|
+
const historicalEdges = edges.filter((e) => e.status === "historical");
|
|
106
|
+
const nodes = Array.from(nodeMap.values());
|
|
107
|
+
const safeMermaidId = (raw, idx) => `N${idx}_${raw.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 14)}`;
|
|
108
|
+
const nodeIndex = new Map();
|
|
109
|
+
nodes.slice(0, 24).forEach((n, idx) => {
|
|
110
|
+
nodeIndex.set(n.id, safeMermaidId(n.label, idx));
|
|
111
|
+
});
|
|
112
|
+
const mermaidLines = ["flowchart LR"];
|
|
113
|
+
for (const n of nodes.slice(0, 24)) {
|
|
114
|
+
const mId = nodeIndex.get(n.id);
|
|
115
|
+
const cleanLabel = n.label.replace(/"/g, "'").slice(0, 22);
|
|
116
|
+
mermaidLines.push(` ${mId}["${cleanLabel}"]`);
|
|
117
|
+
}
|
|
118
|
+
for (const e of edges.slice(0, 24)) {
|
|
119
|
+
const srcId = nodeIndex.get(e.source);
|
|
120
|
+
const tgtId = nodeIndex.get(e.target);
|
|
121
|
+
if (!srcId || !tgtId)
|
|
122
|
+
continue;
|
|
123
|
+
const pred = e.predicate.replace(/"/g, "'").slice(0, 18);
|
|
124
|
+
if (e.status === "active") {
|
|
125
|
+
mermaidLines.push(` ${srcId} -->|${pred}| ${tgtId}`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
mermaidLines.push(` ${srcId} -.->|RETIRED: ${pred}| ${tgtId}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const headline = `Bi-Temporal Knowledge Graph (${ws.name}): ${activeEdges.length} active relationship${activeEdges.length === 1 ? "" : "s"}, ${historicalEdges.length} superseded historical relationship${historicalEdges.length === 1 ? "" : "s"} across ${nodes.length} node${nodes.length === 1 ? "" : "s"}.`;
|
|
132
|
+
const activePreview = activeEdges
|
|
133
|
+
.slice(0, 4)
|
|
134
|
+
.map((e) => `${e.source} --[${e.predicate}]--> ${e.target}`)
|
|
135
|
+
.join("; ");
|
|
136
|
+
const nonTechnical = [
|
|
137
|
+
headline,
|
|
138
|
+
activeEdges.length > 0
|
|
139
|
+
? `Current facts: ${activeEdges.slice(0, 3).map((e) => e.summary).join(" · ")}.`
|
|
140
|
+
: "No active facts recorded yet.",
|
|
141
|
+
historicalEdges.length > 0
|
|
142
|
+
? `Time-travel lineage: ${historicalEdges.length} older fact${historicalEdges.length === 1 ? " was" : "s were"} automatically retired when newer workspace settings or facts replaced them.`
|
|
143
|
+
: "No superseded historical facts yet.",
|
|
144
|
+
].join(" ");
|
|
145
|
+
const technical = `workspace=${ws.id} | namespace=${input.namespace} | nodes=${nodes.length} | activeEdges=${activeEdges.length} | historicalEdges=${historicalEdges.length} | memories=${input.memoriesCount} | activePreview=[${activePreview || "none"}]`;
|
|
146
|
+
const explanation = formatHumanFriendlyExplanation({
|
|
147
|
+
headline,
|
|
148
|
+
nonTechnical,
|
|
149
|
+
technical,
|
|
150
|
+
policy: ws.policy,
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
workspaceId: ws.id,
|
|
154
|
+
workspaceSlug: ws.slug,
|
|
155
|
+
namespace: input.namespace,
|
|
156
|
+
generatedAt: new Date().toISOString(),
|
|
157
|
+
query: query ?? null,
|
|
158
|
+
nodes,
|
|
159
|
+
edges,
|
|
160
|
+
activeEdges,
|
|
161
|
+
historicalEdges,
|
|
162
|
+
stats: {
|
|
163
|
+
nodesCount: nodes.length,
|
|
164
|
+
activeEdgesCount: activeEdges.length,
|
|
165
|
+
historicalEdgesCount: historicalEdges.length,
|
|
166
|
+
memoriesCount: input.memoriesCount,
|
|
167
|
+
resourcesCount: input.resources.length,
|
|
168
|
+
connectionsCount: input.activeConnections.length,
|
|
169
|
+
},
|
|
170
|
+
mermaid: mermaidLines.join("\n"),
|
|
171
|
+
explanation,
|
|
172
|
+
};
|
|
173
|
+
}
|
package/dist/nl.js
CHANGED
|
@@ -80,23 +80,39 @@ export function extractParametersFromNaturalLanguage(prompt, tool, explicitInput
|
|
|
80
80
|
input.numResults = extractNumberAfterKeyword(text, ["results", "limit", "top"], 5);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
else if (slug === "instant.code.execute") {
|
|
84
|
-
if (!input.language) {
|
|
83
|
+
else if (slug === "instant.code.execute" || slug === "instant.composio.workbench") {
|
|
84
|
+
if (!input.language && slug === "instant.code.execute") {
|
|
85
85
|
input.language = /\b(javascript|js|node|typescript|ts)\b/i.test(text)
|
|
86
86
|
? "javascript"
|
|
87
87
|
: "python";
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
const codeBlock = text.match(/```(?:\w+)?\n([\s\S]*?)```/);
|
|
90
|
+
const inlineCode = text.match(/`([^`]+)`/);
|
|
91
|
+
const extractedCode = codeBlock
|
|
92
|
+
? codeBlock[1].trim()
|
|
93
|
+
: inlineCode
|
|
94
|
+
? inlineCode[1].trim()
|
|
95
|
+
: text;
|
|
96
|
+
if (slug === "instant.composio.workbench") {
|
|
97
|
+
if (!input.code_to_execute) {
|
|
98
|
+
input.code_to_execute = String(input.code || extractedCode);
|
|
93
99
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (inlineCode)
|
|
97
|
-
input.code = inlineCode[1].trim();
|
|
100
|
+
if (!input.thought) {
|
|
101
|
+
input.thought = `Natural language workbench execution: ${text.slice(0, 80)}`;
|
|
98
102
|
}
|
|
99
103
|
}
|
|
104
|
+
else if (!input.code) {
|
|
105
|
+
input.code = extractedCode;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (slug === "instant.github.repo_intel") {
|
|
109
|
+
const repoMatch = text.match(/\b([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)\b/);
|
|
110
|
+
if (repoMatch && !repoMatch[0].includes(".ai") && !repoMatch[0].includes(".com")) {
|
|
111
|
+
if (!input.owner)
|
|
112
|
+
input.owner = repoMatch[1];
|
|
113
|
+
if (!input.repo)
|
|
114
|
+
input.repo = repoMatch[2];
|
|
115
|
+
}
|
|
100
116
|
}
|
|
101
117
|
else if (slug.startsWith("resource.")) {
|
|
102
118
|
if (!input.resource_name && !input.name) {
|
|
@@ -207,6 +223,93 @@ export function extractParametersFromNaturalLanguage(prompt, tool, explicitInput
|
|
|
207
223
|
input.limit = extractNumberAfterKeyword(text, ["top", "stories", "items", "limit", "posts"], 5);
|
|
208
224
|
}
|
|
209
225
|
}
|
|
226
|
+
else if (slug === "app.jira.create_issue") {
|
|
227
|
+
if (!input.project_key) {
|
|
228
|
+
const projMatch = text.match(/\bproject\s+([A-Z][A-Z0-9]{1,9})\b|\b([A-Z]{2,8})-\d+\b/);
|
|
229
|
+
input.project_key = projMatch ? projMatch[1] || projMatch[2] : "ENG";
|
|
230
|
+
}
|
|
231
|
+
if (!input.summary) {
|
|
232
|
+
const sumMatch = text.match(/\b(?:summary|titled|title)\s*[:=]?\s*["']?([^"'\n]+)["']?/i);
|
|
233
|
+
input.summary = sumMatch ? sumMatch[1].trim() : text.slice(0, 80);
|
|
234
|
+
}
|
|
235
|
+
if (!input.issue_type)
|
|
236
|
+
input.issue_type = "Task";
|
|
237
|
+
}
|
|
238
|
+
else if (slug === "app.linear.create_issue") {
|
|
239
|
+
if (!input.team_id) {
|
|
240
|
+
const teamMatch = text.match(/\bteam\s+["']?([a-zA-Z0-9_-]+)["']?/i);
|
|
241
|
+
input.team_id = teamMatch ? teamMatch[1] : "ENG";
|
|
242
|
+
}
|
|
243
|
+
if (!input.title) {
|
|
244
|
+
const titleMatch = text.match(/\b(?:titled|title)\s*[:=]?\s*["']?([^"'\n]+)["']?/i);
|
|
245
|
+
input.title = titleMatch ? titleMatch[1].trim() : text.slice(0, 80);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else if (slug === "app.googlecalendar.list_events") {
|
|
249
|
+
if (!input.calendarId)
|
|
250
|
+
input.calendarId = "primary";
|
|
251
|
+
if (input.maxResults === undefined) {
|
|
252
|
+
input.maxResults = extractNumberAfterKeyword(text, ["events", "meetings", "limit", "next"], 10);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else if (slug === "app.hubspot.search_contacts") {
|
|
256
|
+
if (!input.query) {
|
|
257
|
+
const cleaned = text.replace(/^(?:search\s+hubspot\s+(?:contacts\s+)?(?:for)?|find\s+contact)\s*/i, "").trim();
|
|
258
|
+
input.query = cleaned || text;
|
|
259
|
+
}
|
|
260
|
+
if (input.limit === undefined)
|
|
261
|
+
input.limit = 10;
|
|
262
|
+
}
|
|
263
|
+
else if (slug === "app.discord.send_message") {
|
|
264
|
+
if (!input.channel_id) {
|
|
265
|
+
const chMatch = text.match(/#([a-zA-Z0-9_-]+)|\bchannel\s+([a-zA-Z0-9_-]+)/i);
|
|
266
|
+
input.channel_id = chMatch ? chMatch[1] || chMatch[2] : "general";
|
|
267
|
+
}
|
|
268
|
+
if (!input.content) {
|
|
269
|
+
const msgMatch = text.match(/\b(?:saying|message|text|content)\s*[:=]?\s*["']?([\s\S]+?)["']?$/i);
|
|
270
|
+
input.content = msgMatch ? msgMatch[1].trim() : text;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else if (slug === "app.googledrive.search_files") {
|
|
274
|
+
if (!input.query) {
|
|
275
|
+
const cleaned = text.replace(/^(?:search\s+(?:google\s+)?drive\s+(?:files\s+)?(?:for)?)\s*/i, "").trim();
|
|
276
|
+
input.query = cleaned || text;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (slug === "app.airtable.list_records") {
|
|
280
|
+
if (!input.base_id) {
|
|
281
|
+
const baseMatch = text.match(/\b(app[a-zA-Z0-9]{10,})\b/);
|
|
282
|
+
input.base_id = baseMatch ? baseMatch[1] : "appDefaultBase";
|
|
283
|
+
}
|
|
284
|
+
if (!input.table_name) {
|
|
285
|
+
const tblMatch = text.match(/\btable\s+["']?([a-zA-Z0-9_-]+)["']?/i);
|
|
286
|
+
input.table_name = tblMatch ? tblMatch[1] : "Tasks";
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (slug === "app.salesforce.query_records" || slug === "app.salesforce.soql_query") {
|
|
290
|
+
if (!input.query) {
|
|
291
|
+
const soqlMatch = text.match(/\b(SELECT\s+[\s\S]+)$/i);
|
|
292
|
+
input.query = soqlMatch ? soqlMatch[1].trim() : "SELECT Id, Name, Email FROM Contact LIMIT 10";
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else if (slug === "app.pagerduty.create_incident") {
|
|
296
|
+
if (!input.title)
|
|
297
|
+
input.title = text.slice(0, 80);
|
|
298
|
+
if (!input.service_id)
|
|
299
|
+
input.service_id = "PDEFAULT";
|
|
300
|
+
}
|
|
301
|
+
else if (slug === "billing.stripe.refund") {
|
|
302
|
+
if (!input.paymentIntentId) {
|
|
303
|
+
const piMatch = text.match(/\b(pi_[a-zA-Z0-9_]+|ch_[a-zA-Z0-9_]+)\b/);
|
|
304
|
+
if (piMatch)
|
|
305
|
+
input.paymentIntentId = piMatch[1];
|
|
306
|
+
}
|
|
307
|
+
if (input.amountCents === undefined) {
|
|
308
|
+
const dollarMatch = text.match(/\$(\d+(?:\.\d{1,2})?)/);
|
|
309
|
+
if (dollarMatch)
|
|
310
|
+
input.amountCents = Math.round(parseFloat(dollarMatch[1]) * 100);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
210
313
|
const schemaReq = Array.isArray(tool.inputSchema?.required)
|
|
211
314
|
? tool.inputSchema.required
|
|
212
315
|
: [];
|
|
@@ -235,13 +338,19 @@ function scoreToolForPrompt(prompt, tool) {
|
|
|
235
338
|
if (slug === "instant.web.scrape")
|
|
236
339
|
score += 110;
|
|
237
340
|
}
|
|
238
|
-
|
|
341
|
+
const isExplicitWebSearch = /\b(search\s+(?:the\s+)?web|web\s+search|search\s+online|search\s+(?:for|about)|look\s+up|find\s+(?:news|info|information|articles)|exa|google)\b/i.test(q);
|
|
342
|
+
const hasProvisionVerb = /\b(provision|create|deploy|spin\s+up|setup|set\s+up|add\s+resource|new\s+db|new\s+database|database|db)\b/i.test(q);
|
|
343
|
+
if (isExplicitWebSearch) {
|
|
239
344
|
if (slug === "instant.web.search")
|
|
240
|
-
score +=
|
|
345
|
+
score += 155;
|
|
241
346
|
}
|
|
242
|
-
if (/\b(run\s+python|execute\s+code|run\s+script|sandbox|calculate|e2b)\b/i.test(q)) {
|
|
243
|
-
if (slug === "instant.code.execute")
|
|
244
|
-
score +=
|
|
347
|
+
if (/\b(run\s+python|execute\s+code|run\s+script|sandbox|workbench|calculate|e2b)\b/i.test(q)) {
|
|
348
|
+
if (slug === "instant.code.execute" || slug === "instant.composio.workbench")
|
|
349
|
+
score += 125;
|
|
350
|
+
}
|
|
351
|
+
if (/\b(repo\s+intel|github\s+repo|inspect\s+repo|repository\s+intel|stars\s+and\s+forks)\b/i.test(q)) {
|
|
352
|
+
if (slug === "instant.github.repo_intel")
|
|
353
|
+
score += 135;
|
|
245
354
|
}
|
|
246
355
|
if (/\b(top\s*up|add\s+credits|buy\s+credits|refill\s+credits|more\s+credits|\d+\s+credits)\b/i.test(q)) {
|
|
247
356
|
if (slug === "billing.credits.topup")
|
|
@@ -255,27 +364,53 @@ function scoreToolForPrompt(prompt, tool) {
|
|
|
255
364
|
if (slug === "billing.checkout.create")
|
|
256
365
|
score += 125;
|
|
257
366
|
}
|
|
258
|
-
if (/\b(
|
|
259
|
-
if (slug.
|
|
260
|
-
score +=
|
|
261
|
-
if (slug.includes("database"))
|
|
262
|
-
score += 15;
|
|
263
|
-
}
|
|
264
|
-
if (/\b(neon|postgres|postgresql|pg\s+database)\b/i.test(q)) {
|
|
265
|
-
if (slug.startsWith("resource.neon."))
|
|
266
|
-
score += 120;
|
|
267
|
-
}
|
|
268
|
-
if (/\b(upstash|redis|cache\s+store|kv\s+store)\b/i.test(q)) {
|
|
269
|
-
if (slug.startsWith("resource.upstash."))
|
|
270
|
-
score += 120;
|
|
271
|
-
}
|
|
272
|
-
if (/\b(supabase)\b/i.test(q)) {
|
|
273
|
-
if (slug.startsWith("resource.supabase."))
|
|
274
|
-
score += 120;
|
|
367
|
+
if (/\b(refund|chargeback|pi_[a-z0-9_]+)\b/i.test(q)) {
|
|
368
|
+
if (slug === "billing.stripe.refund" || slug === "app.shopify.create_refund")
|
|
369
|
+
score += 130;
|
|
275
370
|
}
|
|
276
|
-
if (
|
|
277
|
-
if (
|
|
278
|
-
|
|
371
|
+
if (!isExplicitWebSearch || hasProvisionVerb) {
|
|
372
|
+
if (/\b(turso|libsql|sqlite\s+database|sqlite\s+db)\b/i.test(q)) {
|
|
373
|
+
if (slug.startsWith("resource.turso."))
|
|
374
|
+
score += 120;
|
|
375
|
+
if (slug.includes("database"))
|
|
376
|
+
score += 15;
|
|
377
|
+
}
|
|
378
|
+
if (/\b(neon|postgres|postgresql|pg\s+database)\b/i.test(q)) {
|
|
379
|
+
if (slug.startsWith("resource.neon."))
|
|
380
|
+
score += 120;
|
|
381
|
+
}
|
|
382
|
+
if (/\b(upstash|redis|cache\s+store|kv\s+store)\b/i.test(q)) {
|
|
383
|
+
if (slug.startsWith("resource.upstash."))
|
|
384
|
+
score += 120;
|
|
385
|
+
}
|
|
386
|
+
if (/\b(supabase)\b/i.test(q)) {
|
|
387
|
+
if (slug.startsWith("resource.supabase."))
|
|
388
|
+
score += 120;
|
|
389
|
+
}
|
|
390
|
+
if (/\b(mongodb|mongo)\b/i.test(q)) {
|
|
391
|
+
if (slug.startsWith("resource.mongodb."))
|
|
392
|
+
score += 120;
|
|
393
|
+
}
|
|
394
|
+
if (/\b(cloudflare\s+worker|d1\s+database|r2\s+storage)\b/i.test(q)) {
|
|
395
|
+
if (slug.startsWith("resource.cloudflare."))
|
|
396
|
+
score += 120;
|
|
397
|
+
}
|
|
398
|
+
if (/\b(vercel\s+project|deploy\s+vercel)\b/i.test(q)) {
|
|
399
|
+
if (slug.startsWith("resource.vercel."))
|
|
400
|
+
score += 120;
|
|
401
|
+
}
|
|
402
|
+
if (/\b(resend\s+email|transactional\s+email\s+api)\b/i.test(q)) {
|
|
403
|
+
if (slug.startsWith("resource.resend."))
|
|
404
|
+
score += 120;
|
|
405
|
+
}
|
|
406
|
+
if (/\b(clerk\s+auth)\b/i.test(q)) {
|
|
407
|
+
if (slug.startsWith("resource.clerk."))
|
|
408
|
+
score += 120;
|
|
409
|
+
}
|
|
410
|
+
if (/\b(workos|enterprise\s+sso|scim)\b/i.test(q)) {
|
|
411
|
+
if (slug.startsWith("resource.workos."))
|
|
412
|
+
score += 120;
|
|
413
|
+
}
|
|
279
414
|
}
|
|
280
415
|
if (/\b(hacker\s*news|hn\s+stories|top\s+stories)\b/i.test(q)) {
|
|
281
416
|
if (slug === "instant.hackernews.top_stories")
|
|
@@ -305,6 +440,10 @@ function scoreToolForPrompt(prompt, tool) {
|
|
|
305
440
|
if (slug === "app.github.list_pull_requests" || slug === "app.github.list_issues")
|
|
306
441
|
score += 110;
|
|
307
442
|
}
|
|
443
|
+
if (/\b(list\s+github\s+repos|github\s+repositories|my\s+repos)\b/i.test(q)) {
|
|
444
|
+
if (slug === "app.github.list_repos")
|
|
445
|
+
score += 120;
|
|
446
|
+
}
|
|
308
447
|
if (/\b(notion\s+page|search\s+notion)\b/i.test(q)) {
|
|
309
448
|
if (slug === "app.notion.search_pages")
|
|
310
449
|
score += 115;
|
|
@@ -313,6 +452,46 @@ function scoreToolForPrompt(prompt, tool) {
|
|
|
313
452
|
if (slug === "app.linear.create_issue")
|
|
314
453
|
score += 115;
|
|
315
454
|
}
|
|
455
|
+
if (/\b(jira\s+issue|create\s+jira|atlassian|jql)\b/i.test(q)) {
|
|
456
|
+
if (slug.startsWith("app.jira."))
|
|
457
|
+
score += 120;
|
|
458
|
+
}
|
|
459
|
+
if (/\b(hubspot|crm\s+contacts?|search\s+contacts?)\b/i.test(q)) {
|
|
460
|
+
if (slug.startsWith("app.hubspot."))
|
|
461
|
+
score += 120;
|
|
462
|
+
}
|
|
463
|
+
if (/\b(discord\s+message|send\s+discord|post\s+to\s+discord)\b/i.test(q)) {
|
|
464
|
+
if (slug.startsWith("app.discord."))
|
|
465
|
+
score += 120;
|
|
466
|
+
}
|
|
467
|
+
if (/\b(google\s+drive|gdrive|drive\s+files?)\b/i.test(q)) {
|
|
468
|
+
if (slug.startsWith("app.googledrive."))
|
|
469
|
+
score += 120;
|
|
470
|
+
}
|
|
471
|
+
if (/\b(airtable|base\s+records?)\b/i.test(q)) {
|
|
472
|
+
if (slug.startsWith("app.airtable."))
|
|
473
|
+
score += 120;
|
|
474
|
+
}
|
|
475
|
+
if (/\b(salesforce|soql|crm\s+records?)\b/i.test(q)) {
|
|
476
|
+
if (slug.startsWith("app.salesforce."))
|
|
477
|
+
score += 120;
|
|
478
|
+
}
|
|
479
|
+
if (/\b(zendesk|support\s+tickets?)\b/i.test(q)) {
|
|
480
|
+
if (slug.startsWith("app.zendesk."))
|
|
481
|
+
score += 120;
|
|
482
|
+
}
|
|
483
|
+
if (/\b(shopify|store\s+orders?)\b/i.test(q)) {
|
|
484
|
+
if (slug.startsWith("app.shopify."))
|
|
485
|
+
score += 120;
|
|
486
|
+
}
|
|
487
|
+
if (/\b(pagerduty|on-call|trigger\s+incident)\b/i.test(q)) {
|
|
488
|
+
if (slug.startsWith("app.pagerduty."))
|
|
489
|
+
score += 120;
|
|
490
|
+
}
|
|
491
|
+
if (/\b(sentry|error\s+issues?|stack\s+traces?)\b/i.test(q)) {
|
|
492
|
+
if (slug.startsWith("app.sentry."))
|
|
493
|
+
score += 120;
|
|
494
|
+
}
|
|
316
495
|
if (/\b(google\s+calendar|schedule\s+meeting|list\s+events|calendar\s+events)\b/i.test(q)) {
|
|
317
496
|
if (slug.includes("calendar") || slug.includes("gcal"))
|
|
318
497
|
score += 110;
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Shared OAuth 2.1 (PKCE + consent) helpers for the Node server and the edge worker. */
|
|
2
|
+
export declare const DEFAULT_OAUTH_SCOPE = "mcp:tools muxo:mcp muxo:read muxo:execute muxo:provision";
|
|
3
|
+
export declare const PKCE_VERIFIER_PATTERN: RegExp;
|
|
4
|
+
export declare const OAUTH_CODE_TTL_MS: number;
|
|
5
|
+
/** API keys minted through OAuth do not expire; this is the advisory lifetime clients see. */
|
|
6
|
+
export declare const OAUTH_ACCESS_TOKEN_TTL_SECONDS = 2592000;
|
|
7
|
+
export declare const LOOPBACK_CLIENT_NAME = "An app on this computer";
|
|
8
|
+
export interface AuthorizeRequest {
|
|
9
|
+
clientId: string;
|
|
10
|
+
clientName: string;
|
|
11
|
+
redirectUri: string;
|
|
12
|
+
state: string;
|
|
13
|
+
codeChallenge: string | null;
|
|
14
|
+
codeChallengeMethod: "S256" | "plain" | null;
|
|
15
|
+
scope: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function base64UrlEncode(bytes: Uint8Array): string;
|
|
18
|
+
export declare function pkceS256(verifier: string): Promise<string>;
|
|
19
|
+
export declare function timingSafeEqualText(a: string, b: string): boolean;
|
|
20
|
+
/** Checks a token-endpoint code_verifier against the challenge stored with the authorization code. */
|
|
21
|
+
export declare function verifyPkce(verifier: string, challenge: string, method: "S256" | "plain" | null): Promise<"ok" | "missing" | "mismatch">;
|
|
22
|
+
export declare function oauthClientName(redirect: URL, clientId: string): string;
|
|
23
|
+
/** Name of the workspace API key minted for an OAuth connection, as listed in the console. */
|
|
24
|
+
export declare function oauthConnectionKeyName(clientName: string): string;
|
|
25
|
+
/** Origin shown on the consent screen; app-scheme redirects have no origin, so show the scheme. */
|
|
26
|
+
export declare function oauthRedirectOrigin(redirectUri: string): string;
|
|
27
|
+
export declare function parseAuthorizeRequest(params: URLSearchParams): AuthorizeRequest | {
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
/** Parses a token-endpoint body (form or JSON) and HTTP Basic client credentials into one parameter set. */
|
|
31
|
+
export declare function parseOAuthFormBody(rawBody: string, contentType: string, authorizationHeader: string): URLSearchParams;
|
|
32
|
+
export declare function oauthSecretToken(prefix: string): string;
|
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { MCP_TOOLS_OAUTH_SCOPE } from "./mcp-schema.js";
|
|
2
|
+
/** Shared OAuth 2.1 (PKCE + consent) helpers for the Node server and the edge worker. */
|
|
3
|
+
export const DEFAULT_OAUTH_SCOPE = `${MCP_TOOLS_OAUTH_SCOPE} muxo:mcp muxo:read muxo:execute muxo:provision`;
|
|
4
|
+
export const PKCE_VERIFIER_PATTERN = /^[A-Za-z0-9._~-]{43,128}$/;
|
|
5
|
+
export const OAUTH_CODE_TTL_MS = 10 * 60 * 1000;
|
|
6
|
+
/** API keys minted through OAuth do not expire; this is the advisory lifetime clients see. */
|
|
7
|
+
export const OAUTH_ACCESS_TOKEN_TTL_SECONDS = 2_592_000;
|
|
8
|
+
export const LOOPBACK_CLIENT_NAME = "An app on this computer";
|
|
9
|
+
const BLOCKED_REDIRECT_SCHEMES = new Set(["javascript:", "data:", "file:", "vbscript:", "about:", "blob:", "ftp:"]);
|
|
10
|
+
export function base64UrlEncode(bytes) {
|
|
11
|
+
let binary = "";
|
|
12
|
+
for (const b of bytes)
|
|
13
|
+
binary += String.fromCharCode(b);
|
|
14
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
15
|
+
}
|
|
16
|
+
export async function pkceS256(verifier) {
|
|
17
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
18
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
19
|
+
}
|
|
20
|
+
export function timingSafeEqualText(a, b) {
|
|
21
|
+
if (a.length !== b.length)
|
|
22
|
+
return false;
|
|
23
|
+
let diff = 0;
|
|
24
|
+
for (let i = 0; i < a.length; i++)
|
|
25
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
26
|
+
return diff === 0;
|
|
27
|
+
}
|
|
28
|
+
/** Checks a token-endpoint code_verifier against the challenge stored with the authorization code. */
|
|
29
|
+
export async function verifyPkce(verifier, challenge, method) {
|
|
30
|
+
if (!PKCE_VERIFIER_PATTERN.test(verifier))
|
|
31
|
+
return "missing";
|
|
32
|
+
const computed = method === "S256" ? await pkceS256(verifier) : verifier;
|
|
33
|
+
return timingSafeEqualText(computed, challenge) ? "ok" : "mismatch";
|
|
34
|
+
}
|
|
35
|
+
export function oauthClientName(redirect, clientId) {
|
|
36
|
+
const host = redirect.hostname.toLowerCase();
|
|
37
|
+
if (host === "chatgpt.com" || host.endsWith(".chatgpt.com") || host.endsWith(".openai.com"))
|
|
38
|
+
return "ChatGPT";
|
|
39
|
+
if (host === "claude.ai" || host.endsWith(".claude.ai") || host.endsWith(".anthropic.com"))
|
|
40
|
+
return "Claude";
|
|
41
|
+
if (host === "127.0.0.1" || host === "localhost" || host === "[::1]")
|
|
42
|
+
return LOOPBACK_CLIENT_NAME;
|
|
43
|
+
if (redirect.protocol !== "https:" && redirect.protocol !== "http:")
|
|
44
|
+
return redirect.protocol.replace(/:$/, "");
|
|
45
|
+
return clientId.length <= 64 && /^[\w.-]+$/.test(clientId) && !clientId.startsWith("muxo_dcr_") ? clientId : host;
|
|
46
|
+
}
|
|
47
|
+
/** Name of the workspace API key minted for an OAuth connection, as listed in the console. */
|
|
48
|
+
export function oauthConnectionKeyName(clientName) {
|
|
49
|
+
return `${clientName === LOOPBACK_CLIENT_NAME ? "Local app" : clientName} connection`;
|
|
50
|
+
}
|
|
51
|
+
/** Origin shown on the consent screen; app-scheme redirects have no origin, so show the scheme. */
|
|
52
|
+
export function oauthRedirectOrigin(redirectUri) {
|
|
53
|
+
const target = new URL(redirectUri);
|
|
54
|
+
return target.origin === "null" ? target.protocol : target.origin;
|
|
55
|
+
}
|
|
56
|
+
export function parseAuthorizeRequest(params) {
|
|
57
|
+
const responseType = params.get("response_type");
|
|
58
|
+
if (responseType && responseType !== "code")
|
|
59
|
+
return { error: "Only response_type=code is supported." };
|
|
60
|
+
const redirectUri = (params.get("redirect_uri") || "").trim();
|
|
61
|
+
if (!redirectUri || redirectUri.length > 2048)
|
|
62
|
+
return { error: "A valid redirect_uri is required." };
|
|
63
|
+
let redirect;
|
|
64
|
+
try {
|
|
65
|
+
redirect = new URL(redirectUri);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return { error: "redirect_uri is not a valid URL." };
|
|
69
|
+
}
|
|
70
|
+
const loopback = ["127.0.0.1", "localhost", "[::1]"].includes(redirect.hostname);
|
|
71
|
+
if (redirect.hash ||
|
|
72
|
+
BLOCKED_REDIRECT_SCHEMES.has(redirect.protocol) ||
|
|
73
|
+
(redirect.protocol === "http:" && !loopback)) {
|
|
74
|
+
return { error: "redirect_uri must use https, a loopback http address, or an app scheme." };
|
|
75
|
+
}
|
|
76
|
+
const clientId = (params.get("client_id") || "muxo-mcp-client").trim().slice(0, 256);
|
|
77
|
+
const codeChallenge = (params.get("code_challenge") || "").trim() || null;
|
|
78
|
+
const rawMethod = (params.get("code_challenge_method") || "").trim();
|
|
79
|
+
if (codeChallenge && !PKCE_VERIFIER_PATTERN.test(codeChallenge)) {
|
|
80
|
+
return { error: "code_challenge is malformed." };
|
|
81
|
+
}
|
|
82
|
+
if (rawMethod && rawMethod !== "S256" && rawMethod !== "plain") {
|
|
83
|
+
return { error: "code_challenge_method must be S256 or plain." };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
clientId,
|
|
87
|
+
clientName: oauthClientName(redirect, clientId),
|
|
88
|
+
redirectUri,
|
|
89
|
+
state: (params.get("state") || "").slice(0, 1024),
|
|
90
|
+
codeChallenge,
|
|
91
|
+
codeChallengeMethod: codeChallenge ? (rawMethod === "S256" ? "S256" : "plain") : null,
|
|
92
|
+
scope: (params.get("scope") || DEFAULT_OAUTH_SCOPE).slice(0, 512),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Parses a token-endpoint body (form or JSON) and HTTP Basic client credentials into one parameter set. */
|
|
96
|
+
export function parseOAuthFormBody(rawBody, contentType, authorizationHeader) {
|
|
97
|
+
const params = new URLSearchParams();
|
|
98
|
+
if (contentType.includes("application/json")) {
|
|
99
|
+
let body = {};
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(rawBody || "{}");
|
|
102
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
103
|
+
body = parsed;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
body = {};
|
|
107
|
+
}
|
|
108
|
+
for (const [k, v] of Object.entries(body))
|
|
109
|
+
if (typeof v === "string")
|
|
110
|
+
params.set(k, v);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
for (const [k, v] of new URLSearchParams(rawBody))
|
|
114
|
+
params.set(k, v);
|
|
115
|
+
}
|
|
116
|
+
const basic = authorizationHeader.match(/^basic\s+(.+)$/i);
|
|
117
|
+
if (basic && !params.get("client_id")) {
|
|
118
|
+
try {
|
|
119
|
+
const decoded = atob(basic[1].trim());
|
|
120
|
+
params.set("client_id", decodeURIComponent(decoded.split(":")[0] || ""));
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Public clients do not need client credentials, so a malformed header is ignored.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return params;
|
|
127
|
+
}
|
|
128
|
+
export function oauthSecretToken(prefix) {
|
|
129
|
+
const bytes = new Uint8Array(24);
|
|
130
|
+
crypto.getRandomValues(bytes);
|
|
131
|
+
return `${prefix}_${Array.from(bytes)
|
|
132
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
133
|
+
.join("")}`;
|
|
134
|
+
}
|