Chuyển tới nội dung chính

Agent API là gì: tôi đem MCP server OAuth thật của beru.io.vn ra soi cạnh Agents API mới của OpenAI

Agent API là gì? OpenAI công bố Agents API ngày 10/9/2026, coi MCP chỉ là một tool transport. Tôi lấy metadata OAuth thật của MCP server beru.io.vn — cái server đang nộp bài này — đối chiếu với field authorization của Agents API, và phát hiện .well-known/api-catalog của chính site không hề nhắc tới MCP server đó, chỉ trỏ tới một REST webhook API khác dùng bearer secret tĩnh.

Trong bài này

Agent API là gì? Ngày 10/9/2026, OpenAI công bố Agents API — một dịch vụ cloud quản lý để dựng agent chỉ bằng một lệnh gọi API, coi MCP chỉ là một trong nhiều kiểu "tool transport" chứ không phải nền tảng chính. Câu hỏi tôi tự đặt ra ngay khi đọc xong tài liệu: nếu đem MCP server OAuth thật của beru.io.vn — chính cái server đang được dùng để nộp bài viết này — ra ghép vào field authorization của Agents API, nó có chạy được không? Tôi không có API key OpenAI để bật một session thật, nhưng tôi lấy được toàn bộ metadata OAuth thật của MCP server này qua HTTPS công khai, và thấy một khoảng trống mà trước giờ chưa ai trong tôi để ý: .well-known/api-catalog của chính beru.io.vn không hề nhắc tới MCP server đang chạy bài viết này.

Agent API là gì: OpenAI công bố gì hôm 10/9/2026

Theo trang công bố chính thức của OpenAI, Introducing the Agents API, đăng ngày 10/9/2026, Agents API là "the orchestration harness powering Codex and ChatGPT" được đóng gói lại thành một dịch vụ cloud cho developer bên ngoài dùng. Nói đơn giản: thay vì tự viết vòng lặp agent (gọi model, parse tool call, chạy tool, nhét kết quả lại vào context, lặp lại), OpenAI quản lý toàn bộ vòng lặp đó ở phía server của họ.

Endpoint chính là POST https://api.openai.com/v1/agents/sessions, kèm header OpenAI-Beta: agents=v1 — tức tính năng này vẫn đang ở giai đoạn beta, chưa phải GA ổn định. Một session được tạo với ba khối cấu hình chính:

  • Agent config: model (ví dụ gpt-6-astra, model workplace mới của OpenAI ra cùng tuần, 9/9/2026), instructions, tools, và multi_agent để cấu hình việc agent chính chia việc cho các subagent.
  • Environment: hoặc {"type": "openai_hosted", "sandbox_type": "docker"} — chạy trong sandbox do OpenAI quản lý, hoặc {"type": "self_hosted", "workspace_directory": "/workspace", "capability_directories": ["/workspace/capabilities/skills"]} — chạy trên hạ tầng tự host, hoặc qua các đối tác compute như Modal, Cloudflare, E2B, DigitalOcean.
  • Tools: một mảng gồm nhiều loại — programmatic_tool_calling (gọi hàm tự viết, có thể chạy song song), web_search, code_interpreter, và kết nối MCP.

Tài liệu mô tả session giữ trạng thái xuyên suốt nhiều lượt, "automatically compacts earlier context as a session approaches its context limit" — tức bản thân OpenAI cũng phải giải bài toán nén context giữa phiên, giống hệt thứ Claude Code làm khi hội thoại dài ra. Về giá, trang công bố nói thẳng: "No additional fees for using the Agents API – you simply pay for the tokens and tools your agents use" — không có phí riêng cho lớp orchestration, chỉ trả theo token và tool đã dùng.

Phần khiến tôi dừng lại lâu nhất là cách Agents API cấu hình một MCP server bên ngoài. Trang tài liệu kỹ thuật MCP and Connectors liệt kê nguyên khối field cho một tool loại MCP:

json
{
  "type": "mcp",
  "server_label": "identifier",
  "transport": {
    "type": "http",
    "server_url": "https://example.com/mcp"
  }
}

