UAT: MCPToolAdapter.register_tools() infers resource slots but never wires them into domain Tool.resource_slots — registry and DB don't consume them #4628

Open
opened 2026-04-08 17:40:05 +00:00 by HAL9000 · 0 comments
Owner

Bug Report

Tested by: UAT tester worker uat-worker-tool-registry-001
Feature area: Tool registry and MCP integration — resource binding slots
Severity: Medium — MCP tool resource bindings are silently dropped


Summary

MCPToolAdapter.register_tools() correctly infers ResourceSlot objects from MCP tool parameter schemas (via infer_resource_slots()), but then stores them only in ToolSpec.source_metadata["resource_slots"] as plain dicts. The inferred slots are never wired into the domain Tool.resource_slots list, so the ToolRegistry and database (tool_resource_bindings table) never see them. This means MCP tools cannot participate in resource binding resolution, polymorphic tool matching (find_tools_for_resource), or sandbox path mapping.


Location

File: src/cleveragents/mcp/adapter.py
Lines: 299–318 (the TODO comment and slot_dicts construction)

# TODO(#882): resource_slots are stored in source_metadata but
# nothing downstream reads them yet.  The ToolRegistry persists
# resource bindings via domain Tool.resource_slots, and the DB
# migration (c1_001) stores them in tool_resource_bindings.
# A follow-up ticket should wire inferred slots into the domain
# Tool objects so the registry and DB actually consume them.
cap_meta = self.capability_metadata
spec = ToolSpec(
    name=tool_name,
    description=desc.description or f"MCP tool: {desc.name}",
    input_schema=desc.input_schema,
    capabilities=capabilities,
    handler=_make_handler(desc.name, adapter_ref),
    source="mcp",
    source_metadata={
        "server": self._config.name,
        "resource_slots": slot_dicts,   # <-- stored here only
        ...
    },
)
registry.register(spec)

Expected Behavior (from spec, line 127)

