Skip to content

Fix: Functions Passed to Agents Are Now Available as Tools #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 8, 2025

Conversation

rholinshead
Copy link
Member

Summary

Currently, functions passed to agents do not properly get listed as tools since the async initialize is not (always?) called. To fix, pull the _function_tool_map construction into __init__.

Changes

  • Construct _function_tool_map in __init__ in agent.py
  • Add functions example to show working usage

Test Plan

uv run scripts/example.py run functions --debug

See our functions listed as tools:

[INFO] 2025-04-07T20:15:13 mcp_agent.mcp_agent_using_functions - Tools available:
{
  "data": {
    "meta": null,
    "nextCursor": null,
    "tools": [
      {
        "name": "add_numbers",
        "description": "\n    Adds two numbers.\n    ",
        "inputSchema": {
          "properties": {
            "a": {
              "title": "A",
              "type": "integer"
            },
            "b": {
              "title": "B",
              "type": "integer"
            }
          },
          "required": [
            "a",
            "b"
          ],
          "title": "add_numbersArguments",
          "type": "object"
        }
      },
      {
        "name": "multiply_numbers",
        "description": "\n    Multiplies two numbers.\n    ",
        "inputSchema": {
          "properties": {
            "a": {
              "title": "A",
              "type": "integer"
            },
            "b": {
              "title": "B",
              "type": "integer"
            }
          },
          "required": [
            "a",
            "b"
          ],
          "title": "multiply_numbersArguments",
          "type": "object"
        }
      },

See them being called as tools:

...
 {
        "finish_reason": "tool_calls",
        "index": 0,
        "logprobs": null,
        "message": {
          "content": null,
          "refusal": null,
          "role": "assistant",
          "annotations": [],
          "audio": null,
          "function_call": null,
          "tool_calls": [
            {
              "id": "call_fp1JQviBV6p4Yfbb2gU6pASf",
              "function": {
                "arguments": "{\"a\":2,\"b\":3}",
                "name": "add_numbers"
              },
              "type": "function"
            }
          ]
        }
      }
...

 {
        "finish_reason": "tool_calls",
        "index": 0,
        "logprobs": null,
        "message": {
          "content": null,
          "refusal": null,
          "role": "assistant",
          "annotations": [],
          "audio": null,
          "function_call": null,
          "tool_calls": [
            {
              "id": "call_P4LwnJKUtYQ8wjKLxMo5JIt7",
              "function": {
                "arguments": "{\"a\":5,\"b\":4}",
                "name": "multiply_numbers"
              },
              "type": "function"
            }
          ]
        }
      }
...

And correct response:

[INFO] 2025-04-07T20:15:16 mcp_agent.mcp_agent_using_functions - Expert math result: The result of adding 2 and 3 is 5. 
When you multiply that result by 4, you get 20.

@rholinshead rholinshead requested a review from saqadri April 8, 2025 00:19
Copy link
Collaborator

@saqadri saqadri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix @rholinshead! I think your fix uncovered another issue which is that initialize shouldn't be calling aenter, but rather an aenter should be calling initialize. That mistake is why this wasn't working as intended.

functions=[add_numbers, multiply_numbers],
)

async with math_agent:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should initialize the agent

@@ -79,10 +82,6 @@ async def initialize(self):
self.__aenter__()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to change the initialize function to get rid of this, and instead to implement the aenter asynccontextmanager for this class, which a) calls super.aenter, and b) calls initialize.

I think for now initialize can stay in empty and just call super.initialize()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I looked into this and:

  • confirmed that implementing __aenter__ and calling initialize with the function tool map construction in initialize (to test) worked as expected, e.g.
    async def __aenter__(self):
        """
        Initialize the agent and connect to the MCP servers.
        NOTE: This method is called automatically when the agent is used as an async context manager.
        """
        if self.initialized:
            return self

        await super().__aenter__()
        await self.initialize()
        return self

    async def initialize(self):
        for function in self.functions:
            tool: FastTool = FastTool.from_function(function)
            self._function_tool_map[tool.name] = tool

But, having the function tool map construction in __init__ IMO makes sense (sounds like we agree). Looks like there is no initialize in any superclass so super().initialize() is invalid. In that case, we'll end up with an empty initialize and then __aenter__ becomes a redundant override.
So, might make sense to just remove __aenter__ and initialize from this class for now and add back in the future if needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saqadri lastest version here removes them as redundant. If you'd prefer to have them just to be explicit / to allow future updates to initialize please let me know and I can add them back

@@ -64,6 +64,9 @@ def __init__(

# Map function names to tools
self._function_tool_map: Dict[str, FastTool] = {}
for function in self.functions:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good change to have in the constructor.

Copy link
Collaborator

@saqadri saqadri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@saqadri saqadri merged commit f2b507d into main Apr 8, 2025
4 checks passed
saqadri pushed a commit that referenced this pull request Apr 17, 2025
* fix/agent-functions

* Remove redundant __aenter__ and initialize
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants