@hubex/mcp 0.7.0 → 0.8.20260919

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.
package/CONNECTING.md CHANGED
@@ -92,6 +92,8 @@ npm run build
92
92
  > покажут поле ввода; остальным нужно вызвать инструмент `hubex_set_token` и
93
93
  > передать токен в него. По токену запрашивается refresh, дальше сессия
94
94
  > продлевается сама; после перезапуска сервера токен спрашивается заново.
95
+ > Если HubEx отверг токен (ошибка 401), следующий запрос получит новый — по
96
+ > refresh, а если не выйдет, сервер снова попросит токен.
95
97
 
96
98
  > Сервисный токен выпускается один раз администратором:
97
99
  > `POST /fsm/AUTHZ/ServiceTokens` с Bearer и полномочием `ServiceTokenAdd`, тело — массив id
package/README.md CHANGED
@@ -145,6 +145,27 @@ claude mcp add hubex --scope user \
145
145
  Всё перечисленное вписывается в тот же блок `"env"` из шага 2 (в Codex — в секцию
146
146
  `[mcp_servers.hubex.env]`). Значения всегда пишутся в кавычках, даже числа.
147
147
 
148
+ ### Коротко обо всех
149
+
150
+ | Переменная | По умолчанию | Описание |
151
+ |---|---|---|
152
+ | `HUBEX_ENV` | `dev` | Окружение: `dev` \| `stg` \| `prod`. От него зависят базовый URL и то, какой кэш swagger-схем берётся (`prod` или `dev`) |
153
+ | `HUBEX_APPLICATION_ID` | — (**обязательный**) | Значение заголовка `X-Application-ID` (`5` — Api/интеграции, `3` — MainWeb) |
154
+ | `HUBEX_TENANT_ID` | из JWT (`TenantID`) | Задаёт тенант явно. В режиме `service` не нужен |
155
+ | `HUBEX_AUTH_MODE` | `token` | Режим авторизации: `token` \| `login` \| `service` |
156
+ | `HUBEX_USERNAME` | — | Логин. Обязателен при `login` |
157
+ | `HUBEX_PASSWORD` | — | Пароль. Обязателен при `login` |
158
+ | `HUBEX_SERVICE_TOKEN` | — | Интеграционный токен. Обязателен при `service` |
159
+ | `HUBEX_METHODS` | все: `GET,POST,PUT,PATCH,DELETE,HEAD` | Какие HTTP-методы разрешены. Инструменты для остальных методов не регистрируются |
160
+ | `HUBEX_READONLY` | `false` | То же, что `HUBEX_METHODS=GET,HEAD`. Если задан вместе с `HUBEX_METHODS`, действует более узкий набор |
161
+ | `HUBEX_SERVICES` | все 22 | Список сервисов, с которыми можно работать, например `WORK,ES,ADM` |
162
+ | `HUBEX_MASK_PII` | `true` | Скрывает персональные данные в ответах. Отключается только явным `false` |
163
+ | `HUBEX_DEFAULT_REQUEST_METHOD_ID` | — | Подставляется в тело запросов на запись, если поле не заполнено |
164
+ | `HUBEX_DEFAULT_TASK_TYPE_ID` | — | То же |
165
+ | `HUBEX_DEFAULT_COMPANY_ID` | — | То же |
166
+
167
+ Подробнее о каждой — ниже.
168
+
148
169
  ### Обязательная — одна
149
170
 
150
171
  | Настройка | Что делает |
package/dist/auth.js CHANGED
@@ -51,6 +51,11 @@ class RefreshingAuth {
51
51
  prompter;
52
52
  accessToken;
53
53
  accessExp; // seconds since epoch
54
+ /**
55
+ * Set by `invalidate`. The rejected token is kept rather than dropped: `renew`
56
+ * still has to send it as `accessJwt` to preserve the token lifetime.
57
+ */
58
+ accessRejected = false;
54
59
  refreshToken;
55
60
  refreshExp; // seconds since epoch
56
61
  tenantId;
@@ -74,8 +79,12 @@ class RefreshingAuth {
74
79
  this.absorb({ access_token: trimmed });
75
80
  await this.acquireRefreshToken();
76
81
  }
82
+ invalidate(token) {
83
+ if (token === this.accessToken)
84
+ this.accessRejected = true;
85
+ }
77
86
  async getAccessToken() {
78
- if (this.accessToken && !isExpired(this.accessExp)) {
87
+ if (this.accessToken && !this.accessRejected && !isExpired(this.accessExp)) {
79
88
  return this.accessToken;
80
89
  }
81
90
  // De-duplicate concurrent acquisitions.
@@ -222,6 +231,7 @@ class RefreshingAuth {
222
231
  absorb(result) {
223
232
  if (result.access_token) {
224
233
  this.accessToken = result.access_token;
234
+ this.accessRejected = false;
225
235
  const claims = decodeJwt(result.access_token);
226
236
  this.accessExp = claims?.exp;
227
237
  if (!this.tenantId)
package/dist/http.js CHANGED
@@ -122,6 +122,11 @@ export class HubexClient {
122
122
  exposed[name] = value;
123
123
  }
124
124
  if (!res.ok) {
125
+ // 401 — токен отвергнут (отозван, другое окружение), хотя по exp ещё жив.
126
+ // Без сброса провайдер отдавал бы его до истечения срока, и каждый запрос
127
+ // падал бы так же. Сам запрос не повторяем: POST не идемпотентен.
128
+ if (res.status === 401)
129
+ this.auth.invalidate(token);
125
130
  throw new HubexApiError(res.status, req.service, req.path, visible ?? text, this.config.env);
126
131
  }
127
132
  return { status: res.status, data: visible, headers: exposed };