143 lines
6.3 KiB
JavaScript
143 lines
6.3 KiB
JavaScript
"use strict";
|
||
/**
|
||
* Project Caffeine
|
||
* Copyright (c) 2025-2026 Gitconomy Research
|
||
*
|
||
* SPDX-License-Identifier: MIT
|
||
*
|
||
* Contributors:
|
||
* - 郭晧 <guohao@gitconomy.org> (Initial Author)
|
||
*/
|
||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||
if (k2 === undefined) k2 = k;
|
||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||
}
|
||
Object.defineProperty(o, k2, desc);
|
||
}) : (function(o, m, k, k2) {
|
||
if (k2 === undefined) k2 = k;
|
||
o[k2] = m[k];
|
||
}));
|
||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||
}) : function(o, v) {
|
||
o["default"] = v;
|
||
});
|
||
var __importStar = (this && this.__importStar) || (function () {
|
||
var ownKeys = function(o) {
|
||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||
var ar = [];
|
||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||
return ar;
|
||
};
|
||
return ownKeys(o);
|
||
};
|
||
return function (mod) {
|
||
if (mod && mod.__esModule) return mod;
|
||
var result = {};
|
||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||
__setModuleDefault(result, mod);
|
||
return result;
|
||
};
|
||
})();
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
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 promptService_1 = require("./services/promptService");
|
||
const resourceService_1 = require("./services/resourceService");
|
||
const fs = __importStar(require("fs"));
|
||
const path = __importStar(require("path"));
|
||
// ==========================================
|
||
// 1. 初始化 MCP Server
|
||
// ==========================================
|
||
const mcpServer = new mcp_js_1.McpServer({
|
||
name: "Project-Caffeine-Prompt-Strategy",
|
||
version: "1.2.0"
|
||
});
|
||
// ==========================================
|
||
// 2. 注册 Tools (工具) - 赋予大模型主动执行的能力
|
||
// ==========================================
|
||
// 工具 1:5 Whys 提示词策略生成
|
||
mcpServer.tool("generate_5_whys", "使用 5 Whys 模板对用户查询进行深度分解,生成增强的提示词策略", { query: zod_1.z.string().describe("需要分析的查询主题") }, async ({ query }) => {
|
||
console.error(`[Project Caffeine] 大模型调用工具: 正在生成 5 Whys 策略 -> ${query}`);
|
||
const enhancedPrompt = (0, promptService_1.generate5Whys)(query);
|
||
return {
|
||
content: [{ type: "text", text: JSON.stringify(enhancedPrompt, null, 2) }]
|
||
};
|
||
});
|
||
// 工具 2:扫描本地知识库目录
|
||
mcpServer.tool("list_local_notes", "获取本地 Obsidian 知识库中的所有 Markdown 笔记列表,用于了解当前有哪些可用的本地上下文资料。", {}, async () => {
|
||
console.error(`[Project Caffeine] 大模型调用工具: 正在扫描本地笔记列表...`);
|
||
const notes = await (0, resourceService_1.listObsidianNotes)();
|
||
return {
|
||
content: [{
|
||
type: "text",
|
||
text: notes.length > 0 ? `找到了以下笔记:\n${notes.join('\n')}` : "未找到笔记。"
|
||
}]
|
||
};
|
||
});
|
||
// 工具 3:阅读指定的单篇笔记内容
|
||
mcpServer.tool("read_local_note", "读取本地 Obsidian 知识库中指定笔记的完整内容,作为深度分析的上下文参考。", { filename: zod_1.z.string().describe("需要读取的笔记文件名,必须包含 .md 后缀") }, async ({ filename }) => {
|
||
console.error(`[Project Caffeine] 大模型调用工具: 正在深度阅读笔记 -> ${filename}`);
|
||
try {
|
||
const content = await (0, resourceService_1.readObsidianNote)(filename);
|
||
return { content: [{ type: "text", text: content }] };
|
||
}
|
||
catch (error) {
|
||
return {
|
||
content: [{ type: "text", text: `读取失败: ${error.message}` }],
|
||
isError: true // 明确告知大模型此操作抛出了错误
|
||
};
|
||
}
|
||
});
|
||
// ==========================================
|
||
// 3. 注册 Resources (资源) - 暴露给客户端供用户手动勾选的静态数据
|
||
// ==========================================
|
||
// 资源 1:知识库目录索引
|
||
mcpServer.resource("obsidian-index", // 客户端显示的资源 Name/ID
|
||
"obsidian://vault/index", // 唯一的 URI 标识
|
||
{
|
||
description: "本地知识库的目录索引,包含所有 Markdown 笔记的列表"
|
||
}, async (uri) => {
|
||
console.error(`[Project Caffeine] 客户端请求静态资源: ${uri.href}`);
|
||
const notes = await (0, resourceService_1.listObsidianNotes)();
|
||
const textContent = notes.length > 0
|
||
? `当前知识库包含以下文件:\n${notes.join('\n')}`
|
||
: "当前知识库为空。";
|
||
return {
|
||
contents: [{
|
||
uri: uri.href,
|
||
mimeType: "text/plain",
|
||
text: textContent
|
||
}]
|
||
};
|
||
});
|
||
// ==========================================
|
||
// 4. 启动底层 Stdio 传输层
|
||
// ==========================================
|
||
async function start() {
|
||
console.error("[Project Caffeine] 正在启动 TS 版 MCP Server (含 Tools 与 Resources)...");
|
||
const transport = new stdio_js_1.StdioServerTransport();
|
||
await mcpServer.connect(transport);
|
||
console.error("[Project Caffeine] MCP Server 已就绪,等待 Cherry Studio 交互。");
|
||
}
|
||
// 捕获致命错误并安全退出
|
||
start().catch((err) => {
|
||
console.error("服务器启动失败:", err);
|
||
process.exit(1);
|
||
});
|
||
// ==========================================
|
||
// 💡 5. 日志持久化拦截器 (Linux)
|
||
// ==========================================
|
||
const logFilePath = path.resolve(__dirname, '../server.log');
|
||
const originalConsoleError = console.error;
|
||
console.error = (...args) => {
|
||
// 1. 在后台输出
|
||
originalConsoleError(...args);
|
||
// 2. 同时把日志追加写入到项目根目录的 server.log 文件中
|
||
const logMessage = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
|
||
fs.appendFileSync(logFilePath, `[${new Date().toISOString()}] ${logMessage}\n`);
|
||
};
|
||
//# sourceMappingURL=app.js.map
|