Homus
‹ All talks

MCP Apps: Primitives, Discovery, and the Future of Software

AI Engineer World's Fair 2026: Online Track · Video gốc

MCP Apps: tool trả UI vào chat, các primitive setState, ui/message, streaming input, giấu dữ liệu khỏi model, và cách submit lên store của ChatGPT, Claude, Cursor.

AgentsDev ToolsAI UX

1. Ai cũng biết MCP, ít ai biết MCP Apps

Pietro chào mọi người và tự giới thiệu: anh là co-founder của Manufact. Hôm nay anh muốn nói về MCP Apps, cụ thể là ba chuyện. Thứ nhất là các primitive: những app này được build ra sao, chạy ra sao và cho bạn làm được gì. Thứ hai là cách phân phối MCP Apps, tức là cái gì nằm phía sau cơ chế discovery của MCP server và MCP app nói chung. Thứ ba là vì sao anh nghĩ bạn nên quan tâm, vì theo anh đây chính là cách mọi phần mềm sẽ được dùng trong tương lai.

Slide tiêu đề MCP Apps của Pietro Zullo, Manufact
Slide mở đầu: "Primitives, discovery, and why this is how all software will be used", Pietro Zullo, Manufact.

Anh tin hầu hết người đang nghe talk này đều biết MCP là gì. MCP (Model Context Protocol) đã có mặt khá lâu, từ năm 2024. Suốt cả một năm, nó là chủ đề được bàn rất nhiều giữa developer và các công ty đang đua nhau build MCP server.

MCP Apps thì là một khái niệm ít quen hơn. Chúng cũng đã xuất hiện được một thời gian, đại khái từ cuối năm 2025. Nhưng khi nói chuyện với các công ty và với mọi người nói chung, anh thấy nhiều người không hiểu MCP Apps là gì, và không biết là mình có thể build chúng. Đặc biệt, họ không biết cách phân phối MCP Apps, và cũng không biết cách mới để phân phối cả MCP server nữa.

Slide You know MCP. Do you know MCP Apps?
"You know MCP. Do you know MCP Apps?": ai trong phòng cũng biết MCP, nhưng ít người biết MCP Apps là gì, chạy thế nào, build và phân phối ra sao. Hết talk, bạn sẽ có đủ những gì cần để nhập cuộc.

Vì vậy, anh hy vọng tới cuối talk bạn sẽ nắm được những điều này, sẵn sàng build MCP server và MCP app đầu tiên của mình, hoặc tiếp tục iterate trên cái bạn đang có, rồi chia sẻ nó ra thế giới theo một cách hiệu quả hơn, mang về cho bạn nhiều khách hàng và nhiều user hơn.

2. Manufact: mcp-use để build, Inspector để test, cloud để ship

Đôi lời về anh và công ty của anh, Manufact. Nhóm build các SDK open source và dev tool cho MCP, cùng một cloud cho MCP cũng mang tên Manufact.

Các SDK open source mang tên mcp-use, giúp developer build server, client và agent dễ hơn. Nói cách khác, mcp-use là một lớp abstraction phía trên các SDK chính thức, cho phép developer ship nhanh hơn mà không phải lo spec bên dưới vận hành ra sao. Các SDK của nhóm đã có hơn tám triệu lượt download, và mười nghìn star trên GitHub.

Cũng open source nhưng là một sản phẩm riêng là Inspector. Đây là một inspector open source, có thể so sánh với inspector chính thức của những người maintain Model Context Protocol, cho phép bạn test MCP server, và đặc biệt là MCP app, ngay trên máy local.

Slide Open-source SDKs and dev tools for MCP: mcp-use, Inspector, Manufact
"Open-source SDKs and dev tools for MCP": ba sản phẩm theo ba bước, mcp-use để build, Inspector để test, Manufact để ship. Bên dưới là hai con số 8M+ download và 10k GitHub star.

Khi bạn đã build xong và test xong, nhóm đã dựng sẵn cloud để bạn ship. Manufact Cloud là một cloud chuyên biệt (vertical) cho MCP. Nó cung cấp mọi primitive để bạn ship MCP server thẳng từ GitHub repo và test ngay lập tức, chia sẻ với team, chạy eval, chạy các bước kiểm tra trước khi publish để chắc rằng app của bạn đã sẵn sàng để submit, cùng nhiều tính năng khác hoàn toàn dành riêng cho MCP.

Dĩ nhiên, như anh nói, nhóm anh thực sự tin vào MCP.

3. Lịch sử: từ MCP-UI tới MCP Apps và các store

Hãy nhìn lại một chút lịch sử của MCP để hiểu vì sao ta tới được đây.

MCP ra mắt năm 2024. Tới khoảng cuối tháng Năm 2025, Ido Salomon và Liad Yosef bắt đầu làm MCP-UI, một cách để MCP server trả về UI component. Rồi rất, rất nhiều người bắt đầu nói về MCP-UI, vì đây là một cơ hội tuyệt vời để ship UI cùng với MCP server tới agent: build những trải nghiệm tương tác, trong đó agent gọi tool nhưng đồng thời hiển thị UI cho user.

