posterly-mcp-server 0.20.3 → 0.20.5

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
@@ -108,7 +108,7 @@ Add the same server definition to your Cursor MCP settings:
108
108
 
109
109
  ## Available tools
110
110
 
111
- `posterly-mcp-server@0.20.3` exposes 58 tools.
111
+ `posterly-mcp-server@0.20.5` exposes 59 tools.
112
112
 
113
113
  Public setup tools work before `POSTERLY_API_KEY` exists:
114
114
 
@@ -125,6 +125,7 @@ Authenticated tools require `POSTERLY_API_KEY`:
125
125
  - `get_connect_link`
126
126
  - `create_connect_session` (create a guided browser handoff for connecting a social account)
127
127
  - `get_connect_session` (poll connection progress while the user approves OAuth or enters credentials)
128
+ - `create_api_key` (create a new API key after explicit confirmation; scopes cannot exceed the calling dashboard key)
128
129
  - `list_oauth_clients`
129
130
  - `create_oauth_client` (create a public PKCE client after explicit confirmation)
130
131
  - `update_oauth_client` (update redirect URIs/scopes after explicit confirmation)
package/dist/index.js CHANGED
@@ -56,6 +56,7 @@ import { uploadMediaFromUrlTool } from './tools/upload-media-from-url.js';
56
56
  import { getConnectLinkTool } from './tools/get-connect-link.js';
57
57
  import { createConnectSessionTool } from './tools/create-connect-session.js';
58
58
  import { getConnectSessionTool } from './tools/get-connect-session.js';
59
+ import { createApiKeyTool } from './tools/create-api-key.js';
59
60
  import { listOAuthClientsTool } from './tools/list-oauth-clients.js';
60
61
  import { createOAuthClientTool } from './tools/create-oauth-client.js';
61
62
  import { updateOAuthClientTool } from './tools/update-oauth-client.js';
@@ -156,6 +157,15 @@ server.tool(getConnectSessionTool.name, getConnectSessionTool.description, getCo
156
157
  return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
157
158
  }
158
159
  });
