Source code for duck.contrib.mcp.capabilities.accessor

"""
Explicit, traceable accessor for MCP capabilities.

Provides attribute-style access to registered capabilities with clear
error messages and discovery methods.
"""
from typing import Dict

from duck.contrib.mcp.capabilities import Capability
from duck.contrib.mcp.exceptions import (
    MCPError,
    MCPCapabilityExistsError,
    MCPCapabilityNotRegistered,
)


[docs] class CapabilityAccessor: """ Explicit, traceable accessor for MCP capabilities. Provides attribute-style access to registered capabilities with clear error messages and discovery methods. Example: ```python capabilities = CapabilityAccessor(view) capabilities.tools # Returns ToolsCapability capabilities.server # Returns aliased capability capabilities.get('tools') # Safe get with default capabilities.has('server') # Check existence capabilities.list() # List all capabilities ``` """ def __init__(self, view: "MCPView"): self._view = view self._registry = {} # name -> capability self._aliases = {} # alias -> capability @property def registry(self) -> Dict[str, "Capability"]: """ Public property for the capabilities registry. """ return self._registry
[docs] def register(self, name: str, capability: Capability, alias: str = None) -> "Capability": """ Internal: Register a capability. """ if name in self.registry.keys(): raise MCPCapabilityExistsError( f"A capability with the namespace '{name}' is already registered. " "Call `unregister()` before registering it again." ) if alias and alias in self._aliases.keys(): raise MCPCapabilityExistsError( f"A capability with the alias '{alias}' is already registered. " "Call `unregister()` before registering it again." ) # Register capability self._registry[name] = capability if alias: self._aliases[alias] = capability # Set capability namespace. capability.namespace = name # Return registered capability return capability
[docs] def unregister(self, name: str) -> "Capability": """ Internal: Unregister a capability. """ target = self.registry.pop(name, None) if target is None: # Capability does not exist. raise MCPCapabilityNotRegistered("Capability with name `{name}` is not registered.") # Remove any aliases pointing to this capability for alias, cap in list(self._aliases.items()): if cap is target: del self._aliases[alias] # Return the removed capability return target
[docs] def set_alias(self, alias: str, capability_name: str): """ Create an alias for a capability. Args: alias: Short name to use (e.g., 'server', 'tools') capability_name: The registered capability name (e.g., '_server_requests') Example: ```python capabilities.set_alias('server', '_server_requests') # Now capabilities.server works ``` """ cap = self.registry.get(capability_name) if cap is None: raise MCPCapabilityNotRegistered(f"Capability '{capability_name}' not found. Available: {', '.join(self.registry.keys())}") if alias in self._aliases.keys(): raise MCPCapabilityExistsError( f"A capability with the alias '{alias}' is already registered. " "Call `unregister()` before registering it again." ) # Create an alias self._aliases[alias] = cap return self
[docs] def get(self, name: str, default=None): """ Get a capability by name or alias. Args: name: Capability name or alias default: Value to return if capability doesn't exist Example: ```python server_cap = self.capabilities.get('_server_requests') if server_cap: await server_cap.send_request(...) ``` """ # Check aliases first if name in self._aliases: return self._aliases[name] return self.registry.get(name, default)
[docs] def has(self, name: str) -> bool: """ Check if a capability exists (by name or alias). """ return name in self.registry or name in self._aliases
[docs] def list(self) -> Dict[str, str]: """ List all registered capabilities and their aliases. Returns: Dict mapping capability names to their aliases (or None) """ result = {} for name in self.registry: # Find if this capability has an alias alias = None for a, cap in self._aliases.items(): if cap is self.registry[name]: alias = a break result[name] = alias return result
[docs] def __getattr__(self, name: str): """ Attribute-style access to capabilities. Example: self.capabilities.tools self.capabilities.server """ getattribute = object.__getattribute__ aliases = getattribute(self, "_aliases") registry = getattribute(self, "registry") # Check aliases first if name in aliases: return aliases[name] # Then check capability names if name in registry: return registry[name] available = sorted(set(registry) | set(aliases)) raise AttributeError( f"No capability named '{name}'. " f"Available: {', '.join(available) if available else 'none'}" )
[docs] def __dir__(self): """ Help with autocomplete in IDEs. """ return ( list(self.registry.keys()) + list(self._aliases.keys()) + ['get', 'has', 'list', 'alias'] )