frappe-mcp-server
Version:
Enhanced Model Context Protocol server for Frappe Framework with comprehensive API instructions and helper tools
217 lines (206 loc) • 6.45 kB
text/typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
findDocTypes,
getModuleList,
getDocTypesInModule,
doesDocTypeExist,
doesDocumentExist,
getDocumentCount,
getNamingSeriesInfo,
getRequiredFields
} from "./frappe-helpers.js";
import { getInstructions } from "./frappe-instructions.js";
/**
* Format error response with detailed information
*/
function formatErrorResponse(error: any, operation: string): any {
console.error(`Error in ${operation}:`, error);
return {
content: [
{
type: "text",
text: `Error in ${operation}: ${error.message || 'Unknown error'}`,
},
],
isError: true,
};
}
export function setupHelperTools(server: McpServer): void {
// Register find_doctypes tool
server.tool(
"find_doctypes",
"Find DocTypes in the system matching a search term",
{
search_term: z.string().optional().describe("Search term to look for in DocType names"),
module: z.string().optional().describe("Filter by module name (optional)"),
is_table: z.boolean().optional().describe("Filter by table DocTypes (optional)"),
is_single: z.boolean().optional().describe("Filter by single DocTypes (optional)"),
is_custom: z.boolean().optional().describe("Filter by custom DocTypes (optional)"),
limit: z.number().optional().describe("Maximum number of results (optional, default 20)")
},
async ({ search_term, module, is_table, is_single, is_custom, limit }) => {
try {
const searchTerm = search_term || "";
const options = {
module,
isTable: is_table,
isSingle: is_single,
isCustom: is_custom,
limit: limit || 20
};
const doctypes = await findDocTypes(searchTerm, options);
return {
content: [{ type: "text", text: JSON.stringify(doctypes, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "find_doctypes");
}
}
);
// Register get_module_list tool
server.tool(
"get_module_list",
"Get a list of all modules in the system",
{},
async () => {
try {
const modules = await getModuleList();
return {
content: [{ type: "text", text: JSON.stringify(modules, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "get_module_list");
}
}
);
// Register get_doctypes_in_module tool
server.tool(
"get_doctypes_in_module",
"Get a list of DocTypes in a specific module",
{
module: z.string().describe("Module name")
},
async ({ module }) => {
try {
const doctypes = await getDocTypesInModule(module);
return {
content: [{ type: "text", text: JSON.stringify(doctypes, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "get_doctypes_in_module");
}
}
);
// Register check_doctype_exists tool
server.tool(
"check_doctype_exists",
"Check if a DocType exists in the system",
{
doctype: z.string().describe("DocType name to check")
},
async ({ doctype }) => {
try {
const exists = await doesDocTypeExist(doctype);
return {
content: [{ type: "text", text: JSON.stringify({ exists }, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "check_doctype_exists");
}
}
);
// Register check_document_exists tool
server.tool(
"check_document_exists",
"Check if a document exists",
{
doctype: z.string().describe("DocType name"),
name: z.string().describe("Document name to check")
},
async ({ doctype, name }) => {
try {
const exists = await doesDocumentExist(doctype, name);
return {
content: [{ type: "text", text: JSON.stringify({ exists }, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "check_document_exists");
}
}
);
// Register get_document_count tool
server.tool(
"get_document_count",
"Get a count of documents matching filters",
{
doctype: z.string().describe("DocType name"),
filters: z.object({}).optional().describe("Filters to apply (optional)")
},
async ({ doctype, filters }) => {
try {
const count = await getDocumentCount(doctype, filters);
return {
content: [{ type: "text", text: JSON.stringify({ count }, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "get_document_count");
}
}
);
// Register get_naming_info tool
server.tool(
"get_naming_info",
"Get the naming series information for a DocType",
{
doctype: z.string().describe("DocType name")
},
async ({ doctype }) => {
try {
const namingInfo = await getNamingSeriesInfo(doctype);
return {
content: [{ type: "text", text: JSON.stringify(namingInfo, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "get_naming_info");
}
}
);
// Register get_required_fields tool
server.tool(
"get_required_fields",
"Get a list of required fields for a DocType",
{
doctype: z.string().describe("DocType name")
},
async ({ doctype }) => {
try {
const requiredFields = await getRequiredFields(doctype);
return {
content: [{ type: "text", text: JSON.stringify(requiredFields, null, 2) }],
};
} catch (error) {
return formatErrorResponse(error, "get_required_fields");
}
}
);
// Register get_api_instructions tool
server.tool(
"get_api_instructions",
"Get detailed instructions for using the Frappe API",
{
category: z.string().describe("Instruction category (DOCUMENT_OPERATIONS, SCHEMA_OPERATIONS, ADVANCED_OPERATIONS, BEST_PRACTICES)"),
operation: z.string().describe("Operation name (e.g., CREATE, GET, UPDATE, DELETE, LIST, GET_DOCTYPE_SCHEMA, etc.)")
},
async ({ category, operation }) => {
try {
const instructions = getInstructions(category, operation);
return {
content: [{ type: "text", text: instructions }],
};
} catch (error) {
return formatErrorResponse(error, "get_api_instructions");
}
}
);
}