Skip to main content
Glama
HamzaOuadid

mcp-starter-template

by HamzaOuadid

mcp-starter-template

Эталонный каркас MCP-сервера, реализующий паттерны безопасности, которые большинство публичных примеров MCP пропускают: сквозная аутентификация пользователя (никогда не общий служебный аккаунт), инструменты, доступные только для чтения по умолчанию с явным согласием на запись, режим пробного запуска (dry-run) для пишущих инструментов и лимит расходов/частоты на сессию со структурированными отказами вместо молчаливого бездействия или падений. Каждое защитное ограничение подкреплено автоматизированным тестом, а не только docstring.

Создан как элемент портфолио после аудита и удаления мёртвых обработчиков инструментов из продакшен-форка MCP, в котором не было ни одного из этих защитных механизмов.

Родственный проект — mcp-issue-tracker — использует ту же самую архитектуру безопасности (сквозная аутентификация, записи через разрешённый список, dry-run, ограничение частоты, журнал аудита), применённую к реальному локальному домену трекера задач — тот же паттерн, доказанный дважды, а не один раз.

Почему это существует

Большинство публичных примеров MCP-серверов подключают ассистента напрямую к сервисному аккаунту с полными правами и без каких-либо ограждений. Именно так ассистент, отвечающий на разумный вопрос, в итоге раскрывает данные, которые спрашивающий не должен видеть, или молча выполняет запись, которую никто не одобрял. Этот репозиторий — пример более безопасного подхода по умолчанию, достаточно компактного, чтобы прочитать его целиком за один присест.

Защитные механизмы и что каждый из них предотвращает

Защитный механизм

Где

Что предотвращает

Сквозная аутентификация

auth.py, identity.py

Выполнение вызова инструмента под общим/универсальным учётным данным. Каждый вызов определяет личность конкретного вызывающего, и все последующие проверки используют именно эту личность, а не учётную запись администратора или сервисного аккаунта. Предотвращает ситуацию, когда «ассистент видит всё, что видит сервисный аккаунт, независимо от того, кто спросил».

Только чтение по умолчанию + явный список разрешённых для записи

registry.py, server.yaml

Выполнение недавно добавленного или неправильно сконфигурированного пишущего инструмента до того, как кто-то явно проверил и включил его. Инструмент может быть вызван как записывающий только если его имя есть в allowed_write_tools; всё остальное инертно. Предотвращает ситуацию: «мы добавили инструмент и забыли, что он может удалять данные».

Режим dry-run

tools/tickets.py, dryrun.py, server.py

Срабатывание реального побочного эффекта пишущего инструмента на нижестоящем сервисе, пока оператор ещё проверяет поведение. В режиме dry-run реальный API-клиент не вызывается вообще — это проверяется в тестах через шпионаж за самим методом клиента, а не только через изучение ответа. Предотвращает ситуацию: «мы тестировали в проде, потому что dry-run втайне всё равно писал».

Лимит частоты/расходов на сессию

limiter.py

Неограниченный или вышедший из-под контроля клиент, сжигающий бюджет или долбящий нижестоящий API. Как только оконный бюджет сессии (вызовы или единицы стоимости) исчерпан, каждый последующий вызов в этом окне отклоняется со структурированной ошибкой и retry_after, а не только тот вызов, который превысил лимит. Предотвращает ситуацию: «баг в клиенте превратился в безграничный счёт за API».

Структурированный журнал аудита

audit.py

Невозможность восстановить инцидент безопасности после факта. Каждый вызов — разрешённый или отклонённый, чтение или запись, dry-run или реальный — записывается как одна запись в формате JSON-lines и одна строка в SQLite: временная метка, сессия, пользователь, инструмент, чтение/запись, флаг dry-run, флаг разрешения, задержка. Предотвращает ситуацию: «мы на самом деле не знаем, что произошло».

