Metadata-Version: 2.4
Name: revise-mcp
Version: 1.0.0
Summary: Tools to revise and refactor code via MCP
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.14
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Dist: mcp (>=1.1.0,<2.0)
Project-URL: Homepage, https://github.com/davidfstr/revise-mcp
Project-URL: Issues, https://github.com/davidfstr/revise-mcp/issues
Project-URL: Repository, https://github.com/davidfstr/revise-mcp
Description-Content-Type: text/markdown

# Revise MCP

<img src="https://raw.githubusercontent.com/davidfstr/revise-mcp/refs/heads/main/README/logo.png?token=GHSAT0AAAAAADCQFNGCASILHED4BBDR6TBI2O4ZE7Q" alt="Revise MCP logo" align="right" />

Tools to revise and refactor code via MCP (Model Context Protocol).

## Installation

* Install [Python 3.14+](https://www.python.org/downloads/)
* Install pipx: `python3 -m pip install pipx`
* Install Revise MCP:

```bash
pipx install revise-mcp
which revise
# /Users/YOUR_USERNAME/.local/bin/revise
```

## Usage

### VS Code MCP Configuration

Add to your VS Code MCP settings:

```json
{
    "servers": {
        "revise": {
            "type": "stdio",
            "command": "/Users/YOUR_USERNAME/.local/bin/revise",
            "args": []
        },
    }
}
```

### Claude Code MCP Configuration

Run once to register Revise MCP for all your projects:

```bash
claude mcp add --scope user revise -- /Users/YOUR_USERNAME/.local/bin/revise
```

## Tools

### `move_string_in_file`

Moves a contiguous range of lines from one location to another within a file
(or between files). Uses substring matching with a `⬥` marker to identify
cut boundaries and paste location — no line counting or coordinate math required.

Does not alter indentation of the moved text. Use `indent_dedent` afterward
if the moved text needs re-indentation at its new location.

**Parameters:**
- `cutFilePath` — Absolute path to the file containing the text to move
- `cutStartAt` — Substring fragment identifying the line where the cut begins. The `⬥` marks a position on the target line; the cut starts at the beginning of that line
- `cutEndAt` — Substring fragment identifying the line *after* the cut ends. The cut ends *before* this line (exclusive)
- `pasteAt` — Substring fragment marking the insertion point, with `⬥` adjacent to a `\n` or file boundary. Matched against the pre-cut file state
- `pasteFilePath` (optional) — Absolute path to the paste destination file. Defaults to `cutFilePath` (same-file move)

**Example — move a class before another definition:**
```json
{
  "cutFilePath": "/path/to/file.py",
  "cutStartAt": "class MyClass:",
  "cutEndAt": "class NextClass:",
  "pasteAt": "\n⬥def top_level_function():"
}
```

### `indent_dedent`

Indents or unindents a contiguous range of lines by a specified number of
levels. Uses substring matching with a `⬥` marker to identify the range
boundaries — no line counting required.

Empty lines within the range are left unchanged. Indent size is auto-detected
from the file if not specified.

**Parameters:**
- `filePath` — Absolute path to the file to edit
- `indentStartAt` — Substring fragment identifying the first line to indent/unindent. The `⬥` marks a position on the target line; the entire line is included
- `indentEndAt` — Substring fragment identifying the last line to indent/unindent (inclusive)
- `indentDelta` — Number of indent levels to add (positive) or remove (negative)
- `indentSize` (optional) — Spaces per indent level (e.g., `4`), or `"\t"` for tabs. Auto-detected if omitted

**Example — indent a block, then wrap it:**
```json
{
  "filePath": "/path/to/file.py",
  "indentStartAt": "# Create home URL\n⬥",
  "indentEndAt": "(home_ti,) = root_ti.Children⬥\n",
  "indentDelta": 1
}
```
Then use `replace_string_in_file` to insert `if condition:` before the indented block.

### `outline_file`

Returns a high-level outline of a file, similar to VS Code's folded view.
Useful for quickly understanding the structure of a large file before reading
it in full.

Includes only structurally significant lines,
currently optimized for the **Python** language only:
- `class`, `def`, and `async def` definitions (at any indentation level)
- `if __name__ == '__main__':` guards
- Section separator comments (`# === ...` or `# --- ...`), plus the
  immediately following section-name comment if present

Output is one line per entry in the form `LINE_NUMBER:LINE_CONTENT` (1-indexed).

**Parameters:**
- `filePath` — Absolute path to the file to outline

**Example:**
```json
{
  "filePath": "/path/to/file.py"
}
```

### `rename_symbol` *(optional)*

Renames a symbol (function, variable, class, etc.) and all its references
across the workspace using VS Code's LSP-backed rename. Uses text-based
matching to locate the symbol — no coordinate counting required.

> **Note:** This tool is optional and has additional setup requirements. See
> [Optional: `rename_symbol` Setup](#optional-rename_symbol-setup) below.
>
> It is most useful for AI coding harnesses that **do not** provide their own
> symbolic rename tool (e.g. Claude Code). VS Code users already have a native
> rename tool and may skip this.

**Parameters:**
- `filePath` — Absolute path to the file containing the symbol to rename
- `oldString` — Line fragment containing the original symbol name. Must be unique within the file (or within the specified line). Example: `'def mocked_show_modal('`
- `newString` — Line fragment with all occurrences of the symbol renamed. Example: `'def mocked_show_modal_renamed('`
- `line` (optional) — 1-indexed line number to limit the search for `oldString`. Use when `oldString` appears on multiple lines

**Example:**
```json
{
  "filePath": "/path/to/file.py",
  "oldString": "def helper_function(",
  "newString": "def _helper_function("
}
```
This renames `helper_function` to `_helper_function` everywhere it is referenced.

## Optional: `rename_symbol` Setup

The `rename_symbol` tool delegates the actual rename operation to VS Code via
the [VSC as MCP](https://marketplace.visualstudio.com/items?itemName=lsegal.vsc-as-mcp)
extension. This means:

1. **VS Code must be running** with the project open.
2. **The "VSC as MCP" extension must be installed** in VS Code. It exposes a
   local MCP server (by default at `http://localhost:4000/mcp`) that Revise MCP
   calls internally.

### Steps

1. Install the [VSC as MCP extension](https://marketplace.visualstudio.com/items?itemName=lsegal.vsc-as-mcp) in VS Code.
2. Open VS Code with the project you want to rename symbols in.
3. Ensure Revise MCP is configured in your AI coding harness (see [Installation](#installation) and [Usage](#usage)).

No additional configuration of Revise MCP itself is required — `rename_symbol`
will automatically connect to the VSC as MCP server on `localhost:4000`.

## Contributing

See [`doc/DESIGN.md`](doc/DESIGN.md) for a high-level introduction to the server design and the principles guiding tool development.

### Local Development Setup

Requires Python 3.14+ and [Poetry](https://python-poetry.org/).

```bash
# Checkout code
git checkout https://github.com/davidfstr/revise-mcp.git
cd revise-mcp

# Create/update the virtual environment at .venv/
poetry install

# Run the server locally
poetry run python -m revise

# Run tests
poetry run pytest tests/
```

### Testing with MCP Inspector

```bash
# Start in HTTP mode for testing with MCP Inspector
poetry run python -m revise --transport streamable-http

# In another terminal, start the inspector
npx -y @modelcontextprotocol/inspector
```

### VS Code MCP Configuration (Development)

To run the MCP server from your development checkout rather than an installed binary, point VS Code at the Poetry-managed venv:

```json
{
    "servers": {
        "revise": {
            "type": "stdio",
            "cwd": "/path/to/revise-mcp",
            "command": "poetry",
            "args": ["run", "python", "-m", "revise"]
        },
    }
}
```

