Skip to content

Commit a735cb7

Browse files
authored
Moved alembic to inside mcpgateway and fixed wheel (#310)
* Moved alembic to inside mcpgateway and fixed wheel Signed-off-by: Madhav Kandukuri <[email protected]> * Ruff isort fixes Signed-off-by: Madhav Kandukuri <[email protected]> * Flake8 and black fixes Signed-off-by: Madhav Kandukuri <[email protected]> * Moved _pubsub to __init__ Signed-off-by: Madhav Kandukuri <[email protected]> * Update readme for alembic current Signed-off-by: Madhav Kandukuri <[email protected]>
1 parent a047923 commit a735cb7

12 files changed

+520
-205
lines changed
File renamed without changes.

alembic/README.md renamed to mcpgateway/alembic/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ alembic/
7070
### Other Common Commands
7171

7272
```bash
73-
alembic current # Show current DB revision
73+
alembic -c mcpgateway/alembic.ini current # Show current DB revision
7474
alembic history --verbose # Show all migrations and their order
7575
alembic downgrade -1 # Roll back one revision
7676
alembic downgrade <rev> # Roll back to a specific revision hash
@@ -158,7 +158,7 @@ make db-history
158158
| **`Can't locate revision ...`** | You deleted or renamed a revision file that the DB is pointing to. Either restore it or run `alembic stamp base` and recreate the revision. |
159159
| **`script.py.mako` missing** | This file is required. Run `alembic init alembic` in a temp folder and copy the missing template into your project. |
160160
| **SQLite foreign key limitations** | SQLite doesn't allow dropping constraints. Use `create table → copy → drop` flow manually, or plan around it. |
161-
| **DB not updating** | Did you forget to run `alembic upgrade head`? Check with `alembic current`. |
161+
| **DB not updating** | Did you forget to run `alembic upgrade head`? Check with `alembic -c mcpgateway/alembic.ini current`. |
162162
| **Wrong DB URL or config errors** | Confirm `settings.database_url` is valid. Check `env.py` and your `.env`/config settings. Alembic ignores `alembic.ini` for URLs in your setup. |
163163
| **Model changes not detected** | Alembic only picks up declarative models in `Base.metadata`. Ensure all models are imported and not behind `if TYPE_CHECKING:` or other lazy imports. |
164164

alembic/env.py renamed to mcpgateway/alembic/env.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
from logging.config import fileConfig
44

55
# Third-Party
6+
from alembic import context
67
from sqlalchemy import engine_from_config, pool
78

89
# First-Party
9-
from alembic import context
1010
from mcpgateway.config import settings
11+
from mcpgateway.db import Base
1112

1213
# from mcpgateway.db import get_metadata
1314
# target_metadata = get_metadata()
@@ -28,7 +29,6 @@
2829
# add your model's MetaData object here
2930
# for 'autogenerate' support
3031
# from myapp import mymodel
31-
from mcpgateway.db import Base
3232

3333
target_metadata = Base.metadata
3434

@@ -81,9 +81,7 @@ def run_migrations_online() -> None:
8181
)
8282

8383
with connectable.connect() as connection:
84-
context.configure(
85-
connection=connection, target_metadata=target_metadata
86-
)
84+
context.configure(connection=connection, target_metadata=target_metadata)
8785

8886
with context.begin_transaction():
8987
context.run_migrations()
File renamed without changes.

alembic/versions/b77ca9d2de7e_uuid_pk_and_slug_refactor.py renamed to mcpgateway/alembic/versions/b77ca9d2de7e_uuid_pk_and_slug_refactor.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
import uuid
1212

1313
# Third-Party
14+
from alembic import op
1415
import sqlalchemy as sa
1516
from sqlalchemy.orm import Session
1617

1718
# First-Party
18-
from alembic import op
1919
from mcpgateway.config import settings
2020
from mcpgateway.utils.create_slug import slugify
2121

2222
# revision identifiers, used by Alembic.
23-
revision: str = 'b77ca9d2de7e'
23+
revision: str = "b77ca9d2de7e"
2424
down_revision: Union[str, Sequence[str], None] = None
2525
branch_labels: Union[str, Sequence[str], None] = None
2626
depends_on: Union[str, Sequence[str], None] = None
@@ -70,7 +70,6 @@ def upgrade() -> None:
7070
op.add_column("server_resource_association", sa.Column("server_id_new", sa.String(36), nullable=True))
7171
op.add_column("server_prompt_association", sa.Column("server_id_new", sa.String(36), nullable=True))
7272

73-
7473
# ── STAGE 2: POPULATE THE NEW COLUMNS (DATA MIGRATION) ───────────────
7574
gateways = sess.execute(sa.select(sa.text("id, name")).select_from(sa.text("gateways"))).all()
7675
for gid, gname in gateways:
@@ -80,9 +79,7 @@ def upgrade() -> None:
8079
{"u": g_uuid, "s": slugify(gname), "i": gid},
8180
)
8281

