posterly-mcp-server 0.19.4 → 0.19.6
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 +38 -18
- package/dist/index.js +52 -9
- package/dist/lib/api-client.d.ts +87 -0
- package/dist/lib/api-client.js +36 -1
- package/dist/tools/create-connect-session.d.ts +24 -0
- package/dist/tools/create-connect-session.js +38 -0
- package/dist/tools/create-webhook.d.ts +2 -2
- package/dist/tools/generate-image.d.ts +2 -2
- package/dist/tools/get-account-analytics.d.ts +7 -0
- package/dist/tools/get-account-analytics.js +121 -56
- package/dist/tools/get-agent-signup-info.d.ts +8 -0
- package/dist/tools/get-agent-signup-info.js +32 -0
- package/dist/tools/get-connect-session.d.ts +16 -0
- package/dist/tools/get-connect-session.js +26 -0
- package/dist/tools/get-post-analytics.d.ts +7 -0
- package/dist/tools/get-post-analytics.js +133 -54
- package/dist/tools/get-signup-session.d.ts +16 -0
- package/dist/tools/get-signup-session.js +65 -0
- package/dist/tools/get-video-job.d.ts +2 -2
- package/dist/tools/list-posts.d.ts +2 -2
- package/dist/tools/start-signup.d.ts +40 -0
- package/dist/tools/start-signup.js +83 -0
- package/dist/tools/update-webhook.d.ts +2 -2
- package/package.json +1 -1
- package/src/index.ts +77 -9
- package/src/lib/api-client.ts +142 -2
- package/src/tools/create-connect-session.ts +46 -0
- package/src/tools/get-account-analytics.ts +127 -64
- package/src/tools/get-agent-signup-info.ts +37 -0
- package/src/tools/get-connect-session.ts +34 -0
- package/src/tools/get-post-analytics.ts +139 -57
- package/src/tools/get-signup-session.ts +74 -0
- package/src/tools/start-signup.ts +104 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient, PublicSignupTier } from '../lib/api-client.js';
|
|
3
|
+
export declare const startSignupTool: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: z.ZodObject<{
|
|
7
|
+
email: z.ZodString;
|
|
8
|
+
name: z.ZodOptional<z.ZodString>;
|
|
9
|
+
tier: z.ZodDefault<z.ZodEnum<["starter", "pro", "power_user", "agency"]>>;
|
|
10
|
+
interval: z.ZodDefault<z.ZodEnum<["month"]>>;
|
|
11
|
+
api_addon: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
return_path: z.ZodDefault<z.ZodString>;
|
|
13
|
+
source: z.ZodDefault<z.ZodString>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
email: string;
|
|
16
|
+
tier: "starter" | "pro" | "power_user" | "agency";
|
|
17
|
+
interval: "month";
|
|
18
|
+
api_addon: boolean;
|
|
19
|
+
return_path: string;
|
|
20
|
+
source: string;
|
|
21
|
+
name?: string | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
email: string;
|
|
24
|
+
name?: string | undefined;
|
|
25
|
+
tier?: "starter" | "pro" | "power_user" | "agency" | undefined;
|
|
26
|
+
interval?: "month" | undefined;
|
|
27
|
+
api_addon?: boolean | undefined;
|
|
28
|
+
return_path?: string | undefined;
|
|
29
|
+
source?: string | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
execute(client: PosterlyClient, input: {
|
|
32
|
+
email: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
tier?: PublicSignupTier;
|
|
35
|
+
interval?: "month";
|
|
36
|
+
api_addon?: boolean;
|
|
37
|
+
return_path?: string;
|
|
38
|
+
source?: string;
|
|
39
|
+
}): Promise<string>;
|
|
40
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const DEFAULT_RETURN_PATH = '/dashboard/api?api_signup=success';
|
|
3
|
+
export const startSignupTool = {
|
|
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.',
|
|
6
|
+
inputSchema: z.object({
|
|
7
|
+
email: z
|
|
8
|
+
.string()
|
|
9
|
+
.email()
|
|
10
|
+
.describe('User email address to sign up. Confirm this with the user before calling.'),
|
|
11
|
+
name: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Optional user or workspace name to pass into Posterly signup.'),
|
|
15
|
+
tier: z
|
|
16
|
+
.enum(['starter', 'pro', 'power_user', 'agency'])
|
|
17
|
+
.default('starter')
|
|
18
|
+
.describe('Posterly plan to start. Use starter unless the user explicitly chooses another plan.'),
|
|
19
|
+
interval: z
|
|
20
|
+
.enum(['month'])
|
|
21
|
+
.default('month')
|
|
22
|
+
.describe('Billing interval. Public agent signup currently supports monthly billing.'),
|
|
23
|
+
api_addon: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.default(true)
|
|
26
|
+
.describe('Always true for AI agent setup because MCP/API access requires the API add-on.'),
|
|
27
|
+
return_path: z
|
|
28
|
+
.string()
|
|
29
|
+
.default(DEFAULT_RETURN_PATH)
|
|
30
|
+
.describe('Where Posterly should continue after checkout and password setup.'),
|
|
31
|
+
source: z
|
|
32
|
+
.string()
|
|
33
|
+
.default('posterly_mcp_pre_auth_signup')
|
|
34
|
+
.describe('Source label for analytics and support diagnostics.'),
|
|
35
|
+
}),
|
|
36
|
+
async execute(client, input) {
|
|
37
|
+
const response = await client.startSignup({
|
|
38
|
+
email: input.email,
|
|
39
|
+
name: input.name,
|
|
40
|
+
tier: input.tier ?? 'starter',
|
|
41
|
+
interval: input.interval ?? 'month',
|
|
42
|
+
api_addon: input.api_addon ?? true,
|
|
43
|
+
return_path: input.return_path ?? DEFAULT_RETURN_PATH,
|
|
44
|
+
source: input.source ?? 'posterly_mcp_pre_auth_signup',
|
|
45
|
+
});
|
|
46
|
+
return formatSignupStart(response);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
function valueFromLinks(response, key) {
|
|
50
|
+
const links = response.signup_session?.links ?? response.links;
|
|
51
|
+
const value = links?.[key];
|
|
52
|
+
return typeof value === 'string' ? value : null;
|
|
53
|
+
}
|
|
54
|
+
function formatSignupStart(response) {
|
|
55
|
+
const session = response.signup_session;
|
|
56
|
+
const sessionId = session?.id || response.checkout_session_id;
|
|
57
|
+
const checkoutUrl = response.checkout_url || valueFromLinks(response, 'checkout_url');
|
|
58
|
+
const checkoutRedirectUrl = response.checkout_redirect_url || session?.checkout_redirect_url || valueFromLinks(response, 'checkout_redirect_url');
|
|
59
|
+
const pollUrl = response.poll_url || valueFromLinks(response, 'poll_url');
|
|
60
|
+
const completeUrl = response.complete_url || valueFromLinks(response, 'complete_url');
|
|
61
|
+
const status = session?.status || response.status || 'checkout_pending';
|
|
62
|
+
const message = session?.status_message || response.status_message;
|
|
63
|
+
const lines = [
|
|
64
|
+
'Posterly signup started.',
|
|
65
|
+
sessionId ? `Signup session: ${sessionId}` : '',
|
|
66
|
+
`Status: ${status}`,
|
|
67
|
+
message ? `Message: ${message}` : '',
|
|
68
|
+
checkoutRedirectUrl ? `Checkout handoff URL: ${checkoutRedirectUrl}` : '',
|
|
69
|
+
checkoutUrl ? `Checkout URL: ${checkoutUrl}` : '',
|
|
70
|
+
pollUrl ? `Poll URL: ${pollUrl}` : '',
|
|
71
|
+
completeUrl ? `Completion URL: ${completeUrl}` : '',
|
|
72
|
+
'',
|
|
73
|
+
'Next steps for the AI agent:',
|
|
74
|
+
'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.',
|
|
75
|
+
'2. Call get_signup_session with the signup session ID until the status changes.',
|
|
76
|
+
'3. Tell the user when Posterly sends the password setup email.',
|
|
77
|
+
'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.',
|
|
78
|
+
'5. Do not ask for card details, the Posterly password, social passwords, or OAuth codes.',
|
|
79
|
+
'',
|
|
80
|
+
`Raw signup response:\n${JSON.stringify(response, null, 2)}`,
|
|
81
|
+
];
|
|
82
|
+
return lines.filter(Boolean).join('\n');
|
|
83
|
+
}
|
|
@@ -16,16 +16,16 @@ export declare const updateWebhookTool: {
|
|
|
16
16
|
webhook_id: string;
|
|
17
17
|
workspace_id?: string | null | undefined;
|
|
18
18
|
url?: string | undefined;
|
|
19
|
-
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced")[] | undefined;
|
|
20
19
|
description?: string | null | undefined;
|
|
20
|
+
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced")[] | undefined;
|
|
21
21
|
is_active?: boolean | undefined;
|
|
22
22
|
}, {
|
|
23
23
|
confirm: true;
|
|
24
24
|
webhook_id: string;
|
|
25
25
|
workspace_id?: string | null | undefined;
|
|
26
26
|
url?: string | undefined;
|
|
27
|
-
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced")[] | undefined;
|
|
28
27
|
description?: string | null | undefined;
|
|
28
|
+
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced")[] | undefined;
|
|
29
29
|
is_active?: boolean | undefined;
|
|
30
30
|
}>;
|
|
31
31
|
execute(client: PosterlyClient, input: {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
5
|
import { PosterlyClient } from './lib/api-client.js';
|
|
6
|
+
import { getAgentSignupInfoTool } from './tools/get-agent-signup-info.js';
|
|
7
|
+
import { getSignupSessionTool } from './tools/get-signup-session.js';
|
|
8
|
+
import { startSignupTool } from './tools/start-signup.js';
|
|
6
9
|
import { listAccountsTool } from './tools/list-accounts.js';
|
|
7
10
|
import { disconnectAccountTool } from './tools/disconnect-account.js';
|
|
8
11
|
import { listBrandsTool } from './tools/list-brands.js';
|
|
@@ -48,6 +51,8 @@ import { getXPostingQuotaTool } from './tools/get-x-posting-quota.js';
|
|
|
48
51
|
import { createSignedUploadTool } from './tools/create-signed-upload.js';
|
|
49
52
|
import { uploadMediaFromUrlTool } from './tools/upload-media-from-url.js';
|
|
50
53
|
import { getConnectLinkTool } from './tools/get-connect-link.js';
|
|
54
|
+
import { createConnectSessionTool } from './tools/create-connect-session.js';
|
|
55
|
+
import { getConnectSessionTool } from './tools/get-connect-session.js';
|
|
51
56
|
import { listOAuthClientsTool } from './tools/list-oauth-clients.js';
|
|
52
57
|
import { createOAuthClientTool } from './tools/create-oauth-client.js';
|
|
53
58
|
import { updateOAuthClientTool } from './tools/update-oauth-client.js';
|
|
@@ -55,19 +60,54 @@ import { deleteOAuthClientTool } from './tools/delete-oauth-client.js';
|
|
|
55
60
|
|
|
56
61
|
const server = new McpServer({
|
|
57
62
|
name: 'posterly',
|
|
58
|
-
version: '0.19.
|
|
63
|
+
version: '0.19.6',
|
|
59
64
|
});
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
client = new PosterlyClient();
|
|
65
|
-
} catch (err: any) {
|
|
66
|
-
console.error(err.message);
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
66
|
+
const client = new PosterlyClient();
|
|
69
67
|
|
|
70
68
|
// Register tools
|
|
69
|
+
server.tool(
|
|
70
|
+
getAgentSignupInfoTool.name,
|
|
71
|
+
getAgentSignupInfoTool.description,
|
|
72
|
+
getAgentSignupInfoTool.inputSchema.shape,
|
|
73
|
+
async () => {
|
|
74
|
+
try {
|
|
75
|
+
const text = await getAgentSignupInfoTool.execute(client);
|
|
76
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
77
|
+
} catch (err: any) {
|
|
78
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
server.tool(
|
|
84
|
+
startSignupTool.name,
|
|
85
|
+
startSignupTool.description,
|
|
86
|
+
startSignupTool.inputSchema.shape,
|
|
87
|
+
async (input) => {
|
|
88
|
+
try {
|
|
89
|
+
const text = await startSignupTool.execute(client, input as any);
|
|
90
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
91
|
+
} catch (err: any) {
|
|
92
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
server.tool(
|
|
98
|
+
getSignupSessionTool.name,
|
|
99
|
+
getSignupSessionTool.description,
|
|
100
|
+
getSignupSessionTool.inputSchema.shape,
|
|
101
|
+
async (input) => {
|
|
102
|
+
try {
|
|
103
|
+
const text = await getSignupSessionTool.execute(client, input as any);
|
|
104
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
105
|
+
} catch (err: any) {
|
|
106
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
71
111
|
server.tool(
|
|
72
112
|
whoamiTool.name,
|
|
73
113
|
whoamiTool.description,
|
|
@@ -124,6 +164,34 @@ server.tool(
|
|
|
124
164
|
}
|
|
125
165
|
);
|
|
126
166
|
|
|
167
|
+
server.tool(
|
|
168
|
+
createConnectSessionTool.name,
|
|
169
|
+
createConnectSessionTool.description,
|
|
170
|
+
createConnectSessionTool.inputSchema.shape,
|
|
171
|
+
async (input) => {
|
|
172
|
+
try {
|
|
173
|
+
const text = await createConnectSessionTool.execute(client, input as any);
|
|
174
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
175
|
+
} catch (err: any) {
|
|
176
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
server.tool(
|
|
182
|
+
getConnectSessionTool.name,
|
|
183
|
+
getConnectSessionTool.description,
|
|
184
|
+
getConnectSessionTool.inputSchema.shape,
|
|
185
|
+
async (input) => {
|
|
186
|
+
try {
|
|
187
|
+
const text = await getConnectSessionTool.execute(client, input as any);
|
|
188
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
189
|
+
} catch (err: any) {
|
|
190
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
|
|
127
195
|
server.tool(
|
|
128
196
|
listOAuthClientsTool.name,
|
|
129
197
|
listOAuthClientsTool.description,
|
package/src/lib/api-client.ts
CHANGED
|
@@ -152,7 +152,7 @@ export interface AccountAnalyticsSummary {
|
|
|
152
152
|
total_watch_minutes?: number | null;
|
|
153
153
|
total_likes?: number | null;
|
|
154
154
|
platform_metrics?: Record<string, number | null | undefined>;
|
|
155
|
-
display_metrics?: Array<{ label: string; value: number | null | undefined }>;
|
|
155
|
+
display_metrics?: Array<{ key?: string; label: string; value: number | null | undefined; unit?: string }>;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
export interface AccountAnalyticsSnapshot {
|
|
@@ -328,6 +328,35 @@ export interface ConnectOption {
|
|
|
328
328
|
connected_accounts?: Account[];
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
export interface ConnectSession {
|
|
332
|
+
id: string;
|
|
333
|
+
platform: string;
|
|
334
|
+
method: 'dashboard_oauth' | 'manual_credentials';
|
|
335
|
+
status:
|
|
336
|
+
| 'created'
|
|
337
|
+
| 'opened'
|
|
338
|
+
| 'awaiting_provider'
|
|
339
|
+
| 'awaiting_credentials'
|
|
340
|
+
| 'connected'
|
|
341
|
+
| 'failed'
|
|
342
|
+
| 'cancelled'
|
|
343
|
+
| 'expired';
|
|
344
|
+
status_message: string;
|
|
345
|
+
connect_url: string;
|
|
346
|
+
poll_url: string;
|
|
347
|
+
workspace_id?: string | null;
|
|
348
|
+
connected_account_ids: string[];
|
|
349
|
+
connected_accounts: Account[];
|
|
350
|
+
connected_count: number;
|
|
351
|
+
error_code?: string | null;
|
|
352
|
+
error_message?: string | null;
|
|
353
|
+
created_at: string;
|
|
354
|
+
opened_at?: string | null;
|
|
355
|
+
provider_redirected_at?: string | null;
|
|
356
|
+
completed_at?: string | null;
|
|
357
|
+
expires_at: string;
|
|
358
|
+
}
|
|
359
|
+
|
|
331
360
|
export interface OAuthDeveloperClient {
|
|
332
361
|
id: string;
|
|
333
362
|
client_id: string;
|
|
@@ -341,6 +370,60 @@ export interface OAuthDeveloperClient {
|
|
|
341
370
|
updated_at?: string;
|
|
342
371
|
}
|
|
343
372
|
|
|
373
|
+
export type PublicSignupTier = 'starter' | 'pro' | 'power_user' | 'agency';
|
|
374
|
+
|
|
375
|
+
export interface PublicSignupPayload {
|
|
376
|
+
email: string;
|
|
377
|
+
name?: string;
|
|
378
|
+
tier?: PublicSignupTier;
|
|
379
|
+
interval?: 'month';
|
|
380
|
+
api_addon?: boolean;
|
|
381
|
+
return_path?: string;
|
|
382
|
+
source?: string;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface PublicSignupSession {
|
|
386
|
+
id: string;
|
|
387
|
+
status: string;
|
|
388
|
+
status_message?: string;
|
|
389
|
+
next_action?: {
|
|
390
|
+
actor?: string;
|
|
391
|
+
type?: string;
|
|
392
|
+
label?: string;
|
|
393
|
+
description?: string;
|
|
394
|
+
};
|
|
395
|
+
agent_next_steps?: string[];
|
|
396
|
+
checkout_redirect_url?: string | null;
|
|
397
|
+
checkout?: Record<string, unknown>;
|
|
398
|
+
account?: Record<string, unknown>;
|
|
399
|
+
billing?: Record<string, unknown>;
|
|
400
|
+
links?: Record<string, unknown>;
|
|
401
|
+
created_at?: string;
|
|
402
|
+
updated_at?: string;
|
|
403
|
+
expires_at?: string;
|
|
404
|
+
[key: string]: unknown;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export interface PublicSignupResponse {
|
|
408
|
+
signup_session?: PublicSignupSession;
|
|
409
|
+
checkout_session_id?: string;
|
|
410
|
+
checkout_url?: string | null;
|
|
411
|
+
checkout_redirect_url?: string | null;
|
|
412
|
+
poll_url?: string | null;
|
|
413
|
+
complete_url?: string | null;
|
|
414
|
+
dashboard_url?: string | null;
|
|
415
|
+
status?: string;
|
|
416
|
+
status_message?: string;
|
|
417
|
+
links?: Record<string, unknown>;
|
|
418
|
+
account?: Record<string, unknown>;
|
|
419
|
+
billing?: Record<string, unknown>;
|
|
420
|
+
[key: string]: unknown;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export interface PublicSignupSessionResponse {
|
|
424
|
+
signup_session: PublicSignupSession;
|
|
425
|
+
}
|
|
426
|
+
|
|
344
427
|
export interface VideoJob {
|
|
345
428
|
id: string;
|
|
346
429
|
status: string;
|
|
@@ -387,9 +470,17 @@ export class PosterlyClient {
|
|
|
387
470
|
constructor(apiKey?: string, baseUrl?: string) {
|
|
388
471
|
this.apiKey = apiKey || process.env.POSTERLY_API_KEY || '';
|
|
389
472
|
this.baseUrl = (baseUrl || process.env.POSTERLY_URL || 'https://www.poster.ly').replace(/\/$/, '');
|
|
473
|
+
}
|
|
390
474
|
|
|
475
|
+
hasApiKey(): boolean {
|
|
476
|
+
return Boolean(this.apiKey);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
private requireApiKey(): void {
|
|
391
480
|
if (!this.apiKey) {
|
|
392
|
-
throw new Error(
|
|
481
|
+
throw new Error(
|
|
482
|
+
'Posterly access is not installed yet. Use start_signup to begin paid setup, or set POSTERLY_API_KEY after signup.'
|
|
483
|
+
);
|
|
393
484
|
}
|
|
394
485
|
}
|
|
395
486
|
|
|
@@ -398,6 +489,8 @@ export class PosterlyClient {
|
|
|
398
489
|
path: string,
|
|
399
490
|
body?: unknown,
|
|
400
491
|
): Promise<T> {
|
|
492
|
+
this.requireApiKey();
|
|
493
|
+
|
|
401
494
|
const url = `${this.baseUrl}/api/v1${path}`;
|
|
402
495
|
const headers: Record<string, string> = {
|
|
403
496
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -422,6 +515,38 @@ export class PosterlyClient {
|
|
|
422
515
|
return res.json() as Promise<T>;
|
|
423
516
|
}
|
|
424
517
|
|
|
518
|
+
private async publicRequest<T>(
|
|
519
|
+
method: string,
|
|
520
|
+
path: string,
|
|
521
|
+
body?: unknown,
|
|
522
|
+
): Promise<T> {
|
|
523
|
+
const url = `${this.baseUrl}${path}`;
|
|
524
|
+
const headers: Record<string, string> = {};
|
|
525
|
+
const init: RequestInit = { method, headers };
|
|
526
|
+
|
|
527
|
+
if (body) {
|
|
528
|
+
headers['Content-Type'] = 'application/json';
|
|
529
|
+
init.body = JSON.stringify(body);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const res = await fetch(url, init);
|
|
533
|
+
|
|
534
|
+
if (!res.ok) {
|
|
535
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
536
|
+
throw new Error(err.error || `API error: ${res.status}`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return res.json() as Promise<T>;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async startSignup(data: PublicSignupPayload): Promise<PublicSignupResponse> {
|
|
543
|
+
return this.publicRequest('POST', '/api/v1/signup', data);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
async getSignupSession(sessionId: string): Promise<PublicSignupSessionResponse> {
|
|
547
|
+
return this.publicRequest('GET', `/api/v1/signup/sessions/${encodeURIComponent(sessionId)}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
425
550
|
async whoami(): Promise<Whoami> {
|
|
426
551
|
return this.request<Whoami>('GET', '/whoami');
|
|
427
552
|
}
|
|
@@ -453,6 +578,19 @@ export class PosterlyClient {
|
|
|
453
578
|
return this.request('GET', `${path}${qs ? `?${qs}` : ''}`);
|
|
454
579
|
}
|
|
455
580
|
|
|
581
|
+
async createConnectSession(data: {
|
|
582
|
+
platform: string;
|
|
583
|
+
workspace_id?: string;
|
|
584
|
+
auto_start?: boolean;
|
|
585
|
+
}): Promise<{ connect_session: ConnectSession; connect?: ConnectOption }> {
|
|
586
|
+
const { platform, ...body } = data;
|
|
587
|
+
return this.request('POST', `/connect/${encodeURIComponent(platform)}/sessions`, body);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async getConnectSession(sessionId: string): Promise<{ connect_session: ConnectSession }> {
|
|
591
|
+
return this.request('GET', `/connect/sessions/${encodeURIComponent(sessionId)}`);
|
|
592
|
+
}
|
|
593
|
+
|
|
456
594
|
async listOAuthClients(): Promise<{ clients: OAuthDeveloperClient[] }> {
|
|
457
595
|
return this.request('GET', '/oauth/clients');
|
|
458
596
|
}
|
|
@@ -879,6 +1017,8 @@ export class PosterlyClient {
|
|
|
879
1017
|
filename: string;
|
|
880
1018
|
contentType?: string;
|
|
881
1019
|
}): Promise<{ url: string; path: string }> {
|
|
1020
|
+
this.requireApiKey();
|
|
1021
|
+
|
|
882
1022
|
let fileBuffer: Buffer;
|
|
883
1023
|
|
|
884
1024
|
if (input.filePath) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ConnectSession, PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { CONNECT_INPUTS } from '../generated/platform-manifest.js';
|
|
4
|
+
|
|
5
|
+
export const createConnectSessionTool = {
|
|
6
|
+
name: 'create_connect_session',
|
|
7
|
+
description:
|
|
8
|
+
'Create a short-lived Posterly dashboard handoff session for connecting a social account. Open connect_session.connect_url for the user, then poll get_connect_session to narrate progress.',
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
platform: z
|
|
11
|
+
.enum(CONNECT_INPUTS)
|
|
12
|
+
.describe('Connection target such as instagram, meta, linkedin_page, twitter, google_business, telegram, or bluesky.'),
|
|
13
|
+
workspace_id: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Workspace to connect the account into. Workspace-scoped API keys ignore this.'),
|
|
17
|
+
auto_start: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('When true, the dashboard starts the provider flow after the user opens the URL. Defaults to true.'),
|
|
21
|
+
}),
|
|
22
|
+
|
|
23
|
+
async execute(
|
|
24
|
+
client: PosterlyClient,
|
|
25
|
+
input: { platform: string; workspace_id?: string; auto_start?: boolean },
|
|
26
|
+
) {
|
|
27
|
+
const result = await client.createConnectSession(input);
|
|
28
|
+
return formatConnectSession(result.connect_session, true);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function formatConnectSession(session: ConnectSession, includeRaw: boolean): string {
|
|
33
|
+
return [
|
|
34
|
+
`Posterly connect session: ${session.id}`,
|
|
35
|
+
`Platform: ${session.platform}`,
|
|
36
|
+
`Status: ${session.status}`,
|
|
37
|
+
`Message: ${session.status_message}`,
|
|
38
|
+
`Connection URL: ${session.connect_url}`,
|
|
39
|
+
`Poll URL: ${session.poll_url}`,
|
|
40
|
+
`Connected accounts: ${session.connected_count ?? session.connected_accounts?.length ?? 0}`,
|
|
41
|
+
`Expires: ${session.expires_at}`,
|
|
42
|
+
'',
|
|
43
|
+
'Next step: Open the connection URL for the user, then call get_connect_session with this session ID until the status is terminal.',
|
|
44
|
+
includeRaw ? `\nRaw connect session:\n${JSON.stringify(session, null, 2)}` : '',
|
|
45
|
+
].filter(Boolean).join('\n');
|
|
46
|
+
}
|