İçeriğe geç
mcpd
daemon yok
Kaynak

Entegrasyon

Bir yerde bir daemon çalışıyor. Başka her şeyin ona ulaşmasının üç yolu ve aralarında nasıl seçim yapacağınız.

mcpd'nin tek bir kapısı, o kapıdan geçmenin de üç yolu var. Aşağıdakilerin hepsi aynı HTTP yüzeyiyle konuşur: SDK'lar onun üstüne konmuş bir kolaylıktır, MCP sunucusu ise onun bir istemcisidir.

Tek port, dört yüzey

Tek bir süreç bunların hepsini tek portta sunar, varsayılan olarak 4317. Sidecar yok, yanında çalıştırılacak bir collector yok, arayüz için ayrı bir statik dosya sunucusu yok.

YolNe olduğuAnahtar
/v1/traces, /v1/logs, /v1/metrics OpenTelemetry alımı. Uygulamanızda mcpd'ye özgü tek satır kod olmadan, herhangi bir OTel SDK'sı. Alım
/v1/ingest mcpd'nin kendi alımı. Bir yığın, altı sinyalin herhangi bir karışımını ve veri modelinin tamamını taşır; bir istek de bir yığın akışı taşır. Alım
/v1/api/… Sorgu API'si. Arayüz de SDK'lar da yalnızca buradan okur. Okuma, yazma, admin, execute veya worker; her endpoint kendisi bildirir
/ İkili dosyanın içine derlenmiş operatör arayüzü. İşaretleme için gerekmez; verisi sorgu API'sinden geçer

İki yol daha, /healthz ve /readyz, kimlik doğrulaması istemez; böylece bir yoklama onlara ulaşabilir. Bir endpoint'e erişilip erişilmediğini araştırırken buradan başlayın:

orada mı
$ curl -s "$MCPD/healthz"{"status":"ok","version":"0.1.0","spec_version":"1.0.0",…}$ curl -s "$MCPD/readyz"storage is not reachable: …# readyz depoya da dokunur, yani buradaki bir 503 daemon ayakta# ama arkasındaki veritabanı değil demektir.

SDK olmadan HTTP API

Dilinizde bir HTTP istemcisi varsa mcpd ile konuşabilir. Kaydolunacak bir şey, önceden açılacak bir proje ve el sıkışma yoktur. Her SDK aşağıdaki isteklerin üstüne konmuş bir kolaylıktır; bu yolu seçtiğinizde gruplama, yeniden deneme ve kimlik üretimi sizde kalır, onları da kendiniz yaparsınız.

Bir çalıştırmayı yazmak

POST /v1/ingest tek bir yığın alır. Bir yığın; span'leri, log'ları, metrikleri, puanları, geri bildirimleri ve yön notlarını birlikte taşıyabilir. mcpd'nin kendi kapısının OTLP'ninkinin yanında var olma sebebi de budur: altı sinyal aynı trace ve span kimliklerini paylaşır, dolayısıyla hepsini tek istekte göndermek, ikinci bir aramaya gerek kalmadan ilişkilerini korur.

O endpoint'e üç biçim ulaşır ve hangisi olduğunu içerik türü söyler. Hiçbir şey baytlardan çıkarılmaz, yani bir dizginin içine yeni satır eklemek bir gövdenin anlamını asla değiştiremez.

İçerik türüGövde
application/json Tek bir yığın nesnesi, her SDK'nın gönderdiği budur
application/json Yığın nesnelerinden bir dizi, elinde birkaç tane olan bir gönderen için
application/x-ndjson Satır başına bir yığın, tümü birden tutulmak yerine gövde geldikçe okunur. application/jsonl aynı anlama gelir

Her öğe kendi başına bir yığındır: bir ingest_key taşıdığında kendi işleminde saklanır, böylece yeniden gönderilen bir akış tek bir yığın olarak değil satır satır tanınır; anahtarsız öğeler ise birlikte saklanır. Bir satır okunamıyorsa ya da istek bir bütün olarak iki sınırdan birini aşıyorsa isteğin tamamı reddedilir ve hiçbir şey saklanmaz. Yanıt, hepsini kapsayan tek bir IngestResult'tır.

  • Spans

    What ran, nested, with timings

  • Logs

    Lines the run emitted

  • Metrics

    Numbers, with exemplars back to a run

  • Scores

    Machine evaluation

  • Feedback

    What a person thought

  • Aspect notes

    A sentence a model wrote about the run

