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
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdEmpty, mdTable, mdTitle } from '../lib/format.js';
3
4
 
4
5
  export const listBrandsTool = {
5
6
  name: 'list_brands',
@@ -17,20 +18,22 @@ export const listBrandsTool = {
17
18
 
18
19
  if (brands.length === 0) {
19
20
  return input.workspace_id
20
- ? 'No brands found in this workspace.'
21
- : 'No brands found. Create brands in the posterly dashboard first.';
21
+ ? mdEmpty('brands', 'This workspace does not have any brands or clients yet.')
22
+ : mdEmpty('brands', 'No brands or clients were found for this API key.', 'Create a brand in the Posterly dashboard, then run this again.');
22
23
  }
23
24
 
24
- const lines = brands.map((brand) => {
25
- const suffix = [
26
- `ID: ${brand.id}`,
27
- brand.workspace_id ? `ws: ${brand.workspace_id}` : null,
28
- `accounts: ${brand.account_count}`,
29
- brand.source,
30
- ].filter(Boolean).join(', ');
31
- return `• ${brand.name} (${suffix})`;
32
- });
33
-
34
- return `Brands (${brands.length}):\n${lines.join('\n')}`;
25
+ return [
26
+ mdTitle(`Brands (${brands.length})`),
27
+ mdTable(
28
+ ['Brand', 'Brand ID', 'Accounts', 'Source', 'Workspace'],
29
+ brands.map((brand) => [
30
+ brand.name,
31
+ code(brand.id),
32
+ brand.account_count,
33
+ brand.source,
34
+ brand.workspace_id ? code(brand.workspace_id) : 'default',
35
+ ]),
36
+ ),
37
+ ].join('\n\n');
35
38
  },
36
39
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { compactText, formatDateTime, mdEmpty, mdTable, mdTitle, statusLabel } from '../lib/format.js';
3
4
 
4
5
  export const listGoogleBusinessReviewsTool = {
5
6
  name: 'list_google_business_reviews',
@@ -25,19 +26,22 @@ export const listGoogleBusinessReviewsTool = {
25
26
  ) {
26
27
  const result = await client.listGoogleBusinessReviews(input);
27
28
  if (result.reviews.length === 0) {
28
- return 'No Google Business reviews found matching your filters.';
29
+ return mdEmpty('Google Business reviews', 'No reviews matched those filters.');
29
30
  }
30
31
 
31
- const lines = [`Google Business reviews (${result.reviews.length} of ${result.total}):`];
32
- for (const review of result.reviews.slice(0, 25)) {
33
- const name = review.reviewer?.displayName || 'Anonymous';
34
- const rating = review.starRating ? `${review.starRating} stars` : 'unrated';
35
- const location = review.businessName || review.locationName || review.locationId || 'location';
36
- const text = review.comment ? ` - ${review.comment.replace(/\s+/g, ' ').slice(0, 180)}` : '';
37
- const replied = review.reviewReply ? ' replied' : ' unanswered';
38
- lines.push(`- ${name} (${rating}, ${location},${replied})${text}`);
39
- }
40
-
41
- return lines.join('\n');
32
+ return [
33
+ mdTitle(`Google Business reviews (${result.reviews.length} of ${result.total})`),
34
+ mdTable(
35
+ ['Reviewer', 'Rating', 'Location', 'Reply', 'Updated', 'Comment'],
36
+ result.reviews.slice(0, 25).map((review) => [
37
+ review.reviewer?.displayName || 'Anonymous',
38
+ review.starRating ? `${review.starRating} stars` : 'unrated',
39
+ review.businessName || review.locationName || review.locationId || 'location',
40
+ statusLabel(review.reviewReply ? 'replied' : 'unanswered'),
41
+ formatDateTime(review.updateTime || review.createTime),
42
+ compactText(review.comment, 140) || '(rating only)',
43
+ ]),
44
+ ),
45
+ ].join('\n\n');
42
46
  },
43
47
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdEmpty, mdTable, mdTitle, statusLabel } from '../lib/format.js';
3
4
 
4
5
  export const listOAuthClientsTool = {
5
6
  name: 'list_oauth_clients',
@@ -9,10 +10,18 @@ export const listOAuthClientsTool = {
9
10
 
10
11
  async execute(client: PosterlyClient) {
11
12
  const result = await client.listOAuthClients();
12
- if (result.clients.length === 0) return 'No OAuth developer clients found.';
13
+ if (result.clients.length === 0) return mdEmpty('OAuth developer clients', 'No OAuth developer clients were found.');
13
14
  return [
14
- `OAuth developer clients (${result.clients.length}):`,
15
- ...result.clients.map((item) => `- ${item.client_name} (${item.client_id}) scopes=${item.default_scopes.join(', ')} active=${item.is_active}`),
16
- ].join('\n');
15
+ mdTitle(`OAuth developer clients (${result.clients.length})`),
16
+ mdTable(
17
+ ['Client', 'Client ID', 'Scopes', 'State'],
18
+ result.clients.map((item) => [
19
+ item.client_name,
20
+ code(item.client_id),
21
+ item.default_scopes.join(', '),
22
+ statusLabel(item.is_active ? 'active' : 'inactive'),
23
+ ]),
24
+ ),
25
+ ].join('\n\n');
17
26
  },
18
27
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
3
  import { PLANNED_PLATFORM_IDS } from '../generated/platform-manifest.js';
4
+ import { code, mdTable, mdTitle, statusLabel } from '../lib/format.js';
4
5
 
5
6
  export const listPlatformsTool = {
6
7
  name: 'list_platforms',
@@ -15,11 +16,20 @@ export const listPlatformsTool = {
15
16
 
16
17
  async execute(client: PosterlyClient, input: { include_planned?: boolean }) {
17
18
  const result = await client.listPlatforms(input);
18
- const lines = result.platforms.map((platform) => {
19
- const helperCount = platform.helper_tools?.length || 0;
20
- return `• ${platform.label} (${platform.id}) — ${platform.status}; post types: ${platform.post_types.join(', ') || 'n/a'}; helpers: ${helperCount}; analytics: ${platform.analytics_supported ? 'yes' : 'no'}`;
21
- });
22
19
 
23
- return `Posterly platforms (${result.platforms.length}):\n${lines.join('\n')}`;
20
+ return [
21
+ mdTitle(`Posterly platforms (${result.platforms.length})`),
22
+ mdTable(
23
+ ['Platform', 'ID', 'Status', 'Post types', 'Helpers', 'Analytics'],
24
+ result.platforms.map((platform) => [
25
+ platform.label,
26
+ code(platform.id),
27
+ statusLabel(platform.status),
28
+ platform.post_types.join(', ') || 'n/a',
29
+ platform.helper_tools?.length || 0,
30
+ platform.analytics_supported,
31
+ ]),
32
+ ),
33
+ ].join('\n\n');
24
34
  },
25
35
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
3
  import { SUPPORTED_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/platform-manifest.js';
4
+ import { code, compactText, formatDateTime, mdEmpty, mdTable, mdTitle, statusLabel } from '../lib/format.js';
4
5
 
5
6
  export const listPostsTool = {
6
7
  name: 'list_posts',
@@ -47,16 +48,21 @@ export const listPostsTool = {
47
48
  });
48
49
 
49
50
  if (posts.length === 0) {
50
- return 'No posts found matching your criteria.';
51
+ return mdEmpty('posts', 'No posts matched those filters.');
51
52
  }
52
53
 
53
- const lines = posts.map((p) => {
54
- const date = new Date(p.scheduled_at).toLocaleString();
55
- const caption =
56
- p.content.length > 60 ? p.content.slice(0, 60) + '' : p.content;
57
- return `• [${p.status}] #${p.id} — ${caption} (${date})`;
58
- });
59
-
60
- return `Posts (${posts.length} of ${total}):\n${lines.join('\n')}`;
54
+ return [
55
+ mdTitle(`Posts (${posts.length} of ${total})`),
56
+ mdTable(
57
+ ['Status', 'Post ID', 'Caption', 'Scheduled', 'Type'],
58
+ posts.map((post) => [
59
+ statusLabel(post.status),
60
+ code(post.id),
61
+ compactText(post.content, 80) || '(empty)',
62
+ formatDateTime(post.scheduled_at),
63
+ post.post_type || 'post',
64
+ ]),
65
+ ),
66
+ ].join('\n\n');
61
67
  },
62
68
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, compactText, formatDateTime, mdEmpty, mdTable, mdTitle, statusLabel } from '../lib/format.js';
3
4
 
4
5
  export const listWebhooksTool = {
5
6
  name: 'list_webhooks',
@@ -12,19 +13,22 @@ export const listWebhooksTool = {
12
13
  async execute(client: PosterlyClient, input: { workspace_id?: string }) {
13
14
  const { webhooks } = await client.listWebhooks(input);
14
15
  if (webhooks.length === 0) {
15
- return 'No webhooks found.';
16
+ return mdEmpty('webhooks', 'No webhook subscriptions were found.');
16
17
  }
17
18
 
18
- const lines = [`Webhooks (${webhooks.length}):`];
19
- for (const webhook of webhooks) {
20
- const active = webhook.is_active ? 'active' : 'inactive';
21
- const events = webhook.events?.length ? webhook.events.join(', ') : 'default events';
22
- const last = webhook.last_delivery_at
23
- ? ` last ${webhook.last_delivery_status || 'n/a'} at ${new Date(webhook.last_delivery_at).toLocaleString()}`
24
- : '';
25
- lines.push(`- ${webhook.id} [${active}] ${webhook.url} (${events})${last}`);
26
- }
27
- return lines.join('\n');
19
+ return [
20
+ mdTitle(`Webhooks (${webhooks.length})`),
21
+ mdTable(
22
+ ['Webhook ID', 'State', 'URL', 'Events', 'Last delivery', 'Last error'],
23
+ webhooks.map((webhook) => [
24
+ code(webhook.id),
25
+ statusLabel(webhook.is_active ? 'active' : 'inactive'),
26
+ webhook.url,
27
+ webhook.events?.length ? webhook.events.join(', ') : 'default events',
28
+ webhook.last_delivery_at ? `${webhook.last_delivery_status || 'n/a'} at ${formatDateTime(webhook.last_delivery_at)}` : 'n/a',
29
+ compactText(webhook.last_error, 80) || 'n/a',
30
+ ]),
31
+ ),
32
+ ].join('\n\n');
28
33
  },
29
34
  };
30
-
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { mdSuccess } from '../lib/format.js';
3
4
 
4
5
  export const replyGoogleBusinessReviewTool = {
5
6
  name: 'reply_google_business_review',
@@ -27,6 +28,8 @@ export const replyGoogleBusinessReviewTool = {
27
28
  ) {
28
29
  const { confirm: _confirm, ...payload } = input;
29
30
  const result = await client.replyGoogleBusinessReview(payload);
30
- return result.message || 'Google Business review reply posted successfully.';
31
+ return mdSuccess('Google Business review reply posted', [
32
+ ['Message', result.message || 'Google Business review reply posted successfully.'],
33
+ ]);
31
34
  },
32
35
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdJson, mdTitle } from '../lib/format.js';
3
4
 
4
5
  export const runVideoFunctionTool = {
5
6
  name: 'run_video_function',
@@ -24,9 +25,8 @@ export const runVideoFunctionTool = {
24
25
  ) {
25
26
  const result = await client.runVideoFunction(input);
26
27
  return [
27
- `Video function ${input.functionName} completed`,
28
- '',
29
- JSON.stringify(result, null, 2),
30
- ].join('\n');
28
+ mdTitle(`Video function ${code(input.functionName)} completed`),
29
+ mdJson('Result', result),
30
+ ].join('\n\n');
31
31
  },
32
32
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { mdEmpty, mdQuote, mdSection, mdTitle } from '../lib/format.js';
3
4
 
4
5
  export const suggestGoogleBusinessReviewReplyTool = {
5
6
  name: 'suggest_google_business_review_reply',
@@ -34,11 +35,11 @@ export const suggestGoogleBusinessReviewReplyTool = {
34
35
  const result = await client.suggestGoogleBusinessReviewReply(input);
35
36
  const suggestions = Array.isArray(result.suggestions) ? result.suggestions : [];
36
37
  if (suggestions.length === 0) {
37
- return 'No reply suggestions were generated.';
38
+ return mdEmpty('reply suggestions', 'No Google Business reply suggestions were generated.');
38
39
  }
39
40
  return [
40
- `Suggested replies (${result.tone || 'professional'}, ${result.rating || input.star_rating} stars):`,
41
- ...suggestions.map((text: string, index: number) => `${index + 1}. ${text}`),
42
- ].join('\n');
41
+ mdTitle(`Suggested replies`, `${result.tone || 'professional'} tone · ${result.rating || input.star_rating} stars`),
42
+ ...suggestions.map((text: string, index: number) => mdSection(`Option ${index + 1}`, mdQuote(text))),
43
+ ].join('\n\n');
43
44
  },
44
45
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { mdJson, mdSuccess } from '../lib/format.js';
3
4
 
4
5
  export const testWebhookTool = {
5
6
  name: 'test_webhook',
@@ -13,6 +14,11 @@ export const testWebhookTool = {
13
14
  async execute(client: PosterlyClient, input: { webhook_id: string; confirm: true }) {
14
15
  const result = await client.testWebhook(input.webhook_id);
15
16
  const delivery = result.delivery || {};
16
- return `Webhook test sent.\nDelivery: ${JSON.stringify(delivery, null, 2)}`;
17
+ return [
18
+ mdSuccess('Webhook test sent', [
19
+ ['Webhook ID', input.webhook_id],
20
+ ]),
21
+ mdJson('Delivery', delivery),
22
+ ].join('\n\n');
17
23
  },
18
24
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
3
  import { PLATFORM_HELPER_NAMES, SUPPORTED_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/platform-manifest.js';
4
+ import { code, mdJson, mdTitle } from '../lib/format.js';
4
5
 
5
6
  export const triggerPlatformHelperTool = {
6
7
  name: 'trigger_platform_helper',
@@ -29,6 +30,9 @@ export const triggerPlatformHelperTool = {
29
30
  },
30
31
  ) {
31
32
  const result = await client.triggerPlatformHelper(input);
32
- return JSON.stringify(result, null, 2);
33
+ return [
34
+ mdTitle(`Platform helper ${code(input.helper)} completed`),
35
+ mdJson('Result', result),
36
+ ].join('\n\n');
33
37
  },
34
38
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdSuccess, statusLabel } 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
 
@@ -30,9 +31,15 @@ export const updateOAuthClientTool = {
30
31
  is_active?: boolean;
31
32
  confirm: true;
32
33
  },
33
- ) {
34
+ ) {
34
35
  const { client_id, confirm: _confirm, ...payload } = input;
35
36
  const result = await client.updateOAuthClient(client_id, payload);
36
- return `OAuth client updated: ${result.client.client_name} (${result.client.client_id}) active=${result.client.is_active}`;
37
+ return mdSuccess('OAuth client updated', [
38
+ ['Client', result.client.client_name],
39
+ ['Client ID', code(result.client.client_id)],
40
+ ['State', statusLabel(result.client.is_active ? 'active' : 'inactive')],
41
+ ['Redirect URIs', result.client.allowed_redirect_uris.join(', ')],
42
+ ['Default scopes', result.client.default_scopes.join(', ')],
43
+ ]);
37
44
  },
38
45
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdJson, mdTitle } from '../lib/format.js';
3
4
 
4
5
  export const updatePostReleaseIdTool = {
5
6
  name: 'update_post_release_id',
@@ -20,7 +21,9 @@ export const updatePostReleaseIdTool = {
20
21
  release_id: input.release_id,
21
22
  group_id: input.group_id,
22
23
  });
23
- return JSON.stringify(result, null, 2);
24
+ return [
25
+ mdTitle(`✅ Release ID updated for post ${code(input.post_id)}`),
26
+ mdJson('Result', result),
27
+ ].join('\n\n');
24
28
  },
25
29
  };
26
-
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdSuccess, statusLabel } from '../lib/format.js';
3
4
 
4
5
  export const updatePostStatusTool = {
5
6
  name: 'update_post_status',
@@ -26,6 +27,11 @@ export const updatePostStatusTool = {
26
27
  scheduled_at: input.scheduled_at,
27
28
  });
28
29
  const transition = result.transition || {};
29
- return `Post #${input.post_id} status changed from ${transition.old_status || 'unknown'} to ${transition.new_status || input.status}.`;
30
+ return mdSuccess('Post status updated', [
31
+ ['Post ID', code(input.post_id)],
32
+ ['From', statusLabel(transition.old_status || 'unknown')],
33
+ ['To', statusLabel(transition.new_status || input.status)],
34
+ ['Scheduled', input.scheduled_at || undefined],
35
+ ]);
30
36
  },
