@gera-services/mcp-gera-clinic 0.1.0 → 1.0.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/README.md +70 -37
- package/bin/cli.js +11 -1
- package/dist/calculators.d.ts +108 -0
- package/dist/calculators.js +227 -0
- package/dist/cqc.d.ts +102 -0
- package/dist/cqc.js +102 -0
- package/dist/data/cqc-cluster.json +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -28
- package/dist/server.d.ts +17 -0
- package/dist/server.js +248 -316
- package/llms.txt +13 -22
- package/package.json +21 -20
- package/server.json +8 -13
package/dist/cqc.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real Care Quality Commission (CQC) provider dataset, bundled as JSON.
|
|
3
|
+
*
|
|
4
|
+
* Source: Care Quality Commission www.cqc.org.uk, crawled into GeraClinic's
|
|
5
|
+
* `crawled_listings` (vertical='geraclinic', country_code='GB') and snapshotted
|
|
6
|
+
* from apps/gera-clinic-web/src/data/cqc-cluster.generated.ts. Every record
|
|
7
|
+
* traces to a real registered provider. Public sector information licensed
|
|
8
|
+
* under the Open Government Licence v3.0.
|
|
9
|
+
*
|
|
10
|
+
* CQC publishes a CATEGORICAL rating scheme
|
|
11
|
+
* (Outstanding / Good / Requires improvement / Inadequate), never a 0–5 numeric
|
|
12
|
+
* scale, so this dataset carries NO numeric ratings. The `lastInspected` date
|
|
13
|
+
* is the real last-checked date for the provider record.
|
|
14
|
+
*/
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
// JSON is bundled into dist via the package "files" list; resolve relative to
|
|
18
|
+
// this module so it works whether running from src (tsx) or dist (node).
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
20
|
+
const dataset = require('./data/cqc-cluster.json');
|
|
21
|
+
export const CQC_META = dataset.meta;
|
|
22
|
+
export const CQC_AUTHORITIES = dataset.authorities;
|
|
23
|
+
export const CQC_ATTRIBUTION = CQC_META.attribution;
|
|
24
|
+
/** Every distinct provider record across authorities + their localities. */
|
|
25
|
+
function buildProviderIndex() {
|
|
26
|
+
const byId = new Map();
|
|
27
|
+
for (const auth of CQC_AUTHORITIES) {
|
|
28
|
+
for (const p of auth.providers)
|
|
29
|
+
byId.set(p.cqcLocationId, p);
|
|
30
|
+
for (const loc of auth.localities) {
|
|
31
|
+
for (const p of loc.providers)
|
|
32
|
+
byId.set(p.cqcLocationId, p);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return [...byId.values()];
|
|
36
|
+
}
|
|
37
|
+
export const ALL_PROVIDERS = buildProviderIndex();
|
|
38
|
+
/** All distinct service-type labels present in the dataset, sorted. */
|
|
39
|
+
export const SERVICE_TYPES = [
|
|
40
|
+
...new Set(ALL_PROVIDERS.flatMap((p) => p.serviceTypes)),
|
|
41
|
+
].sort((a, b) => a.localeCompare(b));
|
|
42
|
+
function norm(s) {
|
|
43
|
+
return s.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
44
|
+
}
|
|
45
|
+
/** Normalised outward postcode, e.g. "TN12 6AX" -> "TN12". */
|
|
46
|
+
function outwardOf(postcode) {
|
|
47
|
+
if (!postcode)
|
|
48
|
+
return null;
|
|
49
|
+
const m = postcode.trim().toUpperCase().match(/^([A-Z]{1,2}\d[A-Z\d]?)/);
|
|
50
|
+
return m ? m[1] : null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Search the real CQC dataset. All filters are AND-combined; any omitted.
|
|
54
|
+
* Matching is case-insensitive substring for name/serviceType, and outward- or
|
|
55
|
+
* full-postcode prefix for postcode.
|
|
56
|
+
*/
|
|
57
|
+
export function searchProviders(params) {
|
|
58
|
+
const q = params.query ? norm(params.query) : null;
|
|
59
|
+
const st = params.serviceType ? norm(params.serviceType) : null;
|
|
60
|
+
const authSlug = params.authority ? norm(params.authority) : null;
|
|
61
|
+
const pcRaw = params.postcode ? params.postcode.trim().toUpperCase().replace(/\s+/g, '') : null;
|
|
62
|
+
const pcOutward = params.postcode ? outwardOf(params.postcode) : null;
|
|
63
|
+
const results = [];
|
|
64
|
+
for (const auth of CQC_AUTHORITIES) {
|
|
65
|
+
if (authSlug && norm(auth.slug) !== authSlug && norm(auth.name) !== authSlug)
|
|
66
|
+
continue;
|
|
67
|
+
for (const p of auth.providers) {
|
|
68
|
+
if (q && !norm(p.name).includes(q) && !(p.providerName && norm(p.providerName).includes(q)))
|
|
69
|
+
continue;
|
|
70
|
+
if (st && !p.serviceTypes.some((t) => norm(t).includes(st)))
|
|
71
|
+
continue;
|
|
72
|
+
if (pcRaw) {
|
|
73
|
+
const provPc = (p.postcode ?? '').toUpperCase().replace(/\s+/g, '');
|
|
74
|
+
const provOut = outwardOf(p.postcode);
|
|
75
|
+
if (!provPc.startsWith(pcRaw) && provOut !== pcOutward)
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
results.push({
|
|
79
|
+
provider: p,
|
|
80
|
+
authoritySlug: auth.slug,
|
|
81
|
+
authorityName: auth.name,
|
|
82
|
+
region: auth.region,
|
|
83
|
+
});
|
|
84
|
+
if (results.length >= params.limit)
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
/** Look up one authority or locality area by slug or name. */
|
|
91
|
+
export function findArea(slugOrName) {
|
|
92
|
+
const want = norm(slugOrName);
|
|
93
|
+
for (const auth of CQC_AUTHORITIES) {
|
|
94
|
+
if (norm(auth.slug) === want || norm(auth.name) === want)
|
|
95
|
+
return { kind: 'authority', area: auth, authorityName: auth.name };
|
|
96
|
+
for (const loc of auth.localities) {
|
|
97
|
+
if (norm(loc.slug) === want)
|
|
98
|
+
return { kind: 'locality', area: loc, authorityName: auth.name };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|