Đề xuất này tạo ra rất nhiều chuyển động. Nhiều người thích nó, và tới một lúc ChatGPT tung ra Apps SDK, một cách để tạo những MCP app tương tác này, về cơ bản là những MCP server cũng trả về cả UI element.

Cũng vào cuối năm 2025, khá lặng lẽ, cả ChatGPT lẫn Claude đều phát hành và mở store cho MCP. Các store này cho phép bạn submit MCP server và mang lại cho user trải nghiệm install chỉ một click. Phần lớn thời gian, các store này còn đóng, theo nghĩa chúng được thiết kế cho và chỉ cho phép các design partner. Nhưng mọi thứ đã thay đổi, và anh sẽ vui lòng nói thêm về chuyện đó ở phần sau.

Tháng Một năm 2026, MCP-UI, có thể nói là, chuyển thành MCP Apps. MCP Apps giờ là extension chính thức của Model Context Protocol, cho phép MCP server trả về UI element.

2024 MCP ra mắt cuối 5/2025 MCP-UI server trả UI component 2025 Apps SDK của ChatGPT cuối 2025 Store MCP ChatGPT và Claude 1/2026 MCP Apps extension chính thức Hai bước lớn: server không chỉ trả JSON nữa, và store mở cửa
Dòng thời gian Pietro kể lại: từ MCP tới MCP-UI, Apps SDK, các store và cuối cùng là MCP Apps thành extension chính thức của giao thức.

Dòng thời gian này phần nào giải thích giao thức đã tiến hóa ra sao. Và theo anh, có hai điều lớn đã xảy ra trong đó.

Điều thứ nhất là chính MCP Apps: MCP server không còn chỉ trả về JSON, và điều đó cho phép những trải nghiệm phong phú hơn nhiều.

Điều thứ hai, có lẽ còn lớn hơn, là các store đã mở. Đây là một bước đi rất lớn của các model provider, Anthropic, OpenAI, Cursor, về cơ bản mọi LLM client ngoài kia, như thể họ nói: "MCP là con đường, và chúng tôi muốn có một cách để mọi người publish những MCP server đã được kiểm duyệt và có chất lượng, để người dùng có thể dùng chúng với trải nghiệm install một click."

4. Store đang mở: đây là lúc publish app của bạn

Đây là tình hình hiện tại của các store. Những app đang được submit lên ChatGPT và Claude, tức "apps" cho ChatGPT và "connectors" cho Claude, ngày càng được chấp nhận nhiều hơn.

Như anh đã nói, lúc đầu đây là tính năng bị khóa sau quan hệ design partner, vì hệ sinh thái còn rất non trẻ. Nhưng giờ cả ChatGPT lẫn Claude đều đang chấp nhận ngày càng nhiều app. Nên đây chính là thời điểm để publish app của bạn.

Biểu đồ cột cộng dồn số ChatGPT apps và Claude connectors theo tuần
"Apps are landing in the stores, fast": số ChatGPT apps (xanh) và Claude connectors (cam) cộng dồn theo tuần, từ W1 tới W18, tăng từ vài chục lên gần 800. Nguồn ghi trên slide: appdiscoverability.com.

5. Một MCP App hoạt động thế nào

Giờ hãy xem MCP app thực sự là gì và bạn làm được gì với nó.

Slide Part 1: What MCP Apps actually are
Phần 1, "What MCP Apps actually are": một App được khai báo, render và nối vào model như thế nào.

Một MCP app vận hành như sau, và phần lớn rất giống MCP thường. Model nằm trong host, cùng với các tool. Tool nằm trong một MCP server, nhưng ở đây MCP server không trả về một chuỗi JSON nữa, mà trả về một widget nằm trong một sandboxed iframe.

Trải nghiệm bạn thấy là thế này: model đang stream text, tới một lúc nó quyết định gọi một tool call. Tool call đó không chỉ trả JSON, nó trả về một UI nằm ngay bên dưới. UI này là một sandboxed iframe, nên là một công ty phát triển app, bạn có thể đặt vào gần như bất cứ thứ gì bạn muốn.

Nhưng chưa hết. Thực ra có một kênh giao tiếp hai chiều giữa iframe và ứng dụng host. Từ iframe, tức là từ các UI element của MCP app, bạn có thể gửi message ngược về host. Bạn có thể tương tác theo nhiều cách, và anh sẽ nói kỹ hơn ở phần sau.

Cơ chế là MCP khai báo các UI resource ngay lúc khởi tạo (initialization). Khi model gọi tool và điền các argument của tool, tool có thể điền tiếp argument cho UI resource, và client hiển thị, render nó ra.

