textlint
Version:
The pluggable linting tool for natural language.
264 lines • 11 kB
JavaScript
"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 makeLinterOptions = async () => {
const descriptor = await (0, index_js_1.loadTextlintrc)();
return {
descriptor
};
};
// 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 () => {
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)).nonempty()
},
outputSchema: {
results: zod_1.z.array(zod_1.z.object({
filePath: zod_1.z.string(),
messages: zod_1.z.array(zod_1.z.object({
ruleId: zod_1.z.string().optional(),
message: zod_1.z.string(),
line: zod_1.z.number(),
column: zod_1.z.number(),
severity: zod_1.z.number(),
fix: zod_1.z
.object({
range: zod_1.z.array(zod_1.z.number()),
text: zod_1.z.string()
})
.optional()
})),
output: zod_1.z.string().optional()
})),
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();
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(),
stdinFilename: zod_1.z.string().nonempty()
},
outputSchema: {
filePath: zod_1.z.string(),
messages: zod_1.z.array(zod_1.z.object({
ruleId: zod_1.z.string().optional(),
message: zod_1.z.string(),
line: zod_1.z.number(),
column: zod_1.z.number(),
severity: zod_1.z.number(),
fix: zod_1.z
.object({
range: zod_1.z.array(zod_1.z.number()),
text: zod_1.z.string()
})
.optional()
})),
output: zod_1.z.string().optional(),
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();
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)).nonempty()
},
outputSchema: {
results: zod_1.z.array(zod_1.z.object({
filePath: zod_1.z.string(),
messages: zod_1.z.array(zod_1.z.object({
ruleId: zod_1.z.string().optional(),
message: zod_1.z.string(),
line: zod_1.z.number(),
column: zod_1.z.number(),
severity: zod_1.z.number(),
fix: zod_1.z
.object({
range: zod_1.z.array(zod_1.z.number()),
text: zod_1.z.string()
})
.optional()
})),
output: zod_1.z.string().optional()
})),
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();
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(),
stdinFilename: zod_1.z.string().nonempty()
},
outputSchema: {
filePath: zod_1.z.string(),
messages: zod_1.z.array(zod_1.z.object({
ruleId: zod_1.z.string().optional(),
message: zod_1.z.string(),
line: zod_1.z.number(),
column: zod_1.z.number(),
severity: zod_1.z.number(),
fix: zod_1.z
.object({
range: zod_1.z.array(zod_1.z.number()),
text: zod_1.z.string()
})
.optional()
})),
output: zod_1.z.string().optional(),
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();
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 () => {
const server = await (0, exports.setupServer)();
const transport = new stdio_js_1.StdioServerTransport();
await server.connect(transport);
return server;
};
exports.connectStdioMcpServer = connectStdioMcpServer;
//# sourceMappingURL=server.js.map