Skip to main content
Glama

Portfolio MCP

用于管理 Salman Butt 作品集所依托的 Supabase 工程博客的独立 Model Context Protocol (MCP) 服务器。

公开的 Next.js 作品集保持只读。本服务负责博客管理这一特权操作面,并将 Supabase 密钥保留在前端部署之外。

提供的功能

文章工具

  • list_blog_posts

  • get_blog_post

  • create_blog_post

  • update_blog_post

  • publish_blog_post

  • unpublish_blog_post

  • delete_blog_post

图片工具

  • upload_blog_image

  • replace_blog_image

  • delete_blog_image

  • get_blog_image_url

该服务器不暴露任意 SQL 或不受限制的 Supabase 访问权限。

Related MCP server: Self-Hosted Supabase MCP Server

架构

ChatGPT / remote MCP host / local MCP client
             |
             | Streamable HTTP or stdio
             v
      portfolio-mcp service
             |
             +--> MCP token authentication (HTTP)
             |
             +--> MCP SDK v2 tool layer
             |
             +--> Supabase REST: public.blogs
             |
             +--> Supabase Storage: blog-images

Public visitors
      |
      v
Next.js portfolio --> Supabase anon read-only access

环境要求

  • Node.js 22+

  • 一个包含作品集 blogs 表的 Supabase 项目

  • 一个具有博客表和 Storage 存储桶访问权限的服务端 Supabase 密钥

  • 对于 ChatGPT:本 MCP 服务器需部署为可通过 HTTPS 访问的远程服务

设置

git clone https://github.com/salman0butt/portfolio-mcp.git
cd portfolio-mcp
npm ci
cp .env.example .env

配置 .env:

SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_SECRET_KEY=sb_secret_REPLACE_ME
SUPABASE_BLOG_BUCKET=blog-images

PORTFOLIO_MCP_TOKEN=replace-with-long-random-bearer-token
PORTFOLIO_MCP_URL_TOKEN=replace-with-different-long-random-url-token

PORT=3000
HOST=0.0.0.0
MCP_ALLOWED_ORIGINS=*
MCP_MAX_REQUEST_BYTES=5242880

HTTP 和 stdio 入口在存在本地 .env 文件时会自动加载。部署平台注入的环境变量继续正常工作。

生成 MCP 令牌

运行此命令两次:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

使用两个不同的输出:

  • PORTFOLIO_MCP_TOKEN — 供能够发送 Authorization 请求头的客户端使用的 bearer 令牌。

  • PORTFOLIO_MCP_URL_TOKEN — 供无法方便配置静态自定义请求头的客户端使用的一次性令牌。

两个令牌都必须至少 32 个字符,且必须不同。

切勿将 Supabase 密钥用作 MCP 令牌。切勿将 SUPABASE_SECRET_KEY 放入 ChatGPT 连接器 URL 中。

Supabase 身份验证

优先使用现代的 Supabase 服务端密钥:

sb_secret_...

本服务仅在 Supabase apikey 请求头中发送现代的 sb_secret_* 密钥。这些密钥是不透明的 API 密钥,不会作为 Authorization: Bearer JWT 发送。

基于 JWT 的旧版 service_role 密钥仍受支持以兼容迁移,但新部署应使用 sb_secret_*。

开发

远程 HTTP 模式:

npm run dev:http

MCP 端点:

http://localhost:3000/mcp

健康检查:

http://localhost:3000/healthz

本地 stdio 模式:

npm run dev:stdio

stdio 模式不使用 HTTP MCP 令牌,因为访问由启动服务器的本地进程控制。

生产部署

直接构建并运行:

npm run build
npm start

或使用 Docker:

docker build -t portfolio-mcp .
docker run --rm -p 3000:3000 --env-file .env portfolio-mcp

容器从 package-lock.json 安装依赖,以非 root 的 node 用户运行,并暴露 /healthz Docker 健康检查。

将此服务部署到支持长时间运行的 Node HTTP 进程/容器的平台,例如 Railway、Render、Fly.io、Kubernetes 或 VPS。当前实现不是 Vercel serverless 函数入口。

对于 ChatGPT,部署的 MCP 端点必须可通过 HTTPS 访问,例如:

https://portfolio-mcp.example.com/mcp

HTTP 身份验证

支持请求头的客户端应使用:

Authorization: Bearer <PORTFOLIO_MCP_TOKEN>

对于不方便配置静态 bearer 请求头的客户端,端点也接受:

https://YOUR_MCP_HOST/mcp?token=YOUR_PORTFOLIO_MCP_URL_TOKEN

查询字符串凭据可能出现在基础设施/访问日志中。请将 PORTFOLIO_MCP_URL_TOKEN 视为一次性令牌,一旦泄露即轮换。当 MCP 客户端支持时,优先使用 bearer 身份验证。

连接到 ChatGPT

ChatGPT 连接到远程 MCP 服务器,而不是仅运行在 localhost 上的服务器。

在本仓库更新时(2026 年 8 月),OpenAI 文档记载了完整的自定义 MCP 支持,包括 ChatGPT Business、Enterprise 和 Edu 工作区在网页端的写入/修改操作。可用性可能发生变化,因此如果你的界面不同,请查阅当前的 OpenAI ChatGPT 自定义应用/MCP 文档。

