mcp-osp-prompt 1.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,358 @@
1
+ /**
2
+ * 资源管理器模块
3
+ *
4
+ * 提供统一的资源访问API,用于获取projects和rules内容
5
+ * Task 2.1 - 资源访问接口层
6
+ */
7
+
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import { CFG } from './config.js';
11
+
12
+ /**
13
+ * 读取资源缓存
14
+ * @returns {Object|null} 缓存的资源对象
15
+ */
16
+ export async function readResourceCache() {
17
+ const cacheFile = path.join(CFG.cacheDir, 'resource-cache.json');
18
+
19
+ try {
20
+ const data = await fs.readFile(cacheFile, 'utf8');
21
+ const resources = JSON.parse(data);
22
+
23
+ if (resources && resources.prompts && resources.projects && resources.rules) {
24
+ return resources;
25
+ }
26
+
27
+ return null;
28
+ } catch (error) {
29
+ console.warn(`[ResourceManager] No resource cache found: ${error.message}`);
30
+ return null;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * 获取项目列表
36
+ * @returns {Array} 项目文档列表
37
+ */
38
+ export async function getProjectsList() {
39
+ try {
40
+ const cache = await readResourceCache();
41
+
42
+ if (!cache || !cache.projects) {
43
+ console.warn('[ResourceManager] No projects found in cache');
44
+ return [];
45
+ }
46
+
47
+ console.log(`[ResourceManager] Found ${cache.projects.length} projects`);
48
+ return cache.projects;
49
+ } catch (error) {
50
+ console.error(`[ResourceManager] Failed to get projects list: ${error.message}`);
51
+ return [];
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 获取项目文档内容
57
+ * @param {String} projectName - 项目文件名 (如 'kiki-framework-wiki.md')
58
+ * @returns {String|null} 项目文档内容
59
+ */
60
+ export async function getProjectContent(projectName) {
61
+ try {
62
+ // 项目文档缓存在 projects 子目录
63
+ const cacheFile = path.join(CFG.cacheDir, 'projects', projectName);
64
+
65
+ const content = await fs.readFile(cacheFile, 'utf8');
66
+ console.log(`[ResourceManager] Loaded project: ${projectName} (${content.length} bytes)`);
67
+
68
+ return content;
69
+ } catch (error) {
70
+ console.warn(`[ResourceManager] Failed to load project ${projectName}: ${error.message}`);
71
+
72
+ // 尝试从远程拉取(TODO: 在后续优化中实现)
73
+ return null;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * 根据语言获取规则文件信息
79
+ * @param {String} language - 语言名称 (如 'java', 'python')
80
+ * @returns {Object|null} 规则文件信息
81
+ */
82
+ export async function getRulesByLanguage(language) {
83
+ try {
84
+ const cache = await readResourceCache();
85
+
86
+ if (!cache || !cache.rules) {
87
+ console.warn('[ResourceManager] No rules found in cache');
88
+ return null;
89
+ }
90
+
91
+ // 查找匹配的规则文件
92
+ const rule = cache.rules.find(r => r.language === language);
93
+
94
+ if (rule) {
95
+ console.log(`[ResourceManager] Found rule for language: ${language}`);
96
+ return rule;
97
+ }
98
+
99
+ console.warn(`[ResourceManager] No rule found for language: ${language}`);
100
+ return null;
101
+ } catch (error) {
102
+ console.error(`[ResourceManager] Failed to get rules for ${language}: ${error.message}`);
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 获取规则文档内容
109
+ * @param {String} ruleFilename - 规则文件名 (如 'java-code-rules.md')
110
+ * @returns {String|null} 规则文档内容
111
+ */
112
+ export async function getRuleContent(ruleFilename) {
113
+ try {
114
+ // 规则文档缓存在 rules 子目录
115
+ const cacheFile = path.join(CFG.cacheDir, 'rules', ruleFilename);
116
+
117
+ const content = await fs.readFile(cacheFile, 'utf8');
118
+ console.log(`[ResourceManager] Loaded rule: ${ruleFilename} (${content.length} bytes)`);
119
+
120
+ return content;
121
+ } catch (error) {
122
+ console.warn(`[ResourceManager] Failed to load rule ${ruleFilename}: ${error.message}`);
123
+
124
+ // 尝试从远程拉取(TODO: 在后续优化中实现)
125
+ return null;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * 根据语言获取规则内容(便捷函数)
131
+ * @param {String} language - 语言名称
132
+ * @returns {String|null} 规则内容
133
+ */
134
+ export async function getRuleContentByLanguage(language) {
135
+ try {
136
+ const ruleInfo = await getRulesByLanguage(language);
137
+
138
+ if (!ruleInfo) {
139
+ return null;
140
+ }
141
+
142
+ return await getRuleContent(ruleInfo.filename);
143
+ } catch (error) {
144
+ console.error(`[ResourceManager] Failed to get rule content for ${language}: ${error.message}`);
145
+ return null;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * 获取所有可用的规则语言列表
151
+ * @returns {Array} 语言名称数组
152
+ */
153
+ export async function getAvailableLanguages() {
154
+ try {
155
+ const cache = await readResourceCache();
156
+
157
+ if (!cache || !cache.rules) {
158
+ return [];
159
+ }
160
+
161
+ const languages = cache.rules
162
+ .map(r => r.language)
163
+ .filter(Boolean);
164
+
165
+ console.log(`[ResourceManager] Available languages: ${languages.join(', ')}`);
166
+ return languages;
167
+ } catch (error) {
168
+ console.error(`[ResourceManager] Failed to get available languages: ${error.message}`);
169
+ return [];
170
+ }
171
+ }
172
+
173
+ /**
174
+ * 检查资源是否已同步
175
+ * @returns {Boolean} true表示资源已同步
176
+ */
177
+ export async function isResourcesSynced() {
178
+ try {
179
+ const cache = await readResourceCache();
180
+ return cache !== null;
181
+ } catch (error) {
182
+ return false;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * 获取资源同步时间
188
+ * @returns {String|null} ISO时间字符串
189
+ */
190
+ export async function getLastSyncTime() {
191
+ try {
192
+ const cache = await readResourceCache();
193
+ return cache ? cache.lastSync : null;
194
+ } catch (error) {
195
+ return null;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * 🟢 GREEN: 读取文件的前几行作为摘要
201
+ * @param {string} filePath - 文件路径
202
+ * @param {number} maxLines - 最多读取的行数
203
+ * @returns {string} 摘要内容
204
+ */
205
+ async function readFileSummary(filePath, maxLines = 2) {
206
+ try {
207
+ const content = await fs.readFile(filePath, 'utf-8');
208
+ const allLines = content.split('\n');
209
+
210
+ // 去掉markdown标题符号和空行,获取前N行有效内容
211
+ const validLines = allLines
212
+ .map(line => line.replace(/^#+\s*/, '').trim())
213
+ .filter(line => line.length > 0)
214
+ .slice(0, maxLines); // 取前N行有效内容
215
+
216
+ // 每行限制120字符,用换行符连接
217
+ const summary = validLines
218
+ .map(line => line.substring(0, 120))
219
+ .join('\n '); // 第二行缩进以便区分
220
+
221
+ return summary || 'No description available';
222
+ } catch (error) {
223
+ return 'Description not available';
224
+ }
225
+ }
226
+
227
+ /**
228
+ * 🟢 GREEN: 格式化resources列表为轻量级文本(供prompt使用)
229
+ * @param {Array<string>} resourceTypes - 需要的resource类型 ['projects', 'rules']
230
+ * @returns {string} 格式化的文本
231
+ */
232
+ export async function formatResourcesList(resourceTypes = ['projects', 'rules']) {
233
+ try {
234
+ const cache = await readResourceCache();
235
+ if (!cache) {
236
+ return '# Available Resources\n\n(No resources cached yet)\n';
237
+ }
238
+
239
+ let text = '# Available Resources\n\n';
240
+
241
+ // 格式化projects
242
+ if (resourceTypes.includes('projects') && cache.projects && cache.projects.length > 0) {
243
+ text += '## Projects Documentation\n';
244
+ for (const project of cache.projects) {
245
+ // 尝试读取文件摘要
246
+ const projectPath = path.join(CFG.cacheDir, 'projects', project.filename);
247
+ const summary = await readFileSummary(projectPath);
248
+ text += `- **${project.filename}**: ${summary}\n`;
249
+ }
250
+ text += '\n';
251
+ }
252
+
253
+ // 格式化rules
254
+ if (resourceTypes.includes('rules') && cache.rules && cache.rules.length > 0) {
255
+ text += '## Rules & Standards\n';
256
+ for (const rule of cache.rules) {
257
+ // 尝试读取文件摘要
258
+ const rulePath = path.join(CFG.cacheDir, 'rules', rule.filename);
259
+ const summary = await readFileSummary(rulePath);
260
+ text += `- **${rule.filename}**: ${summary}\n`;
261
+ }
262
+ text += '\n';
263
+ }
264
+
265
+ text += '---\n\n';
266
+ text += '*To load full content of a resource, use the `load-resource` tool when needed.*\n\n';
267
+
268
+ return text;
269
+ } catch (error) {
270
+ console.error('[ResourceManager] Failed to format resources list:', error.message);
271
+ return '# Available Resources\n\n(Error loading resources)\n';
272
+ }
273
+ }
274
+
275
+ /**
276
+ * 🟢 GREEN: 加载resource的完整内容
277
+ * @param {string} resourceName - 资源名称(不含路径,如 "java-rules.md")
278
+ * @returns {Object} { success: boolean, content: string, error?: string }
279
+ */
280
+ export async function loadResourceContent(resourceName) {
281
+ try {
282
+ const cache = await readResourceCache();
283
+ if (!cache) {
284
+ return { success: false, error: 'No resources cached' };
285
+ }
286
+
287
+ // 查找resource在哪个类型中
288
+ let resourcePath = null;
289
+ let resourceType = null;
290
+
291
+ // 检查projects
292
+ if (cache.projects.some(p => p.filename === resourceName)) {
293
+ resourcePath = path.join(CFG.cacheDir, 'projects', resourceName);
294
+ resourceType = 'project';
295
+ }
296
+ // 检查rules
297
+ else if (cache.rules.some(r => r.filename === resourceName)) {
298
+ resourcePath = path.join(CFG.cacheDir, 'rules', resourceName);
299
+ resourceType = 'rule';
300
+ }
301
+
302
+ if (!resourcePath) {
303
+ return { success: false, error: `Resource "${resourceName}" not found in cache` };
304
+ }
305
+
306
+ // 读取文件内容
307
+ const content = await fs.readFile(resourcePath, 'utf-8');
308
+ return {
309
+ success: true,
310
+ content: content,
311
+ type: resourceType,
312
+ filename: resourceName
313
+ };
314
+ } catch (error) {
315
+ return { success: false, error: error.message };
316
+ }
317
+ }
318
+
319
+ /**
320
+ * 获取资源统计信息
321
+ * @returns {Object} 统计信息
322
+ */
323
+ export async function getResourceStats() {
324
+ try {
325
+ const cache = await readResourceCache();
326
+
327
+ if (!cache) {
328
+ return {
329
+ synced: false,
330
+ prompts: 0,
331
+ projects: 0,
332
+ rules: 0,
333
+ total: 0,
334
+ lastSync: null
335
+ };
336
+ }
337
+
338
+ return {
339
+ synced: true,
340
+ prompts: cache.prompts?.length || 0,
341
+ projects: cache.projects?.length || 0,
342
+ rules: cache.rules?.length || 0,
343
+ total: (cache.prompts?.length || 0) + (cache.projects?.length || 0) + (cache.rules?.length || 0),
344
+ lastSync: cache.lastSync
345
+ };
346
+ } catch (error) {
347
+ console.error(`[ResourceManager] Failed to get resource stats: ${error.message}`);
348
+ return {
349
+ synced: false,
350
+ prompts: 0,
351
+ projects: 0,
352
+ rules: 0,
353
+ total: 0,
354
+ lastSync: null
355
+ };
356
+ }
357
+ }
358
+
package/server.js ADDED
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ import process from 'process';
3
+ import { initializePrompts, getToolsConfiguration } from './prompt-manager.js';
4
+ import { handleDevTool, handleDevManual, handleDevFeedback, handleLoadResource } from './tools.js';
5
+ import { CFG } from './config.js';
6
+ import {
7
+ createSuccessResponse,
8
+ createErrorResponse,
9
+ MCP_ERROR_CODES,
10
+ isNotification,
11
+ createToolDescription,
12
+
13
+ withErrorHandling,
14
+ SERVER_INFO,
15
+ SERVER_CAPABILITIES
16
+ } from './utils.js';
17
+
18
+ // 🛡️ 增强错误处理 - 防止进程崩溃导致Cursor显示红色状态
19
+ const debug = process.env.DEBUG_LOG === 'true';
20
+
21
+ process.on('uncaughtException', (error) => {
22
+ console.error('🚨 [MCP-Server] Uncaught Exception:', error.message);
23
+ if (debug) {
24
+ console.error(error.stack);
25
+ }
26
+ // 不要退出进程,继续服务
27
+ });
28
+
29
+ process.on('unhandledRejection', (reason, promise) => {
30
+ console.error('🚨 [MCP-Server] Unhandled Rejection:', reason);
31
+ // 不要退出进程,继续服务
32
+ });
33
+
34
+ // 📊 内存监控 - 防止内存泄漏
35
+ if (debug) {
36
+ setInterval(() => {
37
+ const usage = process.memoryUsage();
38
+ if (usage.heapUsed > 100 * 1024 * 1024) { // 100MB
39
+ console.warn('⚠️ [MCP-Server] High memory usage:', Math.round(usage.heapUsed / 1024 / 1024) + 'MB');
40
+ }
41
+ }, 300000); // 每5分钟检查一次
42
+ }
43
+
44
+ // 🔄 优雅关闭处理
45
+ process.on('SIGTERM', () => {
46
+ console.log('📴 [MCP-Server] Received SIGTERM, shutting down gracefully');
47
+ process.exit(0);
48
+ });
49
+
50
+ process.on('SIGINT', () => {
51
+ console.log('📴 [MCP-Server] Received SIGINT, shutting down gracefully');
52
+ process.exit(0);
53
+ });
54
+
55
+ // 全局工具配置和就绪状态
56
+ let globalToolsConfig = [];
57
+ let isServerReady = false;
58
+
59
+ // 同步初始化函数 - 阻塞式等待初始化完成
60
+ async function initializeServerSync() {
61
+ try {
62
+ console.error('[MCP-Server] Starting synchronous initialization...');
63
+
64
+ // 同步初始化prompt管理器
65
+ globalToolsConfig = await initializePrompts();
66
+
67
+ // 标记服务器就绪
68
+ isServerReady = true;
69
+ console.error(`[MCP-Server] ✅ Server ready with ${globalToolsConfig.length} tools`);
70
+
71
+ return true;
72
+ } catch (error) {
73
+ console.error('[MCP-Server] ❌ Initialization failed:', error.message);
74
+ console.error('[MCP-Server] Server will not accept requests');
75
+ isServerReady = false;
76
+
77
+ // 🟢 GREEN: Task 2.1 - 优雅降级而不是崩溃
78
+ console.error('[MCP-Server] ⚠️ Entering degraded mode due to initialization failure');
79
+
80
+ // 设置基本的降级工具配置
81
+ globalToolsConfig = getBasicFallbackTools();
82
+ isServerReady = true; // 允许服务器继续运行
83
+
84
+ console.error(`[MCP-Server] ✅ Degraded mode active with ${globalToolsConfig.length} basic tools`);
85
+ return false; // 表示降级模式
86
+ }
87
+ }
88
+
89
+ const handleRequest = withErrorHandling(async (req) => {
90
+ if (isNotification(req)) return null;
91
+
92
+ // 检查服务器是否就绪
93
+ if (!isServerReady && (req.method === 'tools/list' || req.method === 'tools/call')) {
94
+ return createErrorResponse(req.id, MCP_ERROR_CODES.INTERNAL_ERROR,
95
+ 'MCP server still initializing, please wait');
96
+ }
97
+
98
+ switch (req.method) {
99
+ case 'initialize':
100
+ return createSuccessResponse(req.id, {
101
+ protocolVersion: '2024-11-05',
102
+ capabilities: SERVER_CAPABILITIES,
103
+ serverInfo: SERVER_INFO
104
+ });
105
+
106
+
107
+ case 'tools/list':
108
+ return createSuccessResponse(req.id, {
109
+ tools: getToolsList()
110
+ });
111
+
112
+ case 'tools/call':
113
+ return await handleToolCall(req);
114
+
115
+ default:
116
+ return createErrorResponse(req.id, MCP_ERROR_CODES.METHOD_NOT_FOUND,
117
+ `Method ${req.method} not found`);
118
+ }
119
+ });
120
+
121
+
122
+
123
+ // 🟢 GREEN: Task 2.1 - 降级模式下的基础工具
124
+ function getBasicFallbackTools() {
125
+ return [
126
+ {
127
+ name: 'dev-manual',
128
+ type: 'handler',
129
+ handler: 'handleDevManual',
130
+ description: '手动选择开发类型(降级模式)',
131
+ schema: {
132
+ type: 'object',
133
+ properties: {
134
+ random_string: { description: 'Dummy parameter for no-parameter tools', type: 'string' }
135
+ },
136
+ required: ['random_string']
137
+ }
138
+ },
139
+ {
140
+ name: 'dev-feedback',
141
+ type: 'handler',
142
+ handler: 'handleDevFeedback',
143
+ description: '反馈确认工具(降级模式)',
144
+ schema: {
145
+ type: 'object',
146
+ properties: {
147
+ title: { type: 'string' },
148
+ message: { type: 'string' },
149
+ options: { type: 'array', items: { type: 'string' } }
150
+ },
151
+ required: ['title', 'message']
152
+ }
153
+ }
154
+ ];
155
+ }
156
+
157
+ function getToolsList() {
158
+ // Generate tools from unified prompt manager
159
+ if (!isServerReady) {
160
+ console.warn('[MCP-Server] Warning: getToolsList called before initialization completed');
161
+ return [];
162
+ }
163
+
164
+ return globalToolsConfig.map(toolConfig =>
165
+ createToolDescription(
166
+ toolConfig.name,
167
+ toolConfig.description,
168
+ toolConfig.schema
169
+ )
170
+ );
171
+ }
172
+
173
+ async function handleToolCall(req) {
174
+ const name = req.params?.name;
175
+ const args = req.params?.arguments || {};
176
+ let resultText;
177
+
178
+
179
+ // Find tool in global configuration
180
+ const tool = globalToolsConfig.find(t => t.name === name);
181
+ if (!tool) {
182
+ return createErrorResponse(req.id, MCP_ERROR_CODES.INVALID_PARAMS, 'Unknown tool');
183
+ }
184
+
185
+ try {
186
+ if (tool.type === 'handler') {
187
+ // Handle special tools with dedicated handlers
188
+ switch (tool.handler) {
189
+ case 'handleDevManual':
190
+ resultText = await handleDevManual();
191
+ break;
192
+ case 'handleDevFeedback':
193
+ const feedbackResult = await handleDevFeedback(args);
194
+
195
+ // Format feedback result for AI consumption
196
+ if (typeof feedbackResult === 'object' && feedbackResult.hasUserInput) {
197
+ resultText = `📝 **用户反馈处理结果**
198
+
199
+ **用户选择:** ${feedbackResult.action}
200
+ **具体建议:** "${feedbackResult.input}"
201
+
202
+ 🔄 **AI指令:** 请根据用户的具体建议调整当前的执行计划。用户明确要求进行修改,请:
203
+ 1. 分析用户的建议内容
204
+ 2. 调整相应的实现方案
205
+ 3. 说明具体如何应用这个建议
206
+ 4. 继续执行调整后的计划
207
+
208
+ ⚠️ **MANDATORY NEXT STEP REMINDER:** 在完成下一个实施步骤后,必须再次调用dev-feedback工具汇报进度并获取确认。
209
+
210
+ **重要:** 不要忽略用户的建议,必须基于此建议修改后续的执行步骤。`;
211
+ } else {
212
+ resultText = `✅ **用户确认:** ${feedbackResult}
213
+
214
+ 继续按原计划执行。
215
+
216
+ ⚠️ **MANDATORY NEXT STEP REMINDER:** 在完成下一个实施步骤后,必须再次调用dev-feedback工具汇报进度并获取确认。每个步骤完成后都需要调用feedback工具 - 这是强制要求!`;
217
+ }
218
+ break;
219
+ case 'handleLoadResource':
220
+ resultText = await handleLoadResource(args);
221
+ break;
222
+ default:
223
+ throw new Error(`Unknown handler: ${tool.handler}`);
224
+ }
225
+ } else if (tool.type === 'prompt') {
226
+ // Handle prompt-based tools
227
+ const promptResult = await handleDevTool(name, args);
228
+
229
+ // Add mandatory feedback reminder to all prompt-based tools
230
+ resultText = `${promptResult}
231
+
232
+ ⚠️ **CRITICAL WORKFLOW REQUIREMENT:**
233
+ 1. 在制定好plan后,第一步开始执行前,必须调用dev-feedback工具将计划展示给用户确认
234
+ 2. 每个实施步骤完成后,必须调用dev-feedback工具汇报进度并获取确认后再继续下一步
235
+ 3. 这是强制要求,不可跳过!每次调用feedback后会自动提醒下一步也需要再次调用feedback
236
+
237
+ **请严格遵守此工作流程,确保每个步骤都有用户确认。**`;
238
+ } else {
239
+ throw new Error(`Unknown tool type: ${tool.type}`);
240
+ }
241
+
242
+ return createSuccessResponse(req.id, {
243
+ content: [{ type: 'text', text: resultText }]
244
+ });
245
+ } catch (error) {
246
+ // 简洁的错误日志记录
247
+ console.error(`[MCP错误] 工具: ${name}, 错误: ${error.message}`);
248
+
249
+ // 特殊处理:某些"错误"实际上是正常的用户交互结果
250
+ if (error.message.includes('用户要求调整计划')) {
251
+ console.log(`[MCP信息] 用户计划调整请求: ${error.message}`);
252
+ return createSuccessResponse(req.id, {
253
+ content: [{ type: 'text', text: error.message }]
254
+ });
255
+ }
256
+
257
+ // 提供简洁直观的错误信息
258
+ let errorMsg = error.message;
259
+
260
+ // 针对常见错误场景提供更清晰的信息
261
+ if (error.message.includes('fetch fail') || error.message.includes('API')) {
262
+ errorMsg = `网络请求失败 - ${error.message}`;
263
+ } else if (error.message.includes('RESOURCE_PATH')) {
264
+ errorMsg = `配置错误 - ${error.message}`;
265
+ } else if (error.message.includes('Unknown prompt file')) {
266
+ errorMsg = `文件未找到 - ${error.message}`;
267
+ } else if (error.message.includes('429') || error.message.includes('API速率限制')) {
268
+ // 429错误不应该导致MCP崩溃,这是正常的速率限制
269
+ errorMsg = `缓存已生效,临时API限制 - 功能正常可用`;
270
+ }
271
+
272
+ return createErrorResponse(req.id, MCP_ERROR_CODES.INTERNAL_ERROR, errorMsg);
273
+ }
274
+ }
275
+
276
+ // 🟢 GREEN: Task 2.1 - 支持优雅降级的初始化
277
+ const initSuccess = await initializeServerSync();
278
+ if (!initSuccess) {
279
+ console.error('[MCP-Server] ⚠️ Running in degraded mode');
280
+ }
281
+
282
+ if (CFG.debug) {
283
+ console.error('[MCP-Server] Debug mode enabled');
284
+ }
285
+
286
+ // 初始化完成后才开始监听stdin
287
+ process.stdin.setEncoding('utf8');
288
+ let buffer = '';
289
+ process.stdin.on('data', chunk => {
290
+ buffer += chunk;
291
+ let idx;
292
+ while ((idx = buffer.indexOf('\n')) >= 0) {
293
+ const line = buffer.slice(0, idx);
294
+ buffer = buffer.slice(idx + 1);
295
+ if (!line.trim()) continue;
296
+ let req;
297
+ try { req = JSON.parse(line); } catch { continue; }
298
+ handleRequest(req).then(res => {
299
+ if (res !== undefined && res !== null) process.stdout.write(JSON.stringify(res) + '\n');
300
+ }).catch(err => {
301
+ const id = req?.id;
302
+ if (id !== undefined) process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32603, message: err.message } }) + '\n');
303
+ });
304
+ }
305
+ });