Sơ đồ How an MCP App works: Model, Host, MCP server, Widget
"How an MCP App works": Model (suy luận, gọi tool) nói chuyện với Host (ChatGPT, Claude, Cursor); Host gọi MCP server qua tools/call và resources/read; server có tool và các resource ui://. Widget là một sandboxed iframe nói chuyện với Host qua ui/* trên postMessage. Dòng chú thích: server công bố UI dưới dạng một resource ui://, host render nó trong sandboxed iframe và chuyển tool input, kết quả và các lệnh gọi của widget qua lại bằng JSON-RPC.

Như bạn thấy ở đây, đây là kiểu trải nghiệm bạn có với một MCP app. MCP server trả về tool, trả về một UI resource được điền bằng các argument của tool. Nếu không có UI, bạn sẽ thấy một bức tường chữ. UI cho phép bạn sắp xếp thông tin theo cách con người dễ đọc hơn.

Demo Rendered by the host, inline, chế độ Without UI: danh sách giày dạng text
"Rendered by the host, inline", chế độ Without UI: user hỏi giày chạy dưới 150 đô, tool search_catalogue được gọi và kết quả chỉ là một danh sách chữ.
Cùng demo ở chế độ With UI: ba thẻ giày Trailblazer Pro, Cloudstride 2, Tempo Racer
Cùng MCP server đó, có thêm lớp UI: host render ngay trong chat ba thẻ sản phẩm Trailblazer Pro ($129), Cloudstride 2 ($145) và Tempo Racer ($110), mỗi thẻ có nút "Add to cart".

Đó là phần cơ bản của MCP apps, và anh nghĩ nó đã được nói tới nhiều lần. Hôm nay anh muốn cho thấy thêm những gì bạn có thể làm, vì phần này không hay được nhắc tới, mà anh thấy nó rất thú vị để thiết kế những trải nghiệm mới mà giao thức mới này cho phép.

6. Primitive 1: model context và setState

Trước hết, nhắc lại: UI được hiển thị trong chat, và nó mở ra một kênh giao tiếp giữa UI element và ứng dụng host. Ứng dụng host sẽ lắng nghe các message đi qua kênh đó và phản ứng tương ứng.

Primitive đầu tiên anh nói tới là model context. Trong UI, bạn có thể hiển thị bất cứ thông tin gì. Ví dụ trong trường hợp này đang hiển thị ba món hàng, nhưng model thật ra không biết. Nó không thể tự nhìn vào (introspect) theo thời gian thực xem điều gì đang diễn ra trong UI.

Nhưng giao thức quy định một primitive state, hay setState, cho phép bạn cập nhật trạng thái mà model biết liên quan tới các UI component. Ngay từ trong widget, bạn có thể gọi primitive setState (đây là cú pháp mcp-use, giúp việc này dễ hơn một chút) và cập nhật điều model biết về những gì đang được hiển thị.

Có một demo nhỏ minh họa. Dĩ nhiên message của user kích hoạt tool, và tool hiển thị UI. Trạng thái của UI element này là chưa có gì được chọn, và model biết điều đó. Nhưng nếu bạn thay đổi trạng thái UI, bạn có thể truyền thay đổi đó tới chính model. Về cơ bản, nếu ở đây bạn gửi thêm một message, model sẽ biết điều gì đã xảy ra trong UI element. Và bạn làm được điều này chỉ bằng cách dùng primitive setState để cập nhật trạng thái mà model biết.

Widget user chọn một món setState({...}) Host nhận trạng thái mới của UI Model context đã cập nhật Message kế tiếp của user: model đã biết điều gì vừa xảy ra trong UI
Primitive model context: model không tự nhìn được vào UI, nên widget dùng setState để đẩy trạng thái hiện tại của UI vào context của model.

7. Primitive 2: ui/message, widget nói ngược vào cuộc chat

Tiếp theo là UI message, một tính năng rất hay khác: từ UI element, tức là UI widget mà tool của bạn trả về, bạn có thể gửi message ngược về model.

Như vậy tương tác không chỉ còn là: tôi là user, tôi có giao diện chat, tôi thấy UI, và tôi muốn gửi thêm một message. Có thể có những message mang tính ngữ cảnh mà bạn muốn gửi. Ví dụ, đây vẫn là ví dụ đôi giày, và bạn muốn tìm hiểu thêm về Trailblazer Pro. Tất nhiên bạn có thể gắn cú click vào nút "Learn More" với primitive sendFollowUpMessage, nó sẽ gửi một message vào chính cuộc chat, và model bắt đầu cho bạn thêm thông tin về đôi Trailblazer Pro trong trường hợp này.

Slide ui/message với code sendFollowUpMessage và demo danh sách giày có nút Learn more
"ui/message": App nói ngược vào cuộc hội thoại mà không cần user gõ. Một cú click trong UI gửi đi một message như thể chính user gửi. Bên phải là widget ba đôi giày, mỗi đôi có nút "Learn more".

Đoạn code trên slide:

const { sendFollowUpMessage } = useWidget();
sendFollowUpMessage("Add the Tempo Racer");

