2026-07-08

Bir Protokol Olarak Araçlar

Araçlar, başka hiçbir host'un keşfedemeyeceği hardcoded bir dict. Elle yazılmış minimal bir MCP server ve host, tool kullanımını discovery ve multiplexing'li bir JSON-RPC wire protokolüne dönüştürür.

Neler öğreneceksin

Core track production-şeklinde bir ajanla bitti: plan yapabiliyor, kötü bir step’ten toparlanabiliyor, koşular arasında hatırlayabiliyor, spirale girmeden önce kendini durdurabiliyor, bir crash’ten sağ çıkabiliyor, bir insan için duraklayabiliyor ve içine bakılabiliyor. Makul her ölçüye göre gerçek bir ajan. Yine de Part 1’den beri tutan ve hiç sorgulanmamış tek bir varsayım var: ajanın tool’ları aynı process’in içine gömülmüş hardcoded bir Python dictionary’si. Bu, Part 1’deki TOOL_SCHEMAS’tı ve o zamandan beri ajanın tüm aksiyon uzayı oldu. Sorun şu ki, bir process’teki bir dict başka bir ajanla paylaşılamaz, deploy time’da takas edilemez ya da onu import etmemiş bir host tarafından keşfedilemez. Her entegrasyon, ajanın kendi koduna elle lehimlenmiş özel bir wiring. Bu parça frontier track’i bunu düzelterek açıyor ve çözüm bir protokol. Model Context Protocol (MCP), “ne tür tool’ların var ve onları nasıl çağırırım” sorusunu bir wire protokolüne dönüştürür: bir server capability’leri ilan eder ve bir host onları JSON-RPC üzerinden keşfedip çağırır. Her iki tarafı da elle, yalnızca stdlib ile inşa ediyoruz. İlki, JSON-RPC isteklerini yanıtlayan bir MCP server: initialize handshake’i (protokol versiyonu artı capability negotiation), tools/list, tools/call ve MCP’nin üç server primitive’i, tools, resources ve prompts. İkincisi, handshake’i yapan, tools/list’i çağıran ve keşfedilen JSON inputSchema’yı doğrudan Part 1’in validator ve controller’ına besleyen bir MCP host: hardcoded dict’in yerini alır, sonra iki server’ı tek bir tool paletinde multiplex eder. Tool’lar bir protokol konuştuğu an, herhangi bir host herhangi bir server’ı kullanabilir ve ajanın aksiyon uzayı, neye bağlanırsa ondan, çalışma anında bir araya getirilir.

Ön koşullar

Temel Python ve JSON için bir his yeterli: bir object, bir liste, bir string. Hepsi bu. Part 1 yardımcı olur, çünkü host onun tool-argümanı validator’ını değiştirmeden yeniden kullanıyor, ve Part 2 yardımcı olur, çünkü buradaki refund dikkatle ele almayı öğrendiğimiz aynı side-effect üreten tool. Ama yazı kendi içinde bütünlüklü: daha önceki bir fikre yaslandığı yerde onu tek bir cümleyle yeniden ifade eder ve ReAct loop’unu yeniden öğretmez ya da validator’ı yeniden türetmez; bunlar Part 1 ve 2’ye aittir ve burada referans verilir. Eşlik eden kod, mcp_server_and_host.py, API anahtarı, ağ ve bağımlılık olmadan offline çalışır, böylece her satırı okuyup bu yazıdaki her JSON-RPC frame’ini kendiniz yeniden üretebilirsiniz. Varsayılan transport, gerçek protokol frame’leri yazdıran in-process bir shim’dir; gerçek LLM controller’ı (generate()) ve gerçek subprocess-over-stdio transport’u her biri tek bir adım uzaklıkta, tam olarak Part 1’den 11’e kadarki gibi.

Core track’ten beri yeni olan ne

