design-pattern-mcp
design-pattern-mcp
An MCP (Model Context Protocol) server that provides design-pattern selection expertise to AI coding agents. Given a free-text description of a system, it analyses the problem, retrieves matching design patterns (from 486 built-in patterns, each realized in 13 concrete languages plus a generic binding), generates a concrete design with a primary pattern, supporting patterns, participants, interactions, implementation steps, and language-bound variants, and evaluates it against eight design-level quality axes (testability, extensibility, maintainability, simplicity, performance, reliability, security, scalability). The pipeline (analyze → weights ∥ retrieval → generate → evaluate → bounded refine) and its wire contract are specified in docs/implementation-guide.md.
Table of Contents
Related MCP server: MCPatterns
⚡ Quickstart
# 1. Clone
git clone https://git.odvlh.xyz/olli/design-pattern-mcp.git && cd design-pattern-mcp
# 2. Add your API key
export MINIMAXAI_API_KEY=your_key_here
# 3. Build + start (three images: MCP server + TEI embed + TEI rerank)
make docker-build-all
make docker-up
# 4. Demo (analyze-only: fast. For the full design: make client-design)
make client-analyze
make client-async # full design via the background job trioServer starts on streamable-http at http://localhost:8822/mcp (dev compose maps host 8822 → container 8050). make client-analyze / make client-design target http://localhost:8822/mcp; override with CLIENT_URL.
Install via AI Agent
To have an AI coding agent install the Docker stack from prebuilt images (no local build), point it at INSTALL.md — the non-interactive agent runbook (copy-paste commands, expected outputs, verification probes).
Clone this repo, then follow INSTALL.md Path A (compose stack, port 8822).
My generator API key is: sk-...Pick one path in INSTALL.md:
Path A — Compose stack (
:8822): evaluating / developing, no sudo needed. Own TEI sidecar copies.Path B — systemd stack (
:8812): persistent host service, starts at boot, needssudo. Uses the sharedpattern-tei-infraTEI stack.
The agent needs a repo clone, Docker ≥ v2.20 with group membership, ~8 GB free, a MINIMAXAI_API_KEY (or another provider per INSTALL.md#switching-the-generator-llm), and linux/amd64 (on arm64 it builds locally via make docker-build-all). Dev and systemd stacks run concurrently on their default host ports (8822 vs 8812). After install, continue with Connect Your Agent below.
🔌 Connect Your Agent
Claude Code
# Install (one-time)
uv pip install -e .
# Run as stdio subprocess — pass the API key via env
claude mcp add design-patterns \
-e GENERATOR_PROVIDER=minimax \
-e GENERATOR_MODEL=minimax/MiniMax-M2.7 \
-e GENERATOR_BASE_URL=https://api.minimax.io/v1 \
-e GENERATOR_API_KEY=your_key \
-- design-pattern-mcp --transport stdioStdio mode needs the TEI endpoints reachable (default http://design-pattern-tei-embed:8080 and the reranker URL from config/config.json) — either run the compose stack's sidecars or set EMBEDDER_BASE_URL / RERANKER_BASE_URL to your own. For a no-network smoke run, start with --offline (deterministic lexical-only retrieval instead of TEI).
OpenCode
OpenCode uses HTTP transport. Start the server first, then configure opencode:
# Terminal 1: start the server
make docker-up
# or locally:
uv run design-pattern-mcp --transport streamable-http --port 8050
# Terminal 2: add to ~/.config/opencode/opencode.json{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"design-patterns": {
"type": "remote",
"url": "http://localhost:8822/mcp"
}
}
}Note: the generator API key is read by the server from its config file (
config/config.json,{env:...}placeholders), not from opencode's environment.
Codex CLI
# Install (one-time)
uv pip install -e .Add to ~/.codex/config.toml:
[mcp_servers.design-patterns]
command = "design-pattern-mcp"
args = ["--transport", "stdio"]
[mcp_servers.design-patterns.env]
GENERATOR_PROVIDER = "minimax"
GENERATOR_MODEL = "minimax/MiniMax-M2.7"
GENERATOR_BASE_URL = "https://api.minimax.io/v1"
GENERATOR_API_KEY = "your_key"Or via CLI:
codex mcp add design-patterns \
-e GENERATOR_API_KEY=your_key \
-- design-pattern-mcp --transport stdio🧑🏫 SKILL for AI Agents
The SKILL in skills/design-pattern-mcp/ is written for Oh My Pi (OMP), where this server's tools are reached as xd://mcp__design_pattern_local_* devices and every MCP request is bounded by a deadline (OMP_MCP_TIMEOUT_MS → per-server timeout → 30 s). It teaches the agent which entry point fits that deadline, how to read the results, and the full workflow recipes.
skills/design-pattern-mcp/
├── SKILL.md # OMP constraints, entry-point decision guide, device quick reference
└── references/
├── tools.md # 9 tool signatures, arg shapes, status codes
└── workflows.md # workflow recipes, result interpretation, troubleshootingInstall for OMP — copy the skill directory into OMP's user skills root:
cp -r skills/design-pattern-mcp ~/.omp/agent/skills/The skill then tells the agent:
Which entry point fits OMP's 30 s deadline: the async job trio by default;
select_design_patterns(full pipeline) takes 2–10 minWhich path fits the job:
analyze_designwhen the agent writes the code itself (decisions and rationale, no generated design), the full pipeline when the caller wants the designThat
job_idis the only durable handle — persist it (SQLite job store,JOBS_DB)How to phrase
description(component names + responsibilities + constraints), andlanguage,paradigm,focusas separate structured argumentsHow to interpret
overview.score(0–100),findings[], and per-part selections
The tool schemas in references/ are client-agnostic; only the deadline/device guidance is OMP-specific.
🧪 Use the Tools
All pipeline tools take a free-text description as the primary argument. The examples below show the exact tool call shape so you can use them in any MCP client or API consumer.
Design your first selection
In Claude Code (or any MCP client), paste the natural-language instruction:
We're building the ingestion path of an IoT platform: a Kafka consumer
service parses 10k events/sec, an enricher adds geolocation from Redis,
and a writer persists to InfluxDB and S3. Components: consumer, parser,
enricher, sink. Constraints: at-least-once delivery, backpressure, Python.Your agent calls select_design_patterns internally. The server returns the DesignResult: overview (primary pattern, supporting patterns, principles, score, reasoning), applications[] (participants, interactions, implementation_steps, code_sketch, language_binding with the bound variant), interactions[], quality_attributes, test_strategy, and risks[].
Coding agents that write the code themselves should call analyze_design instead. It runs the same ANALYZE phase (§1–§8: brief repair → decision parts → per-part retrieval → quality weights → selection) and stops there: you get the repaired brief, the decision parts and one selection per part (primary, supporting patterns, ranked alternatives, veto ledger, weights) with no generated applications and no evaluation. The selection names what to apply per part, not how — participants, implementation steps,
code sketches and language bindings are materialized by generate_design from that payload (or
read via get_design_pattern per pattern); implement from the applications, never from the
bare pattern names. select_design_patterns remains the design path — it continues through GENERATE → EVALUATE, where the bulk of a full run's wall clock goes. Either way, the decisions come from the same ANALYZE stage — a fresh LLM analysis per call, so neither path is idempotent; pass the analyze_design payload to generate_design if you later want the materialized applications.
Or call tools directly from your agent:
Call select_design_patterns with:
description: "IoT ingestion path: Kafka consumer (10k events/sec) → JSON parse
→ Redis geo-enrich → InfluxDB + S3 sink. At-least-once, backpressure."
language: "python"
Call list_design_patterns() # all 486 patterns
Call list_design_patterns(category="workflow-messaging")
Call get_design_pattern(name="process-manager")Async job pattern: submit_design_job + get_design_status
For clients with short request timeouts. submit_design_job returns a job_id immediately; poll get_design_status until done:
# Step 1: start the job
Call submit_design_job with:
description: "IoT ingestion path: Kafka consumer → parse → Redis enrich → InfluxDB + S3"
# Step 2: poll every 10-30 seconds
Call get_design_status with:
job_id: "<job_id from step 1>"
# → status is "pending" | "running" | "completed" | "failed" | "cancelled"
# When status is "completed", the full DesignResult is in result
# When status is "failed", the error is in errorIn Python (via the MCP client API — see examples/design_pattern_client_async.py):
import asyncio
from fastmcp import Client
SERVER = "http://localhost:8822/mcp"
POLL_EVERY = 15 # seconds
async def main():
client = Client(SERVER)
async with client:
# Step 1: start the job
result = await client.call_tool(
"submit_design_job",
{"description": "IoT ingestion: Kafka → parse → enrich → sink"},
)
job_id = result.data["job_id"]
print(f"Job started: {job_id}")
# Step 2: poll until terminal state
while True:
await asyncio.sleep(POLL_EVERY)
status = await client.call_tool("get_design_status", {"job_id": job_id})
data = status.data
print(f" status={data['status']}")
if data["status"] in ("completed", "failed", "cancelled"):
break
print(data.get("result", data)) # full DesignResult when completed
asyncio.run(main())See examples/design_pattern_client_async.py for the complete runnable example. Run it with:
make docker-up # Terminal 1
make client-async # Terminal 2🛠️ Tools at a Glance
Tool | Description |
| Design path for callers that want the generated design: full pipeline (analyze → weights ∥ retrieval → generate → evaluate → refine, |
| Analysis-only path for coding agents that write the code themselves: requirements → brief, decision parts and per-part selection (primary, supporting, alternatives, vetoes, weights) with no generated design. Follow with |
| Materialize applications from a prior |
| Score an existing design JSON against the eight quality axes. Long-running (~30 s). |
| Start a background selection job and return a |
| Poll job status; returns the full |
| Cancel a running job (best-effort; takes effect at the next pipeline stage boundary). |
| List patterns with name/category/description; filter by |
| Full pattern JSON for one name (loader-resolvable, alias-aware). |
Description, language, paradigm, focus are structured parameters — pass them as separate tool arguments, not embedded in the description text. Variant selection is signal-based: there is no catalog-wide variant_id enum (variant ids are per-(pattern, language) and change with catalog edits; valid ids are discoverable via the design-patterns://variants resource and previous runs' bound_variant).
💬 Prompts
This server also exposes four user-invoked workflow prompts (slash commands in MCP clients). Unlike tools, the LLM does not autonomously invoke prompts — the user selects one and fills in its arguments. Each prompt encodes a tested tool-orchestration recipe, and none hardcode pattern names (category guidance quotes the live catalog's own structure).
Prompt | Args | What it does |
| description*, language, architecture_style, paradigm, focus | Guided end-to-end selection workflow |
|
| Live catalog discovery via the |
| description* | Compare the catalog's selection against an existing design |
| decision*, candidates* | Two or three named patterns side-by-side for one decision; one selection run per candidate |
* = required argument
Tool-only clients
Clients that only support the tools protocol (no native prompts/list or prompts/get) can access all four workflow prompts via the generated list_prompts and get_prompt tools, which route through the server's middleware chain exactly as native prompt calls do.
📖 Pattern Catalog
Via MCP tools (recommended — works in all clients)
list_design_patterns() # all 486 patterns
list_design_patterns(category="concurrency-resilience") # filter by category
get_design_pattern(name="acceptor-connector") # full pattern JSONValid category values (the nine decision groups, catalog sections 1–9): domain-modeling, construction-composition, behavior-selection, interaction-boundary, workflow-messaging, persistence-state, concurrency-resilience, security-verification, specialized-components.
Via MCP resources
mcp_read_resource(uri="design-patterns://variants")Groups every pattern's language realizations by language, then pattern — the realization vocabulary for language/paradigm/focus hints and variant selection.
What one pattern record contains
The loader (src/patterns/loader.py) joins each record from 14 files: one generic entry plus one per concrete language (13 languages: C, C++, C#, Go, Java, JavaScript, Kotlin, Python, Ruby, Rust, Scala, Swift, TypeScript). Each language binding carries support (native / idiomatic / discouraged), idiom_name, implementation_notes, an optional code_sketch, pitfalls, and — for discouraged support — a required alternative. Language bindings can declare variants[] (per-language realizations, 4,953 across the catalog) with a default_variant. Each record also declares related_patterns (loader-resolvable canonical names only) and related_patterns_notes (per-link prose distinctions).
Alias resolution is built in: the loader turns 993 raw alias entries → 978 distinct after case-folding → 966 indexed alias mappings, dropping ambiguous aliases (an alias that is itself another record's name or claimed by two records; the ambiguous_aliases health field lists them), so get_design_pattern(name="circuit-breaker") and its alias spellings resolve to the same record. Startup fails fast when the catalog's enums drift from docs/pattern-schema.json (assert_enums_in_sync).
Working Together with architecture-pattern-mcp
This server answers how each unit is coded (design patterns); the sibling architecture-pattern-mcp answers which system shape to build (architecture). An architecture pattern structures the whole system — its components, their relationships, and its API/data/event contracts (e.g. event-driven, pipe-and-filter, microservices, chosen from the sibling's 40-pattern catalog). A design pattern solves one recurring implementation problem inside a single component — its participants, interactions, and language-bound code (e.g. observer, process-manager, circuit-breaker, chosen from the 486-pattern catalog above with 13 language bindings each).
Recommended order — macro first, micro second:
1. Call the sibling's design_architecture with:
requirements: "ETL pipeline for IoT: Kafka → JSON → Redis geo-enrich → InfluxDB + S3"
domain: "data-processing"
# → components (Kafka source, parser filter, enricher, sinks),
# relationships, API contracts, data models, event contracts
# Keep design_architecture for callers that want the design: the sibling's LLM
# generates the architecture artifact (5–10 min) rather than only advising.
2. For each component with non-trivial internal logic, ask for the pattern decisions:
- coding agent writes the code itself → analyze_design with:
description: "<component name + responsibilities + constraints from step 1>"
language: "python"
# → decision parts + per-part selection with rationale; no generated applications
- caller wants the generated pattern design → select_design_patterns with:
description: "<component name + responsibilities + constraints from step 1>"
language: "python"
# → primary + supporting patterns, participants, implementation_steps,
# code_sketch with the bound language variantBoth pattern calls run the same ANALYZE stage (§1–§8); analyze_design stops there, before generation and evaluation. Each call runs its own LLM analysis (neither tool is idempotent), so two calls can select differently on the same description.
Concrete handoff on the IoT example: the sibling emits the pipe-and-filter architecture (Kafka source → JSON parser filter → geolocation enricher → InfluxDB/S3 sinks, with at-least-once delivery and backpressure in the contracts). The geolocation enricher's cache-miss path then comes here: analyze_design returns the circuit-breaker + retry decision with its rationale when your agent writes the enricher, while select_design_patterns additionally materializes the applications (participants, implementation_steps, a Python code_sketch) when the caller wants the generated design — internals the architecture-level component contract references but never implements.
On the analyze_design path, run generate_design on the returned payload (or
get_design_pattern("circuit-breaker") for the Python binding and pitfalls) before writing
the enricher code.
Run both servers side by side (dev host ports :8060 architecture, :8822 design-pattern; systemd :8050 / :8812). They are independent — either order works for exploration — but architecture-first keeps component boundaries stable while this server fills in the internals.
Install Alternatives
Docker (manual)
# Build the three images (dense vector cache generated and baked in)
make docker-build-all
# Run with your API key
MINIMAXAI_API_KEY=your_key make docker-upFirst docker-build-all generates the FAISS dense cache (data/dense-cache/) via the TEI embed sidecar and bakes it into the server image; make docker-build-nocache skips that (first startup then embeds the corpus, ~19 min).
Both compose stacks back that cache directory with a named Docker volume
(design-pattern-mcp-dev-dense-cache / design-pattern-mcp-systemd-dense-cache,
mounted at /app/data/dense-cache). A fresh volume is seeded from the image's baked
snapshot, and every later write-back lands in the volume — so a restarted or recreated
container reloads the vectors instead of re-embedding the catalog (one probe embedding
instead of ~486 document embeddings). docker compose down keeps the volume.
Two operational consequences: an existing volume shadows a newer baked snapshot
after an image rebuild, and the fingerprint guards then force exactly one re-embed — so
after a catalogue- or model-changing rebuild delete the volume first
(docker volume rm design-pattern-mcp-dev-dense-cache) and let up -d re-seed it.
And DENSE_CACHE_MODE=off disables the cache entirely — the compose stacks forward the
host value into the container, and a host run reads it directly. Details:
Dense embedding cache.
Local Development (uv)
Prerequisites: Python 3.12+, uv
# Install
make install
# Configure — the shipped wire config with {env:...} placeholders
export GENERATOR_PROVIDER=minimax
export GENERATOR_MODEL=minimax/MiniMax-M2.7
export GENERATOR_BASE_URL=https://api.minimax.io/v1
export GENERATOR_API_KEY=your_key
# Dense cache (host runs): the image default /app/data/dense-cache does not exist
# on a workstation, so point it at the gitignored repo snapshot. `make embed-cache`
# fills it; otherwise the first start embeds once and writes it (later starts warm).
export DENSE_CACHE_PATH="$PWD/data/dense-cache"
# Run the server
uv run design-pattern-mcp --transport stdio # for Claude Code / Codex
uv run design-pattern-mcp --transport streamable-http --port 8050 # HTTP
uv run design-pattern-mcp --health # offline readiness check (config + catalog + index)Or use the installed console script (after make install): design-pattern-mcp.
The TEI sidecars are required for full retrieval quality: a dense leg (Qwen3-Embedding-0.6B via the embed sidecar) and a BM25 leg are fused at startup (service_healthy ordering guarantees the embedder is up before the server accepts requests — a failed index warm-up refuses server start). The reranker sidecar (gte-reranker-modernbert-base) reranks candidate patterns. Without reachable sidecars, start with --offline (deterministic lexical-only retrieval, no network).
TEI startup: both sidecar images (
docker/Dockerfile.tei-embed,docker/Dockerfile.tei-rerank) ship a self-built TEI router (v1.9.4 + unmerged PR #884) withWARMUP_TOKENS=0by default — this skips the synthetic warmup pass (measured ~131 s embed / ~78 s rerank) so containers become ready after weight load only. Override per sidecar viaTEI_WARMUP_TOKENS/TEI_RERANK_WARMUP_TOKENS(positive N warms with N tokens instead; values >MAX_BATCH_TOKENSare rejected at startup).
GPU TEI: the shipped sidecars are CPU images (cpu-1.9). On a host with a TEI-CUDA-supported NVIDIA GPU (compute capability ≥ 7.5) plus the NVIDIA container toolkit, make docker-build-tei-embed-gpu builds the embed sidecar on the CUDA base (ghcr.io/huggingface/text-embeddings-inference:cuda-1.9) and make docker-up-gpu starts the dev stack with docker/docker-compose.gpu.yml (reserves 1 GPU). The override covers the embed sidecar only — the reranker stays on CPU. On GPU a small positive WARMUP_TOKENS warms kernels at startup instead of skipping the pass.
Already-running embedder/reranker: when embedding and reranking endpoints already run on other servers, point the MCP server at them via EMBEDDER_BASE_URL (OpenAI-compatible route, http://<host>:8080/v1) plus RERANKER_BASE_URL (http://<host>:8080) and skip the sidecars. The embedder forwards EMBEDDER_MODEL (default openai//data/embedding-model) and EMBEDDER_API_KEY through LiteLLM — provider/model syntax is documented in the LiteLLM embedding docs. The reranker is not a LiteLLM client — it POSTs {base_url}/rerank directly (src/patterns/safe_tei_rerank.py), so RERANKER_MODEL (default Alibaba-NLP/gte-reranker-modernbert-base) is a TEI model name, not a LiteLLM model string. A remote embedder serving a different model invalidates the baked dense cache — regenerate it with make embed-cache so vectors match the serving model.
Configuration
config.json
The server reads config/config.json relative to the repo (override with --config-path or CONFIG_PATH). Every value is a {env:VAR:-default} placeholder that expands at load time, so the shipped file doubles as the env-var reference. The schema (abridged):
{
"logging_level": "{env:LOGGING_LEVEL:-INFO}",
"transport": "{env:TRANSPORT:-stdio}",
"host": "{env:HOST:-0.0.0.0}",
"port": "{env:PORT:-8050}",
"generator": {
"provider": "{env:GENERATOR_PROVIDER:-openai}",
"config": {
"model": "{env:GENERATOR_MODEL:-gpt-4o-mini}",
"base_url": "{env:GENERATOR_BASE_URL:-https://api.openai.com/v1}",
"api_key": "{env:GENERATOR_API_KEY:-}",
"temperature": "{env:GENERATOR_TEMPERATURE:-0.1}",
"timeout_seconds": "{env:GENERATOR_TIMEOUT_SECONDS:-120.0}",
"generation_batch_size": "{env:GENERATOR_GENERATION_BATCH_SIZE:-5}",
"generation_max_concurrency": "{env:GENERATION_MAX_CONCURRENCY:-2}",
"circuit_breaker_failure_threshold": "{env:GENERATOR_CIRCUIT_BREAKER_FAILURE_THRESHOLD:-3}",
"circuit_breaker_cooldown_seconds": "{env:GENERATOR_CIRCUIT_BREAKER_COOLDOWN_SECONDS:-60.0}"
}
},
"embedder": { "provider": "{env:EMBEDDER_PROVIDER:-tei}", "config": { "base_url": "{env:EMBEDDER_BASE_URL:-http://design-pattern-tei-embed:8080}" } },
"retrieval": { "bm25_weight": "{env:RETRIEVAL_BM25_WEIGHT:-0.3}", "dense_weight": "{env:RETRIEVAL_DENSE_WEIGHT:-0.7}", "max_concurrent_parts": "{env:RETRIEVAL_MAX_CONCURRENT_PARTS:-1}" },
"reranker": { "rerank_text": "{env:RERANK_TEXT:-full}", "rerank_top_n": "{env:RERANK_TOP_N:-10}" },
"reasoning": { "enabled": "{env:REASONING_ENABLED:-true}", "per_phase": { "analyze": { "enabled": "{env:REASONING_ANALYZE_ENABLED:-true}" }, "weights": { "enabled": "{env:REASONING_WEIGHTS_ENABLED:-true}" }, "generate": { "enabled": "{env:REASONING_GENERATE_ENABLED:-true}" }, "evaluate": { "enabled": "{env:REASONING_EVALUATE_ENABLED:-true}" } }, "max_injected_chars": "{env:REASONING_MAX_INJECTED_CHARS:-}" },
"validation": { "max_retries": "{env:VALIDATION_MAX_RETRIES:-1}", "retry_on_fail": "{env:VALIDATION_RETRY_ON_FAIL:-true}", "truncation_rerun": "{env:VALIDATION_TRUNCATION_RERUN:-repair}", "truncation_boost_max_completion_tokens": "{env:VALIDATION_TRUNCATION_BOOST_TOKENS:-65536}" },
"prompts": { "stable_prefix_first": "{env:PROMPT_STABLE_PREFIX:-false}" },
"loop": { "max_tries": "{env:LOOP_MAX_TRIES:-2}", "min_quality_score": "{env:LOOP_MIN_QUALITY_SCORE:-50}" },
"tasks": { "deadline_seconds": "{env:TASKS_DEADLINE_SECONDS:-1800.0}", "heartbeat_enabled": "{env:TASKS_HEARTBEAT_ENABLED:-true}", "heartbeat_interval_seconds": "{env:TASKS_HEARTBEAT_INTERVAL_SECONDS:-30}" },
"jobs": { "db_path": "{env:JOBS_DB:-~/.config/design-pattern-mcp/jobs.db}" }
}Generator LLM (LlamaIndex LiteLLM)
The generator LLM is accessed through the LlamaIndex LiteLLM integration. All provider settings follow LiteLLM's model syntax: <provider>/<model>. The dev stack and benchmark harness default to MiniMax:
Config / env | Dev-stack default |
|
|
|
|
|
|
| falls back to |
Provider list and syntax: LiteLLM Providers documentation. temperature, top_p, top_k, timeout_seconds, and the circuit-breaker knobs map to the corresponding LiteLLM/provider parameters.
Dense embedding cache (restart-durable retrieval)
On by default. The shipped configuration resolves to dense_cache.mode=auto with
write=true (Pydantic defaults and config/config.json; both compose stacks set the
same values explicitly), and the serving path always passes the cache — the container
stacks need no cache configuration at all, and a host run needs exactly one export
(DENSE_CACHE_PATH, see the Local Development block). It is switched off
only deliberately: DENSE_CACHE_MODE=off (compose stack or host run), mode=off in a
config file, the
--offline/--health paths (deterministic embedder, no cache), and the benchmark
compose override (cold-start measurement).
tests/unit/test_config.py::test_dense_cache_defaults and
::test_shipped_config_validates pin the defaults.
The dense retrieval leg is one FAISS index over every catalog record (486 records → 486
document_text embeddings at startup). That matrix is snapshotted to disk next to a
manifest and reloaded on the next start, so a restart costs one probe embedding
instead of a full corpus pass (~19 min on the CPU TEI sidecar).
Path:
DENSE_CACHE_PATH(/app/data/dense-cachein the image). Contents:manifest.json(the commit marker) +vectors.npy. A directory without a usable manifest is an ordinary miss;mode=requireturns a miss into a startup error (ERR_008),mode=offskips the cache read entirely.Written immediately after a cold embed (
DENSE_CACHE_WRITE=true), so the snapshot is on disk before any later startup step can fail. The vectors file is written before the manifest, so a crash mid-write leaves a directory the next start treats as a miss.Containers persist it in a named volume (
design-pattern-mcp-dev-dense-cache/design-pattern-mcp-systemd-dense-cache) — the snapshot then survives container recreation,docker compose downand image upgrades. A fresh volume is seeded from the image's baked snapshot; an existing one keeps its own, so after a catalogue- or model-changing rebuild delete it (docker volume rm <name>) beforeup -d, otherwise the guards force one re-embed (~19 min) and rewrite the volume. A genuinely cold cache needs a cache-less image (make docker-build-nocache) ormode=off—DENSE_CACHE_MODE=offcovers both the compose stacks and host runs.Rebuilt automatically when the embedding function changes (model string, text instruction, declared
model_repo/onnx_variant), when the catalog changes (record count, name order or anydocument_text—corpus_sha256), whenvectors.npyis absent/shape-invalid/off-norm, or when a probe vector shows a different model behind an unchanged configuration (a redeployed TEI sidecar; cosine belowdense_cache.probe_min_cosine). Each such change costs exactly one re-embed: the rebuilt snapshot is written back with the new identity, so the next restart hits again (a cache baked by an older release, e.g. withoutEMBED_MODEL_REPO, misses once for the same reason). Every decision is logged with the directory it used (JSON logger fields in the compose stacks):Dense cache hit(path,records,mode),Dense cache miss(reason,path),Dense cache written(path,records,dimension),Dense cache disabled(mode,path),Dense cache write failed(path,reason) — plus the retriever's summaryDense vectors resolved(source=cache-hit/embedded/disabled,documents).Best-effort writes: a read-only or full cache directory logs
Dense cache write failedand the run keeps the vectors it just embedded — the cache never fails a start.Single writer: no locking. Run one server process per cache directory (dev and systemd stacks use separate volumes; the benchmark harness pins its own disposable
data/dense-cacheand refuses a cold cache unlessmode=off).Host runs (
uv run python -m src.main) must pointDENSE_CACHE_PATHat a writable directory — the shipped default/app/data/dense-cacheonly exists in the image. Eitherexport DENSE_CACHE_PATH=$PWD/data/dense-cacheso amake embed-cachesnapshot is reused, or point it at~/.config/design-pattern-mcp/dense-cacheand let the first start write it.
Key environment variables
Variable | Default | Description |
| (required) | Generator API key (dev stack falls back to |
|
| LiteLLM provider prefix ( |
|
| Model name; |
|
| API base URL (LiteLLM |
|
| Sampling temperature |
|
| Bounds ONE HTTP request to the model |
|
| Patterns per GENERATE batch |
|
| GENERATE batches in flight at once |
|
| Consecutive qualifying provider failures (timeout, connection, 408/429/5xx, empty response) opening the circuit; auth/configuration 4xx never count |
|
| Open-state window before one half-open probe |
|
| TEI embedder endpoint |
|
| Embedding batch size |
|
| Dense-leg snapshot directory ( |
|
|
|
|
| Persist a re-embedded corpus (best-effort — a failed write logs |
|
| Dense-leg fusion weight (must sum with |
|
| BM25-leg fusion weight |
|
| Per-part rerank concurrency. Cap-2 saturates the CPU TEI rerank sidecar's 16,384-token buffer (HTTP 429) — leave at 1 unless you measured your sidecar |
|
| Rerank input text: |
|
| Rerank top N |
|
| Master switch for the pre-LLM reasoning layer |
|
| Per-phase reasoning switches; a disabled phase contributes an empty reasoning context (no generator call, no subprocess, no cache entry), while an outage in an enabled phase degrades silently to the in-prompt scaffold |
|
| Reasoning steps before each phase's main call |
|
| Per-thought tool-call timeout |
|
| Hard cap on reasoning steps per phase |
| uncapped | Cap on the rendered reasoning trace injected into downstream prompts; overflow is cut and marked |
|
| Fail server startup when a reasoning tool is unreachable (default: degrade, log ERROR) |
|
| Silence reasoning-subprocess stderr; set |
|
| Structured-output repair budget (model-output failures only — provider errors are re-raised, never repair-prompted) |
|
| Truncation handling: |
|
|
|
|
| Reorder each operation's user prompt so the static response-contract block leads and per-run payload trails (MiniMax passive prompt-cache optimization; content-identical either way) |
|
| Design-loop GENERATE→EVALUATE attempts |
|
| Early-stop score threshold |
|
| Whole-run budget for one tool call / job; on expiry the run ends with |
|
| Progress-notification interval (keep below the client's idle timeout) |
|
| SQLite path for the async job trio |
|
| Pattern files directory |
|
|
|
|
|
|
|
| HTTP bind port (container |
|
| Config file path |
CLI flags
Flag | Description |
| Path to the config file (default: |
| Override transport mode |
| Override HTTP bind host (default: |
| Override HTTP port (default: |
| Run the offline readiness check (config + catalog + index) and exit |
| Serve with the deterministic offline embedder/reranker (no network; lexical-only retrieval) |
Structured Reasoning
Before each prompted LLM phase call (analyze / weights / generate / evaluate), the server runs a bounded ThoughtGenerator loop: each step is authored with the generator LLM (one completion per step) and submitted to the shannonthinking and/or code-reasoning MCP servers — structured thinking scratchpads that validate, number, and record each step. The resulting trace is injected into the phase prompt as a <reasoning_context> block. Contract: each thought = 1 LLM completion + 1 MCP tool call, capped by REASONING_MAX_TOTAL_STEPS (default 8). The tools list per phase defaults to both scratchpads; startup health-checks exactly the tools the enabled phases use.
Key properties:
Embedded in Docker — the image bakes both npm packages into the image; the runtime invokes them directly via
node(no network, no npx).Auto-fallback to npx outside Docker — missing embedded entry points fall back to
npx -y <pkg>(first call downloads).Silent per-call degradation — any spawn/timeout/tool failure in an enabled phase logs a WARNING and the phase proceeds with a degraded in-prompt thinking scaffold; a reasoning outage never fails a tool call. A phase disabled via
REASONING_<PHASE>_ENABLED=falseinstead contributes an empty reasoning context — no generator call, no subprocess, no cache entry.Loud startup, soft failure — the lifespan health-check reports reasoning health; set
REASONING_FAIL_FAST=trueto make a broken tool fail startup instead.Trace caching — analyze and generate traces are computed once per design request and reused across design-loop attempts; weights and evaluate traces are per-run.
REASONING_MAX_INJECTED_CHARS— the rendered trace injected downstream can be capped (default uncapped) to cut input tokens on every downstream call.
Latency note: a full run issues 8 reasoning tool calls (2 per phase) as serial subprocesses; reasoning is a material share of end-to-end latency. Measured on the Stage-0 benchmark (MiniMax-M2.7 generator, 18 scenarios, NO_WARMUP=1): shipping config (all phases on) p50 288.4 s / p95 439.6 s; with REASONING_WEIGHTS_ENABLED=false REASONING_EVALUATE_ENABLED=false p50 253.0 s / p95 413.9 s — both arms passed the quality gate (overall hit ≥ baseline Wilson low). The per-phase toggles are the documented way to opt in.
Long-running tools & timeouts
select_design_patterns runs a multi-stage pipeline that takes minutes per call. This is inherent to the workload: the generator LLM processes the fused retrieval candidates, per-part quality weights, and the full output of every previous stage, and produces a large, strictly structured JSON document one token at a time. The design loop repeats generate → evaluate up to LOOP_MAX_TRIES times. If the caller only needs the pattern decisions (the agent writes the code itself), analyze_design runs §1–§8 alone and skips the generate → evaluate loop.
Four bounds govern a run — do not confuse them
Bound | Default | Scope |
| 120 s | ONE HTTP request to the model |
| 1 | Repair re-issues exactly one call, and only for model-output failures |
| 2 | GENERATE→EVALUATE design-loop attempts |
| 1800 s | The whole run; expiry cancels in-flight work and ends with |
The heartbeat defence (applied by default)
Every long-running tool emits progress notifications from a parallel coroutine every 30 seconds (TASKS_HEARTBEAT_INTERVAL_SECONDS). Clients whose idle timers reset on any received data stay alive for the full pipeline duration.
TS-SDK clients (Claude Desktop, Cursor) do not reset their timeout on progress notifications.
The async job trio (for timeout-constrained clients)
submit_design_job(description, language?, ...) → job_id
get_design_status(job_id) → {status, result | error}
cancel_design(job_id) → best-effort, next stage boundarysubmit_design_job returns a job_id in milliseconds; the pipeline runs in a background task and the store is SQLite (JOBS_DB). Poll every 10–30 seconds. This is the fix that works for TS-SDK clients.
Bypassing client timeouts with a direct HTTP client
examples/design_pattern_client.py is a direct Python HTTP client with no client-side idle timeout — one blocking request, full response regardless of duration:
make docker-up # Terminal 1
make client-analyze # Terminal 2: analyze_design only
make client-design # Terminal 2: select_design_patterns, the long synchronous callBoth targets drive the same direct client (--tool analyze|design). make client-design demonstrates that the server correctly completes long requests — the timeout issue is purely a client-side problem. Host applications that wrap MCP (Claude Desktop, Cursor, …) impose their own timeouts; use the async job trio there.
Benchmarks & Verification
This repo treats selection quality and pipeline latency as measured properties, not claims.
Benchmark harness (tests/benchmark/, make benchmark-*): an 18-scenario corpus with holdout split; make benchmark-live NO_WARMUP=1 OUT=benchmarks/<arm> RUN_ID=<id> runs the full pipeline in-process with per-stage attribution (reasoning., llm., rerank, embed — calls, latency, chars/s, repair counters). make benchmark-analyze runs the same seams and stops after ANALYZE (selection hit/recall/score, no generation); make benchmark-compare accepts an analyze/live pair and reports the hit-rate Newcombe interval, the selection metric deltas and the hit flips, with the p95 latency rule marked inapplicable because the e2e delta is the skipped generation work. make benchmark-selfcheck hand-verifies every metric formula. make benchmark-compare A=<baseline> B=<candidate> applies the preregistered rule — candidate wins iff holdout p95 e2e improves ≥ 15 % AND candidate overall acceptable-primary hit rate ≥ the baseline's overall Wilson lower bound — and exits 1 on candidate-loss. The e2e mode measures the deployed compose stack and is not comparable with the in-process mode.
Reference arms (committed under benchmarks/, all MiniMax-M2.7, 18 scenarios):
Arm | Configuration | e2e p50 | e2e p95 |
| shipped defaults (reasoning all phases on) | 288.4 s | 439.6 s |
| + weights/evaluate reasoning off (opt-in env) | 253.0 s | 413.9 s |
| + L5 truncation-boost, L6 trace cap, L7 stable-prefix (all default-off, env-driven) | 223.6 s | 379.2 s |
| analyze-only arm (no generate/evaluate), same config/env as | 161.5 s | 378.1 s |
| full pipeline, | 273.0 s | 474.6 s |
The analyze-vs-live pair (2026-10-25, baseline live-20261025, candidate analyze-20261025)
measured 12/18 vs 12/18 overall acceptable-primary hits (Newcombe Δ +0.000 [−0.286, +0.286]);
holdout 4/6 vs 5/6 (Δ −0.167 [−0.558, +0.295]), recall@5 0.378 vs 0.367. Two scenarios flip in
each direction (4 total), so the equal totals are different scenarios; the intervals cover zero on
every split. No default was changed on this evidence.
The code levers are shipped as opt-in env configuration, not defaults: the cumulative win claim passed the preregistered gate against base-fresh (holdout p95 410.9 → 297.5 s = 27.6 % ≥ 15 %, overall 13/18 ≥ floor), but the attribution comparison against cfg-reasoning recorded a candidate-loss (+12.5 % < 15 %), so defaults stay behavior-neutral. To opt in:
export VALIDATION_TRUNCATION_RERUN=boost
export REASONING_MAX_INJECTED_CHARS=4000
export PROMPT_STABLE_PREFIX=trueVerification layers (the repo's differentiator):
Layer | What | Where |
L1 | Unit tests (418 collected, 362 test functions) |
|
L2 | Hypothesis property oracles (15 in |
|
L3 | Spec gardens (FG-* mutants) |
|
L4 | Exhaustive FizzBee models (8 specs: jobs runner/protocol, pipeline control, design loop, reasoning retry, TEI fallback, retrieval fusion, dense cache) |
|
— | Mutation ratchet ( |
|
— | Ledger (gating) + cross-consistency (advisory NL-doc triage) |
|
Every .fizz assertion must kill at least one spec-garden mutant or carry a # spec-explains: justification; a change to a decision module updates the matching .fizz model in the same PR. The normative pipeline spec is docs/implementation-guide.md (§-numbered, mirrored in German as docs/selektions-process-de.md); the wire contract of the tool result is docs/result-schema.json, the catalog record contract is docs/pattern-schema.json.
Troubleshooting
Server starts but tools are not visible
Check the agent's MCP connection: Claude Code
/mcp, OpenCodeopencode mcp list, Codexcodex mcp listVerify the server process started and the catalog loaded:
uv run design-pattern-mcp --healthprintsOKplus a JSON readiness report (records, language files, aliases)Confirm the TEI embedder is healthy:
docker compose -f docker/docker-compose.yml ps(healthchecks with 120 s embed-model start period)
"Connection refused" or startup failure
Index warm-up refuses server start when the embedder is unreachable (fail-fast rather than breaking your first request):
docker compose -f docker/docker-compose.yml logs design-pattern-tei-embedLocal non-Docker runs must point EMBEDDER_BASE_URL / RERANKER_BASE_URL at reachable endpoints — or start with --offline.
LLM provider errors (502 / 401)
Confirm
GENERATOR_API_KEY(orMINIMAXAI_API_KEYon the dev stack) is set and not expiredVerify
GENERATOR_BASE_URLmatches your provider's endpointAfter 3 consecutive transport failures the circuit opens for 60 s (
..._CIRCUIT_BREAKER_*knobs); auth/configuration 4xx never count toward it
ERR_012 / ERR_014
ERR_012= cancelled (viacancel_designor a client disconnect at the next stage boundary)ERR_014= whole-run deadline exceeded (TASKS_DEADLINE_SECONDS); raise it explicitly for legitimately longer runs — the in-flight provider coroutine is cancelled, partial results are discarded
Reranker 429s
One full-text rerank request already saturates the CPU TEI sidecar's 16,384-token inference buffer (MAX_BATCH_TOKENS=8192 interacts with request packing; measured 2026-09-25): a cap-2 retrieve_many leg fails outright with HTTP 429 "Model is overloaded". Keep RETRIEVAL_MAX_CONCURRENT_PARTS=1 (shipped default); the server-side invariant also keeps dense_top_k + bm25_top_k ≤ the sidecar's MAX_CLIENT_BATCH_SIZE (48).
Pattern JSON files not loading
Records are joined from 14 files per pattern (
pattern/<name>.generic.json+ 13<language>.json); the generic file is requiredValidate the catalog with
./scripts/verify-patterns.sh(both schemas plus the ajv-inexpressible variant checks; map agreement/orphans are enforced by the startup gate)Startup fails on enum drift from the schema (
assert_enums_in_sync) — that is intentional; fix the data, not the guard
Building & Development
Common make targets:
Target | Description |
| Sync dependencies into |
| Install the reasoning MCP servers globally (local dev; Docker embeds them) |
| ruff |
| mypy |
| vulture / deptry |
| All four check gates |
| Unit tests ( |
| Executable oracles: L1 canary, L2 property oracles, spec-garden replay ( |
| mutmut ratchet against |
| Exhaustive FizzBee model checks (L4) |
| Ledger + NL-doc consistency gates |
| Benchmark harness (see above) |
| Force-regenerate |
| Build all three images (dense cache baked in) |
| Start / stop the dev stack |
| Demo clients against |
| Push the server image to Docker Hub + GHCR (+ git tag) |
Development workflow:
make install # first-time setup
uv run pre-commit install # First-time setup: commit hook
make check-all # before pushing
make test-unit # behavioral oracle
uv run mypy --strict # src/ is strict; must exit 0
make docker-up # after image-affecting changesGround rules (from AGENTS.md): src/ is mypy-strict; schema changes change docs/implementation-guide.md + the schema + the code in the same commit; pipeline control-flow changes update the matching FizzBee model in the same PR.
Publishing
All three images are published to two registries simultaneously:
Image | Docker Hub | GHCR | Tags |
MCP server |
|
|
|
TEI embedder |
|
|
|
TEI reranker |
|
|
|
All three images share the same $(DOCKER_TAG) (the version from pyproject.toml). The TEI images are shared with the sibling architecture-pattern-mcp server and change rarely — blob deduplication keeps re-tagging unchanged TEI images cheap, and TEI pushes can be skipped when the sidecars are unchanged.
Bandwidth note: the TEI embedder image is ~5 GB (ONNX fp32 weights baked in). First push to each registry is ~5 GB upload. Subsequent pushes are incremental — only changed layers are transferred.
Prerequisites
Docker Hub — already authenticated locally (docker login).
GitHub Container Registry — requires a classic PAT with write:packages scope. 2FA is not an issue — PATs bypass it. After login the token is discarded; the credential persists in ~/.docker/config.json until you log out.
Publish (one-time setup + per-session)
# 1. Login to GHCR (interactive — paste token at the password prompt)
docker login ghcr.io -u olk
# 2. Build and push all three images (MCP + TEI embedder + TEI reranker)
# The umbrella target builds the MCP image, tags it, pushes it, creates the git tag,
# then builds and pushes each TEI image in sequence.
make docker-publish-all
# 3. Logout from GHCR immediately after publishing
# (removes the ghcr.io credential from ~/.docker/config.json)
docker logout ghcr.ioOn subsequent publishes repeat steps 1–3. If your PAT has expired, generate a new one at the link above. make docker-publish pushes only the MCP server image (plus the v$(DOCKER_TAG) git tag and the MCP Registry entry via mcp-publisher); make docker-publish-tei pushes only the two TEI sidecars.
First push — set packages public (GHCR only)
GHCR packages default to private. After the first make docker-publish-all, flip all three packages to public:
Package | Settings URL |
MCP server |
|
TEI embedder |
|
TEI reranker |
|
Set each to Public and save.
Partial failure recovery
If the push fails mid-way (e.g., GHCR auth was not configured), Docker Hub layers are already uploaded. After fixing auth, re-running make docker-publish-all is safe — each registry reports a cache hit for already-uploaded layers and completes the remaining push. For targeted retries, the TEI sidecars can be pushed with make docker-publish-tei.
systemd Service (Linux)
The server can run as a systemd service on any systemd-based Linux host. It starts the Docker Compose stack automatically at boot. Full agent-runbook install: INSTALL.md (Path B). systemd/ contains:
File | Purpose |
| The systemd unit |
| Production compose variant (no |
| Full runbook: install, verify, troubleshooting |
The production compose is the deployment variant of the dev file: pre-built images only, absolute paths, deployable to /etc/design-pattern-mcp/. The TEI sidecars live in the shared pattern-tei-infra stack; this stack joins its Docker network and reaches them by DNS name. Start the service only after the shared TEI infra is active (running).
# Fast path (see INSTALL.md Path B for the full verified sequence):
VERSION=$(grep -m 1 '^version' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')
docker pull "olkowa/design-pattern-mcp:${VERSION}"
docker tag "olkowa/design-pattern-mcp:${VERSION}" design-pattern-mcp:latest
sudo install -d /etc/design-pattern-mcp/config
sudo install -m 644 systemd/docker-compose.yml /etc/design-pattern-mcp/
sudo install -m 644 config/config.json /etc/design-pattern-mcp/config/
sudo install -m 644 systemd/design-pattern-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now design-pattern-mcpDay-to-day: systemctl start|stop|restart design-pattern-mcp, logs via journalctl -u design-pattern-mcp -f. Full details in systemd/README.md.
License
MIT License. Copyright (c) 2026 Oliver Kowalke. See the license headers in the source files.
Available Tools
13 toolsanalyze_designAnalyze DesignA
Analyze a system description and select design patterns per decision part — the analysis-only path for coding agents that write the code themselves. Returns the repaired design brief, the decision parts with their scope/category hypothesis, and one selection per part (primary pattern, supporting patterns with roles, ranked alternatives, veto ledger, quality-attribute weights that drove the scoring) without generating applications or evaluating them. The selection is a routing decision, not an implementation spec: it carries no participants, implementation steps, code sketches or language bindings — before implementing, call generate_design with this payload to materialize the bound applications without re-running the analysis, or get_design_pattern for each selected pattern when only the catalog record and language bindings are needed. A true is_fallback (payload level, or per part with fallback_reason) marks a selection with no eligible primary — treat it as fallback guidance, not a real match. Use select_design_patterns when the caller wants the generated design.
| Name | Required | Description | Default |
|---|---|---|---|
| focus | No | Optional free text narrowing the decision or naming a realization, e.g. 'use the coroutine-based acceptor variant'. | |
| language | No | Optional target language: generic, c, cplusplus, csharp, go, java, javascript, kotlin, python, ruby, rust, scala, swift, typescript. The selections are bound to that language; absent, empty or unrecognized values run in generic mode. | |
| paradigm | No | Optional paradigm narrowing the realization variant: oo, functional, concurrent, data-oriented, declarative, procedural. Unknown values are ignored. | |
| description | Yes | Free-text description of the system to design: component names with their responsibilities, cross-component flows, and the constraints the design must satisfy (40-100000 characters). Architecture-level choices alone are not enough — name the components to implement. | |
| architecture_style | No | Optional architecture style from the architecture server's style enum (e.g. 'event-driven', 'hexagonal', 'microservices'). Unknown values are ignored; a known value takes precedence over any style extracted from the description and adds a soft category prior. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Description discloses the return payload (repaired brief, decision parts, selection, veto ledger, weights) and what it does NOT produce (no apps, no evaluation, no participants/steps/bindings), plus is_fallback semantics — meaningful context beyond annotations. One gap: readOnlyHint=false is not explained despite the description framing this as pure analysis, leaving the non-readonly flag unexplained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the routing distinction, then alternatives. Dense but nearly every sentence carries decision-relevant content; the pattern-enumeration in the returns sentence is slightly long but justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description explains the returned structure and non-returned artifacts, plus fallback handling. Combined with annotations and rich schema, an agent has everything needed to invoke correctly and route downstream.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all five params including enums-by-name and defaults. The description adds no param-level detail beyond noting the payload is reusable for generate_design; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (analyze) and resource (system description → design pattern selection) and explicitly positions itself as the 'analysis-only path for coding agents that write the code themselves.' This immediately distinguishes it from generate_design, select_design_patterns, and get_design_pattern.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use alternatives are named with conditions: 'call generate_design with this payload to materialize the bound applications without re-running the analysis, or get_design_pattern for each selected pattern when only the catalog record and language bindings are needed' and 'Use select_design_patterns when the caller wants the generated design.' Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cancel_designCancel Design JobA
Cancel a background design job started with submit_design_job. Best-effort: the job stops at its next stage boundary. Reports the resulting status; a job that already finished is reported as terminal with cancelled=false.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The job_id returned by submit_design_job. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds crucial behavior beyond the annotations: cancellation is best-effort and only takes effect at the next stage boundary, and a completed job returns terminal with cancelled=false. These details materially change how an agent should interpret the result and are not derivable from readOnlyHint/destructiveHint/idempotentHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact sentences: action first, then the best-effort caveat, then the result semantics. Every sentence contributes and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter job control tool with an output schema, the description covers what the tool does, its non-atomic stop behavior, and how terminal jobs are reported. Nothing an agent needs in order to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so job_id is already fully documented as the identifier returned by submit_design_job. The description adds no syntax or format detail beyond that, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Cancel a background design job') and ties it to the sibling that creates the job ('submit_design_job'). An agent can distinguish this from get_design_status or list_design_patterns without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It establishes the prerequisite that the job must have been started with submit_design_job and explains the outcome for already-finished jobs, which implicitly tells the agent when cancellation is moot. It does not name an alternative tool (e.g., get_design_status to check first), so it stops short of explicit when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
evaluate_designEvaluate DesignA
Evaluate a design draft against the requirements it must serve: scores 0-100 with per-axis ratings, findings (a 'selection' finding names the offending pattern and drives reselection upstream), and advisory integrity findings of the design itself. Pass the design returned by generate_design or select_design_patterns, plus the requirement text or a prior analyze_design payload.
| Name | Required | Description | Default |
|---|---|---|---|
| analysis | No | Optional prior analyze_design payload: its decision parts drive the axis weighting; without it the requirement text becomes a single decision part | |
| design_json | Yes | The design draft to score (docs/result-schema.json shape) | |
| requirements | Yes | The requirement text the design must serve (40-100000 characters) |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safety profile: non-destructive, not read-only, not idempotent, closed-world. The description adds valuable behavior beyond that by explaining score ranges, per-axis ratings, advisory integrity findings, and that a 'selection' finding names the offending pattern and drives reselection upstream. It does not clarify why the tool is not read-only or whether any evaluation state is persisted, leaving a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly written sentences with no wasted clauses. The purpose and output shape are front-loaded, followed by the required inputs and upstream tool relationships. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema, so the description need not fully restate return values, and it still summarizes the important output categories. Annotations cover the safety profile, and the description explains upstream input sources and downstream reselection behavior. For this complexity level, the definition is complete enough for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents design_json, requirements, and the optional analysis payload. The description adds useful provenance by saying design_json should come from generate_design or select_design_patterns and that analysis can be a prior analyze_design payload. That meaningfully supplements the schema, though it does not add format or validation detail beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: evaluate a design draft against requirements. It also names the outputs (scores 0-100, per-axis ratings, findings) and distinguishes the special 'selection' finding that drives upstream reselection. An agent can identify this as the scoring/validation step versus generation or selection siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly tells the agent to pass a design returned by generate_design or select_design_patterns, plus requirement text or a prior analyze_design payload. This establishes the upstream/downstream flow. It does not explicitly state when not to use it or name alternative evaluation paths, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_designGenerate DesignA
Materialize the pattern applications for a design: either pass the payload of a prior analyze_design call ('analysis'), or pass explicit catalog pattern names ('patterns') together with the system 'description'. Returns a complete design draft — applications with participants, interactions, implementation steps, code sketches and language bindings, plus interactions, quality attributes, a test strategy and risks. Unknown pattern names raise ERR_003.
| Name | Required | Description | Default |
|---|---|---|---|
| analysis | No | A prior analyze_design payload (brief, parts, per-part selections) | |
| language | No | Optional target language: generic, c, cplusplus, csharp, go, java, javascript, kotlin, python, ruby, rust, scala, swift, typescript (explicit path) | |
| patterns | No | Explicit catalog pattern names or aliases to realize (explicit path) | |
| description | No | System description (40-100000 characters); required with 'patterns', ignored when 'analysis' is supplied |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint=false, idempotentHint=false, destructiveHint=false), so the description adds context: it enumerates what the draft contains and discloses failure behavior ('Unknown pattern names raise ERR_003'). That error contract is genuinely useful beyond structured fields, though it doesn't say whether generation overwrites prior drafts or requires a completed analysis.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the core action and the two input modes. The enumeration of returned artifacts is somewhat long but is compacted into one clause with no wasted framing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so the description needn't detail return values, yet it still summarizes them; combined with the ERR_003 note, an agent has enough to call it correctly. It does not explicitly state that one of 'analysis' or 'patterns' must be supplied despite all four params being optional, which is a small gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents the anyOf paths, the 'required with patterns, ignored when analysis is supplied' constraint, and the language list. The description adds only marginal value by noting that pattern names may be aliases. Baseline 3 is appropriate when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('materialize the pattern applications') and resource ('a design'), and distinguishes the two input modes by naming the sibling tool analyze_design whose payload feeds one of them. An agent can tell what the tool produces and how it differs from analyze_design/select_design_patterns without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the two invocation paths (reuse a prior analyze_design payload vs. explicit pattern names plus a system description) and notes 'description' is ignored when 'analysis' is supplied. It stops short of stating when NOT to use this tool in favor of siblings like select_design_patterns or evaluate_design, but the path-level routing is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_design_patternGet Design PatternARead-onlyIdempotent
Get the full JSON of a single design pattern by its name or a resolved alias (e.g. 'singleton', 'borg'). Returns the complete record: intent, problem, context, benefits, tradeoffs, quality_attributes, participants, use cases, related patterns, principles, pitfalls, sources, and the joined per-language implementations with their variants. Raises ERR_013 when the pattern is unknown.
| Name | Required | Description | Default |
|---|---|---|---|
| _ctx | No | ||
| name | Yes | Pattern name or alias, e.g. 'singleton', 'borg' |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, closed-world and non-destructive, so safety is covered. The description adds value beyond that by disclosing what the payload contains (per-language implementations joined with variants) and the failure behavior (ERR_013 on unknown pattern), which an agent cannot learn from the annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the action and lookup key, then the payload contents and error case. The long enumeration of record fields is somewhat list-heavy but each item does tell the agent what is available in the response.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so the field enumeration is partly redundant, but the error code (ERR_013) and the alias-resolution behavior are things the schema does not convey. For a two-parameter read tool with full annotations, this is essentially complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%, with the `name` parameter already documented and exemplified in the schema itself. The description adds only the notion of a 'resolved alias', which is a marginal refinement over the schema's 'Pattern name or alias', and says nothing about the undocumented `_ctx` parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get the full JSON'), a single resource ('a single design pattern'), and the lookup key (name or resolved alias). This clearly separates it from siblings like list_design_patterns and select_design_patterns, which return sets rather than one full record.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrasing 'a single design pattern by its name' implies the precondition that you already know the identifier, but no sibling is named and there is no explicit when-not or 'use X instead' routing. Usage is inferable rather than instructed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_design_statusGet Design StatusARead-onlyIdempotent
Poll the status of a background design job started with submit_design_job. Returns job_id, status ('pending'|'running'|'completed'|'failed'|'cancelled'), a message and the timestamps; 'result' when completed, 'error' when failed.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The job_id returned by submit_design_job. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, closed-world, non-destructive behavior, so the bar is low. The description adds conditional behavior the annotations don't: which states are possible and that 'result' appears only when completed while 'error' appears only when failed, which tells the agent how to interpret terminal versus in-flight responses.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the action, no filler. Every clause contributes either routing context or state semantics.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter polling tool with rich annotations and an output schema, nothing an agent needs is missing. The description covers origin of the job, expected states, and how to read terminal responses.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single job_id parameter is already documented as coming from submit_design_job, so the schema carries the load. The description's mention of job_id adds nothing beyond that baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (poll status) and resource (background design job), and ties it directly to the sibling that creates the job, submit_design_job. Enumeration of the status values makes the tool's scope unmistakable against siblings like cancel_design or analyze_design.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly signals the context of use: a job previously started via submit_design_job, i.e., polling until completion. It does not explicitly state when not to use it (e.g., to abort a job use cancel_design), so it falls short of full alternative routing, but the use case is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_promptGet PromptARead-onlyIdempotent
Get a prompt by name with optional arguments.
Returns the rendered prompt as JSON with a messages array. Arguments should be provided as a dict mapping argument names to values.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The name of the prompt to get | |
| arguments | No | Optional arguments for the prompt |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, so the safety profile is fully covered. The description adds that a prompt is rendered with the supplied arguments and returned as JSON with a messages array, which is useful context beyond annotations, but it says nothing about behavior for unknown names or missing required arguments.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the verb and resource, and every sentence conveys usable information. The return-format sentence is slightly redundant given the output schema exists, but it is not wasted space.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description needn't enumerate return fields, and it does note the rendered messages array at a high level. Input-side semantics are covered by the schema; only error/missing-name behavior is left unaddressed, which is a minor gap for a simple read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are already documented and the baseline is 3. The description adds modest value by clarifying that arguments is a name-to-value dict used for rendering, but gives no detail on how argument names relate to the prompt's declared arguments.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (get a prompt) plus the lookup key (by name), which clearly distinguishes it from list_prompts. It stops short of explicitly contrasting itself with the sibling that enumerates prompts, but the 'by name' scoping makes the retrieval semantics unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: the agent can infer this is the tool to call once it knows a prompt name. There is no statement about how to discover valid names (e.g., use list_prompts first) or when not to use this tool, which is a meaningful gap for a name-keyed lookup.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_design_patternsList Design PatternsARead-onlyIdempotent
List all design patterns known to the server. Returns a minimal view with 'name', 'category' and 'description' for each pattern. Optional filters: 'category' (one of: domain-modeling, construction-composition, behavior-selection, interaction-boundary, workflow-messaging, persistence-state, concurrency-resilience, security-verification, specialized-components) and 'language' (one of: generic, c, cplusplus, csharp, go, java, javascript, kotlin, python, ruby, rust, scala, swift, typescript; keeps patterns with a joined implementation for that language).
| Name | Required | Description | Default |
|---|---|---|---|
| _ctx | No | ||
| category | No | Optional category filter. Valid values: domain-modeling, construction-composition, behavior-selection, interaction-boundary, workflow-messaging, persistence-state, concurrency-resilience, security-verification, specialized-components. Unknown values return an empty list. | |
| language | No | Optional language filter: keeps patterns with an implementation entry. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and closed-world, so safety is fully covered. The description adds the shape of the returned 'minimal view' and the notable edge case that unknown category values yield an empty list, but says nothing about ordering, pagination, or size limits for a full listing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and return shape, then filters. The long parenthetical enumerations make the text bulky, but they encode enum values that the schema does not fully supply, so most of the length is justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, zero-required-parameter listing tool with an output schema, the description covers purpose, both filters with accepted values, filter semantics, and the returned fields. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%, and the language parameter's enum is absent from the schema description while the tool description spells out all thirteen values plus the join semantics ('keeps patterns with a joined implementation for that language'). It genuinely adds meaning the schema does not carry, though the category list duplicates schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List all design patterns known to the server') and immediately characterizes the payload as a minimal view of name/category/description. This clearly separates it from get_design_pattern (single pattern) and the selection/generation siblings without needing their schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The two optional filters imply the main usage modes (browse everything vs. narrow by category/language), but there is no explicit statement of when to reach for this versus select_design_patterns or get_design_pattern. An agent must infer the routing from the sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_promptsList PromptsBRead-onlyIdempotent
List all available prompts.
Returns JSON with prompt metadata including name, description, and optional arguments.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is fully covered by structured data. The only behavioral sentence describes the return payload, which duplicates the existing output schema rather than adding new context such as pagination, ordering, or whether built-in prompts are included.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with the purpose front-loaded, and no filler or hedging. The second sentence ('Returns JSON with prompt metadata...') is slightly redundant given the output schema, so it does not fully earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only list operation with full annotation coverage and an output schema, the description gives everything an agent needs to decide to call it. Ordering, filtering, and the list-vs-get boundary are the only real omissions, and all are minor here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is no parameter semantics to explain and the baseline is 4. The empty schema with additionalProperties=false is self-explanatory and requires no narration.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('List all available prompts'), which is unambiguous. It implicitly separates itself from the 'get_prompt' sibling by scoping to 'all', but never names that sibling or states the distinction explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: 'all available prompts' signals the enumeration case versus fetching a single prompt. There is no explicit 'use this when / use get_prompt instead when' guidance, and prerequisites or scoping (which prompts are exposed) are unstated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_resourcesList ResourcesARead-only
List all available resources and resource templates.
Returns JSON with resource metadata. Static resources have a 'uri' field, while templates have a 'uri_template' field with placeholders like {name}.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds that results split into static resources ('uri') versus templates ('uri_template'), which is genuinely useful behavioral context. It does not cover pagination or whether the list is complete/unbounded, so with annotations carrying the safety burden a 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with the core purpose front-loaded and no filler. The second/third sentences describe the return shape, which slightly overlaps the existing output schema, but the uri vs uri_template distinction earns its place as a discriminator for callers.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema already exists, so the description is not obligated to explain return values, yet it does add a useful layer about the two result shapes. Combined with the readOnly annotation and zero parameters, an agent has what it needs; only the relationship to read_resource is unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so there is no parameter semantics to convey and the baseline of 4 applies. The description correctly focuses on output structure instead of inventing parameter detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("List all available resources and resource templates") and further clarifies the two categories it returns. It is clear on its own, but never names or contrasts with the obvious sibling read_resource, so a small amount of differentiation is left to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance, and no mention of read_resource as the follow-up action. Usage is only implied by the discovery nature of the tool. For a zero-arg catalog tool this is adequate but thin.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_resourceRead ResourceARead-only
Read a resource by its URI.
For static resources, provide the exact URI. For templated resources, provide the URI with template parameters filled in.
Returns the resource content as a string. Binary content is base64-encoded.
| Name | Required | Description | Default |
|---|---|---|---|
| uri | Yes | The URI of the resource to read |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so safety is covered. The description adds genuine behavioral context beyond that: the return is a string and binary payloads are base64-encoded, which materially affects how an agent must handle the result. It omits error/not-found behavior, but the encoding disclosure is a real addition.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core action and then the two caveats an agent needs. Zero wasted words, though the 'Returns...' sentence partially overlaps the existing output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and annotations covering the read-only profile, the description need not detail return structure. It supplies the URI-construction nuance and encoding caveat, leaving only minor gaps such as invalid-URI or missing-resource behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single string parameter, so baseline would be 3. The description goes beyond the schema by specifying that templated resources require the URI with template parameters already filled in, which is meaningful semantic guidance the schema does not carry.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource ('Read a resource by its URI'), which cleanly separates it from list_resources and the legal_* tools. It does not explicitly name or contrast with any sibling, so it lands at a solid but not exemplary 4.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains how to construct the URI for static vs templated resources, which is practical invocation guidance, but never states when to pick this tool over alternatives (e.g., list_resources first, or the legal_* lookups) or any preconditions. Usage is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
select_design_patternsSelect Design PatternsA
Select the design patterns to apply to a described system, with per-application language bindings and a traceable reasoning trail. Send the components, their responsibilities and the constraints derived from an architecture result. Returns applications (pattern, participants, implementation steps, bound language variant), supporting patterns, interactions, quality attributes, a test strategy and risks. Use this when the caller wants the generated design; when your agent writes the code itself and only needs the decisions, call analyze_design instead — it runs the same ANALYZE phase and skips generation and evaluation.
| Name | Required | Description | Default |
|---|---|---|---|
| focus | No | Optional free text narrowing the decision or naming a realization, e.g. 'use the coroutine-based acceptor variant'. A verbatim variant_id is bound exactly; readable ids come from the design-patterns://variants resource. | |
| language | No | Optional target language: generic, c, cplusplus, csharp, go, java, javascript, kotlin, python, ruby, rust, scala, swift, typescript. The patterns are returned realized in that language; absent, empty or unrecognized values run in generic (language-neutral) mode. | |
| paradigm | No | Optional paradigm narrowing the realization variant: oo, functional, concurrent, data-oriented, declarative, procedural. Unknown values are ignored. | |
| description | Yes | Free-text description of the system to design: component names with their responsibilities, cross-component flows, and the constraints the design must satisfy (40-100000 characters). Architecture-level choices alone are not enough — name the components to implement. | |
| architecture_style | No | Optional architecture style from the architecture server's style enum (e.g. 'event-driven', 'hexagonal', 'microservices'). Unknown values are ignored; a known value takes precedence over any style extracted from the description and adds a soft category prior. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and non-idempotent, and the description adds substantial context beyond them: what the call produces (applications with participants and implementation steps, supporting patterns, quality attributes, test strategy, risks) and its position in the pipeline relative to analyze_design. It does not mention cost, latency, or run-to-run variability of the generated output, which are the remaining behavioral unknowns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and scope, and the routing sentence is well placed at the end. The mid-sentence enumeration of return contents largely duplicates the output schema, which costs a bit of density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a five-parameter generative tool with full schema coverage and an output schema, the description covers purpose, inputs to supply, expected artifacts, and sibling routing. An agent has everything needed to decide to call it and to fill the required description field sensibly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and each parameter already documents its own fallbacks (unknown language -> generic, unknown architecture_style ignored). The description adds a little provenance value by saying inputs should be 'derived from an architecture result' and that outputs are bound to the language, but it does not reinterpret any individual parameter beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Select the design patterns to apply to a described system') plus the distinctive outputs (per-application language bindings, traceable reasoning trail). It explicitly contrasts itself with the sibling analyze_design, so the agent can route without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear use case ('Use this when the caller wants the generated design') and a named alternative with the condition that selects it (agent writes code itself, only needs decisions -> analyze_design). It also implies via 'skips generation and evaluation' that this tool spans the full ANALYZE+generate+evaluate pipeline, though generate_design and evaluate_design are never named directly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
submit_design_jobSubmit Design Job (async)A
Start a background design-pattern selection job and return a job_id immediately (returns in <100 ms; the job itself takes minutes). ONLY use this when the calling client cannot wait for a long response. After starting, poll get_design_status(job_id) every 10-30 s until status is 'completed', 'failed', or 'cancelled'.
| Name | Required | Description | Default |
|---|---|---|---|
| focus | No | Optional focus text. | |
| language | No | Optional target language hint. | |
| paradigm | No | Optional paradigm hint. | |
| description | Yes | The same description argument as select_design_patterns. | |
| architecture_style | No | Optional architecture style hint. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safety profile (readOnly=false, destructive=false, idempotent=false), and the description goes well beyond them by disclosing the latency split (returns <100 ms, job takes minutes), the async lifecycle, the polling interval, and the terminal states. That is the operational context an agent needs to drive a job-based tool correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, no filler; the latency fact is front-loaded and the polling instruction follows immediately. Every clause carries information an agent would otherwise have to guess.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and the description still covers the async pattern, latency expectation, follow-up tool, and terminal states. Nothing an agent needs in order to submit and track a job is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with every parameter already documented in the input schema, so the baseline is 3. The description adds only one piece of meaning, that 'description' is the same argument as select_design_patterns, and says nothing about the optional hint parameters (focus, language, paradigm, architecture_style).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Start a background design-pattern selection job') plus what it returns ('job_id immediately'), and implicitly distinguishes itself from the synchronous sibling select_design_patterns. An agent can tell exactly what this tool does and what it hands back without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit gating condition ('ONLY use this when the calling client cannot wait for a long response') and names the follow-up tool and cadence ('poll get_design_status(job_id) every 10-30 s until status is completed/failed/cancelled'). This is exactly the when/when-not/alternative guidance the dimension rewards.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
13 tool updates
v1.0.1- First observed
analyze_design - First observed
cancel_design - First observed
evaluate_design - First observed
generate_design - First observed
get_design_pattern - First observed
get_design_status - First observed
get_prompt - First observed
list_design_patterns - First observed
list_prompts - First observed
list_resources - First observed
read_resource - First observed
select_design_patterns - First observed
submit_design_job
TDQS
Scored across 13 tools
The main design tools form a closely related pipeline, but the descriptions explicitly distinguish analyze_design (analysis only), generate_design (materialize applications), and select_design_patterns (full generated design). The background job tools and prompt/resource tools are clearly separate, though the pipeline overlap still creates some risk of misselection.
All tool names use a consistent snake_case verb_noun pattern: select_design_patterns, analyze_design, generate_design, evaluate_design, list_design_patterns, get_design_pattern, submit_design_job, get_design_status, cancel_design, list_prompts, get_prompt, list_resources, read_resource. The only minor deviation is cancel_design omitting 'job', but the convention remains clear throughout.
With 13 tools, the server is well within a reasonable range for a focused design-pattern assistant. The set covers the design pipeline, catalog lookup, asynchronous job control, and generic prompt/resource access without obvious bloat.
The server covers the core lifecycle: analysis, generation, evaluation, combined selection, catalog listing/retrieval, and background job submission/status/cancel, plus prompt and resource access. Minor gaps exist, such as no tool to list active background jobs and no pattern mutation operations, though these are likely out of scope or workable from provided job IDs.
Maintenance
Related MCP Connectors
MCP server for building and testing AI agents with multi-model experimentation and insights.
MCP server for AI agent profiles and smart notes. 60+ coding prompt packs with expert personas.
MCP Server for an Agent Task Marketplace
MCP server that lets your AI assistant build and deploy apps, hosting each one at a live URL.
Related MCP Servers
- AlicenseAqualityDmaintenanceAn MCP server that allows coding agents to look up contextual rules and patterns on demand, providing just-in-time guidance for specific tasks like writing tests or authoring UI.28MIT
- AlicenseBqualityDmaintenanceAn MCP server that enables users to store and retrieve personalized coding patterns, serving as a persistent memory layer for LLM agents. It allows AI models to generate code and refactor projects according to a user's specific styles, technologies, and established development conventions.71MIT
- AlicenseBqualityCmaintenanceMCP server that gives AI coding assistants persistent memory, structural code graph analysis, and safe multi-agent coordination, enabling them to answer architectural questions, track decisions across sessions, and coordinate safely in multi-agent workflows.394MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI coding agents structured access to a project's architecture, rules, modules, and technical decisions.MIT