Mỗi client có hành vi khác nhau với tính năng này, và điều đó đúng với nhiều tính năng MCP khác. Ví dụ, Claude sẽ đặt message vào ô nhập chat và để user lựa chọn có gửi hay không. Còn OpenAI thì tích hợp sâu hơn, theo nghĩa nó gửi thẳng message tới model và bắt đầu stream câu trả lời cho message đó ngay lập tức.

8. Primitive 3: streaming tool input

Đây là một tính năng rất hay. Nhắc lại: ta có tool, và ta có một UI element được điền từ chính tool đó, đúng không? Nếu model stream các input token vào argument của tool, bạn có thể lấy ngay những input chưa hoàn chỉnh đó (partial input) theo thời gian thực và cập nhật UI dần dần.

Như bạn thấy trong ví dụ này, tool input đang được điền dần dần, và UI phản ứng theo. Anh sẽ có một demo rất hay về chuyện này trong một video ở phần sau, theo anh là một trong những demo MCP app hay nhất, dùng đúng pattern này.

Slide Streaming tool input: tools/call draw_scene với ba element Idea, Build, Ship đã vẽ xong
"Streaming tool input": model stream token thẳng vào widget, và widget render chúng ngay khi tới. Ví dụ trên slide là Excalidraw MCP vẽ diagram từng hình một khi model sinh ra: lệnh tools/call draw_scene với ba element "Idea", "Build", "Ship", trạng thái "complete".
Cùng slide ở trạng thái đang streaming, mới có element Idea
Cùng ví dụ khi đang "streaming": JSON mới có một element "Idea", và widget mới vẽ được một khối tương ứng.

Đoạn code trên slide:

const { partialToolInput } = useWidget();
// render shapes as they stream in
draw(partialToolInput?.elements);

Bạn có thể hình dung, ví dụ, một UI component render ra thứ gì đó như một SVG, và đã có MCP app làm đúng điều này. Nhóm anh cũng đã làm một MCP app cho Remotion: dùng Remotion để tạo video bằng React, và render video Remotion bên trong widget theo thời gian thực, ngay khi token stream tới.

9. Gọi tool khác ngay từ widget

Một điều nữa bạn có thể làm là từ chính widget, bạn có thể gọi những tool khác. Trước hết, dĩ nhiên bạn có thể gọi tool, tức là tool đã được gọi ban đầu, để lấy thêm dữ liệu. Nhưng từ UI, ví dụ, bạn có thể có một nút bấm kích hoạt một tool call khác để lấy thêm dữ liệu về những gì có trong MCP server.

Một lần nữa, primitive rất đơn giản. Đoạn code bên trái trên slide vẫn là cú pháp mcp-use.

Widget nút "xem thêm" Host tools/call MCP server tool khác Nét liền: widget gọi tool · nét đứt: dữ liệu mới quay về widget
Widget có thể tự gọi tool của MCP server, ví dụ qua một nút bấm, để lấy thêm dữ liệu mà không cần user gõ thêm.

10. Tell the model less than you show: giữ dữ liệu riêng khỏi model

Đây là một điều rất thú vị khác bạn có thể làm với MCP apps. Đôi khi chuyện xảy ra là bạn muốn MCP server trả về một số thông tin, nhưng bạn không muốn đưa toàn bộ thông tin đó ra, vì có thể đó là thông tin riêng tư mà bạn không muốn trao cho các model provider. Vì vậy bạn muốn che bớt (redact) thông tin đó, đúng không? Đây là một vấn đề privacy đã được biết tới với MCP server: bạn không muốn trả về và đưa thông tin riêng tư của mình vào đó.

Và MCP apps cho phép bạn làm điều này. Khi bạn gọi một tool, bạn trả về một widget được điền bằng một số argument, hoặc bạn cũng có thể trả về những output khác. Như với MCP server bình thường, giá trị trả về của một MCP tool là một danh sách output thuộc nhiều kiểu khác nhau. Trong trường hợp này, bạn có thể hình dung có một structured output được gửi vào chính widget, và một output bổ sung có thể được gửi thẳng tới model.

Một việc phổ biến là bạn hiển thị một UI rất phong phú, như trong trường hợp này, và đó là thứ UI sẽ hiển thị. Đây là một thẻ hiển thị thông tin, thông tin riêng tư của một người, nhưng model chỉ thấy phần thông tin bạn muốn. Vậy là có hai loại output, nói đơn giản: loại hiển thị trong UI, và loại gửi tới model.

Để hiểu rõ hơn, một pattern phổ biến là bạn hiển thị đầy đủ thông tin trong UI, rồi dùng một text output để cho model biết user đang thấy gì. Ví dụ, ta trả về thẻ UI này, và thậm chí có thể không trả gì cho model cả, chỉ nói rằng user đang xem thông tin riêng tư của họ trong widget phía trên.

Đây là một pattern mở ra những trải nghiệm trong các lĩnh vực mà có lẽ không thể chia sẻ dữ liệu cho LLM vì vấn đề privacy. Trong trường hợp đó, bạn có thể hiển thị UI cho user, nhưng model sẽ không thấy dữ liệu bạn hiển thị trong UI, trừ khi bạn chọn cho nó thấy.

