Metadata-Version: 2.4
Name: iiq-rules-mcp
Version: 1.6.4
Summary: Rules-only MCP server for SailPoint IdentityIQ -- makes LLM-authored IIQ Rules provably correct via Java reflection, BeanShell parsing, the rule registry, and the DTD. No customer data leaves the machine.
Author: SIMPLIFYAUTH CONSULTANCY SERVICES (OPC) PRIVATE LIMITED
License: Proprietary
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: cryptography>=42
Requires-Dist: jpype1>=1.5.0
Requires-Dist: mcp<2,>=1.0.0
Requires-Dist: pydantic<3,>=2.0
Description-Content-Type: text/markdown

# iiq-rules-mcp

**An MCP server whose single job is: an LLM that uses it cannot present an
incorrect SailPoint IdentityIQ Rule as verified.**

Every answer is grounded in *your* licensed IIQ installation — Java
reflection over your `identityiq.jar` + `WEB-INF/lib`, your `sailpoint.dtd`,
the rule registry, and the no-eval BeanShell parser — and every answer
carries a closed verdict: `confirmed`, `refuted`, `unverifiable`, or
`advisory`. "We could not check" is never allowed to look like "clean."

## What "verified" means (the correctness contract)

| Layer | Obligation | Ground truth | Provable? |
|---|---|---|---|
| Syntax | `<Source>` parses as BeanShell | `bsh.Parser` (never executed) | Yes |
| Packaging / import | XML validates against the DTD; raw `<` CDATA-wrapped; DOCTYPE present and matching the root (the import parser rejects DOCTYPE-less files — measured live) | Xerces + your DTD + packaging checks | Yes — backed by a 156-rule calibration sweep ([report](docs/calibration-2026-07-16.md)) |
| API existence | Every imported class, static member, and method call with a grounded receiver exists on your JAR, incl. inherited members; an untypable call prevents `confirmed`, and compiler bridges are never reported as real API | JPype reflection over the attested classpath (cross-checked against `javap`) | Yes |
| Signature conformance | The rule's `type` and declared `<Signature>` match the registry contract | `Rule$Type` enum + IIQDA registry (your export's `<RuleRegistry>` overrides per-type, per-field labeled) | Yes |
| Variable scope | No reads of variables neither defined nor injected for the rule type | registry contract + a calibrated scope model (0 false positives across 156 real rules) | Calibrated — advisory where the model says so |
| Object references | `<Reference>`s resolve in your deployment | your local `export -clean` catalog (optional) | Only with the export — else labeled `unverifiable` |
| **Runtime semantics** | Logic does what the requirement means; data assumptions; connector behavior | — | **No. Always labeled out of scope. The server never claims it.** |

The composite gate `iiq_verify_rule_artifact` runs all provable layers as
seven stages and returns ONE verdict: any refuted stage refutes the
artifact; any unverifiable stage means it cannot be called verified;
`confirmed` only when every gating stage confirms.

## Privacy & grounding boundary

- **No customer data or rule examples are ever sent to the model.** There
  is no source/example-retrieval surface in this product — the capability
  is absent by construction, not switched off.
- What crosses to the model: the artifact *you are authoring*, framework
  metadata (signatures, DTD content models, registry contracts), and
  verdicts. The optional export is read **locally** for reference
  resolution; the only customer-derived strings a response can carry are
  ≤3 did-you-mean object names (`IIQ_DISABLE_CATALOG_SUGGESTIONS=true`
  removes those).
- stdio only; the verification engine makes no network calls and no
  telemetry — the sole network module is the license client (trial,
  activation, ~monthly lease renewal; see *Privacy & network, exactly*
  below). All verifiable at runtime with any process/network monitor.
  Bundled data is first-party (functional facts + own-words prose) plus
  synthetic fixtures.
- **You bring your own licensed IIQ artifacts.** Nothing of SailPoint's is
  redistributed here.

## Install

Requires Python 3.11+, a JDK (17+ recommended, 11 floor — enforced at
boot), and a licensed IIQ installation to point at.

```bash
# from a GitHub release wheel:
uv tool install --from https://github.com/simplifyauth/iiq-rules-mcp/releases/latest iiq-rules-mcp
# or from a checkout:
uv tool install --from /path/to/iiq-rules-mcp iiq-rules-mcp
```

### Environment

