/**
* Utility functions for HackerNews MCP Server
*/
/**
* Convert ISO 8601 date string to Unix timestamp
*/
export function dateToUnixTimestamp(isoDate: string): number {
return Math.floor(new Date(isoDate).getTime() / 1000);
}
/**
* Convert Unix timestamp to ISO 8601 date string
*/
export function unixTimestampToDate(timestamp: number): string {
return new Date(timestamp * 1000).toISOString();
}
/**
* Build HackerNews Algolia API URL
*/
export function buildApiUrl(endpoint: string, params?: Record<string, string>): string {
const baseUrl = "https://hn.algolia.com/api/v1";
const url = new URL(`${baseUrl}/${endpoint}`);
if (params) {
for (const [key, value] of Object.entries(params)) {
url.searchParams.append(key, value);
}
}
return url.toString();
}
/**
* Format numeric filters for HN API
*/
export function formatNumericFilters(filters: string[]): string {
return filters.join(",");
}
/**
* Format tags for HN API
*/
export function formatTags(tags: string[]): string {
return tags.map((tag) => `(${tag})`).join(",");
}
/**
* Calculate account age in years from Unix timestamp
*/
export function calculateAccountAge(createdAt: number): number {
const now = Date.now() / 1000;
const ageInSeconds = now - createdAt;
const ageInYears = ageInSeconds / (365.25 * 24 * 60 * 60);
return Math.floor(ageInYears * 10) / 10; // Round to 1 decimal place
}
/**
* Calculate karma per year
*/
export function calculateKarmaPerYear(karma: number, createdAt: number): number {
const accountAge = calculateAccountAge(createdAt);
if (accountAge === 0) return karma;
return Math.floor((karma / accountAge) * 10) / 10; // Round to 1 decimal place
}
/**
* Safely parse JSON with error handling
*/
export function safeJsonParse<T>(json: string): T | null {
try {
return JSON.parse(json) as T;
} catch {
return null;
}
}
/**
* Format MCP tool response
*/
export function formatToolResponse(
data: any,
isError = false,
): {
content: Array<{ type: "text"; text: string }>;
isError?: boolean;
} {
return {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2),
},
],
...(isError && { isError: true }),
};
}
/**
* Format error message for MCP response
*/
export function formatErrorResponse(error: Error): {
content: Array<{ type: "text"; text: string }>;
isError: boolean;
} {
return {
content: [
{
type: "text",
text: JSON.stringify(
{
error: error.name,
message: error.message,
},
null,
2,
),
},
],
isError: true,
};
}