Trên slide "Tell the model less than you show", ý được viết gọn: một tool trả về hai thứ, props mà widget render, và output mà model đọc. Hai thứ này không cần khớp nhau. Hãy trả toàn bộ bản ghi dưới dạng props, và một bản tóm tắt đã che bớt dưới dạng output. Demo trên slide là một user hỏi bản ghi khách hàng của một đơn hàng, tool get_customer được gọi, và một nút chuyển cho phép xem lần lượt "What the model sees" và "What the UI shows": UI hiện đủ email, số thẻ và tổng tiền, còn model không bao giờ nhận các trường đó.

return widget({
  props: record,              // UI renders
  output: text("Gold tier"),  // model reads
});
Tool call get_customer props output Widget (user thấy) bản ghi đầy đủ, cả dữ liệu riêng Model (đọc) tóm tắt đã che, ví dụ "Gold tier"
Pattern "tell the model less than you show": dữ liệu đầy đủ chỉ đi vào UI, model chỉ nhận phần bạn chọn đưa cho nó.

11. Display mode và các primitive còn lại

Còn một nhóm tính năng nữa mà anh coi là phụ, hay nói cách khác là ít gây bất ngờ hơn, ít nâng cao hơn.

Ở đây là requestDisplayMode. Về cơ bản, widget MCP app của bạn mặc định hiển thị inline cùng tool call, nhưng nó thậm chí có thể chuyển sang full screen. Khi đó toàn bộ khung chat sẽ là widget MCP của bạn, và ô nhập chat sẽ nằm chồng lên trên widget.

Ví dụ, điều này rất hay cho việc chỉnh sửa video. Bạn có thể hình dung một widget hiển thị một giao diện đồ họa nào đó, bạn chat với nó để cải thiện những gì đang hiển thị trong widget, và model có thể stream thẳng vào widget bạn đang nhìn.

Widget cũng có thể ở chế độ picture-in-picture, hoặc inline, là trường hợp bình thường.

Slide Display modes, and the rest: inline, pip, fullscreen
"Display modes, and the rest": ba chế độ inline, pip (picture-in-picture) và fullscreen, gọi bằng requestDisplayMode("fullscreen"). Dòng bên cạnh: còn có mở link, theme và locale, và kích thước nội tại (intrinsic size).

Có những primitive khác cho phép bạn mở link ra ngoài ngay từ widget MCP. Bạn có thể lắng nghe theme của host, để MCP app của bạn đồng bộ theme với host mà user đang dùng. Và còn nhiều thứ khác nữa.

12. Demo thật: Cursor, Claude với Excalidraw, ChatGPT

Tới giờ anh mới chỉ cho xem một số mock-up anh tạo cho bài trình bày này. Nên anh muốn cho xem vài video về MCP app thật.

Video đầu tiên là trong Cursor, đang dùng MCP app. Cursor là một trong những client hỗ trợ MCP apps. Ở đây, MCP app của Manufact trả về số liệu analytics của chính MCP server và app Remotion mà anh vừa nhắc tới ở trên.

Widget analytics trong Cursor: biểu đồ cột Clients 30 ngày và bảng sessions, requests theo client
Trong Cursor, user yêu cầu dùng Manufact để xem usage analytics của MCP server Remotion. Widget trả về biểu đồ "Clients" 30 ngày gần nhất và bảng số session, số request theo từng client, đứng đầu là Anthropic/ClaudeAI với 11.776 session và 57.914 request; phía dưới còn Unknown Client, claude-ai và nhiều phiên bản claude-code.

Và đây là một ví dụ cho thấy nó rất hữu ích trong analytics. Chẳng hạn, nhóm anh dùng Postgres MCP rất nhiều, và Postgres MCP sẽ hiển thị cho bạn một UI element với analytics của bạn, để bạn, một con người, hiểu điều gì đang diễn ra, còn chính model thì đọc được những số liệu đó và tiếp tục làm việc của nó trên đoạn code bạn đang viết.

Tiếp theo là trên Claude, đây là cái demo anh đã hứa. Ở đây đang dùng Excalidraw MCP server, và bạn sẽ thấy tính năng streaming mà anh vừa nói. Bạn thấy Claude đầu tiên đọc một số hướng dẫn mà Excalidraw MCP server trả về dưới dạng tool. Tới một lúc, nó sẽ gọi tool hiển thị canvas, và stream token vào canvas.

Claude dùng Excalidraw MCP, canvas How MCP Apps Work bắt đầu được vẽ
Trên Claude, user nhờ Excalidraw vẽ diagram về cách MCP apps hoạt động. Claude kiểm tra định dạng element của Excalidraw trước, rồi canvas "How MCP Apps Work" bắt đầu hiện ra, mới có khung Host.

