posterly-mcp-server 0.19.1 → 0.19.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.
Files changed (111) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +50 -49
  3. package/dist/lib/api-client.js +1 -1
  4. package/dist/lib/format.d.ts +21 -0
  5. package/dist/lib/format.js +125 -0
  6. package/dist/tools/audit-google-business-profile.js +15 -12
  7. package/dist/tools/create-oauth-client.js +7 -1
  8. package/dist/tools/create-post.js +20 -27
  9. package/dist/tools/create-posts-batch.js +23 -15
  10. package/dist/tools/create-signed-upload.js +11 -8
  11. package/dist/tools/create-webhook.js +7 -2
  12. package/dist/tools/delete-google-business-review-reply.d.ts +1 -1
  13. package/dist/tools/delete-google-business-review-reply.js +4 -1
  14. package/dist/tools/delete-oauth-client.js +4 -1
  15. package/dist/tools/delete-post-group.js +5 -1
  16. package/dist/tools/delete-post.js +4 -1
  17. package/dist/tools/delete-webhook.js +4 -1
  18. package/dist/tools/disconnect-account.js +7 -5
  19. package/dist/tools/find-slot.js +10 -3
  20. package/dist/tools/generate-image.js +17 -19
  21. package/dist/tools/generate-video.js +11 -9
  22. package/dist/tools/get-account-analytics.js +64 -45
  23. package/dist/tools/get-brand-profile.js +34 -40
  24. package/dist/tools/get-brand.js +14 -16
  25. package/dist/tools/get-connect-link.js +26 -7
  26. package/dist/tools/get-google-business-review-link.js +6 -6
  27. package/dist/tools/get-platform-schema.js +23 -16
  28. package/dist/tools/get-post-analytics.js +53 -50
  29. package/dist/tools/get-post-missing.js +5 -1
  30. package/dist/tools/get-post.js +16 -14
  31. package/dist/tools/get-video-job.d.ts +2 -2
  32. package/dist/tools/get-video-job.js +28 -11
  33. package/dist/tools/get-video-options.js +21 -15
  34. package/dist/tools/get-x-posting-quota.js +12 -9
  35. package/dist/tools/list-accounts.js +12 -4
  36. package/dist/tools/list-activity.js +13 -18
  37. package/dist/tools/list-brand-accounts.js +11 -3
  38. package/dist/tools/list-brands.js +12 -11
  39. package/dist/tools/list-google-business-reviews.js +13 -11
  40. package/dist/tools/list-oauth-clients.js +10 -4
  41. package/dist/tools/list-platforms.js +12 -5
  42. package/dist/tools/list-posts.js +12 -7
  43. package/dist/tools/list-webhooks.js +13 -11
  44. package/dist/tools/reply-google-business-review.d.ts +1 -1
  45. package/dist/tools/reply-google-business-review.js +4 -1
  46. package/dist/tools/run-video-function.js +4 -4
  47. package/dist/tools/suggest-google-business-review-reply.js +5 -4
  48. package/dist/tools/test-webhook.js +7 -1
  49. package/dist/tools/trigger-platform-helper.js +5 -1
  50. package/dist/tools/update-oauth-client.js +8 -1
  51. package/dist/tools/update-post-release-id.js +5 -1
  52. package/dist/tools/update-post-status.d.ts +2 -2
  53. package/dist/tools/update-post-status.js +7 -1
  54. package/dist/tools/update-post.js +10 -4
  55. package/dist/tools/update-webhook.js +7 -1
  56. package/dist/tools/upload-media-from-url.js +7 -9
  57. package/dist/tools/upload-media.js +6 -2
  58. package/dist/tools/whoami.js +18 -17
  59. package/package.json +1 -1
  60. package/src/index.ts +50 -49
  61. package/src/lib/api-client.ts +1 -1
  62. package/src/lib/format.ts +132 -0
  63. package/src/tools/audit-google-business-profile.ts +18 -12
  64. package/src/tools/create-oauth-client.ts +8 -2
  65. package/src/tools/create-post.ts +20 -28
  66. package/src/tools/create-posts-batch.ts +23 -18
  67. package/src/tools/create-signed-upload.ts +11 -8
  68. package/src/tools/create-webhook.ts +7 -3
  69. package/src/tools/delete-google-business-review-reply.ts +4 -1
  70. package/src/tools/delete-oauth-client.ts +4 -1
  71. package/src/tools/delete-post-group.ts +5 -1
  72. package/src/tools/delete-post.ts +4 -1
  73. package/src/tools/delete-webhook.ts +4 -2
  74. package/src/tools/disconnect-account.ts +7 -5
  75. package/src/tools/find-slot.ts +13 -5
  76. package/src/tools/generate-image.ts +20 -20
  77. package/src/tools/generate-video.ts +11 -9
  78. package/src/tools/get-account-analytics.ts +70 -49
  79. package/src/tools/get-brand-profile.ts +34 -38
  80. package/src/tools/get-brand.ts +14 -14
  81. package/src/tools/get-connect-link.ts +29 -7
  82. package/src/tools/get-google-business-review-link.ts +6 -6
  83. package/src/tools/get-platform-schema.ts +29 -16
  84. package/src/tools/get-post-analytics.ts +58 -51
  85. package/src/tools/get-post-missing.ts +5 -2
  86. package/src/tools/get-post.ts +16 -13
  87. package/src/tools/get-video-job.ts +31 -11
  88. package/src/tools/get-video-options.ts +27 -15
  89. package/src/tools/get-x-posting-quota.ts +12 -9
  90. package/src/tools/list-accounts.ts +15 -6
  91. package/src/tools/list-activity.ts +16 -20
  92. package/src/tools/list-brand-accounts.ts +14 -6
  93. package/src/tools/list-brands.ts +16 -13
  94. package/src/tools/list-google-business-reviews.ts +16 -12
  95. package/src/tools/list-oauth-clients.ts +13 -4
  96. package/src/tools/list-platforms.ts +15 -5
  97. package/src/tools/list-posts.ts +15 -9
  98. package/src/tools/list-webhooks.ts +16 -12
  99. package/src/tools/reply-google-business-review.ts +4 -1
  100. package/src/tools/run-video-function.ts +4 -4
  101. package/src/tools/suggest-google-business-review-reply.ts +5 -4
  102. package/src/tools/test-webhook.ts +7 -1
  103. package/src/tools/trigger-platform-helper.ts +5 -1
  104. package/src/tools/update-oauth-client.ts +9 -2
  105. package/src/tools/update-post-release-id.ts +5 -2
  106. package/src/tools/update-post-status.ts +7 -1
  107. package/src/tools/update-post.ts +10 -5
  108. package/src/tools/update-webhook.ts +7 -2
  109. package/src/tools/upload-media-from-url.ts +7 -9
  110. package/src/tools/upload-media.ts +6 -2
  111. package/src/tools/whoami.ts +21 -23
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { mdError } from './lib/format.js';
5
6
  import { PosterlyClient } from './lib/api-client.js';
