updateObservation
Modify existing observations in Medplum FHIR servers by providing the observation ID and updating fields like status, numeric value, or string value using the MCP server.
Instructions
Updates an existing observation. Requires the observation ID and the fields to update.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| observationId | Yes | The unique ID of the observation to update. | |
| status | No | New status for the observation. | |
| valueQuantity | No | New numeric value of the observation. Optional. | |
| valueString | No | New string value of the observation. Optional. |
Implementation Reference
- src/tools/observationUtils.ts:191-306 (handler)The core handler function that updates an existing Observation FHIR resource. It reads the current resource, applies partial updates with special handling for fields like note, identifier, references (subject, encounter, performer), ensures value[x] exclusivity, converts nulls to undefined, and calls Medplum's updateResource.export async function updateObservation(observationId: string, updates: UpdateObservationArgs): Promise<Observation> { await ensureAuthenticated(); if (!observationId) { throw new Error('Observation ID is required to update an observation.'); } if (!updates || Object.keys(updates).length === 0) { throw new Error('Updates object cannot be empty for updating an observation.'); } const existingObservation = await medplum.readResource('Observation', observationId); if (!existingObservation) { throw new Error(`Observation with ID ${observationId} not found.`); } const { note: noteInput, identifier: identifierInput, encounterId: encounterIdInput, performerIds: performerIdsInput, subjectId: subjectIdInput, // Though less common to update subject, handle if passed ...restOfUpdates // These are fields that are mostly 1:1 or simple null->undefined } = updates; const workingUpdates: Partial<Observation> = {}; // Handle fields from restOfUpdates, converting null to undefined for (const key in restOfUpdates) { if (Object.prototype.hasOwnProperty.call(restOfUpdates, key)) { const value = (restOfUpdates as any)[key]; if (value === null) { (workingUpdates as any)[key] = undefined; } else if (value !== undefined) { (workingUpdates as any)[key] = value; } } } // Handle specific conversions for note if (typeof noteInput === 'string') { workingUpdates.note = [{ text: noteInput }]; } else if (noteInput === null) { workingUpdates.note = undefined; } else if (noteInput !== undefined) { // If it was already Annotation[] workingUpdates.note = noteInput as any; // Cast if UpdateObservationArgs not updated } // Handle specific conversions for identifier if (identifierInput && typeof identifierInput === 'object') { workingUpdates.identifier = [identifierInput as Identifier]; } else if (identifierInput === null) { workingUpdates.identifier = undefined; } // Handle subjectId to subject reference if (typeof subjectIdInput === 'string') { workingUpdates.subject = { reference: `Patient/${subjectIdInput}` }; } else if (subjectIdInput === null) { // Allow clearing subject if necessary workingUpdates.subject = undefined; } // Handle encounterId to encounter reference if (typeof encounterIdInput === 'string') { workingUpdates.encounter = { reference: `Encounter/${encounterIdInput}` }; } else if (encounterIdInput === null) { workingUpdates.encounter = undefined; // Clear the encounter } // Handle performerIds to performer references if (Array.isArray(performerIdsInput)) { workingUpdates.performer = performerIdsInput.map(id => ({ reference: `Practitioner/${id}` })); } else if (performerIdsInput === null) { workingUpdates.performer = undefined; // Clear performers } // value[x] exclusivity logic const valueFields: (keyof Observation)[] = [ 'valueQuantity', 'valueCodeableConcept', 'valueString', 'valueBoolean', 'valueInteger', 'valueRange', 'valueRatio', 'valueSampledData', 'valueTime', 'valueDateTime', 'valuePeriod' ]; let valueKeyPresentInUpdates: keyof Observation | undefined; for (const key of valueFields) { // Check if the key (potentially a value[x] field) exists in the original `updates` object if ((updates as any)[key] !== undefined) { if (valueKeyPresentInUpdates) { // This indicates multiple value[x] fields were in the input `updates`. // The test "should throw error if updating with multiple value[x] types" // expects the Medplum server to reject this. So, we don't throw here, // but let Medplum handle it if workingUpdates still contains multiple. } valueKeyPresentInUpdates = key; } } // If a value[x] is being set in updates, ensure all other value[x] fields are cleared // from workingUpdates to ensure only one is sent to Medplum. if (valueKeyPresentInUpdates) { for (const key of valueFields) { if (key !== valueKeyPresentInUpdates) { (workingUpdates as any)[key] = undefined; } } } const updatedResource: Observation = { ...existingObservation, ...workingUpdates, resourceType: 'Observation', // Ensure resourceType is correctly maintained id: observationId, // Ensure ID is correctly maintained }; return medplum.updateResource(updatedResource); }
- src/tools/observationUtils.ts:43-69 (schema)TypeScript interface defining the input arguments for updating an Observation, including all possible FHIR fields that can be updated.export interface UpdateObservationArgs { status?: Observation['status']; code?: CodeableConcept; subjectId?: string; encounterId?: string | null; effectiveDateTime?: string | null; effectivePeriod?: Period | null; issued?: string; performerIds?: string[] | null; valueQuantity?: Quantity; valueCodeableConcept?: CodeableConcept; valueString?: string; valueBoolean?: boolean; valueInteger?: number; valueRange?: Range; valueRatio?: Ratio; valueSampledData?: SampledData; valueTime?: string; valueDateTime?: string; valuePeriod?: Period; bodySite?: CodeableConcept | null; method?: CodeableConcept | null; referenceRange?: any[] | null; note?: string | null; interpretation?: CodeableConcept[] | null; identifier?: { system?: string; value: string } | null; }
- src/index.ts:507-533 (schema)MCP input schema definition for the 'updateObservation' tool, used by the MCP server for tool listing (listTools) and request validation (callTool).{ name: "updateObservation", description: "Updates an existing observation. Requires the observation ID and the fields to update.", inputSchema: { type: "object", properties: { observationId: { type: "string", description: "The unique ID of the observation to update.", }, status: { type: "string", description: "New status for the observation.", enum: ["registered", "preliminary", "final", "amended", "corrected", "cancelled"], }, valueQuantity: { type: "number", description: "New numeric value of the observation. Optional.", }, valueString: { type: "string", description: "New string value of the observation. Optional.", }, }, required: ["observationId"], }, },
- src/index.ts:970-970 (registration)Registration of the updateObservation handler function in the toolMapping object, which maps tool names to their implementations for execution in the MCP callTool handler.updateObservation,
- src/index.ts:39-43 (registration)Import of the updateObservation function from observationUtils.ts into the main index.ts server file.createObservation, getObservationById, updateObservation, searchObservations, } from './tools/observationUtils.js';