Neyin yeni olduğu konusunda kesin olmak istiyorum, çünkü bu gerçekten yeni bir zemin, daha önceki serideki herhangi bir şeyin rafine edilmiş hâli değil. Şu ana kadarki her parça, ta RAG’e kadar, ajana tool’larını aynı şekilde verdi: yazarın wire ettiği bir tek-process dictionary’si olarak. RAG Part 19 bir tool dict’ini hardcode etti; buradaki Part 1 TOOL_SCHEMAS’ı hardcode etti; Part 6’daki memory tool’ları aynı in-process contract’la deklare edildi. Her durumda ajan tool’larını biliyordu, çünkü birisi onları ajanın kendi kaynağına yazmıştı. MCP’nin değiştirdiği şey, o bilginin kaynağı. Tool’lar, yazma anında import edilen sembollerden değil, çalışma anında JSON-RPC üzerinden gelen keşfedilmiş, şema ile tanımlanmış capability’ler hâline gelir. Bir server, bir tool’un adını, açıklamasını ve inputSchema’sını yayımlar; host onları sorarak öğrenir. Ve MCP tool’lardan fazlasını standartlaştırır: üç primitive tanımlar, tools (çağrılabilir aksiyonlar), resources (host’un fetch edebileceği read-only veri) ve prompts (adlandırılmış prompt template’leri), böylece bir server her üçünü tek bir handshake’in arkasında sunabilir. Bu parçanın yeniden inşa etmek yerine bilerek yeniden kullandığı tek şey Part 1’in validator’ı. Kontrol ettiği şema artık hardcoded bir dict’ten gelmiyor; tools/list’ten hat üzerinden geliyor. Aynı validator, aynı garanti, farklı kaynak. Ona referans veriyoruz ve onu yeniden türetmiyoruz.

Handshake ve discovery

Bir protokol bir handshake ile başlar, ve MCP’ninki initialize. Host, konuştuğu protokol versiyonunu ve kim olduğunu ilan ederek bağlantıyı açar; server, üzerinde anlaştığı versiyon, kendi kimliği ve desteklediği capability setiyle yanıt verir. İşte hat üzerindeki ilk iki frame, artefaktın gerçek çıktısından alıntılanmış:

    --> {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "clientInfo": {"name": "agents-by-hand-host"}}}
    <-- {"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "2025-06-18", "serverInfo": {"name": "support-server"}, "capabilities": {"tools": {}, "resources": {}, "prompts": {}}}}
    [host] connected to support-server (protocol 2025-06-18, capabilities: tools, resources, prompts)

O frame’lerin şeklini oku, çünkü bu sadece JSON-RPC: her istek "jsonrpc": "2.0", bir id, bir method ve params taşıyan bir JSON object’i; her yanıt id’yi echo eder ve ya bir result ya da bir error taşır. id, birkaçı aynı anda havadayken bile isteğin ve yanıtın eşleştirilmesini sağlayan şeydir. Handshake’te host protocolVersion: "2025-06-18" gönderir ve server aynı versiyonu geri onaylar: bu version negotiation, yıllar arayla yazılmış bir host ile bir server’ın başka hiçbir şey söylemeden önce bir lehçe üzerinde anlaşmasını sağlayan adım. Server ayrıca capability’lerini ilan eder: support-server tools, resources ve prompts deklare eder, yani her üç primitive’i de sunar. Yalnızca çağrılabilir aksiyonları olan bir server sadece tools ilan ederdi ve host ondan resources ya da prompts istememesi gerektiğini bilirdi. Handshake bitince host discovery call’unu yapar:

    --> {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}
    <-- {"jsonrpc": "2.0", "id": 2, "result": {"tools": [{"name": "search_policy", "description": "search the support/policy index", "inputSchema": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}}, {"name": "process_refund", "description": "issue a refund for an order (side-effecting)", "inputSchema": {"type": "object", "properties": {"order_id": {"type": "string"}, "amount": {"type": "number"}}, "required": ["order_id", "amount"]}}]}}
    [host] discovered tools: ['search_policy', 'process_refund']

O tek yanıt protokolün tüm meselesi. Host TOOL_SCHEMAS’ı import etmedi; sordu, ve server ona adları, açıklamaları ve inputSchema object’leriyle iki tool geri verdi. search_policy zorunlu bir query string’i alır; process_refund zorunlu bir order_id string’i ve zorunlu bir amount number’ı alır ve açıklaması onu side-effect üreten olarak işaretler. Host artık server’ın tüm aksiyon uzayını biliyor ve onu hat üzerinden öğrendi. discovered tools: ['search_policy', 'process_refund'] satırı, ajanın paletinin kaynağını hiç görmediği bir server’dan çalışma anında bir araya getirilmesi.

Üç primitive