The association between a tool and the resources it operates on, declared via typed resource slots. Slots resolve through contextual binding (from the plan's project), static binding (hardcoded at registration), or parameter binding (passed at invocation).

The ToolRegistry.find_tools_for_resource() method reads spec.source_metadata["resource_bindings"] (note: different key from "resource_slots") to perform polymorphic matching. Even if the key name were consistent, the ToolSpec model has no resource_slots field — that field lives on the domain Tool model. The inferred slots must be:

  1. Stored in source_metadata["resource_bindings"] (matching the key that find_tools_for_resource reads)
  2. Persisted to the tool_resource_bindings DB table when the tool is registered

Actual Behavior

  • infer_resource_slots() correctly produces ResourceSlot objects
  • They are serialized to slot_dicts and stored in source_metadata["resource_slots"]
  • ToolRegistry.find_tools_for_resource() reads source_metadata["resource_bindings"] (different key) — so MCP tools are never returned by polymorphic resource matching
  • The DB tool_resource_bindings table is never populated for MCP tools

Secondary Issue: Key Name Mismatch

MCPToolAdapter.register_tools() stores slots under "resource_slots" but ToolRegistry.find_tools_for_resource() reads from "resource_bindings":

# In registry.py line 121:
bindings = spec.source_metadata.get("resource_bindings", [])
# In adapter.py line 307:
source_metadata={
    ...
    "resource_slots": slot_dicts,  # wrong key
    ...
}

Impact

  • MCP tools with file/directory/repository parameters are never matched by find_tools_for_resource()
  • Resource binding resolution for MCP tools is completely broken
  • The tool_resource_bindings DB table has no entries for MCP tools

Steps to Reproduce

  1. Create an MCP server that exposes a tool with a file_path parameter
  2. Connect via MCPToolAdapter and call register_tools(registry, "mcp-server")
  3. Call registry.find_tools_for_resource("file", type_registry)
  4. Observe: the MCP tool is not returned (should be returned since file_path maps to a file resource slot)

Fix

In MCPToolAdapter.register_tools():

  1. Change the source_metadata key from "resource_slots" to "resource_bindings" to match what ToolRegistry.find_tools_for_resource() reads
  2. Ensure each binding dict has a "resource_type" key (currently uses "resource_type" — correct)
source_metadata={
    "server": self._config.name,
    "resource_bindings": slot_dicts,  # fix key name
    ...
},

Commit Message: fix(mcp): use correct resource_bindings key in MCPToolAdapter.register_tools() source_metadata


Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: uat-tester

## Bug Report **Tested by:** UAT tester worker `uat-worker-tool-registry-001` **Feature area:** Tool registry and MCP integration — resource binding slots **Severity:** Medium — MCP tool resource bindings are silently dropped --- ## Summary `MCPToolAdapter.register_tools()` correctly infers `ResourceSlot` objects from MCP tool parameter schemas (via `infer_resource_slots()`), but then stores them only in `ToolSpec.source_metadata["resource_slots"]` as plain dicts. The inferred slots are **never** wired into the domain `Tool.resource_slots` list, so the `ToolRegistry` and database (`tool_resource_bindings` table) never see them. This means MCP tools cannot participate in resource binding resolution, polymorphic tool matching (`find_tools_for_resource`), or sandbox path mapping. --- ## Location **File:** `src/cleveragents/mcp/adapter.py` **Lines:** 299–318 (the TODO comment and slot_dicts construction) ```python # TODO(#882): resource_slots are stored in source_metadata but # nothing downstream reads them yet. The ToolRegistry persists # resource bindings via domain Tool.resource_slots, and the DB # migration (c1_001) stores them in tool_resource_bindings. # A follow-up ticket should wire inferred slots into the domain # Tool objects so the registry and DB actually consume them. cap_meta = self.capability_metadata spec = ToolSpec( name=tool_name, description=desc.description or f"MCP tool: {desc.name}", input_schema=desc.input_schema, capabilities=capabilities, handler=_make_handler(desc.name, adapter_ref), source="mcp", source_metadata={ "server": self._config.name, "resource_slots": slot_dicts, # <-- stored here only ... }, ) registry.register(spec) ``` --- ## Expected Behavior (from spec, line 127) > The association between a tool and the resources it operates on, declared via typed resource slots. Slots resolve through **contextual binding** (from the plan's project), **static binding** (hardcoded at registration), or **parameter binding** (passed at invocation). The `ToolRegistry.find_tools_for_resource()` method reads `spec.source_metadata["resource_bindings"]` (note: different key from `"resource_slots"`) to perform polymorphic matching. Even if the key name were consistent, the `ToolSpec` model has no `resource_slots` field — that field lives on the domain `Tool` model. The inferred slots must be: 1. Stored in `source_metadata["resource_bindings"]` (matching the key that `find_tools_for_resource` reads) 2. Persisted to the `tool_resource_bindings` DB table when the tool is registered --- ## Actual Behavior - `infer_resource_slots()` correctly produces `ResourceSlot` objects - They are serialized to `slot_dicts` and stored in `source_metadata["resource_slots"]` - `ToolRegistry.find_tools_for_resource()` reads `source_metadata["resource_bindings"]` (different key) — so MCP tools are **never** returned by polymorphic resource matching - The DB `tool_resource_bindings` table is never populated for MCP tools --- ## Secondary Issue: Key Name Mismatch `MCPToolAdapter.register_tools()` stores slots under `"resource_slots"` but `ToolRegistry.find_tools_for_resource()` reads from `"resource_bindings"`: ```python # In registry.py line 121: bindings = spec.source_metadata.get("resource_bindings", []) ``` ```python # In adapter.py line 307: source_metadata={ ... "resource_slots": slot_dicts, # wrong key ... } ``` --- ## Impact - MCP tools with file/directory/repository parameters are never matched by `find_tools_for_resource()` - Resource binding resolution for MCP tools is completely broken - The `tool_resource_bindings` DB table has no entries for MCP tools --- ## Steps to Reproduce 1. Create an MCP server that exposes a tool with a `file_path` parameter 2. Connect via `MCPToolAdapter` and call `register_tools(registry, "mcp-server")` 3. Call `registry.find_tools_for_resource("file", type_registry)` 4. Observe: the MCP tool is not returned (should be returned since `file_path` maps to a `file` resource slot) --- ## Fix In `MCPToolAdapter.register_tools()`: 1. Change the `source_metadata` key from `"resource_slots"` to `"resource_bindings"` to match what `ToolRegistry.find_tools_for_resource()` reads 2. Ensure each binding dict has a `"resource_type"` key (currently uses `"resource_type"` — correct) ```python source_metadata={ "server": self._config.name, "resource_bindings": slot_dicts, # fix key name ... }, ``` --- **Commit Message:** `fix(mcp): use correct resource_bindings key in MCPToolAdapter.register_tools() source_metadata` --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.5.0 milestone 2026-04-08 17:41:17 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#4628
No description provided.