Chuyển đến nội dung
26/30Chương 26 trên 30

Giải thích MCP theo đặc tả: Server thực sự là gì

Một dòng JSON vào subprocess, 13 định nghĩa tool trả về, đối chiếu bản 2026-07-28 đã bỏ handshake.

Trên trang này

Cài một MCP server đã phát hành, gửi cho nó một dòng JSON, rồi đọc thứ trả về.

terminalBASH
npm i @modelcontextprotocol/server-everything@2026.8.31
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | npx @modelcontextprotocol/server-everything stdio
TEXT
{"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes
back the input string","inputSchema":{"$schema":"http://json-schema.org/draft-07/
schema#","type":"object","properties":{"message":{"type":"string","description":
"Message to echo"}},"required":["message"]},"annotations":{"readOnlyHint":true,
…                                        … 7,663 bytes on one line …
"jsonrpc":"2.0","id":1}

Mười ba định nghĩa tool, trên một dòng duy nhất, từ một process đã đọc một dòng từ standard input của nó. Bạn vừa nói chuyện bằng Model Context Protocol, không SDK, không thư viện client và không framework. Toàn bộ chỉ có vậy: một transport, một định dạng thông điệp, và một tập nhỏ các method có tên.

Chương 18 định nghĩa tool gồm hai phần — một JSON Schema mà model nhìn thấy, và một endpoint trong code của bạn mà model không bao giờ thấy. Chương 23 xây một harness giữ một catalogue các tool đó. Cả hai đều chưa trả lời câu hỏi quyết định liệu thứ này có tái sử dụng được hay không: ai viết schema, và nó đi từ người viết vào prompt của bạn bằng cách nào? MCP là một câu trả lời cho câu hỏi đó, và rất đáng đọc bản gốc, vì gần như mọi thứ được viết về nó đều mô tả một revision không còn tồn tại.

Có ba điều sai trong lệnh bạn vừa chạy, và mỗi điều là một phần của chương này. Nó không mang protocol version, nên một server tuân thủ đặc tả sẽ từ chối. Dù vậy nó vẫn nhận được câu trả lời, vì một lý do mà specification gọi là hazard chứ không phải feature. Và nó hỏi một trong ba primitive mà không hề khám phá rằng hai primitive còn lại tồn tại.

Vấn đề nó giải quyết, và phép so sánh mà chính spec đưa ra

Liên kết đến mục: Vấn đề nó giải quyết, và phép so sánh mà chính spec đưa ra

Trước khi nói về wire, hãy làm phép tính. Bạn có NN ứng dụng AI và MM thứ chúng cần truy cập — lịch, ticket tracker, database kho dữ liệu, công cụ thiết kế. Không có một contract chung, ai đó phải viết N×MN \times M tích hợp, và mỗi tích hợp là một schema cộng một endpoint cộng một câu chuyện xác thực cộng một gánh nặng bảo trì. Có contract chung, nhà cung cấp tool viết một server, nhà cung cấp ứng dụng viết một client, và tổng số là N+MN + M.

Đây không phải quan sát mới, và specification nói rõ ý tưởng đó đến từ đâu:

MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.1

Hãy hiểu phép so sánh đó theo nghĩa đen, không phải như một lời khen. Trước protocol đó, hỗ trợ một ngôn ngữ trong editor nghĩa là một plugin cho mỗi editor; sau đó, một nhóm ngôn ngữ phát hành một server và mọi editor đều có được hỗ trợ. Thước đo thành công không phải là sự thanh nhã, mà là số lượng tích hợp ngừng nhân lên. Điều tương tự diễn ra ở đây: giá trị nằm ở số lượng implementation, không nằm ở thiết kế. Một protocol mà chỉ hai sản phẩm nói được chỉ là một định dạng dữ liệu kèm thêm nghi thức.

Thông điệp MCP là JSON-RPC 2.0. Một request là object có jsonrpc, một id, một methodparams tùy chọn; response mang cùng id và hoặc result hoặc error; notification là request không có id và không nhận reply. Specification thêm ba ràng buộc bên trên: id phải là string hoặc number và không được là null, không được trùng với một request khác còn đang chạy, và mọi result phải mang trường resultType.2

Trên stdio transport — transport mà lệnh ở trên dùng — quy tắc framing là mỗi thông điệp một dòng:

Messages are delimited by newlines, and MUST NOT contain embedded newlines. […] The server MUST NOT write anything to its stdout that is not a valid MCP message.3

Mệnh đề cuối là cách phổ biến nhất khiến một server tự làm bị hỏng, và nó hỏng trong im lặng: một console.log lạc chỗ, một progress bar, một deprecation warning từ dependency, rồi line parser của client đụng phải thứ không phải JSON. Lối thoát nằm trong cùng phần đó — server may write anything it likes to stderr, và client should not treat that as an error. Reference server ở trên in Starting default (STDIO) server... mỗi lần khởi chạy, trên stderr, nên pipe vẫn chạy được.

