Metadata-Version: 2.4
Name: rosentic-mcp
Version: 0.1.5
Summary: MCP server for Rosentic - cross-branch conflict detection for parallel AI coding agents: catches breaking signature changes, HTTP route breaks, and schema drift between agent branches before merge
Project-URL: Homepage, https://rosentic.com
Project-URL: Repository, https://github.com/Rosentic/rosentic-action
Project-URL: Issues, https://github.com/Rosentic/rosentic-action/issues
Author-email: Rosentic <hello@rosentic.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agent-orchestration,ai-agents,ai-coding-agents,breaking-change,ci-cd,code-review,cross-branch-conflict,git,mcp,mcp-server,merge-conflicts,merge-safety,model-context-protocol,parallel-agents,schema-drift,semantic-conflict,signature-change,static-analysis,tree-sitter
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.11
Requires-Dist: fastmcp>=3.1.0
Description-Content-Type: text/markdown

# Rosentic MCP Server

CI checks each PR against main. It doesn't check open PRs against each other.

Rosentic does. Install one package, then ask your coding agent to check for cross-branch conflicts before you merge.

One agent changes a function signature. Another agent still calls the old version. Both pass CI. Git merges the text cleanly. Main breaks.

Rosentic compares active branches against each other. Deterministic static analysis across 13 languages (Python, JavaScript, TypeScript, Go, Java, Rust, Swift, C++, Kotlin, C#, Ruby, PHP, Dart). No LLM. In local MCP mode, code and scan metadata stay on your machine.

MCP gives your editor or coding agent access to Rosentic tools on demand. It is not a hard automatic gate by itself: a CLAUDE.md instruction is advisory, and an agent can forget or ignore it. For a gate that BLOCKS, run `rosentic-mcp install-hooks` and commit the result (see Enforce below), or use the GitHub Action as a CI backstop.

## Freshness and offline behavior

- Scan results are cached for 60 seconds per (repo, base, branch-set); a
  repeat call inside that window returns the cached result instantly.
- `check_file` marks sibling-branch index data stale after 300 seconds
  (`stale_after_seconds` to override) and reports freshness metadata in
  every response, so an agent always knows how old its sibling context is.
- Sibling context comes from your local git refs: run `git fetch` to pick
  up new sibling work; the server never fetches for you.
- Offline: `check_file`, `check_conflicts`, `explain_conflict`, and
  `list_branches` are fully local and work with no network. `get_policy`
  and `get_verdict` read the dashboard API and degrade gracefully offline -
  they return an explanatory payload (never raise), and `get_policy`
  reports the built-in default (advisory, UNSAFE fails), which is exactly
  what the CI gate falls back to.
- Linked git worktrees are supported (repo detection uses git rev-parse,
  not a .git-directory test). One caveat: when the engine runs via Docker
  (no local checkout), scan the MAIN checkout rather than a linked worktree -
  a worktree's gitdir lives outside the mounted path. With a local engine
  (dev checkout / ROSENTIC_ENGINE_PATH), worktrees work everywhere.
- Call metering is a local counter at `~/.rosentic/mcp-usage.json`.
  Nothing is billed and the counter never blocks a call.

## Install

```bash
pip3 install --upgrade rosentic-mcp
```

Use `--upgrade` even on a machine that already has the package: a plain `pip install` is a no-op for returning users and silently keeps stale code. If `pip3` is not on PATH, use `python3 -m pip install --upgrade rosentic-mcp`.

## Configure

### Claude Code

Add to `~/.claude.json`:

```json
{
  "mcpServers": {
    "rosentic": {
      "command": "rosentic-mcp"
    }
  }
}
```

Add this instruction to your repo's `CLAUDE.md`:

```text
Before running git push, call the Rosentic check_conflicts MCP tool to verify your branch is compatible with other active branches.
```

### Codex

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.rosentic]
command = "rosentic-mcp"
args = []
```

Add the same push-boundary instruction to `AGENTS.md`.

### Cursor / VS Code

Add to MCP settings:

```json
{
  "rosentic": {
    "command": "rosentic-mcp"
  }
}
```

## Enforce (make the gate block, not advise)

The steps above give agents the tools and an instruction. Nothing yet blocks a bad push. One command installs the committed enforcement path:

```bash
cd your-repo
rosentic-mcp install-hooks
git add .githooks .claude/settings.json
git commit -m "Add Rosentic enforcement gate"
```

What it installs:

- `.githooks/pre-push` - a committed git hook that runs `rosentic-mcp gate` and exits nonzero on UNSAFE findings. The gate fails closed: if the scan cannot run, the push is blocked with an explanation. Bypass one push with `ROSENTIC_SKIP=1 git push`; make WARNING findings block too with `ROSENTIC_STRICT=1`.
- `git config core.hooksPath .githooks` - the per-clone wiring. git config cannot be committed, so each fresh clone runs `rosentic-mcp install-hooks` once (it is idempotent).
- `.claude/settings.json` - committed Claude Code hooks: `PreToolUse` blocks an UNSAFE write before it lands, and `Stop` keeps the agent working while UNSAFE cross-branch findings exist. Because this file is committed, every fresh clone gates Claude Code sessions immediately, with zero per-user setup. If the file already exists, install-hooks appends the Rosentic entries and never removes yours.

You can also run the gate directly, in CI or by hand:

```bash
rosentic-mcp gate --repo . --base main   # exit 0 clean, 1 UNSAFE, 2 scan error
```

## Tools

### `check_conflicts`

Scan a git repo for cross-branch conflicts affecting your current branch.

**Parameters:**
- `repo_path` (required) - path to local git repository
- `branch` - branch to check (defaults to current branch)
- `base` - base branch to compare against (defaults to `main`)
- `format` - `"summary"` (natural language) or `"json"` (structured findings)

**Example response:**
> Rosentic found 4 unique conflict(s) for feature/auth-refactor: 2 UNSAFE, 2 WARNING. Layer breakdown: 3 L1 signature, 1 L2 route. Most critical: createUser() in src/auth/users.ts - changed from 2 to 3 required params. Recommendation: inspect the UNSAFE findings first and update the stale branch before merging.

### `check_file`

Check proposed complete file content against active sibling branches before writing.

**Parameters:**
- `repo_path` (required) - path to local git repository
- `file` (required) - repository-relative file path being edited
- `content` (required) - proposed complete post-edit file content
- `branch` - current branch to use as the proposed-content overlay
- `base` - base branch to compare against (defaults to `main`)
- `stale_after_seconds` - mark branch index data stale after this many seconds

The response includes SAFE/WARNING/UNSAFE verdict, conflict direction, consumers with branch attribution, checked branches, skipped branches with reasons, and index freshness metadata.

### `explain_conflict`

Get detailed explanation of a specific finding, including affected branches, consumer locations, and remediation steps.

**Parameters:**
- `repo_path` (required) - path to local git repository
- `finding_id` (required) - ID from a previous `check_conflicts` result

### `list_branches`

List local branches sorted by most recent commit date.

**Parameters:**
- `repo_path` (required) - path to local git repository

### `get_verdict`

Look up recorded gate verdicts in the Rosentic audit ledger by SHA (append-only gate ledger via the dashboard API). Requires `ROSENTIC_API_KEY` (a `ros_live_` workspace key) for the org's workspace. Degrades gracefully offline - returns an explanatory payload instead of raising.

**Parameters:**
- `org` (required) - GitHub org/owner of the repository
- `repo` (required) - repository name
- `head_sha` - PR head SHA to look up; if omitted, returns the most recent gate verdicts for the repo

### `get_policy`

Fetch the effective Rosentic gate policy (repo > org default > built-in): gate mode, severity threshold, per-layer enforce/advisory, branch-pattern rules, and the stamped policy version. Public read - no key needed. Degrades gracefully offline with the built-in default and a notice.

**Parameters:**
- `org` (required) - GitHub org/owner of the repository
- `repo` - repository name; omit for the org default policy

## Troubleshooting

### A user-scope entry shadows the project `.mcp.json`

A `rosentic` entry in your user scope (`mcpServers.rosentic` in `~/.claude.json`) silently takes precedence over a repo's committed `.mcp.json` under the same server name. Every session in that repo then talks to whatever the user-scope entry points at (often a dev checkout) instead of the published package the project pins, and results can come from a different engine version. This is most likely on developer machines that have both.

The server detects the common case: on the first scan of a repo whose committed `.mcp.json` declares a different rosentic server than the one actually answering, the tool result opens with `MCP CONFIG SHADOWING DETECTED`, naming the repo's `.mcp.json` and the running server.

Remedy:

```bash
claude mcp get rosentic          # Scope shows where the entry resolves from
claude mcp remove rosentic -s user
# or keep the dev server under a different name so it cannot collide:
# rename the user-scope entry to rosentic-dev
```

Then restart the session and re-check `claude mcp get rosentic`: Scope should say project.

## What it detects

| Layer | What | Example |
|-------|------|---------|
| L1 | Function signature mismatches | Branch A adds required param, Branch B still calls with old arity |
| L2 | HTTP route contract breaks | Route path or method changes, consumers still call old endpoint |
| L3 | Schema incompatibilities | Proto field removed, GraphQL type changed, OpenAPI contract broken |

## How it works

- **Deterministic** - no LLM in the scan path. Tree-sitter AST parsing + evidence-gated verdicts.
- **Fast** - scans 30 branches in under 30 seconds for most repos.
- **Evidence-based** - every finding includes proof: which symbol changed, where it's called, why it's linked.

## Requirements

- Python 3.11+
- Git repository with branches to scan
- Works on macOS, Linux, Windows (WSL)

## Verify

From a checkout that has this repository's helper scripts:

```bash
python3 scripts/mcp-smoke-test.py
```

Expected output:

```text
Rosentic MCP OK: check_conflicts, check_file, explain_conflict, get_policy, get_verdict, list_branches
```

## Links

- [Website](https://rosentic.com)
- [GitHub](https://github.com/Rosentic/rosentic-action)
- [GitHub Action](https://github.com/Rosentic/rosentic-action)

<!-- mcp-name: io.github.Rosentic/rosentic -->
