mirror of
https://github.com/ILoveBingLu/CipherTalk.git
synced 2026-05-25 14:00:14 +08:00
feat: 新增 CipherTalk MCP 服务
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const Module = require("module");
|
||||
|
||||
const appDir = path.dirname(process.execPath);
|
||||
const appAsarPath = path.join(appDir, "resources", "app.asar");
|
||||
|
||||
function getUserDataPath() {
|
||||
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
||||
return path.join(appData, "ciphertalk");
|
||||
}
|
||||
|
||||
function getDocumentsPath() {
|
||||
return path.join(os.homedir(), "Documents");
|
||||
}
|
||||
|
||||
const electronShim = {
|
||||
app: {
|
||||
isPackaged: true,
|
||||
getPath(name) {
|
||||
switch (name) {
|
||||
case "userData":
|
||||
return getUserDataPath();
|
||||
case "documents":
|
||||
return getDocumentsPath();
|
||||
case "exe":
|
||||
return process.execPath;
|
||||
default:
|
||||
return appDir;
|
||||
}
|
||||
},
|
||||
getAppPath() {
|
||||
return appAsarPath;
|
||||
},
|
||||
getVersion() {
|
||||
try {
|
||||
return require(path.join(appAsarPath, "package.json")).version || "0.0.0";
|
||||
} catch {
|
||||
return "0.0.0";
|
||||
}
|
||||
},
|
||||
},
|
||||
BrowserWindow: {
|
||||
getAllWindows() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const originalLoad = Module._load;
|
||||
Module._load = function patchedLoad(request, parent, isMain) {
|
||||
if (request === "electron") {
|
||||
return electronShim;
|
||||
}
|
||||
return originalLoad.call(this, request, parent, isMain);
|
||||
};
|
||||
|
||||
const entry = String(process.env.CIPHERTALK_MCP_ENTRY || "").trim();
|
||||
if (!entry) {
|
||||
process.stderr.write("[CipherTalk MCP Bootstrap] CIPHERTALK_MCP_ENTRY is not set\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
require(entry);
|
||||
@@ -0,0 +1,32 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set "APP_DIR=%~dp0"
|
||||
set "EXE_PATH=%APP_DIR%CipherTalk.exe"
|
||||
set "MCP_ARCHIVE=%APP_DIR%resources\app.asar"
|
||||
set "MCP_ENTRY_UNPACKED=%APP_DIR%resources\app.asar.unpacked\dist-electron\mcp.js"
|
||||
set "MCP_ENTRY=%MCP_ARCHIVE%\dist-electron\mcp.js"
|
||||
set "MCP_BOOTSTRAP=%APP_DIR%ciphertalk-mcp-bootstrap.cjs"
|
||||
|
||||
if not exist "%EXE_PATH%" (
|
||||
>&2 echo [CipherTalk MCP Launcher] CipherTalk.exe not found at "%EXE_PATH%"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist "%MCP_BOOTSTRAP%" (
|
||||
>&2 echo [CipherTalk MCP Launcher] MCP bootstrap not found at "%MCP_BOOTSTRAP%"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if exist "%MCP_ENTRY_UNPACKED%" (
|
||||
set "MCP_ENTRY=%MCP_ENTRY_UNPACKED%"
|
||||
) else if not exist "%MCP_ARCHIVE%" (
|
||||
>&2 echo [CipherTalk MCP Launcher] app.asar not found at "%MCP_ARCHIVE%"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set "ELECTRON_RUN_AS_NODE=1"
|
||||
set "CIPHERTALK_MCP_LAUNCHER=packaged-launcher"
|
||||
set "CIPHERTALK_MCP_ENTRY=%MCP_ENTRY%"
|
||||
|
||||
"%EXE_PATH%" "%MCP_BOOTSTRAP%" %*
|
||||
@@ -0,0 +1,53 @@
|
||||
const path = require('path')
|
||||
|
||||
async function main() {
|
||||
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js')
|
||||
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js')
|
||||
|
||||
const mode = process.argv[2] || 'dev'
|
||||
const cwd = process.cwd()
|
||||
let command
|
||||
let args
|
||||
let transportCwd = cwd
|
||||
|
||||
if (mode === 'packaged') {
|
||||
const launcherPath = process.argv[3] || path.join(cwd, 'ciphertalk-mcp.cmd')
|
||||
command = launcherPath
|
||||
args = []
|
||||
transportCwd = path.dirname(launcherPath)
|
||||
} else {
|
||||
command = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
||||
args = ['run', 'mcp']
|
||||
}
|
||||
|
||||
const transport = new StdioClientTransport({
|
||||
command,
|
||||
args,
|
||||
cwd: transportCwd,
|
||||
stderr: 'pipe'
|
||||
})
|
||||
|
||||
const client = new Client({
|
||||
name: 'ciphertalk-mcp-probe',
|
||||
version: '1.0.0'
|
||||
})
|
||||
|
||||
try {
|
||||
await client.connect(transport)
|
||||
const tools = await client.listTools()
|
||||
const health = await client.callTool({ name: 'health_check', arguments: {} })
|
||||
|
||||
console.log(JSON.stringify({
|
||||
mode,
|
||||
tools: (tools.tools || []).map((tool) => tool.name),
|
||||
health
|
||||
}, null, 2))
|
||||
} finally {
|
||||
await client.close()
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('[CipherTalk MCP Probe] failed:', error)
|
||||
process.exit(1)
|
||||
})
|
||||
+20
-1
@@ -1,15 +1,34 @@
|
||||
const { spawn } = require('child_process')
|
||||
const { spawnSync } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const electronBinary = require('electron')
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..')
|
||||
const entry = path.join(rootDir, 'dist-electron', 'mcp.js')
|
||||
|
||||
if (!fs.existsSync(entry)) {
|
||||
process.stderr.write('[CipherTalk MCP Runner] dist-electron/mcp.js not found, running build:mcp...\n')
|
||||
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
||||
const build = spawnSync(npmCmd, ['run', 'build:mcp'], {
|
||||
cwd: rootDir,
|
||||
env: process.env,
|
||||
stdio: 'inherit',
|
||||
windowsHide: true
|
||||
})
|
||||
|
||||
if (build.status !== 0 || !fs.existsSync(entry)) {
|
||||
process.stderr.write('[CipherTalk MCP Runner] build:mcp failed, cannot start MCP server\n')
|
||||
process.exit(build.status ?? 1)
|
||||
}
|
||||
}
|
||||
|
||||
const child = spawn(electronBinary, [entry], {
|
||||
cwd: rootDir,
|
||||
env: {
|
||||
...process.env,
|
||||
ELECTRON_RUN_AS_NODE: '1'
|
||||
ELECTRON_RUN_AS_NODE: '1',
|
||||
CIPHERTALK_MCP_LAUNCHER: 'dev-runner'
|
||||
},
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
windowsHide: true
|
||||
|
||||
Reference in New Issue
Block a user