Transport chuẩn còn lại là Streamable HTTP: mỗi message là một POST tới một endpoint duy nhất, và reply hoặc là một JSON object hoặc là một stream Server-Sent Events theo phạm vi request — wire format mà Chương 14 đã parse bằng tay. Semantics giống hệt trên cả hai, vì transport là một binding: nó định nghĩa framing và delivery, không định nghĩa meaning.4

Lệnh ở trên gửi tools/list và không gì khác. Theo revision hiện tại, request đó malformed, và một server tuân thủ phải reject nó.

Kể từ 2026-07-28, MCP là stateless protocol, và specification nói điều đó không vòng vo:

The Model Context Protocol (MCP) is a stateless protocol: all the information needed to process a request is contained in the request itself. A server processes each request independently; no state should be inferred from previous requests, even those on the same connection or stream.2

Vì vậy mọi request đều mang protocol version riêng và client capabilities riêng, trong một object _meta dành riêng bên trong params. Hai trường trong đó là bắt buộc trên từng request; một request thiếu một trong hai là malformed và server must answer -32602:2

key _metabắt buộcđó là gì
io.modelcontextprotocol/protocolVersionrevision mà request này nói, ví dụ "2026-07-28"
io.modelcontextprotocol/clientCapabilitiesclient có thể làm gì cho server trên request này
io.modelcontextprotocol/clientInfokhông (nhưng should)tên và version của client, chỉ để hiển thị và log
io.modelcontextprotocol/logLevelkhôngmức log tối thiểu mà server nên emit cho request này

Viết đầy đủ, một tools/list đúng là như sau — và đây là lần cuối chương này hiển thị metadata đầy đủ, vì từ đây nó có trên mọi request:

one line, split for the pageTEXT
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{
  "io.modelcontextprotocol/protocolVersion":"2026-07-28",
  "io.modelcontextprotocol/clientCapabilities":{"elicitation":{"form":{}}},
  "io.modelcontextprotocol/clientInfo":{"name":"bare-hands","version":"0.0.1"}}}}

Capability object chính là negotiation. Không còn bước negotiation riêng nữa: client khai báo nó có thể làm gì trên mỗi request, server khai báo nó có thể làm gì trong result, và không bên nào được dùng một feature mà bên kia chưa claim. Một server cần capability mà client không khai báo must answer -32021 và nêu capability bị thiếu trong data.requiredCapabilities. Một server không nói version được yêu cầu must answer -32022 và liệt kê các version nó nói được.2

Client muốn có câu trả lời ngay từ đầu có thể hỏi: server/discover là RPC bắt buộc, trả về supported versions, capabilities, identity và một block instructions tùy chọn trong một round trip.5 Gọi nó là tùy chọn. Implement nó thì không.

Lệnh đã chạy được. Theo revision hiện tại, nó không nên chạy được, và lý do nên được đo hơn là viết thành một đoạn văn, vì nó là trạng thái của cả ecosystem trong một dòng.

Probe reference server theo cách specification bảo một modern client probe:

terminalBASH
echo '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
  "io.modelcontextprotocol/protocolVersion":"2026-07-28",
  "io.modelcontextprotocol/clientCapabilities":{}}}}' \
  | npx @modelcontextprotocol/server-everything stdio
TEXT
{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}

Đó là nhánh thứ ba của compatibility rule: một DiscoverResult nghĩa là modern, một lỗi modern được nhận diện nghĩa là modern-nhưng-sai-version, và bất cứ thứ gì khác — gồm cả -32601 — nghĩa là legacy, hãy fall back về handshake initialize.3 Vậy hãy làm thế, yêu cầu revision hiện tại:

TEXT
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28",
   "capabilities":{},"clientInfo":{"name":"bare-hands","version":"0.0.1"}}}

← {"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true},
   "prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},
   "logging":{},"tasks":{…},"completions":{}},"serverInfo":{"name":"mcp-servers/everything",
   "title":"Everything Reference Server","version":"2.0.0"},"instructions":"…"}}

Client yêu cầu 2026-07-28 và server trả lời 2025-11-25. Ngày 7 tháng 9 năm 2026, official reference server — npm package @modelcontextprotocol/server-everything, version 2026.8.31, phát hành ngày 31 tháng 8 năm 2026 — chưa implement revision hiện tại. TypeScript SDK mà nó dựa trên cũng vậy: release 1.30.0 ra ngày 27 tháng 7 năm 2026, một ngày trước revision đó.

Hãy đọc hệ quả thay vì chuyện bên lề. Gần như mọi thứ được viết về MCP mô tả một protocol có handshake initialize, một session, một request roots/list mà server gửi cho client, và HTTP+SSE transport. Cả bốn đều đã biến mất hoặc đang rời đi. Khi bạn đọc bất cứ thứ gì về MCP, kể cả trang này, điều đầu tiên cần tìm là revision number.

