awslabs.mysql-mcp-server 1.0.8__tar.gz → 1.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/PKG-INFO +1 -1
  2. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/__init__.py +1 -1
  3. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/connection/asyncmy_pool_connection.py +2 -2
  4. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/server.py +9 -1
  5. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/pyproject.toml +1 -1
  6. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_asyncmy_pool_connection.py +8 -8
  7. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_server.py +42 -0
  8. awslabs_mysql_mcp_server-1.0.9/uv-requirements.txt +27 -0
  9. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/uv.lock +1 -1
  10. awslabs_mysql_mcp_server-1.0.8/uv-requirements.txt +0 -24
  11. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/.gitignore +0 -0
  12. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/.python-version +0 -0
  13. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/CHANGELOG.md +0 -0
  14. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/Dockerfile +0 -0
  15. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/LICENSE +0 -0
  16. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/NOTICE +0 -0
  17. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/README.md +0 -0
  18. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/__init__.py +0 -0
  19. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/connection/__init__.py +0 -0
  20. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/connection/abstract_db_connection.py +0 -0
  21. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/connection/db_connection_singleton.py +0 -0
  22. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/connection/rds_data_api_connection.py +0 -0
  23. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/mutable_sql_detector.py +0 -0
  24. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/docker-healthcheck.sh +0 -0
  25. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/conftest.py +0 -0
  26. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_abstract_db_connection.py +0 -0
  27. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_db_connection_singleton.py +0 -0
  28. {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_rds_data_api_connection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awslabs.mysql-mcp-server
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Summary: An AWS Labs Model Context Protocol (MCP) server for mysql
5
5
  Project-URL: homepage, https://awslabs.github.io/mcp/
6
6
  Project-URL: docs, https://awslabs.github.io/mcp/servers/mysql-mcp-server/
@@ -14,4 +14,4 @@
14
14
 
15
15
  """awslabs.mysql_mcp_server"""
16
16
 
17
- __version__ = '1.0.8'
17
+ __version__ = '1.0.9'
@@ -132,7 +132,7 @@ class AsyncmyPoolConnection(AbstractDBConnection):
132
132
  await cursor.execute('SET TRANSACTION READ ONLY')
133
133
  # Execute the query
134
134
  if parameters:
135
- params = _convert_parameters(self, parameters).values()
135
+ params = list(_convert_parameters(self, parameters).values())
136
136
  await cursor.execute(sql, params)
137
137
  else:
138
138
  await cursor.execute(sql)
@@ -146,7 +146,7 @@ class AsyncmyPoolConnection(AbstractDBConnection):
146
146
  rows = await cursor.fetchall()
147
147
 
148
148
  # Structure the response to match the interface contract required by server.py
149
- column_metadata = [{'name': col} for col in columns]
149
+ column_metadata = [{'label': col} for col in columns]
150
150
  records = []
151
151
 
152
152
  # Convert each row to the expected format
@@ -18,6 +18,7 @@ import argparse
18
18
  import asyncio
19
19
  import sys
20
20
  from awslabs.mysql_mcp_server.connection import DBConnectionSingleton
21
+ from awslabs.mysql_mcp_server.connection.asyncmy_pool_connection import AsyncmyPoolConnection
21
22
  from awslabs.mysql_mcp_server.mutable_sql_detector import (
22
23
  check_sql_injection_risk,
23
24
  detect_mutating_keywords,
@@ -193,9 +194,16 @@ async def get_table_schema(
193
194
  ORDER BY
194
195
  ORDINAL_POSITION
195
196
  """
197
+ db_connection = DBConnectionSingleton.get().db_connection
198
+
199
+ if isinstance(db_connection, AsyncmyPoolConnection):
200
+ # Convert to positional parameters for asyncmy
201
+ sql = sql.replace(':database_name', '%s').replace(':table_name', '%s')
202
+
203
+ # Use consistent parameter order matching SQL placeholders
196
204
  params = [
197
- {'name': 'table_name', 'value': {'stringValue': table_name}},
198
205
  {'name': 'database_name', 'value': {'stringValue': database_name}},
206
+ {'name': 'table_name', 'value': {'stringValue': table_name}},
199
207
  ]
200
208
 
201
209
  return await run_query(sql=sql, ctx=ctx, query_parameters=params)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "awslabs.mysql-mcp-server"
3
- version = "1.0.8"
3
+ version = "1.0.9"
4
4
  description = "An AWS Labs Model Context Protocol (MCP) server for mysql"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -97,7 +97,7 @@ async def test_execute_query_returns_results():
97
97
  conn.pool = fake_pool
98
98
  result = await conn.execute_query('SELECT id, name FROM users')
99
99
 
100
- assert result['columnMetadata'] == [{'name': 'id'}, {'name': 'name'}]
100
+ assert result['columnMetadata'] == [{'label': 'id'}, {'label': 'name'}]
101
101
  assert result['records'] == [
102
102
  [{'longValue': 1}, {'stringValue': 'Alice'}],
103
103
  [{'longValue': 2}, {'stringValue': 'Bob'}],
@@ -400,13 +400,13 @@ async def test_execute_query_readonly_and_parameter_types_mapped_boolean_as_long
400
400
 
401
401
  fake_cursor.execute.assert_any_await('SET TRANSACTION READ ONLY')
402
402
  assert result['columnMetadata'] == [
403
- {'name': 'n'},
404
- {'name': 's'},
405
- {'name': 'i'},
406
- {'name': 'f'},
407
- {'name': 'b'},
408
- {'name': 'blob'},
409
- {'name': 'odd'},
403
+ {'label': 'n'},
404
+ {'label': 's'},
405
+ {'label': 'i'},
406
+ {'label': 'f'},
407
+ {'label': 'b'},
408
+ {'label': 'blob'},
409
+ {'label': 'odd'},
410
410
  ]
411
411
  # Note: bool mapped via int branch -> {'longValue': True}
412
412
  assert result['records'] == [
@@ -20,6 +20,7 @@ import json
20
20
  import pytest
21
21
  import sys
22
22
  import uuid
23
+ from awslabs.mysql_mcp_server.connection.asyncmy_pool_connection import AsyncmyPoolConnection
23
24
  from awslabs.mysql_mcp_server.server import (
24
25
  DBConnectionSingleton,
25
26
  client_error_code_key,
@@ -30,6 +31,7 @@ from awslabs.mysql_mcp_server.server import (
30
31
  write_query_prohibited_key,
31
32
  )
32
33
  from conftest import DummyCtx, Mock_DBConnection, MockException
34
+ from unittest.mock import AsyncMock, Mock, patch
33
35
 
34
36
 
35
37
  SAFE_READONLY_QUERIES = [
@@ -834,6 +836,46 @@ def test_main_with_valid_asyncmy_parameters(monkeypatch, capsys):
834
836
  main()
835
837
 
836
838
 
839
+ @pytest.mark.asyncio
840
+ async def test_get_table_schema_asyncmy_connection():
841
+ """Test get_table_schema with asyncmy connection type detection and SQL conversion."""
842
+ with patch(
843
+ 'awslabs.mysql_mcp_server.connection.asyncmy_pool_connection._get_credentials_from_secret',
844
+ return_value=('user', 'pass'),
845
+ ):
846
+ DBConnectionSingleton.initialize(
847
+ 'mock', 'mock', 'mock', hostname='mock', port=3306, readonly=False, is_test=True
848
+ )
849
+
850
+ # Replace the real asyncmy connection with a mock to avoid actual DB connection
851
+ mock_asyncmy_connection = Mock(spec=AsyncmyPoolConnection)
852
+ mock_asyncmy_connection.execute_query = AsyncMock(
853
+ return_value=get_mock_normal_query_response()
854
+ )
855
+ DBConnectionSingleton._instance._db_connection = mock_asyncmy_connection # type: ignore
856
+
857
+ ctx = DummyCtx()
858
+ tool_response = await get_table_schema(table_name='table_name', database_name='mysql', ctx=ctx)
859
+
860
+ # Verify SQL was converted from :name to %s for asyncmy
861
+ call_args = mock_asyncmy_connection.execute_query.call_args
862
+ sql_used = call_args[0][0] # First positional argument is the SQL
863
+
864
+ assert '%s' in sql_used
865
+ assert ':database_name' not in sql_used
866
+ assert ':table_name' not in sql_used
867
+
868
+ # validate tool_response
869
+ assert (
870
+ isinstance(tool_response, (list, tuple))
871
+ and len(tool_response) == 1
872
+ and isinstance(tool_response[0], dict)
873
+ and 'error' not in tool_response[0]
874
+ )
875
+ column_records = tool_response[0]
876
+ validate_normal_query_response(column_records)
877
+
878
+
837
879
  if __name__ == '__main__':
838
880
  DBConnectionSingleton.initialize(
839
881
  'mock', 'mock', 'mock', resource_arn='mock', readonly=True, is_test=True
@@ -0,0 +1,27 @@
1
+ #
2
+ # This file is autogenerated by pip-compile with Python 3.12
3
+ # by the following command:
4
+ #
5
+ # pip-compile --generate-hashes --output-file=uv-requirements.txt --strip-extras uv-requirements.in
6
+ #
7
+ uv==0.9.6 \
8
+ --hash=sha256:0169a85d3ba5ef1c37089d64ff26de573439ca84ecf549276a2eee42d7f833f2 \
9
+ --hash=sha256:0fde18c22376c8b02954c7db3847bc75ac42619932c44b43f49d056e5cfb05f9 \
10
+ --hash=sha256:166175ba952d2ad727e1dbd57d7cfc1782dfe7b8d79972174a46a7aa33ddceec \
11
+ --hash=sha256:3c2c2b2b093330e603d838fec26941ab6f62e8d62a012f9fa0d5ed88da39d907 \
12
+ --hash=sha256:538716ec97f8d899baa7e1c427f4411525459c0ef72ea9b3625ce9610c9976e6 \
13
+ --hash=sha256:547fd27ab5da7cd1a833288a36858852451d416a056825f162ecf2af5be6f8b8 \
14
+ --hash=sha256:62e3f057a9ae5e5003a7cd56b617e940f519f6dabcbb22d36cdd0149df25d409 \
15
+ --hash=sha256:6403176b55388cf94fb8737e73b26ee2a7b1805a9139da5afa951210986d4fcd \
16
+ --hash=sha256:7e89c964f614fa3f0481060cac709d6da50feac553e1e11227d6c4c81c87af7c \
17
+ --hash=sha256:86e05782f9b75d39ab1c0af98bf11e87e646a36a61d425021d5b284073e56315 \
18
+ --hash=sha256:90122a76e6441b8c580fc9faf06bd8c4dbe276cb1c185ad91eceb2afa78e492a \
19
+ --hash=sha256:95a62c1f668272555ad0c446bf44a9924dee06054b831d04c162e0bad736dc28 \
20
+ --hash=sha256:a7c6067919d87208c4a6092033c3bc9799cb8be1c8bc6ef419a1f6d42a755329 \
21
+ --hash=sha256:b2f934737c93f88c906b6a47bcc083170210fe5d66565e80a7c139599e5cbf2f \
22
+ --hash=sha256:b31377ebf2d0499afc5abe3fe1abded5ca843f3a1161b432fe26eb0ce15bab8e \
23
+ --hash=sha256:d1072db92cc9525febdf9d113c23916dfc20ca03e21218cc7beefe7185a90631 \
24
+ --hash=sha256:e700b2098f9d365061c572d0729b4e8bc71c6468d83dfaae2537cd66e3cb1b98 \
25
+ --hash=sha256:ea67369918af24ea7e01991dfc8b8988d1b0b7c49cb39d9e5bc0c409930a0a3f \
26
+ --hash=sha256:f0ba311b3ca49d246f36d444d3ee81571619ef95e5f509eb694a81defcbed262
27
+ # via -r uv-requirements.in (contents of `uv==0.9.6`)
@@ -83,7 +83,7 @@ wheels = [
83
83
 
84
84
  [[package]]
85
85
  name = "awslabs-mysql-mcp-server"
86
- version = "1.0.8"
86
+ version = "1.0.9"
87
87
  source = { editable = "." }
88
88
  dependencies = [
89
89
  { name = "asyncmy" },
@@ -1,24 +0,0 @@
1
- # This file was autogenerated by uv via the following command:
2
- # echo "uv==0.8.10" > uv-requirements.in
3
- # uv pip compile --generate-hashes --output-file=uv-requirements.txt --strip-extras --python=3.10 uv-requirements.in
4
- uv==0.8.10 \
5
- --hash=sha256:31e4fc37ee94b94c032384a0957ad32ba7dce4ce6c04b4880fd3e31e25e51a82 \
6
- --hash=sha256:36a5ce708d52388c37043e7335f9eb3fea5a19a56166a2cc6adb365179a1cd77 \
7
- --hash=sha256:38286d230daad82388469c8dc7a1d2f5dc279c11178319c886d1a88d7938e513 \
8
- --hash=sha256:3e190cee3bb2b4f574a419eef87ae8e33f713e9cd6f856b83277ece70ad9ca9b \
9
- --hash=sha256:3fdf89fc40af9902141c39ed943bcfca15664623363335eb032a44f22001e2b4 \
10
- --hash=sha256:4cc190d403a89e46d13cec83b6f8e8d7d07aaf1e5a996eac9a3f0c2a8cd92537 \
11
- --hash=sha256:57b71dc79eff25a5419d3fe4a563d3b9397f55d789f685ef27f43f033b31f482 \
12
- --hash=sha256:86fe044c2be43977566a0d184a487edd7aace2febb757fd95927684b629ef50b \
13
- --hash=sha256:88df34c32555064fae459cce665757619fd1af7deb2dc393352b15d909d2d131 \
14
- --hash=sha256:9ad21eeaa4156a1bf5ed85903f80db06e2c02badd3a587ba98d3171517960555 \
15
- --hash=sha256:a5495b5a6e3111c03cf5e4dbdd598bc8fd1da887e3920d58cd5a2d4c8bc9a473 \
16
- --hash=sha256:ab072cd3bf2f9dc264659a1ff48ad91a910ac4830bcfe965e2d3f89c86646f46 \
17
- --hash=sha256:af8a5526b0e331775a264fa0dbccfd53c183cb974f269a208af136d7561f9eb2 \
18
- --hash=sha256:b00637c63d5dfc9f879281c5c91db2bb909ab1f9ab275dab015e7fb6cac6be5b \
19
- --hash=sha256:b3ff3c451fcd23ea78356d8c18e802d0e423cbe655273601e3ec039a51b33286 \
20
- --hash=sha256:c4a493cd4b15b3aef11523531aff96a77a586666a63e842fa437966b7b7ee62d \
21
- --hash=sha256:defc50bb319be2d58be74a680710cd4b7697e88d5f79974eacd354df95f0b6b0 \
22
- --hash=sha256:e0a02bcec766eb0862b7082ab746b204add7d9fcaa62322502d159b5a7ccc54a \
23
- --hash=sha256:eb79a46d8099f563ef58237bf4e9009f876a40145e757ea883a92b24b724d01e
24
- # via -r uv-requirements.in