# Changelog

All notable changes to `itasca-mcp` (formerly `pfc-mcp`) are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<!--
Convention (mirrors the yade-dev/trunk ChangeLog discipline):

- `## [Unreleased]` is curated by the maintainer from commit history at
  release time. Contributors do NOT edit this file -- a clear conventional
  commit message is enough. This keeps the changelog complete with zero
  changelog friction on any single change or PR.
- Documentation is first-class here: agents read the docs and the agentic
  install guides to understand and operate pfc-mcp, so notable documentation
  / install-flow changes are logged like behaviour changes.
- "Notable" = anything that changes how a user or agent understands or uses
  pfc-mcp: behaviour changes, added/removed tools, bug fixes, and notable
  documentation / install-flow changes. Pure internal refactors, tests, CI,
  and trivial doc fixes (typos, formatting) are omitted.
- Bridge changes that users perceive (deadlock fixes, new tool surfaces,
  protocol shifts requiring a minimum `itasca-mcp-bridge` version) are
  re-stated here even though they ship from a separate repo — readers of
  pfc-mcp's release notes shouldn't have to follow a second changelog to
  understand what changed for them. The notability test is the same: ask
  "if a user upgrades pfc-mcp without thinking about bridge versions, do
  they perceive this change?" Bridge-internal refactors and silent
  improvements stay only in `itasca-mcp-bridge`'s own changelog.
- Group entries under these categories (Keep a Changelog + Documentation):
  Added / Changed / Deprecated / Removed / Fixed / Security / Documentation.
- Referencing a commit/PR is optional; the entry must stand on its own.

At release the maintainer curates `## [Unreleased]` from `git log` since the
previous release, renames it to `## [x.y.z] - YYYY-MM-DD`, then starts a fresh
empty `## [Unreleased]`. The publish workflow extracts the section whose
header matches the tag version exactly (`## [x.y.z]`); `## [Unreleased]` is
never published until promoted, and a release fails if no matching version
section exists.

## [x.y.z] - YYYY-MM-DD

### Changed
- ...
-->

## [Unreleased]

## [0.6.1] - 2026-07-04

### Removed
- **FLAC 6.0 documentation.** FLAC 6.x embeds Python 2.7, which cannot run
  `itasca-mcp-bridge` (Python >= 3.6), so its command docs were removed from
  the corpus and from the doc tools' version selectors. Requesting
  `software="flac"` with `version="6.0"` now returns an `unsupported_version`
  error stating the covered versions (FLAC: 7.0/9.0). PFC 6.0 is unaffected —
  it ships Python 3.6 and remains fully supported (PFC: 6.0/7.0/9.0). Shared
  `_common` docs no longer advertise versions outside the requesting engine's
  coverage.

### Fixed
- **Bridge (`itasca-mcp-bridge` 0.4.1): client disconnects no longer dump
  tracebacks into the engine GUI console.** A dropped MCP client socket on
  Windows (`WinError 10053`, e.g. an SSE stream reconnecting) surfaced as a
  noisy `ConnectionAbortedError` traceback; the connection now deregisters
  quietly. Bridges pick this up automatically via self-upgrade on start.

### Documentation
- README prerequisites and the agentic bootstrap guide now state per-engine
  minimum versions, with an engine-aware embedded-interpreter mapping; during
  bootstrap the agent stops early with a clear report when the interpreter
  resolves to `python27` (FLAC 6.x) instead of failing at the pip step.
- README and bootstrap guide reframed as multi-engine (PFC → ITASCA), with
  client-native `mcp add`-style commands for manual setup and an explicit
  9.0+ engine recommendation.

## [0.6.0] - 2026-06-27

First release under the `itasca-mcp` name (formerly `pfc-mcp`). The server is
now multi-engine and the bridge transport is HTTP-based.

### Changed
- **Renamed `pfc-mcp` → `itasca-mcp`.** The Python package is `itasca_mcp`,
  the console script is `itasca-mcp`, and every tool is renamed `pfc_*` →
  `itasca_*`: `itasca_execute_code`, `itasca_execute_task`,
  `itasca_check_task_status`, `itasca_list_tasks`, `itasca_interrupt_task`,
  `itasca_browse_commands`, `itasca_browse_python_api`,
  `itasca_browse_reference`, `itasca_query_command`, `itasca_query_python_api`.
  Update your MCP client config to `uvx itasca-mcp`. The `pfc-mcp` package
  continues only as a deprecation shim that forwards here.
