---
id: 3
title: "Claude Skill: API vs Filesystem for Task Operations"
tags: [skill, api, dx, claude]
created: 2026-02-10
updated: 2026-02-10
revision: 1
---

## Problem

The mdboard Claude skill (`SKILL.md`) instructs Claude to manage tasks by directly manipulating the filesystem:

```
3. Move task to in-progress: `mv tasks/todo/XXX.md tasks/in-progress/`
6. When complete: add `completed: YYYY-MM-DD` to frontmatter, then `mv tasks/in-progress/XXX.md tasks/done/`
7. If you discover bugs or new work, create task files in `tasks/backlog/`
```

Meanwhile, the mdboard server exposes a full REST API that handles all of these operations with proper validation, auto-incrementing IDs, frontmatter management, and consistent formatting. Claude never uses this API because the skill doesn't mention it.

---

## Current Skill Workflow (Filesystem)

The skill tells Claude to:

1. **Scan** directories with glob/grep to find assigned tasks
2. **Move** tasks via `mv tasks/todo/XXX.md tasks/in-progress/`
3. **Edit** frontmatter directly in the markdown file (check off criteria, add `completed` date)
4. **Create** tasks by writing new files with manually constructed frontmatter
5. **Move to done** via `mv tasks/in-progress/XXX.md tasks/done/`

### Problems with this approach

| Issue | Detail |
|-------|--------|
| **No ID management** | Claude must scan all columns to find the next available ID. The API does this automatically via `auto_increment_id` in config. |
| **Fragile frontmatter** | Claude hand-writes YAML frontmatter. Misformatted YAML, wrong key order, or missing fields can break the board. The API uses `build_frontmatter()` which enforces consistent key order and formatting. |
| **No validation** | Moving a file to a non-existent column silently creates a broken state. The API validates column names. |
| **No `updated` timestamp** | Claude's skill doesn't mention updating the `updated` field on edits. The API handles this automatically. |
| **Comments are invisible** | The skill never mentions comments (`tasks/comments/{task_id}/`). The API has full comment CRUD. |
| **Race conditions** | If the web UI and Claude both edit a task file simultaneously, changes can conflict. The API provides a single point of mutation. |
| **Prompts/documents ignored** | The skill only covers tasks. The API also handles prompts and documents with revision tracking. |

---

## Available API Endpoints

The mdboard server (default `http://localhost:8080`) exposes:

### Tasks
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/board` | Full board state (all columns + tasks) |
| POST | `/api/task` | Create task (auto-assigns ID, builds frontmatter) |
| GET | `/api/task/{column}/{filename}` | Read a specific task |
| PUT | `/api/task/{column}/{filename}` | Update task (meta fields or full content) |
| PATCH | `/api/task/move` | Move task between columns |
| DELETE | `/api/task/{column}/{filename}` | Delete task |

### Comments
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/comments/{task_id}` | List comments on a task |
| POST | `/api/comments/{task_id}` | Add comment |
| DELETE | `/api/comments/{task_id}/{filename}` | Delete comment |

### Prompts & Documents
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/{type}` | List all resources |
| POST | `/api/{type}` | Create (auto revision tracking) |
| GET | `/api/{type}/{dir_name}` | Read specific resource |
| PUT | `/api/{type}/{dir_name}` | Update (auto-increments revision) |
| DELETE | `/api/{type}/{dir_name}` | Delete resource + all revisions |
| GET | `/api/{type}/{dir_name}/revisions` | List revision history |
| GET | `/api/{type}/{dir_name}/revisions/{rev}` | Read specific revision |

### Utility
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/config` | Board configuration |
| GET | `/api/poll` | State hashes for change detection |
| GET | `/api/activity` | Recent changes across all resource types |

---

## API vs Filesystem: Comparison

### Example: Move a task to in-progress

**Filesystem (current):**
```bash
mv tasks/todo/005-my-task.md tasks/in-progress/005-my-task.md
```

