Thực thi code với MCP: Xây dựng AI agents hiệu quả hơn
"LLMs are better at writing code to call MCP, than at calling MCP directly."
TL;DR
"LLMs are better at writing code to call MCP, than at calling MCP directly."
Vấn đề: Khi AI agents kết nối với nhiều tools qua MCP, việc tải tất cả định nghĩa tools và truyền kết quả trung
gian qua context window khiến tốn rất nhiều tokens, tăng chi phí và độ trễ.
Giải pháp: Thay vì gọi tools trực tiếp, cho agents viết code để tương tác với MCP servers. Agents chỉ tải tools
cần thiết và xử lý dữ liệu trong môi trường thực thi trước khi trả kết quả về model.
Kết quả:
- Giảm từ 150,000 tokens → 2,000 tokens (tiết kiệm 98.7%)
- Giảm độ trễ và chi phí
- Bảo vệ dữ liệu nhạy cảm (PII không qua model)
- Lọc dữ liệu lớn hiệu quả (10,000 rows → 5 rows)
- Lưu trữ state và tạo skills có thể tái sử dụng
Trade-off: Cần môi trường thực thi an toàn với sandboxing, giới hạn tài nguyên và giám sát.
Ví dụ thực tế: Thay vì agent gọi tool để lấy transcript 50,000 tokens rồi gọi tool khác để lưu (transcript đi qua
model 2 lần), agent viết code lấy transcript và lưu trực tiếp—transcript không bao giờ vào context.
Giới thiệu
Model Context Protocol (MCP) là một tiêu chuẩn mở để kết nối AI agents với các hệ thống bên ngoài. Việc kết nối agents với các tools và dữ liệu theo cách truyền thống đòi hỏi phải tạo tích hợp riêng cho từng cặp kết nối, dẫn đến sự phân mảnh và trùng lặp công sức, khiến việc mở rộng quy mô các hệ thống kết nối thực sự trở nên khó khăn. MCP cung cấp một giao thức phổ quát—các nhà phát triển chỉ cần triển khai MCP một lần trong agent của họ và nó sẽ mở khóa toàn bộ hệ sinh thái tích hợp.
Kể từ khi ra mắt MCP vào tháng 11 năm 2024, việc áp dụng đã diễn ra rất nhanh: cộng đồng đã xây dựng hàng nghìn MCP servers, SDKs có sẵn cho tất cả các ngôn ngữ lập trình chính, và ngành công nghiệp đã chấp nhận MCP như tiêu chuẩn thực tế để kết nối agents với tools và dữ liệu.
Ngày nay, các nhà phát triển thường xuyên xây dựng agents có quyền truy cập vào hàng trăm hoặc hàng nghìn tools trên hàng chục MCP servers. Tuy nhiên, khi số lượng tools được kết nối tăng lên, việc tải tất cả định nghĩa tools ngay từ đầu và truyền các kết quả trung gian qua context window làm chậm agents và tăng chi phí.
Trong bài blog này, chúng ta sẽ khám phá cách thực thi code (code execution) có thể cho phép agents tương tác với MCP servers hiệu quả hơn, xử lý nhiều tools hơn trong khi sử dụng ít tokens hơn.
Việc tiêu thụ tokens quá mức từ tools khiến agents kém hiệu quả hơn
Khi việc sử dụng MCP mở rộng quy mô, có hai pattern phổ biến có thể làm tăng chi phí và độ trễ của agent:
- Các định nghĩa tool làm quá tải context window
- Kết quả trung gian của tool tiêu thụ thêm tokens
1. Các định nghĩa tool làm quá tải context window
Hầu hết các MCP clients tải tất cả định nghĩa tools ngay từ đầu trực tiếp vào context, hiển thị chúng cho model bằng cách sử dụng cú pháp gọi tool trực tiếp. Các định nghĩa tool này có thể trông như sau:
gdrive.getDocument
Description: Retrieves a document from Google Drive
Parameters:
documentId (required, string): The ID of the document to retrieve
fields (optional, string): Specific fields to return
Returns: Document object with title, body content, metadata, permissions, etc.
salesforce.updateRecord
Description: Updates a record in Salesforce
Parameters:
objectType (required, string): Type of Salesforce object (Lead, Contact, Account, etc.)
recordId (required, string): The ID of the record to update
data (required, object): Fields to update with their new values
Returns: Updated record object with confirmation
Các mô tả tool chiếm nhiều không gian context window hơn, tăng thời gian phản hồi và chi phí. Trong trường hợp agents được kết nối với hàng nghìn tools, chúng sẽ cần xử lý hàng trăm nghìn tokens trước khi đọc một request.
2. Kết quả trung gian của tool tiêu thụ thêm tokens
Hầu hết các MCP clients cho phép models gọi trực tiếp các MCP tools. Ví dụ, bạn có thể yêu cầu agent của mình: "Tải transcript cuộc họp của tôi từ Google Drive và đính kèm vào lead Salesforce."
Model sẽ thực hiện các lời gọi như:
TOOL CALL: gdrive.getDocument(documentId: "abc123")
→ returns "Discussed Q4 goals...\n[full transcript text]"
(loaded into model context)
TOOL CALL: salesforce.updateRecord(
objectType: "SalesMeeting",
recordId: "00Q5f000001abcXYZ",
data: { "Notes": "Discussed Q4 goals...\n[full transcript text written out]" }
)
(model needs to write entire transcript into context again)
Mọi kết quả trung gian đều phải đi qua model. Trong ví dụ này, toàn bộ transcript cuộc gọi chảy qua hai lần. Đối với cuộc họp bán hàng kéo dài 2 giờ, điều đó có nghĩa là xử lý thêm 50,000 tokens. Các tài liệu lớn hơn thậm chí có thể vượt quá giới hạn context window, phá vỡ workflow.
Với các tài liệu lớn hoặc cấu trúc dữ liệu phức tạp, models có thể dễ mắc lỗi hơn khi sao chép dữ liệu giữa các lời gọi tool.
MCP client tải các định nghĩa tool vào context window của model và điều phối vòng lặp message, trong đó mỗi lời gọi tool và kết quả đều phải đi qua model giữa các operations.
Thực thi code với MCP cải thiện hiệu quả context
Với môi trường thực thi code ngày càng trở nên phổ biến đối với agents, một giải pháp là trình bày MCP servers dưới dạng code APIs thay vì gọi tool trực tiếp. Agent sau đó có thể viết code để tương tác với MCP servers. Cách tiếp cận này giải quyết cả hai thách thức: agents chỉ có thể tải các tools mà chúng cần và xử lý dữ liệu trong môi trường thực thi trước khi truyền kết quả trở lại model.
Có nhiều cách để thực hiện điều này. Một cách tiếp cận là tạo một cây thư mục file (file tree) của tất cả các tools có sẵn từ các MCP servers đã kết nối. Đây là một triển khai sử dụng TypeScript:
servers
├── google-drive
│ ├── getDocument.ts
│ ├── ... (other tools)
│ └── index.ts
├── salesforce
│ ├── updateRecord.ts
│ ├── ... (other tools)
│ └── index.ts
└── ... (other servers)
Sau đó, mỗi tool tương ứng với một file, giống như:
// ./servers/google-drive/getDocument.ts
import { callMCPTool } from "../../../client.js";
interface GetDocumentInput {
documentId: string;
}
interface GetDocumentResponse {
content: string;
}
/* Read a document from Google Drive */
export async function getDocument(input: GetDocumentInput): Promise<GetDocumentResponse> {
return callMCPTool<GetDocumentResponse>('google_drive__get_document', input);
}
Ví dụ Google Drive đến Salesforce ở trên trở thành đoạn code:
// Read transcript from Google Docs and add to Salesforce prospect
import * as gdrive from './servers/google-drive';
import * as salesforce from './servers/salesforce';
const transcript = (await gdrive.getDocument({ documentId: 'abc123' })).content;
await salesforce.updateRecord({
objectType: 'SalesMeeting',
recordId: '00Q5f000001abcXYZ',
data: { Notes: transcript }
});
Agent khám phá tools bằng cách khám phá hệ thống file: liệt kê thư mục ./servers/ để tìm các servers có sẵn (như google-drive và salesforce), sau đó đọc các file tool cụ thể mà nó cần (như getDocument.ts và updateRecord.ts) để hiểu interface của từng tool. Điều này cho phép agent chỉ tải các định nghĩa mà nó cần cho task hiện tại. Cách tiếp cận này giảm việc sử dụng tokens từ 150,000 tokens xuống 2,000 tokens—tiết kiệm thời gian và chi phí 98.7%.
Cloudflare đã công bố các phát hiện tương tự, gọi thực thi code với MCP là "Code Mode". Insight cốt lõi là giống nhau: LLMs rất giỏi trong việc viết code và các nhà phát triển nên tận dụng thế mạnh này để xây dựng agents tương tác với MCP servers hiệu quả hơn.
Lợi ích của việc thực thi code với MCP
Thực thi code với MCP cho phép agents sử dụng context hiệu quả hơn bằng cách tải tools theo yêu cầu, lọc dữ liệu trước khi nó đến model, và thực thi logic phức tạp trong một bước duy nhất. Ngoài ra còn có các lợi ích về bảo mật và quản lý trạng thái khi sử dụng cách tiếp cận này.
Progressive disclosure (tiết lộ dần dần)
Models rất giỏi trong việc điều hướng filesystems. Việc trình bày tools dưới dạng code trên filesystem cho phép models đọc các định nghĩa tool theo yêu cầu, thay vì đọc tất cả chúng ngay từ đầu.
Ngoài ra, một tool search_tools có thể được thêm vào server để tìm các định nghĩa liên quan. Ví dụ, khi làm việc với Salesforce server giả định được sử dụng ở trên, agent tìm kiếm "salesforce" và chỉ tải những tools mà nó cần cho task hiện tại. Việc bao gồm tham số mức độ chi tiết trong tool search_tools cho phép agent chọn mức độ chi tiết cần thiết (chẳng hạn như chỉ tên, tên và mô tả, hoặc định nghĩa đầy đủ với schemas) cũng giúp agent tiết kiệm context và tìm tools hiệu quả.
Kết quả tool hiệu quả về context
Khi làm việc với các tập dữ liệu lớn, agents có thể lọc và chuyển đổi kết quả trong code trước khi trả về chúng. Hãy xem xét việc tải một bảng tính 10,000 hàng:
// Without code execution - all rows flow through context
TOOL CALL: gdrive.getSheet(sheetId: 'abc123')
→ returns 10,000 rows in context to filter manually
// With code execution - filter in the execution environment
const allRows = await gdrive.getSheet({ sheetId: 'abc123' });
const pendingOrders = allRows.filter(row =>
row["Status"] === 'pending'
);
console.log(`Found ${pendingOrders.length} pending orders`);
console.log(pendingOrders.slice(0, 5)); // Only log first 5 for review
Agent chỉ thấy năm hàng thay vì 10,000. Các pattern tương tự hoạt động cho các phép tổng hợp (aggregations), nối (joins) qua nhiều nguồn dữ liệu, hoặc trích xuất các trường cụ thể—tất cả đều không làm phình to context window.
Control flow mạnh mẽ và hiệu quả hơn về context
Vòng lặp, điều kiện và xử lý lỗi có thể được thực hiện bằng các pattern code quen thuộc thay vì chuỗi các lời gọi tool riêng lẻ. Ví dụ, nếu bạn cần thông báo deployment trong Slack, agent có thể viết:
let found = false;
while (!found) {
const messages = await slack.getChannelHistory({ channel: 'C123456' });
found = messages.some(m => m.text.includes('deployment complete'));
if (!found) await new Promise(r => setTimeout(r, 5000));
}
console.log('Deployment notification received');
Cách tiếp cận này hiệu quả hơn so với việc luân phiên giữa các lời gọi MCP tool và lệnh sleep thông qua vòng lặp agent.
Ngoài ra, khả năng viết ra một cây điều kiện được thực thi cũng giúp tiết kiệm độ trễ "time to first token": thay vì phải đợi model đánh giá một câu lệnh if, agent có thể để môi trường thực thi code làm điều này.
Các operations bảo vệ quyền riêng tư
Khi agents sử dụng thực thi code với MCP, các kết quả trung gian theo mặc định vẫn ở trong môi trường thực thi. Theo cách này, agent chỉ thấy những gì bạn ghi log hoặc trả về một cách rõ ràng, có nghĩa là dữ liệu bạn không muốn chia sẻ với model có thể chảy qua workflow của bạn mà không bao giờ vào context của model.
Đối với các workload nhạy cảm hơn, agent harness có thể tự động tokenize dữ liệu nhạy cảm. Ví dụ, hãy tưởng tượng bạn cần nhập thông tin liên hệ khách hàng từ bảng tính vào Salesforce. Agent viết:
const sheet = await gdrive.getSheet({ sheetId: 'abc123' });
for (const row of sheet.rows) {
await salesforce.updateRecord({
objectType: 'Lead',
recordId: row.salesforceId,
data: {
Email: row.email,
Phone: row.phone,
Name: row.name
}
});
}
console.log(`Updated ${sheet.rows.length} leads`);
MCP client chặn dữ liệu và tokenize thông tin nhận dạng cá nhân (PII) trước khi nó đến model:
// What the agent would see, if it logged the sheet.rows:
[
{ salesforceId: '00Q...', email: '[EMAIL_1]', phone: '[PHONE_1]', name: '[NAME_1]' },
{ salesforceId: '00Q...', email: '[EMAIL_2]', phone: '[PHONE_2]', name: '[NAME_2]' },
...
]
Sau đó, khi dữ liệu được chia sẻ trong một lời gọi MCP tool khác, nó được untokenize thông qua tra cứu trong MCP client. Địa chỉ email, số điện thoại và tên thực sự chảy từ Google Sheets đến Salesforce, nhưng không bao giờ qua model. Điều này ngăn agent vô tình ghi log hoặc xử lý dữ liệu nhạy cảm. Bạn cũng có thể sử dụng cách này để xác định các quy tắc bảo mật xác định, chọn nơi dữ liệu có thể chảy đến và từ đâu.
State persistence và skills
Thực thi code với quyền truy cập filesystem cho phép agents duy trì trạng thái qua các operations. Agents có thể ghi các kết quả trung gian vào files, cho phép chúng tiếp tục công việc và theo dõi tiến trình:
const leads = await salesforce.query({
query: 'SELECT Id, Email FROM Lead LIMIT 1000'
});
const csvData = leads.map(l => `${l.Id},${l.Email}`).join('\n');
await fs.writeFile('./workspace/leads.csv', csvData);
// Later execution picks up where it left off
const saved = await fs.readFile('./workspace/leads.csv', 'utf-8');
Agents cũng có thể lưu trữ code của riêng chúng dưới dạng các functions có thể tái sử dụng. Khi agent phát triển code hoạt động cho một task, nó có thể lưu implementation đó để sử dụng trong tương lai:
// In ./skills/save-sheet-as-csv.ts
import * as gdrive from './servers/google-drive';
export async function saveSheetAsCsv(sheetId: string) {
const data = await gdrive.getSheet({ sheetId });
const csv = data.map(row => row.join(',')).join('\n');
await fs.writeFile(`./workspace/sheet-${sheetId}.csv`, csv);
return `./workspace/sheet-${sheetId}.csv`;
}
// Later, in any agent execution:
import { saveSheetAsCsv } from './skills/save-sheet-as-csv';
const csvPath = await saveSheetAsCsv('abc123');
Điều này gắn liền chặt chẽ với khái niệm Skills, các thư mục chứa các hướng dẫn, scripts và tài nguyên có thể tái sử dụng để models cải thiện hiệu suất trên các tasks chuyên biệt. Việc thêm file SKILL.md vào các functions đã lưu này tạo ra một skill có cấu trúc mà models có thể tham khảo và sử dụng. Theo thời gian, điều này cho phép agent của bạn xây dựng một hộp công cụ các khả năng cấp cao hơn, phát triển scaffolding mà nó cần để hoạt động hiệu quả nhất.
Lưu ý rằng thực thi code tạo ra sự phức tạp riêng của nó. Chạy code do agent tạo ra đòi hỏi một môi trường thực thi an toàn với sandboxing phù hợp, giới hạn tài nguyên và giám sát. Các yêu cầu cơ sở hạ tầng này thêm vào chi phí vận hành và các cân nhắc về bảo mật mà các lời gọi tool trực tiếp tránh được. Lợi ích của thực thi code—giảm chi phí token, độ trễ thấp hơn và cải thiện composition của tool—nên được cân nhắc với các chi phí triển khai này.
Tóm tắt
MCP cung cấp một giao thức nền tảng cho agents để kết nối với nhiều tools và hệ thống. Tuy nhiên, khi quá nhiều servers được kết nối, các định nghĩa tool và kết quả có thể tiêu thụ quá nhiều tokens, làm giảm hiệu quả của agent.
Mặc dù nhiều vấn đề ở đây có vẻ mới lạ—quản lý context, composition của tool, state persistence—chúng đã có các giải pháp từ kỹ thuật phần mềm. Thực thi code áp dụng các patterns đã được thiết lập này cho agents, cho phép chúng sử dụng các cấu trúc lập trình quen thuộc để tương tác với MCP servers hiệu quả hơn. Nếu bạn triển khai cách tiếp cận này, chúng tôi khuyến khích bạn chia sẻ phát hiện của mình với cộng đồng MCP.
Lời cảm ơn
Bài viết này được viết bởi Adam Jones và Conor Kelly. Cảm ơn Jeremy Fox, Jerome Swannack, Stuart Ritchie, Molly Vorwerck, Matt Samuels, và Maggie Vo đã góp ý về các bản nháp của bài viết này.
Nguồn: Code execution with MCP: Building more efficient agents - Anthropic Engineering Blog