6
7
  import { listAccountsTool } from './tools/list-accounts.js';
7
8
  import { disconnectAccountTool } from './tools/disconnect-account.js';
@@ -77,7 +78,7 @@ server.tool(
77
78
  const text = await whoamiTool.execute(client);
78
79
  return { content: [{ type: 'text' as const, text }] };
79
80
  } catch (err: any) {
80
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
81
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
81
82
  }
82
83
  }
83
84
  );
@@ -91,7 +92,7 @@ server.tool(
91
92
  const text = await listAccountsTool.execute(client, input as any);
92
93
  return { content: [{ type: 'text' as const, text }] };
93
94
  } catch (err: any) {
94
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
95
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
95
96
  }
96
97
  }
97
98
  );
@@ -105,7 +106,7 @@ server.tool(
105
106
  const text = await disconnectAccountTool.execute(client, input as any);
106
107
  return { content: [{ type: 'text' as const, text }] };
107
108
  } catch (err: any) {
108
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
109
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
109
110
  }
110
111
  }
111
112
  );
@@ -119,7 +120,7 @@ server.tool(
119
120
  const text = await getConnectLinkTool.execute(client, input as any);
120
121
  return { content: [{ type: 'text' as const, text }] };
121
122
  } catch (err: any) {
122
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
123
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
123
124
  }
