sTOobyDOo
A collaborative, MCP-enabled todo app for families and teams.
Built with Next.js, TypeScript, Prisma, and LibSQL.
Features · Quick start · Configuration · MCP · Deployment
sTOobyDOo combines a multi-user family task manager with a first-class AI integration surface. Organize shared and private lists, nest subtasks, assign work to family members, and receive push or email notifications — while any MCP-compatible AI assistant (Claude, Cursor, and others) can manage the same tasks through natural language using permission-scoped tokens.
Features
| Area | What is included |
|---|---|
| Families and members | One family instance with multiple members, admin roles, and per-member private lists. |
| Lists | Named lists with descriptions and colors, per-member sharing with edit/delete/share grants, and archiving. |
| Tasks | Two levels of nested subtasks, descriptions, priorities (low/medium/high), due dates with optional times, scheduled reminders, assignment, completion, and soft-delete archiving. |
| AI integration (MCP) | A Model Context Protocol server at /api/mcp exposing 17 tools over SSE and HTTP POST, authenticated by per-user tokens with granular permissions and per-list access restriction. |
| Notifications | Pushover push and Resend email notifications for task assignment, completion, and due-date reminders. Credentials can be set globally by an admin, per member, or via environment variables. |
| Reminder scheduling | A secret-protected /api/cron/reminders endpoint, a ready-to-use GitHub Actions workflow, and in-app polling as a no-setup fallback. |
| Interface | Responsive App Router UI with light/dark themes via next-themes. |
| Deployment | Zero-config Vercel deployment with automatic, data-safe database initialization against a hosted LibSQL database such as Turso. |
Technology stack
| Layer | Technology |
|---|---|
| Application | Next.js 16 App Router, React 19, TypeScript 5 |
| Data | Prisma 6, @prisma/adapter-libsql, SQLite-compatible LibSQL (local file or hosted) |
| Authentication | NextAuth.js 4 credentials provider with JWT sessions and bcrypt password hashing |
| Styling | Tailwind CSS 4, CSS variables, next-themes |
| Notifications | pushover-js, Resend |
| AI protocol | Model Context Protocol (SSE transport, protocol version 2024-11-05) |
| Deployment | Vercel (serverless) with a hosted LibSQL database |
Quick start
Prerequisites
- Node.js 20.9 or newer
- npm
1. Install the project
git clone https://github.com/tunwinlat/sTOobyDOo.git
cd sTOobyDOo
npm install
2. Configure the local environment
Create a .env.local file in the repository root:
DATABASE_URL="file:./dev.db"
NEXTAUTH_SECRET="replace-with-a-long-random-secret"
Generate a suitable secret with:
openssl rand -base64 32
Environment files are ignored by Git. See .env.example for the full variable reference.
3. Prepare the local database
npx prisma generate
npx prisma db push
4. Start sTOobyDOo
npm run dev
Open http://localhost:3000 and complete the setup wizard to create your family and admin account.
Configuration
Environment variables
| Variable | Requirement | Purpose |
|---|---|---|
DATABASE_URL |
Required | Local file: URL for development, or a hosted libsql:// connection URL for deployment. |
DATABASE_AUTH_TOKEN |
Hosted databases | Authentication token for the LibSQL provider (required for remote URLs). |
NEXTAUTH_SECRET |
Required | Signs NextAuth JWT sessions. Use a strong random value. |
NEXTAUTH_URL |
Optional | Absolute application URL; auto-detected on Vercel. |
CRON_SECRET |
Recommended | Bearer secret protecting the /api/cron/reminders endpoint. |
PUSHOVER_APP_TOKEN |
Optional | Fallback Pushover application token when none is stored in family settings. |
PUSHOVER_USER_KEY |
Optional | Fallback Pushover user key when none is stored in family or member settings. |
RESEND_API_KEY |
Optional | Fallback Resend API key when none is stored in family settings. |
RESEND_FROM_EMAIL |
Optional | Sender address for Resend email notifications. |
Notification credentials are resolved in order: per-member settings, then family (admin) settings, then these environment-variable fallbacks. All of them can be managed from the settings UI after deployment — no environment variables are required for notifications.
Database options
| Provider | Use case | URL format |
|---|---|---|
| SQLite | Local development | file:./dev.db |
| Turso (or another hosted LibSQL provider) | Production | libsql://<db>.turso.io |
Model Context Protocol (MCP)
MCP is the differentiator of sTOobyDOo: every member can issue tokens that let AI assistants read and mutate tasks through natural language — "add 'buy milk' to the shopping list", "what is due this week?", "complete the grocery run".
Creating a token
- Log in to your sTOobyDOo instance.
- Go to Settings → MCP Tokens and click Create Token.
- Name the connection (for example, "Claude Desktop") and choose permissions.
- Copy the token; it is used as
?token=YOUR_TOKENor aBearerheader against/api/mcp.
The endpoint speaks MCP over SSE (GET /api/mcp, protocol version 2024-11-05) and accepts plain JSON-RPC over HTTP POST for simple clients.
Claude Desktop
Claude Desktop connects to remote SSE servers through a bridge such as mcp-remote. Add to claude_desktop_config.json:
{
"mcpServers": {
"stoobydoo": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://your-app.vercel.app/api/mcp?token=YOUR_TOKEN"
]
}
}
}
Cursor
Add a server in Cursor's MCP settings:
{
"mcpServers": {
"stoobydoo": {
"url": "https://your-app.vercel.app/api/mcp?token=YOUR_TOKEN"
}
}
}
Available tools (17)
List management
| Tool | Permission required | Description |
|---|---|---|
get_lists |
— | All accessible lists with open-task counts. |
get_list |
— | A single list by ID. |
get_list_tasks |
— | Tasks in a list, including two levels of subtasks. |
create_list |
canCreateLists |
Create a list with name, description, and color. |
update_list |
canEditLists |
Update list name, description, or color. |
delete_list |
canDeleteLists |
Permanently delete a list and its tasks. |
Task queries
| Tool | Permission required | Description |
|---|---|---|
get_task |
— | Full task details including subtasks. |
get_all_open_tasks |
— | All incomplete tasks across accessible lists. |
get_archived_tasks |
— | Archived tasks, optionally filtered by list. |
search_tasks |
— | Search tasks by title or description. |
Task management
| Tool | Permission required | Description |
|---|---|---|
create_task |
canCreateTasks |
Create a task with optional parent (two-level nesting). |
complete_task |
canCompleteTasks |
Mark a task completed. |
uncomplete_task |
canEditTasks |
Reopen a completed task. |
update_task |
canEditTasks |
Update title, description, priority, due date/time, or reminder. |
delete_task |
canDeleteTasks |
Permanently delete a task. |
archive_task |
canEditTasks |
Archive a completed task (soft delete). |
unarchive_task |
canEditTasks |
Restore an archived task. |
All task tools also operate on subtasks by ID; create_task accepts a parentId up to two levels deep (task → subtask → sub-subtask).
Default token permissions
| Permission | Default |
|---|---|
canCreateTasks |
✅ true |
canCompleteTasks |
✅ true |
canEditTasks |
✅ true |
canDeleteTasks |
❌ false |
canCreateLists |
❌ false |
canEditLists |
❌ false |
canDeleteLists |
❌ false |
allowAllLists |
✅ true (or restrict to selected lists) |
Destructive permissions are opt-in, and tokens can be scoped to specific lists — a safe default for handing an assistant access to a shared household.
Reminder scheduling
Reminders fire when /api/cron/reminders is called. Three options:
Option 1: GitHub Actions (included)
The repository ships .github/workflows/reminders.yml, which calls the endpoint every five minutes.
- In Vercel → project → Settings → Environment Variables, add
CRON_SECRET(any random string, e.g.openssl rand -base64 32) and redeploy. - In GitHub → Settings → Secrets and variables → Actions, add:
VERCEL_URL— your deployment URL withhttps://(e.g.https://your-app.vercel.app)CRON_SECRET— the same value as above
The workflow uses the Production environment; ensure the secrets are available there.
GitHub's free tier includes ~2,000 Actions minutes per month; a five-minute schedule uses ~2,160. Adjust the cron expression (for example */10 * * * *) to stay within the limit.
Option 2: External cron service
Point any scheduler (cron-job.org, UptimeRobot, and similar) at:
GET https://your-app.vercel.app/api/cron/reminders
Authorization: Bearer YOUR_CRON_SECRET
Option 3: In-app polling
The app checks for due reminders during normal use. No setup required, but delivery depends on someone opening the app.
Architecture
flowchart LR
Members[Family members] --> UI[App Router UI]
Assistants[AI assistants] --> MCP[MCP endpoint /api/mcp]
Scheduler[Cron scheduler] --> Cron[/api/cron/reminders]
UI --> API[REST route handlers]
MCP --> Tools[Permission-checked MCP tools]
API --> Services[Task, list, and notification services]
Tools --> Services
Cron --> Services
Services --> Prisma[Prisma + LibSQL adapter]
Prisma --> DB[(SQLite / hosted LibSQL)]
Services --> Push[Pushover]
Services --> Email[Resend]
Repository layout
sTOobyDOo/
├── app/ # Next.js 16 App Router
│ ├── api/ # REST routes, MCP endpoint, cron, setup
│ ├── dashboard/ # Main dashboard
│ ├── lists/ # List views
│ ├── settings/ # User, admin, and MCP token settings
│ └── setup/ # First-run setup wizard
├── components/ # Shared UI (app shell, task items, base UI)
├── lib/ # Auth, Prisma client, notifications, utilities
├── prisma/ # Schema
├── scripts/ # Data-safe database initialization (ensure-db.js)
├── .github/workflows/ # Reminder scheduler (GitHub Actions)
└── middleware.ts # Route protection
Development
| Command | Purpose |
|---|---|
npm run dev |
Start the development server. |
npm run build |
Create a production build. |
npm run build:vercel |
Vercel build: Prisma generate, data-safe DB initialization, then build. |
npm run db:ensure |
Run the safe database initialization script. |
npm run db:push |
Synchronize the local schema with prisma db push. |
npm run db:push:force |
Push schema with the data-loss flag. |
npm run db:studio |
Open Prisma Studio. |
npx eslint . |
Run ESLint. |
Deployment
- Create a hosted LibSQL database (for example, with the Turso CLI):
turso db create stoobydoo
turso db show stoobydoo # copy the libsql:// URL
turso db tokens create stoobydoo # copy the auth token
Deploy to Vercel and set
DATABASE_URL,DATABASE_AUTH_TOKEN, andNEXTAUTH_SECRET. The configured build command (npm run build:vercel) generates Prisma Client and initializes the schema automatically.Complete the setup wizard on the deployed URL to create the family and admin account.
Optional: set
CRON_SECRETand configure the reminder scheduler (see above), and add notification credentials under settings.
Data safety
Redeploys are safe. The build runs scripts/ensure-db.js, which creates tables on the first deploy and skips destructive changes afterward — existing data is never dropped automatically. See DATA_SAFETY.md for details, and DEPLOY.md for the extended deployment guide.
Troubleshooting
| Symptom | Fix |
|---|---|
| "Database connection failed" | Verify DATABASE_URL; ensure DATABASE_AUTH_TOKEN is set for remote URLs. |
| "Unauthorized" during build | The Turso token is missing or expired — create a new one with turso db tokens create. |
| First request is slow | Hosted LibSQL databases sleep when idle; the first request wakes them (5–10 seconds). |
| Setup wizard keeps appearing | No family exists yet — complete the wizard once. |
Contributing
Issues and pull requests are welcome. Keep changes focused, verify them with npm run build and npx eslint ., and describe the motivation and verification performed.
License
sTOobyDOo is licensed under the Mozilla Public License 2.0. Modifications to MPL-covered files must remain available under the MPL when distributed.