Custom MCP · Three-Pattern Architecture

Content & Research
MCP Server

A custom MCP server I built and use daily. Five tools, three deliberate patterns — retrieval, critique, mutation. Claude decides; the tools enforce the rules.

5 MCP tools 3 deliberate patterns Notion-backed Node · TypeScript
At a glance 30-second read

What it is

A custom MCP server in daily use. Five Notion-backed tools across three patterns: retrieval (returns options for Claude to choose), critique (flags what's wrong), mutation (writes the change). Tools enforce constraints; Claude reasons.

Maps to

AI / Agent Engineer · Applied AI Engineer · AI Tooling Engineer · Developer Experience (AI)

Skills demonstrated

  • Custom MCP server design from scratch
  • Three-pattern architecture: retrieval / critique / mutation
  • Tool-as-constraint thinking (Claude as decision-maker)
  • Notion API integration with structured data modeling
  • Dry-run / preview pattern for safe mutations
MCP SDK Node TypeScript Notion API Zod
The Brief

Most projects that use Claude treat it as a generator wrapped in code. You write a function, the function calls Claude, Claude returns text, the function parses the text back into something usable. Claude does the work; the code is plumbing.

This project flips that. Claude is the decision-maker; the tools are the constraints — rules about what fits my voice, my color library, my workflow. Three patterns, deliberately: retrieval (the tool returns options, Claude picks), critique (the tool flags problems, Claude rewrites), mutation (Claude decides, the tool writes the change).

The tools never call Claude; Claude calls the tools. This page was drafted, voice-checked, and scaffolded by Claude using the same five tools the page documents.

01 — The Tools 5 tools · 3 patterns · ~785 lines of source code

Five tools, three patterns.

Pattern, name, role. Each tool is one file. Inputs are validated on every call with Zod.

Retrieval
vault_search
Search my Inspiration Vault on Notion. Filter by section. Matches both the entry title and the first ~20 blocks of body content.
Retrieval
palette_suggest
Pulls color palettes from my Color Lab on Notion — each with hex codes, mood, category, and notes. Returns options, not a ranking. Picking the right one is Claude's job.
Critique
voice_check
Runs a piece of writing against my voice rules — no hedging, no filler transitions, no marketing warmth, no repetition, short sentences. Returns the specific violations and a verdict. Does not rewrite (that's a separate decision).
Mutation
vault_capture
Saves a new entry to my Vault on Notion, formatted to match the layout my command-line tool already uses. Section is set via a tag in the title.
Mutation
project_scaffold
Generates a new project card and writes it into the site's index.html. Auto-numbers the project. Supports a "preview-only" mode (dry_run).
Design rationale The pattern label matters more than the tool name. Claude picks better when it knows which of the three patterns it's reaching for. The three patterns — retrieval, critique, mutation — are the structure; each tool is one case of a pattern.
02 — A real workflow captured 2026-05-10 · this page

A real workflow.

The traces below are real. The card on the home page that linked you here was generated by the third tool call in this sequence.

step 1 of 3
tools/call · palette_suggest [ RETRIEVAL ]
request
{
  "brief": "AI engineering, restrained,
            design-engineer aesthetic,
            signal-orange-friendly",
  "limit": 4
}
response (4 of 16 palettes)
the candidate I picked
{
  "name": "Bone-White Editorial — Signal Accents",
  "hexes": [
    "#F4F0E8", "#C8102E",
    "#111111", "#E8601C",
    "#D4CAAD"
  ],
  "notes": "The benchmark. Bone-white
    carrier with a hot-red signal.
    Signal orange as the one
    controlled disruption..."
}
Reasoning The brief asked for restrained, signal-orange-friendly. The notes on this palette read "Signal orange as the one controlled disruption." The server didn't rank — it handed me four options, each with its color codes, mood, and author notes. My pick was the one whose author's own note matched the brief word-for-word. The options carry their own justifications, so Claude matches against them rather than scoring from scratch.
step 2 of 3
tools/call · voice_check [ CRITIQUE ]
verdict ship violations 0 context project_card
draft
Five custom Claude tools I built to extend
my own creative workflow — find ideas in my
vault, suggest a color palette, check copy
in my voice, scaffold a new project page,
save a new idea. The tools hand Claude the
options; Claude does the picking and the
writing. Proves: AI tool design, server
engineering, and the idea of encoding
taste as rules rather than agents.
verdict + violations
{
  "verdict": "ship",
  "violation_count": 0,
  "violations": [],
  "note": "Heuristics passed.
    Consider whether the copy actually
    says something — heuristics
    don't catch empty."
}
Reasoning Verdict ship means the rule-based checks found nothing — no hedge words, no filler transitions, no marketing warmth, no repeated phrases, no oversize sentences. The note states the limit: rules can't catch empty writing. That's why the tool doesn't rewrite. Rewriting is a tone judgment; rule-checking is enforcement.
step 3 of 3
tools/call · project_scaffold (dry_run) [ MUTATION ]
file index.html auto-detected 008 written false (dry_run)
@@ around line 1688 @@
-     </div>
+
+       <!-- Project 008 — Content & Research Automation (ACTIVE) -->
+       <a href="008-six-percent-mcp.html" class="exhibit-card live reveal" aria-label="Open Project 008 — Content & Research Automation">
+         <div class="exhibit-art" aria-hidden="true">
+           <div class="exhibit-art-num">008</div>
+           <div class="exhibit-art-glow"></div>
+           <div class="exhibit-art-icon">⌬</div>
+         </div>
+         <div class="exhibit-plaque">
+           <div class="exhibit-plaque-num">Project 008 · MCP</div>
+           <div class="exhibit-plaque-title">Six Percent <span style="color:var(--signal);">MCP</span></div>
+           <div class="exhibit-plaque-medium">@modelcontextprotocol/sdk · Node ESM · Notion API · Zod</div>
+           <div class="exhibit-plaque-desc">
+             [the description that just passed voice_check]
+           </div>
+           <div class="exhibit-before-after">
+             <span class="ex-before">AI As Wrapper</span>
+             <span class="ex-arrow">→</span>
+             <span class="ex-after">Tools Returning Contracts</span>
+           </div>
+           <div class="exhibit-op-tags">
+             <span class="ex-op-tag">// MCP</span>
+             <span class="ex-op-tag">// Tool Design</span>
+             <span class="ex-op-tag">// Claude Engineering</span>
+           </div>
+         </div>
+         <div class="exhibit-floor"></div>
+       </a>
+
+     </div>
Reasoning The scaffold tool finds the right spot in index.html — the project-gallery container — and inserts the new card just before it closes. Indentation matches the existing cards. The new project number is auto-detected by scanning for the existing <!-- Project NNN --> markers. dry_run returns the change as a preview, not a write — so Claude can review it before approving. After approval, the same call (without dry_run) wrote the card you saw on the home page.
03 — Inside voice_check rule-based · predictable · no AI call

Constraints, encoded.

The voice rules from my project's instructions, encoded as plain-text patterns. Read aloud: hedge words, filler transitions, marketing warmth. The tool flags every match with the exact excerpt and a one-line suggestion.

no_hedging
perhapsmaybemightpossiblyprobablysomewhatsort ofkind ofI thinkI believearguablytends toseems to
no_filler
thereforethusthat saidbasicallyessentiallyto be honestat the end of the daywhen it comes toin terms ofin many ways
no_marketing_warmth
amazingincredibleawesomepassionatejourneydive deepleveragesynergyelevateempowerunleashtransformcutting-edgegame-changer
Why heuristic, not LLM-backed A Claude-powered voice_check would be more expressive but less reliable. Claude already reads the draft; asking it to call a tool that just asks Claude again is a circular system. Rule-based detection is what the tool can offer that Claude can't: the rules are visible, the failure cases are knowable, the output is repeatable. The note on every passing verdict — "heuristics don't catch empty" — states what the tool can't check.