da2-mcp-socket 0.2.0__py3-none-any.whl
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.
- da2_mcp_socket-0.2.0.dist-info/METADATA +204 -0
- da2_mcp_socket-0.2.0.dist-info/RECORD +8 -0
- da2_mcp_socket-0.2.0.dist-info/WHEEL +4 -0
- da2_mcp_socket-0.2.0.dist-info/entry_points.txt +2 -0
- da2_mcp_socket-0.2.0.dist-info/licenses/LICENSE +21 -0
- mcp_socket/__init__.py +6 -0
- mcp_socket/server.py +559 -0
- mcp_socket/socket_manager.py +1103 -0
mcp_socket/server.py
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026 Danny, DA2 Studio (https://da2.35g.tw)
|
|
3
|
+
Socket MCP Server - Main server implementation.
|
|
4
|
+
|
|
5
|
+
Provides tools for AI assistants to communicate via TCP/IP sockets
|
|
6
|
+
including connection management, data transmission, and status monitoring.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
from collections.abc import AsyncIterator
|
|
14
|
+
from contextlib import asynccontextmanager
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import Any, Optional
|
|
17
|
+
|
|
18
|
+
from dotenv import load_dotenv
|
|
19
|
+
from mcp.server.fastmcp import FastMCP
|
|
20
|
+
|
|
21
|
+
# Support both direct execution and module import
|
|
22
|
+
try:
|
|
23
|
+
from .socket_manager import SocketManager
|
|
24
|
+
except ImportError:
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
27
|
+
from socket_manager import SocketManager
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Load environment variables from .env file
|
|
31
|
+
env_path = os.path.join(os.getcwd(), '.env')
|
|
32
|
+
if os.path.exists(env_path):
|
|
33
|
+
load_dotenv(env_path)
|
|
34
|
+
else:
|
|
35
|
+
load_dotenv()
|
|
36
|
+
|
|
37
|
+
# Configure logging
|
|
38
|
+
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
|
|
39
|
+
logging.basicConfig(
|
|
40
|
+
level=getattr(logging, LOG_LEVEL, logging.INFO),
|
|
41
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
42
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
43
|
+
stream=sys.stderr
|
|
44
|
+
)
|
|
45
|
+
logger = logging.getLogger("mcp-socket")
|
|
46
|
+
|
|
47
|
+
# Default settings from environment
|
|
48
|
+
DEFAULT_TIMEOUT = float(os.getenv("DEFAULT_TIMEOUT", "10.0"))
|
|
49
|
+
DEFAULT_READ_TIMEOUT = float(os.getenv("DEFAULT_READ_TIMEOUT", "5.0"))
|
|
50
|
+
DEFAULT_BUFFER_SIZE = int(os.getenv("DEFAULT_BUFFER_SIZE", "4096"))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Global socket manager instance
|
|
54
|
+
_socket_manager: Optional[SocketManager] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_socket_manager() -> SocketManager:
|
|
58
|
+
"""Get or create the global socket manager instance."""
|
|
59
|
+
global _socket_manager
|
|
60
|
+
if _socket_manager is None:
|
|
61
|
+
_socket_manager = SocketManager()
|
|
62
|
+
return _socket_manager
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class AppContext:
|
|
67
|
+
"""Application context with socket manager."""
|
|
68
|
+
manager: SocketManager
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@asynccontextmanager
|
|
72
|
+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
|
73
|
+
"""Manage application lifecycle with socket manager."""
|
|
74
|
+
logger.info("Starting Socket MCP Server...")
|
|
75
|
+
manager = get_socket_manager()
|
|
76
|
+
logger.info(f"Default settings: timeout={DEFAULT_TIMEOUT}s, read_timeout={DEFAULT_READ_TIMEOUT}s, buffer_size={DEFAULT_BUFFER_SIZE}")
|
|
77
|
+
try:
|
|
78
|
+
yield AppContext(manager=manager)
|
|
79
|
+
finally:
|
|
80
|
+
logger.info("Shutting down Socket MCP Server...")
|
|
81
|
+
result = manager.disconnect_all()
|
|
82
|
+
if result.get("count", 0) > 0:
|
|
83
|
+
logger.info(f"Closed {result['count']} connections: {result['closed_connections']}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Create MCP Server
|
|
87
|
+
mcp = FastMCP(
|
|
88
|
+
"Socket MCP Server",
|
|
89
|
+
lifespan=app_lifespan,
|
|
90
|
+
debug=True
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _format_result(data: Any) -> str:
|
|
95
|
+
"""Format data as JSON string for MCP response."""
|
|
96
|
+
return json.dumps(data, ensure_ascii=False, indent=2, default=str)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ==================== Connection Management ====================
|
|
100
|
+
|
|
101
|
+
@mcp.tool()
|
|
102
|
+
def connect(
|
|
103
|
+
host: str,
|
|
104
|
+
port: int,
|
|
105
|
+
timeout: Optional[float] = None,
|
|
106
|
+
read_timeout: Optional[float] = None,
|
|
107
|
+
buffer_size: Optional[int] = None
|
|
108
|
+
) -> str:
|
|
109
|
+
"""建立 TCP 連線到指定的主機和埠號。
|
|
110
|
+
|
|
111
|
+
連線成功後會返回 connection_id,用於後續操作。一個 MCP Server 可以同時維護多個連線。
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
host: 目標主機(IP 位址或主機名稱)
|
|
115
|
+
port: 目標埠號(1-65535)
|
|
116
|
+
timeout: 連線超時秒數(預設 10.0)
|
|
117
|
+
read_timeout: 讀取超時秒數(預設 5.0)
|
|
118
|
+
buffer_size: 接收緩衝區大小(預設 4096)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
JSON 格式的執行結果,包含 connection_id
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
- connect("192.168.1.100", 8080)
|
|
125
|
+
- connect("localhost", 80)
|
|
126
|
+
- connect("example.com", 443, timeout=5.0)
|
|
127
|
+
"""
|
|
128
|
+
logger.info(f"Connecting to {host}:{port}")
|
|
129
|
+
manager = get_socket_manager()
|
|
130
|
+
result = manager.connect(
|
|
131
|
+
host=host,
|
|
132
|
+
port=port,
|
|
133
|
+
timeout=timeout or DEFAULT_TIMEOUT,
|
|
134
|
+
read_timeout=read_timeout or DEFAULT_READ_TIMEOUT,
|
|
135
|
+
buffer_size=buffer_size or DEFAULT_BUFFER_SIZE
|
|
136
|
+
)
|
|
137
|
+
if result.get("success"):
|
|
138
|
+
logger.info(f"Connected: {result.get('connection_id')}")
|
|
139
|
+
else:
|
|
140
|
+
logger.warning(f"Connection failed: {result.get('error')}")
|
|
141
|
+
return _format_result(result)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@mcp.tool()
|
|
145
|
+
def disconnect(connection_id: str) -> str:
|
|
146
|
+
"""關閉指定的 TCP 連線。
|
|
147
|
+
|
|
148
|
+
關閉連線後會釋放資源,並顯示傳輸統計資訊。
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
connection_id: 要關閉的連線 ID(由 connect 返回)
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
JSON 格式的執行結果,包含傳輸統計
|
|
155
|
+
|
|
156
|
+
Examples:
|
|
157
|
+
- disconnect("192.168.1.100:8080_abc12345")
|
|
158
|
+
"""
|
|
159
|
+
logger.info(f"Disconnecting: {connection_id}")
|
|
160
|
+
manager = get_socket_manager()
|
|
161
|
+
result = manager.disconnect(connection_id)
|
|
162
|
+
return _format_result(result)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@mcp.tool()
|
|
166
|
+
def get_connection_status(connection_id: Optional[str] = None) -> str:
|
|
167
|
+
"""查詢連線狀態。
|
|
168
|
+
|
|
169
|
+
可以查詢特定連線或所有已建立連線的狀態。
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
connection_id: 連線 ID(選填,不指定則列出所有連線)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
JSON 格式的連線狀態資訊
|
|
176
|
+
|
|
177
|
+
Examples:
|
|
178
|
+
- get_connection_status() # 列出所有連線
|
|
179
|
+
- get_connection_status("192.168.1.100:8080_abc12345") # 查詢特定連線
|
|
180
|
+
"""
|
|
181
|
+
manager = get_socket_manager()
|
|
182
|
+
result = manager.get_connection_status(connection_id)
|
|
183
|
+
return _format_result(result)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# ==================== Data Transmission ====================
|
|
187
|
+
|
|
188
|
+
@mcp.tool()
|
|
189
|
+
def send_data(
|
|
190
|
+
connection_id: str,
|
|
191
|
+
data: str,
|
|
192
|
+
encoding: str = "utf-8",
|
|
193
|
+
as_hex: bool = False
|
|
194
|
+
) -> str:
|
|
195
|
+
"""通過 Socket 連線發送資料。
|
|
196
|
+
|
|
197
|
+
可以發送文字或十六進位資料。
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
connection_id: 連線 ID
|
|
201
|
+
data: 要發送的資料(文字或 Hex 字串)
|
|
202
|
+
encoding: 文字編碼(預設 utf-8)
|
|
203
|
+
as_hex: 是否將 data 解析為 Hex 字串(如 \"48454C4C4F\" 或 \"48 45 4C 4C 4F\")
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
JSON 格式的執行結果,包含發送的位元組數
|
|
207
|
+
|
|
208
|
+
Examples:
|
|
209
|
+
- send_data("conn_id", "Hello\\r\\n")
|
|
210
|
+
- send_data("conn_id", "GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n")
|
|
211
|
+
- send_data("conn_id", "48454C4C4F", as_hex=True)
|
|
212
|
+
"""
|
|
213
|
+
logger.debug(f"Sending to {connection_id}: {data[:50]}{'...' if len(data) > 50 else ''}")
|
|
214
|
+
manager = get_socket_manager()
|
|
215
|
+
result = manager.send_data(connection_id=connection_id, data=data, encoding=encoding, as_hex=as_hex)
|
|
216
|
+
if result.get("success"):
|
|
217
|
+
logger.debug(f"Sent {result.get('bytes_sent')} bytes")
|
|
218
|
+
return _format_result(result)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@mcp.tool()
|
|
222
|
+
def receive_data(
|
|
223
|
+
connection_id: str,
|
|
224
|
+
size: Optional[int] = None,
|
|
225
|
+
timeout: Optional[float] = None,
|
|
226
|
+
as_hex: bool = False
|
|
227
|
+
) -> str:
|
|
228
|
+
"""從 Socket 連線接收資料。
|
|
229
|
+
|
|
230
|
+
接收指定數量的位元組,或直到超時。
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
connection_id: 連線 ID
|
|
234
|
+
size: 最大接收位元組數(預設使用 buffer_size)
|
|
235
|
+
timeout: 接收超時秒數(選填,預設使用連線的設定)
|
|
236
|
+
as_hex: 只返回 Hex 格式(預設同時返回文字和 Hex)
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
JSON 格式的接收結果,包含資料內容
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
- receive_data("conn_id")
|
|
243
|
+
- receive_data("conn_id", size=1024, timeout=10.0)
|
|
244
|
+
"""
|
|
245
|
+
manager = get_socket_manager()
|
|
246
|
+
result = manager.receive_data(connection_id=connection_id, size=size, timeout=timeout, as_hex=as_hex)
|
|
247
|
+
if result.get("success"):
|
|
248
|
+
logger.debug(f"Received {result.get('bytes_received')} bytes")
|
|
249
|
+
return _format_result(result)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@mcp.tool()
|
|
253
|
+
def receive_line(
|
|
254
|
+
connection_id: str,
|
|
255
|
+
timeout: Optional[float] = None,
|
|
256
|
+
encoding: str = "utf-8"
|
|
257
|
+
) -> str:
|
|
258
|
+
"""從 Socket 連線接收一行(直到換行符號)。
|
|
259
|
+
|
|
260
|
+
適合接收以換行結尾的文字訊息。
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
connection_id: 連線 ID
|
|
264
|
+
timeout: 接收超時秒數(選填)
|
|
265
|
+
encoding: 文字編碼(預設 utf-8)
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
JSON 格式的接收結果,包含接收的行
|
|
269
|
+
|
|
270
|
+
Examples:
|
|
271
|
+
- receive_line("conn_id")
|
|
272
|
+
- receive_line("conn_id", timeout=5.0)
|
|
273
|
+
"""
|
|
274
|
+
manager = get_socket_manager()
|
|
275
|
+
result = manager.receive_line(connection_id=connection_id, timeout=timeout, encoding=encoding)
|
|
276
|
+
return _format_result(result)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@mcp.tool()
|
|
280
|
+
def receive_until(
|
|
281
|
+
connection_id: str,
|
|
282
|
+
terminator: str = "\n",
|
|
283
|
+
size: int = 4096,
|
|
284
|
+
timeout: Optional[float] = None
|
|
285
|
+
) -> str:
|
|
286
|
+
"""接收資料直到遇到指定的結束符號。
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
connection_id: 連線 ID
|
|
290
|
+
terminator: 結束符號(預設換行 \\n)
|
|
291
|
+
size: 最大接收位元組數(預設 4096)
|
|
292
|
+
timeout: 接收超時秒數(選填)
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
JSON 格式的接收結果
|
|
296
|
+
|
|
297
|
+
Examples:
|
|
298
|
+
- receive_until("conn_id", terminator="\\r\\n")
|
|
299
|
+
- receive_until("conn_id", terminator=">", timeout=10.0)
|
|
300
|
+
"""
|
|
301
|
+
manager = get_socket_manager()
|
|
302
|
+
result = manager.receive_until(connection_id=connection_id, terminator=terminator, size=size, timeout=timeout)
|
|
303
|
+
return _format_result(result)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ==================== Protocol Management ====================
|
|
307
|
+
|
|
308
|
+
@mcp.tool()
|
|
309
|
+
def set_protocol(
|
|
310
|
+
connection_id: str,
|
|
311
|
+
mode: str = "raw",
|
|
312
|
+
start_delimiter: Optional[str] = None,
|
|
313
|
+
end_delimiter: Optional[str] = None,
|
|
314
|
+
start_delimiter_hex: Optional[str] = None,
|
|
315
|
+
end_delimiter_hex: Optional[str] = None,
|
|
316
|
+
include_delimiters: bool = False
|
|
317
|
+
) -> str:
|
|
318
|
+
"""設定連線的接收協議(封包格式)。
|
|
319
|
+
|
|
320
|
+
配置資料應該如何被接收和解析。
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
connection_id: 連線 ID
|
|
324
|
+
mode: 接收模式
|
|
325
|
+
- raw: 原始模式,直接接收資料
|
|
326
|
+
- line: 行模式,以換行符號 (\\n) 為結尾
|
|
327
|
+
- packet: 封包模式,使用自訂的起始/結束符號解析資料流
|
|
328
|
+
start_delimiter: 封包起始符號(文字格式)
|
|
329
|
+
end_delimiter: 封包結束符號(文字格式)
|
|
330
|
+
start_delimiter_hex: 起始符號(Hex 格式,如 "0A")
|
|
331
|
+
end_delimiter_hex: 結束符號(Hex 格式,如 "0D0A")
|
|
332
|
+
include_delimiters: 是否在接收的資料中包含分隔符號
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
JSON 格式的協議設定結果
|
|
336
|
+
|
|
337
|
+
Examples:
|
|
338
|
+
- set_protocol("conn_id", mode="line")
|
|
339
|
+
- set_protocol("conn_id", mode="packet", end_delimiter_hex="0D0A")
|
|
340
|
+
- set_protocol("conn_id", mode="packet", start_delimiter_hex="0A", end_delimiter_hex="0D0A")
|
|
341
|
+
"""
|
|
342
|
+
manager = get_socket_manager()
|
|
343
|
+
result = manager.set_protocol(
|
|
344
|
+
connection_id=connection_id,
|
|
345
|
+
mode=mode,
|
|
346
|
+
start_delimiter=start_delimiter,
|
|
347
|
+
end_delimiter=end_delimiter,
|
|
348
|
+
start_delimiter_hex=start_delimiter_hex,
|
|
349
|
+
end_delimiter_hex=end_delimiter_hex,
|
|
350
|
+
include_delimiters=include_delimiters
|
|
351
|
+
)
|
|
352
|
+
if result.get("success"):
|
|
353
|
+
logger.info(f"Set protocol for {connection_id}: mode={mode}")
|
|
354
|
+
return _format_result(result)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@mcp.tool()
|
|
358
|
+
def receive_packet(
|
|
359
|
+
connection_id: str,
|
|
360
|
+
timeout: Optional[float] = None,
|
|
361
|
+
max_size: int = 4096
|
|
362
|
+
) -> str:
|
|
363
|
+
"""依照設定的協議接收完整封包。
|
|
364
|
+
|
|
365
|
+
使用 set_protocol 設定的起始/結束符號解析資料流。
|
|
366
|
+
- packet 模式:等待 start_delimiter 和 end_delimiter 之間的完整資料
|
|
367
|
+
- line 模式:等同於 receive_line
|
|
368
|
+
- raw 模式:等同於 receive_data
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
connection_id: 連線 ID
|
|
372
|
+
timeout: 接收超時秒數(選填)
|
|
373
|
+
max_size: 最大封包大小(預設 4096)
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
JSON 格式的封包資料,包含 payload_hex 和 payload_text
|
|
377
|
+
|
|
378
|
+
Examples:
|
|
379
|
+
- receive_packet("conn_id")
|
|
380
|
+
- receive_packet("conn_id", timeout=10.0)
|
|
381
|
+
"""
|
|
382
|
+
manager = get_socket_manager()
|
|
383
|
+
result = manager.receive_packet(
|
|
384
|
+
connection_id=connection_id,
|
|
385
|
+
timeout=timeout,
|
|
386
|
+
max_size=max_size
|
|
387
|
+
)
|
|
388
|
+
return _format_result(result)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@mcp.tool()
|
|
392
|
+
def get_protocol(connection_id: str) -> str:
|
|
393
|
+
"""查詢連線的當前接收協議設定。
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
connection_id: 連線 ID
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
JSON 格式的協議設定資訊
|
|
400
|
+
|
|
401
|
+
Examples:
|
|
402
|
+
- get_protocol("conn_id")
|
|
403
|
+
"""
|
|
404
|
+
manager = get_socket_manager()
|
|
405
|
+
result = manager.get_protocol(connection_id)
|
|
406
|
+
return _format_result(result)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# ==================== Background Listener ====================
|
|
410
|
+
|
|
411
|
+
@mcp.tool()
|
|
412
|
+
def start_listening(connection_id: str) -> str:
|
|
413
|
+
"""啟動背景封包監聽器。
|
|
414
|
+
|
|
415
|
+
開始在背景執行緒中持續接收資料並解析封包,解析後的封包會存入 buffer。
|
|
416
|
+
使用 get_packets 取得累積的封包。
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
connection_id: 連線 ID
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
JSON 格式的執行結果
|
|
423
|
+
|
|
424
|
+
Examples:
|
|
425
|
+
- start_listening("conn_id")
|
|
426
|
+
"""
|
|
427
|
+
logger.info(f"Starting listener for {connection_id}")
|
|
428
|
+
manager = get_socket_manager()
|
|
429
|
+
result = manager.start_listening(connection_id)
|
|
430
|
+
return _format_result(result)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
@mcp.tool()
|
|
434
|
+
def stop_listening(connection_id: str) -> str:
|
|
435
|
+
"""停止背景封包監聽器。
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
connection_id: 連線 ID
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
JSON 格式的執行結果
|
|
442
|
+
|
|
443
|
+
Examples:
|
|
444
|
+
- stop_listening("conn_id")
|
|
445
|
+
"""
|
|
446
|
+
logger.info(f"Stopping listener for {connection_id}")
|
|
447
|
+
manager = get_socket_manager()
|
|
448
|
+
result = manager.stop_listening(connection_id)
|
|
449
|
+
return _format_result(result)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
@mcp.tool()
|
|
453
|
+
def get_packets(connection_id: str, clear: bool = True) -> str:
|
|
454
|
+
"""取得所有累積的封包。
|
|
455
|
+
|
|
456
|
+
返回背景監聽器收到的所有封包陣列,預設會清空 buffer。
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
connection_id: 連線 ID
|
|
460
|
+
clear: 取得後是否清空 buffer(預設 True)
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
JSON 格式的封包陣列
|
|
464
|
+
|
|
465
|
+
Examples:
|
|
466
|
+
- get_packets("conn_id")
|
|
467
|
+
- get_packets("conn_id", clear=False)
|
|
468
|
+
"""
|
|
469
|
+
manager = get_socket_manager()
|
|
470
|
+
result = manager.get_packets(connection_id, clear=clear)
|
|
471
|
+
if result.get("success"):
|
|
472
|
+
logger.info(f"Got {result.get('count', 0)} packets from {connection_id}")
|
|
473
|
+
return _format_result(result)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
@mcp.tool()
|
|
477
|
+
def get_buffer_status(connection_id: str) -> str:
|
|
478
|
+
"""查詢 buffer 狀態。
|
|
479
|
+
|
|
480
|
+
返回目前 buffer 中的封包數量、最大容量、溢出次數等資訊。
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
connection_id: 連線 ID
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
JSON 格式的 buffer 狀態
|
|
487
|
+
|
|
488
|
+
Examples:
|
|
489
|
+
- get_buffer_status("conn_id")
|
|
490
|
+
"""
|
|
491
|
+
manager = get_socket_manager()
|
|
492
|
+
result = manager.get_buffer_status(connection_id)
|
|
493
|
+
return _format_result(result)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
# ==================== Server Info ====================
|
|
497
|
+
|
|
498
|
+
@mcp.tool()
|
|
499
|
+
def get_server_info() -> str:
|
|
500
|
+
"""取得 MCP Server 的資訊。
|
|
501
|
+
|
|
502
|
+
返回伺服器版本、支援的功能和預設設定。
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
JSON 格式的伺服器資訊
|
|
506
|
+
"""
|
|
507
|
+
manager = get_socket_manager()
|
|
508
|
+
status = manager.get_connection_status()
|
|
509
|
+
|
|
510
|
+
return _format_result({
|
|
511
|
+
"server_name": "Socket MCP Server",
|
|
512
|
+
"version": "0.2.0",
|
|
513
|
+
"features": {
|
|
514
|
+
"connection_management": ["connect", "disconnect", "status"],
|
|
515
|
+
"data_transmission": ["send_data", "receive_data", "receive_line", "receive_until"],
|
|
516
|
+
"protocol_management": ["set_protocol", "receive_packet", "get_protocol"],
|
|
517
|
+
"background_listener": ["start_listening", "stop_listening", "get_packets", "get_buffer_status"],
|
|
518
|
+
},
|
|
519
|
+
"default_settings": {
|
|
520
|
+
"timeout": DEFAULT_TIMEOUT,
|
|
521
|
+
"read_timeout": DEFAULT_READ_TIMEOUT,
|
|
522
|
+
"buffer_size": DEFAULT_BUFFER_SIZE
|
|
523
|
+
},
|
|
524
|
+
"protocol_modes": ["raw", "line", "packet"],
|
|
525
|
+
"active_connections": status.get("count", 0)
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
# ==================== Entry Point ====================
|
|
530
|
+
|
|
531
|
+
def main():
|
|
532
|
+
"""Entry point for the MCP server."""
|
|
533
|
+
import argparse
|
|
534
|
+
|
|
535
|
+
parser = argparse.ArgumentParser(description="Socket MCP Server")
|
|
536
|
+
parser.add_argument("--http", action="store_true", help="Run in HTTP mode (default: stdio mode)")
|
|
537
|
+
parser.add_argument("--port", type=int, default=8000, help="Port for HTTP mode (default: 8000)")
|
|
538
|
+
parser.add_argument("--host", type=str, default="0.0.0.0", help="Host for HTTP mode (default: 0.0.0.0)")
|
|
539
|
+
args = parser.parse_args()
|
|
540
|
+
|
|
541
|
+
logger.info("=" * 50)
|
|
542
|
+
logger.info("Socket MCP Server v0.1.0")
|
|
543
|
+
logger.info("=" * 50)
|
|
544
|
+
logger.info(f"Log level: {LOG_LEVEL}")
|
|
545
|
+
|
|
546
|
+
if args.http:
|
|
547
|
+
mcp.settings.host = args.host
|
|
548
|
+
mcp.settings.port = args.port
|
|
549
|
+
logger.info(f"Starting HTTP mode on http://localhost:{args.port}/mcp")
|
|
550
|
+
logger.info("Use MCP Inspector to connect: npx @modelcontextprotocol/inspector")
|
|
551
|
+
mcp.run(transport="streamable-http")
|
|
552
|
+
else:
|
|
553
|
+
logger.info("Starting stdio mode (for Claude Desktop, Cursor, etc.)")
|
|
554
|
+
logger.info("Waiting for MCP client connection...")
|
|
555
|
+
mcp.run()
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
if __name__ == "__main__":
|
|
559
|
+
main()
|