Skip to main content
Glama
baatjie-mcp-server

baatjie-mcp-server

by dantalan

baatjie-mcp-server

An MCP server exposing the two Baatjie Group systems through one interface:

  • tanOS — property OS, sales pipeline, and the dependency-aware action plan

  • sigscheCore — multi-brand signal scheduler across 33 platforms

Sixteen tools: five generic CRUD tools covering every allowlisted table, and eleven workflow tools for the paths used daily.


Install

npm install
npm run build
cp .env.example .env      # then fill in both service role keys

Related MCP server: fallpharm-mcp

Run

# stdio (local MCP clients — Claude Desktop, Claude Code)
npm start

# streamable HTTP
TRANSPORT=http PORT=3000 npm start

Claude Desktop / Claude Code config

{
  "mcpServers": {
    "baatjie": {
      "command": "node",
      "args": ["/absolute/path/to/baatjie-mcp-server/dist/index.js"],
      "env": {
        "TANOS_URL": "https://tlhzwovsgjptifhnzcvh.supabase.co",
        "TANOS_SERVICE_KEY": "...",
        "SIGSCHE_URL": "https://bikmrclrpgxenncqodti.supabase.co",
        "SIGSCHE_SERVICE_KEY": "..."
      }
    }
  }
}

⚠️ Read this before pointing it at production

Service role keys are required, and this is not a preference. RLS is enabled on every table in both projects with zero policies defined. A non-privileged key therefore returns empty result sets rather than errors — the server would cheerfully report 0 agencies signed, 0 tenants, 0 todos as though those were the facts. This was observed during testing, not theorised.

baatjie_dashboard warns when every table reads zero, since all-zero across nineteen tables is far more likely to be a credentials problem than a genuinely empty group. Treat that warning as a hard stop.

The same fact has a second consequence worth stating plainly: because there are no RLS policies, the service role key is the only thing standing between this server and every row in both databases — tenant ID numbers, passport numbers and permit status, landlord banking details, and the policy book. Until RLS policies exist, guard the key accordingly and keep the server local rather than exposed over HTTP.


Design notes

Personal data is withheld by default. Reads of tenants, foreign_nationals, landlords and agents drop direct identifiers (ID numbers, passport numbers, phone, email, banking details) unless include_pii: true is passed, and the response reports which fields were withheld. Routine queries shouldn't pull identity numbers into an agent's context as a side effect.

Every write is audited. Inserts, updates and deletes write a row to tanOS audit_log with the actor you supply. Pass a meaningful actor (baatjie, marius-ai, deon-ai) rather than the default. If the audit write fails, the response says so rather than pretending it succeeded.

Destructive operations are guarded. baatjie_delete requires confirm: true. Unfiltered updates and deletes are refused outright rather than rewriting a whole table. Tools carry MCP annotations (readOnlyHint, destructiveHint, idempotentHint) so calling agents can reason about consequences.

Table access is allowlisted. The generic CRUD tools reach only the nineteen tanOS and five sigscheCore tables named in constants.ts. They are not an arbitrary-schema backdoor.


Tools

Generic — comprehensive coverage

Tool

Purpose

baatjie_describe_schema

List reachable tables per project. Start here when unsure.

baatjie_query

Read any table: filters, column selection, ordering, pagination

baatjie_insert

Insert rows into any table

baatjie_update

Update rows matching a filter

baatjie_delete

Delete rows — requires confirm: true

Sequencing — the dependency graph

Tool

Purpose

baatjie_next_actions

What is genuinely startable now, ranked by downstream unblock count

baatjie_list_todos

Full board with wave, track, effort, blockers

baatjie_create_todo

Add an item, optionally sequenced

baatjie_update_todo

Change status/sequencing; reports what the change unblocked

baatjie_next_actions is the one to reach for on "what should I do next". A flat todo list hides the fact that most items are blocked; this answers the question the list is standing in for.

Operations

Tool

Purpose

baatjie_dashboard

Counts across every table, sales position, BDOP clock and 33/22 sprint block

baatjie_pipeline

Sales rollup per BRM against the 26/day objective

baatjie_log_activity

Record a BRM's outreach/demos/sales for a sprint block

baatjie_arrears

Active leases with no recent payment, ranked, with notices already sent

sigscheCore

Tool

Purpose

baatjie_signal_queue

Scheduled, sent and failed posts

baatjie_schedule_signal

Queue a post; sets the angel-window label automatically

baatjie_brand_snapshot

Per-brand readiness across platforms

