Skip to content

Commit 0dda1e5

Browse files
committed
Removing mcp context to functions not needed and improved descriptions
Signed-off-by: S3B4SZ17 <[email protected]>
1 parent 36fd753 commit 0dda1e5

File tree

9 files changed

+81
-120
lines changed

9 files changed

+81
-120
lines changed

tests/conftest.py

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1+
"""
2+
Module for general utilities and fixtures used in tests.
3+
"""
4+
15
import json
26
import pytest
37
import os
48
from unittest.mock import patch
5-
from fastmcp import Context
6-
from fastmcp import FastMCP
7-
8-
9-
class MockMCP(FastMCP):
10-
"""
11-
Mock class for FastMCP
12-
"""
13-
14-
pass
159

1610

1711
def util_load_json(path):
@@ -43,21 +37,6 @@ def mock_success_response():
4337
patch.stopall()
4438

4539

46-
@pytest.fixture
47-
def mock_ctx():
48-
"""
49-
Fixture to create a mock Context object with 'fastmcp' tags.
50-
Returns:
51-
Context: A mocked Context object with 'fastmcp' tags.
52-
"""
53-
54-
fastmcp: MockMCP = MockMCP(
55-
tags=["sysdig", "mcp", "stdio"],
56-
)
57-
ctx = Context(fastmcp=fastmcp)
58-
return ctx
59-
60-
6140
@pytest.fixture
6241
def mock_creds():
6342
"""

tests/events_feed_test.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
from unittest.mock import MagicMock, AsyncMock
99
import os
1010

11-
from fastmcp import Context
12-
1311
# Get the absolute path of the current module file
1412
module_path = os.path.abspath(__file__)
1513

@@ -19,11 +17,11 @@
1917
EVENT_INFO_RESPONSE = util_load_json(f"{module_directory}/test_data/events_feed/event_info_response.json")
2018

2119

22-
def test_get_event_info(mock_success_response: MagicMock | AsyncMock, mock_ctx: Context, mock_creds) -> None:
20+
def test_get_event_info(mock_success_response: MagicMock | AsyncMock, mock_creds) -> None:
2321
"""Test the get_event_info tool method.
2422
Args:
2523
mock_success_response (MagicMock | AsyncMock): Mocked response object.
26-
mock_ctx (Context): Mocked Context object.
24+
mock_creds: Mocked credentials.
2725
"""
2826

2927
# Successful response
@@ -32,7 +30,7 @@ def test_get_event_info(mock_success_response: MagicMock | AsyncMock, mock_ctx:
3230

3331
tools_client = EventsFeedTools()
3432
# Pass the mocked Context object
35-
result: dict = tools_client.tool_get_event_info("12345", mock_ctx)
33+
result: dict = tools_client.tool_get_event_info("12345")
3634
results: dict = result["results"]
3735

3836
assert result.get("status_code") == HTTPStatus.OK

tools/events_feed/tool.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from datetime import datetime
1212
from typing import Optional, Annotated, Any, Dict
1313
from pydantic import Field
14-
from fastmcp import Context
1514
from sysdig_client import ApiException
1615
from fastmcp.prompts.prompt import PromptMessage, TextContent
1716
from fastmcp.exceptions import ToolError
@@ -38,21 +37,21 @@ class EventsFeedTools:
3837
This class provides methods to retrieve event information and list runtime events.
3938
"""
4039

