Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Directus Safe

БесплатноНе проверен

A schema-aware, dry-run/verify-guarded MCP sidecar that wraps the Directus REST API with safety features like collection allowlists, batch limits, and mutation

GitHubEmbed

Описание

A schema-aware, dry-run/verify-guarded MCP sidecar that wraps the Directus REST API with safety features like collection allowlists, batch limits, and mutation verification, enabling secure and LLM-friendly data operations.

README

Schema-aware, dry-run/verify-guarded Directus MCP sidecar.

Directus REST API'yi güvenli, schema-first ve LLM dostu bir MCP (Model Context Protocol) ara katmanıyla saran bağımsız Node.js / TypeScript servisi. Directus resmi remote MCP'deki items tool'undaki stringified-JSON serialization bug sınıfını (#26891 / PR #27005) kendi içinde çözer; ek olarak dry-run, verify, after-read doğrulaması, collection allowlist, system collection guard ve batch limit politikaları uygular.

Bu MCP Directus'un yerine geçmez. Directus API'yi güvenli ve LLM dostu bir ara katmanla kullanır. RBAC bypass etmez — token'ın sahip olduğu izinler neyse MCP de onu kullanır.

Bu MCP iş kuralı bilmez. Koleksiyon anlamları (örneğin "şu koleksiyon satın alma kaynağıdır", "bu alan şu vendor'a bağlıdır") MCP'nin değil, bağlanan ajan prompt'larının / skill'lerin / ai_prompts kayıtlarının sorumluluğundadır. MCP sadece: schema oku, veri oku, güvenli yaz, validate et, dry-run yap, verify yap, after-read yap.