Архитектура

                         ┌─────────────────────────────┐
   MCP client  ───────▶  │      transport adapter      │
 (stdio / HTTP)          │  mcp_app.py  /  http_app.py  │
                         └──────────────┬───────────────┘
                                        │ token, session_id, tool_name, args
                                        ▼
                         ┌─────────────────────────────┐
                         │      MCPStarterServer        │   server.py — single
                         │        .call_tool()          │   choke point every
                         └──────────────┬───────────────┘   call passes through
                    1) resolve tool ────┤
                    2) authenticate ────┤──▶ AuthMiddleware ──▶ MockIdentityProvider
                    3) allowlist check ─┤──▶ ToolRegistry
                    4) rate/spend check ┤──▶ SessionLimiter
                    5) execute ─────────┤──▶ tool handler (search_docs / create_ticket)
                    6) audit log ───────┴──▶ AuditLogger ──▶ audit.jsonl + SQLite
  • Промежуточный слой аутентификации (auth.py) преобразует bearer-токен в объект User с помощью MockIdentityProvider (identity.py) — с явной пометкой «только для разработки», с двумя разными тестовыми пользователями (alice/engineering, bob/sales) и администратором. Отсутствующие или нераспознанные токены отклоняются; запасной личности нет.

  • Реестр инструментов (registry.py) — единственное место, где хранится классификация каждого инструмента по чтению/записи; при регистрации она сверяется с секцией tools: в server.yaml — расхождение между тем, что объявляет код, и тем, что говорит конфиг, не даст серверу запуститься. Пишущий инструмент становится вызываемым только после того, как его имя попадает в allowed_write_tools; при этом он остаётся видимым в list_tools() в любом случае, чтобы ревьюер мог видеть всю поверхность, а не только то, что сейчас включено.

  • Обёртка dry-run: обработчик каждого пишущего инструмента принимает dry_run: bool, и для create_ticket при значении true никогда не обращается к TicketSystemClient.create (заглушке нижестоящего API) — вместо этого возвращает синтетический идентификатор DRYRUN-.... dryrun.py форматирует строку аудита [DRY RUN].

  • Лимитер частоты/расходов (limiter.py) — счётчик фиксированного окна для каждого session_id: calls_per_min и cost_per_session (стоимость инструмента берётся из реестра) сбрасываются вместе каждые window_seconds. Отклонённые вызовы сами по себе не потребляют бюджет.

  • Журнал аудита (audit.py) пишет JSON-lines в файл и дублирует каждую запись в таблицу SQLite audit_log, соответствующую модели данных из спецификации, так что его можно читать как текст или запрашивать через SQL.

Два транспорта оборачивают одно и то же ядро MCPStarterServer:

  • mcp_app.py — настоящий MCP stdio-сервер на официальном MCP Python SDK (FastMCP). Поскольку stdio — это единый локальный процесс без заголовков запроса, token и session_id передаются как явные аргументы инструментов — распространённое документированное упрощение для локальных/dev MCP-серверов. Именно с ним будет общаться реальный MCP-клиент (Claude Desktop, CLI mcp и т.п.).

  • http_app.py — HTTP-транспорт на FastAPI, где токен берётся из настоящего заголовка Authorization: Bearer <token>, а сессия — из X-Session-Id, то есть в форме, которую использовал бы настоящий мультитенантный деплой.

Примеры инструментов

  • search_docs(query) -> list[DocResult]только чтение. Выполняет поиск по небольшому статическому корпусу в памяти, отфильтрованному по документам, видимым команде вызывающего пользователя (или общедоступным документам компании). Именно это делает сквозную аутентификацию доказуемой: один и тот же запрос от alice (engineering) и bob (sales) возвращает разные результаты.

  • create_ticket(title, body) -> TicketIdзапись, ограничено списком разрешённых. Заменяет реальный API тикетов (TicketSystemClient); dry-run перехватывает вызов до того, как этот клиент будет затронут.

Модель данных

  • audit_log: timestamp, session_id, user_id, tool_name, read_or_write, dry_run, allowed, latency_ms, error_code, detail — таблица SQLite + файл JSON-lines, записывается при каждом вызове.

  • конфиг tool_registry (секция tools: в server.yaml): read_only, cost_units, description для каждого имени инструмента.

  • session_limits: оконный лимит сессии в памяти (call_count, cost_used, сброс через window_seconds), управляется параметром rate_limit: в server.yaml.

Контракт ошибок

Каждый отказ — это структурированная ошибка MCPError{code, message, retry_after?, details?} — никогда не голое исключение и не молчаливое бездействие:

код

когда

