portfolio-mcp

portfolio-mcp

Provides tools to manage a Supabase-backed engineering blog, covering blog post lifecycle (create, update, publish, unpublish, delete) and image operations (upload, replace, delete, get URL).

Category
访问服务器

README

Portfolio MCP

Standalone Model Context Protocol (MCP) server for managing the Supabase-backed engineering blog used by Salman Butt's portfolio.

The public Next.js portfolio remains read-only. This service owns the privileged blog-management surface and keeps the Supabase secret key outside the frontend deployment.

Tools

Article tools:

  • list_blog_posts
  • get_blog_post
  • create_blog_post
  • update_blog_post
  • publish_blog_post
  • unpublish_blog_post
  • delete_blog_post

Image tools:

  • upload_blog_image
  • replace_blog_image
  • delete_blog_image
  • get_blog_image_url

The server does not expose arbitrary SQL or unrestricted Supabase access.

Deployment modes

This repository supports two production deployment models:

  1. Vercel Functions — explicit api/* Web handlers with rewrites for /, /healthz, /readyz, and /mcp.
  2. Long-running Node/Docker — src/http.ts for Railway, Render, Fly.io, Kubernetes, a VPS, or Docker.

Both modes use the same MCP SDK v2 tool definitions and Supabase access layer.

Requirements

  • Node.js 22+
  • A Supabase project containing the portfolio blogs table
  • A server-side Supabase secret key with access to the blog table and Storage bucket
  • For ChatGPT: an HTTPS-accessible remote deployment

Environment variables

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

MCP_ALLOWED_ORIGINS=*
MCP_MAX_REQUEST_BYTES=4000000

For the standalone Node server you may additionally set:

PORT=3000
HOST=0.0.0.0

Do not manually set PORT or HOST for the Vercel Function deployment.

Generate the two MCP tokens separately:

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

Run it twice and use different values. Both tokens must be at least 32 characters.

Never use SUPABASE_SECRET_KEY as an MCP token and never place the Supabase secret in a ChatGPT connector URL.

Supabase authentication

Prefer the modern Supabase server-side secret key:

sb_secret_...

Modern sb_secret_* keys are sent only in the Supabase apikey header. They are opaque API keys and are not sent as JWT bearer tokens. Legacy JWT-based service_role keys remain supported for migration compatibility.

Vercel deployment

The Vercel deployment uses explicit Function entrypoints:

api/index.ts    -> /
api/healthz.ts  -> /healthz
api/readyz.ts   -> /readyz
api/mcp.ts      -> /mcp

vercel.json contains the rewrites and MCP function duration configuration.

Import the GitHub repository into Vercel, select the Other framework preset if Vercel asks for one, configure the environment variables above for Production, and deploy the latest main branch.

Do not configure a custom Output Directory. The repository's vercel.json and api/* Functions define the deployment surface.

Vercel verification

After deployment, test in this order:

https://YOUR_HOST/
https://YOUR_HOST/healthz
https://YOUR_HOST/readyz

/ should return a service document.

/healthz is a liveness check and should return HTTP 200 even if Supabase/MCP environment configuration is incomplete:

{
  "ok": true,
  "service": "portfolio-mcp",
  "runtime": "vercel-function",
  "transport": "streamable-http"
}

/readyz validates the Supabase and MCP environment configuration. A valid deployment returns HTTP 200:

{
  "ready": true,
  "service": "portfolio-mcp"
}

An invalid configuration returns HTTP 503 with a safe diagnostic, for example:

{
  "ready": false,
  "service": "portfolio-mcp",
  "error": "SUPABASE_URL is not configured."
}

/mcp without authentication should return HTTP 401 when configuration is valid.

For Vercel, keep MCP_MAX_REQUEST_BYTES at or below the platform request-body limit. 4000000 is the recommended value for this deployment. Large media should not be transported as large base64 MCP requests.

Standalone Node / Docker deployment

Install and run locally:

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

Production Node process:

npm run build
npm start

Docker:

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

The container installs dependencies from package-lock.json, runs as the non-root node user, and exposes a /healthz Docker health check.

HTTP authentication

Clients that support request headers should use:

Authorization: Bearer <PORTFOLIO_MCP_TOKEN>

For clients where a static bearer header is inconvenient, the server also accepts the disposable URL token:

https://YOUR_MCP_HOST/mcp?token=YOUR_PORTFOLIO_MCP_URL_TOKEN

Query-string credentials can appear in infrastructure/access logs. Treat PORTFOLIO_MCP_URL_TOKEN as disposable and rotate it if exposed. Prefer bearer authentication whenever possible.

Connect to ChatGPT

ChatGPT connects to a remote HTTPS MCP server, not localhost.

For a ChatGPT connector UI that does not provide a static custom bearer-header field, use:

https://YOUR_MCP_HOST/mcp?token=YOUR_PORTFOLIO_MCP_URL_TOKEN

Then choose No Auth in ChatGPT. Authentication is still enforced by this MCP server through the URL token.

Use Scan Tools. The server should expose the article and image tools listed above.

Recommended test sequence:

List my portfolio blog posts.

Then:

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

Verify it, then delete the test draft only when you explicitly intend to remove it.

Do not enter SUPABASE_SECRET_KEY into ChatGPT.

CORS

MCP_ALLOWED_ORIGINS accepts a comma-separated list:

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

The remote handlers support current MCP request headers including Mcp-Protocol-Version, Mcp-Method, Mcp-Name, and Mcp-Session-Id in browser CORS preflights.

The default * maximizes compatibility while token authentication remains mandatory.

Image constraints

Accepted content types:

  • PNG
  • JPEG
  • WebP
  • GIF
  • AVIF

Decoded image uploads are limited to 3 MiB. Storage paths are normalized and reject traversal such as ../, and image payloads must contain valid base64.

Recommended object paths:

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

Deleting a blog article does not automatically delete its images.

Security model

  • Supabase secret credentials remain server-side.
  • Modern sb_secret_* keys are sent as Supabase API keys, not JWT bearer tokens.
  • The public Next.js portfolio remains read-only.
  • HTTP MCP requests require a bearer token or URL token.
  • MCP tokens must be strong and distinct.
  • Token comparison uses timing-safe equality.
  • No generic SQL/query executor is exposed.
  • Slugs, publication dates, image paths, image types, base64 payloads, image sizes, and HTTP request sizes are validated.
  • Overwrite, unpublish, replace, and delete tools use risk-appropriate MCP annotations.
  • Secrets must never be committed to GitHub.

Validation

Run the same application validation used by CI:

npm run check

This runs strict TypeScript typechecking, runtime tests, and the production TypeScript build. The TypeScript project includes both src/**/*.ts and api/**/*.ts, so Vercel Function entrypoints are validated in CI.

GitHub Actions also builds the production Docker image and installs the exact dependency graph with npm ci.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选