- **Bridge transport switched from WebSocket to HTTP + SSE** (httpx client).
  **Requires `itasca-mcp-bridge >= 0.4.0`** (submodule pin bumped to `v0.4.0`).
  The bridge self-upgrades from PyPI on every start, so existing installs move
  in step; WebSocket-era bridges are no longer supported.

### Added
- **Multi-engine documentation corpus** with a required `software` selector on
  the browse/query tools, spanning PFC, FLAC, 3DEC, MPoint, and MassFlow:
  - **3DEC**: commands plus the full engine-specific Python API (block family
    and all proprietary modules), with references for FISH intrinsics,
    initial/boundary conditions, geometry data tables, structural properties,
    plot items, histories-and-results, and joint constitutive models.
  - **MPoint (MPM)** and **MassFlow (gravity flow / caving)**: commands, Python
    SDK skeletons, engine-exposed modules, and plot-items references.
  - Shared references (range-elements, zone constitutive models) factored via a
    `_common` layer across PFC / FLAC / 3DEC where the engines agree.

### Fixed
- Engine resolution folds FLAC 9.x into a single key and resolves 9.0-only
  engines at 9.0.
- Docker dev container passes an explicit bridge mode instead of `mode=auto`.

### Documentation
- Command docs sourced from engine HTML now carry verified figure references
  and flag figure-defined entries as text-incomplete, so an agent knows when a
  command's full definition lives in an image rather than text.
- Version-gated the `program call` GIL-blocking warning to the engine versions
  it actually applies to.
- README, install docs, and the agentic bootstrap guides updated for the
  `itasca-mcp` name; daily-start flow recommends the self-upgrading two-liner
  with `addon.py` as first-install only.

## [0.5.0] - 2026-06-04

### Added
- `--bridge-port` CLI argument to override the bridge connection port —
  shorthand for `--bridge-url ws://localhost:PORT`. When a full
  `--bridge-url` / `PFC_MCP_BRIDGE_URL` is also supplied, only its port is
  overridden.

### Changed
- `pfc_execute_task` now submits over the bridge's `execute_task` message
  (renamed from `pfc_task` so the shared bridge protocol is product-neutral;
  the bridge keeps `pfc_task` as a deprecated alias). **Requires
  `itasca-mcp-bridge >= 0.1.5`** — older bridges silently ignore the message
  and submission times out. If that happens, confirm the bridge version with
  `pfc_execute_code` (`import itasca_mcp_bridge;
  print(itasca_mcp_bridge.__version__)`) and upgrade by re-running addon.py.
- Slimmed `pfc_check_task_status` pagination to `total_lines` + `line_range`,
  replacing the heavier pagination object. Output windows are still selected
  with `skip_newest` / `limit` / `filter_text`.
- Bumped `itasca-mcp-bridge` to `0.1.5` (submodule pin). Picks up the PySide6
  Qt task-pump fix so the bridge starts on PFC 9.7+ (Python 3.10 / Qt6),
  where a hard `PySide2` import previously froze the GUI on startup, and the
  `execute_task` protocol rename.

## [0.4.4] - 2026-06-03

### Fixed
- Bumped `itasca-mcp-bridge` to `0.1.3` (submodule pin `25668d7`), picking up
  two abspath-vs-CWD fixes since 0.4.3. Command-log capture: `itasca.command()`
  output no longer comes back empty when the engine's working directory
  diverges from Python's (headless consoles, or after a task `os.chdir()`).
  Task-log resolution: `pfc_check_task_status` no longer returns `(no output)`
  for a task that called `os.chdir()` — the task log and `tasks.json` are now
  read from a bridge root frozen at startup rather than the live working
  directory.

### Changed
- Task `elapsed_time` is now rounded to 2 decimals in `pfc_check_task_status`
  and `pfc_list_tasks` (e.g. `0.01` instead of `0.010000944137573242`). The
  field stays numeric; the bridge still reports full precision, with rounding
  applied at the MCP presentation layer alongside the existing
  `start_time` / `end_time` formatting.

## [0.4.3] - 2026-05-26

Contact API documentation now distinguishes mechanical and thermal
contact interfaces, eliminating fictitious `force_normal` /
`force_shear` advertisements on thermal contacts and adding version
gating for the PFC 9.0-only `VertexFacetContact`. Rblock contacts
and thermal-specific power methods join their respective interfaces
as first-class entries.

