@gizmodata/hardcharger-mcp 2.0.0 → 2.2.0

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/CHANGELOG.md CHANGED
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.2.0] - 2026-10-30
11
+
12
+ ### Added
13
+ - Per-person credentials through a connection broker (`HARDCHARGER_MCP_BROKER_URL`,
14
+ `HARDCHARGER_MCP_BROKER_TOKEN`, HTTP transport with OAuth). The server then holds no Hardcharger
15
+ credentials: a broker, such as the control plane that runs the servers, lists the servers each
16
+ signed-in person may reach and mints them short-lived sign-in tokens, which the client exchanges
17
+ (token_exchange). Every statement runs as that person, under the server's grants, row policies and
18
+ masking. The person's token is presented to the broker as an RFC 8693 subject token, never as its
19
+ credential. The broker also checks each request's access token (`/introspect`, answers reused for 60 s), so
20
+ opaque access tokens such as Clerk's work and this server needs no provider keys; a broker outage answers 503,
21
+ not 401. Writes need the broker's per-server `allow_writes` as well as `HARDCHARGER_ALLOW_WRITES`.
22
+ Protocol: `docs/connection-broker.md`.
23
+ - `list_connections` and `server_info` report whether each connection accepts writes
24
+ (`allow_writes`), and a brokered connection's description.
25
+
26
+ ### Fixed
27
+ - The README said Clerk issues JWT access tokens by default; it issues opaque ones unless the instance turns JWT
28
+ access tokens on.
29
+
30
+ ## [2.1.0] - 2026-10-29
31
+
32
+ ### Changed
33
+ - Built on the Chargewire protocol and clients: `@gizmodata/hardcharger` 2.1.0, on
34
+ `@gizmodata/chargewire` 2.0.0. `server_info` reports
35
+ the protocol as `chargewire 1`. Tool names, settings and connection URIs are unchanged.
36
+ - Still works with Hardcharger servers released before the rename (2.2.0 and earlier): the
37
+ client falls back to them on its own, and `HARDCHARGER_URI` values written for earlier
38
+ versions keep working.
39
+
10
40
  ## [2.0.0] - 2026-10-20
11
41
 
12
42
  The first published release, with Hardcharger 2.0.0 (0.1.0 was never published). The Changed and
@@ -14,11 +44,11 @@ Fixed entries describe what moved since 0.1.0, for anyone who installed it from
14
44
  <!-- Set the date above at tag time. -->
15
45
 
16
46
  ### Changed
