futu-stock-mcp-server 0.1.0__tar.gz → 0.1.1__tar.gz
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.
Potentially problematic release.
This version of futu-stock-mcp-server might be problematic. Click here for more details.
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/LICENSE +1 -1
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/PKG-INFO +174 -8
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/README.md +173 -7
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/pyproject.toml +1 -1
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/src/futu_stock_mcp_server/server.py +24 -8
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/.gitignore +0 -0
- {futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/src/futu_stock_mcp_server/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: futu-stock-mcp-server
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A Model Context Protocol (MCP) server for accessing Futu OpenAPI functionality
|
|
5
5
|
Project-URL: Homepage, https://github.com/shuizhengqi1/futu-stock-mcp-server
|
|
6
6
|
Project-URL: Documentation, https://github.com/shuizhengqi1/futu-stock-mcp-server#readme
|
|
@@ -216,22 +216,75 @@ Run formatting:
|
|
|
216
216
|
ruff format .
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
-
##
|
|
219
|
+
## 🔧 MCP Server 配置
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
### 在 Claude Desktop 中配置
|
|
222
|
+
|
|
223
|
+
1. **找到配置文件位置**:
|
|
224
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
225
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
226
|
+
|
|
227
|
+
2. **添加服务器配置**:
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"mcpServers": {
|
|
231
|
+
"futu-stock": {
|
|
232
|
+
"command": "futu-mcp-server",
|
|
233
|
+
"env": {
|
|
234
|
+
"FUTU_HOST": "127.0.0.1",
|
|
235
|
+
"FUTU_PORT": "11111"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
224
240
|
```
|
|
225
241
|
|
|
226
|
-
|
|
242
|
+
3. **如果使用虚拟环境或从源码运行**:
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"mcpServers": {
|
|
246
|
+
"futu-stock": {
|
|
247
|
+
"command": "/path/to/your/venv/bin/futu-mcp-server",
|
|
248
|
+
"env": {
|
|
249
|
+
"FUTU_HOST": "127.0.0.1",
|
|
250
|
+
"FUTU_PORT": "11111"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
或者使用 Python 模块方式:
|
|
258
|
+
```json
|
|
259
|
+
{
|
|
260
|
+
"mcpServers": {
|
|
261
|
+
"futu-stock": {
|
|
262
|
+
"command": "python",
|
|
263
|
+
"args": ["-m", "futu_stock_mcp_server.server"],
|
|
264
|
+
"cwd": "/path/to/futu-stock-mcp-server",
|
|
265
|
+
"env": {
|
|
266
|
+
"FUTU_HOST": "127.0.0.1",
|
|
267
|
+
"FUTU_PORT": "11111"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### 在其他 MCP 客户端中配置
|
|
275
|
+
|
|
276
|
+
#### 使用 Python MCP 客户端
|
|
227
277
|
```python
|
|
228
278
|
from mcp import ClientSession, StdioServerParameters
|
|
229
279
|
from mcp.client.stdio import stdio_client
|
|
230
280
|
|
|
231
281
|
async def main():
|
|
232
282
|
server_params = StdioServerParameters(
|
|
233
|
-
command="
|
|
234
|
-
|
|
283
|
+
command="futu-mcp-server",
|
|
284
|
+
env={
|
|
285
|
+
"FUTU_HOST": "127.0.0.1",
|
|
286
|
+
"FUTU_PORT": "11111"
|
|
287
|
+
}
|
|
235
288
|
)
|
|
236
289
|
|
|
237
290
|
async with stdio_client(server_params) as (read, write):
|
|
@@ -241,7 +294,120 @@ async def main():
|
|
|
241
294
|
|
|
242
295
|
# List available tools
|
|
243
296
|
tools = await session.list_tools()
|
|
297
|
+
print("Available tools:", [tool.name for tool in tools.tools])
|
|
298
|
+
```
|
|
244
299
|
|
|
300
|
+
#### 使用 Node.js MCP 客户端
|
|
301
|
+
```javascript
|
|
302
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
303
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
304
|
+
|
|
305
|
+
const transport = new StdioClientTransport({
|
|
306
|
+
command: "futu-mcp-server",
|
|
307
|
+
env: {
|
|
308
|
+
FUTU_HOST: "127.0.0.1",
|
|
309
|
+
FUTU_PORT: "11111"
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const client = new Client({
|
|
314
|
+
name: "futu-stock-client",
|
|
315
|
+
version: "1.0.0"
|
|
316
|
+
}, {
|
|
317
|
+
capabilities: {}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
await client.connect(transport);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## 📋 使用方法
|
|
324
|
+
|
|
325
|
+
### 1. 启动服务器(独立运行)
|
|
326
|
+
```bash
|
|
327
|
+
# 通过 pip 安装后
|
|
328
|
+
futu-mcp-server
|
|
329
|
+
|
|
330
|
+
# 或从源码运行
|
|
331
|
+
python -m futu_stock_mcp_server.server
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 2. 环境变量配置
|
|
335
|
+
创建 `.env` 文件或设置环境变量:
|
|
336
|
+
```bash
|
|
337
|
+
FUTU_HOST=127.0.0.1
|
|
338
|
+
FUTU_PORT=11111
|
|
339
|
+
LOG_LEVEL=INFO
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 3. 验证连接
|
|
343
|
+
启动服务器后,你应该看到类似的日志:
|
|
344
|
+
```
|
|
345
|
+
2024-10-02 14:20:52 | INFO | Initializing Futu connection...
|
|
346
|
+
2024-10-02 14:20:52 | INFO | Successfully initialized Futu connection
|
|
347
|
+
2024-10-02 14:20:52 | INFO | Starting MCP server in stdio mode...
|
|
348
|
+
2024-10-02 14:20:52 | INFO | Press Ctrl+C to stop the server
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 4. 在 AI 工具中使用
|
|
352
|
+
配置完成后,重启 Claude Desktop 或其他 MCP 客户端,你就可以:
|
|
353
|
+
- 查询股票实时行情
|
|
354
|
+
- 获取历史K线数据
|
|
355
|
+
- 订阅股票数据推送
|
|
356
|
+
- 查询账户信息
|
|
357
|
+
- 执行交易操作(需要交易权限)
|
|
358
|
+
|
|
359
|
+
## 🔧 故障排除
|
|
360
|
+
|
|
361
|
+
### 常见问题
|
|
362
|
+
|
|
363
|
+
#### 1. 命令 `futu-mcp-server` 找不到
|
|
364
|
+
```bash
|
|
365
|
+
# 确保已正确安装
|
|
366
|
+
pip install futu-stock-mcp-server
|
|
367
|
+
|
|
368
|
+
# 或者检查是否在虚拟环境中
|
|
369
|
+
which futu-mcp-server
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
#### 2. Ctrl+C 无法退出服务器
|
|
373
|
+
- 新版本已修复此问题
|
|
374
|
+
- 如果仍然遇到,可以使用 `kill -9 <pid>` 强制终止
|
|
375
|
+
|
|
376
|
+
#### 3. 连接富途 OpenD 失败
|
|
377
|
+
```bash
|
|
378
|
+
# 检查 OpenD 是否运行
|
|
379
|
+
netstat -an | grep 11111
|
|
380
|
+
|
|
381
|
+
# 检查环境变量
|
|
382
|
+
echo $FUTU_HOST
|
|
383
|
+
echo $FUTU_PORT
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### 4. Claude Desktop 无法识别服务器
|
|
387
|
+
- 确保配置文件路径正确
|
|
388
|
+
- 检查 JSON 格式是否有效
|
|
389
|
+
- 重启 Claude Desktop
|
|
390
|
+
- 查看 Claude Desktop 的日志文件
|
|
391
|
+
|
|
392
|
+
#### 5. 权限问题
|
|
393
|
+
```bash
|
|
394
|
+
# 确保有执行权限
|
|
395
|
+
chmod +x ~/.local/bin/futu-mcp-server
|
|
396
|
+
|
|
397
|
+
# 或者使用完整路径
|
|
398
|
+
python -m futu_stock_mcp_server.server
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### 日志调试
|
|
402
|
+
启用详细日志:
|
|
403
|
+
```bash
|
|
404
|
+
export LOG_LEVEL=DEBUG
|
|
405
|
+
futu-mcp-server
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
日志文件位置:`./logs/futu_server.log`
|
|
409
|
+
tools = await session.list_tools()
|
|
410
|
+
|
|
245
411
|
# Call a tool
|
|
246
412
|
result = await session.call_tool(
|
|
247
413
|
"get_stock_quote",
|
|
@@ -174,22 +174,75 @@ Run formatting:
|
|
|
174
174
|
ruff format .
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
##
|
|
177
|
+
## 🔧 MCP Server 配置
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
### 在 Claude Desktop 中配置
|
|
180
|
+
|
|
181
|
+
1. **找到配置文件位置**:
|
|
182
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
183
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
184
|
+
|
|
185
|
+
2. **添加服务器配置**:
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"mcpServers": {
|
|
189
|
+
"futu-stock": {
|
|
190
|
+
"command": "futu-mcp-server",
|
|
191
|
+
"env": {
|
|
192
|
+
"FUTU_HOST": "127.0.0.1",
|
|
193
|
+
"FUTU_PORT": "11111"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
182
198
|
```
|
|
183
199
|
|
|
184
|
-
|
|
200
|
+
3. **如果使用虚拟环境或从源码运行**:
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"mcpServers": {
|
|
204
|
+
"futu-stock": {
|
|
205
|
+
"command": "/path/to/your/venv/bin/futu-mcp-server",
|
|
206
|
+
"env": {
|
|
207
|
+
"FUTU_HOST": "127.0.0.1",
|
|
208
|
+
"FUTU_PORT": "11111"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
或者使用 Python 模块方式:
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"mcpServers": {
|
|
219
|
+
"futu-stock": {
|
|
220
|
+
"command": "python",
|
|
221
|
+
"args": ["-m", "futu_stock_mcp_server.server"],
|
|
222
|
+
"cwd": "/path/to/futu-stock-mcp-server",
|
|
223
|
+
"env": {
|
|
224
|
+
"FUTU_HOST": "127.0.0.1",
|
|
225
|
+
"FUTU_PORT": "11111"
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 在其他 MCP 客户端中配置
|
|
233
|
+
|
|
234
|
+
#### 使用 Python MCP 客户端
|
|
185
235
|
```python
|
|
186
236
|
from mcp import ClientSession, StdioServerParameters
|
|
187
237
|
from mcp.client.stdio import stdio_client
|
|
188
238
|
|
|
189
239
|
async def main():
|
|
190
240
|
server_params = StdioServerParameters(
|
|
191
|
-
command="
|
|
192
|
-
|
|
241
|
+
command="futu-mcp-server",
|
|
242
|
+
env={
|
|
243
|
+
"FUTU_HOST": "127.0.0.1",
|
|
244
|
+
"FUTU_PORT": "11111"
|
|
245
|
+
}
|
|
193
246
|
)
|
|
194
247
|
|
|
195
248
|
async with stdio_client(server_params) as (read, write):
|
|
@@ -199,7 +252,120 @@ async def main():
|
|
|
199
252
|
|
|
200
253
|
# List available tools
|
|
201
254
|
tools = await session.list_tools()
|
|
255
|
+
print("Available tools:", [tool.name for tool in tools.tools])
|
|
256
|
+
```
|
|
202
257
|
|
|
258
|
+
#### 使用 Node.js MCP 客户端
|
|
259
|
+
```javascript
|
|
260
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
261
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
262
|
+
|
|
263
|
+
const transport = new StdioClientTransport({
|
|
264
|
+
command: "futu-mcp-server",
|
|
265
|
+
env: {
|
|
266
|
+
FUTU_HOST: "127.0.0.1",
|
|
267
|
+
FUTU_PORT: "11111"
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const client = new Client({
|
|
272
|
+
name: "futu-stock-client",
|
|
273
|
+
version: "1.0.0"
|
|
274
|
+
}, {
|
|
275
|
+
capabilities: {}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
await client.connect(transport);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## 📋 使用方法
|
|
282
|
+
|
|
283
|
+
### 1. 启动服务器(独立运行)
|
|
284
|
+
```bash
|
|
285
|
+
# 通过 pip 安装后
|
|
286
|
+
futu-mcp-server
|
|
287
|
+
|
|
288
|
+
# 或从源码运行
|
|
289
|
+
python -m futu_stock_mcp_server.server
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 2. 环境变量配置
|
|
293
|
+
创建 `.env` 文件或设置环境变量:
|
|
294
|
+
```bash
|
|
295
|
+
FUTU_HOST=127.0.0.1
|
|
296
|
+
FUTU_PORT=11111
|
|
297
|
+
LOG_LEVEL=INFO
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 3. 验证连接
|
|
301
|
+
启动服务器后,你应该看到类似的日志:
|
|
302
|
+
```
|
|
303
|
+
2024-10-02 14:20:52 | INFO | Initializing Futu connection...
|
|
304
|
+
2024-10-02 14:20:52 | INFO | Successfully initialized Futu connection
|
|
305
|
+
2024-10-02 14:20:52 | INFO | Starting MCP server in stdio mode...
|
|
306
|
+
2024-10-02 14:20:52 | INFO | Press Ctrl+C to stop the server
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 4. 在 AI 工具中使用
|
|
310
|
+
配置完成后,重启 Claude Desktop 或其他 MCP 客户端,你就可以:
|
|
311
|
+
- 查询股票实时行情
|
|
312
|
+
- 获取历史K线数据
|
|
313
|
+
- 订阅股票数据推送
|
|
314
|
+
- 查询账户信息
|
|
315
|
+
- 执行交易操作(需要交易权限)
|
|
316
|
+
|
|
317
|
+
## 🔧 故障排除
|
|
318
|
+
|
|
319
|
+
### 常见问题
|
|
320
|
+
|
|
321
|
+
#### 1. 命令 `futu-mcp-server` 找不到
|
|
322
|
+
```bash
|
|
323
|
+
# 确保已正确安装
|
|
324
|
+
pip install futu-stock-mcp-server
|
|
325
|
+
|
|
326
|
+
# 或者检查是否在虚拟环境中
|
|
327
|
+
which futu-mcp-server
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### 2. Ctrl+C 无法退出服务器
|
|
331
|
+
- 新版本已修复此问题
|
|
332
|
+
- 如果仍然遇到,可以使用 `kill -9 <pid>` 强制终止
|
|
333
|
+
|
|
334
|
+
#### 3. 连接富途 OpenD 失败
|
|
335
|
+
```bash
|
|
336
|
+
# 检查 OpenD 是否运行
|
|
337
|
+
netstat -an | grep 11111
|
|
338
|
+
|
|
339
|
+
# 检查环境变量
|
|
340
|
+
echo $FUTU_HOST
|
|
341
|
+
echo $FUTU_PORT
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### 4. Claude Desktop 无法识别服务器
|
|
345
|
+
- 确保配置文件路径正确
|
|
346
|
+
- 检查 JSON 格式是否有效
|
|
347
|
+
- 重启 Claude Desktop
|
|
348
|
+
- 查看 Claude Desktop 的日志文件
|
|
349
|
+
|
|
350
|
+
#### 5. 权限问题
|
|
351
|
+
```bash
|
|
352
|
+
# 确保有执行权限
|
|
353
|
+
chmod +x ~/.local/bin/futu-mcp-server
|
|
354
|
+
|
|
355
|
+
# 或者使用完整路径
|
|
356
|
+
python -m futu_stock_mcp_server.server
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### 日志调试
|
|
360
|
+
启用详细日志:
|
|
361
|
+
```bash
|
|
362
|
+
export LOG_LEVEL=DEBUG
|
|
363
|
+
futu-mcp-server
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
日志文件位置:`./logs/futu_server.log`
|
|
367
|
+
tools = await session.list_tools()
|
|
368
|
+
|
|
203
369
|
# Call a tool
|
|
204
370
|
result = await session.call_tool(
|
|
205
371
|
"get_stock_quote",
|
{futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/src/futu_stock_mcp_server/server.py
RENAMED
|
@@ -167,15 +167,24 @@ def signal_handler(signum, frame):
|
|
|
167
167
|
"""Handle process signals"""
|
|
168
168
|
global _is_shutting_down
|
|
169
169
|
if _is_shutting_down:
|
|
170
|
-
|
|
170
|
+
logger.info("Already shutting down, forcing exit...")
|
|
171
|
+
os._exit(1)
|
|
171
172
|
|
|
172
173
|
# 只处理 SIGINT 和 SIGTERM
|
|
173
174
|
if signum not in (signal.SIGINT, signal.SIGTERM):
|
|
174
175
|
return
|
|
175
176
|
|
|
176
177
|
logger.info(f"Received signal {signum}, cleaning up...")
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
_is_shutting_down = True
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
cleanup_all()
|
|
182
|
+
logger.info("Cleanup completed, exiting...")
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error(f"Error during cleanup: {e}")
|
|
185
|
+
finally:
|
|
186
|
+
# 强制退出,确保进程能够终止
|
|
187
|
+
os._exit(0)
|
|
179
188
|
|
|
180
189
|
# Register cleanup functions
|
|
181
190
|
atexit.register(cleanup_all)
|
|
@@ -1438,7 +1447,8 @@ async def get_current_time() -> Dict[str, Any]:
|
|
|
1438
1447
|
'timezone': datetime.now().astimezone().tzname()
|
|
1439
1448
|
}
|
|
1440
1449
|
|
|
1441
|
-
|
|
1450
|
+
def main():
|
|
1451
|
+
"""Main entry point for the futu-mcp-server command."""
|
|
1442
1452
|
try:
|
|
1443
1453
|
# 清理旧的进程和文件
|
|
1444
1454
|
cleanup_stale_processes()
|
|
@@ -1457,19 +1467,25 @@ if __name__ == "__main__":
|
|
|
1457
1467
|
if init_futu_connection():
|
|
1458
1468
|
logger.info("Successfully initialized Futu connection")
|
|
1459
1469
|
logger.info("Starting MCP server in stdio mode...")
|
|
1470
|
+
logger.info("Press Ctrl+C to stop the server")
|
|
1460
1471
|
try:
|
|
1461
1472
|
mcp.run(transport='stdio')
|
|
1462
1473
|
except KeyboardInterrupt:
|
|
1463
1474
|
logger.info("Received keyboard interrupt, shutting down...")
|
|
1464
|
-
|
|
1475
|
+
cleanup_all()
|
|
1476
|
+
os._exit(0)
|
|
1465
1477
|
except Exception as e:
|
|
1466
1478
|
logger.error(f"Error running server: {str(e)}")
|
|
1467
|
-
|
|
1479
|
+
cleanup_all()
|
|
1480
|
+
os._exit(1)
|
|
1468
1481
|
else:
|
|
1469
1482
|
logger.error("Failed to initialize Futu connection. Server will not start.")
|
|
1470
|
-
|
|
1483
|
+
os._exit(1)
|
|
1471
1484
|
except Exception as e:
|
|
1472
1485
|
logger.error(f"Error starting server: {str(e)}")
|
|
1473
1486
|
sys.exit(1)
|
|
1474
1487
|
finally:
|
|
1475
|
-
cleanup_all()
|
|
1488
|
+
cleanup_all()
|
|
1489
|
+
|
|
1490
|
+
if __name__ == "__main__":
|
|
1491
|
+
main()
|
|
File without changes
|
{futu_stock_mcp_server-0.1.0 → futu_stock_mcp_server-0.1.1}/src/futu_stock_mcp_server/__init__.py
RENAMED
|
File without changes
|