Angel windows (08:17, 11:11, 13:13, 22:22) are recognised automatically when scheduling. Times outside them are accepted; the label is simply null.


A note on notices

notices rows and the baatjie_arrears report describe real occupants. baatjie_arrears is deliberately read-only — it reports the position and does not send anything, because serving a notice should be a separate deliberate act.

South African arrears and eviction notices carry statutory requirements under the Rental Housing Act and the PIE Act, and proof-of-service technicalities are exactly where cases fail. Route wording and timing through the legal-exposure-check skill before anything reaches an occupant rather than after.


Development

npm run dev            # watch mode
npm run build          # compile to dist/
node test-harness.mjs  # protocol + guardrail checks (needs env vars set)
npm run test:rls       # RLS isolation regression (needs TANOS_ANON_KEY too, see below)

test-harness.mjs verifies initialisation, tool registration, annotation coverage, description quality, the delete confirmation guard, unfiltered-write refusal, Zod range validation, and actionable error text. All 13 checks should pass.

tests/rls-regression.mjs automates the P0.1 isolation check that was previously run by hand: seeds two agencies and two real Supabase Auth users, confirms an unauthenticated (anon) read returns empty rather than erroring, confirms agency A can read its own row, and confirms agency A's cross-agency read/update/delete/insert against agency B's row are all blocked — then tears every fixture down in a finally block regardless of outcome. Needs TANOS_ANON_KEY in addition to the usual service key, since the whole point is to test as a non-privileged client rather than the key that bypasses RLS. Scoped to the 10 tables that actually carry account_id + policies today (landlords, properties, rooms, tenants, foreign_nationals, leases, lease_agreements, payments, maintenance, notices) — a live get_advisors check on 2026-08-11 found ten other tables (agents, ai_agents, audit_log, brms, daily_activity, employers, locare_accounts, member_consents, policies, todos) still have RLS enabled with zero policies. That's default-deny, not a live hole, but there's no per-agency scoping on those tables yet for a test like this one to exercise.

evaluations.xml contains ten questions for testing whether an LLM can actually use this server to answer realistic operational questions.

Available Tools

16 tools
baatjie_arrearsArrears ReportA
Read-onlyIdempotent

Identify active leases with no recent payment, ranked by days since last payment, alongside how many notices have already been sent on each.

Read-only. It reports the position; it does not send anything. Serving a notice is a separate, deliberate act — in South Africa arrears and eviction notices carry statutory requirements under the Rental Housing Act and PIE Act, so wording and timing should be checked before anything reaches an occupant.

Args:

  • min_days_overdue (number): Only leases with no payment in at least this many days (default: 1)

  • limit (number): Max leases (default: 25)

  • include_pii (boolean): Include tenant identifiers (default: false)

  • response_format ('markdown' | 'json'): Output format

Returns: { "leases": [{ "lease_id","room_id","rent_due_day","status", "last_payment_date","days_since_payment","total_paid","notices_sent" }], "summary": { "active_leases","in_arrears","never_paid","total_notices_sent" } }

Examples:

  • "Who's behind on rent?" -> no args

  • "Anyone more than 30 days down?" -> min_days_overdue=30

Error Handling:

  • Leases with no payment history report last_payment_date=null and are counted under never_paid rather than being silently dropped

Inputs: limit, include_pii, response_format, min_days_overdue.

baatjie_brand_snapshotBrand Snapshot Across PlatformsA
Read-onlyIdempotent

Per-brand status across sigscheCore: platform registration progress, queued and sent counts, library depth and how many brand cards exist.

Answers "is this brand ready to broadcast" and "where are we still unregistered".

Args:

  • brand_id (string, optional): One brand; omit to roll up every brand present

  • response_format ('markdown' | 'json'): Output format

Returns: { "brands": [{ "brand_id","queued","sent","failed","library_items", "brand_cards","platforms_registered","platforms_pending" }], "totals": { "brands","queued","sent","registered" } }

Examples:

  • "Is locare ready to post?" -> brand_id='locare'

  • "Which brands still need registration?" -> no args

Inputs: brand_id, response_format.

baatjie_create_todoCreate a TodoA

Add a todo to the tanOS action plan, optionally with sequencing metadata.

