This is an automated email from the ASF dual-hosted git repository.

yaozhq pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/geaflow.git


The following commit(s) were added to refs/heads/master by this push:
     new 017893ce [MCP] Implement MCP local tools for demo case (#610)
017893ce is described below

commit 017893ce838caac25c368fb905d9137bbd80bb90
Author: Leomrlin <[email protected]>
AuthorDate: Wed Sep 10 19:27:50 2025 +0800

    [MCP] Implement MCP local tools for demo case (#610)
    
    * add local graph mcp server
    
    * add type query tool
    
    * update mcp version
    
    * fix license
    
    * fix pom
    
    * fix pom
    
    * refine code
    
    * minor fix
    
    * fix comments
    
    * fix codestyle
    
    * fix tools prompt
    
    * minor fix
    
    * refine code
    
    * fix comment
---
 geaflow-mcp/pom.xml                                |  71 ++++++-
 .../geaflow/mcp/server/GeaFlowMcpActions.java      |  33 +++
 .../mcp/server/GeaFlowMcpActionsLocalImpl.java     | 221 +++++++++++++++++++++
 .../geaflow/mcp/server/GeaFlowMcpServer.java       |   2 +
 .../geaflow/mcp/server/GeaFlowMcpServerTools.java  |  98 ++++++++-
 .../org/apache/geaflow/mcp/server/ToolDesc.java    |  64 ++++++
 .../geaflow/mcp/server/util/McpConstants.java      |  34 ++++
 .../geaflow/mcp/server/util/McpLocalFileUtil.java  |  66 ++++++
 .../geaflow/mcp/server/util/QueryFormatUtil.java   |  98 +++++++++
 .../geaflow/mcp/server/util/QueryLocalRunner.java  | 142 +++++++++++++
 .../geaflow/mcp/server/GeaFlowMcpClientTest.java   | 127 +++++++++++-
 .../geaflow/mcp/server/util/YamlParserTest.java    |   9 +-
 geaflow-mcp/src/test/resources/gql/insert1         |  22 ++
 geaflow-mcp/src/test/resources/gql/insert1_error   |  22 ++
 geaflow-mcp/src/test/resources/gql/licenseHead     |  19 ++
 geaflow-mcp/src/test/resources/gql/modern          |  43 ++++
 geaflow-mcp/src/test/resources/gql/modern_error    |  43 ++++
 geaflow-mcp/src/test/resources/gql/query1          |  28 +++
 .../apache/geaflow/dsl/runtime/QueryContext.java   |   4 +
 .../dsl/runtime/command/CreateGraphCommand.java    |   1 +
 20 files changed, 1133 insertions(+), 14 deletions(-)

diff --git a/geaflow-mcp/pom.xml b/geaflow-mcp/pom.xml
index 45bc101d..95020ac3 100644
--- a/geaflow-mcp/pom.xml
+++ b/geaflow-mcp/pom.xml
@@ -31,7 +31,7 @@
     <artifactId>geaflow-mcp</artifactId>
 
     <properties>
-        <solon-mcp.version>3.3.3-M1</solon-mcp.version>
+        <solon-mcp.version>3.5.1</solon-mcp.version>
         <junit.version>5.10.1</junit.version>
         <slf4j.version>1.7.15</slf4j.version>
         <log4j.version>1.2.17</log4j.version>
@@ -120,6 +120,75 @@
             <version>${junit.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-cluster</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-cluster</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-on-local</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-assembly</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-pipeline</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-dsl-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-dsl-plan</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-dsl-connector-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-dsl-connector-console</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-dsl-connector-random</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geaflow</groupId>
+            <artifactId>geaflow-dsl-connector-file</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
     </dependencies>
 
     <repositories>
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpActions.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpActions.java
new file mode 100644
index 00000000..4c66fedb
--- /dev/null
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpActions.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server;
+
+public interface GeaFlowMcpActions {
+
+    String createGraph(String graphName, String ddl);
+
+    String queryGraph(String graphName, String gql);
+
+    String queryType(String graphName, String type);
+
+    String getGraphSchema(String graphName);
+
+    void withUser(String user);
+}
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpActionsLocalImpl.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpActionsLocalImpl.java
new file mode 100644
index 00000000..eefb2e3c
--- /dev/null
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpActionsLocalImpl.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.geaflow.cluster.local.client.LocalEnvironment;
+import org.apache.geaflow.common.exception.GeaflowRuntimeException;
+import org.apache.geaflow.dsl.common.types.TableField;
+import org.apache.geaflow.dsl.schema.GeaFlowGraph;
+import org.apache.geaflow.dsl.schema.GeaFlowTable;
+import org.apache.geaflow.env.IEnvironment;
+import org.apache.geaflow.mcp.server.util.McpLocalFileUtil;
+import org.apache.geaflow.mcp.server.util.QueryFormatUtil;
+import org.apache.geaflow.mcp.server.util.QueryLocalRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GeaFlowMcpActionsLocalImpl implements GeaFlowMcpActions {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(GeaFlowMcpActionsLocalImpl.class);
+
+    private Map<String, Object> configs;
+    private String user;
+    private IEnvironment localEnv = new LocalEnvironment();
+
+    public GeaFlowMcpActionsLocalImpl(Map<String, Object> configs) {
+        this.configs = configs;
+    }
+
+    @Override
+    public String createGraph(String graphName, String ddl) {
+        QueryLocalRunner runner = new QueryLocalRunner();
+        runner.withGraphName(graphName).withGraphDefine(ddl);
+        GeaFlowGraph graph;
+        try {
+            graph = runner.compileGraph();
+        } catch (Throwable e) {
+            LOGGER.error("Compile error: " + e.getCause().getMessage());
+            throw new GeaflowRuntimeException("Compile error: " + 
e.getCause().getMessage());
+        }
+        if (graph == null) {
+            LOGGER.error("Cannot create graph: " + graphName);
+            throw new GeaflowRuntimeException("Cannot create graph: " + 
graphName);
+        }
+        //Store graph ddl to schema
+        try {
+            McpLocalFileUtil.createAndWriteFile(
+                    QueryLocalRunner.DSL_STATE_REMOTE_SCHEM_PATH, ddl, 
graphName);
+        } catch (Throwable e) {
+            return runner.getErrorMsg();
+        }
+        return "Create graph " + graphName + " success.";
+    }
+
+    @Override
+    public String queryGraph(String graphName, String dml) {
+        QueryLocalRunner compileRunner = new QueryLocalRunner();
+        String ddl = null;
+        try {
+            ddl = 
McpLocalFileUtil.readFile(QueryLocalRunner.DSL_STATE_REMOTE_SCHEM_PATH, 
graphName);
+        } catch (Throwable e) {
+            LOGGER.error("Cannot get graph schema for: " + graphName);
+            throw new GeaflowRuntimeException("Cannot get graph schema for: " 
+ graphName);
+        }
+        compileRunner.withGraphName(graphName).withGraphDefine(ddl);
+        GeaFlowGraph graph;
+        try {
+            graph = compileRunner.compileGraph();
+        } catch (Throwable e) {
+            LOGGER.error("Compile error: " + compileRunner.getErrorMsg());
+            throw new GeaflowRuntimeException("Compile error: " + 
compileRunner.getErrorMsg());
+        }
+        if (graph == null) {
+            LOGGER.error("Cannot create graph: " + graphName);
+            throw new GeaflowRuntimeException("Cannot create graph: " + 
graphName);
+        }
+
+        QueryLocalRunner runner = new QueryLocalRunner();
+        runner.withGraphDefine(ddl);
+        runner.withQuery(ddl + "\n" + QueryFormatUtil.makeUseGraph(graphName) 
+ dml);
+        try {
+            runner.execute();
+        } catch (Throwable e) {
+            LOGGER.error("Run query error: " + e.getCause().getMessage());
+            throw new GeaflowRuntimeException("Run query error: " + 
e.getCause().getMessage());
+        }
+        return "run query success: " + dml;
+    }
+
+    @Override
+    public String queryType(String graphName, String type) {
+        QueryLocalRunner compileRunner = new QueryLocalRunner();
+        String ddl = null;
+        try {
+            ddl = 
McpLocalFileUtil.readFile(QueryLocalRunner.DSL_STATE_REMOTE_SCHEM_PATH, 
graphName);
+        } catch (Throwable e) {
+            LOGGER.error("Cannot get graph schema for: " + graphName);
+            return "Cannot get graph schema for: " + graphName;
+        }
+        compileRunner.withGraphName(graphName).withGraphDefine(ddl);
+        GeaFlowGraph graph;
+        try {
+            graph = compileRunner.compileGraph();
+        } catch (Throwable e) {
+            return compileRunner.getErrorMsg();
+        }
+        if (graph == null) {
+            LOGGER.error("Cannot create graph: " + graphName);
+            throw new GeaflowRuntimeException("Cannot create graph: " + 
graphName);
+        }
+
+        QueryLocalRunner runner = new QueryLocalRunner();
+        runner.withGraphDefine(ddl);
+        String dql = null;
+        String dirName = "query_result_" + Instant.now().toEpochMilli();
+        String resultPath = QueryLocalRunner.DSL_STATE_REMOTE_PATH + "/" + 
dirName;
+        GeaFlowTable resultTable = null;
+        for (GeaFlowGraph.VertexTable vertexTable : graph.getVertexTables()) {
+            if (vertexTable.getTypeName().equals(type)) {
+                dql = QueryFormatUtil.makeResultTable(vertexTable, resultPath)
+                        + "\n" + 
QueryFormatUtil.makeEntityTableQuery(vertexTable);
+                resultTable = vertexTable;
+            }
+        }
+        for (GeaFlowGraph.EdgeTable edgeTable : graph.getEdgeTables()) {
+            if (edgeTable.getTypeName().equals(type)) {
+                dql = QueryFormatUtil.makeResultTable(edgeTable, resultPath)
+                        + "\n" + 
QueryFormatUtil.makeEntityTableQuery(edgeTable);
+                resultTable = edgeTable;
+            }
+        }
+        if (resultTable == null) {
+            LOGGER.error("Cannot find type: " + type + " in graph: " + 
graphName);
+            throw new GeaflowRuntimeException("Cannot find type: " + type + " 
in graph: " + graphName);
+        }
+        runner.withQuery(ddl + "\n" + QueryFormatUtil.makeUseGraph(graphName) 
+ dql);
+        String resultContent = "null";
+        try {
+            runner.execute();
+            resultContent = readFile(resultPath);
+        } catch (Throwable e) {
+            return runner.getErrorMsg();
+        }
+        String schemaContent = "type: " + type + "\nschema: " + 
resultTable.getFields().stream()
+                .map(TableField::getName).collect(Collectors.joining("|"));
+        return schemaContent + "\n" + resultContent;
+    }
+
+    @Override
+    public String getGraphSchema(String graphName) {
+        String ddl = null;
+        try {
+            ddl = 
McpLocalFileUtil.readFile(QueryLocalRunner.DSL_STATE_REMOTE_SCHEM_PATH, 
graphName);
+        } catch (Throwable e) {
+            LOGGER.error("Cannot get graph schema for: " + graphName);
+            return "Cannot get graph schema for: " + graphName;
+        }
+        return ddl;
+    }
+
+    @Override
+    public void withUser(String user) {
+        this.user = user;
+    }
+
+    private String readFile(String path) throws IOException {
+        File file = new File(path);
+        if (file.isHidden()) {
+            return "";
+        }
+        if (file.isFile()) {
+            return IOUtils.toString(new File(path).toURI(), 
Charset.defaultCharset()).trim();
+        }
+        File[] files = file.listFiles();
+        StringBuilder content = new StringBuilder();
+        List<String> readTextList = new ArrayList<>();
+        if (files != null) {
+            for (File subFile : files) {
+                String readText = readFile(subFile.getAbsolutePath());
+                if (StringUtils.isBlank(readText)) {
+                    continue;
+                }
+                readTextList.add(readText);
+            }
+        }
+        readTextList = 
readTextList.stream().sorted().collect(Collectors.toList());
+        for (String readText : readTextList) {
+            if (content.length() > 0) {
+                content.append("\n");
+            }
+            content.append(readText);
+        }
+        return content.toString().trim();
+    }
+}
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServer.java 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServer.java
index 8377ec3b..d580c9f0 100644
--- 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServer.java
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServer.java
@@ -31,6 +31,7 @@ import org.noear.solon.annotation.Controller;
 @Controller
 public class GeaFlowMcpServer {
     private static final String SERVER_NAME = "geaflow-mcp-server";
+    private static final String SSE_CHANNEL = "sse";
     private static final String SSE_ENDPOINT = "/geaflow/sse";
 
     public static void main(String[] args) {
@@ -38,6 +39,7 @@ public class GeaFlowMcpServer {
             // Manually build the mcp service endpoint.
             McpServerEndpointProvider endpointProvider = 
McpServerEndpointProvider.builder()
                 .name(SERVER_NAME)
+                .channel(SSE_CHANNEL)
                 .sseEndpoint(SSE_ENDPOINT)
                 .build();
             endpointProvider.addTool(new MethodToolProvider(new 
GeaFlowMcpServerTools()));
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServerTools.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServerTools.java
index 155ea9b4..e19625d9 100644
--- 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServerTools.java
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/GeaFlowMcpServerTools.java
@@ -26,6 +26,7 @@ import 
org.apache.geaflow.analytics.service.client.AnalyticsClient;
 import org.apache.geaflow.analytics.service.client.AnalyticsClientBuilder;
 import org.apache.geaflow.analytics.service.query.QueryResults;
 import org.apache.geaflow.common.config.Configuration;
+import org.apache.geaflow.mcp.server.util.McpConstants;
 import org.apache.geaflow.mcp.util.YamlParser;
 import org.noear.solon.ai.annotation.ResourceMapping;
 import org.noear.solon.ai.annotation.ToolMapping;
@@ -34,7 +35,7 @@ import org.noear.solon.annotation.Param;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@McpServerEndpoint(name = "geaflow-mcp-server", sseEndpoint = "/geaflow/sse")
+@McpServerEndpoint(name = "geaflow-mcp-server", channel = "sse", sseEndpoint = 
"/geaflow/sse")
 public class GeaFlowMcpServerTools {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(GeaFlowMcpServerTools.class);
 
@@ -65,7 +66,6 @@ public class GeaFlowMcpServerTools {
      * @param query GQL query.
      * @return query result or error code.
      */
-    @ToolMapping(description = "execute query")
     public String executeQuery(@Param(name = "query", description = "query") 
String query) {
         AnalyticsClient analyticsClient = null;
 
@@ -114,4 +114,98 @@ public class GeaFlowMcpServerTools {
             }
         }
     }
+
+
+    /**
+     * A tool that provides create graph capabilities.
+     *
+     * @param graphName graph name to create.
+     * @param ddl Create graph ddl.
+     * @return execute result or error message.
+     */
+    @ToolMapping(description = ToolDesc.createGraph)
+    public String createGraph(@Param(name = McpConstants.GRAPH_NAME, 
description = "create graph name") String graphName,
+                              @Param(name = McpConstants.DDL, description = 
"create graph ddl") String ddl) {
+        try {
+            Map<String, Object> config = YamlParser.loadConfig();
+            GeaFlowMcpActions mcpActions = new 
GeaFlowMcpActionsLocalImpl(config);
+            if (config.containsKey(SERVER_USER)) {
+                mcpActions.withUser(config.get(SERVER_USER).toString());
+            }
+            return mcpActions.createGraph(graphName, ddl);
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage(), e);
+            return e.getMessage();
+        }
+    }
+
+    /**
+     * A tool that get graph schema.
+     *
+     * @param graphName graphName to get.
+     * @return execute result or error message.
+     */
+    @ToolMapping(description = ToolDesc.getGraphSchema)
+    public String getGraphSchema(@Param(name = McpConstants.GRAPH_NAME, 
description = "get graph schema name") String graphName) {
+        try {
+            Map<String, Object> config = YamlParser.loadConfig();
+            GeaFlowMcpActions mcpActions = new 
GeaFlowMcpActionsLocalImpl(config);
+            if (config.containsKey(SERVER_USER)) {
+                mcpActions.withUser(config.get(SERVER_USER).toString());
+            }
+            return mcpActions.getGraphSchema(graphName);
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage(), e);
+            return e.getMessage();
+        }
+    }
+
+
+    /**
+     * A tool that provides insert data into graph capabilities.
+     *
+     * @param graphName graph name to operate.
+     * @param dml dml to run with graph.
+     * @return execute result or error message.
+     */
+    @ToolMapping(description = ToolDesc.insertGraph)
+    public String insertGraph(@Param(name = McpConstants.GRAPH_NAME, 
description = "graph name") String graphName,
+                              @Param(name = McpConstants.DML, description = 
"dml insert values into graph") String dml) {
+        try {
+            Map<String, Object> config = YamlParser.loadConfig();
+            GeaFlowMcpActions mcpActions = new 
GeaFlowMcpActionsLocalImpl(config);
+            if (config.containsKey(SERVER_USER)) {
+                mcpActions.withUser(config.get(SERVER_USER).toString());
+            }
+            return mcpActions.queryGraph(graphName, dml);
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage(), e);
+            return e.getMessage();
+        }
+    }
+
+
+    /**
+     * A tool that provides graph query capabilities.
+     *
+     * @param graphName graph name to query.
+     * @param type query graph entity type.
+     * @return execute result or error message.
+     */
+    @ToolMapping(description = ToolDesc.queryType)
+    public String queryType(@Param(name = McpConstants.GRAPH_NAME, description 
= "query graph name") String graphName,
+                            @Param(name = McpConstants.TYPE, description = 
"query graph vertex or edge type name") String type) {
+        try {
+            Map<String, Object> config = YamlParser.loadConfig();
+            GeaFlowMcpActions mcpActions = new 
GeaFlowMcpActionsLocalImpl(config);
+            if (config.containsKey(SERVER_USER)) {
+                mcpActions.withUser(config.get(SERVER_USER).toString());
+            }
+            return mcpActions.queryType(graphName, type);
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage(), e);
+            return e.getMessage();
+        }
+    }
+
 }
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/ToolDesc.java 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/ToolDesc.java
new file mode 100644
index 00000000..41bcd896
--- /dev/null
+++ b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/ToolDesc.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server;
+
+public class ToolDesc {
+
+    public static final String createGraph = "create graph with ddl, Set the 
storeType to rocksdb, "
+            + "ensuring the syntax is correct and do not use any syntax not 
present in the examples. "
+            + "DDL statements must end with a semicolon. "
+            + "example: CREATE GRAPH `modern` (\n"
+            + "\tVertex `person` (\n"
+            + "\t  `id` bigint ID,\n"
+            + "\t  `name` varchar,\n"
+            + "\t  `age` int\n"
+            + "\t),\n"
+            + "\tVertex `software` (\n"
+            + "\t  `id` bigint ID,\n"
+            + "\t  `name` varchar,\n"
+            + "\t  `lang` varchar\n"
+            + "\t),\n"
+            + "\tEdge `knows` (\n"
+            + "\t  `srcId` bigint SOURCE ID,\n"
+            + "\t  `targetId` bigint DESTINATION ID,\n"
+            + "\t  `weight` double\n"
+            + "\t),\n"
+            + "\tEdge `created` (\n"
+            + "\t  `srcId` bigint SOURCE ID,\n"
+            + "  \t`targetId` bigint DESTINATION ID,\n"
+            + "  \t`weight` double\n"
+            + "\t)\n"
+            + ") WITH (\n"
+            + "\tstoreType='rocksdb'\n"
+            + ");";
+
+    public static final String insertGraph = "Insert into graph with dml. "
+            + "A single call can only insert data into one vertex or edge 
type, and can only use the VALUES syntax. "
+            + "Do not use any syntax not present in the examples. "
+            + "example: INSERT INTO `modern`.`person`(`id`, `name`, `age`)\n"
+            + "VALUES (1, 'jim', 20), (2, 'kate', 22)\n"
+            + ";";
+
+    public static final String queryType = "You need to provide the graph name 
and the type name of the vertex or edge "
+            + "type to be queried. The query tool will return all data of this 
type in the graph. A single call can only "
+            + "query data from one vertex or edge type, and only one type name 
needs to be provided.";
+
+    public static final String getGraphSchema = "query graph schema.";
+}
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/McpConstants.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/McpConstants.java
new file mode 100644
index 00000000..1b00fd6c
--- /dev/null
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/McpConstants.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server.util;
+
+public class McpConstants {
+
+    public static final String CREATE_GRAPH = "create_graph";
+    public static final String GRAPH_NAME = "graph_name";
+    public static final String DDL = "create_graph_ddl";
+    public static final String DQL = "query_graph_dql";
+    public static final String DML = "insert_graph_dml";
+    public static final String TYPE = "query_type_name";
+    public static final String CREATE_GRAPH_TOOL_NAME = "createGraph";
+    public static final String INSERT_GRAPH_TOOL_NAME = "insertGraph";
+    public static final String QUERY_GRAPH_TOOL_NAME = "queryGraph";
+    public static final String QUERY_TYPE_TOOL_NAME = "queryType";
+}
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/McpLocalFileUtil.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/McpLocalFileUtil.java
new file mode 100644
index 00000000..2852ea8b
--- /dev/null
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/McpLocalFileUtil.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server.util;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+public class McpLocalFileUtil {
+
+
+    public static String createAndWriteFile(String root, String text, 
String... fileNames) throws IOException {
+        Files.createDirectories(Paths.get(root));
+        String fileName = "execute_query_" + Instant.now().toEpochMilli();
+        if (fileNames != null && fileNames.length > 0) {
+            fileName = fileNames[0];
+        }
+
+        String fullPath = Paths.get(root, fileName).toString();
+
+        try (FileWriter writer = new FileWriter(fullPath)) {
+            if (text != null) {
+                writer.write(text);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return fileName;
+    }
+
+    public static String readFile(String root, String fileName) throws 
IOException {
+        Path filePath = Paths.get(root, fileName);
+
+        if (!Files.exists(filePath)) {
+            throw new IOException("File not exist: " + filePath);
+        }
+
+        if (!Files.isRegularFile(filePath)) {
+            throw new IOException("Path is not file: " + filePath);
+        }
+
+        return new String(Files.readAllBytes(filePath), 
StandardCharsets.UTF_8);
+    }
+}
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/QueryFormatUtil.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/QueryFormatUtil.java
new file mode 100644
index 00000000..08ce8217
--- /dev/null
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/QueryFormatUtil.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server.util;
+
+import java.util.Locale;
+import org.apache.geaflow.dsl.common.types.TableField;
+import org.apache.geaflow.dsl.schema.GeaFlowGraph;
+import org.apache.geaflow.dsl.schema.GeaFlowTable;
+
+public class QueryFormatUtil {
+
+    public static String makeResultTable(GeaFlowTable table, String 
resultPath) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("CREATE TABLE output_table(\n");
+        int index = 0;
+        for (TableField field : table.getFields()) {
+            if (index > 0) {
+                builder.append(",\n");
+            }
+            builder.append("`").append(field.getName()).append("` ")
+                    .append(tableTypeMapper(field.getType().getName()));
+            index++;
+        }
+        builder.append("\n) WITH ( \n");
+        builder.append("type='file',\n");
+        
builder.append("geaflow.dsl.file.path='").append(resultPath).append("'\n");
+        builder.append(");\n");
+        return builder.toString();
+    }
+
+    public static String makeEntityTableQuery(GeaFlowTable table) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("INSERT INTO output_table\n");
+        if (table instanceof GeaFlowGraph.VertexTable) {
+            builder.append("Match(a:`")
+                    .append(((GeaFlowGraph.VertexTable)table).getTypeName())
+                    .append("`)\n");
+        } else {
+            builder.append("Match()-[a:`")
+                    .append(((GeaFlowGraph.EdgeTable)table).getTypeName())
+                    .append("`]-()\n");
+        }
+        builder.append("Return ");
+        int index = 0;
+        for (TableField field : table.getFields()) {
+            if (index > 0) {
+                builder.append(",\n");
+            }
+            builder.append("a.`").append(field.getName()).append("` ");
+            index++;
+        }
+        builder.append("\n;\n");
+        return builder.toString();
+    }
+
+    public static String makeUseGraph(String graphName) {
+        return "USE GRAPH " + graphName + ";\n";
+    }
+
+    private static String tableTypeMapper(String iType) {
+        String upper = iType.toUpperCase(Locale.ROOT);
+        switch (upper) {
+            case "STRING":
+            case "BINARY_STRING":
+            case "VARCHAR":
+                return "VARCHAR";
+            case "LONG":
+            case "INTEGER":
+            case "SHORT":
+                return "BIGINT";
+            case "FLOAT":
+            case "DOUBLE":
+                return "DOUBLE";
+            case "BOOL":
+            case "BOOLEAN":
+                return "BOOL";
+            default:
+                throw new RuntimeException("Cannt convert type name: " + 
iType);
+        }
+    }
+}
diff --git 
a/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/QueryLocalRunner.java
 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/QueryLocalRunner.java
new file mode 100644
index 00000000..3f662a48
--- /dev/null
+++ 
b/geaflow-mcp/src/main/java/org/apache/geaflow/mcp/server/util/QueryLocalRunner.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.geaflow.mcp.server.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.geaflow.cluster.system.ClusterMetaStore;
+import org.apache.geaflow.common.config.Configuration;
+import org.apache.geaflow.common.config.keys.DSLConfigKeys;
+import org.apache.geaflow.common.config.keys.FrameworkConfigKeys;
+import org.apache.geaflow.dsl.common.compile.CompileContext;
+import org.apache.geaflow.dsl.runtime.QueryClient;
+import org.apache.geaflow.dsl.runtime.QueryContext;
+import org.apache.geaflow.dsl.runtime.QueryEngine;
+import org.apache.geaflow.dsl.runtime.engine.GQLPipeLine;
+import org.apache.geaflow.dsl.runtime.engine.GeaFlowQueryEngine;
+import org.apache.geaflow.dsl.schema.GeaFlowGraph;
+import org.apache.geaflow.env.Environment;
+import org.apache.geaflow.env.EnvironmentFactory;
+import org.apache.geaflow.file.FileConfigKeys;
+import org.apache.geaflow.runtime.pipeline.PipelineContext;
+import org.apache.geaflow.runtime.pipeline.PipelineTaskType;
+import org.apache.geaflow.runtime.pipeline.task.PipelineTaskContext;
+
+public class QueryLocalRunner {
+
+
+    public static final String DSL_STATE_REMOTE_PATH = "/tmp/dsl/mcp";
+    public static final String DSL_STATE_REMOTE_SCHEM_PATH = 
"/tmp/dsl/mcp/schema";
+    private String graphDefine;
+    private String graphName;
+    private String errorMsg;
+    private String query;
+    private final Map<String, String> config = new HashMap<>();
+
+    public QueryLocalRunner withConfig(Map<String, String> config) {
+        this.config.putAll(config);
+        return this;
+    }
+
+    public QueryLocalRunner withConfig(String key, Object value) {
+        this.config.put(key, String.valueOf(value));
+        return this;
+    }
+
+    public QueryLocalRunner withGraphDefine(String graphDefine) {
+        this.graphDefine = Objects.requireNonNull(graphDefine);
+        return this;
+    }
+
+    public QueryLocalRunner withGraphName(String graphName) {
+        this.graphName = Objects.requireNonNull(graphName);
+        return this;
+    }
+
+    public QueryLocalRunner withQuery(String query) {
+        this.query = Objects.requireNonNull(query);
+        return this;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public GeaFlowGraph compileGraph() throws Exception {
+        Map<String, String> config = new HashMap<>();
+        config.put(DSLConfigKeys.GEAFLOW_DSL_WINDOW_SIZE.getKey(), 
String.valueOf(-1L));
+        if (this.graphDefine == null) {
+            throw new RuntimeException("Create graph ddl is empty");
+        }
+        config.put(FrameworkConfigKeys.SYSTEM_STATE_BACKEND_TYPE.getKey(), 
"memory");
+        config.put(FileConfigKeys.ROOT.getKey(), DSL_STATE_REMOTE_PATH);
+        String fileName = 
McpLocalFileUtil.createAndWriteFile(DSL_STATE_REMOTE_PATH, this.graphDefine);
+        config.put(DSLConfigKeys.GEAFLOW_DSL_QUERY_PATH.getKey(), 
DSL_STATE_REMOTE_PATH + "/" + fileName);
+        config.put(DSLConfigKeys.GEAFLOW_DSL_QUERY_PATH_TYPE.getKey(), "file");
+        config.putAll(this.config);
+        Environment environment = EnvironmentFactory.onLocalEnvironment();
+        environment.getEnvironmentContext().withConfig(config);
+        // Compile graph name
+        CompileContext compileContext = new CompileContext();
+        
config.put(DSLConfigKeys.GEAFLOW_DSL_COMPILE_PHYSICAL_PLAN_ENABLE.getKey(), 
"false");
+        compileContext.setConfig(config);
+        PipelineContext pipelineContext = new 
PipelineContext(PipelineTaskType.CompileTask.name(),
+                new Configuration(compileContext.getConfig()));
+        PipelineTaskContext pipelineTaskCxt = new PipelineTaskContext(0L, 
pipelineContext);
+        QueryEngine engineContext = new GeaFlowQueryEngine(pipelineTaskCxt);
+        QueryContext queryContext = QueryContext.builder()
+                .setEngineContext(engineContext)
+                .setCompile(true)
+                .setTraversalParallelism(-1)
+                .build();
+        QueryClient queryClient = new QueryClient();
+        queryClient.executeQuery(this.graphDefine, queryContext);
+        return queryContext.getGraph(graphName);
+    }
+
+    public QueryLocalRunner execute() throws Exception {
+        Map<String, String> config = new HashMap<>();
+        config.put(DSLConfigKeys.GEAFLOW_DSL_WINDOW_SIZE.getKey(), 
String.valueOf(-1L));
+        if (this.graphDefine == null) {
+            throw new RuntimeException("Create graph ddl is empty");
+        }
+        config.put(FrameworkConfigKeys.SYSTEM_STATE_BACKEND_TYPE.getKey(), 
"memory");
+        config.put(FileConfigKeys.ROOT.getKey(), DSL_STATE_REMOTE_PATH);
+        String fileName = 
McpLocalFileUtil.createAndWriteFile(DSL_STATE_REMOTE_PATH, this.query);
+        config.put(DSLConfigKeys.GEAFLOW_DSL_QUERY_PATH.getKey(), 
DSL_STATE_REMOTE_PATH + "/" + fileName);
+        config.put(DSLConfigKeys.GEAFLOW_DSL_QUERY_PATH_TYPE.getKey(), "file");
+        config.put(FrameworkConfigKeys.BATCH_NUMBER_PER_CHECKPOINT.getKey(), 
"1");
+        config.putAll(this.config);
+
+        Environment environment = EnvironmentFactory.onLocalEnvironment();
+        environment.getEnvironmentContext().withConfig(config);
+
+        GQLPipeLine gqlPipeLine = new GQLPipeLine(environment, 0);
+
+        try {
+            gqlPipeLine.execute();
+        } finally {
+            environment.shutdown();
+            ClusterMetaStore.close();
+        }
+        return this;
+    }
+}
diff --git 
a/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/GeaFlowMcpClientTest.java
 
b/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/GeaFlowMcpClientTest.java
index 1a2e66e7..a564d638 100644
--- 
a/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/GeaFlowMcpClientTest.java
+++ 
b/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/GeaFlowMcpClientTest.java
@@ -19,10 +19,8 @@
 
 package org.apache.geaflow.mcp.server;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import org.apache.commons.io.IOUtils;
+import org.apache.geaflow.mcp.server.util.McpConstants;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.noear.solon.ai.mcp.client.McpClientProvider;
@@ -30,10 +28,15 @@ import org.noear.solon.test.SolonTest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.*;
+
 @SolonTest(GeaFlowMcpServer.class)
 public class GeaFlowMcpClientTest {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(GeaFlowMcpClientTest.class);
 
+    private static final String SSE_CHANNEL = "sse";
     private static final String SSE_ENDPOINT = 
"http://localhost:8088/geaflow/sse";;
     private static final String QUERY = "query";
     private static final String EXECUTE_QUERY_TOOL_NAME = "executeQuery";
@@ -60,7 +63,6 @@ public class GeaFlowMcpClientTest {
     /**
      * Call execute query tool.
      */
-    @Test
     public void testExecuteQuery() {
         McpClientProvider toolProvider = McpClientProvider.builder()
             .apiUrl(SSE_ENDPOINT)
@@ -77,6 +79,7 @@ public class GeaFlowMcpClientTest {
     @Test
     public void testGetServerVersion() {
         McpClientProvider toolProvider = McpClientProvider.builder()
+            .channel(SSE_CHANNEL)
             .apiUrl(SSE_ENDPOINT)
             .build();
 
@@ -87,6 +90,7 @@ public class GeaFlowMcpClientTest {
     @Test
     public void testListTools() {
         McpClientProvider toolProvider = McpClientProvider.builder()
+            .channel(SSE_CHANNEL)
             .apiUrl(SSE_ENDPOINT)
             .build();
 
@@ -95,7 +99,118 @@ public class GeaFlowMcpClientTest {
             LOGGER.info("Tool: {}, desc: {}, input schema: {}", tool.name(), 
tool.description(), tool.inputSchema());
             toolNames.add(tool.name());
         });
-        Assertions.assertTrue(toolNames.contains(EXECUTE_QUERY_TOOL_NAME));
+        
Assertions.assertTrue(toolNames.contains(McpConstants.CREATE_GRAPH_TOOL_NAME));
+    }
+
+    /**
+     * Call create graph tool.
+     */
+    @Test
+    public void testCreateGraph() throws IOException {
+        McpClientProvider toolProvider = McpClientProvider.builder()
+                .channel(SSE_CHANNEL)
+                .apiUrl(SSE_ENDPOINT)
+                .build();
+
+        String gql = IOUtils.resourceToString("/gql/modern", 
Charset.defaultCharset()).trim();
+        Map<String, Object> map = new HashMap<>();
+        map.put(McpConstants.DDL, gql);
+        map.put(McpConstants.GRAPH_NAME, "modern");
+
+        String queryResults = 
toolProvider.callToolAsText(McpConstants.CREATE_GRAPH_TOOL_NAME, 
map).getContent();
+        LOGGER.info("queryResults: {}", queryResults);
+        Assertions.assertEquals("Create graph modern success.",
+                queryResults);
+
+    }
+
+    /**
+     * Call insert graph tool.
+     */
+    @Test
+    public void testInsertGraph() throws IOException {
+        McpClientProvider toolProvider = McpClientProvider.builder()
+                .channel(SSE_CHANNEL)
+                .apiUrl(SSE_ENDPOINT)
+                .build();
+
+        String gql = IOUtils.resourceToString("/gql/insert1", 
Charset.defaultCharset()).trim();
+        Map<String, Object> map = new HashMap<>();
+        map.put(McpConstants.DML, gql);
+        map.put(McpConstants.GRAPH_NAME, "modern");
+
+        String queryResults = 
toolProvider.callToolAsText(McpConstants.INSERT_GRAPH_TOOL_NAME, 
map).getContent();
+        LOGGER.info("queryResults: {}", queryResults);
+        String licenseHead = IOUtils.resourceToString("/gql/licenseHead", 
Charset.defaultCharset());
+        Assertions.assertEquals("run query success: " + licenseHead + "INSERT 
INTO modern.person(id, name, age)\n" +
+                        "VALUES (1, 'jim', 20), (2, 'kate', 22)\n" +
+                        ";",
+                queryResults);
+
     }
 
+    /**
+     * Call query graph tool.
+     */
+    @Test
+    public void testQueryGraphType() throws IOException {
+        McpClientProvider toolProvider = McpClientProvider.builder()
+                .channel(SSE_CHANNEL)
+                .apiUrl(SSE_ENDPOINT)
+                .build();
+
+        Map<String, Object> map = new HashMap<>();
+        map.put(McpConstants.TYPE, "person");
+        map.put(McpConstants.GRAPH_NAME, "modern");
+
+        String queryResults = 
toolProvider.callToolAsText(McpConstants.QUERY_TYPE_TOOL_NAME, 
map).getContent();
+        LOGGER.info("queryResults: {}", queryResults);
+        Assertions.assertTrue(queryResults.startsWith("type: person"));
+        Assertions.assertTrue(queryResults.contains("schema: id|name|age"));
+        Assertions.assertTrue(queryResults.contains("1,jim,20"));
+        Assertions.assertTrue(queryResults.contains("2,kate,22"));
+
+    }
+
+    /**
+     * Call create graph tool with error.
+     */
+    @Test
+    public void testCreateGraphFailed() throws IOException {
+        McpClientProvider toolProvider = McpClientProvider.builder()
+                .channel(SSE_CHANNEL)
+                .apiUrl(SSE_ENDPOINT)
+                .build();
+
+        String gql = IOUtils.resourceToString("/gql/modern_error", 
Charset.defaultCharset()).trim();
+        Map<String, Object> map = new HashMap<>();
+        map.put(McpConstants.DDL, gql);
+        map.put(McpConstants.GRAPH_NAME, "modern");
+
+        String queryResults = 
toolProvider.callToolAsText(McpConstants.CREATE_GRAPH_TOOL_NAME, 
map).getContent();
+        LOGGER.info("queryResults: {}", queryResults);
+        Assertions.assertTrue(queryResults.startsWith("Compile error: 
Encountered \"character\""));
+
+    }
+
+    /**
+     * Call insert graph tool.
+     */
+    @Test
+    public void testInsertGraphWithError() throws IOException {
+        McpClientProvider toolProvider = McpClientProvider.builder()
+                .channel(SSE_CHANNEL)
+                .apiUrl(SSE_ENDPOINT)
+                .build();
+
+        String gql = IOUtils.resourceToString("/gql/insert1_error", 
Charset.defaultCharset()).trim();
+        Map<String, Object> map = new HashMap<>();
+        map.put(McpConstants.DML, gql);
+        map.put(McpConstants.GRAPH_NAME, "modern");
+
+        String queryResults = 
toolProvider.callToolAsText(McpConstants.INSERT_GRAPH_TOOL_NAME, 
map).getContent();
+        LOGGER.info("queryResults: {}", queryResults);
+        Assertions.assertTrue(queryResults.contains("Field:er is not found"));
+
+    }
 }
diff --git 
a/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/util/YamlParserTest.java
 
b/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/util/YamlParserTest.java
index 615f8b9c..b5af4f02 100644
--- 
a/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/util/YamlParserTest.java
+++ 
b/geaflow-mcp/src/test/java/org/apache/geaflow/mcp/server/util/YamlParserTest.java
@@ -19,17 +19,16 @@
 
 package org.apache.geaflow.mcp.server.util;
 
-import static org.apache.geaflow.mcp.server.GeaFlowMcpServerTools.CONFIG;
-import static org.apache.geaflow.mcp.server.GeaFlowMcpServerTools.SERVER_HOST;
-import static org.apache.geaflow.mcp.server.GeaFlowMcpServerTools.SERVER_PORT;
-
 import com.alibaba.fastjson.JSON;
-import java.util.Map;
 import org.apache.geaflow.analytics.service.config.AnalyticsClientConfigKeys;
 import org.apache.geaflow.mcp.util.YamlParser;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.util.Map;
+
+import static org.apache.geaflow.mcp.server.GeaFlowMcpServerTools.*;
+
 public class YamlParserTest {
 
     private static final String LOCAL_HOST = "localhost";
diff --git a/geaflow-mcp/src/test/resources/gql/insert1 
b/geaflow-mcp/src/test/resources/gql/insert1
new file mode 100644
index 00000000..a5b6c42e
--- /dev/null
+++ b/geaflow-mcp/src/test/resources/gql/insert1
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+INSERT INTO modern.person(id, name, age)
+VALUES (1, 'jim', 20), (2, 'kate', 22)
+;
\ No newline at end of file
diff --git a/geaflow-mcp/src/test/resources/gql/insert1_error 
b/geaflow-mcp/src/test/resources/gql/insert1_error
new file mode 100644
index 00000000..f3708ff3
--- /dev/null
+++ b/geaflow-mcp/src/test/resources/gql/insert1_error
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+INSERT INTO modern.er(id, name, age)
+VALUES (1, 'jim', 20), (2, 'kate', 22)
+;
\ No newline at end of file
diff --git a/geaflow-mcp/src/test/resources/gql/licenseHead 
b/geaflow-mcp/src/test/resources/gql/licenseHead
new file mode 100644
index 00000000..bd244d07
--- /dev/null
+++ b/geaflow-mcp/src/test/resources/gql/licenseHead
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
diff --git a/geaflow-mcp/src/test/resources/gql/modern 
b/geaflow-mcp/src/test/resources/gql/modern
new file mode 100644
index 00000000..d65e4694
--- /dev/null
+++ b/geaflow-mcp/src/test/resources/gql/modern
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+CREATE GRAPH modern (
+       Vertex person (
+         id bigint ID,
+         name varchar,
+         age int
+       ),
+       Vertex software (
+         id bigint ID,
+         name varchar,
+         lang varchar
+       ),
+       Edge knows (
+         srcId bigint SOURCE ID,
+         targetId bigint DESTINATION ID,
+         weight double
+       ),
+       Edge created (
+         srcId bigint SOURCE ID,
+       targetId bigint DESTINATION ID,
+       weight double
+       )
+) WITH (
+       storeType='rocksdb'
+);
\ No newline at end of file
diff --git a/geaflow-mcp/src/test/resources/gql/modern_error 
b/geaflow-mcp/src/test/resources/gql/modern_error
new file mode 100644
index 00000000..705948e2
--- /dev/null
+++ b/geaflow-mcp/src/test/resources/gql/modern_error
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+CREATE GRAPH modern (
+       Vertex character (
+         id bigint ID,
+         name varchar,
+         age int
+       ),
+       Vertex software (
+         id bigint ID,
+         name varchar,
+         lang varchar
+       ),
+       Edge knows (
+         srcId bigint SOURCE ID,
+         targetId bigint DESTINATION ID,
+         weight double
+       ),
+       Edge created (
+         srcId bigint SOURCE ID,
+       targetId bigint DESTINATION ID,
+       weight double
+       )
+) WITH (
+       storeType='rocksdb'
+);
\ No newline at end of file
diff --git a/geaflow-mcp/src/test/resources/gql/query1 
b/geaflow-mcp/src/test/resources/gql/query1
new file mode 100644
index 00000000..dfeab063
--- /dev/null
+++ b/geaflow-mcp/src/test/resources/gql/query1
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+CREATE TABLE output_console(
+       res BIGINT
+) WITH (
+       type='file',
+       geaflow.dsl.file.path='/tmp/dsl/mcp/result'
+);
+
+INSERT INTO output_console
+Match (a) return a.id;
\ No newline at end of file
diff --git 
a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/QueryContext.java
 
b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/QueryContext.java
index 6f012610..c7d8c62e 100644
--- 
a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/QueryContext.java
+++ 
b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/QueryContext.java
@@ -291,6 +291,10 @@ public class QueryContext {
         return graphs.get(graphName);
     }
 
+    public void addGraph(String graphName, GeaFlowGraph graph) {
+        graphs.put(graphName, graph);
+    }
+
     public void addMaterializedGraph(String graphName) {
         this.materializedGraphs.add(graphName);
     }
diff --git 
a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/command/CreateGraphCommand.java
 
b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/command/CreateGraphCommand.java
index 9202823b..9efc49d0 100644
--- 
a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/command/CreateGraphCommand.java
+++ 
b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/main/java/org/apache/geaflow/dsl/runtime/command/CreateGraphCommand.java
@@ -62,6 +62,7 @@ public class CreateGraphCommand implements IQueryCommand {
         GeaFlowGraph graph = gContext.convertToGraph(createGraph);
         gContext.registerGraph(graph);
         processUsing(graph, context);
+        context.addGraph(graph.getName(), graph);
         LOGGER.info("Succeed to create graph: {}.", graph);
         return new QueryResult(true);
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to