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トークンの生成

このコマンドを2回実行します:

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

2つの異なる出力を使用します:

  • PORTFOLIO_MCP_TOKEN — Authorizationヘッダーを送信できるクライアント用のベアラートークン。

  • PORTFOLIO_MCP_URL_TOKEN — 静的なカスタムヘッダーの設定が不便なクライアント用の使い捨てトークン。

両方のトークンは少なくとも32文字以上で、互いに異なる必要があります。

絶対にSupabaseのシークレットキーをMCPトークンとして使用しないでください。SUPABASE_SECRET_KEYをChatGPTコネクタのURLに入れないでください。

Supabase認証

最新のSupabaseサーバーサイドシークレットキーを推奨します:

sb_secret_...

このサービスは、最新のsb_secret_*キーをSupabaseのapikeyヘッダーでのみ送信します。これらのキーは不透明な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ヘルスチェックを公開します。

このサービスを、Railway、Render、Fly.io、Kubernetes、VPSなど、長時間実行されるNode HTTPプロセス/コンテナをサポートするプラットフォームにデプロイしてください。現在の実装はVercelのサーバーレス関数エントリポイントではありません。

ChatGPTの場合、デプロイされたMCPエンドポイントはHTTPS経由で到達可能である必要があります。例:

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

HTTP認証

リクエストヘッダーをサポートするクライアントは次を使用してください:

Authorization: Bearer <PORTFOLIO_MCP_TOKEN>

静的なベアラーヘッダーの設定が不便なクライアントの場合、エンドポイントは次も受け入れます:

https://YOUR_MCP_HOST/mcp?token=YOUR_PORTFOLIO_MCP_URL_TOKEN

クエリ文字列の認証情報はインフラストラクチャ/アクセスログに表示される可能性があります。PORTFOLIO_MCP_URL_TOKENは使い捨てとして扱い、漏洩した場合はローテーションしてください。MCPクライアントがサポートする場合はベアラー認証を優先してください。

ChatGPTへの接続

ChatGPTはリモートMCPサーバーに接続します。localhostのみで実行されているサーバーには接続しません。

このリポジトリの更新時点(2026年8月)では、OpenAIはChatGPT Business、Enterprise、EduワークスペースのWeb向けに、書き込み/変更アクションを含む完全なカスタムMCPサポートを文書化しています。可用性は変更される可能性があるため、UIが異なる場合は現在のOpenAI ChatGPTカスタムアプリ/MCPドキュメントを確認してください。

ChatGPTワークスペースでカスタムMCPアプリ/コネクタが公開されている場合:

  1. このリポジトリをHTTPSエンドポイントにデプロイします。

  2. すべてのサーバー環境変数をデプロイメントプラットフォームで設定します。

  3. ChatGPTで、ワークスペースの権限に応じて開発者モード/カスタムアプリを有効にします。

  4. カスタムMCPアプリを作成します。

  5. ChatGPTのフォームに静的なカスタムベアラーヘッダーフィールドがない場合は、URLトークンエンドポイントを使用します:

    https://YOUR_MCP_HOST/mcp?token=YOUR_PORTFOLIO_MCP_URL_TOKEN
  6. ChatGPTでそのコネクタにNo Authを選択します。認証はこのサーバーによってURLトークンを介して引き続き強制されます。

  7. Scan Toolsを選択します。サーバーは上記の記事ツールと画像ツールを公開するはずです。

  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サーバーは、ブラウザのCORSプリフライトでMcp-Protocol-Version、Mcp-Method、Mcp-Name、Mcp-Session-Idなどの現在のMCPリクエストヘッダーをサポートします。

デフォルトの*は互換性を最大化しますが、トークン認証は必須のままです。サービスを呼び出す必要がある正確なブラウザオリジンがわかっている場合は、リストを絞り込んでください。

リクエストと画像の制限

デフォルトのHTTP MCPリクエスト上限は5 MiBです:

MCP_MAX_REQUEST_BYTES=5242880

これは、base64が約3分の1のオーバーヘッドとJSONフレーミングを追加するため、3 MiBのデコード済み画像制限よりも意図的に大きくなっています。

受け入れられる画像コンテンツタイプ:

  • 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ベアラートークンとしては送信されません。

  • Next.jsポートフォリオは、公開された読み取り専用のSupabaseアクセスモデルを維持します。

  • HTTP MCPリクエストにはベアラートークンまたはURLトークンが必要です。

  • MCPトークンは強力で、互いに異なる必要があります。

  • トークン比較にはタイミングセーフな等価性を使用します。

  • 汎用のSQL/クエリ実行は公開されません。

  • スラッグ、公開日、画像パス、画像タイプ、base64ペイロード、画像サイズ、HTTPリクエストサイズが検証されます。

  • 上書き、非公開、置換、削除ツールは、リスクに適したMCP注釈を使用します。

  • シャットダウンは新しいトラフィックの受け入れを停止し、MCPリソースが閉じられる前にアクティブなリクエストに制限付きのドレイン期間を与えます。

  • シークレットはGitHubにコミットしてはなりません。

MCPプロトコル

HTTPサーバーは安定版のMCP TypeScript SDK v2を使用し、/mcpでStreamable HTTPを公開します。ローカルMCPホスト用にstdioエントリポイントが含まれています。

リモート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.
    -