import { z } from "zod";
import fs from "fs/promises";
import path from "path";
// --- Tools ---
export const analyzePythonAstSchema = {
name: "analyze_python_ast",
description: "Analyze Python code structure (classes, functions, imports) using static analysis.",
inputSchema: z.object({
code: z.string().describe("Python code to analyze"),
}),
};
export const checkPythonDepsSchema = {
name: "check_python_deps",
description: "Check Python dependencies from requirements.txt or pyproject.toml",
inputSchema: z.object({
filePath: z.string().describe("Path to requirements.txt or pyproject.toml"),
}),
};
// --- Handlers ---
export async function analyzePythonAstHandler(args: { code: string }) {
const { code } = args;
// Simple regex-based analysis
const classes = [...code.matchAll(/^class\s+([a-zA-Z0-9_]+)/gm)].map(m => m[1]);
const functions = [...code.matchAll(/^def\s+([a-zA-Z0-9_]+)/gm)].map(m => m[1]);
const imports = [...code.matchAll(/^(?:from|import)\s+([a-zA-Z0-9_.]+)/gm)].map(m => m[1]);
return {
content: [{
type: "text",
text: JSON.stringify({
classes,
functions,
imports,
summary: `Found ${classes.length} classes, ${functions.length} functions, and ${imports.length} imports.`
}, null, 2)
}],
};
}
export async function checkPythonDepsHandler(args: { filePath: string }) {
try {
const content = await fs.readFile(args.filePath, "utf-8");
const isToml = args.filePath.endsWith(".toml");
let dependencies: string[] = [];
if (isToml) {
// Very basic TOML parsing for [tool.poetry.dependencies] or [project.dependencies]
const lines = content.split("\n");
let insideDeps = false;
for (const line of lines) {
if (line.includes("[tool.poetry.dependencies]") || line.includes("[project.dependencies]")) {
insideDeps = true;
continue;
}
if (line.startsWith("[") && insideDeps) {
insideDeps = false;
continue;
}
if (insideDeps && line.includes("=") && !line.startsWith("#")) {
dependencies.push(line.split("=")[0].trim());
}
}
} else {
// requirements.txt
dependencies = content
.split("\n")
.map(l => l.split("#")[0].trim()) // remove comments
.filter(l => l && !l.startsWith("-")); // remove flags
}
return {
content: [{
type: "text",
text: JSON.stringify({
file: path.basename(args.filePath),
count: dependencies.length,
dependencies
}, null, 2)
}]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error reading file: ${error instanceof Error ? error.message : String(error)}` }],
isError: true
};
}
}