All agents now prefer Claude API when ANTHROPIC_API_KEY is configured, with automatic fallback to Ollama when offline or unconfigured. New src/anthropic/ module provides model selection via get_model() factory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
402 lines
17 KiB
Markdown
402 lines
17 KiB
Markdown
# Tatlock Enhancement Plan: Bidirectional Claude Integration
|
|
|
|
## Executive Summary
|
|
|
|
Implement a **bidirectional architecture** that:
|
|
1. **Superpowers Tatlock** by swapping Ollama→Claude backend (200k context, better reasoning, same butler personality)
|
|
2. **Exposes Tatlock as MCP server** for Claude instances on any device (phone, browser, desktop)
|
|
|
|
This gives you the flexibility to use whichever AI is best/most accessible at any moment.
|
|
|
|
## Key Insight: Blanket Backend Swap (Simpler Than Sidecar)
|
|
|
|
Instead of adding a Claude "Analyst" sidecar agent, **swap the underlying model for ALL agents**:
|
|
|
|
```
|
|
CURRENT: TatlockAgent → OpenAIChatModel → OllamaProvider → Ollama (mistral-nemo)
|
|
PROPOSED: TatlockAgent → AnthropicModel → AnthropicProvider → Claude API
|
|
↘ (fallback when offline) → OllamaProvider → Ollama
|
|
```
|
|
|
|
**Why this works:**
|
|
- PydanticAI natively supports Anthropic via `AnthropicModel` + `AnthropicProvider`
|
|
- The same `TATLOCK_SYSTEM_PROMPT` is passed to Claude - butler personality preserved
|
|
- Claude is **better** at following system prompts than mistral-nemo
|
|
- 200k context for ALL queries, not just "complex" ones
|
|
- Simpler architecture: no routing logic, no sidecar delegation
|
|
|
|
---
|
|
|
|
## Research Findings
|
|
|
|
### Industry Best Practices (2025-2026)
|
|
|
|
**MCP Protocol Updates** ([MCP Spec Updates June 2025](https://auth0.com/blog/mcp-specs-update-all-about-auth/)):
|
|
- Streamable HTTP replaced SSE (March 2025) - better for cloud deployment
|
|
- OAuth 2.0 required for remote servers - MCP servers are OAuth Resource Servers
|
|
- Tool Output Schemas now available - better structured data handling
|
|
- MCP Registry launched (Sept 2025) - community server discovery
|
|
|
|
**Community Patterns** ([Claude Code Router](https://github.com/musistudio/claude-code-router)):
|
|
- Task-based routing is becoming standard: route simple→local, complex→cloud
|
|
- Translation proxies bridge Anthropic Messages API ↔ OpenAI format
|
|
- Cost savings of up to 98% reported with smart routing
|
|
|
|
**Home Automation MCP** ([ha-mcp](https://github.com/homeassistant-ai/ha-mcp)):
|
|
- Production-ready MCP servers exist for Home Assistant
|
|
- Support Claude Code, Gemini CLI, Open WebUI, VSCode, Cursor
|
|
- Pattern: expose local tools securely to remote AI clients
|
|
|
|
**Remote MCP Access** ([mcp-remote](https://www.npmjs.com/package/mcp-remote)):
|
|
- Bridge local MCP servers to Claude Desktop/Browser via proxy
|
|
- Supports authentication headers for security
|
|
- Works with ngrok/Cloudflare Tunnel for HTTPS
|
|
|
|
---
|
|
|
|
## Recommended Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ BIDIRECTIONAL TATLOCK-CLAUDE ARCHITECTURE │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ╔═══════════════════════════════════════════════════════════════════════════════╗ │
|
|
│ ║ SCENARIO A: Using Tatlock (Open WebUI, local apps) ║ │
|
|
│ ║ ───────────────────────────────────────────────── ║ │
|
|
│ ║ ║ │
|
|
│ ║ Request → Steward → Tatlock → Tools + Expert Delegation ║ │
|
|
│ ║ │ ║ │
|
|
│ ║ ├─→ Librarian (Claude) → research, wiki, RAG ║ │
|
|
│ ║ ├─→ Biographer (Claude) → memory, preferences ║ │
|
|
│ ║ ├─→ Housekeeper (Claude) → home automation ║ │
|
|
│ ║ └─→ All powered by Claude with Ollama fallback ║ │
|
|
│ ║ ║ │
|
|
│ ║ Butler personality preserved, 200k context for all queries ║ │
|
|
│ ╚═══════════════════════════════════════════════════════════════════════════════╝ │
|
|
│ │
|
|
│ ╔═══════════════════════════════════════════════════════════════════════════════╗ │
|
|
│ ║ SCENARIO B: Using Claude.ai / Claude Desktop / Phone ║ │
|
|
│ ║ ──────────────────────────────────────────────────── ║ │
|
|
│ ║ ║ │
|
|
│ ║ Claude ──[MCP over HTTPS]──► Tatlock MCP Server → Household Tools ║ │
|
|
│ ║ │ ║ │
|
|
│ ║ ├─→ calculator, datetime ║ │
|
|
│ ║ ├─→ web_search, wiki_search ║ │
|
|
│ ║ ├─→ hybrid_search (RAG) ║ │
|
|
│ ║ ├─→ memory_recall, store_insight ║ │
|
|
│ ║ └─→ home_control (lights, climate) ║ │
|
|
│ ║ ║ │
|
|
│ ║ Full 200k context, your local tools accessible from anywhere ║ │
|
|
│ ╚═══════════════════════════════════════════════════════════════════════════════╝ │
|
|
│ │
|
|
│ ╔═══════════════════════════════════════════════════════════════════════════════╗ │
|
|
│ ║ SCENARIO C: Offline (internet down) ║ │
|
|
│ ║ ────────────────────────────────── ║ │
|
|
│ ║ ║ │
|
|
│ ║ Tatlock operates fully locally with Ollama ║ │
|
|
│ ║ • All tools work (except web search) ║ │
|
|
│ ║ • Graceful degradation with same butler personality ║ │
|
|
│ ╚═══════════════════════════════════════════════════════════════════════════════╝ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: Blanket Backend Swap (Claude for All Agents)
|
|
|
|
Replace Ollama with Claude as the default backend for all PydanticAI agents, with automatic offline fallback.
|
|
|
|
**New Files:**
|
|
```
|
|
src/anthropic/
|
|
├── __init__.py
|
|
├── provider.py # Claude provider with health check
|
|
└── model_selector.py # Chooses Claude or Ollama based on availability
|
|
```
|
|
|
|
**Key Implementation (`src/anthropic/provider.py`):**
|
|
```python
|
|
from pydantic_ai.models.anthropic import AnthropicModel
|
|
from pydantic_ai.providers.anthropic import AnthropicProvider
|
|
from pydantic_ai.models.openai import OpenAIChatModel
|
|
from src.ollama.provider import get_ollama_provider
|
|
from src.core.config import config
|
|
|
|
_anthropic_available: bool | None = None
|
|
|
|
async def check_anthropic_health() -> bool:
|
|
"""Check if Anthropic API is reachable."""
|
|
global _anthropic_available
|
|
try:
|
|
from anthropic import AsyncAnthropic
|
|
client = AsyncAnthropic(api_key=config.ANTHROPIC_API_KEY)
|
|
await client.messages.create(
|
|
model=config.ANTHROPIC_MODEL,
|
|
max_tokens=1,
|
|
messages=[{"role": "user", "content": "hi"}]
|
|
)
|
|
_anthropic_available = True
|
|
except Exception:
|
|
_anthropic_available = False
|
|
return _anthropic_available
|
|
|
|
def get_model(prefer_cloud: bool = True):
|
|
"""Get the best available model. Returns Claude if available, otherwise Ollama."""
|
|
if prefer_cloud and config.ANTHROPIC_API_KEY and _anthropic_available:
|
|
provider = AnthropicProvider(api_key=config.ANTHROPIC_API_KEY)
|
|
return AnthropicModel(
|
|
model_name=config.ANTHROPIC_MODEL,
|
|
provider=provider,
|
|
)
|
|
else:
|
|
return OpenAIChatModel(
|
|
model_name=config.OLLAMA_DEFAULT_MODEL,
|
|
provider=get_ollama_provider()
|
|
)
|
|
```
|
|
|
|
**Modify TatlockAgent (`src/agents/tatlock.py`):**
|
|
```python
|
|
def _ensure_agent(self):
|
|
if self._agent is not None:
|
|
return
|
|
|
|
from src.anthropic.model_selector import get_model
|
|
|
|
model = get_model(prefer_cloud=True)
|
|
|
|
self._agent = Agent(
|
|
model,
|
|
system_prompt=TATLOCK_SYSTEM_PROMPT, # Same butler personality!
|
|
)
|
|
self._register_tools()
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 2: MCP Server (Expose Tools to Claude)
|
|
|
|
Create an MCP server that exposes Tatlock's household tools to external Claude instances.
|
|
|
|
**New Files:**
|
|
```
|
|
src/mcp/
|
|
├── __init__.py
|
|
├── server.py # MCP server using mcp Python SDK
|
|
├── tool_adapters.py # Convert PydanticAI tools → MCP schemas
|
|
├── auth.py # API key authentication
|
|
└── transport.py # Streamable HTTP transport
|
|
```
|
|
|
|
**Docker Stack Addition (`stacks/agents.yml`):**
|
|
```yaml
|
|
tatlock-mcp:
|
|
image: git.schweitz.internal/jpmschweitzer/tatlock:latest
|
|
command: ["python", "-m", "src.mcp.server"]
|
|
ports:
|
|
- "8002:8002"
|
|
environment:
|
|
- MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
|
|
networks:
|
|
- docker-dataplane
|
|
```
|
|
|
|
**Claude Desktop Configuration:**
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"tatlock": {
|
|
"command": "npx",
|
|
"args": ["mcp-remote", "https://mcp.schweitz.net/sse", "--header", "Authorization: Bearer ${MCP_AUTH_TOKEN}"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Files to Modify
|
|
|
|
### Phase 1 - Backend Swap
|
|
|
|
**New Files:**
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `src/anthropic/__init__.py` | Package init |
|
|
| `src/anthropic/provider.py` | Claude provider with health check |
|
|
| `src/anthropic/model_selector.py` | Choose Claude or Ollama based on availability |
|
|
|
|
**Modified Files:**
|
|
| File | Changes |
|
|
|------|---------|
|
|
| `src/core/config.py` | Add `ANTHROPIC_API_KEY`, `ANTHROPIC_MODEL`, `PREFER_CLOUD_BACKEND` |
|
|
| `src/agents/tatlock.py` | Use `get_model()` instead of hardcoded Ollama |
|
|
| `src/agents/librarian/agent.py` | Use `get_model()` instead of hardcoded Ollama |
|
|
| `src/agents/biographer/agent.py` | Use `get_model()` instead of hardcoded Ollama |
|
|
| `src/agents/steward/agent.py` | Convert to PydanticAI or add Anthropic API support |
|
|
| `src/core/startup.py` | Add Anthropic health check on startup |
|
|
| `requirements.txt` | Add `anthropic>=0.40.0` |
|
|
| `.env.example` | Document new environment variables |
|
|
|
|
### Phase 2 - MCP Server
|
|
|
|
**New Files:**
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `src/mcp/__init__.py` | Package init |
|
|
| `src/mcp/server.py` | MCP server implementation |
|
|
| `src/mcp/tool_adapters.py` | PydanticAI → MCP schema conversion |
|
|
| `src/mcp/auth.py` | Token-based authentication |
|
|
|
|
---
|
|
|
|
## Cost Analysis
|
|
|
|
- **Claude API**: $5-30/month (10-50 calls/day, ~2k input + 1k output tokens/call)
|
|
- **MCP via Claude Pro**: Included in subscription
|
|
- **Total**: ~$10-80/month for full bidirectional integration
|
|
|
|
---
|
|
|
|
## Verification Plan
|
|
|
|
### Phase 1 Testing
|
|
```bash
|
|
# 1. Run with Claude backend
|
|
ANTHROPIC_API_KEY=your-key docker-compose up -d tatlock
|
|
|
|
# 2. Verify Claude is being used
|
|
docker logs tatlock 2>&1 | grep -i "anthropic\|claude"
|
|
|
|
# 3. Test butler personality
|
|
curl -X POST http://tatlock.schweitz.internal:8000/v1/responses \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"model": "Tatlock", "input": "Hello, who are you?"}'
|
|
|
|
# 4. Test offline fallback
|
|
ANTHROPIC_API_KEY="" docker-compose up -d tatlock
|
|
docker logs tatlock 2>&1 | grep -i "ollama\|fallback"
|
|
```
|
|
|
|
### Phase 2 Testing
|
|
```bash
|
|
# 1. Start MCP server
|
|
docker-compose up -d tatlock-mcp
|
|
|
|
# 2. Test MCP endpoint
|
|
curl -X POST https://mcp.schweitz.net/tools/list \
|
|
-H "Authorization: Bearer $MCP_AUTH_TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Priority
|
|
|
|
1. **Phase 1: Backend Swap** (~1 week)
|
|
- Immediate value: 200k context for ALL queries
|
|
- Low risk: provider abstraction, graceful offline fallback
|
|
|
|
2. **Phase 2: MCP Server** (~2-3 weeks)
|
|
- Enables cross-device access
|
|
- Bidirectional: Tatlock superpowered by Claude AND accessible to Claude
|
|
|
|
---
|
|
|
|
## Future Phases (Optional)
|
|
|
|
- **Phase 3: LiteLLM Gateway** - Unified endpoint for all models, config-driven routing
|
|
- **Phase 4: Multi-Provider** - Add OpenAI, Vertex AI, etc.
|
|
- **Phase 5: Smart Routing** - Context-aware model selection, cost ceiling enforcement
|
|
|
|
---
|
|
|
|
## Offline Behavior
|
|
|
|
| Scenario | Behavior |
|
|
|----------|----------|
|
|
| No API key | Use Ollama exclusively |
|
|
| API unreachable | Use Ollama, log warning |
|
|
| API rate limited | Fallback to Ollama |
|
|
|
|
| Aspect | Claude | Ollama |
|
|
|--------|--------|--------|
|
|
| Context | 200k tokens | ~8k tokens |
|
|
| Latency | 1-3s (network) | 0.5-1s (local) |
|
|
| Personality | Preserved | Preserved |
|
|
| Tools | All work | All work |
|
|
| Cost | API charges | Free |
|
|
|
|
---
|
|
|
|
## Implementation Status
|
|
|
|
### Phase 1: Backend Swap - CODE COMPLETE (awaiting API access)
|
|
|
|
- [x] Add Anthropic config settings to `src/core/config.py`
|
|
- [x] Add `pydantic-ai-slim[openai,anthropic]` to requirements.txt
|
|
- [x] Create `src/anthropic/` module (model_selector.py)
|
|
- [x] Add Claude health check to startup.py
|
|
- [x] Refactor all PydanticAI agents to use `get_model()`
|
|
- [x] Librarian
|
|
- [x] Biographer
|
|
- [x] Housekeeper
|
|
- [x] Tatlock (6 locations)
|
|
- [x] Add Claude API path to Steward agent (direct API calls)
|
|
- [x] Update `.env.example` with new variables
|
|
- [x] Test Ollama fallback (working)
|
|
- [ ] Test with Claude API key (blocked: no API access currently)
|
|
|
|
**Note:** Implementation complete. Currently runs in Ollama-only mode. Will automatically use Claude when `ANTHROPIC_API_KEY` is configured.
|
|
|
|
### Phase 2: MCP Server - NOT STARTED
|
|
|
|
- [ ] Create `src/mcp/` module
|
|
- [ ] Tool adapters (PydanticAI → MCP schema)
|
|
- [ ] Authentication middleware
|
|
- [ ] Streamable HTTP transport
|
|
- [ ] Docker stack configuration
|
|
|
|
---
|
|
|
|
## Related Repository Handovers
|
|
|
|
Handover documents created in each repo: `PROJECT_CLAUDIFICATION_HANDOVER.md`
|
|
|
|
### library-desk - HANDOVER CREATED
|
|
|
|
- [x] Write handover document
|
|
- [ ] Review HybridRAG response size limits
|
|
- [ ] Review smart_create endpoint for Claude optimization
|
|
- [ ] Evaluate response formats for LLM consumption
|
|
|
|
### core-api - HANDOVER CREATED
|
|
|
|
- [x] Write handover document
|
|
- [ ] Review list_devices response format
|
|
- [ ] Review error messages for LLM consumption
|
|
- [ ] Evaluate rate limiting for faster Claude processing
|
|
|
|
### portainer-core - HANDOVER CREATED (blocking for production)
|
|
|
|
- [x] Write handover document
|
|
- [ ] Update stack with new environment variables
|
|
- [ ] Configure secrets management for API key
|
|
- [ ] Update CONTAINERS.md documentation
|
|
|
|
### webber - HANDOVER CREATED
|
|
|
|
- [x] Write handover document
|
|
- [ ] Review content truncation limits
|
|
- [ ] Evaluate extraction quality for LLM consumption
|
|
|
|
### tatlock-ui - HANDOVER CREATED
|
|
|
|
- [x] Write handover document
|
|
- [ ] Test streaming responses with Claude backend
|
|
- [ ] Test conversation history with larger context
|
|
- [ ] Verify tool call display and reasoning rendering
|