Args:

  • title (string): Short imperative summary

  • detail (string, optional): Context, why it matters, what "done" looks like

  • category (string): Grouping label, e.g. 'security', 'legal', 'pricing' (default: 'general')

  • product (string): 'locare' | 'tanos' | 'sigschecore' | 'brand' | 'cross-cutting' (default: 'cross-cutting')

  • priority ('critical' | 'high' | 'medium' | 'low'): Default 'medium'

  • wave (number, optional): 0=today, 1=root, 2=near, 3=downstream, 4=later

  • track ('A' | 'B' | 'SPINE' | 'TODAY', optional): Which workstream owns it

  • blocked_by (string[], optional): Todo ids that must complete first

  • effort (string, optional): Rough size, e.g. '1 hour', '2-3 days'

  • actor (string): Who is creating it, for the audit trail

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "todo": { "id","title",... }, "audit_logged": boolean }

Examples:

  • Quick capture -> title='Confirm iKhokha reversal path', priority='high'

  • Sequenced item -> title='Load the policy book', wave=2, track='SPINE', blocked_by=[''], effort='1 day'

Inputs: title (required), wave, actor, track, +7 more.

baatjie_dashboardBaatjie Group Operations SnapshotA
Read-onlyIdempotent

One call for the whole operational picture: row counts across every tanOS table, the sales position against the 26/day objective, and where the clock sits in the BDOP day (phase, 33/22 sprint block, next angel window).

This is the cheapest way to orient at the start of a session or a sprint. It replaces roughly a dozen separate count queries.

Args:

  • response_format ('markdown' | 'json'): Output format (default: 'markdown')

Returns: { "counts": { "": number | null }, "sales": { "accounts_signed": number, "daily_target": 26, "brms": number, "agent_pool": number, "employer_pool": number, "inherited_book": number }, "property": { "landlords","properties","rooms","tenants","leases", "payments","notices","maintenance" }, "clock": { "phase","sprint","block","minutes_left","next_angel_window" } }

Examples:

  • "Where are we?" / "Status?" -> no args

  • Start of a Factory sprint -> no args, read the clock block

Error Handling:

  • A table that cannot be counted returns null for that entry rather than failing the whole call

Inputs: response_format.

baatjie_deleteDelete RowsA
DestructiveIdempotent

Permanently delete rows matching a filter. Requires confirm=true and at least one filter. Recorded in tanOS audit_log.

Deletes are irreversible and there is no soft-delete on these tables. Prefer a status change (baatjie_update) over deletion wherever the schema has a status column — the ledger is append-only by design, and payment corrections belong as compensating rows rather than deletions.

Args:

  • project ('tanos' | 'sigsche'): Which system

  • table (string): Target table

  • filters (Filter[]): Which rows to delete — must not be empty

  • confirm (boolean): Must be true; guards against accidental invocation

  • actor (string): Who is deleting, for the audit trail

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "table": string, "action": "delete", "affected": number, "audit_logged": boolean }

Error Handling:

  • confirm=false returns an explanation without deleting anything

  • Foreign key violations name the dependent rows blocking the delete

Inputs: table (required), project (required), actor, confirm, +2 more.

baatjie_describe_schemaDescribe Baatjie Group SchemaA
Read-onlyIdempotent

List every table reachable through this server, with row counts and column names.

Call this first when you are unsure which table or column to use. It is the cheapest way to orient before querying.

Args:

  • project ('tanos' | 'sigsche' | omit for both): Which system to describe

  • response_format ('markdown' | 'json'): Output format (default: 'markdown')

Returns: { "projects": { "tanos": { "tables": [{ "name": string, "columns": string[] }] }, "sigsche": { "tables": [...] } } }

tanOS tables: Property: landlords, properties, rooms, tenants, foreign_nationals, leases, lease_agreements, payments, maintenance, notices Commercial: locare_accounts, brms, agents, employers, policies, daily_activity Internal: todos, ai_agents, audit_log

sigscheCore tables: profiles, queue_items, library_items, brand_cards, registration_status

Inputs: project, response_format.

baatjie_insertInsert RowsA

Insert one or more rows into any table in tanOS or sigscheCore. Every insert is recorded in tanOS audit_log with the actor you supply.

Args:

  • project ('tanos' | 'sigsche'): Which system

  • table (string): Target table

  • rows (object[]): Rows to insert, 1-100. Omit columns with database defaults.

  • actor (string): Who is writing, for the audit trail (default: 'mcp')

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "table": string, "action": "insert", "affected": number, "rows": object[], "audit_logged": boolean, "note"?: string }

