sorb-mcp 0.1.0__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.
- sorb_mcp-0.1.0/.gitignore +196 -0
- sorb_mcp-0.1.0/PKG-INFO +92 -0
- sorb_mcp-0.1.0/PYPI_README.md +74 -0
- sorb_mcp-0.1.0/pyproject.toml +35 -0
- sorb_mcp-0.1.0/src/sorb_mcp/__init__.py +1 -0
- sorb_mcp-0.1.0/src/sorb_mcp/config.py +59 -0
- sorb_mcp-0.1.0/src/sorb_mcp/confirmation.schema.json +863 -0
- sorb_mcp-0.1.0/src/sorb_mcp/contracts.py +80 -0
- sorb_mcp-0.1.0/src/sorb_mcp/estimate.schema.json +2171 -0
- sorb_mcp-0.1.0/src/sorb_mcp/files.py +177 -0
- sorb_mcp-0.1.0/src/sorb_mcp/generation.schema.json +2328 -0
- sorb_mcp-0.1.0/src/sorb_mcp/http.py +230 -0
- sorb_mcp-0.1.0/src/sorb_mcp/inputs.py +195 -0
- sorb_mcp-0.1.0/src/sorb_mcp/server.py +508 -0
- sorb_mcp-0.1.0/src/sorb_mcp/upload.schema.json +81 -0
- sorb_mcp-0.1.0/src/sorb_mcp/voice-profile.schema.json +84 -0
- sorb_mcp-0.1.0/src/sorb_mcp/voice-train.schema.json +68 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# ============================================
|
|
2
|
+
# Python
|
|
3
|
+
# ============================================
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
*.so
|
|
8
|
+
.Python
|
|
9
|
+
build/
|
|
10
|
+
develop-eggs/
|
|
11
|
+
dist/
|
|
12
|
+
downloads/
|
|
13
|
+
eggs/
|
|
14
|
+
.eggs/
|
|
15
|
+
lib/
|
|
16
|
+
lib64/
|
|
17
|
+
parts/
|
|
18
|
+
sdist/
|
|
19
|
+
var/
|
|
20
|
+
wheels/
|
|
21
|
+
*.egg-info/
|
|
22
|
+
.installed.cfg
|
|
23
|
+
*.egg
|
|
24
|
+
MANIFEST
|
|
25
|
+
pip-log.txt
|
|
26
|
+
pip-delete-this-directory.txt
|
|
27
|
+
|
|
28
|
+
# Virtual Environment
|
|
29
|
+
venv/
|
|
30
|
+
ENV/
|
|
31
|
+
env/
|
|
32
|
+
.venv
|
|
33
|
+
.virtualenv
|
|
34
|
+
|
|
35
|
+
# PyInstaller
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# ============================================
|
|
39
|
+
# IDEs & Editors
|
|
40
|
+
# ============================================
|
|
41
|
+
.vscode/
|
|
42
|
+
.idea/
|
|
43
|
+
*.swp
|
|
44
|
+
*.swo
|
|
45
|
+
*~
|
|
46
|
+
*.suo
|
|
47
|
+
*.ntvs*
|
|
48
|
+
*.njsproj
|
|
49
|
+
*.sln
|
|
50
|
+
*.sw?
|
|
51
|
+
.project
|
|
52
|
+
.classpath
|
|
53
|
+
.settings/
|
|
54
|
+
.claude/
|
|
55
|
+
.worktrees/
|
|
56
|
+
|
|
57
|
+
# ============================================
|
|
58
|
+
# OS
|
|
59
|
+
# ============================================
|
|
60
|
+
.DS_Store
|
|
61
|
+
.DS_Store?
|
|
62
|
+
._*
|
|
63
|
+
.Spotlight-V100
|
|
64
|
+
.Trashes
|
|
65
|
+
ehthumbs.db
|
|
66
|
+
Thumbs.db
|
|
67
|
+
|
|
68
|
+
# ============================================
|
|
69
|
+
# Logs
|
|
70
|
+
# ============================================
|
|
71
|
+
*.log
|
|
72
|
+
logs/
|
|
73
|
+
*.log.*
|
|
74
|
+
npm-debug.log*
|
|
75
|
+
yarn-debug.log*
|
|
76
|
+
yarn-error.log*
|
|
77
|
+
pnpm-debug.log*
|
|
78
|
+
lerna-debug.log*
|
|
79
|
+
|
|
80
|
+
# ============================================
|
|
81
|
+
# Database
|
|
82
|
+
# ============================================
|
|
83
|
+
*.db
|
|
84
|
+
*.sqlite
|
|
85
|
+
*.sqlite3
|
|
86
|
+
data/
|
|
87
|
+
*.db-journal
|
|
88
|
+
|
|
89
|
+
# ============================================
|
|
90
|
+
# Media & Uploads
|
|
91
|
+
# ============================================
|
|
92
|
+
static/uploads/
|
|
93
|
+
backend/data/
|
|
94
|
+
backend/media/
|
|
95
|
+
backend/logs/
|
|
96
|
+
|
|
97
|
+
# ============================================
|
|
98
|
+
# Environment Variables
|
|
99
|
+
# ============================================
|
|
100
|
+
.env
|
|
101
|
+
.env.local
|
|
102
|
+
.env.*.local
|
|
103
|
+
.env.development
|
|
104
|
+
.env.production
|
|
105
|
+
.env.test
|
|
106
|
+
|
|
107
|
+
# ============================================
|
|
108
|
+
# Docker
|
|
109
|
+
# ============================================
|
|
110
|
+
.dockerignore
|
|
111
|
+
!backend/.dockerignore # 允许 backend 的 .dockerignore
|
|
112
|
+
docker-compose.override.yml
|
|
113
|
+
docker/backup/
|
|
114
|
+
# 本地备份目录
|
|
115
|
+
docker/data/
|
|
116
|
+
docker/logs/
|
|
117
|
+
docker/media/
|
|
118
|
+
|
|
119
|
+
# ============================================
|
|
120
|
+
# Redis
|
|
121
|
+
# ============================================
|
|
122
|
+
dump.rdb
|
|
123
|
+
*.rdb
|
|
124
|
+
|
|
125
|
+
# ============================================
|
|
126
|
+
# Celery
|
|
127
|
+
# ============================================
|
|
128
|
+
celerybeat-schedule
|
|
129
|
+
celerybeat.pid
|
|
130
|
+
celerybeat-schedule.db
|
|
131
|
+
|
|
132
|
+
# ============================================
|
|
133
|
+
# Test Coverage
|
|
134
|
+
# ============================================
|
|
135
|
+
htmlcov/
|
|
136
|
+
.coverage
|
|
137
|
+
.coverage.*
|
|
138
|
+
.pytest_cache/
|
|
139
|
+
nosetests.xml
|
|
140
|
+
coverage.xml
|
|
141
|
+
*.cover
|
|
142
|
+
.hypothesis/
|
|
143
|
+
.tox/
|
|
144
|
+
|
|
145
|
+
# ============================================
|
|
146
|
+
# Node.js / Frontend
|
|
147
|
+
# ============================================
|
|
148
|
+
node_modules/
|
|
149
|
+
npm-debug.log*
|
|
150
|
+
yarn-debug.log*
|
|
151
|
+
yarn-error.log*
|
|
152
|
+
pnpm-debug.log*
|
|
153
|
+
.pnpm-store/
|
|
154
|
+
.yarn/cache
|
|
155
|
+
.yarn/unplugged
|
|
156
|
+
.yarn/build-state.yml
|
|
157
|
+
.yarn/install-state.gz
|
|
158
|
+
.pnp.*
|
|
159
|
+
|
|
160
|
+
# ============================================
|
|
161
|
+
# Build Outputs
|
|
162
|
+
# ============================================
|
|
163
|
+
dist/
|
|
164
|
+
dist-ssr/
|
|
165
|
+
build/
|
|
166
|
+
.nuxt/
|
|
167
|
+
.next/
|
|
168
|
+
.cache/
|
|
169
|
+
out/
|
|
170
|
+
*.tsbuildinfo
|
|
171
|
+
|
|
172
|
+
# ============================================
|
|
173
|
+
# Temporary Files
|
|
174
|
+
# ============================================
|
|
175
|
+
*.tmp
|
|
176
|
+
*.temp
|
|
177
|
+
.temp/
|
|
178
|
+
temp/
|
|
179
|
+
*.bak
|
|
180
|
+
*.backup
|
|
181
|
+
|
|
182
|
+
# ============================================
|
|
183
|
+
# Documentation (optional)
|
|
184
|
+
# ============================================
|
|
185
|
+
# Uncomment if you don't want to commit these
|
|
186
|
+
# PROJECT_STATUS*.md
|
|
187
|
+
# IMPLEMENTATION_SUMMARY.md
|
|
188
|
+
# FINAL_REPORT.md
|
|
189
|
+
|
|
190
|
+
# ============================================
|
|
191
|
+
# AI/Editor specific
|
|
192
|
+
# ============================================
|
|
193
|
+
.qoder/
|
|
194
|
+
Default/
|
|
195
|
+
.antigravitycli/
|
|
196
|
+
.serena/
|
sorb_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sorb-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sorb task tools for external MCP agents
|
|
5
|
+
Project-URL: Homepage, https://sorb.huche.games
|
|
6
|
+
Project-URL: Documentation, https://sorb.huche.games/settings/me/mcp?tab=help
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Requires-Python: <3.14,>=3.13
|
|
10
|
+
Requires-Dist: anyio==4.15.1
|
|
11
|
+
Requires-Dist: httpx==0.28.1
|
|
12
|
+
Requires-Dist: mcp==2.2.0
|
|
13
|
+
Requires-Dist: pydantic==2.13.4
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest-asyncio==0.23.3; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest==7.4.4; extra == 'dev'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# Sorb MCP
|
|
20
|
+
|
|
21
|
+
Sorb MCP is the local stdio MCP integration for [Sorb](https://sorb.huche.games), a multimodal content generation platform. It connects an external Agent to Sorb's existing projects, models, resources and task APIs using your personal access token (PAT).
|
|
22
|
+
|
|
23
|
+
The package provides 38 tools for discovery, task creation and estimation, status and output queries, task operations, and bounded local file upload/download. Tasks retain Sorb's existing project permissions, billing, confirmation, idempotency and audit behavior. Canvas operations are outside this package's scope.
|
|
24
|
+
|
|
25
|
+
Requires **Python 3.13**. A Sorb account and access to a running Sorb server are required. This package does not contain the Sorb backend or any model Provider credentials.
|
|
26
|
+
|
|
27
|
+
## Install and connect
|
|
28
|
+
|
|
29
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/). The MCP client can then launch a pinned version directly from PyPI:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
uvx --python 3.13 --from sorb-mcp==0.1.0 sorb-mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or install the command in a Python 3.13 environment:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
python -m pip install sorb-mcp==0.1.0
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
In Sorb, open **个人中心 → 用户信息 → MCP → 访问令牌** (Personal Center → User Information → MCP → Access Tokens). Create a token with the operations you need. Start with the default read scopes to verify the connection. The token is displayed only once.
|
|
42
|
+
|
|
43
|
+
For clients using the `mcpServers` configuration format:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"sorb": {
|
|
49
|
+
"command": "uvx",
|
|
50
|
+
"args": ["--python", "3.13", "--from", "sorb-mcp==0.1.0", "sorb-mcp"],
|
|
51
|
+
"env": {
|
|
52
|
+
"SORB_BASE_URL": "https://sorb.huche.games",
|
|
53
|
+
"SORB_PAT": "<your personal access token>"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use your own Sorb server's root URL, without `/api` or `/api/mcp`. Remote servers must use HTTPS; HTTP is permitted only for loopback development addresses. If the client cannot find `uvx`, set `command` to its actual executable path. Store the PAT in your client's secure configuration, never in chat messages or shared files.
|
|
61
|
+
|
|
62
|
+
After restarting or refreshing the client, ask the Agent to list your visible projects using `sorb_list_projects`. Successful tool output verifies the connection. Use `sorb_select_project`, `sorb_list_models`, `sorb_get_model_schema` and `sorb_get_task_capabilities` before requesting generation. Query operations do not create Sorb tasks; generation can incur the existing Sorb model fees.
|
|
63
|
+
|
|
64
|
+
## Local files
|
|
65
|
+
|
|
66
|
+
Local upload/download requires explicitly configured, existing absolute directories:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"SORB_INPUT_DIRS": "/absolute/path/references",
|
|
71
|
+
"SORB_OUTPUT_DIRS": "/absolute/path/outputs"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Add these keys to the client's `env` only when needed. Multiple directories use the operating system's path separator (`:` on macOS/Linux, `;` on Windows). Client roots may further narrow these directories. Downloads do not overwrite existing files; symlink traversal is rejected. Transfer limits default to 512 MiB and 120 seconds, and Sorb's server-side upload limits still apply.
|
|
76
|
+
|
|
77
|
+
Optional settings: `SORB_CA_FILE` for a trusted private CA, `SORB_TIMEOUT_SECONDS` (default 60), `SORB_MAX_TRANSFER_BYTES`, and `SORB_TRANSFER_TIMEOUT_SECONDS`. TLS verification remains enabled.
|
|
78
|
+
|
|
79
|
+
## Permissions and retries
|
|
80
|
+
|
|
81
|
+
- Each operation rechecks the token scopes and current user/project permissions. Read-only projects cannot create tasks.
|
|
82
|
+
- Keep the same `client_namespace` and `idempotency_key` for one logical write. After a timeout, query `sorb_get_submission` with the original identity instead of generating a new key and repeating the task.
|
|
83
|
+
- Tasks requiring human preview or confirmation return a Sorb user-action link. The Agent prepares the proposal and queries its state; the user confirms in Sorb.
|
|
84
|
+
- Revoke or replace a PAT in the MCP panel. Revocation prevents subsequent calls but does not cancel tasks already accepted by Sorb.
|
|
85
|
+
|
|
86
|
+
## Remote OAuth alternative
|
|
87
|
+
|
|
88
|
+
Compatible clients can connect directly to `https://<your Sorb host>/api/mcp` over Streamable HTTP and OAuth without installing this package. The remote endpoint uses MCP `2026-07-28` and supports CIMD or administrator pre-registered public clients, without a DCR endpoint. OAuth tokens are distinct from PATs; do not put a PAT into the remote OAuth connection.
|
|
89
|
+
|
|
90
|
+
Remote client compatibility must be verified by an authenticated tool call. This PyPI package installs the local **stdio + PAT** integration; it does not configure or enable the remote Sorb server.
|
|
91
|
+
|
|
92
|
+
For the Chinese installation guide, configuration examples and troubleshooting, open **个人中心 → 用户信息 → MCP → 接入帮助** on your Sorb server. The guide can also be downloaded as Markdown.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Sorb MCP
|
|
2
|
+
|
|
3
|
+
Sorb MCP is the local stdio MCP integration for [Sorb](https://sorb.huche.games), a multimodal content generation platform. It connects an external Agent to Sorb's existing projects, models, resources and task APIs using your personal access token (PAT).
|
|
4
|
+
|
|
5
|
+
The package provides 38 tools for discovery, task creation and estimation, status and output queries, task operations, and bounded local file upload/download. Tasks retain Sorb's existing project permissions, billing, confirmation, idempotency and audit behavior. Canvas operations are outside this package's scope.
|
|
6
|
+
|
|
7
|
+
Requires **Python 3.13**. A Sorb account and access to a running Sorb server are required. This package does not contain the Sorb backend or any model Provider credentials.
|
|
8
|
+
|
|
9
|
+
## Install and connect
|
|
10
|
+
|
|
11
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/). The MCP client can then launch a pinned version directly from PyPI:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
uvx --python 3.13 --from sorb-mcp==0.1.0 sorb-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or install the command in a Python 3.13 environment:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
python -m pip install sorb-mcp==0.1.0
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
In Sorb, open **个人中心 → 用户信息 → MCP → 访问令牌** (Personal Center → User Information → MCP → Access Tokens). Create a token with the operations you need. Start with the default read scopes to verify the connection. The token is displayed only once.
|
|
24
|
+
|
|
25
|
+
For clients using the `mcpServers` configuration format:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"sorb": {
|
|
31
|
+
"command": "uvx",
|
|
32
|
+
"args": ["--python", "3.13", "--from", "sorb-mcp==0.1.0", "sorb-mcp"],
|
|
33
|
+
"env": {
|
|
34
|
+
"SORB_BASE_URL": "https://sorb.huche.games",
|
|
35
|
+
"SORB_PAT": "<your personal access token>"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use your own Sorb server's root URL, without `/api` or `/api/mcp`. Remote servers must use HTTPS; HTTP is permitted only for loopback development addresses. If the client cannot find `uvx`, set `command` to its actual executable path. Store the PAT in your client's secure configuration, never in chat messages or shared files.
|
|
43
|
+
|
|
44
|
+
After restarting or refreshing the client, ask the Agent to list your visible projects using `sorb_list_projects`. Successful tool output verifies the connection. Use `sorb_select_project`, `sorb_list_models`, `sorb_get_model_schema` and `sorb_get_task_capabilities` before requesting generation. Query operations do not create Sorb tasks; generation can incur the existing Sorb model fees.
|
|
45
|
+
|
|
46
|
+
## Local files
|
|
47
|
+
|
|
48
|
+
Local upload/download requires explicitly configured, existing absolute directories:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"SORB_INPUT_DIRS": "/absolute/path/references",
|
|
53
|
+
"SORB_OUTPUT_DIRS": "/absolute/path/outputs"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Add these keys to the client's `env` only when needed. Multiple directories use the operating system's path separator (`:` on macOS/Linux, `;` on Windows). Client roots may further narrow these directories. Downloads do not overwrite existing files; symlink traversal is rejected. Transfer limits default to 512 MiB and 120 seconds, and Sorb's server-side upload limits still apply.
|
|
58
|
+
|
|
59
|
+
Optional settings: `SORB_CA_FILE` for a trusted private CA, `SORB_TIMEOUT_SECONDS` (default 60), `SORB_MAX_TRANSFER_BYTES`, and `SORB_TRANSFER_TIMEOUT_SECONDS`. TLS verification remains enabled.
|
|
60
|
+
|
|
61
|
+
## Permissions and retries
|
|
62
|
+
|
|
63
|
+
- Each operation rechecks the token scopes and current user/project permissions. Read-only projects cannot create tasks.
|
|
64
|
+
- Keep the same `client_namespace` and `idempotency_key` for one logical write. After a timeout, query `sorb_get_submission` with the original identity instead of generating a new key and repeating the task.
|
|
65
|
+
- Tasks requiring human preview or confirmation return a Sorb user-action link. The Agent prepares the proposal and queries its state; the user confirms in Sorb.
|
|
66
|
+
- Revoke or replace a PAT in the MCP panel. Revocation prevents subsequent calls but does not cancel tasks already accepted by Sorb.
|
|
67
|
+
|
|
68
|
+
## Remote OAuth alternative
|
|
69
|
+
|
|
70
|
+
Compatible clients can connect directly to `https://<your Sorb host>/api/mcp` over Streamable HTTP and OAuth without installing this package. The remote endpoint uses MCP `2026-07-28` and supports CIMD or administrator pre-registered public clients, without a DCR endpoint. OAuth tokens are distinct from PATs; do not put a PAT into the remote OAuth connection.
|
|
71
|
+
|
|
72
|
+
Remote client compatibility must be verified by an authenticated tool call. This PyPI package installs the local **stdio + PAT** integration; it does not configure or enable the remote Sorb server.
|
|
73
|
+
|
|
74
|
+
For the Chinese installation guide, configuration examples and troubleshooting, open **个人中心 → 用户信息 → MCP → 接入帮助** on your Sorb server. The guide can also be downloaded as Markdown.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sorb-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Sorb task tools for external MCP agents"
|
|
5
|
+
readme = "PYPI_README.md"
|
|
6
|
+
requires-python = ">=3.13,<3.14"
|
|
7
|
+
dependencies = ["mcp==2.2.0", "httpx==0.28.1", "pydantic==2.13.4", "anyio==4.15.1"]
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Programming Language :: Python :: 3",
|
|
10
|
+
"Programming Language :: Python :: 3.13",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.urls]
|
|
14
|
+
Homepage = "https://sorb.huche.games"
|
|
15
|
+
Documentation = "https://sorb.huche.games/settings/me/mcp?tab=help"
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
sorb-mcp = "sorb_mcp.server:main"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = ["pytest==7.4.4", "pytest-asyncio==0.23.3"]
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["hatchling==1.27.0"]
|
|
25
|
+
build-backend = "hatchling.build"
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel]
|
|
28
|
+
packages = ["src/sorb_mcp"]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.sdist]
|
|
31
|
+
include = ["src/sorb_mcp", "pyproject.toml", "PYPI_README.md"]
|
|
32
|
+
|
|
33
|
+
[tool.pytest.ini_options]
|
|
34
|
+
asyncio_mode = "auto"
|
|
35
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Lightweight task MCP client; no backend or Provider imports."""
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
import ipaddress
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import ssl
|
|
6
|
+
from urllib.parse import urlsplit
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class Settings:
|
|
11
|
+
base_url: str
|
|
12
|
+
pat: str = field(repr=False)
|
|
13
|
+
ca_file: str | None = None
|
|
14
|
+
timeout_seconds: float = 60
|
|
15
|
+
input_dirs: tuple[str, ...] = ()
|
|
16
|
+
output_dirs: tuple[str, ...] = ()
|
|
17
|
+
max_transfer_bytes: int = 512 * 1024 * 1024
|
|
18
|
+
transfer_timeout_seconds: float = 120
|
|
19
|
+
|
|
20
|
+
def __post_init__(self):
|
|
21
|
+
url = urlsplit(self.base_url)
|
|
22
|
+
# urlsplit defers validation of malformed or out-of-range ports.
|
|
23
|
+
_ = url.port
|
|
24
|
+
if (url.scheme not in {"http", "https"} or not url.hostname or url.username or url.password
|
|
25
|
+
or url.query or url.fragment or url.path not in {"", "/"}):
|
|
26
|
+
raise ValueError("SORB_BASE_URL 必须为 HTTP/HTTPS 服务根地址,不含凭据、路径或查询参数")
|
|
27
|
+
loopback = url.hostname == "localhost"
|
|
28
|
+
try:
|
|
29
|
+
loopback = loopback or ipaddress.ip_address(url.hostname).is_loopback
|
|
30
|
+
except ValueError:
|
|
31
|
+
pass
|
|
32
|
+
if url.scheme == "http" and not loopback:
|
|
33
|
+
raise ValueError("非本机 Sorb 连接必须使用 HTTPS")
|
|
34
|
+
if not self.pat.startswith("sorb_") or not 40 <= len(self.pat) <= 100 or any(c.isspace() for c in self.pat):
|
|
35
|
+
raise ValueError("SORB_PAT 缺失或格式无效,请在 Sorb 访问令牌页面创建")
|
|
36
|
+
if not 5 <= self.timeout_seconds <= 300:
|
|
37
|
+
raise ValueError("SORB_TIMEOUT_SECONDS 必须在 5–300 秒之间")
|
|
38
|
+
if self.ca_file and not Path(self.ca_file).is_file():
|
|
39
|
+
raise ValueError("SORB_CA_FILE 不存在")
|
|
40
|
+
for directory in (*self.input_dirs, *self.output_dirs):
|
|
41
|
+
if not Path(directory).is_absolute() or not Path(directory).is_dir():
|
|
42
|
+
raise ValueError("Sorb 文件输入/输出目录必须为已存在的绝对目录")
|
|
43
|
+
if not 1 <= self.max_transfer_bytes <= 2 * 1024 * 1024 * 1024:
|
|
44
|
+
raise ValueError("SORB_MAX_TRANSFER_BYTES 必须在 1–2147483648 之间")
|
|
45
|
+
if not 5 <= self.transfer_timeout_seconds <= 300:
|
|
46
|
+
raise ValueError("SORB_TRANSFER_TIMEOUT_SECONDS 必须在 5–300 秒之间")
|
|
47
|
+
|
|
48
|
+
def tls_context(self):
|
|
49
|
+
return ssl.create_default_context(cafile=self.ca_file)
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_env(cls):
|
|
53
|
+
return cls(base_url=os.getenv("SORB_BASE_URL", "http://localhost:8042"),
|
|
54
|
+
pat=os.getenv("SORB_PAT", ""), ca_file=os.getenv("SORB_CA_FILE") or None,
|
|
55
|
+
timeout_seconds=float(os.getenv("SORB_TIMEOUT_SECONDS", "60")),
|
|
56
|
+
input_dirs=tuple(filter(None, os.getenv("SORB_INPUT_DIRS", "").split(os.pathsep))),
|
|
57
|
+
output_dirs=tuple(filter(None, os.getenv("SORB_OUTPUT_DIRS", "").split(os.pathsep))),
|
|
58
|
+
max_transfer_bytes=int(os.getenv("SORB_MAX_TRANSFER_BYTES", str(512 * 1024 * 1024))),
|
|
59
|
+
transfer_timeout_seconds=float(os.getenv("SORB_TRANSFER_TIMEOUT_SECONDS", "120")))
|