mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Merge #1284: declare the labs availability metadata in get_labs
The tool module was split into one file per tool on this branch, so the change lands in tools/get-labs.ts, and the advertised tool list snapshot gains the two declared fields.
This commit is contained in:
commit
a895f16e08
3 files changed
+117
-5
No files matched your search
@@ -411,6 +411,74 @@
|
||||
"grounding": {
|
||||
"type": "string"
|
||||
},
|
||||
"searchedWindow": {
|
||||
"type": "string"
|
||||
},
|
||||
"available": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "number"
|
||||
},
|
||||
"firstDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"series": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"series": {
|
||||
"type": "string"
|
||||
},
|
||||
"unit": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"count": {
|
||||
"type": "number"
|
||||
},
|
||||
"mean": {
|
||||
"type": "number"
|
||||
},
|
||||
"min": {
|
||||
"type": "number"
|
||||
},
|
||||
"max": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"series",
|
||||
"unit",
|
||||
"count",
|
||||
"mean",
|
||||
"min",
|
||||
"max"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"reachableWithWindow": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"count",
|
||||
"firstDate",
|
||||
"lastDate",
|
||||
"reachableWithWindow"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"analyte": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -510,6 +510,52 @@ describe("get_illness_recovery — condition body site", () => {
|
||||
});
|
||||
|
||||
describe("get_labs", () => {
|
||||
it.each([{}, { analyte: "LDL" }])(
|
||||
"declares empty-read availability for ordinary and filtered reads: %j",
|
||||
async (args) => {
|
||||
const { z } = await import("zod/v4");
|
||||
const empty = {
|
||||
present: false,
|
||||
reason: "out_of_window",
|
||||
searchedWindow: "lastYear",
|
||||
available: {
|
||||
count: 2,
|
||||
firstDate: "2020-01-01",
|
||||
lastDate: "2020-01-02",
|
||||
reachableWithWindow: null,
|
||||
},
|
||||
};
|
||||
vi.mocked(executeCoachTool).mockResolvedValue(empty);
|
||||
const def = tool("get_labs");
|
||||
const result = await def.run(CTX, args);
|
||||
expect(executeCoachTool).toHaveBeenCalledWith({
|
||||
userId: CTX.userId,
|
||||
name: "get_labs",
|
||||
rawArguments: JSON.stringify(args),
|
||||
});
|
||||
expect(z.object(def.outputShape!).strict().parse(result)).toEqual(empty);
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps output validation closed and availability typed", async () => {
|
||||
const { z } = await import("zod/v4");
|
||||
const schema = z.object(tool("get_labs").outputShape!).strict();
|
||||
expect(z.toJSONSchema(schema).additionalProperties).toBe(false);
|
||||
expect(schema.safeParse({ present: false, unexpected: true }).success).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
schema.safeParse({ present: false, searchedWindow: 7 }).success,
|
||||
).toBe(false);
|
||||
expect(
|
||||
schema.safeParse({ present: false, available: { count: "2" } }).success,
|
||||
).toBe(false);
|
||||
expect(schema.parse({ present: false, reason: "no_data" })).toEqual({
|
||||
present: false,
|
||||
reason: "no_data",
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards the optional analyte filter and returns readings with units + bands", async () => {
|
||||
vi.mocked(executeCoachTool).mockResolvedValue({
|
||||
present: true,
|
||||
|
||||
@@ -4,16 +4,14 @@ import { resolveMarkerForRead } from "@/lib/labs/biomarker-resolve";
|
||||
import { getLabHistory, LAB_HISTORY_MAX_LIMIT } from "@/lib/mcp/rich-reads";
|
||||
import { decodeOffsetCursor } from "@/lib/mcp/pagination";
|
||||
import type { McpAuthContext } from "../auth";
|
||||
import { coachReadOutput } from "./output-shapes";
|
||||
import { runCoachTool, whenModuleOn } from "./shared";
|
||||
import { READ_ONLY_ANNOTATIONS, type McpToolDefinition } from "./types";
|
||||
|
||||
/** Output schema for `get_labs` — latest-per-biomarker OR a paginated history. */
|
||||
const getLabsOutput: z.ZodRawShape = {
|
||||
present: z.boolean(),
|
||||
reason: z.string().optional(),
|
||||
// Latest-mode payload (the Coach labs section, carried opaque).
|
||||
data: z.unknown().optional(),
|
||||
grounding: z.string().optional(),
|
||||
// Latest-mode payload, including typed empty-read availability metadata.
|
||||
...coachReadOutput,
|
||||
// History-mode payload (per-analyte trajectory + pagination).
|
||||
analyte: z.string().optional(),
|
||||
readings: z
|
||||
|
||||
Reference in new issue
Block a user