17
- - Built on `@gizmodata/hardcharger` 2.0.0 (hardcharger-js `a6e93a2`, on `@gizmodata/warp-sql` 1.0.0,
18
- warp-sql-js `903feb4`), the clients for Warp SQL protocol 1.0.0 with browser sign-in (were
47
+ - Built on `@gizmodata/hardcharger` 2.0.0 (hardcharger-js `a6e93a2`, on `@gizmodata/chargewire` 1.0.0,
48
+ chargewire-js `903feb4`), the clients for Chargewire protocol 1.0.0 with browser sign-in (were
19
49
  `a4ac97c` and `6dea14c`). The chart, the extension manifest and the npm package are 2.0.0: the
20
50
  Hardcharger clients and integrations launch with the server's version.
21
- - **Breaking:** `login_sso` runs Warp SQL browser sign-in (§4.3) on the connection's own URI and
51
+ - **Breaking:** `login_sso` runs Chargewire browser sign-in (§4.3) on the connection's own URI and
22
52
  port, through `@gizmodata/hardcharger`, as Hardcharger now offers it (its separate OAuth port and
23
53
  `/oauth/initiate` routes are gone). It signs in with the access token the server hands out, used
24
54
  as is (call `login_sso` again when it expires), instead of exchanging an ID token. A failed
@@ -31,10 +61,10 @@ Fixed entries describe what moved since 0.1.0, for anyone who installed it from
31
61
  `client_transport_error` and `client_connection_closed` (was `transport_error`,
32
62
  `connection_closed`), the plain-HTTP hint keys on `client_transport_error`, and error messages
33
63
  name the new codes (e.g. `(client_transport_error)`, `(client_invalid_argument)`).
34
- - `server_info` on a Warp SQL server other than Hardcharger reads the DuckDB version from the
64
+ - `server_info` on a Chargewire server other than Hardcharger reads the DuckDB version from the
35
65
  server-info key `duckdb_version` (was `engine_version`, which protocol 1.0.0 dropped).
36
66
  - A session ended with reason `token_revoked`, `credentials_changed` or `principal_dropped` is no
37
- longer reopened with the configured credentials (Warp SQL §4.2, §5.6): the error, with its
67
+ longer reopened with the configured credentials (Chargewire §4.2, §5.6): the error, with its
38
68
  reason, is returned. `killed`, `token_expired`, no reason or an unknown reason still reopen once.
39
69
 
40
70
  ### Added
@@ -50,8 +80,8 @@ Fixed entries describe what moved since 0.1.0, for anyone who installed it from
50
80
  ### Fixed
51
81
  - CI's live tests on main failed: the browser sign-in suite started `hardcharger-server` with
52
82
  `--oauth-port`, which Hardcharger 2.0.0-rc.1 removed (browser sign-in runs on the server's own
53
- port, Warp SQL §4.3), so the server exited with "unknown option". The suite now passes
54
- `--oauth-base-url` with the server's port (from the merged Warp SQL 1.0 work).
83
+ port, Chargewire §4.3), so the server exited with "unknown option". The suite now passes
84
+ `--oauth-base-url` with the server's port (from the merged Chargewire 1.0 work).
55
85
 
56
86
  ### Documentation
57
87
 
@@ -64,8 +94,8 @@ Fixed entries describe what moved since 0.1.0, for anyone who installed it from
64
94
 
