@gera-services/mcp-gera-clinic 0.1.0 → 0.1.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gera-services/mcp-gera-clinic",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "MCP server for GeraClinic telemedicine platform — search doctors, check availability, and book medical appointments across 50+ countries",
5
5
  "mcpName": "io.github.geraservicesuk/mcp-gera-clinic",
6
6
  "main": "dist/server.js",
package/server.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.geraservicesuk/mcp-gera-clinic",
4
- "description": "Search doctors, check availability, book medical appointments. 7 tools. 50+ countries.",
4
+ "description": "Telemedicine MCP server for GeraClinic. Search doctors by specialty and language, check real-time availability, book and reschedule video consultations, run symptom triage, request prescriptions, and manage patient records. Works in 50+ countries with support for Armenian, Georgian, Azerbaijani, Russian, Swahili, and English. 7 tools: search_doctors, get_doctor, check_availability, book_appointment, cancel_appointment, run_symptom_triage, list_specialties. Public read; booking requires OAuth.",
5
5
  "repository": {
6
6
  "url": "https://github.com/geraservicesuk/mcp-gera-clinic",
7
7
  "source": "github"
package/dist/index.js DELETED
@@ -1,28 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var index_exports = {};
20
- __export(index_exports, {
21
- server: () => import_server.server
22
- });
23
- module.exports = __toCommonJS(index_exports);
24
- var import_server = require("./server.js");
25
- // Annotate the CommonJS export names for ESM import in node:
26
- 0 && (module.exports = {
27
- server
28
- });
package/dist/server.js DELETED
@@ -1,325 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var server_exports = {};
20
- __export(server_exports, {
21
- server: () => server
22
- });
23
- module.exports = __toCommonJS(server_exports);
24
- var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
25
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
26
- var import_zod = require("zod");
27
- const API_BASE = process.env.GERACLINIC_API_URL || "https://api.geraclinic.com";
28
- async function apiCall(path, options = {}) {
29
- const res = await fetch(`${API_BASE}${path}`, {
30
- ...options,
31
- headers: {
32
- "Content-Type": "application/json",
33
- "X-MCP-Client": "mcp-gera-clinic/1.0.0",
34
- ...options.headers
35
- }
36
- });
37
- if (!res.ok) {
38
- throw new Error(`API error ${res.status}: ${await res.text()}`);
39
- }
40
- return res.json();
41
- }
42
- const server = new import_mcp.McpServer({
43
- name: "gera-clinic",
44
- version: "1.0.0"
45
- });
46
- server.tool(
47
- "search_doctors",
48
- "Search for doctors by specialty, location, and availability. Returns a list of verified doctors with their ratings, languages, consultation fees, and appointment types (video, in-person, chat).",
49
- {
50
- specialty: import_zod.z.string().optional().describe('Medical specialty (e.g., "cardiology", "dermatology", "general-practice")'),
51
- country: import_zod.z.string().optional().describe('ISO 3166-1 alpha-2 country code (e.g., "GE", "UG", "GH")'),
52
- language: import_zod.z.string().optional().describe('Preferred language (e.g., "en", "ka", "fr")'),
53
- appointment_type: import_zod.z.enum(["VIDEO", "IN_PERSON", "CHAT"]).optional().describe("Type of consultation"),
54
- min_rating: import_zod.z.number().min(0).max(5).optional().describe("Minimum doctor rating (0-5)"),
55
- page: import_zod.z.number().int().positive().optional().describe("Page number for pagination"),
56
- limit: import_zod.z.number().int().min(1).max(50).optional().describe("Results per page (max 50)")
57
- },
58
- async (params) => {
59
- const query = new URLSearchParams();
60
- if (params.specialty) query.set("specialty", params.specialty);
61
- if (params.language) query.set("language", params.language);
62
- if (params.appointment_type) query.set("appointment_type", params.appointment_type);
63
- if (params.min_rating !== void 0) query.set("min_rating", String(params.min_rating));
64
- if (params.page) query.set("page", String(params.page));
65
- if (params.limit) query.set("limit", String(params.limit));
66
- const headers = {};
67
- if (params.country) headers["X-Tenant-ID"] = params.country;
68
- const result = await apiCall(`/doctors?${query}`, { headers });
69
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
70
- }
71
- );
72
- server.tool(
73
- "get_doctor_profile",
74
- "Get detailed profile for a specific doctor including bio, qualifications, languages, consultation fees, and supported appointment types.",
75
- {
76
- doctor_id: import_zod.z.string().uuid().describe("Doctor UUID"),
77
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code")
78
- },
79
- async (params) => {
80
- const headers = {};
81
- if (params.country) headers["X-Tenant-ID"] = params.country;
82
- const result = await apiCall(`/doctors/${params.doctor_id}`, { headers });
83
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
84
- }
85
- );
86
- server.tool(
87
- "get_available_slots",
88
- "Get available appointment time slots for a specific doctor on a given date. Returns a list of open slots with start/end times.",
89
- {
90
- doctor_id: import_zod.z.string().uuid().describe("Doctor UUID"),
91
- date: import_zod.z.string().describe("Date in ISO format YYYY-MM-DD"),
92
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code")
93
- },
94
- async (params) => {
95
- const headers = {};
96
- if (params.country) headers["X-Tenant-ID"] = params.country;
97
- const result = await apiCall(`/doctors/${params.doctor_id}/slots?date=${params.date}`, { headers });
98
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
99
- }
100
- );
101
- server.tool(
102
- "list_specialties",
103
- "List all medical specialties available in a given country. Useful for discovering what types of doctors are available before searching.",
104
- {
105
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code")
106
- },
107
- async (params) => {
108
- const headers = {};
109
- if (params.country) headers["X-Tenant-ID"] = params.country;
110
- const result = await apiCall("/doctors/specialties", { headers });
111
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
112
- }
113
- );
114
- server.tool(
115
- "get_featured_doctors",
116
- "Get top-rated, verified doctors. Useful for recommending the best available doctors to users.",
117
- {
118
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
119
- limit: import_zod.z.number().int().min(1).max(20).optional().describe("Number of featured doctors to return")
120
- },
121
- async (params) => {
122
- const headers = {};
123
- if (params.country) headers["X-Tenant-ID"] = params.country;
124
- const query = params.limit ? `?limit=${params.limit}` : "";
125
- const result = await apiCall(`/doctors/featured${query}`, { headers });
126
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
127
- }
128
- );
129
- server.tool(
130
- "get_health_services",
131
- "Get all health service types available in a specific country on GeraClinic. Returns services such as telemedicine video consultations, in-person appointments, pharmacy delivery, lab test booking, and home nursing.",
132
- {
133
- country: import_zod.z.string().describe('ISO 3166-1 alpha-2 country code (e.g., "GB", "US", "GE", "KE")')
134
- },
135
- async (params) => {
136
- const headers = {
137
- "X-Tenant-ID": params.country
138
- };
139
- const result = await apiCall("/health-services", { headers });
140
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
141
- }
142
- );
143
- server.tool(
144
- "book_appointment",
145
- "Book a medical appointment with a doctor. Requires authentication. Returns booking confirmation with payment URL if applicable.",
146
- {
147
- doctor_id: import_zod.z.string().uuid().describe("Doctor UUID"),
148
- date: import_zod.z.string().describe("Appointment date in ISO format YYYY-MM-DD"),
149
- start_time: import_zod.z.string().describe("Start time in HH:MM format"),
150
- end_time: import_zod.z.string().describe("End time in HH:MM format"),
151
- appointment_type: import_zod.z.enum(["VIDEO", "IN_PERSON", "CHAT"]).describe("Type of consultation"),
152
- reason: import_zod.z.string().optional().describe("Reason for visit / symptoms"),
153
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
154
- auth_token: import_zod.z.string().describe("User bearer token for authentication")
155
- },
156
- async (params) => {
157
- const headers = {
158
- Authorization: `Bearer ${params.auth_token}`
159
- };
160
- if (params.country) headers["X-Tenant-ID"] = params.country;
161
- const result = await apiCall("/appointments", {
162
- method: "POST",
163
- headers,
164
- body: JSON.stringify({
165
- doctor_id: params.doctor_id,
166
- date: params.date,
167
- start_time: params.start_time,
168
- end_time: params.end_time,
169
- type: params.appointment_type,
170
- reason: params.reason
171
- })
172
- });
173
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
174
- }
175
- );
176
- server.tool(
177
- "get_my_appointments",
178
- "List upcoming and past appointments for the authenticated patient. Returns appointments with doctor info, date/time, status, and consultation type. Use this to check a patient's appointment history or upcoming schedule.",
179
- {
180
- status: import_zod.z.enum(["upcoming", "completed", "cancelled"]).optional().describe("Filter by appointment status. Omit to return all."),
181
- from_date: import_zod.z.string().optional().describe("Filter appointments from this date (ISO format YYYY-MM-DD)"),
182
- to_date: import_zod.z.string().optional().describe("Filter appointments up to this date (ISO format YYYY-MM-DD)"),
183
- page: import_zod.z.number().int().positive().optional().describe("Page number for pagination"),
184
- limit: import_zod.z.number().int().min(1).max(50).optional().describe("Results per page (max 50)"),
185
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
186
- auth_token: import_zod.z.string().describe("User bearer token for authentication")
187
- },
188
- async (params) => {
189
- const headers = {
190
- Authorization: `Bearer ${params.auth_token}`
191
- };
192
- if (params.country) headers["X-Tenant-ID"] = params.country;
193
- const query = new URLSearchParams();
194
- if (params.status) query.set("status", params.status);
195
- if (params.from_date) query.set("from_date", params.from_date);
196
- if (params.to_date) query.set("to_date", params.to_date);
197
- if (params.page) query.set("page", String(params.page));
198
- if (params.limit) query.set("limit", String(params.limit));
199
- const result = await apiCall(`/appointments?${query}`, { headers });
200
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
201
- }
202
- );
203
- server.tool(
204
- "cancel_appointment",
205
- "Cancel an existing appointment. Requires authentication. Returns cancellation confirmation and refund details if applicable. Must be called at least 1 hour before the scheduled appointment time.",
206
- {
207
- appointment_id: import_zod.z.string().uuid().describe("Appointment UUID to cancel"),
208
- reason: import_zod.z.string().optional().describe("Reason for cancellation (helps improve the platform)"),
209
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
210
- auth_token: import_zod.z.string().describe("User bearer token for authentication")
211
- },
212
- async (params) => {
213
- const headers = {
214
- Authorization: `Bearer ${params.auth_token}`
215
- };
216
- if (params.country) headers["X-Tenant-ID"] = params.country;
217
- const result = await apiCall(`/appointments/${params.appointment_id}/cancel`, {
218
- method: "POST",
219
- headers,
220
- body: JSON.stringify({ reason: params.reason })
221
- });
222
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
223
- }
224
- );
225
- server.tool(
226
- "get_lab_results",
227
- "Retrieve lab test results for the authenticated patient. Returns digitally signed diagnostic reports including blood work, imaging, and other diagnostic results. Results are returned as structured data with normal ranges and doctor notes.",
228
- {
229
- result_id: import_zod.z.string().uuid().optional().describe("Specific lab result UUID. Omit to list all results."),
230
- from_date: import_zod.z.string().optional().describe("Filter results from this date (ISO format YYYY-MM-DD)"),
231
- to_date: import_zod.z.string().optional().describe("Filter results up to this date (ISO format YYYY-MM-DD)"),
232
- test_type: import_zod.z.string().optional().describe('Filter by test type (e.g., "blood-panel", "urine", "imaging", "ecg")'),
233
- page: import_zod.z.number().int().positive().optional().describe("Page number for pagination (when listing all results)"),
234
- limit: import_zod.z.number().int().min(1).max(50).optional().describe("Results per page (max 50)"),
235
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
236
- auth_token: import_zod.z.string().describe("User bearer token for authentication")
237
- },
238
- async (params) => {
239
- const headers = {
240
- Authorization: `Bearer ${params.auth_token}`
241
- };
242
- if (params.country) headers["X-Tenant-ID"] = params.country;
243
- if (params.result_id) {
244
- const result2 = await apiCall(`/lab-results/${params.result_id}`, { headers });
245
- return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
246
- }
247
- const query = new URLSearchParams();
248
- if (params.from_date) query.set("from_date", params.from_date);
249
- if (params.to_date) query.set("to_date", params.to_date);
250
- if (params.test_type) query.set("test_type", params.test_type);
251
- if (params.page) query.set("page", String(params.page));
252
- if (params.limit) query.set("limit", String(params.limit));
253
- const result = await apiCall(`/lab-results?${query}`, { headers });
254
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
255
- }
256
- );
257
- server.tool(
258
- "order_medication",
259
- "Order medication from a pharmacy via GeraClinic. Can be linked to a doctor's prescription or ordered as OTC (over-the-counter). Requires authentication. Returns order confirmation with estimated delivery time and pharmacy details.",
260
- {
261
- prescription_id: import_zod.z.string().uuid().optional().describe("Prescription UUID from a GeraClinic doctor \u2014 required for prescription-only medications"),
262
- items: import_zod.z.array(import_zod.z.object({
263
- medication_name: import_zod.z.string().describe('Medication name (e.g., "Paracetamol 500mg", "Amoxicillin 250mg")'),
264
- quantity: import_zod.z.number().int().positive().describe("Number of units/packs to order"),
265
- notes: import_zod.z.string().optional().describe('Additional instructions (e.g., "brand preferred: X")')
266
- })).min(1).describe("List of medications to order"),
267
- delivery_address: import_zod.z.string().describe("Full delivery address for the medication"),
268
- pharmacy_id: import_zod.z.string().uuid().optional().describe("Preferred pharmacy UUID. If omitted, the nearest available pharmacy is selected automatically."),
269
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
270
- auth_token: import_zod.z.string().describe("User bearer token for authentication")
271
- },
272
- async (params) => {
273
- const headers = {
274
- Authorization: `Bearer ${params.auth_token}`
275
- };
276
- if (params.country) headers["X-Tenant-ID"] = params.country;
277
- const result = await apiCall("/pharmacy/orders", {
278
- method: "POST",
279
- headers,
280
- body: JSON.stringify({
281
- prescription_id: params.prescription_id,
282
- items: params.items,
283
- delivery_address: params.delivery_address,
284
- pharmacy_id: params.pharmacy_id
285
- })
286
- });
287
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
288
- }
289
- );
290
- server.tool(
291
- "search_pharmacies",
292
- "Find pharmacies near a location that deliver medications via GeraClinic. Returns pharmacies with their operating hours, delivery radius, and available payment methods.",
293
- {
294
- lat: import_zod.z.number().optional().describe("Latitude for proximity search"),
295
- lng: import_zod.z.number().optional().describe("Longitude for proximity search"),
296
- city: import_zod.z.string().optional().describe("City name to search within"),
297
- country: import_zod.z.string().optional().describe("ISO 3166-1 alpha-2 country code"),
298
- open_now: import_zod.z.boolean().optional().describe("Filter to only show pharmacies currently open"),
299
- page: import_zod.z.number().int().positive().optional().describe("Page number"),
300
- limit: import_zod.z.number().int().min(1).max(50).optional().describe("Results per page (max 50)")
301
- },
302
- async (params) => {
303
- const headers = {};
304
- if (params.country) headers["X-Tenant-ID"] = params.country;
305
- const query = new URLSearchParams();
306
- if (params.lat !== void 0) query.set("lat", String(params.lat));
307
- if (params.lng !== void 0) query.set("lng", String(params.lng));
308
- if (params.city) query.set("city", params.city);
309
- if (params.open_now !== void 0) query.set("open_now", String(params.open_now));
310
- if (params.page) query.set("page", String(params.page));
311
- if (params.limit) query.set("limit", String(params.limit));
312
- const result = await apiCall(`/pharmacies?${query}`, { headers });
313
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
314
- }
315
- );
316
- async function main() {
317
- const transport = new import_stdio.StdioServerTransport();
318
- await server.connect(transport);
319
- console.error("GeraClinic MCP server running on stdio");
320
- }
321
- main().catch(console.error);
322
- // Annotate the CommonJS export names for ESM import in node:
323
- 0 && (module.exports = {
324
- server
325
- });