UNAUTHENTICATED

токен отсутствует или не распознан

WRITE_NOT_ALLOWED

вызван пишущий инструмент, отсутствующий в allowed_write_tools

RATE_LIMIT_EXCEEDED

сессия превысила calls_per_min или cost_per_session

TOOL_NOT_FOUND

неизвестное имя инструмента

INVALID_ARGUMENTS

обработчик вызвал TypeError на переданных аргументах

По HTTP они сопоставляются с 401 / 403 / 429 / 404 / 400 соответственно, с тем же телом {code, message, ...} в поле detail ответа.

Установка

Требуется Python 3.10+ (разработано и протестировано на 3.10; спецификация требовала 3.11+ — см. Отклонения ниже, почему использована 3.10).

git clone https://github.com/HamzaOuadid/mcp-starter-template.git
cd mcp-starter-template
pip install -e ".[dev]"

Использование

Вывод реестра инструментов (проверка безопасности)

mcp-starter tools

Реальный вывод из этого репозитория:

  create_ticket    WRITE [DISABLED (not allowlisted)] cost=5   Create a ticket in the downstream ticket system (write, allowlist-gated).
  search_docs      read-only                        cost=1   Search internal docs visible to the calling user's team (read-only).

Запуск проработанного демо «что это предотвращает»

Это результат этапа 4: симуляция сценария с двумя тестовыми пользователями от начала до конца и демонстрация сохранения границы прав с использованием реального server.yaml из этого репозитория (dry_run: true, пустой allowed_write_tools).

mcp-starter demo

Фактический вывод реального запуска с server.yaml этого репозитория (rate_limit.calls_per_min: 5):

=== 1. Per-user auth passthrough: same tool, same query, different results ===
  alice (engineering): sees docs ['eng-001', 'eng-002', 'all-001']
  bob (sales): sees docs ['sales-001', 'sales-002', 'all-001']

=== 2. Missing/invalid identity is rejected, not defaulted ===
  token=None -> ok=False error={'code': 'UNAUTHENTICATED', 'message': 'Missing or invalid identity token; call rejected.'}

=== 3. Write tool default posture ===
  create_ticket denied: {'code': 'WRITE_NOT_ALLOWED', 'message': "Tool 'create_ticket' is a write tool and is not in allowed_write_tools. Add it to server.yaml's allowlist to enable it."}

=== 4. Rate limit: burst of calls past the cap ===
  call 1/6: allowed
  call 2/6: allowed
  call 3/6: allowed
  call 4/6: allowed
  call 5/6: allowed
  call 6/6: DENIED (RATE_LIMIT_EXCEEDED)

=== Audit log written to <repo>\demo_audit.jsonl ===
  {"allowed": true, "detail": "", "dry_run": false, "error_code": null, "latency_ms": 0.0, "read_or_write": "read", "session_id": "demo-burst-session", ...}
  {"allowed": true, ...}
  {"allowed": false, "error_code": "RATE_LIMIT_EXCEEDED", "detail": "Session 'demo-burst-session' exceeded its rate/spend cap (5 calls or 10 cost units per 60s window).", ...}

alice (engineering) и bob (sales) видят непересекающиеся наборы документов плюс общий общедоступный справочник (all-001) — граница прав сохраняется при использовании одного и того же инструмента и запроса. Токен None отклоняется сразу. create_ticket отклоняется, потому что список разрешённых по умолчанию пуст. 6-й вызов в сессии с лимитом 5 вызовов в минуту отклоняется со структурированной ошибкой.

Запустите его с добавлением пишущего инструмента в список разрешённых (всё ещё dry-run, так как это значение по умолчанию в конфиге), чтобы увидеть форму ответа dry-run:

mcp-starter demo --allow-writes
=== 3. Write tool default posture ===
  create_ticket allowed (allowlisted): TicketId(ticket_id='DRYRUN-8ffc09d5', dry_run=True)

Ни один реальный тикет не был создан — TicketSystemClient.created остаётся пустым в режиме dry-run; это напрямую проверяется в tests/test_dry_run.py через шпионаж за самим методом клиента.

Запуск HTTP-транспорта

mcp-starter serve-http --port 8000
curl http://127.0.0.1:8000/tools

