@socketly/mcp
v0.2.0
Published
Model Context Protocol server for Socketly — publish, subscribe and inspect realtime channels from an agent.
Downloads
19
Maintainers
Readme
@socketly/mcp
An MCP server for Socketly, the hosted WebSocket API. It gives an agent four things a developer does constantly while wiring up realtime — publish an event, watch a channel, read a presence roster, and read the docs — so the agent can verify its own work instead of writing code and hoping.
That last part is the point. An agent that publishes blind and reports success is how generated realtime code ends up quietly wrong. One that can subscribe and see the message come back cannot.
Install
No install step. Point your MCP client at it with npx.
Claude Code
claude mcp add socketly \
--env SOCKETLY_SECRET_KEY=sk_app_… \
--env SOCKETLY_PUBLIC_KEY=pk_app_… \
-- npx -y @socketly/mcpAnything reading mcp.json (Claude Desktop, Cursor, Windsurf, VS Code…)
{
"mcpServers": {
"socketly": {
"command": "npx",
"args": ["-y", "@socketly/mcp"],
"env": {
"SOCKETLY_SECRET_KEY": "sk_app_…",
"SOCKETLY_PUBLIC_KEY": "pk_app_…"
}
}
}
}Both keys are minted together when you create an app at app.socketly.co. The secret is shown exactly once.
Configuration
| Variable | Required | What it does |
|---|---|---|
| SOCKETLY_SECRET_KEY | yes | sk_app_…. Publishing and channel inspection. The app id is read out of it, so there is no second variable that can disagree. |
| SOCKETLY_PUBLIC_KEY | no | pk_app_…. Needed to watch channels. Without it, socketly_subscribe / receive / unsubscribe are not registered at all — a tool that is always going to fail is worse than one that is absent. |
| SOCKETLY_URL | no | Defaults to https://api.socketly.co. |
| SOCKETLY_DOCS_URL | no | Defaults to https://docs.socketly.co. |
The secret key can publish to any channel on the app and sign a presence membership claiming to be anybody. That is the right credential for your own app on your own machine. It is the wrong one to hand an agent acting on someone else's behalf — see Not yet.
Tools
| Tool | Reversible | What it does |
|---|---|---|
| socketly_publish | no | Publishes an event to one or more channels, as your server would. Real clients receive it. |
| socketly_channel_info | read-only | A channel's kind, and for presence- channels who is currently in it. |
| socketly_subscribe | read-only | Opens a connection and starts buffering. Returns a handle. |
| socketly_receive | read-only | Drains a handle. waitSeconds long-polls for the first message. |
| socketly_unsubscribe | yes | Closes the subscription and its connection. |
socketly_publish is annotated readOnlyHint: false so a client can gate it behind
confirmation. It is the only tool here that changes anything.
Resources
socketly://docs — the full documentation as markdown, fetched from
docs.socketly.co/llms-full.txt, which the docs site generates from the same constants its
own pages render.
That indirection is deliberate. A toolkit multiplies whatever is underneath it, so a copy of the code samples baked into this package would multiply a copy that drifts. Fetching means that when the pattern improves, every agent's output improves without anyone republishing this package.
Read it before writing Socketly code. It carries the thing most likely to be got wrong from memory: channels are named by their access rule, and the prefix is the permission.
How watching works, and why it is a buffer
MCP cannot push a message into a model's turn. The specification's only server-to-client
mechanism, subscriptions/listen, carries four notification types — tools, prompts and
resources list-changed, plus resources/updated — and none of them reaches the model.
So this server holds the socket and buffers what arrives; the agent drains it with a tool call. That is the spec's own Stateful Tools pattern, and it has consequences worth knowing:
- A subscription closes after 5 minutes without a
receive. An expired handle returns an error saying to resubscribe, rather than an empty result that reads exactly like a quiet channel. - The buffer holds 256 messages. Past that the oldest are dropped and
receivereports the count — a silently truncated buffer is a lie an agent would reason from. - An open subscription is a connection, and it counts against your app's concurrent
connection limit. Call
socketly_unsubscribewhen done.
Only public- channels can be watched. private- and presence- need a signature from
the app owner's backend, which is the whole point of them.
Not yet
Creating apps and rolling keys. Those are control-plane operations behind a session cookie, and exposing them needs a personal access token the API does not have. They are also a dashboard click and they are rare; this package covers the frequent half.
An agent as a channel member. Subscribing to presence-support-4821 and replying as a
participant needs a credential scoped to a channel and to one identity — the secret key
grants far too much. That is a new key type in the API, not a feature of this package.
Links
MIT
