Dùng pattern hiện tại của GraphQL plugin như sau: MCP tool chỉ trả metadata/download URL, còn dữ liệu lớn thì stream qua REST endpoint riêng. Đừng nhét nội dung file lớn vào JSON-RPC response vì client/LLM có thể đọc vào context và tốn token.
Trong repo đã có case mẫu:
Luồng Chuẩn
sequenceDiagram
    participant Agent as MCP Client / Agent
    participant MCP as GraphQL MCP Server
    participant Tool as get_sql_scripts Tool
    participant API as REST Download API
    participant FS as SQL Files

    Agent->>MCP: tools/list
    MCP-->>Agent: get_sql_scripts schema

    Agent->>MCP: tools/call get_sql_scripts
    MCP->>Tool: get_sql_scripts_mcp_tool_event
    Tool-->>MCP: download path + curl command
    MCP-->>Agent: /graphql/api/v1/sql-scripts

    Agent->>API: curl -o .agents/sql-scripts.sql /graphql/api/v1/sql-scripts
    API->>FS: open SQL InputStreams
    FS-->>API: stream chunks
    API-->>Agent: sql-scripts.sql
Các Bước Cài Đặt MCP Tool Stream Dữ Liệu Lớn
  1. Tạo MCP tool schema handler
Tên event phải theo format:
<tool_name>_mcp_tool_schema_event
Ví dụ:
@EzySingleton
public class AdminMcpGetSqlScriptsToolSchemaEventHandler
    extends AbstractEventHandler<Map<String, Object>, Object> {

    @Override
    public Object handleEventData(Map<String, Object> data) {
        return EzyMapBuilder.mapBuilder()
            .put("name", "get_sql_scripts")
            .put("title", "Get SQL Scripts")
            .put(
                "description",
                "Returns the download path for all SQL scripts. " +
                    "Use curl/wget to save the file to disk — do NOT read into context."
            )
            .put("inputSchema", newInputSchema())
            .put("outputSchema", newOutputSchema())
            .toMap();
    }

    @Override
    public String getEventName() {
        return "get_sql_scripts_mcp_tool_schema_event";
    }
}
  1. Tạo MCP tool handler
Tên event phải theo format:
<tool_name>_mcp_tool_event
Tool handler không trả file content, chỉ trả hướng dẫn download:
@EzySingleton
public class AdminMcpGetSqlScriptsToolEventHandler
    extends AbstractEventHandler<Map<String, Object>, Object> {

    private static final String DOWNLOAD_PATH =
        "/graphql/api/v1/sql-scripts";

    @Override
    public Object handleEventData(Map<String, Object> arguments) {
        return EzyMapBuilder.mapBuilder()
            .put(
                "content",
                Collections.singletonList(
                    EzyMapBuilder.mapBuilder()
                        .put("type", "text")
                        .put(
                            "text",
                            "SQL scripts are available at: " + DOWNLOAD_PATH +
                                "nnUse curl to save to disk:n" +
                                "curl --create-dirs -o .agents/sql-scripts.sql " +
                                "<server_base_url>" + DOWNLOAD_PATH
                        )
                        .toMap()
                )
            )
            .put("isError", Boolean.FALSE)
            .toMap();
    }

    @Override
    public String getEventName() {
        return "get_sql_scripts_mcp_tool_event";
    }
}
  1. Tạo REST endpoint để stream file
@DoGet("/sql-scripts")
public void sqlScriptsGet(HttpServletResponse response) throws Exception {
    response.setContentType("text/plain");
    response.setHeader(
        "Content-Disposition",
        "attachment; filename="sql-scripts.sql""
    );

    try (InputStream inputStream =
             graphQLSqlScriptFilesInputStreamFactory.get()
    ) {
        OutputStream outputStream = response.getOutputStream();
        byte[] buffer = new byte[8192];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        outputStream.flush();
    }
}
  1. Gom nhiều file bằng SequenceInputStream
Case SQL hiện tại đang làm đúng hướng: gom SQL admin + SQL của activated modules, ngăn cách bằng nn, rồi trả về một InputStream.
return new SequenceInputStream(
    Collections.enumeration(streams)
);
Cách Agent Sử Dụng
Sau khi MCP server chạy, agent gọi tool:
get_sql_scripts
Tool trả về path:
/graphql/api/v1/sql-scripts
Agent tải file:
curl --create-dirs -o .agents/sql-scripts.sql \
  http://localhost:<port>/graphql/api/v1/sql-scripts
Quy Ước Nên Theo
  • Dữ liệu nhỏ: có thể trả trực tiếp trong MCP response.
  • Dữ liệu lớn: MCP tool chỉ trả downloadUrl, downloadPath, hoặc command mẫu.
  • File lớn nên lưu vào .agents/ để agent đọc bằng tool file/terminal khi cần.
  • Không để MCP tool trả raw SQL/schema/swagger lớn vào content.
  • Tool là application feature; MCP method chỉ dành cho protocol như initialize, ping, tools/list, tools/call.
Một điểm nhỏ nên chỉnh: AdminMcpGetSqlScriptsToolSchemaEventHandler hiện mô tả outputSchema.content là “SQL scripts content”, nhưng thực tế tool chỉ trả text hướng dẫn download. Nên đổi description thành “download instruction content” cho khớp hành vi hiện tại.