mcbp 0.2.4__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.
- mcbp-0.2.4/.gitignore +27 -0
- mcbp-0.2.4/LICENSE +21 -0
- mcbp-0.2.4/PKG-INFO +177 -0
- mcbp-0.2.4/README.md +152 -0
- mcbp-0.2.4/deploy/README.md +236 -0
- mcbp-0.2.4/deploy/client-kit/README.md +62 -0
- mcbp-0.2.4/deploy/client-kit/setup-codex.cmd +59 -0
- mcbp-0.2.4/deploy/client-kit/trust-cert.cmd +41 -0
- mcbp-0.2.4/deploy/mcbp-mcp.env.example +77 -0
- mcbp-0.2.4/deploy/mcbp-mcp@.service +37 -0
- mcbp-0.2.4/deploy/nginx-mcp.conf +91 -0
- mcbp-0.2.4/deploy/portable/build.ps1 +111 -0
- mcbp-0.2.4/deploy/portable/payload/README.md +99 -0
- mcbp-0.2.4/deploy/portable/payload/make-cert.ps1 +140 -0
- mcbp-0.2.4/deploy/portable/payload/new-token.cmd +15 -0
- mcbp-0.2.4/deploy/portable/payload/serve.py +142 -0
- mcbp-0.2.4/deploy/portable/payload/server.env +73 -0
- mcbp-0.2.4/deploy/portable/payload/start.cmd +11 -0
- mcbp-0.2.4/deploy/portable/payload/tokens.example.toml +20 -0
- mcbp-0.2.4/deploy/portable/payload/update.cmd +16 -0
- mcbp-0.2.4/deploy/portable/payload//320/222/320/241/320/242/320/220/320/235/320/236/320/222/320/233/320/225/320/235/320/235/320/257.html +470 -0
- mcbp-0.2.4/pyproject.toml +61 -0
- mcbp-0.2.4/server.json +77 -0
- mcbp-0.2.4/src/mcbp_mcp_server/__init__.py +19 -0
- mcbp-0.2.4/src/mcbp_mcp_server/__main__.py +5 -0
- mcbp-0.2.4/src/mcbp_mcp_server/credentials.py +298 -0
- mcbp-0.2.4/src/mcbp_mcp_server/errors.py +52 -0
- mcbp-0.2.4/src/mcbp_mcp_server/http.py +317 -0
- mcbp-0.2.4/src/mcbp_mcp_server/registry.py +112 -0
- mcbp-0.2.4/src/mcbp_mcp_server/server.py +206 -0
- mcbp-0.2.4/tests/test_credentials.py +650 -0
- mcbp-0.2.4/tests/test_http.py +110 -0
- mcbp-0.2.4/tests/test_registry.py +210 -0
- mcbp-0.2.4/tests/test_server.py +220 -0
mcbp-0.2.4/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Secrets — NEVER commit
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
*.key
|
|
5
|
+
*.pem
|
|
6
|
+
*.crt
|
|
7
|
+
|
|
8
|
+
# Python
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.pyc
|
|
11
|
+
*.pyo
|
|
12
|
+
.venv/
|
|
13
|
+
.venv-*/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
dist/
|
|
16
|
+
build/
|
|
17
|
+
|
|
18
|
+
# Tooling caches
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
|
|
23
|
+
# IDE / OS
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
.DS_Store
|
|
27
|
+
Thumbs.db
|
mcbp-0.2.4/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MCBP.PLUS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mcbp-0.2.4/PKG-INFO
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mcbp
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Summary: Standalone MCP server exposing BAS MCBP_AI (mcbp.plus)
|
|
5
|
+
Project-URL: Homepage, https://mcbp.plus
|
|
6
|
+
Project-URL: Repository, https://github.com/kzavrazhnyi/mcbp-mcp-server
|
|
7
|
+
Author: MCBP.PLUS
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: 1c,bas,erp,mcbp,mcp
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: anyio>=4.0
|
|
13
|
+
Requires-Dist: mcbp-core<0.2,>=0.1.1
|
|
14
|
+
Requires-Dist: mcp>=2.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
18
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
19
|
+
Requires-Dist: ruff>=0.7; extra == 'dev'
|
|
20
|
+
Requires-Dist: starlette>=0.40; extra == 'dev'
|
|
21
|
+
Provides-Extra: http
|
|
22
|
+
Requires-Dist: starlette>=0.40; extra == 'http'
|
|
23
|
+
Requires-Dist: uvicorn>=0.30; extra == 'http'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
<!-- mcp-name: io.github.kzavrazhnyi/mcbp-ai -->
|
|
27
|
+
|
|
28
|
+
# mcbp-ai — MCP server for BAS / 1C (MCBP+)
|
|
29
|
+
|
|
30
|
+
Read your BAS (1C) database from any MCP client. Catalogs, documents, register balances and records,
|
|
31
|
+
and the full configuration metadata tree — exposed as MCP tools over the `MCBP_AI` HTTP service.
|
|
32
|
+
|
|
33
|
+
**Read-only by default.** None of the tools available out of the box modifies your data.
|
|
34
|
+
|
|
35
|
+
**Model-agnostic.** MCP is a vendor-neutral standard, so this server works with **Claude Desktop /
|
|
36
|
+
Code, ChatGPT desktop, Gemini, Microsoft and GitHub Copilot, Cursor, Windsurf, VS Code and Zed** —
|
|
37
|
+
it serves `tools/list` and executes `tools/call` without knowing which model is asking. The
|
|
38
|
+
transport is **stdio**, so the client launches the process locally; browser-based clients would
|
|
39
|
+
need a remote HTTP server, which this one does not expose.
|
|
40
|
+
|
|
41
|
+
> ### ⚠ This server needs a server-side component that is not in this repository
|
|
42
|
+
>
|
|
43
|
+
> `mcbp-ai` is a client for the **`MCBP_AI` HTTP service** — a BSL module (a common module plus an
|
|
44
|
+
> HTTP service definition) that must be installed into your BAS/1C configuration through
|
|
45
|
+
> Конфігуратор. **That module is licensed separately and is not part of this repository.**
|
|
46
|
+
>
|
|
47
|
+
> Without it this server starts, connects, and every route answers `404`. If you want to run it
|
|
48
|
+
> against your own base, contact MCBP.PLUS to obtain the `MCBP_AI` module.
|
|
49
|
+
>
|
|
50
|
+
> The Python code here — the MCP adapter and the shared BAS client — is open source and complete.
|
|
51
|
+
|
|
52
|
+
The MCBP+ configuration and the `MCBP_AI` service module are proprietary works of
|
|
53
|
+
**[MCBP.PLUS](https://mcbp.plus)** and are distributed separately from this repository. This MIT
|
|
54
|
+
licence covers the Python packages only.
|
|
55
|
+
|
|
56
|
+
## Why this exists
|
|
57
|
+
|
|
58
|
+
BAS/1C holds the data, but it is not reachable from an LLM: the platform speaks its own query
|
|
59
|
+
language, metadata names are inherited Russian identifiers while synonyms are Ukrainian, and a raw
|
|
60
|
+
OData feed is both heavy and hostile to a model (opaque errors, no self-correction path).
|
|
61
|
+
|
|
62
|
+
`MCBP_AI` solves that on the BAS side — canonical English field names in list rows, compact
|
|
63
|
+
responses, and errors that name the offending field verbatim so a model can fix its own call. This
|
|
64
|
+
server is the thin MCP adapter in front of it.
|
|
65
|
+
|
|
66
|
+
## Tools
|
|
67
|
+
|
|
68
|
+
10 read tools are always registered. Three write tools appear only when `MCP_ALLOW_WRITE` is on.
|
|
69
|
+
|
|
70
|
+
| Tool | What it does |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `list_metadata` | Every object of a metadata kind (names + synonyms) — start here |
|
|
73
|
+
| `describe_metadata` | Full attribute tree of one object, incl. tabular sections |
|
|
74
|
+
| `search_catalog` | Substring search over a catalog by name |
|
|
75
|
+
| `filter_catalog` | Filter/sort/aggregate a catalog by any field (`agg`, `groupby`) |
|
|
76
|
+
| `get_documents` | Documents over a period, with filters, sorting and aggregation |
|
|
77
|
+
| `get_schema` | Data structure of one document type |
|
|
78
|
+
| `get_object` | **All** attribute values of one record + its tabular sections |
|
|
79
|
+
| `get_register_balance` | Accumulation-register balance (native query) |
|
|
80
|
+
| `get_register_records` | Raw rows of an information or accumulation register |
|
|
81
|
+
| `health` | Service ping — reports whether the infobase key matches |
|
|
82
|
+
| `write_object` *(gated)* | Create/update an object through MCBP Plus conversion rules |
|
|
83
|
+
| `patch_object` *(gated)* | Update header attributes of an existing object natively |
|
|
84
|
+
| `save_context` *(gated)* | Push a conversation turn upstream (skeleton) |
|
|
85
|
+
|
|
86
|
+
The model is expected to walk `list_metadata → describe_metadata → search/filter → get_object`.
|
|
87
|
+
Errors are deliberately not swallowed: an unknown field comes back as `BAD_PARAMETER` naming that
|
|
88
|
+
field, which is how the model corrects itself.
|
|
89
|
+
|
|
90
|
+
## Install
|
|
91
|
+
|
|
92
|
+
Requires **Python 3.11+**.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install mcbp
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Configure
|
|
99
|
+
|
|
100
|
+
### Claude Desktop
|
|
101
|
+
|
|
102
|
+
`%APPDATA%\Claude\claude_desktop_config.json` (Windows) or
|
|
103
|
+
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"mcbp-ai": {
|
|
109
|
+
"command": "python",
|
|
110
|
+
"args": ["-m", "mcbp_mcp_server"],
|
|
111
|
+
"env": {
|
|
112
|
+
"ONEC_BASE_URL": "http://host/base/hs/mcbp_ai",
|
|
113
|
+
"ONEC_USER": "ai_service",
|
|
114
|
+
"ONEC_PASSWORD": "***"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Claude Code
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
claude mcp add mcbp-ai --env ONEC_BASE_URL=http://host/base/hs/mcbp_ai \
|
|
125
|
+
--env ONEC_USER=ai_service \
|
|
126
|
+
--env ONEC_PASSWORD=*** \
|
|
127
|
+
-- python -m mcbp_mcp_server
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Environment
|
|
131
|
+
|
|
132
|
+
| Variable | Required | Default | Purpose |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| `ONEC_BASE_URL` | yes | — | `http(s)://<host>/<base>/hs/mcbp_ai` — see the note below |
|
|
135
|
+
| `ONEC_USER` | yes | — | HTTP Basic user of the BAS publication |
|
|
136
|
+
| `ONEC_PASSWORD` | yes | — | Password. **May be empty**, but the variable must be present |
|
|
137
|
+
| `ONEC_TIMEOUT` | no | `30` | Request timeout, seconds |
|
|
138
|
+
| `ONEC_POOL_MAX` | no | `10` | Connection pool size |
|
|
139
|
+
| `ONEC_VERIFY_SSL` | no | `1` | `0` for self-signed intranet publications |
|
|
140
|
+
| `MCP_ALLOW_WRITE` | no | `0` | `1` enables the three write tools |
|
|
141
|
+
|
|
142
|
+
### `ONEC_BASE_URL` — leave off the `/ai/v1`
|
|
143
|
+
|
|
144
|
+
The client appends `/ai/v1/...` itself, so the base URL stops at the service name:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
http://localhost/mybase/hs/mcbp_ai ← correct
|
|
148
|
+
http://localhost/mybase/hs/mcbp_ai/ai/v1/ ← also accepted (normalized away)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Note that the published path genuinely repeats the segment: `rootUrl` is `mcbp_ai` in `default.vrd`,
|
|
152
|
+
and the service's own URL templates start with `ai/v1`.
|
|
153
|
+
|
|
154
|
+
## Security
|
|
155
|
+
|
|
156
|
+
- Read-only unless you deliberately set `MCP_ALLOW_WRITE=1`. The write tools are not merely hidden
|
|
157
|
+
— they are never registered, so they cannot be invoked.
|
|
158
|
+
- Credentials live only in the MCP client's env block; nothing is written to disk by this server.
|
|
159
|
+
- The BAS side adds its own checks: the publication's user rights, plus an infobase-key check. If
|
|
160
|
+
the key does not match, `health` reports `key: false` and every other route answers
|
|
161
|
+
`403 KEY_MISMATCH`.
|
|
162
|
+
- `write_object` additionally requires the MCBP Plus extension and a configured conversion rule;
|
|
163
|
+
without one it returns `422 CONVERSION_NOT_CONFIGURED` and writes nothing.
|
|
164
|
+
|
|
165
|
+
## Troubleshooting
|
|
166
|
+
|
|
167
|
+
| Symptom | Cause |
|
|
168
|
+
|---|---|
|
|
169
|
+
| Every route `404`, `health` also fails | `ONEC_BASE_URL` wrong, or the `MCBP_AI` module is not installed in the base |
|
|
170
|
+
| Every route `403 KEY_MISMATCH` | Infobase key mismatch — check `health`, fix the publication key |
|
|
171
|
+
| `ONEC_PASSWORD is required` | The variable is absent. An empty value is fine; an absent one is not |
|
|
172
|
+
| `501 PLUS_REQUIRED` | Only `write_object` raises it — that base has no MCBP Plus |
|
|
173
|
+
| Model says a register is unreadable | Information registers have no `Ref`; use `get_register_records` |
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
See `LICENSE`.
|
mcbp-0.2.4/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<!-- mcp-name: io.github.kzavrazhnyi/mcbp-ai -->
|
|
2
|
+
|
|
3
|
+
# mcbp-ai — MCP server for BAS / 1C (MCBP+)
|
|
4
|
+
|
|
5
|
+
Read your BAS (1C) database from any MCP client. Catalogs, documents, register balances and records,
|
|
6
|
+
and the full configuration metadata tree — exposed as MCP tools over the `MCBP_AI` HTTP service.
|
|
7
|
+
|
|
8
|
+
**Read-only by default.** None of the tools available out of the box modifies your data.
|
|
9
|
+
|
|
10
|
+
**Model-agnostic.** MCP is a vendor-neutral standard, so this server works with **Claude Desktop /
|
|
11
|
+
Code, ChatGPT desktop, Gemini, Microsoft and GitHub Copilot, Cursor, Windsurf, VS Code and Zed** —
|
|
12
|
+
it serves `tools/list` and executes `tools/call` without knowing which model is asking. The
|
|
13
|
+
transport is **stdio**, so the client launches the process locally; browser-based clients would
|
|
14
|
+
need a remote HTTP server, which this one does not expose.
|
|
15
|
+
|
|
16
|
+
> ### ⚠ This server needs a server-side component that is not in this repository
|
|
17
|
+
>
|
|
18
|
+
> `mcbp-ai` is a client for the **`MCBP_AI` HTTP service** — a BSL module (a common module plus an
|
|
19
|
+
> HTTP service definition) that must be installed into your BAS/1C configuration through
|
|
20
|
+
> Конфігуратор. **That module is licensed separately and is not part of this repository.**
|
|
21
|
+
>
|
|
22
|
+
> Without it this server starts, connects, and every route answers `404`. If you want to run it
|
|
23
|
+
> against your own base, contact MCBP.PLUS to obtain the `MCBP_AI` module.
|
|
24
|
+
>
|
|
25
|
+
> The Python code here — the MCP adapter and the shared BAS client — is open source and complete.
|
|
26
|
+
|
|
27
|
+
The MCBP+ configuration and the `MCBP_AI` service module are proprietary works of
|
|
28
|
+
**[MCBP.PLUS](https://mcbp.plus)** and are distributed separately from this repository. This MIT
|
|
29
|
+
licence covers the Python packages only.
|
|
30
|
+
|
|
31
|
+
## Why this exists
|
|
32
|
+
|
|
33
|
+
BAS/1C holds the data, but it is not reachable from an LLM: the platform speaks its own query
|
|
34
|
+
language, metadata names are inherited Russian identifiers while synonyms are Ukrainian, and a raw
|
|
35
|
+
OData feed is both heavy and hostile to a model (opaque errors, no self-correction path).
|
|
36
|
+
|
|
37
|
+
`MCBP_AI` solves that on the BAS side — canonical English field names in list rows, compact
|
|
38
|
+
responses, and errors that name the offending field verbatim so a model can fix its own call. This
|
|
39
|
+
server is the thin MCP adapter in front of it.
|
|
40
|
+
|
|
41
|
+
## Tools
|
|
42
|
+
|
|
43
|
+
10 read tools are always registered. Three write tools appear only when `MCP_ALLOW_WRITE` is on.
|
|
44
|
+
|
|
45
|
+
| Tool | What it does |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `list_metadata` | Every object of a metadata kind (names + synonyms) — start here |
|
|
48
|
+
| `describe_metadata` | Full attribute tree of one object, incl. tabular sections |
|
|
49
|
+
| `search_catalog` | Substring search over a catalog by name |
|
|
50
|
+
| `filter_catalog` | Filter/sort/aggregate a catalog by any field (`agg`, `groupby`) |
|
|
51
|
+
| `get_documents` | Documents over a period, with filters, sorting and aggregation |
|
|
52
|
+
| `get_schema` | Data structure of one document type |
|
|
53
|
+
| `get_object` | **All** attribute values of one record + its tabular sections |
|
|
54
|
+
| `get_register_balance` | Accumulation-register balance (native query) |
|
|
55
|
+
| `get_register_records` | Raw rows of an information or accumulation register |
|
|
56
|
+
| `health` | Service ping — reports whether the infobase key matches |
|
|
57
|
+
| `write_object` *(gated)* | Create/update an object through MCBP Plus conversion rules |
|
|
58
|
+
| `patch_object` *(gated)* | Update header attributes of an existing object natively |
|
|
59
|
+
| `save_context` *(gated)* | Push a conversation turn upstream (skeleton) |
|
|
60
|
+
|
|
61
|
+
The model is expected to walk `list_metadata → describe_metadata → search/filter → get_object`.
|
|
62
|
+
Errors are deliberately not swallowed: an unknown field comes back as `BAD_PARAMETER` naming that
|
|
63
|
+
field, which is how the model corrects itself.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
Requires **Python 3.11+**.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install mcbp
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configure
|
|
74
|
+
|
|
75
|
+
### Claude Desktop
|
|
76
|
+
|
|
77
|
+
`%APPDATA%\Claude\claude_desktop_config.json` (Windows) or
|
|
78
|
+
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"mcbp-ai": {
|
|
84
|
+
"command": "python",
|
|
85
|
+
"args": ["-m", "mcbp_mcp_server"],
|
|
86
|
+
"env": {
|
|
87
|
+
"ONEC_BASE_URL": "http://host/base/hs/mcbp_ai",
|
|
88
|
+
"ONEC_USER": "ai_service",
|
|
89
|
+
"ONEC_PASSWORD": "***"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Claude Code
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
claude mcp add mcbp-ai --env ONEC_BASE_URL=http://host/base/hs/mcbp_ai \
|
|
100
|
+
--env ONEC_USER=ai_service \
|
|
101
|
+
--env ONEC_PASSWORD=*** \
|
|
102
|
+
-- python -m mcbp_mcp_server
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Environment
|
|
106
|
+
|
|
107
|
+
| Variable | Required | Default | Purpose |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `ONEC_BASE_URL` | yes | — | `http(s)://<host>/<base>/hs/mcbp_ai` — see the note below |
|
|
110
|
+
| `ONEC_USER` | yes | — | HTTP Basic user of the BAS publication |
|
|
111
|
+
| `ONEC_PASSWORD` | yes | — | Password. **May be empty**, but the variable must be present |
|
|
112
|
+
| `ONEC_TIMEOUT` | no | `30` | Request timeout, seconds |
|
|
113
|
+
| `ONEC_POOL_MAX` | no | `10` | Connection pool size |
|
|
114
|
+
| `ONEC_VERIFY_SSL` | no | `1` | `0` for self-signed intranet publications |
|
|
115
|
+
| `MCP_ALLOW_WRITE` | no | `0` | `1` enables the three write tools |
|
|
116
|
+
|
|
117
|
+
### `ONEC_BASE_URL` — leave off the `/ai/v1`
|
|
118
|
+
|
|
119
|
+
The client appends `/ai/v1/...` itself, so the base URL stops at the service name:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
http://localhost/mybase/hs/mcbp_ai ← correct
|
|
123
|
+
http://localhost/mybase/hs/mcbp_ai/ai/v1/ ← also accepted (normalized away)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Note that the published path genuinely repeats the segment: `rootUrl` is `mcbp_ai` in `default.vrd`,
|
|
127
|
+
and the service's own URL templates start with `ai/v1`.
|
|
128
|
+
|
|
129
|
+
## Security
|
|
130
|
+
|
|
131
|
+
- Read-only unless you deliberately set `MCP_ALLOW_WRITE=1`. The write tools are not merely hidden
|
|
132
|
+
— they are never registered, so they cannot be invoked.
|
|
133
|
+
- Credentials live only in the MCP client's env block; nothing is written to disk by this server.
|
|
134
|
+
- The BAS side adds its own checks: the publication's user rights, plus an infobase-key check. If
|
|
135
|
+
the key does not match, `health` reports `key: false` and every other route answers
|
|
136
|
+
`403 KEY_MISMATCH`.
|
|
137
|
+
- `write_object` additionally requires the MCBP Plus extension and a configured conversion rule;
|
|
138
|
+
without one it returns `422 CONVERSION_NOT_CONFIGURED` and writes nothing.
|
|
139
|
+
|
|
140
|
+
## Troubleshooting
|
|
141
|
+
|
|
142
|
+
| Symptom | Cause |
|
|
143
|
+
|---|---|
|
|
144
|
+
| Every route `404`, `health` also fails | `ONEC_BASE_URL` wrong, or the `MCBP_AI` module is not installed in the base |
|
|
145
|
+
| Every route `403 KEY_MISMATCH` | Infobase key mismatch — check `health`, fix the publication key |
|
|
146
|
+
| `ONEC_PASSWORD is required` | The variable is absent. An empty value is fine; an absent one is not |
|
|
147
|
+
| `501 PLUS_REQUIRED` | Only `write_object` raises it — that base has no MCBP Plus |
|
|
148
|
+
| Model says a register is unreadable | Information registers have no `Ref`; use `get_register_records` |
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
See `LICENSE`.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Deploying `mcbp-ai` as a remote MCP server
|
|
2
|
+
|
|
3
|
+
Runs the same server the stdio entry point runs, over Streamable HTTP. One process per BAS base.
|
|
4
|
+
|
|
5
|
+
Two stages, and only the first is being done now:
|
|
6
|
+
|
|
7
|
+
| | Stage 1 — internal network | Stage 2 — external server, public IP |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Client reaches | the process, `http://`, directly | nginx, `https://` |
|
|
10
|
+
| Process listens on | the LAN interface | `127.0.0.1` |
|
|
11
|
+
| Access control | the network boundary, nothing else | TLS + token or IP allow-list in nginx |
|
|
12
|
+
| Files used | `mcbp-mcp@.service`, `mcbp-mcp.env.example` | the same two + `nginx-mcp.conf` |
|
|
13
|
+
|
|
14
|
+
Stage 2 is a config delta on top of stage 1, not a redeployment: same venv, same unit, same env
|
|
15
|
+
files with three values changed. It is written out in section 6.
|
|
16
|
+
|
|
17
|
+
## 0. Which clients can reach an internal endpoint
|
|
18
|
+
|
|
19
|
+
The rule is **where the MCP client process runs**, not which model is behind it.
|
|
20
|
+
|
|
21
|
+
- **Runs on a machine inside the network → works in stage 1.** Claude Code is the case at hand;
|
|
22
|
+
so is any in-house application that speaks MCP itself, whatever model it drives (OpenAI
|
|
23
|
+
included).
|
|
24
|
+
- **Hosted connector surfaces → cannot work in stage 1.** claude.ai, Claude Desktop and ChatGPT
|
|
25
|
+
connectors do not connect from the user's machine: the provider's infrastructure does, from the
|
|
26
|
+
public internet (Anthropic's egress is `160.79.104.0/21`). Such a client cannot see an internal
|
|
27
|
+
host at all. That is architecture, not configuration — it changes only in stage 2.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Stage 1: internal network
|
|
32
|
+
|
|
33
|
+
## 1. Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
sudo useradd --system --home /opt/mcbp-mcp --shell /usr/sbin/nologin mcbp
|
|
37
|
+
sudo mkdir -p /opt/mcbp-mcp && sudo chown mcbp:mcbp /opt/mcbp-mcp
|
|
38
|
+
sudo -u mcbp python3.11 -m venv /opt/mcbp-mcp/.venv
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The distribution is named `mcbp` and is published on **TestPyPI**. Release `0.2.0` must be
|
|
42
|
+
uploaded there before this command can work — `mcbp-core` first, then `mcbp`; a published version
|
|
43
|
+
is immutable, which is why the HTTP transport ships as a new version rather than a re-upload.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
sudo -u mcbp /opt/mcbp-mcp/.venv/bin/pip install \
|
|
47
|
+
--index-url https://test.pypi.org/simple/ \
|
|
48
|
+
--extra-index-url https://pypi.org/simple/ \
|
|
49
|
+
"mcbp[http]==0.2.0"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **`--extra-index-url https://pypi.org/simple/` is mandatory.** The runtime dependencies —
|
|
53
|
+
httpx, mcp, starlette, uvicorn — are not on TestPyPI, so resolution fails without it. That is
|
|
54
|
+
a property of TestPyPI, not a defect of the package.
|
|
55
|
+
- **Pin the version.** A bare `mcbp[http]` resolves to whatever is newest on TestPyPI, which is
|
|
56
|
+
not always what was tested. `0.2.0` is the first release carrying the `mcbp-http` entry point
|
|
57
|
+
and the `http` extra; 0.1.0 predates the HTTP transport and gives a stdio-only server.
|
|
58
|
+
- **TestPyPI is a sandbox.** It promises no durability and its packages can be pruned. Fine for
|
|
59
|
+
the internal stage; publishing to the production PyPI is a stage-2 item, before the endpoint
|
|
60
|
+
faces a public IP.
|
|
61
|
+
|
|
62
|
+
**Fallback — install from the checkout.** For a machine with no access to TestPyPI, or to test a
|
|
63
|
+
change that is not released yet:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
sudo -u mcbp /opt/mcbp-mcp/.venv/bin/pip install -e /srv/mcbp-mcp-server/mcbp-core
|
|
67
|
+
sudo -u mcbp /opt/mcbp-mcp/.venv/bin/pip install -e "/srv/mcbp-mcp-server/mcp-server[http]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Here — and only here — `mcbp-core` must go **first and editable (`-e`)**. A plain `pip install`
|
|
71
|
+
leaves a snapshot copy in `site-packages`, and later edits to the checkout then silently do
|
|
72
|
+
nothing. That failure has already happened on this box, in the backend's venv (19.08).
|
|
73
|
+
|
|
74
|
+
**Keep this venv separate from the backend's** at `/opt/mcbp-ai/backend/.venv`. The two install
|
|
75
|
+
the same packages at different versions and share a machine, nothing else.
|
|
76
|
+
|
|
77
|
+
One venv serves every base: updating is one `pip install` plus
|
|
78
|
+
`sudo systemctl restart 'mcbp-mcp@*'`.
|
|
79
|
+
|
|
80
|
+
## 2. One env file per base
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
sudo mkdir -p /etc/mcbp-mcp
|
|
84
|
+
sudo cp mcbp-mcp.env.example /etc/mcbp-mcp/prod.env
|
|
85
|
+
sudo chmod 600 /etc/mcbp-mcp/prod.env && sudo chown root:root /etc/mcbp-mcp/prod.env
|
|
86
|
+
sudo -e /etc/mcbp-mcp/prod.env
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The file name is the systemd instance name: `/etc/mcbp-mcp/prod.env` → `mcbp-mcp@prod`. Repeat
|
|
90
|
+
per base, changing `ONEC_BASE_URL`, the credentials and — mandatory — `MCP_HTTP_PORT`.
|
|
91
|
+
|
|
92
|
+
For stage 1 set `MCP_HTTP_HOST` to the LAN address (or `0.0.0.0`) and leave
|
|
93
|
+
`MCP_HTTP_ALLOWED_HOSTS` **empty**.
|
|
94
|
+
|
|
95
|
+
Read the comments in that file. Three of them mark mistakes that have already cost a live
|
|
96
|
+
round-trip each: the `/ai/v1` suffix, the absent `ONEC_PASSWORD`, the empty host allow-list.
|
|
97
|
+
|
|
98
|
+
## 3. Run
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sudo cp mcbp-mcp@.service /etc/systemd/system/
|
|
102
|
+
sudo systemctl daemon-reload
|
|
103
|
+
sudo systemctl enable --now mcbp-mcp@prod mcbp-mcp@demo
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
One unit file, any number of bases — the instance name after `@` picks the env file.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
systemctl status 'mcbp-mcp@*'
|
|
110
|
+
journalctl -u mcbp-mcp@prod -n 30 # startup logs the BAS base_url and the write flag
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Bring the first base up alone and verify it end to end before adding the rest.
|
|
114
|
+
|
|
115
|
+
## 4. Verify
|
|
116
|
+
|
|
117
|
+
Two checks that answer different questions.
|
|
118
|
+
|
|
119
|
+
**a) The process is alive.** Says nothing about BAS — it is a static string:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
curl -s 127.0.0.1:8011/healthz # -> ok
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**b) The transport really speaks MCP.** A bare `GET /mcp` is expected to fail (400, no session),
|
|
126
|
+
so the check is an `initialize` round-trip. In stage 1 there is no TLS and no allow-list, so this
|
|
127
|
+
runs unchanged from the server or from a workstation — swap the host:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
curl -sS http://<server-lan-address>:8011/mcp \
|
|
131
|
+
-H 'content-type: application/json' \
|
|
132
|
+
-H 'accept: application/json, text/event-stream' \
|
|
133
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A `serverInfo` with a non-empty `version` means the transport is live. It still says nothing
|
|
137
|
+
about BAS: the connection to the base is made lazily, on the first tool call.
|
|
138
|
+
|
|
139
|
+
**c) BAS is actually reachable.** Only a real tool call proves that. Register the server with a
|
|
140
|
+
client and confirm `tools/list` returns 10 tools (13 if `MCP_ALLOW_WRITE=1`), then call `health`.
|
|
141
|
+
|
|
142
|
+
## 5. Connect a client
|
|
143
|
+
|
|
144
|
+
Claude Code, from a machine on the same network, connecting as the user's OWN BAS account:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
claude mcp add --transport http mcbp-prod http://<server-lan-address>:8011/mcp --header "Authorization: Basic $(printf '%s' 'user:password' | base64)"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Each base is a separate entry with its own port.
|
|
151
|
+
|
|
152
|
+
**What the header does.** Every tool call from that client runs under the named BAS account: BAS
|
|
153
|
+
rights decide what it may read or write, and the BAS registration log names that person rather
|
|
154
|
+
than the service account. One process still serves one base — the header carries credentials
|
|
155
|
+
only, never a base selector.
|
|
156
|
+
|
|
157
|
+
**Without the header** the client falls back to the process's `ONEC_USER` (section 2). That is
|
|
158
|
+
still supported, and it is what stdio always does — but it puts every action under the one
|
|
159
|
+
service identity.
|
|
160
|
+
|
|
161
|
+
**A malformed header is rejected, never downgraded.** A non-Basic scheme, undecodable base64, or
|
|
162
|
+
a value that is not `user:password` answers `400 BAD_AUTHORIZATION`. A silent fallback to the
|
|
163
|
+
service account is exactly the failure this feature removes. A *wrong* password is not this case:
|
|
164
|
+
it reaches BAS and comes back as the normal BAS authentication failure.
|
|
165
|
+
|
|
166
|
+
**Where the password ends up.** In the client's own MCP config file, in clear text (base64 is not
|
|
167
|
+
encryption) — `~/.claude.json` for Claude Code. This differs from the web backend, where the
|
|
168
|
+
password is typed into a login form and lives only in that session's memory. Treat the config
|
|
169
|
+
file accordingly, and prefer a per-person BAS account over a shared one.
|
|
170
|
+
|
|
171
|
+
## Security property of stage 1 — read this before opening the port
|
|
172
|
+
|
|
173
|
+
**The endpoint itself authenticates nobody.** It accepts every connection; what it does NOT do
|
|
174
|
+
is grant every connection the same power.
|
|
175
|
+
|
|
176
|
+
- **With an `Authorization` header** (section 5) the caller acts as their own BAS account. BAS
|
|
177
|
+
rights are then the access control, and the registration log names the person. The header is
|
|
178
|
+
passed through to BAS and verified there — this server never checks a password itself, so it
|
|
179
|
+
cannot be tricked into accepting one.
|
|
180
|
+
- **Without a header** the caller acts as the process's `ONEC_USER`, and every such action looks
|
|
181
|
+
identical in the BAS log. Keep that account low-privilege for exactly this reason.
|
|
182
|
+
|
|
183
|
+
`MCP_ALLOW_WRITE` remains a property of the PROCESS, not of the caller: a read-only instance
|
|
184
|
+
exposes the 10 read tools to everyone who reaches it, whatever account they present. Write
|
|
185
|
+
permission is therefore decided twice — by the instance, then by BAS rights.
|
|
186
|
+
|
|
187
|
+
What is still missing: **nothing stops an unauthorized person from reaching the port** and
|
|
188
|
+
falling back to the service account. Handing out "only the bases you are allowed" is a
|
|
189
|
+
convenience, not a control: ports are predictable, and someone inside the network who learns a
|
|
190
|
+
neighbouring base's URL simply adds it. So **the network boundary is still the control over WHO
|
|
191
|
+
connects** — the header changes what they can do once connected, not whether they may connect.
|
|
192
|
+
Both must be replaced before the endpoint gets a public IP.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
# Stage 2: external server with a public IP
|
|
197
|
+
|
|
198
|
+
What changes. Nothing is rebuilt.
|
|
199
|
+
|
|
200
|
+
1. **nginx in front, terminating TLS.** Take the `location` blocks from `nginx-mcp.conf` — one
|
|
201
|
+
per instance, mapping a public path to that instance's port — into the TLS `server { }` block,
|
|
202
|
+
then reload. Do not drop `proxy_buffering off` or the 310 s timeouts: the first breaks SSE
|
|
203
|
+
streaming, the second lets nginx cut a long tool call.
|
|
204
|
+
2. **`MCP_HTTP_HOST=127.0.0.1`** in every env file, then restart. The process must be reachable
|
|
205
|
+
only through the proxy; leaving it on the LAN interface would publish an unauthenticated
|
|
206
|
+
endpoint next to the guarded one.
|
|
207
|
+
3. **`MCP_HTTP_ALLOWED_HOSTS=<public hostname>`** in every env file. This turns on DNS-rebinding
|
|
208
|
+
protection. It must agree with what nginx forwards, which is why the config keeps
|
|
209
|
+
`proxy_set_header Host $host`.
|
|
210
|
+
4. **Access control, chosen deliberately** — the header of `nginx-mcp.conf` lists the options
|
|
211
|
+
(per-base token in a custom header via an nginx `map`, source-IP allow-list, OAuth) and what
|
|
212
|
+
each one is and is not good for. Whatever is chosen there decides WHO may connect; the BAS
|
|
213
|
+
account in the caller's `Authorization` header decides what they may then do. That header is
|
|
214
|
+
taken: nginx must forward it untouched and must not run `auth_basic` on these locations.
|
|
215
|
+
5. **A publicly valid certificate** if hosted connector surfaces are to be clients (section 0).
|
|
216
|
+
6. **Publish `mcbp` to the production PyPI** and reinstall from it. TestPyPI makes no durability
|
|
217
|
+
promise; a public deployment must not depend on a sandbox index.
|
|
218
|
+
|
|
219
|
+
## Troubleshooting
|
|
220
|
+
|
|
221
|
+
| Symptom | Cause |
|
|
222
|
+
|---|---|
|
|
223
|
+
| Connection refused from a workstation, fine from the server | `MCP_HTTP_HOST` is still `127.0.0.1`. |
|
|
224
|
+
| `421 Invalid Host header` | `MCP_HTTP_ALLOWED_HOSTS` is set and disagrees with the Host the client sends. In stage 1 it should be empty; behind nginx, keep `proxy_set_header Host $host`. |
|
|
225
|
+
| `307` to `/mcp/` | Not from this server — the route matches `/mcp` exactly. Look for a proxy rewriting the path. |
|
|
226
|
+
| `400` on POST | Missing `mcp-session-id`, or `accept` without `text/event-stream`. |
|
|
227
|
+
| `400 BAD_AUTHORIZATION` | The `Authorization` header is not `Basic <base64 user:password>`. Rebuild it; the server deliberately does not fall back to the service account. |
|
|
228
|
+
| Tools answer with a BAS auth error | The credentials in the header are wrong for this base. The header reached BAS — that is the base rejecting them, not the transport. |
|
|
229
|
+
| Actions show as the service account in the BAS log | The client sends no `Authorization` header. |
|
|
230
|
+
| Response hangs, then arrives all at once | `proxy_buffering` is still on (stage 2 only). |
|
|
231
|
+
| Two instances, one won't start | Same `MCP_HTTP_PORT` in both env files. `journalctl` shows the bind error. |
|
|
232
|
+
| Every tool 403 `KEY_MISMATCH` while `health` works | Infobase key does not match the publication — a BAS-side issue; startup logs a warning for it. |
|
|
233
|
+
| `404` on every tool, `health` fine | `ONEC_BASE_URL` still carries the `/ai/v1` suffix. |
|
|
234
|
+
| `No matching distribution found for httpx` (or mcp/starlette/uvicorn) | The TestPyPI install is missing `--extra-index-url https://pypi.org/simple/`. |
|
|
235
|
+
| `mcbp-http: command not found` | Version 0.1.0 got installed — it predates the HTTP transport. Pin `==0.2.0`. |
|
|
236
|
+
| Edits to the checkout have no effect | Fallback install only: `mcbp-core` went in without `-e`. |
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Підключення до MCP-сервера mcbp-ai (Windows)
|
|
2
|
+
|
|
3
|
+
Комплект для машини **клієнта**. Сервер уже піднятий кимось іншим — тут лише три кроки.
|
|
4
|
+
|
|
5
|
+
Що вам мають видати:
|
|
6
|
+
|
|
7
|
+
| Що | Приклад | Звідки |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Адреса сервера | `https://<адреса сервера>:8443/mcp` | адміністратор сервера |
|
|
10
|
+
| Токен доступу | довгий випадковий рядок | адміністратор (`new-token.cmd` на сервері) |
|
|
11
|
+
| Файл сертифіката | `mcbp-mcp.cer` | адміністратор — лише якщо адреса на `https` |
|
|
12
|
+
|
|
13
|
+
Токен — це ваш пароль до BAS: він зіставлений з конкретною обліковкою, і всі дії
|
|
14
|
+
в журналі реєстрації бази підуть від вашого імені. Не пересилайте його далі.
|
|
15
|
+
|
|
16
|
+
## Крок 1. Довіра до сертифіката (лише для `https`)
|
|
17
|
+
|
|
18
|
+
Покладіть `mcbp-mcp.cer` поруч із `trust-cert.cmd`, натисніть на скрипті правою
|
|
19
|
+
кнопкою → **Запуск від імені адміністратора**. Без прав адміністратора скрипт
|
|
20
|
+
одразу скаже про це і нічого не змінить.
|
|
21
|
+
|
|
22
|
+
Скрипт вносить сертифікат у сховище «Довірені кореневі центри сертифікації».
|
|
23
|
+
Якщо адреса сервера на `http://` — цей крок пропускається.
|
|
24
|
+
|
|
25
|
+
## Крок 2. Реєстрація сервера в Codex
|
|
26
|
+
|
|
27
|
+
Запустіть `setup-codex.cmd`. Він спитає адресу та токен (або візьміть їх
|
|
28
|
+
аргументами: `setup-codex.cmd https://<адреса сервера>:8443/mcp <токен>`).
|
|
29
|
+
|
|
30
|
+
Скрипт:
|
|
31
|
+
|
|
32
|
+
1. знаходить `codex.exe` у `PATH`, а якщо там немає — у
|
|
33
|
+
`%LOCALAPPDATA%\OpenAI\Codex\bin\`;
|
|
34
|
+
2. зберігає токен у змінній оточення користувача `MCBP_TOKEN` (`setx`);
|
|
35
|
+
3. виконує `codex mcp add mcbp --url <адреса> --bearer-token-env-var MCBP_TOKEN`.
|
|
36
|
+
|
|
37
|
+
**Після цього закрийте вікно і відкрийте нове** — `setx` діє лише на нові процеси.
|
|
38
|
+
|
|
39
|
+
## Крок 3. Перевірка
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
codex mcp list
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Сервер `mcbp` має бути у списку. Далі в Codex попросіть, наприклад, показати
|
|
46
|
+
перелік довідників — має відповісти база, а не помилка авторизації.
|
|
47
|
+
|
|
48
|
+
## Якщо не працює
|
|
49
|
+
|
|
50
|
+
| Симптом | Причина |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `400 BAD_AUTHORIZATION` | токен не той або застарів — запитайте новий |
|
|
53
|
+
| помилка сертифіката / TLS | не виконано крок 1, або сертифікат виданий на іншу адресу |
|
|
54
|
+
| `codex.exe not found` | не встановлено Codex CLI |
|
|
55
|
+
| працювало, перестало після зміни токена | не перевідкрито термінал після `setx` |
|
|
56
|
+
| з'єднання не встановлюється | сервер вимкнено або закрито порт — до адміністратора |
|
|
57
|
+
|
|
58
|
+
## Claude Code замість Codex
|
|
59
|
+
|
|
60
|
+
Claude Code вміє довільні заголовки, тож токен передається так само —
|
|
61
|
+
заголовком `Authorization: Bearer <токен>`. Схема `Basic` з обліковкою BAS
|
|
62
|
+
теж і далі приймається.
|