65
95
  The Hardcharger™ port of [gizmosql-mcp](https://github.com/gizmodata/gizmosql-mcp) 0.4.11: the same
66
96
  tools, resource, transports, OAuth resource-server mode, per-user HTTP sessions, Claude Desktop
67
- extension, container image and Helm chart, on Hardcharger's Warp SQL protocol through
68
- `@gizmodata/hardcharger` (hardcharger-js `a4ac97c` on warp-sql-js `6dea14c`).
97
+ extension, container image and Helm chart, on Hardcharger's Chargewire protocol through
98
+ `@gizmodata/hardcharger` (hardcharger-js `a4ac97c` on chargewire-js `6dea14c`).
69
99
 
70
100
  ### Added
71
101
  - Tools `list_connections`, `use_connection`, `list_catalogs`, `list_schemas`, `list_tables`,
@@ -81,21 +111,21 @@ extension, container image and Helm chart, on Hardcharger's Warp SQL protocol th
81
111
  (`HARDCHARGER_MTLS_CERT` + `HARDCHARGER_MTLS_KEY`: alone, the certificate is the credential), or
82
112
  the server's browser sign-in (`login_sso`, the same OAuth routes as `hardcharger --auth-type
83
113
  external`; `HARDCHARGER_BROWSER` names the browser). `HARDCHARGER_TLS_ROOTS` trusts a private CA.
84
- - Query timeouts are the server's own (`statementTimeoutMs`, Warp SQL §6.9): a runaway statement is
114
+ - Query timeouts are the server's own (`statementTimeoutMs`, Chargewire §6.9): a runaway statement is
85
115
  cancelled on the server and reported as `statement_timeout`, with a client-side cancel a few
86
116
  seconds later as backstop. This covers `execute_statement` too.
87
117
  - Lost sessions: the client replaces a session the server evicted and replays its catalog and
88
118
  schema, including ones set by `use_schema` or a `USE` run through `run_query`; a session an
89
119
  administrator killed, a closed connection or a transport failure reopens the connection once.
90
120
  `server_info` reports `session_replacements` and `reconnects`.
91
- - Errors keep the server's message with its Warp SQL code, SQLSTATE, HTTP status and request id
121
+ - Errors keep the server's message with its Chargewire code, SQLSTATE, HTTP status and request id
92
122
  (`... (sql_error, SQLSTATE 42P01, HTTP 400, request r_...)`); errors raised before an answer get
93
123
  their code added (`(transport_error)`), and a TLS handshake against a plain-HTTP server says to
94
124
  add `?tls=false`.
95
125
  - `server_info` reports `HARDCHARGER_VERSION()`, `HARDCHARGER_USER()`, `HARDCHARGER_ROLE()`, the
96
126
  protocol and capabilities from discovery, the server's idle timeout from the session document,
97
127
  and settings from `hardcharger_settings()`.
98
- - Bind parameters travel as Warp SQL JSON parameters; a date, timestamp or binary parameter binds
128
+ - Bind parameters travel as Chargewire JSON parameters; a date, timestamp or binary parameter binds
99
129
  the row as Arrow data through a prepared statement.
100
130
  - Claude Desktop extension (`.mcpb`): one all-JavaScript bundle for every platform (no native
101
131
  driver), with URI, token, client certificate and CA settings.
@@ -113,7 +143,7 @@ extension, container image and Helm chart, on Hardcharger's Warp SQL protocol th
113
143
  - The native ADBC driver and everything around it (bundled drivers per platform,
114
144
  `GIZMOSQL_DRIVER_LIB`, the install-script approval): the client is pure JavaScript.
115
145
  - The timed session refresh (`GIZMOSQL_SESSION_REFRESH_SECONDS`): GizmoSQL silently recreated an
116
- evicted session, so the search path had to be re-applied on a timer; Warp SQL reports the loss
146
+ evicted session, so the search path had to be re-applied on a timer; Chargewire reports the loss
117
147
  and the client replays the session's options.
118
148
  - Host/port/plaintext settings (`GIZMOSQL_HOST`, `_PORT`, `_PLAINTEXT`): the URI says all three.
119
- - Flight SQL error unwrapping and the SqlInfo fallback, which have no Warp SQL counterpart.
149
+ - Flight SQL error unwrapping and the SqlInfo fallback, which have no Chargewire counterpart.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  <img src="https://img.shields.io/badge/MCP-server%20%2B%20Claude%20Desktop%20extension-d97757.svg" alt="MCP server and Claude Desktop extension">
13
13
  <img src="https://img.shields.io/badge/TypeScript-ESM-3178C6.svg?logo=typescript&logoColor=white" alt="TypeScript, ESM">
14
14
  <img src="https://img.shields.io/badge/Node-%E2%89%A522.19-339933.svg?logo=node.js&logoColor=white" alt="Node 22.19 or later">
15
- <a href="https://github.com/gizmodata/warp-sql"><img src="https://img.shields.io/badge/protocol-Warp%20SQL%201.0.0-31a8db.svg" alt="Warp SQL protocol"></a>
15
+ <a href="https://github.com/gizmodata/chargewire"><img src="https://img.shields.io/badge/protocol-Chargewire%201.0.0-31a8db.svg" alt="Chargewire protocol"></a>
16
16
  <a href="https://github.com/gizmodata/hardcharger-js"><img src="https://img.shields.io/badge/built%20on-Hardcharger%20JS-31a8db.svg" alt="Built on the Hardcharger JavaScript client"></a>
17
17
  <a href="https://github.com/gizmodata/hardcharger"><img src="https://img.shields.io/badge/server-Hardcharger-f15a24.svg" alt="Hardcharger"></a>
18
18
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg" alt="Apache 2.0 license"></a>
@@ -31,16 +31,17 @@ lets Claude Desktop, Claude Code and any other MCP client explore your schema an
31
31
  a Hardcharger server you can reach from your machine (including private networks over VPN), or,
32
32
  over Streamable HTTP, serves a whole team behind OAuth.
33
33
 
34
- It speaks [Warp SQL](https://github.com/gizmodata/warp-sql), Hardcharger's HTTP protocol, through
34
+ It speaks [Chargewire](https://github.com/gizmodata/chargewire), Hardcharger's HTTP protocol, through
35
35
  the Hardcharger JavaScript client ([`@gizmodata/hardcharger`](https://github.com/gizmodata/hardcharger-js)):
36
36
  pure JavaScript, no native driver, so one extension bundle serves every platform Claude Desktop
37
37
  runs on. It is the Hardcharger port of
38
38
  [gizmosql-mcp](https://github.com/gizmodata/gizmosql-mcp), with the same tools and settings
39
39
  (`HARDCHARGER_*` instead of `GIZMOSQL_*`; see [CHANGELOG.md](CHANGELOG.md) for what changed).
40
40
 
41
- > **Status: 2.0.0, with Hardcharger 2.0.0.** Built on `@gizmodata/hardcharger` 2.0.0 (hardcharger-js
42
- > `a6e93a2`, on `@gizmodata/warp-sql` 1.0.0, Warp SQL protocol 1.0.0); tested against
43
- > `hardcharger-server` 2.0.0-rc.1 and the image of Hardcharger's main branch.
41
+ > **Status: 2.2.0, with Hardcharger 2.3.0.** Built on `@gizmodata/hardcharger` 2.1.0 (hardcharger-js
42
+ > `1a524f6`, on `@gizmodata/chargewire` 2.0.0, chargewire-js `1ea10dd`, Chargewire protocol 1.0.0);
43
+ > tested against the image of Hardcharger's main branch and against Hardcharger 2.2.0, released
44
+ > before the protocol was renamed to Chargewire.
44
45
 
45
46
  ## 🧰 Tools
46
47
 
@@ -154,7 +155,7 @@ Each connection takes one credential:
154
155
  | Setting | Sign-in |
155
156
  | --- | --- |
156
157
  | `HARDCHARGER_USERNAME` + `HARDCHARGER_PASSWORD` | Password (any character, `:` and `@` included) |
157
- | `HARDCHARGER_TOKEN` | A signed sign-in token the server trusts (`--token-allowed-issuer` with `--token-jwks-uri` or `--token-signature-verify-cert-path`), sent with Warp SQL's `token_exchange` grant. The same token also works as the password of user `token`. Its `role` claim (`admin`, `user`, `readonly`) decides what it may do |
158
+ | `HARDCHARGER_TOKEN` | A signed sign-in token the server trusts (`--token-allowed-issuer` with `--token-jwks-uri` or `--token-signature-verify-cert-path`), sent with Chargewire's `token_exchange` grant. The same token also works as the password of user `token`. Its `role` claim (`admin`, `user`, `readonly`) decides what it may do |
158
159
  | `HARDCHARGER_MTLS_CERT` + `HARDCHARGER_MTLS_KEY` | Client certificate sign-in (mutual TLS) on a server started with `--mtls-certificate-sign-in`: the certificate is the credential. With a password or token beside them, the certificate only admits the connection |
159
160
  | `HARDCHARGER_ENABLE_SSO=true` | No stored credential: `login_sso` signs in through the server's identity provider in your browser |
160
161
 
@@ -175,7 +176,7 @@ the server's codes: `unknown_certificate_identity` (401) for a certificate that
175
176
  `HARDCHARGER_TLS_ROOTS` or `HARDCHARGER_TLS_SKIP_VERIFY`).
176
177
 
177
178
  **Browser sign-in (`login_sso`).** On a server started with `--oauth-client-id` (and its identity
178
- provider settings), `login_sso` opens the provider's login page through the server's Warp SQL
179
+ provider settings), `login_sso` opens the provider's login page through the server's Chargewire
179
180
  browser sign-in (on the connection's own URI and port), waits for you to finish, and connects with
180
181
  the Hardcharger access token the server hands out, kept in memory only; when it expires, call
181
182
  `login_sso` again. A refusal names its code (`access_denied`, `identity_provider_error`,
@@ -196,7 +197,8 @@ Microsoft Entra ID, Okta (custom authorization server), Auth0, Keycloak, Cognito
196
197
  is an OAuth 2.1 *resource server*: it verifies each bearer token against the provider's JWKS
197
198
  (issuer, audience, signature, expiry) and serves the RFC 9728 metadata at
198
199
  `/.well-known/oauth-protected-resource[/mcp]` so clients find the provider on their own. The token
199
- is never forwarded; the Hardcharger connection uses the configured service credentials.
200
+ is never forwarded; the Hardcharger connection uses the configured service credentials, or, with a
201
+ [connection broker](#-per-person-credentials-connection-broker), each person's own.
200
202
 
201
203
  <!-- example: http-oauth -->
202
204
  ```bash
@@ -236,8 +238,10 @@ connector enters the app registration's client ID and secret under *Advanced set
236
238
  `https://claude.ai/api/mcp/auth_callback`, *Expose an API* with the MCP URL as Application ID URI
237
239
  and a scope such as `access_as_user`, `requestedAccessTokenVersion` 2; v2 tokens carry the client ID
238
240
  as `aud`). **Okta** needs a custom authorization server; **Auth0** an API with the audience;
