Typecho MCP 服务器完整教程

概述

本教程将指导你从零开始搭建一个 MCP (Model Context Protocol) 服务器,让 AI 助手(如 Trae、Claude Desktop 等)能够直接发布文章到你的 Typecho 博客。

最终效果:在 AI 对话中输入"帮我发布一篇文章,标题是xxx,内容是xxx",AI 就会自动调用工具发布到你的博客。


目录

  1. 准备工作
  2. 安装 Python 依赖
  3. 创建项目文件
  4. 配置博客信息
  5. 测试发布功能
  6. 配置 MCP 客户端
  7. 使用教程
  8. 常见问题

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-mcp

2. 安装 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.pytest_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 地址

如果你不确定,可以:

  1. 登录 Typecho 后台
  2. 访问 https://你的博客域名/index.php/action/xmlrpc
  3. 如果看到 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 中配置

  1. 打开 Trae
  2. 找到 MCP 配置入口(通常在设置 → 集成 → MCP Servers)
  3. 点击"手动配置"
  4. 粘贴以下 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
- 列表项2

AI 会自动调用 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: 检查以下几点:

  1. 博客地址是否正确(是否以 https:// 开头)
  2. 用户名密码是否正确
  3. 博客是否开启了 XML-RPC

Q2: 文章发布成功但前台显示 404?

A: Typecho 的伪静态未开启。解决方法:

  1. 登录博客后台
  2. 设置 → 永久链接
  3. 选择 pathinfo 模式
  4. 保存

Q3: AI 不调用工具,而是自己写代码?

A: 这是 MCP 客户端的问题。尝试:

  1. 重启 MCP 客户端
  2. 检查配置是否正确保存
  3. 确认 MCP 服务器已启动(命令行运行 python server.py 看是否有错误)
  4. 尝试使用其他 MCP 客户端(如 Claude Desktop)

Q4: 提示 "No module named 'fastmcp'"?

A: 未安装依赖,运行:

pip install fastmcp requests

Q5: 文章发布后是草稿?

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 服务器的搭建。现在你可以:

  1. ✅ 在 AI 对话中直接发布文章
  2. ✅ 让 AI 帮你撰写并发布内容
  3. ✅ 批量创作博客内容

文件清单

D:\typecho-mcp\
├── server.py          # MCP 服务器(主要文件)
├── test_publish.py    # 测试脚本

暂无评论