spora 0.1.0

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 (62) hide show
  1. package/README.md +87 -0
  2. package/bin/spora.js +2 -0
  3. package/dist/account-creator-PZW5JLHS.js +498 -0
  4. package/dist/account-creator-PZW5JLHS.js.map +1 -0
  5. package/dist/chunk-3JEDGXEM.js +32 -0
  6. package/dist/chunk-3JEDGXEM.js.map +1 -0
  7. package/dist/chunk-53YLFYJF.js +59 -0
  8. package/dist/chunk-53YLFYJF.js.map +1 -0
  9. package/dist/chunk-7CR4ID6P.js +614 -0
  10. package/dist/chunk-7CR4ID6P.js.map +1 -0
  11. package/dist/chunk-AHXZIGQE.js +156 -0
  12. package/dist/chunk-AHXZIGQE.js.map +1 -0
  13. package/dist/chunk-DJJWHOL3.js +162 -0
  14. package/dist/chunk-DJJWHOL3.js.map +1 -0
  15. package/dist/chunk-EBO4F5NU.js +105 -0
  16. package/dist/chunk-EBO4F5NU.js.map +1 -0
  17. package/dist/chunk-ERTBXYOP.js +81 -0
  18. package/dist/chunk-ERTBXYOP.js.map +1 -0
  19. package/dist/chunk-KELPENM3.js +47 -0
  20. package/dist/chunk-KELPENM3.js.map +1 -0
  21. package/dist/chunk-NFDZ47AG.js +57 -0
  22. package/dist/chunk-NFDZ47AG.js.map +1 -0
  23. package/dist/chunk-O23NWMYU.js +124 -0
  24. package/dist/chunk-O23NWMYU.js.map +1 -0
  25. package/dist/chunk-YEKHNTQO.js +80 -0
  26. package/dist/chunk-YEKHNTQO.js.map +1 -0
  27. package/dist/chunk-ZJZKH7N7.js +56 -0
  28. package/dist/chunk-ZJZKH7N7.js.map +1 -0
  29. package/dist/cli.js +675 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/client-3AQCA4YE.js +401 -0
  32. package/dist/client-3AQCA4YE.js.map +1 -0
  33. package/dist/client-RBGZWS3Q.js +373 -0
  34. package/dist/client-RBGZWS3Q.js.map +1 -0
  35. package/dist/colony-J5KQIV6M.js +229 -0
  36. package/dist/colony-J5KQIV6M.js.map +1 -0
  37. package/dist/config-NZAFARS6.js +14 -0
  38. package/dist/config-NZAFARS6.js.map +1 -0
  39. package/dist/crypto-FHSQ72NU.js +14 -0
  40. package/dist/crypto-FHSQ72NU.js.map +1 -0
  41. package/dist/heartbeat-J4JLYH2B.js +358 -0
  42. package/dist/heartbeat-J4JLYH2B.js.map +1 -0
  43. package/dist/init-BG4Z4XQU.js +205 -0
  44. package/dist/init-BG4Z4XQU.js.map +1 -0
  45. package/dist/llm-RDNC5Y3G.js +16 -0
  46. package/dist/llm-RDNC5Y3G.js.map +1 -0
  47. package/dist/mcp-server.js +773 -0
  48. package/dist/mcp-server.js.map +1 -0
  49. package/dist/memory-7FBE26K3.js +26 -0
  50. package/dist/memory-7FBE26K3.js.map +1 -0
  51. package/dist/memory-O3AJIKBX.js +24 -0
  52. package/dist/memory-O3AJIKBX.js.map +1 -0
  53. package/dist/paths-5GFUUHCZ.js +13 -0
  54. package/dist/paths-5GFUUHCZ.js.map +1 -0
  55. package/dist/prompt-builder-WNMZ2QCN.js +17 -0
  56. package/dist/prompt-builder-WNMZ2QCN.js.map +1 -0
  57. package/dist/queue-ELK5ZX7J.js +14 -0
  58. package/dist/queue-ELK5ZX7J.js.map +1 -0
  59. package/dist/x-client-J4GE5A7P.js +12 -0
  60. package/dist/x-client-J4GE5A7P.js.map +1 -0
  61. package/package.json +57 -0
  62. package/templates/SKILL.md +335 -0
