Skip to content
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

KAB-871-mcp-ro-component-tools2 #12

Open
wants to merge 23 commits into
base: master
Choose a base branch
from

Conversation

mariankrotil
Copy link

@mariankrotil mariankrotil commented Mar 18, 2025

Partial PR refactoring current component tools code, no new feature added yet
checkout from (KAB-817)

  • modularize component tools code into separate files
  • utilizing pydantic.BaseModel for structured output of the tool functions

Verified

This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
…ools code
@vita-stejskal vita-stejskal changed the base branch from master to KAB-817-statefull-sessions March 19, 2025 13:59
Copy link
Contributor

@vita-stejskal vita-stejskal left a comment

Choose a reason for hiding this comment

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

Also please:

  • Add tests for the tools -- mock the SAPI client in the Context and make sure the tools return expected response objects.
  • Try to retrieve the list of the tools exposed by the MCP server and confirm that their descriptions really contain the specifications (presumably JSON schema) of the parameter and output values. You can use the MCP client from mcp package to list the available tools.
  • Increment the project version in pyproject.toml file.


def add_component_tools(mcp: FastMCP) -> None:
"""Add tools to the MCP server."""
mcp.add_tool(list_components, "list_components", "List all available components.")
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't have to specify the name and description of the tool explicitly. FastMCP infers this information from the function's signature and dosctring.

Copy link
Author

Choose a reason for hiding this comment

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

Done here b153083



async def list_component_configs(
component_id: str,
Copy link
Contributor

Choose a reason for hiding this comment

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

Use component_id: Annotated[str, Field(description='The ID of a component which configurations should be listed.')] to describe the meaning of the parameter. See examples in the FastMCP docs.

Copy link
Author

Choose a reason for hiding this comment

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

Done here b153083

client = ctx.session.state["sapi_client"]
assert isinstance(client, KeboolaClient)
r_components = client.storage_client.components.list()
return [Component(id=r_comp["id"], name=r_comp["name"]) for r_comp in r_components]
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally, you should use Component.model_validate(r_comp) instead of translating the Storage API response data to the Component instance manually. The same applies for the ComponentConfig class below.

Copy link
Author

Choose a reason for hiding this comment

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

Done here b153083

@vita-stejskal vita-stejskal changed the base branch from KAB-817-statefull-sessions to master March 20, 2025 18:24
@mariankrotil
Copy link
Author

cc @vita-stejskal
I added a ComponentClient to handle component requests, avoiding the need to use KeboolClient's get method and expose endpoints within the tool function, which I find messy.
is it ok?

@mariankrotil
Copy link
Author

cc @vita-stejskal I added a ComponentClient to handle component requests, avoiding the need to use KeboolClient's get method and expose endpoints within the tool function, which I find messy. is it ok?

Oops. I ran into an issue where we would be modifying the same file. So, I went with the first option and exposed the endpoints, but we can refactor it in the future if needed.

@mariankrotil
Copy link
Author

mariankrotil commented Mar 21, 2025

Also please:

  • Add tests for the tools -- mock the SAPI client in the Context and make sure the tools return expected response objects.
  • Try to retrieve the list of the tools exposed by the MCP server and confirm that their descriptions really contain the specifications (presumably JSON schema) of the parameter and output values. You can use the MCP client from mcp package to list the available tools.
  • Increment the project version in pyproject.toml file.
  • Tests are here: 4806aa3 febc2d9
  • List of available tools in mcp server:
    image
    it contains fc name, fc descriptions and fc params, but I cannot find param description.

coverage.xml Outdated
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are you changing the .coverage and coverage.xml` files?

Copy link
Author

Choose a reason for hiding this comment

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

I removed it from Git tracking in this repo because it was already listed in the .gitignore file, but that was not working. Coverage files update automatically whenever you create or modify tests and run pytest command as they track how much of our code is covered by the tests. But I think they do not have to be shared in the git repo. Or am I wrong?



@pytest.fixture
def mock_context() -> Tuple[Context, KeboolaClient]:
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not very nice. I'd slit this fixture into two fixtures -- mcp_context() -> Context and keboola_client(mcp_context) -> KeboolaClient.

Copy link
Author

Choose a reason for hiding this comment

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

Done here: 81c8bb7



@pytest.fixture
def mock_context_with_db() -> Tuple[Context, KeboolaClient, ConnectionManager, DatabasePathManager]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you define this fixture? It seems unused.

Copy link
Author

Choose a reason for hiding this comment

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

Removed here: 81c8bb7



@pytest.fixture
def mock_snowflake_connection():
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you define this fixture? It seems unused.

Copy link
Author

Choose a reason for hiding this comment

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

removed here: 81c8bb7

# Mock KeboolaClient
mock_client = MagicMock(spec=KeboolaClient)
mock_client.storage_client = MagicMock(spec=Client)
mock_client.storage_client.components = MagicMock()
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that you need to set this mock here. You set it again in the tests anyway.

Copy link
Author

Choose a reason for hiding this comment

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

Done here: 81c8bb7

async def test_list_component_configs(mock_context):
"""Test list_component_configs tool."""
context, mock_client = mock_context
mock_client.storage_client.configurations = MagicMock()
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that you have to set all those mocks explicitly. MagicMock will replicate itself into all attributes that you access on the mock.

Copy link
Author

@mariankrotil mariankrotil Mar 27, 2025

Choose a reason for hiding this comment

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

Removing it leads to an error FAILED tests/test_component_tools.py::test_list_component_configs- AttributeError: Mock object has no attribute 'configurations'

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.

None yet

2 participants