Skip to main content
Glama

berbotu-mcp

An MCP server that lets an AI read and write real records in a live repository — without being able to break anything quietly.

It sits over the vault behind a small record label: releases, demos, and label state, all markdown with YAML frontmatter. Four tools over stdio. Three read, one writes.

"what demos came in this week I haven't decided on yet?"   → list_demos  { stage: "Listening" }
"what's the next release scheduled?"                        → list_releases { stage: "Scheduled", limit: 1 }
"log the demo that just came in from X"                     → add_demo    ✍️ writes a real git commit

Why it's built the way it is

The interesting problem isn't connecting an AI to data. That's an afternoon. The problem is that an AI with write access is an intern who never asks twice — so every design decision here is about what happens when it's wrong.

Every write is its own git commit, through the GitHub Contents API. Not a filesystem write. A commit. Which means every single thing the AI ever did is attributable, diffable, and revertible with one command. If it writes garbage at 3am, I don't need a backup — I need git revert. This costs an API round-trip per write and it's worth it.

Existence is SHA-checked before anything is touched. getVaultFile resolves the file's SHA first. Passing a sha to the create endpoint turns a create into an overwrite — so the check isn't defensive, it's the difference between "this demo already exists" and silently destroying a record.

Inputs are validated at the boundary, then again in the handler. Tool inputs are zod schemas — stage is an enum, so a hallucinated stage gets a clean rejection with the valid options instead of writing nonsense. Then addDemo re-validates the same fields itself. The code calls this defence in depth: the schema is the contract, but a future caller might not go through it.

Tools carry honest annotations.

readOnlyHint:    false  — it writes
destructiveHint: true   — appends a commit to a real git repo
idempotentHint:  false  — calling twice with the same args creates two demos

These are hints to the model about what it can safely retry. add_demo is not idempotent and saying otherwise would invite exactly the double-write it warns about.

Read-only first, writes later. Stages 1–2 shipped with no write path at all. add_demo landed only once the read tools had been running against the real vault long enough to trust the parsing. There was no deadline — that's just the order that made the mistakes cheap.

Errors return, they don't throw. Every tool returns isError: true with a human-readable reason — missing env var, missing folder, malformed frontmatter, 401, 422, network down. An MCP server that throws gives the model a stack trace to hallucinate around. One that explains gives it something to say to the user.


Related MCP server: git-mcp

The bug that justifies all of it

While building the data layer, list_releases returned 1 of 12 releases. No error. No warning. Just a confident, wrong, almost-empty list.

The cause: gray-matter's default YAML engine treats a duplicate map key as a fatal parse error and returns {} for the whole file. The vault had duplicate keys in real files — a separate script had been appending ig_posted: true twice. Eleven records were being silently dropped on the floor.

Fix was the yaml package with { uniqueKeys: false } — lenient, last-duplicate-wins, parses all twelve.

The point isn't the fix. The point is that nothing failed. No exception, no red text. If I'd trusted the output, an AI would have been confidently telling me I had one release. That's the whole reason this repo is paranoid: the dangerous failures are the quiet ones.


Quick start

npm install
npm run build            # TS → dist/
npm run inspect          # MCP Inspector web UI, call the tools by hand

Attach to Claude Desktop — add to claude_desktop_config.json:

{
  "mcpServers": {
    "berbotu": {
      "command": "node",
      "args": ["/path/to/berbotu-mcp/dist/index.js"],
      "env": { "BERBOTU_VAULT_PATH": "/path/to/vault" }
    }
  }
}

Read tools need BERBOTU_VAULT_PATH. add_demo also needs VAULT_WRITE_PAT — a fine-grained GitHub PAT scoped to one repo with Contents: Read and write. See .env.example.

Tools

Tool

Args

Returns

ping

none

version, vault path, pid, node — confirms it's alive and pointed at the right vault

list_demos

stage? limit?

[stage] artist — title ⭐score (received YYYY-MM-DD), newest first

list_releases

stage? includeEps? includeTracks? limit?

[stage] YYYY-MM-DD · artist — title (single|EP · genre)