MCP, bir server’ın açığa çıkarabileceği üç tür şeyi standartlaştırır ve bunlar ajanın gerçekten ihtiyaç duyduğu üç şeye karşılık gelir. Tools, side-effect’leri ya da hesabı olan çağrılabilir aksiyonlardır. Resources, bir doküman ya da bir config dosyası gibi, host’un URI ile fetch edebileceği read-only veridir. Prompts, host’un wording’i hardcode etmek zorunda kalmaması için server’ın küratörlüğünü yaptığı adlandırılmış, yeniden kullanılabilir prompt template’leridir. Tek handshake, üç primitive, her biri kendi method’uyla. İşte host her üçünü kullanırken, artefaktın gerçek çıktısından alıntılanmış:

  tools/call:
    --> {"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "search_policy", "arguments": {"query": "refund window"}}}
    <-- {"jsonrpc": "2.0", "id": 3, "result": {"content": [{"type": "text", "text": "Refunds are accepted within 30 days; after the window a 10% restocking fee applies."}], "isError": false}}
    [host] result: Refunds are accepted within 30 days; after the window a 10% restocking fee applies.
  resources/read:
    --> {"jsonrpc": "2.0", "id": 4, "method": "resources/read", "params": {"uri": "file:///policies/refund.md"}}
    <-- {"jsonrpc": "2.0", "id": 4, "result": {"contents": [{"uri": "file:///policies/refund.md", "text": "Refunds within 30 days; 10% restocking fee after."}]}}
    [host] resource text: 'Refunds within 30 days; 10% restocking fee after.'
  prompts/get:
    --> {"jsonrpc": "2.0", "id": 5, "method": "prompts/get", "params": {"name": "refund_decision"}}
    <-- {"jsonrpc": "2.0", "id": 5, "result": {"messages": [{"role": "user", "content": {"type": "text", "text": "Decide the refund for {order}, citing the policy."}}]}}
    [host] prompt template: 'Decide the refund for {order}, citing the policy.'

Her call’u kendi primitive’ine karşı oku. tools/call (id 3) search_policy’yi arguments: {"query": "refund window"} ile çalıştırır ve type: "text" olan bir content bloğu, Refunds are accepted within 30 days; after the window a 10% restocking fee applies., artı bir server’ın başarıyı tool-seviyesinde bir başarısızlıktan ayırt etme şekli olan isError: false geri alır. resources/read (id 4) file:///policies/refund.md URI’sini fetch eder ve metnini, Refunds within 30 days; 10% restocking fee after., döner: read-only veri, execution yok, adıyla çağrılmak yerine URI ile adreslenmiş. prompts/get (id 5) adlandırılmış template refund_decision’ı alır, ki Decide the refund for {order}, citing the policy. metniyle bir mesaj olarak geri gelir, host’un doldurup modeline gönderebileceği server-küratörlüğünde bir prompt. Üç method, üç primitive, hepsi aynı handshake’in ve aynı JSON-RPC envelope’unun arkasında. id’nin tırmandığına dikkat et (3, 4, 5), böylece her yanıt isteğiyle eşleşir, ve bunları çağıran host kodunun, hangi primitive’e uzandığından bağımsız olarak şekil olarak aynı olduğuna.

A diagram of one MCP server, the support-server, exposing three primitives to a host across a labelled JSON-RPC boundary. At the top, an initialize handshake box notes protocol version 2025-06-18 negotiated and capabilities tools, resources, prompts advertised. Below it, three parallel lanes each show a request arrow from host to server and a response arrow back. The first lane, tools, labelled callable actions, shows tools/call id 3 with name search_policy and arguments query refund window going right, and a text content block coming back reading refunds are accepted within 30 days, after the window a 10 percent restocking fee applies, tagged isError false. The second lane, resources, labelled read-only data fetched by URI, shows resources/read id 4 with uri file slash slash slash policies slash refund dot md going right, and the text refunds within 30 days, 10 percent restocking fee after coming back. The third lane, prompts, labelled reusable templates, shows prompts/get id 5 with name refund_decision going right, and a user message coming back reading decide the refund for the order, citing the policy. A caption strip reads: one handshake, three primitives, tools to call, resources to read, prompts to fill, all over the same JSON-RPC envelope.
Fig 1 MCP's three server primitives behind one handshake. After the initialize handshake negotiates the protocol version and the server advertises its capabilities, the host reaches the support-server through three JSON-RPC methods, one per primitive. tools/call (id 3) runs the callable action search_policy with arguments query refund window and gets back a text content block, refunds are accepted within 30 days then a 10 percent restocking fee, with isError false. resources/read (id 4) fetches the read-only resource at the URI file slash slash slash policies slash refund dot md and returns its text, refunds within 30 days, 10 percent restocking fee after. prompts/get (id 5) retrieves the named prompt template refund_decision, decide the refund for the order, citing the policy. The figure shows that tools are callable actions, resources are read-only data fetched by URI, and prompts are reusable templates, all exposed by one server behind one handshake over the same JSON-RPC envelope.

Keşfedilen şemalar controller’ı sürer

İşte protokolün karşılığını verdiği ve Part 1’e geri bağlandığı yer. Host’un process_refund için keşfettiği inputSchema, sıradan bir JSON Schema object’i, ve bu tam olarak Part 1’in validator’ının zaten kontrol etmeyi bildiği şey. Dolayısıyla host hat üzerinden keşfedilen şemayı doğrudan değiştirilmemiş validator’a verir ve controller’ın argüman kontrolü her zamanki gibi çalışır, sadece artık kurallar bir dict’ten değil ağ üzerinden geldi. Keşfedilen process_refund şemasına karşı doğrulanan üç argüman setini izle, artefaktın gerçek çıktısından alıntılanmış:

    validate {"order_id": "ORD-3300", "amount": 180.0} -> OK
    validate {"order_id": "ORD-3300"} -> REJECTED: missing required arg 'amount'
    validate {"order_id": "ORD-3300", "amount": "lots"} -> REJECTED: arg 'amount' must be number
    The schema was not hardcoded; it arrived from tools/list. Part 1's guarantee, over MCP.

Üçünü de oku. İyi biçimlendirilmiş bir call, {"order_id": "ORD-3300", "amount": 180.0}, geçer: her iki zorunlu key de mevcut ve amount bir number, dolayısıyla validator OK döner. amount’u düşür ve validator onu missing required arg 'amount' ile reddeder, çünkü şemanın (host’un hat üzerinden okuduğu) required listesi onu adlandırır. amount’u "lots" string’i olarak gönder ve arg 'amount' must be number ile reddedilir, çünkü şemanın properties’i amount’un type: "number" olduğunu söyler. Bu byte-byte Part 1 contract’ı, call hiç fire etmeden önce kontrol edilen tipli bir şema, sadece şema artık ajanın yazarının yazdığı bir şey değil. tools/list’ten geldi. Son satır bunu açıkça söyler: şema hardcode edilmedi, protokolden geldi, ve Part 1’in garantisi MCP üzerinde tutar. Aşağıdaki amiral figür, handshake’ten discovery’ye, oradan doğrulanmış bir call’a kadar tüm sekansta adım adım ilerlemeni sağlar, her JSON-RPC frame’inin hat üzerinden geçişini izleyerek.

Open figure ↗

Fig 2 The full MCP exchange, interactive, from handshake to a schema-validated tool call. Step through the wire frames in order: the initialize handshake where the host announces protocol version 2025-06-18 and the server confirms it back and advertises capabilities tools, resources, prompts; the tools/list discovery call where the host learns search_policy and process_refund with their inputSchemas; and then the discovered process_refund schema being fed straight into Part 1's validator. Three argument sets are checked against that wire-discovered schema: order_id ORD-3300 with amount 180 passes as OK, order_id ORD-3300 alone is rejected for the missing required argument amount, and order_id ORD-3300 with amount the string lots is rejected because amount must be a number. The figure makes the central claim concrete, that the schema arrived over JSON-RPC rather than from a hardcoded dict, and Part 1's typed-argument guarantee runs unchanged over the protocol.

Tek host, çok server

Tek bir server faydalıdır, ama bir protokolün gerçek erişimi birden fazlasıyla ortaya çıkar. Her server aynı handshake’i ve aynı discovery call’unu konuştuğu için, bir host aynı anda birkaçına bağlanıp onları multiplex edebilir: her birinin ilan ettiğini tek bir tool paletinde birleştirir, ve ajan onun üzerindeki herhangi bir tool’u, hangi server’ın arkasında olduğunu bilmeden ya da umursamadan çağırır. İşte host iki server’a, support-server ve bir catalog-server’a bağlanırken ve ortaya çıkan birleşik palet, artefaktın gerçek çıktısından alıntılanmış:

    [host] connected to support-server (protocol 2025-06-18, capabilities: tools, resources, prompts)
    [host] connected to catalog-server (protocol 2025-06-18, capabilities: tools)
    [host] palette across 2 servers:
      search_policy    from support-server
      process_refund   from support-server
      search_products  from catalog-server
    A multi-hop task now uses tools from different servers transparently:
      search_products (catalog-server) -> Acme Corp was acquired by Globex in 2024.
      search_products (catalog-server) -> Globex-branded wireless earbuds carry a 2-year limited warranty.
      process_refund  (support-server) available on the same palette

Paleti oku. support-server tools, resources, prompts ilan etti; catalog-server yalnızca tools ilan etti, ve host buna saygı duyar, ondan sunmadığı hiçbir şeyi istemez. Birleşik palet üç tool tutar: support-server’dan search_policy ve process_refund, ve catalog-server’dan search_products. Host her tool adından onu destekleyen server’a giden küçük bir map tutar, böylece ajan search_products’u çağırdığında host tools/call’u catalog-server’a yönlendirir ve process_refund’u çağırdığında support-server’a yönlendirir, hepsi ajanın mantığı değişmeden. Multi-hop koşu bunun çalıştığını gösterir: iki search_products call’u catalog-server’a vurur (Acme Corp was acquired by Globex in 2024. ve Globex-branded wireless earbuds carry a 2-year limited warranty. döndürerek), ve support-server’dan process_refund tam aynı palette hazır oturur. Protokolün bileşik getirisi budur: bir server ekle, ve onun tool’ları ajanın aksiyon uzayında basitçe belirir, yeni wiring yok, ajana edit yok.

A diagram of one host in the centre connected to two MCP servers, with a merged tool palette below it. On the left, a server box labelled support-server lists capabilities tools, resources, prompts and the tools search_policy and process_refund. On the right, a server box labelled catalog-server lists capability tools only and the tool search_products. Two handshake arrows, each marked protocol 2025-06-18, connect the host to the two servers. Below the host, a single merged palette box headed palette across 2 servers lists three rows: search_policy from support-server, process_refund from support-server, and search_products from catalog-server, each row with a small routing arrow pointing back to its backing server. At the bottom, a multi-hop strip shows search_products called twice against catalog-server returning Acme Corp was acquired by Globex in 2024 and Globex-branded wireless earbuds carry a 2-year limited warranty, with process_refund from support-server marked available on the same palette. A caption strip reads: every server speaks the same handshake, so the palette is the union of all of them, assembled at run time with no new wiring.
Fig 3 One host multiplexing two MCP servers into a single tool palette. The host completes the handshake with two servers: support-server, which advertises capabilities tools, resources, and prompts, and catalog-server, which advertises only tools. The host merges what each one discovers into one palette of three tools, keeping a map from tool name to backing server: search_policy and process_refund are routed to support-server, and search_products is routed to catalog-server. A multi-hop task then calls across servers transparently, two search_products calls go to catalog-server and return that Acme Corp was acquired by Globex in 2024 and that Globex-branded wireless earbuds carry a 2-year limited warranty, while process_refund from support-server sits ready on the same palette. The figure shows that because every server speaks the same handshake and discovery call, the agent's action space is the union of all connected servers, assembled at run time with no new wiring.

Şimdi in-process, production’da stdio

Şimdi dürüst kısım, çünkü transport tüm feasibility sorusu ve üstünden el sallamak kolay. Eşlik eden koddaki varsayılan, in-process bir JSON-RPC shim: host server.handle(request)’i doğrudan, aynı Python process’inde, socket olmadan ve ikinci bir process işin içinde olmadan çağırır. Ama gönderdiği ve aldığı her mesaj verbatim yazdırılan gerçek bir JSON-RPC frame’i, dolayısıyla yukarıda okuduğun byte’lar tam olarak bir wire’dan geçecek byte’lar. Koşunun açılış satırı bunu söyler:

[transport] in-process JSON-RPC shim (default): the host calls server.handle()
directly, but every frame below is a real JSON-RPC message printed verbatim.

Koşuyu deterministic ve reproducible kılan şey budur: ağ yok, timing yok, koordine edilecek ikinci bir process yok, sadece protokol frame’lerine bürünmüş fonksiyon call’ları. Gerçek deployment farklıdır. Server kendi işletim sistemi process’i olarak çalışır ve host ona stdio üzerinden konuşur: JSON-RPC isteklerini server’ın standard input’una yazar ve yanıtları onun standard output’undan okur, bir lokal process diğerine boru bağlar, lokal bir server için hâlâ ağ gerekmez. Eşlik eden kod o path’i de gösterir, ama açıkça etiketlenmiş, açıklayıcı bir transcript olarak, dondurulmuş doğrulanmış bir koşu olarak değil. Tam olarak şöyle işaretlenmiştir:

THE REAL TRANSPORT (illustrative, not executed here): server as a subprocess over
stdio. Two local processes, no network. Shown for reference; the verified run above
uses the in-process shim.
    $ python mcp_server.py            # process A: reads JSON-RPC from stdin
    host -> server (stdin):  {"jsonrpc":"2.0","id":1,"method":"initialize",...}
    server -> host (stdout): {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":...}}
    host -> server (stdin):  {"jsonrpc":"2.0","id":2,"method":"tools/list"}
    server -> host (stdout): {"jsonrpc":"2.0","id":2,"result":{"tools":[...]}}
    (same frames as above, now crossing a pipe between two OS processes)

Akılda tutulacak şey, iki path’in yalnızca transport’ta farklı olması, protokolde değil. Frame’ler özdeş; değişen şey, handle()’ın aynı process’te bir fonksiyon call’u mu yoksa iki process arasında bir boru mu olduğu. Bu tasarım gereği: bir protokol tam olarak, byte’ların ne söylediğine dokunmadan nasıl yolculuk ettiğini takas etmeni sağlayan katmandır. In-process shim doğrulanmış varsayılandır, tam olarak protokolü process yönetiminin karmaşasından izole ettiği için, ve stdio path’i, tam aynı frame’leri taşıyan birkaç gerçek transport’tan biridir (lokal server’lar için stdio, uzak olanlar için HTTP). Stdio transcript’ini bilerek açıklayıcı olarak etiketledim: process başlatma, buffering ve protokol versiyonunun kendisi hızlı hareket eder, dolayısıyla onu byte-dondurulmuş bir koşu değil, şeklin sadık bir resmi olarak ele al.

Kenar notu: skills ve progressive tool disclosure

Çok sayıda server’a bağlanan bir host, palet büyüdüğü an bir ölçeklenme sorunuyla karşılaşır. Bir düzine server’a bağlan ve yüzlerce tool tutuyor olabilirsin, ve her birinin şemasını her turn’de modelin context’ine tıkmak hem pahalı hem kafa karıştırıcı: model hiç kullanmayacağı tool’ları okuyup geçmek zorunda ve fatura görevle değil katalogla büyür. Çözüm MCP’nin discovery call’larıyla doğal olarak eşleşir: progressive tool disclosure. Tüm şemaları en baştan prompt’a koymak yerine, host tool’ları aşamalı olarak açar, önce adlar ve tek satırlık açıklamalar, ve bir tool’un tam inputSchema’sını yalnızca model gerçekten ona uzandığında fetch eder. Bu, skills’in arkasındaki aynı fikir: ajanın ucuza göz atıp gerektiğinde detaylı yükleyebileceği büyük bir capability kütüphanesi. MCP’nin tools/list’i (ucuz, adlar ve açıklamalar) ve tools/call’u (tam şema ve execution, yalnızca gerektiğinde ulaşılan) progressive disclosure’ın istediği tam olarak iki-aşamalı şekildir, dolayısıyla protokol ve disclosure stratejisi ekstra makine olmadan birbirine uyar.

💡 Deneyimden. Bu protokolün karşılığını en net verdiği an, bir entegrasyon yazmak yerine sildiğim gündü. Bir takımın ajanına elle wire edilmiş bir refund tool’umuz vardı, alışılmış özel iş: argüman şeması ajanın kodunda, HTTP call’u inline, error handling loop’a dolanmış. İkinci bir takım aynı capability’yi kendi ajanlarında istedi, ve eski cevap wiring’i kopyalamak, şemayı fork etmek ve sonsuza dek birbirinden hafifçe sapan iki versiyonu senkronda tutmak olurdu. Bunun yerine tool’u tools/list üzerinden ilan eden küçük bir MCP server’a çektim ve her iki host’u ona yönelttim. İkinci takımın ajanı tool’u keşfetti, inputSchema’sını kendi validator’larına besledi ve onu çağırdı, kendi taraflarında sıfır yeni entegrasyon kodu ile. Şema sapamadı çünkü artık tam olarak bir tane vardı, protokolün arkasında yaşıyordu. Diğer ders handshake’ten geldi ve bir hikaye değil bir kurtarma olarak geldi: bağlandığımız bir server capability’lerinde yalnızca tools ilan etti, resources yok, ve host’umuz ondan bir config dosyasını resources/read edebileceğini varsaymıştı. Capability negotiation initialize’da gerçekleştiği için, uyumsuzluk bağlantı anında temiz bir “bu server resources sunmuyor” olarak yüzeye çıktı, canlı bir koşunun üç adım içinde kafa karıştırıcı bir başarısızlık olarak değil. Handshake, bir runtime gizemi olacak şeyi bir startup kontrolüne dönüştürdü.

Özet / Çıkarımlar

  • Ajanın tool’ları Part 1’den beri bir process’te hardcoded bir Python dict’iydi (TOOL_SCHEMAS). Bir dict başka bir host’la paylaşılamaz, deploy time’da takas edilemez ya da onu import etmemiş kod tarafından keşfedilemez. Frontier track o dict’i bir protokolle değiştirerek açılır.
  • MCP tool kullanımını bir wire protokolüne dönüştürür. Bir server capability’leri ilan eder; bir host onları JSON-RPC üzerinden keşfeder ve çağırır ("jsonrpc": "2.0", bir id, bir method, params, ve eşleşen bir result ya da error). Ajanın aksiyon uzayı, neye bağlanırsa o server’lardan çalışma anında bir araya getirilir.
  • initialize handshake’i protokol versiyonunu (burada 2025-06-18) müzakere eder ve capability’leri ilan eder. support-server tools, resources, prompts ilan eder; catalog-server yalnızca tools ilan eder. Sonra tools/list tool’ları keşfeder: host search_policy ve process_refund’u import ederek değil, sorarak öğrendi.
  • MCP tek bir handshake’in arkasında üç primitive tanımlar: tools (çağrılabilir aksiyonlar, tools/call ile), resources (URI ile fetch edilen read-only veri, resources/read ile), ve prompts (adlandırılmış template’ler, prompts/get ile). Her birini çağıran host kodu şekil olarak aynıdır.
  • Keşfedilen inputSchema Part 1’in validator’ını değiştirmeden sürer: {"order_id": "ORD-3300", "amount": 180.0} OK doğrulanır, amount’u düşürmek REJECTED: missing required arg 'amount', ve amount: "lots" REJECTED: arg 'amount' must be number. Aynı tipli-argüman garantisi, ama şema bir dict’ten değil tools/list’ten geldi.
  • Tek host çok server’ı multiplex eder: iki server’daki palet birleşimdir (support-server’dan search_policy, process_refund; catalog-server’dan search_products), ve bir multi-hop görev onların arasında şeffafça çağrı yapar. Bir server ekle ve tool’ları basitçe belirir, yeni wiring olmadan.
  • Doğrulanmış varsayılan in-process bir JSON-RPC shim’dir (gerçek frame’ler, ağ yok); gerçek transport server’ı stdio üzerinden bir subprocess olarak çalıştırır (açıklayıcı, çalıştırılmamış bir transcript olarak gösterilir). İkisi yalnızca transport’ta farklıdır, protokolde değil; frame’ler özdeştir. Büyük bir palet progressive tool disclosure ile eşleşir (önce adlar, talep üzerine tam şema), skills ile aynı fikir.

Sözlük

  • MCP (Model Context Protocol): “ne tür tool’ların var ve onları nasıl çağırırım”ı bir wire protokolüne dönüştüren bir standart, böylece bir server capability’leri ilan edebilir ve herhangi bir host onları keşfedip çağırabilir. Hardcoded, tek-process tool dict’inin yerini alır.
  • JSON-RPC: MCP’nin üzerinde taşındığı mesaj formatı. Her istek "jsonrpc": "2.0", bir id, bir method ve params taşıyan bir JSON object’idir; her yanıt id’yi echo eder ve ya bir result ya da bir error taşır. id yanıtları isteklere eşleştirir.
  • initialize handshake’i: bir bağlantıdaki ilk değiş tokuş, host’un protokol versiyonunu ve kimliğini ilan ettiği ve server’ın üzerinde anlaşılan versiyon, kimliği ve capability’leriyle yanıt verdiği yer. O tamamlanana kadar başka hiçbir şey olmaz.
  • protocolVersion / capability negotiation: handshake bir lehçe üzerinde anlaşır (burada 2025-06-18), böylece ayrı yazılmış bir host ve server interoperate edebilir, ve server hangi capability’leri desteklediğini ilan eder (tools, resources, prompts), böylece host ne isteyebileceğini bilir. catalog-server gibi yalnızca-tools olan bir server’dan resources istenmez.
  • Üç primitive (tools / resources / prompts): bir MCP server’ının açığa çıkarabileceği üç tür şey. Tools çağrılabilir aksiyonlardır (tools/call); resources URI ile fetch edilen read-only veridir (resources/read); prompts adlandırılmış, yeniden kullanılabilir prompt template’leridir (prompts/get).
  • inputSchema: bir tool’un argümanları için JSON Schema’sı, tools/list’te taşınır (type, properties, required). Sadece veri olduğu için host onu doğrudan Part 1’in validator’ına besleyebilir; şema artık bir dict’ten değil hat üzerinden gelir.
  • Host vs server: server capability’lere sahip olur ve onları açığa çıkarır (initialize, tools/list, tools/call ve resource ile prompt method’larını yanıtlar); host bağlanır, keşfeder ve onları çağırır, sonra keşfedilen şemaları ajanın controller’ına besler. Ajan host tarafında yaşar.
  • Multiplexing: tek bir host’un birkaç server’a bağlanması ve ilan ettikleri capability’leri tek bir tool paletinde birleştirmesi, her tool adından onu destekleyen server’a bir map tutarak, böylece ajan herhangi bir tool’u hangi server’ın yerine getirdiğini bilmeden çağırır.
  • In-process shim vs stdio transport: aynı frame’leri taşımanın iki yolu. In-process shim host’un server.handle()’ı tek bir process’te doğrudan çağırmasını sağlar (deterministic, doğrulanmış varsayılan); stdio transport server’ı ayrı bir OS process’i olarak çalıştırır ve JSON-RPC’yi standard input ve output üzerinden boru bağlar (gerçek lokal deployment). Yalnızca transport’ta farklıdırlar, protokolde değil.
  • Progressive tool disclosure: yüzlerce tool tutan bir host için bir strateji, onları aşamalı olarak açar (önce adlar ve açıklamalar, tam inputSchema’yı yalnızca model bir tool’a uzandığında) her şemayı prompt’a koymak yerine. Skills ile aynı fikir, ve MCP’nin ucuz tools/list’i artı talep üzerine tools/call’u için doğal bir uyum.

Bu parçanın kuralı: ajanın tool’larını kendi process’ine hardcode etmeyi bırak, ve bunun yerine tool kullanımını bir protokol yap. Capability’lerini ilan eden bir server ve onları JSON-RPC üzerinden keşfeden bir host ayağa kaldır, protokol versiyonunu ve capability’leri bir initialize handshake’inde müzakere et, üç primitive’i (tools, resources, prompts) açığa çıkar, keşfedilen inputSchema’yı zaten sahip olduğun validator’a doğrudan besle, ve çok server’ı çalışma anında bir araya getirilen tek bir palette multiplex et. Reproducibility için in-process shim’i tut ve gerçek deployment için stdio transport’unu takas et; aynı frame’leri taşırlar. Bu, tool’ların özel bir dictionary’sini herhangi bir host’un bağlanabileceği, keşfedebileceği ve çağırabileceği bir şeye dönüştürür. Ama tartıştığımız her tool’un şekline dikkat et: ajanın doldurduğu ve server’ın çalıştırdığı tipli bir JSON fonksiyonu. Bazı görevler bu kalıba uymaz. İş “bu dizini keşfet, bu dosyaları dönüştür, bu şeyi hesapla” olduğunda, ajan yüzlerce tipli fonksiyonu çağırmak istemez; kod yazıp çalıştırmak ister. Part 13, The Code-Running Tool, ajana kod çalıştırmak ve bir bilgisayar işletmek için bir sandbox verir, ki bu muazzam derecede daha güçlü ve, ajan keyfi kod çalıştırabildiği an, muazzam derecede daha tehlikeli: kod çalıştırabildiği an gerçek hasar verebilir, ve sandbox faydalı bir ajanla felaket bir ajan arasında duran şeydir.

AgentsMCPProtocolsJSON-RPCTool UseAI