124
125
  }
125
126
  );
@@ -133,7 +134,7 @@ server.tool(
133
134
  const text = await listOAuthClientsTool.execute(client);
134
135
  return { content: [{ type: 'text' as const, text }] };
135
136
  } catch (err: any) {
136
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
137
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
137
138
  }
138
139
  }
139
140
  );
@@ -147,7 +148,7 @@ server.tool(
147
148
  const text = await createOAuthClientTool.execute(client, input as any);
148
149
  return { content: [{ type: 'text' as const, text }] };
149
150
  } catch (err: any) {
150
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
151
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
151
152
  }
152
153
  }
153
154
  );
@@ -161,7 +162,7 @@ server.tool(
161
162
  const text = await updateOAuthClientTool.execute(client, input as any);
162
163
  return { content: [{ type: 'text' as const, text }] };
163
164
  } catch (err: any) {
164
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
165
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
165
166
  }
166
167
  }
167
168
  );
@@ -175,7 +176,7 @@ server.tool(
175
176
  const text = await deleteOAuthClientTool.execute(client, input as any);
176
177
  return { content: [{ type: 'text' as const, text }] };
177
178
  } catch (err: any) {
178
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
179
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
179
180
  }
180
181
  }
181
182
  );
@@ -189,7 +190,7 @@ server.tool(
189
190
  const text = await listPlatformsTool.execute(client, input as any);
190
191
  return { content: [{ type: 'text' as const, text }] };
191
192
  } catch (err: any) {
192
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
193
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
193
194
  }
194
195
  }
195
196
  );
@@ -203,7 +204,7 @@ server.tool(
203
204
  const text = await getPlatformSchemaTool.execute(client, input as any);
204
205
  return { content: [{ type: 'text' as const, text }] };
205
206
  } catch (err: any) {
206
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
207
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
207
208
  }
208
209
  }
209
210
  );
@@ -217,7 +218,7 @@ server.tool(
217
218
  const text = await triggerPlatformHelperTool.execute(client, input as any);
218
219
  return { content: [{ type: 'text' as const, text }] };
219
220
  } catch (err: any) {
220
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
221
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
221
222
  }
222
223
  }
223
224
  );
@@ -231,7 +232,7 @@ server.tool(
231
232
  const text = await listBrandsTool.execute(client, input as any);
232
233
  return { content: [{ type: 'text' as const, text }] };
233
234
  } catch (err: any) {
234
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
235
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
235
236
  }
236
237
  }
237
238
  );
@@ -245,7 +246,7 @@ server.tool(
245
246
  const text = await getBrandTool.execute(client, input as any);
246
247
  return { content: [{ type: 'text' as const, text }] };
247
248
  } catch (err: any) {
248
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
249
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
249
250
  }
250
251
  }
251
252
  );
@@ -259,7 +260,7 @@ server.tool(
259
260
  const text = await listBrandAccountsTool.execute(client, input as any);
260
261
  return { content: [{ type: 'text' as const, text }] };
261
262
  } catch (err: any) {
262
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
263
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
263
264
  }
264
265
  }
265
266
  );
@@ -273,7 +274,7 @@ server.tool(
273
274
  const text = await getBrandProfileTool.execute(client, input as any);
274
275
  return { content: [{ type: 'text' as const, text }] };
275
276
  } catch (err: any) {
276
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
277
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
277
278
  }
278
279
  }
279
280
  );
@@ -287,7 +288,7 @@ server.tool(
287
288
  const text = await createPostTool.execute(client, input as any);
288
289
  return { content: [{ type: 'text' as const, text }] };
289
290
  } catch (err: any) {
290
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
291
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
291
292
  }