Theo anh, Remotion đã làm một số animation hay nhất về cách những token đó được hiển thị. Như bạn thấy, đây là một kiểu cú pháp Mermaid được gửi vào tool (trên màn hình, thứ được stream vào là danh sách element của Excalidraw), và UI cập nhật khi token stream tới. Đây là một trong những demo hay nhất. Nhân tiện, nó cũng rất, rất hữu ích để vẽ diagram.

Diagram Excalidraw hoàn thiện: User, Host gồm Agent/LLM, MCP Server, các bước prompt, tools/call, result + UI, render
"Tools return UI; the host renders it inline in chat". User gửi prompt (1) tới Agent/LLM trong Host; Agent gọi tools/call (2) tới MCP Server, nhận "result + UI" (3), render (4) xuống widget, và widget lại có thể gọi tools/call (6).

Và đây lại là ChatGPT, dùng MCP app của Manufact, hiển thị đúng phần analytics anh vừa nói. Như bạn thấy, phần render trên ChatGPT có phần tốt hơn một chút. Về cơ bản thì nó rất giống với cách nó được dùng ở các client kia.

Slide Real MCP apps, in production với ba video demo
"Real MCP apps, in production": slide gom ba video demo, mỗi video theo một client, bấm vào để xem full screen.

13. Client nào hỗ trợ MCP Apps, và xử lý client không hỗ trợ

Có lẽ đây là lúc tốt để nói về mức hỗ trợ của các client. Nhiều client hỗ trợ MCP apps, có client hỗ trợ nhiều, có client hỗ trợ ít. Theo anh, ba client chính mà mọi người đang dùng là Claude, ChatGPT và Cursor, và dĩ nhiên kèm theo đủ các phiên bản khác nhau của Claude và ChatGPT.

  • Claude: cả Claude Cowork và Claude Desktop đều hỗ trợ MCP app.
  • ChatGPT và Codex đều hỗ trợ MCP app.
  • Cursor hỗ trợ MCP app cả ở agent mode lẫn trong khung side chat thông thường (tài liệu MCP của Cursor).

Nhưng còn nhiều client khác cũng hỗ trợ MCP apps, như VS Code và vô số client khác.

Anh thấy cũng đáng nhắc ở đây một điều khác bạn có thể làm. Có thể bạn đang phát triển MCP server và không biết host mà user của bạn đang dùng có hỗ trợ MCP apps hay không. Điều bạn có thể làm là: vì ta biết client là ai từ metadata được trao đổi qua MCP, bạn có thể chỉ trả về UI element cho những host thực sự chấp nhận và render được các widget đó.

Thật ra chuyện này không lớn lắm, vì phần lớn client không hỗ trợ MCP app sẽ đơn giản là không hiển thị widget. Nhưng qua nhiều lần phát triển server, anh thấy rằng nếu widget không được hiển thị, bạn cần trả về một output khác, vì một phần thông tin vốn được trả về trong widget, và có lẽ bạn muốn đưa nó cho model khi widget không hiện ra. Đây cũng là điều mcp-use giúp bạn, với một số primitive cho phép bạn biết client mà MCP server đang kết nối có thực sự hỗ trợ MCP apps hay không.

MCP server đọc metadata client Host hỗ trợ MCP Apps trả widget, model nhận phần tóm tắt Host không hỗ trợ không có widget, trả đủ thông tin cho model
Biết client là ai qua metadata của MCP, server chỉ trả widget cho host render được, và bù bằng một output khác cho host không hiển thị widget.

14. Build một MCP App với mcp-use

Thêm một chút ý tưởng về cách bạn build các MCP app này với mcp-use. Nhóm anh là một trong những SDK phổ biến nhất để build MCP app.

Cách nhóm thiết kế SDK là: về cơ bản bạn thiết kế MCP server như bạn vẫn luôn làm. Bạn có constructor của MCP server, rồi định nghĩa các tool. Từ tool, bạn có thể đơn giản trả về các widget, và những widget này được tự động đăng ký từ file widget trong thư mục resources. Bất cứ thứ gì bạn đặt làm file widget trong thư mục resources sẽ được đăng ký thành một UI resource mà bạn có thể trả về từ một tool.

File widget chỉ là một React component. Bạn cũng có thể dùng các UI component có sẵn của mình. Nó sẽ được compile thành HTML và CSS, rồi được trả về và gắn với tool.

Slide Build one with mcp-use: cây thư mục my-app với src/index.ts và resources/catalogue/widget.tsx
"Build one with mcp-use": viết một tool, thả một React component vào resources/, mcp-use bundle và đăng ký widget cho bạn, rồi ship nó. Trong cây thư mục, src/index.ts khai báo server.tool({ widget: { name: "catalogue" } }), còn resources/catalogue/widget.tsx là React component được tự đăng ký thành ui://widget/catalogue.html.

Nhóm có một skill, và có một template. Bạn chỉ cần chạy lệnh dưới đây, nó sẽ cho bạn một template để bắt đầu (xem thêm tài liệu mcp-use):

