ropey
Enables coding agents to perform safe, project-wide Python refactoring (rename, move, extract, inline, change signature, organize imports, etc.) with a dry-run safety contract and LSP-coordinate addressing.
README
ropey
Safe, project-wide Python refactoring for coding agents. ropey is an MCP server that exposes the rope refactoring library as tools a coding agent (Claude in Claude Code, OpenCode, or any MCP client) can call.
Why
Coding agents read and navigate Python well. An LSP like Astral's ty answers "where is this defined?" and "who references it?" precisely. What agents lack is a safe way to change Python structurally. Renaming a symbol used across thirty files by hand-editing text is slow and unreliable: the agent can't be sure it found every reference, can't prove a textual match is the same binding, and routinely leaves half-renamed code.
ropey closes that gap with one division of labour:
ty finds and reads; ropey changes.
The agent points at code using the exact line/character coordinates its
LSP already returned, and ropey performs the behaviour-preserving
transformation across the whole project: rename, move, extract, inline,
change signature, organise imports, and so on. One sibling tool, rewrite,
takes a pattern→goal transformation for structural changes none of the
refactorings express, addressed by a code template rather than a
coordinate (see the safety contract).
The safety contract
- Behaviour is preserved, with one exception. Every refactoring
alters the structure of your code without changing what it does, and
rope proves each transformation safe before ropey writes it.
rewriteis the exception: it makes no behaviour-preservation claim. The agent asserts that pattern and goal are equivalent, and the tool guarantees only that it rewrites exactly the matches it reports. In exchange it surfaces every Match Site (file + range, flaggedmatchedorunsure) for the agent to audit, leaves unprovable sites un-rewritten unless explicitly opted in, and refuses any rewrite that would produce unparsable Python. Both preview and apply mode enforce that refusal. - Dry Run by default. Every tool takes an
applyflag. Withapply=false(the default) the full consequence is computed and reported but nothing is written.apply=trueperforms the same change for real. Both report identical detail. - The Blast Radius. Every result enumerates every affected file with
what happened to it:
modified,created,moved(with its old path), ordeleted. The list is never truncated and never carries file contents. After a live run,git diffshows the exact text. - Uncertain Occurrences. Python is dynamically typed, so sometimes rope
cannot prove that
obj.save()refers to the method being renamed. ropey applies only the certain occurrences and reports every uncertain one as a flagged location for the agent to adjudicate. Nothing is silently included or silently dropped. - Freshness is self-established. Before every refactoring the server
re-checks the source on disk, so edits from any writer are reflected,
whether they came from the agent, a human editor,
git checkout, or a formatter. Correctness never depends on the host announcing its edits. - git is the undo. ropey writes no cache artifacts into your repo
(no
.ropeproject/), never edits gitignored files (git couldn't revert them), and recommends a clean working tree before applying sogit diff/git checkoutare always a complete reversal mechanism. - Failures are structured. A refactoring that cannot proceed returns a
machine-readable reason ("the selection crosses a scope boundary", "the
file
broken.pycannot be parsed") rather than a stack trace.
Install
Claude Code (plugin marketplace)
/plugin marketplace add andrewesweet/ropey
/plugin install ropey@ropey
The plugin bundles the MCP server config; tools appear after a restart. Requires uv on your PATH.
OpenCode
Add to opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"ropey": {
"type": "local",
"command": [
"uvx", "--from", "git+https://github.com/andrewesweet/ropey", "ropey"
],
"enabled": true
}
}
}
Any other MCP client
ropey is a standard stdio MCP server. Generic config:
{
"mcpServers": {
"ropey": {
"command": "uvx",
"args": ["--from", "git+https://github.com/andrewesweet/ropey", "ropey"]
}
}
}
Or run it directly: uvx --from git+https://github.com/andrewesweet/ropey ropey
The catalogue
| Tool | What it does |
|---|---|
rename |
Rename a symbol everywhere, optionally in docstrings/comments and across a class hierarchy |
move |
Move a global, a method, or a whole module; imports updated project-wide |
module_to_package |
Convert a module file into a package |
extract_method / extract_variable |
Extract a selection into a helper or a named value |
inline |
Inline a method, variable, or parameter (kind auto-detected) |
change_signature |
Add / remove / reorder parameters with every call site updated |
organize_imports |
Sort, dedupe, expand star-imports, relative→absolute |
introduce_parameter |
Turn a selected expression into a new parameter that defaults to it, with call sites updated |
encapsulate_field |
Wrap a class attribute behind getter/setter; reads and writes rewritten project-wide |
introduce_factory |
Add a factory (static method or module function) for a class and route instantiations through it |
method_object |
Convert a method into a method object (a class whose __call__ holds the body) to decompose a complex method |
local_to_field |
Promote a method-local variable to an instance field (self.<name>) |
use_function |
Replace code that duplicates a function's body with calls to it, project-wide |
rewrite |
Pattern→goal rewrite of every matching site (${obj}.get_attribute(${key}) → ${obj}[${key}]), with per-wildcard match constraints, certainty-flagged Match Sites, and a syntax guard |
Targets are addressed with LSP coordinates (0-based line/character, UTF-16
units), the same coordinates an LSP returns; byte offsets never appear.
Point refactorings accept an optional expected_symbol so a stale position
fails loudly instead of refactoring the wrong code. The exception is
rewrite, which is addressed by a pattern (Python source with
${wildcard} placeholders) instead of a coordinate, and reports its Match
Sites back as LSP ranges. ty finds and reads; ropey changes.
Development
uv sync
uv run pytest
Domain documentation lives in CONTEXT.md, the decision
records in docs/adr/, and the PRD in
docs/prd/. Operability notes and measured latency envelopes:
docs/operability.md.
Recommended Servers
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.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.