Hướng dẫn cài đặt mcp tool tương thích với GraphQL plugin
Back to graphqlGraphQL plugin cung cấp một MCP server để AI client có thể khám phá và gọi các công cụ liên quan đến schema, REST swagger, SQL scripts, prompt, resource... MCP server này chạy trên endpoint
/graphql/mcp, dùng JSON-RPC 2.0 và chuyển các method chuẩn của MCP thành event nội bộ.Cơ chế hoạt động
MCP server không yêu cầu mỗi tool phải đăng ký thủ công vào một danh sách tập trung. Thay vào đó, mỗi tool được cài bằng 2 event handler:
- Tool schema handler: mô tả tool cho
tools/list. - Tool call handler: xử lý khi MCP client gọi
tools/call.
Tên event là contract quan trọng nhất:
<tool_name>_mcp_tool_schema_event <tool_name>_mcp_tool_event
Ví dụ tool
get_web_view_schema sẽ có:get_web_view_schema_mcp_tool_schema_event get_web_view_schema_mcp_tool_event
Luồng tổng quát:
sequenceDiagram
participant Client as MCP Client
participant Server as GraphQL MCP Server
participant Event as Event Handler Manager
participant Schema as Tool Schema Handler
participant Tool as Tool Call Handler
Client->>Server: POST /graphql/mcp initialize
Server->>Event: initialize_mcp_method_event
Event-->>Server: capabilities, serverInfo
Server-->>Client: JSON-RPC result
Client->>Server: POST /graphql/mcp tools/list
Server->>Event: tools/list_mcp_method_event
Event->>Schema: scan *_mcp_tool_schema_event
Schema-->>Event: tool metadata
Event-->>Server: danh sách tools
Server-->>Client: JSON-RPC result
Client->>Server: POST /graphql/mcp tools/call
Server->>Event: tools/call_mcp_method_event
Event->>Tool: gọi <tool_name>_mcp_tool_event
Tool-->>Event: content, structuredContent, isError
Event-->>Server: tool result
Server-->>Client: JSON-RPC result
MCP Method và MCP Tool
Trong plugin, cần phân biệt rõ:
- MCP method dùng cho protocol:
initialize,ping,tools/list,tools/call,resources/list,resources/read,prompts/list... - MCP tool dùng cho tính năng ứng dụng: lấy schema, lấy swagger, tạo dữ liệu, gọi API quản trị...
Khi thêm tính năng mới cho AI client, hãy tạo MCP tool, không tạo MCP method mới, trừ khi bạn thật sự mở rộng protocol-level behavior.
Endpoint MCP
MCP server expose:
GET /graphql/mcp POST /graphql/mcp
GET /graphql/mcp trả về thông tin mô tả server, protocol version, endpoint và danh sách method được hỗ trợ.POST /graphql/mcp nhận JSON-RPC request. Ví dụ:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
Server sẽ tự chuyển method thành event name:
tools/list -> tools/list_mcp_method_event tools/call -> tools/call_mcp_method_event initialize -> initialize_mcp_method_event
Với
tools/call, server đọc params.name, tìm tool handler tương ứng, rồi truyền params.arguments vào handler.Cấu trúc một MCP Tool
Một tool hoàn chỉnh gồm 2 class.
Tool Schema Handler
Schema handler trả về metadata để MCP client biết tool tên gì, nhận input nào và trả output dạng nào.
Ví dụ theo pattern của
AdminMcpGetWebViewSchemaToolSchemaEventHandler:@EzySingleton public class AdminMcpGetWebViewSchemaToolSchemaEventHandler extends AbstractEventHandler<Map<String, Object>, Object> { @Override public Object handleEventData(Map<String, Object> data) { return EzyMapBuilder.mapBuilder() .put("name", "get_web_view_schema") .put("title", "Get Web View Schema") .put( "description", "Returns the download URL for the web view schema. " + "Use curl to save to disk; do not read into context." ) .put("inputSchema", newInputSchema()) .put("outputSchema", newOutputSchema()) .toMap(); } private Map<String, Object> newInputSchema() { return EzyMapBuilder.mapBuilder() .put("type", "object") .put("properties", Collections.emptyMap()) .put("additionalProperties", Boolean.FALSE) .toMap(); } private Map<String, Object> newOutputSchema() { return EzyMapBuilder.mapBuilder() .put("type", "object") .put( "properties", EzyMapBuilder.mapBuilder() .put( "schema", EzyMapBuilder.mapBuilder() .put("type", "string") .put( "description", "JSON view schema content of the web plugin." ) .toMap() ) .toMap() ) .put("required", Collections.singletonList("schema")) .put("additionalProperties", Boolean.FALSE) .toMap(); } @Override public String getEventName() { return "get_web_view_schema_mcp_tool_schema_event"; } }
Điểm cần nhớ:
-
namephải đúng với phần<tool_name>trong event name. -
inputSchemanên là JSON Schema object. - Nếu tool không nhận tham số, dùng
propertiesrỗng vàadditionalProperties = false. -
outputSchemamô tảstructuredContent, không thay thế phần text content.
Tool Call Handler
Tool call handler xử lý logic thật khi MCP client gọi tool.
Ví dụ:
@EzySingleton public class AdminMcpGetWebViewSchemaToolEventHandler extends AbstractEventHandler<Map<String, Object>, Object> { private static final String DOWNLOAD_PATH = "/graphql/api/v1/schemas/web/view"; private static final String RESPONSE_TEXT = "Web view schema is available for download at: " + DOWNLOAD_PATH + "nn" + "IMPORTANT: Do NOT read the file content into context; " + "it can be very large and will waste tokens.nn" + "Save to the project's .agents/ folder instead:n" + " curl --create-dirs -o .agents/web-view-schema.json <server_base_url>" + DOWNLOAD_PATH; @Override public Object handleEventData(Map<String, Object> arguments) { return EzyMapBuilder.mapBuilder() .put( "content", Collections.singletonList( EzyMapBuilder.mapBuilder() .put("type", "text") .put("text", RESPONSE_TEXT) .toMap() ) ) .put("isError", Boolean.FALSE) .toMap(); } @Override public String getEventName() { return "get_web_view_schema_mcp_tool_event"; } }
Tool result nên có dạng:
{
"content": [
{
"type": "text",
"text": "Result text"
}
],
"structuredContent": {
"schema": "{}"
},
"isError": false
}
structuredContent nên được thêm khi output có dữ liệu có cấu trúc. Nếu tool chỉ hướng dẫn tải file hoặc trả thông báo text, có thể chỉ cần content và isError.Ví dụ Tool Có Tham Số
Với tool nhận tham số, khai báo
required trong inputSchema.Ví dụ tool lấy schema của một view theo URI:
private Map<String, Object> newInputSchema() { return EzyMapBuilder.mapBuilder() .put("type", "object") .put( "properties", EzyMapBuilder.mapBuilder() .put( "viewUri", EzyMapBuilder.mapBuilder() .put("type", "string") .put("description", "The URI of the web view.") .toMap() ) .toMap() ) .put("required", Collections.singletonList("viewUri")) .put("additionalProperties", Boolean.FALSE) .toMap(); }
Trong call handler:
@Override public Object handleEventData(Map<String, Object> arguments) { arguments = arguments != null ? arguments : Collections.emptyMap(); String viewUri = (String) arguments.get("viewUri"); if (viewUri == null || viewUri.isEmpty()) { return newToolCallResult("viewUri is required", true); } String schemaJson = loadSchemaByViewUri(viewUri); return newToolCallResult(schemaJson, false); }
Kiểm Tra Tool
Sau khi thêm 2 handler và restart plugin, gọi
tools/list:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
Kết quả phải có tool mới trong
result.tools.Gọi tool:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_web_view_schema",
"arguments": {}
}
}
Với tool có tham số:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_web_view_schema_by_uri",
"arguments": {
"viewUri": "/articles"
}
}
}
Lưu Ý Bảo Mật
Endpoint MCP được bảo vệ bằng cơ chế authenticated admin. Khi request đi qua MCP server, admin access token được đưa vào
params, và nếu có arguments, token cũng được đưa vào arguments.Điều này giúp tool handler có thể gọi tiếp các API nội bộ cần quyền admin hoặc quyền quản trị web mà không bắt MCP client tự truyền token trong input schema.
Không nên khai báo access token là field public trong
inputSchema.Checklist Khi Thêm Tool Mới
- Tạo schema handler với
@EzySingleton. - Handler extends
AbstractEventHandler<Map<String, Object>, Object>. -
getEventName()của schema handler trả về<tool_name>_mcp_tool_schema_event. - Metadata có đủ
name,title,description,inputSchema. - Tạo call handler với
@EzySingleton. -
getEventName()của call handler trả về<tool_name>_mcp_tool_event. - Call handler validate
arguments. - Tool trả về
contentvàisError. - Nếu có dữ liệu máy đọc được, thêm
structuredContent. - Restart plugin và kiểm tra bằng
tools/list, sau đótools/call.
Kết Luận
Để cài một MCP tool tương thích với GraphQL plugin, chỉ cần tuân thủ contract event name và response shape của MCP tool. Plugin sẽ tự động scan các schema handler cho
tools/list và các call handler cho tools/call, nên việc mở rộng tool mới khá gọn: một handler để mô tả, một handler để thực thi.