Skip to main content
Glama

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.

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

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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