Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .ai/mcp/mcp.json
Empty file.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pip install nox
And multiple Python versions:

```commandline
uv python install 3.9 3.10 3.11 3.12 3.13
uv python install 3.10 3.11 3.12 3.13
```

To run all nox sessions (defined in ``noxfile.py``), run in the root of the project:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The most common questions with answers can be found at the bottom ⬇ of this RE

## Requirements

Python 3.9+ :snake: and Robot Framework 4.0+ :robot:.
Python 3.10+ :snake: and Robot Framework 5.0+ :robot:.

## Installation

Expand Down
38 changes: 29 additions & 9 deletions docs/integrations/ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ Install Robocop with MCP support:
pip install robotframework-robocop[mcp]
```

!!! note "Python Version Requirement"
MCP support requires Python 3.10 or higher. The core Robocop functionality still supports Python 3.9+.

### Running the Server

#### Standalone (Testing)
Expand Down Expand Up @@ -139,6 +136,14 @@ If the server doesn't appear or tools don't work:
4. **Check config syntax**: Ensure valid JSON (no trailing commas)
5. **Path issues**: If `robocop-mcp` isn't found, use the full path (e.g., `/usr/local/bin/robocop-mcp`)

### Configuration

Robocop MCP loads its configuration in this order:

- from the default config file when the MCP server is started in a directory that contains it
- from a config file provided via the `config_path` tool parameter
- from any tool-specific override options, when available

### Available Tools

#### Linting Tools
Expand All @@ -147,15 +152,16 @@ If the server doesn't appear or tools don't work:

Lint Robot Framework code provided as text content.

| Parameter | Type | Description |
|-----------|------|-------------|
| `content` | string | Robot Framework source code to lint (required) |
| `filename` | string | Virtual filename for file type detection (default: "stdin.robot") |
| Parameter | Type | Description |
|-----------|-----------|-------------|
| `content` | string | Robot Framework source code to lint (required) |
| `filename` | string | Virtual filename for file type detection (default: "stdin.robot") |
| `select` | list[str] | Rule IDs/names to enable (e.g., `["LEN01", "too-long-keyword"]`) |
| `ignore` | list[str] | Rule IDs/names to ignore |
| `threshold` | string | Minimum severity: `I` (Info), `W` (Warning), or `E` (Error) |
| `limit` | int | Maximum number of issues to return |
| `threshold` | string | Minimum severity: `I` (Info), `W` (Warning), or `E` (Error) |
| `limit` | int | Maximum number of issues to return |
| `configure` | list[str] | Rule configurations (e.g., `["line-too-long.line_length=140"]`) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** List of diagnostic issues, each containing `rule_id`, `name`, `message`, `severity`, `line`, `column`, `end_line`, `end_column`.

Expand All @@ -171,6 +177,7 @@ Lint a single Robot Framework file from disk.
| `threshold` | string | Minimum severity: `I`, `W`, or `E` |
| `limit` | int | Maximum number of issues to return |
| `configure` | list[str] | Rule configurations |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** List of diagnostic issues (same format as lint_content).

Expand All @@ -190,6 +197,7 @@ Lint multiple Robot Framework files using paths or glob patterns. Useful for che
| `configure` | list[str] | Rule configurations |
| `group_by` | string | Group results: `"severity"`, `"rule"`, or `"file"` |
| `summarize_only` | bool | Return only stats without individual issues (default: false) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `total_files`, `total_issues`, `files_with_issues`, `issues` (omitted when `summarize_only`), `summary`, `limited`, `offset`, `has_more`, `unmatched_patterns`, `group_counts` (when grouped), and `top_rules` (when `summarize_only`).

Expand All @@ -202,6 +210,7 @@ Analyze Robot Framework code and get actionable fix suggestions for each issue.
| `content` | string | Robot Framework source code to analyze (required) |
| `filename` | string | Virtual filename (default: "stdin.robot") |
| `rule_ids` | list[str] | Specific rule IDs to get suggestions for |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with:

Expand All @@ -224,6 +233,7 @@ Format Robot Framework code and return the formatted result.
| `select` | list[str] | Formatter names to apply (default: all enabled formatters) |
| `space_count` | int | Spaces for indentation (default: 4) |
| `line_length` | int | Maximum line length (default: 120) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `formatted` (the code), `changed` (boolean), and `diff` (unified diff if changed).

Expand All @@ -245,6 +255,7 @@ Format Robot Framework code and lint the result in one operation. **Recommended
| `limit` | int | Maximum issues to return |
| `configure` | list[str] | Rule configurations |
| `overwrite` | bool | If True and `file_path` is used, write formatted content back to file (default: false) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `formatted`, `changed`, `diff`, `issues` (remaining), `issues_before`, `issues_after`, `issues_fixed`. When `file_path` is used, also includes `file` and `written`.

Expand All @@ -259,6 +270,7 @@ Format a Robot Framework file from disk. Can optionally overwrite the file with
| `space_count` | int | Spaces for indentation (default: 4) |
| `line_length` | int | Maximum line length (default: 120) |
| `overwrite` | bool | Write formatted content back to file (default: false) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `file`, `formatted`, `changed`, `diff`, and `written` (whether file was overwritten).

Expand All @@ -275,6 +287,7 @@ Format multiple Robot Framework files using paths or glob patterns. Can optional
| `line_length` | int | Maximum line length (default: 120) |
| `overwrite` | bool | Write formatted content back to files (default: false) |
| `summarize_only` | bool | Return only stats without per-file results (default: false) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `total_files`, `files_changed`, `files_unchanged`, `files_written`, `results` (omitted when `summarize_only`), `errors`, `unmatched_patterns`.

Expand Down Expand Up @@ -334,6 +347,7 @@ Explain a specific issue at a given line with surrounding context. More detailed
| `line` | int | The line number to explain (1-indexed, required) |
| `filename` | string | Virtual filename (default: "stdin.robot") |
| `context_lines` | int | Number of lines to show before/after (default: 3) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with:

Expand All @@ -359,6 +373,7 @@ Get rich context for LLM-assisted fixing of Robot Framework code issues. This to
| `line` | int | Specific line to get context for (None = all issues) |
| `rule_ids` | list[str] | Filter to specific rule IDs (e.g., `["LEN01", "NAME02"]`) |
| `context_lines` | int | Lines of context before and after target (default: 5) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with:

Expand Down Expand Up @@ -423,6 +438,7 @@ List all available linting rules with optional filtering.
| `category` | string | Filter by category: `"LEN"`, `"NAME"`, `"DOC"`, `"SPACE"`, etc. |
| `severity` | string | Filter by severity: `"I"`, `"W"`, or `"E"` |
| `enabled_only` | bool | Only return enabled rules (default: false) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** List of rule summaries with `rule_id`, `name`, `severity`, `enabled`, `message`.

Expand All @@ -433,6 +449,7 @@ List all available formatters.
| Parameter | Type | Description |
|-----------|------|-------------|
| `enabled_only` | bool | Only return enabled formatters (default: true) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** List of formatter summaries with `name`, `enabled`, `description`.

Expand All @@ -443,6 +460,7 @@ Get detailed documentation for a specific linting rule.
| Parameter | Type | Description |
|-----------|------|-------------|
| `rule_name_or_id` | string | Rule name (e.g., `"too-long-keyword"`) or ID (e.g., `"LEN01"`) (required) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `rule_id`, `name`, `message`, `severity`, `enabled`, `deprecated`, `docs`, `parameters`, `added_in_version`, `version_requirement`.

Expand All @@ -453,6 +471,7 @@ Get detailed documentation for a specific formatter.
| Parameter | Type | Description |
|-----------|------|-------------|
| `formatter_name` | string | Formatter name (e.g., `"NormalizeSeparators"`) (required) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** Dictionary with `name`, `enabled`, `docs`, `min_version`, `parameters`, `skip_options`.

Expand All @@ -467,6 +486,7 @@ Search for linting rules by keyword. Searches across rule names, messages, and d
| `category` | string | Filter by category: `"LEN"`, `"NAME"`, `"DOC"`, etc. |
| `severity` | string | Filter by severity: `"I"`, `"W"`, or `"E"` |
| `limit` | int | Maximum results to return (default: 20) |
| `config_path` | Path | Path to the Robocop toml configuration file |

**Returns:** List of matching rules with `rule_id`, `name`, `message`, `severity`, `enabled`, `match_field`, `match_snippet`.

Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Requirements

Python 3.9+ and Robot Framework 4+.
Python 3.10+ and Robot Framework 5+.

## Installation

Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

nox.options.default_venv_backend = "uv"

PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
PYTHON_VERSIONS = ["3.10", "3.11", "3.12", "3.13", "3.14"]

ROBOT_VERSIONS = ["robotframework==4.*", "robotframework==5.*", "robotframework==6.*", "robotframework==7.*"]
ROBOCOP_VERSIONS = (
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ robocop-mcp = "robocop.mcp.server:main"

[project.optional-dependencies]
mcp = [
"fastmcp>=2.13.0"
"fastmcp>=2.13.0,<3"
]

[project.urls]
Expand Down
14 changes: 8 additions & 6 deletions src/robocop/mcp/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

from robocop.runtime.resolved_config import ResolvedConfig


@lru_cache(maxsize=1)
def get_linter_config() -> ResolvedConfig:
@lru_cache
def get_linter_config(config_path: Path | None) -> ResolvedConfig:
"""
Get cached LinterConfig with all rules loaded.
Expand All @@ -24,14 +26,14 @@ def get_linter_config() -> ResolvedConfig:
from robocop.config.manager import ConfigManager
from robocop.runtime.resolver import ConfigResolver

manager = ConfigManager()
manager = ConfigManager(config=config_path)
resolver = ConfigResolver(load_rules=True)

return resolver.resolve_config(manager.default_config)


@lru_cache(maxsize=1)
def get_formatter_config() -> ResolvedConfig:
@lru_cache
def get_formatter_config(config_path: Path | None) -> ResolvedConfig:
"""
Get cached FormatterConfig with all formatters loaded.
Expand All @@ -45,7 +47,7 @@ def get_formatter_config() -> ResolvedConfig:
from robocop.config.manager import ConfigManager
from robocop.runtime.resolver import ConfigResolver

manager = ConfigManager()
manager = ConfigManager(config=config_path)
resolver = ConfigResolver(load_formatters=True)

return resolver.resolve_config(manager.default_config)
Expand Down
23 changes: 16 additions & 7 deletions src/robocop/mcp/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
from robocop.mcp.cache import get_formatter_config, get_linter_config

if TYPE_CHECKING:
from pathlib import Path

from fastmcp import FastMCP


def _get_rules_catalog() -> list[dict[str, Any]]:
def _get_rules_catalog(config_path: Path | None = None) -> list[dict[str, Any]]:
"""
Get catalog of all available linting rules.

Args:
config_path: Path to the Robocop toml configuration file

Returns:
list[dict[str, Any]]: List of rules with basic information.

"""
linter_config = get_linter_config()
linter_config = get_linter_config(config_path)

# Use a set to deduplicate (rules dict has both id and name pointing to same rule)
seen_ids: set[str] = set()
Expand All @@ -47,15 +52,18 @@ def _get_rules_catalog() -> list[dict[str, Any]]:
return sorted(rules, key=operator.itemgetter("rule_id"))


def _get_formatters_catalog() -> list[dict[str, Any]]:
def _get_formatters_catalog(config_path: Path | None = None) -> list[dict[str, Any]]:
"""
Get catalog of all available formatters.

Args:
config_path: Path to the Robocop toml configuration file

Returns:
list[dict[str, Any]]: List of formatters with basic information.

"""
formatter_config = get_formatter_config()
formatter_config = get_formatter_config(config_path)

formatters = []
for name, formatter in formatter_config.formatters.items():
Expand All @@ -73,19 +81,20 @@ def _get_formatters_catalog() -> list[dict[str, Any]]:
return sorted(formatters, key=operator.itemgetter("name"))


def _get_rule_details(rule_id: str) -> dict[str, Any]:
def _get_rule_details(rule_id: str, config_path: Path | None = None) -> dict[str, Any]:
"""
Get detailed information about a specific rule.

Args:
rule_id: Rule ID (e.g., "LEN01") or name (e.g., "too-long-keyword")
config_path: Path to the Robocop toml configuration file

Returns:
dict[str, Any]: Detailed rule information including full documentation
and configurable parameters.

"""
linter_config = get_linter_config()
linter_config = get_linter_config(config_path)

if rule_id not in linter_config.rules:
raise ResourceError(f"Rule '{rule_id}' not found")
Expand Down Expand Up @@ -123,7 +132,7 @@ def _get_rule_details(rule_id: str) -> dict[str, Any]:
def register_resources(mcp: FastMCP) -> None:
"""Register all MCP resources with the server."""

@mcp.resource("robocop://rules")
@mcp.resource("robocop://rules") # FIXME: add config handling
def get_rules_catalog() -> list[dict[str, Any]]:
"""
Get catalog of all available linting rules.
Expand Down
Loading