summaryrefslogtreecommitdiff
path: root/AI_AGENT_ACCESS.md
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-12 09:27:16 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-12 09:27:16 -1000
commit9fe0998436488537a8a2e8ffeefb0c4424b41c60 (patch)
treece877f04e60a187c2bd0e481e80298ec5e7cdf80 /AI_AGENT_ACCESS.md
Initial commit: Personal Consolidation Dashboard (Phase 1 Complete)
Implemented a unified web dashboard aggregating tasks, notes, and meal planning: Core Features: - Trello integration (PRIMARY feature - boards, cards, lists) - Todoist integration (tasks and projects) - Obsidian integration (20 most recent notes) - PlanToEat integration (optional - 7-day meal planning) - Mobile-responsive web UI with auto-refresh (5 min) - SQLite caching with 5-minute TTL - AI agent endpoint with Bearer token authentication Technical Implementation: - Go 1.21+ backend with chi router - Interface-based API client design for testability - Parallel data fetching with goroutines - Graceful degradation (partial data on API failures) - .env file loading with godotenv - Comprehensive test coverage (9/9 tests passing) Bug Fixes: - Fixed .env file not being loaded at startup - Fixed nil pointer dereference with optional API clients (typed nil interface gotcha) Documentation: - START_HERE.md - Quick 5-minute setup guide - QUICKSTART.md - Fast track setup - SETUP_GUIDE.md - Detailed step-by-step instructions - PROJECT_SUMMARY.md - Complete project overview - CLAUDE.md - Guide for Claude Code instances - AI_AGENT_ACCESS.md - AI agent design document - AI_AGENT_SETUP.md - Claude.ai integration guide - TRELLO_AUTH_UPDATE.md - New Power-Up auth process Statistics: - Binary: 17MB - Code: 2,667 lines - Tests: 5 unit + 4 acceptance tests (all passing) - Dependencies: chi, sqlite3, godotenv Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'AI_AGENT_ACCESS.md')
-rw-r--r--AI_AGENT_ACCESS.md340
1 files changed, 340 insertions, 0 deletions
diff --git a/AI_AGENT_ACCESS.md b/AI_AGENT_ACCESS.md
new file mode 100644
index 0000000..40fe6a1
--- /dev/null
+++ b/AI_AGENT_ACCESS.md
@@ -0,0 +1,340 @@
+# AI Agent Access Design
+
+## Overview
+Add a dedicated API endpoint optimized for AI agents (like Claude) to access dashboard data in a structured, machine-readable format with simple authentication.
+
+## Authentication Strategy
+
+### API Key-Based Auth
+Simple bearer token authentication that's easy to use from Claude.ai:
+- Generate a long, random API key for AI access
+- Store in environment variable: `AI_AGENT_API_KEY`
+- Use standard `Authorization: Bearer {key}` header
+- No complex OAuth flows or JWT rotation
+
+### Why This Approach?
+1. **Simple for Claude**: Just include auth header in requests
+2. **Stateless**: No session management needed
+3. **Revocable**: Change key in .env to revoke access
+4. **Standard**: Uses common HTTP authorization pattern
+
+## API Endpoints
+
+### GET /api/ai/dashboard
+Returns complete dashboard data in AI-optimized JSON format.
+
+**Request:**
+```http
+GET /api/ai/dashboard HTTP/1.1
+Host: localhost:8080
+Authorization: Bearer {AI_AGENT_API_KEY}
+```
+
+**Response (Success - 200 OK):**
+```json
+{
+ "status": "success",
+ "timestamp": "2026-01-09T14:30:00Z",
+ "data": {
+ "trello_boards": [
+ {
+ "id": "board123",
+ "name": "Work Projects",
+ "cards": [
+ {
+ "id": "card1",
+ "name": "Complete project proposal",
+ "list": "In Progress",
+ "due_date": "2026-01-15",
+ "url": "https://trello.com/c/card1"
+ }
+ ]
+ }
+ ],
+ "todoist_tasks": [
+ {
+ "id": "task1",
+ "content": "Buy groceries",
+ "project": "Personal",
+ "priority": 1,
+ "due_date": "2026-01-10",
+ "completed": false,
+ "labels": ["shopping"],
+ "url": "https://todoist.com/task/1"
+ }
+ ],
+ "obsidian_notes": [
+ {
+ "filename": "meeting-notes.md",
+ "title": "Team Meeting Notes",
+ "content_preview": "Discussed Q1 goals...",
+ "modified": "2026-01-09T10:00:00Z",
+ "tags": ["meetings", "work"]
+ }
+ ],
+ "plantoeat_meals": [
+ {
+ "id": "meal1",
+ "recipe": "Spaghetti Carbonara",
+ "date": "2026-01-09",
+ "meal_type": "dinner",
+ "url": "https://plantoeat.com/recipe/123"
+ }
+ ]
+ },
+ "metadata": {
+ "cache_age_seconds": 45,
+ "sources_refreshed": ["trello", "todoist"],
+ "errors": []
+ }
+}
+```
+
+**Response (Unauthorized - 401):**
+```json
+{
+ "status": "error",
+ "error": "Invalid or missing API key",
+ "hint": "Include 'Authorization: Bearer {key}' header"
+}
+```
+
+### GET /api/ai/boards/{board_id}/cards
+Get cards from a specific Trello board.
+
+**Request:**
+```http
+GET /api/ai/boards/board123/cards HTTP/1.1
+Authorization: Bearer {AI_AGENT_API_KEY}
+```
+
+### GET /api/ai/tasks?filter={filter}
+Get filtered tasks (today, week, overdue, completed).
+
+**Request:**
+```http
+GET /api/ai/tasks?filter=today HTTP/1.1
+Authorization: Bearer {AI_AGENT_API_KEY}
+```
+
+### POST /api/ai/task
+Create a new Todoist task (Phase 2).
+
+**Request:**
+```http
+POST /api/ai/task HTTP/1.1
+Authorization: Bearer {AI_AGENT_API_KEY}
+Content-Type: application/json
+
+{
+ "content": "Buy milk",
+ "project": "Personal",
+ "due_date": "2026-01-10",
+ "priority": 1,
+ "labels": ["shopping"]
+}
+```
+
+## AI-Optimized Response Format
+
+### Design Principles
+1. **Flat structure**: Avoid deep nesting where possible
+2. **Explicit field names**: Use `due_date` not `dueDate` for clarity
+3. **Include metadata**: Provide context about data freshness
+4. **Clear errors**: Return actionable error messages
+5. **Consistent types**: Always use ISO 8601 for dates
+
+### Example Claude.ai Usage
+```markdown
+User: "What tasks do I have due today?"
+
+Claude uses MCP tool or API call:
+GET /api/ai/tasks?filter=today
+Authorization: Bearer sk-ai-1234567890abcdef
+
+Claude response: "You have 3 tasks due today:
+1. Buy groceries (Personal, Priority 1)
+2. Complete project proposal (Work, Priority 2)
+3. Call dentist (Health, Priority 3)"
+```
+
+## Implementation
+
+### Middleware: Auth Check
+```go
+// internal/middleware/ai_auth.go
+func AIAuthMiddleware(apiKey string) func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ authHeader := r.Header.Get("Authorization")
+
+ if !strings.HasPrefix(authHeader, "Bearer ") {
+ respondJSON(w, 401, map[string]string{
+ "status": "error",
+ "error": "Missing or invalid Authorization header",
+ "hint": "Include 'Authorization: Bearer {key}' header",
+ })
+ return
+ }
+
+ token := strings.TrimPrefix(authHeader, "Bearer ")
+ if token != apiKey {
+ respondJSON(w, 401, map[string]string{
+ "status": "error",
+ "error": "Invalid API key",
+ })
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+ }
+}
+```
+
+### Handler: AI Dashboard
+```go
+// internal/handlers/ai_handlers.go
+func (h *Handler) HandleAIDashboard(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ // Fetch all data
+ data, err := h.fetchDashboardData(ctx, false)
+ if err != nil {
+ respondJSON(w, 500, map[string]interface{}{
+ "status": "error",
+ "error": err.Error(),
+ })
+ return
+ }
+
+ // Format for AI consumption
+ response := map[string]interface{}{
+ "status": "success",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "data": map[string]interface{}{
+ "trello_boards": formatBoardsForAI(data.Boards),
+ "todoist_tasks": formatTasksForAI(data.Tasks),
+ "obsidian_notes": formatNotesForAI(data.Notes),
+ "plantoeat_meals": formatMealsForAI(data.Meals),
+ },
+ "metadata": map[string]interface{}{
+ "errors": data.Errors,
+ },
+ }
+
+ respondJSON(w, 200, response)
+}
+```
+
+### Router Setup
+```go
+// cmd/dashboard/main.go
+func main() {
+ // ... existing setup ...
+
+ // AI Agent routes (protected by auth middleware)
+ if cfg.AIAgentAPIKey != "" {
+ aiRouter := chi.NewRouter()
+ aiRouter.Use(middleware.AIAuthMiddleware(cfg.AIAgentAPIKey))
+
+ r.Route("/api/ai", func(r chi.Router) {
+ r.Use(middleware.AIAuthMiddleware(cfg.AIAgentAPIKey))
+ r.Get("/dashboard", h.HandleAIDashboard)
+ r.Get("/boards/{boardID}/cards", h.HandleAIBoardCards)
+ r.Get("/tasks", h.HandleAITasks)
+ // Phase 2: Write operations
+ // r.Post("/task", h.HandleAICreateTask)
+ })
+ }
+}
+```
+
+## Security Considerations
+
+1. **Key Storage**: Never commit API key to git
+2. **Key Rotation**: Easy to change in .env file
+3. **Rate Limiting**: Consider adding rate limits per key
+4. **HTTPS Only**: Use HTTPS in production
+5. **Logging**: Log API key usage (last 4 chars only)
+
+## Configuration
+
+### .env.example
+```bash
+# AI Agent Access
+AI_AGENT_API_KEY=sk-ai-1234567890abcdef...
+# Generate with: openssl rand -hex 32
+```
+
+### config.go
+```go
+type Config struct {
+ // ... existing fields ...
+ AIAgentAPIKey string
+}
+
+func Load() (*Config, error) {
+ cfg := &Config{
+ // ... existing fields ...
+ AIAgentAPIKey: os.Getenv("AI_AGENT_API_KEY"),
+ }
+ return cfg, nil
+}
+```
+
+## Usage from Claude.ai
+
+### With MCP (Model Context Protocol)
+```json
+{
+ "mcpServers": {
+ "dashboard": {
+ "command": "curl",
+ "args": [
+ "-H", "Authorization: Bearer ${AI_AGENT_API_KEY}",
+ "http://localhost:8080/api/ai/dashboard"
+ ]
+ }
+ }
+}
+```
+
+### Direct API Call
+Claude can make HTTP requests directly:
+```
+GET http://localhost:8080/api/ai/dashboard
+Authorization: Bearer sk-ai-1234567890abcdef
+```
+
+## Future Enhancements
+
+1. **Webhook Support**: POST updates to Claude.ai when data changes
+2. **Streaming**: Server-sent events for real-time updates
+3. **Scoped Keys**: Different keys for read vs write access
+4. **GraphQL**: More flexible querying for AI agents
+5. **Natural Language Queries**: `/api/ai/query?q=What's due today?`
+
+## Testing
+
+### Manual Test
+```bash
+# Generate API key
+export AI_KEY=$(openssl rand -hex 32)
+
+# Add to .env
+echo "AI_AGENT_API_KEY=sk-ai-$AI_KEY" >> .env
+
+# Test endpoint
+curl -H "Authorization: Bearer sk-ai-$AI_KEY" \
+ http://localhost:8080/api/ai/dashboard | jq .
+```
+
+### Unit Test
+```go
+func TestAIAuthMiddleware(t *testing.T) {
+ // Test valid key
+ // Test invalid key
+ // Test missing header
+}
+```