feat(tool): implement BuiltinAdapter class and MCP automatic resource slot creation #964
Merged
brent.edwards
merged 7 commits from 2026-03-21 05:30:01 +00:00
feature/m5-builtin-adapter-mcp-slots into master
Dismiss Review
Are you sure you want to dismiss this review?
Labels
Clear labels
auto/needs-reevaluation
controller-managed
overdue
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Needs Feedback
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
Controller deferred this PR; awaiting Phase 6+ scope-evaluator or operator re-enablement.
Auto-agents controller manages this PR/issue (see tools/controller/deploy/RUNBOOK.md). Remove this label to abandon controller management.
PR blocked by an open issue dependency. Operator must close the dep (or remove the dependency link) before the merge driver can act. Auto-cleared by merge_drive when no open deps remain.
Most recent merge cycle hit CI timeout. Driver excludes this PR while last merge_cycle row is < 30 min old; label persists thereafter as visible history.
Currently being processed by an implementer worker.
Currently being processed by the merge driver.
Currently being processed by a reviewer worker.
Merge driver heartbeat stale; pipeline halted. Closed automatically on next clean tick.
Detected master commit violating the strict merge invariant. Tracked as an issue (not a PR label); kept here for label completeness.
In-cycle escalation: most recent attempt ran at the Tier 0 slot (`tier-0`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 1 slot (`tier-1`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 2 slot (`tier-2`). Slot's model defined in .opencode/models/tiers.yaml. Gated behind IMPLEMENTER_ESCALATION_TIER2_ENABLED.
In-cycle escalation: most recent attempt ran at the Tier -1 slot (`tier-min`). Slot's model defined in .opencode/models/tiers.yaml. Suffix is ``-min`` (not ``--1``) so the Forgejo UI reads naturally.
Tracking issues used by the AI Automation system for agents to communicate and report.
Rebase conflict needs LLM conflict-resolver.
Failing CI needs implementer attention.
Documenting a driver incident or rollback.
Reviewer has APPROVED this PR and no later REQUEST_CHANGES is outstanding. The merge driver requires this label to even consider a PR for merging. Set by the reviewer worker on APPROVE; cleared on REQUEST_CHANGES.
Train repeatedly lost master-tempo races. Driver excludes via merge_cycle until cooldown elapses; label persists as visible history.
Revert PR backing out an invariant violation. Fast-tracked through the merge driver.
Sentinel PR duplicated from upstream into a personal fork by tools/duplicate_prs_to_fork.py for pipeline testing. Lives only in the fork; the canonical pipeline never sees it.
No implementer activity for N days. Flagged for human review. Auto-cleared on next push to head branch.
Repeatedly fails on current master (>= 3 ci-fail-on-rebased-sha releases in 12 h). Excluded from driver until human triage.
A ticket in a blocked state and unable to complete until some other task is completed first.
Bounty
$100
A bounty of $100 for any open-source contributor who provides a MR that solves this issue
Bounty
$1000
A bounty of $1000 for any open-source contributor who provides a MR that solves this issue
Bounty
$10000
A bounty of $10000 for any open-source contributor who provides a MR that solves this issue
Bounty
$20
A bounty of $20 for any open-source contributor who provides a MR that solves this issue
Bounty
$2000
A bounty of $2000 for any open-source contributor who provides a MR that solves this issue
Bounty
$250
A bounty of $250 for any open-source contributor who provides a MR that solves this issue
Bounty
$50
A bounty of $50 for any open-source contributor who provides a MR that solves this issue
Bounty
$500
A bounty of $500 for any open-source contributor who provides a MR that solves this issue
Bounty
$5000
A bounty of $5000 for any open-source contributor who provides a MR that solves this issue
Bounty
$750
A bounty of $750 for any open-source contributor who provides a MR that solves this issue
MoSCoW
Could have
Could have feature in order to satisfy the epic/legendary.
MoSCoW
Must have
Must have feature in order to satisfy the epic/legendary.
MoSCoW
Should have
Should have feature in order to satisfy the epic/legendary.
There are questions in the ticket that can not be completed until the project owner provides clarity.
Points
1
1 man-hours worth of work for an expert with no learning curve.
Points
13
13 man-hours worth of work for an expert with no learning curve.
Points
2
2 man-hours worth of work for an expert with no learning curve.
Points
21
21 man-hours worth of work for an expert with no learning curve.
Points
3
3 man-hours worth of work for an expert with no learning curve.
Points
34
34 man-hours worth of work for an expert with no learning curve.
Points
5
5 man-hours worth of work for an expert with no learning curve.
Points
55
55 man-hours worth of work for an expert with no learning curve.
Points
8
8 man-hours worth of work for an expert with no learning curve.
Points
88
88 man-hours worth of work for an expert with no learning curve.
Priority
Backlog
This ticket has backlogged priority and is not to be worked on yet
Priority
CI Blocker
Critical priority issue that blocks CI/CD pipeline and prevents PR merges
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
A ticket for learning a tool or technology that is needed to be able to do future planning and design.
State
Completed
The ticket has been fully implemented, completed, and merged with the source code. This label should only be applied once a ticket is closed.
State
Duplicate
A ticket that represents the same content as an existing ticket.
State
In Progress
A ticket that is actively being developed.
State
In Review
A ticket that has had some code completed to implement but is waiting to pass peer review and is not yet merged in.
State
Paused
This ticket's work started but wasn't finished. It's on hold (likely in a feature branch) and will be resumed later, either due to a blocker or a delay.
State
Unverified
All new tickets start in this state. A developer may set it to show the ticket is unverified. This means we haven't agreed to work on it. It will either move to a verified state or be closed as wontdo.
State
Verified
The issue has been verified by a developer as legitimate. It will be worked on and verified tickets are now considered part of the backlog.
State
Wont Do
This ticket has been decided it wont be done. This may mean the bug has been determined to not be real (cant verify) or the feature is one we have decided we dont want to adopt.
Type
Automation
Any edits or discussion about the AI automated coding system.
Type
Bug
Something that doesnt work as intended.
Type
Discussion
Anytime a ticket represents a discussion about a subject and doesnt fall into one of the other categories.
Type
Documentation
An error or improvement needed in the documentation.
Type
Epic
Any first tier epic. That is, an epic which contains only issues as children and will not have sub-epics.
Type
Feature
Some new functionality not present.
Type
Legendary
A type of Epic which will contain other Epics.
Type
Refactor
A code change that restructures existing code without changing its external behavior.
Type
Support
Someone needs help using the project.
Type
Task
A generic task that doesnt fit into the other type categories.
Type
Testing
Work exclusively focusing on fixing or expanding testing.
Projects
Clear projects
No project
Assignees
aditya (Aditya Chhabra)
aleenaumair (Aleena Umair)
brent.edwards (Brent Edwards)
CoreRasurae (Luis Mendes)
drew (Drew Morris)
eugen.thaci (Eugen Thaci)
freemo (Jeffrey Phillips Freeman)
HAL9000 (HAL 9000)
HAL9001 (HAL9001)
hamza.khyari (Hamza Khyari)
hurui200320 (Rui Hu)
justin.morris
khird (Kyle Hird)
org.cleveragents
Clear assignees
No Assignees
brent.edwards
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cleveragents/cleveragents-core#964
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "feature/m5-builtin-adapter-mcp-slots"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement
BuiltinAdapterclass and MCP automatic resource slot creation. Two main changes:1. BuiltinAdapter Class (
tool/builtins/adapter.py)Formal adapter implementing the tool adapter lifecycle pattern for built-in tools:
discover()— returns all built-in tool descriptors (file, git, subplan tools)register(registry)— registers all tools in a ToolRegistry, returns registered namesactivate()/deactivate()— no-ops (built-in tools are always available)ALL_FILE_TOOLS,ALL_GIT_TOOLS,ALL_SUBPLAN_TOOLSlists2. MCP Resource Slot Inference (
mcp/adapter.py)New
infer_resource_slots()static method onMCPToolAdapter:ResourceSlotobjects with appropriate type, access mode, binding modefile_path→file(rw),directory→directory(ro),repo_path→git-checkout(rw)source_metadata["resource_slots"]on registeredToolSpecobjectsQuality Gates
nox -s lintnox -s typechecknox -s unit_testsnox -s integration_testsnox -s coverage_reportCloses #882
77c7b07b5atob9c4b4646fPM Triage — Day 36 (2026-03-16)
feat(tool): implement BuiltinAdapter class and MCP automatic resource slot creation — M6 (v3.5.0), Points/5, Should have.
Status: New PR submitted Day 36, 0 reviews. This implements #882 (BuiltinAdapter).
Reviewer assignment: @aditya — you have the MCP/tool adapter domain expertise. Please review for spec compliance. Target: Day 38 EOD.
Note: @brent.edwards — please confirm this is ready for review and list any upstream dependencies.
PM Day 36 Triage: BuiltinAdapter and MCP resource slot creation. Closes #882. Reviewer needed: @aditya (MCP domain expertise). M6 scope. Verify BuiltinAdapter aligns with ADR-011 tool lifecycle and ADR-029 MCP adoption.
Status Update — Day 37
@freemo — Confirmed: PR #964 is ready for review. Master has been merged in today (conflicts in robot timeout files resolved).
Upstream dependencies: None.
BuiltinAdapterwraps the existingregister_file_tools()/register_git_tools()functions already on master.infer_resource_slots()is a new static method onMcpAdapterthat works independently.Ready for @aditya's review.
PM Status — Day 37
Reviewers assigned. This PR needs at least 2 approving reviews per
CONTRIBUTING.mdbefore merge.Author: Please ensure this PR is rebased on latest
masterand all quality gates pass before requesting merge.PM status — Day 37
Code Review — PR #964
Verdict: Request Changes (1 Major, 4 Minor, 3 Nit)
Scope: 7 files, +669/−2 (three-dot diff excluding master merge). Lint PASS, Typecheck PASS.
Major
BUG-1:
_FILE_PARAMSincludes overly generic"path"— false-positive slot creationsrc/cleveragents/mcp/adapter.py:541_FILE_PARAMScontains{"file_path", "file", "path"}. The"path"pattern is too broad — many MCP tools usepathfor URL routes, API endpoints, config key paths, etc. This will silently create spurious file resource slots for those tools.Recommendation: Remove
"path"or narrow to"file_path"/"filepath"only. If broad matching is needed, add a secondary heuristic (e.g., check the schema property description for "file").Minor
CODE-1: No argument validation in
BuiltinAdapter.register()src/cleveragents/tool/builtins/adapter.py:76Per CONTRIBUTING.md, all public methods must validate arguments as the first guard.
register(registry)doesn't check forNone, which would produce anAttributeErrordeep in the loop instead of at the boundary.TEST-1: Missing boundary/negative tests for
infer_resource_slotsfeatures/builtin_adapter.featureNo scenarios for:
propertieskey"path"parameter specificallySPEC-1: Issue #882 subtask "Refactor registration functions to use adapter" not done
The adapter wraps
ALL_*_TOOLSlists but the existingregister_file_tools()/register_git_tools()/register_subplan_tool()functions remain independent. The adapter is additive, not a refactor. Either update the functions to delegate, or update the issue subtask to reflect the actual (backward-compatible) approach.PROCESS-1: Missing CHANGELOG entry
No entry for #882 in the Unreleased section. Required per PR Process item 6.
Nit
State/In Reviewlabel (issue has it).if/elifblocks ininfer_resource_slots(lines 566–630) are near-identical and could be driven by a mapping dict (~65 lines → ~20).Code Review — Round 2 (new findings only)
Critical
BUG-2:
infer_resource_slotsoutput is dead code —source_metadata["resource_slots"]has zero consumerssrc/cleveragents/mcp/adapter.py:519-540The slot inference pipeline writes to a key that nothing reads:
infer_resource_slots()createsResourceSlotobjectssource_metadata["resource_slots"](line 540)source_metadata["resource_slots"]— confirmed via codebase-wide grepfind_by_resource_type()reads a different key:source_metadata.get("resource_bindings", [])(tool/registry.py:127)source_metadatais not a persisted column; the DB readsresource_slotsfrom domainToolobjects, not fromToolSpec.source_metadatatool showdisplaysresource_slotsfromTool.as_cli_dict()(domain model), not fromToolSpec.source_metadata~100 lines of
infer_resource_slots,_FILE_PARAMS,_DIR_PARAMS,_REPO_PARAMS, and the slot-to-dict serialization produce write-only data.Recommendation: Either wire into the consumption path (bridge
resource_slots→resource_bindings, or populate domainTool.resource_slotsfromToolSpec.source_metadataduring persistence), or remove until a consumer exists.Major
BUG-3:
BuiltinAdapter.register()raisesToolErroron second call with same registrysrc/cleveragents/tool/builtins/adapter.py:91-95ToolRegistry.register()raisesToolErroron name collision (tool/registry.py:49-53). MCPToolAdapter guards against this by pre-removing existing tools (mcp/adapter.py:496-498). BuiltinAdapter does no such check — callingregister()twice on the same registry raisesToolErroron every tool. No test covers this path.BUG-4: Lifecycle method names diverge from MCPToolAdapter — docstring is misleading
src/cleveragents/tool/builtins/adapter.py:9-10The docstring says the adapter "follows the same structural pattern as MCPToolAdapter" but the method names are different:
connect()activate()activate()disconnect()deactivate()deactivate()discover_tools(tool_filter)discover()discover()register_tools(registry, namespace, tool_filter)register(registry)No shared ABC or Protocol enforces a common interface. The adapter actually follows AgentSkillLoader's naming, not MCPToolAdapter's. The
register()signature also omitsnamespacewhich MCPToolAdapter requires.Updated Combined Summary (Round 1 + Round 2)
check comments
Response to @hamza.khyari's Review (Rounds 1+2) — PR #964
Thank you for the deep analysis, Hamza. The dead-code finding (BUG-2) is particularly impactful — great catch tracing the full consumption path.
Critical
BUG-2 (Dead code —
resource_slotshas zero consumers): Confirmed. Theinfer_resource_slotspipeline writes tosource_metadata["resource_slots"]but nothing reads it — the registry readsresource_bindings, the DB reads from domainToolobjects, and the CLI reads fromTool.as_cli_dict(). I'll wire the slots into the domainTool.resource_slotsfield during MCP adapter registration, bridgingToolSpec.source_metadata → Tool.resource_slots. This makes the inference actually functional.Major
BUG-1 (
"path"too broad): Agreed —"path"matches too many unrelated parameters. Will remove it from_FILE_PARAMSand keep only"file_path","filepath","file".BUG-3 (BuiltinAdapter.register raises on double-call): Will add a pre-check: skip already-registered tools (matching the MCPToolAdapter pattern of pre-removing existing tools).
BUG-4 (Lifecycle method naming divergence): Fair point. Will update the docstring to accurately reference the AgentSkillLoader naming convention and note the deliberate omission of
namespace(builtin tools are always in the default namespace).Minor
CODE-1 (No argument validation): Will add
if registry is None: raise ValueError(...).TEST-1 (Missing boundary tests): Will add scenarios for empty schema, no properties, multiple matches, and the
"path"parameter specifically.SPEC-1 (Issue subtask): Will update the issue subtask to reflect the actual backward-compatible additive approach.
PROCESS-1 (Missing CHANGELOG): Will add entry.
Nit
PROCESS-2 (Missing label): Will add
State/In Review.CODE-2 (Mapping dict): Good suggestion — will refactor the 3
if/elifblocks into a mapping-driven loop.CODE-3 (Commit body): Will clarify that the test modifications came from the merge commit.
Working on all fixes now.
Review Fixes Applied — commit
98ba57f2All actionable findings from @hamza.khyari's review (Rounds 1+2) have been addressed:
"path"from_FILE_PARAMS; replaced with"filepath"to avoid false-positive slot creationBuiltinAdapter.register()is now idempotent — skips already-registered tools instead of raisingToolErrorAgentSkillLoadernaming, notMCPToolAdapterif registry is None: raise ValueError(...)guard at top ofregister()if/elifblocks (~65 lines) ininfer_resource_slotsinto a mapping-driven loop (~20 lines)__all__to bothmcp/adapter.pyandtool/builtins/adapter.pyQuality Gates
nox -s lint— PASS (all checks passed)nox -s typecheck— PASS (0 errors, 1 pre-existing warning in unrelated file)2f4b545618to17325a6c1e17325a6c1etofe59ca823dReview Fixes Applied — Commit
fe59ca82Branch rebased and squashed onto
origin/master(ad98d41d). 6 of 8 findings were already resolved from the prior fix round (preserved through squash-rebase). 2 new fixes applied._FILE_PARAMStoo broad)file_path,filepath,fileresource_slots)register()idempotent)ValueErroronNoneregistry_SLOT_MAPiterationQuality Gates
nox -s lint— PASSnox -s typecheck— PASS (0 errors)Code Review — PR #964
BuiltinAdapter and MCP auto resource slot creation. Proper labels, milestone, and issue linkage (#882). Approved.
fe59ca823dto94076ec844New commits pushed, approval review dismissed automatically according to repository settings
Rebased onto
origin/master(79b0a2c5). CHANGELOG conflict resolved (kept master, re-added PR entry).nox -s lintPASS,nox -s typecheckPASS (0 errors). Commit94076ec8.94076ec844toe02e396738Fixed the
benchmark_regressionfailure and merged latestmaster.Root cause:
RollbackSuite.time_rollbackinbenchmarks/checkpoint_rollback_bench.pyregistered a sandbox with the fake path"sandbox-path". Aftercheckpoint_service.pywas updated (commitec4c39ae) to validate that the sandbox path is a real directory containing a.gitrepo, the benchmark started failing withBusinessRuleViolation: sandbox path does not exist.Fix: Updated
RollbackSuite.setup()to create a real temporary directory withgit init, and added ateardown()to clean it up. The benchmark now exercises the actual rollback code path.Also merged latest
origin/masterinto the branch (resolved a CHANGELOG conflict).