Task tracking and time logging that lives inside Claude Code.
Connect your workspace in one command — no config files, no manual token copying. Ask Claude to create tasks, track focus time, run daily standups, and review your week — all from inside your editor.
claude mcp add --transport http tendon https://api.tendon.alashed.kz/mcp- Create a free account →
- Run the command above in your terminal
- Open Claude Code → say
start my day→ browser opens → click Allow - Done. Just talk: "Create tasks…", "What did I do yesterday?"
Full web dashboard, team features, Telegram reports, analytics.
npx tendon-cliInteractive setup wizard — starts PostgreSQL + API + MCP via Docker, creates your account, prints the Claude command. No external accounts required.
"Start my day" → /morning prompt
"What did I do yesterday?" → get_daily_summary
"Create tasks for today's sprint" → create_task × N
"Start a focus session on the auth bug" → start_focus_session
"Mark the login refactor as done" → update_task_status
"I'm blocked — can't reproduce in prod" → log_blocker
"Wrap up for today" → /wrap_up prompt
"Show me my week" → week_summary
| Tool | Description |
|---|---|
create_task |
Create a task with title, priority, due date |
list_tasks |
List tasks filtered by status |
update_task |
Edit title, description, priority, or due date |
update_task_status |
Move a task to planned / in_progress / done |
archive_task |
Remove a task from your active list |
start_focus_session |
Begin a timed work session, auto-stops the previous |
stop_focus_session |
End the session and log duration |
get_today_plan |
Prioritized view of today: in-progress, planned, time tracked |
get_daily_summary |
Summary for any date — supports "yesterday" |
week_summary |
7-day breakdown: focus time, tasks done, best day |
log_blocker |
Append a blocker note to a task |
| Prompt | What it does |
|---|---|
/morning |
Show today's plan, suggest first task, start focus session |
/wrap_up |
Stop tracking, recap day, suggest tomorrow's top 3 |
/standup |
Generate Yesterday / Today / Blockers in standup format |
/review |
Weekly review with productivity patterns and suggestions |
tendon.alashed.kz → Next.js web app (port 3030)
api.tendon.alashed.kz → Fastify API + MCP (port 3001)
api.tendon.alashed.kz/mcp → nginx proxy → API/mcp (port 3001)
MCP logic lives inside the API process. api.tendon.alashed.kz/mcp is an nginx proxy to api.tendon.alashed.kz/mcp.
| Layer | Technology |
|---|---|
| Web | Next.js 15, React 19, Tailwind CSS, Clerk |
| API | Fastify v5, TypeScript, PostgreSQL |
| MCP | @modelcontextprotocol/sdk v1, Streamable HTTP, session store |
| Auth | Clerk (web) + OAuth 2.1 + PKCE (Claude Code) + argon2id |
| Infra | AWS EC2, RDS PostgreSQL, S3, SSM |
Claude Code MCP (mcp.*) API (api.*) Web (tendon.*)
│ │ │ │
├─ POST /mcp ────────►│ │ │
│ ├─ proxy ───────────►│ │
│◄─ 401 WWW-Auth ─────┤ resource_metadata= │ │
│ │ api.*/oauth-prot.. │ │
│ │ │ │
├─ GET api.*/.well-known/oauth-protected-resource ───────────────►│ (api.*)
│◄─ { resource, authorization_servers: [api.*] } ─────────────────┤
│ │ │ │
├─ GET api.*/.well-known/oauth-authorization-server ─────────────►│ (api.*)
│◄─ { authorization_endpoint, token_endpoint } ───────────────────┤
│ │ │ │
├─ browser: /oauth/authorize ──────────────────────────────────────►│ (consent UI)
│◄─ redirect with code ────────────────────────────────────────────┤
│ │ │ │
├─ POST api.*/oauth/token ──────────────►│ │
│◄─ access_token ─────────────────────────┤ │
│ │ │ │
├─ POST /mcp (Bearer) ►│ │ │
│ ├─ proxy ───────────►│ (validates token DB) │
│◄─ tool result ───────┤ │ │
tendon/
├── packages/
│ ├── api/ Fastify REST API + OAuth 2.1 server
│ ├── mcp/ MCP server (OAuth-protected resource)
│ ├── web/ Next.js dashboard + OAuth consent UI
│ └── shared/ TypeScript types (no runtime)
├── .github/
│ └── workflows/ CI/CD — deploys API, MCP, Web on push to main
└── infra/ Nginx configs
git clone https://github.com/Alashed/tendon-mcp.git
cd tendon-mcp
docker compose upThat's it. PostgreSQL, the API, and the MCP server all start automatically.
Then add to Claude Code:
claude mcp add --transport http tendon http://localhost:3002/mcpClaude opens http://localhost:3001/oauth/authorize — create an account with email/password and click Allow. No Clerk, no external services.
To create your first account:
curl -X POST http://localhost:3001/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","name":"You","password":"yourpassword"}'- Node.js ≥ 20
- PostgreSQL 14+ running locally
git clone https://github.com/Alashed/tendon-mcp.git
cd tendon-mcp
npm install
# Copy and fill in env files
cp packages/api/.env.example packages/api/.env
cp packages/mcp/.env.example packages/mcp/.env
# Edit packages/api/.env — set DATABASE_URL and JWT_SECRET at minimum
# Run migrations
cd packages/api && node -e "import('./dist/scripts/migrate.js')" || npm run build && node dist/scripts/migrate.js
cd ../..
# Start services
npm run dev:api # http://localhost:3001
npm run dev:mcp # http://localhost:3002claude mcp add --transport http tendon http://localhost:3002/mcpIf you want the full web dashboard (task list, sessions, team view):
- Create a project at clerk.com (free)
- Copy keys into env files — see Environment variables
- Add redirect URLs in Clerk dashboard:
http://localhost:3000/login/sso-callbackhttp://localhost:3000/register/sso-callback
npm run dev:web— http://localhost:3000
PORT=3001
NODE_ENV=development
# Database — use one or the other
DATABASE_URL=postgresql://user:pass@localhost:5432/tendon
# or individual vars:
# DB_HOST=localhost
# DB_PORT=5432
# DB_NAME=tendon
# DB_USER=postgres
# DB_PASSWORD=postgres
# DB_SSL=false
JWT_SECRET=your-random-secret-min-32-chars
# Clerk
CLERK_SECRET_KEY=sk_test_...
# Service URLs (must match where each service is running)
API_BASE_URL=http://localhost:3001
WEB_BASE_URL=http://localhost:3000
MCP_BASE_URL=http://localhost:3002
CORS_ORIGINS=http://localhost:3000
# Optional
TELEGRAM_BOT_TOKEN=PORT=3002
ALASHED_API_URL=http://localhost:3001
MCP_BASE_URL=http://localhost:3002NEXT_PUBLIC_API_URL=http://localhost:3001
# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/login
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/register
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboardingBase URL: https://api.tendon.alashed.kz (or your local http://localhost:3001)
All endpoints except /auth/*, GET /invites/:code, and /oauth/* require:
Authorization: Bearer <token>
Accepts both Clerk JWT (from the web app) and OAuth access token (from Claude Code).
POST /auth/register { name, email, password }
POST /auth/login { email, password }
GET /auth/me → current user + workspaces (with role)
GET /tasks?workspace_id=&status=&assignee_id=
POST /tasks { workspace_id, title, priority?, due_date?, source? }
GET /tasks/:id
PATCH /tasks/:id { status?, title?, description?, priority?, due_date? }
DELETE /tasks/:id → archives the task (204)
POST /activities/start { workspace_id, task_id?, source? }
POST /activities/stop { workspace_id, activity_id? }
GET /activities?workspace_id=&date=YYYY-MM-DD&user_id=
POST /sync batch sync for offline agents
GET /reports/daily?workspace_id=&date=YYYY-MM-DD&user_id=
→ { date, workspace_id, users: [{ user_name, focus_minutes, tasks_done_today, ... }], totals }
GET /workspaces
POST /workspaces { name, type: "personal"|"team" }
GET /workspaces/:id
POST /workspaces/:id/members { user_id, role }
POST /workspaces/:id/invites { email?, role? } → invite link
GET /invites/:code
POST /invites/:code/accept
POST /telegram/webhook Telegram Bot webhook
GET /telegram/link?code= verify link code
POST /telegram/link/confirm { code, workspace_id }
GET /.well-known/oauth-authorization-server RFC 8414 metadata
POST /oauth/register RFC 7591 dynamic client registration
GET /oauth/authorize redirects to web consent page
POST /oauth/consent { ...params, workspace_id }
POST /oauth/token { grant_type, code, code_verifier, ... }
POST /oauth/introspect { token }
- Create a bot via @BotFather, copy the token
- Add
TELEGRAM_BOT_TOKEN=...topackages/api/.env - The API registers the webhook automatically on startup
- In Telegram, send
/connectto the bot to link a chat to your workspace - The bot sends daily reports at the hour set in
telegram_chats.report_hour(UTC)
Bot commands:
/connect— link this chat to a workspace/today— show today's summary/help— list commands
Self-hosted is always free and unlimited (MIT license).
| Free | Pro | Team | |
|---|---|---|---|
| Price | $0 | $9/mo | $19/mo |
| Workspaces | 1 | 1 | Unlimited |
| Tasks | 50 | Unlimited | Unlimited |
| History | 7 days | Unlimited | Unlimited |
| MCP tools | All | All | All |
| Telegram reports | — | ✓ | ✓ |
| Team members | — | — | Up to 10 |
| Team dashboard | — | — | ✓ |
| Self-hosted | ✓ unlimited | — | — |
Pro / Team: email hello@tendon.alashed.kz
We welcome contributions! See CONTRIBUTING.md for guidelines.
Good first issues:
- Timezone-aware nightly reports (use
workspace.timezonefor cron) -
/dashboard/tasksand/dashboard/sessionsworkspace switcher - Local agent (
packages/agent) for terminal history tracking - Docker Compose for one-command local setup
- Week-over-week trend in
week_summary