python-patterns-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@python-patterns-mcpGenerate a Singleton pattern in Python"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
python-patterns-mcp
A Model Context Protocol (MCP) server that exposes the 23 Gang of Four design patterns to AI coding agents — generation, canonical examples, AST-based detection, validation, and anti-pattern refactoring for Python codebases.
This is the Python sibling of java-patterns-mcp.
Same 7-tool API, same intent — but every example, detector, validator, and
refactoring is rewritten around Python idioms (__new__, dataclass,
abc.ABC, generators, weakref.WeakSet, functools.singledispatch, …).
Why a Python version
Generic LLMs can describe design patterns, but their generated Python code
often misses Pythonic alternatives — a class-based Strategy when a callable
parameter would do, a metaclass Singleton when a module would suffice, a
hand-rolled iterator when a generator function is one line shorter. And no
LLM can deterministically scan a real codebase to say "this class is a
half-broken Singleton because __init__ re-runs on every call".
This MCP server fills that gap with deterministic, AST-backed tooling.
Related MCP server: OHM-MCP
Pythonic decisions baked into the catalog
Every entry in patterns.json carries a python_note that the agent reads
through list_patterns. The notes tell the LLM when not to reach for
the classic GoF recipe — because Python has a better idiom for it.
Category | Pattern | Classic GoF | Pythonic alternative the catalog suggests |
Creational | Abstract Factory | abstract |
|
Creational | Builder | dedicated | keyword arguments + |
Creational | Factory Method | abstract | a |
Creational | Prototype | abstract |
|
Creational | Singleton | private ctor + static getter + metaclass | a module-level object (modules are cached in |
Structural | Adapter | wrapper class with a translated interface | often unnecessary — duck typing lets you call the original directly; a thin wrapper function usually suffices |
Structural | Bridge | abstraction × implementation hierarchies | composition over inheritance — plain composition with a |
Structural | Composite | abstract | duck typing lets Leaf and Composite share an implicit interface; |
Structural | Decorator |
| NOT the same as |
Structural | Facade |
| a module's |
Structural | Flyweight | flyweight factory + intrinsic/extrinsic split |
|
Structural | Proxy | proxy class forwarding every method |
|
Behavioral | Chain of Responsibility |
| a list of callables and a small dispatch loop is usually enough |
Behavioral | Command |
| a callable + closure already captures "a request"; full Command shines when you need |
Behavioral | Interpreter | one class per grammar rule | rarely needed in pure form — Python's own |
Behavioral | Iterator |
| the iterator protocol ( |
Behavioral | Mediator | central | often combined with an EventBus / pub-sub; |
Behavioral | Memento |
|
|
Behavioral | Observer |
|
|
Behavioral | State | one class per state | a callable stored on the context — first-class functions are an alternative to a class hierarchy |
Behavioral | Strategy | abstract | a callable parameter ( |
Behavioral | Template Method | abstract base with |
|
Behavioral | Visitor |
|
|
The detectors and validators are tuned for this. For example, the Strategy
detector intentionally flags an abstract *Strategy class only if there are
two or more concrete subclasses — so the agent never gets nagged about
a one-variant "Strategy" that should just be a function.
Tools
Tool | What it does |
| Health check — server name, version, registered tools |
| All 23 GoF patterns with intent / problem / Python note / refactoring.guru URL |
| Canonical, runnable Pythonic example source(s) for a pattern |
| Render a customised implementation (your |
| AST-based detection: inline source, file list, or recursive directory scan |
| Pattern-specific code-quality check (ERROR / WARNING / INFO) |
| Idempotent AST rewrite — turn anti-pattern code into proper pattern |
Coverage in this build
Capability | Supported patterns |
| All 23 GoF patterns |
| All 23 GoF patterns |
| All 23 GoF patterns |
| All 23 GoF patterns |
| 18 patterns (Group A + Group B; Group C — Prototype, Flyweight, Interpreter, Iterator, Memento — is deliberately detection-only, mirroring Java |
| 13 idempotent recipes across 11 patterns |
Per-pattern feature matrix
Legend: A = full 4-tool + refactor · B = 4-tool, no refactor · C = detection-only.
Pattern | Category | Tier | Examples | Generator | Detector | Validator | Refactorings |
Singleton | Creational | A | ✅ | ✅ | ✅ | ✅ |
|
Factory Method | Creational | A | ✅ | ✅ | ✅ | ✅ |
|
Builder | Creational | A | ✅ | ✅ | ✅ | ✅ | (no reducible recipe) |
Adapter | Structural | A | ✅ | ✅ | ✅ | ✅ |
|
Decorator | Structural | A | ✅ | ✅ | ✅ | ✅ |
|
Composite | Structural | A | ✅ | ✅ | ✅ | ✅ |
|
Proxy | Structural | A | ✅ | ✅ | ✅ | ✅ |
|
Command | Behavioral | A | ✅ | ✅ | ✅ | ✅ |
|
Observer | Behavioral | A | ✅ | ✅ | ✅ | ✅ |
|
State | Behavioral | A | ✅ | ✅ | ✅ | ✅ |
|
Strategy | Behavioral | A | ✅ | ✅ | ✅ | ✅ | (no reducible recipe) |
Template Method | Behavioral | A | ✅ | ✅ | ✅ | ✅ |
|
Abstract Factory | Creational | B | ✅ | ✅ | ✅ | ✅ | — |
Bridge | Structural | B | ✅ | ✅ | ✅ | ✅ | — |
Facade | Structural | B | ✅ | ✅ | ✅ | ✅ | — |
Chain of Responsibility | Behavioral | B | ✅ | ✅ | ✅ | ✅ | — |
Mediator | Behavioral | B | ✅ | ✅ | ✅ | ✅ | — |
Visitor | Behavioral | B | ✅ | ✅ | ✅ | ✅ | — |
Prototype | Creational | C | ✅ | ✅ | ✅ | — | — |
Flyweight | Structural | C | ✅ | ✅ | ✅ | — | — |
Interpreter | Behavioral | C | ✅ | ✅ | ✅ | — | — |
Iterator | Behavioral | C | ✅ | ✅ | ✅ | — | — |
Memento | Behavioral | C | ✅ | ✅ | ✅ | — | — |
See COVERAGE.md for the same matrix plus the Java-parity delta.
Tools — full reference with examples
Every example below shows the tools/call arguments and a (truncated)
result.content[0].text payload. The server always returns JSON-encoded
text, so the agent's call wrapper can json.loads it directly.
1. ping — health check
Arguments: none.
→ {}
← {
"server": "python-patterns-mcp",
"version": "0.1.0",
"tools": ["ping", "list_patterns", "pattern_examples", "generate_pattern",
"detect_pattern", "validate_pattern", "refactor_to_pattern"]
}2. list_patterns — catalog of all 23 GoF patterns
Arguments:
category(optional) —"Creational","Structural","Behavioral", or any case variant. Omit to get all 23.
→ { "category": "Creational" }
← {
"count": 5,
"patterns": [
{
"id": "SINGLETON", "display_name": "Singleton", "slug": "singleton",
"category": "Creational",
"intent": "Ensures a class has only one instance …",
"problem": "You need exactly one instance of a class …",
"python_note": "Python idiom: a module-level object is already a singleton …",
"aliases": ["Borg"],
"reference_url": "https://refactoring.guru/design-patterns/singleton"
},
…
]
}3. pattern_examples — canonical Pythonic examples
Arguments:
pattern(required) — enum name ("SINGLETON"), slug ("singleton"), or display name ("Singleton"). Case-insensitive.include_source(optional, defaulttrue) — set tofalsefor a metadata-only response (lighter token cost when you only need filenames).
→ { "pattern": "observer", "include_source": false }
← {
"pattern": {
"id": "OBSERVER", "display_name": "Observer", "slug": "observer",
"category": "Behavioral",
"reference_url": "https://refactoring.guru/design-patterns/observer",
"python_note": "Iterate over a snapshot of subscribers when emitting …"
},
"example_count": 1,
"files": [{ "file_name": "event_bus.py",
"relative_path": "examples/observer/event_bus.py",
"source": null }]
}4. generate_pattern — render a customised implementation
Arguments:
pattern(required) — one ofsingleton,builder,strategy,observer.type_name(required) — the main class name (e.g."Logger","Pizza"). Must be a valid Python identifier.module_name(optional) — overrides the snake_case derived fromtype_name. Used in docstrings/comments only.
→ { "pattern": "singleton", "type_name": "Config" }
← {
"pattern": "SINGLETON", "type_name": "Config", "module_name": "",
"file_count": 1,
"files": [{
"file_name": "config.py",
"source": "\"\"\"Thread-safe Singleton — generated by python-patterns-mcp.\n…\"\"\"\n\nimport threading\n\nclass Config:\n _instance: \"Config | None\" = None\n _lock: threading.Lock = threading.Lock()\n\n def __new__(cls, *args, **kwargs) -> \"Config\":\n if cls._instance is None:\n with cls._lock:\n …\n"
}]
}Unsupported patterns return a clear error pointing at pattern_examples:
→ { "pattern": "visitor", "type_name": "AstWalker" }
← ToolError: "Generation not yet implemented for VISITOR. Try pattern_examples instead. Supported: BUILDER, OBSERVER, SINGLETON, STRATEGY"5. detect_pattern — AST-based scan
Arguments (pass exactly one of):
source— inline Python source as a single string.paths— list of absolute / relative.pyfile paths on disk.directory— directory to scan recursively (cap: 1000.pyfiles).
→ {
"source": "import threading\nclass Logger:\n _instance = None\n _lock = threading.Lock()\n def __new__(cls):\n if cls._instance is None:\n with cls._lock:\n if cls._instance is None:\n cls._instance = super().__new__(cls)\n return cls._instance\n @classmethod\n def get_instance(cls): return cls()\n"
}
← {
"file_count": 1, "hit_count": 1, "errors": [],
"supported_patterns": ["SINGLETON", "BUILDER", "STRATEGY", "OBSERVER"],
"hits": [{
"pattern": "SINGLETON", "display_name": "Singleton",
"location": "Logger", "file": "<inline>", "line": 2,
"confidence": 0.95,
"signals": [
"class attribute '_instance' initialised to None",
"__new__ returns cached instance",
"get_instance() factory method"
]
}]
}Parse failures (per file) are reported in errors and never abort the batch.
6. validate_pattern — flag implementation pitfalls
Arguments:
source(required) — Python source to inspect.pattern(optional) — narrow the check to one pattern; omit to run every supported validator.
→ {
"pattern": "singleton",
"source": "class Logger:\n _instance = None\n def __new__(cls):\n if cls._instance is None:\n cls._instance = super().__new__(cls)\n return cls._instance\n def __init__(self):\n self.log = []\n"
}
← {
"scope": "SINGLETON", "issue_count": 3,
"errors": [],
"warnings": [
{ "severity": "WARNING", "location": "Logger", "line": 1,
"message": "Singleton class defines both __new__ and __init__. __init__ runs on every instantiation …",
"suggestion": "Either guard __init__ with 'if self._initialized: return', or move initialisation into a private _initialize() method called once from __new__." },
{ "severity": "WARNING", "location": "Logger", "line": 3,
"message": "__new__ does not appear to use a threading.Lock. Two threads calling the class concurrently can each create a separate instance …",
"suggestion": "Add a class-level 'threading.Lock' and a double-checked lock around the 'if cls._instance is None' branch." }
],
"infos": [
{ "severity": "INFO", "location": "Logger", "line": 1,
"message": "No get_instance() classmethod found. Calling the class directly works …",
"suggestion": "Add 'def get_instance(cls) -> Self: return cls()' as a @classmethod for readability." }
]
}7. refactor_to_pattern — idempotent AST rewrite
Arguments:
source(required) — Python source to rewrite.refactoring(required) — one ofsingleton-add-new-guard,singleton-add-init-once-guard.
→ {
"refactoring": "singleton-add-init-once-guard",
"source": "class Logger:\n _instance = None\n def __new__(cls):\n if cls._instance is None:\n cls._instance = super().__new__(cls)\n return cls._instance\n def __init__(self):\n self.log = []\n"
}
← {
"refactoring": "singleton-add-init-once-guard",
"changed": true,
"log": ["Added once-only __init__ guard to class 'Logger' at line 7"],
"source": "class Logger:\n _instance = None\n\n def __new__(cls):\n if cls._instance is None:\n cls._instance = super().__new__(cls)\n return cls._instance\n\n def __init__(self):\n if getattr(self, '_initialized', False):\n return\n self._initialized = True\n self.log = []\n",
"supported_refactorings": ["singleton-add-init-once-guard", "singleton-add-new-guard"]
}Re-running the same refactoring on already-correct code is a no-op
(changed: false, empty log).
End-to-end agent workflow
A typical conversation: "clean up this Logger so it's actually a singleton".
1. detect_pattern { source: <user's code> }
→ SINGLETON @ Logger, confidence 0.6 — only 1 signal fired
2. validate_pattern { source: …, pattern: "singleton" }
→ 2 WARNINGs (__init__ re-runs, no Lock) + 1 INFO (no get_instance)
3. refactor_to_pattern { source: …, refactoring: "singleton-add-init-once-guard" }
→ rewritten source with `_initialized` guard at the top of __init__
4. validate_pattern { source: <rewritten>, pattern: "singleton" }
→ only the Lock warning + get_instance INFO remain
→ agent decides to add a threading.Lock by hand, then declares doneRequirements
Python 3.11+ (uses
Self,slots=Truedataclasses,match-friendly enums)mcpSDK ≥ 1.2.0 (providesFastMCP)jinja2≥ 3.1.0 (forgenerate_pattern)
Install
Pick whichever Python package manager you already have. All three give you the same working server.
Option A — uv (fastest, recommended)
git clone https://github.com/<you>/python-patterns-mcp.git
cd python-patterns-mcp
uv venv
uv pip install -e ".[dev]"The interpreter for OpenCode wiring is then .venv/bin/python.
Option B — pipx (one-shot, isolated, no venv juggling)
If you just want to use the server (not develop on it):
pipx install git+https://github.com/<you>/python-patterns-mcp.gitpipx exposes the python-patterns-mcp console script on your $PATH,
which you can plug straight into OpenCode without any path gymnastics:
{ "mcp": { "python-patterns": { "type": "local",
"command": ["python-patterns-mcp"] } } }Option C — plain pip + venv
git clone https://github.com/<you>/python-patterns-mcp.git
cd python-patterns-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Behind a corporate
pip.conf? Some SAP/internal mirrors fail name resolution off-VPN andpiphangs on retries. Bypass with:PIP_CONFIG_FILE=/dev/null pip install -e ".[dev]"
Run
python -m python_patterns_mcpThe server speaks the standard MCP stdio transport — stdin for JSON-RPC requests, stdout for JSON-RPC responses, stderr for all log lines (so stdout stays clean). It blocks until stdin closes.
A convenience console script is also installed: python-patterns-mcp.
Smoke test
python tests/smoke_stdio.pyThis script boots the server as a subprocess, exchanges 7 JSON-RPC frames,
and prints the parsed responses for initialize, tools/list, ping,
list_patterns, detect_pattern, generate_pattern, and pattern_examples.
Expected tail:
Received 7 response(s) on stdout
[initialize] serverInfo={'name': 'python-patterns-mcp', 'version': '0.1.0'}
[tools/list] 7 tools: ping, list_patterns, pattern_examples, generate_pattern,
detect_pattern, validate_pattern, refactor_to_pattern
[ping] {'server': 'python-patterns-mcp', 'version': '0.1.0', ...}
[list_patterns] 5 entries
[detect_pattern] 1 hit(s); errors=[]
• SINGLETON @ Logger (conf=0.95, signals=3)
[generate_pattern] produced 1 file: config.py
[pattern_examples] pattern=ITERATOR, files=1: name_roster.pyUnit tests
pytest -v
# 117 passed in 0.6sThe test suite covers every tool end-to-end. A particularly satisfying test
is test_refactored_singleton_runs: it takes a plain class, applies two
refactorings in sequence, executes the result, and asserts the rewritten
class genuinely behaves like a singleton.
Wire into OpenCode
Add to ~/.config/opencode/opencode.json:
{
"mcp": {
"python-patterns": {
"type": "local",
"command": [
"/Users/<you>/git/com/python-patterns-mcp/.venv/bin/python",
"-m",
"python_patterns_mcp"
]
}
}
}OpenCode then sees all 7 tools under the python-patterns namespace.
Project layout
python-patterns-mcp/
├── pyproject.toml
├── README.md
├── LICENSE
├── resources/
│ ├── catalog/patterns.json ← refactoring.guru-style metadata
│ ├── examples/<slug>/*.py ← canonical Pythonic examples
│ └── templates/<slug>/*.py.jinja ← code-generation templates
├── src/python_patterns_mcp/
│ ├── __init__.py
│ ├── __main__.py ← `python -m python_patterns_mcp`
│ ├── server.py ← FastMCP bootstrap
│ ├── catalog/ ← Pattern enum + registry + examples
│ ├── tools/ ← 7 MCP tool handlers
│ ├── generate/ ← Jinja2 generator
│ ├── detect/ ← AST-based detectors
│ ├── validate/ ← pattern-quality validators
│ └── refactor/ ← AST rewriters
└── tests/
├── test_catalog.py
├── test_examples.py
├── test_generator.py
├── test_detect.py
├── test_validate.py
├── test_refactor.py
├── test_server.py
└── smoke_stdio.py ← end-to-end stdio smoke testRoadmap
The architecture lets you grow any of the five capability tables just by adding one file per pattern — no changes to the existing engines. The initial full-parity milestone is done — every GoF pattern has an example, a generator, and a detector, and Group A + B additionally have validators. What's still open is either out of scope or quality improvement rather than "coverage".
Out-of-scope
validate_patternfor Group C (Prototype, Flyweight, Interpreter, Iterator, Memento) — matches Java'sCOVERAGE.mddecision. The pitfalls in these five patterns are too diffuse to reduce to a small set of actionable rules; adding validators here tends to produce noise.refactor_to_patternfor Group B (Abstract Factory, Bridge, Facade, Visitor, Chain of Responsibility, Mediator) — these patterns' typical fixes require design-level judgement, not a one-line AST edit.
Nice-to-haves
Detector confidence tuning — every pattern already emits a 0.0..1.0 score and a
signals[]breakdown. Fine-tuning the base + per-signal weights against a wider corpus of real Python code would reduce false positives further.More validator rules — e.g. Observer listener leak (missing
weakref), Iterator single-shot detection, Decorator delegation completeness, Factory Method concrete-override coverage.Custom refactorings — e.g.
builder-make-fields-final(convert a Builder's product into a frozen@dataclass(slots=True)).
Project-level work
GitHub Actions CI (mirror the Java repo's
mvn verifysetup)PyPI publish workflow
mcp dev/ MCP Inspector wiring for interactive explorationMore tests around the
directorymode ofdetect_pattern(large recursive scans, parse-error reporting)
PRs welcome. The simplest way to contribute is to open an issue with a real-world Python snippet where a detector misfires — the fix is usually a single-line adjustment to the pattern's signal weights.
License
MIT © 2026 contributors.
Pattern examples are adapted from refactoring.guru and the original Design Patterns: Elements of Reusable Object-Oriented Software (Gamma, Helm, Johnson, Vlissides). All adapted code is original re-implementation; no third-party source is reproduced verbatim.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only Pattern-Assist corpus for aspiring software engineers: grounded dev patterns with sources.
Evidence-backed architecture-quality analysis for Python agent applications.
Code intelligence platform for AI agents. 20 tools for architecture, security & impact analysis.
Codebase intelligence for agents: 152 structured artifacts across 21 programs, one call.
Related MCP Servers
- AlicenseBqualityBmaintenanceProvides intelligent design pattern recommendations using semantic search through a comprehensive catalog of 200+ patterns across 20 categories. Users can describe programming problems in natural language to discover appropriate design patterns with contextual recommendations.429MIT
- AlicenseNot gradedqualityDmaintenanceAI-powered Python refactoring assistant that provides AST-based code analysis, SOLID violation detection, dead code elimination, type safety analysis, performance optimization, and automated refactoring with rollback capabilities.2MIT
- AlicenseAqualityCmaintenanceEnables LLMs to apply Martin Fowler's 71+ refactoring patterns to codebases through a pluggable, language-agnostic architecture. Supports previewing and applying refactorings, analyzing code smells, and inspecting code structure with safe-by-default operations.519 PyPI5MIT
- AlicenseAqualityDmaintenanceProvides design pattern templates and anti-pattern guidance to AI coding agents for correct pattern implementation.2MIT