-
Notifications
You must be signed in to change notification settings - Fork 3
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
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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.") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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] |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done here b153083
cc @vita-stejskal |
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. |
|
coverage.xml
Outdated
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
tests/conftest.py
Outdated
|
||
|
||
@pytest.fixture | ||
def mock_context() -> Tuple[Context, KeboolaClient]: |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done here: 81c8bb7
tests/conftest.py
Outdated
|
||
|
||
@pytest.fixture | ||
def mock_context_with_db() -> Tuple[Context, KeboolaClient, ConnectionManager, DatabasePathManager]: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed here: 81c8bb7
tests/conftest.py
Outdated
|
||
|
||
@pytest.fixture | ||
def mock_snowflake_connection(): |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed here: 81c8bb7
tests/conftest.py
Outdated
# Mock KeboolaClient | ||
mock_client = MagicMock(spec=KeboolaClient) | ||
mock_client.storage_client = MagicMock(spec=Client) | ||
mock_client.storage_client.components = MagicMock() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done here: 81c8bb7
tests/test_component_tools.py
Outdated
async def test_list_component_configs(mock_context): | ||
"""Test list_component_configs tool.""" | ||
context, mock_client = mock_context | ||
mock_client.storage_client.configurations = MagicMock() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'
Partial PR refactoring current component tools code, no new feature added yet
checkout from (KAB-817)