@liangshanli/mcp-server-project-standards 3.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,172 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Generate rules content based on project standards
6
+ * @param {Object} params - Parameters
7
+ * @param {boolean} params.save - Whether to save to file (default: false)
8
+ * @param {Object} config - Server configuration
9
+ * @returns {Object} Content, savePath and status
10
+ */
11
+ async function generate_cursorrules(params, config) {
12
+ const { save = false } = params || {};
13
+ const projectPath = process.env.PROJECT_PATH || (global.isCursor ? '.' : null);
14
+
15
+ if (!projectPath) {
16
+ throw new Error('Rules generation is disabled: PROJECT_PATH is not set and client is not Cursor.');
17
+ }
18
+
19
+ if (!config) {
20
+ throw new Error('Configuration not found');
21
+ }
22
+
23
+ let rules = `---
24
+ alwaysApply: true
25
+ ---
26
+
27
+ # Project Standards and Rules
28
+
29
+ ## AI Assistant Guidelines
30
+ You are an expert AI developer assistant. Your primary goal is to maintain and evolve this project while strictly adhering to the standards defined below.
31
+
32
+ ### Core Principles
33
+ 1. **Always Discover**: Before making changes, use \`list_directory\` and \`project_structure\` to understand the current context.
34
+ 2. **Follow Standards**: Consult \`api_standards\`, \`development_standards\`, and \`database_standards\` before implementing new logic.
35
+ 3. **API Integrity**: Use \`api_debug\` to test APIs and ensure they match the \`api_standards\`.
36
+ 4. **Consistency**: Match existing code patterns, naming conventions, and architectural styles.
37
+
38
+ ### MCP Tool Usage Workflow
39
+ - **Information**: Use \`project_info\` to get/set high-level project metadata.
40
+ - **Navigation**: Use \`list_directory\` (with depth) to explore the file system.
41
+ - **Compliance**: Use \`api_standards\`, \`development_standards\`, and \`database_standards\` to get specific requirements.
42
+ - **Testing**: Use \`api_debug\` for direct testing and \`api_execute\` for regression testing of configured APIs.
43
+ - **Configuration**: Use \`api_config\` to manage the API list and base URLs.
44
+
45
+ ## MCP-Driven Project Standards Enforcement Rule
46
+
47
+ ### 1. Core Roles and Principles
48
+ - **Role Positioning**: You are a Chief Architect strictly governed by the \`mcp-server-project-standards\` specifications.
49
+ - **Supreme Principle**: **Tool Data > Documentation Content > Memory Hallucinations**. It is forbidden to assume project structure, API specifications, or development standards from memory without calling MCP tools.
50
+
51
+ ### 2. Mandatory Tool Call (Tool-First Policy)
52
+ - **No Hard Reading of Long Documents**: When obtaining project specifications, architectural standards, or API definitions, **it is forbidden** to read full \`.md\` files in the \`docs/\` directory or long documents exceeding 100 lines.
53
+ - **Mandatory Initialization**: When a conversation starts or a task switches, tools provided by \`mcp-server-project-standards\` (e.g., \`project_info\`, \`project_structure\`, \`api_standards\`) must be prioritized.
54
+ - **Request on Demand**: Only apply to read specific file code snippets when the summary information returned by MCP tools is insufficient to support current code writing.
55
+
56
+ ### 3. Context Anti-Forgetfulness and Alignment Mechanism (Context Maintenance)
57
+ - **Reread After Compression (Refresh Trigger)**: When you perceive that context is compressed (Summarized), conversations exceed 10 rounds, or project background becomes blurred, **you must proactively re-call** MCP tools to refresh the latest standard data.
58
+ - **Confirmation Before Action**: Before executing refactoring or generating new modules, please declare: "Aligning project specifications via MCP..." and operate based on the results returned by the tools.
59
+ - **Reject Deviation**: If the user's instructions violate the \`standards\` returned by MCP tools, be sure to point out the conflict points and provide correction plans that comply with the specifications.
60
+
61
+ ### 4. Token Consumption and Cost Optimization (Gemini 3 Flash Optimized)
62
+ - **Cache-First Strategy**: Obtain short, structured results via MCP tools. This high-repetition short text can significantly trigger Gemini's Context Caching, reducing reading costs to $0.05/1M.
63
+ - **Incremental Output**: Full rewriting is strictly prohibited. You must only output the affected code Diff or minimized code blocks (Output cost is $3/1M, must be streamlined).
64
+ - **No Redundant Replies**: No need for polite language; directly output tool call results and code change suggestions.
65
+
66
+ ### 5. Automated Documentation Synchronization
67
+ - **State Awareness**: After modifying core logic, you must ask whether to call MCP tools to synchronously update project standard descriptions or API snapshots, ensuring "code is the standard".
68
+
69
+ ## Dynamic MCP Tool Call Logic
70
+ - **Project Identification**: Before executing any task, please first determine the current working sub-project path (e.g., \`./apps/user-service\`).
71
+ - **Parameter Binding**: When calling tools of \`mcp-server-project-standards\`, the \`path\` or \`project_name\` parameters **must** be automatically bound to the currently detected sub-project.
72
+ - **Tool Mapping Guidance**:
73
+ - To understand architectural layering -> Call \`project_structure\`
74
+ - To understand API interface standards -> Call \`api_standards\`
75
+ - To perform compliance checks -> Call \`development_standards\`
76
+ - **Auto Alignment**: When the model finds that context is compressed or project background is lost, it must **force** a re-call of \`project_info\` and specify the current sub-project path to ensure that standards do not cross-project contaminate.
77
+
78
+ ## Context Management and Anti-Forgetfulness Mechanism
79
+ - **Reread After Compression**: Whenever you (AI) perceive that context is compressed, conversation rounds are too many (e.g., more than 10), or "Context Drift" begins to appear, **you must proactively re-call** core tools of \`mcp-server-project-standards\` (e.g., \`project_info\`).
80
+ - **Synchronization Declaration**: Before executing the first code generation after compression, please briefly declare at the beginning of the reply: "Project specification tools re-aligned" to ensure logical consistency.
81
+ - **Mandatory Alignment**: Any time code is generated, if you are uncertain about the current API standards or structural standards due to context compression, you must first call tools to query; writing based on memory (hallucination) is strictly prohibited.
82
+
83
+ `;
84
+
85
+ // Project Info
86
+ if (config.project_info) {
87
+ rules += `## Project Information\n`;
88
+ if (config.project_info.projectName) rules += `- **Project Name**: ${config.project_info.projectName}\n`;
89
+ if (config.project_info.developmentLanguage) rules += `- **Development Language**: ${config.project_info.developmentLanguage}\n`;
90
+ if (config.project_info.basicInfo) rules += `- **Basic Info**: ${config.project_info.basicInfo}\n`;
91
+ rules += `\n`;
92
+ }
93
+
94
+ // Project Structure
95
+ if (config.project_structure && config.project_structure.length > 0) {
96
+ rules += `## Project Structure\n`;
97
+ config.project_structure.forEach(item => {
98
+ rules += `- \`${item.path}\`: ${item.description}\n`;
99
+ });
100
+ rules += `\n`;
101
+ }
102
+
103
+ // API Standards
104
+ if (config.api_standards) {
105
+ rules += `## API Standards\n`;
106
+ if (config.api_standards.interfaceType) rules += `- **Interface Type**: ${config.api_standards.interfaceType}\n`;
107
+ if (config.api_standards.successStructure) rules += `- **Success Structure**: \`${config.api_standards.successStructure}\`\n`;
108
+ if (config.api_standards.errorStructure) rules += `- **Error Structure**: \`${config.api_standards.errorStructure}\`\n`;
109
+
110
+ if (config.api_standards.requirements && config.api_standards.requirements.length > 0) {
111
+ rules += `### API Requirements\n`;
112
+ config.api_standards.requirements.forEach(req => {
113
+ rules += `- ${req}\n`;
114
+ });
115
+ }
116
+ rules += `\n`;
117
+ }
118
+
119
+ // Development Standards
120
+ if (config.development_standards && config.development_standards.length > 0) {
121
+ rules += `## Development Standards\n`;
122
+ config.development_standards.forEach(std => {
123
+ rules += `- ${std}\n`;
124
+ });
125
+ rules += `\n`;
126
+ }
127
+
128
+ // Database Standards
129
+ if (config.database_standards && config.database_standards.length > 0) {
130
+ rules += `## Database Standards\n`;
131
+ config.database_standards.forEach(std => {
132
+ rules += `- ${std}\n`;
133
+ });
134
+ rules += `\n`;
135
+ }
136
+
137
+ // Determine file name and path
138
+ const toolPrefix = process.env.TOOL_PREFIX || 'project';
139
+ const fileName = global.isCursor ? path.join('.cursor', 'rules', toolPrefix, 'RULE.md') : 'PROJECT_RULES.md';
140
+ const rulesPath = path.resolve(projectPath, fileName);
141
+
142
+ // System Metadata
143
+ rules += `## System Environment\n`;
144
+ rules += `- **Project Path**: \`${projectPath}\`\n`;
145
+ rules += `\n`;
146
+
147
+ if (save) {
148
+ try {
149
+ // Ensure directory exists
150
+ await fs.ensureDir(path.dirname(rulesPath));
151
+ await fs.writeFile(rulesPath, rules, 'utf8');
152
+ return {
153
+ success: true,
154
+ message: `${path.basename(rulesPath)} has been saved to ${rulesPath}`,
155
+ content: rules,
156
+ savePath: rulesPath
157
+ };
158
+ } catch (err) {
159
+ throw new Error(`Failed to save ${path.basename(rulesPath)}: ${err.message}`);
160
+ }
161
+ }
162
+
163
+ return {
164
+ success: true,
165
+ content: rules,
166
+ savePath: rulesPath,
167
+ suggestedFileName: fileName,
168
+ message: `Rules generated. Please review the content. You can call this tool with { "save": true } to save it to ${rulesPath}`
169
+ };
170
+ }
171
+
172
+ module.exports = generate_cursorrules;
@@ -23,7 +23,7 @@ async function api_standards(params, config, saveConfig) {
23
23
 
24
24
  if (action === 'get') {
25
25
  try {
26
- // 从配置文件的 api_standards 字段中获取标准,如果没有则使用默认值
26
+ // Get standards from api_standards field in config file, use default if not present
27
27
  const apiStandards = config?.api_standards || {
28
28
  interfaceType: 'restful',
29
29
  successStructure: {
@@ -74,33 +74,33 @@ async function api_standards(params, config, saveConfig) {
74
74
  }
75
75
 
76
76
  try {
77
- // 确保 config.api_standards 存在
77
+ // Ensure config.api_standards exists
78
78
  if (!config.api_standards) {
79
79
  config.api_standards = {};
80
80
  }
81
81
 
82
- // 处理数组类型的合并逻辑
82
+ // Handle merging logic for array types
83
83
  if (Array.isArray(value) && !forceOverwrite) {
84
- // 如果 forceOverwrite false,则合并数组而不是覆盖
84
+ // Merge array instead of overwrite if forceOverwrite is false
85
85
  if (!config.api_standards[key]) {
86
86
  config.api_standards[key] = [];
87
87
  }
88
88
 
89
- // 确保现有值是数组
89
+ // Ensure existing value is an array
90
90
  if (!Array.isArray(config.api_standards[key])) {
91
91
  config.api_standards[key] = [];
92
92
  }
93
93
 
94
- // 合并数组,去重
94
+ // Merge array and remove duplicates
95
95
  const existingArray = config.api_standards[key];
96
96
  const newArray = [...new Set([...existingArray, ...value])];
97
97
  config.api_standards[key] = newArray;
98
98
  } else {
99
- // 直接覆盖
99
+ // Overwrite directly
100
100
  config.api_standards[key] = value;
101
101
  }
102
102
 
103
- // 保存配置
103
+ // Save configuration
104
104
  const saved = saveConfig(config);
105
105
  if (!saved) {
106
106
  throw new Error('Failed to save configuration');
@@ -131,23 +131,23 @@ async function api_standards(params, config, saveConfig) {
131
131
  }
132
132
 
133
133
  try {
134
- // 确保 config.api_standards 存在
134
+ // Ensure config.api_standards exists
135
135
  if (!config.api_standards) {
136
136
  config.api_standards = {};
137
137
  }
138
138
 
139
- // 删除 header
139
+ // Delete header
140
140
  if (headerName) {
141
- // 确保 basicHeaders 存在
141
+ // Ensure basicHeaders exists
142
142
  if (!config.api_standards.basicHeaders) {
143
143
  config.api_standards.basicHeaders = {};
144
144
  }
145
145
 
146
- // 删除指定的 header
146
+ // Delete specified header
147
147
  if (config.api_standards.basicHeaders.hasOwnProperty(headerName)) {
148
148
  delete config.api_standards.basicHeaders[headerName];
149
149
 
150
- // 保存配置
150
+ // Save configuration
151
151
  const saved = saveConfig(config);
152
152
  if (!saved) {
153
153
  throw new Error('Failed to save configuration');
@@ -164,19 +164,19 @@ async function api_standards(params, config, saveConfig) {
164
164
  }
165
165
  }
166
166
 
167
- // 删除 requirement
167
+ // Delete requirement
168
168
  if (requirement) {
169
- // 确保 requirements 存在
169
+ // Ensure requirements exists
170
170
  if (!config.api_standards.requirements) {
171
171
  config.api_standards.requirements = [];
172
172
  }
173
173
 
174
- // 查找并删除指定的 requirement
174
+ // Find and delete specified requirement
175
175
  const requirementIndex = config.api_standards.requirements.indexOf(requirement);
176
176
  if (requirementIndex !== -1) {
177
177
  config.api_standards.requirements.splice(requirementIndex, 1);
178
178
 
179
- // 保存配置
179
+ // Save configuration
180
180
  const saved = saveConfig(config);
181
181
  if (!saved) {
182
182
  throw new Error('Failed to save configuration');
@@ -21,7 +21,7 @@ async function development_standards(params, config, saveConfig) {
21
21
 
22
22
  if (action === 'get') {
23
23
  try {
24
- // 从配置文件的 development_standards 字段中获取标准,如果没有则使用默认值
24
+ // Get standards from development_standards field in config file, use default if not present
25
25
  const developmentStandards = config?.development_standards || [
26
26
  'Use 2 spaces for indentation',
27
27
  'Use single quotes instead of double quotes',
@@ -61,23 +61,23 @@ async function development_standards(params, config, saveConfig) {
61
61
  }
62
62
 
63
63
  try {
64
- // 更新配置
64
+ // Update configuration
65
65
  if (!config.development_standards) {
66
66
  config.development_standards = [];
67
67
  }
68
68
 
69
- // 处理数组类型的合并逻辑
69
+ // Handle merging logic for array types
70
70
  if (!forceOverwrite) {
71
- // 如果 forceOverwrite false,则合并数组而不是覆盖
71
+ // Merge array instead of overwrite if forceOverwrite is false
72
72
  const existingArray = config.development_standards;
73
73
  const newArray = [...new Set([...existingArray, ...standards])];
74
74
  config.development_standards = newArray;
75
75
  } else {
76
- // 直接覆盖
76
+ // Overwrite directly
77
77
  config.development_standards = standards;
78
78
  }
79
79
 
80
- // 保存配置
80
+ // Save configuration
81
81
  const saved = saveConfig(config);
82
82
  if (!saved) {
83
83
  throw new Error('Failed to save configuration');
@@ -103,17 +103,17 @@ async function development_standards(params, config, saveConfig) {
103
103
  }
104
104
 
105
105
  try {
106
- // 确保 config.development_standards 存在
106
+ // Ensure config.development_standards exists
107
107
  if (!config.development_standards) {
108
108
  config.development_standards = [];
109
109
  }
110
110
 
111
- // 查找并删除指定的 standard
111
+ // Find and delete specified standard
112
112
  const standardIndex = config.development_standards.indexOf(standard);
113
113
  if (standardIndex !== -1) {
114
114
  config.development_standards.splice(standardIndex, 1);
115
115
 
116
- // 保存配置
116
+ // Save configuration
117
117
  const saved = saveConfig(config);
118
118
  if (!saved) {
119
119
  throw new Error('Failed to save configuration');
@@ -20,20 +20,20 @@ async function project_info(params, config, saveConfig) {
20
20
 
21
21
  if (action === 'get') {
22
22
  try {
23
- // 从配置文件的 project_info 字段中获取项目信息,如果没有则使用默认值
23
+ // Get project info from project_info field in config file, use default if not present
24
24
  const projectInfo = config?.project_info || '';
25
25
  const projectName = projectInfo.projectName || '';
26
26
  const developmentLanguage = projectInfo.developmentLanguage || '';
27
27
  const basicInfo = projectInfo.basicInfo || '';
28
28
 
29
29
  return {
30
- // 项目名称
30
+ // Project name
31
31
  projectName: projectName,
32
32
 
33
- // 开发语言
33
+ // Development language
34
34
  developmentLanguage: developmentLanguage,
35
35
 
36
- // 基本信息
36
+ // Basic info
37
37
  basicInfo: basicInfo,
38
38
 
39
39
  timestamp: new Date().toISOString()
@@ -55,15 +55,15 @@ async function project_info(params, config, saveConfig) {
55
55
  }
56
56
 
57
57
  try {
58
- // 确保 config.project_info 存在
58
+ // Ensure config.project_info exists
59
59
  if (!config.project_info) {
60
60
  config.project_info = {};
61
61
  }
62
62
 
63
- // 更新指定字段
63
+ // Update specified field
64
64
  config.project_info[key] = value;
65
65
 
66
- // 保存配置
66
+ // Save configuration
67
67
  const saved = saveConfig(config);
68
68
  if (!saved) {
69
69
  throw new Error('Failed to save configuration');
@@ -20,7 +20,7 @@ async function project_structure(params, config, saveConfig) {
20
20
 
21
21
  if (action === 'get') {
22
22
  try {
23
- // 从配置文件的 project_structure 字段中获取结构,如果没有则使用默认值
23
+ // Get structure from project_structure field in config file, use default if not present
24
24
  const projectStructure = config?.project_structure || [];
25
25
 
26
26
  return projectStructure;
@@ -37,29 +37,29 @@ async function project_structure(params, config, saveConfig) {
37
37
  }
38
38
 
39
39
  try {
40
- // 获取当前结构
40
+ // Get current structure
41
41
  let currentStructure = config?.project_structure || [];
42
42
 
43
- // 创建新结构数组
43
+ // Create new structure array
44
44
  const newStructure = [];
45
45
 
46
- // 循环处理每个新结构项
46
+ // Loop through each new structure item
47
47
  for (const item of structure) {
48
48
  if (!item.path || !item.description) {
49
49
  throw new Error('Each structure item must have "path" and "description" properties');
50
50
  }
51
51
 
52
- // 检查路径是否已存在
52
+ // Check if path already exists
53
53
  const existingIndex = currentStructure.findIndex(existing => existing.path === item.path);
54
54
 
55
55
  if (existingIndex !== -1) {
56
- // 路径存在,替换
56
+ // Path exists, replace
57
57
  currentStructure[existingIndex] = {
58
58
  path: item.path,
59
59
  description: item.description
60
60
  };
61
61
  } else {
62
- // 路径不存在,添加到新结构
62
+ // Path doesn't exist, add to new structure
63
63
  newStructure.push({
64
64
  path: item.path,
65
65
  description: item.description
@@ -67,16 +67,16 @@ async function project_structure(params, config, saveConfig) {
67
67
  }
68
68
  }
69
69
 
70
- // 将新结构附加到现有结构
70
+ // Append new structure to existing structure
71
71
  const updatedStructure = [...currentStructure, ...newStructure];
72
72
 
73
- // 更新配置
73
+ // Update configuration
74
74
  if (!config.project_structure) {
75
75
  config.project_structure = [];
76
76
  }
77
77
  config.project_structure = updatedStructure;
78
78
 
79
- // 保存配置
79
+ // Save configuration
80
80
  const saved = saveConfig(config);
81
81
  if (!saved) {
82
82
  throw new Error('Failed to save configuration');
@@ -103,17 +103,17 @@ async function project_structure(params, config, saveConfig) {
103
103
  }
104
104
 
105
105
  try {
106
- // 确保 config.project_structure 存在
106
+ // Ensure config.project_structure exists
107
107
  if (!config.project_structure) {
108
108
  config.project_structure = [];
109
109
  }
110
110
 
111
- // 查找并删除指定的路径
111
+ // Find and delete specified path
112
112
  const pathIndex = config.project_structure.findIndex(item => item.path === deletePath);
113
113
  if (pathIndex !== -1) {
114
114
  const deletedItem = config.project_structure.splice(pathIndex, 1)[0];
115
115
 
116
- // 保存配置
116
+ // Save configuration
117
117
  const saved = saveConfig(config);
118
118
  if (!saved) {
119
119
  throw new Error('Failed to save configuration');
@@ -0,0 +1,85 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * List directory structure
6
+ * @param {Object} params - Parameters
7
+ * @param {string} params.path - Subdirectory path to list (relative to PROJECT_PATH)
8
+ * @param {number} params.depth - Max depth to traverse (default: 2)
9
+ * @returns {Object} Directory structure
10
+ */
11
+ async function list_directory(params) {
12
+ const projectPath = process.env.PROJECT_PATH || (global.isCursor ? '.' : null);
13
+
14
+ if (!projectPath) {
15
+ throw new Error('Project structure tool is disabled: PROJECT_PATH is not set and client is not Cursor.');
16
+ }
17
+
18
+ const subPath = params?.path || '';
19
+ const maxDepth = params?.depth || 2;
20
+
21
+ const targetPath = path.resolve(projectPath, subPath);
22
+
23
+ if (!targetPath.startsWith(path.resolve(projectPath))) {
24
+ throw new Error('Access denied: Path is outside of project directory');
25
+ }
26
+
27
+ if (!fs.existsSync(targetPath)) {
28
+ throw new Error(`Path does not exist: ${subPath}`);
29
+ }
30
+
31
+ const stats = fs.statSync(targetPath);
32
+ if (!stats.isDirectory()) {
33
+ return {
34
+ name: path.basename(targetPath),
35
+ type: 'file',
36
+ path: subPath
37
+ };
38
+ }
39
+
40
+ async function getTree(currentPath, currentRelativePath, currentDepth) {
41
+ const name = path.basename(currentPath);
42
+ const result = {
43
+ name: name || '.',
44
+ type: 'directory',
45
+ path: currentRelativePath,
46
+ children: []
47
+ };
48
+
49
+ if (currentDepth >= maxDepth) {
50
+ return result;
51
+ }
52
+
53
+ try {
54
+ const items = await fs.readdir(currentPath);
55
+ for (const item of items) {
56
+ // Skip hidden files/folders (starting with .)
57
+ if (item.startsWith('.') && item !== '.setting') continue;
58
+ if (item === 'node_modules') continue;
59
+
60
+ const itemPath = path.join(currentPath, item);
61
+ const itemRelativePath = path.join(currentRelativePath, item);
62
+ const itemStats = await fs.stat(itemPath);
63
+
64
+ if (itemStats.isDirectory()) {
65
+ result.children.push(await getTree(itemPath, itemRelativePath, currentDepth + 1));
66
+ } else {
67
+ result.children.push({
68
+ name: item,
69
+ type: 'file',
70
+ path: itemRelativePath
71
+ });
72
+ }
73
+ }
74
+ } catch (err) {
75
+ console.error(`Error reading directory ${currentPath}:`, err.message);
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
+ return await getTree(targetPath, subPath, 0);
82
+ }
83
+
84
+ module.exports = list_directory;
85
+