239
- **Keycloak** works out of the box; **Clerk** issues JWT access tokens by default. Providers with
240
- opaque access tokens (Google) are not supported in this mode.
241
+ **Keycloak** works out of the box; **Clerk** issues opaque access tokens unless the instance has
242
+ *JWT access tokens* turned on (checked 2026-10-30), so turn that on for this mode. Providers with
243
+ opaque access tokens (Google) are not supported in this mode; with a connection broker they are,
244
+ because the broker checks the tokens.
241
245
 
242
246
  **Static bearer token.** For a quick shared secret instead of OAuth, set
243
247
  `HARDCHARGER_MCP_BEARER_TOKEN`; every request must then send `Authorization: Bearer <token>`. The
@@ -252,6 +256,38 @@ HARDCHARGER_MCP_BEARER_TOKEN=a-long-random-secret \
252
256
  npx -y @gizmodata/hardcharger-mcp --transport http --port 3000
253
257
  ```
254
258
 
259
+ ### 🪪 Per-person credentials (connection broker)
260
+
261
+ With a connection broker, the server holds no Hardcharger credentials at all. A broker, usually
262
+ the control plane that runs your Hardcharger servers (GizmoData Cloud is one), tells it which
263
+ servers each signed-in person may reach and mints that person a short-lived sign-in token for
264
+ each one. The client exchanges the token (Chargewire token_exchange), so every statement runs
265
+ **as that person**: the server's access control (grants, row policies, masking) decides what
266
+ they may see and change, and its audit log names them. One connector serves everyone, and each
267
+ person sees only their own servers in `list_connections`.
268
+
269
+ ```bash
270
+ HARDCHARGER_MCP_PUBLIC_URL=https://mcp.example.com/mcp \
271
+ HARDCHARGER_MCP_OAUTH_ISSUER=https://login.example.com \
272
+ HARDCHARGER_MCP_BROKER_URL=https://control-plane.example.com/api/mcp-broker \
273
+ HARDCHARGER_MCP_BROKER_TOKEN=this-servers-own-broker-credential \
274
+ HARDCHARGER_ALLOW_WRITES=true \
275
+ npx -y @gizmodata/hardcharger-mcp --transport http --host 0.0.0.0 --port 3000
276
+ ```
277
+
278
+ The broker also checks each request's access token (`/introspect`), so the provider's tokens may be
279
+ opaque (Clerk's are by default) and this server needs none of the provider's keys or credentials;
280
+ `HARDCHARGER_MCP_OAUTH_ISSUER` only names the authorization server in the metadata clients read.
281
+ It needs OAuth (each request must carry a verified person) and the HTTP transport, and takes no
282
+ `HARDCHARGER_URI`: a configured connection's credentials would be shared by every user. The
283
+ person's access token is never the broker credential: the server authenticates to the broker
284
+ with `HARDCHARGER_MCP_BROKER_TOKEN` and presents the person's token as an RFC 8693 subject token.
285
+ Writes need both `HARDCHARGER_ALLOW_WRITES=true` (the server-wide ceiling) and the broker's
286
+ `allow_writes` for that person and server. Table resources are listed only for servers the
287
+ person has already used, so a resource listing never signs in to every server they can reach.
288
+ The protocol, for anyone writing a broker, is in
289
+ [docs/connection-broker.md](docs/connection-broker.md).
290
+
255
291
  ### Container image and Helm chart
256
292
 
257
293
  [`Dockerfile`](Dockerfile) builds an image whose entrypoint runs the HTTP transport on port 3000, and
@@ -299,6 +335,10 @@ Only `HARDCHARGER_*` names are read.
299
335
  | `HARDCHARGER_MCP_OAUTH_ALLOW_INSECURE` | `false` | Accept `http://` issuer and public URLs (local testing only) |
