portfolio-mcp
Provides tools for managing blog posts and images stored in Supabase, including creating, updating, publishing, unpublishing, deleting posts, and uploading, replacing, deleting, and retrieving image URLs from Supabase Storage.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@portfolio-mcpcreate a draft post about MCP servers"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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_postsget_blog_postcreate_blog_postupdate_blog_postpublish_blog_postunpublish_blog_postdelete_blog_post
Image tools:
upload_blog_imagereplace_blog_imagedelete_blog_imageget_blog_image_url
The server does not expose arbitrary SQL or unrestricted Supabase access.
Related MCP server: Self-Hosted Supabase MCP Server
Deployment modes
This repository supports two production deployment models:
Vercel Functions — explicit
api/*Web handlers with rewrites for/,/healthz,/readyz, and/mcp.Long-running Node/Docker —
src/http.tsfor 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
blogstableA 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=4000000For the standalone Node server you may additionally set:
PORT=3000
HOST=0.0.0.0Do 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 -> /mcpvercel.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:httpProduction Node process:
npm run build
npm startDocker:
docker build -t portfolio-mcp .
docker run --rm -p 3000:3000 --env-file .env portfolio-mcpThe 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_TOKENQuery-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_TOKENThen 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.exampleThe 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.webpDeleting 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 checkThis 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.
This server cannot be installed
Maintenance
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
- AlicenseNot gradedqualityDmaintenanceManage 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.23MIT
- FlicenseNot gradedqualityDmaintenanceEnables 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.
- AlicenseAqualityFmaintenanceEnables 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.119MIT
- AlicenseNot gradedqualityDmaintenanceManages Supabase projects, databases, edge functions, secrets, and branches across organizations using the Supabase Management API with 24 tools.MIT
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
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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