Skip to main content

Documentation Index

Fetch the complete documentation index at: https://ahvn.top/llms.txt

Use this file to discover all available pages before exploring further.

In this workshop, you will:
  1. Create a workspace with built-in backends
  2. Define a typed Task entity
  3. Write, read, update, delete, and query data
  4. Expose the workspace as an MCP server for code agents
By the end, your task tracker will be usable from Claude Code, OpenCode, Copilot, and any MCP client.

1. Create a workspace

A HeavenBase workspace bundles schema definitions, backends, routing, and data into one container.
import heavenbase as hb

ws = hb.HeavenBase("tasks-demo")
print(ws.backends.names())
# ['main', 'vec']
By default a workspace gets an SQLite backend (main) for structured data and an in-memory vector backend (vec) for embeddings. No config files needed.

2. Define an entity

Entities are typed schemas. They define what fields your data has and what types they are. Every entity has an implicit id: Identifier — supply one at write time or HeavenBase generates a deterministic hash.
class Task(hb.Entity):
    title:       hb.ShortText
    description: hb.MediumText
    priority:    hb.Integer
    done:        hb.Boolean
Register the entity so the workspace knows its schema:
ws.register(Task)

3. Write data

Use ws.set() to insert or update rows. The first argument is an entity instance with an id.
ws.set(Task(id="t1", title="Set up CI",  description="Add GitHub Actions", priority=1, done=False))
ws.set(Task(id="t2", title="Write docs", description="Finish quickstart",  priority=2, done=True))
ws.set(Task(id="t3", title="Ship v0.1", description="Tag and release",    priority=1, done=False))

print(ws.count(Task))
# 3

4. Read and query

# Get one by ID (returns a dict)
task = ws.get("t1", entity=Task)
print(task["title"])
# Set up CI

# List all items
for task_id, row in ws.items(Task):
    print(task_id, row["title"], row["done"])

# Filter with the query builder
done_tasks = list(ws.query(Task).where(Task.done == True))
print(len(done_tasks))  # 1 (Write docs)

# In-memory filtering
incomplete = [row for _, row in ws.items(Task) if not row["done"]]
print(len(incomplete))  # 2

5. Update and delete

# Update — same id, changed fields
ws.set(Task(id="t1", priority=1, done=True))

# Delete — pass (entity, entity_id) or {"entity": ..., "id": ...}
ws.delete({"entity": Task, "id": "t3"})

print(ws.count(Task))
# 2
ws.delete() accepts a tuple (entity, entity_id), a dict {"entity": ..., "id": ...}, or just the entity id when unambiguous.

6. Expose as MCP server

Your workspace exposes CRUD operations (get, query, upsert, delete, etc.) as MCP tools. Serve it with one call:
ws.serve(name="tasks-mcp")
The server starts on http://127.0.0.1:7001/tasks-mcp/mcp by default. Connect any MCP client:
claude mcp add --transport http tasks-mcp http://127.0.0.1:7001/tasks-mcp/mcp
The server runs until interrupted. For production, use a process manager or the wait=False option to run it in the background. Use ws.to_mcp_json(name="tasks-mcp") to print the config without starting the server.

7. Full demo script


Further Exploration

Related resources:
  • Entities — field types, computed fields, routing
  • Workspace — lifecycle, inspect, MCP serving
  • Backends — SQLite, Postgres, MySQL, vector stores
  • Query — filtering, sorting, JSON queries