npx create-mcp-use-app

15. Distribution: store là kênh phân phối mới, và quy trình submit

Giờ hãy nói về distribution và discovery, mà anh nghĩ dĩ nhiên là một chủ đề rất quan trọng, có lẽ còn ít được biết tới hơn cả cách MCP apps hoạt động. Anh nói chuyện với rất nhiều người, và họ không biết có store cho MCP, cũng không biết cách submit. Nên anh muốn nói một chút về chuyện này.

Slide Part 3: Distribution and discovery
"Distribution and discovery": một cửa trước dẫn vào sản phẩm của bạn, thứ chưa hề tồn tại một năm trước.

Store là một kênh phân phối mới rất lớn. Ở đây anh lại nhắc tới ba client phổ biến nhất, ChatGPT, Claude và Cursor, và cả ba đều hỗ trợ quy trình submit tự phục vụ (self-serve). ChatGPT là một trong những nơi hỗ trợ điều này đầu tiên. Claude thì từ vài tuần nay đã có form submit self-serve cho tài khoản Team và Enterprise. Và Cursor cũng cho phép bạn submit MCP server.

Cách submit ở ba nơi khác nhau, nhưng về cơ bản điều xảy ra là bạn cần đảm bảo MCP app của mình tuân thủ yêu cầu. Cả MCP app lẫn MCP server đều có thể được submit lên cả ba store, nên server của bạn không cần trả về UI mới đủ điều kiện submit.

Ba quy trình để app được chấp nhận là khác nhau, và có tốc độ khác nhau. Hiện tại có lẽ trên Claude sẽ mất lâu hơn một chút, còn ChatGPT thì đang tăng tốc rất nhiều việc chấp nhận các app.

Một lần nữa, quy trình là: bạn liên kết remote MCP server của mình, họ sẽ quét các tool, đảm bảo mọi tool đều được annotate đúng và có argument đúng. Khi xong bước đó, họ sẽ quét cả phần authentication. Nếu server của bạn cần authentication, bạn phải khai báo, và cần đảm bảo nó chạy được. Còn vài bước khác nữa, là chi tiết riêng của từng provider.

Nhưng điều quan trọng cần nói là khi app của bạn đã được submit, nó sẽ được test một phần thủ công, một phần tự động. Bạn sẽ phải cung cấp một số test case và test prompt, rồi app sẽ được chấp nhận hoặc bị từ chối. Nếu được chấp nhận, bạn có thể publish nó và đưa nó lên các store: tại chatgpt.com/apps, trong Connectors directory trên Claude, hoặc trong directory của Cursor (Cursor marketplace).

Liên kết remote server Quét tool annotation, argument Kiểm tra authentication Test test case, prompt Publish lên store ChatGPT, Claude và Cursor đều cho submit self-serve; chi tiết và tốc độ duyệt khác nhau
Khung chung của quy trình submit mà Pietro mô tả; mỗi provider còn có vài bước riêng.

16. Install một click và dynamic discovery

Nhóm anh đã làm rất nhiều lần submit, và đã cố gắng làm cho quy trình này dễ hơn. Nên nếu bạn muốn submit app của mình, anh nghĩ bạn nên vào manufact.com, nơi nhóm kiểm tra app của bạn để đảm bảo nó đã sẵn sàng để submit. Nhóm cố gắng chạy tất cả các bước kiểm tra mà những client kia sẽ chạy trong quy trình submit. Ngoài ra, nhóm còn tạo giúp bạn một số artifact cần cho việc submit, như screenshot và test case, ngay trên cloud của nhóm nếu đã kết nối với MCP server của bạn.

Một điều rất, rất hay là khi app của bạn đã có mặt trên store, không chỉ người ta có thể tìm thấy nó bằng cách search trên store, không chỉ bạn có thể gửi một URL cho khách hàng và họ install ứng dụng của bạn chỉ bằng một click. Nhờ vậy bạn không phải chia sẻ cái file JSON xấu xí chứa cấu hình MCP nữa.

Mà điều rất quan trọng là dynamic discovery của MCP server đang diễn ra. Hôm nay, Claude là client duy nhất thực sự làm điều này. Với mọi app trên store, khi Claude được giao một task mà nó không có tool cụ thể để làm, nó sẽ thực sự tìm trong MCP registry connector phù hợp cho task đó.

Slide The agent proposes your app: Claude đề xuất connect ShopMCP giữa cuộc chat
"The agent proposes your app": khi một yêu cầu khớp với app của bạn, agent đề xuất kết nối nó ngay giữa cuộc hội thoại, trước cả khi user cài gì. Demo: user cần giày chạy dưới 150 đô, Claude báo "Found an app that can help" và đưa ra ShopMCP với nút Connect. Ghi chú bên trái: Claude làm điều này hôm nay, ChatGPT được dự đoán là tiếp theo.