add_demo ✍️

artist track stage? receivedDate? notes?

creates a demo file as a GitHub commit

Honest limits

  • stdio only. Local. HTTP transport + JWT auth was the next stage and hasn't been built — there was no reason to.

  • One writer. No conflict handling beyond the SHA existence check. Fine for one operator, wrong for a team.

  • Read tools are filesystem, writes are API. A deliberate split — reads want to be fast and local, writes want to be auditable. It does mean a write isn't visible to a read until the repo syncs.

  • Four tools. Small on purpose. Every tool with write access is a thing that can be wrong at 3am.

Stack

TypeScript · @modelcontextprotocol/sdk · zod · yaml · GitHub Contents API · stdio

Built AI-assisted. I'm not an engineer and I'm not trying to be — I'm someone who knows what to build and how to keep it from breaking things.

Available Tools

4 tools
add_demoAdd a new demoA
Destructive

Creates a new demo entry in the Berbotu vault by committing a Markdown file to the berbotu/vault GitHub repo (path: 00 Releases/Demos/{ARTIST} - {TITLE}.md). Use this when the user wants to log a fresh submission — e.g. 'add a demo from RULACID called UNTITLED', or 'log a new demo from the email I just pasted'. REQUIRED: artist + track. Both must be non-empty strings. OPTIONAL: stage (default 'Listening'), received_date (default today UTC), notes (default empty). Refuses to overwrite if a demo with the same artist+title already exists — in that case the user should edit via admin.berbotu.com instead. On success returns the created file path, the full frontmatter that was written, and a permalink to the GitHub commit so the user can audit the change.

Inputs: track (required), artist (required), notes, stage, +1 more.

list_demosList demosA

Lists demo submissions from the Berbotu vault, sorted newest-first by received_date. Each item has: artist, track title, status (stage), received date, and an optional 1-5 star score. Use this when the user asks about incoming submissions, the demo pipeline, A&R inbox, or anything like 'what demos have we got?'. Optional stage arg filters by stage substring (case-insensitive) — e.g. 'listening', 'interested', 'pass'.

Inputs: limit, stage.

list_releasesList releasesA

Lists track + EP releases from the Berbotu vault, sorted newest-first by release_date. Each item has: title, artist, release date, status (stage), and EP/single flag. Use this when the user asks about the label catalog, scheduled releases, what's coming out, what shipped, or anything about 'releases'. Optional stage filter (substring, case-insensitive) — common: 'Released', 'Scheduled', 'DITTO', 'On Hold'.

Inputs: limit, stage, includeEps, includeTracks.

pingHealth checkA

Returns 'OK' plus server name, version, vault path, current timestamp, process id, and Node.js version. Use this to confirm the Berbotu MCP server is reachable and configured correctly.

Inputs: no named parameters.

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 4 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: ping for health check, list_demos for demo submissions, list_releases for catalog releases, and add_demo for creating new entries. No overlapping resource or action.

    Naming Consistency5/5

    Tools follow a consistent verb_noun pattern with snake_case (list_demos, list_releases, add_demo). ping is a simple verb but is standard and does not disrupt the pattern.

    Tool Count4/5

    Four tools is small but well-scoped for a niche vault/music label server. It covers health, listing, and creation, though could add more operations in the future.

    Completeness4/5

    The core workflow—check health, view demos and releases, add new demos—is covered. Missing update/delete operations, but this is intentionally delegated to the admin web interface, so no critical dead ends.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      A unified MCP server with composable tools for GitHub operations, file management, shell execution, kanban boards, Discord messaging, and package management. Features role-based security, HTTP/stdio transports, and a web-based development UI.
      GPL 3.0
    • F
      license
      Not graded
      quality
      D
      maintenance
      Standalone MCP server for GitHub that enables repository management, branch operations, pull request handling, and commit retrieval via tools listed in the README.
      1
      -
    • F
      license
      Not graded
      quality
      D
      maintenance
      A minimal MCP server for homelab environments, providing demo tools like ping and echo over Streamable HTTP for testing client-server integration.
      -