292
293
  }
293
294
  );
@@ -301,7 +302,7 @@ server.tool(
301
302
  const text = await createPostsBatchTool.execute(client, input as any);
302
303
  return { content: [{ type: 'text' as const, text }] };
303
304
  } catch (err: any) {
304
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
305
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
305
306
  }
306
307
  }
307
308
  );
@@ -315,7 +316,7 @@ server.tool(
315
316
  const text = await findSlotTool.execute(client, input as any);
316
317
  return { content: [{ type: 'text' as const, text }] };
317
318
  } catch (err: any) {
318
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
319
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
319
320
  }
320
321
  }
321
322
  );
@@ -329,7 +330,7 @@ server.tool(
329
330
  const text = await listPostsTool.execute(client, input as any);
330
331
  return { content: [{ type: 'text' as const, text }] };
331
332
  } catch (err: any) {
332
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
333
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
333
334
  }
334
335
  }
335
336
  );
@@ -343,7 +344,7 @@ server.tool(
343
344
  const text = await uploadMediaTool.execute(client, input as any);
344
345
  return { content: [{ type: 'text' as const, text }] };
345
346
  } catch (err: any) {
346
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
347
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
347
348
  }
348
349
  }
349
350
  );
@@ -357,7 +358,7 @@ server.tool(
357
358
  const text = await uploadMediaFromUrlTool.execute(client, input as any);
358
359
  return { content: [{ type: 'text' as const, text }] };
359
360
  } catch (err: any) {
360
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
361
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
361
362
  }
362
363
  }
363
364
  );
@@ -371,7 +372,7 @@ server.tool(
371
372
  const text = await createSignedUploadTool.execute(client, input as any);
372
373
  return { content: [{ type: 'text' as const, text }] };
373
374
  } catch (err: any) {
374
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
375
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
375
376
  }
376
377
  }
377
378
  );
@@ -385,7 +386,7 @@ server.tool(
385
386
  const text = await getVideoOptionsTool.execute(client);
386
387
  return { content: [{ type: 'text' as const, text }] };
387
388
  } catch (err: any) {
388
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
389
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
389
390
  }
390
391
  }
391
392
  );
@@ -399,7 +400,7 @@ server.tool(
399
400
  const text = await runVideoFunctionTool.execute(client, input as any);
400
401
  return { content: [{ type: 'text' as const, text }] };
401
402
  } catch (err: any) {
402
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
403
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
403
404
  }
404
405
  }
405
406
  );
@@ -413,7 +414,7 @@ server.tool(
413
414
  const text = await generateVideoTool.execute(client, input as any);
414
415
  return { content: [{ type: 'text' as const, text }] };
415
416
  } catch (err: any) {
416
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
417
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
417
418
  }
418
419
  }
419
420
  );
@@ -427,7 +428,7 @@ server.tool(
427
428
  const text = await getVideoJobTool.execute(client, input as any);
428
429
  return { content: [{ type: 'text' as const, text }] };
429
430
  } catch (err: any) {
430
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
431
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
431
432
  }
432
433
  }
433
434
  );
@@ -441,7 +442,7 @@ server.tool(
441
442
  const text = await generateImageTool.execute(client, input as any);
442
443
  return { content: [{ type: 'text' as const, text }] };
443
444
  } catch (err: any) {
444
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
445
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
445
446
  }
446
447
  }
447
448
  );
@@ -455,7 +456,7 @@ server.tool(
455
456
  const text = await getPostTool.execute(client, input as any);
456
457
  return { content: [{ type: 'text' as const, text }] };
457
458
  } catch (err: any) {
458
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
459
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
459
460
  }
460
461
  }
461
462
  );
@@ -469,7 +470,7 @@ server.tool(
469
470
  const text = await getPostMissingTool.execute(client, input as any);
470
471
  return { content: [{ type: 'text' as const, text }] };
471
472
  } catch (err: any) {
472
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
473
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
473
474
  }
474
475
  }
475
476
  );
