Skip to content
如何为你的博客网站构建 MCP 服务

使用 Nextra 和大语言模型打造 MCP 驱动的博客文章生成器

Updated on

如何构建一个 MCP 服务?在本教程中,我们将构建一个 MCP 服务,让你通过与 OpenAI 或 Claude 聊天即可撰写和发布文章。

在本教程中,我们将搭建一个技术方案,使你能够与大语言模型(LLM)对话,并让它为你的基于 Nextra 博客生成内容。生成的内容将以 MDX 格式写入,并自动提交到你博客的 GitHub 仓库,作为新的博文发布。我们将探索这套 Model Context Protocol(MCP) 接入的两种实现方式:

  1. 无服务器方案(通过 GitHub Actions): 利用 GitHub Actions 工作流,自动完成 LLM 调用和新文章的提交;
  2. 托管服务方案(基于 Next.js API): 构建一个简单的 MCP 服务器端点作为 Next.js API(以 jojocys/nextra-agent-blog 作为示例平台),用作 MCP 客户端。该端点会调用 LLM,并实时将文章推送到 GitHub。

MCP 是一种 AI 助手对接外部工具和数据的开放标准。它为 AI 模型与云服务、数据库、甚至 GitHub、Slack 等平台的互通提供了统一接口,无需为每个工具单独集成(Model Context Protocol (MCP) Explained (opens in a new tab))。换句话说,MCP 让 AI 代理可以使用“工具”,以突破其基础知识完成操作。例如,AI 助手可以通过 MCP 实现发邮件、部署代码、更重要的是发布博客文章Build and deploy Remote Model Context Protocol (MCP) servers to Cloudflare (opens in a new tab))。在我们的案例中,“工具”就是在 Git 仓库中创建新的 MDX 文件并提交,亦即博客发布。

看完本指南,你将拥有一套工作流:你(单用户场景)可以请求 AI 代理撰写博客并自动发布到站点。我们将重点关注各环节如何对接(MCP 客户端↔︎LLM 及 LLM↔︎GitHub),默认仅限单用户场景,无需增加额外认证。

方案总览

在动手写代码前,先梳理下整体架构和流程:

  • 博客平台: 网站为 Next.js 项目,采用 Nextra 的“blog 主题”(例如 jojocys/nextra-agent-blog 仓库),内容存放于仓库的 MDX 文件(如 pages/posts 目录下)。每个 MDX 文件就是一篇文章,由 Nextra 读取 frontmatter(如 title, date 等)和正文进行展示。

  • LLM(AI 助理): 通过 AI 模型(如 OpenAI 的 GPT、Anthropic 的 Claude)生成博客内容。模型接收写作主题提示(通过聊天界面或 API),返回 MDX 格式的文章内容。

  • MCP 客户端与服务端: 在 MCP 概念下,LLM 充当主机,可通过客户端-服务器接口调用外部工具。我们的实现方式分两种:

    • GitHub Actions 方案 下,GitHub Actions 工作流本身作为 MCP 客户端,发起与 LLM 的会话,并执行“写文章”工具即提交文章到仓库。
    • Next.js API 方案 下,我们创建自定义 API 路由,既作为 MCP 客户端(驱动 LLM 生成内容),又为博客仓库提供 MCP 服务端(负责生成 commit),起到 LLM 与 GitHub 间的桥梁作用。
  • GitHub 集成: 两种方案都通过 GitHub API 将新文章写入仓库,可以通过直接的 Git 操作,或用 GitHub HTTP API 实现(Anthropic 的 MCP 生态甚至已提供现成的 GitHub 连接器支持仓库操作,GitHub - modelcontextprotocol/servers: Model Context Protocol Servers (opens in a new tab),但我们这里选择手动实现以便深入理解)。

明确了这些背景后,我们先从无服务器 GitHub Actions 方案开讲。

方式一:使用 GitHub Actions 实现无服务器文章生成