160
+ server.tool(createApiKeyTool.name, createApiKeyTool.description, createApiKeyTool.inputSchema.shape, async (input) => {
161
+ try {
162
+ const text = await createApiKeyTool.execute(client, input);
163
+ return { content: [{ type: 'text', text }] };
164
+ }
165
+ catch (err) {
166
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
167
+ }
168
+ });
159
169
  server.tool(listOAuthClientsTool.name, listOAuthClientsTool.description, listOAuthClientsTool.inputSchema.shape, async () => {
160
170
  try {
161
171
  const text = await listOAuthClientsTool.execute(client);
@@ -159,6 +159,34 @@ export type AskSupportPayload = {
159
159
  request_human?: boolean;
160
160
  confirm_escalation?: boolean;
161
161
  };
162
+ export type ApiKeyScope = 'accounts:read' | 'accounts:write' | 'posts:read' | 'posts:write' | 'media:write' | 'analytics:read';
163
+ export type CreateApiKeyPayload = {
164
+ name?: string;
165
+ scopes?: ApiKeyScope[];
166
+ workspace_id?: string;
167
+ expires_in_days?: number;
168
+ confirm: true;
169
+ };
170
+ export type CreateApiKeyResponse = {
171
+ key: string;
172
+ api_key: {
173
+ id: string;
174
+ name: string;
175
+ key_prefix: string;
176
+ scopes: ApiKeyScope[];
177
+ workspace_id?: string | null;
178
+ expires_at?: string | null;
179
+ created_at: string;
180
+ };
181
+ warning?: string;
182
+ limits?: {
183
+ tier?: string;
184
+ max_user_keys?: number;
185
+ active_user_keys?: number;
186
+ remaining_user_keys?: number;
187
+ uses_grandfathered_keys?: boolean;
188
+ };
189
+ };
162
190
  export type AskSupportResponse = {
163
191
  success: boolean;
164
192
  workspace_id: string;
@@ -552,6 +580,7 @@ export declare class PosterlyClient {
552
580
  startSignup(data: PublicSignupPayload): Promise<PublicSignupResponse>;
553
581
  getSignupSession(sessionId: string): Promise<PublicSignupSessionResponse>;
554
582
  whoami(): Promise<Whoami>;
583
+ createApiKey(data: CreateApiKeyPayload): Promise<CreateApiKeyResponse>;
555
584
  listAccounts(params?: {
556
585
  workspace_id?: string;
557
586
  }): Promise<Account[]>;
@@ -113,6 +113,9 @@ export class PosterlyClient {
113
113
  async whoami() {
114
114
  return this.request('GET', '/whoami');
115
115
  }
116
+ async createApiKey(data) {
117
+ return this.request('POST', '/api-keys', data);
118
+ }
116
119
  async listAccounts(params) {
117
120
  const searchParams = new URLSearchParams();
118
121
  if (params?.workspace_id)
@@ -1 +1 @@
1
- export declare const POSTERLY_MCP_VERSION = "0.20.3";
1
+ export declare const POSTERLY_MCP_VERSION = "0.20.5";
@@ -1 +1 @@
1
- export const POSTERLY_MCP_VERSION = '0.20.3';
1
+ export const POSTERLY_MCP_VERSION = '0.20.5';
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+ import type { CreateApiKeyPayload, PosterlyClient } from '../lib/api-client.js';
3
+ export declare const createApiKeyTool: {
4
+ name: string;
5
+ description: string;
6
+ inputSchema: z.ZodObject<{
7
+ name: z.ZodOptional<z.ZodString>;
8
+ scopes: z.ZodOptional<z.ZodArray<z.ZodEnum<["accounts:read", "accounts:write", "posts:read", "posts:write", "media:write", "analytics:read"]>, "many">>;
9
+ workspace_id: z.ZodOptional<z.ZodString>;
10
+ expires_in_days: z.ZodOptional<z.ZodNumber>;
11
+ confirm: z.ZodLiteral<true>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ confirm: true;
14
+ workspace_id?: string | undefined;
15
+ name?: string | undefined;
16
+ scopes?: ("accounts:read" | "accounts:write" | "posts:read" | "posts:write" | "media:write" | "analytics:read")[] | undefined;
17
+ expires_in_days?: number | undefined;
18
+ }, {
19
+ confirm: true;
20
+ workspace_id?: string | undefined;
21
+ name?: string | undefined;
22
+ scopes?: ("accounts:read" | "accounts:write" | "posts:read" | "posts:write" | "media:write" | "analytics:read")[] | undefined;
23
+ expires_in_days?: number | undefined;
24
+ }>;
25
+ execute(client: PosterlyClient, input: CreateApiKeyPayload): Promise<string>;
26
+ };
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod';
2
+ import { code, mdKeyValue, mdSection, mdTitle } from '../lib/format.js';
3
+ const scopeSchema = z.enum(['accounts:read', 'accounts:write', 'posts:read', 'posts:write', 'media:write', 'analytics:read']);
4
+ function formatCreatedApiKey(result) {
5
+ const key = result.api_key;
6
+ return [
7
+ mdTitle('API key created'),
8
+ `Full key (shown once): ${code(result.key)}`,
9
+ mdSection('Details', mdKeyValue([
10
+ ['Name', key.name],
11
+ ['Key ID', code(key.id)],
12
+ ['Prefix', code(key.key_prefix)],
13
+ ['Scopes', key.scopes.join(', ')],
14
+ ['Workspace', key.workspace_id ? code(key.workspace_id) : 'all accessible workspaces'],
15
+ ['Expires', key.expires_at || 'no expiry'],
16
+ ['Remaining key slots', result.limits?.remaining_user_keys ?? 'unknown'],
17
+ ])),
18
+ mdSection('Warning', result.warning || 'Store this securely. It will not be shown again.'),
19
+ ].filter(Boolean).join('\n\n');
20
+ }
21
+ export const createApiKeyTool = {
22
+ name: 'create_api_key',
23
+ description: 'Create a new Posterly API key for the authenticated user. SECRET-CREATING WRITE: only use after explicit user confirmation. The new key can only request scopes already present on the calling dashboard-created API key; OAuth and managed assistant tokens cannot mint keys.',
24
+ inputSchema: z.object({
25
+ name: z.string().trim().min(1).max(120).optional().describe('Human-readable key name.'),
26
+ scopes: z.array(scopeSchema).min(1).max(6).optional().describe('Scopes for the new key. Omit to copy the calling key scopes. Cannot exceed the calling key scopes.'),
27
+ workspace_id: z.string().trim().min(1).optional().describe('Optional workspace restriction. Workspace-scoped calling keys cannot create keys outside their workspace.'),
28
+ expires_in_days: z.number().int().min(1).max(365).optional().describe('Optional expiry from now, up to 365 days. Omit for no expiry.'),
29
+ confirm: z.literal(true).describe('Must be true after the user explicitly confirms that a new secret API key should be created.'),
30
+ }),
31
+ async execute(client, input) {
32
+ const result = await client.createApiKey(input);
33
+ return formatCreatedApiKey(result);
34
+ },
35
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posterly-mcp-server",
3
- "version": "0.20.3",
3
+ "version": "0.20.5",
4
4
  "description": "MCP server for posterly: schedule social media posts from Claude Desktop",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.poster.ly/mcp",
package/src/index.ts CHANGED
@@ -57,6 +57,7 @@ import { uploadMediaFromUrlTool } from './tools/upload-media-from-url.js';
57
57
  import { getConnectLinkTool } from './tools/get-connect-link.js';
58
58
  import { createConnectSessionTool } from './tools/create-connect-session.js';
59
59
  import { getConnectSessionTool } from './tools/get-connect-session.js';
60
+ import { createApiKeyTool } from './tools/create-api-key.js';
60
61
  import { listOAuthClientsTool } from './tools/list-oauth-clients.js';
61
62
  import { createOAuthClientTool } from './tools/create-oauth-client.js';
62
63
  import { updateOAuthClientTool } from './tools/update-oauth-client.js';
@@ -210,6 +211,20 @@ server.tool(
210
211
  }
211
212
  );
212
213
 
214
+ server.tool(
215
+ createApiKeyTool.name,
216
+ createApiKeyTool.description,
217
+ createApiKeyTool.inputSchema.shape,
218
+ async (input) => {
219
+ try {
220
+ const text = await createApiKeyTool.execute(client, input as any);
221
+ return { content: [{ type: 'text' as const, text }] };
222
+ } catch (err: any) {
223
+ return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
224
+ }
225
+ }
226
+ );
227
+
213
228
  server.tool(
214
229
  listOAuthClientsTool.name,
215
230
  listOAuthClientsTool.description,
@@ -163,6 +163,43 @@ export type AskSupportPayload = {
163
163
  confirm_escalation?: boolean;
164
164
  };
165
165
 
166
+ export type ApiKeyScope =
167
+ | 'accounts:read'
168
+ | 'accounts:write'
169
+ | 'posts:read'
170
+ | 'posts:write'
171
+ | 'media:write'
172
+ | 'analytics:read';
173
+
174
+ export type CreateApiKeyPayload = {
175
+ name?: string;
176
+ scopes?: ApiKeyScope[];
177
+ workspace_id?: string;
178
+ expires_in_days?: number;
179
+ confirm: true;
180
+ };
181
+
182
+ export type CreateApiKeyResponse = {
183
+ key: string;
184
+ api_key: {
185
+ id: string;
186
+ name: string;
187
+ key_prefix: string;
188
+ scopes: ApiKeyScope[];
189
+ workspace_id?: string | null;
190
+ expires_at?: string | null;
191
+ created_at: string;
192
+ };
193
+ warning?: string;
194
+ limits?: {
195
+ tier?: string;
196
+ max_user_keys?: number;
197
+ active_user_keys?: number;
198
+ remaining_user_keys?: number;
199
+ uses_grandfathered_keys?: boolean;
200
+ };
201
+ };
202
+
166
203
  export type AskSupportResponse = {
167
204
  success: boolean;
168
205
  workspace_id: string;
@@ -693,6 +730,10 @@ export class PosterlyClient {
693
730
  return this.request<Whoami>('GET', '/whoami');
694
731
  }
695
732
 
733
+ async createApiKey(data: CreateApiKeyPayload): Promise<CreateApiKeyResponse> {
734
+ return this.request('POST', '/api-keys', data);
735
+ }
736
+
696
737
  async listAccounts(params?: { workspace_id?: string }): Promise<Account[]> {
697
738
  const searchParams = new URLSearchParams();
698
739
  if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
@@ -1 +1 @@
1
- export const POSTERLY_MCP_VERSION = '0.20.3';
1
+ export const POSTERLY_MCP_VERSION = '0.20.5';
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ import type { CreateApiKeyPayload, CreateApiKeyResponse, PosterlyClient } from '../lib/api-client.js';
3
+ import { code, mdKeyValue, mdSection, mdTitle } from '../lib/format.js';
4
+
5
+ const scopeSchema = z.enum(['accounts:read', 'accounts:write', 'posts:read', 'posts:write', 'media:write', 'analytics:read']);
6
+
7
+ function formatCreatedApiKey(result: CreateApiKeyResponse): string {
8
+ const key = result.api_key;
9
+ return [
10
+ mdTitle('API key created'),
11
+ `Full key (shown once): ${code(result.key)}`,
12
+ mdSection('Details', mdKeyValue([
13
+ ['Name', key.name],
14
+ ['Key ID', code(key.id)],
15
+ ['Prefix', code(key.key_prefix)],
16
+ ['Scopes', key.scopes.join(', ')],
17
+ ['Workspace', key.workspace_id ? code(key.workspace_id) : 'all accessible workspaces'],
18
+ ['Expires', key.expires_at || 'no expiry'],
19
+ ['Remaining key slots', result.limits?.remaining_user_keys ?? 'unknown'],
20
+ ])),
21
+ mdSection('Warning', result.warning || 'Store this securely. It will not be shown again.'),
22
+ ].filter(Boolean).join('\n\n');
23
+ }
24
+
25
+ export const createApiKeyTool = {
26
+ name: 'create_api_key',
27
+ description:
28
+ 'Create a new Posterly API key for the authenticated user. SECRET-CREATING WRITE: only use after explicit user confirmation. The new key can only request scopes already present on the calling dashboard-created API key; OAuth and managed assistant tokens cannot mint keys.',
29
+ inputSchema: z.object({
30
+ name: z.string().trim().min(1).max(120).optional().describe('Human-readable key name.'),
31
+ scopes: z.array(scopeSchema).min(1).max(6).optional().describe('Scopes for the new key. Omit to copy the calling key scopes. Cannot exceed the calling key scopes.'),
32
+ workspace_id: z.string().trim().min(1).optional().describe('Optional workspace restriction. Workspace-scoped calling keys cannot create keys outside their workspace.'),
33
+ expires_in_days: z.number().int().min(1).max(365).optional().describe('Optional expiry from now, up to 365 days. Omit for no expiry.'),
34
+ confirm: z.literal(true).describe('Must be true after the user explicitly confirms that a new secret API key should be created.'),
35
+ }),
36
+
37
+ async execute(client: PosterlyClient, input: CreateApiKeyPayload) {
38
+ const result = await client.createApiKey(input);
39
+ return formatCreatedApiKey(result);
40
+ },
41
+ };