JavaClaw is a Java-based personal AI assistant that runs on your own devices. It acts as a control plane (gateway) for an AI agent that can interact across multiple communication channels, manage tasks, execute shell commands, browse the web, and more — all while keeping your data local.
NOTE: this project was originally created as a demo to show the use of JobRunr, JavaClaw is now an open invitation to the Java community, let’s build the future of Java-based AI agents together ☕.
- Multi-Channel Support — Chat UI (WebSocket), Telegram, Discord, WhatsApp, Microsoft Teams, and an extensible plugin-based channel architecture
- Task Management — Create, schedule (one-off, delayed, or recurring via cron), and track tasks as human-readable Markdown files
- Extensible Skills — Load skills from
workspace/skills/and fromskills jarpackages on the classpath - LLM Provider Choice — Plug in OpenAI, Anthropic, your Claude Pro/Max subscription (via the official Claude Code CLI), or Ollama (local); switchable during onboarding
- MCP Support — Model Context Protocol client for connecting external tool servers
- Shell & File Access — Agent can read/write files and run bash commands on your machine
- Smart Web Tools — Brave web search and intelligent web scraping
- Background Jobs — Powered by JobRunr with a built-in dashboard at
:8081 - Privacy-First — Runs entirely on your own hardware; no data leaves unless you configure an external LLM
| Layer | Technology |
|---|---|
| Language | Java 25 |
| Framework | Spring Boot 4.0.3, Spring Modulith 2.0.3 |
| LLM | Spring AI 2.0.0-SNAPSHOT (OpenAI, Anthropic, Ollama) |
| Agent | Spring AI Agent Utils |
| MCP | Spring AI MCP Client |
| Jobs | JobRunr 8.5.0 |
| Database | H2 (embedded, file-backed) |
| Templating | Pebble 4.1.1 |
| Frontend | htmx 2.0.8 + Bulma 1.0.4 |
| Discord | JDA 6.1.1 |
| Telegram | Telegrambots 9.4.0 |
JavaClaw/
├── base/ # Core: agent, tasks, tools, channels, config
├── app/ # Spring Boot entry point, onboarding UI, web routes, chat channel
└── plugins/
├── brave/ # Brave web search integration
├── discord/ # Discord Gateway channel plugin
├── playwright/ # Browser automation tools
├── teams/ # Microsoft Teams webhook channel plugin
└── telegram/ # Telegram long-poll channel plugin
- Java 25
- Gradle (or use the included
./gradlewwrapper) - An LLM API key (OpenAI / Anthropic), a Claude Pro/Max subscription with Claude Code installed and logged in (
claude auth login), or a running Ollama instance
./gradlew :app:bootRunYou can also build a docker image and use it as follows (from the cloned repo):
./gradlew app:jibDockerBuild
docker run -it -p 8080:8080 -p:8081:8081 -v "$(pwd)/workspace:/workspace" jobrunr.io/javaclawThen open http://localhost:8080/onboarding to complete the guided onboarding.
- Welcome — Introduction screen
- Provider — Choose Ollama, OpenAI, Anthropic, or Claude subscription
- Credentials — Enter your API key and model name
- Agent Prompt — Customize
workspace/AGENT.mdwith your personal info (name, email, role, etc.) - MCP Servers — Optionally configure Model Context Protocol servers
- Channel/Tool Plugins — Optional steps such as Telegram, Discord, and other plugin-provided setup
- Complete — Review and save your configuration
Configuration is persisted to app/src/main/resources/application.yaml and takes effect immediately.
The workspace/ directory is the agent's home:
| Path | Purpose |
|---|---|
workspace/AGENT.md |
System prompt — edit during onboarding or at any time |
workspace/INFO.md |
Environment context injected into every prompt |
workspace/context/ |
Agent memory and long-term context files |
workspace/skills/ |
Drop a SKILL.md here to add new capabilities |
workspace/tasks/ |
Task files, date-bucketed (yyyy-MM-dd/HHmmss-<state>-<name>.md) |
workspace/tasks/recurring/ |
Cron-scheduled recurring task templates |
todo → in_progress → completed / awaiting_human_input
Available at http://localhost:8080/chat. Uses WebSocket for real-time delivery and falls back to REST polling when no WebSocket session is active.
Configure during onboarding or by setting:
agent:
channels:
telegram:
token: <your-bot-token>
username: <your-telegram-username>Configure during onboarding or by setting:
agent:
channels:
discord:
token: <your-discord-bot-token>
allowed-user: <your-discord-user-id>WhatsApp connectivity uses wacli, a small Go CLI that pairs as a
WhatsApp Web linked device. There is no Java WhatsApp library — JavaClaw launches wacli as a
subprocess and receives inbound messages through a local webhook.
1. Install wacli
# macOS
brew install openclaw/tap/wacli
# Linux
go install github.com/openclaw/wacli@latest2. Pair your phone
Run the following and scan the QR code with WhatsApp on your phone (Settings → Linked devices → Link a device):
wacli auth3. Configure
Configure during onboarding or by setting:
agent:
channels:
whatsapp:
enabled: true
allowed-chat-jid: "1234567890@s.whatsapp.net"allowed-chat-jid is the JID of the single WhatsApp chat the assistant responds in. During
onboarding you pick it by name from your synced chats, so you rarely need to type it — WhatsApp also
issues opaque @lid identifiers that cannot be derived from a phone number. To find one yourself:
wacli chats list # every chat, with its JID
wacli chats list --query "alice" # search by nameOnly one-to-one chats are accepted. Groups, newsletters and broadcasts are deliberately refused:
the assistant authorises by chat, so every member of a group would be able to instruct it — and the
agent can run shell commands. Point it at your own "Message Yourself" chat to talk to the assistant
from your own number. wacli itself is taken from your PATH.
Disclaimer: wacli uses the unofficial WhatsApp Web protocol — use at your own risk.
Teams delivers messages to a bot through Azure Bot Service: Microsoft calls a public HTTPS webhook on JavaClaw, and JavaClaw answers through the Bot Framework Connector API.
1. Expose the webhook
The webhook lives at /api/teams/webhook. Azure must be able to reach it over public HTTPS, so for
a self-hosted setup put a tunnel in front of JavaClaw, for example:
ngrok http 8080 # gives you https://<random>.ngrok-free.app2. Create the bot
In the Azure portal, create an Azure Bot resource, set its messaging endpoint to
https://<your-host>/api/teams/webhook, and enable the Microsoft Teams channel. Note the bot's
App ID, create a client secret for it, and note your Tenant ID.
3. Configure
Configure during onboarding or by setting:
agent:
channels:
teams:
enabled: true
app-id: <bot-app-id>
app-secret: <bot-client-secret>
tenant-id: <azure-tenant-id>
allowed-user-id: <your-entra-object-id>allowed-user-id is the only Teams user the assistant responds to: either your Microsoft Entra
object ID (Azure portal → Microsoft Entra ID → Users) or your Teams user ID (29:...). It is
required, because the agent can run shell commands; without it every message is ignored. Every
webhook call must carry a valid Bot Framework JWT (signature, issuer, audience, expiry and
serviceUrl are checked), so requests that do not come from Microsoft are rejected with 401.
The assistant only responds in personal (1:1) chats with the bot. Messages in team channels or group chats are ignored, even from the allowed user, because replies can contain shell output or file contents. Messages the agent sends on its own, e.g. from scheduled tasks, also only go to the 1:1 chat.
Skills extend the agent's capabilities at runtime without code changes. Create a directory under workspace/skills/<skill-name>/ containing a SKILL.md file and the agent will load it automatically via SkillsTool.
You can also package skills into a jar (or use a prebuilt one) and put it on the runtime classpath. JavaClaw can load skills from classpath:/META-INF/skills.
- Add a dependency that contains the skills (example):
dependencies {
runtimeOnly("com.skillsjars:some-skill-pack:VERSION")
}- Configure the classpath scan path:
agent:
skills:
paths: classpath:/META-INF/skills- Ensure the jar contains this layout:
META-INF/skills/<skill-name>/SKILL.md
SKILL.md must include YAML frontmatter with a name (this is what you invoke):
---
name: <skill-name>
description: <optional>
---
<instructions...>See the SkillsJars docs for packaging and published skill packs: https://www.skillsjars.com/
JobRunr's job dashboard is available at http://localhost:8081 for monitoring background task execution.
Key properties in application.yaml:
| Property | Description |
|---|---|
agent.workspace |
Path to the workspace root (default: file:./workspace/) |
agent.onboarding.completed |
Set to true after onboarding is done |
agent.skills.paths |
Classpath resource roots to scan for skills (e.g. classpath:/META-INF/skills) |
spring.ai.model.chat |
Active LLM provider/model |
javaclaw.tools.dynamic-discovery.enabled |
Enable dynamic tool discovery (Tool Search Tool pattern) instead of exposing all tools up front |
jobrunr.dashboard.port |
JobRunr dashboard port (default: 8081) |
jobrunr.background-job-server.worker-count |
Concurrent job workers (default: 1) |
When enabled, JavaClaw uses Spring AI's "Tool Search Tool" pattern ("tool search") so the model discovers relevant tools at runtime instead of receiving every tool definition up front.
Use it when:
- You have many tools (plugins, MCP servers, skills) and prompts are getting large.
- The model picks the wrong tool because the tool list is too big or too similar.
javaclaw:
tools:
dynamic-discovery:
enabled: true
# Optional tuning:
max-results: 8
lucene-min-score-threshold: 0.25Flag behavior:
enabled=true(default): uses the Tool Search advisor (dynamic discovery, Lucene keyword search).enabled=false: eager tool exposure (legacy behavior).
Notes:
- Tool search quality depends on
@Tool(description = "..."). Keep descriptions specific and disambiguating. - Tuning: raise
lucene-min-score-thresholdto be stricter; lower it if tools are not found. Adjustmax-resultsto control how many tools get surfaced.
./gradlew testTests cover task management, Telegram and Discord channel authorization/flow, onboarding steps, and the full Spring context.
Release Demo Video Website Release Blog post
This project is open-source. See LICENSE for details.