Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/dbjavagenix/database/mcp_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ def get_connection_tools() -> List[Tool]:
"table": {
"type": "string",
"description": "Table name"
},
"schema": {
"type": "string",
"description": "PostgreSQL schema (optional)"
}
},
"required": ["connection_id", "database", "table"]
Expand Down Expand Up @@ -867,6 +871,7 @@ async def handle_db_query_table_exists(arguments: Dict[str, Any]) -> List[TextCo
connection_id = arguments["connection_id"]
database = arguments["database"]
table = arguments["table"]
schema = arguments.get("schema") or None

# Get connection info to determine database type
config = connection_manager.get_connection_info(connection_id)
Expand All @@ -883,14 +888,17 @@ async def handle_db_query_table_exists(arguments: Dict[str, Any]) -> List[TextCo
results = connection_manager.execute_query(connection_id, query, (database, table))

elif config.type == DatabaseType.POSTGRESQL:
schema_filter = "AND table_schema = %s" if schema else ""
query = """
SELECT COUNT(*) AS count
FROM information_schema.tables
WHERE table_catalog = %s
AND table_name = %s
AND table_schema NOT IN ('pg_catalog', 'information_schema')
"""
results = connection_manager.execute_query(connection_id, query, (database, table))
{schema_filter}
""".format(schema_filter=schema_filter)
params = (database, table, schema) if schema else (database, table)
results = connection_manager.execute_query(connection_id, query, params)

elif config.type == DatabaseType.SQLITE:
query = """
Expand All @@ -906,10 +914,11 @@ async def handle_db_query_table_exists(arguments: Dict[str, Any]) -> List[TextCo
exists = results[0]["count"] > 0 if results else False

response = {
"success": True,
"database": database,
"table": table,
"exists": exists
"success": True,
"database": database,
"table": table,
"schema": schema,
"exists": exists
}

status = "exists" if exists else "does not exist"
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/test_database_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ def test_connection_tool_schema_only_advertises_implemented_databases():
)


def test_table_exists_tool_advertises_optional_postgresql_schema():
table_tool = next(
tool for tool in get_connection_tools() if tool.name == "db_query_table_exists"
)

assert table_tool.inputSchema["properties"]["schema"] == {
"type": "string",
"description": "PostgreSQL schema (optional)",
}
assert "schema" not in table_tool.inputSchema["required"]


def test_cli_version_only_advertises_implemented_databases():
result = CliRunner().invoke(cli.app, ["version"])

Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_mcp_error_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,6 @@ async def test_table_exists_success_raw_response_is_json(monkeypatch):
"success": True,
"database": "app",
"table": "users",
"schema": None,
"exists": True,
}
24 changes: 24 additions & 0 deletions tests/unit/test_postgresql_mcp_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,30 @@ def execute_query(connection_id, query, params=None):
assert calls[0][2] == ("app", "users")


@pytest.mark.asyncio
async def test_table_exists_applies_postgresql_schema_filter_with_bound_parameter(
monkeypatch, postgres_info
):
calls = []
monkeypatch.setattr(
mcp_tools.connection_manager, "get_connection_info", lambda cid: postgres_info
)

def execute_query(connection_id, query, params=None):
calls.append((connection_id, query, params))
return [{"count": 1}]

monkeypatch.setattr(mcp_tools.connection_manager, "execute_query", execute_query)

response = await mcp_tools.handle_db_query_table_exists(
{"connection_id": "pg-1", "database": "app", "table": "users", "schema": "tenant_a"}
)

assert "exists in database" in response[0].text
assert "table_schema = %s" in calls[0][1]
assert calls[0][2] == ("app", "users", "tenant_a")


def test_postgresql_java_mapping_normalizes_catalog_type_names():
assert mcp_tools._get_java_type_mapping(
DatabaseType.POSTGRESQL, "timestamp with time zone"
Expand Down
Loading