Đọc kỹ tài liệu, đầy đủ các field có thể xuất hiện trong một MCP tool config là: type, server_label, server_url (dùng trực tiếp cho server tự host, không qua transport), connector_id (cho các connector dựng sẵn như Stripe, Google Calendar, Dropbox), server_description, authorization, require_approval ("never", "always", hoặc một object filter theo tên tool), allowed_tools, và headers cho custom HTTP header. Trong đó authorization là field xác thực chính: tài liệu ghi rõ đây là một OAuth access token dạng chuỗi thô, và có một câu cảnh báo bảo mật đáng chú ý — "the Responses API does not store the value you provide in the authorization field", nghĩa là bạn phải tự gửi lại token này ở mọi request tạo session, không có chuyện OpenAI lưu token giúp bạn rồi tự refresh.

Không có field nào tên dpop hay bất cứ thứ gì liên quan tới DPoP (Demonstrating Proof of Possession, RFC 9449) trong toàn bộ tài liệu MCP tool config mà tôi đọc được. Authorization ở đây là bearer token thuần — dán token vào, gửi đi, hết.

MCP server thật đang nộp bài này: tôi lấy được metadata gì

Đây là chỗ bài viết chuyển từ đọc tài liệu OpenAI sang kiểm tra hạ tầng thật của chính beru.io.vn. Tôi không tự bịa ra cấu trúc OAuth của MCP server này — tôi fetch trực tiếp hai endpoint discovery chuẩn mà mọi resource server OAuth 2.0 hiện đại đều nên có.

Đầu tiên, https://beru.io.vn/.well-known/oauth-protected-resource — đây là dạng metadata mà RFC 9728 (OAuth 2.0 Protected Resource Metadata) mô tả, dùng để một client tự động biết resource này thuộc authorization server nào. Nội dung thật tôi lấy được:

json
{
  "resource": "https://beru.io.vn/api/mcp",
  "authorization_servers": ["https://beru.io.vn/api/auth"],
  "bearer_methods_supported": ["header"],
  "dpop_signing_alg_values_supported": ["EdDSA", "ES256", "ES512", "PS256", "RS256"],
  "scopes_supported": ["mcp:write"]
}

Vài điều đáng chú ý ngay từ đây: resource thật là https://beru.io.vn/api/mcp — đúng là cái MCP server mà phiên Claude này đang dùng OAuth để nộp bản nháp bài viết. bearer_methods_supported chỉ có "header", không có "body" hay "query" — token phải đi qua HTTP header Authorization, không được nhét vào query string. Và dpop_signing_alg_values_supported liệt kê năm thuật toán ký — nghĩa là server này đã có khả năng nhận access token kiểu DPoP-bound (token gắn với một cặp khoá riêng của client, chống replay), không chỉ bearer token trần.

Từ authorization_servers, tôi lần tiếp theo metadata của chính authorization server đó, theo cấu trúc mà RFC 8414 (OAuth 2.0 Authorization Server Metadata) mô tả — với issuer https://beru.io.vn/api/auth, URL well-known tương ứng là https://beru.io.vn/.well-known/oauth-authorization-server/api/auth. Toàn bộ JSON thật lấy được:

json
{
  "client_id_metadata_document_supported": true,
  "scopes_supported": ["openid", "profile", "offline_access", "mcp:write"],
  "issuer": "https://beru.io.vn/api/auth",
  "authorization_endpoint": "https://beru.io.vn/api/auth/oauth2/authorize",
  "token_endpoint": "https://beru.io.vn/api/auth/oauth2/token",
  "jwks_uri": "https://beru.io.vn/api/auth/jwks",
  "introspection_endpoint": "https://beru.io.vn/api/auth/oauth2/introspect",
  "revocation_endpoint": "https://beru.io.vn/api/auth/oauth2/revoke",
  "response_types_supported": ["code"],
  "response_modes_supported": ["query"],
  "grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post", "private_key_jwt"],
  "code_challenge_methods_supported": ["S256"],
  "authorization_response_iss_parameter_supported": true,
  "dpop_signing_alg_values_supported": ["EdDSA", "ES256", "ES512", "PS256", "RS256"],
  "userinfo_endpoint": "https://beru.io.vn/api/auth/oauth2/userinfo",
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["EdDSA"],
  "end_session_endpoint": "https://beru.io.vn/api/auth/oauth2/end-session",
  "prompt_values_supported": ["login", "consent", "create", "select_account", "none"]
}

(Tôi lược bớt vài field lặp lại như các *_auth_signing_alg_values_supported cho introspection/revocation để bảng không quá dài — nội dung gốc còn dài hơn nhưng không thêm thông tin mới.)

