tina-python 0.6.5__tar.gz → 0.6.7__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.
Files changed (53) hide show
  1. {tina_python-0.6.5/tina_python.egg-info → tina_python-0.6.7}/PKG-INFO +47 -32
  2. {tina_python-0.6.5 → tina_python-0.6.7}/README.md +44 -29
  3. {tina_python-0.6.5 → tina_python-0.6.7}/pyproject.toml +3 -3
  4. {tina_python-0.6.5 → tina_python-0.6.7}/setup.py +1 -1
  5. {tina_python-0.6.5 → tina_python-0.6.7}/tina/__init__.py +1 -1
  6. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/agent.py +65 -5
  7. {tina_python-0.6.5 → tina_python-0.6.7}/tina/mcp/client.py +210 -14
  8. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/tui/tui.py +28 -11
  9. {tina_python-0.6.5 → tina_python-0.6.7/tina_python.egg-info}/PKG-INFO +47 -32
  10. {tina_python-0.6.5 → tina_python-0.6.7}/tina_python.egg-info/requires.txt +2 -2
  11. {tina_python-0.6.5 → tina_python-0.6.7}/LICENSE +0 -0
  12. {tina_python-0.6.5 → tina_python-0.6.7}/MANIFEST.in +0 -0
  13. {tina_python-0.6.5 → tina_python-0.6.7}/setup.cfg +0 -0
  14. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/__init__.py +0 -0
  15. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/__init__.py +0 -0
  16. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/agent_response.py +0 -0
  17. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/agent_runtime.py +0 -0
  18. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/context_manager.py +0 -0
  19. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/events.py +0 -0
  20. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/executor.py +0 -0
  21. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/keyword_actions.py +0 -0
  22. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/prompt.py +0 -0
  23. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/state.py +0 -0
  24. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/core/tools.py +0 -0
  25. {tina_python-0.6.5 → tina_python-0.6.7}/tina/agent/multimodal_agent.py +0 -0
  26. {tina_python-0.6.5 → tina_python-0.6.7}/tina/core/__init__.py +0 -0
  27. {tina_python-0.6.5 → tina_python-0.6.7}/tina/core/error.py +0 -0
  28. {tina_python-0.6.5 → tina_python-0.6.7}/tina/core/logger.py +0 -0
  29. {tina_python-0.6.5 → tina_python-0.6.7}/tina/llm/BaseNewAPI.py +0 -0
  30. {tina_python-0.6.5 → tina_python-0.6.7}/tina/llm/__init__.py +0 -0
  31. {tina_python-0.6.5 → tina_python-0.6.7}/tina/llm/base_api.py +0 -0
  32. {tina_python-0.6.5 → tina_python-0.6.7}/tina/llm/base_multimodal_api.py +0 -0
  33. {tina_python-0.6.5 → tina_python-0.6.7}/tina/llm/ollama_api.py +0 -0
  34. {tina_python-0.6.5 → tina_python-0.6.7}/tina/mcp/__init__.py +0 -0
  35. {tina_python-0.6.5 → tina_python-0.6.7}/tina/mcp/mcp_tools_executor.py +0 -0
  36. {tina_python-0.6.5 → tina_python-0.6.7}/tina/py.typed +0 -0
  37. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/__init__.py +0 -0
  38. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/agent_worker.py +0 -0
  39. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/deepseek.py +0 -0
  40. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/doc_parser.py +0 -0
  41. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/env_reader.py +0 -0
  42. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/multimodal_formatter.py +0 -0
  43. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/output_parser.py +0 -0
  44. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/run_agent_in_cli.py +0 -0
  45. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/system_tools.py +0 -0
  46. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/timer.py +0 -0
  47. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/tui/__init__.py +0 -0
  48. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/tui/context_manager.py +0 -0
  49. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/tui/token.py +0 -0
  50. {tina_python-0.6.5 → tina_python-0.6.7}/tina/utils/type_mapper.py +0 -0
  51. {tina_python-0.6.5 → tina_python-0.6.7}/tina_python.egg-info/SOURCES.txt +0 -0
  52. {tina_python-0.6.5 → tina_python-0.6.7}/tina_python.egg-info/dependency_links.txt +0 -0
  53. {tina_python-0.6.5 → tina_python-0.6.7}/tina_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina-python
3
- Version: 0.6.5
3
+ Version: 0.6.7
4
4
  Summary: tina is in your computer!
5
5
  Home-page: https://gitee.com/wang-churi/tina
6
6
  Author: 王出日
@@ -20,14 +20,14 @@ Requires-Dist: python-dotenv
20
20
  Provides-Extra: tui
21
21
  Requires-Dist: textual>=0.80; extra == "tui"
22
22
  Provides-Extra: mcp
23
- Requires-Dist: mcp; extra == "mcp"
23
+ Requires-Dist: mcp>=2.0; extra == "mcp"
24
24
  Provides-Extra: test
25
25
  Requires-Dist: pytest>=9.0; extra == "test"
26
26
  Requires-Dist: pytest-asyncio>=1.4; extra == "test"
27
27
  Requires-Dist: pytest-cov>=7.0; extra == "test"
28
28
  Provides-Extra: all
29
29
  Requires-Dist: textual>=0.80; extra == "all"
30
- Requires-Dist: mcp; extra == "all"
30
+ Requires-Dist: mcp>=2.0; extra == "all"
31
31
  Dynamic: author
32
32
  Dynamic: home-page
33
33
  Dynamic: license-file
@@ -50,6 +50,12 @@ pip install tina-python
50
50
  # 如果需要使用 MCP(模型上下文协议)服务
51
51
  pip install tina-python[mcp]
52
52
 
53
+ # 如果需要 TUI 终端界面
54
+ pip install tina-python[tui]
55
+
56
+ # 一次装齐所有可选功能(TUI + MCP)
57
+ pip install tina-python[all]
58
+
53
59
  # 如果需要运行测试
54
60
  pip install tina-python[test]
55
61
  ```
@@ -211,8 +217,9 @@ result = await llm.apredict(
211
217
  stream=False
212
218
  )
213
219
 
214
- # 异步流式
215
- async for chunk in llm.apredict(input_text="讲个故事", stream=True):
220
+ # 异步流式:先 await 拿到异步生成器
221
+ result = await llm.apredict(input_text="讲个故事", stream=True)
222
+ async for chunk in result:
216
223
  print(chunk["content"], end="")
217
224
  ```