trace_id Store Search Export

tek bir yığın

# One batch carries any mix of the five signals. Send the whole run at once.
curl -s -X POST "$MCPD/v1/ingest" \
  -H 'content-type: application/json' \
  -H "authorization: Bearer ${MCPD_API_KEY:-}" \
  -d @- <<JSON
{
  "ingest_key": "run-$TRACE",
  "spans": [
    {
      "trace_id": "$TRACE",
      "span_id": "$ROOT",
      "name": "support_agent",
      "kind": "agent_run",
      "status": "ok",
      "start_time": "$NOW", "end_time": "$NOW",
      "is_root": true, "is_event": false, "received_at": "$NOW",
      "input":  { "question": "Where is order 4417?" },
      "output": { "answer": "It is in the Leeds depot." },
      "correlation": { "project": "default", "environment": "production" }
    },
    {
      "trace_id": "$TRACE",
      "span_id": "$GEN",
      "parent_span_id": "$ROOT",
      "name": "chat",
      "kind": "model_generation",
      "status": "ok",
      "start_time": "$NOW", "end_time": "$NOW",
      "is_root": false, "is_event": false, "received_at": "$NOW",
      "model": "gpt-4o",
      "provider": "openai",
      "usage": { "input_tokens": 1284, "output_tokens": 210 },
      "correlation": { "project": "default" }
    }
  ]
}
JSON
# → {"accepted":2,"rejected":0,"duplicate":0}
#
# mcpd prices the model span from its own price book on the way in, so cost is
# stored with the run rather than recomputed later from rates that may have
# changed since.

İsteğin doğru yapması gereken dört şey:

  • Kimlikler OpenTelemetry biçimindedir. Bir trace için 32, bir span için 16 küçük harfli onaltılık karakter, hiçbiri tamamen sıfır olamaz. Başka bir şey, çalıştırdığınız diğer bütün araçlarla ilişkilendirmeyi bozar.
  • ingest_key yeniden denemeyi güvenli kılar. Bir yığın için sabit kalan bir idempotency anahtarıdır; böylece tekrar gönderim tanınır ve iki kez yazılmak yerine yinelenen olarak sayılır.
  • Anahtar bir başlıkta gider. Authorization: Bearer … ya da X-Mcpd-Key. Alım bir alım anahtarı ister, o da yalnızca daemon'da tanımlı anahtar varsa.
  • Yığınların sınırları vardır. Varsayılan olarak 32 MiB gövde ve 10.000 sinyal; ikisi de --max-body-bytes ve --max-batch-signals ile ayarlanır. Sinyal sınırı isteğin tamamını sayar, yani aynı sinyalleri satırlara bölmek bu sınırı aşmanızı sağlamaz. Büyük bir şey için content-encoding: gzip gönderin.

Yanıt her sinyal için ne olduğunu bildirir: {"accepted":2,"rejected":0,"duplicate":0}, reddedilen her şeyin sırasını ve sebebini veren bir errors dizisiyle birlikte. OTLP endpoint'leri bunun yerine OTLP'nin kendi biçiminde, reddedilen sayısını taşıyan bir partialSuccess nesnesiyle yanıt verir, çünkü bir OpenTelemetry gönderenin okuduğu budur.

Geri okumak

okuma

# Read it back. Any filter in the API works here.
curl -s "$MCPD/v1/api/traces?project=default&limit=10"

# The whole run, with its logs, scores and feedback in one request.
curl -s "$MCPD/v1/api/traces/$TRACE?include=all"

# Find the run by something the model actually said.
curl -s --get "$MCPD/v1/api/traces" \
  --data-urlencode 'project=default' \
  --data-urlencode 'q=Leeds depot'

# Find the runs a scorer marked down. This is the query that finds bad answers.
curl -s --get "$MCPD/v1/api/traces" \
  --data-urlencode 'project=default' \
  --data-urlencode 'has_score=answer_relevance' \
  --data-urlencode 'score_below=0.5'

# Where the money went, over the last week, by model.
curl -s --get "$MCPD/v1/api/analytics/cost" \
  --data-urlencode 'project=default' \
  --data-urlencode 'from=-7d' \
  --data-urlencode 'group_by=model'

