概述
本教程将指导你从零开始搭建一个 MCP (Model Context Protocol) 服务器,让 AI 助手(如 Trae、Claude Desktop 等)能够直接发布文章到你的 Typecho 博客。
最终效果:在 AI 对话中输入"帮我发布一篇文章,标题是xxx,内容是xxx",AI 就会自动调用工具发布到你的博客。
目录
1. 准备工作
1.1 确认你的博客支持 XML-RPC
Typecho 默认支持 XML-RPC,访问以下地址:
https://你的博客域名/index.php/action/xmlrpc如果看到类似以下内容,说明正常:
XML-RPC server accepts POST requests only.1.2 确认 Python 已安装
打开命令行(Win+R 输入 cmd),输入:
python --version需要 Python 3.8 或更高版本。如果没有安装,请访问 python.org 下载安装。
1.3 创建项目文件夹
在任意位置创建一个文件夹,例如:
D:\typecho-mcp2. 安装 Python 依赖
打开命令行,进入项目文件夹:
cd D:\typecho-mcp安装所需依赖:
pip install fastmcp requests等待安装完成,看到 Successfully installed 即可。
3. 创建项目文件
在项目文件夹中创建两个文件。
3.1 创建 server.py
这是 MCP 服务器的主文件。复制以下完整代码:
from fastmcp import FastMCP
import requests
import xml.etree.ElementTree as ET
# ========== 请修改这里的配置 ==========
BLOG_URL = "https://你的博客域名/index.php/action/xmlrpc"
USERNAME = "你的Typecho用户名"
PASSWORD = "你的Typecho密码"
# ====================================
# 创建 MCP 服务器实例
mcp = FastMCP("Typecho MCP")
def build_xml(method_name, params):
"""构造 XML-RPC 请求体"""
root = ET.Element("methodCall")
method = ET.SubElement(root, "methodName")
method.text = method_name
params_elem = ET.SubElement(root, "params")
for param in params:
param_elem = ET.SubElement(params_elem, "param")
value_elem = ET.SubElement(param_elem, "value")
if isinstance(param, str):
string_elem = ET.SubElement(value_elem, "string")
string_elem.text = param
elif isinstance(param, int):
int_elem = ET.SubElement(value_elem, "int")
int_elem.text = str(param)
elif isinstance(param, bool):
boolean_elem = ET.SubElement(value_elem, "boolean")
boolean_elem.text = "1" if param else "0"
elif isinstance(param, dict):
struct_elem = ET.SubElement(value_elem, "struct")
for key, val in param.items():
member = ET.SubElement(struct_elem, "member")
name = ET.SubElement(member, "name")
name.text = key
val_elem = ET.SubElement(member, "value")
if isinstance(val, str):
string_elem = ET.SubElement(val_elem, "string")
string_elem.text = val
elif isinstance(val, list):
array_elem = ET.SubElement(val_elem, "array")
data_elem = ET.SubElement(array_elem, "data")
for item in val:
item_elem = ET.SubElement(data_elem, "value")
item_string = ET.SubElement(item_elem, "string")
item_string.text = item
return ET.tostring(root, encoding="utf-8", xml_declaration=True)
def parse_response(xml_response):
"""解析 XML-RPC 响应,提取文章 ID"""
try:
root = ET.fromstring(xml_response)
value = root.find(".//params/param/value/int")
if value is not None:
return int(value.text)
value = root.find(".//params/param/value/string")
if value is not None:
return value.text
return None
except Exception:
return None
@mcp.tool()
def publish_article(title: str, content: str, categories: list = None) -> dict:
"""
发布文章到 Typecho 博客
Args:
title: 文章标题
content: 文章内容(支持 HTML 格式)
categories: 文章分类列表,如 ["blog", "tech"](可选)
Returns:
包含发布结果的字典
"""
# 构造文章结构
post_struct = {
'title': title,
'description': content,
}
if categories:
post_struct['categories'] = categories
# 参数说明:blogid, username, password, struct, publish
# 最后一个参数 True = 立即发布
params = ['', USERNAME, PASSWORD, post_struct, True]
xml_body = build_xml("metaWeblog.newPost", params)
headers = {'Content-Type': 'text/xml'}
try:
response = requests.post(BLOG_URL, data=xml_body, headers=headers, verify=False, timeout=30)
if response.status_code == 200:
post_id = parse_response(response.text)
if post_id:
# 将域名替换成你的博客域名
blog_domain = BLOG_URL.replace("/index.php/action/xmlrpc", "")
return {
'success': True,
'post_id': post_id,
'url': f'{blog_domain}/archives/{post_id}/',
'message': f'文章发布成功!访问链接:{blog_domain}/archives/{post_id}/'
}
else:
return {
'success': False,
'error': '解析响应失败,请检查博客设置'
}
else:
return {
'success': False,
'error': f'HTTP {response.status_code}: {response.text[:200]}'
}
except requests.exceptions.Timeout:
return {'success': False, 'error': '请求超时,请检查网络连接'}
except requests.exceptions.ConnectionError:
return {'success': False, 'error': '连接失败,请检查博客地址是否正确'}
except Exception as e:
return {'success': False, 'error': str(e)}
if __name__ == "__main__":
mcp.run()3.2 创建 test_publish.py(测试脚本)
这个文件用于测试发布功能是否正常,不依赖 MCP。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""测试 Typecho 发布功能"""
import requests
import xml.etree.ElementTree as ET
# ========== 请修改这里的配置 ==========
BLOG_URL = "https://你的博客域名/index.php/action/xmlrpc"
USERNAME = "你的Typecho用户名"
PASSWORD = "你的Typecho密码"
# ====================================
def test_publish():
"""测试发布文章"""
# 构造文章
title = "测试文章"
content = "<p>这是一篇测试文章,用于验证发布功能是否正常。</p><p>如果你看到这篇文章,说明发布成功!</p>"
# 构建 XML-RPC 请求
root = ET.Element("methodCall")
ET.SubElement(root, "methodName").text = "metaWeblog.newPost"
params = ET.SubElement(root, "params")
for val in ['', USERNAME, PASSWORD, {'title': title, 'description': content}, True]:
p = ET.SubElement(params, "param")
v = ET.SubElement(p, "value")
if isinstance(val, str):
ET.SubElement(v, "string").text = val
elif isinstance(val, int):
ET.SubElement(v, "int").text = str(val)
elif isinstance(val, bool):
ET.SubElement(v, "boolean").text = "1" if val else "0"
elif isinstance(val, dict):
s = ET.SubElement(v, "struct")
for k, v2 in val.items():
m = ET.SubElement(s, "member")
ET.SubElement(m, "name").text = k
v3 = ET.SubElement(m, "value")
if isinstance(v2, str):
ET.SubElement(v3, "string").text = v2
xml_body = ET.tostring(root, encoding="utf-8", xml_declaration=True)
headers = {'Content-Type': 'text/xml'}
try:
response = requests.post(BLOG_URL, data=xml_body, headers=headers, verify=False, timeout=30)
print(f"HTTP 状态码: {response.status_code}")
if response.status_code == 200:
# 解析响应获取文章 ID
resp_root = ET.fromstring(response.text)
post_id = resp_root.find(".//params/param/value/int")
if post_id is not None:
print(f"✅ 发布成功!")
print(f" 文章ID: {post_id.text}")
blog_domain = BLOG_URL.replace("/index.php/action/xmlrpc", "")
print(f" 链接: {blog_domain}/archives/{post_id.text}/")
else:
print("❌ 解析响应失败")
print(f"响应内容: {response.text[:500]}")
else:
print(f"❌ 发布失败: HTTP {response.status_code}")
print(f"响应内容: {response.text[:500]}")
except Exception as e:
print(f"❌ 错误: {e}")
if __name__ == "__main__":
test_publish()4. 配置博客信息
4.1 修改配置文件
打开 server.py 和 test_publish.py,找到开头的配置部分:
BLOG_URL = "https://你的博客域名/index.php/action/xmlrpc"
USERNAME = "你的Typecho用户名"
PASSWORD = "你的Typecho密码"修改为你的实际信息:
BLOG_URL:你的博客 XML-RPC 地址(Typecho 默认就是这个路径)USERNAME:登录 Typecho 后台的用户名PASSWORD:登录密码
4.2 获取 XML-RPC 地址
如果你不确定,可以:
- 登录 Typecho 后台
- 访问
https://你的博客域名/index.php/action/xmlrpc - 如果看到
XML-RPC server accepts POST requests only.,说明地址正确
5. 测试发布功能
重要:先测试,确保配置正确!
在命令行运行:
cd D:\typecho-mcp
python test_publish.py预期输出:
HTTP 状态码: 200
✅ 发布成功!
文章ID: 1
链接: https://你的博客域名/archives/1/如果看到这个结果,说明配置正确,可以继续下一步。
如果失败,请检查:
- 博客地址是否正确
- 用户名密码是否正确
- 博客是否开启了 XML-RPC(Typecho 默认开启)
6. 配置 MCP 客户端
6.1 测试 MCP 服务器能否正常启动
在命令行运行:
cd D:\typecho-mcp
python server.py你应该看到类似这样的输出:
┌──────────────────────────────────────────────────────────────────────────────┐
│ FastMCP 3.2.1 │
│ 🖥 Server: Typecho MCP │
└──────────────────────────────────────────────────────────────────────────────┘
INFO Starting MCP server 'Typecho MCP' with transport 'stdio'保持这个窗口打开(不要关闭),或者按 Ctrl+C 关闭(配置客户端后会自动启动)。
6.2 在 Trae 中配置
- 打开 Trae
- 找到 MCP 配置入口(通常在设置 → 集成 → MCP Servers)
- 点击"手动配置"
- 粘贴以下 JSON:
{
"mcpServers": {
"typecho-mcp": {
"command": "python",
"args": ["D:\\typecho-mcp\\server.py"]
}
}
}注意:路径中的反斜杠要写成 \\,如果路径不同请修改。
6.3 在 Claude Desktop 中配置(可选)
如果你使用 Claude Desktop,配置文件位置:
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
添加配置:
{
"mcpServers": {
"typecho-mcp": {
"command": "python",
"args": ["D:\\typecho-mcp\\server.py"]
}
}
}7. 使用教程
7.1 在 AI 中发布文章
配置完成后,在 AI 对话中输入:
帮我发布一篇文章,标题是"我的第一篇文章",内容如下:
这是文章内容,支持 **Markdown** 格式。
- 列表项1
- 列表项2AI 会自动调用 publish_article 工具,返回:
✅ 文章发布成功!
文章ID: 5
链接: https://你的博客域名/archives/5/7.2 带分类发布
发布一篇文章,标题是"技术分享",分类是"tech",内容是...7.3 常用提示词示例
| 需求 | 提示词 |
|---|---|
| 发布普通文章 | "帮我发布一篇文章,标题是xxx,内容是xxx" |
| 发布带分类文章 | "发布文章到blog分类,标题xxx,内容xxx" |
| 发布技术文章 | "写一篇关于Python的技术文章并发布到我的博客" |
| 草稿 | "保存为草稿,标题xxx,内容xxx"(需要修改 publish 参数) |
8. 常见问题
Q1: 运行 test_publish.py 显示连接失败?
A: 检查以下几点:
- 博客地址是否正确(是否以
https://开头) - 用户名密码是否正确
- 博客是否开启了 XML-RPC
Q2: 文章发布成功但前台显示 404?
A: Typecho 的伪静态未开启。解决方法:
- 登录博客后台
- 设置 → 永久链接
- 选择
pathinfo模式 - 保存
Q3: AI 不调用工具,而是自己写代码?
A: 这是 MCP 客户端的问题。尝试:
- 重启 MCP 客户端
- 检查配置是否正确保存
- 确认 MCP 服务器已启动(命令行运行
python server.py看是否有错误) - 尝试使用其他 MCP 客户端(如 Claude Desktop)
Q4: 提示 "No module named 'fastmcp'"?
A: 未安装依赖,运行:
pip install fastmcp requestsQ5: 文章发布后是草稿?
A: 检查 server.py 中 publish 参数是否为 True:
params = ['', USERNAME, PASSWORD, post_struct, True] # True = 发布Q6: 中文乱码?
A: 确保代码文件保存为 UTF-8 编码。在 VS Code 中,右下角点击编码,选择 "Save with UTF-8"。
Q7: SSL 证书警告?
A: 这是正常现象,不影响使用。如果想消除警告,在代码开头添加:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)总结
恭喜!你已经完成了 Typecho MCP 服务器的搭建。现在你可以:
- ✅ 在 AI 对话中直接发布文章
- ✅ 让 AI 帮你撰写并发布内容
- ✅ 批量创作博客内容
文件清单:
D:\typecho-mcp\
├── server.py # MCP 服务器(主要文件)
├── test_publish.py # 测试脚本
暂无评论