83-
tools = sess.execute(
84-
sa.select(sa.text("id, name, gateway_id")).select_from(sa.text("tools"))
85-
).all()
82+
tools = sess.execute(sa.select(sa.text("id, name, gateway_id")).select_from(sa.text("tools"))).all()
8683
for tid, tname, g_old in tools:
8784
t_uuid = uuid.uuid4().hex
8885
tool_slug = slugify(tname)
@@ -102,8 +99,12 @@ def upgrade() -> None:
10299
"""
103100
),
104101
{
105-
"u": t_uuid, "on": tname, "ons": tool_slug,
106-
"sep": settings.gateway_tool_name_separator, "g": g_old, "i": tid,
102+
"u": t_uuid,
103+
"on": tname,
104+
"ons": tool_slug,
105+
"sep": settings.gateway_tool_name_separator,
106+
"g": g_old,
107+
"i": tid,
107108
},
108109
)
109110

@@ -123,7 +124,10 @@ def upgrade() -> None:
123124
sess.execute(sa.text("UPDATE prompts SET gateway_id_new=(SELECT id_new FROM gateways WHERE id=:g) WHERE id=:i"), {"g": g_old, "i": pid})
124125
sta = sess.execute(sa.select(sa.text("server_id, tool_id")).select_from(sa.text("server_tool_association"))).all()
125126
for s_old, t_old in sta:
126-
sess.execute(sa.text("UPDATE server_tool_association SET server_id_new=(SELECT id_new FROM servers WHERE id=:s), tool_id_new=(SELECT id_new FROM tools WHERE id=:t) WHERE server_id=:s AND tool_id=:t"), {"s": s_old, "t": t_old})
127+
sess.execute(
128+
sa.text("UPDATE server_tool_association SET server_id_new=(SELECT id_new FROM servers WHERE id=:s), tool_id_new=(SELECT id_new FROM tools WHERE id=:t) WHERE server_id=:s AND tool_id=:t"),
129+
{"s": s_old, "t": t_old},
130+
)
127131
tool_metrics = sess.execute(sa.select(sa.text("id, tool_id")).select_from(sa.text("tool_metrics"))).all()
128132
for tmid, t_old in tool_metrics:
129133
sess.execute(sa.text("UPDATE tool_metrics SET tool_id_new=(SELECT id_new FROM tools WHERE id=:t) WHERE id=:i"), {"t": t_old, "i": tmid})
@@ -220,6 +224,7 @@ def upgrade() -> None:
220224
with op.batch_alter_table("server_prompt_association") as batch_op:
221225
batch_op.create_foreign_key("fk_server_prompt_association_server_id", "servers", ["server_id"], ["id"])
222226

227+
223228
# def upgrade() -> None:
224229
# bind = op.get_bind()
225230
# sess = Session(bind=bind)

alembic/versions/e4fc04d1a442_add_annotations_to_tables.py renamed to mcpgateway/alembic/versions/e4fc04d1a442_add_annotations_to_tables.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,12 @@
1010
from typing import Sequence, Union
1111

1212
# Third-Party
13-
import sqlalchemy as sa
14-
from sqlalchemy.orm import Session
15-
16-
# First-Party
1713
from alembic import op
14+
import sqlalchemy as sa
1815

1916
# revision identifiers, used by Alembic.
20-
revision: str = 'e4fc04d1a442'
21-
down_revision: Union[str, Sequence[str], None] = 'b77ca9d2de7e'
17+
revision: str = "e4fc04d1a442"
18+
down_revision: Union[str, Sequence[str], None] = "b77ca9d2de7e"
2219
branch_labels: Union[str, Sequence[str], None] = None
2320
depends_on: Union[str, Sequence[str], None] = None
2421

@@ -32,14 +29,13 @@ def upgrade() -> None:
3229
that existing rows get a non-null default value.
3330
"""
3431
bind = op.get_bind()
35-
sess = Session(bind=bind)
3632
inspector = sa.inspect(bind)
3733

3834
if not inspector.has_table("gateways"):
3935
print("Fresh database detected. Skipping migration.")
4036
return
4137

42-
op.add_column('tools', sa.Column('annotations', sa.JSON(), server_default=sa.text("'{}'"), nullable=False))
38+
op.add_column("tools", sa.Column("annotations", sa.JSON(), server_default=sa.text("'{}'"), nullable=False))
4339

4440