| Variable | Required | Purpose |
|---|---|---|
| `IIQ_JAR_PATH` | yes | your install's `identityiq.jar` |
| `IIQ_LIB_DIRS` | yes | your `WEB-INF/lib` (+ `WEB-INF/lib-connectors`), `;`-separated — ships `bsh.jar`; boot **refuses** without it |
| `IIQ_DTD_PATH` | yes* | your `sailpoint.dtd` (same release as the JAR) — without it, DTD stages answer `unverifiable` |
| `IIQ_OBJECTS_EXPORT` | optional | `iiq console` → `export -clean <path>` — enables object-reference resolution + registry override; absent → rules that CARRY `<Reference>`s cap at `unverifiable` (never silently clean), while reference-free rules still reach `confirmed` |
| `IIQ_JVM_PATH` | optional | override when the Windows-registry default JVM is wrong (boot names this fix when needed) |
| `IIQ_RULE_CONFIG_DIR` | optional | your install's `DefaultRuleArgs.xml`/`DefaultRuleRegistry.xml` (else the bundled MIT snapshot) |
| `IIQ_DISABLE_CATALOG_SUGGESTIONS` | optional | strict mode: suppress did-you-mean names |
| `IIQ_BEST_PRACTICES_FILE` / `IIQ_SECURITY_RULES_FILE` / `IIQ_PERFORMANCE_RULES_FILE` | optional | review-ruleset overrides |
| `IIQ_LICENSE_FILE` | optional | license file location override (default: `~/.iiq-rules-mcp/license.json`, written by the `activate` command). Verification is local Ed25519 |
| `IIQ_LOG_LEVEL`, `IIQ_VERSION_OVERRIDE`, `IIQ_CONFIG_FILE` | optional | logging / version stamp / JSON config file (env always wins) |

Boot is attested: JVM identity readback, marker classes
(`sailpoint.object.Identity`, `sailpoint.object.Rule`, `bsh.Parser`),
classpath fingerprint. A mis-grounded configuration **refuses to boot
(exit 4) with the fix named** — this server never serves half-truths.
Structural pins additionally verify your export/DTD/JAR belong to the same
release; `pins_ok: false` is stamped on every response when they don't.

### Claude Desktop

```json
"iiq-rules-mcp": {
  "command": "iiq-rules-mcp",
  "env": {
    "IIQ_JAR_PATH": "C:\\iiq\\WEB-INF\\lib\\identityiq.jar",
    "IIQ_LIB_DIRS": "C:\\iiq\\WEB-INF\\lib;C:\\iiq\\WEB-INF\\lib-connectors",
    "IIQ_DTD_PATH": "C:\\iiq\\sailpoint.dtd",
    "IIQ_OBJECTS_EXPORT": "C:\\iiq\\exports\\objects.xml"
  }
}
```

## The authoring loop

1. `iiq_grounding_status` — see what this session is grounded in.
2. Type unknown? `iiq_rule_catalog(keyword)` to shortlist candidates.
   Then ground the draft: `iiq_rule_signature(<type>)`,
   `iiq_xml_schema_for("Rule")`, `iiq_class_lookup` /
   `iiq_method_signature` for every API touched.
3. Draft (CDATA-wrap `<Source>`, contract-true `<Signature>`).
4. **`iiq_verify_rule_artifact`** — the gate. Repair per stage findings;
   repeat until `confirmed`.
5. Present with advisories + unverifiable items surfaced and the runtime-
   semantics ceiling stated.

The bundled [`system_prompt.md`](src/iiq_rules_mcp/prompts/system_prompt.md)
teaches this loop to the model.

## Tools (21)

Full reference — arguments, verdict semantics, example asks:
[docs/TOOLS.md](docs/TOOLS.md).

**The gate:** `iiq_verify_rule_artifact` (7 stages, one verdict) ·
**Selection:** `iiq_rule_catalog` (which type for which scenario — 102
types by functional family, keyword-searchable; always advisory) ·
**Status:** `iiq_grounding_status` ·
**Reflection:** `iiq_class_lookup`, `iiq_method_signature` (inherited +
`declared_on`; bridges segregated), `iiq_implementations_of`,
`iiq_search_api`, `iiq_verify_class_references` (three-state) ·
**DTD:** `iiq_xml_schema_for`, `iiq_xml_element_attrs` (raw content model
primary), `iiq_verify_xml` (CDATA fingerprint) ·
**Registry:** `iiq_rule_signature` (per-field source labels + doc-derived
behavior facts: purpose / fires-when / executor / return-content contract,
own-words from the Rules guide, honestly labeled where uncovered) ·
**BeanShell:** `iiq_verify_beanshell`, `iiq_analyze_beanshell`,
`iiq_verify_beanshell_scope`, `iiq_verify_beanshell_members`,
`iiq_verify_rule` ·
**References:** `iiq_verify_references` ·
**Reviews (always advisory):** `iiq_best_practices`, `iiq_security_review`,
`iiq_performance_review`

## Evidence

Every claim above is backed by recorded, reproducible evidence
([full trail](docs/launch-evidence.md)):

