<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mykola Bielousov</title>
    <description>The latest articles on DEV Community by Mykola Bielousov (@mykola-bielousov).</description>
    <link>https://dev.to/mykola-bielousov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3757495%2F85d43bea-f512-4a40-8a44-2688f8c67a77.jpg</url>
      <title>DEV Community: Mykola Bielousov</title>
      <link>https://dev.to/mykola-bielousov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mykola-bielousov"/>
    <language>en</language>
    <item>
      <title>Otto is live: what it took to ship an AI browser agent to the Chrome Web Store</title>
      <dc:creator>Mykola Bielousov</dc:creator>
      <pubDate>Sun, 09 Aug 2026 20:58:08 +0000</pubDate>
      <link>https://dev.to/mykola-bielousov/otto-is-live-what-it-took-to-ship-an-ai-browser-agent-to-the-chrome-web-store-1pgl</link>
      <guid>https://dev.to/mykola-bielousov/otto-is-live-what-it-took-to-ship-an-ai-browser-agent-to-the-chrome-web-store-1pgl</guid>
      <description>&lt;p&gt;On July 15, Google rejected my Chrome extension. I found out on July 23. Years ago I had set up a Gmail filter that auto-deleted every email from Google, so the rejection notice spent that week sitting in my Trash while I refreshed the developer dashboard, wondering why review was taking so long.&lt;/p&gt;

&lt;p&gt;The extension is Otto, and it's live on the Chrome Web Store now: &lt;a href="https://chromewebstore.google.com/detail/otto/pbkhgagiaojcgbpcecmpmldgaifkcpoo" rel="noopener noreferrer"&gt;https://chromewebstore.google.com/detail/otto/pbkhgagiaojcgbpcecmpmldgaifkcpoo&lt;/a&gt; It's a side-panel agent that drives your real, logged-in browser: you type what you want done, and it navigates, reads pages, clicks, types, takes screenshots, and reports back in chat. You can stop it at any moment, and Chrome shows a banner the whole time it's in control.&lt;/p&gt;

&lt;p&gt;The rejection reason was "Keyword Spam". My description listed which AI providers Otto supports, and naming other companies' brands in a listing counts as keyword stuffing, true or not. I deleted the names (the list lives in the README now), resubmitted, and got approved in about a day. The whole detour cost two weeks. The actual fix took twenty minutes.&lt;/p&gt;

&lt;p&gt;What makes Otto different from the hundred other AI sidebars: there is no Otto server and no Otto account. You bring your own API key, it lives in Chrome's local extension storage, and it goes to exactly one place, the AI provider you picked. I never see your data. I couldn't if I wanted to; there is nothing to see it with.&lt;/p&gt;

&lt;p&gt;Under the hood it's plain ES modules, with no build step and zero runtime dependencies. The agent loop and the provider adapters take fetch and the chrome APIs as arguments instead of importing them, so the whole logic layer runs under Node's built-in test runner, offline, with Chrome nowhere in sight. When a provider changes its streaming format, I can reproduce the failure in a unit test in seconds instead of clicking through the extension by hand.&lt;/p&gt;

&lt;p&gt;The one scary dependency is the chrome.debugger permission. Gmail, Google Docs, and most login and checkout flows ignore synthetic DOM events, so an extension that "clicks" with JavaScript does nothing there. Input dispatched through the DevTools Protocol is trusted, the same as a real mouse and keyboard. It also lets Otto screenshot background tabs, so it never has to steal your focus. That permission is the reason my review took the slow, in-depth track.&lt;/p&gt;

&lt;p&gt;A few smaller things that might save someone a day. The manifest description caps at 132 characters, and the store rejects the whole upload over it. Screenshots at 1280x800 worked for me, but PNGs with an alpha channel get refused, so export JPEGs. Each permission justification caps at 1,000 characters, which for chrome.debugger is a genuinely hard writing exercise. And don't promise more than the product does: Otto needs an API key, not a chatbot subscription, so the listing has to say so up front.&lt;/p&gt;

