feat(skill): add MCP refresh hooks #455

Merged
aditya merged 2 commits from feature/m4-skill-registry-refresh into master 2026-02-27 13:17:21 +00:00
Member

Description

Implements MCP refresh hooks for the skill registry. Adds SkillRegistry.refresh(name)
and refresh_all() to recompute flattened tool sets on demand, and wires MCP
notifications/tools/list_changed events to trigger them via a new MCPRefreshHook
class with configurable debouncing. Introduces SkillRefreshResult for structured
refresh outcome reporting.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring (no functional changes)
  • Documentation update
  • Test improvements
  • CI/CD changes

Quality Checklist

  • Code follows the project's coding standards (see CONTRIBUTING.md)
  • All public/protected methods have argument validation
  • Static typing is complete (no Any unless justified)
  • nox -s typecheck passes with no errors
  • nox -s lint passes with no errors
  • Unit tests written/updated (Behave scenarios in features/)
  • Integration tests written/updated (Robot suites in robot/) if applicable
  • Coverage remains above 97% (nox -s coverage_report)
  • No security issues introduced (nox -s security_scan)
  • No dead code introduced (nox -s dead_code)
  • Documentation updated if behavior changed

Testing

Behave unit tests were written before implementation (BDD-first). All tests
passed after implementation with no modifications to the test expectations.

Test Commands Run

nox -s unit_tests       # 19 Behave scenarios, 97 steps — all passed
nox -s typecheck        # 0 errors, 0 warnings
nox -s lint             # All checks passed
nox -s coverage_report  # SkillRefreshResult: 100%, MCPRefreshHook: 89%
nox -s security_scan    # No issues found

ISSUES CLOSED #168

Implementation Notes

New components

SkillRefreshResult (src/cleveragents/skills/refresh.py)
Immutable frozen dataclass summarising a refresh operation with refreshed,
failed, and skipped lists/dicts. Provides to_summary() for one-line
CLI/log output and merge() for aggregating per-skill results in refresh_all().

MCPRefreshHook (src/cleveragents/mcp/refresh_hook.py)
Registers itself as a notification listener on an MCPToolAdapter. On receiving
notifications/tools/list_changed, it schedules a threading.Timer to call
SkillRegistry.refresh_all() after debounce_seconds (default 0.5 s). Each
new notification resets the timer, coalescing bursts into a single refresh.
Thread-safe via threading.Lock. Exposes refresh_count and cancel().

Modified components

SkillRegistry (src/cleveragents/skills/registry.py)
Added refresh(name) and refresh_all(). Resolves tools via SkillResolver,
validates refs against ToolRegistry if configured, returns SkillRefreshResult.

MCPToolAdapter (src/cleveragents/mcp/adapter.py)
Added add_notification_listener(callback) and dispatch_notification(method, params)
for a generic, loosely-coupled notification fan-out mechanism.

Key design decisions

  • Debounce over immediate refresh: prevents refresh storms when MCP servers
    emit multiple list_changed notifications in quick succession.
  • Graceful degradation: if no ToolRegistry is configured, refresh() skips
    validation and counts the skill as refreshed, logging a single WARNING with
    recovery steps rather than failing hard.
  • Immutable result type: SkillRefreshResult is frozen=True so callers
    cannot mutate shared results passed across thread boundaries.