31
37
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, formatDateTime, mdKeyValue, mdQuote, mdSection, mdTitle, statusLabel } from '../lib/format.js';
3
4
 
4
5
  const instagramSettingsSchema = z.object({
5
6
  __type: z.enum(['instagram', 'instagram-standalone']).optional(),
@@ -108,10 +109,14 @@ export const updatePostTool = {
108
109
  const result = await client.updatePost(post_id, payload);
109
110
  const p = result.post as Record<string, any>;
110
111
 
111
- const when = p.scheduled_at
112
- ? new Date(p.scheduled_at).toLocaleString()
113
- : 'N/A';
114
-
115
- return `Post #${p.id} updated successfully!\n• Status: ${p.status}\n• Caption: ${p.content || '(empty)'}\n• Scheduled: ${when}`;
112
+ return [
113
+ mdTitle(`✅ Post ${code(p.id)} updated`),
114
+ mdKeyValue([
115
+ ['Status', statusLabel(p.status)],
116
+ ['Scheduled', formatDateTime(p.scheduled_at)],
117
+ ['Type', p.post_type || 'unknown'],
118
+ ]),
119
+ mdSection('Caption', p.content ? mdQuote(p.content) : '_(empty)_'),
120
+ ].join('\n\n');
116
121
  },
117
122
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
3
  import { webhookEventSchema } from './webhook-events.js';
4
+ import { code, mdSuccess, statusLabel } from '../lib/format.js';
4
5
 
5
6
  export const updateWebhookTool = {
6
7
  name: 'update_webhook',
@@ -31,7 +32,11 @@ export const updateWebhookTool = {
31
32
  const { webhook_id, confirm: _confirm, ...payload } = input;
32
33
  const result = await client.updateWebhook(webhook_id, payload);
33
34
  const active = result.webhook.is_active ? 'active' : 'inactive';
34
- return `Webhook updated: ${result.webhook.id}\nURL: ${result.webhook.url}\nState: ${active}\nEvents: ${result.webhook.events.join(', ')}`;
35
+ return mdSuccess('Webhook updated', [
36
+ ['Webhook ID', code(result.webhook.id)],
37
+ ['URL', result.webhook.url],
38
+ ['State', statusLabel(active)],
39
+ ['Events', result.webhook.events.join(', ')],
40
+ ]);
35
41
  },
36
42
  };
37
-
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { formatBytes, mdSuccess } from '../lib/format.js';
3
4
 
4
5
  export const uploadMediaFromUrlTool = {
5
6
  name: 'upload_media_from_url',
@@ -20,14 +21,11 @@ export const uploadMediaFromUrlTool = {
20
21
  filename: input.filename,
21
22
  contentType: input.content_type,
22
23
  });
23
- return [
24
- 'Remote media uploaded successfully.',
25
- `• URL: ${result.url}`,
26
- `• Source URL: ${result.source_url}`,
27
- `• Content type: ${result.content_type}`,
28
- `• Size: ${result.size} bytes`,
29
- '',
30
- 'Use this URL as media_url when creating a post.',
31
- ].join('\n');
24
+ return mdSuccess('Remote media uploaded', [
25
+ ['URL', result.url],
26
+ ['Source URL', result.source_url],
27
+ ['Content type', result.content_type],
28
+ ['Size', formatBytes(result.size)],
29
+ ], 'Use this URL as `media_url` when creating a post.');
32
30
  },
33
31
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { mdError, mdSuccess } from '../lib/format.js';
3
4
 
4
5
  export const uploadMediaTool = {
5
6
  name: 'upload_media',
@@ -35,7 +36,7 @@ export const uploadMediaTool = {
35
36
  }
36
37
  ) {
37
38
  if (!input.file_path && !input.base64_data) {
38
- return 'Error: Provide either file_path or base64_data.';
39
+ return mdError('Provide either `file_path` or `base64_data`.');
39
40
  }
40
41
 
41
42
  const result = await client.uploadMedia({
@@ -45,6 +46,9 @@ export const uploadMediaTool = {
45
46
  contentType: input.content_type,
46
47
  });
47
48
 
48
- return `Media uploaded successfully!\n• URL: ${result.url}\n\nUse this URL as media_url when creating a post.`;
49
+ return mdSuccess('Media uploaded', [
50
+ ['URL', result.url],
51
+ ['Storage path', result.path],
52
+ ], 'Use this URL as `media_url` when creating a post.');
49
53
  },
50
54
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdKeyValue, mdTable, mdTitle } from '../lib/format.js';
3
4
 
4
5
  export const whoamiTool = {
5
6
  name: 'whoami',
@@ -10,28 +11,25 @@ export const whoamiTool = {
10
11
  async execute(client: PosterlyClient) {
11
12
  const info = await client.whoami();
12
13
 
13
- const lines: string[] = [];
14
- lines.push(`User: ${info.user.email || info.user.id}`);
15
- lines.push(`API scopes: ${info.api_key.scopes.join(', ') || 'none'}`);
16
- lines.push('');
17
- lines.push(
18
- `Default workspace: ${info.default_workspace.name} (${info.default_workspace.id})` +
19
- (info.default_workspace.timezone ? ` — tz: ${info.default_workspace.timezone}` : '')
20
- );
21
- lines.push('');
22
- lines.push(`All workspaces (${info.workspaces.length}):`);
23
- for (const ws of info.workspaces) {
24
- const marker = ws.id === info.default_workspace.id ? ' [default]' : '';
25
- const personal = ws.is_personal ? ' [personal]' : '';
26
- const tz = ws.timezone ? ` — tz: ${ws.timezone}` : '';
27
- lines.push(`• ${ws.name} — ${ws.id} (role: ${ws.role || 'member'})${personal}${marker}${tz}`);
28
- }
29
-
30
- lines.push('');
31
- lines.push(
32
- 'Tip: pass workspace_id to create_post / list_posts / list_accounts / find_available_slot to scope actions to a specific workspace. Omit it to use the default.'
33
- );
34
-
35
- return lines.join('\n');
14
+ return [
15
+ mdTitle('Posterly identity'),
16
+ mdKeyValue([
17
+ ['User', info.user.email || code(info.user.id)],
18
+ ['API scopes', info.api_key.scopes.join(', ') || 'none'],
19
+ ['Default workspace', `${info.default_workspace.name} (${code(info.default_workspace.id)})`],
20
+ ['Default timezone', info.default_workspace.timezone || 'n/a'],
21
+ ]),
22
+ mdTable(
23
+ ['Workspace', 'Workspace ID', 'Role', 'Timezone', 'Default'],
24
+ info.workspaces.map((workspace) => [
25
+ `${workspace.name}${workspace.is_personal ? ' (personal)' : ''}`,
26
+ code(workspace.id),
27
+ workspace.role || 'member',
28
+ workspace.timezone || 'n/a',
29
+ workspace.id === info.default_workspace.id,
30
+ ]),
31
+ ),
32
+ '**Tip:** Pass `workspace_id` to `create_post`, `list_posts`, `list_accounts`, and `find_available_slot` when you want the assistant to stay inside one workspace.',
33
+ ].join('\n\n');
36
34
  },
37
35
  };