Examples:

  • Log a signed agency -> table='locare_accounts', rows=[{"agency_name":"Cape Letting","brm_id":"...","tier":"Growth","status":"active"}]

  • Record a payment -> table='payments', rows=[{"lease_id":"...","amount":3400,"payment_date":"2026-08-08","method":"payroll"}]

Note: writes to occupant-facing tables (tenants, leases, notices, payments) are flagged in the response, because a row there corresponds to a real person's tenancy or money. A 'notices' row in particular represents a notice served on an occupant — South African arrears and eviction notices carry statutory requirements, so route the wording through your legal-exposure-check skill before sending rather than after.

Error Handling:

  • Foreign key failures name the missing parent record

  • CHECK constraint failures point at columns with restricted values

Inputs: rows (required), table (required), project (required), actor, +1 more.

baatjie_list_todosList Todos With SequencingA
Read-onlyIdempotent

List todos with their wave, track, effort, blocker count and unblock count.

Use this for a full picture of the board. For "what do I do next", baatjie_next_actions is the better tool because it filters to what is actually actionable.

Args:

  • status ('open' | 'in_progress' | 'done' | 'blocked', optional): Filter by status. Omit to return everything except done.

  • track ('A' | 'B' | 'SPINE' | 'TODAY', optional): Filter by workstream

  • wave (number, optional): Filter by wave (0=today, 1=roots, 2..4=later)

  • priority ('critical' | 'high' | 'medium' | 'low', optional): Filter by priority

  • limit (number): Max items (default: 25)

  • offset (number): Pagination offset (default: 0)

  • response_format ('markdown' | 'json'): Output format

Returns: { "total","count","offset","items":[...],"has_more","next_offset"? } where each item carries id, title, priority, status, wave, track, effort, open_blockers, startable and unblocks.

Examples:

  • "Show the critical list" -> priority='critical'

  • "What's in wave 1?" -> wave=1

  • "Everything on the housing rail" -> track='B'

Inputs: wave, limit, track, offset, +3 more.

baatjie_log_activityLog BRM Sprint ActivityA

Record a BRM's outreach, demos and sales for a 33/22 sprint block. This is how the daily 26/day objective gets measured — unlogged work is invisible to baatjie_pipeline.

Args:

  • brm_id (string): BRM identifier, must exist in tanOS brms

  • outreach (number): Contacts made in this block (default: 0)

  • demos (number): Demos booked or run (default: 0)

  • sales (number): Agencies signed (default: 0)

  • sprint_block (string, optional): Which block, e.g. 'sprint-3-build'. Defaults to the current 33/22 position from the clock.

  • activity_date (string, optional): YYYY-MM-DD, defaults to today

  • actor (string): Who is logging, for the audit trail

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "activity": {...}, "clock": {...}, "audit_logged": boolean }

Examples:

  • End of a build block -> brm_id='marius-ai', outreach=12, demos=3

  • Backfill yesterday -> brm_id='...', sales=1, activity_date='2026-08-07'

Inputs: brm_id (required), actor, demos, sales, +4 more.

baatjie_next_actionsWhat Can Actually Be Started NowA
Read-onlyIdempotent

Return the todos that are genuinely startable right now — every blocker done — ranked by how much each one unblocks downstream.

This is the tool to reach for when asked "what should I do next", "what's the priority", or "what's blocking us". A raw todo list hides the fact that most items cannot be started; this one answers the question the list is standing in for.

Args:

  • track ('A' | 'B' | 'SPINE' | 'TODAY', optional): Restrict to one workstream. A = agency business, B = payroll housing rail, SPINE = shared infrastructure, TODAY = time-boxed launch-day items

  • priority ('critical' | 'high' | 'medium' | 'low', optional): Minimum priority

  • include_blocked (boolean): Also list blocked items with their blocker counts (default: false)

  • limit (number): Max items (default: 25)

  • response_format ('markdown' | 'json'): Output format