@@ -483,7 +484,7 @@ server.tool(
483
484
  const text = await updatePostTool.execute(client, input as any);
484
485
  return { content: [{ type: 'text' as const, text }] };
485
486
  } catch (err: any) {
486
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
487
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
487
488
  }
488
489
  }
489
490
  );
@@ -497,7 +498,7 @@ server.tool(
497
498
  const text = await updatePostStatusTool.execute(client, input as any);
498
499
  return { content: [{ type: 'text' as const, text }] };
499
500
  } catch (err: any) {
500
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
501
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
501
502
  }
502
503
  }
503
504
  );
@@ -511,7 +512,7 @@ server.tool(
511
512
  const text = await updatePostReleaseIdTool.execute(client, input as any);
512
513
  return { content: [{ type: 'text' as const, text }] };
513
514
  } catch (err: any) {
514
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
515
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
515
516
  }
516
517
  }
517
518
  );
@@ -525,7 +526,7 @@ server.tool(
525
526
  const text = await deletePostTool.execute(client, input as any);
526
527
  return { content: [{ type: 'text' as const, text }] };
527
528
  } catch (err: any) {
528
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
529
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
529
530
  }
530
531
  }
531
532
  );
@@ -539,7 +540,7 @@ server.tool(
539
540
  const text = await deletePostGroupTool.execute(client, input as any);
540
541
  return { content: [{ type: 'text' as const, text }] };
541
542
  } catch (err: any) {
542
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
543
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
543
544
  }
544
545
  }
545
546
  );
@@ -553,7 +554,7 @@ server.tool(
553
554
  const text = await getAccountAnalyticsTool.execute(client, input as any);
554
555
  return { content: [{ type: 'text' as const, text }] };
555
556
  } catch (err: any) {
556
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
557
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
557
558
  }
558
559
  }
559
560
  );
@@ -567,7 +568,7 @@ server.tool(
567
568
  const text = await getPostAnalyticsTool.execute(client, input as any);
568
569
  return { content: [{ type: 'text' as const, text }] };
569
570
  } catch (err: any) {
570
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
571
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
571
572
  }
572
573
  }
573
574
  );
@@ -581,7 +582,7 @@ server.tool(
581
582
  const text = await listGoogleBusinessReviewsTool.execute(client, input as any);
582
583
  return { content: [{ type: 'text' as const, text }] };
583
584
  } catch (err: any) {
584
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
585
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
585
586
  }
586
587
  }
587
588
  );
@@ -595,7 +596,7 @@ server.tool(
595
596
  const text = await getGoogleBusinessReviewLinkTool.execute(client, input as any);
596
597
  return { content: [{ type: 'text' as const, text }] };
597
598
  } catch (err: any) {
598
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
599
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
599
600
  }
600
601
  }
601
602
  );
@@ -609,7 +610,7 @@ server.tool(
609
610
  const text = await auditGoogleBusinessProfileTool.execute(client, input as any);
610
611
  return { content: [{ type: 'text' as const, text }] };
611
612
  } catch (err: any) {
612
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
613
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
613
614
  }
614
615
  }
615
616
  );
@@ -623,7 +624,7 @@ server.tool(
623
624
  const text = await suggestGoogleBusinessReviewReplyTool.execute(client, input as any);
624
625
  return { content: [{ type: 'text' as const, text }] };
625
626
  } catch (err: any) {
626
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
627
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
627
628
  }
628
629
  }
629
630
  );
@@ -637,7 +638,7 @@ server.tool(
637
638
  const text = await replyGoogleBusinessReviewTool.execute(client, input as any);
638
639
  return { content: [{ type: 'text' as const, text }] };
639
640
  } catch (err: any) {
640
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
641
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
641
642
  }
642
643
  }
643
644
  );
@@ -651,7 +652,7 @@ server.tool(
651
652
  const text = await deleteGoogleBusinessReviewReplyTool.execute(client, input as any);
652
653
  return { content: [{ type: 'text' as const, text }] };
653
654
  } catch (err: any) {
654
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
655
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
655
656
  }
