@soldy_ai/mcp 0.1.6

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 (47) hide show
  1. package/README.md +42 -0
  2. package/dist/client.d.ts +103 -0
  3. package/dist/client.js +165 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/errors.d.ts +5 -0
  6. package/dist/errors.js +22 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/files.d.ts +6 -0
  9. package/dist/files.js +49 -0
  10. package/dist/files.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +18 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/resources/brands.d.ts +3 -0
  15. package/dist/resources/brands.js +170 -0
  16. package/dist/resources/brands.js.map +1 -0
  17. package/dist/resources/materials.d.ts +3 -0
  18. package/dist/resources/materials.js +134 -0
  19. package/dist/resources/materials.js.map +1 -0
  20. package/dist/resources/messages.d.ts +3 -0
  21. package/dist/resources/messages.js +135 -0
  22. package/dist/resources/messages.js.map +1 -0
  23. package/dist/resources/projects.d.ts +3 -0
  24. package/dist/resources/projects.js +78 -0
  25. package/dist/resources/projects.js.map +1 -0
  26. package/dist/server.d.ts +6 -0
  27. package/dist/server.js +171 -0
  28. package/dist/server.js.map +1 -0
  29. package/dist/subscriptions.d.ts +45 -0
  30. package/dist/subscriptions.js +275 -0
  31. package/dist/subscriptions.js.map +1 -0
  32. package/dist/tools/brand.d.ts +3 -0
  33. package/dist/tools/brand.js +141 -0
  34. package/dist/tools/brand.js.map +1 -0
  35. package/dist/tools/material.d.ts +2 -0
  36. package/dist/tools/material.js +23 -0
  37. package/dist/tools/material.js.map +1 -0
  38. package/dist/tools/message.d.ts +3 -0
  39. package/dist/tools/message.js +241 -0
  40. package/dist/tools/message.js.map +1 -0
  41. package/dist/tools/project.d.ts +3 -0
  42. package/dist/tools/project.js +240 -0
  43. package/dist/tools/project.js.map +1 -0
  44. package/dist/tools/subscribe.d.ts +12 -0
  45. package/dist/tools/subscribe.js +74 -0
  46. package/dist/tools/subscribe.js.map +1 -0
  47. package/package.json +37 -0
@@ -0,0 +1,275 @@
1
+ /** Events that indicate project status changed. */
2
+ const STATUS_EVENTS = new Set([
3
+ "RunStarted",
4
+ "RunCompleted",
5
+ "RunError",
6
+ "RunPaused",
7
+ "RunCancelled",
8
+ "RunContinued",
9
+ "TeamRunStarted",
10
+ "TeamRunCompleted",
11
+ "TeamRunError",
12
+ "TeamRunCancelled",
13
+ "AgentSystemError",
14
+ ]);
15
+ /** Events that carry new message content. */
16
+ const MESSAGE_EVENTS = new Set([
17
+ "RunContent",
18
+ "RunContentCompleted",
19
+ "RunIntermediateContent",
20
+ "ToolCallStarted",
21
+ "ToolCallCompleted",
22
+ "TeamRunContent",
23
+ "TeamRunContentCompleted",
24
+ "TeamRunIntermediateContent",
25
+ "TeamToolCallStarted",
26
+ "TeamToolCallCompleted",
27
+ "ReasoningStarted",
28
+ "ReasoningStep",
29
+ "ReasoningCompleted",
30
+ "TeamReasoningStarted",
31
+ "TeamReasoningStep",
32
+ "TeamReasoningCompleted",
33
+ // Status events also produce messages
34
+ ...STATUS_EVENTS,
35
+ ]);
36
+ /**
37
+ * Bridges WebSocket project subscriptions to MCP resource update notifications.
38
+ *
39
+ * When an MCP client subscribes to a resource URI (e.g. `soldy://project/{id}/status`),
40
+ * this bridge connects to the API WebSocket, subscribes to the project, and forwards
41
+ * relevant events as `sendResourceUpdated` notifications.
42
+ */
43
+ export class SubscriptionBridge {
44
+ client;
45
+ apiKey;
46
+ ws = null;
47
+ subscribedProjects = new Set();
48
+ connecting = false;
49
+ reconnectTimer = null;
50
+ reconnectDelay = 1000;
51
+ maxReconnectDelay = 30000;
52
+ server = null;
53
+ /** Brand task polling: taskId → { interval, lastStatus } */
54
+ brandTaskPolls = new Map();
55
+ static BRAND_TASK_POLL_MS = 3000;
56
+ constructor(client, _apiUrl, apiKey) {
57
+ this.client = client;
58
+ this.apiKey = apiKey;
59
+ }
60
+ /** Attach the low-level MCP Server instance for sending notifications. */
61
+ setServer(server) {
62
+ this.server = server;
63
+ }
64
+ /** Subscribe to a project's WebSocket events. Call when MCP client subscribes to a resource. */
65
+ async subscribeProject(projectId) {
66
+ if (this.subscribedProjects.has(projectId))
67
+ return;
68
+ this.subscribedProjects.add(projectId);
69
+ await this.ensureConnected();
70
+ this.sendProjectSubscribe(projectId);
71
+ }
72
+ /** Unsubscribe from a project. Call when no more MCP subscriptions reference it. */
73
+ unsubscribeProject(projectId) {
74
+ this.subscribedProjects.delete(projectId);
75
+ // WebSocket protocol doesn't support unsubscribe, but we stop sending notifications
76
+ }
77
+ /** Subscribe to brand task status changes via polling. */
78
+ subscribeBrandTask(taskId) {
79
+ if (this.brandTaskPolls.has(taskId))
80
+ return;
81
+ const poll = {
82
+ lastStatus: "",
83
+ interval: setInterval(async () => {
84
+ if (!this.server)
85
+ return;
86
+ try {
87
+ const task = await this.client.getBrandTaskResult(taskId);
88
+ if (!task)
89
+ return;
90
+ const entry = this.brandTaskPolls.get(taskId);
91
+ if (!entry)
92
+ return;
93
+ if (task.status !== entry.lastStatus) {
94
+ entry.lastStatus = task.status;
95
+ this.server.sendResourceUpdated({
96
+ uri: `soldy://brand/task/${taskId}`,
97
+ });
98
+ // Also notify brand list when a new brand is extracted
99
+ if (task.status === "finished") {
100
+ this.server.sendResourceUpdated({ uri: "soldy://brands" });
101
+ if (task.brand_id) {
102
+ this.server.sendResourceUpdated({
103
+ uri: `soldy://brand/${task.brand_id}`,
104
+ });
105
+ }
106
+ // Task completed — stop polling
107
+ this.unsubscribeBrandTask(taskId);
108
+ }
109
+ else if (task.status === "failed") {
110
+ // Task failed — stop polling
111
+ this.unsubscribeBrandTask(taskId);
112
+ }
113
+ }
114
+ }
115
+ catch (err) {
116
+ console.error(`[SubscriptionBridge] Brand task poll error (${taskId}):`, err);
117
+ }
118
+ }, SubscriptionBridge.BRAND_TASK_POLL_MS),
119
+ };
120
+ this.brandTaskPolls.set(taskId, poll);
121
+ }
122
+ /** Stop polling a brand task. */
123
+ unsubscribeBrandTask(taskId) {
124
+ const entry = this.brandTaskPolls.get(taskId);
125
+ if (entry) {
126
+ clearInterval(entry.interval);
127
+ this.brandTaskPolls.delete(taskId);
128
+ }
129
+ }
130
+ /** Disconnect the WebSocket and stop all polling. */
131
+ disconnect() {
132
+ if (this.reconnectTimer) {
133
+ clearTimeout(this.reconnectTimer);
134
+ this.reconnectTimer = null;
135
+ }
136
+ if (this.ws) {
137
+ this.ws.close();
138
+ this.ws = null;
139
+ }
140
+ this.connecting = false;
141
+ // Stop all brand task polls
142
+ for (const [taskId, entry] of this.brandTaskPolls) {
143
+ clearInterval(entry.interval);
144
+ this.brandTaskPolls.delete(taskId);
145
+ }
146
+ }
147
+ async ensureConnected() {
148
+ if (this.ws?.readyState === WebSocket.OPEN)
149
+ return;
150
+ if (this.connecting) {
151
+ // Wait for ongoing connection
152
+ await new Promise((resolve) => {
153
+ const check = () => {
154
+ if (this.ws?.readyState === WebSocket.OPEN)
155
+ resolve();
156
+ else
157
+ setTimeout(check, 100);
158
+ };
159
+ check();
160
+ });
161
+ return;
162
+ }
163
+ await this.connect();
164
+ }
165
+ async connect() {
166
+ this.connecting = true;
167
+ const wsUrl = this.client.getWebSocketUrl(this.apiKey);
168
+ return new Promise((resolve, reject) => {
169
+ const ws = new WebSocket(wsUrl);
170
+ ws.onopen = () => {
171
+ this.ws = ws;
172
+ this.connecting = false;
173
+ this.reconnectDelay = 1000;
174
+ console.error("[SubscriptionBridge] WebSocket connected");
175
+ // Re-subscribe all projects
176
+ for (const projectId of this.subscribedProjects) {
177
+ this.sendProjectSubscribe(projectId);
178
+ }
179
+ resolve();
180
+ };
181
+ ws.onmessage = (event) => {
182
+ this.handleMessage(String(event.data));
183
+ };
184
+ ws.onclose = () => {
185
+ console.error("[SubscriptionBridge] WebSocket closed");
186
+ this.ws = null;
187
+ this.connecting = false;
188
+ this.scheduleReconnect();
189
+ };
190
+ ws.onerror = (err) => {
191
+ console.error("[SubscriptionBridge] WebSocket error:", err);
192
+ this.connecting = false;
193
+ if (!this.ws)
194
+ reject(new Error("WebSocket connection failed"));
195
+ };
196
+ });
197
+ }
198
+ scheduleReconnect() {
199
+ if (this.subscribedProjects.size === 0)
200
+ return;
201
+ if (this.reconnectTimer)
202
+ return;
203
+ this.reconnectTimer = setTimeout(() => {
204
+ this.reconnectTimer = null;
205
+ this.connect().catch((err) => {
206
+ console.error("[SubscriptionBridge] Reconnect failed:", err);
207
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
208
+ this.scheduleReconnect();
209
+ });
210
+ }, this.reconnectDelay);
211
+ }
212
+ sendProjectSubscribe(projectId) {
213
+ if (this.ws?.readyState !== WebSocket.OPEN)
214
+ return;
215
+ this.ws.send(JSON.stringify({
216
+ event: "ClientProjectSubscribe",
217
+ project_id: projectId,
218
+ }));
219
+ }
220
+ handleMessage(data) {
221
+ if (!this.server)
222
+ return;
223
+ let msg;
224
+ try {
225
+ msg = JSON.parse(data);
226
+ }
227
+ catch {
228
+ return;
229
+ }
230
+ const projectId = msg.context?.project_id;
231
+ if (!projectId || !this.subscribedProjects.has(projectId))
232
+ return;
233
+ const event = msg.event;
234
+ const runId = msg.context?.run_id;
235
+ // Status change notifications
236
+ if (STATUS_EVENTS.has(event)) {
237
+ this.server.sendResourceUpdated({
238
+ uri: `soldy://project/${projectId}/status`,
239
+ });
240
+ }
241
+ // Message notifications
242
+ if (MESSAGE_EVENTS.has(event)) {
243
+ this.server.sendResourceUpdated({
244
+ uri: `soldy://project/${projectId}/messages`,
245
+ });
246
+ if (runId) {
247
+ this.server.sendResourceUpdated({
248
+ uri: `soldy://project/${projectId}/runs/${runId}/messages`,
249
+ });
250
+ }
251
+ }
252
+ // Material notifications
253
+ if (msg.message?.materials?.length) {
254
+ this.server.sendResourceUpdated({
255
+ uri: `soldy://project/${projectId}/materials`,
256
+ });
257
+ if (runId) {
258
+ this.server.sendResourceUpdated({
259
+ uri: `soldy://project/${projectId}/runs/${runId}/materials`,
260
+ });
261
+ }
262
+ }
263
+ }
264
+ }
265
+ /** Extract project_id from a soldy://project/... resource URI. */
266
+ export function extractProjectId(uri) {
267
+ const match = uri.match(/^soldy:\/\/project\/([^/]+)/);
268
+ return match?.[1] ?? null;
269
+ }
270
+ /** Extract task_id from a soldy://brand/task/... resource URI. */
271
+ export function extractBrandTaskId(uri) {
272
+ const match = uri.match(/^soldy:\/\/brand\/task\/([^/]+)/);
273
+ return match?.[1] ?? null;
274
+ }
275
+ //# sourceMappingURL=subscriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { SoldyAPIClient } from "../client.js";
3
+ export declare function registerBrandTools(server: McpServer, client: SoldyAPIClient): void;
@@ -0,0 +1,141 @@
1
+ import { z } from "zod";
2
+ import { formatApiError } from "../errors.js";
3
+ export function registerBrandTools(server, client) {
4
+ server.tool("create_brand", "Create a brand. Use before create_project if user has brand identity to associate.", {
5
+ name: z.string(),
6
+ description: z.string().optional(),
7
+ stage: z.string().optional(),
8
+ }, async ({ name, description, stage }) => {
9
+ const wsId = await client.getDefaultWorkspaceId();
10
+ const resp = await client.post("/public/brand", {
11
+ name,
12
+ slug: name
13
+ .toLowerCase()
14
+ .replace(/\s+/g, "-")
15
+ .replace(/[^a-z0-9-]/g, ""),
16
+ description: description ?? "",
17
+ stage: stage ?? "",
18
+ workspace_id: wsId,
19
+ });
20
+ if (resp.code !== 0 || !resp.data)
21
+ return {
22
+ content: [{ type: "text", text: formatApiError(resp) }],
23
+ isError: true,
24
+ };
25
+ const b = resp.data;
26
+ return {
27
+ content: [
28
+ {
29
+ type: "text",
30
+ text: `Brand created: **${b.name}** (ID: \`${b.id}\`)\nView: https://soldy.ai/app/brands/${b.id}`,
31
+ },
32
+ ],
33
+ };
34
+ });
35
+ server.tool("list_brands", "List all brands. Check here first if user mentions a brand or company.", {}, async () => {
36
+ const wsId = await client.getDefaultWorkspaceId();
37
+ const resp = await client.get("/public/brand/list", {
38
+ workspace_id: wsId,
39
+ });
40
+ if (resp.code !== 0)
41
+ return {
42
+ content: [{ type: "text", text: formatApiError(resp) }],
43
+ isError: true,
44
+ };
45
+ const brands = resp.data ?? [];
46
+ if (brands.length === 0) {
47
+ return {
48
+ content: [
49
+ {
50
+ type: "text",
51
+ text: "No brands yet. Use create_brand or extract_brand to add one.",
52
+ },
53
+ ],
54
+ };
55
+ }
56
+ const lines = ["| Name | ID | Stage |", "|---|---|---|"];
57
+ for (const b of brands) {
58
+ lines.push(`| ${b.name} | \`${b.id}\` | ${b.stage} |`);
59
+ }
60
+ return { content: [{ type: "text", text: lines.join("\n") }] };
61
+ });
62
+ server.tool("extract_brand", `Extract brand identity from a product URL or website URL. This is IMPORTANT — when the user provides a product page URL, call this BEFORE create_project to give the agent brand context (colors, tone, positioning).
63
+
64
+ Returns a task_id. Use watch_brand_task(task_id) to subscribe for completion notifications (preferred), or poll get_brand_task_result. Usually takes 30-60s. Once finished, use the returned brand_id in send_message options.`, {
65
+ content: z
66
+ .string()
67
+ .describe("Product page URL, brand website URL, or text describing the brand"),
68
+ brand_id: z.string().optional(),
69
+ }, async ({ content, brand_id }) => {
70
+ const wsId = await client.getDefaultWorkspaceId();
71
+ const resp = await client.post("/public/brand/task", {
72
+ content,
73
+ brand_id: brand_id ?? "",
74
+ workspace_id: wsId,
75
+ });
76
+ if (resp.code !== 0 || !resp.data)
77
+ return {
78
+ content: [{ type: "text", text: formatApiError(resp) }],
79
+ isError: true,
80
+ };
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: `Brand extraction started (task: \`${resp.data.id}\`). Use watch_brand_task to subscribe for completion, or poll with get_brand_task_result. Usually takes 30-60s.`,
86
+ },
87
+ ],
88
+ };
89
+ });
90
+ server.tool("get_brand_task_result", "Check brand extraction progress and result. For real-time updates, prefer watch_brand_task(task_id) instead of polling this tool. You can also read the resource URI soldy://brand/task/{task_id}.", { task_id: z.string() }, async ({ task_id }) => {
91
+ const wsId = await client.getDefaultWorkspaceId();
92
+ const resp = await client.post("/public/brand/task/result", {
93
+ task_ids: [task_id],
94
+ workspace_id: wsId,
95
+ });
96
+ if (resp.code !== 0 || !resp.data || resp.data.length === 0) {
97
+ return {
98
+ content: [{ type: "text", text: "Task not found." }],
99
+ isError: true,
100
+ };
101
+ }
102
+ const task = resp.data[0];
103
+ switch (task.status) {
104
+ case "running":
105
+ return {
106
+ content: [
107
+ {
108
+ type: "text",
109
+ text: `Running (${Math.round(task.progress)}%). Check again shortly.`,
110
+ },
111
+ ],
112
+ };
113
+ case "finished":
114
+ return {
115
+ content: [
116
+ {
117
+ type: "text",
118
+ text: `Brand extracted: ID \`${task.brand_id}\`\nView: https://soldy.ai/app/brands/${task.brand_id}`,
119
+ },
120
+ ],
121
+ };
122
+ case "failed":
123
+ return {
124
+ content: [
125
+ {
126
+ type: "text",
127
+ text: `Extraction failed: ${task.reason || "unknown"}`,
128
+ },
129
+ ],
130
+ isError: true,
131
+ };
132
+ default:
133
+ return {
134
+ content: [
135
+ { type: "text", text: `Status: ${task.status}` },
136
+ ],
137
+ };
138
+ }
139
+ });
140
+ }
141
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../../src/tools/brand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMaterialTools(server: McpServer, apiUrl: string): void;
@@ -0,0 +1,23 @@
1
+ export function registerMaterialTools(server, apiUrl) {
2
+ server.tool("upload_material", "Returns HTTP upload endpoint for local file upload. Note: send_message handles local files automatically — you usually don't need this.", {}, async () => {
3
+ return {
4
+ content: [
5
+ {
6
+ type: "text",
7
+ text: `## File Upload
8
+
9
+ send_message automatically handles local file paths — just pass them as material_urls.
10
+
11
+ For manual upload, POST multipart/form-data to:
12
+ ${apiUrl}/api/v1/public/material
13
+
14
+ Header: X-API-Key: <your-key>
15
+ Field: file (required)
16
+
17
+ The response contains a URL to use in send_message's material_urls.`,
18
+ },
19
+ ],
20
+ };
21
+ });
22
+ }
23
+ //# sourceMappingURL=material.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"material.js","sourceRoot":"","sources":["../../src/tools/material.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,MAAc;IACrE,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,yIAAyI,EACzI,EAAE,EACF,KAAK,IAAI,EAAE;QACT,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;;;;;EAKhB,MAAM;;;;;oEAK4D;iBACzD;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { SoldyAPIClient } from "../client.js";
3
+ export declare function registerMessageTools(server: McpServer, client: SoldyAPIClient): void;