UNPKG

textlint

Version:

The pluggable linting tool for natural language.

231 lines 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.connectStdioMcpServer = exports.setupServer = void 0; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const zod_1 = require("zod"); const index_js_1 = require("../index.js"); const node_fs_1 = require("node:fs"); const schemas_js_1 = require("./schemas.js"); const makeLinterOptions = async (options = {}) => { // If descriptor is directly provided, use it; otherwise load from config const descriptor = options.descriptor || (await (0, index_js_1.loadTextlintrc)({ configFilePath: options.configFilePath, node_modulesDir: options.node_modulesDir })); return { descriptor, ignoreFilePath: options.ignoreFilePath, quiet: options.quiet, cwd: options.cwd }; }; // Helper functions for common MCP operations const createStructuredErrorResponse = (error, type, isError = true) => { const structuredContent = { results: [], error, type, timestamp: new Date().toISOString(), isError }; return { content: [ { type: "text", text: JSON.stringify(structuredContent, null, 2) } ], structuredContent, isError }; }; const createStructuredSuccessResponse = (data, isError = false) => { const structuredContent = { ...data, isError, timestamp: new Date().toISOString() }; return { content: [ { type: "text", text: JSON.stringify(structuredContent, null, 2) } ], structuredContent, isError }; }; const checkFilesExist = (filePaths) => { return filePaths.filter((filePath) => !(0, node_fs_1.existsSync)(filePath)); }; const validateInputAndReturnError = (value, fieldName, errorType) => { if (!value.trim()) { return createStructuredErrorResponse(`${fieldName} cannot be empty`, errorType); } return null; }; const setupServer = async (options = {}) => { var _a, _b; const { readPackageUpSync } = await import("read-package-up"); const version = (_b = (_a = readPackageUpSync({ cwd: __dirname })) === null || _a === void 0 ? void 0 : _a.packageJson.version) !== null && _b !== void 0 ? _b : "unknown"; const server = new mcp_js_1.McpServer({ name: "textlint", version }); // ツール登録 server.registerTool("lintFile", { description: "Lint files using textlint", inputSchema: { filePaths: zod_1.z .array(zod_1.z.string().min(1).describe("File path to lint")) .nonempty() .describe("Array of file paths to lint") }, outputSchema: { results: zod_1.z.array(zod_1.z.object({ filePath: zod_1.z.string(), messages: zod_1.z.array(schemas_js_1.TextlintMessageSchema), output: zod_1.z.string().optional().describe("Fixed content if available") })), isError: zod_1.z.boolean(), timestamp: zod_1.z.string().optional(), error: zod_1.z.string().optional(), type: zod_1.z.string().optional() } }, async ({ filePaths }) => { try { // Check if files exist before processing const nonExistentFiles = checkFilesExist(filePaths); if (nonExistentFiles.length > 0) { return createStructuredErrorResponse(`File(s) not found: ${nonExistentFiles.join(", ")}`, "lintFile_error"); } const linterOptions = await makeLinterOptions(options); const linter = (0, index_js_1.createLinter)(linterOptions); const results = await linter.lintFiles(filePaths); // Return structured content as per MCP 2025-06-18 specification // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content return createStructuredSuccessResponse({ results }); } catch (error) { // Handle errors with isError flag for MCP compliance return createStructuredErrorResponse(error instanceof Error ? error.message : "Unknown error occurred", "lintFile_error"); } }); server.registerTool("lintText", { description: "Lint text using textlint", inputSchema: { text: zod_1.z.string().nonempty().describe("Text content to lint"), stdinFilename: zod_1.z.string().nonempty().describe("Filename for context (e.g., 'stdin.md')") }, outputSchema: { filePath: zod_1.z.string(), messages: zod_1.z.array(schemas_js_1.TextlintMessageSchema), output: zod_1.z.string().optional().describe("Fixed content if available"), isError: zod_1.z.boolean(), timestamp: zod_1.z.string().optional(), error: zod_1.z.string().optional(), type: zod_1.z.string().optional() } }, async ({ text, stdinFilename }) => { try { // Validate input parameters const validationError = validateInputAndReturnError(stdinFilename, "stdinFilename", "lintText_error"); if (validationError) { return validationError; } const linterOptions = await makeLinterOptions(options); const linter = (0, index_js_1.createLinter)(linterOptions); const result = await linter.lintText(text, stdinFilename); // Return structured content as per MCP 2025-06-18 specification // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content return createStructuredSuccessResponse(result); } catch (error) { return createStructuredErrorResponse(error instanceof Error ? error.message : "Unknown error occurred", "lintText_error"); } }); server.registerTool("getLintFixedFileContent", { description: "Get lint-fixed content of files using textlint", inputSchema: { filePaths: zod_1.z .array(zod_1.z.string().min(1).describe("File path to fix")) .nonempty() .describe("Array of file paths to get fixed content for") }, outputSchema: { results: zod_1.z.array(zod_1.z.object({ filePath: zod_1.z.string(), messages: zod_1.z.array(schemas_js_1.TextlintMessageSchema), output: zod_1.z.string().optional().describe("Fixed content") })), isError: zod_1.z.boolean(), timestamp: zod_1.z.string().optional(), error: zod_1.z.string().optional(), type: zod_1.z.string().optional() } }, async ({ filePaths }) => { try { // Check if files exist before processing const nonExistentFiles = checkFilesExist(filePaths); if (nonExistentFiles.length > 0) { return createStructuredErrorResponse(`File(s) not found: ${nonExistentFiles.join(", ")}`, "fixFiles_error"); } const linterOptions = await makeLinterOptions(options); const linter = (0, index_js_1.createLinter)(linterOptions); const results = await linter.fixFiles(filePaths); // Return structured content as per MCP 2025-06-18 specification // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content return createStructuredSuccessResponse({ results }); } catch (error) { // Handle errors with isError flag for MCP compliance return createStructuredErrorResponse(error instanceof Error ? error.message : "Unknown error occurred", "fixFiles_error"); } }); server.registerTool("getLintFixedTextContent", { description: "Get lint-fixed content of text using textlint", inputSchema: { text: zod_1.z.string().nonempty().describe("Text content to fix"), stdinFilename: zod_1.z.string().nonempty().describe("Filename for context (e.g., 'stdin.md')") }, outputSchema: { filePath: zod_1.z.string(), messages: zod_1.z.array(schemas_js_1.TextlintMessageSchema), output: zod_1.z.string().optional().describe("Fixed content"), isError: zod_1.z.boolean(), timestamp: zod_1.z.string().optional(), error: zod_1.z.string().optional(), type: zod_1.z.string().optional() } }, async ({ text, stdinFilename }) => { try { // Validate input parameters const validationError = validateInputAndReturnError(stdinFilename, "stdinFilename", "fixText_error"); if (validationError) return validationError; const linterOptions = await makeLinterOptions(options); const linter = (0, index_js_1.createLinter)(linterOptions); const result = await linter.fixText(text, stdinFilename); // Return structured content as per MCP 2025-06-18 specification // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content return createStructuredSuccessResponse(result); } catch (error) { // Handle errors with isError flag for MCP compliance return createStructuredErrorResponse(error instanceof Error ? error.message : "Unknown error occurred", "fixText_error"); } }); return server; }; exports.setupServer = setupServer; const connectStdioMcpServer = async (options = {}) => { const server = await (0, exports.setupServer)(options); const transport = new stdio_js_1.StdioServerTransport(); await server.connect(transport); return server; }; exports.connectStdioMcpServer = connectStdioMcpServer; //# sourceMappingURL=server.js.map