bohr-agent-sdk 0.1.105__tar.gz → 0.1.107__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 (89) hide show
  1. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/PKG-INFO +29 -2
  2. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/README.md +27 -0
  3. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/pyproject.toml +2 -2
  4. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/setup.py +3 -2
  5. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/bohr_agent_sdk.egg-info/PKG-INFO +29 -2
  6. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/bohr_agent_sdk.egg-info/SOURCES.txt +2 -0
  7. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/bohr_agent_sdk.egg-info/requires.txt +1 -1
  8. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/files.py +3 -16
  9. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/utils.py +7 -6
  10. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/connection.py +10 -6
  11. bohr_agent_sdk-0.1.107/src/dp/agent/client/__init__.py +3 -0
  12. bohr_agent_sdk-0.1.107/src/dp/agent/client/mcp_client.py +162 -0
  13. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/calculation_mcp_server.py +4 -2
  14. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/setup.cfg +0 -0
  15. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/bohr_agent_sdk.egg-info/dependency_links.txt +0 -0
  16. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/bohr_agent_sdk.egg-info/entry_points.txt +0 -0
  17. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/bohr_agent_sdk.egg-info/top_level.txt +0 -0
  18. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/__init__.py +0 -0
  19. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/__init__.py +0 -0
  20. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/adk/__init__.py +0 -0
  21. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/adk/client/__init__.py +0 -0
  22. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/adk/client/calculation_mcp_tool.py +0 -0
  23. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/adk/storage_artifact_service.py +0 -0
  24. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/adk/utils.py +0 -0
  25. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/camel/__init__.py +0 -0
  26. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/camel/client/__init__.py +0 -0
  27. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/adapter/camel/client/calculation_mcp_client.py +0 -0
  28. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/__init__.py +0 -0
  29. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/cli.py +0 -0
  30. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/__init__.py +0 -0
  31. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/calculation/simple.py.template +0 -0
  32. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/device/tescan_device.py.template +0 -0
  33. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/main.py.template +0 -0
  34. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/__init__.py +0 -0
  35. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/__init__.py +0 -0
  36. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/config.py +0 -0
  37. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/constants.py +0 -0
  38. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/debug.py +0 -0
  39. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/files_upload.py +0 -0
  40. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/files_user.py +0 -0
  41. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/messages.py +0 -0
  42. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/projects.py +0 -0
  43. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/sessions.py +0 -0
  44. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/api/websocket.py +0 -0
  45. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/config/__init__.py +0 -0
  46. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/config/agent_config.py +0 -0
  47. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/index.html +0 -0
  48. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/package.json +0 -0
  49. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/tsconfig.json +0 -0
  50. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/tsconfig.node.json +0 -0
  51. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/ui-static/assets/index-DdAmKhul.js +0 -0
  52. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/ui-static/assets/index-DfN2raU9.css +0 -0
  53. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/ui-static/index.html +0 -0
  54. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/frontend/vite.config.ts +0 -0
  55. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/scripts/build_ui.py +0 -0
  56. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/__init__.py +0 -0
  57. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/app.py +0 -0
  58. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/file_watcher.py +0 -0
  59. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/middleware.py +0 -0
  60. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/models.py +0 -0
  61. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/session_manager.py +0 -0
  62. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/user_files.py +0 -0
  63. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/server/utils.py +0 -0
  64. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/test_download.py +0 -0
  65. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/ui_utils.py +0 -0
  66. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cli/templates/ui/websocket-server.py +0 -0
  67. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cloud/__init__.py +0 -0
  68. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cloud/main.py +0 -0
  69. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cloud/mcp.py +0 -0
  70. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/cloud/mqtt.py +0 -0
  71. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/device/__init__.py +0 -0
  72. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/device/device/__init__.py +0 -0
  73. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/device/device/device.py +0 -0
  74. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/device/device/types.py +0 -0
  75. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/device/mqtt_device_twin.py +0 -0
  76. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/__init__.py +0 -0
  77. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/executor/__init__.py +0 -0
  78. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/executor/base_executor.py +0 -0
  79. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/executor/dispatcher_executor.py +0 -0
  80. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/executor/local_executor.py +0 -0
  81. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/preprocessor.py +0 -0
  82. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/storage/__init__.py +0 -0
  83. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/storage/base_storage.py +0 -0
  84. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/storage/bohrium_storage.py +0 -0
  85. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/storage/http_storage.py +0 -0
  86. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/storage/local_storage.py +0 -0
  87. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/storage/oss_storage.py +0 -0
  88. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/src/dp/agent/server/utils.py +0 -0
  89. {bohr_agent_sdk-0.1.105 → bohr_agent_sdk-0.1.107}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bohr-agent-sdk
