planpost-mcp 0.2.1 → 0.4.0
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/CHANGELOG.md +49 -0
- package/README.md +228 -16
- package/dist/annotations.d.ts +3 -0
- package/dist/annotations.js +66 -0
- package/dist/index.js +271 -39
- package/dist/oauth.d.ts +73 -0
- package/dist/oauth.js +122 -0
- package/package.json +36 -11
- package/skills/planpost/SKILL.md +114 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `planpost-mcp`. Versions follow [semver](https://semver.org).
|
|
4
|
+
|
|
5
|
+
## 0.4.0 — 2026-11-09
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **OAuth sign-in for the hosted server.** With `--http`, the server is now an
|
|
9
|
+
OAuth 2.1 resource server per the MCP authorization spec (revision
|
|
10
|
+
2026-07-28): protected-resource metadata at
|
|
11
|
+
`/.well-known/oauth-protected-resource`, a `401` +
|
|
12
|
+
`WWW-Authenticate: Bearer resource_metadata="…"` challenge for
|
|
13
|
+
unauthenticated requests, access-token validation by introspection with an
|
|
14
|
+
audience check, and no token passthrough. Clients that support remote MCP
|
|
15
|
+
authorization (Claude, ChatGPT, Cursor, Gemini CLI, VS Code) can connect to
|
|
16
|
+
`https://mcp.planpo.st/mcp` with no key. Enabled by
|
|
17
|
+
`PLANPOST_MCP_PUBLIC_URL` + `PLANPOST_MCP_SERVICE_SECRET`; without them the
|
|
18
|
+
server behaves exactly as before.
|
|
19
|
+
- **Tool annotations** on all 49 tools: `title`, `readOnlyHint`,
|
|
20
|
+
`destructiveHint`, `idempotentHint`, `openWorldHint`.
|
|
21
|
+
- **Agent bundles** in the repository: `skills/planpost/SKILL.md`
|
|
22
|
+
(`npx skills add`), `gemini-extension.json` + `GEMINI.md` (Gemini CLI
|
|
23
|
+
extension), `.claude-plugin/` (Claude Code plugin and marketplace) and
|
|
24
|
+
`.cursor-plugin/` + `mcp.json` (Cursor plugin).
|
|
25
|
+
- Tests for hosted OAuth mode and for manifest consistency; `npm test` now
|
|
26
|
+
builds first.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- `server.json`: the hosted remote's `Authorization` header is optional
|
|
30
|
+
(omit it to sign in with OAuth).
|
|
31
|
+
- API-key authentication (`Authorization: Bearer pk_…` over HTTP,
|
|
32
|
+
`PLANPOST_API_KEY` over stdio) is unchanged.
|
|
33
|
+
|
|
34
|
+
## 0.3.0 — 2026-11-08
|
|
35
|
+
|
|
36
|
+
- Listed on the official MCP Registry as `io.github.lukBB/planpost`
|
|
37
|
+
(`server.json`, `mcpName`).
|
|
38
|
+
- Hosted-first README: install snippets for Claude Code, Claude Desktop,
|
|
39
|
+
Cursor, Codex CLI and Gemini CLI.
|
|
40
|
+
- `retry_failed` and `get_delivery_report` tools; publish failures carry an
|
|
41
|
+
error class and a suggested fix.
|
|
42
|
+
- `npm test`: registry schema validation and a stdio boot test.
|
|
43
|
+
|
|
44
|
+
## 0.2.x
|
|
45
|
+
|
|
46
|
+
- Streamable HTTP mode (`--http`) with per-request API keys, powering
|
|
47
|
+
`https://mcp.planpo.st/mcp`.
|
|
48
|
+
- Account groups, approvals, smart scheduling, carousels, video rendering,
|
|
49
|
+
content sources, brand voice, revenue overview.
|
package/README.md
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
# planpost-mcp
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) server for [PlanPost](https://planpo.st)
|
|
3
|
+
MCP (Model Context Protocol) server for [PlanPost](https://planpo.st), the agentic social scheduler with revenue attribution. It lets Claude, ChatGPT, Cursor, Gemini CLI and other AI agents schedule social media posts, create videos and carousels, manage content sources, and read analytics and revenue data by talking to your PlanPost account.
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
|
+
### The short way: sign in
|
|
8
|
+
|
|
9
|
+
Add `https://mcp.planpo.st/mcp` to your agent as a remote MCP server (a
|
|
10
|
+
"connector" in Claude and ChatGPT) and leave the authentication fields empty.
|
|
11
|
+
The first time the agent uses it, a browser opens on planpo.st: log in, review
|
|
12
|
+
what the agent may do, and choose whether its posts wait for your approval.
|
|
13
|
+
No key to create or paste.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
claude mcp add --transport http planpost https://mcp.planpo.st/mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{ "mcpServers": { "planpost": { "url": "https://mcp.planpo.st/mcp" } } }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Connected agents are listed under **Settings → API Keys → Connected apps**,
|
|
24
|
+
where you can remove one at any time. They use the same permissions and the
|
|
25
|
+
same monthly call allowance as API keys.
|
|
26
|
+
|
|
27
|
+
The repository also ships ready-made bundles: a Claude Code plugin
|
|
28
|
+
(`.claude-plugin/`), a Cursor plugin (`.cursor-plugin/`), a Gemini CLI
|
|
29
|
+
extension (`gemini-extension.json`) and an agent skill:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx skills add lukBB/planpost-mcp --skill planpost
|
|
33
|
+
gemini extensions install https://github.com/lukBB/planpost-mcp
|
|
34
|
+
claude plugin marketplace add lukBB/planpost-mcp && claude plugin install planpost@planpost-plugins
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The rest of this section is the API-key route: use it for clients without
|
|
38
|
+
OAuth support, for CI, or when you want a key limited to a few permissions.
|
|
39
|
+
|
|
7
40
|
### 1. Get an API key
|
|
8
41
|
|
|
9
42
|
In the PlanPost dashboard go to **Settings → API Keys → Create Key** and select the permissions you need (see the table below). The key (`pk_live_…`) is shown **once** — copy it.
|
|
@@ -12,13 +45,36 @@ In the PlanPost dashboard go to **Settings → API Keys → Create Key** and sel
|
|
|
12
45
|
|
|
13
46
|
### 2. Add the server to your agent
|
|
14
47
|
|
|
15
|
-
**
|
|
48
|
+
Two ways to connect. **Hosted** is the default: point your client at
|
|
49
|
+
`https://mcp.planpo.st/mcp`, nothing to install, your key travels as a header.
|
|
50
|
+
**Local** runs the same server on your machine with `npx`, so the key never
|
|
51
|
+
leaves it. Both expose the same 49 tools.
|
|
52
|
+
|
|
53
|
+
Replace `pk_live_xxx` with your key throughout.
|
|
54
|
+
|
|
55
|
+
**Claude Code**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
claude mcp add --transport http planpost https://mcp.planpo.st/mcp \
|
|
59
|
+
--header "Authorization: Bearer pk_live_xxx"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Local fallback:
|
|
16
63
|
|
|
17
64
|
```bash
|
|
18
65
|
claude mcp add planpost -e PLANPOST_API_KEY=pk_live_xxx -- npx -y planpost-mcp
|
|
19
66
|
```
|
|
20
67
|
|
|
21
|
-
|
|
68
|
+
Run `claude mcp list` afterwards to confirm it is connected.
|
|
69
|
+
|
|
70
|
+
**Claude Desktop**
|
|
71
|
+
|
|
72
|
+
Hosted: Settings → Connectors → Add custom connector. Name `PlanPost`, URL
|
|
73
|
+
`https://mcp.planpo.st/mcp`, then add the header `Authorization: Bearer pk_live_xxx`.
|
|
74
|
+
|
|
75
|
+
Local fallback, in `claude_desktop_config.json` (macOS:
|
|
76
|
+
`~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`), then
|
|
77
|
+
restart the app:
|
|
22
78
|
|
|
23
79
|
```json
|
|
24
80
|
{
|
|
@@ -32,11 +88,71 @@ claude mcp add planpost -e PLANPOST_API_KEY=pk_live_xxx -- npx -y planpost-mcp
|
|
|
32
88
|
}
|
|
33
89
|
```
|
|
34
90
|
|
|
35
|
-
**
|
|
91
|
+
**Cursor** (`~/.cursor/mcp.json`, or `.cursor/mcp.json` per project)
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"planpost": {
|
|
97
|
+
"url": "https://mcp.planpo.st/mcp",
|
|
98
|
+
"headers": { "Authorization": "Bearer pk_live_xxx" }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Local fallback: replace the entry with
|
|
105
|
+
`{ "command": "npx", "args": ["-y", "planpost-mcp"], "env": { "PLANPOST_API_KEY": "pk_live_xxx" } }`.
|
|
106
|
+
A project-level `.cursor/mcp.json` is committed with your repo, so prefer the
|
|
107
|
+
hosted form and keep the key out of the file.
|
|
108
|
+
|
|
109
|
+
**OpenAI Codex CLI** (`~/.codex/config.toml`)
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
[mcp_servers.planpost]
|
|
113
|
+
url = "https://mcp.planpo.st/mcp"
|
|
114
|
+
bearer_token_env_var = "PLANPOST_API_KEY"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Export `PLANPOST_API_KEY` in your shell; Codex reads the token from the
|
|
118
|
+
environment, not the file. `codex mcp add` does the same thing interactively.
|
|
119
|
+
|
|
120
|
+
Local fallback:
|
|
36
121
|
|
|
37
|
-
|
|
122
|
+
```toml
|
|
123
|
+
[mcp_servers.planpost]
|
|
124
|
+
command = "npx"
|
|
125
|
+
args = ["-y", "planpost-mcp"]
|
|
126
|
+
|
|
127
|
+
[mcp_servers.planpost.env]
|
|
128
|
+
PLANPOST_API_KEY = "pk_live_xxx"
|
|
129
|
+
```
|
|
38
130
|
|
|
39
|
-
|
|
131
|
+
**Gemini CLI** (`~/.gemini/settings.json`, or `.gemini/settings.json` per project)
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"planpost": {
|
|
137
|
+
"httpUrl": "https://mcp.planpo.st/mcp",
|
|
138
|
+
"headers": { "Authorization": "Bearer pk_live_xxx" },
|
|
139
|
+
"timeout": 10000
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Gemini CLI uses `httpUrl` for streamable HTTP, not `url`; the wrong key is a
|
|
146
|
+
silent no-op. Local fallback: the same `command`/`args`/`env` entry as Cursor.
|
|
147
|
+
|
|
148
|
+
**Any other MCP client:** connect to `https://mcp.planpo.st/mcp` (streamable
|
|
149
|
+
HTTP, `Authorization: Bearer pk_live_xxx` on every request), or run
|
|
150
|
+
`npx -y planpost-mcp` over stdio with `PLANPOST_API_KEY` set. Per-client
|
|
151
|
+
walkthroughs with screenshots: [planpo.st/docs/agents](https://planpo.st/docs/agents).
|
|
152
|
+
|
|
153
|
+
### Self-hosting the HTTP endpoint
|
|
154
|
+
|
|
155
|
+
`mcp.planpo.st` is this same package running with `--http`. To run your own:
|
|
40
156
|
|
|
41
157
|
```bash
|
|
42
158
|
npx -y planpost-mcp --http --port 3020 # exposes POST /mcp (+ /health)
|
|
@@ -48,16 +164,27 @@ Behind a reverse proxy, bind to loopback so the port isn't reachable directly:
|
|
|
48
164
|
HOST=127.0.0.1 npx -y planpost-mcp --http --port 3020
|
|
49
165
|
```
|
|
50
166
|
|
|
51
|
-
|
|
167
|
+
The transport is stateless (streamable HTTP); credentials are read from every
|
|
168
|
+
request and scoped to it, so one hosted instance serves many users safely. Put
|
|
169
|
+
it behind HTTPS in production. `--host` / `HOST` default to `0.0.0.0`, so
|
|
170
|
+
containers and LAN use work without extra flags.
|
|
52
171
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```
|
|
172
|
+
A self-hosted endpoint accepts API keys (`Authorization: Bearer pk_...`) out of
|
|
173
|
+
the box. OAuth sign-in is switched on by three variables, and needs a PlanPost
|
|
174
|
+
API that runs the authorization server (`/oauth/*`):
|
|
57
175
|
|
|
58
|
-
|
|
176
|
+
| Variable | Description |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `PLANPOST_MCP_PUBLIC_URL` | Public URL of this endpoint, e.g. `https://mcp.example.com/mcp`. Tokens are issued for exactly this URL. |
|
|
179
|
+
| `PLANPOST_MCP_SERVICE_SECRET` | 32+ random characters shared with the API (`MCP_SERVICE_SECRET` there). |
|
|
180
|
+
| `PLANPOST_OAUTH_ISSUER` | Authorization server origin. Defaults to `PLANPOST_API_URL`. |
|
|
181
|
+
| `OPENAI_APPS_CHALLENGE` | Optional. Token served at `/.well-known/openai-apps-challenge` for ChatGPT directory domain verification. |
|
|
59
182
|
|
|
60
|
-
|
|
183
|
+
With those set the server follows the MCP authorization spec: it serves
|
|
184
|
+
`/.well-known/oauth-protected-resource`, answers unauthenticated requests with
|
|
185
|
+
`401` and a `WWW-Authenticate: Bearer resource_metadata="…"` challenge,
|
|
186
|
+
validates access tokens by introspection, checks they were issued for this
|
|
187
|
+
endpoint, and never forwards them upstream.
|
|
61
188
|
|
|
62
189
|
### 3. Talk to it
|
|
63
190
|
|
|
@@ -73,11 +200,13 @@ The transport is stateless (streamable HTTP); the `Authorization: Bearer pk_...`
|
|
|
73
200
|
|
|
74
201
|
| Variable | Required | Description |
|
|
75
202
|
|---|---|---|
|
|
76
|
-
| `PLANPOST_API_KEY` |
|
|
203
|
+
| `PLANPOST_API_KEY` | stdio only | API key from Settings → API Keys (`pk_live_…`). Not needed with `--http`, where each request brings its own credentials. |
|
|
77
204
|
| `PLANPOST_API_URL` | — | API base URL (default `https://api.planpo.st`) |
|
|
78
205
|
|
|
79
206
|
## Tools
|
|
80
207
|
|
|
208
|
+
49 tools, grouped below. The permission column is the scope a tool needs, whether it comes from an API key or from OAuth sign-in; a credential without the scope gets a clear error rather than a silent failure. Every tool carries MCP annotations (a title plus read-only, destructive and open-world hints) so clients know which calls to confirm.
|
|
209
|
+
|
|
81
210
|
### Posts & publishing
|
|
82
211
|
|
|
83
212
|
| Tool | Required permission | Description |
|
|
@@ -88,6 +217,13 @@ The transport is stateless (streamable HTTP); the `Authorization: Bearer pk_...`
|
|
|
88
217
|
| `update_post` | `posts:write` | Update a draft/scheduled post |
|
|
89
218
|
| `delete_post` | `posts:write` | Delete a post |
|
|
90
219
|
| `publish_post` | `posts:write` | Publish immediately |
|
|
220
|
+
| `retry_failed` | `posts:write` | Re-queue the platforms that failed for a transient reason; never re-posts one that went out |
|
|
221
|
+
| `get_delivery_report` | `posts:read` | The morning check: recent failed or partly failed posts with each platform's error and fix, unhealthy connections, approvals running late |
|
|
222
|
+
| `preview_post` | `posts:read` | Show exactly what each platform will receive (text after overrides, attachments, warnings) |
|
|
223
|
+
| `bulk_create_posts` | `posts:write` | Create a whole campaign of posts from one manifest |
|
|
224
|
+
| `list_campaigns` | `posts:read` | Campaign tags in use, with post counts by status and platform |
|
|
225
|
+
| `delete_posts` | `posts:write` | Bulk-delete posts matching a filter (a filter is required) |
|
|
226
|
+
| `list_media` | `media:read` | Media already uploaded, so an asset can be reused instead of re-uploaded |
|
|
91
227
|
| `upload_media` | `media:write` | Get a presigned URL to upload an image/video |
|
|
92
228
|
| `repurpose_content` | `repurpose:write` | Save repurposed content as draft posts |
|
|
93
229
|
|
|
@@ -98,8 +234,31 @@ The transport is stateless (streamable HTTP); the `Authorization: Bearer pk_...`
|
|
|
98
234
|
| `list_connections` | `connections:read` | Connected social accounts |
|
|
99
235
|
| `list_account_groups` | `groups:read` | Named account groups (e.g. "Polish Pages") |
|
|
100
236
|
| `get_account_group` | `groups:read` | Resolve a group by id/name/default → connection IDs |
|
|
237
|
+
| `create_account_group` | `groups:write` | Create a named group bundling connections |
|
|
238
|
+
| `update_account_group` | `groups:write` | Rename, recolour, set default, or replace a group's connections |
|
|
239
|
+
| `delete_account_group` | `groups:write` | Delete a group (the accounts stay connected) |
|
|
240
|
+
| `add_connections_to_group` | `groups:write` | Add connections to a group |
|
|
241
|
+
| `remove_connections_from_group` | `groups:write` | Remove connections from a group |
|
|
242
|
+
| `get_connection_link` | `connections:read` | Link the user opens in a browser to connect a new social account (OAuth needs a human) |
|
|
243
|
+
|
|
244
|
+
Connecting a new social account requires OAuth in a browser: `get_connection_link` hands the user the link, and the agent can target the account once it is connected.
|
|
245
|
+
|
|
246
|
+
### Approvals
|
|
247
|
+
|
|
248
|
+
| Tool | Required permission | Description |
|
|
249
|
+
|---|---|---|
|
|
250
|
+
| `submit_for_approval` | `approvals:write` | Send a post to a human reviewer (required when the API key is approval-gated) |
|
|
251
|
+
| `list_pending_approvals` | `approvals:read` | Posts waiting for review, with outcomes |
|
|
252
|
+
| `get_approval_status` | `approvals:read` | Pending / approved / rejected / changes requested for one submission |
|
|
253
|
+
|
|
254
|
+
### Scheduling
|
|
101
255
|
|
|
102
|
-
|
|
256
|
+
| Tool | Required permission | Description |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `get_best_times` | `schedule:read` | Best posting times per platform from the account's engagement history |
|
|
259
|
+
| `suggest_schedule` | `schedule:read` | Spread N posts across a date range at optimal times; returns concrete timestamps |
|
|
260
|
+
| `get_posting_schedule` | `schedule:read` | Weekly queue slots, global plus per-group overrides |
|
|
261
|
+
| `set_posting_schedule` | `schedule:write` | Replace the weekly queue slots for the global schedule or one group (Pro) |
|
|
103
262
|
|
|
104
263
|
### Video & carousels
|
|
105
264
|
|
|
@@ -113,6 +272,7 @@ Connecting a new social account requires OAuth in the dashboard — agents can t
|
|
|
113
272
|
| `render_carousel` | `carousels:write` | Render slides into branded PNGs in the media library |
|
|
114
273
|
| `get_carousel_render` | `carousels:read` | Poll a render; completed renders return post-ready media assets |
|
|
115
274
|
| `list_brand_kits` | `brand-kits:read` | Saved brand kits (palette, fonts, logo, handle) for `render_carousel` |
|
|
275
|
+
| `generate_image` | `images:write` | Generate an image with AI for a post (1 AI credit); pass the URL to `create_post` |
|
|
116
276
|
|
|
117
277
|
### Content sources
|
|
118
278
|
|
|
@@ -133,6 +293,13 @@ Connecting a new social account requires OAuth in the dashboard — agents can t
|
|
|
133
293
|
|
|
134
294
|
Revenue data is read-only over the API; connecting Stripe/RevenueCat/Shopify happens in the dashboard (Settings → Revenue).
|
|
135
295
|
|
|
296
|
+
### Brand voice (Pro)
|
|
297
|
+
|
|
298
|
+
| Tool | Required permission | Description |
|
|
299
|
+
|---|---|---|
|
|
300
|
+
| `get_brand_voices` | `brand-voice:read` | Tone-of-voice profiles analysed from the user's content |
|
|
301
|
+
| `get_brand_voice_prompt` | `brand-voice:read` | Ready-to-use system prompt for a voice; apply it when drafting posts |
|
|
302
|
+
|
|
136
303
|
## A typical agent workflow
|
|
137
304
|
|
|
138
305
|
1. `get_account_group` (`default: true`) → connection IDs
|
|
@@ -152,10 +319,55 @@ Or, for a branded carousel post end-to-end:
|
|
|
152
319
|
```bash
|
|
153
320
|
npm install
|
|
154
321
|
npm run build # tsc → dist/
|
|
322
|
+
npm test # builds, validates server.json and the plugin manifests, boots stdio, exercises hosted OAuth mode
|
|
155
323
|
PLANPOST_API_KEY=pk_test_xxx node dist/index.js
|
|
156
324
|
```
|
|
157
325
|
|
|
158
|
-
The server speaks MCP over stdio
|
|
326
|
+
The server speaks MCP over stdio, or streamable HTTP with `--http`. It calls the PlanPost public API (`/api/public/v1`) with your credentials; nothing else is stored or sent anywhere. Changes are listed in [CHANGELOG.md](./CHANGELOG.md).
|
|
327
|
+
|
|
328
|
+
## Maintainers
|
|
329
|
+
|
|
330
|
+
Releasing a version means publishing twice: the code to npm, then the metadata
|
|
331
|
+
to the [official MCP Registry](https://registry.modelcontextprotocol.io), which
|
|
332
|
+
only stores `server.json` and checks that the npm package it points at exists
|
|
333
|
+
and carries the matching `mcpName`. Glama and other directories import from the
|
|
334
|
+
registry, so the second step is what makes a release discoverable.
|
|
335
|
+
|
|
336
|
+
1. Bump `version` in `package.json` **and** in `server.json` (top level and
|
|
337
|
+
`packages[0].version`), `gemini-extension.json`, `.claude-plugin/plugin.json`
|
|
338
|
+
and `.cursor-plugin/plugin.json`, and add a `CHANGELOG.md` entry. `npm test`
|
|
339
|
+
fails if any of them disagree.
|
|
340
|
+
2. Publish to npm first; the registry rejects a version npm does not have yet:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
npm publish --access public
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
3. Install the publisher CLI once (`brew install mcp-publisher`, or the tarball
|
|
347
|
+
from the registry's GitHub releases), then log in. The `io.github.lukBB/*`
|
|
348
|
+
namespace is proven by GitHub device-flow login as that account:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
mcp-publisher login github
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
4. Validate and publish from this directory:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
mcp-publisher validate
|
|
358
|
+
mcp-publisher publish
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
5. Confirm the listing:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.lukBB/planpost"
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
An expired token shows as "Invalid or expired Registry JWT token": run the login
|
|
368
|
+
again. `mcp-publisher status` deprecates or deletes a published version.
|
|
369
|
+
A tagged GitHub release is what the Gemini CLI extension gallery and the
|
|
370
|
+
plugin marketplaces pick up, so tag `vX.Y.Z` after publishing.
|
|
159
371
|
|
|
160
372
|
## License
|
|
161
373
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const TOOLS = {
|
|
2
|
+
list_posts: ["List posts", "read"],
|
|
3
|
+
get_post: ["Get a post", "read"],
|
|
4
|
+
// Can publish immediately, and a published post cannot be recalled.
|
|
5
|
+
create_post: ["Create, schedule or publish a post", "destroy", "world"],
|
|
6
|
+
preview_post: ["Preview a post per platform", "read"],
|
|
7
|
+
bulk_create_posts: ["Create several posts", "destroy", "world"],
|
|
8
|
+
list_campaigns: ["List campaigns", "read"],
|
|
9
|
+
delete_posts: ["Delete several posts", "destroy"],
|
|
10
|
+
list_media: ["List media library", "read"],
|
|
11
|
+
update_post: ["Edit a post", "destroy", "world"],
|
|
12
|
+
delete_post: ["Delete a post", "destroy"],
|
|
13
|
+
retry_failed: ["Retry failed platforms", "destroy", "world"],
|
|
14
|
+
get_delivery_report: ["Get delivery report", "read"],
|
|
15
|
+
publish_post: ["Publish a post now", "destroy", "world"],
|
|
16
|
+
list_connections: ["List connected social accounts", "read"],
|
|
17
|
+
list_account_groups: ["List account groups", "read"],
|
|
18
|
+
get_account_group: ["Get an account group", "read"],
|
|
19
|
+
create_account_group: ["Create an account group", "write"],
|
|
20
|
+
update_account_group: ["Edit an account group", "destroy"],
|
|
21
|
+
delete_account_group: ["Delete an account group", "destroy"],
|
|
22
|
+
add_connections_to_group: ["Add accounts to a group", "write"],
|
|
23
|
+
remove_connections_from_group: ["Remove accounts from a group", "destroy"],
|
|
24
|
+
get_connection_link: ["Get a link to connect an account", "read"],
|
|
25
|
+
submit_for_approval: ["Submit a post for approval", "write"],
|
|
26
|
+
list_pending_approvals: ["List pending approvals", "read"],
|
|
27
|
+
get_approval_status: ["Get approval status", "read"],
|
|
28
|
+
get_analytics: ["Get post analytics", "read"],
|
|
29
|
+
upload_media: ["Get a media upload URL", "write"],
|
|
30
|
+
repurpose_content: ["Repurpose content with AI", "write"],
|
|
31
|
+
list_video_templates: ["List video templates", "read"],
|
|
32
|
+
create_video: ["Render a video", "write"],
|
|
33
|
+
get_video_status: ["Get video render status", "read"],
|
|
34
|
+
generate_carousel: ["Draft carousel slides", "write"],
|
|
35
|
+
list_carousel_styles: ["List carousel styles", "read"],
|
|
36
|
+
render_carousel: ["Render a carousel", "write"],
|
|
37
|
+
get_carousel_render: ["Get carousel render status", "read"],
|
|
38
|
+
list_brand_kits: ["List brand kits", "read"],
|
|
39
|
+
list_content_sources: ["List content sources", "read"],
|
|
40
|
+
add_content_source: ["Add a content source", "write", "world"],
|
|
41
|
+
delete_content_source: ["Delete a content source", "destroy"],
|
|
42
|
+
fetch_source_content: ["Sync a content source", "write", "world"],
|
|
43
|
+
list_imported_content: ["List imported content", "read"],
|
|
44
|
+
get_revenue_overview: ["Get revenue overview", "read"],
|
|
45
|
+
generate_image: ["Generate an image", "write"],
|
|
46
|
+
get_brand_voices: ["List brand voices", "read"],
|
|
47
|
+
get_brand_voice_prompt: ["Get a brand voice prompt", "read"],
|
|
48
|
+
get_best_times: ["Get best times to post", "read"],
|
|
49
|
+
suggest_schedule: ["Suggest a schedule", "read"],
|
|
50
|
+
get_posting_schedule: ["Get posting schedule", "read"],
|
|
51
|
+
set_posting_schedule: ["Replace posting schedule", "destroy"],
|
|
52
|
+
};
|
|
53
|
+
export const ANNOTATED_TOOLS = Object.keys(TOOLS);
|
|
54
|
+
export function annotationsFor(name) {
|
|
55
|
+
const entry = TOOLS[name];
|
|
56
|
+
if (!entry)
|
|
57
|
+
throw new Error(`Tool "${name}" has no entry in annotations.ts`);
|
|
58
|
+
const [title, kind, world] = entry;
|
|
59
|
+
return {
|
|
60
|
+
title,
|
|
61
|
+
readOnlyHint: kind === "read",
|
|
62
|
+
destructiveHint: kind === "destroy",
|
|
63
|
+
idempotentHint: kind === "read",
|
|
64
|
+
openWorldHint: world === "world",
|
|
65
|
+
};
|
|
66
|
+
}
|