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.
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/PKG-INFO +1 -1
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/__init__.py +1 -1
- {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
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/server.py +9 -1
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/pyproject.toml +1 -1
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_asyncmy_pool_connection.py +8 -8
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_server.py +42 -0
- awslabs_mysql_mcp_server-1.0.9/uv-requirements.txt +27 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/uv.lock +1 -1
- awslabs_mysql_mcp_server-1.0.8/uv-requirements.txt +0 -24
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/.gitignore +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/.python-version +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/CHANGELOG.md +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/Dockerfile +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/LICENSE +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/NOTICE +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/README.md +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/__init__.py +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/connection/__init__.py +0 -0
- {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
- {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
- {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
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/mutable_sql_detector.py +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/docker-healthcheck.sh +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/conftest.py +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_abstract_db_connection.py +0 -0
- {awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/tests/test_db_connection_singleton.py +0 -0
- {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.
|
|
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/
|
|
@@ -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 = [{'
|
|
149
|
+
column_metadata = [{'label': col} for col in columns]
|
|
150
150
|
records = []
|
|
151
151
|
|
|
152
152
|
# Convert each row to the expected format
|
{awslabs_mysql_mcp_server-1.0.8 → awslabs_mysql_mcp_server-1.0.9}/awslabs/mysql_mcp_server/server.py
RENAMED
|
@@ -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)
|
|
@@ -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'] == [{'
|
|
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
|
-
{'
|
|
404
|
-
{'
|
|
405
|
-
{'
|
|
406
|
-
{'
|
|
407
|
-
{'
|
|
408
|
-
{'
|
|
409
|
-
{'
|
|
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`)
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|