velog-mcp
Provides tools for managing Velog blog posts, including reading, drafting, publishing, unpublishing, and exporting posts, as well as searching and aggregating blog statistics.
Click on "Install 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., "@velog-mcpShow me my recent drafts"
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.
velog-mcp
An MCP server for Velog, the Korean developer blogging platform. Read your blog, draft posts, publish them, and back everything up — from Claude or any MCP client.
Why another one?
Two Velog MCP servers already exist. This one differs in three ways.
1. Publishing is a permission, not a default. Out of the box the server can create drafts and publish privately. Public publishing requires you to set an environment variable. The model cannot flip that switch — only you can, in your MCP config.
2. Every quirk is measured, not assumed. Velog's GraphQL API is undocumented. This repo records what it actually does, verified against velog-io/velog source and live calls. Six server-side quirks are written up in docs/api-reference.md — including one that silently returns an empty list, and one that can turn your published posts private.
3. Two runtime dependencies. @modelcontextprotocol/sdk and zod. HTTP, test
runner, and TypeScript execution all come from Node 24 itself.
Related MCP server: velog-mcp
Install
Requires Node.js 24 or newer.
git clone https://github.com/milcho0604/velog-mcp.git
cd velog-mcp
npm install && npm run buildConfigure
Add this to your MCP client config (claude_desktop_config.json, .mcp.json, …):
{
"mcpServers": {
"velog": {
"command": "node",
"args": ["/absolute/path/to/velog-mcp/dist/index.js"],
"env": {
"VELOG_REFRESH_TOKEN": "your_refresh_token"
}
}
}
}With the Claude Code CLI:
claude mcp add velog -- node /absolute/path/to/velog-mcp/dist/index.js
# then add the "env" block to the entry it createdGetting your token
Velog has no public write API, so the server authenticates with your browser session cookie.
Log in at velog.io
Open DevTools (
F12) → Application → Cookies →https://velog.ioCopy the value of
refresh_token
VELOG_REFRESH_TOKEN alone is enough. Velog's server reissues the short-lived
access_token on its own (authPlugin.mts),
and this server picks the refreshed cookie out of the response. One paste lasts
30 days.
VELOG_ACCESS_TOKEN also works but expires in about an hour by itself.
Tokens are read from the environment only. They are never written to disk, and the server never reads your browser's cookie database or your OS keychain. Whatever you put in your MCP config file does live there in plain text, though — that file is yours to protect.
Without a token the server still starts, read-only. Public posts, search, trending, and blog stats all work unauthenticated.
Permissions
Environment | What you get |
(nothing set) | Read everything · create drafts · publish privately · draw and upload images — 21 tools |
| …plus public publishing (adds an |
| …plus profile editing (adds 5 tools) |
The two switches are independent — enable either, both, or neither.
"env": {
"VELOG_REFRESH_TOKEN": "...",
"VELOG_ALLOW_PUBLIC": "1",
"VELOG_ALLOW_PROFILE": "1"
}Accepted as "on": 1, true, yes, on. Anything else is off — a typo won't quietly
enable it.
When public publishing is off, the is_private parameter does not exist on any
tool, so the model has no way to ask for it. When it's on, is_private appears and
still defaults to true.
Why private-by-default
Not caution for its own sake. Velog's rate limiter counts only is_private: false
posts:
// apps/server/src/services/PostApiService/index.mts
count({ where: { fk_user_id, is_private: false, released_at: { gt: fiveMinutesAgo } } })
if (count >= 10) {
updateMany({ where: { fk_user_id, released_at: { gt: fiveMinutesAgo } },
data: { is_private: true } }) // flips *everything* recent to private
}Private posts don't increment that count. But isPostLimitReached() runs
unconditionally, before privacy is examined — so if ten public posts already exist in
the last five minutes, even a private draft request can trigger the sweep. "Doesn't
increment" is not "can't trigger." That's why write retries stay disabled and the local
limiter stays in place.
Public posts do increment it, and once a post is public it has already gone out through RSS, search indexes, and subscriber email, none of which a delete reaches. That asymmetry is what deserves an explicit opt-in.
Full reasoning: docs/security.md
Tools
21 tools. Only 9 of them change anything on Velog.
Reading — no auth required
Tool | Purpose |
| Read one post, body included |
| A user's posts, optionally filtered by tag |
| Keyword search; pass |
| Trending by |
| Newest posts across Velog |
| Profile, follower counts, bio |
| A user's series, with post counts and IDs |
| Tags a user writes about, with counts |
Reading — auth required
Tool | Purpose |
| Which account the token belongs to (also a token health check) |
| Your saved drafts, with IDs |
Derived — things Velog doesn't provide
Tool | Purpose |
| Aggregate views/likes/comments, top posts, per-year and per-tag breakdown |
| Save posts as Markdown files with YAML front matter |
Writing
Tool | Effect |
| Save a draft. Never publishes, under any configuration |
| Replace a draft entirely — omitted fields are reset |
| Publish a new post |
| Publish an existing draft, reusing its stored body |
| Send a published post back to drafts |
| Edit a published post — omitted fields are kept |
velog_update_draftresets what you omit;velog_update_postpreserves it. The asymmetry is deliberate — see docs/tools.md.
Tools that take a username — velog_list_drafts, velog_blog_stats,
velog_export_posts, velog_search_posts — fall back to your own account when you
omit it.
Diagrams and images
Tool | Effect |
| Draw an architecture/flow diagram and upload it |
| Draw a 1200×630 cover card for a post |
| Upload a local image file, get the Markdown back |
You describe what exists and what flows where; the renderer owns everything else — palette, spacing, text measurement, corner rounding, canvas size. That is deliberate: a diagram redrawn from scratch each time looks different each time.
Every measurement is real. Node widths and line breaks come from the browser's
getBBox(), never from a character count — with mixed Korean and English text, counting
characters is wrong every time. The canvas is sized after drawing, from the content's
bounding box, so a diagram cannot be clipped.
Then it audits itself and reports five classes of defect:
text spilling outside its card · letter-spacing squeezed to fit
a line crossing (or hiding behind) a node
two lines overlapping · two nodes overlapping · a label sitting on a cardIf the audit finds anything, nothing is uploaded — and there is no flag to turn that
off. Velog has no delete-image API and every upload counts against your quota, so a
flawed diagram is worth redrawing rather than shipping. An override that the model can
set itself is not a safeguard (same reasoning as the publishing switch in
ADR 0004). If you really want a flawed
diagram online, render with upload: false, look at the PNG, then pass its path to
velog_upload_image.
Icons are 28 built-in glyphs (server, database, cloud, clock, alert, …) drawn
from primitive shapes. Nothing is fetched — the renderer runs with DNS disabled.
Requires Chrome (or any Chromium-based browser: Edge, Brave, Chromium). It is found
automatically on macOS/Linux/Windows; set VELOG_CHROME_PATH if yours lives elsewhere.
Only these three tools need it — the other 18 work without a browser.
Cost, measured: one diagram is ~1 GB peak across 9–11 Chrome processes for 3–4 seconds, then back to zero. That's Chrome's floor, not our content. Renders are serialized — MCP clients call tools in parallel, and without that a five-diagram request would mean 45 Chrome processes and 6 GB. Serialized, four concurrent requests still peak at one render's worth. Ten renders in a row show no accumulation.
Profile editing — VELOG_ALLOW_PROFILE=1
Five more tools appear: velog_update_profile (display name, bio),
velog_update_about, velog_update_blog_title, velog_update_social_links,
velog_update_profile_image. Without the flag they aren't registered at all.
The gate isn't about danger — these are reversible, affect only your own account, and
aren't distributed anywhere. It's about confusion: a profile's short_bio and a
post's short_description sound alike. "Fix my description" is ambiguous, and with the
switch off a wrong guess can't reach your profile.
velog_update_profile keeps what you omit. Velog's UpdateProfileInput requires
both display_name and short_bio, so sending one alone would blank the other — the
tool reads your current values and fills them in.
Usage
Once it's configured, just talk to your MCP client.
"Draft a Velog post about the bug I fixed today"
→ writes Markdown, saves it as a draft, hands back the edit URL
"What did I write about HTTP/2 last year?"
→ searches inside your own posts
"Show my top 10 posts by views, and which tags get read most"
→ walks your whole blog and aggregates
"Back up all my posts to ~/blog-backup"
→ writes .md files with front matter
"Publish that draft"
→ private by default; public only with VELOG_ALLOW_PUBLIC=1
"Draw how the request flows from the LB through the workers to Redis"
→ renders a diagram, audits it, uploads it, hands back the Markdown line
"Make a cover image for this post"
→ 1200×630 card; pass the URL to velog_update_post's thumbnailYour MCP client asks for approval before each tool call, and irreversible tools carry
destructiveHint, so nothing gets published without you seeing it first.
Exported file format
---
title: "Post title"
date: 2022-12-31T18:32:39.790Z
slug: "url-slug"
url: "https://velog.io/@username/url-slug"
tags: ["tag1", "tag2"]
likes: 260
views: 16323
---
Post body in Markdown…Development
npm test # node:test, runs .ts directly — no jest, no ts-node
npm run typecheck # includes tests — they used to be excluded, which hid real errors
npm run lint # typescript-eslint, type-aware
npm run build # tsconfig.build.json (tests excluded from dist)
npm run schema:dump # dump Velog's current GraphQL schema161 tests. The ones in src/__tests__/safety.test.ts pin the security invariants —
if that file fails, find out why instead of working around it.
Documentation
Document | Contents |
Goals, non-goals, success criteria | |
Layering, and the TypeScript subset Node's type stripping allows | |
Measured Velog GraphQL schema and server quirks | |
Token handling, capability model, what's deliberately unimplemented | |
Full tool catalog with gotchas | |
Architecture decision records |
Notes
This talks to Velog's internal GraphQL API, which is undocumented and can change
without warning. When something breaks, run npm run schema:dump and diff it against
docs/api-reference.md — that's the fastest way to find what moved.
Velog's terms of service contain no clause restricting automated access. Using your own token to manage your own posts stays within scope, and your posts remain yours (Article 5).
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityDmaintenanceA custom MCP server for interacting with Google Blogger blogs. It provides tools to list, create, edit, delete, and publish blog posts through Claude Code or Claude Desktop.Last updated9
- Alicense-qualityBmaintenanceMCP server for Velog blog platform enabling reading, searching, and writing articles via AI assistants.Last updated32MIT
- Flicense-qualityDmaintenanceAn MCP server that automatically generates technical blog posts using AI (Gemini and Claude), supporting various input types, styles, and collaborative workflow.Last updated
- Alicense-qualityAmaintenanceMCP server for Hashnode GraphQL API to create drafts, publish posts, and manage your blog via Claude.Last updated29MIT
Related MCP Connectors
Markdown-first MCP server for Notion API with 8 composite tools and 39 actions.
A MCP server built for developers enabling Git based project management with project and personal…
Augments MCP Server - A comprehensive framework documentation provider for Claude Code
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/milcho0604/velog-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server