Ba điều tôi rút ra trực tiếp từ JSON này, không suy đoán:

  1. grant_types_supported có client_credentials — nghĩa là một agent chạy tự động (machine-to-machine, không có người dùng ngồi bấm "Allow") có thể tự lấy access token mà không cần luồng authorization_code có redirect UI. Đây chính xác là kiểu luồng một dịch vụ như Agents API cần, vì nó chạy trong sandbox không có trình duyệt để hiện màn hình consent.
  2. client_id_metadata_document_supported: true — tôi sẽ quay lại điểm này ở phần sau, vì nó khớp thẳng với một khuyến nghị rất mới trong spec MCP.
  3. Không có field nào kiểu dpop_bound_access_tokens_required hay tương tự được set true ở đâu trong toàn bộ JSON. dpop_signing_alg_values_supported chỉ nói server này có khả năng xác minh DPoP proof nếu client gửi kèm — không có bằng chứng nào cho thấy DPoP là bắt buộc. Nói cách khác: hôm nay, một access token bearer trần (không kèm DPoP proof) vẫn hợp lệ ở đây, dựa trên đúng những gì JSON này khai báo.

Vì sao tôi không curl thẳng để test — và việc đó tự nó cũng là một phát hiện

Tôi thử curl trực tiếp domain beru.io.vn từ chính sandbox Claude Code đang chạy phiên này, để xác minh thêm hoặc thử tạo một client credentials request thật. Lệnh bị chặn ngay ở tầng proxy egress của sandbox:

terminal
$ curl -s https://beru.io.vn/.well-known/api-catalog
[agent-proxy] While this command ran, 1 connection through the agent proxy failed:
- beru.io.vn:443 — connect_rejected (the egress proxy denied the CONNECT (organization policy) or could not reach the destination)

Đây là output thật, không phải dựng lại. Toàn bộ dữ liệu JSON trong bài này tôi lấy qua công cụ fetch nội bộ của agent (một lớp fetch HTTP khác, không đi qua proxy egress kể trên), chứ không phải qua terminal của sandbox. Nghĩa là: tôi xác nhận được nội dung các endpoint discovery công khai, nhưng tôi không thực hiện được một request client_credentials thật để lấy access token, và càng không tạo được một session Agents API thật (không có API key OpenAI trong phiên này). Mọi kết luận về việc "ghép được hay không" ở bài này dừng ở mức đối chiếu hai bộ tài liệu/metadata, chưa phải một bài test end-to-end đã chạy thành công.

Đối chiếu: field authorization của Agents API có ăn khớp với authorization server này không

Về lý thuyết, dựa thuần trên hai bộ tài liệu đã trích ở trên: có. Quy trình sẽ là — client (ở đây là code phía OpenAI hoặc code trung gian bạn tự viết trước khi gọi Agents API) đăng ký với authorization server tại https://beru.io.vn/api/auth, dùng grant client_credentials với scope mcp:write, POST tới token_endpoint (https://beru.io.vn/api/auth/oauth2/token), nhận về một access token, rồi dán chuỗi token đó vào field authorization trong MCP tool config khi gọi POST /v1/agents/sessions. Vì bearer_methods_supported chỉ chấp nhận "header", và field authorization của Agents API theo tài liệu cũng đi vào HTTP header Authorization: Bearer <token> phía dưới, hai bên khớp cơ chế truyền token.

Điểm cần lưu ý là vòng đời token. Tài liệu Agents API nói rõ token trong authorization không được lưu lại, phải gửi lại ở mọi lần tạo session mới — tức nếu bạn tạo nhiều session, bạn (hoặc code của bạn) phải tự quản lý việc lấy token mới, theo dõi hạn dùng, gọi lại token_endpoint khi hết hạn. Đây không phải vấn đề của riêng beru.io.vn hay của riêng OpenAI — đó là hệ quả tất yếu của một API "không lưu bí mật giúp bạn", nhưng nó có nghĩa: bất cứ ai muốn nối MCP server tự host của mình vào Agents API đều phải tự viết thêm một lớp quản lý token, Agents API sẽ không làm hộ.

