Skip to main content
Glama

Портфолио MCP

Автономный сервер Model Context Protocol (MCP) для управления блогом на базе Supabase, используемым в портфолио Салмана Батта.

Публичное 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+

  • Проект Supabase, содержащий таблицу blogs портфолио

  • Серверный секретный ключ Supabase с доступом к таблице блога и корзине Storage

  • Для ChatGPT: доступное по HTTPS удалённое развёртывание этого MCP-сервера

Настройка

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 — bearer-токен для клиентов, которые могут отправлять заголовки Authorization.

  • PORTFOLIO_MCP_URL_TOKEN — одноразовый токен для клиентов, где настройка статического пользовательского заголовка неудобна.

Оба токена должны быть не менее 32 символов и должны различаться.

Никогда не используйте секретный ключ Supabase в качестве MCP-токена. Никогда не помещайте SUPABASE_SECRET_KEY в URL коннектора ChatGPT.

Аутентификация Supabase

Предпочитайте современный серверный секретный ключ Supabase:

sb_secret_...

Сервис отправляет современные ключи sb_secret_* только в заголовке apikey Supabase. Эти ключи являются непрозрачными ключами API и не отправляются как JWT в Authorization: Bearer.

Устаревшие ключи service_role на основе JWT остаются поддерживаемыми для совместимости при миграции, но новые развёртывания должны использовать 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, работает от непривилегированного пользователя node и предоставляет Docker health check /healthz.

Разверните этот сервис на платформе, поддерживающей долго работающий Node HTTP-процесс/контейнер, например Railway, Render, Fly.io, Kubernetes или VPS. Текущая реализация не является точкой входа для serverless-функций Vercel.

Для 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 как к одноразовому и ротируйте его при раскрытии. Предпочитайте bearer-аутентификацию, когда MCP-клиент её поддерживает.

Подключение к ChatGPT

ChatGPT подключается к удалённым MCP-серверам, а не к серверу, работающему только на localhost.

На момент обновления этого репозитория (август 2026) OpenAI документирует полную поддержку пользовательских MCP, включая действия записи/изменения для рабочих пространств ChatGPT Business, Enterprise и Edu в вебе. Доступность может меняться, поэтому проверяйте текущую документацию OpenAI по пользовательским приложениям/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. Выберите No Auth в ChatGPT для этого коннектора. Аутентификация по-прежнему обеспечивается этим сервером через 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-сервер поддерживает современные заголовки MCP-запросов, включая Mcp-Protocol-Version, Mcp-Method, Mcp-Name и Mcp-Session-Id в CORS-предварительных запросах браузера.

Значение по умолчанию * максимизирует совместимость, при этом токен-аутентификация остаётся обязательной. Ужесточите список, когда вы знаете точные источники браузера, которые должны вызывать сервис.

Лимиты запросов и изображений

Максимальный размер HTTP MCP-запроса по умолчанию — 5 МиБ:

MCP_MAX_REQUEST_BYTES=5242880

Это намеренно больше, чем лимит декодированного изображения в 3 МиБ, потому что 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_* отправляются как ключи API Supabase, а не как JWT bearer-токены.

  • Next.js-портфолио сохраняет свою публичную модель доступа к Supabase только для чтения.

  • HTTP MCP-запросы требуют bearer-токен или URL-токен.

  • MCP-токены должны быть надёжными и различаться.

  • Сравнение токенов использует устойчивое к времени равенство.

  • Не предоставляется общий исполнитель SQL/запросов.

  • Проверяются слаги, даты публикации, пути изображений, типы изображений, полезные нагрузки base64, размеры изображений и размеры HTTP-запросов.

  • Инструменты перезаписи, снятия с публикации, замены и удаления используют аннотации MCP, соответствующие уровню риска.

  • Завершение работы прекращает приём нового трафика и даёт активным запросам ограниченный период дренажа перед закрытием ресурсов MCP.

  • Секреты никогда не должны попадать в GitHub.

Протокол MCP

HTTP-сервер использует стабильный MCP TypeScript SDK v2 и предоставляет Streamable HTTP на /mcp. Включена точка входа stdio для локальных MCP-хостов.

Удалённая HTTP-обёртка поддерживает как современный MCP-трафик, так и устаревший без сохранения состояния fallback SDK для максимальной совместимости с клиентами.

Проверка

Запустите ту же проверку, что используется в CI:

npm run check

Это выполняет:

  • строгую проверку типов TypeScript

  • регрессионные тесты времени выполнения

  • производственную сборку TypeScript

Тесты времени выполнения охватывают обработку секретных ключей Supabase, HTTP-аутентификацию, CORS, лимиты запросов, загрузку окружения, проверку токенов и реальный запрос tools/list MCP через удалённый HTTP-адаптер.

GitHub Actions устанавливает точный граф зависимостей с помощью npm ci из зафиксированного lockfile.

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.
    -