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-VersionMcp-MethodMcp-NameMcp-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 安装精确的依赖图。

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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.
    14
    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.
  • A
    license
    A
    quality
    F
    maintenance
    Enables AI tools to programmatically manage Substack content, including creating drafts, publishing posts, and posting to Substack Notes. It supports image uploads, live blogging, and document formatting compatible with Substack's ProseMirror editor.
    11
    8
    MIT

View all related MCP servers

Related MCP Connectors

  • Publish and manage articles, series, comments, reactions, newsletters and blog analytics.

  • Manage Supabase projects end to end across database, auth, storage, realtime, and migrations. Moni…

  • Manage Appwrite projects, databases, auth, storage, functions, and messaging; search Appwrite docs

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/salman0butt/portfolio-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server