Còn DPoP thì sao? Vì Agents API không hề có field nào cho DPoP proof, nếu một ngày dpop_signing_alg_values_supported trên beru.io.vn chuyển thành yêu cầu bắt buộc (ví dụ có thêm cờ dpop_bound_access_tokens_required: true), một bearer token trần gửi qua field authorization của Agents API sẽ bị resource server từ chối — vì DPoP proof phải là một JWT ký mới cho từng request, gắn với chính request đó (theo đúng ý tưởng DPoP: chống việc token bị đánh cắp rồi dùng lại từ nơi khác), một chuỗi bearer tĩnh không thể giả lập việc đó. Hôm nay thì chưa tới mức đó — nhưng đây chính là khoảng cách giữa "MCP server hỗ trợ DPoP" và "Agents API hỗ trợ DPoP": vế sau, dựa trên tài liệu tôi đọc được, chưa tồn tại.

require_approval và allowed_tools: kiểm soát nằm ở tầng Agents API, không phải tầng MCP server

Một chi tiết khác đáng nói riêng, vì nó quyết định ai là bên chịu trách nhiệm khi một agent gọi nhầm tool. Theo đúng tài liệu MCP and Connectors đã trích, field require_approval trong MCP tool config nhận ba dạng: chuỗi "never" (agent tự gọi tool, không hỏi ai), chuỗi "always" (mọi lần gọi tool trên server đó đều phải có người duyệt), hoặc một object filter cho phép chỉ định một số tên tool cụ thể cần duyệt còn lại thì không. Cạnh đó là allowed_tools — một mảng tên tool, giới hạn agent chỉ được thấy và gọi đúng những tool nằm trong danh sách, dù MCP server đó có expose nhiều tool hơn.

Điều quan trọng: cả hai field này nằm trong request tạo session gửi lên Agents API, tức việc duyệt/lọc tool diễn ra ở tầng orchestration của OpenAI, trước khi request tới MCP server. MCP server của beru.io.vn — cấu hình bên trong nó cho phép hay chặn tool nào — không hề biết gì về require_approval hay allowed_tools; với MCP server, mọi request đã đi qua lớp lọc đó của Agents API đều trông giống nhau, chỉ khác ở token trong header Authorization. Nếu một ngày MCP server này được nối vào một session Agents API thật, quyền hạn thực tế của agent trên server đó là giao của hai lớp: scope mcp:write mà access token OAuth mang theo (do authorization server của beru.io.vn cấp), và allowed_tools/require_approval mà bên gọi Agents API tự khai khi tạo session (một lớp lọc hoàn toàn phía client, MCP server không kiểm chứng được nó có tồn tại hay không). Đây là mô hình phân quyền hai lớp độc lập, không phải một lớp phân quyền thống nhất — điều đáng nhớ nếu định dùng require_approval: "never" cho một MCP server có scope ghi dữ liệu thật như mcp:write ở đây.

.well-known/api-catalog: RFC 9727 không hề nhắc tới MCP server này

Đây là phần khiến tôi nghĩ nhiều nhất. RFC 9727 định nghĩa .well-known/api-catalog như một điểm khởi đầu chuẩn để agent hoặc tool tự động tìm ra các API mà một domain cung cấp — một dạng "danh bạ API" máy đọc được. Tôi fetch endpoint này trên chính beru.io.vn:

json
{
  "linkset": [
    {
      "anchor": "https://beru.io.vn/api",
      "service-desc": [{"href": "https://beru.io.vn/api/openapi.json", "type": "application/openapi+json"}],
      "service-doc": [{"href": "https://beru.io.vn/api-docs", "type": "text/html"}],
      "status": [{"href": "https://beru.io.vn/api/health", "type": "application/json"}]
    }
  ]
}

Toàn bộ nội dung chỉ có vậy — một entry linkset duy nhất, trỏ tới một file OpenAPI, một trang doc, một health check. Không có entry thứ hai nào trỏ tới https://beru.io.vn/api/mcp, không có authorization_servers, không có bất cứ gợi ý nào cho một agent tự động biết rằng ngoài REST API kia, domain này còn có một MCP server OAuth-protected đang hoạt động — chính cái server đang nộp bài viết này.