&lt;p&gt;Otto is free and open source. Install it from the Chrome Web Store: &lt;a href="https://chromewebstore.google.com/detail/otto/pbkhgagiaojcgbpcecmpmldgaifkcpoo" rel="noopener noreferrer"&gt;https://chromewebstore.google.com/detail/otto/pbkhgagiaojcgbpcecmpmldgaifkcpoo&lt;/a&gt;. The code, the test suite, and the manual test plan are on GitHub: &lt;a href="https://github.com/legisus/otto-ai-sidebar" rel="noopener noreferrer"&gt;https://github.com/legisus/otto-ai-sidebar&lt;/a&gt;. If you try it and something breaks, open an issue. The debugger attach flow and provider quirks are where the dragons live, and real-world reports are the only way I find them.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>ai</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Let an AI agent drive your real, logged-in Chrome — safely, on localhost</title>
      <dc:creator>Mykola Bielousov</dc:creator>
      <pubDate>Sat, 11 Jul 2026 07:28:45 +0000</pubDate>
      <link>https://dev.to/mykola-bielousov/let-an-ai-agent-drive-your-real-logged-in-chrome-safely-on-localhost-m5e</link>
      <guid>https://dev.to/mykola-bielousov/let-an-ai-agent-drive-your-real-logged-in-chrome-safely-on-localhost-m5e</guid>
      <description>&lt;p&gt;An AI coding agent in my terminal kept hitting the same wall: it needed to do real work in the browser — on sites I was already logged into. Pull an authenticated file, fill a review form, operate a web editor. Reading a public page is easy. Acting &lt;em&gt;as me&lt;/em&gt;, inside my own session, turned out to be surprisingly hard.&lt;/p&gt;

&lt;p&gt;Here's why every obvious approach failed, and the small tool I built instead: &lt;a href="https://github.com/legisus/ai-bridge" rel="noopener noreferrer"&gt;&lt;strong&gt;ai-browser-bridge&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the obvious approaches don't work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OS-level input scripting&lt;/strong&gt; (AppleScript, xdotool, and friends) is platform-specific, steals window focus, and races against whatever you're doing with your own mouse and keyboard. It also can't be trusted by every app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Synthetic DOM events&lt;/strong&gt; — &lt;code&gt;element.dispatchEvent(new MouseEvent(...))&lt;/code&gt; — carry &lt;code&gt;isTrusted: false&lt;/code&gt;. Rich editors built on contenteditable, ProseMirror, Slate, and most CSP-strict web apps deliberately ignore untrusted events. So your automated "click" or "paste" just... does nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headless / profile-isolated automation&lt;/strong&gt; (Puppeteer, Playwright) spins up a browser that doesn't have your session. You'd have to export authenticated cookies to it — and shipping those to any cloud service is a hard no.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic CDP attach&lt;/strong&gt; (&lt;code&gt;chrome --remote-debugging-port=9222&lt;/code&gt;) stopped being an option for the default profile in Chrome 136+.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;Three small pieces, no cloud, no SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI agent / your scripts          bridge server               Chrome extension
   bridge &amp;lt;cmd&amp;gt; CLI    ⇄   ws://127.0.0.1:8765 (token)  ⇄   service worker (MV3)
                                                              │ chrome.tabs / scripting
                                                              │ chrome.debugger  → trusted input, CSP-proof eval
                                                              │ chrome.downloads → authenticated downloads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;CLI&lt;/strong&gt; the agent calls once per operation. It prints JSON to stdout and exits
non-zero on failure — a contract any agent can drive without a library.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;WebSocket relay&lt;/strong&gt; bound to &lt;code&gt;127.0.0.1&lt;/code&gt;, gated by a token.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Manifest V3 extension&lt;/strong&gt; that lives in your &lt;em&gt;normal&lt;/em&gt; Chrome profile, so every
session you're logged into is already there.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The key mechanism: chrome.debugger
&lt;/h2&gt;