4541
def downgrade() -> None:
@@ -50,11 +46,10 @@ def downgrade() -> None:
5046
'annotations' column from the 'tool' table.
5147
"""
5248
bind = op.get_bind()
53-
sess = Session(bind=bind)
5449
inspector = sa.inspect(bind)
5550

5651
if not inspector.has_table("gateways"):
5752
print("Fresh database detected. Skipping migration.")
5853
return
5954

60-
op.drop_column('tools', 'annotations')
55+
op.drop_column("tools", "annotations")

alembic/versions/e75490e949b1_add_improved_status_to_tables.py renamed to mcpgateway/alembic/versions/e75490e949b1_add_improved_status_to_tables.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
from typing import Sequence, Union
1111

1212
# Third-Party
13-
import sqlalchemy as sa
14-
15-
# First-Party
1613
# Alembic / SQLAlchemy
1714
from alembic import op
15+
import sqlalchemy as sa
1816

1917
# Revision identifiers.
2018
revision: str = "e75490e949b1"
@@ -28,20 +26,20 @@ def upgrade():
2826
Renames 'is_active' to 'enabled' and adds a new 'reachable' column (default True)
2927
in both 'tools' and 'gateways' tables.
3028
"""
31-
op.alter_column('tools', 'is_active', new_column_name='enabled')
32-
op.add_column('tools', sa.Column('reachable', sa.Boolean(), nullable=False, server_default=sa.true()))
29+
op.alter_column("tools", "is_active", new_column_name="enabled")
30+
op.add_column("tools", sa.Column("reachable", sa.Boolean(), nullable=False, server_default=sa.true()))
3331

34-
op.alter_column('gateways', 'is_active', new_column_name='enabled')
35-
op.add_column('gateways', sa.Column('reachable', sa.Boolean(), nullable=False, server_default=sa.true()))
32+
op.alter_column("gateways", "is_active", new_column_name="enabled")
33+
op.add_column("gateways", sa.Column("reachable", sa.Boolean(), nullable=False, server_default=sa.true()))
3634

3735

3836
def downgrade():
3937
"""
4038
Reverts the changes by renaming 'enabled' back to 'is_active'
4139
and dropping the 'reachable' column in both 'tools' and 'gateways' tables.
4240
"""
43-
op.alter_column('tools', 'enabled', new_column_name='is_active')
44-
op.drop_column('tools', 'reachable')
41+
op.alter_column("tools", "enabled", new_column_name="is_active")
42+
op.drop_column("tools", "reachable")
4543

46-
op.alter_column('gateways', 'enabled', new_column_name='is_active')
47-
op.drop_column('gateways', 'reachable')
44+
op.alter_column("gateways", "enabled", new_column_name="is_active")
45+
op.drop_column("gateways", "reachable")

mcpgateway/bootstrap_db.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121

2222
# Standard
2323
import asyncio
24+
from importlib.resources import files
2425
import logging
25-
from pathlib import Path
2626

2727
# Third-Party
28+
from alembic import command
29+
from alembic.config import Config
2830
from sqlalchemy import create_engine, inspect
2931

3032
# First-Party
31-
from alembic import command
32-
from alembic.config import Config
3333
from mcpgateway.config import settings
3434
from mcpgateway.db import Base
3535

@@ -47,9 +47,8 @@ async def main() -> None:
4747
None
4848
"""
4949
engine = create_engine(settings.database_url)
50-
project_root = Path(__file__).resolve().parents[1]
51-
ini_path = project_root / "alembic.ini"
52-
cfg = Config(ini_path) # path in container
50+
ini_path = files("mcpgateway").joinpath("alembic.ini")
51+
cfg = Config(str(ini_path)) # path in container
5352
cfg.attributes["configure_logger"] = False
5453

5554
command.ensure_version(cfg)

mcpgateway/cache/session_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def __init__(
9494
raise ValueError("Redis backend requires redis_url")
9595

9696
self._redis = Redis.from_url(redis_url)
97+
self._pubsub = self._redis.pubsub()
9798

9899
elif self._backend == "database":
99100
if not SQLALCHEMY_AVAILABLE:
@@ -151,7 +152,6 @@ async def initialize(self) -> None:
151152
logger.info("Database cleanup task started")
152153

153154
elif self._backend == "redis":
154-
self._pubsub = self._redis.pubsub()
155155
await self._pubsub.subscribe("mcp_session_events")
156156

157157
elif self._backend == "none":

mcpgateway/main.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,13 @@
145145
wait_for_db_ready(max_tries=int(settings.db_max_retries), interval=int(settings.db_retry_interval_ms) / 1000, sync=True) # Converting ms to s
146146

147147
# Create database tables
148-
asyncio.run(bootstrap_db())
148+
try:
149+
loop = asyncio.get_running_loop()
150+
except RuntimeError:
151+
asyncio.run(bootstrap_db())
152+
else:
153+
loop.create_task(bootstrap_db())
154+
149155

150156
# Initialize services
151157
tool_service = ToolService()

0 commit comments

Comments
 (0)