## Description Implements MCP refresh hooks for the skill registry. Adds `SkillRegistry.refresh(name)` and `refresh_all()` to recompute flattened tool sets on demand, and wires MCP `notifications/tools/list_changed` events to trigger them via a new `MCPRefreshHook` class with configurable debouncing. Introduces `SkillRefreshResult` for structured refresh outcome reporting. ## Type of Change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Refactoring (no functional changes) - [x] Documentation update - [x] Test improvements - [ ] CI/CD changes ## Quality Checklist - [x] Code follows the project's coding standards (see CONTRIBUTING.md) - [x] All public/protected methods have argument validation - [x] Static typing is complete (no `Any` unless justified) - [x] `nox -s typecheck` passes with no errors - [x] `nox -s lint` passes with no errors - [x] Unit tests written/updated (Behave scenarios in `features/`) - [x] Integration tests written/updated (Robot suites in `robot/`) if applicable - [x] Coverage remains above 97% (`nox -s coverage_report`) - [x] No security issues introduced (`nox -s security_scan`) - [ ] No dead code introduced (`nox -s dead_code`) - [x] Documentation updated if behavior changed ## Testing Behave unit tests were written **before** implementation (BDD-first). All tests passed after implementation with no modifications to the test expectations. ### Test Commands Run ```bash nox -s unit_tests # 19 Behave scenarios, 97 steps — all passed nox -s typecheck # 0 errors, 0 warnings nox -s lint # All checks passed nox -s coverage_report # SkillRefreshResult: 100%, MCPRefreshHook: 89% nox -s security_scan # No issues found ``` ## Related Issues ISSUES CLOSED #168 ## Implementation Notes ### New components **`SkillRefreshResult`** (`src/cleveragents/skills/refresh.py`) Immutable frozen dataclass summarising a refresh operation with `refreshed`, `failed`, and `skipped` lists/dicts. Provides `to_summary()` for one-line CLI/log output and `merge()` for aggregating per-skill results in `refresh_all()`. **`MCPRefreshHook`** (`src/cleveragents/mcp/refresh_hook.py`) Registers itself as a notification listener on an `MCPToolAdapter`. On receiving `notifications/tools/list_changed`, it schedules a `threading.Timer` to call `SkillRegistry.refresh_all()` after `debounce_seconds` (default 0.5 s). Each new notification resets the timer, coalescing bursts into a single refresh. Thread-safe via `threading.Lock`. Exposes `refresh_count` and `cancel()`. ### Modified components **`SkillRegistry`** (`src/cleveragents/skills/registry.py`) Added `refresh(name)` and `refresh_all()`. Resolves tools via `SkillResolver`, validates refs against `ToolRegistry` if configured, returns `SkillRefreshResult`. **`MCPToolAdapter`** (`src/cleveragents/mcp/adapter.py`) Added `add_notification_listener(callback)` and `dispatch_notification(method, params)` for a generic, loosely-coupled notification fan-out mechanism. ### Key design decisions - **Debounce over immediate refresh**: prevents refresh storms when MCP servers emit multiple `list_changed` notifications in quick succession. - **Graceful degradation**: if no `ToolRegistry` is configured, `refresh()` skips validation and counts the skill as refreshed, logging a single `WARNING` with recovery steps rather than failing hard. - **Immutable result type**: `SkillRefreshResult` is `frozen=True` so callers cannot mutate shared results passed across thread boundaries.
Implement MCPToolAdapter to connect to external MCP servers, enumerate
tools, and register them in ToolRegistry with source="mcp". Includes
connect/reconnect/disconnect lifecycle with timeout enforcement, input
validation on invoke, capability inference, Behave/Robot/ASV tests, and
docs/reference/mcp_adapter.md.