Filtreler birleşir, sayfalama imleçle yapılır ve project yazılmadığında daemon'ın varsayılan projesi kullanılır. Saklanan payload'lar üzerinde tam metin araması q parametresiyle yapılır; "modelin şunu söylediği çalıştırmayı bul" isteğini bir grep değil de bir sorgu hâline getiren odur.

mcpd'nin kendi ürettiği bir hata, makinenin okuyabileceği sabit bir error kodu, sorunu çözecek kişi için yazılmış bir message ve tek bir parametre hatalıysa bir field taşıyan JSON olarak döner. Koda göre dallanın, mesajı gösterin.

Bir hata türü bu biçimde değildir ve öyle olduğunu varsayan bir istemci yanıtı ayrıştıramaz. Endpoint'in beklediği türe çözülemeyen bir istek gövdesi, mcpd kodu çalışmadan önce web çerçevesi tarafından reddedilir; yanıt 422, content-type: text/plain ve missing field `scorer_id` gibi bir mesajdır. Hiç JSON olmayan gövde sözdizimi ise yine düz metin olarak 400 alır. İki durumda da dallanılacak bir error kodu yoktur. Bu, türü belirtilmiş bir gövde alan her endpoint için geçerlidir: POST /v1/api/scores, /v1/api/feedback, /v1/api/score, /v1/api/datasets, onun items ve capture yolları ve /v1/api/experiments. Alım kapıları gövdeyi kendileri okur, bu yüzden /v1/ingest ve OTLP yolları JSON biçiminde yanıt verir. Bir 4xx üzerindeki JSON olmayan gövdeyi kendi kodunuzdan gelen bozuk bir istek sayın ve metin olarak okuyun.

SDK'lar

Altı dil: TypeScript, Python, Rust, Go, Kotlin ve Elixir. Her biri olabildiği yerde kendi diline uygun, önemli olan yerde ise aynıdır. TypeScript hızlı başlangıcını okumuş biri Kotlin'inkini önceden kestirebilir.

Ham HTTP'ye göre kazandıklarınız:

  • Gruplama ve arka planda bir worker, yani bir span kaydetmek kuyruğa ekleyip geri döner.
  • 429 ve yeniden denemeye değer 5xx kodlarında beklemeli yeniden deneme, bozuk kalacak bir 4xx'te ise hiç denememe.
  • Kimlik üretimi, bağlam taşıma ve gelirken ayrıştırılıp giderken yayılan W3C traceparent, böylece bir trace servis sınırını aşar.
  • Daemon'ınkiyle aynı belirtimden üretilen türler, yani bir alan SDK'da başka, depoda başka bir anlama gelemez.

Her dilde geçerli üç söz vardır ve bir SDK'yı yoğun bir kod yoluna koymayı güvenli kılan da onlardır. Ölçümleme asla hata fırlatmaz: erişilemeyen bir daemon en fazla aralıkta bir kez loglanır ve yutulur. Asla engellemez: ağ işi worker'da olur, yani ajanınızın gecikmesi mcpd'nin ayakta olmasına bağlı değildir. Kuyruğu da sınırlıdır: dolduğunda en eski span'ler düşürülür ve bir sayaç artar, çünkü sınırsız bir tampon bir ağ kesintisini bellek yetmezliğiyle sonlanan bir sürece çevirir.

bağlanma

import { Mcpd } from '@mcpd/sdk'

// Every field here also reads an environment variable, so the same code runs
// unchanged in development and in production. With no arguments at all this
// talks to a daemon on http://localhost:4317.
const mcpd = new Mcpd({
  project: 'support',
  environment: 'development',
  serviceName: 'support-agent',
  serviceVersion: '1.4.0',
})

Argümansız kurulan bir istemci yerel bir daemon'a karşı çalışır. Her şey kod içinden ya da ortam değişkeniyle ayarlanabilir, kod önceliklidir; değişken adları her dilde aynıdır, böylece iki servis arasında kopyalanan bir değer, ikisi farklı dillerde yazılmış olsa da çalışmayı sürdürür: MCPD_ENDPOINT, MCPD_API_KEY, MCPD_PROJECT, MCPD_ENVIRONMENT, MCPD_SERVICE_NAME, MCPD_SERVICE_VERSION, MCPD_SAMPLE ve MCPD_ENABLED. MCPD_ENABLED=false her çağrıyı, hâlâ geçerli nesneler döndüren bir boş işleme çevirir; böylece bir test takımı hiçbir koda dokunmadan mcpd'yi kapatabilir.