Và lý do lệnh đầu tiên chạy được được specification mô tả là hazard, không phải feature:

some legacy servers do not validate that a request arrives after initialize and would process an era-ambiguous method (such as tools/call) under legacy semantics. Probing yields a deterministic failure instead.3

Đã đo: gửi tools/list tới server đó mà không handshake nào vẫn trả về toàn bộ catalogue. Một method lẽ ra phải bị từ chối đã được phục vụ, chính xác là lý do specification nói phải probe bằng server/discover trước ngay cả khi bạn chỉ hỗ trợ modern versions.

Ba vai trò, và câu đáng trích dẫn nhất trong toàn bộ tài liệu

Liên kết đến mục: Ba vai trò, và câu đáng trích dẫn nhất trong toàn bộ tài liệu

MCP có ba bên, và sự khác biệt giữa hai bên đầu là thứ mọi người hay nhập làm một:

Host. Ứng dụng: sản phẩm chat, editor, agent. Nó sở hữu conversation, model, credentials và consent của user. Nó tạo client và áp đặt security boundary giữa chúng.

Client. Một connector bên trong host. Mỗi client nói chuyện với chính xác một server — quan hệ 1:1 nghiêm ngặt — và gắn protocol version cùng capabilities vào mọi request nó route.

Server. Một process hoặc service phơi ra resources, tools và prompts. Nó có thể local hoặc remote, hoạt động độc lập, và toàn bộ công việc của nó là một phạm vi tập trung.6

Quy tắc “chính xác một server” đó không phải sổ sách. Nó là thứ làm cho design principle bên dưới có thể implement, và đây là câu nên lấy từ specification nếu bạn chỉ lấy một câu:

Servers should not be able to read the whole conversation, nor "see into" other servers. Servers receive only necessary contextual information. Full conversation history stays with the host. Each server maintains isolation. Cross-server interactions are controlled by the host.6

Điều đó lật ngược mental model mà hầu hết mọi người mang tới. Một weather server bạn kết nối vào assistant của mình không thấy bạn đã hỏi gì. Nó thấy một tools/call với arguments mà model đã chọn, và không gì khác — không các lượt trước, không system prompt của bạn, không kết quả mà calendar server vừa trả về. Nếu hai server cần phối hợp, host cố ý mang một giá trị từ server này sang server kia, vì model yêu cầu. Đó là lý do isolation là security property mà Chương 30 dựa vào: một server bị compromise có blast radius nhỏ, được định nghĩa rõ, và việc mở rộng nó đòi hỏi host phải hợp tác.

Điều thứ ba: ba primitive, sắp theo ai có quyền quyết định

Liên kết đến mục: Điều thứ ba: ba primitive, sắp theo ai có quyền quyết định

Lệnh đầu tiên hỏi server đó về tools và nhận được mười ba. Hỏi nó hai câu còn lại thì nó cũng trả lời: resources/list trả về bảy, prompts/list trả về bốn. Không cái nào xuất hiện, vì không có gì hỏi. Điều đó đưa ta tới xương sống sư phạm của MCP, nằm trong specification dưới dạng một bảng mà hầu như không ai trích:

PrimitiveControlDescriptionExample
PromptsUser-controlledInteractive templates invoked by user choiceSlash commands, menu options
ResourcesApplication-controlledContextual data attached and managed by the clientFile contents, git history
ToolsModel-controlledFunctions exposed to the LLM to take actionsAPI POST requests, file writing

Không phải “ba cách để phơi ra một capability”. Mà là ba câu trả lời cho ai quyết định việc này xảy ra. Model quyết định gọi một tool. Ứng dụng quyết định gắn một resource. Con người quyết định chạy một prompt. Hiểu sai thì feature vẫn chạy, nhưng chạy sai thời điểm và vì sai lý do.

Cách rõ nhất để cảm nhận là một calendar. Đây là một server phơi ra cùng một calendar ba lần, mỗi lần như một primitive, trong một trăm dòng Node thuần không dependencies:

calendar.mjs — the parts that matterJS
const TOOL = {
  name: "create_event",
  description: "Create a calendar event. Writes to the calendar.",
  inputSchema: {
    type: "object",
    properties: {
      title:    { type: "string", description: "Event title." },
      startsAt: { type: "string", format: "date-time", description: "Start, ISO 8601 UTC." },
    },
    required: ["title"],
  },
};

switch (method) {
  case "resources/read":                                          
    return ok(id, { contents: [{ uri: "calendar://week",
      mimeType: "application/json", text: JSON.stringify(EVENTS) }],
      ttlMs: 60000, cacheScope: "private" });

  case "prompts/get":                                             
    return ok(id, { description: PROMPT.description, messages: [{ role: "user",
      content: { type: "text", text: `Read calendar://week and draft a plan. ` +
        `Focus: ${params.arguments?.focus ?? "balance"}.` } }] });

  case "tools/list":                                              
    return ok(id, { tools: [TOOL], ttlMs: 300000, cacheScope: "public" });
}

