blog-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
blog-mcp
MCP server exposing three tools against the posts table on db.w3hc.org:
Tool | Description |
| List recent posts, optionally filtered by slug prefix |
| Fetch the most recent post whose slug matches a prefix |
| Insert or update a post by slug |
No raw SQL, no delete: the surface area is intentionally limited to what's safe to expose over the internet.
Architecture
Claude --HTTPS--> reverse proxy (TLS) --HTTP, localhost--> this server --> PostgresThe server binds to 127.0.0.1 only. It is never directly reachable from the
internet — whatever already terminates TLS on the VPS (nginx, Caddy, ...) is
the sole public entry point. Auth is a single bearer token, checked with a
constant-time comparison. Transport is MCP's stateless Streamable HTTP: every
request builds a fresh in-memory server, handles the call, and tears it down.
Related MCP server: WordPress MCP
Local development
pnpm install
cp .env.example .envFill in .env:
DATABASE_URL— already-issued credentials, just append?sslmode=verify-full. Do not addsslrootcert=system: that's a libpq-only value (works withpsql) and makes node-postgres try to read a literal file namedsystem. Plainsslmode=verify-fullgives correct full chain + hostname verification under Node, using its bundled CA store.MCP_BEARER_TOKEN— generate withopenssl rand -hex 32.
pnpm start/pnpm dev load .env via Node's built-in --env-file flag
(Node 20.6+), so no dotenv dependency is needed — but .env must exist in
the working directory or the process exits immediately with a missing
variable error.
Run it:
pnpm build && pnpm start
# or, with reload on change:
pnpm devRunning the tests
pnpm testRuns the full suite via Node's built-in test runner (node:test, through
tsx --test). No database connection or network access is required — pg's
pool.query is mocked in test/db.test.ts and test/tools.test.ts.
Testing with curl
TOKEN=... # from .env
# Handshake
curl -s -X POST http://127.0.0.1:3939/mcp \
-H "Authorization: Bearer $TOKEN" \
-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-test","version":"1.0"}}}'
# List tools
curl -s -X POST http://127.0.0.1:3939/mcp \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# Call a tool
curl -s -X POST http://127.0.0.1:3939/mcp \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"posts_list","arguments":{"limit":3}}}'Testing with MCP Inspector
npx @modelcontextprotocol/inspectorPoint it at http://127.0.0.1:3939/mcp, transport "Streamable HTTP", and set
the Authorization header to Bearer <your token> in its auth settings.
Deployment (Infomaniak Ubuntu VPS)
Ship the code. On the VPS:
sudo mkdir -p /opt/blog-mcp sudo useradd --system --home /opt/blog-mcp --shell /usr/sbin/nologin blog-mcpCopy the repo there (git clone, rsync, or CI), then:
cd /opt/blog-mcp pnpm install --prod=false # devDependencies needed for the build step pnpm build pnpm prune --prod # drop devDependencies after buildingWrite the real
.envat/opt/blog-mcp/.env(copy.env.example, fill in the realDATABASE_URLand a freshly generatedMCP_BEARER_TOKEN). Lock it down:sudo chown blog-mcp:blog-mcp /opt/blog-mcp/.env sudo chmod 600 /opt/blog-mcp/.env sudo chown -R blog-mcp:blog-mcp /opt/blog-mcpInstall the systemd unit:
sudo cp deploy/blog-mcp.service /etc/systemd/system/blog-mcp.service sudo systemctl daemon-reload sudo systemctl enable --now blog-mcp sudo systemctl status blog-mcpReverse proxy. Example nginx server block terminating TLS and forwarding to the local port (adjust the domain/cert paths to whatever already manages TLS on this VPS, e.g. certbot):
server { listen 443 ssl http2; server_name blog.mcp.w3hc.org; ssl_certificate /etc/letsencrypt/live/blog.mcp.w3hc.org/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/blog.mcp.w3hc.org/privkey.pem; location /mcp { proxy_pass http://127.0.0.1:3939/mcp; proxy_set_header Host $host; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_read_timeout 300s; } }Because
proxy_set_header Host $host;forwards the public hostname, setALLOWED_HOSTS=blog.mcp.w3hc.org(pluslocalhost,127.0.0.1,[::1]for local testing) in.env— otherwise the SDK's DNS-rebinding protection will 403 every proxied request. Restart the service after changing.env:sudo systemctl restart blog-mcpRegister the connector with your MCP client. The server is a standard MCP endpoint (Streamable HTTP, bearer auth) — it isn't tied to any one provider. See Connecting a client below for Claude, OpenAI, Mistral, and Qwen.
Connecting a client
This server speaks plain MCP over Streamable HTTP with a bearer token — no
Claude-specific behavior anywhere in src/. Any MCP-compatible client can
call it; the config shape just differs per provider.
Claude
Claude Desktop, Claude.ai, and Claude Code all support custom connectors:
add one with URL https://blog.mcp.w3hc.org/mcp and the bearer token from
.env.
OpenAI
The Responses API has a native mcp tool type:
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-5",
input="List the 5 most recent posts",
tools=[{
"type": "mcp",
"server_label": "blog-mcp",
"server_url": "https://blog.mcp.w3hc.org/mcp",
"headers": {"Authorization": "Bearer <MCP_BEARER_TOKEN>"},
"require_approval": "never",
}],
)Mistral
In Mistral Studio: Connectors → Add Connector → Custom MCP Connector,
then set the URL to https://blog.mcp.w3hc.org/mcp and add a static header
Authorization: Bearer <MCP_BEARER_TOKEN>. Mistral requires the token to
be entered in Studio itself — it doesn't support passing it
programmatically per request.
Qwen
Qwen Code supports remote MCP
servers over HTTP. In .qwen/settings.json:
{
"mcpServers": {
"blog-mcp": {
"httpUrl": "https://blog.mcp.w3hc.org/mcp",
"headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
}
}
}or via the CLI:
qwen mcp add --transport http blog-mcp https://blog.mcp.w3hc.org/mcp \
--header "Authorization: Bearer <MCP_BEARER_TOKEN>"DeepSeek
DeepSeek's API (chat completions and Responses API) has no native remote-MCP
tool as of this writing — its Responses API explicitly ignores the mcp
tool type. To use this server from DeepSeek, put an MCP-aware host in
between: an agent framework (e.g. LangChain's MCP adapter) or a coding CLI
that owns the MCP connection while delegating generation to
deepseek-chat/deepseek-reasoner as the backend model. There's no direct
provider-to-server config to hand you here — check back as DeepSeek's API
evolves.
Operations
Logs:
journalctl -u blog-mcp -f. Each tool call logs one line (tool=posts_list result=ok duration=12ms, orresult=error:<category>with aref=<id>for unexpected errors, matching the id the client sees). Rejected tokens log at most one401line per minute.Startup check: the server runs
select 1 from posts limit 1before listening and exits with code 1 if it fails, logging a one-line reason with the password redacted, e.g.Database check failed (auth): password authentication failed for user "website". A bad.envtherefore shows up as a failed unit rather than as silently failing tool calls.Health:
curl -s 127.0.0.1:3939/healthon the VPS returns200 {"db":"ok"}or503 {"db":"error","reason":"auth|unreachable|timeout"}. It needs no token, and nginx only forwards/mcp, so it isn't public. It goes through the sameALLOWED_HOSTScheck as/mcp, so keep127.0.0.1in that list (or pass-H "Host: blog.mcp.w3hc.org").Restart:
sudo systemctl restart blog-mcpToken rotation: generate a new one (
openssl rand -hex 32), update.env, restart, update the connector config in Claude. No fixed cadence is enforced; rotate every few months or after any suspected exposure.Resource footprint: idle ~40-80 MB resident memory, negligible CPU. Load is a handful of requests per day, millisecond-scale CPU cost each — far lighter than the nightly
pg_dumpall/restic backup jobs already running on this box.
Security notes
Never add a raw-SQL or delete tool. If a new use case needs one, write a new narrow, purpose-built tool instead of widening an existing one.
The
websitePostgres role is reused as-is — no elevated grants, samepg_hba/connection-limit restrictions as everything else using it.The process never binds to a public interface; only the reverse proxy is internet-facing.
License
GPL-3.0
Contact
Julien Béranger (GitHub)
Element: @julienbrg:matrix.org
Farcaster: julien-
Telegram: @julienbrg
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Create, edit, organize, publish, and configure JustBlogged blogs from MCP clients.
Read-only access to MojaLab.com blog content: search and read posts, pages and tags as Markdown.
Headless CMS. Search the docs and read, draft, publish, and schedule entries via OAuth.
Blog MCP server for AI agents. One signup call returns a blog, an API key and a live URL; then your agent publishes markdown posts and gets public URLs back. Your agent does the writing; you get a dashboard for stats.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceEnables interaction with WordPress sites through the REST API, supporting content management for posts, pages, users, plugins, and custom post types with Application Password authentication.2,530 npm2MIT
- AlicenseNot gradedqualityDmaintenanceEnables WordPress content management via the Model Context Protocol, allowing creation, update, search, and retrieval of posts, as well as fetching block type schemas for generating block-based content.48 npm10ISC

Postoria MCP Serverofficial
AlicenseAqualityCmaintenanceEnables MCP-compatible clients to create, schedule, queue, inspect, and delete Postoria posts through the Postoria Public API.1417 npm1MIT- AlicenseBqualityBmaintenanceEnables reading and managing WordPress sites via the WP REST API, including posts, pages, media, taxonomies, users, and arbitrary REST endpoints, with optional authenticated write operations.1714 npm1MIT