300
336
  | `HARDCHARGER_MCP_SESSION_IDLE_SECONDS` | `1800` | HTTP transport with OAuth: close a user's session after this long without a request |
301
337
  | `HARDCHARGER_MCP_MAX_SESSIONS` | `200` | HTTP transport with OAuth: cap on concurrent user sessions |
338
+ | `HARDCHARGER_MCP_BROKER_URL` | | HTTP transport with OAuth: a connection broker supplies each person's servers and sign-in tokens ([protocol](docs/connection-broker.md)); no `HARDCHARGER_URI` then |
339
+ | `HARDCHARGER_MCP_BROKER_TOKEN` | | This server's own credential at the broker (required with the URL) |
340
+ | `HARDCHARGER_MCP_BROKER_CACHE_SECONDS` | `60` | How long a person's server list is reused (`list_connections` always asks again) |
341
+ | `HARDCHARGER_MCP_BROKER_ALLOW_INSECURE` | `false` | Accept a plain `http` broker URL (local testing) |
302
342
  | `HARDCHARGER_2_URI`, `HARDCHARGER_3_URI`, … | | Additional connections, see below |
303
343
  | `HARDCHARGER_CONNECTIONS` | | Comma-separated names of further connections, see below |
304
344
 
@@ -346,13 +386,14 @@ clients ask before running it.
346
386
  The guard is defense in depth; the real boundary is the privileges of the Hardcharger user the
