
Blogger Posting
from beanspapa
Automate blog posting on Google Blogger using the Blogger API.
MCP Blogger Posting Server (MCP λΈλ‘κ·Έ ν¬μ€ν μλ²)
Google Blogger APIμ MCP(Model Context Protocol) Toolμ κ²°ν©ν μ€μ ν λΈλ‘κ·Έ ν¬μ€ν μλν μλ²
π μ£Όμ μ λ°μ΄νΈ (2025-06-14)
- server/info μλν¬μΈνΈ μ§μ: MCP Inspector/ν΄λΌμ΄μΈνΈμμ
server/infoμμ²μΌλ‘ μλ² μ΄λ¦/λ²μ μ 보λ₯Ό μ§μ μ‘°νν μ μμ΅λλ€. - λ‘κΉ μ μ± κ°ν: MCP μλ²μ λͺ¨λ λ‘κ·Έλ λ°λμ stderr(console.error) λλ MCP νλ‘ν μ½ μ μ© λ‘κΉ ν¨μλ§ μ¬μ©ν΄μΌ νλ©°, stdout μ€μΌ μ νλ‘ν μ½ νμ± μλ¬κ° λ°μν μ μμ΅λλ€.
- μ€μκ° JSON μ‘μ μΆμ :
[DEBUG][STDIO_SEND] ...ννμ stderr λ‘κ·Έλ‘ μ€μ μ‘μ λλ JSON λ©μμ§λ₯Ό μ€μκ°μΌλ‘ μΆμ ν μ μμ΅λλ€.
β¨ μ£Όμ κΈ°λ₯
- Google Blogger API μ°λ: MCP Toolλ‘ λΈλ‘κ·Έ ν¬μ€ν /λ°°μΉν¬μ€ν μλν
- MCP Tool νμ€ μ§μ: LLM/MCP ν΄λΌμ΄μΈνΈμμ blog-post, blog-batch-post Tool νΈμΆ κ°λ₯
- νκ²½λ³μ κΈ°λ° λ³΄μ/μ€μ : .env, client_secret νμΌ, μΈμ¦ ν ν° μμ κ΄λ¦¬
- ν μ€νΈ/μ΄μ/νμ₯μ±: μ λ/ν΅ν© ν μ€νΈ, νκ²½λ³ μ€μ , 보μ κ°μ΄λ μ 곡
- κ²½λνλ ꡬ쑰: λΆνμν 리μμ€/ν둬ννΈ/μμ μ½λ μ κ±°, Tool μ€μ¬μ μμ MCP μλ²
- νλΌλ―Έν° κΈ°λ° config: credentialPath, blogUrl λ± νκ²½λ³μ/νλΌλ―Έν°λ‘ μ μ°νκ² μ€μ
- μλ² μ 보 μλν¬μΈνΈ(server/info) μ 곡: MCP Inspector/ν΄λΌμ΄μΈνΈμμ
server/infoμμ²μΌλ‘ μλ² μ΄λ¦/λ²μ μ 보λ₯Ό μ§μ μ‘°ν κ°λ₯
π λΉ λ₯Έ μμ
1. νμ νμΌ μ€λΉ
.envνμΌ(νκ²½λ³μ)- Google OAuth2 client_secret JSON νμΌ(κ²½λ‘λ νκ²½λ³μλ‘ μ§μ )
.gitignoreμ.env,client_secret*.jsonλ°λμ ν¬ν¨
2. νκ²½λ³μ μμ(.env)
GOOGLE_CLIENT_SECRET_PATH=C:/dev/mcp-servers/mcp-blogspot-posting/client_secret_...json
SESSION_SECRET=your-session-secret
BLOG_ID=your-blog-id (μ ν)
PORT=30003. μμ‘΄μ± μ€μΉ λ° λΉλ
npm install
npm run build4. μλ² μ€ν
npm run dev5. MCP Tool μ¬μ© μμ
- MCP Inspector/ν΄λΌμ΄μΈνΈμμ blog-post, blog-batch-post Tool νμΈ λ° νΈμΆ
- μ λ ₯ μμ:
{
"title": "ν
μ€νΈ ν¬μ€νΈ",
"content": "<h1>λ΄μ©</h1>",
"labels": ["ν
μ€νΈ", "μλν"],
"isDraft": false
}- μΈμ¦ ν ν°μ΄ μμΌλ©΄ μλ¬, μΈμ¦ ν μ μ ν¬μ€ν
π μ£Όμ ꡬ쑰
mcp-blogspot-posting/
βββ src/
β βββ managers/ # Tool νΈλ€λ¬
β βββ services/ # BloggerService λ± λΉμ¦λμ€ λ‘μ§
β βββ types/ # νμ
μ μ (bloggerTypes λ±)
β βββ lib/ # μΈμ¦/ν ν° κ΄λ¦¬ λ±
βββ test/ # μ λ/ν΅ν© ν
μ€νΈ
βββ .env # νκ²½λ³μ (gitignore νμ)
βββ client_secret_*.json # Google OAuth2 μν¬λ¦Ώ (gitignore νμ)
βββ .gitignore
βββ package.json
βββ README.mdπ¦ μλ² μμ λ° λΈλ‘κ·Έ ID κ΄λ¦¬ μ μ±
-
μλ² μμ μ
- νκ²½λ³μ
BLOG_URL(νμ)λ‘ λΈλ‘κ·Έ μ£Όμλ₯Ό μ λ¬λ°μ΅λλ€. - Google μΈμ¦ ν ν°μ 체ν¬(μκ±°λ λ§λ£ μ μΈμ¦, μ€ν¨ μ μλ² μμ μ€λ¨)
- λΈλ‘κ·Έ μ£Όμλ‘ Google Blogger APIλ₯Ό ν΅ν΄ λΈλ‘κ·Έ IDλ₯Ό μ‘°νν©λλ€.
- μ‘°ν μ±κ³΅ μ, λΈλ‘κ·Έ IDλ₯Ό
.blog_id_cache.jsonνμΌμ μ μ₯ν©λλ€. - μ‘°ν μ€ν¨(λ€νΈμν¬, μΈμ¦, μ£Όμ μ€λ₯ λ±) μ MCP μλ¬λ‘ μλ² μμμ΄ μ€λ¨λ©λλ€.
- νκ²½λ³μ
-
API νΈμΆ μ
- νμ μΊμλ λΈλ‘κ·Έ ID(
.blog_id_cache.json)λ₯Ό μ¬μ©ν΄ APIλ₯Ό νΈμΆν©λλ€. - API νΈμΆ μ€ λΈλ‘κ·Έ ID κ΄λ ¨ μλ¬(404/403 λ±) λ°μ μ
- λΈλ‘κ·Έ μ£Όμλ‘ λΈλ‘κ·Έ IDλ₯Ό 1ν μ¬μ‘°ν(μ¬μλ)
- μ¬μ‘°ν μ±κ³΅ μ, IDλ₯Ό κ°±μ νμ¬ λ€μ API νΈμΆ
- μ¬μ‘°ν μ€ν¨ μ μλ¬ λ°ν
- νμ μΊμλ λΈλ‘κ·Έ ID(
-
μ΄μ/보μ μ£Όμμ¬ν
.blog_id_cache.jsonνμΌμ λ°λμ .gitignoreμ μΆκ°ν΄ gitμ 컀λ°νμ§ λ§μΈμ.- μΊμ νμΌ ν¬λ§· μμ:
{ "blogUrl": "https://yourblog.blogspot.com", "blogId": "1234567890" } - μλ² μ¬μμ μ μΊμ νμΌμ΄ μμΌλ©΄ μ°μ μ¬μ©, μμΌλ©΄ μλ‘ μ‘°ν
- λ€νΈμν¬/μΈμ¦/μ£Όμ μ€λ₯ λ±μΌλ‘ λΈλ‘κ·Έ IDλ₯Ό μ‘°νν μ μμΌλ©΄ μλ²κ° μμλμ§ μμ΅λλ€.
π§ͺ ν μ€νΈ
npm testλ‘ μ λ ν μ€νΈ μ€ν- ν΅ν© ν μ€νΈλ .env/νκ²½λ³μ/μ€μ blogUrl λ± μ€λ°μ΄ν° μ£Όμ νμ
- MCP Tool μ μ λμ, μΈμ¦ μ€ν¨/μ±κ³΅ μΌμ΄μ€ μλ κ²μ¦
π μΆκ° μ°Έκ³
- MCP Inspector λ±μΌλ‘ Tool λͺ©λ‘/νΈμΆ ν μ€νΈ κΆμ₯
- μΈμ¦/ν ν° λ°κΈμ Google OAuth2 곡μ κ°μ΄λ μ°Έκ³
- Tool/μλΉμ€ νμ₯ μ managers/services ꡬ쑰 νμ©
- BaseServer ꡬ쑰 κ°μ μΌλ‘ λΆνμν 리μμ€/ν둬ννΈ/μμ μ½λ μμ΄ κ²½λν κ°λ₯
π οΈ MCP Tool μ λ ₯/μΆλ ₯ ν¬λ§· λ° μμ
blog-post Tool
- μ€λͺ : Google Bloggerμ λ¨μΌ ν¬μ€νΈλ₯Ό μμ±ν©λλ€.
- μ λ ₯(JSON):
{
"title": "ν¬μ€νΈ μ λͺ©", // (νμ)
"content": "<h1>ν¬μ€νΈ λ΄μ©(HTML)</h1>", // (νμ)
"labels": ["λΌλ²¨1", "λΌλ²¨2"], // (μ ν)
"isDraft": true, // (μ ν, κΈ°λ³Έκ°: true)
}-
μ λ ₯ νλ μ€λͺ :
νλλͺ νμ νμ μ€λͺ title string β ν¬μ€νΈ μ λͺ© content string β ν¬μ€νΈ λ΄μ©(HTML) labels string[] β λΌλ²¨ λͺ©λ‘ isDraft boolean β μ΄μ μ¬λΆ(true/false, κΈ°λ³Έκ°: true) -
λΉκ³ : λΈλ‘κ·Έ IDλ μλ² νκ²½λ³μ(BLOG_ID)λ‘ κ΄λ¦¬λλ©°, μ¬μ©μκ° μ λ ₯ν νμκ° μμ΅λλ€.
-
μΆλ ₯(JSON):
{
"content": [
{ "type": "text", "text": "ν¬μ€νΈ μμ± μ±κ³΅!\nURL: https://...\nμ λͺ©: ..." }
],
"isError": false
}-
μΆλ ₯ νλ μ€λͺ :
νλλͺ νμ μ€λͺ content array λ©μμ§(μ±κ³΅/μ€ν¨/μμΈμ 보) isError boolean μ€ν¨ μ¬λΆ(true: μλ¬, false: μ±κ³΅) -
μλ¬ μμ:
{
"content": [
{ "type": "text", "text": "μΈμ¦ ν ν°μ΄ μμ΅λλ€. λ¨Όμ μΈμ¦μ μλ£νμΈμ." }
],
"isError": true
}blog-batch-post Tool
- μ€λͺ : μ¬λ¬ ν¬μ€νΈλ₯Ό ν λ²μ μμ±ν©λλ€.
- μ λ ₯(JSON):
{
"posts": [
// (νμ)
{
"title": "μ λͺ©1", // (νμ)
"content": "<h1>λ΄μ©1</h1>", // (νμ)
"labels": ["λΌλ²¨A"], // (μ ν)
"isDraft": true, // (μ ν, κΈ°λ³Έκ°: true)
},
{
"title": "μ λͺ©2", // (νμ)
"content": "<h1>λ΄μ©2</h1>", // (νμ)
},
],
}-
μ λ ₯ νλ μ€λͺ :
νλλͺ νμ νμ μ€λͺ posts object[] β ν¬μ€νΈ λ°°μ΄ β title string β ν¬μ€νΈ μ λͺ© β content string β ν¬μ€νΈ λ΄μ©(HTML) β labels string[] β λΌλ²¨ λͺ©λ‘ β isDraft boolean β μ΄μ μ¬λΆ(true/false, κΈ°λ³Έκ°: true) -
λΉκ³ : λΈλ‘κ·Έ IDλ μλ² νκ²½λ³μ(BLOG_ID)λ‘ κ΄λ¦¬λλ©°, μ¬μ©μκ° μ λ ₯ν νμκ° μμ΅λλ€.
-
μΆλ ₯(JSON):
{
"content": [
{ "type": "text", "text": "λ°°μΉ ν¬μ€ν
μλ£! μ±κ³΅: 2, μ€ν¨: 0" },
{ "type": "text", "text": "[ {\"success\":true,\"postId\":\"...\",...} ]" }
],
"isError": false
}-
μΆλ ₯ νλ μ€λͺ :
νλλͺ νμ μ€λͺ content array λ©μμ§(μ±κ³΅/μ€ν¨/μμΈμ 보) isError boolean μ€ν¨ μ¬λΆ(true: μλ¬, false: μ±κ³΅) -
μλ¬ μμ:
{
"content": [
{ "type": "text", "text": "μΈμ¦ ν ν°μ΄ μμ΅λλ€. λ¨Όμ μΈμ¦μ μλ£νμΈμ." }
],
"isError": true
}π λΌμ΄μ μ€
μ΄ νλ‘μ νΈλ MIT λΌμ΄μ μ€λ₯Ό λ°λ¦ λλ€.
βοΈ ν΄λΌμ΄μΈνΈμμ MCP μλ² μ€μ μμ
MCP ν΄λΌμ΄μΈνΈ(μ: MCP Inspector, ν΅ν© MCP νλ«νΌ λ±)μμ blogspot-posting μλ²λ₯Ό μ€νν λλ μλμ κ°μ΄ mcpServers μ€μ μ μ¬μ©ν©λλ€.
{
"mcpServers": {
"blogspot-posting": {
"command": "npx",
"args": ["-y", "server-blogspot-posting"],
"env": {
"GOOGLE_CLIENT_SECRET_PATH": "/path/to/client_secret_xxx.json",
"BLOG_ID": "your-blog-id",
},
},
},
}command,args: MCP μλ² μ€ν λͺ λ Ή λ° μΈμenv: μλ²μ μ£Όμ ν νκ²½λ³μ(μν¬λ¦Ώ κ²½λ‘)- νμμ λ°λΌ μΆκ° νκ²½λ³μ(.envμ μλ κ°)λ₯Ό λͺ¨λ envμ λͺ μ κ°λ₯
NOTE: νμ¬ λ²μ μμλ SESSION_SECRETμ νμνμ§ μμ΅λλ€. (μΈμ κΈ°λ° μΈμ¦/λ‘κ·ΈμΈ κΈ°λ₯μ΄ μΆκ°λ κ²½μ°μλ§ νμ)
β οΈ blogId μΊμ νμΌ(.blog_id_cache.json) κ²½λ‘ μ μ± λ° μ£Όμμ¬ν
- λΈλ‘κ·Έ ID μΊμ νμΌ(.blog_id_cache.json)μ νμ νλ‘μ νΈ λ£¨νΈ(μ΅μμ ν΄λ)μλ§ μμ±λ©λλ€.
- λΉλ(dist) ν΄λ λ± λ€λ₯Έ μμΉμ μΊμ νμΌμ΄ μμ±λλ λ¬Έμ λ₯Ό 2025-06-12μ μμ νμμΌλ©°, μ΄μ μ΄λ€ νκ²½μμ μ€νν΄λ 루νΈμλ§ μμ±λ©λλ€.
- dist/.blog_id_cache.json λ± μλͺ»λ μμΉμ μΊμ νμΌμ΄ μλ€λ©΄ μλμΌλ‘ μμ νμΈμ.
- λ°λμ .gitignoreμ .blog_id_cache.jsonμ ν¬ν¨ν΄ gitμ 컀λ°λμ§ μλλ‘ νμΈμ.
- μλ² μ€ν/λΉλ μμΉμ 무κ΄νκ² μΊμ νμΌμ΄ 루νΈμλ§ μμ±λλμ§ μ΄μ/ν μ€νΈ νκ²½μμ νμΈνμΈμ.
π§βπ» λλ²κΉ /μ΄μ κ°μ΄λ
- MCP μλ²μ λͺ¨λ λ‘κ·Έλ λ°λμ stderr(console.error) λλ MCP νλ‘ν μ½ μ μ© λ‘κΉ ν¨μλ§ μ¬μ©ν΄μΌ νλ©°, stdout μ€μΌ μ νλ‘ν μ½ νμ± μλ¬κ° λ°μν μ μμ΅λλ€.
[DEBUG][STDIO_SEND] ...stderr λ‘κ·Έλ₯Ό ν΅ν΄ μ€μ μ‘μ λλ JSON λ©μμ§λ₯Ό μ€μκ°μΌλ‘ μΆμ νμ¬, νμ± μλ¬ λ° νλ‘ν μ½ λ¬Έμ λ₯Ό μ μνκ² μ§λ¨ν μ μμ΅λλ€.- Inspector/ν΄λΌμ΄μΈνΈμμ
server/infoλ‘ μλ² μ 보 μ‘°ν λ° tools/list λ± μ μ λμμ νμΈνμΈμ.
π¦ MCP Tool/μλ² μλν¬μΈνΈ μμ
- μλ² μ 보 μ‘°ν(server/info):
β μλ΅ μμ:
{ "method": "server/info" }{ "name": "blogspot-mcp-server", "version": "1.0.0" }
npm install
npm run buildBefore it works, you'll need: GOOGLE_CLIENT_SECRET_PATHBLOG_ID
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.