posterly-mcp-server 0.43.0 → 0.43.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.
package/README.md CHANGED
@@ -122,13 +122,13 @@ Add the same server definition to your Cursor MCP settings:
122
122
 
123
123
  ## Available tools
124
124
 
125
- `posterly-mcp-server@0.43.0` exposes 90 tools.
125
+ `posterly-mcp-server@0.43.2` exposes 90 tools.
126
126
 
127
127
  Public setup tools work before `POSTERLY_API_KEY` exists:
128
128
 
129
129
  - `get_mcp_status` (show the installed server version, latest npm version, MCP endpoint health, API auth health, and update guidance)
130
130
  - `get_agent_signup_info`
131
- - `start_signup` (start paid signup and return a Posterly checkout handoff URL)
131
+ - `start_signup` (start paid signup and return a poll URL, browser Checkout fallback, and prepaid PaymentIntent fields when agent-card pay is enabled)
132
132
  - `get_signup_session` (poll checkout, payment, password, and agent-access status)
133
133
 
134
134
  Authenticated tools require `POSTERLY_API_KEY`:
@@ -821,6 +821,26 @@ export interface PublicSignupPayload {
821
821
  api_addon?: boolean;
822
822
  return_path?: string;
823
823
  source?: string;
824
+ tos_accepted?: boolean;
825
+ }
826
+ export interface PublicSignupAgentPay {
827
+ enabled?: boolean;
828
+ method?: string;
829
+ credential_type?: string;
830
+ amount?: number;
831
+ currency?: string;
832
+ amount_display?: string;
833
+ payment_intent_id?: string;
834
+ client_secret?: string | null;
835
+ publishable_key?: string | null;
836
+ confirm_url?: string;
837
+ poll_url?: string;
838
+ checkout_redirect_url?: string;
839
+ pay_instructions?: string;
840
+ status?: string;
841
+ period?: Record<string, unknown>;
842
+ requires_tos_acceptance?: boolean;
843
+ [key: string]: unknown;
824
844
  }