Özellikler

  • MCP TypeScript SDK üzerinde streamable-http (varsayılan, production) + stdio (local debug) transport. Legacy HTTP+SSE transport desteklenmez; Streamable HTTP kendi içinde gerektiğinde text/event-stream response kullanabilir (bu, SDK'nın resmi Streamable HTTP davranışıdır).
  • Stateless Streamable HTTPsessionIdGenerator: undefined ile gerçek stateless mod. Her HTTP request için taze McpServer + taze StreamableHTTPServerTransport oluşturulur; mcp-session-id header üretilmez. Bu sayede initialize sonrası tools/list tek HTTP bağlantısında çalışır.
  • Endpoint path guard — sadece MCP_ENDPOINT_PATH (default /mcp) MCP endpoint. GET /healthz liveness probe, diğer path'ler 404.
  • Bearer auth gateMCP_REQUIRE_AUTH=true (default) ise her HTTP request Authorization: Bearer <MCP_AUTH_TOKEN> taşır. Constant-time compare. Stdio exempt (local subprocess).
  • Origin / Host guardMCP_ALLOWED_ORIGINS ve MCP_ALLOWED_HOSTS ile DNS rebinding + CSRF koruması.
  • Verify enforcementMUTATION_REQUIRE_VERIFY=true (default) iken directus_update_item ve directus_batch_update_items verify olmadan çalışmaz; directus_update_items_same_data tamamen reddedilir (per-key verify desteklemez).
  • All-or-nothing batch applydirectus_batch_update_items ve directus_create_items apply (dry_run=false) sırasında önce preflight çalıştırır. Herhangi bir item validation/verify/dedupe hatası verirse tüm batch abort edilir, sıfır yazma yapılır. allow_partial_apply=true ile eski partial-success davranışına geri dönülebilir.
  • Single-mode read querydirectus_read_item artık limit/page/offset parametrelerini reddeder (tek kayıt endpoint'inde anlamsız; LLM karışıklığını gösterir). fields/deep/version hala destekleniyor.
  • Token-bounded content.text — Tüm tool response'larında content.text artık sadece kısa bir etiket değil; modelin karar vermesi için gereken gerçek sonucu (şema field listesi, dönen kayıtlar, before/after/diff, hata kodları, batch summary) kompakt formatta içerir. LibreChat structuredContent'i modele göstermese bile LLM gerçek sonucu görür. Limitler: SCHEMA_TEXT_MAX_FIELDS=80, READ_TEXT_MAX_ROWS=10, READ_TEXT_MAX_CHARS=12000.
  • Recursive JSON normalisationdata, query, filter, deep, keys, headers, verify, dedupe, items, operations alanları JSON string olarak gelirse otomatik parse.
  • Schema-first validation — her yazma işleminde field set şemadan doğrulanır; unknown ve readonly field'lar Directus'a gitmeden reddedilir.
  • Read-before, verify, diff, after-read doğrulama zinciri update'lerde.
  • Dry-run defaultMUTATION_DRY_RUN_DEFAULT=true ile yazma işlemleri varsayılan olarak simulate edilir.
  • Collection allowlist + directus_* blocklist.
  • Delete default kapalıDIRECTUS_ALLOW_DELETE=true + confirm="DELETE <collection>:<keys>" gerekli.
  • Batch size limitiMUTATION_MAX_BATCH_SIZE.
  • Filter operator whitelist — sadece spec'te listelenen operatorlere izin verilir.
  • Safe default fieldsfields verilmezse PK + ilk 10 scalar field döner; * wildcard default olarak reddedilir.
  • Partial success reporting — batch işlemler per-item sonuç döner.
  • Audit log — her mutation girişimi loglanır.

Hızlı Başlangıç

1. Environment

.env.example'ı kopyalayıp doldurun:

cp .env.example .env
# .env içine:
#   DIRECTUS_URL=https://your-directus.example.com
#   DIRECTUS_TOKEN=replace-with-directus-static-token
#   MCP_AUTH_TOKEN=<random 32+ byte string>

2. Production (Docker, Streamable HTTP)

docker compose up --build

Container 3333 portunda Streamable HTTP MCP sunar. Bearer token ile auth zorunludur (MCP_REQUIRE_AUTH=true default).

3. Local debug (Node, stdio)

npm install
npm run build
MCP_TRANSPORT=stdio node dist/index.js

Stdio modu local subprocess olduğu için MCP_REQUIRE_AUTH yok sayılır — auth parent process'in sorumluluğundadır.

4. Local HTTP debug

MCP_TRANSPORT=streamable-http \
MCP_HTTP_PORT=3333 \
MCP_REQUIRE_AUTH=true \
MCP_AUTH_TOKEN=dev-token \
node dist/index.js

Configuration (env)

Variable Default Açıklama
DIRECTUS_URL (zorunlu) Directus instance URL'i
DIRECTUS_TOKEN (zorunlu) Directus access token (RBAC'ye tabi)
MCP_TRANSPORT streamable-http streamable-http, http (alias), veya stdio
MCP_HTTP_PORT 3333 Streamable HTTP port
MCP_BIND_HOST 0.0.0.0 Network interface (127.0.0.1 = local-only)
MCP_ENDPOINT_PATH /mcp MCP HTTP endpoint path. /healthz her zaman açık.
MCP_REQUIRE_AUTH true HTTP transport'ta Bearer auth zorunlu mu
MCP_AUTH_TOKEN (zorunlu HTTP auth için) Bearer token; constant-time compare
MCP_ALLOWED_ORIGINS (boş = tümü) Virgülle ayrılmış Origin allowlist (DNS rebinding koruması)
MCP_ALLOWED_HOSTS (boş = tümü) Virgülle ayrılmış Host allowlist. Port-tolerant: mcp.example.com allowlist'i hem mcp.example.com hem mcp.example.com:3333 Host header'larını kabul eder.
DIRECTUS_ALLOWED_COLLECTIONS (boş = hepsi) Virgülle ayrılmış allowlist
DIRECTUS_DENIED_COLLECTION_PREFIXES directus_ Yasaklı prefix'ler
DIRECTUS_ALLOW_DELETE false Delete tool'larını açar
DIRECTUS_ALLOW_SCHEMA_WRITE false (rezerve)
MUTATION_DRY_RUN_DEFAULT true dry-run default
MUTATION_REQUIRE_VERIFY true Update'lerde verify zorunlu; update_items_same_data reddedilir
MUTATION_MAX_BATCH_SIZE 100 Batch limit
READ_DEFAULT_LIMIT 50 Read default limit
READ_MAX_LIMIT 500 Read maksimum limit (clamp)
ALLOW_WILDCARD_FIELDS false fields: ["*"] izni
SCHEMA_CACHE_TTL_SECONDS 300 Schema cache TTL
VERIFY_CASE_INSENSITIVE false Verify string compare modu
SCHEMA_TEXT_MAX_FIELDS 80 content.text için şema detayında maksimum field sayısı
READ_TEXT_MAX_ROWS 10 content.text için read/batch sonuçlarında maksimum satır
READ_TEXT_MAX_CHARS 12000 content.text toplam karakter limiti (truncation marker dahil)
APPLY_REQUIRES_PLAN true Direct dry_run:false reddedilir; model önce dry-run yapıp plan almalı, sonra directus_apply_plan çağırmalı
PLAN_STORE file Plan store backend: file (production) veya memory (test)
PLAN_STORE_DIR /tmp/directus-safe-mcp-plans File-based plan store dizini (0600 izinleri)
PLAN_TTL_SECONDS 900 Plan TTL (saniye, default 15 dk)
PLAN_MAX_BYTES 1048576 Maks plan payload boyutu (byte)
LOG_LEVEL info pino log level

MCP Tool Listesi

20 tool register edilir (hepsi directus_ prefix'i ile):

Tool Açıklama
directus_schema_overview Koleksiyon listesi + PK + field count
directus_schema_detail Bir veya birden fazla koleksiyonun tam şema detayı
directus_read_items Liste okuma (validate edilmiş query ile)
directus_read_item Tek item okuma
directus_create_item Tek item create (dedupe + dry-run + plan destekli)
directus_create_items Batch create (serial, per-item result, all-or-nothing preflight)
directus_update_item Tek item update (verify_fields auto-gen destekli)
directus_update_items_same_data Bulk PATCH /items/{collection} (aynı data, multi key)
directus_batch_update_items Per-item different data update (serial PATCH)
directus_delete_items Delete (default kapalı, confirm gerekli)
directus_dry_run_mutation Çoklu operasyon planı (dry-run only, planId üretmez)
directus_update_by_query_plan Query ile toplu update planı + bundle (model kayıt listesi üretmez)
directus_apply_plan Tek plan uygular (gerçek yazma + read-back verification)
directus_apply_plans Çoklu plan toplu uygular (stop_on_error destekli)
directus_apply_plan_bundle Bundle'ı toplu uygular (already-applied tolerant + verification)
directus_cancel_plan Tek pending plan'ı iptal eder
directus_cancel_plans Çoklu pending plan'ı toplu iptal eder
directus_plan_bundle_status Bundle durumunu sorgular (pending/applied/expired/cancelled)
directus_verify_fields_empty Belirli field'ların boş olduğunu doğrular
directus_verify_fields_value Belirli field'ların beklenen değere eşit olduğunu doğrular (deep equality)

Tool input kuralları

  • data, items, keys, query, filter, deep, verify, dedupe, operations hem native JSON hem de <field>_json formunda kabul edilir.
  • *_json alanları string, array VE object kabul eder (LibreChat uyumluluğu için). Handler otomatik normalize eder.
  • LibreChat bazen keys_json: [1,2,3] (array) veya data_json: {tags:[]} (object) gönderir — bu artık schema validation'da patlamaz, handler'da normalize edilir.
  • Update işlemlerinde verify field'ları mevcut kayıtla eşleşmezse VERIFY_FAILED hatası döner; yazma yapılmaz.
  • Unknown field → UNKNOWN_FIELD hatası (sessizce drop edilmez).
  • Readonly / system audit field'lar (user_created, date_created, user_updated, date_updated) → READONLY_FIELD hatası.
  • Primary key update → PRIMARY_KEY_UPDATE_DENIED.
  • directus_* koleksiyonlarında mutation → SYSTEM_COLLECTION_DENIED.
  • Filter operator whitelist dışı → INVALID_FILTER_OPERATOR.
  • Wildcard field default olarak reddedilir (ALLOW_WILDCARD_FIELDS=false).

JSON array alanlarında filtre güvenilirliği uyarısı

Directus JSON/array alanlarında _nnull, _nempty filtreleri her zaman güvenilir sonuç vermeyebilir. JSON kolonlar "null", "[]", "{}" veya whitespace string saklayabilir; Directus bunları non-null/non-empty sayabilir.

Güvenli yöntem: directus_verify_fields_empty tool'unu kullanın. Bu tool kayıtları okur ve client-side boş/dolu kontrolü yapar:

  • null, undefined, "", whitespace-only string, [], {} → boş
  • diğer her şey → dolu

Örnek:

{
  "tool": "directus_verify_fields_empty",
  "input": {
    "collection": "suppliers",
    "fields": ["tags", "products"]
  }
}

Response'da nonEmpty listesi döner — bu kayıtlar tekrar işlenmelidir. Toplu temizlik sonrası "tüm kayıtlar temiz" demeden önce bu tool ile doğrulama yapın.

Hata formatı

{
  "ok": false,
  "error": {
    "code": "UNKNOWN_FIELD",
    "message": "Field 'bogus' does not exist in 'articles'",
    "details": { "collection": "articles", "field": "bogus" }
  }
}

Hata kodları: CONFIG_ERROR, DIRECTUS_API_ERROR, COLLECTION_NOT_ALLOWED, SYSTEM_COLLECTION_DENIED, SCHEMA_NOT_FOUND, PRIMARY_KEY_NOT_FOUND, UNKNOWN_FIELD, READONLY_FIELD, PRIMARY_KEY_UPDATE_DENIED, REQUIRED_FIELD_MISSING, INVALID_QUERY, INVALID_FILTER_OPERATOR, VERIFY_FAILED, VERIFY_REQUIRED, ABORTED_BY_PREFLIGHT, DUPLICATE_FOUND, BATCH_LIMIT_EXCEEDED, DELETE_DISABLED, CONFIRMATION_REQUIRED, INVALID_JSON, INVALID_DATA_TYPE, DRY_RUN_REQUIRED, NOT_FOUND, PLAN_NOT_FOUND, PLAN_EXPIRED, PLAN_ALREADY_APPLIED, PLAN_ALREADY_IN_PROGRESS, PLAN_CANCELLED, PLAN_CHECKSUM_MISMATCH, APPLY_REQUIRES_PLAN, PLAN_STORE_ERROR, PLAN_TOO_LARGE, READBACK_MISMATCH, CONFIRM_TRUE_REQUIRED.


Dry-run → Approval → Apply Plan Flow

Bu akış, modelin "uyguladım" deyip gerçekte yazmaması problemini kökten çözer.

Akış

  1. Mutation tool dry_run:true ile çağrılır (örn. directus_update_item).
  2. Server dry-run çalıştırır: read-before → verify → validate → diff. Hiçbir yazma yapmaz.
  3. Server bir planId oluşturur ve store eder (file-based, TTL 15 dk).
  4. Response'da planId, requiresApplyPlan: true, written: false döner.
  5. content.text'te açıkça "DRY-RUN ONLY — hiçbir veri yazılmadı" yazar ve directus_apply_plan çağrı önerisi verilir.
  6. Kullanıcı onay verince model aynı payload'u tekrar üretmez — sadece directus_apply_plan({ plan_id, confirm: true }) çağırır.
  7. directus_apply_plan planı yükler, checksum doğrular, collection guard + schema + field validation + verify tekrar yapar, gerçek yazma yapar, read-back verification yapar, plan'ı applied olarak işaretler.
  8. Response'da applied: true, written: true, dryRun: false, readBackOk: true döner.
  9. content.text'te "APPLIED — gerçek yazma yapıldı" yazar.

Kritik kurallar

  • dry_run:true asla veri yazmaz. Kullanıcı onayı sadece apply iznidir; gerçek yazma directus_apply_plan çağrısıyla olur.
  • APPLY_REQUIRES_PLAN=true (default) iken direct dry_run:false mutation tool çağrısı APPLY_REQUIRES_PLAN hatası alır.
  • Her plan sadece bir kez uygulanabilir (idempotency). İkinci apply → PLAN_ALREADY_APPLIED.
  • Concurrent apply protection: plan claim atomik pending → applying geçişi yapar. Eşzamanlı iki apply çağrısından ilki claim alır, ikincisi PLAN_ALREADY_IN_PROGRESS alır.
  • Süresi dolmuş plan → PLAN_EXPIRED. Yeni dry-run gerekir.
  • Apply sırasında verify koşulları artık geçerli değilse → VERIFY_FAILED, yazma yapılmaz (pre-write error, plan cancelled).
  • Read-back mismatch post-write davranışı: yazma başarılı olursa, plan mutlaka terminal duruma geçer. Read-back mismatch olursa applied_with_warning statüsü alır ve warning.code: "READBACK_MISMATCH" response'da döner — throw etmez. Plan tekrar apply edilemez. Bu, "yazıldı ama error döndü, tekrar yazıldı" riskini önler.
  • Post-write unexpected error → failed_after_write (terminal, tekrar apply edilemez, caller read ile durumu kontrol etmeli).
  • Plan store file-based (default), 0600 dosya izinleri, TTL 15 dk, max 1 MB payload.
  • Startup + periyodik (5 dk) cleanup: expired/cancelled/applied plan dosyaları temizlenir. applied_with_warning ve failed_after_write planları korunur (post-mortem inspection için).
  • Audit log plan payload'unu loglamaz — sadece planId, operation, collection, changedFields.

directus_dry_run_mutation plan üretmez

directus_dry_run_mutation tool'u çoklu işlem planlama/görünüm içindir. Apply edilebilir planId üretmez.

Apply edilebilir plan gerekiyorsa spesifik mutation tool dry_run:true kullanın:

  • Tek kayıt update: directus_update_item dry_run:true
  • Çoklu farklı update: directus_batch_update_items dry_run:true
  • Tek/çoklu create: directus_create_item(s) dry_run:true
  • Delete: directus_delete_items dry_run:true

Onay sonrası sadece directus_apply_plan çağırın.

Plan lifecycle durumları

pending → applying → applied                          (write + readback OK)
                   → applied_with_warning             (write OK, readback mismatch)
                   → failed_after_write               (write happened, post-write error)
pending → cancelled                                   (user rejected or pre-write validation fail)
pending/applying → expired                            (TTL passed)

applied, applied_with_warning, failed_after_write, expired, cancelled hepsi terminal — plan bir daha apply edilemez.

Örnek akış

// 1. Model: dry-run update
{ "tool": "directus_update_item", "input": {
    "collection": "articles", "key": 1,
    "verify": { "title": "Intro to MCP" },
    "data": { "slug": "intro-to-mcp" },
    "dry_run": true
}}
// Response: { "planId": "plan_...", "requiresApplyPlan": true, "written": false, ... }

// 2. Kullanıcı: "onaylıyorum"

// 3. Model: apply plan (payload'u tekrar üretmez!)
{ "tool": "directus_apply_plan", "input": {
    "plan_id": "plan_...",
    "confirm": true
}}
// Response: { "applied": true, "written": true, "dryRun": false, "readBackOk": true, ... }

Content text örnekleri

Dry-run:

DRY-RUN UPDATE articles — OK (dryRun=true) NOT WRITTEN
Plan ID: plan_...
Plan expires at: 2024-01-01T00:15:00.000Z
Changed fields: slug
Before: {"id":1,"title":"Intro to MCP","slug":null}
After: {"id":1,"title":"Intro to MCP","slug":"intro-to-mcp"}

⚠ DRY-RUN ONLY — hiçbir veri yazılmadı.
Kullanıcı onay verirse şu tool çağrılmalı:
directus_apply_plan({ "plan_id": "plan_...", "confirm": true })
Başarı mesajı ancak directus_apply_plan sonucunda applied:true / written:true görüldükten sonra verilebilir.

Apply:

APPLIED UPDATE articles — OK (dryRun=false) written=true
Plan ID: plan_...
Read-back verification: OK
Changed fields: slug

Delete işlemleri

Delete işlemleri de aynı plan akışını kullanır:

  1. directus_delete_items ile dry_run:true → plan oluşturulur (confirm token dahil).
  2. directus_apply_plan → gerçek delete + read-back (kayıtların artık okunamadığını doğrular).

Local debug (plan akışı olmadan)

APPLY_REQUIRES_PLAN=false ile direct dry_run:false çağrılabilir (eski davranış). Sadece local debug için önerilir.


Bundle Flow: Query-Based Toplu Update (düşük parametreli modeller için)

Bu akış, modelin kayıt listesi toplamasını, verify üretmesini, planId'leri takip etmesini engeller. Model sadece niyeti söyler.

Akış

  1. Model: directus_update_by_query_plan çağırır (query + data + verify_fields)
  2. MCP: kayıtları okur, verify üretir, chunk'lara böler, planlar oluşturur, bundle_id döndürür
  3. Kullanıcı: onay verir
  4. Model: directus_apply_plan_bundle({ bundle_id, confirm: true }) çağırır (payload tekrar üretmez)
  5. MCP: tüm planları uygular, read-back + bundle-level verification yapar
  6. Response: applied: true, written: true, readBackStatus: 'ok', verification: { ok: true }

Örnek

// 1. Model: "Tüm tedarikçilerin tags alanı ['test'] olsun"
{
  "tool": "directus_update_by_query_plan",
  "input": {
    "collection": "suppliers",
    "query": { "fields": ["id", "company"], "sort": ["id"], "limit": 100 },
    "data": { "tags": ["test"] },
    "verify_fields": ["company"],
    "dry_run": true,
    "chunk_size": 25
  }
}
// Response: { "bundleId": "bundle_...", "planIds": [...], "totalMatched": 49, ... }

// 2. Kullanıcı: "onaylıyorum"

// 3. Model: apply bundle (payload tekrar üretmez!)
{
  "tool": "directus_apply_plan_bundle",
  "input": { "bundle_id": "bundle_...", "confirm": true }
}
// Response: { "applied": true, "written": true, "readBackStatus": "ok", "verification": { "ok": true, "totalChecked": 49 } }

Neden bundle?

  • Model planId listesi takip etmez — tek bundle_id yeterli
  • Model kayıt listesi üretmez — MCP server-side okur
  • Model verify üretmez — MCP verify_fields'tan otomatik üretir
  • Model id tahmini yapamaz — sadece gerçek dönen kayıtlar kullanılır
  • PLAN_ALREADY_APPLIED durumu otomatik verification ile çözülür

verify_fields auto-generation

directus_update_item ve directus_update_by_query_plan artık verify_fields parametresi destekliyor. verify nesnesi vermek yerine field adları verilir; MCP kaydı okur ve verify'yi server-side üretir.

{
  "collection": "suppliers",
  "key": 61,
  "verify_fields": ["company"],
  "data": { "ai_info": "# O KIMYA" },
  "dry_run": true
}

MCP içeride şunu üretir:

{ "verify": { "company": "O KIMYA" } }

Bu, modelin verify: { ai_info: true } gibi hatalı verify'ler üretmesini engeller.


LibreChat Agent Instructions

Bağlanan ajana şu talimatı verin:

Directus mutation işlemlerinde (create/update/delete) her zaman önce dry_run:true kullan.

Dry-run sonucu planId dönerse, kullanıcı onayı sonrası AYNI PAYLOAD'U TEKRAR ÜRETTME.
Sadece şu çağrıyı yap:
  directus_apply_plan({ "plan_id": "<önceki planId>", "confirm": true })

Kullanıcı "onaylıyorum", "uygula", "tamam", "devam et" derse directus_apply_plan çağır.

directus_apply_plan çalışmadan ve çıktıda applied:true / dryRun:false / written:true görmeden
"başarıyla güncellendi", "uygulandı", "kaydedildi" DEME.

Dry-run çıktısı başarı değildir. Dry-run sadece plan üretir.
Eğer planId yoksa veya apply başarısız olursa, kullanıcıya gerçeği söyle.

directus_dry_run_mutation tool'u planId ÜRETMEZ — sadece çoklu işlem görünümü içindir.
Apply edilebilir plan için spesifik mutation tool dry_run:true kullan (update_item, batch_update_items, create_item, delete_items).

Eğer directus_apply_plan çıktısında warning alanı varsa (örn. READBACK_MISMATCH),
yazma gerçekleşmiş ama doğrulama başarısız olmuş demektir. Kullanıcıya gerçeği söyle:
"Yazma yapıldı ama read-back doğrulaması başarısız. Kaydı kontrol edin."
Plan zaten terminal durumdadır, tekrar apply ETME.

Client Bağlantı Örnekleri

1. Streamable HTTP (production, Docker/LibreChat)

LibreChat veya başka bir MCP client url alanını destekliyorsa:

mcpServers:
  directus-safe:
    url: http://directus-safe-mcp:3333/mcp
    headers:
      Authorization: "Bearer ${MCP_AUTH_TOKEN}"
    timeout: 90000

Container'ı MCP_TRANSPORT=streamable-http + MCP_AUTH_TOKEN=<token> ile çalıştırın.

2. stdio (local debug, Claude Desktop / Cursor)

mcpServers:
  directus-safe:
    command: docker
    args:
      - exec
      - -i
      - directus-safe-mcp
      - node
      - dist/index.js
    env:
      DIRECTUS_URL: "${DIRECTUS_URL}"
      DIRECTUS_TOKEN: "${DIRECTUS_TOKEN}"
      MCP_TRANSPORT: "stdio"
    timeout: 90000
    initTimeout: 60000

Stdio modunda MCP_REQUIRE_AUTH yok sayılır (auth parent process'in sorumluluğunda).


Örnek Kullanım

1. Schema keşfi

{ "tool": "directus_schema_overview", "input": { "include_system": false } }
{ "tool": "directus_schema_detail", "input": { "collections": ["articles"] } }

2. Okuma

{
  "tool": "directus_read_items",
  "input": {
    "collection": "articles",
    "query": {
      "fields": ["id", "title", "slug"],
      "filter": { "title": { "_icontains": "MCP" } },
      "limit": 10
    }
  }
}

3. Dry-run update (önerilen akış)

{
  "tool": "directus_batch_update_items",
  "input": {
    "collection": "articles",
    "items": [
      { "key": 1, "verify": { "title": "Intro to MCP" }, "data": { "status": "published" } },
      { "key": 2, "verify": { "title": "Directus Deep Dive" }, "data": { "status": "published" } }
    ],
    "dry_run": true
  }
}

LLM sonucu kullanıcıya sunar; kullanıcı onaylayınca dry_run: false ile gerçek yazma yapılır.

4. Stringified JSON regression test

Eğer client data'yı string olarak gönderirse (Directus issue #26891):

{
  "tool": "directus_update_item",
  "input": {
    "collection": "articles",
    "key": 1,
    "data": "{\"status\":\"published\"}",
    "dry_run": true
  }
}

Sidecar bunu otomatik parse eder ve validation'a sokar.


Ajan Kullanım Talimatı

Bağlanan ajana şunu verin:

Directus verisi için resmi Directus remote MCP items tool yerine directus-safe-mcp tool'larını kullan.

Her görevde sıra:
1. directus_schema_detail ile ilgili koleksiyonları incele.
2. directus_read_items veya directus_read_item ile mevcut kayıtları oku.
3. Yazma işleminden önce dry_run=true kullan.
4. Kullanıcı/ana ajan onayı olmadan dry_run=false yapma.
5. Update'lerde verify kullan.
6. Unknown/readonly field hatasında tahminle devam etme.
7. Tool schema hatası alırsan aynı payload'u tekrar etme; data_json veya query_json kullan.

İş kuralları (koleksiyon anlamları, vendor mapping'leri, domain-specific davranışlar) MCP'de değil, ajan prompt'larında / skill'lerde / ai_prompts kayıtlarında tutulur.


Test

npm test                # vitest run
npm run test:watch      # vitest watch
npm run typecheck       # tsc --noEmit (strict)

Test kapsamı:

  • normalize.test.ts — recursive JSON normalisation (regression testleri dahil)
  • diff-verify.test.ts — diff ve verify davranışı
  • permissions.test.ts — collection / batch guard'lar
  • validators.test.ts — query validation + field validation
  • mutations.test.ts — DirectusRestClient + items operations + relation type inference
  • mcp-tools.test.ts — fetch-mock ile MCP tool entegrasyon testleri (dry-run, verify fail, unknown field, batch partial success, stringified JSON regression)
  • transports.test.ts — Streamable HTTP auth gate + transport alias parsing

Mimari Notlar

Stateless Streamable HTTP

StreamableHTTPServerTransport'a sessionIdGenerator: undefined veriyoruz. Bu, SDK'ya "session üretme, her request self-contained" der. Bu olmadan SDK her initialize'da mcp-session-id header üretir; client sonraki request'te bu header'ı göndermek zorundadır; ama server tarafında session map tutmadığımız için ikinci request "Server not initialized" 400 hatası alır. undefined ile bu davranış tamamen kapanır.

Her HTTP request için taze McpServer + taze transport oluşturulur. Tool'lar zaten stateless — Directus REST çağrısı yapıyorlar, server içinde kullanıcı state'i tutmaya gerek yok.

Bearer auth

MCP_REQUIRE_AUTH=true (default) ise her HTTP request Authorization: Bearer <MCP_AUTH_TOKEN> taşır. Token karşılaştırması constant-time yapılır (timing side-channel önlemi). Stdio transport exempt — auth parent process'in sorumluluğunda.

Config yükleme sırasında: eğer streamable-http + MCP_REQUIRE_AUTH=true + MCP_AUTH_TOKEN boşsa server refuse-to-start eder. Yanlışlıkla unauthenticated HTTP açmamak için.

Transaction garantisi yok (V1)

directus_batch_update_items tek tek PATCH /items/{collection}/{id} çağrıları yapar. Bir kayıt başarısız olursa diğerleri devam eder (fail_fast=false default). Partial success summary alanında raporlanır. All-or-nothing transaction gerekiyorsa Directus API extension içinde ItemsService + database transaction kullanmak gerekir (V2 planı).

Different-data batch update

Resmi Directus Items API "Update Multiple Items" endpoint'i (PATCH /items/{collection}) sadece aynı data'yı birden fazla key'e uygular ({ keys, data } body). Per-key farklı data için sidecar serial PATCH /items/{collection}/{id} kullanır.

RBAC

Sidecar Directus RBAC'yi bypass etmez. Token'ın sahip olduğu permission'lar geçerlidir. 403/401 hataları DIRECTUS_API_ERROR olarak sarılır.

Schema cache

/collections, /fields/{collection}, /relations çağrıları TTL cache'lenir (default 5 dakika). Schema değişikliği olursa servisi restart edin veya cache'i temizleyin.


Dosya Yapısı

directus-safe-mcp/
  Dockerfile
  docker-compose.yml
  .env.example
  package.json
  tsconfig.json
  vitest.config.ts
  README.md
  scripts/
    smoke-stdio.mjs              # stdio transport smoke test (initialize + tools/list)
    smoke-http.mjs               # Streamable HTTP smoke test (healthz, 404, 401, initialize, tools/list)
  src/
    index.ts                     # entry: load config → server factory → connect transport
    config.ts                    # env loader + pino logger + transport alias parsing
    mcp/
      server.ts                  # ToolContext + buildServer
      transports.ts              # stdio / stateless streamable-http + Bearer auth
      tools.ts                   # registerAllTools wrapper (normalise + error mapping)
    directus/
      client.ts                  # low-level items operations
      rest.ts                    # DirectusRestClient + DirectusApiError
      schema.ts                  # internal schema model + relation inference
      schemaService.ts           # /collections + /fields + /relations cache
      query.ts                   # read query validation + filter operator whitelist
      validators.ts              # field validation (unknown, readonly, pk, required)
      mutations.ts               # read/create/update/delete orchestration
      errors.ts                  # McpUserError + ErrorCode union
    safety/
      normalize.ts               # recursive JSON normaliser
      permissions.ts             # collection / batch / delete guards
      diff.ts                    # MutationDiff + deepEqual
      dryRun.ts                  # dry-run result shape
      verify.ts                  # verify record vs expectations
      audit.ts                   # in-memory audit log
      textFormat.ts              # compact content.text formatters (token-bounded)
      plans.ts                   # MutationPlan + PlanStore (file/memory) + checksum
      bundles.ts                 # PlanBundle + BundleStore + status computation
    tools/
      schemaOverview.ts
      schemaDetail.ts
      readItems.ts
      readItem.ts
      createItem.ts
      createItems.ts
      updateItem.ts
      updateItemsSameData.ts
      batchUpdateItems.ts
      deleteItems.ts
      dryRunMutation.ts
      applyPlan.ts                # single plan apply
      applyPlans.ts               # batch plan apply (stop_on_error)
      applyPlanBundle.ts          # bundle apply (already-applied tolerant + verification)
      cancelPlan.ts               # single plan cancel
      cancelPlans.ts              # batch plan cancel
      planBundleStatus.ts         # bundle status query
      updateByQueryPlan.ts        # query-based batch plan + bundle creation
      verifyFieldsEmpty.ts        # post-apply verification helper (fields empty)
      verifyFieldsValue.ts        # post-apply verification helper (fields match expected)
    test/
      helpers.ts                 # expectErrorCode test helper
      normalize.test.ts
      diff-verify.test.ts
      permissions.test.ts
      validators.test.ts
      mutations.test.ts
      mcp-tools.test.ts
      transports.test.ts

Kabul Kriterleri

Tüm kabul kriterleri (spec §25) karşılanır:

  1. ✅ Docker Compose ile ayağa kalkar.
  2. stdio MCP transport çalışır (+ streamable-http production default).
  3. ✅ 11 tool register edilir (spec minimum 8).
  4. data stringified JSON olarak gelirse native objeye çevrilir.
  5. query stringified JSON olarak gelirse native objeye çevrilir.
  6. ✅ Update dry-run before/after diff döndürür.
  7. ✅ Update apply sonrası after-read doğrulama yapar.
  8. ✅ Unknown field update Directus'a gitmeden reddedilir.
  9. ✅ Readonly field update Directus'a gitmeden reddedilir.
  10. directus_* collection mutation reddedilir.
  11. ✅ Delete default kapalıdır.
  12. ✅ Batch update partial success raporlar.
  13. ✅ Tests pass (npm test).
  14. ✅ README içinde hem Streamable HTTP hem stdio bağlantı örneği vardır.
  15. ✅ Kod TypeScript strict modda compile olur.
  16. ✅ Streamable HTTP varsayılan transport, Bearer auth gate zorunlu.
  17. ✅ Şirket/koleksiyon sabiti yok — generic Directus ürünü.
  18. ✅ Stateless Streamable HTTP: sessionIdGenerator: undefined ile initializetools/list tek HTTP bağlantısında çalışır.
  19. ✅ Endpoint path guard: sadece /mcp MCP endpoint, /healthz liveness probe, diğerleri 404.
  20. ✅ Origin/Host guard (DNS rebinding + CSRF).
  21. MUTATION_REQUIRE_VERIFY=true iken update'lerde verify zorunlu, update_items_same_data reddedilir.
  22. ✅ Smoke testler proje içinde (scripts/smoke-stdio.mjs, scripts/smoke-http.mjs) — HTTP smoke test tools/list'i de doğrular.
  23. ✅ Host allowlist port-tolerant: mcp.example.com allowlist'i mcp.example.com:3333 Host header'ını da kabul eder.
  24. batch_update_items ve create_items apply sırasında all-or-nothing preflight (allow_partial_apply default false). Abort durumunda sıfır yazma yapılır.
  25. read_item single-mode: limit/page/offset reddedilir, default limit enjekte edilmez.
  26. content.text token-bounded: gerçek sonuç (şema, kayıtlar, diff, hata, summary) kompakt formatta, SCHEMA_TEXT_MAX_FIELDS / READ_TEXT_MAX_ROWS / READ_TEXT_MAX_CHARS limitleriyle.
  27. directus_apply_plan tool: dry-run plan'ını uygular, read-back verification yapar, idempotent (plan sadece bir kez apply edilir).
  28. directus_cancel_plan tool: pending plan'ı iptal eder.
  29. ✅ Dry-run mutation response'ları planId döndürür; content.text "DRY-RUN ONLY — hiçbir veri yazılmadı" içerir.
  30. APPLY_REQUIRES_PLAN=true (default): direct dry_run:false reddedilir, model önce plan almalı.
  31. ✅ Plan store: file-based (default) / memory, 0600 izinleri, TTL, max bytes, checksum.
  32. ✅ Delete işlemleri de plan akışını kullanır (confirm token plan içinde saklanır).
  33. ✅ Read-back mismatch post-write: warning olarak döner (throw etmez), plan applied_with_warning terminal durumu alır, tekrar apply edilemez.
  34. ✅ Concurrent apply protection: atomik pending → applying claim, ikinci eşzamanlı apply PLAN_ALREADY_IN_PROGRESS alır.
  35. ✅ Post-write unexpected error: plan failed_after_write terminal durumu alır, tekrar apply edilemez.
  36. directus_dry_run_mutation planId üretmez — README ve agent instruction'da belgelendi.
  37. ✅ Create işlemleri için gerçek read-back: created id bulunur, kayıt tekrar okunur, field değerleri doğrulanır.
  38. ✅ Startup + periyodik (5 dk) plan cleanup: expired/cancelled/applied plan dosyaları temizlenir.
  39. ✅ Batch tool *_json alanları string + array + object kabul eder (LibreChat uyumluluğu).
  40. directus_apply_plans tool: çoklu plan toplu apply + stop_on_error desteği.
  41. directus_cancel_plans tool: çoklu plan toplu cancel.
  42. directus_verify_fields_empty tool: post-apply verification (JSON array field doluluk kontrolü, client-side).
  43. ✅ JSON array alanlarında _nnull/_nempty filtrelerinin güvenilmez olduğu README'de belgelendi.
  44. directus_update_by_query_plan tool: query ile toplu update planı + bundle (model kayıt listesi üretmez).
  45. directus_apply_plan_bundle tool: bundle toplu apply + already-applied tolerant + verification.
  46. directus_plan_bundle_status tool: bundle durum sorgulama.
  47. directus_verify_fields_value tool: deep equality ile field değer kontrolü.
  48. verify_fields auto-generation: update_item + update_by_query_plan (model verify üretmez).
  49. ✅ Bundle sistemi: bundle_id ile tek referans (model planId listesi takip etmez).
  50. ✅ NEXT ACTION text'leri tüm mutation/apply response'larında.

Kaynaklar

from github.com/asukakimya/directus-mcp

Установка Directus Safe

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/asukakimya/directus-mcp

FAQ

Directus Safe MCP бесплатный?

Да, Directus Safe MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Directus Safe?

Нет, Directus Safe работает без API-ключей и переменных окружения.

Directus Safe — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Directus Safe в Claude Desktop, Claude Code или Cursor?

Открой Directus Safe на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Directus Safe with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории ai