pubky-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pubky-mcppost "gm" from my linked account"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
pubky-mcp
An MCP server that lets an AI assistant read and publish pubky.app posts, from several Pubky accounts, signed in with Pubky Ring.
Remote MCP server (Streamable HTTP) with its own OAuth 2.1 authorization server: MCP clients such as Claude Code discover it, register themselves and sign in without any manual setup.
The only login step is a Pubky Ring QR code. The approving key becomes the operator; its posting session is stored, encrypted, on the server.
More accounts are linked with
link_account: a page, opened through MCP URL elicitation, shows a QR code for the account to add. Each linked account was approved by its own key in Pubky Ring.Posts are written to each account's homeserver in the pubky.app format and appear on pubky.app once Nexus has indexed them.
Tools
Tool | What it does |
| Accounts this connection can post as, with their Nexus name |
| Link another account (URL elicitation, or a link in the result) |
| End an account's stored session and forget it |
| Public timeline, or what an account follows (Nexus) |
| Latest posts of one user (Nexus) |
| One post and its replies (Nexus) |
| Publish a short post (≤ 2000 characters) or a reply, from one or several accounts |
| Repost a post from one or several accounts, plain or with a comment (quote) |
| Delete a post written by a linked account |
create_post and repost take the accounts explicitly; there is no default
account. A repost is written as pubky.app writes one: {"content": "", "embed": {"kind": "short", "uri": "<original>"}, "kind": "short"}, with the
comment in content for a quote; the original is read from its author's
homeserver, so a post just published can be reposted at once.
Related MCP server: mastodon-mcp-server
How it works
MCP client ──(Bearer JWT, aud = /mcp)──> /mcp MCP tools
│ │
└─ OAuth: /.well-known/*, /reg, /auth, /token oidc-provider
│
/interaction/:uid ── Pubky Ring QR (capabilities /pub/pubky.app/posts/:rw)
/connect/:id ── link another accountAuthorization server: oidc-provider with dynamic client registration, Client ID Metadata Documents, PKCE required, and JWT access tokens bound to the MCP URL (RFC 8707). Refresh tokens for clients registered with the
refresh_tokengrant.Sessions: the Pubky Ring flow is the legacy cookie flow, the one Pubky Ring reads today. The session secret is stored AES-256-GCM encrypted with
SESSION_ENCRYPTION_KEY, keyed by operator and account.Linking: the
/connectpage must be completed by the operator who asked (MCP spec, URL elicitation, phishing). The browser that signed in is recognised by its session cookie; any other browser must first confirm the operator's key with Pubky Ring (no capabilities requested).Storage: one SQLite file (
node:sqlite, built into Node) for the OAuth records and the accounts.
Run
Node 24 (TypeScript runs directly, type stripping).
pnpm install
cp .env.example .env # set SESSION_ENCRYPTION_KEY and PUBLIC_URL
node --env-file=.env src/server.tsDocker: the image ghcr.io/pastagringo/pubky-mcp is published on every push
to main (tags latest and the commit SHA), or docker build -t pubky-mcp .;
run it with the same variables and a
volume on /data. Put it behind a TLS reverse proxy; PUBLIC_URL is the
public HTTPS address.
Add it to Claude Code:
claude mcp add --transport http pubky https://mcp.example.com/mcpTest
test/e2e.ts plays every part with real protocol messages: the MCP client
(discovery, registration, PKCE, refresh), the browser (cookies, redirects) and
Pubky Ring (throwaway identities that sign up and approve). It checks the
phishing protection, URL elicitation and its completion notice, the stored
format of a post (exactly {content, kind}), replies, reposts and quotes,
refusals, deletion (48 checks).
Against a local Pubky testnet (synonymsoft/homeserver-testnet, which needs a
PostgreSQL on its localhost:5432 with a root role):
PUBKY_TESTNET=localhost node --env-file=.env src/server.ts
E2E_HOMESERVER=8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo E2E_TESTNET=localhost node test/e2e.tsOn the public network, with sign-up codes for the homeserver, E2E_NEXUS=1
also waits for Nexus to index the posts:
E2E_HOMESERVER=<key> E2E_INVITES=<code1>,<code2>,<code3> E2E_NEXUS=1 node test/e2e.tstest/probe-post.ts checks that a stored session still publishes after a
restart. test/probe-foreign.ts checks ALLOWED_HOMESERVERS: a key signed up
on another homeserver (FOREIGN_HOMESERVER, FOREIGN_INVITE) approves the
sign-in and must be refused (a key hosted nowhere fails earlier and proves
nothing).
Known pitfalls
Avast breaks MCP over HTTP on the machine it protects. Its Web Shield rewrites responses on paths ending in
/mcpor/sse, and MCP SSE streams:transfer-encoding: chunkedwith an unframed body, so Node clients fail withInvalid character in chunk size. Measured on loopback (127.0.0.1, ::1 and the LAN address); reported by others for HTTPS too, and confirmed by Avast staff (fix announced, no version given). On such a machine: add the service's address as an Avast exception, or turn HTTPS scanning off in Web Shield, and run the tests inside Docker. The server stays standard on purpose; a measured server-side workaround (always answer chunked: Avast leaves chunked answers alone) is in commit fbdbd0c, reverted in 5a8a9ff.@synonymdev/pubkyreplacesglobalThis.fetchwithfetch-cookie(one cookie jar per process) when it is loaded. A test that simulates several browsers in one process must keep the originalfetch.pubky-app-specstoJson()writes absent fields asnull. Both forms exist on pubky.app (posts with and without thenullfields, all indexed by Nexus); the service writes the shorter one.The capability
/pub/pubky.app/posts/:rwis enough to post and delete; pubky.app paths are planned to move (/pub/pubky.app/→/pub/social/v1/).
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server that lets your AI assistant build and deploy apps, hosting each one at a live URL.
- PostHeroOAuthai.posthero
PostHero MCP server — schedule and publish social media posts from any MCP-compatible AI tool
Driflyte MCP server which lets AI assistants query topic-specific knowledge from web and GitHub.
- ZapierOAuthcom.zapier
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
Related MCP Servers
- AlicenseAqualityFmaintenanceAn MCP server that enables AI assistants to access and interact with Reddit content through features like user analysis, post retrieval, subreddit statistics, and authenticated posting capabilities.15302MIT
- AlicenseCqualityAmaintenanceComprehensive MCP server for Mastodon integration, enabling AI assistants to read timelines, post statuses, manage accounts, search, and more on any Mastodon instance.543MIT

Publora MCP Serverofficial
AlicenseBqualityCmaintenanceOfficial MCP server for Publora that enables AI assistants to schedule posts, manage accounts, and retrieve analytics across multiple social media platforms through natural language.186 npm6MIT- AlicenseAqualityDmaintenanceA local MCP server that enables AI assistants to post to social media platforms (LinkedIn, X, Bluesky, Mastodon, Reddit, etc.) using your own app credentials, with encrypted token storage and no middleman.5MIT