Client setup
Add your plym MCP endpoint to Claude Code, Cursor, VS Code, or Claude Desktop. The two credential headers, a full config for each client, and the curl pair that proves the endpoint works before you blame the client.
Updated Aug 04, 2026
On this page
Connect an AI client to your blog so it can draft posts and upload images without the admin UI. This page assumes the MCP server is already on — see Introduction if plym enable mcp has not been run.
Every client needs the same three things: the endpoint URL, a plym account email in X-User-Identity, and that account's password in X-Mcp-Token. Clients that can send custom HTTP headers connect directly. Clients that cannot need the mcp-remote bridge, covered below.
Before you start
Create a dedicated account for the client in Admin > Users and give it the editor role. An editor can create posts and upload media; it cannot change users, roles, or instance config, which an administrator account handed to a client could.
Add the server to Claude Code
- Add the endpoint with both headers:
claude mcp add --transport http plym https://blog.example.com/mcp \
--header "X-User-Identity: ada@example.com" \
--header "X-Mcp-Token: YOUR_PLYM_PASSWORD"
- Confirm what was stored and whether the connection works:
claude mcp get plym
plym:
Scope: Local config (private to you in this project)
Status: ✓ Connected
Type: http
URL: https://blog.example.com/mcp
Headers:
X-User-Identity: ada@example.com
X-Mcp-Token: YOUR_PLYM_PASSWORD
- Ask the client to list your posts. Six tools named
create_post,upload_media,list_posts,list_users,get_from_url, andmd_from_htmlare now available.
Use the command above rather than a checked-in file. The equivalent .mcp.json works and is shared with everyone who clones the repository, password included:
{
"mcpServers": {
"plym": {
"type": "http",
"url": "https://blog.example.com/mcp",
"headers": {
"X-User-Identity": "ada@example.com",
"X-Mcp-Token": "YOUR_PLYM_PASSWORD"
}
}
}
}
Cursor
Write ~/.cursor/mcp.json for every project, or .cursor/mcp.json for one. Cursor expands ${env:NAME} in header values, so export PLYM_MCP_TOKEN in your shell profile and keep the password out of the file:
{
"mcpServers": {
"plym": {
"url": "https://blog.example.com/mcp",
"headers": {
"X-User-Identity": "ada@example.com",
"X-Mcp-Token": "${env:PLYM_MCP_TOKEN}"
}
}
}
}
VS Code
Write .vscode/mcp.json. A promptString input with password: true makes VS Code ask for the password once and store it in the credential manager:
{
"inputs": [
{
"type": "promptString",
"id": "plym-password",
"description": "plym account password",
"password": true
}
],
"servers": {
"plym": {
"type": "http",
"url": "https://blog.example.com/mcp",
"headers": {
"X-User-Identity": "ada@example.com",
"X-Mcp-Token": "${input:plym-password}"
}
}
}
}
Claude Desktop and other stdio-only clients
Claude Desktop connects to remote servers by OAuth and has no field for a custom header, so it cannot authenticate against plym directly. Bridge it with mcp-remote, which speaks stdio to the client and HTTP to your endpoint:
{
"mcpServers": {
"plym": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://blog.example.com/mcp",
"--header",
"X-User-Identity:${PLYM_EMAIL}",
"--header",
"X-Mcp-Token:${PLYM_PASSWORD}"
],
"env": {
"PLYM_EMAIL": "ada@example.com",
"PLYM_PASSWORD": "YOUR_PLYM_PASSWORD"
}
}
}
}
Two rules for that config. ${NAME} substitutes a header value only, so keep the header name literal in the argument — "--header", "${WHOLE_HEADER}" reaches the server as nothing and every tool call returns Missing credentials. Add --allow-http to the argument list when the endpoint is http://, which it is when you point a client at http://localhost:9173/mcp for a local test.
Do not run python -m plym.mcp as a stdio server for these clients. It starts and lists tools, but stdio carries no HTTP headers, so no tool call can authenticate.
Test the endpoint without a client
The first call proves the endpoint is reachable and returns a session id. It does not check credentials, because initialize accepts requests without them:
curl -sD - -o /dev/null -X POST https://blog.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
HTTP/1.1 200 OK
mcp-session-id: 49887e27b2664b7f9d3a1c05e7b8a2f1
content-type: text/event-stream
This pair checks the credentials, by carrying that session id into a real tool call:
SESSION=$(curl -sD - -o /dev/null -X POST https://blog.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}' \
| grep -i '^mcp-session-id:' | tr -d '\r' | cut -d' ' -f2)
curl -s -X POST https://blog.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "mcp-session-id: $SESSION" \
-H 'X-User-Identity: ada@example.com' \
-H 'X-Mcp-Token: YOUR_PLYM_PASSWORD' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_posts","arguments":{}}}'
event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"[{\"id\":1,\"slug\":\"hello-world\",...}]"}],"isError":false}}
An isError of true with a message inside content means the endpoint is fine and the credentials or the role are not.