41-
def init_client(self, config_tags: set[str], old_api: bool = False) -> SecureEventsApi | OldSysdigApi:
40+
def init_client(self, old_api: bool = False) -> SecureEventsApi | OldSysdigApi:
4241
"""
4342
Initializes the SecureEventsApi client from the request state.
4443
If the request does not have the API client initialized, it will create a new instance
4544
using the Sysdig Secure token and host from the environment variables.
4645
Args:
47-
config_tags (set[str]): The tags associated with the MCP server configuration, used to determine the transport mode.
46+
old_api (bool): If True, initializes the OldSysdigApi client instead of SecureEventsApi.
4847
Returns:
4948
SecureEventsApi | OldSysdigApi: An instance of the SecureEventsApi or OldSysdigApi client.
5049
Raises:
5150
ValueError: If the SYSDIG_SECURE_TOKEN environment variable is not set.
5251
"""
5352
secure_events_api: SecureEventsApi = None
5453
old_sysdig_api: OldSysdigApi = None
55-
if "streamable-http" in config_tags:
54+
if app_config.get("mcp", {}).get("transport", "") == "streamable-http":
5655
# Try to get the HTTP request
5756
log.debug("Attempting to get the HTTP request to initialize the Sysdig API client.")
5857
request: Request = get_http_request()
@@ -77,7 +76,7 @@ def init_client(self, config_tags: set[str], old_api: bool = False) -> SecureEve
7776
return old_sysdig_api
7877
return secure_events_api
7978

80-
def tool_get_event_info(self, event_id: str, ctx: Context) -> dict:
79+
def tool_get_event_info(self, event_id: str) -> dict:
8180
"""
8281
Retrieves detailed information for a specific security event.
8382
@@ -88,7 +87,7 @@ def tool_get_event_info(self, event_id: str, ctx: Context) -> dict:
8887
Event: The Event object containing detailed information about the specified event.
8988
"""
9089
# Init of the sysdig client
91-
secure_events_api = self.init_client(config_tags=ctx.fastmcp.tags)
90+
secure_events_api = self.init_client()
9291
try:
9392
# Get the HTTP request
9493
start_time = time.time()
@@ -106,7 +105,6 @@ def tool_get_event_info(self, event_id: str, ctx: Context) -> dict:
106105

