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:
Create a workspace with built-in backends
Define a typed Task entity
Write, read, update, delete, and query data
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:
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 Code
OpenCode
VS Code / Copilot
LM Studio
OpenClaw
Codex CLI / Codex Agent
Hermes
claude mcp add --transport http tasks-mcp http://127.0.0.1:7001/tasks-mcp/mcp
Add to opencode.json: {
"mcp" : {
"tasks-mcp" : {
"type" : "remote" ,
"url" : "http://127.0.0.1:7001/tasks-mcp/mcp"
}
}
}
Add to .vscode/mcp.json: {
"servers" : {
"tasks-mcp" : {
"type" : "http" ,
"url" : "http://127.0.0.1:7001/tasks-mcp/mcp"
}
}
}
In Developer > mcp.json, paste the MCP config: {
"mcpServers" : {
"tasks-mcp" : {
"url" : "http://127.0.0.1:7001/tasks-mcp/mcp" ,
"transport" : "http"
}
}
}
Add to ~/.openclaw/openclaw.json: {
"mcpServers" : {
"tasks-mcp" : {
"url" : "http://127.0.0.1:7001/tasks-mcp/mcp" ,
"transport" : "http"
}
}
}
Codex supports MCP via settings. Add a new MCP server:
Name: tasks-mcp
Type: Streamable HTTP
URL: http://127.0.0.1:7001/tasks-mcp/mcp
Add to ~/.hermes/config.yaml: mcp_servers :
tasks-mcp :
url : "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
Show Complete task tracker
import heavenbase as hb
# 1. Workspace
ws = hb.HeavenBase( "tasks-demo" )
# 2. Entity
class Task ( hb . Entity ):
title: hb.ShortText
description: hb.MediumText
priority: hb.Integer
done: hb.Boolean
ws.register(Task)
# 3. Seed data
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 ))
# 4. Query
done = list (ws.query(Task).where(Task.done == True ))
print ( f "Done tasks: {len (done) } " )
# 5. Upsert
ws.upsert(Task, { "id" : "t1" , "done" : True , "priority" : 1 })
ws.upsert(Task, { "id" : "t4" , "title" : "Party" , "priority" : 3 , "done" : False })
# 6. Serve as MCP
print ( "Serving on http://127.0.0.1:7001/tasks-mcp/mcp" )
ws.serve( name = "tasks-mcp" )
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