### Fixed
- `pfc_browse_python_api` / `pfc_query_python_api` no longer falsely
  advertise mechanical force methods on thermal contacts. The five
  `*ThermalContact` types (`BallBallThermalContact`,
  `BallFacetThermalContact`, `BallPebbleThermalContact`,
  `PebblePebbleThermalContact`, `PebbleFacetThermalContact`) are
  now documented through a dedicated `ThermalContact` interface that
  exposes contact state, geometry, grouping, model/property, and
  orientation methods — without `force_normal` / `force_shear`.
  Verified against the live PFC 9 bridge: 0 cross-contamination
  across all 15 contact classes. Closes #10.
- `VertexFacetContact` is now correctly tagged as PFC 9.0-only via
  per-type `availability.versions`, so 6.0 / 7.0 queries no longer
  suggest it exists on those versions.

### Added
- Mechanical `Contact` interface now expands to all four rblock
  contact types (`BallRBlockContact`, `PebbleRBlockContact`,
  `RBlockFacetContact`, `RBlockRBlockContact`) and the 9.0-only
  `VertexFacetContact`, matching the official PFC class hierarchy.
- Thermal-specific `power()` / `set_power(value)` methods are now
  documented for all five thermal contact types, sourced from the
  official PFC 9 Python HTML reference.

## [0.4.2] - 2026-05-25

Expanded Python SDK array API coverage (9 new modules) and bumped the
fastmcp dependency to the 3.x line so tests against its public API
surface work on a fresh install.

### Added
- `pfc_browse_python_api` / `pfc_query_python_api` now cover the
  vectorized array API: `ballarray`, `clumparray`, `wallarray`,
  `rblockarray`, and the four contact-pair arrays (`ballballarray`,
  `ballfacetarray`, `ballpebblearray`, `pebblepebblearray`,
  `pebblefacetarray`). Availability is encoded per function with
  `availability.versions` covering PFC 6.0 / 7.0 / 9.0.

### Changed
- Bump minimum `fastmcp` to `>=3.0.0`. Tests now use FastMCP's public
  `mcp.call_tool(...)` / `mcp.list_tools()` API.
- `pfc_browse_commands` lower-cases command input so mixed-case paths
  (e.g. `"Ball Create"`) resolve correctly, and the "command not found"
  suggestion list is now version-filtered (no more 9.0-only commands
  surfaced when querying 6.0).
- `pfc_browse_python_api` no longer echoes the full `functions` block
  inside `summary` when browsing a module, avoiding response truncation
  on large modules.

