Skip to content
Sathi

saathi · सहायक · companion

A quiet assistant for the next forty years of you.

Sathi keeps your habits, tasks, finance, goals, documents, memories, and skills under one MCP roof. Same store that powers your dashboard answers every Claude question your other agents ask — your reading list, your stash of decisions, your morning routine — without the agent ever having to guess.

~70
typed mcp tools
8
life pillars
1536d
hybrid memory search
OAuth 2.0
+ pkce s256

today · 14 may

2 of 5 done

Good morning. Three things to look at, two already moving.

Morning brief

07:30

Workout · push day

09:00

Sankalp · review draft queue

13:00

Read · 30 min

17:00

Evening reflection

21:30

All five rows live in the same Supabase project as your habits, goals, and memories. The 21:30 reflection writes back into the memory vault automatically — no copy-paste between apps.

Eight pillars

One MCP server. Every part of a life worth tracking.

Each pillar is its own typed tool group inside the same MCP server — same auth, same RLS, same Supabase project. No bespoke ETL between life areas.

Habits

10

Track frequency, log days, surface streaks. The habit calendar lives in the same store every agent reads from.

Tasks

17

Subtasks, projects, real-time tracking, dependency-aware "what next" — and an auto-link memory pipeline so the agent never asks twice.

Goals

9

Outcome goals auto-track against habits + tasks + finance. Milestone goals stay manual. Reviews aggregate across pillars.

Finance

11

Spending categories, transaction list, summaries by date range — Claude can pull a weekly money read in one tool call.

Memory

9

pgvector + tsvector hybrid search. Near-duplicate detection at save time. Stale hints for low-importance items.

Documents

7

Signed-URL upload, chunked extraction, semantic search across the wallet. Bills, IDs, receipts — agent-reachable.

Skills

5

Platform-aware skill registry. Bidirectional sync with ~/.claude/skills via a hand-written meta-skill.

Graph

4

Soft links between any two entities. Cross-pillar context: a task can reference a memory can reference a goal.

How it's built

Three decisions that earned their keep.

Each is in production traffic. Each has a debug session behind it — the kind of detail recruiters skim past on a resume.

Decision 01

Stateless MCP, per-request

A persistent MCP server inside a Vercel function would never survive cold-start budgets, and a singleton would fork state when two instances spun up concurrently. Sathi needed an MCP shape that didn't fight the runtime.

POST-onlyone transport contract
  1. Every POST /api/mcp call constructs a fresh MCP server with the requesting user's scope baked in. No singleton, no cross-request leakage.

  2. Bearer token verifier dispatches by prefix (sha-256 lookup for stored tokens, jose verify for OAuth access tokens, raw JWT fallthrough for Supabase sessions).

  3. GET /api/mcp returns 405 — MCP Streamable HTTP clients were treating a 200 JSON response as a dropped SSE stream and reconnecting at 2 Hz (99k edge requests in 12 hours on 2026-04-20). POST-only.

excerpt · 01.code
// app/api/mcp/route.ts
export const runtime = 'nodejs'

export async function POST(req: Request) {
  const user = await verifyBearer(req)
  const server = createMcpServer({ userId: user.id })
  return await streamableHttp(server).handle(req)
}

export const GET = () => new Response(null, { status: 405 })
export const HEAD = GET

Decision 02

Hybrid memory search, not pure vector

Pure cosine-similarity over text-embedding-3-small misses exact-keyword matches (project names, person names, dates). Pure tsvector misses semantic adjacency. The memory vault needed both at once.

0.7 / 0.3semantic / keyword blend
  1. pa_memory_items carries both an embedding (vector 1536) and a search_vector (tsvector, maintained by trigger).

  2. pa_hybrid_search RPC scores each candidate by weighted sum of semantic_score + keyword_score, returns top-K with both scores attached for tunable reranking.

  3. Save-time duplicate detection runs pa_match_memories at threshold 0.9 and returns "duplicates_found" instead of inserting — agents stop saving the same fact thirty times across sessions.

excerpt · 02.code
create function pa_hybrid_search(
  user_id uuid, query text, query_embedding vector,
  category text default null, project text default null,
  match_count int default 10
) returns table (...) as $$
  select id, title, content,
    1 - (embedding <=> query_embedding) as semantic_score,
    ts_rank_cd(search_vector, websearch_to_tsquery(query)) as keyword_score,
    /* weighted blend */
    (1 - (embedding <=> query_embedding)) * 0.7
    + ts_rank_cd(search_vector, websearch_to_tsquery(query)) * 0.3 as final_score
  from pa_memory_items
  where user_id = $1 and is_active = true ...
$$ language sql stable;

Decision 03

Cross-pillar links, soft and audited

Tasks reference memories, memories reference goals, goals depend on habits. Hard-FK relationships across that graph would lock the schema and make a single soft-delete cascade everything.

4 link typesone table
  1. pa_entity_links carries (from_type, from_id, to_type, to_id, link_type, notes) — no FK to source tables. Soft links survive deletes.

  2. link_type ∈ {related_to, references, blocks, part_of}. blocks links power get_task_dependencies + get_next_tasks (ready-to-start computation).

  3. Every mutating MCP call appends a row to pa_audit_log with changed_fields JSONB. The link history is auditable years after the source row is gone.

excerpt · 03.code
-- the entire graph in one table
create table pa_entity_links (
  id uuid primary key,
  user_id uuid references auth.users,
  from_type text not null, from_id uuid not null,
  to_type text not null,   to_id uuid not null,
  link_type text default 'related_to',
  notes text, active bool default true,
  unique (user_id, from_type, from_id,
          to_type,   to_id,   link_type)
);

Open to work

I designed the schema, wrote the MCP, and built every pillar — I can do the same for you.

Sathi is one of seven dogfooded apps I ship in parallel. Same Supabase project, same MCP fabric, same Claude Code stack. Happy to walk a hiring panel through any layer live — the hybrid-search RPC, the audit log, the OAuth broker, or the cross-pillar link graph.

Senior / staff full-stackAI engineer · MCP & agent infraNext.js · TypeScript freelancePostgres · pgvector · RLS