@youhaozhao/cninfo-mcp 1.0.6 → 1.0.7

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.
package/bin/cninfo-mcp.js CHANGED
@@ -5,22 +5,45 @@
5
5
  * 自动检测 Python 并安装依赖,然后启动 Python MCP 服务器。
6
6
  */
7
7
 
8
- const { spawn } = require('child_process');
9
- const path = require('path');
10
- const fs = require('fs');
8
+ const { spawn } = require("child_process");
9
+ const path = require("path");
10
+ const fs = require("fs");
11
+ const os = require("os");
11
12
 
12
13
  // 配置路径
13
- const PYTHON_SCRIPT = path.join(__dirname, '..', 'python', 'mcp_server.py');
14
- const PYTHON_REQUIREMENTS = path.join(__dirname, '..', 'python', 'requirements.txt');
14
+ const PYTHON_SCRIPT = path.join(__dirname, "..", "python", "mcp_server.py");
15
+ const PYTHON_REQUIREMENTS = path.join(
16
+ __dirname,
17
+ "..",
18
+ "python",
19
+ "requirements.txt",
20
+ );
21
+
22
+ // 虚拟环境目录,放在用户目录下保证跨 npx 调用持久化
23
+ const VENV_DIR = path.join(os.homedir(), ".cninfo-mcp", "venv");
24
+
25
+ // 获取虚拟环境中的 Python 可执行文件路径
26
+ function getVenvPython() {
27
+ if (process.platform === "win32") {
28
+ return path.join(VENV_DIR, "Scripts", "python.exe");
29
+ }
30
+ return path.join(VENV_DIR, "bin", "python3");
31
+ }
15
32
 
