Skip to content

Commit 11f92db

Browse files
committed
Add instrumentation for autogen MCP tool adapter. (#1409)
* Add instrumentation for MCP tool adapter. * [MegaLinter] Apply linters fixes
1 parent 13fc061 commit 11f92db

File tree

5 files changed

+143
-0
lines changed

5 files changed

+143
-0
lines changed

newrelic/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,6 +2864,10 @@ def _process_module_builtin_defaults():
28642864
_process_module_definition("loguru", "newrelic.hooks.logger_loguru", "instrument_loguru")
28652865
_process_module_definition("loguru._logger", "newrelic.hooks.logger_loguru", "instrument_loguru_logger")
28662866

2867+
_process_module_definition(
2868+
"autogen_ext.tools.mcp._base", "newrelic.hooks.mlmodel_autogen", "instrument_autogen_ext_tools_mcp__base"
2869+
)
2870+
28672871
_process_module_definition("mcp.client.session", "newrelic.hooks.adapter_mcp", "instrument_mcp_client_session")
28682872

28692873
_process_module_definition("structlog._base", "newrelic.hooks.logger_structlog", "instrument_structlog__base")

newrelic/hooks/mlmodel_autogen.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from newrelic.api.function_trace import FunctionTrace
16+
from newrelic.api.transaction import current_transaction
17+
from newrelic.common.object_names import callable_name
18+
from newrelic.common.object_wrapper import wrap_function_wrapper
19+
from newrelic.common.signature import bind_args
20+
21+
22+
async def wrap_from_server_params(wrapped, instance, args, kwargs):
23+
transaction = current_transaction()
24+
if not transaction:
25+
return await wrapped(*args, **kwargs)
26+
27+
func_name = callable_name(wrapped)
28+
bound_args = bind_args(wrapped, args, kwargs)
29+
tool_name = bound_args.get("tool_name") or "tool"
30+
function_trace_name = f"{func_name}/{tool_name}"
31+
with FunctionTrace(name=function_trace_name, group="Llm", source=wrapped):
32+
return await wrapped(*args, **kwargs)
33+
34+
35+
def instrument_autogen_ext_tools_mcp__base(module):
36+
if hasattr(module, "McpToolAdapter"):
37+
if hasattr(module.McpToolAdapter, "from_server_params"):
38+
wrap_function_wrapper(module, "McpToolAdapter.from_server_params", wrap_from_server_params)

tests/mlmodel_autogen/conftest.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from testing_support.fixture.event_loop import event_loop as loop
16+
from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture
17+
18+
_default_settings = {
19+
"package_reporting.enabled": False,
20+
"transaction_tracer.explain_threshold": 0.0,
21+
"transaction_tracer.transaction_threshold": 0.0,
22+
"transaction_tracer.stack_trace_threshold": 0.0,
23+
"debug.log_data_collector_payloads": True,
24+
"debug.record_transaction_failure": True,
25+
"ai_monitoring.enabled": True,
26+
}
27+
28+
collector_agent_registration = collector_agent_registration_fixture(
29+
app_name="Python Agent Test (mlmodel_autogen)", default_settings=_default_settings
30+
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from unittest.mock import AsyncMock
16+
17+
import pytest
18+
from autogen_ext.tools.mcp import SseMcpToolAdapter, SseServerParams
19+
from mcp import ClientSession, Tool
20+
from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
21+
22+
from newrelic.api.background_task import background_task
23+
24+
# Test setup derived from: https://github.com/microsoft/autogen/blob/main/python/packages/autogen-ext/tests/tools/test_mcp_tools.py
25+
# autogen MIT license: https://github.com/microsoft/autogen/blob/main/LICENSE and
26+
# https://github.com/microsoft/autogen/blob/main/LICENSE-CODE
27+
28+
29+
@pytest.fixture
30+
def mock_sse_session():
31+
session = AsyncMock(spec=ClientSession)
32+
session.initialize = AsyncMock()
33+
session.call_tool = AsyncMock()
34+
session.list_tools = AsyncMock()
35+
return session
36+
37+
38+
@pytest.fixture
39+
def add_exclamation():
40+
return Tool(
41+
name="add_exclamation",
42+
description="A test SSE tool that adds an exclamation mark to a string",
43+
inputSchema={"type": "object", "properties": {"input": {"type": "string"}}, "required": ["input"]},
44+
)
45+
46+
47+
@validate_transaction_metrics(
48+
"test_mcp_tool_adapter:test_from_server_params_tracing",
49+
scoped_metrics=[("Llm/autogen_ext.tools.mcp._sse:SseMcpToolAdapter.from_server_params/add_exclamation", 1)],
50+
rollup_metrics=[("Llm/autogen_ext.tools.mcp._sse:SseMcpToolAdapter.from_server_params/add_exclamation", 1)],
51+
background_task=True,
52+
)
53+
@background_task()
54+
def test_from_server_params_tracing(loop, mock_sse_session, monkeypatch, add_exclamation):
55+
async def _test():
56+
params = SseServerParams(url="http://test-url")
57+
mock_context = AsyncMock()
58+
mock_context.__aenter__.return_value = mock_sse_session
59+
monkeypatch.setattr(
60+
"autogen_ext.tools.mcp._base.create_mcp_server_session", lambda *args, **kwargs: mock_context
61+
)
62+
63+
mock_sse_session.list_tools.return_value.tools = [add_exclamation]
64+
65+
adapter = await SseMcpToolAdapter.from_server_params(params, "add_exclamation")
66+
67+
loop.run_until_complete(_test())

tox.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ envlist =
156156
python-logger_logging-{py37,py38,py39,py310,py311,py312,py313,pypy310},
157157
python-logger_loguru-{py37,py38,py39,py310,py311,py312,py313,pypy310}-logurulatest,
158158
python-logger_structlog-{py37,py38,py39,py310,py311,py312,py313,pypy310}-structloglatest,
159+
python-mlmodel_autogen-{py310,py311,py312,py313,pypy310},
159160
python-mlmodel_gemini-{py39,py310,py311,py312,py313},
160161
python-mlmodel_langchain-{py39,py310,py311,py312},
161162
;; Package not ready for Python 3.13 (uses an older version of numpy)
@@ -407,6 +408,8 @@ deps =
407408
framework_tornado: pycurl
408409
framework_tornado-tornadolatest: tornado
409410
framework_tornado-tornadomaster: https://github.com/tornadoweb/tornado/archive/master.zip
411+
mlmodel_autogen: autogen-ext
412+
mlmodel_autogen: mcp
410413
mlmodel_gemini: google-genai
411414
mlmodel_openai-openai0: openai[datalib]<1.0
412415
mlmodel_openai-openai107: openai[datalib]<1.8
@@ -564,6 +567,7 @@ changedir =
564567
messagebroker_kafkapython: tests/messagebroker_kafkapython
565568
messagebroker_kombu: tests/messagebroker_kombu
566569
messagebroker_pika: tests/messagebroker_pika
570+
mlmodel_autogen: tests/mlmodel_autogen
567571
mlmodel_gemini: tests/mlmodel_gemini
568572
mlmodel_langchain: tests/mlmodel_langchain
569573
mlmodel_openai: tests/mlmodel_openai

0 commit comments

Comments
 (0)