&lt;p&gt;The extension attaches &lt;code&gt;chrome.debugger&lt;/code&gt; and speaks the DevTools Protocol per tab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Input.dispatchMouseEvent&lt;/code&gt; / &lt;code&gt;Input.insertText&lt;/code&gt; produce &lt;strong&gt;genuinely trusted&lt;/strong&gt; input
events (&lt;code&gt;isTrusted: true&lt;/code&gt;) — the ones strict editors actually accept.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Runtime.evaluate&lt;/code&gt; runs in the page &lt;strong&gt;regardless of its Content-Security-Policy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Everything happens in &lt;strong&gt;background tabs&lt;/strong&gt; (&lt;code&gt;active: false&lt;/code&gt;), so your focus is never
stolen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because it runs inside your existing profile, there's no re-authentication and no cookie export — your sessions just work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bridge&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; node /path/to/ai-bridge/server/cli.js &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

bridge listTabs
bridge newTab   &lt;span class="s1"&gt;'{"url":"https://example.com"}'&lt;/span&gt;          &lt;span class="c"&gt;# opens in background&lt;/span&gt;
bridge &lt;span class="nb"&gt;eval&lt;/span&gt;     &lt;span class="s1"&gt;'{"tabId":123,"code":"document.title"}'&lt;/span&gt;  &lt;span class="c"&gt;# CSP-proof&lt;/span&gt;
bridge click    &lt;span class="s1"&gt;'{"tabId":123,"x":420,"y":310}'&lt;/span&gt;          &lt;span class="c"&gt;# trusted click&lt;/span&gt;
bridge insertText &lt;span class="s1"&gt;'{"tabId":123,"text":"Hello"}'&lt;/span&gt;         &lt;span class="c"&gt;# trusted paste at caret&lt;/span&gt;
bridge download &lt;span class="s1"&gt;'{"url":"https://.../file.pdf","filename":"file.pdf"}'&lt;/span&gt;  &lt;span class="c"&gt;# your cookies&lt;/span&gt;
bridge pdf      &lt;span class="s1"&gt;'{"tabId":123}'&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; page.pdf
bridge screenshot &lt;span class="s1"&gt;'{"tabId":123}'&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; page.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an agent like Claude Code, you drop a short block into &lt;code&gt;CLAUDE.md&lt;/code&gt; describing these commands, and it composes the calls itself: &lt;em&gt;"open my dashboard and screenshot the error"&lt;/em&gt; becomes a sequence of &lt;code&gt;newTab&lt;/code&gt; / &lt;code&gt;eval&lt;/code&gt; / &lt;code&gt;screenshot&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security was the design constraint
&lt;/h2&gt;