107106
def tool_list_runtime_events(
108107
self,
109-
ctx: Context,
110108
cursor: Optional[str] = None,
111109
scope_hours: int = 1,
112110
limit: int = 50,
@@ -148,15 +146,15 @@ def tool_list_runtime_events(
148146
cursor (Optional[str]): Cursor for pagination.
149147
scope_hours (int): Number of hours back from now to include events. Defaults to 1.
150148
severity_level (Optional[str]): One of "info", "low", "medium", "high". If provided, filters by that severity.
151-
If None, includes all severities.
149+
If None, includes all severities.
152150
cluster_name (Optional[str]): Name of the Kubernetes cluster to filter events. If None, includes all clusters.
153151
limit (int): Maximum number of events to return. Defaults to 50.
154152
filter_expr (Optional[str]): An optional filter expression to further narrow down events.
155153
156154
Returns:
157-
List[Event]: A list of Event objects matching the criteria.
155+
dict: A dictionary containing the results of the runtime events query, including pagination information.
158156
"""
159-
secure_events_api = self.init_client(config_tags=ctx.fastmcp.tags)
157+
secure_events_api = self.init_client()
160158
start_time = time.time()
161159
# Compute time window
162160
now_ns = time.time_ns()
@@ -188,13 +186,12 @@ def tool_list_runtime_events(
188186

189187
# A tool to retrieve all the process-tree information for a specific event.Add commentMore actions
190188

191-
def tool_get_event_process_tree(self, ctx: Context, event_id: str) -> Dict[str, Any]:
189+
def tool_get_event_process_tree(self, event_id: str) -> dict:
192190
"""
193191
Retrieves the process tree for a specific security event.
194192
Not every event has a process tree, so this may return an empty tree.
195193
196194
Args:
197-
ctx (Context): The context object containing request-specific information.
198195
event_id (str): The unique identifier of the security event.
199196
200197
Returns:
@@ -203,7 +200,7 @@ def tool_get_event_process_tree(self, ctx: Context, event_id: str) -> Dict[str,
203200
try:
204201
start_time = time.time()
205202
# Get process tree branches
206-
old_api_client = self.init_client(config_tags=ctx.fastmcp.tags, old_api=True)
203+
old_api_client = self.init_client(old_api=True)
207204
branches = old_api_client.request_process_tree_branches(event_id)
208205
# Get process tree
209206
tree = old_api_client.request_process_tree_trees(event_id)

tools/inventory/tool.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from typing import Annotated
99
from pydantic import Field
1010
from fastmcp.server.dependencies import get_http_request
11-
from fastmcp import Context
1211
from fastmcp.exceptions import ToolError
1312
from starlette.requests import Request
1413
from sysdig_client import ApiException
@@ -32,20 +31,18 @@ class InventoryTools:
3231
This class provides methods to list resources and retrieve a single resource by its hash.
3332
"""
3433

35-
def init_client(self, config_tags: set[str]) -> InventoryApi:
34+
def init_client(self) -> InventoryApi:
3635
"""
3736
Initializes the InventoryApi client from the request state.
3837
If the request does not have the API client initialized, it will create a new instance
3938
using the Sysdig Secure token and host from the environment variables.
40-
Args:
41-
config_tags (set[str]): The tags associated with the MCP server configuration, used to determine the transport mode.
4239
Returns:
4340
InventoryApi: An instance of the InventoryApi client.
4441
Raises:
4542
ValueError: If the SYSDIG_SECURE_TOKEN environment variable is not set.
4643
"""
4744
secure_events_api: InventoryApi = None
48-
if "streamable-http" in config_tags:
45+
if app_config.get("mcp", {}).get("transport", "") == "streamable-http":
4946
# Try to get the HTTP request
5047
log.debug("Attempting to get the HTTP request to initialize the Sysdig API client.")
5148
request: Request = get_http_request()
@@ -64,7 +61,6 @@ def init_client(self, config_tags: set[str]) -> InventoryApi:
6461

6562
def tool_list_resources(
6663
self,
67-
ctx: Context,
6864
filter_exp: Annotated[
6965
str,
7066
Field(
@@ -154,20 +150,28 @@ def tool_list_resources(
154150
List inventory items based on a filter expression, with optional pagination.
155151
156152
Args:
157-
filter_exp (str): Sysdig Secure query filter expression.
153+
filter_exp (str): Sysdig query filter expression to filter inventory resources.
154+
Use the resource://filter-query-language to get the expected filter expression format.
155+
Supports operators: =, !=, in, exists, contains, startsWith.
156+
Combine with and/or/not.
158157
Examples:
159-
- not isExposed exists
160-
- category in ("IAM") and isExposed exists
161-
- category in ("IAM","Audit & Monitoring")
158+
- zone in ("zone1") and machineImage = "ami-0b22b359fdfabe1b5"
159+
- (projectId = "1235495521" or projectId = "987654321") and vuln.severity in ("Critical")
160+
- vuln.name in ("CVE-2023-0049")
161+
- vuln.cvssScore >= "3"
162+
- container.name in ("sysdig-container") and not labels exists
163+
- imageId in ("sha256:3768ff6176e29a35ce1354622977a1e5c013045cbc4f30754ef3459218be8ac")
164+
- platform in ("GCP", "AWS", "Azure", "Kubernetes") and isExposed exists
162165
page_number (int): Page number for pagination (1-based).
163166
page_size (int): Number of items per page.
164167
with_enrich_containers (bool): Include enriched container information.
165168
166169
Returns:
167-
InventoryResourceResponse: The API response containing inventory items.
170+
dict: A dictionary containing the results of the inventory query, including pagination information.
171+
Or a dict containing an error message if the call fails.
168172
"""
169173
try:
170-
inventory_api = self.init_client(config_tags=ctx.fastmcp.tags)
174+
inventory_api = self.init_client()
171175
start_time = time.time()
172176

173177
api_response = inventory_api.get_resources_without_preload_content(
@@ -185,7 +189,6 @@ def tool_list_resources(
185189

186190
def tool_get_resource(
187191
self,
188-
ctx: Context,
189192
resource_hash: Annotated[str, Field(description="The unique hash of the inventory resource to retrieve.")],
190193
) -> dict:
191194
"""
@@ -195,11 +198,10 @@ def tool_get_resource(
195198
resource_hash (str): The hash identifier of the resource.
196199
197200
Returns:
198-
InventoryResourceExtended: The detailed resource object.
199-
Or a dict containing an error message if the call fails.
201+
dict: A dictionary containing the details of the requested inventory resource.
200202
"""
201203
try:
202-
inventory_api = self.init_client(config_tags=ctx.fastmcp.tags)
204+
inventory_api = self.init_client()
203205
start_time = time.time()
204206

205207
api_response = inventory_api.get_resource_without_preload_content(hash=resource_hash)

tools/sysdig_sage/tool.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import os
99
import time
1010
from typing import Any, Dict
11-
from fastmcp import Context
1211
from fastmcp.exceptions import ToolError
1312
from utils.sysdig.old_sysdig_api import OldSysdigApi
1413
from starlette.requests import Request
@@ -31,21 +30,19 @@ class SageTools:
3130
language questions and execute them against the Sysdig API.
3231
"""
3332

34-
def init_client(self, config_tags: set[str]) -> OldSysdigApi:
33+
def init_client(self) -> OldSysdigApi:
3534
"""
3635
Initializes the OldSysdigApi client from the request state.
3736
If the request does not have the API client initialized, it will create a new instance
3837
using the Sysdig Secure token and host from the environment variables.
39-
Args:
40-
config_tags (set[str]): The tags associated with the MCP server configuration, used to determine the transport mode.
4138
Returns:
4239
OldSysdigApi: An instance of the OldSysdigApi client.
4340
4441
Raises:
4542
ValueError: If the SYSDIG_SECURE_TOKEN environment variable is not set.
4643
"""
4744
old_sysdig_api: OldSysdigApi = None
48-
if "streamable-http" in config_tags:
45+
if app_config.get("mcp", {}).get("transport", "") == "streamable-http":
4946
# Try to get the HTTP request
5047
log.debug("Attempting to get the HTTP request to initialize the Sysdig API client.")
5148
request: Request = get_http_request()
@@ -63,7 +60,7 @@ def init_client(self, config_tags: set[str]) -> OldSysdigApi:
6360
old_sysdig_api = OldSysdigApi(api_client)
6461
return old_sysdig_api
6562

66-
async def tool_sysdig_sage(self, ctx: Context, question: str) -> Dict[str, Any]:
63+
async def tool_sage_to_sysql(self, question: str) -> dict:
6764
"""
6865
Queries Sysdig Sage with a natural language question, retrieves a SysQL query,
6966
executes it against the Sysdig API, and returns the results.
@@ -72,20 +69,20 @@ async def tool_sysdig_sage(self, ctx: Context, question: str) -> Dict[str, Any]:
7269
question (str): A natural language question to send to Sage.
7370
7471
Returns:
75-
Dict: JSON-decoded response of the executed SysQL query, or an error object.
72+
dict: A dictionary containing the results of the SysQL query execution and the query text.
7673
7774
Raises:
7875
ToolError: If the SysQL query generation or execution fails.
7976
8077
Examples:
81-
# tool_sysdig_sage(question="Match Cloud Resource affected by Critical Vulnerability")
82-
# tool_sysdig_sage(question="Match Kubernetes Workload affected by Critical Vulnerability")
83-
# tool_sysdig_sage(question="Match AWS EC2 Instance that violates control 'EC2 - Instances should use IMDSv2'")
78+
# tool_sage_to_sysql(question="Match Cloud Resource affected by Critical Vulnerability")
79+
# tool_sage_to_sysql(question="Match Kubernetes Workload affected by Critical Vulnerability")
80+
# tool_sage_to_sysql(question="Match AWS EC2 Instance that violates control 'EC2 - Instances should use IMDSv2'")
8481
"""
8582
# 1) Generate SysQL query
8683
try:
8784
start_time = time.time()
88-
old_sysdig_api = self.init_client(config_tags=ctx.fastmcp.tags)
85+
old_sysdig_api = self.init_client()
8986
sysql_response = await old_sysdig_api.generate_sysql_query(question)
9087
if sysql_response.status > 299:
9188
raise ToolError(f"Sysdig Sage returned an error: {sysql_response.status} - {sysql_response.data}")

0 commit comments

Comments
 (0)