postlayer-connect
README.md
# postlayer-connect
**Let your AI post to your socials β an open MCP server for LinkedIn, X, Bluesky, Mastodon, Reddit, and more.**
`postlayer-connect` is a local [Model Context Protocol](https://modelcontextprotocol.io) server. Point any MCP client at it β Claude Desktop, Claude Code, Cursor, ChatGPT desktop β connect your social accounts with **your own** app credentials, and your assistant can publish posts and read analytics for you.
- Runs 100% locally. Your tokens never leave your machine.
- Uses **your** developer apps and **your** accounts. No middleman API, no per-post fees.
- Tokens are encrypted at rest (AES-256-GCM) in a local file.
- MIT licensed. Fork it, audit it, extend it.
```
you: "post this to LinkedIn and X: shipping day. the open MCP social server is live π"
AI: β
posted to linkedin (id 7β¦) β
posted to x (id 18β¦)
```
---
## 30-second quickstart
**1. Add it to your MCP client.** No install step β `npx` fetches and runs it.
Claude Desktop / Claude Code β add to your `claude_desktop_config.json` (or `.mcp.json`):
```json
{
"mcpServers": {
"postlayer-connect": {
"command": "npx",
"args": ["-y", "postlayer-connect"],
"env": {
"LINKEDIN_CLIENT_ID": "your_id",
"LINKEDIN_CLIENT_SECRET": "your_secret"
}
}
}
}
```
Only add the credentials for the platforms you want. Bluesky and Nostr need **none** β you connect them with an app password / key. The server boots fine with an empty `env` and tells you what each platform needs.
**2. Restart your client and ask it to connect an account:**
> "List the social platforms postlayer supports."
> "Connect my Bluesky β my handle is `you.bsky.social` and here's an app password."
**3. Post:**
> "Post 'hello world from my AI' to Bluesky."
That's it.
---
## Supported platforms
**Works now** β register your own app (a 5-minute, free, self-serve process), paste the keys, done. Bluesky and Nostr need no app at all.
| Platform | Connect with | What you register | Post analytics you get back |
|---|---|---|---|
| **LinkedIn** | OAuth | [LinkedIn app](https://www.linkedin.com/developers/apps) | likes, comments |
| **X / Twitter** | OAuth 1.0a | [X developer app](https://developer.x.com/en/portal/dashboard) (Read+Write) | impressions, likes, reposts, quotes, bookmarks, clicks |
| **Reddit** | OAuth | [Reddit app](https://www.reddit.com/prefs/apps) (type: web app) | score, comments, crossposts |
| **Mastodon** | OAuth | app on your instance (Preferences β Development) | favourites, boosts, replies |
| **Bluesky** | App password | *nothing* β Settings β App Passwords | likes, reposts, replies, quotes |
| **Nostr** | Private key (nsec) | *nothing* | β (no canonical analytics) |
| **Discord** | Bot token | [Discord app](https://discord.com/developers/applications) β Bot | reaction count |
| **Telegram** | Bot token | @BotFather | β (Bot API exposes none) |
| **Threads** | OAuth | [Meta / Threads app](https://developers.facebook.com) | views, likes, replies, reposts, quotes |
> Threads works for you + testers in dev mode. Scaling it to *other* people's accounts needs Meta App Review.
**Needs your own approved app** β the connector code is complete and correct, but these platforms only allow programmatic posting through a developer app that has passed their review. Get your app approved, flip one env flag, and it works.
| Platform | Blocker | Flip when approved |
|---|---|---|
| **Instagram** | Meta App Review + Business Verification | `INSTAGRAM_APP_APPROVED=true` |
| **TikTok** | Content Posting API audit | `TIKTOK_APP_APPROVED=true` |
| **YouTube** | Google OAuth verification (sensitive scopes) + quota | `YOUTUBE_APP_APPROVED=true` |
Until then these tools return an honest, specific "here's exactly what to submit" message β never a fake success.
---
## The MCP tools
| Tool | What it does |
|---|---|
| `list_platforms` | Every platform, its status, the credentials it needs, and the analytics it returns. |
| `connect_account` | Start a connect. OAuth platforms return a URL to open in your browser; credential platforms (Bluesky, Nostr, Discord, Telegram) take the fields inline. |
| `list_accounts` | Show your connected accounts and their ids. |
| `post` | Publish text + optional media (local file path or URL) to an account. Per-platform `settings` (e.g. Reddit `{subreddit, title}`). |
| `get_analytics` | Normalized analytics for one post. A metric that's missing means the platform doesn't expose it β never a faked zero. |
### Example prompts
- *"Post this to LinkedIn and X, and attach `~/Desktop/launch.png`."*
- *"Submit this as a text post to r/SideProject with the title 'I built an open MCP social server'."*
- *"How did my last LinkedIn post do?"* (feed it the post id `post` returned)
- *"Connect my Mastodon on mastodon.social."*
---
## How the OAuth connect works (local)
Because this server runs locally with no public URL, it starts a tiny loopback listener to catch the OAuth redirect.
1. Register **`http://localhost:8790/callback`** as an authorized redirect URL in each OAuth app you create.
2. Call `connect_account` β it returns an authorize URL.
3. Open it, approve, and the browser redirects back to the local listener, which exchanges the code and saves the account automatically.
Change the port with `POSTLAYER_OAUTH_PORT` (and `POSTLAYER_OAUTH_HOST`) if 8790 is taken β just register the matching URL.
---
## Configuration
Set these in your MCP client's `env` block, or in a `.env` file in the working directory (auto-loaded). See [`.env.example`](./.env.example) for the full list.
| Var | Purpose |
|---|---|
| `POSTLAYER_ENC_KEY` | Passphrase used to encrypt stored tokens (AES-256-GCM). **Recommended.** Generate with `openssl rand -base64 32`. If unset, a random key is generated once and saved (0600) in the data dir. |
| `POSTLAYER_DATA_DIR` | Where tokens + the key live. Default `~/.postlayer-connect`. |
| `POSTLAYER_OAUTH_PORT` / `POSTLAYER_OAUTH_HOST` | Local OAuth redirect listener. Default `localhost:8790`. |
| Per-platform app keys | e.g. `LINKEDIN_CLIENT_ID`, `X_API_KEY`, `REDDIT_CLIENT_ID`, β¦ (see `.env.example`). |
### Where your tokens are stored
Connected-account tokens are written to `~/.postlayer-connect/tokens.json`. The secret fields (access + refresh tokens, app passwords, signing keys) are **encrypted at rest**; the file is created `0600`. Nothing is ever sent anywhere except directly to the platform's own API.
---
## Run it yourself / develop
```bash
git clone https://github.com/<you>/postlayer-connect
cd postlayer-connect
npm install
npm run build
node dist/index.js # boots on stdio, prints its status to stderr
```
Node 20+. The server speaks MCP over stdio, so run it through an MCP client rather than typing at it directly.
---
## Want it fully managed + learning what works for you?
`postlayer-connect` is the open, run-it-yourself core. If you'd rather not manage apps and tokens β and want a system that **schedules, approves, and learns which posts actually perform** β that's [**Postlayer**](https://postlayer.io).
---
## License
MIT Β© 2026 Chris Poka / Designpulse. See [LICENSE](./LICENSE).
TDQS
A4.3/5.0
Scored across 5 tools
Disambiguation5/5
Each tool has a clear, distinct purpose: account connection, account listing, platform listing, posting, and analytics. No overlap or ambiguity between them.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern in snake_case (e.g., connect_account, list_accounts), with 'post' being a short but imperative verb, maintaining stylistic consistency.
Tool Count5/5
With 5 tools covering the core workflows of account connection, posting, and analytics, the count is well-scoped for the server's purposeβneither too few nor too many.
Completeness3/5
The tool surface covers essential operations but lacks tools for disconnecting accounts, deleting or editing posts, and listing published posts, which are notable gaps for a social media management server.
Maintenance
ActivityStale
ResponsivenessNo issues