Chạy nó và hỏi theo cả ba cách. Output thật, mỗi message một dòng trên wire, được wrap lại ở đây cho trang, với request _meta và identity block của server đã lược bỏ:

TEXT
→ resources/read  {"uri":"calendar://week"}
← {"resultType":"complete","contents":[{"uri":"calendar://week",
   "mimeType":"application/json","text":"[{\"id\":\"e1\",\"title\":\"Standup\",
   \"startsAt\":\"2026-09-07T09:00:00Z\"},{\"id\":\"e2\",\"title\":\"Design review\",
   \"startsAt\":\"2026-09-09T15:00:00Z\"}]"}],"ttlMs":60000,"cacheScope":"private"}

→ prompts/get    {"name":"prepare_week","arguments":{"focus":"deep work"}}
← {"resultType":"complete","description":"Read the week and draft a plan.",
   "messages":[{"role":"user","content":{"type":"text",
   "text":"Read calendar://week and draft a plan. Focus: deep work."}}]}

→ tools/call     {"name":"create_event","arguments":{"title":"Dentist",
                  "startsAt":"2026-09-10T08:30:00Z"}}
← {"resultType":"complete","content":[{"type":"text",
   "text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],
   "structuredContent":{"id":"e3","title":"Dentist","startsAt":"2026-09-10T08:30:00Z"},
   "isError":false}

Ba method, ba shape, một calendar. Giờ là điểm chính:

Nó được định địa chỉ bằng URI, nó inert, và ứng dụng quyết định có gắn nó vào conversation hay không. Không có gì trong protocol cho phép model tự với tới nó. Result mang ttlMscacheScope, mới trong revision này, để client có thể cache tuần đó trong một phút thay vì polling.

Nó có schema, có side effects, và model quyết định khi nào gọi nó. Result của nó mang isError, chính là trường mà Chương 18 đã lập luận cần có: một validation failure trở về dưới dạng tool result để model đọc và sửa, không phải protocol error.

Nó là template có tên, nhận argument, mà con người invoke — slash command trong menu. Nó trả về messages, không phải một câu trả lời. Nó là cách để tác giả server ship phrasing hoạt động tốt với tool của chính họ, tức đúng loại tri thức mà tác giả server có còn user thì không.

Hầu như ai cũng biến cả ba thứ này thành tool. Kết quả là một catalogue nơi một read đáng lẽ ứng dụng phải âm thầm attach lại cạnh tranh attention của model với một write cần approval, và thứ duy nhất con người muốn có button lại bị chôn trong schema. Làm đúng không tốn gì, và được quyết định trước khi bạn viết một dòng.

Calendar tool có một argument bắt buộc, title, và một startsAt tùy chọn. Yêu cầu nó tạo event mà không có ngày, và một thứ thú vị trả về:

TEXT
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"}}

← {"resultType":"input_required",
   "inputRequests":{"when":{"method":"elicitation/create","params":{"mode":"form",
     "message":"When should \"Dentist\" start?",
     "requestedSchema":{"type":"object",
       "properties":{"startsAt":{"type":"string","format":"date-time"}},
       "required":["startsAt"]}}}},
   "requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}

Server không gửi request. Nó trả lời request được giao, với resultType: "input_required" và mô tả thứ nó vẫn cần. Client thu câu trả lời từ con người, rồi gửi lại original call — với id mới, mang inputResponses và echo lại opaque requestState:

TEXT
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"},
   "inputResponses":{"when":{"action":"accept",
     "content":{"startsAt":"2026-09-10T08:30:00Z"}}},
   "requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}

← {"resultType":"complete","content":[{"type":"text",
   "text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],"isError":false}

Đây là Multi Round-Trip Requests, được giới thiệu trong revision hiện tại, và nó thay thế thiết kế cũ nơi server gửi JSON-RPC requests ngược về clients. Transport specification giờ nêu quy tắc thẳng thừng: “servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses”.4 Chỉ có một hướng chủ động, và nó thuộc về host.

Hai feature phía client chạy trên cơ chế đó, và một cái có tên dễ làm bạn vấp.

Elicitation là server hỏi con người một thứ gì đó: một form với JSON Schema bị giới hạn có chủ ý — flat objects, primitive properties, không nesting — để client nào cũng render được mà không cần layout engine. Nó mang một quy tắc cứng: servers must not use form mode to ask for “passwords, API keys, access tokens, or payment credentials”, và must use URL mode for those, tức đưa user tới một trang mà client không bao giờ đọc.7

Sampling là server hỏi model của host để tạo generation, để server có thể thông minh mà không giữ API key. Và đây là cảnh báo về từ vựng, vì từ này đã có nghĩa khác trong khóa này: đây không phải sampling của Chương 17. Ở đây không có gì liên quan đến temperature, top-p hay hình dạng của probability distribution. Nó là một nested model call đi ngược qua protocol.

Có lý do thứ hai để không vội dùng nó: tính tới revision này, sampling đã deprecated, cùng roots và logging, theo SEP-2577, với migration gợi ý rất thẳng — “integrate directly with LLM provider APIs instead of Sampling”.8 Ý tưởng này không thất bại về kỹ thuật; nó không biện minh được surface area của mình, và một protocol có thể loại bỏ thứ không cần thiết thì lành mạnh hơn một protocol không thể.

Cố tình làm hỏng: connections không phải sessions

Liên kết đến mục: Cố tình làm hỏng: connections không phải sessions

Statelessness nghe như chi tiết wire-format cho tới khi bạn test. Lấy exchange ba message ở trên và chạy mỗi message trong một process riêng — một node calendar.mjs mới, không shared memory, không mang gì qua:

TEXT
process A   tools/call (no date)   → resultType: input_required
                                     requestState: eyJ0aXRsZSI6IkRlbnRpc3QifQ==
process B   tools/call (with the answer, same requestState)
                                   → resultType: complete
                                     "Created e3: Dentist at 2026-09-10T08:30:00Z"
process C   resources/read calendar://week
                                   → events: 2  (Standup, Design review)

Process B, vốn chưa từng thấy câu hỏi, đã hoàn tất một multi-round-trip call mà process A bắt đầu. Đó là ý nghĩa của requestState: continuation đi trong message, nên không gì phụ thuộc vào process có phải cùng một process hay không.

Process C là thất bại. Event đã được tạo và không có ở đó — vì toy server giữ EVENTS trong một module-level array, và module-level array là connection state. Note của specification gọi đúng tên lỗi này:

an open connection, such as a STDIO process, is not a conversation or session: clients may interleave unrelated requests on the same transport, and a server must not treat connection or process identity as a proxy for conversation or session continuity.2

Cách sửa được chỉ định không phải session. Đó là một explicit handle: creation tool trả về một opaque identifier, và mọi call sau đó nhận nó như argument bình thường. Protocol hoàn toàn không có khái niệm đó — “from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls”.9 Điều này đặt model chịu trách nhiệm mang nó, và đặt server chịu trách nhiệm validate rằng caller này được phép dùng nó trên từng call, vì handle là một cái tên chứ không phải permission.

Một server tốn gì trước khi làm bất cứ thứ gì

Liên kết đến mục: Một server tốn gì trước khi làm bất cứ thứ gì

Mọi tool mà server phơi ra là một schema đi vào prompt của bạn trên mọi request, và Chương 24 đã đo điều đó làm gì với window. MCP thêm một line item thứ hai dễ bị bỏ lỡ, nên cả hai đều đáng đếm trên reference server ở trên.

o200k_base tokensTEXT
13 tool definitions (name + description + inputSchema):  1,307 tokens
  cheapest tool, get-tiny-image                              52
  costliest tool, gzip-file-as-resource                     235
server `instructions`, returned by discovery:               312 tokens
                                                          ------
one server, connected, before it is used:                 1,619 tokens

Hai quan sát. Thứ nhất là số học: kết nối năm server cỡ này và khoảng tám nghìn token trong window của bạn đã bị chiếm trên mọi turn, mãi mãi, dù model có dùng chúng hay không — đó là cơ chế phía sau mức giảm từ 150.000 xuống 2.000 mà Chương 24 đã trích, và là lý do just-in-time tool discovery tồn tại.

Thứ hai là một security note khoác áo kế toán. instructionsnatural-language text, do tác giả server viết, rơi vào prompt của host, và tool descriptions bên cạnh nó cũng vậy. Specification nói phải làm gì về việc đó trong security principles của chính nó: tool annotations and descriptions “should be considered untrusted, unless obtained from a trusted server”, và hosts “must obtain explicit user consent before invoking any tool”.1 Kết nối một MCP server không phải là thêm một dependency. Đó là cấp cho một người lạ 1.619 token trong system prompt của bạn và quyền được gọi. Chương 30 là chuyện xảy ra khi người lạ đó có ác ý.

Phần có ngày tháng: revision 2026-07-28, và nó làm hỏng gì

Liên kết đến mục: Phần có ngày tháng: revision 2026-07-28, và nó làm hỏng gì

Mọi thứ trong phần này đúng với protocol revision 2026-07-28, bản hiện tại, đọc ngày 7 tháng 9 năm 2026. Revisions được ghi ngày theo YYYY-MM-DD và ngày đó là lần cuối một backwards-incompatible change được thực hiện.10 Normative document là một TypeScript file, schema/2026-07-28/schema.ts; JSON Schema cạnh nó được generate từ đó, nên specification ở đây được đọc trong TypeScript và dạy MCP từ bất cứ thứ gì khác là dạy một bản dịch.

What changedWasIs nowBreaks
Handshakeinitialize + notifications/initialized, một lần cho mỗi connectionbị bỏ; mọi request mang version và capabilities _metamọi client viết trước revision này
Sessionsheader Mcp-Session-Id, state theo phạm vi connectionbị bỏ; state đi trong explicit handles do server mintlist endpoints từng thay đổi theo connection
Discoverysuy ra từ result initializeserver/discover, servers must implementkhông gì, nhưng giờ bắt buộc implement
Server-to-client callsserver gửi roots/list, sampling/createMessage, elicitation/createInputRequiredResult và client retrymọi server từng push request tới client
Result shapeobject bất kỳbắt buộc resultType: "complete" hoặc "input_required"không gì: trường vắng mặt phải được đọc là "complete"
SubscriptionsHTTP GET stream, resources/subscribemột stream subscriptions/listen với opt-in typesGET endpoint không còn
Stream resumptionreplay Last-Event-ID trên Streamable HTTPbị bỏ; stream đứt làm mất request, re-issue với id mớiclients dựa vào redelivery
Rootsmột client feature mà servers có thể hỏideprecated (SEP-2577); truyền paths như tool arguments hoặc resource URIschưa gì — cửa sổ mười hai tháng
Sampling and loggingclient featuresdeprecated (SEP-2577)chưa gì — cửa sổ mười hai tháng
HTTP+SSE transportdeprecated từ 2025-03-26Deprecated theo lifecycle policy (SEP-2596)migrate sang Streamable HTTP
Client registrationOAuth 2.0 Dynamic Client Registration, RFC 7591deprecated để chuyển sang Client ID Metadata Documentsgiữ cho authorization servers không có chúng
Error codes-32002 cho resource not found-32602; -32020-32099 reserved cho specnew codes -32020, -32021, -32022

Thay đổi governance bên dưới bảng đó quan trọng hơn bất kỳ dòng đơn lẻ nào. Revision này đã adopted một feature lifecycle and deprecation policy: features là Active, Deprecated hoặc Removed, một deprecated feature ghi migration path và ở lại specification ít nhất mười hai tháng trước khi đủ điều kiện removal, và có một registry liệt kê mọi thứ hiện ở trạng thái Deprecated.8 Trước policy đó, “deprecated” trong một AI protocol có nghĩa là bất cứ điều gì bài blog gần nhất nói. Bây giờ nó có nghĩa là một ngày.

Hiện chi tiết

Extensions, phần mà chưa ai viết nhiều đến.

Ngoài core, MCP định nghĩa các extensions tùy chọn — “always opt-in and require explicit support from both client and server”, được khai báo qua trường extensions trong capabilities của client và server.1 Ba cái đáng biết tên:

  • Tasks (io.modelcontextprotocol/tasks), được chuyển khỏi core protocol thành official extension trong revision này: thực thi bất đồng bộ các long-running operations, polling qua tasks/get, input giữa chừng qua tasks/update, và durable handles. Đây là câu trả lời cho một tool mất hai mươi phút, thứ Chương 23 xử lý bằng progress event và một signal chạm tới tool.
  • Skills over MCP, một working group làm cho agent skills — chủ đề của Chương 28 — discoverable và consumable qua protocol.
  • MCP Apps, UI tương tác render inline trong conversation: charts, forms, video players.

Và lưu ý “negotiated” giờ nghĩa là gì: không có initialization để negotiate, nên extension được khai báo per request như mọi thứ khác.

MCP nằm ở đâu, so với mọi thứ nó hay bị nhầm lẫn

Liên kết đến mục: MCP nằm ở đâu, so với mọi thứ nó hay bị nhầm lẫn

Đây là vocabulary của toàn bộ khối trong một chỗ.

Nó là gìAi nói với aiKhi nào nó là câu trả lời
Một API thườngInterface cho một chương trìnhcode của bạn ↔ một serviceBạn đang viết caller. Bạn kiểm soát schema, auth và error handling, và không có discovery problem cần giải quyết.
MCPProtocol để phơi ra tools, data và templates cho một ứng dụng AIhost ↔ server, mỗi bên một clientNgười khác đã viết capability và nhiều hosts nên dùng được nó mà không cần bespoke integration.
RAGKỹ thuật tìm text và đưa nó vào promptcode của bạn ↔ index của bạnModel cần biết một thứ. Chương 19. MCP là một cách deliver retriever; nó không phải retriever.
Agent skillsMột folder có SKILL.md mà model đọcmodel ↔ một documentTri thức mang tính procedural — chúng ta làm việc này thế nào — và là prose, không phải function. Chương 28.
A2AProtocol để agents cộng tác như peersagent ↔ agentPhía bên kia reasoning, planning và giữ state qua một task dài, thay vì trả lời một call.
ACPTừng là một agent-communication protocol riêngNó không còn là phép so sánh sống nữa. Xem bên dưới.

Hai dòng trong đó xứng đáng mỗi dòng một câu, vì đó là nơi nhầm lẫn thực sự tồn tại.

MCP so với A2A không phải một cuộc đối đầu, và cả hai specifications đều nói vậy. Documentation của A2A vạch ranh giới bằng thứ nằm ở đầu bên kia: MCP “defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API”, trong đó tool thực hiện “specific, often stateless, functions”; A2A xử lý agents, “more autonomous systems” có thể “reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues”. Tóm tắt của chính nó là câu cần nhớ: “A2A is about agents partnering on tasks, while MCP is more about agents using capabilities.”11 Hai cái lồng vào nhau — một ứng dụng dùng A2A để đến các agents khác, và mỗi agent dùng MCP để đến tools của chính nó. Chương 25 vạch ranh giới đó bên trong một process, giữa việc hỏi một sub-agent và giao conversation cho nó; A2A vạch ranh giới đó giữa các tổ chức.

MCP so với ACP là một phép so sánh với tiền đề đã cũ, chính vì vậy nó đáng được trả lời. Agent Communication Protocol từng là một open standard riêng cho agent-to-agent messaging. Documentation của chính nó giờ mở đầu bằng thông báo: “ACP is now part of A2A under the Linux Foundation!”12 Câu trả lời trung thực cho “MCP hay ACP?” vào tháng 9 năm 2026 là câu hỏi đó có ít hơn một lựa chọn so với các trang đang xếp hạng cho nó gợi ý.

Và phép so sánh mọi người hỏi nhiều nhất, mcp vs api, lại có câu trả lời ít thú vị nhất: MCP là một API. Thứ nó thêm không phải power, mà là conventions — một tập method names cố định, một discovery call, một control hierarchy trên các primitive, và một isolation model. Bạn từ bỏ tự do thiết kế interface của riêng mình và nhận về mọi host nói protocol đó, chính là trade-off mà mọi protocol từng đưa ra.

Giờ bạn có thể đọc specification mà không cần người dịch, phân biệt resource với tool với prompt theo ai có quyền quyết định, gõ một request bằng tay khi client library nói dối bạn, và gắn ngày cho bất cứ bài viết MCP nào bạn đọc bằng deprecated features mà nó vẫn dạy như hiện hành.

Thứ bạn chưa làm là ship một cái. Chương 27 viết cùng một server hai lần — TypeScript và Python, đặt cạnh nhau, vì MCP là vùng lãnh thổ thực sự song ngữ duy nhất trong khóa này và các con số nói vậy theo cả hai hướng. Nó trình bày đúng hai live transports, inspector, packaging, và nửa protocol mà chương này cố ý để lại: authorization. Vì khoảnh khắc server của bạn là remote thay vì subprocess trên chính laptop của bạn, client của một người lạ sẽ trình một token, và quy tắc của specification về thứ bạn được làm với nó nghiêm ngặt khác thường.

Điều đó đặt ra câu hỏi mà chương tiếp theo phải trả lời, và nó không hề thân thiện: nếu một token đến server của bạn và nó được issued cho audience của người khác, chính xác thì điều gì ngăn bạn forward nó?


Mọi quotation, method name, error code và rule trong chương này được đọc từ Model Context Protocol specification, revision 2026-07-28, vào 7 tháng 9 năm 2026. Mọi trace được tạo local trên Node 22: toy calendar server dài 101 dòng không dependencies, và reference server là npm package đã phát hành được nêu bên dưới. Không paid API nào được gọi để viết chương này — không thứ gì ở đây cần model, và bản thân đó chính là điểm chính.

Các phép đo: @modelcontextprotocol/server-everything@2026.8.31, phát hành ngày 31 tháng 8 năm 2026, built on @modelcontextprotocol/sdk@1.30.0, phát hành ngày 27 tháng 7 năm 2026 — một ngày trước revision mà chương này mô tả. Nó trả lời server/discover bằng -32601, negotiate 2025-11-25 khi được yêu cầu 2026-07-28, và serve tools/list mà không handshake nào. Catalogue của nó gồm 13 tools trong 7.663 bytes; token counts là o200k_base qua tiktoken, trên name, descriptioninputSchema của mỗi definition, tức thứ provider render vào prompt của bạn chứ không phải trọng lượng của JSON-RPC frame.

Anthropic, Code execution with MCP: building more efficient agents, 4 tháng 11 năm 2025, là nguồn của con số 150.000-xuống-2.000, được trích và dùng trong Chương 24 và chỉ được tham chiếu ở đây.

  1. Specification, modelcontextprotocol.io/specification/latest (redirect tới /2026-07-28), đọc ngày 7 tháng 9 năm 2026. Nguồn của phép so sánh với Language Server Protocol; phát biểu rằng specification “based on the TypeScript schema in schema.ts”; tóm tắt base-protocol (“Stateless, self-contained requests”, “Per-request capability negotiation”); danh sách extension (Tasks, Skills over MCP, MCP Apps) và phát biểu rằng extensions “are always opt-in and require explicit support from both client and server”; cũng như Security và Trust & Safety principles, gồm “Hosts must obtain explicit user consent before invoking any tool” và cách xem tool annotations là untrusted. 2 3

  2. Base Protocol, modelcontextprotocol.io/specification/2026-07-28/basic. Nguồn của các ràng buộc JSON-RPC (non-null id, không reuse id, bắt buộc resultType); phần Statelessness và note rằng một open stdio process không phải session; bảng reserved-key _meta và trạng thái required/optional của từng per-request field; quy tắc -32602 cho missing required field; quy tắc MissingRequiredClientCapability (-32021); và error-code allocation policy. 2 3 4 5

  3. stdio transport, modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Nguồn của newline-delimited framing rules, yêu cầu purity của stdout, allowance của stderr, và three-outcome backward-compatibility probe — gồm warning rằng một số legacy servers xử lý era-ambiguous methods không handshake, điều mà phép đo trong chương này tái hiện. 2 3

  4. Transports overview, modelcontextprotocol.io/specification/2026-07-28/basic/transports. Nguồn của framing “a transport is a binding” và phát biểu rằng servers không initiate JSON-RPC requests và clients không gửi JSON-RPC responses. 2

  5. Discovery, modelcontextprotocol.io/specification/2026-07-28/server/discover. Nguồn của trạng thái bắt buộc của server/discover, shape của DiscoverResult, và trường instructions được mô tả là “optional natural-language guidance for LLMs on how to use this server effectively”.

  6. Architecture, modelcontextprotocol.io/specification/2026-07-28/architecture. Nguồn của các định nghĩa host/client/server, quy tắc 1:1 client-to-server, bốn design principles, trong đó isolation principle được trích ở đây không có bullet thứ năm, “Host process enforces security boundaries”, và phần capability-negotiation. 2

  7. Elicitation, .../client/elicitation, và Sampling, .../client/sampling. Nguồn của hai elicitation modes và restricted schema của chúng; lệnh cấm request credentials qua form mode; sampling definition, human-in-the-loop requirement của nó, và deprecation warning gắn với nó.

  8. Key Changes, modelcontextprotocol.io/specification/2026-07-28/changelog, và Feature lifecycle and deprecation policy, .../community/feature-lifecycle. Nguồn của mọi dòng trong change table: removal of sessions và header Mcp-Session-Id (SEP-2567); statelessness và removal of initialize (SEP-2575); server/discover (SEP-2575); subscriptions/listen (SEP-2575); Multi Round-Trip Requests và resultType (SEP-2322); removal of stream resumability (SEP-2575); deprecation of Roots, Sampling and Logging (SEP-2577); reclassification of HTTP+SSE (SEP-2596); deprecation of Dynamic Client Registration để chuyển sang Client ID Metadata Documents; error-code renumbering; và twelve-month deprecation window. 2

  9. Tools, modelcontextprotocol.io/specification/2026-07-28/server/tools, và Server Features, .../server. Nguồn của control-hierarchy table được tái hiện ở trên; các shape tools/listtools/call; phân biệt isError giữa protocol errors và tool execution errors; tool-name rules và namespace note khuyến nghị “prefixing tool names with a server identifier”; cùng guidance non-normative “Stateful Tools” về explicit handles.

  10. Versioning, modelcontextprotocol.io/specification/versioning. Nguồn của scheme YYYY-MM-DD, các trạng thái revision Draft/Current/Final, xác nhận rằng 2026-07-28 là current, và per-request negotiation rules. Bảng SDK tier tại modelcontextprotocol.io/docs/sdk liệt kê TypeScript, Python, C#, Go và Rust ở Tier 1, Java và Ruby ở Tier 2, và Swift, PHP và Kotlin ở Tier 3.

  11. A2A Protocol, version 1.0.0, a2a-protocol.org — specification và trang A2A and MCP: Relationship and Distinction, đọc ngày 7 tháng 9 năm 2026. Nguồn của phân biệt tools-so-với-agents, phát biểu rằng hai protocols “address distinct but highly complementary needs”, và formulation partnering/using.

  12. Agent Communication Protocol, agentcommunicationprotocol.dev, đọc ngày 7 tháng 9 năm 2026: “ACP is now part of A2A under the Linux Foundation!”, một banner được thêm phía trên một specification vẫn được serve đầy đủ — architecture, agent manifest, agent discovery, message structure, stateful agents, run lifecycle và REST endpoint list đều vẫn trả 200. Specification không biến mất; project thì có.

Sẵn sàng để LIA chọn giúp bạn chưa?

Xây dựng cùng mọi mô hình AI ở một nơi — bắt đầu miễn phí ngay hôm nay.