Hãy hình dung điều này có nghĩa gì với sản phẩm cụ thể của bạn. Dĩ nhiên có rất nhiều user đang hoạt động trên những ứng dụng đó: hơn một tỷ user hoạt động, những người sẽ bày tỏ một ý định (intent) ngay trong chat. Và qua trí thông minh của model, model sẽ chọn đâu là connector tốt nhất. Nếu bạn có mặt ở đó, và bạn làm phần việc của mình để trở thành connector được chọn, đây sẽ là một làn sóng rất lớn những người có intent cao, cần sản phẩm của bạn, và sẽ tìm thấy nó một cách động và tự nhiên (organic) trên các nền tảng đó.

Nên điều này rất quan trọng. Claude làm điều này hôm nay, và ChatGPT được kỳ vọng sẽ làm điều này khá sớm.

17. AI apps là browser mới, MCP là website mới

Đó là phần mô tả của talk. Anh chỉ muốn nói thêm: tới giờ có lẽ đã khá rõ việc có mặt trên store quan trọng thế nào. Anh có thể kể kinh nghiệm của mình: có mặt trên store đã mang về cho nhóm rất nhiều traffic.

Và cá nhân anh, với tư cách một user của MCP, hôm nay anh kiểm tra xem một sản phẩm có MCP server hay không, và với anh đó là điều kiện cơ bản nhất để quyết định mua. Anh chạy phần lớn công việc hằng ngày trên Claude Cowork hoặc Claude Code, vì anh thích khả năng chia sẻ context giữa codebase và các connector khác nhau của mình. Với anh, điều này cực kỳ quan trọng.

Ví dụ, một workflow anh hay chạy: anh có Granola MCP chứa ghi chú các cuộc họp, có Linear nơi anh theo dõi ticket. Dĩ nhiên anh đang ở trong codebase nếu dùng từ Claude Code. Về cơ bản anh có thể kéo ghi chú cuộc họp có feedback của khách hàng rồi đưa vào Linear, có thể tạo ticket cho phần còn lại của team. Sau đó anh có agent kéo ticket Linear qua Linear MCP và bắt tay làm luôn: mở PR, rồi đóng ticket Linear. Trong một thế giới lý tưởng, nó thậm chí còn gửi email lại cho khách hàng qua MCP, nói rằng "Ồ, cái này đã được sửa rồi." Nhưng có lẽ ta chưa tới được đó.

Granola MCP ghi chú họp Linear tạo ticket Agent kéo ticket, code Mở PR đóng ticket Email báo khách Tất cả chạy trong Claude Code; ô nét đứt là bước lý tưởng, chưa tới
Workflow hằng ngày Pietro kể: context đi xuyên qua codebase và các connector, từ ghi chú họp tới PR.

Nhưng điều đó chắc chắn là đúng. Thực tế, chỉ vài ngày trước, Paul Graham, nhà sáng lập Y Combinator, đã nói: "AI apps are the new browsers" (AI app là những trình duyệt mới). Và trong trường hợp này, AI app chính là Claude Code, Codex, Claude Cowork. Anh hoàn toàn đồng ý.

Slide Be there, or get bypassed với tweet của Paul Graham AI apps are the new browsers
"Be there, or get bypassed": mọi sản phẩm được con người dùng qua một UI sẽ cần một phiên bản MCP App, nếu không sẽ bị những người làm mọi thứ qua agent bỏ qua. Bên phải là tweet của Paul Graham "AI apps are the new browsers", trích lại một tweet của Austen Allred kể rằng anh ngày càng chỉ mở một AI app (Claude Code hoặc Codex) và làm gần như mọi thứ khác trên máy từ đó.

Nếu bạn nghĩ về nó, Google Search theo một cách nào đó đã bị thay thế bằng việc hỏi trên ChatGPT. Giờ ta có connector, nơi bạn không chỉ search mà còn làm được việc trong thế giới thật, nên tất cả những thao tác đó cũng sẽ chuyển vào chat.

Vậy theo một cách nào đó, anh nghĩ nếu AI app là trình duyệt mới, thì MCP là website mới. Và như một website, chúng có thể trả về UI, nhờ MCP apps.

Nên một lần nữa: anh không muốn nhìn vào dashboard của bạn nữa. Anh muốn dùng nó trong Claude. Và nếu có dashboard nào, anh muốn thấy nó trong ứng dụng Claude Code. Vậy hãy ship một MCP app.

Slide kết Nobody wants to look at your dashboard. Ship an MCP App.
Slide kết: "Nobody wants to look at your dashboard. Ship an MCP App." Bên dưới là logo ChatGPT, Claude và Cursor, cùng dòng Pietro Zullo, Manufact, docs.mcp-use.com.

Cảm ơn mọi người rất nhiều. Anh hy vọng bạn thích talk này, và hy vọng sẽ có nhiều câu hỏi về chủ đề này; anh rất sẵn lòng giúp. Tới giờ thì, anh cười nói, anh đã khá là chuyên gia về chủ đề này rồi. Cảm ơn rất nhiều, chúc một ngày tốt lành.

Nguồn và liên kết