**API:**
```bash
curl -X PATCH http://localhost:8080/api/task/move \
  -d '{"filename": "005-my-task.md", "from_column": "todo", "to_column": "in-progress"}'
```

### Example: Create a new task

**Filesystem (current):**
Claude must: scan all columns for highest ID, increment it, construct YAML frontmatter, write the file with correct zero-padded filename.

**API:**
```bash
curl -X POST http://localhost:8080/api/task \
  -d '{"title": "Fix login bug", "assignee": "claude", "tags": ["bug"], "description": "..."}'
```
The API handles ID assignment, filename generation, frontmatter formatting, and default column placement.

### Example: Mark task complete

**Filesystem (current):**
Claude must: edit the file to add `completed: 2026-02-10`, then `mv` the file.

**API:**
```bash
curl -X PUT http://localhost:8080/api/task/in-progress/005-my-task.md \
  -d '{"completed": "2026-02-10"}'
curl -X PATCH http://localhost:8080/api/task/move \
  -d '{"filename": "005-my-task.md", "from_column": "in-progress", "to_column": "done"}'
```

---

## Trade-offs

### Arguments for API

- **Correctness**: API enforces validation, consistent formatting, auto-incrementing IDs
- **Feature completeness**: Comments, prompts, documents, revisions are all accessible
- **Consistency**: Single source of truth for how mutations happen, shared with the web UI
- **Simpler skill**: Claude doesn't need to know frontmatter format, directory structure, or ID management
- **Activity feed**: API-driven changes show up in the activity feed consistently

### Arguments for Filesystem

- **No server dependency**: Filesystem works whether or not `uvx mdboard` is running
- **Git-native**: File changes are directly visible in `git diff` and can be committed alongside code
- **Simpler tooling**: Claude already has file read/write/move tools; `curl` is an extra step
- **Offline**: No HTTP server needed for basic task management

### Hybrid argument

The strongest case is a **hybrid approach**: use the API when the server is running, fall back to filesystem when it's not. This preserves the zero-dependency ethos while gaining API benefits when available.

---

## Recommendation

### Update the skill to prefer API, with filesystem fallback

The revised skill should instruct Claude to:

1. **Check for a running server** by reading `.mdboard.port` (see document 002) or attempting a `GET /api/config`
2. **If server is available**: use the API for all task operations (create, move, update, comment)
3. **If server is unavailable**: fall back to direct filesystem operations (current behavior)
4. **Always read files directly** for scanning assigned tasks (faster than API for bulk reads, and doesn't require the server)

### Specific skill changes

```markdown
### Working with the Board

#### Port Discovery
If `.mdboard.port` exists in the project root, read it to find the running server port.
Otherwise, assume `http://localhost:8080`.

#### Preferred: API Operations (when server is running)
- Create task: `POST /api/task` with {title, assignee, tags, description}
- Move task: `PATCH /api/task/move` with {filename, from_column, to_column}
- Update task: `PUT /api/task/{column}/{filename}` with field updates
- Add comment: `POST /api/comments/{task_id}` with {author: "claude", body}
- Create document: `POST /api/documents` with {title, tags, body}

#### Fallback: Direct Filesystem (when server is not running)
- Move: `mv tasks/{from}/{file} tasks/{to}/{file}`
- Create: write file to `tasks/{column}/{id:03d}-{slug}.md`
- (current skill instructions)
```

### Additional benefits after change

- Claude can **add comments** on tasks (not possible with current skill)
- Claude can **create and update documents/prompts** with proper revision tracking
- Claude can **check activity feed** to understand recent project changes
- ID conflicts from manual ID assignment disappear entirely

---

## Implementation Scope

| Change | Effort |
|--------|--------|
| Update `SKILL.md` template in `init.py` | Small |
| Update `CLAUDE.md` instructions | Small |
| Add API usage examples to skill | Small |
| Implement port discovery (see document 002) | Medium (prerequisite) |
| Test API-based workflow end-to-end | Small |

Port discovery (document 002) is a soft prerequisite: without it, Claude must assume port 8080, which works for single-instance setups but breaks with multiple projects.
