Skip to main content
Glama
mshowaib

Jotty MCP Server

by mshowaib
README.md
# Jotty MCP Server

Model Context Protocol (MCP) server for [Jotty](https://jotty.showaib.com) — provides programmatic access to notes, checklists, tasks (Kanban boards), and categories.

## Features

- **Notes**: Create, read, update, delete markdown notes
- **Checklists**: Full checklist management with nested items, priorities, timestamps
- **Tasks/Kanban**: Kanban-style task boards with custom columns and item status management
- **Search**: Full-text search across notes and checklists
- **Multi-user**: API key passed per tool call (not env var) — different users can share the same MCP instance

## Setup

### Local

```bash
pip install -r requirements.txt
```

**Stdio mode** (default — for local editors/tools like Claude Desktop, Cursor):

```bash
JOTTY_BASE_URL=https://jotty.showaib.com python -m src.main
```

**HTTP mode** (for remote MCP clients):

```bash
JOTTY_BASE_URL=https://jotty.showaib.com PORT=3008 python -m src.main --transport http
```

### Docker

```bash
# Stdio mode (default — local CLI)
docker compose up --build -d jotty-mcp

# HTTP mode (remote clients)
MCP_TRANSPORT=http docker compose up --build -d jotty-mcp
```

Server is available at `http://localhost:3008/mcp` when using HTTP transport. Connect with any MCP HTTP client.

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `JOTTY_BASE_URL` | `http://localhost:3000` | URL of your Jotty instance |
| `MCP_TRANSPORT` | `stdio` | Transport mode (`stdio` or `http`) |
| `PORT` | `3008` | HTTP server port (docker-compose uses host networking) |

> **Note**: The API key is **not** an environment variable — it is passed per-tool call to support multiple users.

## MCP Tools

### Notes

| Tool | Description |
|------|-------------|
| `list_notes(api_key, category?, q?)` | List notes with optional category filter and search |
| `get_note(api_key, note_id)` | Get a single note by ID |
| `create_note(api_key, title, content?, category?)` | Create a new note (content supports Markdown) |
| `update_note(api_key, note_id, title?, content?, category?)` | Update a note (provide only fields to change) |
| `delete_note(api_key, note_id)` | Delete a note by ID |

### Checklists

| Tool | Description |
|------|-------------|
| `list_checklists(api_key, category?, checklist_type?, q?)` | List checklists with optional filters (checklist_type: simple/task) |
| `get_checklist(api_key, list_id)` | Get a single checklist by ID |
| `create_checklist(api_key, title, category?, checklist_type?)` | Create a new checklist (use checklist_type='task' for Kanban) |
| `delete_checklist(api_key, list_id)` | Delete a checklist by ID |

### Checklist Items

| Tool | Description |
|------|-------------|
| `add_checklist_item(api_key, list_id, text, parent_index?, status?)` | Add item to checklist (use parent_index for nesting) |
| `update_checklist_item(api_key, list_id, item_index, text?, description?, priority?, score?, start_date?, target_date?, estimated_time?)` | Update item fields |
| `delete_checklist_item(api_key, list_id, item_index)` | Delete an item by index path (e.g., "0", "0.1") |
| `check_checklist_item(api_key, list_id, item_index)` | Mark item as completed |
| `uncheck_checklist_item(api_key, list_id, item_index)` | Mark item as incomplete |
| `reorder_checklist_items(api_key, list_id, active_item_id, over_item_id, position?, is_drop_into?)` | Reorder items (before/after/nest) |

### Tasks (Kanban)

| Tool | Description |
|------|-------------|
| `list_tasks(api_key, category?, status?, q?)` | List Kanban task boards |
| `get_task(api_key, task_id)` | Get a single Kanban board by ID |
| `create_task(api_key, title, category?, statuses?)` | Create a Kanban board (statuses: optional JSON string of columns) |
| `update_task(api_key, task_id, title?, category?)` | Update board metadata (title or category only) |
| `delete_task(api_key, task_id)` | Delete a Kanban board by ID |

### Kanban Columns (Statuses)

| Tool | Description |
|------|-------------|
| `get_task_statuses(api_key, task_id)` | Get all Kanban columns for a board |
| `add_task_status(api_key, task_id, status_id, label, color?, order?)` | Add a new Kanban column |
| `update_task_status(api_key, task_id, status_id, label?, color?, order?)` | Update a column's properties |
| `delete_task_status(api_key, task_id, status_id)` | Delete a column (items move to first available) |

### Task Items

| Tool | Description |
|------|-------------|
| `create_task_item(api_key, task_id, text, status?, parent_index?, description?)` | Add item to a Kanban board (supports nested sub-items and descriptions) |
| `get_task_item(api_key, task_id, item_index)` | Get an item by index |
| `update_task_item(api_key, task_id, item_index, text?, description?, priority?, score?, start_date?, target_date?, estimated_time?)` | Update a Kanban card's metadata fields (PATCH semantics) |
| `update_task_item_status(api_key, task_id, item_index, status)` | Move item to different column |
| `delete_task_item(api_key, task_id, item_index)` | Delete an item by index |

### General

| Tool | Description |
|------|-------------|
| `search_jotty(api_key, q, search_type?)` | Full-text search across notes and checklists (search_type: note/checklist) |
| `get_categories(api_key)` | Get all categories organized by type |
| `get_summary(api_key, username?)` | Get user statistics (note/checklist counts, completion rates) |

## Example Usage

```python
# List all notes for a user
mcp.list_notes(
    api_key="user1-api-key-here",
    category="Work"
)

# Create a note
mcp.create_note(
    api_key="user1-api-key-here",
    title="Meeting Notes",
    content="# Weekly Sync\n- Discussed roadmap\n- Action items...",
    category="Work"
)

# Add item to checklist
mcp.add_checklist_item(
    api_key="user2-api-key-here",
    list_id="<checklist-uuid>",
    text="Review PRs",
    status="in_progress"
)

# Move task between Kanban columns
mcp.update_task_item_status(
    api_key="user1-api-key-here",
    task_id="<task-uuid>",
    item_index="0",
    status="in_progress"
)

# Update a Kanban card's metadata (priority, dates, etc.)
mcp.update_task_item(
    api_key="user1-api-key-here",
    task_id="<task-uuid>",
    item_index="0",
    priority="high",
    target_date="2026-07-01"
)

# Create a card with description
mcp.create_task_item(
    api_key="user1-api-key-here",
    task_id="<task-uuid>",
    text="Implement authentication",
    status="todo",
    description="Add JWT-based auth flow"
)
```

## License

MIT