mirror of
https://github.com/wso2/fhir-mcp-server.git
synced 2025-11-14 22:18:14 +03:00
feat: Add docker support
This commit is contained in:
40
.choreo/component.yaml
Normal file
40
.choreo/component.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com/) All Rights Reserved.
|
||||
#
|
||||
# WSO2 LLC. licenses this file to you under the Apache License,
|
||||
# Version 2.0 (the "License"); you may not use this file except
|
||||
# in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# +required The configuration file schema version
|
||||
schemaVersion: 1.2
|
||||
|
||||
# +optional Incoming connection details for the component
|
||||
endpoints:
|
||||
# +required Unique name for the endpoint.
|
||||
# This name will be used when generating the managed API
|
||||
- name: fhir_mcp_server
|
||||
# +optional Display name for the endpoint.
|
||||
displayName: FHIR MCP Server
|
||||
# +required Service section has the user service endpoint details
|
||||
service:
|
||||
# +optional Base path of the API that gets exposed via the endpoint.
|
||||
# This is mandatory if the endpoint type is set to REST or GraphQL.
|
||||
basePath: /
|
||||
# +required Numeric port value that gets exposed via the endpoint
|
||||
port: 8000
|
||||
# +required Type of traffic that the endpoint is accepting.
|
||||
# Allowed values: REST, GraphQL, GRPC, TCP, UDP.
|
||||
type: REST
|
||||
# +optional Network level visibilities of the endpoint.
|
||||
# Accepted values: Project|Organization|Public(Default).
|
||||
networkVisibilities:
|
||||
- Public
|
||||
@@ -1,14 +1,14 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Install uv (for fast dependency management)
|
||||
RUN pip install --upgrade pip && pip install uv
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
# Set workdir
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only requirements first for better caching
|
||||
COPY requirements.txt ./
|
||||
RUN uv pip sync --system requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Copy the rest of the code
|
||||
COPY . .
|
||||
@@ -22,6 +22,7 @@ EXPOSE 8000
|
||||
|
||||
# Set environment variables (can be overridden at runtime)
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONPATH=/app/src
|
||||
|
||||
# Default command to run the server (can be overridden)
|
||||
CMD ["uv", "run", "fhir-mcp-server", "--disable-mcp-auth"]
|
||||
CMD ["python", "-m", "fhir_mcp_server", "--disable-mcp-auth"]
|
||||
|
||||
@@ -10,9 +10,9 @@ classifiers = [
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
dependencies = [
|
||||
"mcp[cli]>=1.9.1",
|
||||
"aiohttp>=3.12.13",
|
||||
"fhirpy>=2.0.15",
|
||||
"mcp[cli]==1.9.1",
|
||||
"aiohttp==3.12.13",
|
||||
"fhirpy==2.0.15",
|
||||
]
|
||||
description = "A Model Context Protocol (MCP) server that provides seamless, standardized access to Fast Healthcare Interoperability Resources (FHIR) data from any compatible FHIR server. Designed for easy integration with AI tools, developer workflows, and healthcare applications, it enables natural language and programmatic search, retrieval, and analysis of clinical data."
|
||||
keywords = [
|
||||
@@ -28,7 +28,7 @@ license-files = ["LICEN[CS]E*"]
|
||||
name = "fhir-mcp-server"
|
||||
readme = {file = "README.md", content-type = "text/markdown"}
|
||||
requires-python = ">=3.12"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
|
||||
[build-system]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
@@ -140,11 +140,11 @@ class FHIRClientProvider(httpx.Auth):
|
||||
return
|
||||
|
||||
# Try refreshing existing token
|
||||
if await self._refresh_access_token(token_id):
|
||||
return
|
||||
# if await self._refresh_access_token(token_id):
|
||||
# return
|
||||
|
||||
# Fall back to full OAuth flow
|
||||
await self._perform_oauth_flow(token_id)
|
||||
# Fall back to full OAuth token flow
|
||||
await self._perform_token_flow(token_id)
|
||||
|
||||
async def _perform_oauth_flow(self, token_id: str) -> None:
|
||||
"""Execute OAuth2 authorization code flow with PKCE."""
|
||||
@@ -188,6 +188,30 @@ class FHIRClientProvider(httpx.Auth):
|
||||
# Redirect user for authorization
|
||||
await self.redirect_handler(auth_url)
|
||||
|
||||
async def _perform_token_flow(self, token_id: str) -> None:
|
||||
"""Execute OAuth2 client credentials flow."""
|
||||
logger.debug("Starting client credentials flow.")
|
||||
|
||||
access_token_payload: dict = {
|
||||
"grant_type": "client_credentials",
|
||||
"scope": self.configs.scope,
|
||||
"client_id": self.configs.client_id,
|
||||
"client_secret": self.configs.client_secret,
|
||||
"resource": "https://ohfhirrepositorypoc-ohfhirrepositorypoc.fhir.azurehealthcareapis.com",
|
||||
}
|
||||
|
||||
try:
|
||||
token: OAuthToken = await perform_token_flow(
|
||||
url=self._get_token_endpoint(),
|
||||
data=access_token_payload,
|
||||
timeout=self.configs.timeout,
|
||||
)
|
||||
|
||||
self.token_mapping[token_id] = token
|
||||
except Exception as ex:
|
||||
logger.exception("Access token request failed. Caused by, ", exc_info=ex)
|
||||
raise ValueError("Access token request failed")
|
||||
|
||||
async def handle_fhir_oauth_callback(self, code: str, state: str) -> None:
|
||||
|
||||
state_mapping: Dict[str, str] | None = self.state_mapping.get(state)
|
||||
@@ -235,7 +259,8 @@ class FHIRClientProvider(httpx.Auth):
|
||||
|
||||
def _get_token_endpoint(self) -> str:
|
||||
"""Get token endpoint."""
|
||||
return get_endpoint(self._metadata, "token_endpoint")
|
||||
return "https://login.microsoftonline.com/da76d684-740f-4d94-8717-9d5fb21dd1f9/oauth2/token"
|
||||
# return get_endpoint(self._metadata, "token_endpoint")
|
||||
|
||||
async def _refresh_access_token(self, token_id: str) -> None:
|
||||
"""Refresh access token using refresh token."""
|
||||
|
||||
@@ -501,7 +501,7 @@ def register_mcp_tools(mcp: FastMCP) -> None:
|
||||
bundle: dict = await client.resource(resource_type=type, id=id).execute(
|
||||
operation=operation or "",
|
||||
method="PUT",
|
||||
data={id: id, **payload},
|
||||
data={**payload, "id": id},
|
||||
params=searchParam,
|
||||
)
|
||||
return await get_bundle_entries(bundle=bundle)
|
||||
|
||||
Reference in New Issue
Block a user