# LangChain4j MCP Server Implementation Examples This document provides comprehensive, production-ready examples for implementing MCP servers with LangChain4j. ## Basic MCP Server Setup ### Simple MCP Server Implementation Create a basic MCP server with single tool functionality: ```java import dev.langchain4j.mcp.MCPServer; import dev.langchain4j.mcp.ToolProvider; import dev.langchain4j.mcp.server.StdioServer; public class BasicMcpServer { public static void main(String[] args) { MCPServer server = MCPServer.builder() .server(new StdioServer.Builder()) .addToolProvider(new SimpleWeatherToolProvider()) .build(); // Start the server server.start(); } } class SimpleWeatherToolProvider implements ToolProvider { @Override public List listTools() { return List.of(ToolSpecification.builder() .name("get_weather") .description("Get weather information for a city") .inputSchema(Map.of( "type", "object", "properties", Map.of( "city", Map.of( "type", "string", "description", "City name to get weather for" ) ), "required", List.of("city") )) .build()); } @Override public String executeTool(String name, String arguments) { if ("get_weather".equals(name)) { JsonObject args = JsonParser.parseString(arguments).getAsJsonObject(); String city = args.get("city").getAsString(); // Simulate weather API call return String.format("Weather in %s: Sunny, 22°C", city); } throw new UnsupportedOperationException("Unknown tool: " + name); } } ``` ### Spring Boot MCP Server Integration Integrate MCP server with Spring Boot application: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class McpSpringBootApplication { public static void main(String[] args) { SpringApplication.run(McpSpringBootApplication.class, args); } @Bean public MCPServer mcpServer() { return MCPServer.builder() .server(new StdioServer.Builder()) .addToolProvider(new DatabaseToolProvider()) .addToolProvider(new FileToolProvider()) .build(); } } @Component class DatabaseToolProvider implements ToolProvider { @Override public List listTools() { return List.of(ToolSpecification.builder() .name("query_database") .description("Execute SQL queries against the database") .inputSchema(Map.of( "type", "object", "properties", Map.of( "sql", Map.of( "type", "string", "description", "SQL query to execute" ) ), "required", List.of("sql") )) .build()); } @Override public String executeTool(String name, String arguments) { if ("query_database".equals(name)) { JsonObject args = JsonParser.parseString(arguments).getAsJsonObject(); String sql = args.get("sql").getAsString(); // Execute database query return executeDatabaseQuery(sql); } throw new UnsupportedOperationException("Unknown tool: " + name); } private String executeDatabaseQuery(String sql) { // Implementation using Spring Data JPA try { return jdbcTemplate.queryForObject(sql, String.class); } catch (Exception e) { return "Error executing query: " + e.getMessage(); } } } ``` ## Multi-Tool MCP Server ### Enterprise MCP Server with Multiple Tools Create a comprehensive MCP server with multiple tool providers: ```java @Component public class EnterpriseMcpServer { @Bean public MCPServer enterpriseMcpServer( GitHubToolProvider githubToolProvider, DatabaseToolProvider databaseToolProvider, FileToolProvider fileToolProvider, EmailToolProvider emailToolProvider) { return MCPServer.builder() .server(new StdioServer.Builder()) .addToolProvider(githubToolProvider) .addToolProvider(databaseToolProvider) .addToolProvider(fileToolProvider) .addToolProvider(emailToolProvider) .enableLogging(true) .setLogHandler(new CustomLogHandler()) .build(); } } @Component class GitHubToolProvider implements ToolProvider { @Override public List listTools() { return List.of( ToolSpecification.builder() .name("get_issue") .description("Get GitHub issue details") .inputSchema(Map.of( "type", "object", "properties", Map.of( "owner", Map.of( "type", "string", "description", "Repository owner" ), "repo", Map.of( "type", "string", "description", "Repository name" ), "issue_number", Map.of( "type", "integer", "description", "Issue number" ) ), "required", List.of("owner", "repo", "issue_number") )) .build(), ToolSpecification.builder() .name("list_issues") .description("List GitHub issues for a repository") .inputSchema(Map.of( "type", "object", "properties", Map.of( "owner", Map.of( "type", "string", "description", "Repository owner" ), "repo", Map.of( "type", "string", "description", "Repository name" ), "state", Map.of( "type", "string", "description", "Issue state: open, closed, all", "enum", List.of("open", "closed", "all") ) ), "required", List.of("owner", "repo") )) .build() ); } @Override public String executeTool(String name, String arguments) { switch (name) { case "get_issue": return getIssueDetails(arguments); case "list_issues": return listRepositoryIssues(arguments); default: throw new UnsupportedOperationException("Unknown tool: " + name); } } private String getIssueDetails(String arguments) { JsonObject args = JsonParser.parseString(arguments).getAsJsonObject(); String owner = args.get("owner").getAsString(); String repo = args.get("repo").getAsString(); int issueNumber = args.get("issue_number").getAsInt(); // Call GitHub API GitHubIssue issue = githubService.getIssue(owner, repo, issueNumber); return "Issue #" + issueNumber + ": " + issue.getTitle() + "\nState: " + issue.getState() + "\nCreated: " + issue.getCreatedAt(); } private String listRepositoryIssues(String arguments) { JsonObject args = JsonParser.parseString(arguments).getAsJsonObject(); String owner = args.get("owner").getAsString(); String repo = args.get("repo").getAsString(); String state = args.has("state") ? args.get("state").getAsString() : "open"; List issues = githubService.listIssues(owner, repo, state); return issues.stream() .map(issue -> "#%d: %s (%s)".formatted(issue.getNumber(), issue.getTitle(), issue.getState())) .collect(Collectors.joining("\n")); } } ``` ## Resource Provider Implementation ### Static Resource Provider Provide static resources for context enhancement: ```java @Component class StaticResourceProvider implements ResourceListProvider, ResourceReadHandler { private final Map resources = new HashMap<>(); public StaticResourceProvider() { // Initialize with static resources resources.put("company-policies", loadCompanyPolicies()); resources.put("api-documentation", loadApiDocumentation()); resources.put("best-practices", loadBestPractices()); } @Override public List listResources() { return resources.keySet().stream() .map(uri -> McpResource.builder() .uri(uri) .name(uri.replace("-", " ")) .description("Documentation resource") .mimeType("text/plain") .build()) .collect(Collectors.toList()); } @Override public String readResource(String uri) { if (!resources.containsKey(uri)) { throw new ResourceNotFoundException("Resource not found: " + uri); } return resources.get(uri); } private String loadCompanyPolicies() { // Load company policies from file or database return "Company Policies:\n1. Work hours: 9-5\n2. Security compliance\n3. Data privacy"; } private String loadApiDocumentation() { // Load API documentation return "API Documentation:\nGET /api/users - List users\nPOST /api/users - Create user"; } } ``` ### Dynamic Resource Provider Create dynamic resources that update in real-time: ```java @Component class DynamicResourceProvider implements ResourceListProvider, ResourceReadHandler { @Autowired private MetricsService metricsService; @Override public List listResources() { return List.of( McpResource.builder() .uri("system-metrics") .name("System Metrics") .description("Real-time system performance metrics") .mimeType("application/json") .build(), McpResource.builder() .uri("user-analytics") .name("User Analytics") .description("User behavior and usage statistics") .mimeType("application/json") .build() ); } @Override public String readResource(String uri) { switch (uri) { case "system-metrics": return metricsService.getCurrentSystemMetrics(); case "user-analytics": return metricsService.getUserAnalytics(); default: throw new ResourceNotFoundException("Resource not found: " + uri); } } } ``` ## Prompt Template Provider ### Prompt Template Server Create prompt templates for common AI tasks: ```java @Component class PromptTemplateProvider implements PromptListProvider, PromptGetHandler { private final Map templates = new HashMap<>(); public PromptTemplateProvider() { templates.put("code-review", PromptTemplate.builder() .name("Code Review") .description("Review code for quality, security, and best practices") .template("Review the following code for:\n" + "1. Code quality and readability\n" + "2. Security vulnerabilities\n" + "3. Performance optimizations\n" + "4. Best practices compliance\n\n" + "Code:\n" + "```{code}```\n\n" + "Provide a detailed analysis with specific recommendations.") .build()); templates.put("documentation-generation", PromptTemplate.builder() .name("Documentation Generator") .description("Generate technical documentation from code") .template("Generate comprehensive documentation for the following code:\n" + "{code}\n\n" + "Include:\n" + "1. Function/method signatures\n" + "2. Parameters and return values\n" + "3. Purpose and usage examples\n" + "4. Dependencies and requirements") .build()); } @Override public List listPrompts() { return templates.values().stream() .map(template -> Prompt.builder() .name(template.getName()) .description(template.getDescription()) .build()) .collect(Collectors.toList()); } @Override public String getPrompt(String name, Map arguments) { PromptTemplate template = templates.get(name); if (template == null) { throw new PromptNotFoundException("Prompt not found: " + name); } // Replace template variables String content = template.getTemplate(); for (Map.Entry entry : arguments.entrySet()) { content = content.replace("{" + entry.getKey() + "}", entry.getValue()); } return content; } } ``` ## Error Handling and Validation ### Robust Error Handling Implement comprehensive error handling and validation: ```java @Component class RobustToolProvider implements ToolProvider { @Override public List listTools() { return List.of(ToolSpecification.builder() .name("secure_data_access") .description("Access sensitive data with proper validation") .inputSchema(Map.of( "type", "object", "properties", Map.of( "data_type", Map.of( "type", "string", "description", "Type of data to access", "enum", List.of("user_data", "system_data", "admin_data") ), "user_id", Map.of( "type", "string", "description", "User ID requesting access" ) ), "required", List.of("data_type", "user_id") )) .build()); } @Override public String executeTool(String name, String arguments) { if ("secure_data_access".equals(name)) { try { JsonObject args = JsonParser.parseString(arguments).getAsJsonObject(); String dataType = args.get("data_type").getAsString(); String userId = args.get("user_id").getAsString(); // Validate user permissions if (!hasPermission(userId, dataType)) { return "Access denied: Insufficient permissions"; } // Get data securely return getSecureData(dataType, userId); } catch (JsonParseException e) { return "Invalid JSON format: " + e.getMessage(); } catch (Exception e) { return "Error accessing data: " + e.getMessage(); } } throw new UnsupportedOperationException("Unknown tool: " + name); } private boolean hasPermission(String userId, String dataType) { // Implement permission checking if ("admin_data".equals(dataType)) { return userRepository.isAdmin(userId); } return true; } private String getSecureData(String dataType, String userId) { // Implement secure data retrieval if ("user_data".equals(dataType)) { return userDataService.getUserData(userId); } return "Data not available"; } } ``` ## Advanced Server Configuration ### Multi-Transport Server Configuration Configure MCP server with multiple transport options: ```java @Configuration public class AdvancedMcpConfiguration { @Bean public MCPServer advancedMcpServer( List toolProviders, List resourceProviders, List promptProviders) { return MCPServer.builder() .server(new StdioServer.Builder()) .addToolProvider(toolProviders) .addResourceProvider(resourceProviders) .addPromptProvider(promptProviders) .enableLogging(true) .setLogHandler(new StructuredLogHandler()) .enableHealthChecks(true) .setHealthCheckInterval(30) // seconds .setMaxConcurrentRequests(100) .setRequestTimeout(30) // seconds .build(); } @Bean public HttpMcpTransport httpTransport() { return new HttpMcpTransport.Builder() .sseUrl("http://localhost:8080/mcp/sse") .logRequests(true) .logResponses(true) .setCorsEnabled(true) .setAllowedOrigins(List.of("http://localhost:3000")) .build(); } } ``` ## Client Integration Patterns ### Multi-Client MCP Integration Integrate with multiple MCP servers for comprehensive functionality: ```java @Service public class MultiMcpIntegrationService { private final List mcpClients; private final ChatModel chatModel; private final McpToolProvider toolProvider; public MultiMcpIntegrationService(List mcpClients, ChatModel chatModel) { this.mcpClients = mcpClients; this.chatModel = chatModel; // Create tool provider with multiple MCP clients this.toolProvider = McpToolProvider.builder() .mcpClients(mcpClients) .failIfOneServerFails(false) // Continue with available servers .filter((client, tool) -> { // Implement cross-server tool filtering return !tool.name().startsWith("deprecated_"); }) .build(); } public String processUserQuery(String userId, String query) { // Create AI service with multiple MCP integrations AIAssistant assistant = AiServices.builder(AIAssistant.class) .chatModel(chatModel) .toolProvider(toolProvider) .chatMemoryProvider(memoryProvider) .build(); return assistant.chat(userId, query); } public List getAvailableTools() { return mcpClients.stream() .flatMap(client -> { try { return client.listTools().stream(); } catch (Exception e) { log.warn("Failed to list tools from client {}: {}", client.key(), e.getMessage()); return Stream.empty(); } }) .distinct() .collect(Collectors.toList()); } } ``` These comprehensive examples provide a solid foundation for implementing MCP servers with LangChain4j, covering everything from basic setup to advanced enterprise patterns.