ISSUES CLOSED: #159
refactor(mcp): fix mock placement, type annotation, and constructor validation
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 16s
CI / typecheck (pull_request) Successful in 30s
CI / quality (pull_request) Successful in 32s
CI / security (pull_request) Successful in 34s
CI / integration_tests (pull_request) Successful in 3m24s
CI / unit_tests (pull_request) Successful in 10m40s
CI / docker (pull_request) Successful in 1m4s
CI / benchmark-regression (pull_request) Successful in 19m50s
CI / coverage (pull_request) Successful in 28m33s
2f9707cfdf
- Extract MockMCPTransport to features/mocks/mock_mcp_transport.py
- Use ToolRegistry type with TYPE_CHECKING in register_tools()
- Move transport config validation into __init__ via _validate_config()
Merge branch 'master' into feature/m3-mcp-adapter
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 21s
CI / lint (pull_request) Successful in 21s
CI / build (pull_request) Successful in 21s
CI / security (pull_request) Successful in 50s
CI / typecheck (pull_request) Successful in 59s
CI / integration_tests (pull_request) Successful in 4m18s
CI / unit_tests (pull_request) Successful in 10m11s
CI / docker (pull_request) Successful in 59s
CI / benchmark-regression (pull_request) Successful in 20m36s
CI / coverage (pull_request) Successful in 36m7s
519e26fb02
feat(skill): add MCP refresh hooks
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 20s
CI / lint (pull_request) Successful in 24s
CI / build (pull_request) Successful in 27s
CI / typecheck (pull_request) Successful in 34s
CI / security (pull_request) Successful in 1m11s
CI / integration_tests (pull_request) Successful in 4m40s
CI / unit_tests (pull_request) Successful in 9m55s
CI / docker (pull_request) Successful in 1m0s
CI / benchmark-regression (pull_request) Successful in 25m30s
CI / coverage (pull_request) Successful in 1h11m59s
b25a886df8
Implement SkillRegistry.refresh(name) and refresh_all() to recompute
flattened tool sets on demand. Wire MCPToolAdapter.dispatch_notification()
to MCPRefreshHook, which debounces rapid notifications/tools/list_changed
bursts into a single refresh_all() call (default 0.5 s window).

Add safeguard that skips tool-ref validation when no ToolRegistry is
configured, emitting a single WARNING with recovery steps. Introduce
immutable SkillRefreshResult (refreshed/failed/skipped counts) with
to_summary() and merge() for CLI and log output.

New files:
- src/cleveragents/skills/refresh.py        (SkillRefreshResult)
- src/cleveragents/mcp/refresh_hook.py      (MCPRefreshHook)
- features/skill_refresh.feature            (19 Behave scenarios)
- features/steps/skill_refresh_steps.py
- robot/skill_refresh.robot                 (10 Robot tests)
- robot/helper_skill_refresh.py
- benchmarks/skill_refresh_bench.py         (ASV benchmarks)
- docs/reference/skill_refresh.md

Modified:
- src/cleveragents/skills/registry.py       (refresh, refresh_all)
- src/cleveragents/mcp/adapter.py           (notification listener API)
- src/cleveragents/skills/__init__.py       (export SkillRefreshResult)
- src/cleveragents/mcp/__init__.py          (export MCPRefreshHook)
- CHANGELOG.md

ISSUES CLOSED #168
aditya added this to the v3.1.0 milestone 2026-02-26 12:56:29 +00:00
aditya changed title from feature/m4-skill-registry-refresh to feat(skill): add MCP refresh hooks 2026-02-26 14:55:15 +00:00
CoreRasurae approved these changes 2026-02-26 16:09:23 +00:00
Dismissed
CoreRasurae left a comment

Tests passing, changelog updated, documentation updated. Looks good. Approving

Tests passing, changelog updated, documentation updated. Looks good. Approving
Merge branch 'master' into feature/m4-skill-registry-refresh
All checks were successful
CI / lint (pull_request) Successful in 21s
CI / typecheck (pull_request) Successful in 32s
CI / quality (pull_request) Successful in 14s
CI / security (pull_request) Successful in 50s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 24s
CI / integration_tests (pull_request) Successful in 2m44s
CI / unit_tests (pull_request) Successful in 21m48s
CI / docker (pull_request) Successful in 1m2s
CI / benchmark-regression (pull_request) Successful in 26m41s
CI / coverage (pull_request) Successful in 43m59s
9af9ff8d95
aditya dismissed CoreRasurae's review 2026-02-27 12:29:56 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

aditya merged commit fa1794dba9 into master 2026-02-27 13:17:21 +00:00
aditya deleted branch feature/m4-skill-registry-refresh 2026-02-27 13:17:21 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core!455
No description provided.