3
- Version: 0.1.105
3
+ Version: 0.1.107
4
4
  Summary: SDK for scientific agents
5
5
  Home-page: https://github.com/dptech-corp/bohr-agent-sdk/
6
6
  Author: DP Technology
@@ -30,7 +30,7 @@ Requires-Dist: paho-mqtt>=2.1.0
30
30
  Requires-Dist: redis>=6.2.0
31
31
  Requires-Dist: twine>=6.1.0
32
32
  Requires-Dist: build>=1.2.2.post1
33
- Requires-Dist: cloudpickle==2.2.0
33
+ Requires-Dist: cloudpickle
34
34
  Requires-Dist: watchdog>=6.0.0
35
35
  Requires-Dist: fastapi>=0.116.0
36
36
  Requires-Dist: bohrium-open-sdk
@@ -259,6 +259,33 @@ BOHRIUM_PASSWORD=your_password
259
259
 
260
260
  Note: The `dp-agent fetch config` command automatically downloads configuration files and replaces dynamic variables (such as MQTT_DEVICE_ID). For security reasons, this feature is only available in internal network environments.
261
261
 
262
+ ## 🔒 Authentication Configuration
263
+
264
+ For private deployments or development environment debugging, you need to configure the following environment variables:
265
+
266
+ - `BOHR_ACCESS_KEY`: Requires a real Access Key obtained from [Bohrium User Settings](https://www.bohrium.com/settings/user)
267
+ - `BOHR_APP_KEY`: Can be set to any value for development
268
+
269
+ ### Linux/macOS:
270
+ ```bash
271
+ export BOHR_ACCESS_KEY=your_real_ak_from_bohrium_settings
272
+ export BOHR_APP_KEY=any_value_for_dev
273
+ ```
274
+
275
+ ### Windows (Command Prompt):
276
+ ```cmd
277
+ set BOHR_ACCESS_KEY=your_real_ak_from_bohrium_settings
278
+ set BOHR_APP_KEY=any_value_for_dev
279
+ ```
280
+
281
+ ### Windows (PowerShell):
282
+ ```powershell
283
+ $env:BOHR_ACCESS_KEY="your_real_ak_from_bohrium_settings"
284
+ $env:BOHR_APP_KEY="any_value_for_dev"
285
+ ```
286
+
287
+ For agents deployed on Bohrium APP, authentication parameters will be automatically obtained from cookies.
288
+
262
289
  ## 🎯 Application Scenarios
263
290
 
264
291
  - **Materials Science Computing**: Molecular dynamics simulation, first-principles calculations
@@ -200,6 +200,33 @@ BOHRIUM_PASSWORD=your_password
200
200
 
201
201
  Note: The `dp-agent fetch config` command automatically downloads configuration files and replaces dynamic variables (such as MQTT_DEVICE_ID). For security reasons, this feature is only available in internal network environments.
202
202
 
203
+ ## 🔒 Authentication Configuration
204
+
205
+ For private deployments or development environment debugging, you need to configure the following environment variables:
206
+
207
+ - `BOHR_ACCESS_KEY`: Requires a real Access Key obtained from [Bohrium User Settings](https://www.bohrium.com/settings/user)
208
+ - `BOHR_APP_KEY`: Can be set to any value for development
209
+
210
+ ### Linux/macOS:
211
+ ```bash
212
+ export BOHR_ACCESS_KEY=your_real_ak_from_bohrium_settings
213
+ export BOHR_APP_KEY=any_value_for_dev
214
+ ```
215
+
216
+ ### Windows (Command Prompt):
217
+ ```cmd
218
+ set BOHR_ACCESS_KEY=your_real_ak_from_bohrium_settings
219
+ set BOHR_APP_KEY=any_value_for_dev
220
+ ```
221
+
222
+ ### Windows (PowerShell):
223
+ ```powershell
224
+ $env:BOHR_ACCESS_KEY="your_real_ak_from_bohrium_settings"
225
+ $env:BOHR_APP_KEY="any_value_for_dev"
226
+ ```
227
+
228
+ For agents deployed on Bohrium APP, authentication parameters will be automatically obtained from cookies.
229
+
203
230
  ## 🎯 Application Scenarios
204
231
 
205
232
  - **Materials Science Computing**: Molecular dynamics simulation, first-principles calculations
@@ -33,7 +33,7 @@ dependencies = [
33
33
  "redis>=6.2.0",
34
34
  "twine>=6.1.0",
35
35
  "build>=1.2.2.post1",
36
- "cloudpickle==2.2.0",
36
+ "cloudpickle",
37
37
  "watchdog>=6.0.0",
38
38
  "fastapi>=0.116.0",
39
39
  "bohrium-open-sdk"
@@ -131,4 +131,4 @@ requires = [
131
131
  "setuptools>=61.0.0",
132
132
  "wheel>=0.37.0",
133
133
  ]
134
- build-backend = "setuptools.build_meta"
134
+ build-backend = "setuptools.build_meta"
@@ -9,7 +9,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
9
9
 
10
10
  setup(
11
11
  name="bohr-agent-sdk",
12
- version="0.1.105",
12
+ version="0.1.107",
13
13
  description="SDK for science agent and mcp tools",
14
14
  long_description=long_description,
15
15
  long_description_content_type="text/markdown",
@@ -35,6 +35,7 @@ setup(
35
35
  "google-genai", # Google AI SDK
36
36
  "google-generativeai", # Google Generative AI
37
37
  "aiofiles",
38
+ "bohrium-open-sdk==0.1.5",
38
39
  ],
39
40
  # extras_require={
40
41
  # "dev": [
@@ -75,4 +76,4 @@ setup(
75
76
  "Source": "https://github.com/dptech-corp/bohr-agent-sdk/",
76
77
  # "Documentation": "https://bohr-agent-sdk.readthedocs.io/",
77
78
  },
78
- )
79
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bohr-agent-sdk
3
- Version: 0.1.105
3
+ Version: 0.1.107
4
4
  Summary: SDK for scientific agents
5
5
  Home-page: https://github.com/dptech-corp/bohr-agent-sdk/
6
6
  Author: DP Technology
@@ -30,7 +30,7 @@ Requires-Dist: paho-mqtt>=2.1.0
30
30
  Requires-Dist: redis>=6.2.0
31
31
  Requires-Dist: twine>=6.1.0
32
32
  Requires-Dist: build>=1.2.2.post1
33
- Requires-Dist: cloudpickle==2.2.0
33
+ Requires-Dist: cloudpickle
34
34
  Requires-Dist: watchdog>=6.0.0
35
35
  Requires-Dist: fastapi>=0.116.0
36
36
  Requires-Dist: bohrium-open-sdk
@@ -259,6 +259,33 @@ BOHRIUM_PASSWORD=your_password
259
259
 
260
260
  Note: The `dp-agent fetch config` command automatically downloads configuration files and replaces dynamic variables (such as MQTT_DEVICE_ID). For security reasons, this feature is only available in internal network environments.
261
261
 
262
+ ## 🔒 Authentication Configuration
263
+
264
+ For private deployments or development environment debugging, you need to configure the following environment variables:
265
+
266
+ - `BOHR_ACCESS_KEY`: Requires a real Access Key obtained from [Bohrium User Settings](https://www.bohrium.com/settings/user)
267
+ - `BOHR_APP_KEY`: Can be set to any value for development
268
+
269
+ ### Linux/macOS:
270
+ ```bash
271
+ export BOHR_ACCESS_KEY=your_real_ak_from_bohrium_settings
272
+ export BOHR_APP_KEY=any_value_for_dev
273
+ ```
274
+
275
+ ### Windows (Command Prompt):
276
+ ```cmd
277
+ set BOHR_ACCESS_KEY=your_real_ak_from_bohrium_settings
278
+ set BOHR_APP_KEY=any_value_for_dev
279
+ ```
280
+
281
+ ### Windows (PowerShell):
282
+ ```powershell
283
+ $env:BOHR_ACCESS_KEY="your_real_ak_from_bohrium_settings"
284
+ $env:BOHR_APP_KEY="any_value_for_dev"
285
+ ```
286
+
287
+ For agents deployed on Bohrium APP, authentication parameters will be automatically obtained from cookies.
288
+
262
289
  ## 🎯 Application Scenarios
263
290
 
264
291
  - **Materials Science Computing**: Molecular dynamics simulation, first-principles calculations
@@ -59,6 +59,8 @@ src/dp/agent/cli/templates/ui/server/models.py
59
59
  src/dp/agent/cli/templates/ui/server/session_manager.py
60
60
  src/dp/agent/cli/templates/ui/server/user_files.py
61
61
  src/dp/agent/cli/templates/ui/server/utils.py
62
+ src/dp/agent/client/__init__.py
63
+ src/dp/agent/client/mcp_client.py
62
64
  src/dp/agent/cloud/__init__.py
63
65
  src/dp/agent/cloud/main.py
64
66
  src/dp/agent/cloud/mcp.py
@@ -10,7 +10,7 @@ paho-mqtt>=2.1.0
10
10
  redis>=6.2.0
11
11
  twine>=6.1.0
12
12
  build>=1.2.2.post1
13
- cloudpickle==2.2.0
13
+ cloudpickle
14
14
  watchdog>=6.0.0
15
15
  fastapi>=0.116.0
16
16
  bohrium-open-sdk
@@ -36,22 +36,9 @@ def get_user_identifier(access_key: str = None, app_key: str = None, session_id:
36
36
  if cached_user_id:
37
37
  return cached_user_id
38
38
 
39
- # If no cache, call OpenSDK
40
- if access_key and app_key:
41
- try:
42
- # Use OpenSDK to get user info
43
- client = OpenSDK(
44
- access_key=access_key,
45
- app_key=app_key
46
- )
47
- user_info = client.user.get_info()
48
- if user_info and user_info.get('code') == 0:
49
- data = user_info.get('data', {})
50
- bohrium_user_id = data.get('user_id')
51
- if bohrium_user_id:
52
- return bohrium_user_id
53
- except Exception as e:
54
- pass
39
+ # If no cache, return access_key directly (consistent with WebSocket logic)
40
+ if access_key:
41
+ return access_key
55
42
 
56
43
  # If has session_id, use it
57
44
  if session_id:
@@ -141,21 +141,22 @@ def safe_filename(filename: str) -> str:
141
141
  async def check_project_id_required(context_manager, user_identifier: str) -> bool:
142
142
  """
143
143
  Check if project_id is required and set
144
-
144
+
145
145
  Args:
146
146
  context_manager: WebSocket manager instance
147
- user_identifier: User identifier
148
-
147
+ user_identifier: User identifier (can be access_key or bohrium_user_id)
148
+
149
149
  Returns:
150
150
  True if project_id is set, False otherwise
151
151
  """
152
152
  # First check environment variable
153
153
  if os.environ.get('BOHR_PROJECT_ID'):
154
154
  return True
155
-
155
+
156
156
  # Check user's connection context
157
157
  for context in context_manager.active_connections.values():
158
- if context.get_user_identifier() == user_identifier:
158
+ # Match by bohrium_user_id or access_key
159
+ if context.get_user_identifier() == user_identifier or context.access_key == user_identifier:
159
160
  return bool(context.project_id)
160
-
161
+
161
162
  return False
@@ -86,21 +86,25 @@ class ConnectionContext:
86
86
  def get_user_identifier(self) -> str:
87
87
  """
88
88
  Get unique user identifier
89
- Prefer bohrium_user_id, fallback to generated user_id
90
-
89
+ Use access_key if available (for registered users), otherwise use generated user_id
90
+
91
91
  Returns:
92
92
  User identifier
93
93
  """
94
- return self.bohrium_user_id if self.bohrium_user_id else self.user_id
94
+ # 优先使用 access_key,保持与 HTTP API 的一致性
95
+ if self.access_key:
96
+ return self.access_key
97
+ # 临时用户使用生成的 user_id
98
+ return self.user_id
95
99
 
96
100
  def is_registered_user(self) -> bool:
97
101
  """
98
102
  Check if user is registered
99
-
103
+
100
104
  Returns:
101
- True if has bohrium_user_id
105
+ True if has access_key (registered Bohrium user)
102
106
  """
103
- return bool(self.bohrium_user_id)
107
+ return bool(self.access_key)
104
108
 
105
109
  def set_project_id(self, project_id: int):
106
110
  """
@@ -0,0 +1,3 @@
1
+ from .mcp_client import MCPClient
2
+
3
+ __all__ = ["MCPClient"]
@@ -0,0 +1,162 @@
1
+ import asyncio
2
+ import json
3
+ import os
4
+ from contextlib import AsyncExitStack
5
+
6
+ from mcp import ClientSession, StdioServerParameters
7
+ from mcp.client.stdio import stdio_client
8
+ from mcp.client.sse import sse_client
9
+ from mcp.client.streamable_http import streamablehttp_client
10
+
11
+ from ..server.utils import get_logger
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ class MCPClient:
16
+ def __init__(self, server, query_interval=4):
17
+ self.server = server
18
+ self.session = None
19
+ self.exit_stack = AsyncExitStack()
20
+ self.query_interval = query_interval
21
+
22
+ def _is_session_disconnected(self, session: ClientSession) -> bool:
23
+ """Checks if a session is disconnected or closed.
24
+
25
+ Args:
26
+ session: The ClientSession to check.
27
+
28
+ Returns:
29
+ True if the session is disconnected, False otherwise.
30
+ """
31
+ return session._read_stream._closed or session._write_stream._closed
32
+
33
+ async def get_session(self):
34
+ if self.session is not None:
35
+ # Check if the existing session is still connected
36
+ if not self._is_session_disconnected(self.session):
37
+ # Session is still good, return it
38
+ return self.session
39
+ else:
40
+ # Session is disconnected, clean it up
41
+ logger.info(f'Cleaning up disconnected session: {self.server}')
42
+ try:
43
+ await self.exit_stack.aclose()
44
+ except Exception as e:
45
+ logger.warning('Error during disconnected session cleanup: %s',
46
+ e)
47
+ finally:
48
+ self.session = None
49
+
50
+ is_python = self.server.endswith('.py')
51
+ is_js = self.server.endswith('.js')
52
+ is_sse = self.server.startswith('http') and "/sse" in self.server
53
+ is_streamablehttp = self.server.startswith('http') \
54
+ and "/sse" not in self.server
55
+ if not (is_python or is_js or is_sse or is_streamablehttp):
56
+ raise ValueError(
57
+ "Server script must be a .py or .js file or a http link")
58
+
59
+ if is_sse:
60
+ try:
61
+ logger.info(f"SSE: {self.server}")
62
+ streams = await self.exit_stack.enter_async_context(sse_client(
63
+ self.server))
64
+ self.session = await self.exit_stack.enter_async_context(
65
+ ClientSession(*streams))
66
+ await self.session.initialize()
67
+ return self.session
68
+ except Exception as e:
69
+ logger.error(
70
+ f"An error occurred while connecting to the server: {e}")
71
+ raise
72
+ if is_streamablehttp:
73
+ try:
74
+ logger.info(f"StreamableHTTP: {self.server}")
75
+ transports = await self.exit_stack.enter_async_context(
76
+ streamablehttp_client(self.server))
77
+ self.session = await self.exit_stack.enter_async_context(
78
+ ClientSession(*transports[:2]))
79
+ await self.session.initialize()
80
+ return self.session
81
+ except Exception as e:
82
+ logger.error(
83
+ f"An error occurred while connecting to the server: {e}")
84
+ raise
85
+
86
+ command = "python" if is_python else "node"
87
+ server_params = StdioServerParameters(
88
+ command=command,
89
+ args=[self.server],
90
+ env=os.environ.copy(),
91
+ )
92
+
93
+ try:
94
+ stdio_transport = await self.exit_stack.enter_async_context(
95
+ stdio_client(server_params))
96
+ stdio, write = stdio_transport
97
+ self.session = await self.exit_stack.enter_async_context(
98
+ ClientSession(stdio, write))
99
+ await self.session.initialize()
100
+ return self.session
101
+ except Exception as e:
102
+ logger.error(
103
+ f"An error occurred while connecting to the server: {e}")
104
+ raise
105
+
106
+ async def connect_to_server(self):
107
+ await self.get_session()
108
+ response = await self.session.list_tools()
109
+ tools = []
110
+ for tool in response.tools:
111
+ if tool.name.startswith("submit_") or tool.name in [
112
+ "query_job_status", "get_job_results", "terminate_job"]:
113
+ continue
114
+ tools.append(tool)
115
+ logger.info(
116
+ f"Connected to server with tools:{[tool.name for tool in tools]}")
117
+ return tools
118
+
119
+ async def call_tool(self, tool_name: str, arguments: dict,
120
+ async_mode=False):
121
+ await self.get_session()
122
+ if not async_mode:
123
+ result = await self.session.call_tool(tool_name, arguments)
124
+ return result
125
+
126
+ executor = arguments.get("executor")
127
+ storage = arguments.get("storage")
128
+ res = await self.session.call_tool("submit_" + tool_name, arguments)
129
+ if res.isError:
130
+ logger.error("Failed to submit %s: %s" % (
131
+ tool_name, res.content[0].text))
132
+ return res
133
+ job_id = json.loads(res.content[0].text)["job_id"]
134
+ job_info = res.content[0].job_info
135
+ logger.info("Job submitted (ID: %s)" % job_id)
136
+ if job_info.get("extra_info"):
137
+ logger.info(job_info["extra_info"])
138
+
139
+ while True:
140
+ res = await self.session.call_tool("query_job_status", {
141
+ "job_id": job_id, "executor": executor})
142
+ if res.isError:
143
+ logger.error(res.content[0].text)
144
+ else:
145
+ status = res.content[0].text
146
+ logger.info("Job %s status is %s" % (job_id, status))
147
+ if status != "Running":
148
+ break
149
+ await asyncio.sleep(self.query_interval)
150
+
151
+ res = await self.session.call_tool("get_job_results", {
152
+ "job_id": job_id, "executor": executor, "storage": storage})
153
+ if res.isError:
154
+ logger.error("Job %s failed: %s" % (job_id, res.content[0].text))
155
+ else:
156
+ logger.info("Job %s result is %s" % (job_id, res.content))
157
+ res.content[0].job_info = {**job_info,
158
+ **getattr(res.content[0], "job_info", {})}
159
+ return res
160
+
161
+ async def cleanup(self):
162
+ await self.exit_stack.aclose()
@@ -138,14 +138,16 @@ def handle_output_artifacts(results, exec_id, storage):
138
138
  return results, output_artifacts
139
139
 
140
140
 
141
+ # MCP does not regard Any as serializable in Python 3.12
142
+ # use Optional[Any] to work around
141
143
  def get_job_results(job_id: str, executor: Optional[dict] = None,
142
- storage: Optional[dict] = None) -> Any:
144
+ storage: Optional[dict] = None) -> Optional[Any]:
143
145
  """
144
146
  Get results of a calculation job
145
147
  Args:
146
148
  job_id (str): The ID of the calculation job
147
149
  Returns:
148
- results (dict): results of the calculation job
150
+ results (Any): results of the calculation job
149
151
  """
150
152
  trace_id, exec_id = job_id.split("/")
151
153
  with set_directory(trace_id):