@gera-services/mcp-gera-clinic 0.1.0
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/LICENSE +21 -0
- package/README.md +80 -0
- package/bin/cli.js +2 -0
- package/dist/index.js +28 -0
- package/dist/server.js +325 -0
- package/llms.txt +54 -0
- package/package.json +66 -0
- package/server.json +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gera Services Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# @gera-services/mcp-gera-clinic
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for [GeraClinic](https://geraclinic.com) -- the AI-powered telemedicine platform by [Gera Services](https://gera.services).
|
|
4
|
+
|
|
5
|
+
Enables AI assistants (Claude, ChatGPT, etc.) to search doctors, check availability, and book medical appointments across 50+ countries.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description | Auth Required |
|
|
10
|
+
|------|-------------|:---:|
|
|
11
|
+
| `search_doctors` | Search doctors by specialty, location, language, appointment type, and rating | No |
|
|
12
|
+
| `get_doctor_profile` | Get detailed doctor profile (bio, qualifications, fees, languages) | No |
|
|
13
|
+
| `get_available_slots` | Get open appointment time slots for a doctor on a given date | No |
|
|
14
|
+
| `list_specialties` | List all medical specialties available in a country | No |
|
|
15
|
+
| `get_featured_doctors` | Get top-rated, verified doctors | No |
|
|
16
|
+
| `get_health_services` | Get all health service types available in a country (telemedicine, pharmacy, labs, home nursing) | No |
|
|
17
|
+
| `book_appointment` | Book a video, in-person, or chat consultation | Yes |
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @gera-services/mcp-gera-clinic
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### With Claude Desktop
|
|
28
|
+
|
|
29
|
+
Add to your `claude_desktop_config.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"gera-clinic": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["@gera-services/mcp-gera-clinic"]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With a custom API endpoint
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"gera-clinic": {
|
|
48
|
+
"command": "npx",
|
|
49
|
+
"args": ["@gera-services/mcp-gera-clinic"],
|
|
50
|
+
"env": {
|
|
51
|
+
"GERACLINIC_API_URL": "https://your-api-url.example.com"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Standalone
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx @gera-services/mcp-gera-clinic
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
| Environment Variable | Description | Default |
|
|
67
|
+
|---------------------|-------------|---------|
|
|
68
|
+
| `GERACLINIC_API_URL` | Base URL for the GeraClinic API | `https://api.geraclinic.com` |
|
|
69
|
+
|
|
70
|
+
## Multi-Country Support
|
|
71
|
+
|
|
72
|
+
GeraClinic operates across 50+ countries. Pass a country code (ISO 3166-1 alpha-2) to any tool to scope results to that country. Supported countries include Georgia (GE), Uganda (UG), Ghana (GH), Kenya (KE), Nigeria (NG), Pakistan (PK), Bangladesh (BD), Colombia (CO), Peru (PE), Ecuador (EC), and many more.
|
|
73
|
+
|
|
74
|
+
## Authentication
|
|
75
|
+
|
|
76
|
+
Read-only tools (search, availability, specialties) work without authentication. Booking requires a user bearer token passed via the `auth_token` parameter.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
package/bin/cli.js
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
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
|
+
});
|
package/llms.txt
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# GeraClinic MCP Server
|
|
2
|
+
|
|
3
|
+
> AI-accessible telemedicine and healthcare API by Gera Services. Enables AI agents to search doctors, check availability, and book medical appointments across 50+ countries.
|
|
4
|
+
|
|
5
|
+
## What This MCP Server Does
|
|
6
|
+
|
|
7
|
+
The `@gera-services/mcp-gera-clinic` MCP server connects AI assistants (Claude, ChatGPT, Gemini, etc.) directly to [GeraClinic](https://geraclinic.com) — a worldwide telemedicine platform. Using this server, an AI agent can help a user find a doctor, check when they are available, and book a video, in-person, or chat consultation — without the user ever leaving their AI interface.
|
|
8
|
+
|
|
9
|
+
## Available Tools
|
|
10
|
+
|
|
11
|
+
### Read-Only (no authentication required)
|
|
12
|
+
|
|
13
|
+
- [search_doctors](https://geraclinic.com/find-doctor): Search verified doctors by specialty, country, language, appointment type (video/in-person/chat), and minimum rating. Supports pagination.
|
|
14
|
+
- [get_doctor_profile](https://geraclinic.com/doctors): Get a full doctor profile — bio, qualifications, languages spoken, consultation fees, and supported appointment types.
|
|
15
|
+
- [get_available_slots](https://geraclinic.com/book): Get open appointment time slots for a specific doctor on a given date.
|
|
16
|
+
- [list_specialties](https://geraclinic.com/specialties): List all medical specialties available in a country (general practice, cardiology, dermatology, etc.).
|
|
17
|
+
- [get_featured_doctors](https://geraclinic.com/featured): Get top-rated and verified doctors for a country.
|
|
18
|
+
- [get_health_services](https://geraclinic.com/services): Get all health service types available in a country — telemedicine, pharmacy, lab tests, home nursing.
|
|
19
|
+
|
|
20
|
+
### Authenticated (bearer token required)
|
|
21
|
+
|
|
22
|
+
- [book_appointment](https://geraclinic.com/book): Book a medical consultation. Returns confirmation with payment URL if applicable.
|
|
23
|
+
|
|
24
|
+
## Multi-Country Support
|
|
25
|
+
|
|
26
|
+
Pass any ISO 3166-1 alpha-2 country code (e.g., `GB`, `US`, `AM`, `GE`, `UG`, `KE`, `NG`) via the `country` parameter. Each country may have different specialties, pricing, and appointment types available.
|
|
27
|
+
|
|
28
|
+
## Installation & Integration
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx @gera-services/mcp-gera-clinic
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Claude Desktop config (`claude_desktop_config.json`):
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"gera-clinic": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["@gera-services/mcp-gera-clinic"],
|
|
41
|
+
"env": { "GERACLINIC_API_URL": "https://api.geraclinic.com" }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## About GeraClinic
|
|
48
|
+
|
|
49
|
+
GeraClinic is a product of [Gera Services](https://gera.services). It provides telemedicine services across 50+ countries, connecting patients with verified doctors for video, in-person, and chat consultations. Specialties include general practice, cardiology, dermatology, pediatrics, mental health, and more.
|
|
50
|
+
|
|
51
|
+
- Homepage: https://geraclinic.com
|
|
52
|
+
- API Docs: https://api.geraclinic.com/api/docs
|
|
53
|
+
- MCP Package: https://www.npmjs.com/package/@gera-services/mcp-gera-clinic
|
|
54
|
+
- MCP Registry: io.github.geraservicesuk/mcp-gera-clinic
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gera-services/mcp-gera-clinic",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for GeraClinic telemedicine platform — search doctors, check availability, and book medical appointments across 50+ countries",
|
|
5
|
+
"mcpName": "io.github.geraservicesuk/mcp-gera-clinic",
|
|
6
|
+
"main": "dist/server.js",
|
|
7
|
+
"types": "dist/server.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"mcp-gera-clinic": "bin/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"server.json",
|
|
17
|
+
"llms.txt"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "node build.mjs",
|
|
24
|
+
"type-check": "tsc --noEmit",
|
|
25
|
+
"dev": "tsc --watch --noCheck",
|
|
26
|
+
"start": "node dist/server.js",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"mcp",
|
|
31
|
+
"model-context-protocol",
|
|
32
|
+
"ai",
|
|
33
|
+
"healthcare",
|
|
34
|
+
"telemedicine",
|
|
35
|
+
"doctor-search",
|
|
36
|
+
"appointment-booking",
|
|
37
|
+
"gera-clinic",
|
|
38
|
+
"gera"
|
|
39
|
+
],
|
|
40
|
+
"author": {
|
|
41
|
+
"name": "Gera Services",
|
|
42
|
+
"email": "engineering@gera.services",
|
|
43
|
+
"url": "https://gera.services"
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/geraservicesuk/mcp-gera-clinic"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://geraclinic.com",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/geraservicesuk/mcp-gera-clinic/issues"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
59
|
+
"zod": "^3.23.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^20.12.0",
|
|
63
|
+
"esbuild": "^0.28.0",
|
|
64
|
+
"typescript": "^5.4.0"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.geraservicesuk/mcp-gera-clinic",
|
|
4
|
+
"description": "Search doctors, check availability, book medical appointments. 7 tools. 50+ countries.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/geraservicesuk/mcp-gera-clinic",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "@gera-services/mcp-gera-clinic",
|
|
14
|
+
"version": "0.1.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
},
|
|
18
|
+
"environmentVariables": [
|
|
19
|
+
{
|
|
20
|
+
"name": "GERACLINIC_API_URL",
|
|
21
|
+
"description": "Base URL for the GeraClinic API (defaults to https://api.geraclinic.com)",
|
|
22
|
+
"required": false
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|