此方案下,使用 GitHub Actions 工作流,按需生成并发布博客文章。你无需单独部署服务器,每次触发时 GitHub 会自动启动 runner 执行完整步骤:

  1. 触发: 你(用户)发起工作流(可手动或通过仓库事件),输入文章主题或提示语。
  2. LLM 生成: 工作流调用 LLM API(如 OpenAI/Claude),让其输出该主题的 MDX 格式博文。
  3. 文件创建: 捕获 LLM 返回的内容,保存为新的 .mdx 文件(加上 YAML frontmatter)。
  4. 推送到 GitHub: 新文件被 commit 并推送到仓库(如 main 分支),博客即可上线。

现在来写这个工作流文件。在你的博客仓库下(已集成 Nextra),创建 .github/workflows/llm-agent.yml 文件:

name: LLM Blog Post Generator
 
# Allow manual trigger of this workflow
on:
  workflow_dispatch:
    inputs:
      prompt:
        description: "Topic or prompt for the blog post"
        required: true
        type: string
 
permissions:
  contents: write  # allow pushing to the repo
 
jobs:
  generate_post:
    runs-on: ubuntu-latest
    steps:
      # Step 1: Check out the repository code
      - name: Check out repo
        uses: actions/checkout@v3
 
      # Step 2: Install tools (jq for JSON parsing, if not pre-installed)
      - name: Install jq
        run: sudo apt-get -y install jq
 
      # Step 3: Call the LLM API to generate content
      - name: Generate blog content with OpenAI
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: |
          echo "Prompt: ${{ github.event.inputs.prompt }}"
          # Prepare the OpenAI API request payload
          REQUEST_DATA=$(jq -n --arg prompt "${{ github.event.inputs.prompt }}" '{
              "model": "gpt-3.5-turbo",
              "messages": [
                {"role": "system", "content": "You are a technical blogging assistant. Write an MDX blog post with a YAML frontmatter (title, date, description, type: posts) and content on the given topic."},
                {"role": "user", "content": $prompt}
              ]
            }')
          # Call OpenAI API for chat completion
          RESPONSE=$(curl -sS -H "Content-Type: application/json" \
                          -H "Authorization: Bearer $OPENAI_API_KEY" \
                          -d "$REQUEST_DATA" \
                          https://api.openai.com/v1/chat/completions)
          # Extract the assistant's message content (the MDX text)
          POST_CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content')
          # Create a filename for the new post (slug from date)
          TIMESTAMP=$(date +"%Y-%m-%d-%H%M")
          FILENAME="posts/llm-post-$TIMESTAMP.mdx"
          echo "Creating new post file: $FILENAME"
          echo "$POST_CONTENT" > $FILENAME
 
      # Step 4: Commit and push the new post
      - name: Commit changes
        run: |
          git config user.name "github-actions"
          git config user.email "github-actions@users.noreply.github.com"
          git add posts/*.mdx
          git commit -m "Add new blog post generated by LLM"
          git push

下面解释下工作流的每一步:

  • 触发与输入: 采用 workflow_dispatch,工作流可在 Actions 页签手动启动,并填写 prompt 输入。例如输入“如何用 MCP 搭建 Next.js 博客”等。

  • 权限: contents: write 赋予推送权限。工作流使用内置 GITHUB_TOKEN 认证(默认拥有写权限)。

  • Step 1: 拉取仓库代码,runner 可操作本地代码树,新文件直接添加进来。

  • Step 2: 安装 jq,用于解析 JSON,保障后续流程。

  • Step 3: “调用 OpenAI 生成文章” — 关键步骤:

    • 从 secrets 中加载 OpenAI Key。首先到项目 Settings → Secrets 添加 OPENAI_API_KEY。如用 Claude,需使用其 API endpoint 和 Key(后文说明)。
    • jq 组织 API 请求体,采用 Chat Completion API,模型是 gpt-3.5-turbo。system message 说明回复需包含 MDX 格式正文及 frontmatter(含 title、date、description、type: posts)。user message 就是提示词(文章主题)。
    • curl 向 OpenAI API 发送请求,捕获响应。
    • 解析返回 JSON 的 .choices[0].message.content,保存为 POST_CONTENT,即完整 MDX 内容。
    • 构造新文件名。这里以当前时间戳(精确到分钟)命名如 llm-post-2025-04-15-1211.mdx。实际使用可用 frontmatter 的标题生成 slug,但为自动化简便先用时间戳。
    • 将内容写入 posts/ 目录下。
  • Step 4: 提交与推送

    • 配置 git 作者信息。
    • 添加新文件、提交至 main 分支、一旦推送即上线(博客通常自动部署,如 Vercel 或 GitHub Pages)。

工作流跑完后,仓库里会多一个 posts/llm-post-2025-04-15-1211.mdx 文件,内容为 AI 生成的带 frontmatter 的 MDX 文章,Nextra 会自动加载展示。你也可以本地拉取审阅或编辑内容。

测试: 进入仓库 Actions 选项卡,执行 LLM Blog Post Generator 工作流并输入测试提示。workflow 日志会输出操作步骤。等完成后,刷新仓库即可见新文件。如部署在 Vercel,则新文章也会自动上线。若需人工部署,可触发构建。

注意: 该流程也可通过其他方式触发。例如,设定打开特定标签的 GitHub Issue 就自动生成文章,读取 Issue 内容作为 prompt。此处为演示简便采用手触发。API 调用将产成费用及一定延迟,且内容未事先人工审核,正式场景建议生成 Pull Request 便于先行审稿再合并。

如需用 Anthropic Claude,只需将步骤 3 的 API 替换为 Claude 的 endpoint(如 /v1/complete 或新版接口),请求格式稍有不同(比如提示词格式需要带 \n\nHuman: ...\n\nAssistant:,模型用 claude-2),API Key 和 header 也要设置成对应值,其它步骤无变化。

方式二:通过 Next.js API 路由托管 MCP 服务

这一方案将 MCP 客户端/服务端逻辑嵌入博客应用自身。我们将创建一个 Next.js API 路由,接收请求(如带有 prompt 的指令),通过 LLM 生成 MDX 内容,再 commit 到 GitHub 仓库。这样便可在网页界面发起对话或表单,实时请求 AI 生成并发布文章,无需进入 GitHub Actions 后台。

该方法要求 Next.js 应用(博客)有权限访问 GitHub 仓库,通常在云平台(如 Vercel 或自建服务器)部署,并将 GitHub 的 Personal Access Token(PAT)提供给服务端,确保能写入仓库。Token 需具备对应仓库的写权限。

准备: 在 Next.js 项目根目录添加环境变量,例如 .env.local 文件(勿提交仓库):

OPENAI_API_KEY=<your OpenAI API key>
GITHUB_PAT=<your GitHub Personal Access Token>
GITHUB_REPO_OWNER=<github username or org>
GITHUB_REPO_NAME=<repo name, e.g., nextra-agent-blog>

部署时注意环境变量同步到服务器。GitHub Token 需要 repo 权限(至少写 content)。如内容仓库与源码仓库一致,owner/name 即当下仓库;否则填目标仓库信息。

然后新增 API 路由(以 Next.js 13 下 pages 目录为例,新版 App Router 也类似,可适当调整):

// pages/api/generate-post.js
import { Octokit } from "@octokit/rest";
import { Configuration, OpenAIApi } from "openai";
 
export default async function handler(req, res) {
  const { prompt } = req.body;
  if (!prompt) {
    return res.status(400).json({ error: "No prompt provided" });
  }
 
  try {
    // 1. Initialize OpenAI client
    const config = new Configuration({ apiKey: process.env.OPENAI_API_KEY });
    const openai = new OpenAIApi(config);
 
    // 2. Call the OpenAI API to get a blog post content
    const chatCompletion = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        { role: "system", content: "You are a technical blogging assistant. Write an MDX blog post with a YAML frontmatter (title, date, description, type: posts) for the given topic." },
        { role: "user", content: prompt }
      ]
    });
    const mdxContent = chatCompletion.data.choices[0].message.content;
 
    // 3. Prepare the new post file details
    // Extract title from frontmatter for filename (optional improvement)
    const titleMatch = mdxContent.match(/^title:\s*\"?(.+)\"?/m);
    const titleText = titleMatch ? titleMatch[1] : "untitled-post";
    // Create a slug from title (lowercase, hyphens, no special chars)
    const slug = titleText.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
    const dateStr = new Date().toISOString();
    const fileName = `posts/${slug || "untitled"}-${Date.now()}.mdx`;
 
    // If date not in frontmatter, you can prepend or replace it
    // (Assume the LLM included date, but if not, we can insert it)
    let postContentFinal = mdxContent;
    if (!/^date:/m.test(mdxContent)) {
      postContentFinal = postContentFinal.replace(/^(title:.*)$/m, `$1\ndate: ${dateStr}`);
    }
    // Ensure type: posts is present
    if (!/^type:\s*posts/m.test(postContentFinal)) {
      postContentFinal = postContentFinal.replace(/^(title:.*)$/m, `$1\ntype: posts`);
    }
 
    // 4. Commit the file to GitHub using Octokit
    const octokit = new Octokit({ auth: process.env.GITHUB_PAT });
    const owner = process.env.GITHUB_REPO_OWNER;
    const repo = process.env.GITHUB_REPO_NAME;
    const path = fileName;
    const message = `Add new post "${titleText}" via MCP`;
    const contentEncoded = Buffer.from(postContentFinal, "utf-8").toString("base64");
 
    // Create or update the file in the repo
    await octokit.rest.repos.createOrUpdateFileContents({
      owner,
      repo,
      path,
      message,
      content: contentEncoded,
      committer: {
        name: "MCP Bot",
        email: "mcp-bot@example.com"
      },
      author: {
        name: "MCP Bot",
        email: "mcp-bot@example.com"
      }
    });
 
    return res.status(200).json({ message: "Post created", slug });
  } catch (error) {
    console.error("Error generating post:", error);
    return res.status(500).json({ error: error.message });
  }
}

代码说明:

  • 采用官方 openai Node.js SDK 直接调用模型。提示内容同 GitHub Actions 工作流:system prompt 要求回复带完整 frontmatter 的 MDX 博文,user prompt 是请求主题。
  • AI 返回内容存入 mdxContent。使用正则尝试从 frontmatter 提取标题生成 slug。如未提取成功,则用“untitled-post”回退,并确保 frontmatter 至少有 datetype: posts 字段,必要时自动补充。
  • 构造文件路径如 posts/your-post-title-<timestamp>.mdx,使用时间戳避免重名。
  • 用 Octokit(GitHub JS SDK)操作仓库:通过 PAT 认证,指定 ownerrepo、文件 path、commit 提交信息及 Base64 编码的文件内容(符合 API 要求),commit 信息可用机器人身份或自定义。
  • 成功后响应 200,返回状态与 slug,便于前端后续跳转。出错则捕获并输出。

调用方式: 你可以将 Next.js 项目部署到线上(确保环境变量已配置),然后向 /api/generate-post 发送 POST 请求,内容为 JSON,如:{"prompt": "想要AI讲讲如何在 Next.js 实现 Model Context Protocol"}。可用命令如下:

curl -X POST https://<your-deployed-site>/api/generate-post \
  -H "Content-Type: application/json" \
  -d '{"prompt": "How to implement Model Context Protocol in a Next.js application?"}'

服务端将负责调用 LLM 生成内容并 commit 到仓库。稍后即可在 GitHub 仓库看到新 commit,文章自动上线(如自动部署)。

这种做法实际上也是一个简易 MCP 服务端点:暴露了创建博客文章的能力,可由 AI 触发。严格的 MCP 方案下,AI 可自动发现并调用该“工具”。本方案直接由端点集成调用,结构简单,也适用于单用户使用场景。

该 Next.js 方案提示几点:

  • 保证仓库下存在 posts 目录(或根据实际结构调整代码路径)。如使用 jojocys/nextra-agent-blog 通常是这种结构。frontmatter 至少包含 titledatetype: posts,否则 Nextra 不会识别为博文。我们代码中自动补齐。可根据需新增如 description 等字段。
  • 每次请求都会消耗 LLM API 点数并提交一次 commit。最低限度建议不要公开端点链接,或根据实际情况加 auth(如 header 带 secret token、只允许特定网站 origin 请求等)。
  • AI 内容结构可通过更改提词来细化要求。因为 MDX 基本兼容 Markdown,AI 输出通常不会出现语法兼容问题。如 AI 输出含 JSX 组件或 import,需确保博客配置能正确处理(进阶用法,本例未触及)。
  • 如用 Claude,仅需按其 SDK 调用(如 NPM 包 anthropic),API 请求和返回处理基本一致,仅格式有所不同。

拓展与进阶

我们已经展示了两种将 AI 助手与博客内容仓库打通的方式,理念都契合 MCP 精神。实际可根据需求择一,甚至组合使用。例如定时或批量生成可用 GitHub Actions,无缝集成与免费额度适合轻量场景;网站实时对话可采用 Next.js API,更互动。

MCP 的目标是标准化 AI 使用工具的方式,上述方案仅是起点。你可以在系统中添加更多 MCP 服务器,实现更复杂的集成。例如接入 Git MCP 服务器用于仓库浏览,或 Google Drive 服务器检索参考资料。事实上,开源 MCP 服务器已覆盖许多平台(GitHub、GitLab、Slack 等,GitHub - modelcontextprotocol/servers: Model Context Protocol Servers (opens in a new tab)),AI 代理可通过最小化配置即刻接入。我们的定制实现其实就是在模拟 MCP GitHub 服务端,赋能 AI 实现仓库操作(如写入 commit)。

多步交互: 当前设计为“单 prompt —— 单生成”。实际可能需要更互动的流程(如和 AI 头脑风暴几轮再最终发布)。可以基于 Next.js API 开发界面,实现类似聊天,满意后“点击发布”把最终答案发给 generate-post 端点。这样 AI 可综合你的反馈(多轮对话),生成更符合需求的内容。

内容审核: 正式发布建议加审核环节。GitHub Actions 方案完全可以改为自动创建 Pull Request 让你人工审阅再合并,也可在 AI 输出后集成内容审查 API,减少不当内容上线风险。

融入 MCP 生态: 虽然我们的方案直接调用 API,但理念与 MCP 完全一致。如果你的 AI 助理平台原生支持 MCP(如 Claude Desktop 或 IDE 集成),可注册类似“发布博客文章”工具,让 AI 代理动态调用工具接口。MCP 定义了工具、参数、以 JSON-RPC 协议调用。更深入的 MCP 服务/客户端开发可参考其官方文档及实例(Model Context Protocol (MCP) Explained (opens in a new tab)Build and deploy Remote Model Context Protocol (MCP) servers to Cloudflare (opens in a new tab))。

📚

总结

我们搭建了一个把 AI 模型与内容仓库连接起来的系统——让 AI 成为博客内容创作者。既展示了依赖 GitHub 基础设施的无服务器自动化流程,也提供了基于 Next.js 的交互式消息/服务端,实现按需自动化。两种方式都实现了同一目标:AI 生成 MDX 文章并通过 Git commit 自动发布上线。

这充分展现了将集成能力打包为“AI 工具”的强大威力。AI 获得触发实际操作的能力(如博客写入),能力边界得以外扩。MCP 的愿景正是——为 AI 和外部系统打造统一标准接口,而我们的博客生成器即是该愿景的现实实践。借助标准协议,AI 代理可同理接入十余种甚至更多服务。不只是写博客,未来还可以更新数据库、安排推文,而这背后都用的是同一个统一接口(Model Context Protocol (MCP) Explained (opens in a new tab))。

你可以按照自身需求自由改造本项目:调整提词、完善异常处理、集成更多 LLMs。祝你用 AI 博客协作,事半功倍!

参考资料:

  1. Anthropic, Introducing the Model Context Protocol (MCP) — 介绍 AI 助手对接外部系统的开放标准(Model Context Protocol (MCP) Explained (opens in a new tab))。
  2. Cloudflare, Bringing MCP to the masses — 讲解 AI 代理如何通过 MCP 工具实现自动文章发布(Build and deploy Remote Model Context Protocol (MCP) servers to Cloudflare (opens in a new tab))。
  3. Model Context Protocol Open-Source Servers — 提供 GitHub 服务端(文件、API仓库集成等)(GitHub - modelcontextprotocol/servers: Model Context Protocol Servers (opens in a new tab))。
📚