218
225
 
@@ -242,8 +249,6 @@ async for chunk in llm.apredict(input_text="讲个故事", stream=True):
242
249
  {
243
250
  "role": "assistant",
244
251
  "content": "回复内容",
245
- # 推理模型时会出现
246
- "reasoning_content": "推理链...",
247
252
  # 有工具调用时会出现
248
253
  "tool_calls": [
249
254
  {
@@ -255,7 +260,8 @@ async for chunk in llm.apredict(input_text="讲个故事", stream=True):
255
260
  ]
256
261
  }
257
262
 
258
- # 流式:逐块返回,格式与非流式一致
263
+ # 流式:逐块返回片段,工具调用以 tool_name / tool_arguments 片段给出;
264
+ # 推理模型的思维链在 reasoning_content 中,末尾可能带有 usage
259
265
  ```
260
266
 
261
267
  ### 1.8 获取可用模型列表
@@ -392,9 +398,12 @@ tool_calls = [
392
398
  }
393
399
  }
394
400
  ]
395
- results = tools.execute(tool_calls)
401
+ # execute 需要事件对象(Agent 内部会自动传入)
402
+ from tina import AgentEvents
403
+
404
+ results = tools.execute(tool_calls, events=AgentEvents())
396
405
  # [
397
- # {"role": "tool", "tool_call_id": "call_123", "name": "get_weather", "content": "北京今天天气晴朗"}
406
+ # {"role": "tool", "tool_call_id": "call_123", "tool_name": "get_weather", "content": "北京今天天气晴朗"}
398
407
  # ]
399
408
  ```
400
409
 
@@ -542,7 +551,7 @@ elif agent.state == AgentState.ERROR:
542
551
  # 获取消息列表
543
552
  messages = agent.get_messages()
544
553
 
545
- # 清空消息(保留系统提示词)
554
+ # 清空全部消息(含系统提示词,之后需重新 set_system_prompt)
546
555
  agent.clear_messages()
547
556
 
548
557
  # 获取系统提示词
@@ -630,7 +639,7 @@ def on_turn_end():
630
639
  agent_a = Agent(llm=llm, tools=tools, name="agent_a")
631
640
  agent_b = Agent(llm=llm, tools=tools, name="agent_b")
632
641
 
633
- # 连接:共享消息列表
642
+ # 关联另一个 Agent(记录到 other_agents,目前不会自动共享消息列表)
634
643
  agent_a.connect_agent(agent_b)
635
644
 
636
645
  # 断开连接
@@ -707,23 +716,31 @@ mcp = MCPClient()
707
716
  # 添加 stdio 类型 MCP 服务
708
717
  mcp.add_server(
709
718
  server_id="playwright",
710
- config={
711
- "type": "stdio",
712
- "command": "npx",
713
- "args": ["@playwright/mcp@latest"]
714
- }
719
+ type="stdio",
720
+ command="npx",
721
+ args=["@playwright/mcp@latest"],
715
722
  )
716
723
 
717
- # 添加 SSE 类型 MCP 服务
724
+ # 添加 Streamable HTTP 类型 MCP 服务(推荐,取代旧 SSE)
718
725
  mcp.add_server(
719
726
  server_id="my-service",
720
- config={
721
- "type": "sse",
722
- "url": "http://localhost:8080/sse"
723
- }
727
+ type="http", # 也支持 streamable_http / streamableHttp
728
+ url="http://localhost:8080/mcp",
729
+ headers={"Authorization": "Bearer YOUR_API_KEY"}, # 可选
730
+ )
731
+
732
+ # 添加 SSE 类型 MCP 服务(旧传输,仍兼容)
733
+ mcp.add_server(
734
+ server_id="my-legacy-service",
735
+ type="sse",
736
+ url="http://localhost:8080/sse",
737
+ headers={"Authorization": "Bearer YOUR_API_KEY"}, # 可选
724
738
  )
725
739
  ```
726
740
 
741
+ > `type` 为必填项,用于区分传输类型;`command`/`args`/`env` 与 `url`/`headers`
742
+ > 分别对应 stdio 与远程类型,编辑器会根据 `type` 给出对应的参数提示。
743
+
727
744
  ### 4.3 在 Agent 中使用 MCP
728
745
 
729
746
  ```python
@@ -734,11 +751,9 @@ from tina.mcp import MCPClient
734
751
  mcp = MCPClient()
735
752
  mcp.add_server(
736
753
  server_id="playwright",
737
- config={
738
- "type": "stdio",
739
- "command": "npx",
740
- "args": ["@playwright/mcp@latest"]
741
- }
754
+ type="stdio",
755
+ command="npx",
756
+ args=["@playwright/mcp@latest"],
742
757
  )
743
758
 
744
759
  llm = BaseAPI()
@@ -759,8 +774,8 @@ for chunk in result:
759
774
  ### 4.4 MCP 服务管理
760
775
 
761
776
  ```python
762
- # 添加服务
763
- agent.add_mcp_server("server_id", config)
777
+ # 添加服务(参数与 MCPClient.add_server 一致)
778
+ agent.add_mcp_server("server_id", type="http", url="http://localhost:8080/mcp")
764
779
 
765
780
  # 移除服务
766
781
  agent.remove_mcp_server("server_id")
@@ -889,8 +904,8 @@ from tina import Agent, Tools
889
904
  from tina.llm import BaseAPI
890
905
  from tina.utils.system_tools import system_tools
891
906
 
892
- tools = Tools()
893
- tools += system_tools # 添加 tina 自带的系统工具包
907
+ tools = Tools(name="my_tools")
908
+ tools += system_tools # 添加 tina 自带的系统工具包(工具集需命名)
894
909
 
895
910
  agent = Agent(
896
911
  llm=BaseAPI(),
@@ -992,7 +1007,7 @@ run_agent_in_cli(agent)
992
1007
  |------|------|
993
1008
  | `#context` | 查看当前对话上下文(消息列表) |
994
1009
  | `#clear` | 清屏 |
995
- | `#exit` | 退出(或直接输入 `exit` / `quit`) |
1010
+ | `exit` / `quit` | 退出对话(`#exit` 不支持,会被当作普通输入) |
996
1011
 
997
1012
  ### 6.4 多模态消息构建器 `build_multimodal_message`
998
1013
 
@@ -15,6 +15,12 @@ pip install tina-python
15
15
  # 如果需要使用 MCP(模型上下文协议)服务
16
16
  pip install tina-python[mcp]
17
17
 
18
+ # 如果需要 TUI 终端界面
19
+ pip install tina-python[tui]
20
+
21
+ # 一次装齐所有可选功能(TUI + MCP)
22
+ pip install tina-python[all]
23
+
18
24
  # 如果需要运行测试
19
25
  pip install tina-python[test]
20
26
  ```
@@ -176,8 +182,9 @@ result = await llm.apredict(
176
182
  stream=False
177
183
  )
178
184
 
179
- # 异步流式
180
- async for chunk in llm.apredict(input_text="讲个故事", stream=True):
185
+ # 异步流式:先 await 拿到异步生成器
186
+ result = await llm.apredict(input_text="讲个故事", stream=True)
187
+ async for chunk in result:
181
188
  print(chunk["content"], end="")
182
189
  ```
183
190
 
@@ -207,8 +214,6 @@ async for chunk in llm.apredict(input_text="讲个故事", stream=True):
207
214
  {
208
215
  "role": "assistant",
209
216
  "content": "回复内容",
210
- # 推理模型时会出现
211
- "reasoning_content": "推理链...",
212
217
  # 有工具调用时会出现
213
218
  "tool_calls": [
214
219
  {
@@ -220,7 +225,8 @@ async for chunk in llm.apredict(input_text="讲个故事", stream=True):
220
225
  ]
221
226
  }
222
227
 
223
- # 流式:逐块返回,格式与非流式一致
228
+ # 流式:逐块返回片段,工具调用以 tool_name / tool_arguments 片段给出;
229
+ # 推理模型的思维链在 reasoning_content 中,末尾可能带有 usage
224
230
  ```
225
231
 
226
232
  ### 1.8 获取可用模型列表
@@ -357,9 +363,12 @@ tool_calls = [
357
363
  }
358
364
  }
359
365
  ]
360
- results = tools.execute(tool_calls)
366
+ # execute 需要事件对象(Agent 内部会自动传入)
367
+ from tina import AgentEvents
368
+
369
+ results = tools.execute(tool_calls, events=AgentEvents())
361
370
  # [
362
- # {"role": "tool", "tool_call_id": "call_123", "name": "get_weather", "content": "北京今天天气晴朗"}
371
+ # {"role": "tool", "tool_call_id": "call_123", "tool_name": "get_weather", "content": "北京今天天气晴朗"}
363
372
  # ]
364
373
  ```
365
374
 
@@ -507,7 +516,7 @@ elif agent.state == AgentState.ERROR:
507
516
  # 获取消息列表
508
517
  messages = agent.get_messages()
509
518
 
510
- # 清空消息(保留系统提示词)
519
+ # 清空全部消息(含系统提示词,之后需重新 set_system_prompt)
511
520
  agent.clear_messages()
512
521
 
513
522
  # 获取系统提示词
@@ -595,7 +604,7 @@ def on_turn_end():
595
604
  agent_a = Agent(llm=llm, tools=tools, name="agent_a")
596
605
  agent_b = Agent(llm=llm, tools=tools, name="agent_b")
597
606
 
598
- # 连接:共享消息列表
607
+ # 关联另一个 Agent(记录到 other_agents,目前不会自动共享消息列表)
599
608
  agent_a.connect_agent(agent_b)
600
609
 
601
610
  # 断开连接
@@ -672,23 +681,31 @@ mcp = MCPClient()
672
681
  # 添加 stdio 类型 MCP 服务
673
682
  mcp.add_server(
674
683
  server_id="playwright",
675
- config={
676
- "type": "stdio",
677
- "command": "npx",
678
- "args": ["@playwright/mcp@latest"]
679
- }
684
+ type="stdio",
685
+ command="npx",
686
+ args=["@playwright/mcp@latest"],
680
687
  )
681
688
 
682
- # 添加 SSE 类型 MCP 服务
689
+ # 添加 Streamable HTTP 类型 MCP 服务(推荐,取代旧 SSE)
683
690
  mcp.add_server(
684
691
  server_id="my-service",
685
- config={
686
- "type": "sse",
687
- "url": "http://localhost:8080/sse"
688
- }
692
+ type="http", # 也支持 streamable_http / streamableHttp
693
+ url="http://localhost:8080/mcp",
694
+ headers={"Authorization": "Bearer YOUR_API_KEY"}, # 可选
695
+ )
696
+
697
+ # 添加 SSE 类型 MCP 服务(旧传输,仍兼容)
698
+ mcp.add_server(
699
+ server_id="my-legacy-service",
700
+ type="sse",
701
+ url="http://localhost:8080/sse",
702
+ headers={"Authorization": "Bearer YOUR_API_KEY"}, # 可选
689
703
  )
690
704
  ```
691
705
 
706
+ > `type` 为必填项,用于区分传输类型;`command`/`args`/`env` 与 `url`/`headers`
707
+ > 分别对应 stdio 与远程类型,编辑器会根据 `type` 给出对应的参数提示。
708
+
692
709
  ### 4.3 在 Agent 中使用 MCP
693
710
 
694
711
  ```python
@@ -699,11 +716,9 @@ from tina.mcp import MCPClient
699
716
  mcp = MCPClient()
700
717
  mcp.add_server(
701
718
  server_id="playwright",
702
- config={
703
- "type": "stdio",
704
- "command": "npx",
705
- "args": ["@playwright/mcp@latest"]
706
- }
719
+ type="stdio",
720
+ command="npx",
721
+ args=["@playwright/mcp@latest"],
707
722
  )
708
723
 
709
724
  llm = BaseAPI()
@@ -724,8 +739,8 @@ for chunk in result:
724
739
  ### 4.4 MCP 服务管理
725
740
 
726
741
  ```python
727
- # 添加服务
728
- agent.add_mcp_server("server_id", config)
742
+ # 添加服务(参数与 MCPClient.add_server 一致)
743
+ agent.add_mcp_server("server_id", type="http", url="http://localhost:8080/mcp")
729
744
 
730
745
  # 移除服务
731
746
  agent.remove_mcp_server("server_id")
@@ -854,8 +869,8 @@ from tina import Agent, Tools
854
869
  from tina.llm import BaseAPI
855
870
  from tina.utils.system_tools import system_tools
856
871
 
857
- tools = Tools()
858
- tools += system_tools # 添加 tina 自带的系统工具包
872
+ tools = Tools(name="my_tools")
873
+ tools += system_tools # 添加 tina 自带的系统工具包(工具集需命名)
859
874
 
860
875
  agent = Agent(
861
876
  llm=BaseAPI(),
@@ -957,7 +972,7 @@ run_agent_in_cli(agent)
957
972
  |------|------|
958
973
  | `#context` | 查看当前对话上下文(消息列表) |
959
974
  | `#clear` | 清屏 |
960
- | `#exit` | 退出(或直接输入 `exit` / `quit`) |
975
+ | `exit` / `quit` | 退出对话(`#exit` 不支持,会被当作普通输入) |
961
976
 
962
977
  ### 6.4 多模态消息构建器 `build_multimodal_message`
963
978
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tina-python"
7
- version = "0.6.5"
7
+ version = "0.6.7"
8
8
  description = "tina is in your computer!"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.10"
@@ -32,7 +32,7 @@ tui = [
32
32
  "textual>=0.80",
33
33
  ]
34
34
  mcp = [
35
- "mcp",
35
+ "mcp>=2.0",
36
36
  ]
37
37
  test = [
38
38
  "pytest>=9.0",
@@ -41,7 +41,7 @@ test = [
41
41
  ]
42
42
  all = [
43
43
  "textual>=0.80",
44
- "mcp",
44
+ "mcp>=2.0",
45
45
  ]
46
46
 
47
47
  [tool.pytest.ini_options]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="tina-python",
5
- version="0.6.5",
5
+ version="0.6.7",
6
6
  packages=find_packages(),
7
7
  package_data={"tina": ["py.typed"]},
8
8
  install_requires=["httpx", "python-dotenv"],
@@ -19,4 +19,4 @@ __all__ = [
19
19
  "AgentState",
20
20
  "AgentEvents",
21
21
  ]
22
- __version__ = "0.6.5"
22
+ __version__ = "0.6.7"
@@ -10,7 +10,17 @@ Agent类:基础智能体类,默认支持API调用
10
10
  from __future__ import annotations
11
11
 
12
12
  import re
13
- from typing import List, Literal, Union, Generator, Dict, Any, AsyncGenerator, overload
13
+ from typing import (
14
+ List,
15
+ Literal,
16
+ Union,
17
+ Generator,
18
+ Dict,
19
+ Any,
20
+ Optional,
21
+ AsyncGenerator,
22
+ overload,
23
+ )
14
24
 
15
25
  from tina.agent.core.agent_response import AgentResponse, ToolCall
16
26
  from tina.agent.core.state import AgentState
@@ -18,7 +28,7 @@ from tina.agent.core.state import AgentState
18
28
  from ..llm.base_api import BaseAPI
19
29
  from .core.tools import Tools
20
30
  from .core.keyword_actions import KeywordActions
21
- from ..mcp.client import MCPClient
31
+ from ..mcp.client import MCPClient, RemoteType, StdioType
22
32
  from .core.prompt import Prompt
23
33
  from .core.context_manager import ContextManager
24
34
  from .core.agent_runtime import ToolCallingAgentRuntime, BaseAgentRuntime
@@ -466,19 +476,69 @@ class Agent:
466
476
  def get_tools_call(self) -> list:
467
477
  return self.context_manager.get_tool_calls()
468
478
 
479
+ @overload
480
+ def add_mcp_server(
481
+ self,
482
+ server_id: str,
483
+ *,
484
+ type: StdioType,
485
+ command: str,
486
+ args: Optional[List[str]] = None,
487
+ env: Optional[Dict[str, str]] = None,
488
+ max_retries: int = 3,
489
+ timeout: int = 90,
490
+ ) -> bool: ...
491
+
492
+ @overload
469
493
  def add_mcp_server(
470
- self, server_id: str, config: Dict[str, Any], max_retries=3, timeout=90
494
+ self,
495
+ server_id: str,
496
+ *,
497
+ type: RemoteType,
498
+ url: str,
499
+ headers: Optional[Dict[str, str]] = None,
500
+ max_retries: int = 3,
501
+ timeout: int = 90,
502
+ ) -> bool: ...
503
+
504
+ def add_mcp_server(
505
+ self,
506
+ server_id: str,
507
+ *,
508
+ type: str,
509
+ command: Optional[str] = None,
510
+ args: Optional[List[str]] = None,
511
+ env: Optional[Dict[str, str]] = None,
512
+ url: Optional[str] = None,
513
+ headers: Optional[Dict[str, str]] = None,
514
+ max_retries: int = 3,
515
+ timeout: int = 90,
471
516
  ) -> bool:
472
517
  """
473
518
  添加MCP服务器
474
519
  Args:
475
520
  server_id:服务器ID
476
- config:服务器配置
521
+ type:传输类型(`stdio` / `sse` / `http`)
522
+ command:`stdio` 类型的启动命令
523
+ args:`stdio` 类型的命令参数
524
+ env:`stdio` 类型的环境变量
525
+ url:`sse` / `http` 类型的服务地址
526
+ headers:`sse` / `http` 类型的请求头
477
527
  max_retries:最大重试次数
478
528
  timeout:超时时间
479
529
  """
480
530
  if self.mcp_client is not None:
481
- return self.mcp_client.add_server(server_id, config, max_retries, timeout)
531
+ return self.mcp_client.add_server(
532
+ server_id,
533
+ type=type,
534
+ command=command,
535
+ args=args,
536
+ env=env,
537
+ url=url,
538
+ headers=headers,
539
+ max_retries=max_retries,
540
+ timeout=timeout,
541
+ )
482
542
  else:
483
543
  raise ValueError("MCP客户端未初始化,请先初始化MCP客户端")
484
544
 
@@ -3,7 +3,16 @@ import concurrent.futures
3
3
  import json
4
4
  import uuid
5
5
  import threading
6
- from typing import Dict, List, Any, Optional, Union, Tuple
6
+ from typing import (
7
+ Dict,
8
+ List,
9
+ Any,
10
+ Optional,
11
+ Union,
12
+ Tuple,
13
+ Literal,
14
+ overload,
15
+ )
7
16
  from contextlib import AsyncExitStack
8
17
  from datetime import datetime
9
18
  from ..core import logger # 假设你有统一的logger
@@ -12,12 +21,78 @@ try:
12
21
  from mcp import ClientSession, StdioServerParameters
13
22
  from mcp.client.stdio import stdio_client
14
23
  from mcp.client.sse import sse_client
24
+ from mcp.client.streamable_http import (
25
+ streamable_http_client,
26
+ create_mcp_http_client,
27
+ )
15
28
 
16
29
  MCP_AVAILABLE = True
17
30
  except ImportError:
18
31
  MCP_AVAILABLE = False
19
32
 
20
33
 
34
+ # 远程传输类型别名:Streamable HTTP 在不同客户端里叫法不一,这里统一归一化。
35
+ # 官方新规范为 Streamable HTTP,`sse` 为旧版(已废弃)传输。
36
+ _REMOTE_TYPE_ALIASES = {
37
+ "sse": "sse",
38
+ "http": "http",
39
+ "https": "http",
40
+ "streamable_http": "http",
41
+ "streamablehttp": "http",
42
+ "streamable-http": "http",
43
+ }
44
+
45
+
46
+ def _normalize_server_type(server_type: str) -> str:
47
+ return _REMOTE_TYPE_ALIASES.get(str(server_type).lower(), str(server_type).lower())
48
+
49
+
50
+ StdioType = Literal["stdio"]
51
+ RemoteType = Literal[
52
+ "sse",
53
+ "http",
54
+ "https",
55
+ "streamable_http",
56
+ "streamableHttp",
57
+ "streamable-http",
58
+ ]
59
+
60
+
61
+ def _build_server_config(
62
+ server_type: str,
63
+ *,
64
+ command: Optional[str] = None,
65
+ args: Optional[List[str]] = None,
66
+ env: Optional[Dict[str, str]] = None,
67
+ url: Optional[str] = None,
68
+ headers: Optional[Dict[str, str]] = None,
69
+ ) -> Dict[str, Any]:
70
+ """把扁平化参数组装为内部 config 字典,并做必要校验。"""
71
+ stype = _normalize_server_type(server_type)
72
+ if stype == "stdio":
73
+ if not command:
74
+ raise ValueError("stdio 类型必须提供 command 参数")
75
+ config: Dict[str, Any] = {
76
+ "type": "stdio",
77
+ "command": command,
78
+ "args": list(args) if args else [],
79
+ }
80
+ if env:
81
+ config["env"] = dict(env)
82
+ return config
83
+ if stype in ("sse", "http"):
84
+ if not url:
85
+ raise ValueError(f"{server_type} 类型必须提供 url 参数")
86
+ config = {"type": stype, "url": url}
87
+ if headers:
88
+ config["headers"] = dict(headers)
89
+ return config
90
+ raise ValueError(
91
+ f"Unsupported server type: {server_type} "
92
+ f"(supported: stdio, sse, http/streamable_http)"
93
+ )
94
+
95
+
21
96
  def _tool_input_schema(tool: Any) -> Dict[str, Any]:
22
97
  """获取 MCP 工具的输入 schema,兼容 SDK 的字段改名
23
98
 
@@ -30,6 +105,70 @@ def _tool_input_schema(tool: Any) -> Dict[str, Any]:
30
105
  return schema or {}
31
106
 
32
107
 
108
+ def _normalize_property(prop: Any) -> Dict[str, Any]:
109
+ """将单个 MCP 属性 schema 归一化为 Tina/大模型友好的形式。
110
+
111
+ 修复原先只保留 `type` 且缺省退化为 Python 类型名 ``"str"`` 的问题:
112
+ - 缺少顶层 `type` 时,从 enum / anyOf 推断,最后回退为 JSON Schema 的
113
+ ``"string"``,不再输出 ``"str"``;
114
+ - 保留 `enum` 等约束信息,避免模型失去取值范围;
115
+ - 将带 null 的 `anyOf`/`oneOf` 及 ``["string", "null"]`` 形式的 type
116
+ 压平为 `type` + `nullable`,提升对不支持联合类型的模型兼容性。
117
+ """
118
+ prop = dict(prop) if isinstance(prop, dict) else {}
119
+ description = prop.get("description", "")
120
+
121
+ # 压平 nullable 联合类型(例如 zod 的 z.enum([...]).nullable())
122
+ variants = prop.get("anyOf") or prop.get("oneOf")
123
+ if isinstance(variants, list):
124
+ non_null = [
125
+ v for v in variants if isinstance(v, dict) and v.get("type") != "null"
126
+ ]
127
+ has_null = any(
128
+ isinstance(v, dict) and v.get("type") == "null" for v in variants
129
+ )
130
+ if len(non_null) == 1 and has_null:
131
+ merged = dict(non_null[0])
132
+ merged.setdefault("description", description)
133
+ merged["nullable"] = True
134
+ return _normalize_property(merged)
135
+
136
+ # 压平 type 为数组的形式,如 ["string", "null"]
137
+ raw_type = prop.get("type")
138
+ if isinstance(raw_type, list):
139
+ types = [t for t in raw_type if t != "null"]
140
+ prop["nullable"] = prop.get("nullable", "null" in raw_type)
141
+ if len(types) == 1:
142
+ prop["type"] = types[0]
143
+ else:
144
+ prop.pop("type", None)
145
+
146
+ # 缺少 type 时从 enum 推断,否则回退 "string"(而非 Python 的 "str")
147
+ if "type" not in prop:
148
+ values = prop.get("enum")
149
+ if isinstance(values, list) and values:
150
+ if all(isinstance(v, str) for v in values):
151
+ prop["type"] = "string"
152
+ elif all(
153
+ isinstance(v, (int, float)) and not isinstance(v, bool)
154
+ for v in values
155
+ ):
156
+ prop["type"] = "number"
157
+ elif not (
158
+ prop.get("anyOf") or prop.get("oneOf") or prop.get("$ref")
159
+ ):
160
+ prop["type"] = "string"
161
+
162
+ prop["description"] = description
163
+ return prop
164
+
165
+
166
+ def _convert_input_schema(schema: Dict[str, Any]) -> Dict[str, Any]:
167
+ """将 MCP 工具的 input schema.properties 转为 Tina 工具参数格式"""
168
+ properties = schema.get("properties", {}) or {}
169
+ return {name: _normalize_property(prop) for name, prop in properties.items()}
170
+
171
+
33
172
  class MCPClient:
34
173
  def __init__(self):
35
174
  if not MCP_AVAILABLE:
@@ -79,7 +218,8 @@ class MCPClient:
79
218
  stack = AsyncExitStack()
80
219
  stop = asyncio.Event()
81
220
  try:
82
- server_type = config.get("type", "").lower()
221
+ server_type = _normalize_server_type(config.get("type", ""))
222
+ headers = config.get("headers") or None
83
223
  if server_type == "stdio":
84
224
  params = StdioServerParameters(
85
225
  command=config["command"],
@@ -91,12 +231,28 @@ class MCPClient:
91
231
  ClientSession(transport[0], transport[1])
92
232
  )
93
233
  elif server_type == "sse":
94
- transport = await stack.enter_async_context(sse_client(config["url"]))
234
+ transport = await stack.enter_async_context(
235
+ sse_client(config["url"], headers=headers)
236
+ )
237
+ session = await stack.enter_async_context(
238
+ ClientSession(transport[0], transport[1])
239
+ )
240
+ elif server_type == "http":
241
+ # Streamable HTTP:单端点,取代旧的 SSE 传输。
242
+ # headers 需通过预配置的 httpx 客户端传入;该客户端由 stack 负责关闭。
243
+ http_client = create_mcp_http_client(headers=headers)
244
+ await stack.enter_async_context(http_client)
245
+ transport = await stack.enter_async_context(
246
+ streamable_http_client(config["url"], http_client=http_client)
247
+ )
95
248
  session = await stack.enter_async_context(
96
249
  ClientSession(transport[0], transport[1])
97
250
  )
98
251
  else:
99
- raise ValueError(f"Unsupported server type: {server_type}")
252
+ raise ValueError(
253
+ f"Unsupported server type: {server_type} "
254
+ f"(supported: stdio, sse, http/streamable_http)"
255
+ )
100
256
 
101
257
  await session.initialize()
102
258
  res = await session.list_tools()
@@ -127,14 +283,61 @@ class MCPClient:
127
283
 
128
284
  # --- 同步接口桥接 ---
129
285
 
286
+ @overload
130
287
  def add_server(
131
288
  self,
132
289
  server_id: str,
133
- config: Dict[str, Any],
290
+ *,
291
+ type: StdioType,
292
+ command: str,
293
+ args: Optional[List[str]] = None,
294
+ env: Optional[Dict[str, str]] = None,
295
+ max_retries: int = 1,
296
+ timeout: int = 90,
297
+ ) -> bool: ...
298
+
299
+ @overload
300
+ def add_server(
301
+ self,
302
+ server_id: str,
303
+ *,
304
+ type: RemoteType,
305
+ url: str,
306
+ headers: Optional[Dict[str, str]] = None,
307
+ max_retries: int = 1,
308
+ timeout: int = 90,
309
+ ) -> bool: ...
310
+
311
+ def add_server(
312
+ self,
313
+ server_id: str,
314
+ *,
315
+ type: str,
316
+ command: Optional[str] = None,
317
+ args: Optional[List[str]] = None,
318
+ env: Optional[Dict[str, str]] = None,
319
+ url: Optional[str] = None,
320
+ headers: Optional[Dict[str, str]] = None,
134
321
  max_retries: int = 1,
135
322
  timeout: int = 90,
136
323
  ) -> bool:
137
- """添加并连接一个 MCP 服务器,失败时按 max_retries 重试"""
324
+ """添加并连接一个 MCP 服务器,失败时按 max_retries 重试。
325
+
326
+ 参数按传输类型扁平化:
327
+
328
+ - ``type="stdio"``:需要 ``command``,可选 ``args`` / ``env``。
329
+ - ``type="http"``(或 ``streamable_http`` 等别名)/ ``type="sse"``:
330
+ 需要 ``url``,可选 ``headers``。
331
+ """
332
+ config = _build_server_config(
333
+ type,
334
+ command=command,
335
+ args=args,
336
+ env=env,
337
+ url=url,
338
+ headers=headers,
339
+ )
340
+
138
341
  with self._lock:
139
342
  if server_id in self.servers:
140
343
  return True
@@ -269,17 +472,10 @@ class MCPClient:
269
472
  # 注意:Tina Tools 内部可能还会根据包名加一层前缀
270
473
  # 这里的 register_no_function 保持你习惯的格式
271
474
  schema = _tool_input_schema(tool)
272
- properties = schema.get("properties", {}) or {}
273
475
  tina_tools.register_no_function(
274
476
  name=f"{sid}_{tool.name}", # 外部包名是mcp,内部就是 mcp_sid_name
275
477
  description=f"[MCP:{sid}] {tool.description}",
276
- parameters={
277
- k: {
278
- "type": v.get("type", "str"),
279
- "description": v.get("description", ""),
280
- }
281
- for k, v in properties.items()
282
- },
478
+ parameters=_convert_input_schema(schema),
283
479
  required_parameters=schema.get("required", []) or [],
284
480
  )
285
481
  return tina_tools
@@ -498,9 +498,10 @@ class TinaTUI(App):
498
498
 
499
499
  # 渲染泵间隔:界面更新与滚动统一在这里做,与 chunk 速率解耦
500
500
  PUMP_INTERVAL = 0.1
501
- # 流式块重渲染的最小间隔(秒),内容越长间隔越大,避免 O(n²) 重解析
502
- RENDER_INTERVAL = 0.08
503
- RENDER_INTERVAL_MAX = 0.5
501
+ # 流式块重渲染的最小间隔(秒)。采用增量 append 后,开销与新增内容成正比,
502
+ # 因此只需轻微随长度放宽,长文本也能保持较顺滑的刷新。
503
+ RENDER_INTERVAL = 0.06
504
+ RENDER_INTERVAL_MAX = 0.2
504
505
 
505
506
  # 确认框内联显示参数的上限,超过则只显示提示(按 Ctrl+O 查看完整参数)
506
507
  CONFIRM_INLINE_ARGS = 200
@@ -1149,7 +1150,7 @@ class TinaTUI(App):
1149
1150
  collapsed=bool(block.metadata.get("collapsed", False)),
1150
1151
  markup=bool(block.metadata.get("markup", False)),
1151
1152
  )
1152
- return Markdown("", classes="assistant-message")
1153
+ return Markdown(classes="assistant-message")
1153
1154
 
1154
1155
  def _ensure_widget(self, block: TuiBlock) -> Any:
1155
1156
  widget = self._widgets.get(block.id)
@@ -1160,15 +1161,31 @@ class TinaTUI(App):
1160
1161
  self._messages_widget().mount(widget)
1161
1162
  return widget
1162
1163
 
1164
+ def _render_markdown(self, widget: Markdown, content: str) -> Any:
1165
+ """增量渲染助手 Markdown:只解析新增片段,避免整篇重解析。
1166
+
1167
+ `Markdown.update()` 每次都会整篇重解析并重建所有子块,流式输出时呈
1168
+ O(n²) 开销,正文越长越卡。挂载完成后改用 `append()`,只解析新增片段,
1169
+ 开销与新增内容成正比。
1170
+
1171
+ 注意:`_initial_markdown` 要始终保持为最新内容,供 `Markdown._on_mount`
1172
+ 首次渲染使用,避免挂载时用空串覆盖已写入的内容。
1173
+ """
1174
+ widget._initial_markdown = content
1175
+ current = widget.source
1176
+ if content == current:
1177
+ return None # 内容未变化,跳过昂贵的 Markdown 解析
1178
+ if widget.is_mounted and content.startswith(current):
1179
+ # 纯追加:append() 只解析新增部分
1180
+ return widget.append(content[len(current):])
1181
+ # 尚未挂载完成 或 内容被替换/回退:整篇刷新一次
1182
+ return widget.update(content)
1183
+
1163
1184
  def _apply_block(self, block: TuiBlock) -> Any:
1164
1185
  """把块更新到组件;Markdown 为异步渲染,返回可等待对象"""
1165
1186
  widget = self._ensure_widget(block)
1166
1187
  if isinstance(widget, Markdown):
1167
- # Markdown.update() 异步全量重解析,必须在渲染泵里按节流调用。
1168
- # Textual 挂载时会用 _initial_markdown 再渲染一次,这里同步成最新内容,
1169
- # 避免它用空串覆盖已流式写入的内容。
1170
- widget._initial_markdown = block.content
1171
- return widget.update(block.content)
1188
+ return self._render_markdown(widget, block.content)
1172
1189
  if isinstance(widget, (ReasoningBlock, ToolBlock, ResultBlock)):
1173
1190
  widget.set_block(block)
1174
1191
  elif block.role in ("user", "error", "system"):
@@ -1178,10 +1195,10 @@ class TinaTUI(App):
1178
1195
  return None
1179
1196
 
1180
1197
  def _should_render(self, block_id: int, length: int = 0) -> bool:
1181
- """流式节流:内容越长间隔越大,避免反复全量重解析"""
1198
+ """流式节流:增量 append 足够便宜,仅轻微随长度放宽间隔"""
1182
1199
  now = time.monotonic()
1183
1200
  interval = min(
1184
- self.RENDER_INTERVAL_MAX, self.RENDER_INTERVAL + length / 20000
1201
+ self.RENDER_INTERVAL_MAX, self.RENDER_INTERVAL + length / 100000
1185
1202
  )
1186
1203
  last = self._last_render.get(block_id, 0.0)
1187
1204
  if now - last >= interval:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina-python
3
- Version: 0.6.5
3
+ Version: 0.6.7
4
4
  Summary: tina is in your computer!
5
5
  Home-page: https://gitee.com/wang-churi/tina
6
6
  Author: 王出日
@@ -20,14 +20,14 @@ Requires-Dist: python-dotenv
20
20
  Provides-Extra: tui
21
21
  Requires-Dist: textual>=0.80; extra == "tui"
22
22
  Provides-Extra: mcp
23
- Requires-Dist: mcp; extra == "mcp"
23
+ Requires-Dist: mcp>=2.0; extra == "mcp"
24
24
  Provides-Extra: test
25
25
  Requires-Dist: pytest>=9.0; extra == "test"
26
26
  Requires-Dist: pytest-asyncio>=1.4; extra == "test"
27
27
  Requires-Dist: pytest-cov>=7.0; extra == "test"
28
28
  Provides-Extra: all
29
29
  Requires-Dist: textual>=0.80; extra == "all"
30
- Requires-Dist: mcp; extra == "all"
30
+ Requires-Dist: mcp>=2.0; extra == "all"
31
31
  Dynamic: author
32
32
  Dynamic: home-page
33
33
  Dynamic: license-file
@@ -50,6 +50,12 @@ pip install tina-python
50
50
  # 如果需要使用 MCP(模型上下文协议)服务
51
51
  pip install tina-python[mcp]
52
52
 
53
+ # 如果需要 TUI 终端界面
54
+ pip install tina-python[tui]
55
+
56
+ # 一次装齐所有可选功能(TUI + MCP)
57
+ pip install tina-python[all]
58
+
53
59
  # 如果需要运行测试
54
60
  pip install tina-python[test]
55
61
  ```
@@ -211,8 +217,9 @@ result = await llm.apredict(
211
217
  stream=False
212
218
  )
213
219
 
214
- # 异步流式
215
- async for chunk in llm.apredict(input_text="讲个故事", stream=True):
220
+ # 异步流式:先 await 拿到异步生成器
221
+ result = await llm.apredict(input_text="讲个故事", stream=True)
222
+ async for chunk in result:
216
223
  print(chunk["content"], end="")
217
224
  ```
218
225
 
@@ -242,8 +249,6 @@ async for chunk in llm.apredict(input_text="讲个故事", stream=True):
242
249
  {
243
250
  "role": "assistant",
244
251
  "content": "回复内容",
245
- # 推理模型时会出现
246
- "reasoning_content": "推理链...",
247
252
  # 有工具调用时会出现
248
253
  "tool_calls": [
249
254
  {
@@ -255,7 +260,8 @@ async for chunk in llm.apredict(input_text="讲个故事", stream=True):
255
260
  ]
256
261
  }
257
262
 
258
- # 流式:逐块返回,格式与非流式一致
263
+ # 流式:逐块返回片段,工具调用以 tool_name / tool_arguments 片段给出;
264
+ # 推理模型的思维链在 reasoning_content 中,末尾可能带有 usage
259
265
  ```
260
266
 
261
267
  ### 1.8 获取可用模型列表
@@ -392,9 +398,12 @@ tool_calls = [
392
398
  }
393
399
  }
394
400
  ]
395
- results = tools.execute(tool_calls)
401
+ # execute 需要事件对象(Agent 内部会自动传入)
402
+ from tina import AgentEvents
403
+
404
+ results = tools.execute(tool_calls, events=AgentEvents())
396
405
  # [
397
- # {"role": "tool", "tool_call_id": "call_123", "name": "get_weather", "content": "北京今天天气晴朗"}
406
+ # {"role": "tool", "tool_call_id": "call_123", "tool_name": "get_weather", "content": "北京今天天气晴朗"}
398
407
  # ]
399
408
  ```
400
409
 
@@ -542,7 +551,7 @@ elif agent.state == AgentState.ERROR:
542
551
  # 获取消息列表
543
552
  messages = agent.get_messages()
544
553
 
545
- # 清空消息(保留系统提示词)
554
+ # 清空全部消息(含系统提示词,之后需重新 set_system_prompt)
546
555
  agent.clear_messages()
547
556
 
548
557
  # 获取系统提示词
@@ -630,7 +639,7 @@ def on_turn_end():
630
639
  agent_a = Agent(llm=llm, tools=tools, name="agent_a")
631
640
  agent_b = Agent(llm=llm, tools=tools, name="agent_b")
632
641
 
633
- # 连接:共享消息列表
642
+ # 关联另一个 Agent(记录到 other_agents,目前不会自动共享消息列表)
634
643
  agent_a.connect_agent(agent_b)
635
644
 
636
645
  # 断开连接
@@ -707,23 +716,31 @@ mcp = MCPClient()
707
716
  # 添加 stdio 类型 MCP 服务
708
717
  mcp.add_server(
709
718
  server_id="playwright",
710
- config={
711
- "type": "stdio",
712
- "command": "npx",
713
- "args": ["@playwright/mcp@latest"]
714
- }
719
+ type="stdio",
720
+ command="npx",
721
+ args=["@playwright/mcp@latest"],
715
722
  )
716
723
 
717
- # 添加 SSE 类型 MCP 服务
724
+ # 添加 Streamable HTTP 类型 MCP 服务(推荐,取代旧 SSE)
718
725
  mcp.add_server(
719
726
  server_id="my-service",
720
- config={
721
- "type": "sse",
722
- "url": "http://localhost:8080/sse"
723
- }
727
+ type="http", # 也支持 streamable_http / streamableHttp
728
+ url="http://localhost:8080/mcp",
729
+ headers={"Authorization": "Bearer YOUR_API_KEY"}, # 可选
730
+ )
731
+
732
+ # 添加 SSE 类型 MCP 服务(旧传输,仍兼容)
733
+ mcp.add_server(
734
+ server_id="my-legacy-service",
735
+ type="sse",
736
+ url="http://localhost:8080/sse",
737
+ headers={"Authorization": "Bearer YOUR_API_KEY"}, # 可选
724
738
  )
725
739
  ```
726
740
 
741
+ > `type` 为必填项,用于区分传输类型;`command`/`args`/`env` 与 `url`/`headers`
742
+ > 分别对应 stdio 与远程类型,编辑器会根据 `type` 给出对应的参数提示。
743
+
727
744
  ### 4.3 在 Agent 中使用 MCP
728
745
 
729
746
  ```python
@@ -734,11 +751,9 @@ from tina.mcp import MCPClient
734
751
  mcp = MCPClient()
735
752
  mcp.add_server(
736
753
  server_id="playwright",
737
- config={
738
- "type": "stdio",
739
- "command": "npx",
740
- "args": ["@playwright/mcp@latest"]
741
- }
754
+ type="stdio",
755
+ command="npx",
756
+ args=["@playwright/mcp@latest"],
742
757
  )
743
758
 
744
759
  llm = BaseAPI()
@@ -759,8 +774,8 @@ for chunk in result:
759
774
  ### 4.4 MCP 服务管理
760
775
 
761
776
  ```python
762
- # 添加服务
763
- agent.add_mcp_server("server_id", config)
777
+ # 添加服务(参数与 MCPClient.add_server 一致)
778
+ agent.add_mcp_server("server_id", type="http", url="http://localhost:8080/mcp")
764
779
 
765
780
  # 移除服务
766
781
  agent.remove_mcp_server("server_id")
@@ -889,8 +904,8 @@ from tina import Agent, Tools
889
904
  from tina.llm import BaseAPI
890
905
  from tina.utils.system_tools import system_tools
891
906
 
892
- tools = Tools()
893
- tools += system_tools # 添加 tina 自带的系统工具包
907
+ tools = Tools(name="my_tools")
908
+ tools += system_tools # 添加 tina 自带的系统工具包(工具集需命名)
894
909
 
895
910
  agent = Agent(
896
911
  llm=BaseAPI(),
@@ -992,7 +1007,7 @@ run_agent_in_cli(agent)
992
1007
  |------|------|
993
1008
  | `#context` | 查看当前对话上下文(消息列表) |
994
1009
  | `#clear` | 清屏 |
995
- | `#exit` | 退出(或直接输入 `exit` / `quit`) |
1010
+ | `exit` / `quit` | 退出对话(`#exit` 不支持,会被当作普通输入) |
996
1011
 
997
1012
  ### 6.4 多模态消息构建器 `build_multimodal_message`
998
1013
 
@@ -3,10 +3,10 @@ python-dotenv
3
3
 
4
4
  [all]
5
5
  textual>=0.80
6
- mcp
6
+ mcp>=2.0
7
7
 
8
8
  [mcp]
9
- mcp
9
+ mcp>=2.0
10
10
 
11
11
  [test]
12
12
  pytest>=9.0
File without changes
File without changes
File without changes
File without changes