Skip to content

Commit 0f513e7

Browse files
committed
Merge branch 'release/6.3.0'
2 parents e9aa07b + bcbc05d commit 0f513e7

File tree

12 files changed

+690
-116
lines changed

12 files changed

+690
-116
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
## [Unreleased](https://github.com/dermatologist/pyomop/tree/HEAD)
44

5-
[Full Changelog](https://github.com/dermatologist/pyomop/compare/v6.1.0...HEAD)
5+
[Full Changelog](https://github.com/dermatologist/pyomop/compare/v6.2.3...HEAD)
66

77
**Implemented enhancements:**
88

9+
- Update MCP server [\#255](https://github.com/dermatologist/pyomop/issues/255)
910
- Add PyHealth compatible export function [\#243](https://github.com/dermatologist/pyomop/pull/243) ([Copilot](https://github.com/apps/copilot-swe-agent))
1011

1112
**Merged pull requests:**
1213

14+
- Add missing MCP tools and HTTP transport support [\#256](https://github.com/dermatologist/pyomop/pull/256) ([Copilot](https://github.com/apps/copilot-swe-agent))
1315
- Feature/fix thread 1 [\#254](https://github.com/dermatologist/pyomop/pull/254) ([dermatologist](https://github.com/dermatologist))
1416
- Fix LLM query engine bugs and switch to native llama-index embeddings [\#253](https://github.com/dermatologist/pyomop/pull/253) ([Copilot](https://github.com/apps/copilot-swe-agent))
1517
- \[GitHub Dependents Info\] Updated markdown file [\#252](https://github.com/dermatologist/pyomop/pull/252) ([github-actions[bot]](https://github.com/apps/github-actions))
@@ -47,7 +49,10 @@
4749
- build\(deps\): bump urllib3 from 1.26.18 to 1.26.19 [\#167](https://github.com/dermatologist/pyomop/pull/167) ([dependabot[bot]](https://github.com/apps/dependabot))
4850
- build\(deps\): bump jinja2 from 3.0.1 to 3.1.3 [\#161](https://github.com/dermatologist/pyomop/pull/161) ([dependabot[bot]](https://github.com/apps/dependabot))
4951
- Feature/llm llama index 1 [\#159](https://github.com/dermatologist/pyomop/pull/159) ([dermatologist](https://github.com/dermatologist))
50-
- build\(deps\): bump actions/setup-python from 2.3.1 to 5.0.0 [\#155](https://github.com/dermatologist/pyomop/pull/155) ([dependabot[bot]](https://github.com/apps/dependabot))
52+
53+
## [v6.2.3](https://github.com/dermatologist/pyomop/tree/v6.2.3) (2025-12-24)
54+
55+
[Full Changelog](https://github.com/dermatologist/pyomop/compare/v6.1.0...v6.2.3)
5156

5257
## [v6.1.0](https://github.com/dermatologist/pyomop/tree/v6.1.0) (2025-09-29)
5358

README.md

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -202,19 +202,9 @@ pyomop includes an MCP (Model Context Protocol) server that exposes tools for in
202202
<img src="https://github.com/dermatologist/pyomop/blob/develop/notes/pyomop-mcp.gif" />
203203
</p>
204204

205-
### Starting the MCP Server
206-
207-
To start the MCP server for stdio interaction:
208-
209-
```bash
210-
# Using the main CLI
211-
pyomop --mcp-server
212-
213-
```
214-
215205
#### Usage with MCP Clients
216206

217-
The server communicates via stdio and can be used with any MCP-compatible client. Example configuration for [vscode](/.vscode/mcp.json):
207+
The MCP server can be used with any MCP-compatible client such as Claude desktop. Example configuration for [VSCODE](/.vscode/mcp.json) as below is already provided in the repository. So if you are viewing this in VSCODE, you can start server and enable tools directly in Copilot.
218208

219209
```json
220210
{
@@ -237,8 +227,38 @@ The server communicates via stdio and can be used with any MCP-compatible client
237227
- **get_single_table_info**: Get detailed table information, including foreign keys
238228
- **get_usable_table_names**: Get a list of all available table names
239229
- **run_sql**: Execute SQL statements with error handling
230+
- **example_query**: Get example queries for specific OMOP CDM tables from OHDSI QueryLibrary
231+
- **check_sql**: Validate SQL query syntax before execution
240232

241233
* create_cdm and create_eunomia support only local sqlite databases to avoid inadvertent data loss in production databases.
234+
235+
#### HTTP Transport Support
236+
237+
The MCP server now supports both stdio (default) and HTTP transports:
238+
239+
**Stdio transport (default):**
240+
```bash
241+
pyomop --mcp-server
242+
# or
243+
pyomop-mcp-server
244+
```
245+
246+
**HTTP transport:**
247+
```bash
248+
pyomop-mcp-server-http
249+
# or with custom host/port
250+
pyomop-mcp-server-http --host 0.0.0.0 --port 8000
251+
# or via Python module
252+
python -m pyomop.mcp.server --http --host 0.0.0.0 --port 8000
253+
```
254+
255+
To use HTTP transport, install additional dependencies:
256+
```bash
257+
pip install pyomop[http]
258+
# or for both LLM and HTTP features
259+
pip install pyomop[llm,http]
260+
```
261+
242262
#### Available Prompts
243263

244264
- **query_execution_steps**: Provides step-by-step guidance for executing database queries based on free text instructions

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ source-exclude = ["tests/*", "examples/*", "docs/*", "notes/*", "notebooks/*"]
7474
[project.scripts]
7575
pyomop = "pyomop.main:main_routine"
7676
pyomop-mcp-server = "pyomop.mcp.server:main_cli"
77+
pyomop-mcp-server-http = "pyomop.mcp.server:main_http_cli"
7778

7879
[project.optional-dependencies]
7980
llm = [
@@ -85,6 +86,10 @@ llm = [
8586
"langchain-openai",
8687
"dhti-elixir-base",
8788
]
89+
http = [
90+
"starlette",
91+
"uvicorn",
92+
]
8893

8994
[tool.hatch.build.targets.wheel]
9095
packages = ["src/pyomop"]

src/pyomop/engine_factory.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,16 @@ def async_session(self):
192192
return async_session
193193
return None
194194

195+
async def dispose(self) -> None:
196+
"""Dispose of the engine and close all connections.
197+
198+
This should be called when done with database operations to
199+
ensure proper cleanup and avoid hanging.
200+
"""
201+
if self._engine is not None:
202+
await self._engine.dispose()
203+
self._engine = None
204+
195205
@db.setter
196206
def db(self, value):
197207
self._db = value

src/pyomop/main.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"""
66

77
import asyncio
8+
89
import click
10+
911
from . import __version__
1012
from .engine_factory import CdmEngineFactory
1113
from .vocabulary import CdmVocabulary
@@ -105,23 +107,29 @@ def cli(
105107
if create:
106108
click.echo(f"Creating CDM {version} tables in {dbtype} database {name}")
107109
cdm = CdmEngineFactory(dbtype, host, port, user, pw, name, schema)
108-
# initialize default engine
109-
engine = cdm.engine
110-
if version == "cdm54":
111-
from .cdm54 import Base
110+
try:
111+
# initialize default engine
112+
engine = cdm.engine
113+
if version == "cdm54":
114+
from .cdm54 import Base
112115

113-
asyncio.run(cdm.init_models(Base.metadata))
114-
else: # default cdm6
115-
from .cdm6 import Base
116+
asyncio.run(cdm.init_models(Base.metadata))
117+
else: # default cdm6
118+
from .cdm6 import Base
116119

117-
asyncio.run(cdm.init_models(Base.metadata))
118-
click.echo("Done")
120+
asyncio.run(cdm.init_models(Base.metadata))
121+
click.echo("Done")
122+
finally:
123+
asyncio.run(cdm.dispose())
119124
if vocab != "":
120125
click.echo(f"Creating CDM {version} vocabulary in {dbtype} database {name}")
121126
cdm = CdmEngineFactory(dbtype, host, port, user, pw, name, schema)
122-
_vocab = CdmVocabulary(cdm)
123-
asyncio.run(_vocab.create_vocab(vocab))
124-
click.echo("Done")
127+
try:
128+
_vocab = CdmVocabulary(cdm)
129+
asyncio.run(_vocab.create_vocab(vocab))
130+
click.echo("Done")
131+
finally:
132+
asyncio.run(cdm.dispose())
125133

126134
if input_path:
127135
click.echo(f"Loading FHIR data from {input_path} into {dbtype} database {name}")
@@ -165,13 +173,16 @@ def cli(
165173
click.echo(f"Data written to temporary file: {temp_file.name}")
166174
# Load CSV into OMOP tables using mapping
167175
loader = CdmCsvLoader(cdm)
168-
asyncio.run(
169-
loader.load(
170-
csv_path=temp_file.name,
171-
chunk_size=500,
176+
try:
177+
asyncio.run(
178+
loader.load(
179+
csv_path=temp_file.name,
180+
chunk_size=500,
181+
)
172182
)
173-
)
174-
click.echo("Done")
183+
click.echo("Done")
184+
finally:
185+
asyncio.run(cdm.dispose())
175186

176187
if eunomia_dataset:
177188
click.echo(
@@ -199,7 +210,7 @@ def cli(
199210
dataset_name=eunomia_dataset,
200211
cdm_version=version,
201212
input_format="csv",
202-
verbose=True
213+
verbose=True,
203214
)
204215
)
205216
click.echo(f"Loaded dataset into configured database: {name}")
@@ -214,19 +225,26 @@ def cli(
214225
except Exception as e:
215226
click.echo(f"Error creating/populating cohort table: {e}", err=True)
216227
raise
228+
finally:
229+
asyncio.run(cdm.dispose())
217230
click.echo("Done")
218231
if cdm and connection_info:
219232
click.echo(click.style("Database connection information:", fg="green"))
220233
click.echo(cdm.print_connection_info())
221234

222235
if mcp_server:
223236
import sys
237+
224238
click.echo("Starting pyomop MCP server...")
225239
try:
226240
from .mcp import mcp_server_main
241+
227242
asyncio.run(mcp_server_main())
228243
except ImportError:
229-
click.echo("MCP server requires 'mcp' package. Install with: pip install mcp", err=True)
244+
click.echo(
245+
"MCP server requires 'mcp' package. Install with: pip install mcp",
246+
err=True,
247+
)
230248
sys.exit(1)
231249
except KeyboardInterrupt:
232250
click.echo("MCP server stopped.")
@@ -236,8 +254,12 @@ def cli(
236254

237255
if pyhealth_path:
238256
import sys
239-
click.echo(f"Exporting PyHealth tables to {pyhealth_path} from {dbtype} database {name}")
257+
258+
click.echo(
259+
f"Exporting PyHealth tables to {pyhealth_path} from {dbtype} database {name}"
260+
)
240261
from .pyhealth import PyHealthExport
262+
241263
cdm = CdmEngineFactory(dbtype, host, port, user, pw, name, schema)
242264
exporter = PyHealthExport(cdm, export_path=pyhealth_path)
243265
try:
@@ -246,6 +268,9 @@ def cli(
246268
except Exception as e:
247269
click.echo(f"Error exporting PyHealth tables: {e}", err=True)
248270
sys.exit(1)
271+
finally:
272+
asyncio.run(cdm.dispose())
273+
249274

250275
def main_routine():
251276
"""Top-level runner used by ``python -m pyomop``."""

0 commit comments

Comments
 (0)