825
845
  export interface PublicSignupSession {
826
846
  id: string;
@@ -837,6 +857,7 @@ export interface PublicSignupSession {
837
857
  checkout?: Record<string, unknown>;
838
858
  account?: Record<string, unknown>;
839
859
  billing?: Record<string, unknown>;
860
+ agent_pay?: PublicSignupAgentPay | null;
840
861
  links?: Record<string, unknown>;
841
862
  created_at?: string;
842
863
  updated_at?: string;
@@ -856,6 +877,7 @@ export interface PublicSignupResponse {
856
877
  links?: Record<string, unknown>;
857
878
  account?: Record<string, unknown>;
858
879
  billing?: Record<string, unknown>;
880
+ agent_pay?: PublicSignupAgentPay | null;
859
881
  [key: string]: unknown;
860
882
  }
861
883
  export interface PublicSignupSessionResponse {
@@ -1 +1 @@
1
- export declare const POSTERLY_MCP_VERSION = "0.43.0";
1
+ export declare const POSTERLY_MCP_VERSION = "0.43.2";
@@ -5,4 +5,4 @@
5
5
  // tool set (minus the intentional pre-auth signup tools that only this stdio
6
6
  // package exposes). `npm run check:mcp-parity` enforces both the version match
7
7
  // and the tool-list match, and runs in the pre-commit hook.
8
- export const POSTERLY_MCP_VERSION = '0.43.0';
8
+ export const POSTERLY_MCP_VERSION = '0.43.2';
@@ -14,8 +14,8 @@ export const getAgentSignupInfoTool = {
14
14
  '',
15
15
  'Pre-auth tools:',
16
16
  '- get_mcp_status: checks the installed server version, latest npm version, MCP endpoint health, API auth health, and whether POSTERLY_API_KEY is configured.',
17
- '- start_signup: starts paid signup and returns a Posterly checkout handoff URL plus a signup poll URL.',
18
- '- get_signup_session: polls checkout, payment, password, and agent-access status.',
17
+ '- start_signup: starts paid signup and returns a signup poll URL, a browser Checkout fallback, and, when enabled, a prepaid PaymentIntent for a Link virtual card (credential_type: card).',
18
+ '- get_signup_session: polls payment, password, and agent-access status.',
19
19
  '',
20
20
  'After Posterly access is installed, onboard the user IN THIS chat (do not send them to /onboarding-v2):',
21
21
  '- whoami: confirm the paid account and read onboarding.next_step.',
@@ -30,9 +30,11 @@ export const getAgentSignupInfoTool = {
30
30
  '- Do not narrate raw curl, HTTP, JSON, API keys, or implementation details unless the user explicitly asks for debugging.',
31
31
  '',
32
32
  'Boundaries:',
33
- '- The user pays in Stripe Checkout in their browser.',
33
+ '- Payment can be a one-time Link Agent Wallet virtual card (credential_type: card) on the prepaid PaymentIntent, or Stripe Checkout in the browser.',
34
+ '- Never send card numbers to posterly. Confirm the PaymentIntent with Stripe, or send checkout_redirect_url.',
34
35
  '- The user sets their Posterly password in their browser.',
35
36
  '- The user approves social OAuth in their browser.',
37
+ '- Publish approval stays with the user.',
36
38
  '- The AI should never ask for card details, Posterly passwords, social passwords, or OAuth codes.',
37
39
  ].join('\n');
38
40
  },
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  export const getSignupSessionTool = {
3
3
  name: 'get_signup_session',
4
- description: 'Poll a public Posterly signup session created by start_signup. Use this to narrate checkout, payment, password setup, and agent access status before a Posterly API key is installed. Keep updates user-facing; do not narrate raw HTTP, curl, or API plumbing.',
4
+ description: 'Poll a public Posterly signup session created by start_signup. Use this to narrate payment (Link virtual card or browser Checkout), password setup, and agent access status before a Posterly API key is installed. Keep updates user-facing; do not narrate raw HTTP, curl, or API plumbing.',
5
5
  inputSchema: z.object({
6
6
  session_id: z
7
7
  .string()
@@ -23,18 +23,25 @@ function formatSignupSession(session, includeRaw) {
23
23
  const checkoutUrl = stringFromRecord(session.links, 'checkout_url');
24
24
  const passwordUrl = stringFromRecord(session.links, 'password_setup_url');
25
25
  const completeUrl = stringFromRecord(session.links, 'complete_url');
26
+ const agentPay = session.agent_pay;
26
27
  const lines = [
27
28
  `Posterly signup session: ${session.id}`,
28
29
  `Status: ${session.status}`,
29
30
  session.status_message ? `Message: ${session.status_message}` : '',
30
31
  nextAction?.label ? `Next action: ${nextAction.label}` : '',
31
32
  nextAction?.description ? `Next action detail: ${nextAction.description}` : '',
33
+ agentPay?.amount_display ? `Agent pay amount: ${agentPay.amount_display}` : '',
34
+ agentPay?.payment_intent_id ? `PaymentIntent: ${agentPay.payment_intent_id}` : '',
35
+ agentPay?.status ? `PaymentIntent status: ${agentPay.status}` : '',
36
+ agentPay?.requires_tos_acceptance ? 'Terms acceptance required before client_secret is released.' : '',
37
+ agentPay?.client_secret ? `PaymentIntent client_secret: ${agentPay.client_secret}` : '',
38
+ agentPay?.confirm_url ? `Confirm URL: ${agentPay.confirm_url}` : '',
32
39
  checkoutRedirectUrl ? `Checkout handoff URL: ${checkoutRedirectUrl}` : '',
33
40
  checkoutUrl ? `Checkout URL: ${checkoutUrl}` : '',
34
41
  passwordUrl ? `Password setup URL: ${passwordUrl}` : '',
35
42
  completeUrl ? `Completion URL: ${completeUrl}` : '',
36
43
  '',
37
- guidanceForStatus(session.status),
44
+ guidanceForStatus(session.status, Boolean(agentPay?.enabled || agentPay?.payment_intent_id), Boolean(agentPay?.requires_tos_acceptance)),
38
45
  session.agent_next_steps?.length
39
46
  ? `Agent next steps:\n${session.agent_next_steps.map((step, index) => `${index + 1}. ${step}`).join('\n')}`
40
47
  : '',
@@ -46,10 +53,15 @@ function stringFromRecord(record, key) {
46
53
  const value = record?.[key];
47
54
  return typeof value === 'string' ? value : null;
48
55
  }
49
- function guidanceForStatus(status) {
56
+ function guidanceForStatus(status, agentPayAvailable = false, requiresTosAcceptance = false) {
50
57
  switch (status) {
51
58
  case 'checkout_pending':
52
- return 'Tell the user checkout is still pending. Keep polling; do not ask for card details.';
59
+ if (agentPayAvailable && requiresTosAcceptance) {
60
+ return 'Payment is still pending on terms acceptance. Ask the user to explicitly confirm they accept the posterly terms at https://www.poster.ly/terms, then POST { "tos_accepted": true } to Confirm URL to receive client_secret, or send the user the Checkout handoff URL. Keep polling; never ask for card numbers.';
61
+ }
62
+ return agentPayAvailable
63
+ ? 'Payment is still pending. Confirm the prepaid PaymentIntent with a Link virtual card (credential_type: card), or send the user the Checkout handoff URL. Keep polling; never ask for card numbers.'
64
+ : 'Tell the user checkout is still pending. Keep polling; do not ask for card details.';
53
65
  case 'checkout_expired':
54
66
  return 'Tell the user checkout expired and offer to call start_signup again.';
55
67
  case 'payment_confirmed':
@@ -11,6 +11,7 @@ export declare const startSignupTool: {
11
11
  api_addon: z.ZodDefault<z.ZodBoolean>;
12
12
  return_path: z.ZodDefault<z.ZodString>;
13
13
  source: z.ZodDefault<z.ZodString>;
14
+ tos_accepted: z.ZodOptional<z.ZodBoolean>;
14
15
  debug: z.ZodOptional<z.ZodBoolean>;
15
16
  }, "strip", z.ZodTypeAny, {
16
17
  email: string;
@@ -21,6 +22,7 @@ export declare const startSignupTool: {
21
22
  source: string;
22
23
  debug?: boolean | undefined;
23
24
  name?: string | undefined;
25
+ tos_accepted?: boolean | undefined;
24
26
  }, {
25
27
  email: string;
26
28
  debug?: boolean | undefined;
@@ -30,6 +32,7 @@ export declare const startSignupTool: {
30
32
  api_addon?: boolean | undefined;
31
33
  return_path?: string | undefined;
32
34
  source?: string | undefined;
35
+ tos_accepted?: boolean | undefined;
33
36
  }>;
34
37
  execute(client: PosterlyClient, input: {
35
38
  email: string;
@@ -39,6 +42,7 @@ export declare const startSignupTool: {
39
42
  api_addon?: boolean;
40
43
  return_path?: string;
41
44
  source?: string;
45
+ tos_accepted?: boolean;
42
46
  debug?: boolean;
43
47
  }): Promise<string>;
44
48
  };
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  const DEFAULT_RETURN_PATH = '/dashboard/api?api_signup=success';
3
3
  export const startSignupTool = {
4
4
  name: 'start_signup',
5
- description: 'Start the public Posterly paid signup flow for a user before Posterly API access exists. Returns a Posterly checkout handoff URL and signup poll URL. The user must handle payment and password setup in the browser. Do not ask for card details or a Posterly password in chat.',
5
+ description: 'Start the public Posterly paid signup flow for a user before Posterly API access exists. Returns a signup poll URL, a browser Checkout fallback URL, and, when enabled, a prepaid PaymentIntent the agent can pay with a Link Agent Wallet virtual card (credential_type: card). The user still sets a password and approves social OAuth in the browser. Never ask for card numbers, a Posterly password, or social passwords in chat.',
6
6
  inputSchema: z.object({
7
7
  email: z
8
8
  .string()
@@ -32,6 +32,10 @@ export const startSignupTool = {
32
32
  .string()
33
33
  .default('posterly_mcp_pre_auth_signup')
34
34
  .describe('Source label for analytics and support diagnostics.'),
35
+ tos_accepted: z
36
+ .boolean()
37
+ .optional()
38
+ .describe('Set true only after the user has explicitly confirmed, in this conversation, that they accept the posterly terms at https://www.poster.ly/terms. Sending it here avoids a separate terms-confirmation step before client_secret is released.'),
35
39
  debug: z
36
40
  .boolean()
37
41
  .optional()
@@ -46,6 +50,7 @@ export const startSignupTool = {
46
50
  api_addon: input.api_addon ?? true,
47
51
  return_path: input.return_path ?? DEFAULT_RETURN_PATH,
48
52
  source: input.source ?? 'posterly_mcp_pre_auth_signup',
53
+ tos_accepted: input.tos_accepted,
49
54
  });
50
55
  return formatSignupStart(response, Boolean(input.debug));
51
56
  },
@@ -64,22 +69,50 @@ function formatSignupStart(response, includeRaw) {
64
69
  const completeUrl = response.complete_url || valueFromLinks(response, 'complete_url');
65
70
  const status = session?.status || response.status || 'checkout_pending';
66
71
  const message = session?.status_message || response.status_message;
72
+ const agentPay = response.agent_pay || session?.agent_pay;
67
73
  const lines = [
68
74
  'Posterly signup started.',
69
75
  sessionId ? `Signup session: ${sessionId}` : '',
70
76
  `Status: ${status}`,
71
77
  message ? `Message: ${message}` : '',
72
- checkoutRedirectUrl ? `Checkout handoff URL: ${checkoutRedirectUrl}` : '',
78
+ agentPay?.amount_display
79
+ ? `Agent pay amount: ${agentPay.amount_display} ${String(agentPay.currency || 'usd').toUpperCase()}`
80
+ : '',
81
+ agentPay?.payment_intent_id ? `PaymentIntent: ${agentPay.payment_intent_id}` : '',
82
+ agentPay?.requires_tos_acceptance ? 'Terms acceptance required before client_secret is released.' : '',
83
+ agentPay?.client_secret ? `PaymentIntent client_secret: ${agentPay.client_secret}` : '',
84
+ agentPay?.confirm_url ? `Confirm URL: ${agentPay.confirm_url}` : '',
85
+ agentPay?.pay_instructions ? `Pay instructions: ${agentPay.pay_instructions}` : '',
86
+ checkoutRedirectUrl ? `Checkout handoff URL (browser fallback): ${checkoutRedirectUrl}` : '',
73
87
  checkoutUrl ? `Checkout URL: ${checkoutUrl}` : '',
74
88
  pollUrl ? `Poll URL: ${pollUrl}` : '',
75
89
  completeUrl ? `Completion URL: ${completeUrl}` : '',
76
90
  '',
77
91
  'Next steps for the AI agent:',
78
- '1. Send the Checkout handoff URL to the user so they can pay securely in their browser. Use the raw Stripe checkout URL only if you preserve the full URL including any fragment. posterly does not email this link, so do not tell the user to check their inbox at this stage.',
79
- '2. Call get_signup_session with the signup session ID until the status changes.',
80
- '3. Tell the user when Posterly sends the password setup email.',
81
- '4. If status becomes agent_access_required, ask the user to install or paste the Posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
82
- '5. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
92
+ ...(agentPay?.enabled && agentPay?.requires_tos_acceptance
93
+ ? [
94
+ '1. Ask the user to explicitly confirm they accept the posterly terms at https://www.poster.ly/terms.',
95
+ '2. Once confirmed, POST { "tos_accepted": true } to Confirm URL (optionally with payment_method in the same call) to receive client_secret.',
96
+ '3. If the user would rather pay on the website, send them the Checkout handoff URL instead.',
97
+ '4. Call get_signup_session with the signup session ID until the status changes.',
98
+ '5. Do not ask for card details, the posterly password, social passwords, or OAuth codes.',
99
+ ]
100
+ : agentPay?.enabled
101
+ ? [
102
+ '1. Prefer agent_pay: confirm the PaymentIntent with a one-time Link Agent Wallet virtual card (credential_type: card). Never paste card numbers into chat or send them to posterly.',
103
+ '2. If the user would rather pay on the website, send them the Checkout handoff URL instead.',
104
+ '3. Call get_signup_session with the signup session ID until the status changes.',
105
+ '4. Tell the user when Posterly sends the password setup email. Password, social OAuth, and publish approval stay in the browser.',
106
+ '5. If status becomes agent_access_required, ask the user to install or paste the Posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
107
+ '6. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
108
+ ]
109
+ : [
110
+ '1. Send the Checkout handoff URL to the user so they can pay securely in their browser. Use the raw Stripe checkout URL only if you preserve the full URL including any fragment. posterly does not email this link, so do not tell the user to check their inbox at this stage.',
111
+ '2. Call get_signup_session with the signup session ID until the status changes.',
112
+ '3. Tell the user when Posterly sends the password setup email.',
113
+ '4. If status becomes agent_access_required, ask the user to install or paste the Posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
114
+ '5. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
115
+ ]),
83
116
  includeRaw ? `\nRaw signup response:\n${JSON.stringify(response, null, 2)}` : '',
84
117
  ];
85
118
  return lines.filter(Boolean).join('\n');
@@ -107,7 +107,7 @@ const platformSettingsSchema = z.object({
107
107
  }).passthrough();
108
108
  export const updatePostTool = {
109
109
  name: 'update_post',
110
- description: 'Update a scheduled or draft post. DESTRUCTIVE WRITE - overwrites the existing post\'s caption/media/schedule. Cannot edit published or currently publishing posts.\n\n' +
110
+ description: 'Update a scheduled or draft post. DESTRUCTIVE WRITE - overwrites the existing post\'s caption/media/schedule. Cannot edit published or currently publishing posts. If a client has already approved the post, a caption edit reopens caption approval and a media or thumbnail edit reopens asset approval. A reschedule, account change or settings change cannot be attributed to one axis and reopens both.\n\n' +
111
111
  'REQUIRED BEFORE CALLING: Show the user a side-by-side of the CURRENT post (caption, scheduled time, media) and the PROPOSED changes, and get explicit confirmation ("yes update it", "apply those changes") before calling. Do not auto-edit posts based on a general instruction - each edit needs its own confirmation.',
112
112
  inputSchema: z.object({
113
113
  post_id: z.number().describe('The post ID to update'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posterly-mcp-server",
3
- "version": "0.43.0",
3
+ "version": "0.43.2",
4
4
  "mcpName": "io.github.awpthorp/posterly",
5
5
  "description": "MCP server for posterly: schedule and publish social media posts across 18 platforms from any MCP client (Claude, ChatGPT, Cursor, Windsurf, Cline, and more)",
6
6
  "license": "MIT",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.awpthorp/posterly",
4
4
  "title": "posterly",
5
5
  "description": "Validate, schedule, publish, and analyze social content across 18 platforms with posterly.",
6
- "version": "0.43.0",
6
+ "version": "0.43.2",
7
7
  "websiteUrl": "https://www.poster.ly/mcp",
8
8
  "repository": {
9
9
  "url": "https://github.com/awpthorp/posterly",
@@ -14,7 +14,7 @@
14
14
  {
15
15
  "registryType": "npm",
16
16
  "identifier": "posterly-mcp-server",
17
- "version": "0.43.0",
17
+ "version": "0.43.2",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },
@@ -850,6 +850,32 @@ export interface PublicSignupPayload {
850
850
  api_addon?: boolean;
851
851
  return_path?: string;
852
852
  source?: string;
853
+ // Set true only after the user has explicitly confirmed, in this
854
+ // conversation, that they accept the posterly terms. Sending it on this
855
+ // first call avoids a separate terms-confirmation round trip.
856
+ tos_accepted?: boolean;
857
+ }
858
+
859
+ export interface PublicSignupAgentPay {
860
+ enabled?: boolean;
861
+ method?: string;
862
+ credential_type?: string;
863
+ amount?: number;
864
+ currency?: string;
865
+ amount_display?: string;
866
+ payment_intent_id?: string;
867
+ client_secret?: string | null;
868
+ publishable_key?: string | null;
869
+ confirm_url?: string;
870
+ poll_url?: string;
871
+ checkout_redirect_url?: string;
872
+ pay_instructions?: string;
873
+ status?: string;
874
+ period?: Record<string, unknown>;
875
+ // True until the user has explicitly accepted the posterly terms. While
876
+ // true, client_secret and publishable_key are withheld.
877
+ requires_tos_acceptance?: boolean;
878
+ [key: string]: unknown;
853
879
  }
854
880
 
855
881
  export interface PublicSignupSession {
@@ -867,6 +893,7 @@ export interface PublicSignupSession {
867
893
  checkout?: Record<string, unknown>;
868
894
  account?: Record<string, unknown>;
869
895
  billing?: Record<string, unknown>;
896
+ agent_pay?: PublicSignupAgentPay | null;
870
897
  links?: Record<string, unknown>;
871
898
  created_at?: string;
872
899
  updated_at?: string;
@@ -887,6 +914,7 @@ export interface PublicSignupResponse {
887
914
  links?: Record<string, unknown>;
888
915
  account?: Record<string, unknown>;
889
916
  billing?: Record<string, unknown>;
917
+ agent_pay?: PublicSignupAgentPay | null;
890
918
  [key: string]: unknown;
891
919
  }
892
920
 
@@ -5,4 +5,4 @@
5
5
  // tool set (minus the intentional pre-auth signup tools that only this stdio
6
6
  // package exposes). `npm run check:mcp-parity` enforces both the version match
7
7
  // and the tool-list match, and runs in the pre-commit hook.
8
- export const POSTERLY_MCP_VERSION = '0.43.0';
8
+ export const POSTERLY_MCP_VERSION = '0.43.2';
@@ -19,8 +19,8 @@ export const getAgentSignupInfoTool = {
19
19
  '',
20
20
  'Pre-auth tools:',
21
21
  '- get_mcp_status: checks the installed server version, latest npm version, MCP endpoint health, API auth health, and whether POSTERLY_API_KEY is configured.',
22
- '- start_signup: starts paid signup and returns a Posterly checkout handoff URL plus a signup poll URL.',
23
- '- get_signup_session: polls checkout, payment, password, and agent-access status.',
22
+ '- start_signup: starts paid signup and returns a signup poll URL, a browser Checkout fallback, and, when enabled, a prepaid PaymentIntent for a Link virtual card (credential_type: card).',
23
+ '- get_signup_session: polls payment, password, and agent-access status.',
24
24
  '',
25
25
  'After Posterly access is installed, onboard the user IN THIS chat (do not send them to /onboarding-v2):',
26
26
  '- whoami: confirm the paid account and read onboarding.next_step.',
@@ -35,9 +35,11 @@ export const getAgentSignupInfoTool = {
35
35
  '- Do not narrate raw curl, HTTP, JSON, API keys, or implementation details unless the user explicitly asks for debugging.',
36
36
  '',
37
37
  'Boundaries:',
38
- '- The user pays in Stripe Checkout in their browser.',
38
+ '- Payment can be a one-time Link Agent Wallet virtual card (credential_type: card) on the prepaid PaymentIntent, or Stripe Checkout in the browser.',
39
+ '- Never send card numbers to posterly. Confirm the PaymentIntent with Stripe, or send checkout_redirect_url.',
39
40
  '- The user sets their Posterly password in their browser.',
40
41
  '- The user approves social OAuth in their browser.',
42
+ '- Publish approval stays with the user.',
41
43
  '- The AI should never ask for card details, Posterly passwords, social passwords, or OAuth codes.',
42
44
  ].join('\n');
43
45
  },
@@ -4,7 +4,7 @@ import type { PosterlyClient, PublicSignupSession } from '../lib/api-client.js';
4
4
  export const getSignupSessionTool = {
5
5
  name: 'get_signup_session',
6
6
  description:
7
- 'Poll a public Posterly signup session created by start_signup. Use this to narrate checkout, payment, password setup, and agent access status before a Posterly API key is installed. Keep updates user-facing; do not narrate raw HTTP, curl, or API plumbing.',
7
+ 'Poll a public Posterly signup session created by start_signup. Use this to narrate payment (Link virtual card or browser Checkout), password setup, and agent access status before a Posterly API key is installed. Keep updates user-facing; do not narrate raw HTTP, curl, or API plumbing.',
8
8
  inputSchema: z.object({
9
9
  session_id: z
10
10
  .string()
@@ -28,6 +28,7 @@ function formatSignupSession(session: PublicSignupSession, includeRaw: boolean):
28
28
  const checkoutUrl = stringFromRecord(session.links, 'checkout_url');
29
29
  const passwordUrl = stringFromRecord(session.links, 'password_setup_url');
30
30
  const completeUrl = stringFromRecord(session.links, 'complete_url');
31
+ const agentPay = session.agent_pay;
31
32
 
32
33
  const lines = [
33
34
  `Posterly signup session: ${session.id}`,
@@ -35,12 +36,22 @@ function formatSignupSession(session: PublicSignupSession, includeRaw: boolean):
35
36
  session.status_message ? `Message: ${session.status_message}` : '',
36
37
  nextAction?.label ? `Next action: ${nextAction.label}` : '',
37
38
  nextAction?.description ? `Next action detail: ${nextAction.description}` : '',
39
+ agentPay?.amount_display ? `Agent pay amount: ${agentPay.amount_display}` : '',
40
+ agentPay?.payment_intent_id ? `PaymentIntent: ${agentPay.payment_intent_id}` : '',
41
+ agentPay?.status ? `PaymentIntent status: ${agentPay.status}` : '',
42
+ agentPay?.requires_tos_acceptance ? 'Terms acceptance required before client_secret is released.' : '',
43
+ agentPay?.client_secret ? `PaymentIntent client_secret: ${agentPay.client_secret}` : '',
44
+ agentPay?.confirm_url ? `Confirm URL: ${agentPay.confirm_url}` : '',
38
45
  checkoutRedirectUrl ? `Checkout handoff URL: ${checkoutRedirectUrl}` : '',
39
46
  checkoutUrl ? `Checkout URL: ${checkoutUrl}` : '',
40
47
  passwordUrl ? `Password setup URL: ${passwordUrl}` : '',
41
48
  completeUrl ? `Completion URL: ${completeUrl}` : '',
42
49
  '',
43
- guidanceForStatus(session.status),
50
+ guidanceForStatus(
51
+ session.status,
52
+ Boolean(agentPay?.enabled || agentPay?.payment_intent_id),
53
+ Boolean(agentPay?.requires_tos_acceptance),
54
+ ),
44
55
  session.agent_next_steps?.length
45
56
  ? `Agent next steps:\n${session.agent_next_steps.map((step, index) => `${index + 1}. ${step}`).join('\n')}`
46
57
  : '',
@@ -55,10 +66,15 @@ function stringFromRecord(record: Record<string, unknown> | undefined, key: stri
55
66
  return typeof value === 'string' ? value : null;
56
67
  }
57
68
 
58
- function guidanceForStatus(status: string): string {
69
+ function guidanceForStatus(status: string, agentPayAvailable = false, requiresTosAcceptance = false): string {
59
70
  switch (status) {
60
71
  case 'checkout_pending':
61
- return 'Tell the user checkout is still pending. Keep polling; do not ask for card details.';
72
+ if (agentPayAvailable && requiresTosAcceptance) {
73
+ return 'Payment is still pending on terms acceptance. Ask the user to explicitly confirm they accept the posterly terms at https://www.poster.ly/terms, then POST { "tos_accepted": true } to Confirm URL to receive client_secret, or send the user the Checkout handoff URL. Keep polling; never ask for card numbers.';
74
+ }
75
+ return agentPayAvailable
76
+ ? 'Payment is still pending. Confirm the prepaid PaymentIntent with a Link virtual card (credential_type: card), or send the user the Checkout handoff URL. Keep polling; never ask for card numbers.'
77
+ : 'Tell the user checkout is still pending. Keep polling; do not ask for card details.';
62
78
  case 'checkout_expired':
63
79
  return 'Tell the user checkout expired and offer to call start_signup again.';
64
80
  case 'payment_confirmed':
@@ -6,7 +6,7 @@ const DEFAULT_RETURN_PATH = '/dashboard/api?api_signup=success';
6
6
  export const startSignupTool = {
7
7
  name: 'start_signup',
8
8
  description:
9
- 'Start the public Posterly paid signup flow for a user before Posterly API access exists. Returns a Posterly checkout handoff URL and signup poll URL. The user must handle payment and password setup in the browser. Do not ask for card details or a Posterly password in chat.',
9
+ 'Start the public Posterly paid signup flow for a user before Posterly API access exists. Returns a signup poll URL, a browser Checkout fallback URL, and, when enabled, a prepaid PaymentIntent the agent can pay with a Link Agent Wallet virtual card (credential_type: card). The user still sets a password and approves social OAuth in the browser. Never ask for card numbers, a Posterly password, or social passwords in chat.',
10
10
  inputSchema: z.object({
11
11
  email: z
12
12
  .string()
@@ -36,6 +36,10 @@ export const startSignupTool = {
36
36
  .string()
37
37
  .default('posterly_mcp_pre_auth_signup')
38
38
  .describe('Source label for analytics and support diagnostics.'),
39
+ tos_accepted: z
40
+ .boolean()
41
+ .optional()
42
+ .describe('Set true only after the user has explicitly confirmed, in this conversation, that they accept the posterly terms at https://www.poster.ly/terms. Sending it here avoids a separate terms-confirmation step before client_secret is released.'),
39
43
  debug: z
40
44
  .boolean()
41
45
  .optional()
@@ -52,6 +56,7 @@ export const startSignupTool = {
52
56
  api_addon?: boolean;
53
57
  return_path?: string;
54
58
  source?: string;
59
+ tos_accepted?: boolean;
55
60
  debug?: boolean;
56
61
  },
57
62
  ) {
@@ -63,6 +68,7 @@ export const startSignupTool = {
63
68
  api_addon: input.api_addon ?? true,
64
69
  return_path: input.return_path ?? DEFAULT_RETURN_PATH,
65
70
  source: input.source ?? 'posterly_mcp_pre_auth_signup',
71
+ tos_accepted: input.tos_accepted,
66
72
  });
67
73
 
68
74
  return formatSignupStart(response, Boolean(input.debug));
@@ -84,23 +90,51 @@ function formatSignupStart(response: PublicSignupResponse, includeRaw: boolean):
84
90
  const completeUrl = response.complete_url || valueFromLinks(response, 'complete_url');
85
91
  const status = session?.status || response.status || 'checkout_pending';
86
92
  const message = session?.status_message || response.status_message;
93
+ const agentPay = response.agent_pay || session?.agent_pay;
87
94
 
88
95
  const lines = [
89
96
  'Posterly signup started.',
90
97
  sessionId ? `Signup session: ${sessionId}` : '',
91
98
  `Status: ${status}`,
92
99
  message ? `Message: ${message}` : '',
93
- checkoutRedirectUrl ? `Checkout handoff URL: ${checkoutRedirectUrl}` : '',
100
+ agentPay?.amount_display
101
+ ? `Agent pay amount: ${agentPay.amount_display} ${String(agentPay.currency || 'usd').toUpperCase()}`
102
+ : '',
103
+ agentPay?.payment_intent_id ? `PaymentIntent: ${agentPay.payment_intent_id}` : '',
104
+ agentPay?.requires_tos_acceptance ? 'Terms acceptance required before client_secret is released.' : '',
105
+ agentPay?.client_secret ? `PaymentIntent client_secret: ${agentPay.client_secret}` : '',
106
+ agentPay?.confirm_url ? `Confirm URL: ${agentPay.confirm_url}` : '',
107
+ agentPay?.pay_instructions ? `Pay instructions: ${agentPay.pay_instructions}` : '',
108
+ checkoutRedirectUrl ? `Checkout handoff URL (browser fallback): ${checkoutRedirectUrl}` : '',
94
109
  checkoutUrl ? `Checkout URL: ${checkoutUrl}` : '',
95
110
  pollUrl ? `Poll URL: ${pollUrl}` : '',
96
111
  completeUrl ? `Completion URL: ${completeUrl}` : '',
97
112
  '',
98
113
  'Next steps for the AI agent:',
99
- '1. Send the Checkout handoff URL to the user so they can pay securely in their browser. Use the raw Stripe checkout URL only if you preserve the full URL including any fragment. posterly does not email this link, so do not tell the user to check their inbox at this stage.',
100
- '2. Call get_signup_session with the signup session ID until the status changes.',
101
- '3. Tell the user when Posterly sends the password setup email.',
102
- '4. If status becomes agent_access_required, ask the user to install or paste the Posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
103
- '5. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
114
+ ...(agentPay?.enabled && agentPay?.requires_tos_acceptance
115
+ ? [
116
+ '1. Ask the user to explicitly confirm they accept the posterly terms at https://www.poster.ly/terms.',
117
+ '2. Once confirmed, POST { "tos_accepted": true } to Confirm URL (optionally with payment_method in the same call) to receive client_secret.',
118
+ '3. If the user would rather pay on the website, send them the Checkout handoff URL instead.',
119
+ '4. Call get_signup_session with the signup session ID until the status changes.',
120
+ '5. Do not ask for card details, the posterly password, social passwords, or OAuth codes.',
121
+ ]
122
+ : agentPay?.enabled
123
+ ? [
124
+ '1. Prefer agent_pay: confirm the PaymentIntent with a one-time Link Agent Wallet virtual card (credential_type: card). Never paste card numbers into chat or send them to posterly.',
125
+ '2. If the user would rather pay on the website, send them the Checkout handoff URL instead.',
126
+ '3. Call get_signup_session with the signup session ID until the status changes.',
127
+ '4. Tell the user when Posterly sends the password setup email. Password, social OAuth, and publish approval stay in the browser.',
128
+ '5. If status becomes agent_access_required, ask the user to install or paste the Posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
129
+ '6. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
130
+ ]
131
+ : [
132
+ '1. Send the Checkout handoff URL to the user so they can pay securely in their browser. Use the raw Stripe checkout URL only if you preserve the full URL including any fragment. posterly does not email this link, so do not tell the user to check their inbox at this stage.',
133
+ '2. Call get_signup_session with the signup session ID until the status changes.',
134
+ '3. Tell the user when Posterly sends the password setup email.',
135
+ '4. If status becomes agent_access_required, ask the user to install or paste the Posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
136
+ '5. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
137
+ ]),
104
138
  includeRaw ? `\nRaw signup response:\n${JSON.stringify(response, null, 2)}` : '',
105
139
  ];
106
140
 
@@ -113,7 +113,7 @@ const platformSettingsSchema = z.object({
113
113
  export const updatePostTool = {
114
114
  name: 'update_post',
115
115
  description:
116
- 'Update a scheduled or draft post. DESTRUCTIVE WRITE - overwrites the existing post\'s caption/media/schedule. Cannot edit published or currently publishing posts.\n\n' +
116
+ 'Update a scheduled or draft post. DESTRUCTIVE WRITE - overwrites the existing post\'s caption/media/schedule. Cannot edit published or currently publishing posts. If a client has already approved the post, a caption edit reopens caption approval and a media or thumbnail edit reopens asset approval. A reschedule, account change or settings change cannot be attributed to one axis and reopens both.\n\n' +
117
117
  'REQUIRED BEFORE CALLING: Show the user a side-by-side of the CURRENT post (caption, scheduled time, media) and the PROPOSED changes, and get explicit confirmation ("yes update it", "apply those changes") before calling. Do not auto-edit posts based on a general instruction - each edit needs its own confirmation.',
118
118
  inputSchema: z.object({
119
119
  post_id: z.number().describe('The post ID to update'),