Tools

A tool is a callable the model can invoke during a turn to do real work. Morpheus uses the native strands-agents tool format: decorated functions passed straight into an agent’s tools list.

Tool implementations live in morpheus-llm so the Lambdas and the runtime share them. agentcore/tools/ is a re-export layer:

# agentcore/tools/__init__.py
from morpheus_llm.tools import (
    add_numbers, create_agentcore_browser, create_hubspot_mcp, create_tavily_mcp,
)

Defining a tool

The @tool decorator from strands derives everything from the function itself: the docstring becomes the description (including the Args: block, which the model sees), and type annotations become the input schema. No separate schema object.

from strands import tool

@tool
def multiply(a: float, b: float) -> float:
    """Multiply two numbers together.

    Args:
        a: First operand
        b: Second operand

    Returns:
        The product of a and b
    """
    return a * b

The decorated function is a Strands-native tool object and can be passed directly to ClaudeAgentService.

Local tools

Tool Signature Purpose

add_numbers

(a: float, b: float) → float

Demonstration tool, also used as a smoke test in agentcore/test/test_main.py

Vended Strands tools

From strands-agents-tools, wired into dialler_agent:

Tool Import Capability

editor

from strands_tools import editor

Read and write files on the container filesystem

shell

from strands_tools import shell

Execute shell commands

http_request

from strands_tools import http_request

Arbitrary HTTP requests

python_repl

from strands_tools import python_repl

Execute Python with state persistence across calls

shell and python_repl give the model arbitrary command and code execution inside the container. Expose them only to workflows whose inputs you control. A workflow that feeds untrusted text — email bodies, scraped web content — into an agent holding these tools is a remote-code-execution path. See prompt injection.

MCP tools

Model Context Protocol servers appear to the agent as tools. Both integrations are factory functions, not module-level objects, because MCPClient transports hold an open HTTP session and cannot be reused across agent invocations.

create_hubspot_mcp()

create_hubspot_mcp(*, client_id: str, client_secret: str, refresh_token: str) -> MCPClient

Connects to the HubSpot Remote MCP server at https://mcp.hubspot.com/ over Streamable HTTP. The OAuth refresh token is exchanged for an access token by refresh_hubspot_access_token() and cached with @lru_cache(maxsize=4) keyed on the credential triple, so the OAuth round-trip happens once per container.

Two transport details:

  • The underlying httpx transport is wrapped in _RetryOn429Transport, which retries HTTP 429 up to 4 times with exponential backoff (1 s base, capped at 30 s per sleep), honouring Retry-After when present.

  • terminate_on_close=False, because HubSpot’s MCP server does not implement the DELETE session-termination endpoint and returns 405 if it is called.

Capabilities the server advertises: contact search and retrieval, call and activity history, task management, company and deal lookups.

create_tavily_mcp()

create_tavily_mcp(*, api_key: str) -> MCPClient

Connects to https://mcp.tavily.com/mcp/ with the API key in the query string. Two tools reach the agent:

  • tavily-search — web search with optional domain filtering, topic selection (general or news), and result-depth control.

  • tavily-extract — clean article content from one or more URLs.

Used by the enrichment agent in enrichment-scorer, not by the runtime’s dialler_agent. See Seller enrichment.

create_agentcore_browser()

create_agentcore_browser(region=None, session_timeout=3600, identifier=None) -> Any

A fully managed cloud Chrome session through Bedrock AgentCore Browser — navigate, fill forms, extract content, click. Requires the optional extras (pip install morpheus-llm[browser]: strands-agents-tools, playwright, nest-asyncio); a missing dependency raises ImportError with the install command. Not currently wired into any agent.

Credentials

Every factory takes credentials as explicit keyword arguments. Libraries never read os.environ. The caller fetches them:

config = load_config()                     # or load_secrets() in a Lambda

tools = lambda: [
    create_hubspot_mcp(
        client_id=config.hubspot_client_id,
        client_secret=config.hubspot_client_secret,
        refresh_token=config.hubspot_refresh_token,
    ),
    create_tavily_mcp(api_key=config.tavily_api_key),
]

This makes credential provenance visible at the call site and keeps the library testable without environment manipulation.

Adding a tool

  1. Create libs/morpheus-llm/morpheus_llm/tools/builtin/<name>.py.

  2. Decorate with @tool from strands; write a precise docstring.

  3. Export it from morpheus_llm/tools/init.py.

  4. For an MCP tool, write a factory taking credentials as keyword-only arguments.

  5. Pass it into an agent’s tools list — a lambda if it is stateful.

  6. Re-export from agentcore/tools/init.py if the runtime needs it.

Guidelines

  • snake_case names. Models handle get_contact_by_phone better than getContactByPhone.

  • Precise docstrings. The Args: descriptions are surfaced to the model. Be specific about inputs, outputs, and side effects.

  • Return strings. Tool results go back to the model as text; return a JSON string for structured data rather than a raw object.

  • Handle errors inside the tool. Catch exceptions and return an error string so the model can reason about the failure and retry, rather than raising and aborting the turn.

  • Never hold state at module level. Anything with a connection — an MCPClient, a browser session — must be created per invocation via a factory.