656
657
  }
657
658
  );
@@ -665,7 +666,7 @@ server.tool(
665
666
  const text = await listActivityTool.execute(client, input as any);
666
667
  return { content: [{ type: 'text' as const, text }] };
667
668
  } catch (err: any) {
668
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
669
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
669
670
  }
670
671
  }
671
672
  );
@@ -679,7 +680,7 @@ server.tool(
679
680
  const text = await listWebhooksTool.execute(client, input as any);
680
681
  return { content: [{ type: 'text' as const, text }] };
681
682
  } catch (err: any) {
682
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
683
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
683
684
  }
684
685
  }
685
686
  );
@@ -693,7 +694,7 @@ server.tool(
693
694
  const text = await createWebhookTool.execute(client, input as any);
694
695
  return { content: [{ type: 'text' as const, text }] };
695
696
  } catch (err: any) {
696
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
697
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
697
698
  }
698
699
  }
699
700
  );
@@ -707,7 +708,7 @@ server.tool(
707
708
  const text = await updateWebhookTool.execute(client, input as any);
708
709
  return { content: [{ type: 'text' as const, text }] };
709
710
  } catch (err: any) {
710
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
711
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
711
712
  }
712
713
  }
713
714
  );
@@ -721,7 +722,7 @@ server.tool(
721
722
  const text = await deleteWebhookTool.execute(client, input as any);
722
723
  return { content: [{ type: 'text' as const, text }] };
723
724
  } catch (err: any) {
724
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
725
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
725
726
  }
726
727
  }
727
728
  );
@@ -735,7 +736,7 @@ server.tool(
735
736
  const text = await testWebhookTool.execute(client, input as any);
736
737
  return { content: [{ type: 'text' as const, text }] };
737
738
  } catch (err: any) {
738
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
739
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
739
740
  }
740
741
  }
741
742
  );
@@ -749,7 +750,7 @@ server.tool(
749
750
  const text = await getXPostingQuotaTool.execute(client, input as any);
750
751
  return { content: [{ type: 'text' as const, text }] };
751
752
  } catch (err: any) {
752
- return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
753
+ return { content: [{ type: 'text' as const, text: mdError(err.message) }], isError: true };
753
754
  }
754
755
  }
755
756
  );