Birbirlerinin yerine geçerler ve bu sınanır

Birim testleri her SDK'nın kendi yazarlarının kastettiğini yaptığını kanıtlar. Altısının birbiriyle uyuştuğuna dair hiçbir şey kanıtlamaz. Bu yüzden uygunluk takımı, tek bir canlı daemon'a karşı her dilden bir tane olmak üzere aynı kanonik çalıştırmayı yazar, buna aynı çalıştırmayı yalnızca curl ile gönderen yedinci bir kanal ekler ve geri gelenin yapıca birebir aynı olduğunu doğrular:

Cross-language conformance

tests/e2e/run.mjs

Channel root tree status error model tokens score feedback ids
TypeScript
Python
Go
Elixir
Kotlin
Rust
cURL
TypeScript
Python
Go
Elixir
Kotlin
Rust

13 languages, one canonical run, one daemon. Every trace comes back the same shape.

481 checks

A conformance panel with one row per language and one column per family of check. Every cell passes. The families are: the root span is the agent run; every child hangs off the root; a failed step makes the whole run an error, and only that step; the error carries a message, on the step that failed; the model call records its model; token usage and cost survive the wire; the scores land, with their scale; the feedback lands; ids are OpenTelemetry-shaped. The suite asserts 37 checks in each of the 13 languages.

Yeni bir servis için dil seçiyorsanız, "trace'ler elimizdekilerle aynı görünecek mi" sorusunun yanıtı budur.

Diliniz bu altısından biri değilse

Bir OpenTelemetry SDK'sını /v1/traces adresine yöneltin, yeni kod yazmanız gerekmez. mcpd bir OTLP alıcısıdır ve hem OpenTelemetry'nin gen_ai.* uzlaşımını hem de OpenInference'ın llm.* uzlaşımını eşler, yani halihazırdaki ölçümlemeniz doğru yere düşer. OpenTelemetry sayfasına bakın.

Bir yapay zekâ ajanından, MCP üzerinden

Üçüncü yol bir servis için değil bir ajan içindir: trace'leri kodunuzun okuması yerine, bir kodlama ya da destek ajanı onları sizin adınıza okur ve onlara dair bir soruyu yanıtlar. "Son çalıştırma neden başarısız oldu", "bu hafta bir puanlayıcının düşük not verdiği çalıştırmaları göster", "bu oturum ne kadara mal oldu". mcpd bunu Model Context Protocol üzerinden sunar.

Üç araç

Otuz değil, üç. Bunlar tek bir incelemenin üç hamlesidir: hangi çalıştırma, içinde ne oldu ve bu tek bir çalıştırma mı yoksa hepsi mi.

AraçYanıtladığı soru
find_runs Hangi çalıştırma. Her biri bir satır: kimlik, yaş, durum, süre, maliyet, adım. Duruma, modele, kullanıcıya, oturuma veya serbest metne göre filtreleyin; en yavaşları almak için süreye göre sıralayın. Varsayılan olarak son 24 saat.
get_run Ne olduğu. Şelale görünümü, başarısız olan adım ve sebebi, prompt'lar ve tamamlamalar, log'lar, puanlar. Payload'lar sınırlanır, böylece tek bir çalıştırma bir bağlam penceresini dolduramaz.
run_stats Tek bir çalıştırma ya da hepsi. Bir pencere boyunca maliyet, hata oranı, gecikme ve token; modele, ada, duruma, ortama veya kullanıcıya göre gruplanır. Yüzde 40 hata oranı, okuduğunuz çalıştırmanın sorun olmadığı anlamına gelir.

Sonuçlar JSON yerine metin olarak döner; bu, kimin bağlamının harcandığına dair bilinçli bir seçimdir. Kırk span nesnesinin çoğu noktalama işaretidir; aynı çalıştırma şelale olarak çizildiğinde birkaç yüz token tutar ve ajan biçimini görebilir.

Nasıl kurulduğu

