From 1a1f4cd6ead37f06adb7c5c64524b24039ffce76 Mon Sep 17 00:00:00 2001 From: alhendrickson <159636032+alhendrickson@users.noreply.github.com.> Date: Thu, 4 Dec 2025 16:47:18 +0000 Subject: [PATCH 1/3] feat(medcat-service): Run MCP server for medcat using Gradio --- .../medcat_service/demo/gradio_demo.py | 23 +++++++++++++++++-- medcat-service/medcat_service/main.py | 2 +- medcat-service/requirements.txt | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/medcat-service/medcat_service/demo/gradio_demo.py b/medcat-service/medcat_service/demo/gradio_demo.py index fbe7db96a..1fc0a50b1 100644 --- a/medcat-service/medcat_service/demo/gradio_demo.py +++ b/medcat-service/medcat_service/demo/gradio_demo.py @@ -95,7 +95,26 @@ def convert_display_model_to_list_of_lists(entity_display_model: list[EntityAnno return [[str(getattr(entity, field)) for field in entity.model_fields] for entity in entity_display_model] -def process_input(input_text: str): +def perform_named_entity_resolution(input_text: str): + """ + Performs clinical coding by processing the input text with MedCAT to extract and annotate medical concepts (entities). + + Returns: + 1. A dictionary following the NER response model (EntityResponse), containing the original text and the list of detected entities. + 2. A datatable-compatible list of lists, where each sublist represents an entity annotation and its attributes for display purposes. + + This method is used as the main function for the Gradio MedCAT demo and MCP server, enabling users to input free text and receive automatic annotation and coding of clinical entities. + + Args: + input_text (str): The input text to be processed and annotated for medical entities by MedCAT. + + Returns: + Tuple: + - dict: A dictionary following the NER response model (EntityResponse), containing the original text and the list of detected entities. + - list[list[str]]: A datatable-compatible list of lists, where each sublist represents an entity annotation and its attributes for display purposes. + + """ + processor = get_medcat_processor(get_settings()) input = ProcessAPIInputContent(text=input_text) @@ -136,7 +155,7 @@ def process_input(input_text: str): """ # noqa: E501 io = gr.Interface( - fn=process_input, + fn=perform_named_entity_resolution, inputs="text", outputs=[ gr.HighlightedText(label="Processed Text"), diff --git a/medcat-service/medcat_service/main.py b/medcat-service/medcat_service/main.py index 99e047a4b..a0d200e5e 100644 --- a/medcat-service/medcat_service/main.py +++ b/medcat-service/medcat_service/main.py @@ -35,7 +35,7 @@ app.include_router(health.router) app.include_router(process.router) -gr.mount_gradio_app(app, io, path="/demo") +gr.mount_gradio_app(app, io, path="/demo", mcp_server=True) def configure_observability(settings: Settings, app: FastAPI): diff --git a/medcat-service/requirements.txt b/medcat-service/requirements.txt index ee1681090..9efa96e98 100644 --- a/medcat-service/requirements.txt +++ b/medcat-service/requirements.txt @@ -9,5 +9,5 @@ requests==2.32.4 fastapi[standard]==0.115.2 pydantic==2.9.2 pydantic-settings==2.10.1 -gradio==5.38.0 +gradio[mcp]==5.38.0 prometheus-fastapi-instrumentator==7.1.0 From 44f4bce5fd1d51e2629e1cfec475d7d2f1efaac4 Mon Sep 17 00:00:00 2001 From: alhendrickson <159636032+alhendrickson@users.noreply.github.com.> Date: Thu, 4 Dec 2025 17:04:43 +0000 Subject: [PATCH 2/3] feat(medcat-service): Add MCP. Fix requirements.txt --- medcat-service/medcat_service/demo/gradio_demo.py | 8 +++----- medcat-service/requirements.txt | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/medcat-service/medcat_service/demo/gradio_demo.py b/medcat-service/medcat_service/demo/gradio_demo.py index 1fc0a50b1..8c5fcd5e8 100644 --- a/medcat-service/medcat_service/demo/gradio_demo.py +++ b/medcat-service/medcat_service/demo/gradio_demo.py @@ -1,5 +1,3 @@ -from typing import Dict, List - import gradio as gr from pydantic import BaseModel @@ -43,7 +41,7 @@ class EntityResponse(BaseModel): Expected data format of gradio highlightedtext component """ - entities: List[EntityAnnotation] + entities: list[EntityAnnotation] text: str @@ -75,7 +73,7 @@ def convert_annotation_to_display_model(entity: Entity) -> EntityAnnotationDispl ) -def convert_entity_dict_to_annotations(entity_dict_list: List[Dict[str, Entity]]) -> list[EntityAnnotation]: +def convert_entity_dict_to_annotations(entity_dict_list: list[dict[str, Entity]]) -> list[EntityAnnotation]: annotations: list[EntityAnnotation] = [] for entity_dict in entity_dict_list: for key, entity in entity_dict.items(): @@ -83,7 +81,7 @@ def convert_entity_dict_to_annotations(entity_dict_list: List[Dict[str, Entity]] return annotations -def convert_entity_dict_to_display_model(entity_dict_list: List[Dict[str, Entity]]) -> list[EntityAnnotationDisplay]: +def convert_entity_dict_to_display_model(entity_dict_list: list[dict[str, Entity]]) -> list[EntityAnnotationDisplay]: annotations: list[EntityAnnotationDisplay] = [] for entity_dict in entity_dict_list: for key, entity in entity_dict.items(): diff --git a/medcat-service/requirements.txt b/medcat-service/requirements.txt index 9efa96e98..dc8f5eca2 100644 --- a/medcat-service/requirements.txt +++ b/medcat-service/requirements.txt @@ -7,7 +7,7 @@ medcat[meta-cat,spacy,deid]~=2.2.0 transformers>=4.34.0,<5.0.0 requests==2.32.4 fastapi[standard]==0.115.2 -pydantic==2.9.2 +pydantic>=2.11.10,<2.12.5 pydantic-settings==2.10.1 gradio[mcp]==5.38.0 prometheus-fastapi-instrumentator==7.1.0 From 6226d8d1797561d2065c2c07769156fae7ca0b6d Mon Sep 17 00:00:00 2001 From: alhendrickson <159636032+alhendrickson@users.noreply.github.com.> Date: Thu, 4 Dec 2025 17:10:49 +0000 Subject: [PATCH 3/3] feat(medcat-service): Add MCP. Fix linting --- .../medcat_service/demo/gradio_demo.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/medcat-service/medcat_service/demo/gradio_demo.py b/medcat-service/medcat_service/demo/gradio_demo.py index 8c5fcd5e8..a19bbab92 100644 --- a/medcat-service/medcat_service/demo/gradio_demo.py +++ b/medcat-service/medcat_service/demo/gradio_demo.py @@ -95,21 +95,27 @@ def convert_display_model_to_list_of_lists(entity_display_model: list[EntityAnno def perform_named_entity_resolution(input_text: str): """ - Performs clinical coding by processing the input text with MedCAT to extract and annotate medical concepts (entities). + Performs clinical coding by processing the input text with MedCAT to extract and + annotate medical concepts (entities). Returns: - 1. A dictionary following the NER response model (EntityResponse), containing the original text and the list of detected entities. - 2. A datatable-compatible list of lists, where each sublist represents an entity annotation and its attributes for display purposes. + 1. A dictionary following the NER response model (EntityResponse), containing the original text + and the list of detected entities. + 2. A datatable-compatible list of lists, where each sublist represents an entity annotation and + its attributes for display purposes. - This method is used as the main function for the Gradio MedCAT demo and MCP server, enabling users to input free text and receive automatic annotation and coding of clinical entities. + This method is used as the main function for the Gradio MedCAT demo and MCP server, + enabling users to input free text and receive automatic annotation and coding of clinical entities. Args: input_text (str): The input text to be processed and annotated for medical entities by MedCAT. Returns: Tuple: - - dict: A dictionary following the NER response model (EntityResponse), containing the original text and the list of detected entities. - - list[list[str]]: A datatable-compatible list of lists, where each sublist represents an entity annotation and its attributes for display purposes. + - dict: A dictionary following the NER response model (EntityResponse), containing the + original text and the list of detected entities. + - list[list[str]]: A datatable-compatible list of lists, where each sublist represents an + entity annotation and its attributes for display purposes. """