&lt;p&gt;This tool can act as you on any site you're logged into, so the threat model came first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server binds &lt;strong&gt;127.0.0.1 only&lt;/strong&gt; — nothing is reachable from the network.&lt;/li&gt;
&lt;li&gt;Every client must present a &lt;strong&gt;token&lt;/strong&gt; (generated on first run, &lt;code&gt;chmod 600&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;An optional &lt;strong&gt;per-domain allowlist&lt;/strong&gt; is enforced before every tab-targeting command.&lt;/li&gt;
&lt;li&gt;Every command is &lt;strong&gt;logged&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Chrome's native &lt;strong&gt;"… is debugging this browser"&lt;/strong&gt; banner is visible whenever the
debugger is attached.&lt;/li&gt;
&lt;li&gt;No analytics, no telemetry, no external requests of any kind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also deliberately kept it &lt;strong&gt;off the Chrome Web Store&lt;/strong&gt;. The install friction — clone the repo, load unpacked — is a feature for something this powerful: it keeps the code auditable and keeps one-click installs out of the hands of people who won't read the security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;It's MIT, three source files, one dependency:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/legisus/ai-bridge" rel="noopener noreferrer"&gt;github.com/legisus/ai-bridge&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feedback and PRs welcome — especially around new CDP-backed commands and a Firefox port.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Implement OpenClaw in a Spring Boot Application (Java)</title>
      <dc:creator>Mykola Bielousov</dc:creator>
      <pubDate>Sat, 07 Feb 2026 22:21:19 +0000</pubDate>
      <link>https://dev.to/mykola-bielousov/how-to-implement-openclaw-in-a-spring-boot-application-java-5c02</link>
      <guid>https://dev.to/mykola-bielousov/how-to-implement-openclaw-in-a-spring-boot-application-java-5c02</guid>
      <description>

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;OpenClaw is an open‑source, self‑hosted AI agent that exposes a &lt;strong&gt;local gateway HTTP API&lt;/strong&gt; (typically running on &lt;code&gt;http://localhost:18789&lt;/code&gt; or a similar endpoint). &lt;a href="https://help.apiyi.com/en/openclaw-beginner-guide-en.html" rel="noopener noreferrer"&gt;help.apiyi&lt;/a&gt;&lt;br&gt;
To use it in a Spring Boot app, you treat OpenClaw as a &lt;strong&gt;remote REST service&lt;/strong&gt; and call it via &lt;code&gt;RestClient&lt;/code&gt; or &lt;code&gt;WebClient&lt;/code&gt; (or &lt;code&gt;RestTemplate&lt;/code&gt; if you prefer) from your Java backend. &lt;a href="https://docs.spring.io/spring-boot/reference/io/rest-client.html" rel="noopener noreferrer"&gt;docs.spring&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 1: Deploy and configure OpenClaw
&lt;/h3&gt;

&lt;p&gt;Before integrating with Spring Boot, ensure OpenClaw is running and accessible:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install OpenClaw on your machine or VPS using the official installer (for example, via the provided shell script). &lt;a href="https://www.codecademy.com/article/open-claw-tutorial-installation-to-first-chat-setup" rel="noopener noreferrer"&gt;codecademy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run the interactive setup and choose a model provider (e.g., OpenAI, Anthropic, Gemini, or an OpenRouter‑backed model). &lt;a href="https://openrouter.ai/docs/guides/guides/openclaw-integration" rel="noopener noreferrer"&gt;openrouter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Note the &lt;strong&gt;gateway URL and auth token&lt;/strong&gt; (usually via a config file like &lt;code&gt;~/.openclaw/openclaw.json&lt;/code&gt; or environment variables). &lt;a href="https://docs.byteplus.com/api/docs/ModelArk/2183190" rel="noopener noreferrer"&gt;docs.byteplus&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example gateway details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL: &lt;code&gt;http://localhost:18789/v1/chat/completions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Auth header: &lt;code&gt;Authorization: Bearer &amp;lt;YOUR_GATEWAY_TOKEN&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Step 2: Set up a Spring Boot project
&lt;/h3&gt;

&lt;p&gt;Create a new Spring Boot project (e.g. with &lt;code&gt;spring-boot-starter-web&lt;/code&gt;). &lt;a href="https://docs.spring.io/spring-boot/reference/io/rest-client.html" rel="noopener noreferrer"&gt;docs.spring&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;spring-boot-starter-web&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Create a simple DTO for the OpenClaw request / response if you want strict typing:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpenClawRequest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

       &lt;span class="c1"&gt;// Getters and setters&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;

   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

       &lt;span class="c1"&gt;// Getters and setters&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;

   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpenClawResponse&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Choice&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

       &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Choice&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
           &lt;span class="c1"&gt;// getters/setters&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="c1"&gt;// Getters and setters&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Call OpenClaw via RestClient
&lt;/h3&gt;

&lt;p&gt;Spring Boot 3+ encourages using &lt;code&gt;RestClient&lt;/code&gt; instead of &lt;code&gt;RestTemplate&lt;/code&gt;. &lt;a href="https://docs.spring.io/spring-boot/reference/io/rest-client.html" rel="noopener noreferrer"&gt;docs.spring&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a service bean:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;   &lt;span class="nd"&gt;@Service&lt;/span&gt;
   &lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpenClawService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

       &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;RestClient&lt;/span&gt; &lt;span class="n"&gt;restClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

       &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;OpenClawResponse&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;OpenClawRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenClawRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
           &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// or your model slug&lt;/span&gt;

           &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
           &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
           &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMessages&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;restClient&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/v1/chat/completions"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieve&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OpenClawResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Configure the &lt;code&gt;RestClient.Builder&lt;/code&gt; in your config:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;   &lt;span class="nd"&gt;@Configuration&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpenClawConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

       &lt;span class="nd"&gt;@Bean&lt;/span&gt;
       &lt;span class="nd"&gt;@Primary&lt;/span&gt;
       &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RestClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="nf"&gt;openClawRestClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
           &lt;span class="nc"&gt;RestClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
           &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${openclaw.base-url:http://localhost:18789}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
           &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${openclaw.auth-token:}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
       &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultHeaders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                       &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bearer "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                   &lt;span class="o"&gt;}&lt;/span&gt;
                   &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
               &lt;span class="o"&gt;});&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;

       &lt;span class="nd"&gt;@Bean&lt;/span&gt;
       &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;OpenClawService&lt;/span&gt; &lt;span class="nf"&gt;openClawService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RestClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;RestClient&lt;/span&gt; &lt;span class="n"&gt;restClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenClawService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restClient&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Add properties to &lt;code&gt;application.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;openclaw&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;base-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:18789&lt;/span&gt;
     &lt;span class="na"&gt;auth-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-gateway-token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Expose a Spring Boot endpoint
&lt;/h3&gt;

&lt;p&gt;Expose a simple REST endpoint in your controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ai"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpenClawController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OpenClawService&lt;/span&gt; &lt;span class="n"&gt;openClawService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OpenClawController&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OpenClawService&lt;/span&gt; &lt;span class="n"&gt;openClawService&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openClawService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openClawService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/chat"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ChatRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;OpenClawResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openClawService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getChoices&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getChoices&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getChoices&lt;/span&gt;&lt;span class="o"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getContent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No response from OpenClaw"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatRequest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// getters/setters&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can call &lt;code&gt;POST /ai/chat&lt;/code&gt; from your frontend or Postman and it will route through your Spring Boot app to OpenClaw’s gateway. &lt;a href="https://stackoverflow.com/questions/42365266/call-another-rest-api-from-my-server-in-spring-boot" rel="noopener noreferrer"&gt;stackoverflow&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5: Handling streaming / SSE (optional)
&lt;/h3&gt;

&lt;p&gt;If you want to stream the OpenClaw response (e.g., for a chat‑style UI), you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;application/stream+json&lt;/code&gt; if your OpenClaw gateway supports SSE‑style streaming. &lt;a href="https://help.apiyi.com/en/openclaw-beginner-guide-en.html" rel="noopener noreferrer"&gt;help.apiyi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;In Spring Boot, expose the streaming endpoint using &lt;code&gt;SseEmitter&lt;/code&gt; or &lt;code&gt;WebClient&lt;/code&gt;‑based streaming, depending on whether you want to proxy the stream or consume it as Java objects. &lt;a href="https://www.youtube.com/watch?v=XEtPVm_SL2Q" rel="noopener noreferrer"&gt;youtube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple streaming‑style sketch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/stream"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SseEmitter&lt;/span&gt; &lt;span class="nf"&gt;streamChat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;SseEmitter&lt;/span&gt; &lt;span class="n"&gt;emitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SseEmitter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30_000L&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Use WebClient or direct HttpURLConnection to stream OpenClaw SSE&lt;/span&gt;
        &lt;span class="c1"&gt;// For brevity, assume you already have a streaming mechanism in place.&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;emitter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Best‑practice notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Do not expose the OpenClaw gateway directly to the public Internet; keep it internal and call it only from your backend or via a reverse proxy with auth. &lt;a href="https://www.atlantic.net/cloud-platform/openclaw-open-source-ai-agent-and-how-to-deploy-it-on-atlantic-net-cloud/" rel="noopener noreferrer"&gt;atlantic&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate‑limiting / retries&lt;/strong&gt;: Use &lt;code&gt;Resilience4j&lt;/code&gt; or Spring Retry around &lt;code&gt;OpenClawService&lt;/code&gt; calls. &lt;a href="https://www.reddit.com/r/java/comments/1nuar2j/the_state_of_http_clients_in_spring/" rel="noopener noreferrer"&gt;reddit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model‑agnostic usage&lt;/strong&gt;: OpenClaw is designed to be model‑agnostic, so you can swap providers (OpenAI, Gemini, OpenRouter, etc.) without changing the Java code—only the gateway config and model name. &lt;a href="https://www.reddit.com/r/AIToolsPerformance/comments/1qumydm/openclaw_glm_47_running_locally_the_combo_that/" rel="noopener noreferrer"&gt;reddit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;To implement OpenClaw in a Spring Boot application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy and configure OpenClaw locally or on a VPS, noting its HTTP gateway URL and auth token. &lt;a href="https://www.codecademy.com/article/open-claw-tutorial-installation-to-first-chat-setup" rel="noopener noreferrer"&gt;codecademy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;In Spring Boot, use &lt;code&gt;RestClient&lt;/code&gt; (or &lt;code&gt;WebClient&lt;/code&gt;) to call the OpenClaw &lt;code&gt;/v1/chat/completions&lt;/code&gt;‑style endpoint. &lt;a href="https://stackoverflow.com/questions/42365266/call-another-rest-api-from-my-server-in-spring-boot" rel="noopener noreferrer"&gt;stackoverflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wrap the HTTP client in a service bean and expose a REST controller that your frontend can talk to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern lets you keep &lt;strong&gt;OpenClaw as a backend AI agent&lt;/strong&gt; while your Spring Boot app remains the orchestration and security layer for your users. &lt;a href="https://www.reddit.com/r/AIToolsPerformance/comments/1qumydm/openclaw_glm_47_running_locally_the_combo_that/" rel="noopener noreferrer"&gt;reddit&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Mykola Bielousov&lt;/p&gt;

</description>
      <category>ai</category>
      <category>java</category>
      <category>springboot</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AI News Daily: Feb 6, 2026 — The Agent Era Begins</title>
      <dc:creator>Mykola Bielousov</dc:creator>
      <pubDate>Fri, 06 Feb 2026 23:49:38 +0000</pubDate>
      <link>https://dev.to/mykola-bielousov/ai-news-daily-feb-6-2026-the-agent-era-begins-4890</link>
      <guid>https://dev.to/mykola-bielousov/ai-news-daily-feb-6-2026-the-agent-era-begins-4890</guid>
      <description>&lt;h1&gt;
  
  
  AI News Daily: Feb 6, 2026
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;A quick roundup of what is happening in AI today.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. OpenAI "Operator" Looming?
&lt;/h2&gt;

&lt;p&gt;Reports are swirling that OpenAI is preparing to release &lt;strong&gt;Operator&lt;/strong&gt;, its autonomous agent capable of controlling desktop environments. This marks a major shift from "chatbots" to "do-bots" in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. EU AI Act Enforcement
&lt;/h2&gt;

&lt;p&gt;Today marks a critical deadline for the &lt;strong&gt;EU AI Act&lt;/strong&gt;. High-risk AI systems must now fully comply with transparency and risk management protocols. Expect some service disruptions as companies adjust.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Google Gemini 3.0 Refinements
&lt;/h2&gt;

&lt;p&gt;Google quietly pushed updates to &lt;strong&gt;Gemini 3.0&lt;/strong&gt;, specifically targeting legal and medical reasoning performance. Benchmarks show a 12% improvement in citing case law accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. NVIDIA B-Series Chips
&lt;/h2&gt;

&lt;p&gt;NVIDIA's latest B-series chips are promising a &lt;strong&gt;30% reduction in inference costs&lt;/strong&gt;, which is great news for anyone running open-weights models like Llama 4 locally.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Mykola Bielousov&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>tech</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