curl -X POST http://127.0.0.1:8000/tools/search_docs/call \
  -H "Authorization: Bearer token-alice" \
  -H "X-Session-Id: demo-1" \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"query": ""}}'

# Write tool, denied by default (empty allowlist):
curl -i -X POST http://127.0.0.1:8000/tools/create_ticket/call \
  -H "Authorization: Bearer token-alice" \
  -H "X-Session-Id: demo-1" \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"title": "Broken build", "body": "CI red on main"}}'
# -> HTTP 403, {"detail":{"code":"WRITE_NOT_ALLOWED", ...}}

Dev-токены: token-alice (engineering), token-bob (sales), token-admin (engineering, установлен флаг администратора).

Запуск настоящего MCP stdio-сервера

mcp-starter serve-stdio

Это запускает настоящий FastMCP stdio-сервер — укажите MCP-клиенту (например, mcp dev из CLI mcp или конфигу Claude Desktop) на python -m mcp_starter.mcp_app. Инструменты: search_docs(query, token, session_id), create_ticket(title, body, token, session_id), list_tools().

Конфигурация

Отредактируйте server.yaml:

dry_run: true                 # write tools log-and-simulate instead of executing
allowed_write_tools: []       # empty = no write tool is callable, by design
rate_limit:
  calls_per_min: 5
  cost_per_session: 10
  window_seconds: 60
tools:
  search_docs:
    read_only: true
    cost_units: 1
  create_ticket:
    read_only: false
    cost_units: 5

Чтобы реально включить создание тикетов: добавьте create_ticket в allowed_write_tools и установите dry_run: false. Каждое из этих действий по отдельности оставляет инструмент либо невидимым для записи, либо имитируемым.

Тестирование

pytest tests/ -v

Реальный вывод из этого репозитория (40 тестов, все проходят):

tests/test_audit_log.py::test_audit_jsonl_reconstructs_a_session PASSED
tests/test_audit_log.py::test_audit_sqlite_table_matches_data_model PASSED
tests/test_audit_log.py::test_query_filters_by_session PASSED
tests/test_audit_log.py::test_rate_limit_denial_is_also_audited PASSED
tests/test_auth_passthrough.py::test_two_users_see_different_results_from_same_tool PASSED
tests/test_auth_passthrough.py::test_missing_token_is_rejected_not_defaulted PASSED
tests/test_auth_passthrough.py::test_invalid_token_is_rejected PASSED
tests/test_auth_passthrough.py::test_empty_string_token_is_rejected PASSED
tests/test_auth_passthrough.py::test_unknown_tool_name_does_not_crash PASSED
tests/test_cli.py::test_tools_command_lists_both_example_tools PASSED
tests/test_cli.py::test_demo_command_runs_full_scenario PASSED
tests/test_cli.py::test_demo_command_with_allow_writes_flag PASSED
tests/test_dry_run.py::test_dry_run_never_invokes_the_real_downstream_client PASSED
tests/test_dry_run.py::test_dry_run_logs_the_would_be_action_with_marker PASSED
tests/test_dry_run.py::test_dry_run_off_with_allowlist_actually_calls_downstream PASSED
tests/test_dry_run.py::test_dry_run_plus_write_tool_never_executes_even_when_allowlisted_repeatedly PASSED
tests/test_dry_run.py::test_read_only_tool_is_unaffected_by_dry_run_flag PASSED
tests/test_http_transport.py::test_list_tools_endpoint PASSED
tests/test_http_transport.py::test_auth_header_passthrough_two_users_differ PASSED
tests/test_http_transport.py::test_missing_auth_header_returns_401 PASSED
tests/test_http_transport.py::test_write_not_allowed_returns_403 PASSED
tests/test_http_transport.py::test_rate_limit_returns_429 PASSED
tests/test_http_transport.py::test_unknown_tool_returns_404 PASSED
tests/test_mcp_stdio.py::test_stdio_server_lists_all_three_tools PASSED
tests/test_mcp_stdio.py::test_stdio_server_two_users_differ PASSED
tests/test_mcp_stdio.py::test_stdio_server_write_tool_denied_by_default PASSED
tests/test_mcp_stdio.py::test_stdio_server_missing_token_rejected PASSED
tests/test_rate_limit.py::test_burst_of_n_plus_one_rejects_the_last_call PASSED
tests/test_rate_limit.py::test_calls_keep_being_rejected_until_window_resets PASSED
tests/test_rate_limit.py::test_cost_cap_is_enforced_independent_of_call_count PASSED
tests/test_rate_limit.py::test_sessions_are_isolated_from_each_other PASSED
tests/test_rate_limit.py::test_rate_limit_via_server_returns_structured_error PASSED
tests/test_rate_limit.py::test_denied_write_does_not_consume_rate_budget PASSED
tests/test_registry_allowlist.py::test_registry_describes_every_tool_classification PASSED
tests/test_registry_allowlist.py::test_all_write_tools_default_to_disabled PASSED
tests/test_registry_allowlist.py::test_write_tool_not_in_allowlist_is_denied PASSED
tests/test_registry_allowlist.py::test_write_tool_in_allowlist_becomes_enabled PASSED
tests/test_registry_allowlist.py::test_registration_refuses_undeclared_tool PASSED
tests/test_registry_allowlist.py::test_registration_refuses_classification_mismatch PASSED
tests/test_registry_allowlist.py::test_unknown_tool_call_is_tool_not_found PASSED

