{"id":3991,"date":"2026-07-24T10:00:00","date_gmt":"2026-07-24T14:00:00","guid":{"rendered":"https:\/\/www.mymiller.name\/wordpress\/?p=3991"},"modified":"2026-07-23T15:30:33","modified_gmt":"2026-07-23T19:30:33","slug":"integrating-model-context-protocol-mcp-tooling-with-spring-ai","status":"publish","type":"post","link":"https:\/\/www.mymiller.name\/wordpress\/spring_ai\/integrating-model-context-protocol-mcp-tooling-with-spring-ai\/","title":{"rendered":"Integrating Model Context Protocol (MCP) Tooling with Spring AI"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The <strong>Model Context Protocol (MCP)<\/strong>, originally introduced by Anthropic, is an open standard designed to standardize how Large Language Models (LLMs) connect with external context sources\u2014such as databases, local file systems, third-party APIs, and custom enterprise logic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By integrating MCP support into <strong>Spring AI<\/strong>, Java developers can decouple tool execution from model providers. This guide outlines how MCP works within the Spring ecosystem, how to set up an MCP client to consume external tools, how to build custom MCP tools, and how to expose Spring components as MCP servers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Core Concepts: Spring AI &amp; MCP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In traditional function calling (tool calling), each LLM provider or framework has custom ways to register tools. MCP standardizes this by separating the architecture into three primary components:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>MCP Server<\/strong>: Exposes capabilities such as <strong>Tools<\/strong> (executable functions), <strong>Resources<\/strong> (data\/files), and <strong>Prompts<\/strong> (reusable templates).<\/li>\n\n\n\n<li><strong>MCP Client<\/strong>: Connects to one or more MCP Servers, discovers available capabilities, and bridges them to the LLM.<\/li>\n\n\n\n<li><strong>Transport Protocol<\/strong>: The communication channel between Client and Server, typically:\n<ul class=\"wp-block-list\">\n<li><strong>STDIO<\/strong>: Process-based communication (ideal for local tooling such as CLI utilities or local databases).<\/li>\n\n\n\n<li><strong>SSE (Server-Sent Events \/ HTTP)<\/strong>: Web-based, asynchronous streaming communication (ideal for remote microservices).<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">In a Spring AI application, Spring AI acts as the <strong>MCP Client<\/strong> (or Server), wrapping MCP tool definitions directly into Spring AI&#8217;s native <code>ToolCallback<\/code> interface.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-scaled.avif\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-1024x559.avif\" alt=\"\" class=\"wp-image-3992\" srcset=\"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-1024x559.avif 1024w, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-300x164.avif 300w, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-768x419.avif 768w, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-1536x838.avif 1536w, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-2048x1117.avif 2048w, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-scaled.avif 1280w, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_9a5ce29a5ce29a5c-scaled.avif 1920w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">2. Prerequisites &amp; Dependency Setup<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Java 17<\/strong> or higher<\/li>\n\n\n\n<li><strong>Spring Boot 3.2+<\/strong> or <strong>3.3+<\/strong><\/li>\n\n\n\n<li><strong>Spring AI 1.0.0-M6<\/strong> or later (includes <code>spring-ai-mcp<\/code> starter modules)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Maven Dependencies<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add the Spring AI BOM and the MCP Client starter to your <code>pom.xml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependencyManagement&gt;\n    &lt;dependencies&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.ai&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-ai-bom&lt;\/artifactId&gt;\n            &lt;version&gt;1.0.0-M6&lt;\/version&gt;\n            &lt;type&gt;pom&lt;\/type&gt;\n            &lt;scope&gt;import&lt;\/scope&gt;\n        &lt;\/dependency&gt;\n    &lt;\/dependencies&gt;\n&lt;\/dependencyManagement&gt;\n\n&lt;dependencies&gt;\n    &lt;!-- Core Spring AI Starter (e.g., OpenAI, Ollama, or Anthropic) --&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.ai&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-ai-openai-spring-boot-starter&lt;\/artifactId&gt;\n    &lt;\/dependency&gt;\n\n    &lt;!-- Spring AI MCP Client Starter --&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.ai&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-ai-mcp-client-spring-boot-starter&lt;\/artifactId&gt;\n    &lt;\/dependency&gt;\n&lt;\/dependencies&gt;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3. Configuring an MCP Client in Spring AI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Spring AI provides auto-configuration for MCP clients. You can configure connections to local STDIO-based servers or remote SSE-based servers via <code>application.yml<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Example <code>application.yml<\/code> Setup<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>spring:\n  ai:\n    openai:\n      api-key: ${OPENAI_API_KEY}\n    mcp:\n      client:\n        enabled: true\n        name: spring-ai-mcp-client\n        version: 1.0.0\n        stdio:\n          connections:\n            # Example 1: Connecting to a SQLite MCP server via npx\n            sqlite-server:\n              command: npx\n              args:\n                - \"-y\"\n                - \"@modelcontextprotocol\/server-sqlite\"\n                - \"--db-path\"\n                - \".\/data\/app.db\"\n            # Example 2: Connecting to a File System MCP server\n            filesystem-server:\n              command: npx\n              args:\n                - \"-y\"\n                - \"@modelcontextprotocol\/server-filesystem\"\n                - \".\/allowed-dir\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. Programmatic Configuration &amp; Using <code>ChatClient<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once the MCP client auto-configuration initializes, Spring AI converts all discovered tools into <code>ToolCallback<\/code> beans that can be injected into <code>ChatClient<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Java Configuration Class<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.config;\n\nimport org.springframework.ai.chat.client.ChatClient;\nimport org.springframework.ai.mcp.SyncMcpToolCallbackProvider;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class McpConfig {\n\n    @Bean\n    public ChatClient chatClient(ChatClient.Builder builder, SyncMcpToolCallbackProvider mcpToolProvider) {\n        return builder\n                \/\/ Automatically registers all tools discovered from connected MCP servers\n                .defaultTools(mcpToolProvider.getToolCallbacks())\n                .build();\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Building an Interactive Service<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Here is how you can use the <code>ChatClient<\/code> inside a REST Controller or Service to execute prompts that leverage MCP tools seamlessly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.service;\n\nimport org.springframework.ai.chat.client.ChatClient;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class AssistantService {\n\n    private final ChatClient chatClient;\n\n    public AssistantService(ChatClient chatClient) {\n        this.chatClient = chatClient;\n    }\n\n    public String processUserQuery(String query) {\n        return this.chatClient.prompt()\n                .user(query)\n                .call()\n                .content();\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5. Manual \/ Advanced MCP Client Assembly<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you prefer programmatic control over client lifecycle and transports rather than Spring Boot auto-configuration, you can construct <code>McpClient<\/code> manually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.config;\n\nimport org.springframework.ai.mcp.client.McpClient;\nimport org.springframework.ai.mcp.client.transport.ServerParameters;\nimport org.springframework.ai.mcp.client.transport.StdioClientTransport;\nimport org.springframework.ai.mcp.spring.McpToolCallbackProvider;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\n\n@Configuration\npublic class ManualMcpConfig {\n\n    @Bean\n    public McpClient customMcpClient() {\n        ServerParameters params = ServerParameters.builder(\"npx\")\n                .args(\"-y\", \"@modelcontextprotocol\/server-sqlite\", \"--db-path\", \".\/data\/app.db\")\n                .build();\n\n        StdioClientTransport transport = new StdioClientTransport(params);\n\n        McpClient mcpClient = McpClient.sync(transport)\n                .clientInfo(\"custom-spring-app\", \"1.0.0\")\n                .build();\n\n        mcpClient.initialize();\n        return mcpClient;\n    }\n\n    @Bean\n    public McpToolCallbackProvider mcpToolCallbackProvider(McpClient mcpClient) {\n        return new McpToolCallbackProvider(List.of(mcpClient));\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6. Creating Custom MCP Tools in Spring AI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Creating custom tools for the Model Context Protocol in Spring AI can be done in two ways: <strong>Declaratively<\/strong> using Spring AI annotations, or <strong>Programmatically<\/strong> using low-level MCP SDK interfaces.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.1 Declarative Tools with <code>@Tool<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The easiest way to define tools is using the <code>@Tool<\/code> annotation on standard Spring Boot components. Spring AI automatically parses method parameters, generates JSON schemas, and converts return types.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Defining Strongly-Typed DTOs<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Define Java <code>record<\/code>s or classes to model input arguments and execution outputs cleanly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.tools.dto;\n\nimport com.fasterxml.jackson.annotation.JsonPropertyDescription;\n\npublic record OrderDetailsRequest(\n    @JsonPropertyDescription(\"The unique identifier of the customer order, e.g., ORD-99182\")\n    String orderId,\n    \n    @JsonPropertyDescription(\"Optional flag to include full shipping tracking history\")\n    boolean includeTrackingHistory\n) {}\n\npublic record OrderStatusResponse(\n    String orderId,\n    String status,\n    String estimatedDelivery,\n    List&lt;String&gt; statusLogs\n) {}\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Implementing the Tool Component<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.tools;\n\nimport com.example.mcp.tools.dto.OrderDetailsRequest;\nimport com.example.mcp.tools.dto.OrderStatusResponse;\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\npublic class OrderManagementTools {\n\n    @Tool(\n        name = \"lookupOrderStatus\",\n        description = \"Retrieves the current status and tracking information for a given order ID.\"\n    )\n    public OrderStatusResponse lookupOrderStatus(OrderDetailsRequest request) {\n        \/\/ Business logic to look up order details from DB or downstream API\n        if (\"ORD-99182\".equalsIgnoreCase(request.orderId())) {\n            List&lt;String&gt; logs = request.includeTrackingHistory()\n                    ? List.of(\"Order Placed\", \"Dispatched from warehouse\", \"In Transit\")\n                    : List.of(\"In Transit\");\n\n            return new OrderStatusResponse(\n                    request.orderId(),\n                    \"IN_TRANSIT\",\n                    \"2026-08-01\",\n                    logs\n            );\n        }\n        \n        return new OrderStatusResponse(\n                request.orderId(),\n                \"NOT_FOUND\",\n                \"N\/A\",\n                List.of()\n        );\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6.2 Programmatic Tool Creation (<code>McpServerFeatures<\/code>)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For dynamic tool registration\u2014such as tools loaded conditionally at runtime or generated based on external configuration\u2014you can register tools programmatically via <code>McpServerFeatures.SyncToolRegistration<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.config;\n\nimport io.modelcontextprotocol.server.McpServerFeatures;\nimport io.modelcontextprotocol.spec.McpSchema;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Configuration\npublic class DynamicMcpToolConfig {\n\n    @Bean\n    public McpServerFeatures.SyncToolRegistration currencyConverterTool() {\n        \/\/ 1. Define the tool metadata and input schema\n        McpSchema.Tool tool = new McpSchema.Tool(\n                \"convertCurrency\",\n                \"Converts an amount from a base currency to a target currency using live rates.\",\n                \"\"\"\n                {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"from\": { \"type\": \"string\", \"description\": \"ISO 4217 base currency code, e.g. USD\" },\n                    \"to\": { \"type\": \"string\", \"description\": \"ISO 4217 target currency code, e.g. EUR\" },\n                    \"amount\": { \"type\": \"number\", \"description\": \"The monetary amount to convert\" }\n                  },\n                  \"required\": &#91;\"from\", \"to\", \"amount\"]\n                }\n                \"\"\"\n        );\n\n        \/\/ 2. Register tool execution logic returning structured MCP TextContent\n        return new McpServerFeatures.SyncToolRegistration(tool, arguments -&gt; {\n            String from = (String) arguments.get(\"from\");\n            String to = (String) arguments.get(\"to\");\n            Double amount = Double.valueOf(arguments.get(\"amount\").toString());\n\n            \/\/ Example conversion logic\n            double exchangeRate = 0.92; \/\/ Mock conversion rate USD -&gt; EUR\n            double converted = amount * exchangeRate;\n\n            String responseMessage = String.format(\"%.2f %s = %.2f %s (Rate: %.4f)\",\n                    amount, from.toUpperCase(), converted, to.toUpperCase(), exchangeRate);\n\n            return new McpSchema.CallToolResult(\n                    List.of(new McpSchema.TextContent(responseMessage)),\n                    false \/\/ isError flag\n            );\n        });\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6.3 Handling Errors &amp; Returning Rich Results<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When building custom MCP tools, tools can return formatted text, JSON strings, or error payloads. Returning structured JSON helps LLMs process response data accurately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.tools;\n\nimport io.modelcontextprotocol.spec.McpSchema;\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\npublic class SystemDiagnosticsTools {\n\n    @Tool(description = \"Executes diagnostic ping against a target internal hostname.\")\n    public McpSchema.CallToolResult pingHost(String hostname) {\n        try {\n            if (\"invalid-host\".equalsIgnoreCase(hostname)) {\n                return new McpSchema.CallToolResult(\n                        List.of(new McpSchema.TextContent(\"Host reachability check failed: Unknown host\")),\n                        true \/\/ Signals to the LLM that the tool call produced an error\n                );\n            }\n\n            String jsonPayload = \"\"\"\n                {\n                  \"host\": \"%s\",\n                  \"status\": \"REACHABLE\",\n                  \"latencyMs\": 14.2\n                }\n                \"\"\".formatted(hostname);\n\n            return new McpSchema.CallToolResult(\n                    List.of(new McpSchema.TextContent(jsonPayload)),\n                    false\n            );\n        } catch (Exception e) {\n            return new McpSchema.CallToolResult(\n                    List.of(new McpSchema.TextContent(\"Execution error: \" + e.getMessage())),\n                    true\n            );\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7. Exposing Custom Spring Beans as an MCP Server<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In addition to consuming external MCP servers, your Spring Boot application can <strong>act as an MCP Server<\/strong>, exposing all declared tools (annotated with <code>@Tool<\/code> or configured programmatically) to external clients.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Add Server Dependency<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.ai&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-ai-mcp-server-spring-boot-starter&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Exposing Tools with <code>@Tool<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.mcp.tools;\n\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class InventoryTools {\n\n    @Tool(description = \"Check current inventory stock levels for a given SKU item.\")\n    public int checkInventoryStock(String sku) {\n        if (\"ITEM-123\".equalsIgnoreCase(sku)) {\n            return 42;\n        }\n        return 0;\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Server Configuration<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>spring:\n  ai:\n    mcp:\n      server:\n        name: inventory-mcp-server\n        version: 1.0.0\n        transport: sse # Options: stdio, sse\n        sse:\n          path: \/mcp\/sse\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When started, external clients (such as Claude Desktop or other Spring AI applications) can connect to <code>http:\/\/localhost:8080\/mcp\/sse<\/code> and discover and execute <code>checkInventoryStock<\/code> or any other registered MCP tools.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. Best Practices &amp; Security Considerations<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Transport Selection<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Use <strong>STDIO<\/strong> for local CLI binaries, local desktop integrations, or sidecar containers in Kubernetes.<\/li>\n\n\n\n<li>Use <strong>SSE (Server-Sent Events)<\/strong> for networked microservices, API gateways, and remote deployments.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Access Control &amp; Schema Documentation<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Always use <code>@JsonPropertyDescription<\/code> or detailed parameter descriptions. LLMs rely directly on schema descriptions to decide when and how to call your tools.<\/li>\n\n\n\n<li>Restrict file system tools or sensitive API tools to strictly authorized scopes.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Error Handling<\/strong>:\n<ul class=\"wp-block-list\">\n<li>When tool execution fails, set the <code>isError<\/code> flag on <code>CallToolResult<\/code> to <code>true<\/code> and return descriptive error messages so the model can attempt self-correction or report issues cleanly.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Monitoring &amp; Logging<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Enable debug logging for MCP transport packets during development:<code>logging: level: org.springframework.ai.mcp: DEBUG<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Combining <strong>Spring AI<\/strong> with <strong>Model Context Protocol (MCP)<\/strong> gives Java applications a standardized, plug-and-playable infrastructure for context expansion and tool execution. By abstracting tool protocols away from specific model vendor APIs, Spring AI enables developers to easily build, publish, and consume ecosystem tools (databases, custom domain logic, file systems, APIs) while retaining clean, idiomatic Spring Boot application code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Model Context Protocol (MCP), originally introduced by Anthropic, is an open standard designed to standardize how Large Language Models (LLMs) connect with external context sources\u2014such as databases, local file systems, third-party APIs, and custom enterprise logic. By integrating MCP support into Spring AI, Java developers can decouple tool execution from model providers. This guide [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3993,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[495,443],"tags":[429,69,319],"series":[],"class_list":["post-3991","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-spring_ai","tag-ai","tag-java-2","tag-spring"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_g6ak5ug6ak5ug6ak-scaled.avif","jetpack-related-posts":[{"id":3965,"url":"https:\/\/www.mymiller.name\/wordpress\/angular\/bringing-worlds-to-life-integrating-ai-personas-in-multi-user-dungeons-muds\/","url_meta":{"origin":3991,"position":0},"title":"Bringing Worlds to Life: Integrating AI Personas in Multi-User Dungeons (MUDs)","author":"Jeffery Miller","date":"April 20, 2026","format":false,"excerpt":"A few weeks ago, I found myself pondering the ultimate objective for an artificial intelligence system. The answer kept returning to a single concept: the ability to truly mimic a human. This spark of an idea gave rise to a challenge\u2014I needed a sandbox where I could work with AI\u2026","rel":"","context":"In &quot;Angular&quot;","block_context":{"text":"Angular","link":"https:\/\/www.mymiller.name\/wordpress\/category\/angular\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_hsr3ethsr3ethsr3-scaled.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_hsr3ethsr3ethsr3-scaled.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_hsr3ethsr3ethsr3-scaled.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_hsr3ethsr3ethsr3-scaled.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_hsr3ethsr3ethsr3-scaled.avif 3x"},"classes":[]},{"id":3970,"url":"https:\/\/www.mymiller.name\/wordpress\/architecture\/vibe-coding-the-next-generation-how-we-built-aimud-using-an-ai-ensemble\/","url_meta":{"origin":3991,"position":1},"title":"Vibe Coding the Next Generation: How We Built AIMUD Using an AI Ensemble","author":"Jeffery Miller","date":"April 21, 2026","format":false,"excerpt":"In the traditional world of software engineering, building a Multi-User Dungeon (MUD) is a rite of passage. It requires handling complex state, real-time networking, concurrency, and deep game logic. Usually, this takes months of meticulous, line-by-line keyboard grinding. But for AIMUD, we didn't just code; we vibe coded. By leveraging\u2026","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.mymiller.name\/wordpress\/category\/ai\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_6veptk6veptk6vep-scaled.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_6veptk6veptk6vep-scaled.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_6veptk6veptk6vep-scaled.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_6veptk6veptk6vep-scaled.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_6veptk6veptk6vep-scaled.avif 3x"},"classes":[]},{"id":3987,"url":"https:\/\/www.mymiller.name\/wordpress\/spring_ai\/enterprise-ai-at-scale-why-spring-ai-and-java-are-built-for-the-long-run\/","url_meta":{"origin":3991,"position":2},"title":"Enterprise AI at Scale: Why Spring AI and Java are Built for the Long Run","author":"Jeffery Miller","date":"July 23, 2026","format":false,"excerpt":"While Python remains the undisputed king of AI research, data exploration, and model training, the landscape shifts dramatically when moving from experimental notebooks to high-throughput, mission-critical production systems. For enterprise engineering teams building user-facing applications, workflow automations, and LLM-powered services, the real challenge isn't training a model\u2014it's integrating, scaling, securing,\u2026","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.mymiller.name\/wordpress\/category\/ai\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_rht72frht72frht7-scaled.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_rht72frht72frht7-scaled.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_rht72frht72frht7-scaled.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_rht72frht72frht7-scaled.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_rht72frht72frht7-scaled.avif 3x"},"classes":[]},{"id":3893,"url":"https:\/\/www.mymiller.name\/wordpress\/spring_ai\/building-intelligent-apps-with-spring-ai\/","url_meta":{"origin":3991,"position":3},"title":"Building Intelligent Apps with Spring AI","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"In today's fast-paced world of software development, integrating artificial intelligence into applications is no longer just a trend\u2014it's a necessity. At the heart of this revolution is Generative AI, a type of artificial intelligence that can create new content, such as text, images, and code, in response to prompts. It's\u2026","rel":"","context":"In &quot;Spring AI&quot;","block_context":{"text":"Spring AI","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring_ai\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-8273796_1280.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-8273796_1280.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-8273796_1280.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-8273796_1280.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-8273796_1280.avif 3x"},"classes":[]},{"id":3931,"url":"https:\/\/www.mymiller.name\/wordpress\/uncategorized\/advanced-spring-ai-creating-agentic-workflows-with-function-calling\/","url_meta":{"origin":3991,"position":4},"title":"Advanced Spring AI: Creating Agentic Workflows with Function Calling","author":"Jeffery Miller","date":"November 24, 2025","format":false,"excerpt":"The landscape of AI is rapidly evolving, moving beyond simple request-response models to more sophisticated, agentic systems. These systems empower Large Language Models (LLMs) to not just generate text, but to act within your applications, making them an active and integral part of your business logic. Spring AI is at\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/11\/Gemini_Generated_Image_kg5i0ykg5i0ykg5i.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/11\/Gemini_Generated_Image_kg5i0ykg5i0ykg5i.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/11\/Gemini_Generated_Image_kg5i0ykg5i0ykg5i.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/11\/Gemini_Generated_Image_kg5i0ykg5i0ykg5i.avif 2x"},"classes":[]},{"id":3951,"url":"https:\/\/www.mymiller.name\/wordpress\/java\/scaling-streams-mastering-virtual-threads-in-spring-boot-4-and-java-25\/","url_meta":{"origin":3991,"position":5},"title":"Scaling Streams: Mastering Virtual Threads in Spring Boot 4 and Java 25","author":"Jeffery Miller","date":"April 20, 2026","format":false,"excerpt":"As a software architect, I\u2019ve seen the industry shift from heavy platform threads to reactive streams, and finally to the \"best of both worlds\": Virtual Threads. With the recent release of Spring Boot 4.0 and Java 25 (LTS), Project Loom's innovations have officially become the bedrock of high-concurrency enterprise Java.\u2026","rel":"","context":"In &quot;JAVA&quot;","block_context":{"text":"JAVA","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/Gemini_Generated_Image_wqijejwqijejwqij-scaled.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/Gemini_Generated_Image_wqijejwqijejwqij-scaled.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/Gemini_Generated_Image_wqijejwqijejwqij-scaled.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/Gemini_Generated_Image_wqijejwqijejwqij-scaled.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/Gemini_Generated_Image_wqijejwqijejwqij-scaled.avif 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/3991","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/comments?post=3991"}],"version-history":[{"count":1,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/3991\/revisions"}],"predecessor-version":[{"id":3994,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/3991\/revisions\/3994"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media\/3993"}],"wp:attachment":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media?parent=3991"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/categories?post=3991"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/tags?post=3991"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/series?post=3991"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}