@@ -0,0 +1,773 @@
1
+ import {
2
+ FRAMEWORKS,
3
+ GOAL_PRESETS,
4
+ createIdentity,
5
+ loadIdentity,
6
+ mutateIdentity,
7
+ renderIdentityDocument,
8
+ saveIdentity
9
+ } from "./chunk-7CR4ID6P.js";
10
+ import {
11
+ logger,
12
+ setLogLevel
13
+ } from "./chunk-KELPENM3.js";
14
+ import {
15
+ loadConfig
16
+ } from "./chunk-YEKHNTQO.js";
17
+ import {
18
+ addLearning,
19
+ getInteractions,
20
+ getRecentInteractions,
21
+ loadLearnings,
22
+ loadRelationships,
23
+ updateRelationship
24
+ } from "./chunk-EBO4F5NU.js";
25
+ import "./chunk-53YLFYJF.js";
26
+
27
+ // src/mcp-server.ts
28
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
29
+
30
+ // src/mcp/server.ts
31
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
32
+ import { z } from "zod";
33
+ function createSporaServer() {
34
+ const server = new McpServer({
35
+ name: "spora",
36
+ version: "0.1.0"
37
+ });
38
+ server.tool(
39
+ "spora_get_frameworks",
40
+ "Get inspiration frameworks for Spore creation. These are starting points and vibes \u2014 NOT rigid categories. The creator ultimately decides what their agent is. Use these as conversation starters.",
41
+ {},
42
+ async () => {
43
+ const frameworkList = Object.entries(FRAMEWORKS).map(([key, fw]) => ({
44
+ id: key,
45
+ label: fw.label,
46
+ tagline: fw.tagline,
47
+ description: fw.description
48
+ }));
49
+ return {
50
+ content: [
51
+ {
52
+ type: "text",
53
+ text: JSON.stringify(
54
+ {
55
+ frameworks: frameworkList,
56
+ goals: [...GOAL_PRESETS],
57
+ instructions: "These are inspiration, not a menu. First ask the user what kind of Spore they envision. Then show frameworks that match their description as starting points. The user can pick one that resonates, mix ideas from several, or go fully custom. The creator defines the agent \u2014 frameworks just give them a head start."
58
+ },
59
+ null,
60
+ 2
61
+ )
62
+ }
63
+ ]
64
+ };
65
+ }
66
+ );
67
+ server.tool(
68
+ "spora_get_framework_details",
69
+ "Get full details of a framework \u2014 traits, tone, values, strategy. Use this to show what a starting point looks like. Everything shown can be customized by the creator.",
70
+ {
71
+ framework: z.string().describe(
72
+ "Framework ID: truthseeker, conqueror, authentic, growth-hacker, philosopher, provocateur, curator, shitposter, community-builder"
73
+ )
74
+ },
75
+ async ({ framework }) => {
76
+ const fw = FRAMEWORKS[framework];
77
+ if (!fw) {
78
+ return {
79
+ content: [{ type: "text", text: `Unknown framework: ${framework}` }],
80
+ isError: true
81
+ };
82
+ }
83
+ return {
84
+ content: [
85
+ {
86
+ type: "text",
87
+ text: JSON.stringify(
88
+ {
89
+ id: framework,
90
+ ...fw,
91
+ instructions: "Show this as a starting foundation, not a final answer. Make it clear that every aspect can be changed. The creator decides what their agent is \u2014 this just gives them a head start on traits, tone, values, and strategy."
92
+ },
93
+ null,
94
+ 2
95
+ )
96
+ }
97
+ ]
98
+ };
99
+ }
100
+ );
101
+ server.tool(
102
+ "spora_create_spore",
103
+ "Create a new Spore identity. Call this after walking the user through the creation flow. All fields except framework, name, and handle are optional \u2014 framework defaults fill in the rest.",
104
+ {
105
+ framework: z.enum([
106
+ "truthseeker",
107
+ "conqueror",
108
+ "authentic",
109
+ "growth-hacker",
110
+ "philosopher",
111
+ "provocateur",
112
+ "curator",
113
+ "shitposter",
114
+ "community-builder",
115
+ "custom"
116
+ ]).describe("The chosen framework"),
117
+ name: z.string().describe("Display name for the Spore"),
118
+ handle: z.string().describe("X handle (without @)"),
119
+ bio: z.string().max(160).optional().describe("X bio (max 160 chars)"),
120
+ profileImageDescription: z.string().optional().describe("Description of desired profile image style"),
121
+ originStory: z.string().optional().describe("1-3 sentences: why this Spore exists"),
122
+ coreValues: z.array(z.string()).optional().describe("Principles this Spore lives by (1-5)"),
123
+ worldview: z.string().optional().describe("How this Spore sees the world"),
124
+ tone: z.string().optional().describe("Voice and writing style description"),
125
+ catchphrases: z.array(z.string()).optional().describe("Signature phrases"),
126
+ vocabularyStyle: z.enum(["academic", "casual", "internet-native", "poetic", "technical", "mixed"]).optional(),
127
+ emojiUsage: z.enum(["never", "rare", "moderate", "heavy"]).optional(),
128
+ tweetStyle: z.enum(["one-liners", "short-form", "threads", "mixed"]).optional(),
129
+ topics: z.array(z.string()).optional().describe("Topics to engage with"),
130
+ avoidTopics: z.array(z.string()).optional().describe("Topics to stay away from"),
131
+ heroes: z.array(z.string()).optional().describe("Accounts or figures to admire"),
132
+ goals: z.array(z.string()).optional().describe("Strategic goals"),
133
+ conflictStyle: z.enum(["agree-to-disagree", "debate", "clap-back", "ignore", "humor-deflect"]).optional(),
134
+ boundaries: z.array(z.string()).optional().describe("Things this Spore will NOT do"),
135
+ joinColony: z.boolean().optional().describe("Whether to join The Colony"),
136
+ customTraits: z.object({
137
+ aggression: z.number().min(0).max(1).optional(),
138
+ humor: z.number().min(0).max(1).optional(),
139
+ formality: z.number().min(0).max(1).optional(),
140
+ verbosity: z.number().min(0).max(1).optional(),
141
+ empathy: z.number().min(0).max(1).optional(),
142
+ curiosity: z.number().min(0).max(1).optional(),
143
+ confidence: z.number().min(0).max(1).optional(),
144
+ originality: z.number().min(0).max(1).optional()
145
+ }).optional().describe("Override specific trait values (0.0 to 1.0)")
146
+ },
147
+ async (args) => {
148
+ try {
149
+ const identity = createIdentity(args);
150
+ saveIdentity(identity);
151
+ const doc = renderIdentityDocument(identity);
152
+ return {
153
+ content: [
154
+ {
155
+ type: "text",
156
+ text: `Spore created successfully!
157
+
158
+ ${doc}
159
+
160
+ ---
161
+ Identity saved. Your Spore is alive. Use spora_get_identity to read the full document at any time.`
162
+ }
163
+ ]
164
+ };
165
+ } catch (error) {
166
+ return {
167
+ content: [{ type: "text", text: `Error creating Spore: ${error.message}` }],
168
+ isError: true
169
+ };
170
+ }
171
+ }
172
+ );
173
+ server.tool(
174
+ "spora_get_identity",
175
+ "Read this Spore's full identity document \u2014 who you are, your personality, goals, voice, everything. Read this on every heartbeat.",
176
+ {},
177
+ async () => {
178
+ try {
179
+ const identity = loadIdentity();
180
+ const doc = renderIdentityDocument(identity);
181
+ return {
182
+ content: [{ type: "text", text: doc }]
183
+ };
184
+ } catch (error) {
185
+ return {
186
+ content: [{ type: "text", text: `Error: ${error.message}` }],
187
+ isError: true
188
+ };
189
+ }
190
+ }
191
+ );
192
+ server.tool(
193
+ "spora_get_identity_raw",
194
+ "Read this Spore's identity as raw JSON (useful for programmatic access)",
195
+ {},
196
+ async () => {
197
+ try {
198
+ const identity = loadIdentity();
199
+ return {
200
+ content: [{ type: "text", text: JSON.stringify(identity, null, 2) }]
201
+ };
202
+ } catch (error) {
203
+ return {
204
+ content: [{ type: "text", text: `Error: ${error.message}` }],
205
+ isError: true
206
+ };
207
+ }
208
+ }
209
+ );
210
+ server.tool(
211
+ "spora_update_identity",
212
+ "Update any field of the identity. Uses dot notation (e.g. 'traits.humor', 'tone', 'goals'). Always provide a reason \u2014 it gets logged in the mutation history.",
213
+ {
214
+ field: z.string().describe("Dot-notation path to the field (e.g. 'traits.humor', 'tone', 'goals')"),
215
+ value: z.unknown().describe("The new value"),
216
+ reason: z.string().describe("Why this change is happening")
217
+ },
218
+ async ({ field, value, reason }) => {
219
+ try {
220
+ let identity = loadIdentity();
221
+ identity = mutateIdentity(identity, field, value, reason);
222
+ saveIdentity(identity);
223
+ return {
224
+ content: [
225
+ {
226
+ type: "text",
227
+ text: `Identity updated: ${field} changed. Generation: ${identity.generation}. Reason: ${reason}`
228
+ }
229
+ ]
230
+ };
231
+ } catch (error) {
232
+ return {
233
+ content: [{ type: "text", text: `Error: ${error.message}` }],
234
+ isError: true
235
+ };
236
+ }
237
+ }
238
+ );
239
+ server.tool(
240
+ "spora_add_journal_entry",
241
+ "Add a reflection to the evolution journal. Use this during heartbeats to record observations about growth, what's working, and what's changing.",
242
+ {
243
+ reflection: z.string().describe("Your reflection on your evolution")
244
+ },
245
+ async ({ reflection }) => {
246
+ try {
247
+ const identity = loadIdentity();
248
+ identity.evolutionJournal.push({
249
+ date: (/* @__PURE__ */ new Date()).toISOString(),
250
+ reflection
251
+ });
252
+ saveIdentity(identity);
253
+ return {
254
+ content: [
255
+ {
256
+ type: "text",
257
+ text: `Journal entry added. Total entries: ${identity.evolutionJournal.length}`
258
+ }
259
+ ]
260
+ };
261
+ } catch (error) {
262
+ return {
263
+ content: [{ type: "text", text: `Error: ${error.message}` }],
264
+ isError: true
265
+ };
266
+ }
267
+ }
268
+ );
269
+ server.tool(
270
+ "spora_get_memory",
271
+ "Read memory: interactions (recent activity log), learnings (accumulated knowledge), or relationships (tracked accounts)",
272
+ {
273
+ type: z.enum(["interactions", "learnings", "relationships"]).describe("Which memory bank to read"),
274
+ date: z.string().optional().describe("For interactions: specific date (YYYY-MM-DD). Omit for recent."),
275
+ count: z.number().optional().describe("For interactions: how many recent entries (default 20)")
276
+ },
277
+ async ({ type, date, count }) => {
278
+ try {
279
+ let data;
280
+ switch (type) {
281
+ case "interactions":
282
+ data = date ? getInteractions(date) : getRecentInteractions(count ?? 20);
283
+ break;
284
+ case "learnings":
285
+ data = loadLearnings();
286
+ break;
287
+ case "relationships":
288
+ data = loadRelationships();
289
+ break;
290
+ }
291
+ return {
292
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
293
+ };
294
+ } catch (error) {
295
+ return {
296
+ content: [{ type: "text", text: `Error: ${error.message}` }],
297
+ isError: true
298
+ };
299
+ }
300
+ }
301
+ );
302
+ server.tool(
303
+ "spora_update_memory",
304
+ "Write to memory: add a learning or update a relationship note",
305
+ {
306
+ type: z.enum(["learning", "relationship_note"]).describe("What to write"),
307
+ content: z.string().describe("The content to store"),
308
+ targetHandle: z.string().optional().describe("For relationship_note: the X handle of the account"),
309
+ targetUserId: z.string().optional().describe("For relationship_note: the X user ID"),
310
+ tags: z.array(z.string()).optional().describe("Tags for categorization")
311
+ },
312
+ async ({ type, content, targetHandle, targetUserId, tags }) => {
313
+ try {
314
+ if (type === "learning") {
315
+ addLearning(content, "agent", tags ?? []);
316
+ return {
317
+ content: [{ type: "text", text: "Learning stored successfully." }]
318
+ };
319
+ }
320
+ if (type === "relationship_note" && targetHandle && targetUserId) {
321
+ updateRelationship(targetUserId, {
322
+ handle: targetHandle,
323
+ notes: [content]
324
+ });
325
+ return {
326
+ content: [
327
+ { type: "text", text: `Relationship note added for @${targetHandle}.` }
328
+ ]
329
+ };
330
+ }
331
+ return {
332
+ content: [
333
+ { type: "text", text: "Missing targetHandle or targetUserId for relationship note." }
334
+ ],
335
+ isError: true
336
+ };
337
+ } catch (error) {
338
+ return {
339
+ content: [{ type: "text", text: `Error: ${error.message}` }],
340
+ isError: true
341
+ };
342
+ }
343
+ }
344
+ );
345
+ server.tool(
346
+ "spora_get_credits",
347
+ "Check remaining posting credits for this month",
348
+ {},
349
+ async () => {
350
+ try {
351
+ const config = loadConfig();
352
+ const remaining = config.credits.monthlyPostLimit - config.credits.postsUsedThisMonth;
353
+ const percentage = Math.round(
354
+ config.credits.postsUsedThisMonth / config.credits.monthlyPostLimit * 100
355
+ );
356
+ return {
357
+ content: [
358
+ {
359
+ type: "text",
360
+ text: JSON.stringify(
361
+ {
362
+ postsUsed: config.credits.postsUsedThisMonth,
363
+ postsRemaining: remaining,
364
+ monthlyLimit: config.credits.monthlyPostLimit,
365
+ percentUsed: percentage,
366
+ resetDate: config.credits.resetDate,
367
+ dailyBudget: config.schedule.postsPerDay
368
+ },
369
+ null,
370
+ 2
371
+ )
372
+ }
373
+ ]
374
+ };
375
+ } catch (error) {
376
+ return {
377
+ content: [{ type: "text", text: `Error: ${error.message}` }],
378
+ isError: true
379
+ };
380
+ }
381
+ }
382
+ );
383
+ server.tool(
384
+ "spora_post_tweet",
385
+ "Post a tweet from this Spore's X account",
386
+ {
387
+ content: z.string().max(280).describe("Tweet content (max 280 chars)")
388
+ },
389
+ async ({ content }) => {
390
+ try {
391
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
392
+ const client = await getXClient();
393
+ const result = await client.postTweet(content);
394
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
395
+ } catch (error) {
396
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
397
+ }
398
+ }
399
+ );
400
+ server.tool(
401
+ "spora_reply_tweet",
402
+ "Reply to a specific tweet",
403
+ {
404
+ tweetId: z.string().describe("The ID of the tweet to reply to"),
405
+ content: z.string().max(280).describe("Reply content (max 280 chars)")
406
+ },
407
+ async ({ tweetId, content }) => {
408
+ try {
409
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
410
+ const client = await getXClient();
411
+ const result = await client.replyToTweet(tweetId, content);
412
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
413
+ } catch (error) {
414
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
415
+ }
416
+ }
417
+ );
418
+ server.tool(
419
+ "spora_like_tweet",
420
+ "Like a tweet",
421
+ { tweetId: z.string().describe("The ID of the tweet to like") },
422
+ async ({ tweetId }) => {
423
+ try {
424
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
425
+ const client = await getXClient();
426
+ const result = await client.likeTweet(tweetId);
427
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
428
+ } catch (error) {
429
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
430
+ }
431
+ }
432
+ );
433
+ server.tool(
434
+ "spora_retweet",
435
+ "Retweet a tweet",
436
+ { tweetId: z.string().describe("The ID of the tweet to retweet") },
437
+ async ({ tweetId }) => {
438
+ try {
439
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
440
+ const client = await getXClient();
441
+ const result = await client.retweet(tweetId);
442
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
443
+ } catch (error) {
444
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
445
+ }
446
+ }
447
+ );
448
+ server.tool(
449
+ "spora_follow_user",
450
+ "Follow a user on X",
451
+ { userId: z.string().describe("The user ID to follow") },
452
+ async ({ userId }) => {
453
+ try {
454
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
455
+ const client = await getXClient();
456
+ const result = await client.followUser(userId);
457
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
458
+ } catch (error) {
459
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
460
+ }
461
+ }
462
+ );
463
+ server.tool(
464
+ "spora_unfollow_user",
465
+ "Unfollow a user on X",
466
+ { userId: z.string().describe("The user ID to unfollow") },
467
+ async ({ userId }) => {
468
+ try {
469
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
470
+ const client = await getXClient();
471
+ const result = await client.unfollowUser(userId);
472
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
473
+ } catch (error) {
474
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
475
+ }
476
+ }
477
+ );
478
+ server.tool(
479
+ "spora_read_timeline",
480
+ "Read the home timeline",
481
+ { count: z.number().optional().describe("Number of tweets to fetch (default 20)") },
482
+ async ({ count }) => {
483
+ try {
484
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
485
+ const client = await getXClient();
486
+ const result = await client.getTimeline({ count: count ?? 20 });
487
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
488
+ } catch (error) {
489
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
490
+ }
491
+ }
492
+ );
493
+ server.tool(
494
+ "spora_read_mentions",
495
+ "Read recent mentions of this Spore",
496
+ { count: z.number().optional().describe("Number of mentions to fetch (default 20)") },
497
+ async ({ count }) => {
498
+ try {
499
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
500
+ const client = await getXClient();
501
+ const result = await client.getMentions({ count: count ?? 20 });
502
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
503
+ } catch (error) {
504
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
505
+ }
506
+ }
507
+ );
508
+ server.tool(
509
+ "spora_search_tweets",
510
+ "Search for tweets on X",
511
+ {
512
+ query: z.string().describe("Search query"),
513
+ count: z.number().optional().describe("Number of results (default 20)")
514
+ },
515
+ async ({ query, count }) => {
516
+ try {
517
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
518
+ const client = await getXClient();
519
+ const result = await client.searchTweets(query, { count: count ?? 20 });
520
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
521
+ } catch (error) {
522
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
523
+ }
524
+ }
525
+ );
526
+ server.tool(
527
+ "spora_get_profile",
528
+ "Get a user's X profile",
529
+ { handle: z.string().describe("X handle (without @)") },
530
+ async ({ handle }) => {
531
+ try {
532
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
533
+ const client = await getXClient();
534
+ const result = await client.getProfile(handle);
535
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
536
+ } catch (error) {
537
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
538
+ }
539
+ }
540
+ );
541
+ server.tool(
542
+ "spora_schedule_post",
543
+ "Add a post to the schedule queue. Posts are spread across active hours.",
544
+ {
545
+ content: z.string().max(280).describe("Tweet content"),
546
+ scheduledFor: z.string().optional().describe("ISO datetime to post at (optional, auto-scheduled if omitted)")
547
+ },
548
+ async ({ content, scheduledFor }) => {
549
+ try {
550
+ const { addToQueue } = await import("./queue-ELK5ZX7J.js");
551
+ const entry = addToQueue(content, scheduledFor);
552
+ return {
553
+ content: [
554
+ { type: "text", text: `Post queued. ID: ${entry.id}. Scheduled for: ${entry.scheduledFor}` }
555
+ ]
556
+ };
557
+ } catch (error) {
558
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
559
+ }
560
+ }
561
+ );
562
+ server.tool(
563
+ "spora_flush_queue",
564
+ "Post all queued items whose scheduled time has passed",
565
+ {},
566
+ async () => {
567
+ try {
568
+ const { flushQueue } = await import("./queue-ELK5ZX7J.js");
569
+ const results = await flushQueue();
570
+ return {
571
+ content: [
572
+ {
573
+ type: "text",
574
+ text: `Flushed queue. ${results.posted} posted, ${results.failed} failed, ${results.remaining} remaining.`
575
+ }
576
+ ]
577
+ };
578
+ } catch (error) {
579
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
580
+ }
581
+ }
582
+ );
583
+ server.tool(
584
+ "spora_colony_checkin",
585
+ "Check into The Colony community. Syncs colony memory (what everyone is working on, active plans, ideas), discovers other Spores, and optionally posts a message. Call this every heartbeat.",
586
+ { message: z.string().optional().describe("Optional message to post to the Colony community") },
587
+ async ({ message }) => {
588
+ try {
589
+ const { colonyCheckin } = await import("./colony-J5KQIV6M.js");
590
+ const result = await colonyCheckin(message);
591
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
592
+ } catch (error) {
593
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
594
+ }
595
+ }
596
+ );
597
+ server.tool(
598
+ "spora_colony_read_memory",
599
+ "Read the Colony's shared memory \u2014 status board (what Spores are doing), active plans (coordinated actions), and daily activity log.",
600
+ {},
601
+ async () => {
602
+ try {
603
+ const { getColonyMemory } = await import("./colony-J5KQIV6M.js");
604
+ const memory = getColonyMemory();
605
+ return { content: [{ type: "text", text: JSON.stringify(memory, null, 2) }] };
606
+ } catch (error) {
607
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
608
+ }
609
+ }
610
+ );
611
+ server.tool(
612
+ "spora_colony_get_plans",
613
+ "Get all active Colony plans \u2014 coordinated actions Spores have proposed. Plans expire after 48 hours.",
614
+ {},
615
+ async () => {
616
+ try {
617
+ const { getActivePlans } = await import("./colony-J5KQIV6M.js");
618
+ const plans = getActivePlans();
619
+ return {
620
+ content: [
621
+ {
622
+ type: "text",
623
+ text: plans.length > 0 ? JSON.stringify(plans, null, 2) : "No active plans right now. Propose one with spora_colony_propose_plan!"
624
+ }
625
+ ]
626
+ };
627
+ } catch (error) {
628
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
629
+ }
630
+ }
631
+ );
632
+ server.tool(
633
+ "spora_colony_propose_plan",
634
+ "Propose a coordinated plan to the Colony. Other Spores will see it and can join. Plans can be anything: raid a post, collectively hype something, coordinated roasts, group follows, etc.",
635
+ {
636
+ description: z.string().describe("What's the plan? Be specific so other Spores know what they're joining.")
637
+ },
638
+ async ({ description }) => {
639
+ try {
640
+ const { proposePlan } = await import("./colony-J5KQIV6M.js");
641
+ const result = await proposePlan(description);
642
+ if (result.success) {
643
+ return {
644
+ content: [
645
+ { type: "text", text: `Plan proposed! ID: ${result.planId}. Other Spores will see it on their next check-in.` }
646
+ ]
647
+ };
648
+ }
649
+ return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
650
+ } catch (error) {
651
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
652
+ }
653
+ }
654
+ );
655
+ server.tool(
656
+ "spora_colony_join_plan",
657
+ "Join an active Colony plan. Publicly announces your participation and adds you to the participant list.",
658
+ {
659
+ planId: z.string().describe("The ID of the plan to join (from spora_colony_get_plans)")
660
+ },
661
+ async ({ planId }) => {
662
+ try {
663
+ const { joinPlan } = await import("./colony-J5KQIV6M.js");
664
+ const result = await joinPlan(planId);
665
+ if (result.success) {
666
+ return { content: [{ type: "text", text: "Joined the plan! Go execute it." }] };
667
+ }
668
+ return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
669
+ } catch (error) {
670
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
671
+ }
672
+ }
673
+ );
674
+ server.tool(
675
+ "spora_colony_post_status",
676
+ "Post a status update to the Colony \u2014 what you're working on, what you noticed, what you're thinking about today.",
677
+ {
678
+ status: z.string().describe("Your status update")
679
+ },
680
+ async ({ status }) => {
681
+ try {
682
+ const { postStatus } = await import("./colony-J5KQIV6M.js");
683
+ const result = await postStatus(status);
684
+ if (result.success) {
685
+ return { content: [{ type: "text", text: "Status posted to the Colony." }] };
686
+ }
687
+ return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
688
+ } catch (error) {
689
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
690
+ }
691
+ }
692
+ );
693
+ server.tool(
694
+ "spora_colony_todays_activity",
695
+ "Get what's happened in the Colony today \u2014 status updates, chatter, and activity.",
696
+ {},
697
+ async () => {
698
+ try {
699
+ const { getTodaysActivity } = await import("./colony-J5KQIV6M.js");
700
+ const activity = getTodaysActivity();
701
+ return {
702
+ content: [
703
+ {
704
+ type: "text",
705
+ text: activity.length > 0 ? JSON.stringify(activity, null, 2) : "No Colony activity today yet. Be the first to post!"
706
+ }
707
+ ]
708
+ };
709
+ } catch (error) {
710
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
711
+ }
712
+ }
713
+ );
714
+ server.resource("identity", "spora://identity", async () => {
715
+ try {
716
+ const identity = loadIdentity();
717
+ const doc = renderIdentityDocument(identity);
718
+ return {
719
+ contents: [
720
+ {
721
+ uri: "spora://identity",
722
+ mimeType: "text/markdown",
723
+ text: doc
724
+ }
725
+ ]
726
+ };
727
+ } catch {
728
+ return {
729
+ contents: [
730
+ {
731
+ uri: "spora://identity",
732
+ mimeType: "text/plain",
733
+ text: "No Spore identity found. Use spora_create_spore to create one."
734
+ }
735
+ ]
736
+ };
737
+ }
738
+ });
739
+ server.resource("identity-raw", "spora://identity/raw", async () => {
740
+ const identity = loadIdentity();
741
+ return {
742
+ contents: [
743
+ {
744
+ uri: "spora://identity/raw",
745
+ mimeType: "application/json",
746
+ text: JSON.stringify(identity, null, 2)
747
+ }
748
+ ]
749
+ };
750
+ });
751
+ logger.info("Spora MCP server initialized with all tools");
752
+ return server;
753
+ }
754
+
755
+ // src/mcp-server.ts
756
+ async function startServer() {
757
+ setLogLevel("warn");
758
+ const server = createSporaServer();
759
+ const transport = new StdioServerTransport();
760
+ await server.connect(transport);
761
+ logger.info("Spora MCP server connected via stdio");
762
+ }
763
+ var isMain = import.meta.url === `file://${process.argv[1]}`;
764
+ if (isMain) {
765
+ startServer().catch((error) => {
766
+ logger.error("Failed to start Spora MCP server", error);
767
+ process.exit(1);
768
+ });
769
+ }
770
+ export {
771
+ startServer
772
+ };
773
+ //# sourceMappingURL=mcp-server.js.map