import { z } from "zod";
import {
GenerateTopicalClustersSchema,
GenerateTopicalClustersSchemaBase,
} from "../schemas/index.js";
import { makeInfraNodusRequest } from "../api/client.js";
import { fetchUrlContentAsText } from "../utils/urlContent.js";
import { generateTopics } from "../utils/transformers.js";
function errorContent(message: string) {
return {
content: [
{ type: "text" as const, text: JSON.stringify({ error: message }) },
],
isError: true,
};
}
export const generateTopicalClustersTool = {
name: "generate_topical_clusters",
definition: {
title: "Generate Topical Clusters",
description:
"Generate topics and clusters of keywords from text, URL, or an existing graph using knowledge graph analysis. ",
inputSchema: GenerateTopicalClustersSchemaBase.shape,
annotations: {
readOnlyHint: true,
idempotentHint: true,
destructiveHint: false,
},
},
handler: async (params: z.infer<typeof GenerateTopicalClustersSchema>) => {
try {
const queryParams = new URLSearchParams({
doNotSave: "true",
addStats: "true",
includeGraphSummary: "false",
extendedGraphSummary: "true",
includeGraph: "false",
includeStatements: "false",
aiTopics: "true",
});
const endpoint = `/graphAndStatements?${queryParams.toString()}`;
let requestBody: { text?: string; name?: string };
if (params.graphName?.trim()) {
requestBody = { name: params.graphName };
} else {
let contentText: string;
if (params.url) {
const result = await fetchUrlContentAsText(params.url);
if (!result.ok) return errorContent(result.error);
contentText = result.contentText;
if (!contentText?.trim())
return errorContent("URL did not return any text content");
} else if (params.text?.trim()) {
contentText = params.text;
} else {
return errorContent(
"Provide either text, url, or graphName for analysis"
);
}
requestBody = { text: contentText };
}
const response = await makeInfraNodusRequest(endpoint, requestBody);
if (response.error) {
return {
content: [
{
type: "text" as const,
text: JSON.stringify({ error: response.error }),
},
],
isError: true,
};
}
const insights = generateTopics(response);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(insights, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: JSON.stringify({
error: error instanceof Error ? error.message : String(error),
}),
},
],
isError: true,
};
}
},
};