当你的 ChatGPT 工作区提供自定义 MCP 应用/连接器时:

  1. 将此仓库部署到 HTTPS 端点。

  2. 在部署平台上配置所有服务器环境变量。

  3. 在 ChatGPT 中,根据你的工作区权限启用开发者模式/自定义应用。

  4. 创建自定义 MCP 应用。

  5. 如果 ChatGPT 表单不提供静态自定义 bearer 请求头字段,请使用 URL 令牌端点:

    https://YOUR_MCP_HOST/mcp?token=YOUR_PORTFOLIO_MCP_URL_TOKEN
  6. 在 ChatGPT 中为该连接器选择无身份验证。本服务器仍通过 URL 令牌强制执行身份验证。

  7. 选择扫描工具。服务器应暴露上述文章和图片工具。

  8. 在新聊天中添加/启用该应用,并在测试写入操作之前先测试诸如 list_blog_posts 之类的读取操作。

  9. ChatGPT 可能会根据工作区/应用权限和工具注解要求对写入/破坏性操作进行确认。

请勿将 SUPABASE_SECRET_KEY 输入 ChatGPT。ChatGPT 只需要远程 MCP 端点(以及在此 URL 令牌设置下的一次性 MCP URL 令牌)。

推荐的 ChatGPT 测试顺序

连接器成功扫描后:

List my portfolio blog posts.

然后:

Create a draft blog post titled "MCP Connection Test". Do not publish it.

再验证:

Get the MCP Connection Test draft and show me its metadata.

最后,仅当你明确打算删除测试草稿时才将其删除。

CORS / 来源

MCP_ALLOWED_ORIGINS 接受逗号分隔的列表:

MCP_ALLOWED_ORIGINS=https://example.com,https://another-client.example

HTTP 服务器支持当前的 MCP 请求头,包括浏览器 CORS 预检中的 Mcp-Protocol-Version、Mcp-Method、Mcp-Name 和 Mcp-Session-Id。

默认值 * 在令牌身份验证仍然强制的前提下最大化兼容性。当你确切知道必须调用本服务的浏览器来源时,请收紧该列表。

请求和图片限制

默认的 HTTP MCP 请求上限为 5 MiB:

MCP_MAX_REQUEST_BYTES=5242880

这有意大于 3 MiB 解码后图片限制,因为 base64 会增加约三分之一的开销,再加上 JSON 框架。

接受的图片内容类型:

  • PNG

  • JPEG

  • WebP

  • GIF

  • AVIF

存储路径会被规范化,并拒绝诸如 ../ 之类的路径穿越。图片负载必须包含有效的 base64。

推荐的对象路径:

senior-software-engineer/cover.webp
production-rag-systems/architecture.webp
nextjs-at-scale/performance.webp

删除博客文章不会自动删除其图片。这避免了意外删除可能被共享或复用的媒体。

博客工作流

推荐的发布流程:

  1. 将文章创建为草稿。

  2. 如有需要,上传封面/示意图。

  3. 使用返回的公开图片 URL 更新草稿。

  4. 检查标题、摘要、Markdown、分类、标签和发布日期。

  5. 使用 publish_blog_post 发布。

  6. 之后根据需要更新或取消发布。

  7. 仅在明确意图时删除文章或图片。

published_at 接受 ISO 8601 日期或日期时间,例如:

2026-08-25
2026-08-25T12:00:00+05:00

安全模型

  • Supabase 密钥凭据仅存在于服务端。

  • 现代的 sb_secret_* 密钥作为 Supabase API 密钥发送,而非 JWT bearer 令牌。

  • Next.js 作品集保持其公开的只读 Supabase 访问模型。

  • HTTP MCP 请求需要 bearer 令牌或 URL 令牌。

  • MCP 令牌必须强且互不相同。

  • 令牌比较使用恒定时间相等性检查。

  • 不暴露通用的 SQL/查询执行器。

  • 对 slug、发布日期、图片路径、图片类型、base64 负载、图片大小和 HTTP 请求大小进行验证。

  • 覆盖、取消发布、替换和删除工具使用与风险相适应的 MCP 注解。

  • 关闭时停止接受新流量,并在关闭 MCP 资源之前为活动请求提供有界排空期。

  • 密钥绝不能提交到 GitHub。

MCP 协议

HTTP 服务器使用稳定的 MCP TypeScript SDK v2,并在 /mcp 暴露 Streamable HTTP。包含一个 stdio 入口,供本地 MCP 主机使用。

远程 HTTP 包装器同时支持现代 MCP 流量和 SDK 的无状态旧版回退,以最大化客户端兼容性。

验证

运行 CI 使用的相同验证:

npm run check

这将运行:

  • 严格的 TypeScript 类型检查

  • 运行时回归测试

  • 生产 TypeScript 构建

运行时测试涵盖 Supabase 密钥处理、HTTP 身份验证、CORS、请求限制、环境加载、令牌验证,以及通过远程 HTTP 适配器发出的真实 MCP tools/list 请求。

GitHub Actions 使用提交的锁文件通过 npm ci 安装精确的依赖图。

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Manage your Ghost blog content directly from Claude, Cursor, or any MCP-compatible client, allowing you to create, edit, search, and delete posts with support for tag management and analytics.
    11 npm
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables developers to interact with self-hosted Supabase instances, providing database introspection, migration management, auth user operations, storage management, and TypeScript type generation directly from MCP-compatible development environments.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables validation and fixing of Dev.to markdown articles locally, and creation of unpublished drafts via the Forem API.
    -