347
387
  server signs in as. A sign-in token with `role: readonly` can only read (Hardcharger refuses
348
388
  anything else with `permission_denied`), and under Hardcharger's access control a store user's
349
- grants decide what each table allows.
389
+ grants decide what each table allows. With a connection broker that user is the person asking,
390
+ not a shared service account.
350
391
 
351
392
  - Credentials never appear in tool output, `server_info` or error messages: every message that
352
393
  reaches the client is redacted.
353
394
  - The row cap is enforced by the server: reads run as `SELECT * FROM (<your query>) LIMIT
354
395
  max_rows + 1`, over one result stream that is closed at the cap, which stops the statement.
355
- - The timeout is the server's (Warp SQL `statementTimeoutMs`): it cancels a statement that runs too
396
+ - The timeout is the server's (Chargewire `statementTimeoutMs`): it cancels a statement that runs too
356
397
  long, `execute_statement` included, and the error says `statement_timeout`. A client-side cancel
357
398
  a few seconds later is the backstop.
358
399
  - Lost sessions: when Hardcharger evicts an idle session, the client opens a replacement with the
@@ -397,17 +438,17 @@ After changing settings, start a new chat.
397
438
 
398
439
  ## 🛠️ Building and testing
399
440
 
400
- Node 22.19 or later. For development and CI, `@gizmodata/hardcharger` and `@gizmodata/warp-sql` are
441
+ Node 22.19 or later. For development and CI, `@gizmodata/hardcharger` and `@gizmodata/chargewire` are
401
442
  built from [gizmodata/hardcharger-js](https://github.com/gizmodata/hardcharger-js) and
402
- [gizmodata/warp-sql-js](https://github.com/gizmodata/warp-sql-js) at the commits pinned in
443
+ [gizmodata/chargewire-js](https://github.com/gizmodata/chargewire-js) at the commits pinned in
403
444
  [scripts/hardcharger-js.commit](scripts/hardcharger-js.commit) and
404
- [scripts/warp-sql-js.commit](scripts/warp-sql-js.commit), and packed to `.clients/`, where
445
+ [scripts/chargewire-js.commit](scripts/chargewire-js.commit), and packed to `.clients/`, where
405
446
  `package-lock.json` finds them, so the tests run against those exact clients. `package.json`, and
406
- so the published package, names the npm package and its range (`^2.0.0`); the tarballs are a
447
+ so the published package, names the npm package and its range (`^2.1.0`); the tarballs are a
407
448
  stand-in for development and CI only (`scripts/check-lock.mjs` checks both).
408
449
 
409
450
  ```bash
410
- npm run clients # clone both clients (git access needed), build and pack the pinned commits
451
+ npm run clients # clone both clients, build and pack the pinned commits
411
452
  npm ci
412
453
  npm run lint:ci # eslint and the lock's shape
413
454
  npm run typecheck # src and tests
@@ -417,14 +458,18 @@ docker build -t hardcharger-mcp . # container image for the HTTP transport
417
458
  helm lint charts/hardcharger-mcp # Helm chart
418
459
  ```
419
460
 
420
- `HARDCHARGER_MCP_WARP_SQL_JS_REPO` and `HARDCHARGER_MCP_HARDCHARGER_JS_REPO` point `npm run clients`
421
- at local checkouts.
461
+ `HARDCHARGER_MCP_CHARGEWIRE_JS_REPO` and `HARDCHARGER_MCP_HARDCHARGER_JS_REPO` point
462
+ `npm run clients` at local checkouts.
422
463
 
423
464
  The live suites start `hardcharger-server` themselves, on free loopback ports, with a fresh
424
465
  administrator password that contains `:` and `@`, an empty home directory (DuckDB's secrets
425
466
  directory included), and only `PATH`, `HOME`, `TMPDIR` and the password in its environment; the MCP
426
467
  server under test gets `PATH`, `HOME` and the settings each test names, never your shell's. Without
427
- the binary they fail; they never skip, and a run in which no test passed fails too.
468
+ the binary they fail; they never skip, and a run in which no test passed fails too. One suite
469
+ checks a Hardcharger released before the protocol was renamed to Chargewire (the
470
+ `ghcr.io/gizmodata/hardcharger:2.2.0` image, already running, plain HTTP), named by
471
+ `HARDCHARGER_MCP_TEST_OLD_SERVER` (`host:port`, with `HARDCHARGER_MCP_TEST_OLD_SERVER_PASSWORD`);
472
+ CI starts one. Without it a full local run leaves that suite out and says so; in CI it fails.
428
473
 
429
474
  ```bash
430
475
  export HARDCHARGER_MCP_TEST_SERVER_BINARY=/path/to/hardcharger-server
@@ -439,9 +484,8 @@ Negative tests assert the error code (`invalid_credentials`, `permission_denied`
439
484
  `invalid_option_value`), each beside a positive twin. CI runs everything against the image of
440
485
  Hardcharger's main branch (`ghcr.io/gizmodata/hardcharger:main`), pulled with the workflow's
441
486
  `GITHUB_TOKEN`; while the image is private the package must grant this repository read access
442
- (package settings, "Manage Actions access"), or the live jobs fail. CI clones the clients with
443
- `DEPLOY_KEY_WARP_SQL_JS` and `DEPLOY_KEY_HARDCHARGER_JS`, read-only deploy keys of those
444
- repositories (needed while they are private).
487
+ (package settings, "Manage Actions access"), or the live jobs fail. CI clones both clients
488
+ anonymously over https.
445
489
 
446
490
  ## 📦 Releasing
447
491
 
@@ -0,0 +1,82 @@
1
+ import { z } from "zod";
2
+ import { type BrokerConfig, type McpConfig } from "./connection.js";
3
+ import type { IntrospectionResult } from "./oauth.js";
4
+ import { ConnectionRegistry } from "./registry.js";
5
+ /** RFC 8693 token type of the caller's OAuth access token. */
6
+ export declare const ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
7
+ declare const connectionSchema: z.ZodObject<{
8
+ id: z.ZodString;
9
+ name: z.ZodString;
10
+ label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
11
+ uri: z.ZodString;
12
+ default_catalog: z.ZodOptional<z.ZodNullable<z.ZodString>>;
13
+ default_schema: z.ZodOptional<z.ZodNullable<z.ZodString>>;
14
+ allow_writes: z.ZodDefault<z.ZodBoolean>;
15
+ }, z.core.$strip>;
16
+ export type BrokeredConnection = z.infer<typeof connectionSchema>;
17
+ /** A broker request that failed; `message` is safe to show the caller. */
18
+ export declare class BrokerError extends Error {
19
+ readonly status?: number | undefined;
20
+ constructor(message: string, status?: number | undefined);
21
+ }
22
+ export interface BrokerHooks {
23
+ /** Test hook: the fetch implementation. */
24
+ fetch?: typeof fetch;
25
+ /** Test hook: clock (ms). */
26
+ now?: () => number;
27
+ }
28
+ /** Talks to the broker's two endpoints. Stateless; one instance serves every caller. */
29
+ export declare class BrokerClient {
30
+ readonly config: BrokerConfig;
31
+ private readonly fetchImpl;
32
+ constructor(config: BrokerConfig, hooks?: BrokerHooks);
33
+ /** The servers the caller may reach. */
34
+ listConnections(subjectToken: string): Promise<{
35
+ connections: unknown[];
36
+ message?: string;
37
+ }>;
38
+ /**
39
+ * Whether the caller's access token is active, and whose it is: the broker verifies the tokens of
40
+ * the provider it trusts, opaque ones included, so this server needs no provider credentials.
41
+ */
42
+ introspect(subjectToken: string): Promise<IntrospectionResult>;
43
+ /** A sign-in token for the caller on one server, and its lifetime in seconds. */
44
+ issueToken(subjectToken: string, connectionId: string): Promise<{
45
+ token: string;
46
+ expiresIn: number;
47
+ }>;
48
+ private post;
49
+ }
50
+ /**
51
+ * One caller's connections, as the broker lists them. Refreshed on a request when the list is
52
+ * older than HARDCHARGER_MCP_BROKER_CACHE_SECONDS (and by list_connections at once); servers
53
+ * the broker no longer lists are closed. Each connection signs in with a token the broker
54
+ * mints for this caller.
55
+ */
56
+ export declare class BrokeredRegistry extends ConnectionRegistry {
57
+ readonly broker: BrokerClient;
58
+ private callerToken;
59
+ private listedAt;
60
+ private refreshing;
61
+ private lastProblem;
62
+ private emptyMessage;
63
+ private readonly clock;
64
+ constructor(config: McpConfig, broker: BrokerClient, log: (message: string) => void, hooks?: Pick<BrokerHooks, "now">);
65
+ now(): number;
66
+ /** The caller's current OAuth access token (verified by the transport), presented to the broker. */
67
+ subjectToken(): string;
68
+ /** Records the token of the request being served: the newest one the client sent. */
69
+ setSubjectToken(token: string): void;
70
+ /** Asks the broker again when the list is stale (or always, with `force`). Never throws. */
71
+ refresh({ force }?: {
72
+ force?: boolean;
73
+ }): Promise<void>;
74
+ private load;
75
+ /** The broker's own words when it listed nothing, or why it could not be asked. */
76
+ status(): string | undefined;
77
+ protected noConnectionsMessage(): string;
78
+ /** Only servers this caller already uses: listing every table would sign in to every server they can reach. */
79
+ resourceConnections(): import("./connection.js").HardchargerConnection[];
80
+ secrets(): Array<string | undefined>;
81
+ }
82
+ export {};