Returns: { "startable": [{ "id","title","priority","track","wave","effort","unblocks" }], "blocked_count": number, "roots": [...], // startable items that unblock the most "summary": { "total","startable","blocked","by_priority": {...} } }

Examples:

  • "What should I work on?" -> no args

  • "What's next on the housing rail?" -> track='B'

  • "Show me everything including what's stuck" -> include_blocked=true

Error Handling:

  • Returns an empty startable list with an explanation if every open item is blocked, which itself signals the roots need attention first

Inputs: limit, track, priority, include_blocked, +1 more.

baatjie_pipelineSales Pipeline by BRMA
Read-onlyIdempotent

Roll up sales performance per Business Relationship Manager: inherited book size, agencies signed, and logged outreach/demo/sale activity.

Answers "who is performing", "where is the pipeline", and "are we hitting 26/day".

Args:

  • since (string, optional): ISO date (YYYY-MM-DD). Restrict activity to on/after this date.

  • limit (number): Max BRMs to return (default: 25)

  • response_format ('markdown' | 'json'): Output format

Returns: { "brms": [{ "brm_id","inherited_book_size","accounts_signed", "outreach","demos","sales" }], "totals": { "accounts_signed","outreach","demos","sales","brm_count" }, "against_target": { "daily_target": 26, "signed_today": number, "gap": number } }

Examples:

  • "How's the pipeline?" -> no args

  • "Activity this week" -> since='2026-08-03'

Error Handling:

  • BRMs with no logged activity appear with zeros rather than being omitted, so silence is visible rather than hidden

Inputs: limit, since, response_format.

baatjie_queryQuery Any Baatjie TableA
Read-onlyIdempotent

Read rows from any table in tanOS or sigscheCore, with filtering, column selection, ordering and pagination.

This is the general-purpose read. For sequencing work prefer baatjie_next_actions, and for the ops snapshot prefer baatjie_dashboard — both are cheaper and better shaped.

Personal identifiers (ID numbers, passport numbers, phone, email, banking details) are withheld unless include_pii is true, and the response reports which fields were withheld.

Args:

  • project ('tanos' | 'sigsche'): Which system

  • table (string): Table name — must be in the allowlist

  • columns (string[], optional): Specific columns; omit for all

  • filters (Filter[]): AND-combined filters

  • order_by (string, optional): Column to sort on

  • ascending (boolean): Sort direction (default: true)

  • limit (number): Max rows, 1-200 (default: 25)

  • offset (number): Rows to skip (default: 0)

  • include_pii (boolean): Include personal identifiers (default: false)

  • response_format ('markdown' | 'json'): Output format

Returns: { "total": number, "count": number, "offset": number, "items": object[], "has_more": boolean, "next_offset"?: number, "redacted_fields": string[] }

Examples:

  • "Which todos are still open?" -> project='tanos', table='todos', filters=[{"column":"status","op":"neq","value":"done"}]

  • "Show agencies signed this month" -> table='locare_accounts', filters=[{"column":"signed_date","op":"gte","value":"2026-08-01"}]

  • "Biggest BRM books" -> table='brms', order_by='inherited_book_size', ascending=false

Error Handling:

  • "Unknown table" lists the valid table names for that project

  • Column errors suggest calling baatjie_describe_schema

tanOS tables: Property: landlords, properties, rooms, tenants, foreign_nationals, leases, lease_agreements, payments, maintenance, notices Commercial: locare_accounts, brms, agents, employers, policies, daily_activity Internal: todos, ai_agents, audit_log

sigscheCore tables: profiles, queue_items, library_items, brand_cards, registration_status

Inputs: table (required), project (required), limit, offset, +6 more.

baatjie_schedule_signalSchedule a Signal PostA

Queue a post or ad for broadcast across platforms, with angel-window awareness.

If the scheduled time matches one of the reserved windows (08:17, 11:11, 13:13, 22:22) the angel label is set automatically and reported back. Times outside those windows are accepted without complaint — the label is simply null.

Args:

  • brand_id (string): Brand key, e.g. 'locare', 'dantalan'

  • brand_name (string): Display name

  • caption (string): Post body

  • platforms (string[]): Target platforms, e.g. ['linkedin','x','instagram']

  • scheduled_date (string): YYYY-MM-DD

  • scheduled_time (string): HH:MM (24h)

  • is_ad (boolean): Whether this is a paid ad (default: false)

  • is_master (boolean): Master/primary signal for the slot (default: false)

  • media (string, optional): Media URL or reference

  • item_type (string, optional): Free-form classification

  • user_id (string, uuid): Owning sigscheCore profile id

  • actor (string): Who is scheduling, for the audit trail

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "item": {...}, "angel_label": string | null, "audit_logged": boolean }

Examples:

  • Launch post -> brand_id='locare', scheduled_date='2026-08-08', scheduled_time='13:13', platforms=['linkedin','x']

  • Evening story -> scheduled_time='22:22', platforms=['instagram']

Error Handling:

  • Invalid time format is rejected before the write

  • A user_id with no matching profile returns a foreign key explanation

Inputs: caption (required), user_id (required), brand_id (required), platforms (required), +9 more.

