scp-py-client 0.0.1__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.
scp_client/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """scp_client
2
+
3
+ Public API for the `scp-client` PyPI distribution.
4
+ """
5
+
6
+ from .scp_client import fetch_scp_Client, stream_scp_Client
7
+
8
+ __all__ = [
9
+ "fetch_scp_Client",
10
+ "stream_scp_Client",
11
+ ]
@@ -0,0 +1,213 @@
1
+ from mcp.client.streamable_http import streamable_http_client
2
+ from mcp.client.sse import sse_client
3
+ from mcp import ClientSession
4
+ import httpx
5
+ import uuid
6
+ import json
7
+ from typing import Literal
8
+
9
+
10
+ class fetch_scp_Client:
11
+ """使用MCP SDK的非流式客户端"""
12
+
13
+ def __init__(self, server_url: str, transport_type: Literal["streamable-http", "sse"] = "streamable-http", headers_config: dict={}):
14
+ self.server_url = server_url
15
+ self.session = None
16
+ self.transport_type = transport_type
17
+ self.headers_config = headers_config
18
+
19
+ async def connect(self):
20
+ """建立连接并初始化会话"""
21
+ print(f"\n{'='*80}")
22
+ print("连接到MCP服务器")
23
+ print(f"{'='*80}")
24
+ print(f"服务器地址: {self.server_url}")
25
+
26
+ try:
27
+ # 建立streamable-http传输连接
28
+ if self.transport_type == "streamable-http":
29
+ self.transport = streamable_http_client(
30
+ url=self.server_url,
31
+ http_client=httpx.AsyncClient(headers = self.headers_config)
32
+ )
33
+ self.read, self.write, self.get_session_id = await self.transport.__aenter__()
34
+ session_id = self.get_session_id()
35
+
36
+ # 建立sse传输连接
37
+ elif self.transport_type == "sse":
38
+ self.transport = sse_client(url=self.server_url, headers=self.headers_config)
39
+ self.read, self.write = await self.transport.__aenter__()
40
+ session_id = str(uuid.uuid4())
41
+
42
+ else:
43
+ raise ValueError(f"不支持的传输类型: {self.transport_type}")
44
+
45
+ # 创建客户端会话
46
+ self.session_ctx = ClientSession(self.read, self.write)
47
+ self.session = await self.session_ctx.__aenter__()
48
+
49
+ # 初始化会话
50
+ await self.session.initialize()
51
+ print(f"✓ 连接成功")
52
+ print(f"✓ 会话ID: {session_id}")
53
+ print(f"{'='*80}\n")
54
+ return True
55
+
56
+ except Exception as e:
57
+ print(f"✗ 连接失败: {e}")
58
+ import traceback
59
+ traceback.print_exc()
60
+ return False
61
+
62
+ async def disconnect(self):
63
+ """断开连接"""
64
+ try:
65
+ if self.session:
66
+ await self.session_ctx.__aexit__(None, None, None)
67
+ if hasattr(self, 'transport'):
68
+ await self.transport.__aexit__(None, None, None)
69
+ print("\n✓ 已断开连接\n")
70
+ except Exception as e:
71
+ print(f"✗ 断开连接时出错: {e}")
72
+
73
+ async def list_tools(self):
74
+ """列出所有可用工具"""
75
+ print(f"\n{'='*80}")
76
+ print("列出所有可用工具")
77
+ print(f"{'='*80}")
78
+
79
+ try:
80
+ tools_list = await self.session.list_tools()
81
+ print(f"\n可用工具 (共{len(tools_list.tools)}个):\n")
82
+
83
+ for i, tool in enumerate(tools_list.tools, 1):
84
+ print(f"{i:2d}. {tool.name}")
85
+ if tool.description:
86
+ desc_line = tool.description
87
+ print(f" {desc_line}")
88
+
89
+ print(f"\n✓ 工具列表获取成功")
90
+ return tools_list.tools
91
+
92
+ except Exception as e:
93
+ print(f"✗ 列出工具失败: {e}")
94
+ return []
95
+
96
+ def parse_result(self, result):
97
+ """解析MCP工具调用结果"""
98
+ try:
99
+ if hasattr(result, 'content') and result.content:
100
+ content = result.content[0]
101
+ if hasattr(content, 'text'):
102
+ return json.loads(content.text)
103
+
104
+ return str(result)
105
+ except Exception as e:
106
+ return {"error": f"解析结果失败: {e}", "raw": str(result)}
107
+
108
+
109
+ class stream_scp_Client:
110
+ """使用MCP SDK的流式客户端"""
111
+
112
+ def __init__(self, server_url: str, headers_config: dict={}):
113
+ self.server_url = server_url
114
+ self.session = None
115
+ self.headers_config = headers_config
116
+
117
+ async def handle_notification(self, message):
118
+ """处理通知"""
119
+ streaming_buffer = []
120
+ # 解析通知对象
121
+ if hasattr(message, 'root'):
122
+ actual = message.root
123
+ else:
124
+ actual = message
125
+
126
+ method = getattr(actual, "method", "")
127
+ params = getattr(actual, "params", None)
128
+
129
+ # 只处理 notifications/message
130
+ if method == "notifications/message" and params:
131
+ data = getattr(params, 'data', None) or {}
132
+
133
+ if isinstance(data, dict):
134
+ data_type = data.get("type", "")
135
+
136
+ if data_type == "stream_chunk":
137
+ # 实时显示流式内容
138
+ content = data.get("content", "")
139
+ print(content, end="", flush=True)
140
+ streaming_buffer.append(content)
141
+
142
+ elif data_type == "stream_complete":
143
+ # 流式完成
144
+ print()
145
+
146
+ async def connect(self):
147
+ """建立连接并初始化会话"""
148
+ print(f"\n{'='*80}")
149
+ print("连接到MCP服务器")
150
+ print(f"{'='*80}")
151
+ print(f"服务器地址: {self.server_url}")
152
+
153
+ try:
154
+ # 建立streamable-http传输连接
155
+ self.transport = streamable_http_client(
156
+ url=self.server_url,
157
+ http_client=httpx.AsyncClient(headers = self.headers_config)
158
+ )
159
+ self.read, self.write, self.get_session_id = await self.transport.__aenter__()
160
+
161
+ # 创建客户端会话
162
+ self.session_ctx = ClientSession(self.read, self.write, message_handler=self.handle_notification)
163
+ self.session = await self.session_ctx.__aenter__()
164
+
165
+ # 初始化会话
166
+ await self.session.initialize()
167
+ session_id = self.get_session_id()
168
+
169
+ print(f"✓ 连接成功")
170
+ print(f"✓ 会话ID: {session_id}")
171
+ print(f"{'='*80}\n")
172
+ return True
173
+
174
+ except Exception as e:
175
+ print(f"✗ 连接失败: {e}")
176
+ import traceback
177
+ traceback.print_exc()
178
+ return False
179
+
180
+ async def disconnect(self):
181
+ """断开连接"""
182
+ try:
183
+ if self.session:
184
+ await self.session_ctx.__aexit__(None, None, None)
185
+ if hasattr(self, 'transport'):
186
+ await self.transport.__aexit__(None, None, None)
187
+ print("\n✓ 已断开连接\n")
188
+ except Exception as e:
189
+ print(f"✗ 断开连接时出错: {e}")
190
+
191
+ async def list_tools(self):
192
+ """列出所有可用工具"""
193
+ print(f"\n{'='*80}")
194
+ print("列出所有可用工具")
195
+ print(f"{'='*80}")
196
+
197
+ try:
198
+ tools_list = await self.session.list_tools()
199
+ print(f"\n可用工具 (共{len(tools_list.tools)}个):\n")
200
+
201
+ for i, tool in enumerate(tools_list.tools, 1):
202
+ print(f"{i:2d}. {tool.name}")
203
+ if tool.description:
204
+ desc_line = tool.description
205
+ print(f" {desc_line}")
206
+
207
+ print(f"\n✓ 工具列表获取成功")
208
+ return tools_list.tools
209
+
210
+ except Exception as e:
211
+ print(f"✗ 列出工具失败: {e}")
212
+ return []
213
+
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: scp-py-client
3
+ Version: 0.0.1
4
+ Summary: SCP Client - SCP协议客户端库,支持流式和非流式通信
5
+ Author: hanxiaozuo
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hanxiaozuo/scp-client
8
+ Project-URL: Repository, https://github.com/hanxiaozuo/scp-client
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: mcp>=1.25.0
12
+ Requires-Dist: httpx>=0.28.0
13
+ Requires-Dist: httpx-sse>=0.4.0
14
+
15
+ # scp-client
16
+
17
+ SCP(Science Context Protocol)相关服务的客户端工具库。
18
+
19
+ ## 安装
20
+
21
+ ```bash
22
+ pip install scp-client
23
+ ```
24
+
25
+ ## 使用
26
+
27
+ ### 非流式(streamable-http / sse)
28
+
29
+ ```python
30
+ import asyncio
31
+ from scp_client import fetch_scp_Client
32
+
33
+
34
+ async def main():
35
+ client = fetch_scp_Client(
36
+ server_url="https://your-mcp-server.example.com/mcp",
37
+ transport_type="streamable-http", # or "sse"
38
+ headers_config={},
39
+ )
40
+ await client.connect()
41
+ await client.list_tools()
42
+ await client.disconnect()
43
+
44
+
45
+ if __name__ == "__main__":
46
+ asyncio.run(main())
47
+ ```
48
+
49
+ ### 流式(streamable-http)
50
+
51
+ ```python
52
+ import asyncio
53
+ from scp_client import stream_scp_Client
54
+
55
+
56
+ async def main():
57
+ client = stream_scp_Client(
58
+ server_url="https://your-mcp-server.example.com/mcp",
59
+ headers_config={},
60
+ )
61
+ await client.connect()
62
+ await client.list_tools()
63
+ await client.disconnect()
64
+
65
+
66
+ if __name__ == "__main__":
67
+ asyncio.run(main())
68
+ ```
@@ -0,0 +1,6 @@
1
+ scp_client/__init__.py,sha256=3KKpSuzsvCMLRnYDTUxJA1VV0oNced7HUNHMUcfkgQs,195
2
+ scp_client/scp_client.py,sha256=q3V2hrQ_9SQ8UT2tDPGoeUG_PiG5krH-_20i_nl2Lxk,7638
3
+ scp_py_client-0.0.1.dist-info/METADATA,sha256=S_oUykvf4m7pvbwp5ofgO3qtE6og83_CiEaYTs6bVOU,1445
4
+ scp_py_client-0.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
5
+ scp_py_client-0.0.1.dist-info/top_level.txt,sha256=8l-5l1L2JQmdqV8KOdfJZbFaNP4fJxpeZQvTw8yuyEw,11
6
+ scp_py_client-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ scp_client