# DocsClaw
A lightweight, configurable agentic runtime that turns any LLM into a
tool-using, [A2A](https://a2a-protocol.org/latest/)-compatible agent.
Deploy with a system prompt, agent card, and tool config — no code
changes needed. Runs at ~10 MiB per pod on OpenShift.
## Quick start
```bash
make build
ANTHROPIC_API_KEY=sk-... ./bin/docsclaw serve \
--config-dir testdata/standalone \
--listen-plain-http
```
Install the [A2A CLI](https://github.com/a2aproject/a2a-go/tree/main/cmd)
to discover and interact with agents:
```bash
go install github.com/a2aproject/a2a-go/v2/cmd/a2a@latest
a2a discover http://localhost:8000
a2a send http://localhost:8000 "Summarize https://go.dev/blog/go1.24"
```
DocsClaw exposes an OpenAI-compatible `/v1/chat/completions`
endpoint, so any chat client that speaks that protocol works out of
the box:
| Client | Type | Notes |
| ------ | ---- | ----- |
| [Open WebUI](https://github.com/open-webui/open-webui) | Web | Full-featured; recommended for teams |
| [Chatbox](https://chatboxai.app) | Desktop | macOS/Windows/Linux; simple setup |
| [oterm](https://github.com/ggozad/oterm) | Terminal | Lightweight TUI |
| [aichat](https://github.com/sigoden/aichat) | Terminal | Multi-model CLI with streaming |
Point the client at `http://<agent-host>:8000/v1` as the base URL
and use any string as the API key (DocsClaw doesn't require one).
The agent uses `web_fetch` to retrieve pages, `exec` to run
commands, and other tools as configured. Replace the text with any
task — the agent decides which tools to call.
## How it works
DocsClaw reads its personality from a config directory:
```text
my-agent/
├── system-prompt.txt # Who the agent is (operator)
├── agent-card.json # A2A metadata (name, skills, URL)
├── agent-config.yaml # Which tools to allow, loop settings
└── skills/ # Optional SKILL.md instructions
└── url-summary/
└── SKILL.md
/workspace/ # Optional workspace context files
├── AGENTS.md # Operating instructions
├── SOUL.md # Persona and tone
├── USER.md # User context
├── MEMORY.md # Long-term memory (read + written by `remember`)
├── IDENTITY.md # Agent name and role
└── TOOLS.md # Tool guidance
```
Same binary, different config = different agent. DocsClaw loads
workspace Markdown as project context. Use `--workspace-profile`
to choose which files are loaded:
```bash
docsclaw serve --workspace-profile openclaw # AGENTS, SOUL, USER, MEMORY, IDENTITY, TOOLS
docsclaw serve --workspace-profile hermes # AGENTS, SOUL, USER, MEMORY
```
The default `docsclaw` profile omits `MEMORY.md`. In Phase 2 mode,
the `remember` tool appends entries to `MEMORY.md`, giving agents
a simple file-based long-term memory that survives restarts.
Without `agent-config.yaml`, DocsClaw runs in **single-shot mode**
(no tools, prompt-in/response-out). With it, the **agentic loop**
is enabled and the LLM can call tools iteratively until the task
is complete.
## Deploy on OpenShift/Kubernetes
```bash
make image-push
oc new-project docsclaw
oc create secret generic llm-secret --from-literal=LLM_API_KEY=sk-...
oc apply -f deploy/standalone-agent.yaml
```
The manifest bundles ConfigMap + Deployment + Service + Route in
a single file. Secret examples for Anthropic, OpenAI, and MaaS
are in [`deploy/`](deploy/README.md). Test via the Route:
```bash
curl -X POST https://$(oc get route docsclaw -o jsonpath='{.spec.host}')/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"message/send","id":"1",
"params":{"message":{"messageId":"m1","role":"user",
"parts":[{"kind":"text","text":"Run uname -a and describe this system"}]}}}'
```
## Skills
Skills are markdown files that teach the agent specialized
behaviors. The agent discovers them at startup and can load them
on demand via the `load_skill` tool.
To add a skill, create a directory under `skills/` with a
`SKILL.md` file:
```markdown
---
name: my-skill
description: What this skill does (shown to the LLM at startup)
---
# My skill
Step-by-step instructions for the LLM to follow...
```
See `testdata/standalone/skills/` for examples (url-summary,
code-review).
### Automatic AgentCard skill injection
Discovered skills are automatically added to the AgentCard served
at `/.well-known/agent-card.json`. If a skill includes a
`skill.yaml` with richer metadata (namespace, author, tools), the
AgentCard entry gets derived tags. Static skills in
`agent-card.json` take precedence on ID conflict.
### OCI skill distribution
Skills can be packaged as OCI artifacts and distributed via
registries (quay.io, GHCR, Harbor, Zot) using the
[skillctl](https://github.com/redhat-et/skillimage) CLI. On
OpenShift 4.20+ they mount directly as image volumes — no init
container needed.
```bash
skillctl pack examples/skills/resume-screener
skillctl push --as-image examples/skills/resume-screener \
quay.io/myorg/skill-resume-screener:1.0.0
skillctl inspect quay.io/myorg/skill-resume-screener:1.0.0
```
See the [OCI skills guide](docs/oci-skills-guide.md) for the full
workflow including signing, verification, and deployment manifests.
## Configuration
### system-prompt.txt
The agent's personality. Plain text, no special format.
### agent-card.json
A2A protocol metadata — name, description, capabilities.
Discovered skills are merged into the card at startup; you only
need to hardcode skills here if you want static entries that
override discovery. See the
[A2A spec](https://google.github.io/A2A/) for the full schema.
### agent-config.yaml
Controls which tools are available and loop behavior:
```yaml
tools:
allowed: # Only these tools are available to the LLM
- exec
- web_fetch
- read_file
- write_file
exec:
timeout: 30 # Max seconds per command
max_output: 50000 # Truncate output after this many chars
web_fetch:
allowed_hosts: # SSRF protection (empty = all allowed)
- go.dev
- kubernetes.io
workspace: /workspace # read_file/write_file root
loop:
max_iterations: 10 # Max tool-use rounds before giving up
```
### Workspace context (OpenClaw compatibility)
DocsClaw loads OpenClaw-compatible workspace files from the
workspace directory (`/workspace` by default) and appends them to
the system prompt as a `## Project Context` section. The operator
defines the agent's job via `system-prompt.txt`; the user provides
project context via workspace files. All files are optional and
work in both single-shot and agentic modes.
Files are loaded in order: AGENTS.md, SOUL.md, USER.md,
IDENTITY.md, TOOLS.md. Each file is capped at 20,000 characters;
total context is capped at 60,000 characters. See the
[agent config guide](docs/agent-config-guide.md#workspace-context)
for details and deployment examples.
### Built-in tools
| Tool | Description |
| ------------ | ---------------------------------------------------- |
| `exec` | Run shell commands (dangerous commands blocked) |
| `web_fetch` | HTTP GET with SSRF protection |
| `read_file` | Read files (workspace-restricted) |
| `write_file` | Write files (workspace-restricted) |
| `rag_search` | Semantic search over a vector database (see below) |
| `load_skill` | Load a SKILL.md (always available when skills exist) |
### RAG search
Add an optional `rag:` block to `agent-config.yaml` to let the
agent retrieve context from a vector database before answering:
```yaml
rag:
backend: weaviate
url: http://weaviate:8080
collection: Docs
```
The `rag_search` tool auto-registers when `rag:` is present (no
`tools.allowed` entry needed). Uses raw HTTP/GraphQL — zero new
dependencies. The `rag.Client` interface supports adding more
backends (Milvus, pgvector) as drop-in connectors. See
[`testdata/rag/README.md`](testdata/rag/README.md) for local
testing with Weaviate and Ollama.
### LLM providers
Set via environment variables:
| Variable | Description |
| ------------------------------------ | ------------------------------------------ |
| `LLM_API_KEY` or `ANTHROPIC_API_KEY` | API key |
| `LLM_PROVIDER` | `anthropic` (default), `openai`, `litellm` |
| `LLM_MODEL` | Model name (default: `claude-sonnet-4-6`) |
| `LLM_BASE_URL` | Base URL for OpenAI-compatible APIs |
## Architecture
```text
┌───────────────────────────────────────────────────┐
│ docsclaw serve │
│ │
│ ┌──────────┐ ┌───────────────┐ ┌────────────┐ │
│ │ A2A │ │ OpenAI-compat │ │ │ │
│ │ /a2a │──│ /v1/chat/ │──│ LLM │ │
│ │ │ │ completions │ │ Provider │ │
│ └──────────┘ └───────┬───────┘ └────────────┘ │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ exec │ │ web │ │ read │ ... │
│ │ │ │ fetch │ │ file │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ Config: system-prompt.txt + agent-config.yaml │
└───────────────────────────────────────────────────┘
```
## Custom agent images
Build purpose-built agent images with a declarative manifest that
specifies OS tools, system prompt, skills, and secrets:
```bash
# Validate
docsclaw build --manifest agent-manifest.yaml --dry-run
# Build and push
make agent-push MANIFEST=agent-manifest.yaml TAG=ghcr.io/org/my-agent:1.0.0
# Deploy
export LLM_API_KEY=sk-...
docsclaw deploy --manifest agent-manifest.yaml | oc apply -f -
```
See the [agent manifest guide](docs/agent-manifest-guide.md) for
the full workflow, tool catalog, and security tiers.
## Build
```bash
make build # Build binary to bin/docsclaw
make test # Run tests
make lint # Run golangci-lint
make image # Build container image (local dev)
make image-push # Build and push to GHCR
```
Override defaults:
```bash
make image REGISTRY=my-registry.io/docsclaw DEV_TAG=v1.0
make image CONTAINER_ENGINE=docker
```
## Release
Releases are automated via [GoReleaser](https://goreleaser.com/).
Tag a commit and push to trigger the workflow:
```bash
git tag v0.1.0
git push --tags
```
This produces:
- Binaries for linux/darwin/windows (amd64, arm64)
- Multi-arch container images on `ghcr.io/redhat-et/docsclaw`
- Homebrew formula (`brew install pavelanni/tap/docsclaw`)
- SHA256 checksums
## License
Apache License 2.0