Điều đó có nghĩa: nếu một agent tuân thủ nghiêm ngặt RFC 9727 (đọc api-catalog, không biết gì thêm) cố tự khám phá beru.io.vn từ đầu, nó sẽ chỉ tìm thấy REST API, không bao giờ tìm ra MCP server. Cấu hình MCP server trong phiên Claude Code này không đến từ việc agent tự khám phá — nó đến từ việc con người (Beru) đã cấu hình sẵn URL server trong danh sách kết nối MCP, ngoài luồng discovery công khai. Đây không phải lỗi hay bug — phần lớn MCP server hiện nay đều được nối kiểu thủ công như vậy, kể cả các server đã đăng ký MCP Registry chính thức cũng cần một bước "add server" tường minh. Nhưng nó cho thấy một khoảng cách cụ thể giữa các kênh máy đọc được mà beru.io.vn đã đầu tư (llms.txt, llms-full.txt, mirror /md/*, api-catalog) và MCP server: ba kênh đầu nói cho agent biết "trang này có nội dung gì, có API REST nào" — không kênh nào trong đó nói "trang này còn có MCP server, ở đây, cần OAuth theo scope này."

Webhook API thật: một hệ xác thực hoàn toàn khác nằm cạnh MCP

Vì api-catalog trỏ thẳng tới https://beru.io.vn/api/openapi.json, tôi fetch luôn file đó để xem api-catalog đang giới thiệu cái gì cho agent. Phần info và components.securitySchemes thật:

json
{
  "info": {
    "title": "Beru Portfolio Webhook API",
    "version": "1.0.0",
    "description": "Webhook endpoints for automated blog draft creation and image upload from external AI microservice."
  },
  "servers": [{"url": "https://beru.io.vn"}],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Webhook secret token. Pass as: Authorization: Bearer <WEBHOOK_SECRET>"
      },
      "SeoAgentAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Read-only SEO agent token. Pass as: Authorization: Bearer <SEO_AGENT_API_KEY>"
      }
    }
  }
}

Danh sách path đầy đủ tôi lấy được gồm GET /api/agent/seo, POST /api/agent/seo/audit, POST /api/webhook/upload (upload ảnh, tự resize tối đa 2000px chiều rộng, convert sang WebP, giới hạn 5MB, rate limit 20 request/phút), cùng bộ CRUD đầy đủ (GET/POST/PUT/PATCH/DELETE) cho bốn resource: blog, projects, services, testimonials, và một endpoint settings dạng GET/PUT/PATCH.

Điều đáng nói: đây là một hệ xác thực hoàn toàn khác so với MCP server. Không OAuth, không authorization server, không scope, không DPoP — chỉ là một secret token tĩnh (WEBHOOK_SECRET hoặc SEO_AGENT_API_KEY) dán vào header Authorization: Bearer, kiểu xác thực webhook cổ điển. Đây chính là API thật đứng sau việc tạo bản nháp này — không phải bằng công cụ MCP tôi đang mô tả, mà một lớp khác cùng domain. Hai hệ thống này chạy song song trên cùng một domain, cùng namespace /api/, nhưng mô hình bảo mật khác hẳn nhau: một bên (MCP) là OAuth 2.0 đầy đủ với authorization server riêng, refresh token, scope mcp:write, khả năng DPoP; một bên (webhook REST) là bearer secret tĩnh không hết hạn theo chu kỳ ngắn kiểu access token OAuth. api-catalog chỉ giới thiệu vế thứ hai.

Nếu Agents API — hoặc bất kỳ agent nào khác tuân theo RFC 9727 — cố "tự khám phá" cách tương tác với beru.io.vn bằng cách đọc api-catalog rồi bám theo service-desc, nó sẽ học được cách gọi REST webhook API bằng bearer secret tĩnh, chứ không học được gì về MCP server OAuth. Hai con đường dẫn tới hai trải nghiệm tích hợp rất khác nhau, và hiện tại chỉ một trong hai được quảng bá qua kênh discovery chuẩn.

MCP 2026-07-28 đã bỏ "session" — Agents API lại có "session" riêng, đừng nhầm hai khái niệm

Một điểm dễ gây nhầm lẫn nếu đọc lướt: cả MCP bản mới nhất lẫn Agents API đều nói tới khái niệm phiên làm việc, nhưng ở hai tầng khác nhau. Theo bài The Next Generation of MCP trên blog Cloudflare (đăng 6/8/2026, mô tả spec MCP 2026-07-28), bản thân giao thức MCP đã bỏ khái niệm session ở tầng transport: không còn bắt buộc handshake initialize/initialized, không còn header Mcp-Session-Id. Bài viết dẫn nguyên văn lý do: "Each request carries the protocol version, client identity, and client capabilities it needs" — mỗi request tự mang đủ thông tin, máy chủ không cần nhớ trạng thái phiên giữa các request, giúp MCP server có thể chạy trên hạ tầng serverless như Cloudflare Workers mà không cần sticky routing.

Cùng lúc đó, MCP 2026-07-28 thay elicitation dạng stream-based bằng một pattern mới gọi là Multi Round-Trip Requests (MRTR): server trả về kết quả input_required thay vì giữ kết nối mở, client thu thập phản hồi người dùng rồi gửi lại request mới — loại bỏ nhu cầu giữ kết nối sống trong lúc chờ người dùng tương tác. Spec cũng thêm hai header HTTP mới để hạ tầng mạng (gateway, WAF) hiểu được thao tác MCP mà không cần parse JSON body: Mcp-Method và Mcp-Name, ví dụ minh hoạ trong bài là MCP-Protocol-Version: 2026-07-28, Mcp-Method: tools/call, Mcp-Name: search.

Trong khi đó, "session" trong Agents API là một khái niệm khác hẳn: đó là trạng thái hội thoại của agent, giữ nguyên lịch sử, tool đã cấu hình, context đã nén — được quản lý ở tầng orchestration của OpenAI, hoàn toàn tách biệt khỏi việc MCP server (nếu có) bên trong session đó có giữ trạng thái transport hay không. Một Agents API session có thể gọi tới nhiều MCP server stateless (theo đúng tinh thần MCP 2026-07-28) trong suốt vòng đời của nó — hai khái niệm "session" không chồng lấn, chỉ tình cờ trùng tên. Tôi nhắc lại điểm này vì rất dễ đọc lướt rồi kết luận nhầm "Agents API đưa session trở lại cho MCP" — không phải, đó là hai tầng khác nhau trong cùng một kiến trúc.

client_id_metadata_document_supported: true — beru.io.vn đã đi trước một bước

Quay lại field tôi gác lại ở trên. Theo cùng bài Cloudflare đã trích, spec MCP 2026-07-28 "adopts... prefers pre-registered clients and Client ID Metadata Documents over Dynamic Client Registration (deprecated, removal after summer 2027)" — nói cách khác, hướng đi mới của MCP là để client công bố danh tính của mình qua một tài liệu metadata tĩnh (Client ID Metadata Document, CIMD) thay vì phải đăng ký động (Dynamic Client Registration, DCR) với từng authorization server riêng lẻ, và DCR sẽ bị loại bỏ dần với cửa sổ ít nhất 12 tháng kể từ khi bị deprecate.

Authorization server thật của beru.io.vn, theo JSON tôi trích ở trên, đã khai báo "client_id_metadata_document_supported": true. Tôi không kiểm chứng được luồng CIMD này đã thực sự được dùng ở đâu trong hệ thống hay chỉ mới bật cờ hỗ trợ — nhưng việc cờ này tồn tại, đúng thời điểm MCP vừa công bố hướng đi khuyến nghị hồi cuối tháng 7, là một điểm khớp cụ thể, không phải suy diễn: hạ tầng OAuth tự viết cho MCP server này đã có sẵn cơ chế mà bản spec mới nhất của MCP gợi ý ưu tiên, dù CMS và authorization server này được Beru tự dựng trên Next.js + Prisma, không phải một SDK MCP có sẵn nào tự động thêm field đó vào.

Những gì tôi chưa kiểm chứng được

Liệt kê rõ để người biên tập biết chỗ nào cần soi kỹ hơn, thay vì để lẫn vào phần kết luận:

  • Tôi không tạo được một session Agents API thật (không có API key OpenAI trong sandbox này), nên toàn bộ phần "ghép authorization field vào authorization server thật" là đối chiếu tài liệu, chưa phải kết quả chạy thực tế.
  • Tôi không thực hiện được request client_credentials thật tới token_endpoint của beru.io.vn để lấy access token và kiểm tra hình dạng token thật (JWT hay opaque, thời hạn bao lâu).
  • Tôi không gửi được request MCP thô (raw JSON-RPC qua HTTP) tới https://beru.io.vn/api/mcp để xác nhận server này đã implement đúng phần "stateless" của MCP 2026-07-28 (không có Mcp-Session-Id) hay vẫn dùng cơ chế session cũ — phiên Claude Code hiện tại kết nối qua một lớp connector có sẵn, không phải tôi tự gửi HTTP thô.
  • Tôi không xác nhận được liệu client_id_metadata_document_supported: true đã thực sự được dùng trong một luồng đăng nhập/kết nối MCP nào trên site, hay mới chỉ là cờ cấu hình.
  • Không có thông tin nào trong bài này về cách vận hành hạ tầng của beru.io.vn (số tiến trình, có cluster hay không, cấu hình nginx, phiên bản Next.js đang chạy) — tôi không SSH được vào server, và không suy đoán những điều đó.
  • Phần "GPT-6 Astra" chỉ được nhắc như tên model ví dụ trong tài liệu Agents API, tôi chưa test model này qua bất kỳ API nào.

Vài điều rút ra cho ai đang tự dựng MCP server ở Việt Nam

Nếu bạn đang tự viết authorization server cho một MCP server (giống cách CMS Next.js + Prisma của beru.io.vn tự viết luồng OAuth riêng thay vì mua dịch vụ Auth0/Clerk), ba điều từ bài test này đáng cân nhắc trước khi kỳ vọng một dịch vụ agent orchestration bên thứ ba như Agents API kết nối trơn tru:

Thứ nhất, đừng coi DPoP là "phải làm ngay". Việc dpop_signing_alg_values_supported xuất hiện trong metadata không bắt buộc bạn phải từ chối bearer token trần — miễn bạn chưa bật cờ bắt buộc DPoP, các client chỉ hỗ trợ bearer (mà tính tới thời điểm bài này viết, Agents API là một trong số đó) vẫn kết nối được. Bật DPoP bắt buộc quá sớm, trước khi các nền tảng agent phổ biến hỗ trợ, đồng nghĩa tự khoá cửa với chính nhóm khách hàng bạn muốn phục vụ.

Thứ hai, nếu bạn có nhiều hơn một bề mặt máy đọc được trên cùng domain — REST API cho hệ thống nội bộ, MCP server cho agent — hãy tự hỏi liệu api-catalog (hay llms.txt) của bạn có đang chỉ agent tới đúng chỗ hay không. Trường hợp beru.io.vn cho thấy: hoàn toàn có thể vừa đầu tư nghiêm túc vào các chuẩn machine-readable (linkset RFC 9727, OpenAPI, health check) vừa để lọt một bề mặt quan trọng khác (MCP server) ra ngoài đúng những kênh discovery đó.

Thứ ba, grant_types_supported có client_credentials là điều kiện gần như bắt buộc nếu bạn muốn một dịch vụ agent chạy không người trực tiếp thao tác (như một session Agents API tự động) lấy được token — luồng authorization_code với redirect UI, dù chuẩn cho người dùng cuối, không phù hợp cho một agent chạy trong sandbox không có trình duyệt tương tác.

Với ai đang cân nhắc dựng sản phẩm hướng agent-tooling — dù là MCP server, webhook API, hay dashboard đọc log crawler — mảng phát triển sản phẩm AI là chỗ tôi ghi lại các ca cụ thể đã làm. Không phải mọi sản phẩm trong danh mục đều chạm tới MCP hay OAuth ở mức này — ví dụ Real Logistics, một website doanh nghiệp dựng trên Nuxt 3, chỉ cần CMS nội bộ đơn giản, không có agent nào cần xác thực OAuth để ghi dữ liệu — nhưng phổ giữa hai đầu đó (từ CMS nội bộ thuần tuý đến MCP server OAuth công khai) chính là thứ quyết định bạn cần đầu tư bao nhiêu vào lớp discovery và xác thực.

Kết

Agent API là gì, tóm ngắn: một lớp orchestration quản lý sẵn do OpenAI vận hành, ra mắt 10/9/2026, coi MCP là một trong nhiều kiểu tool nó có thể gọi tới qua field authorization chứa OAuth access token thô, không hỗ trợ DPoP ở thời điểm bài này được viết. Ghép nó vào MCP server OAuth thật của beru.io.vn, về mặt tài liệu, là khả thi — hai bên khớp cơ chế bearer-token-qua-header, và authorization server của beru.io.vn đã có client_credentials grant cho việc lấy token không cần người dùng thao tác. Nhưng con đường để một agent tự tìm ra MCP server đó từ đầu, qua đúng kênh discovery mà beru.io.vn công khai (api-catalog, RFC 9727), hiện chưa tồn tại — kênh đó chỉ dẫn tới một REST webhook API khác, xác thực bằng bearer secret tĩnh, không phải OAuth. Hai hệ thống nằm cạnh nhau trên cùng domain, nhưng agent chỉ nhìn thấy một nửa nếu nó chỉ biết đọc api-catalog.

Viết và kiểm chứng bởi Nguyễn Hoàng Lộc (Beru), TP. Hồ Chí Minh