@@ -895,7 +895,7 @@ export class PosterlyClient {
895
895
  const SIZE_THRESHOLD = 3 * 1024 * 1024; // 3MB
896
896
 
897
897
  if (fileBuffer.length > SIZE_THRESHOLD) {
898
- // Large file: use signed URL to upload directly to Supabase Storage
898
+ // Large file: use signed URL so the API can validate actual bytes before storage.
899
899
  const signed = await this.getSignedUploadUrl(
900
900
  input.filename,
901
901
  contentType,
@@ -0,0 +1,132 @@
1
+ type CellValue = string | number | boolean | null | undefined;
2
+
3
+ export function mdTitle(title: string, subtitle?: string): string {
4
+ return [`## ${title}`, subtitle ? `_${subtitle}_` : ''].filter(Boolean).join('\n');
5
+ }
6
+
7
+ export function mdTable(headers: string[], rows: CellValue[][]): string {
8
+ const visibleRows = rows.filter((row) => row.some((cell) => cell !== undefined && cell !== null && cell !== ''));
9
+ if (visibleRows.length === 0) return '';
10
+
11
+ const header = `| ${headers.map(escapeCell).join(' | ')} |`;
12
+ const divider = `| ${headers.map(() => '---').join(' | ')} |`;
13
+ const body = visibleRows.map((row) => `| ${row.map((cell) => escapeCell(formatCell(cell))).join(' | ')} |`);
14
+
15
+ return [header, divider, ...body].join('\n');
16
+ }
17
+
18
+ export function mdKeyValue(rows: Array<[string, CellValue]>): string {
19
+ return mdTable(['Field', 'Value'], rows);
20
+ }
21
+
22
+ export function mdSection(title: string, body?: string): string {
23
+ if (!body) return '';
24
+ return [`### ${title}`, body].join('\n');
25
+ }
26
+
27
+ export function mdBullets(items: Array<string | null | undefined>): string {
28
+ return items.filter(Boolean).map((item) => `- ${item}`).join('\n');
29
+ }
30
+
31
+ export function mdJson(title: string, value: unknown): string {
32
+ return [`### ${title}`, '```json', JSON.stringify(value, null, 2), '```'].join('\n');
33
+ }
34
+
35
+ export function mdQuote(text: string): string {
36
+ return text.split('\n').map((line) => `> ${line}`).join('\n');
37
+ }
38
+
39
+ export function mdEmpty(title: string, detail: string, nextStep?: string): string {
40
+ return [
41
+ mdTitle(`No ${title}`),
42
+ detail,
43
+ nextStep ? `\n**Next step:** ${nextStep}` : '',
44
+ ].filter(Boolean).join('\n');
45
+ }
46
+
47
+ export function mdSuccess(title: string, rows: Array<[string, CellValue]>, nextStep?: string): string {
48
+ return [
49
+ mdTitle(`✅ ${title}`),
50
+ mdKeyValue(rows),
51
+ nextStep ? `\n**Next step:** ${nextStep}` : '',
52
+ ].filter(Boolean).join('\n');
53
+ }
54
+
55
+ export function mdError(message: string): string {
56
+ return [
57
+ mdTitle('⚠️ Posterly tool error'),
58
+ `**Message:** ${message}`,
59
+ ].join('\n');
60
+ }
61
+
62
+ export function formatNumber(value: number | null | undefined): string {
63
+ return value == null ? 'n/a' : value.toLocaleString();
64
+ }
65
+
66
+ export function formatPercent(value: number | null | undefined): string {
67
+ return value == null ? 'n/a' : `${value.toLocaleString()}%`;
68
+ }
69
+
70
+ export function formatDelta(value: number | null | undefined): string {
71
+ if (value == null) return 'n/a';
72
+ return value >= 0 ? `+${value.toLocaleString()}` : value.toLocaleString();
73
+ }
74
+
75
+ export function formatDateTime(value: string | null | undefined): string {
76
+ if (!value) return 'n/a';
77
+ const date = new Date(value);
78
+ if (Number.isNaN(date.getTime())) return value;
79
+ return date.toLocaleString();
80
+ }
81
+
82
+ export function formatDate(value: string | null | undefined): string {
83
+ if (!value) return 'n/a';
84
+ const date = new Date(value);
85
+ if (Number.isNaN(date.getTime())) return value;
86
+ return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' });
87
+ }
88
+
89
+ export function formatBytes(value: number | null | undefined): string {
90
+ if (value == null) return 'n/a';
91
+ if (value < 1024) return `${value} B`;
92
+ const units = ['KB', 'MB', 'GB', 'TB'];
93
+ let size = value / 1024;
94
+ let unitIndex = 0;
95
+ while (size >= 1024 && unitIndex < units.length - 1) {
96
+ size /= 1024;
97
+ unitIndex += 1;
98
+ }
99
+ return `${size.toFixed(size >= 10 ? 0 : 1)} ${units[unitIndex]}`;
100
+ }
101
+
102
+ export function compactText(value: string | null | undefined, maxLength = 90): string {
103
+ const text = String(value || '').replace(/\s+/g, ' ').trim();
104
+ if (!text) return '';
105
+ return text.length > maxLength ? `${text.slice(0, Math.max(0, maxLength - 1))}…` : text;
106
+ }
107
+
108
+ export function statusLabel(status: string | null | undefined): string {
109
+ const value = String(status || 'unknown').toLowerCase();
110
+ if (['created', 'success', 'succeeded', 'completed', 'published', 'active'].includes(value)) return `✅ ${status}`;
111
+ if (['scheduled', 'queued', 'pending', 'processing'].includes(value)) return `🕒 ${status}`;
112
+ if (['failed', 'error', 'inactive', 'deleted'].includes(value)) return `⚠️ ${status}`;
113
+ if (['draft', 'paused'].includes(value)) return `⏸️ ${status}`;
114
+ return status || 'unknown';
115
+ }
116
+
117
+ export function code(value: CellValue): string {
118
+ return `\`${formatCell(value).replace(/`/g, '\\`')}\``;
119
+ }
120
+
121
+ function formatCell(value: CellValue): string {
122
+ if (value === null || value === undefined || value === '') return 'n/a';
123
+ if (typeof value === 'boolean') return value ? 'yes' : 'no';
124
+ if (typeof value === 'number') return value.toLocaleString();
125
+ return String(value);
126
+ }
127
+
128
+ function escapeCell(value: CellValue): string {
129
+ return formatCell(value)
130
+ .replace(/\|/g, '\\|')
131
+ .replace(/\r?\n/g, '<br>');
132
+ }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { formatDateTime, mdKeyValue, mdSection, mdTable, mdTitle } from '../lib/format.js';
3
4
 
4
5
  export const auditGoogleBusinessProfileTool = {
5
6
  name: 'audit_google_business_profile',
@@ -22,17 +23,22 @@ export const auditGoogleBusinessProfileTool = {
22
23
  const result = await client.auditGoogleBusinessProfile(input);
23
24
  const audit = result.audit || {};
24
25
  const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
25
- const lines = [
26
- `Google Business audit: ${audit.businessName || 'Unknown business'}`,
27
- `Score: ${audit.overallScore ?? 'n/a'} (${audit.overallGrade || 'n/a'})`,
28
- `Fetched: ${result.fetchedAt || 'unknown'}`,
29
- ];
30
- if (recommendations.length > 0) {
31
- lines.push('Top recommendations:');
32
- for (const rec of recommendations.slice(0, 5)) {
33
- lines.push(`- ${rec.priority || 'medium'}: ${rec.description || rec.title || 'Recommendation'}`);
34
- }
35
- }
36
- return lines.join('\n');
26
+ return [
27
+ mdTitle(`Google Business audit: ${audit.businessName || 'Unknown business'}`),
28
+ mdKeyValue([
29
+ ['Score', audit.overallScore ?? 'n/a'],
30
+ ['Grade', audit.overallGrade || 'n/a'],
31
+ ['Fetched', formatDateTime(result.fetchedAt)],
32
+ ]),
33
+ recommendations.length > 0
34
+ ? mdSection('Top recommendations', mdTable(
35
+ ['Priority', 'Recommendation'],
36
+ recommendations.slice(0, 5).map((rec: any) => [
37
+ rec.priority || 'medium',
38
+ rec.description || rec.title || 'Recommendation',
39
+ ]),
40
+ ))
41
+ : '',
42
+ ].filter(Boolean).join('\n\n');
37
43
  },
38
44
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdSuccess } from '../lib/format.js';
3
4
 
4
5
  const scopeSchema = z.enum(['accounts:read', 'accounts:write', 'posts:read', 'posts:write', 'media:write', 'analytics:read']);
5
6
 
@@ -28,9 +29,14 @@ export const createOAuthClientTool = {
28
29
  is_active?: boolean;
29
30
  confirm: true;
30
31
  },
31
- ) {
32
+ ) {
32
33
  const { confirm: _confirm, ...payload } = input;
33
34
  const result = await client.createOAuthClient(payload);
34
- return `OAuth client created: ${result.client.client_name}\nclient_id: ${result.client.client_id}\nRedirect URIs: ${result.client.allowed_redirect_uris.join(', ')}`;
35
+ return mdSuccess('OAuth client created', [
36
+ ['Client', result.client.client_name],
37
+ ['Client ID', code(result.client.client_id)],
38
+ ['Redirect URIs', result.client.allowed_redirect_uris.join(', ')],
39
+ ['Default scopes', result.client.default_scopes.join(', ')],
40
+ ]);
35
41
  },
36
42
  };