### Fixed
- Corrected `availability.versions` on the new array docs:
  `rotation` / `set_rotation` / `fill_rotation` entries were removed
  (they don't exist in any PFC version), and `euler` / `moi_prin`
  families are now correctly marked as available on PFC 6.0 as well,
  not 7.0/9.0 only. Verified against PFC 6.0 and 9.0 GUI bridges.

## [0.4.1] - 2026-05-24

Two reliability fixes for the bootstrap path users hit on day one — pip
entry-point detection, network fallback — plus a sharp agent-guidance
warning to stop `program call '*.p?dat'` from wedging the bridge, which
many existing PFC workflows would otherwise trigger.

### Changed
- Bridge startup output is quieter: callback registration, `TaskManager`
  init, and historical-task counts now only go to `bridge.log`, not the
  PFC IPython console. The status banner shows URL + log path and stops.
  Ships when pfc-mcp's pin moves past `itasca-mcp-bridge` `bc68380`.
- `addon.py` drops the interactive upgrade prompt. PFC's `pythonfile`
  host has no usable stdin, so the `input()` prompt was effectively
  dead — it either hit the EOFError fallback or silently returned an
  empty string and skipped the upgrade. A top-of-file
  `AUTO_UPGRADE = True` constant replaces it: the bootstrap now installs
  or upgrades to the latest `itasca-mcp-bridge` on every start with zero
  interaction. Flip to `False` to pin the locally installed version.

### Fixed
- `addon.py` no longer hard-codes pip's entry point to `pip.main`. That
  symbol exists in pip <= 9 (what PFC 6.0 ships) and was later restored
  as an internal shim, but it was absent in pip 10.0 through ~19.x -- a
  PFC interpreter carrying a pip from that range crashed with
  `AttributeError` instead of installing the bridge. The bootstrap now
  probes `pip._internal.cli.main`, `pip._internal`, and `pip.main` in
  turn, so it works regardless of which pip the embedded Python carries.
- A failed bridge install now points at pip's real error output and
  offers a manual `pip install` fallback, instead of surfacing only a
  bare `exit code 2` that hid the underlying cause.

### Documentation
- `pfc_execute_code` and `pfc_execute_task` docstrings now warn agents
  off `itasca.command("program call '<file>.p?dat'")`. PFC's
  command-script interpreter blocks the entire bridge for the script's
  duration — even bridge-internal calls like `pfc_list_tasks` time out,
  and recovery requires the user to manually stop PFC. If a user asks
  to run a `.dat` / `.p3dat` / `.p2dat` file, agents are instructed to
  read the file and translate each command into a `itasca.command(...)`
  call in Python instead, which preserves cycle-gap interleaving and
  keeps every command inspectable. Lots of existing PFC workflows are
  `.p?dat` scripts, so pointing an agent at one was previously a usage
  trap that made the toolchain look broken.
- The agentic bootstrap guide's Step 3 (agent-driven bridge install)
  now documents the PyPI -> Tsinghua mirror fallback, matching the
  fallback `addon.py` already performs. An agent installing the bridge
  for a user on a network that blocks PyPI previously had no documented
  retry path and would stall there.

## [0.4.0] - 2026-05-22

Bridge installation path migrates from `pfc-mcp-bridge` to `itasca-mcp-bridge`.
Tool surface (MCP tools and their wire contracts) is unchanged; the minor bump
reflects the user-visible install/dependency move, not a tool API change.

### Changed
- `addon.py` now installs `itasca-mcp-bridge` (PyPI) instead of
  `pfc-mcp-bridge`. End users get the multi-line `it.command()` deadlock fix
  (recognized aliases like `import itasca as it`) automatically — the
  splitter in `pfc-mcp-bridge` only matched the literal `itasca.command(...)`
  form and silently skipped any aliased call, letting one C batch hold the
  GIL while IPython's ZMQIOStream backed up. Re-download `addon.py` from the
  pfc-mcp repo to pick up the new install path.
- Agentic install now defaults the MCP client config to user/global scope
  instead of workspace, with workspace as an explicit last-resort fallback.
  Per-client profiles are CLI-first where a verified user-scope command
  exists (Claude/Codex/Gemini/Copilot) and file-edit for OpenCode. Affects
  nearly all users of the agentic install flow.

### Deprecated
- `pfc-mcp-bridge` PyPI package is frozen at `0.3.3` (final release). It
  emits a `DeprecationWarning` at import and prints a migration banner from
  `start()`. Users still on the old `addon.py` that upgrade will see the
  signal; those who decline the upgrade prompt stay on the affected version
  until they upgrade once. No further fixes ship to this package.

### Removed
- Embedded `pfc-mcp-bridge/` directory. Bridge sources now live in the
  separate [`itasca-mcp-bridge`](https://github.com/yusong652/itasca-mcp-bridge)
  repo and are referenced from this repo as a git submodule
  (`itasca-mcp-bridge/`). Fresh clones intended for bridge development need
  `git clone --recurse-submodules` (or `git submodule update --init
  --recursive`); end users running `addon.py` do not need the submodule.
- `.github/workflows/publish-bridge.yml` — bridge releases now happen from
  the bridge's own repo.
- `tests/test_command_splitter.py` and `tests/test_command_log.py` — they
  reached into the embedded bridge source via `sys.path` and now live with
  the bridge in its repo.

### Documentation
- `docs/development/source-install.{md,zh-CN.md}` document the submodule
  workflow: clone with `--recurse-submodules`, re-sync after pull, pin
  bump recipe, push order (bridge first), and how to read submodule
  status noise.
- Per-AI bootstrap files (CLAUDE/AGENTS/GEMINI/WARP.md) and the agentic
  install guide describe the new layout: `src/pfc_mcp/` for the MCP server,
  `itasca-mcp-bridge/` (submodule) for the bridge runtime.

## [0.3.15] - 2026-05-16

Version-aware reference documentation. The `contact-models` reference grows
from 5 to 22 models (all PFC mechanical + thermal contact models) and
`pfc_browse_reference` becomes version-aware for PFC 6.0/7.0/9.0.

- `pfc_browse_reference` gains a `version` parameter (6.0/7.0/9.0, default
  7.0) mirroring `pfc_browse_commands`: the model list is filtered by
  version availability, requesting a model absent in the target version
  returns a friendly `item_unavailable_for_version` error (with
  `available_versions`), and not-found suggestions are version-filtered.
- New models: arrlinear, bilinear, burger, eepa, fish, flatjoint,
  hysteretic, jkr, lineardipole, null, smoothjoint, softbond,
  springnetwork, thermalnull, thermalpipe, plus 9.0-only `mohr` and
  `subspringnetwork`. The 5 legacy curated models keep their content.
- Content is shared across versions (measured: 7.0 == 9.0, and 6.0 == 7.0
  for shared properties), so no per-version data duplication. Differences
  are encoded as a per-model `availability` map and per-property `since`
  markers (flatjoint `fj_cohres`/`fj_resmode` since 7.0; softbond
  `rgap`/`sb_coh`/`sb_fa`/`sb_mcf` since 9.0). `range-elements` and
  `plot-items` have no `availability` and remain version-agnostic
  (backward compatible).
- Property extraction reworked to split on `<dt id="kwd:...">` markers
  instead of pairing `<dt>/<dd>`, so pages that nest base properties in a
  child `<dl class="keyword">` (e.g. eepa) no longer bleed text or drop
  the nested keywords; model-specific properties collapse into a single
  clean group.

## [0.3.14] - 2026-05-15

Second wave of command documentation expansion: 123 new commands across
8 new scopes, bringing totals to 21 categories / 362 commands (was 13 / 239).

- `geometry` (34): nodes, edges, polygons, import/export, refinement, tessellation
- `fracture` (32): DFN — create, generate, intersections, templates, joint-set
- `table`    (11): numerical x/y tables for boundary conditions and history data
- `group`     (4): named-group lifecycle (create / list / rename / slot)
- `trace`     (7): per-object trace recording (lifecycle / export / interval)
- `project`   (6): GUI project containers (new / save / restore / execute)
- `data`     (26): user data containers — label/scalar/vector/tensor sub-namespaces
- `domain`    (3): domain extent, boundary conditions, periodic-cell strain-rate

All entries populated for PFC 6.0/7.0/9.0 (7 commands marked unavailable in
6.0 as new in later versions; e.g. `fracture intersections` machinery).

Note: the "dfn" doc module is exposed under the scope name `fracture` to
match the actual command verb users type. Dotted HTML stems (e.g.
`cmd_geometry.edge.create.html`, `cmd_data.scalar.create.html`) are mapped
to dash-separated JSON keys (`edge-create`, `scalar-create`) following the
rblock convention. `parse_pfc{600,700,900}.py` `build_html_map` learned the
same dot→dash rule.

`range` was considered but not added — the HTML files under
`range_manual/range_commands/` are syntax reference (rangelogical,
rangenaming, rangephrase), not commands.

Fixes `pfc_browse_commands` to accept the natural space-separated form
of compound sub-commands (e.g. `geometry edge create`, `data scalar
create`, `fracture intersections compute`, `contact cmat add`).
Previously the lookup only matched the stored dash form (`edge-create`,
`cmat-add`), forcing users to type a non-PFC-syntax variant.

## [0.3.13] - 2026-05-15

Expands PFC command documentation coverage from 177 to 239 entries
across 13 categories (was 10). Three new scopes — `program` (28
commands: license, threads, log, system, lifecycle), `history` (8:
interval, export, label, results), and `fish` (11: callback, define,
list, debug, trace) — join filled gaps in `model` (+9: creep,
dynamic, energy, factor-of-safety, fluid, list, precision, step,
title), `contact` (+3: extra, history, list), and `fragment` (+3:
groupisolated, groupslot, map). All entries are populated for PFC
6.0/7.0/9.0. These commands were previously skipped because their
Python SDK equivalents return nothing; now that `itasca.command()`
stdout is captured via the bridge log, exposing them lets agents
read program/model/history/contact list output through normal
documentation lookup. A new `bootstrap_missing.py` script under
`command_docs/` walks the installed PFC HTML manuals and writes
complete versioned JSON.

Trims trailing float-precision noise from BM25 result scores in
`pfc_query_command` and `pfc_query_python_api` (e.g.
`2.4743000000000004` → `2.47`).

## [0.3.12] - 2026-05-14

Surfaces the structured cancellation outcomes from pfc-mcp-bridge >=
0.3.2 through `pfc_execute_code`: `terminated` (bridge aborted at the
deadline, partial output returned, PFC state may be partially
modified), `timeout` with `details.method="stuck_in_c"` (snippet stuck
in a C extension; bridge may recover when the C call returns),
`interrupted`, and the default `timeout` path. Local wait widens to
`timeout_ms / 1000 + 5.0s` so the bridge has room to deliver the
`terminated` response after its own grace window.

Drops `pfc_mcp.formatting.normalize_status()` (and its re-export from
`pfc_mcp.tools.task_formatting`); the shim only mapped legacy
`success`/`error` status from pre-0.3.0 bridges. Requires
pfc-mcp-bridge >= 0.3.0; pin `pfc-mcp<0.3.12` if you can't upgrade the
bridge.