======================== 40 passed, 1 warning in 6.91s ========================

Покрытие по областям:

  • Сквозная аутентификация (test_auth_passthrough.py) — два имитированных пользователя, один и тот же инструмент, разные результаты; отсутствующий/недействительный/пустой токен отклоняется, никогда не подставляется значение по умолчанию; неизвестное имя инструмента корректно завершается ошибкой, а не падением.

  • Реестр / список разрешённых (test_registry_allowlist.py) — классификация каждого инструмента доступна для проверки; все инструменты записи по умолчанию отключены; регистрация отклоняет инструменты, отсутствующие в конфигурации или чьи классификации в коде и конфигурации расходятся; неизвестное имя инструмента даёт корректный TOOL_NOT_FOUND.

  • Пробный запуск (dry-run) (test_dry_run.py) — напрямую шпионит за TicketSystemClient.create, чтобы убедиться, что он действительно никогда не вызывается в режиме dry-run, а не только в том, что ответ выглядит синтетическим; использует caplog, чтобы подтвердить, что маркер [DRY RUN] действительно логируется; подтверждает, что реальный клиент вызывается, когда dry-run выключен и инструмент находится в списке разрешённых; повторяет комбинацию dry-run + разрешённая запись несколько раз для защиты от регрессий; подтверждает, что флаг не влияет на инструменты только для чтения.

  • Ограничение частоты запросов (test_rate_limit.py) — всплеск из N+1 отвергает ровно (N+1)-й вызов; вызовы продолжают отклоняться в течение остатка окна (не только тот, что его вызвал) с помощью фиктивных часов; лимит стоимости соблюдается независимо от количества вызовов; сеансы изолированы друг от друга; отклонённая запись сама не расходует бюджет лимита частоты.

  • Журнал аудита (test_audit_log.py) — JSONL и SQLite обе фиксируют полную сессию с достаточной детализацией, чтобы восстановить кто/что/разрешено/dry-run; строки SQLite фильтруются по сессии; отказы из-за ограничения частоты также попадают в журнал, а не только успешные операции.

  • Оба транспорта (test_http_transport.py, test_mcp_stdio.py) — те же защитные механизмы действуют при работе через TestClient из FastAPI и через асинхронные call_tool/list_tools реального сервера FastMCP, а не только через независимое от транспорта ядро.

  • CLI (test_cli.py) — tools и demo--allow-writes и без него) выполняются полностью без ошибок через typer.testing. CliRunner.

