diff --git a/.gitignore b/.gitignore index c4cc698..9d6d3db 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ target/ !**/src/test/**/target/ ### IntelliJ IDEA ### -.idea/* +.idea/ .idea/modules.xml .idea/jarRepositories.xml .idea/compiler.xml @@ -36,4 +36,4 @@ build/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store diff --git a/mcp-example/pom.xml b/mcp-example/pom.xml new file mode 100644 index 0000000..60a2f8b --- /dev/null +++ b/mcp-example/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + dev.langchain4j + mcp-example + 0.36.1 + + + 17 + 17 + UTF-8 + + + + + + dev.langchain4j + langchain4j + 0.37.0 + + + + dev.langchain4j + langchain4j-open-ai + 0.37.0 + + + + dev.langchain4j + langchain4j-mcp + 0.37.0 + + + + org.junit.jupiter + junit-jupiter-engine + 5.10.0 + + + + org.assertj + assertj-core + 3.25.3 + + + + ch.qos.logback + logback-classic + 1.5.12 + + + + + diff --git a/mcp-example/src/main/java/dev/langchain4j/example/mcp/Bot.java b/mcp-example/src/main/java/dev/langchain4j/example/mcp/Bot.java new file mode 100644 index 0000000..398b89e --- /dev/null +++ b/mcp-example/src/main/java/dev/langchain4j/example/mcp/Bot.java @@ -0,0 +1,9 @@ +package dev.langchain4j.example.mcp; + +import dev.langchain4j.service.UserMessage; + +public interface Bot { + + String chat(@UserMessage String prompt); + +} diff --git a/mcp-example/src/main/java/dev/langchain4j/example/mcp/McpToolsExampleOverHttp.java b/mcp-example/src/main/java/dev/langchain4j/example/mcp/McpToolsExampleOverHttp.java new file mode 100644 index 0000000..fd0eb62 --- /dev/null +++ b/mcp-example/src/main/java/dev/langchain4j/example/mcp/McpToolsExampleOverHttp.java @@ -0,0 +1,63 @@ +package dev.langchain4j.example.mcp; + +import dev.langchain4j.mcp.McpToolProvider; +import dev.langchain4j.mcp.client.DefaultMcpClient; +import dev.langchain4j.mcp.client.McpClient; +import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport; +import dev.langchain4j.mcp.client.transport.McpTransport; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.service.AiServices; +import dev.langchain4j.service.tool.ToolProvider; + +import java.time.Duration; +import java.util.List; + +public class McpToolsExampleOverHttp { + + /** + * This example uses the `server-everything` MCP server that showcases some aspects of the MCP protocol. + * In particular, we use its 'add' tool that adds two numbers. + * + * Before running this example, you need to start the `everything` server in SSE mode on localhost:3001. + * Check out https://github.com/modelcontextprotocol/servers/tree/main/src/everything + * and run `npm install` and `node dist/sse.js`. + * + * Of course, feel free to swap out the server with any other MCP server. + * + * Run the example and check the logs to verify that the model used the tool. + */ + public static void main(String[] args) throws Exception { + ChatLanguageModel model = OpenAiChatModel.builder() + .modelName("gpt-4o") + .apiKey(System.getenv("OPENAI_API_KEY")) + .logRequests(true) + .logResponses(true) + .build(); + McpTransport transport = new HttpMcpTransport.Builder() + .sseUrl("http://localhost:3001/sse") + .timeout(Duration.ofSeconds(60)) + .logRequests(true) + .logResponses(true) + .build(); + McpClient mcpClient = new DefaultMcpClient.Builder() + .transport(transport) + .build(); + ToolProvider toolProvider = McpToolProvider.builder() + .mcpClients(List.of(mcpClient)) + .build(); + Bot bot = AiServices.builder(Bot.class) + .chatLanguageModel(model) + .toolProvider(toolProvider) + .build(); + try { + String response = bot.chat("What is 5+12? Use the provided tool to answer " + + "and always assume that the tool is correct."); + System.out.println(response); + } finally { + mcpClient.close(); + } + } + + +} diff --git a/mcp-example/src/main/java/dev/langchain4j/example/mcp/McpToolsExampleOverStdio.java b/mcp-example/src/main/java/dev/langchain4j/example/mcp/McpToolsExampleOverStdio.java new file mode 100644 index 0000000..d89bf1e --- /dev/null +++ b/mcp-example/src/main/java/dev/langchain4j/example/mcp/McpToolsExampleOverStdio.java @@ -0,0 +1,74 @@ +package dev.langchain4j.example.mcp; + +import dev.langchain4j.mcp.McpToolProvider; +import dev.langchain4j.mcp.client.DefaultMcpClient; +import dev.langchain4j.mcp.client.McpClient; +import dev.langchain4j.mcp.client.transport.McpTransport; +import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.service.AiServices; +import dev.langchain4j.service.tool.ToolProvider; + +import java.io.File; +import java.util.List; + +public class McpToolsExampleOverStdio { + + // We will let the AI read the contents of this file + public static final String FILE_TO_BE_READ = "src/main/resources/file.txt"; + + /** + * This example uses the `server-filesystem` MCP server to showcase how + * to allow an LLM to interact with the local filesystem. + * + * Running this example requires npm to be installed on your machine, + * because it spawns the `server-filesystem` as a subprocess via npm: + * `npm exec @modelcontextprotocol/server-filesystem@0.6.2`. + * + * Of course, feel free to swap out the server with any other MCP server. + * + * The communication with the server is done directly via stdin/stdout. + * + * IMPORTANT: when executing this, make sure that the working directory is + * equal to the root directory of the project + * (`langchain4j-examples/mcp-example`), otherwise the program won't be able to find + * the proper file to read. If you're working from another directory, + * adjust the path inside the StdioMcpTransport.Builder() usage in the main method. + */ + public static void main(String[] args) throws Exception { + ChatLanguageModel model = OpenAiChatModel.builder() + .modelName("gpt-4o") + .apiKey(System.getenv("OPENAI_API_KEY")) +// .logRequests(true) +// .logResponses(true) + .build(); + McpTransport transport = new StdioMcpTransport.Builder() + .command(List.of("/usr/bin/npm", "exec", + "@modelcontextprotocol/server-filesystem@0.6.2", + // allowed directory for the server to interact with + new File("src/main/resources").getAbsolutePath() + )) + .logEvents(true) + .build(); + McpClient mcpClient = new DefaultMcpClient.Builder() + .transport(transport) + .build(); + ToolProvider toolProvider = McpToolProvider.builder() + .mcpClients(List.of(mcpClient)) + .build(); + Bot bot = AiServices.builder(Bot.class) + .chatLanguageModel(model) + .toolProvider(toolProvider) + .build(); + try { + File file = new File(FILE_TO_BE_READ); + String response = bot.chat("Read the contents of the file " + file.getAbsolutePath()); + System.out.println("RESPONSE: " + response); + } finally { + mcpClient.close(); + } + } + + +} diff --git a/mcp-example/src/main/resources/file.txt b/mcp-example/src/main/resources/file.txt new file mode 100644 index 0000000..2414524 --- /dev/null +++ b/mcp-example/src/main/resources/file.txt @@ -0,0 +1 @@ +Kaboom! \ No newline at end of file diff --git a/pom.xml b/pom.xml index acd80a7..5bc3e83 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ infinispan-example jakartaee-microprofile-example jlama-examples + mcp-example milvus-example mistral-ai-examples neo4j-example