| Measurement | Result |
|---|---|
| Live failure classes converted into gating checks | **7** (dangling refs, DOCTYPE packaging, storage limits, CDATA, scope, …) |
| Calibration sweep over real rules | **156 rules, zero unexpected findings** ([latest report](docs/calibration-2026-07-16.md)) |
| Rule-type universe cross-check | **102/102** — JAR enum = DTD enumeration = live importer's own list |
| Gate-confirmed rules imported into live IIQ 8.1 | **11/11 clean** (hand-test rounds 1–2, artifacts in [docs/hand-test-2026-07-07](docs/hand-test-2026-07-07/)) |
| Verification-engine network calls | **0** — enforced by the architecture test suite (all license egress is confined to `license_client.py`) |

## Pricing & commercial licensing

Evaluating the software and personal learning are **free during a 7-day
trial** (start it with your email; one trial per person/device). Production
use requires a commercial license — full terms, definitions, and FAQ in
[COMMERCIAL.md](COMMERCIAL.md):

| Tier | Price | Covers |
|---|---|---|
| Individual | $39/month or $350/year | one named person, one device |
| Team | $99/seat/month, billed annually (5-seat min) | named seats, priority support |
| Enterprise / Site | from $18,000/year | unlimited seats at one company/practice, committed response targets, private distribution, priority live-loop |

**Founder tier:** the first cohort receives a substantial discount in
exchange for feedback and a reference. Email
[contact@simplifyauth.com](mailto:contact@simplifyauth.com).

### Trial, license, and what the software does about it

Start in one command (7 days, full features, one trial per person/device):

```
python -m iiq_rules_mcp.activate trial --email you@example.com
```

Individual licenses are **one user, one device** (2 self-serve migrations a
year: `deactivate` on the old machine, activate on the new). Buying is
zero-step for trial users — the client's background license sync picks the
upgrade up on its own.

| Mode | Tools answer? | Meaning |
|---|---|---|
| `trial` | yes | your 7 days, full features (warns in the last 3) |
| `licensed` | yes | paid, device-bound, lease current |
| `grace` | yes | lease expired ≤ 3 days (offline grace) — reconnect to renew |
| `trial_expired` / `expired` | **no** | trial over / term over — every tool answers with the buy link instead of a verdict |
| `lease_required` | **no** | no valid lease — run `python -m iiq_rules_mcp.activate sync` online |
| `unlicensed` | **no** | no license yet — start the trial above |
| `invalid` | **no** | unreadable/tampered file, device mismatch, or clock rollback — never quieter than no file |

`iiq_grounding_status` always answers, so the state is always inspectable.
Enterprise licenses (root-signed, schema 1) keep their contractual
label-don't-block behavior unchanged.

**Privacy & network, exactly:** verification is local. The ONLY network
calls live in `license_client.py` — trial start, activation, and a roughly
monthly lease renewal (30-day leases) — and they send your email
(signup/deactivation only), the
license id, and salted hashes of device identifiers. **Rule content, file
paths, and anything read from your IIQ install never leave your machine.**
A malformed license file is labeled, never silently degraded; an ended
trial stops issuing verdicts and says so plainly.

## Support & the live loop

- **Bugs & divergences:** open an issue with the console/import error and
  the gate's verdict JSON. A reproducible divergence between "gate said"
  and "IIQ did" is triaged into a new shipped check — that is how all
  seven existing failure classes were born, most within a day of report.
- **Commercial support:** per tier (community / priority / committed
  response targets). Enterprise gets priority live-loop.
- **Security reports:** see [SECURITY.md](SECURITY.md).
- **Contact:** [contact@simplifyauth.com](mailto:contact@simplifyauth.com)

## Version support

Built and calibrated against **IIQ 8.1** (156 real rules, zero unexpected
findings — [evidence](docs/launch-evidence.md)). Other 8.x releases use the
same substrate mechanisms and are expected to work, but are **untested**:
the boot attestation and structural pins will tell you loudly if your
substrates disagree — treat non-8.1 as unverified territory until the
calibration sweep has been run against it.

## Development

```bash
uv run pytest tests/unit           # fast, no JVM
uv run pytest -m integration       # needs the real env vars
uv run ruff check src tests && uv run mypy src/iiq_rules_mcp
uv run python scripts/calibration_sweep.py --help
```

Architecture rules are tests: only `config.py` reads the environment;
tools respond only through the verdict envelope; every tool description
must state its verdict semantics; overclaim phrases fail CI.

## License

Proprietary; distributed as an installable only (see [LICENSE](LICENSE)).
Evaluation and personal learning are free during a 7-day trial (EULA
Evaluation Grant); production use requires a [commercial license](COMMERCIAL.md).
Enterprise customers can arrange source review under NDA and source
escrow. Release history: [CHANGELOG.md](CHANGELOG.md).

---

*(c) 2026 SIMPLIFYAUTH CONSULTANCY SERVICES (OPC) PRIVATE LIMITED.*

*iiq-rules-mcp is an independent product, not affiliated with, endorsed
by, or sponsored by SailPoint Technologies. SailPoint, IdentityIQ, and
related marks are trademarks of SailPoint Technologies, Inc., used here
only to describe compatibility.*