Отклонения от спецификации и почему

  • Python 3.10, а не 3.11+. В окружении разработки/CI используется 3.10; ничто в этой кодовой базе не задействует возможности, доступные только в 3.11, поэтому нижняя граница requires-python была смягчена, а не стала блокироваться обновлением интерпретатора. CI закрепляет 3.10 в соответствии с реально протестированной версией.

  • SQLite, а не PostgreSQL, для журнала аудита. Спецификация допускает любой вариант; Docker/Postgres в этом окружении недоступны. Схема аудита (таблица audit_log в audit.py) написана на чистом SQL без синтаксиса, специфичного для SQLite, поэтому переход на Postgres позже — это замена драйвера (sqlite3.connectpsycopg2/asyncpg) плюс AUTOINCREMENTSERIAL/IDENTITY, а не перепроектирование.

  • Сквозная аутентификация через stdio использует явный аргумент token, а не заголовок транспорта. Транспорт stdio в MCP — это единый локальный процесс без заголовков на каждый запрос, так что перехватывать нечего, в отличие от HTTP, где заголовок Authorization даёт HTTP-транспорту (http_app.py) реальные учётные данные для каждого запроса. Явная передача токена сохраняет тот же эффект (определённая, не используемая по умолчанию идентичность, управляющая каждым вызовом) и тестируемость на обоих транспортах; это документированное упрощение, а не утверждение, что у stdio есть «настоящая» многопользовательская аутентификация. В производственном многопользовательском развёртывании следует использовать HTTP-транспорт либо stdio-транспорт, обёрнутый аутентифицирующим прокси, который внедряет реальные учётные данные выше по потоку от этого кода.

  • Никаких OAuth/JWT/mTLS в MockIdentityProvider. Это статический словарь токен→пользователь, явно предназначенный только для разработки, согласно примечанию о рисках в самой спецификации. Замена на реальную проверку означает реализацию поиска токена в AuthMiddleware.authenticate через реальный IdP; остальная часть конвейера (реестр, ограничитель, dry-run, аудит) не затрагивается, поскольку она зависит только от получения объекта User.

  • Исключено: git-тег v0.1. Веха 4 предусматривает создание тега для релиза v0.1. Этот репозиторий ведётся по коммитам на пользовательскую историю, а не по PR на веху, поэтому создание тега оставлено мейнтейнеру: он сделает это, когда ветка попадёт в основную ветку с зелёным CI (git tag v0.1.0 && git push --tags), а не самостоятельное тегирование репозитория, который никуда не пушился.

  • Исключено: отсутствуют постоянные таблицы session_limits/tool_registry. В модели данных спецификации session_limits и tool_registry перечислены как таблицы наряду с audit_log. Классификация tool_registry хранится в server.yaml (возможно, это лучший единый источник истины, чем таблица БД, к которой рецензенту пришлось бы обращаться), а session_limits живёт только в памяти (limiter.py), что корректно для однопроцессного старта, но не переживёт перезапуск и не масштабируется между процессами — это следует отметить как первое, что нужно исправить (например, счётчики на базе Redis), прежде чем запускать это в конфигурации с более чем одним серверным процессом.

  • Два примерных инструмента, а не три и более. Спецификация запрашивает «2-3» — поставлено ровно два (один на чтение, один на запись), поскольку третий инструмент только для чтения не стал бы проверять какой-либо защитный механизм, не покрытый уже первыми двумя.

Структура проекта

src/mcp_starter/
  identity.py    mock identity provider (dev-only) + User model
  auth.py        auth passthrough middleware
  config.py      server.yaml loading/validation (pydantic)
  registry.py    tool registry: classification + allowlist enforcement
  limiter.py     per-session fixed-window rate/spend limiter
  audit.py       JSONL + SQLite structured audit logging
  dryrun.py      "[DRY RUN]" audit-line formatting
  errors.py      structured MCPError + error codes
  server.py      MCPStarterServer.call_tool — the orchestration core
  mcp_app.py     real MCP stdio server (official MCP Python SDK)
  http_app.py    FastAPI HTTP transport (Authorization header passthrough)
  cli.py         `mcp-starter` CLI: tools / demo / serve-http / serve-stdio
  tools/
    docs.py      search_docs (read-only example tool)
    tickets.py   create_ticket (write example tool) + TicketSystemClient
tests/           37 tests across every guardrail and both transports
server.yaml      tool classification, allowlist, dry-run, rate limits

Лицензия

MIT — см. LICENSE.

-
license - not tested
-
quality - not tested
B
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 Connectors

  • A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready

  • Hosted MCP server for agent governance: MCP config audits, injection scans, scope-policy checks.

  • The official MCP Server from Mia-Platform to interact with Mia-Platform Console

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/HamzaOuadid/mcp-starter-template'

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