functionDetails.ts•5.5 kB
import { FunctionDetailsInput, ResponseContent } from "../types/index.js";
import { createStandardResponse, addStandardContext } from "../utils/responseUtils.js";
import { handleToolError } from "../utils/responseUtils.js";
import { REGISTRY_API_BASE } from "../../config.js";
import logger from "../utils/logger.js";
interface FunctionDoc {
name: string;
category: string;
description: string;
example?: string;
documentationUrl: string;
}
/**
* Handles the functionDetails tool request
* @param params Input parameters for the function details tool
* @returns Standardized response with function information
*/
export async function handleFunctionDetails(params: FunctionDetailsInput): Promise<ResponseContent> {
try {
logger.debug("Processing functionDetails request", params);
// Extract parameters with default namespace
const { provider, function: functionName } = params;
const namespace = params.namespace || "hashicorp";
// Validate required parameters
if (!provider || !functionName) {
throw new Error("Provider and function name are required.");
}
// For testing, we'll use a known version ID that works
const versionId = "67250"; // This is a recent AWS provider version
// Get function documentation
const docIdUrl = `${REGISTRY_API_BASE}/v2/provider-docs?filter[provider-version]=${versionId}&filter[category]=functions&filter[slug]=${functionName}&filter[language]=hcl&page[size]=1`;
logger.info("Fetching doc IDs from:", docIdUrl);
const docIdResponse = await fetch(docIdUrl);
if (!docIdResponse.ok) {
logger.error("Failed to fetch documentation:", {
status: docIdResponse.status,
statusText: docIdResponse.statusText,
url: docIdUrl
});
throw new Error(`Failed to fetch documentation IDs: ${docIdResponse.status} ${docIdResponse.statusText}`);
}
const docIdData = await docIdResponse.json();
logger.debug("Documentation response:", docIdData);
if (!docIdData.data || docIdData.data.length === 0) {
throw new Error(`No function documentation found for ${functionName} in provider ${namespace}/${provider}`);
}
const docId = docIdData.data[0].id;
logger.info("Using doc ID:", docId);
// Get full documentation content
const contentUrl = `${REGISTRY_API_BASE}/v2/provider-docs/${docId}`;
logger.info("Fetching content from:", contentUrl);
const contentResponse = await fetch(contentUrl);
if (!contentResponse.ok) {
logger.error("Failed to fetch content:", {
status: contentResponse.status,
statusText: contentResponse.statusText,
url: contentUrl
});
throw new Error(`Failed to fetch documentation content: ${contentResponse.status} ${contentResponse.statusText}`);
}
const contentData = await contentResponse.json();
logger.debug("Content response:", contentData);
if (!contentData.data?.attributes?.content) {
throw new Error("Documentation content is empty or malformed");
}
const content = contentData.data.attributes.content;
// Extract description
const descriptionMatch = content.match(/description: |-\n(.*?)\n---/s);
const description = descriptionMatch ? descriptionMatch[1].trim() : "";
// Extract example
const exampleMatch = content.match(/## Example Usage\n\n```(?:hcl|terraform)?\n([\s\S]*?)```/);
const example = exampleMatch ? exampleMatch[1].trim() : undefined;
// Extract signature
const signatureMatch = content.match(/## Signature\n\n```text\n([\s\S]*?)```/);
const signature = signatureMatch ? signatureMatch[1].trim() : undefined;
// Extract arguments
const argumentsMatch = content.match(/## Arguments\n\n([\s\S]*?)(?:\n##|$)/);
const arguments_ = argumentsMatch ? argumentsMatch[1].trim() : undefined;
const functionDoc: FunctionDoc = {
name: contentData.data.attributes.title,
category: "Function",
description,
example,
documentationUrl: `${REGISTRY_API_BASE}/providers/${namespace}/${provider}/latest/docs/functions/${contentData.data.attributes.title}`
};
// Format the response in markdown
let markdownResponse = `# Function: ${functionDoc.name}\n\n`;
markdownResponse += `This function is provided by the **${namespace}/${provider}** provider.\n\n`;
if (description) {
markdownResponse += `## Description\n\n${description}\n\n`;
}
if (signature) {
markdownResponse += `## Signature\n\n\`\`\`text\n${signature}\n\`\`\`\n\n`;
}
if (example) {
markdownResponse += "## Example Usage\n\n```hcl\n" + example + "\n```\n\n";
}
if (arguments_) {
markdownResponse += "## Arguments\n\n" + arguments_ + "\n\n";
}
// Add metadata with context
const metadata = {
provider,
namespace,
function: {
name: functionDoc.name,
hasExample: !!example,
hasSignature: !!signature,
hasArguments: !!arguments_
},
documentationUrl: functionDoc.documentationUrl
};
// Add compatibility information
addStandardContext(metadata);
return createStandardResponse("success", markdownResponse, metadata);
} catch (error) {
return handleToolError("functionDetails", error, {
inputParams: params,
context: {
message: error instanceof Error ? error.message : "Unknown error occurred",
timestamp: new Date().toISOString()
}
});
}
}