python-patterns-mcp
Click on "Install 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 |
| 8 patterns: Singleton, Builder, Factory Method, Strategy, Observer, Decorator, Adapter, Iterator |
| 4 patterns: Singleton, Builder, Strategy, Observer |
| 4 patterns: Singleton, Builder, Strategy, Observer |
| 3 patterns: Singleton, Builder, Strategy |
| 2 refactorings: |
Adding a new pattern is a matter of dropping a file in
resources/examples/<slug>/ and (for the AST tools) writing a small
detector / validator / refactoring class. See the Roadmap section below
for what's outstanding.
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
# 36 passed in 0.5sThe 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 (pattern_examples,
generate_pattern, detect_pattern, validate_pattern, refactor_to_pattern)
just by adding one file per pattern — no changes to the existing engines.
Per capability, this is what's still open:
pattern_examples — 15 patterns to go
Add a Pythonic example file under resources/examples/<slug>/ and the
loader picks it up automatically. Examples-loader test (tests/test_examples.py)
auto-asserts each example is valid Python.
Priority | Pattern | Notes for the example |
P1 | Composite |
|
P1 | Command | callable+closure variant and class-based with |
P1 | Template Method |
|
P1 | Chain of Responsibility | list-of-callables variant and class-based with |
P2 | Abstract Factory | ABC factory + two concrete factories |
P2 | Proxy |
|
P2 | State | callable-on-context variant and class-based |
P2 | Mediator | tiny chat-room mediator on top of |
P2 | Visitor |
|
P3 | Bridge | Shape × Renderer via composition + |
P3 | Facade |
|
P3 | Flyweight |
|
P3 | Prototype |
|
P3 | Memento | pickle-protocol-based snapshot/restore |
P3 | Interpreter | tiny boolean-expression evaluator |
generate_pattern — 4 templates to go
Drop a resources/templates/<slug>/__type__.py.jinja and update the
_SUPPORTED set in generate/generator.py. The Jinja context exposes
type_name, module_name, and a pre-computed snake_name.
Factory Method, Decorator, Adapter, Command — the four patterns where a generator pays off (the rest are either too small to template usefully, or too situation-specific).
detect_pattern — 8 detectors to go
Subclass PatternDetector, set self.pattern, walk the AST, return a
list of DetectedPattern. Register in detect/engine.py.
Factory Method, Decorator, Adapter, Composite, Command, Iterator, Template Method, State.
validate_pattern — patterns waiting for a validator
Same recipe (PatternValidator subclass, register in validate/engine.py).
High-value targets where Python-specific mistakes are common:
Observer — flag listeners stored without
weakref(leak risk), detect iteration over the live list instead of a snapshot.Iterator — flag
__iter__returningselfwhile__next__is also destructive (single-shot iterator that masquerades as iterable).Decorator — flag wrappers that forget to delegate one or more methods of the wrappee's interface.
Factory Method — flag concrete subclasses that don't override the factory method.
refactor_to_pattern — more idempotent rewrites
Subclass Refactoring, set refactoring_id, return a RefactoringResult.
singleton-add-threading-lock— wrap the__new__body in a double-checkedwith cls._lock:block.singleton-add-get-instance— add the readability@classmethod.builder-make-fields-final— convert a Builder's product into a frozen@dataclass(slots=True).observer-snapshot-iteration— changefor x in self._listeners:tofor x in list(self._listeners):insideemit.
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 on any line above. The simplest first contribution is one
more pattern_examples entry — it's a single file, the test will
auto-cover it.
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 installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/air237/python-patterns-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server