leshan-mcp-server
Version:
A standards-compliant MCP server for Leshan LwM2M, exposing Leshan as Model Context Protocol tools.
96 lines (84 loc) • 3 kB
JavaScript
import { get } from "../utils/leshanClient.js";
import { validateLwM2MPath } from "../utils/validation.js";
import logger from "../utils/loggerConfig.js";
/**
* Read a resource value from a LwM2M device
* @param {Object} params - Parameters
* @param {string} params.deviceId - Device endpoint
* @param {string} params.objectId - Object ID
* @param {string} params.instanceId - Instance ID
* @param {string} params.resourceId - Resource ID
* @returns {Promise<Object>} MCP tool response
*/
export async function readResource({ deviceId, objectId, instanceId, resourceId }) {
const operationId = `read-resource-${Date.now()}`;
try {
logger.info("Read resource operation started", {
operationId,
deviceId,
path: `${objectId}/${instanceId}/${resourceId}`
});
// Validate and sanitize input parameters
const validatedParams = validateLwM2MPath(deviceId, objectId, instanceId, resourceId);
const endpoint = `/clients/${encodeURIComponent(validatedParams.deviceId)}/${validatedParams.objectId}/${validatedParams.instanceId}/${validatedParams.resourceId}`;
logger.debug("Making read request", { operationId, endpoint });
const result = await get(endpoint);
logger.info("Resource read successfully", {
operationId,
deviceId: validatedParams.deviceId,
path: `${validatedParams.objectId}/${validatedParams.instanceId}/${validatedParams.resourceId}`,
valueType: typeof result,
hasValue: result !== null && result !== undefined
});
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
operation: "readResource",
operationId,
timestamp: new Date().toISOString(),
device: {
endpoint: validatedParams.deviceId,
path: `${validatedParams.objectId}/${validatedParams.instanceId}/${validatedParams.resourceId}`
},
result: {
value: result,
type: typeof result,
isEmpty: result === null || result === undefined || result === ""
}
}, null, 2)
}]
};
} catch (error) {
logger.error("Read resource operation failed", {
operationId,
deviceId,
path: `${objectId}/${instanceId}/${resourceId}`,
error: error.message,
errorType: error.constructor.name
});
return {
content: [{
type: "text",
text: JSON.stringify({
success: false,
operation: "readResource",
operationId,
timestamp: new Date().toISOString(),
error: {
message: error.message,
type: error.constructor.name,
...(error.statusCode && { statusCode: error.statusCode })
},
request: {
deviceId,
path: `${objectId}/${instanceId}/${resourceId}`
}
}, null, 2)
}],
isError: true
};
}
}
export default readResource;