MCP sunucusu ikinci bir servis değil, aynı ikili dosyanın bir alt komutudur. mcpd mcp olarak çalışır, standart giriş ve çıkış üzerinden JSON-RPC konuşur ve kendine ait veri tutmaz. Açılacak bir port, ayakta tutulacak bir süreç ve dağıtılacak bir şey yoktur: ajan host'u, bir konuşma gerektirdiğinde onu bir alt süreç olarak başlatır, sonra da durdurur.

Tuttuğu tek şey bir daemon bağlantısıdır. Her araç çağrısı, yukarıda anlatılan aynı sorgu API'sine bir istek olur; aynı okuma anahtarı ve aynı proje kapsamıyla, yani bir ajan kendisine verilen anahtarın ulaşamadığı bir projeye ulaşamaz. Bir ajana bu sunucuyu vermenin etki alanı, ona o anahtarı vermenin etki alanıyla tam olarak aynıdır; bir sunucu yapılandırmadan önce denetleyeceğiniz özellik de budur.

Araç kümesi bilerek küçüktür. Geniş bir yüzey modeli kötü seçim yapmaya iter ve bir ajanın davranışına dair soruların çoğu çalıştırmaları listelemeye, birini açmaya ve bir pencere üzerinde sayısal bir soru sormaya indirgenir. Bir istemci güncel kümeyi, burada yazılıp geride kalabilecek bir listeden değil, protokolün kendi tools/list çağrısından öğrenir.

Bir istemciyi yapılandırmak

Bir MCP host'u sunucuyu bir komut, birkaç argüman ve bir ortamla başlatır. Tam dosya ve tam anahtar mcpd'nin değil host'un işidir, o yüzden onun belgelerine bakın; sizden istediği üç değer her yerde aynıdır.

Host'un istediğiDeğer
KomutHost'un çalıştırabileceği bir yolda duran mcpd ikili dosyası.
Argümanlar mcp alt komutu. Ortam yerine argüman geçmeyi yeğleyen bir host için --endpoint, yani sorgulanacak daemon, bir de --api-key, yani onun için bir okuma anahtarı alır.
Ortam Aynı iki değer, MCPD_ENDPOINT ve MCPD_API_KEY olarak. Endpoint varsayılan olarak http://127.0.0.1:4317'dir ve daemon'da tanımlı anahtar yoksa anahtar yazılmayabilir.

host'un çalıştırdığı

MCPD_ENDPOINT=https://mcpd.internal MCPD_API_KEY=$READ mcpd mcp

O anahtarı verirken akılda tutulacak üç şey.

  • Ajana bir ingest anahtarı değil, bir read anahtarı verin. Kapsamlar, span yazmaya yarayan bir kimlik bilgisinin aynı zamanda saklanmış her prompt'a erişim anlamına gelmemesi için vardır.
  • Anahtarı bir projeyle sınırlayın. Üst düzeyde yapılandırılmış bir anahtar daemon'daki her projeye ulaşır.
  • Siz bir admin listesi tanımlayana kadar bir okuma anahtarının trace silebildiğini bilin; admin kapsamı tanımsızken okuma listesine geri düşer. Kendisine hiç admin listesi verilmemiş bir daemon'da okuma anahtarı tutan bir ajan bir çalıştırmayı silebilir.

Üçü de Güvenlik sayfasında.

Hangisini kullanmalı

YolNe zaman
HTTP Dilinizin SDK'sı yoksa, bir kabuktan ya da bir işten gönderiyorsanız ya da hiç bağımlılık istemiyorsanız.
Bir SDK Bir uygulamayı ölçümlüyorsanız, yani gruplama, yeniden deneme ve hiç hata fırlatmamak bağımlılıktan daha değerliyse.
OpenTelemetry Zaten OTel yayıyorsanız ve kütüphane eklemektense bir URL değiştirmeyi yeğliyorsanız.
MCP Saklanmış çalıştırmalar hakkında soruları, zamanlı çalışan bir kod değil bir insan bir ajana soruyorsa.

Bunlar karışır. TypeScript SDK'sıyla yayan bir filo, curl ile puan gönderen gecelik bir iş ve hepsini MCP üzerinden geri okuyan bir ajan üç dağıtım değil, tek bir dağıtımdır.

Sonraki