16
- // 查找可用的 Python 可执行文件
33
+ // 查找可用的系统 Python 可执行文件(仅用于创建 venv)
17
34
  async function findPython() {
18
- const pythonCommands = ['python3', 'python', 'python3.12', 'python3.11', 'python3.10'];
35
+ const pythonCommands = [
36
+ "python3",
37
+ "python",
38
+ "python3.12",
39
+ "python3.11",
40
+ "python3.10",
41
+ ];
19
42
 
20
43
  for (const cmd of pythonCommands) {
21
44
  try {
22
- const result = await spawnAsync(cmd, ['--version']);
23
- if (result.stdout && result.stdout.includes('Python')) {
45
+ const result = await spawnAsync(cmd, ["--version"]);
46
+ if (result.stdout && result.stdout.includes("Python")) {
24
47
  return cmd;
25
48
  }
26
49
  } catch (error) {
@@ -29,38 +52,57 @@ async function findPython() {
29
52
  }
30
53
 
31
54
  throw new Error(
32
- 'Python not found. Please install Python 3.10+ from https://python.org\n' +
33
- 'After installation, restart your terminal and try again.'
55
+ "Python not found. Please install Python 3.10+ from https://python.org\n" +
56
+ "After installation, restart your terminal and try again.",
34
57
  );
35
58
  }
36
59
 
37
- // 检查并安装 Python 依赖
38
- async function ensureDependencies(pythonCmd) {
60
+ // 创建虚拟环境(如果不存在)
61
+ async function ensureVenv(systemPythonCmd) {
62
+ const venvPython = getVenvPython();
63
+ if (fs.existsSync(venvPython)) {
64
+ return venvPython;
65
+ }
66
+
67
+ console.error("Creating Python virtual environment...");
68
+ fs.mkdirSync(path.dirname(VENV_DIR), { recursive: true });
69
+ await spawnAsync(systemPythonCmd, ["-m", "venv", VENV_DIR], {
70
+ stdio: "inherit",
71
+ });
72
+ console.error("Virtual environment created\n");
73
+ return venvPython;
74
+ }
75
+
76
+ // 检查并安装 Python 依赖(使用 venv 中的 python)
77
+ async function ensureDependencies(venvPython) {
39
78
  const requirementsPath = PYTHON_REQUIREMENTS;
40
79
 
41
80
  if (!fs.existsSync(requirementsPath)) {
42
- console.error('Error: requirements.txt not found at', requirementsPath);
81
+ console.error("Error: requirements.txt not found at", requirementsPath);
43
82
  process.exit(1);
44
83
  }
45
84
 
46
85
  try {
47
86
  // 检查 mcp 包是否已安装
48
- const checkResult = await spawnAsync(pythonCmd, ['-c', 'import mcp']);
87
+ await spawnAsync(venvPython, ["-c", "import mcp"]);
49
88
  } catch (error) {
50
89
  // 未安装,执行安装
51
- console.error('Installing Python dependencies...');
52
- const installResult = await spawnAsync(pythonCmd, ['-m', 'pip', 'install', '-r', requirementsPath], {
53
- stdio: 'inherit'
54
- });
55
-
56
- if (installResult.code !== 0) {
57
- console.error('\n❌ Failed to install Python dependencies');
58
- console.error('Please run manually:');
59
- console.error(` ${pythonCmd} -m pip install -r ${requirementsPath}`);
90
+ console.error("Installing Python dependencies...");
91
+ try {
92
+ await spawnAsync(
93
+ venvPython,
94
+ ["-m", "pip", "install", "-r", requirementsPath],
95
+ {
96
+ stdio: "inherit",
97
+ },
98
+ );
99
+ console.error("Python dependencies installed successfully\n");
100
+ } catch (installError) {
101
+ console.error("\n❌ Failed to install Python dependencies");
102
+ console.error("Please run manually:");
103
+ console.error(` ${venvPython} -m pip install -r ${requirementsPath}`);
60
104
  process.exit(1);
61
105
  }
62
-
63
- console.error('✅ Python dependencies installed successfully\n');
64
106
  }
65
107
  }
66
108
 
@@ -68,28 +110,28 @@ async function ensureDependencies(pythonCmd) {
68
110
  function spawnAsync(command, args, options = {}) {
69
111
  return new Promise((resolve, reject) => {
70
112
  const child = spawn(command, args, {
71
- stdio: options.stdio || 'pipe',
72
- shell: process.platform === 'win32',
73
- ...options
113
+ stdio: options.stdio || "pipe",
114
+ shell: process.platform === "win32",
115
+ ...options,
74
116
  });
75
117
 
76
- let stdout = '';
77
- let stderr = '';
118
+ let stdout = "";
119
+ let stderr = "";
78
120
  let code = null;
79
121
 
80
122
  if (child.stdout) {
81
- child.stdout.on('data', (data) => {
123
+ child.stdout.on("data", (data) => {
82
124
  stdout += data.toString();
83
125
  });
84
126
  }
85
127
 
86
128
  if (child.stderr) {
87
- child.stderr.on('data', (data) => {
129
+ child.stderr.on("data", (data) => {
88
130
  stderr += data.toString();
89
131
  });
90
132
  }
91
133
 
92
- child.on('close', (exitCode) => {
134
+ child.on("close", (exitCode) => {
93
135
  code = exitCode;
94
136
  if (code === 0) {
95
137
  resolve({ stdout, stderr, code });
@@ -102,7 +144,7 @@ function spawnAsync(command, args, options = {}) {
102
144
  }
103
145
  });
104
146
 
105
- child.on('error', (error) => {
147
+ child.on("error", (error) => {
106
148
  reject(error);
107
149
  });
108
150
  });
@@ -112,36 +154,36 @@ async function main() {
112
154
  try {
113
155
  // 检查 Python 脚本是否存在
114
156
  if (!fs.existsSync(PYTHON_SCRIPT)) {
115
- console.error('Error: mcp_server.py not found at', PYTHON_SCRIPT);
157
+ console.error("Error: mcp_server.py not found at", PYTHON_SCRIPT);
116
158
  process.exit(1);
117
159
  }
118
160
 
119
- const pythonCmd = await findPython();
120
- await ensureDependencies(pythonCmd);
161
+ const systemPython = await findPython();
162
+ const venvPython = await ensureVenv(systemPython);
163
+ await ensureDependencies(venvPython);
121
164
 
122
165
  // 启动 MCP 服务器
123
- console.error('巨潮资讯 MCP 服务器已启动,等待连接...');
124
- const child = spawn(pythonCmd, [PYTHON_SCRIPT], {
125
- stdio: 'inherit',
126
- shell: process.platform === 'win32',
166
+ console.error("巨潮资讯 MCP 服务器已启动,等待连接...");
167
+ const child = spawn(venvPython, [PYTHON_SCRIPT], {
168
+ stdio: "inherit",
169
+ shell: process.platform === "win32",
127
170
  env: {
128
171
  ...process.env,
129
- PYTHONPATH: path.join(__dirname, '..', 'python')
130
- }
172
+ PYTHONPATH: path.join(__dirname, "..", "python"),
173
+ },
131
174
  });
132
175
 
133
176
  // 处理子进程退出
134
- child.on('error', (error) => {
135
- console.error('Failed to start MCP Server:', error.message);
177
+ child.on("error", (error) => {
178
+ console.error("Failed to start MCP Server:", error.message);
136
179
  process.exit(1);
137
180
  });
138
181
 
139
- child.on('exit', (code) => {
182
+ child.on("exit", (code) => {
140
183
  process.exit(code || 0);
141
184
  });
142
-
143
185
  } catch (error) {
144
- console.error('Error:', error.message);
186
+ console.error("Error:", error.message);
145
187
  process.exit(1);
146
188
  }
147
189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youhaozhao/cninfo-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "MCP Server for querying and downloading Chinese listed companies' annual reports from CNINFO (巨潮资讯网)",
5
5
  "keywords": [
6
6
  "mcp",
@@ -50,4 +50,4 @@
50
50
  "README.md",
51
51
  "LICENSE"
52
52
  ]
53
- }
53
+ }
@@ -7,6 +7,7 @@
7
7
  const { spawn } = require("child_process");
8
8
  const fs = require("fs");
9
9
  const path = require("path");
10
+ const os = require("os");
10
11
 
11
12
  const REQUIREMENTS_FILE = path.join(
12
13
  __dirname,
@@ -15,6 +16,15 @@ const REQUIREMENTS_FILE = path.join(
15
16
  "requirements.txt",
16
17
  );
17
18
 
19
+ const VENV_DIR = path.join(os.homedir(), ".cninfo-mcp", "venv");
20
+
21
+ function getVenvPython() {
22
+ if (process.platform === "win32") {
23
+ return path.join(VENV_DIR, "Scripts", "python.exe");
24
+ }
25
+ return path.join(VENV_DIR, "bin", "python3");
26
+ }
27
+
18
28
  async function findPython() {
19
29
  const pythonCommands = [
20
30
  "python3",
@@ -75,16 +85,31 @@ async function main() {
75
85
  return;
76
86
  }
77
87
 
88
+ // 创建虚拟环境(如果不存在)
89
+ const venvPython = getVenvPython();
90
+ if (!fs.existsSync(venvPython)) {
91
+ console.log("Creating Python virtual environment...");
92
+ try {
93
+ fs.mkdirSync(path.dirname(VENV_DIR), { recursive: true });
94
+ await spawnCommand(pythonCmd, ["-m", "venv", VENV_DIR]);
95
+ console.log("Virtual environment created");
96
+ } catch (venvError) {
97
+ console.warn(" Failed to create virtual environment during npm install");
98
+ console.warn(" It will be created automatically on first run");
99
+ return;
100
+ }
101
+ }
102
+
78
103
  try {
79
- // 检查 mcp 是否已安装
80
- await spawnCommand(pythonCmd, ["-c", "import mcp"]);
104
+ // 检查 mcp 是否已安装(用 venv 的 python)
105
+ await spawnCommand(venvPython, ["-c", "import mcp"]);
81
106
  console.log("✅ Python dependencies already installed");
82
107
  } catch (error) {
83
- // 执行安装
108
+ // 执行安装(用 venv 的 pip)
84
109
  console.log("📦 Installing Python dependencies...");
85
110
  try {
86
111
  await spawnCommand(
87
- pythonCmd,
112
+ venvPython,
88
113
  ["-m", "pip", "install", "-r", REQUIREMENTS_FILE],
89
114
  {
90
115
  stdio: "inherit",