baatjie_signal_queueList the Signal QueueA
Read-onlyIdempotent

List scheduled, sent and failed posts in the sigscheCore broadcast queue, optionally filtered by brand, status or date.

Args:

  • brand_id (string, optional): Restrict to one brand

  • status ('scheduled' | 'sent' | 'failed', optional): Filter by state

  • from_date (string, optional): YYYY-MM-DD, on or after

  • to_date (string, optional): YYYY-MM-DD, on or before

  • limit (number): Max items (default: 25)

  • offset (number): Pagination offset (default: 0)

  • response_format ('markdown' | 'json'): Output format

Returns: { "total","count","offset","items":[{ "id","brand_name","scheduled_date", "scheduled_time","angel_label","platforms","status","caption","is_ad" }], "has_more","next_offset"? }

Examples:

  • "What's queued today?" -> from_date='2026-08-08', to_date='2026-08-08'

  • "Anything failed?" -> status='failed'

  • "locare's schedule" -> brand_id='locare'

Inputs: limit, offset, status, to_date, +3 more.

baatjie_updateUpdate RowsA
DestructiveIdempotent

Update rows matching a filter, in any table in either system. At least one filter is required — an unfiltered update is refused rather than rewriting the whole table. Recorded in tanOS audit_log.

Args:

  • project ('tanos' | 'sigsche'): Which system

  • table (string): Target table

  • patch (object): Columns and new values

  • filters (Filter[]): Which rows to change — must not be empty

  • actor (string): Who is writing, for the audit trail

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "table": string, "action": "update", "affected": number, "rows": object[], "audit_logged": boolean }

Examples:

  • Mark a todo done -> table='todos', patch={"status":"done"}, filters=[{"column":"id","op":"eq","value":""}]

  • Close a maintenance ticket -> table='maintenance', patch={"status":"resolved","resolved_at":"2026-08-08T12:00:00Z"}, filters=[{"column":"ticket_id","op":"eq","value":""}]

Error Handling:

  • Empty filters are rejected with an explanation

  • Unknown columns in the patch are reported by name

Inputs: patch (required), table (required), project (required), actor, +2 more.

baatjie_update_todoUpdate a TodoA
DestructiveIdempotent

Change a todo's status or sequencing metadata by id.

Marking an item done automatically unblocks anything that listed it in blocked_by — the dependency graph is recomputed on read, so the next call to baatjie_next_actions will surface newly available work.

Args:

  • id (string, uuid): Todo id

  • status ('open' | 'in_progress' | 'done' | 'blocked', optional)

  • priority ('critical' | 'high' | 'medium' | 'low', optional)

  • wave (number, optional)

  • track ('A' | 'B' | 'SPINE' | 'TODAY', optional)

  • blocked_by (string[], optional): Replaces the existing blocker list

  • effort (string, optional)

  • detail (string, optional)

  • actor (string): Who is writing, for the audit trail

  • response_format ('markdown' | 'json'): Output format

Returns: { "ok": true, "todo": {...}, "newly_unblocked": [{ "id","title" }], "audit_logged": boolean }

Examples:

  • Close an item -> id='', status='done'

  • Re-sequence -> id='', wave=1, track='SPINE'

Inputs: id (required), wave, actor, track, +6 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 16 tools

    Disambiguation4/5

    Tools are mostly distinct: generic CRUD (query/insert/update/delete) is clearly separated from specialized reads like dashboard, pipeline, arrears, and signal_queue. A few overlaps exist (e.g., baatjie_query could be used for many read needs), but descriptions guide to the specialized tools, so confusion is minimal.

    Naming Consistency4/5

    All tools share the 'baatjie_' prefix and mostly use snake_case with verb_noun patterns (schedule_signal, list_todos, create_todo, update_todo, log_activity). However, some are noun-only (dashboard, pipeline, arrears, signal_queue, brand_snapshot), which is a minor deviation from the otherwise consistent pattern.

    Tool Count4/5

    With 16 tools, the server sits just above the ideal 3-15 range but feels justified given the two subsystems (tanOS and sigscheCore) and the combination of generic CRUD plus domain-specific reads/actions. The count is manageable and not bloated.

    Completeness4/5

    Generic CRUD tools (query/insert/update/delete) cover virtually all tables, while specialized tools like baatjie_next_actions, baatjie_pipeline, baatjie_arrears, and baatjie_signal_queue provide high-level views. Minor gaps like a specific cancel_signal or update_signal are covered by the generic update/delete, so there are no critical dead ends.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers