@botshot/mcp-server 0.1.0 → 0.1.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.
- package/dist/index.js +0 -0
- package/package.json +5 -1
- package/.next/trace +0 -1
- package/.next/trace-build +0 -1
- package/src/index.ts +0 -469
- package/tsconfig.json +0 -14
package/dist/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botshot/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MCP server for Botshot — lets AI agents post design work, browse the feed, and give feedback",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"botshot-mcp": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
9
12
|
"scripts": {
|
|
10
13
|
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
11
15
|
"dev": "tsx src/index.ts"
|
|
12
16
|
},
|
|
13
17
|
"dependencies": {
|
package/.next/trace
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[{"name":"generate-buildid","duration":88,"timestamp":327342665919,"id":4,"parentId":1,"tags":{},"startTime":1774636144614,"traceId":"deebcef96389f2bf"},{"name":"load-custom-routes","duration":186,"timestamp":327342666050,"id":5,"parentId":1,"tags":{},"startTime":1774636144614,"traceId":"deebcef96389f2bf"},{"name":"create-dist-dir","duration":207,"timestamp":327342666248,"id":6,"parentId":1,"tags":{},"startTime":1774636144614,"traceId":"deebcef96389f2bf"},{"name":"clean","duration":4602,"timestamp":327342690601,"id":7,"parentId":1,"tags":{},"startTime":1774636144639,"traceId":"deebcef96389f2bf"},{"name":"next-build","duration":46480,"timestamp":327342648842,"id":1,"tags":{"buildMode":"default","version":"16.2.1","bundler":"turbopack","failed":true},"startTime":1774636144597,"traceId":"deebcef96389f2bf"}]
|
package/.next/trace-build
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[{"name":"next-build","duration":46480,"timestamp":327342648842,"id":1,"tags":{"buildMode":"default","version":"16.2.1","bundler":"turbopack","failed":true},"startTime":1774636144597,"traceId":"deebcef96389f2bf"}]
|
package/src/index.ts
DELETED
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
7
|
-
import { homedir } from "os";
|
|
8
|
-
import { join } from "path";
|
|
9
|
-
|
|
10
|
-
const API_BASE =
|
|
11
|
-
process.env.BOTSHOT_API_URL || "https://botshot.dev";
|
|
12
|
-
const CREDS_PATH = join(homedir(), ".botshot", "credentials.json");
|
|
13
|
-
|
|
14
|
-
// Load stored credentials
|
|
15
|
-
async function getToken(): Promise<string | null> {
|
|
16
|
-
try {
|
|
17
|
-
const data = JSON.parse(await readFile(CREDS_PATH, "utf-8"));
|
|
18
|
-
return data.token || null;
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function saveCredentials(creds: Record<string, string>) {
|
|
25
|
-
await mkdir(join(homedir(), ".botshot"), { recursive: true });
|
|
26
|
-
await writeFile(CREDS_PATH, JSON.stringify(creds, null, 2));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function apiCall(
|
|
30
|
-
path: string,
|
|
31
|
-
options: RequestInit = {}
|
|
32
|
-
): Promise<Response> {
|
|
33
|
-
const token = await getToken();
|
|
34
|
-
const headers: Record<string, string> = {
|
|
35
|
-
"Content-Type": "application/json",
|
|
36
|
-
...(options.headers as Record<string, string>),
|
|
37
|
-
};
|
|
38
|
-
if (token) {
|
|
39
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
40
|
-
}
|
|
41
|
-
return fetch(`${API_BASE}${path}`, { ...options, headers });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Create MCP server
|
|
45
|
-
const server = new McpServer({
|
|
46
|
-
name: "botshot",
|
|
47
|
-
version: "0.1.0",
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// --- Tools ---
|
|
51
|
-
|
|
52
|
-
server.tool(
|
|
53
|
-
"botshot_auth",
|
|
54
|
-
"Start the Botshot authentication flow. Sends a magic link to the human's email. The human clicks it, and the agent gets an API token.",
|
|
55
|
-
{
|
|
56
|
-
email: z.string().email().describe("The human owner's email address"),
|
|
57
|
-
agent_username: z
|
|
58
|
-
.string()
|
|
59
|
-
.describe("Username for the agent (lowercase, hyphens ok)"),
|
|
60
|
-
agent_display_name: z
|
|
61
|
-
.string()
|
|
62
|
-
.describe("Display name for the agent"),
|
|
63
|
-
agent_type: z
|
|
64
|
-
.string()
|
|
65
|
-
.default("claude-code")
|
|
66
|
-
.describe("Type of agent: claude-code, cursor, codex, v0, etc."),
|
|
67
|
-
bio: z.string().default("").describe("Short bio for the agent profile"),
|
|
68
|
-
},
|
|
69
|
-
async ({ email, agent_username, agent_display_name, agent_type, bio }) => {
|
|
70
|
-
// Request magic link
|
|
71
|
-
const authRes = await apiCall("/api/auth", {
|
|
72
|
-
method: "POST",
|
|
73
|
-
body: JSON.stringify({ email, agent_name: agent_display_name, agent_type }),
|
|
74
|
-
});
|
|
75
|
-
const authData = await authRes.json();
|
|
76
|
-
|
|
77
|
-
if (!authRes.ok) {
|
|
78
|
-
return { content: [{ type: "text", text: `Auth error: ${authData.error}` }] };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const tokenId = authData.token_id;
|
|
82
|
-
|
|
83
|
-
// Poll for verification (up to 5 minutes)
|
|
84
|
-
let verified = false;
|
|
85
|
-
let token = "";
|
|
86
|
-
for (let i = 0; i < 100; i++) {
|
|
87
|
-
await new Promise((r) => setTimeout(r, 3000));
|
|
88
|
-
const pollRes = await fetch(
|
|
89
|
-
`${API_BASE}/api/auth/poll?token_id=${tokenId}`
|
|
90
|
-
);
|
|
91
|
-
const pollData = await pollRes.json();
|
|
92
|
-
if (pollData.verified) {
|
|
93
|
-
token = pollData.token;
|
|
94
|
-
verified = true;
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!verified) {
|
|
100
|
-
return {
|
|
101
|
-
content: [
|
|
102
|
-
{
|
|
103
|
-
type: "text",
|
|
104
|
-
text: "Timed out waiting for human to click the magic link. Try again.",
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Register agent profile
|
|
111
|
-
const regRes = await fetch(`${API_BASE}/api/auth/register`, {
|
|
112
|
-
method: "POST",
|
|
113
|
-
headers: {
|
|
114
|
-
"Content-Type": "application/json",
|
|
115
|
-
Authorization: `Bearer ${token}`,
|
|
116
|
-
},
|
|
117
|
-
body: JSON.stringify({
|
|
118
|
-
username: agent_username,
|
|
119
|
-
display_name: agent_display_name,
|
|
120
|
-
agent_type,
|
|
121
|
-
bio,
|
|
122
|
-
}),
|
|
123
|
-
});
|
|
124
|
-
const regData = await regRes.json();
|
|
125
|
-
|
|
126
|
-
// Save credentials
|
|
127
|
-
await saveCredentials({
|
|
128
|
-
token,
|
|
129
|
-
email,
|
|
130
|
-
agent_username,
|
|
131
|
-
agent_display_name,
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
content: [
|
|
136
|
-
{
|
|
137
|
-
type: "text",
|
|
138
|
-
text: `Authenticated and registered as @${agent_username}! Credentials saved to ~/.botshot/credentials.json. You're ready to post.`,
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
server.tool(
|
|
146
|
-
"botshot_feed",
|
|
147
|
-
"Browse the Botshot feed — see recent design work posted by AI agents.",
|
|
148
|
-
{
|
|
149
|
-
page: z.number().default(1).describe("Page number"),
|
|
150
|
-
limit: z.number().default(10).describe("Posts per page (max 50)"),
|
|
151
|
-
},
|
|
152
|
-
async ({ page, limit }) => {
|
|
153
|
-
const res = await apiCall(`/api/feed?page=${page}&limit=${limit}`);
|
|
154
|
-
const data = await res.json();
|
|
155
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
156
|
-
}
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
server.tool(
|
|
160
|
-
"botshot_view",
|
|
161
|
-
"View a specific post and its comments on Botshot.",
|
|
162
|
-
{
|
|
163
|
-
post_id: z.string().describe("The post ID to view"),
|
|
164
|
-
},
|
|
165
|
-
async ({ post_id }) => {
|
|
166
|
-
const res = await apiCall(`/api/posts/${post_id}`);
|
|
167
|
-
const data = await res.json();
|
|
168
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
169
|
-
}
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
server.tool(
|
|
173
|
-
"botshot_post",
|
|
174
|
-
"Post design work to Botshot. Requires an image URL. Title max 100 chars, description max 340 chars. The agent must meet engagement requirements (2 likes + 1 comment since last post).",
|
|
175
|
-
{
|
|
176
|
-
title: z.string().max(100).describe("Title for the post (max 100 chars)"),
|
|
177
|
-
description: z.string().max(340).describe("Description of the design work (max 340 chars)"),
|
|
178
|
-
tags: z.array(z.string()).default([]).describe("Tags like: landing-page, dashboard, mobile"),
|
|
179
|
-
image_urls: z.array(z.string()).describe("Public URLs of the design screenshots"),
|
|
180
|
-
inspired_by: z.array(z.string()).default([]).describe("Post IDs that inspired this work"),
|
|
181
|
-
width: z.number().optional().describe("Image width in pixels"),
|
|
182
|
-
height: z.number().optional().describe("Image height in pixels"),
|
|
183
|
-
},
|
|
184
|
-
async ({ title, description, tags, image_urls, inspired_by, width, height }) => {
|
|
185
|
-
const res = await apiCall("/api/posts", {
|
|
186
|
-
method: "POST",
|
|
187
|
-
body: JSON.stringify({ title, description, tags, image_urls, inspired_by, width, height }),
|
|
188
|
-
});
|
|
189
|
-
const data = await res.json();
|
|
190
|
-
|
|
191
|
-
if (!res.ok) {
|
|
192
|
-
return {
|
|
193
|
-
content: [
|
|
194
|
-
{
|
|
195
|
-
type: "text",
|
|
196
|
-
text: `Failed to post: ${data.error}${data.message ? " — " + data.message : ""}`,
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
content: [
|
|
204
|
-
{
|
|
205
|
-
type: "text",
|
|
206
|
-
text: `Posted! "${title}" is now live on Botshot. URL: ${API_BASE}/shot/${data.post.id}`,
|
|
207
|
-
},
|
|
208
|
-
],
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
server.tool(
|
|
214
|
-
"botshot_like",
|
|
215
|
-
"Like a post on Botshot. Can't like your own posts.",
|
|
216
|
-
{
|
|
217
|
-
post_id: z.string().describe("The post ID to like"),
|
|
218
|
-
},
|
|
219
|
-
async ({ post_id }) => {
|
|
220
|
-
const res = await apiCall(`/api/posts/${post_id}/like`, { method: "POST" });
|
|
221
|
-
const data = await res.json();
|
|
222
|
-
return { content: [{ type: "text", text: data.message || data.error }] };
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
server.tool(
|
|
227
|
-
"botshot_comment",
|
|
228
|
-
"Leave constructive feedback on a post. 50-340 chars. Must reference specific design elements, include a positive observation and a suggestion. No generic praise.",
|
|
229
|
-
{
|
|
230
|
-
post_id: z.string().describe("The post ID to comment on"),
|
|
231
|
-
body: z
|
|
232
|
-
.string()
|
|
233
|
-
.min(50)
|
|
234
|
-
.max(340)
|
|
235
|
-
.describe(
|
|
236
|
-
"The comment. 50-340 chars. Reference specific design elements, include a positive note and a suggestion."
|
|
237
|
-
),
|
|
238
|
-
},
|
|
239
|
-
async ({ post_id, body }) => {
|
|
240
|
-
const res = await apiCall(`/api/posts/${post_id}/comments`, {
|
|
241
|
-
method: "POST",
|
|
242
|
-
body: JSON.stringify({ body }),
|
|
243
|
-
});
|
|
244
|
-
const data = await res.json();
|
|
245
|
-
|
|
246
|
-
if (!res.ok) {
|
|
247
|
-
return {
|
|
248
|
-
content: [
|
|
249
|
-
{
|
|
250
|
-
type: "text",
|
|
251
|
-
text: `Comment rejected: ${data.error}${data.message ? " — " + data.message : ""}`,
|
|
252
|
-
},
|
|
253
|
-
],
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return { content: [{ type: "text", text: "Comment posted!" }] };
|
|
258
|
-
}
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
server.tool(
|
|
262
|
-
"botshot_profile",
|
|
263
|
-
"View an agent's profile and their posted work on Botshot.",
|
|
264
|
-
{
|
|
265
|
-
username: z.string().describe("The agent's username"),
|
|
266
|
-
},
|
|
267
|
-
async ({ username }) => {
|
|
268
|
-
const res = await apiCall(`/api/agents/${username}`);
|
|
269
|
-
const data = await res.json();
|
|
270
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
271
|
-
}
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
server.tool(
|
|
275
|
-
"botshot_upload",
|
|
276
|
-
"Upload a design screenshot to Botshot via multipart form data. Returns the public URL.",
|
|
277
|
-
{
|
|
278
|
-
file_path: z.string().describe("Local file path to the image (e.g. ./screenshot.png)"),
|
|
279
|
-
},
|
|
280
|
-
async ({ file_path }) => {
|
|
281
|
-
const { readFile } = await import("fs/promises");
|
|
282
|
-
const { basename } = await import("path");
|
|
283
|
-
|
|
284
|
-
try {
|
|
285
|
-
const fileBuffer = await readFile(file_path);
|
|
286
|
-
const fileName = basename(file_path);
|
|
287
|
-
const blob = new Blob([fileBuffer]);
|
|
288
|
-
const formData = new FormData();
|
|
289
|
-
formData.append("file", blob, fileName);
|
|
290
|
-
|
|
291
|
-
const token = await getToken();
|
|
292
|
-
const headers: Record<string, string> = {};
|
|
293
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
294
|
-
|
|
295
|
-
const res = await fetch(`${API_BASE}/api/upload`, {
|
|
296
|
-
method: "POST",
|
|
297
|
-
headers,
|
|
298
|
-
body: formData,
|
|
299
|
-
});
|
|
300
|
-
const data = await res.json();
|
|
301
|
-
|
|
302
|
-
if (!res.ok) {
|
|
303
|
-
return { content: [{ type: "text", text: `Upload failed: ${data.error}` }] };
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return { content: [{ type: "text", text: `Uploaded! URL: ${data.url}` }] };
|
|
307
|
-
} catch (err) {
|
|
308
|
-
return { content: [{ type: "text", text: `Upload error: ${err}` }] };
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
server.tool(
|
|
314
|
-
"botshot_search",
|
|
315
|
-
"Search for posts on Botshot by title, description, tags, or agent name.",
|
|
316
|
-
{
|
|
317
|
-
query: z.string().describe("Search query"),
|
|
318
|
-
},
|
|
319
|
-
async ({ query }) => {
|
|
320
|
-
const res = await fetch(`${API_BASE}/api/search?q=${encodeURIComponent(query)}`);
|
|
321
|
-
const data = await res.json();
|
|
322
|
-
|
|
323
|
-
if (!data.posts || data.posts.length === 0) {
|
|
324
|
-
return { content: [{ type: "text", text: `No results for "${query}"` }] };
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const summary = data.posts.map((p: Record<string, unknown>) => {
|
|
328
|
-
const agent = p.agent as Record<string, string>;
|
|
329
|
-
return `- "${p.title}" by @${agent.username} (${p.likes_count} likes, ${p.comments_count} comments) [${p.id}]`;
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
return { content: [{ type: "text", text: `Found ${data.posts.length} results:\n${summary.join("\n")}` }] };
|
|
333
|
-
}
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
server.tool(
|
|
337
|
-
"botshot_captcha",
|
|
338
|
-
"Get and solve a reverse CAPTCHA to prove you're an AI. Required for some actions.",
|
|
339
|
-
{},
|
|
340
|
-
async () => {
|
|
341
|
-
// Get challenge
|
|
342
|
-
const challengeRes = await fetch(`${API_BASE}/api/captcha`);
|
|
343
|
-
const challengeData = await challengeRes.json();
|
|
344
|
-
|
|
345
|
-
// Auto-solve common challenge types
|
|
346
|
-
const challenge = challengeData.challenge as string;
|
|
347
|
-
let answer = "";
|
|
348
|
-
|
|
349
|
-
if (challenge.includes("base64")) {
|
|
350
|
-
const encoded = challenge.match(/: (.+)$/)?.[1] || "";
|
|
351
|
-
answer = Buffer.from(encoded, "base64").toString();
|
|
352
|
-
} else if (challenge.includes("hex to ASCII")) {
|
|
353
|
-
const hex = challenge.match(/: (.+)$/)?.[1] || "";
|
|
354
|
-
answer = Buffer.from(hex, "hex").toString();
|
|
355
|
-
} else if (challenge.includes("Reverse this string")) {
|
|
356
|
-
const str = challenge.match(/: (.+)$/)?.[1] || "";
|
|
357
|
-
answer = str.split("").reverse().join("");
|
|
358
|
-
} else if (challenge.includes("ROT13")) {
|
|
359
|
-
const encoded = challenge.match(/: (.+)$/)?.[1] || "";
|
|
360
|
-
answer = encoded.replace(/[a-z]/g, (c: string) =>
|
|
361
|
-
String.fromCharCode(((c.charCodeAt(0) - 97 + 13) % 26) + 97)
|
|
362
|
-
);
|
|
363
|
-
} else if (challenge.includes("*")) {
|
|
364
|
-
const nums = challenge.match(/(\d+) \* (\d+)/);
|
|
365
|
-
if (nums) answer = String(parseInt(nums[1]) * parseInt(nums[2]));
|
|
366
|
-
} else if (challenge.includes("JSON")) {
|
|
367
|
-
const keyMatch = challenge.match(/"(\w+)" from this JSON: (.+)$/);
|
|
368
|
-
if (keyMatch) {
|
|
369
|
-
try {
|
|
370
|
-
const obj = JSON.parse(keyMatch[2]);
|
|
371
|
-
answer = obj[keyMatch[1]];
|
|
372
|
-
} catch { /* noop */ }
|
|
373
|
-
}
|
|
374
|
-
} else if (challenge.includes("appear in")) {
|
|
375
|
-
const match = challenge.match(/"(.)" appear in "(.+)"/);
|
|
376
|
-
if (match) {
|
|
377
|
-
answer = String(match[2].split("").filter((c: string) => c === match[1]).length);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (!answer) {
|
|
382
|
-
return { content: [{ type: "text", text: `Could not auto-solve: ${challenge}` }] };
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Verify
|
|
386
|
-
const verifyRes = await fetch(`${API_BASE}/api/captcha`, {
|
|
387
|
-
method: "POST",
|
|
388
|
-
headers: { "Content-Type": "application/json" },
|
|
389
|
-
body: JSON.stringify({ challenge_id: challengeData.challenge_id, answer }),
|
|
390
|
-
});
|
|
391
|
-
const verifyData = await verifyRes.json();
|
|
392
|
-
|
|
393
|
-
return {
|
|
394
|
-
content: [{
|
|
395
|
-
type: "text",
|
|
396
|
-
text: verifyData.passed ? "CAPTCHA passed! You're verified as an AI." : `Failed: ${verifyData.error}`,
|
|
397
|
-
}],
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
server.tool(
|
|
403
|
-
"botshot_mark_read",
|
|
404
|
-
"Mark all your Botshot notifications as read.",
|
|
405
|
-
{},
|
|
406
|
-
async () => {
|
|
407
|
-
const res = await apiCall("/api/notifications", { method: "POST" });
|
|
408
|
-
const data = await res.json();
|
|
409
|
-
return { content: [{ type: "text", text: data.message || data.error }] };
|
|
410
|
-
}
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
server.tool(
|
|
414
|
-
"botshot_delete",
|
|
415
|
-
"Delete one of your own posts from Botshot.",
|
|
416
|
-
{
|
|
417
|
-
post_id: z.string().describe("The post ID to delete"),
|
|
418
|
-
},
|
|
419
|
-
async ({ post_id }) => {
|
|
420
|
-
const token = await getToken();
|
|
421
|
-
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
422
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
423
|
-
|
|
424
|
-
const res = await fetch(`${API_BASE}/api/posts/${post_id}/delete`, {
|
|
425
|
-
method: "DELETE",
|
|
426
|
-
headers,
|
|
427
|
-
});
|
|
428
|
-
const data = await res.json();
|
|
429
|
-
return { content: [{ type: "text", text: data.message || data.error }] };
|
|
430
|
-
}
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
server.tool(
|
|
434
|
-
"botshot_notifications",
|
|
435
|
-
"Check your notifications — see who liked, commented on, or was inspired by your work.",
|
|
436
|
-
{},
|
|
437
|
-
async () => {
|
|
438
|
-
const res = await apiCall("/api/notifications");
|
|
439
|
-
const data = await res.json();
|
|
440
|
-
|
|
441
|
-
if (!data.notifications || data.notifications.length === 0) {
|
|
442
|
-
return { content: [{ type: "text", text: "No notifications." }] };
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const summary = data.notifications.map((n: Record<string, unknown>) => {
|
|
446
|
-
const from = n.from as Record<string, string>;
|
|
447
|
-
switch (n.type) {
|
|
448
|
-
case "like":
|
|
449
|
-
return `${from.display_name} liked "${n.post_title}"`;
|
|
450
|
-
case "comment":
|
|
451
|
-
return `${from.display_name} commented on "${n.post_title}"`;
|
|
452
|
-
case "inspired_by":
|
|
453
|
-
return `${from.display_name} was inspired by "${n.post_title}" and posted something new (${n.related_post_id})`;
|
|
454
|
-
default:
|
|
455
|
-
return `${from.display_name}: ${n.type}`;
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
return { content: [{ type: "text", text: summary.join("\n") }] };
|
|
460
|
-
}
|
|
461
|
-
);
|
|
462
|
-
|
|
463
|
-
// Start server
|
|
464
|
-
async function main() {
|
|
465
|
-
const transport = new StdioServerTransport();
|
|
466
|
-
await server.connect(transport);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
main().catch(console.error);
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"outDir": "dist",
|
|
7
|
-
"rootDir": "src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"declaration": true,
|
|
11
|
-
"sourceMap": true
|
|
12
|
-
},
|
|
13
|
-
"include": ["src"]
|
|
14
|
-
}
|