@channel.io/be-user-chat-mcp
Version:
Backend UserChat Model Context Protocol server for Channel.io
120 lines • 5 kB
JavaScript
import { DynamoDBClient, GetItemCommand, QueryCommand, } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall, } from "@aws-sdk/util-dynamodb";
let client = null;
// Get or initialize the DynamoDB client (Lazy Singleton for stdio, per-request for HTTP)
function getClient(context) {
// For HTTP transport, always create new client with context credentials
if (context.awsRegion &&
context.awsAccessKeyId &&
context.awsSecretAccessKey) {
return new DynamoDBClient({
region: context.awsRegion,
credentials: {
accessKeyId: context.awsAccessKeyId,
secretAccessKey: context.awsSecretAccessKey,
...(context.awsSessionToken && {
sessionToken: context.awsSessionToken,
}),
},
});
}
// For stdio transport, use singleton with default credential provider chain
// (when context has awsProfile but no direct credentials)
if (client) {
return client;
}
// Initialize client without explicit credentials
// SDK will use the default credential provider chain
// (Environment variables, ~/.aws/credentials, ~/.aws/config, IAM role, etc.)
client = new DynamoDBClient({});
console.error("Initialized DynamoDBClient using default credential provider chain.");
return client;
}
/**
* Type guard to check if an object is not empty.
*/
function isNotEmptyObject(obj) {
return typeof obj === "object" && obj !== null && Object.keys(obj).length > 0;
}
/**
* Fetches a single item from a DynamoDB table by its key.
*
* @template T The expected type of the unmarshalled item.
* @param tableName The name of the DynamoDB table.
* @param key The primary key of the item to fetch (use NativeAttributeValue format).
* @param options Optional GetItemCommandInput options (e.g., ProjectionExpression).
* @param context RequestContext containing AWS credentials.
* @returns The requested item, unmarshalled to type T, or null if not found.
*/
export async function getDynamoDBItem(tableName, key, options = {}, context) {
const commandInput = {
TableName: tableName,
Key: marshall(key),
...options,
};
try {
const dbClient = getClient(context);
const { Item } = await dbClient.send(new GetItemCommand(commandInput));
return Item ? unmarshall(Item) : null;
}
catch (error) {
handleDynamoDBError(error, `GetItem from ${tableName}`);
throw error;
}
}
/**
* Queries a DynamoDB table or index.
*
* @template T The expected type of the unmarshalled items.
* @param tableName The name of the DynamoDB table.
* @param queryInput Query parameters (KeyConditionExpression, etc.).
* Use ExpressionAttributeValues with NativeAttributeValue format for easier use.
* Example: ExpressionAttributeValues: { ':val': 'someValue', ':num': 123 }
* @param context RequestContext containing AWS credentials.
* @returns An array of items matching the query, unmarshalled to type T[].
*/
export async function queryDynamoDB(tableName, queryInput, context) {
const marshalledAttributes = queryInput.ExpressionAttributeValues &&
isNotEmptyObject(queryInput.ExpressionAttributeValues)
? marshall(queryInput.ExpressionAttributeValues)
: undefined;
const commandInput = {
TableName: tableName,
...queryInput,
ExpressionAttributeValues: marshalledAttributes,
};
try {
const dbClient = getClient(context);
const { Items } = await dbClient.send(new QueryCommand(commandInput));
return Items ? Items.map((item) => unmarshall(item)) : [];
}
catch (error) {
handleDynamoDBError(error, `Query on ${tableName}`);
throw error;
}
}
// Centralized error handler
function handleDynamoDBError(error, context) {
// Keep basic logging
console.error(`DynamoDB error during operation (${context}):`, error);
let suggestion = "";
if (error instanceof Error) {
// Adjust credential hint logic for default provider chain
const errorMessage = error.message.toLowerCase();
const errorName = error.name.toLowerCase();
if (errorMessage.includes("credentials") ||
errorMessage.includes("profile") ||
errorMessage.includes("token") ||
errorMessage.includes("region") || // Also check for region errors
errorName.includes("credential") ||
errorName.includes("token")) {
suggestion =
"Hint: Check AWS credentials configuration (environment variables, ~/.aws/credentials, ~/.aws/config, IAM role, SSO session) and ensure the AWS region is correctly set.";
}
console.error(`Error Name: ${error.name}, Message: ${error.message}`);
}
if (suggestion) {
console.error("💡", suggestion);
}
}
//# sourceMappingURL=dynamodb.service.js.map