python-patterns-mcp

python-patterns-mcp

Exposes the 23 Gang of Four design patterns to AI coding agents for generation, canonical examples, AST-based detection, validation, and anti-pattern refactoring in Python codebases.

Category
Visit Server

README

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.

Python 3.11+ MCP License: MIT

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.

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 Factory + concrete factory classes abc.ABC makes the contract explicit; concrete factories typically live as module-level singletons
Creational Builder dedicated XBuilder with telescoping methods keyword arguments + @dataclass; Builder only justified for fluent chaining, staged construction, or cross-field validation
Creational Factory Method abstract Creator.createX() a classmethod factory or a plain module-level function — duck typing means clients don't need the interface
Creational Prototype abstract clone() on every product copy.copy / copy.deepcopy — the pattern is essentially built into the language
Creational Singleton private ctor + static getter + metaclass a module-level object (modules are cached in sys.modules); __new__ override if a class is really required
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 Protocol or ABC defining the implementor
Structural Composite abstract Component + Leaf + Composite duck typing lets Leaf and Composite share an implicit interface; abc.ABC only when you want the contract explicit
Structural Decorator Decorator base wrapping an Operation NOT the same as @decorator syntax (that's definition-time); object-Decorator is justified when you need to stack behaviour at runtime
Structural Facade Facade class hiding subsystem details a module's __init__.py is a natural facade — only the curated names go into __all__
Structural Flyweight flyweight factory + intrinsic/extrinsic split sys.intern() for strings, functools.lru_cache for functions, weakref.WeakValueDictionary for objects
Structural Proxy proxy class forwarding every method __getattr__ / __getattribute__ make transparent proxies trivial; weakref.proxy is a built-in lifetime proxy
Behavioral Chain of Responsibility Handler base + set_next() a list of callables and a small dispatch loop is usually enough
Behavioral Command Command interface + execute() a callable + closure already captures "a request"; full Command shines when you need undo() + serialisability
Behavioral Interpreter one class per grammar rule rarely needed in pure form — Python's own ast module is a fine reference when you do need it
Behavioral Iterator Iterator interface with next() the iterator protocol (__iter__ / __next__) is native; generator functions collapse it to a single yield
Behavioral Mediator central Mediator wired to every colleague often combined with an EventBus / pub-sub; asyncio.Queue is a lightweight mediator for coroutines
Behavioral Memento Memento + Caretaker classes copy.deepcopy + a stack; or __getstate__ / __setstate__ (the pickle protocol) for richer cases
Behavioral Observer Subject + Observer interfaces weakref.WeakSet to avoid listener leaks; snapshot the subscriber list before iterating to survive concurrent unsubscribes
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 Strategy + concrete classes a callable parameter (sorted(xs, key=fn)); class-based Strategy only when the strategy carries state
Behavioral Template Method abstract base with final algorithm + hooks abc.ABC lets you mark only the variable steps as @abstractmethod, freezing the algorithm in the concrete method
Behavioral Visitor accept(visitor) on every element functools.singledispatch — dispatch on the runtime type of the first argument; ast.NodeVisitor is a built-in example

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
ping Health check — server name, version, registered tools
list_patterns All 23 GoF patterns with intent / problem / Python note / refactoring.guru URL
pattern_examples Canonical, runnable Pythonic example source(s) for a pattern
generate_pattern Render a customised implementation (your type_name, your module_name)
detect_pattern AST-based detection: inline source, file list, or recursive directory scan
validate_pattern Pattern-specific code-quality check (ERROR / WARNING / INFO)
refactor_to_pattern Idempotent AST rewrite — turn anti-pattern code into proper pattern

Coverage in this build

Capability Supported patterns
list_patterns All 23 GoF patterns
pattern_examples 8 patterns: Singleton, Builder, Factory Method, Strategy, Observer, Decorator, Adapter, Iterator
generate_pattern 4 patterns: Singleton, Builder, Strategy, Observer
detect_pattern 4 patterns: Singleton, Builder, Strategy, Observer
validate_pattern 3 patterns: Singleton, Builder, Strategy
refactor_to_pattern 2 refactorings: singleton-add-new-guard, singleton-add-init-once-guard

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, default true) — set to false for 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 of singleton, 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 from type_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 .py file paths on disk.
  • directory — directory to scan recursively (cap: 1000 .py files).
{
  "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 of singleton-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 done

Requirements

  • Python 3.11+ (uses Self, slots=True dataclasses, match-friendly enums)
  • mcp SDK ≥ 1.2.0 (provides FastMCP)
  • jinja2 ≥ 3.1.0 (for generate_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.git

pipx 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 and pip hangs on retries. Bypass with:

PIP_CONFIG_FILE=/dev/null pip install -e ".[dev]"

Run

python -m python_patterns_mcp

The 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.py

This 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.py

Unit tests

pytest -v
# 36 passed in 0.5s

The 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 test

Roadmap

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 dataclass Leaf + Composite, duck-typed size()
P1 Command callable+closure variant and class-based with undo()
P1 Template Method abc.ABC with @abstractmethod for the variable step
P1 Chain of Responsibility list-of-callables variant and class-based with set_next()
P2 Abstract Factory ABC factory + two concrete factories
P2 Proxy __getattr__ transparent proxy + caching example
P2 State callable-on-context variant and class-based
P2 Mediator tiny chat-room mediator on top of asyncio.Queue
P2 Visitor functools.singledispatch + a tiny AST walker
P3 Bridge Shape × Renderer via composition + Protocol
P3 Facade __init__.py-style facade with curated __all__
P3 Flyweight WeakValueDictionary-backed factory
P3 Prototype copy.deepcopy recipe + custom __copy__ hook
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__ returning self while __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-checked with 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 — change for x in self._listeners: to for x in list(self._listeners): inside emit.

Project-level work

  • GitHub Actions CI (mirror the Java repo's mvn verify setup)
  • PyPI publish workflow
  • mcp dev / MCP Inspector wiring for interactive exploration
  • More tests around the directory mode of detect_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.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured