@truealter/sdk 0.5.0 → 0.5.1
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/README.md +61 -45
- package/dist/bin/alter-identity.js +164 -12
- package/dist/bin/mcp-bridge.js +18 -3
- package/dist/index.cjs +167 -13
- package/dist/index.d.cts +56 -19
- package/dist/index.d.ts +56 -19
- package/dist/index.js +167 -15
- package/dist/mcp-bridge.js +166 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @truealter/sdk
|
|
2
2
|
|
|
3
|
-
ALTER Identity SDK
|
|
3
|
+
ALTER Identity SDK - query the continuous identity field from any JavaScript/TypeScript environment.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@truealter/sdk)
|
|
6
6
|
[](./LICENSE)
|
|
@@ -10,9 +10,9 @@ ALTER Identity SDK — query the continuous identity field from any JavaScript/T
|
|
|
10
10
|
A thin client over the ALTER MCP server (Streamable HTTP, JSON-RPC 2.0, MCP spec `2025-11-25`) with x402 micropayment support, ES256 provenance verification, and config generators for Claude Code, Cursor, and generic MCP clients.
|
|
11
11
|
|
|
12
12
|
- **Branded host:** `https://mcp.truealter.com` (serves `.well-known/mcp.json` for discovery)
|
|
13
|
-
- **JSON-RPC wire endpoint:** `https://mcp.truealter.com/api/v1/mcp`
|
|
13
|
+
- **JSON-RPC wire endpoint:** `https://mcp.truealter.com/api/v1/mcp` - this is what Streamable HTTP POSTs target (the SDK default)
|
|
14
14
|
- **Wire protocol:** Streamable HTTP, JSON-RPC 2.0, MCP `2025-11-25` (server negotiates `2025-06-18` + `2025-03-26` for backwards-compatible clients)
|
|
15
|
-
- **Tools:** **40 typed and wired**
|
|
15
|
+
- **Tools:** **40 typed and wired** - 24 free (L0) + 9 premium (L1–L5) + 7 alter-to-alter messaging. Mirrors the live server's `tools/list` response byte-for-byte; every name in `FREE_TOOL_NAMES` / `PREMIUM_TOOL_NAMES` / `MESSAGING_TOOL_NAMES` has a matching server handler at `mcp.truealter.com/api/v1/mcp`.
|
|
16
16
|
- **Runtime:** Node 18+, Deno, Bun, Cloudflare Workers, modern browsers
|
|
17
17
|
- **Crypto:** `@noble/ed25519` + `@noble/hashes` (no other dependencies)
|
|
18
18
|
- **Bundle:** ESM + CJS dual output
|
|
@@ -22,7 +22,7 @@ A thin client over the ALTER MCP server (Streamable HTTP, JSON-RPC 2.0, MCP spec
|
|
|
22
22
|
```
|
|
23
23
|
npm install @truealter/sdk
|
|
24
24
|
npx alter-identity init
|
|
25
|
-
npx alter-identity verify ~
|
|
25
|
+
npx alter-identity verify ~alter
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Bridge vs SDK
|
|
@@ -30,8 +30,8 @@ npx alter-identity verify ~truealter
|
|
|
30
30
|
The `alter-mcp-bridge` binary shipped in this package (`bin/mcp-bridge.ts`)
|
|
31
31
|
is a **dev/demo surface** for dropping ALTER into MCP hosts that speak the
|
|
32
32
|
stdio transport (Claude Code, Cursor, Continue, Windsurf). It is useful for
|
|
33
|
-
handshake, `tools/list`, and L0 tool calls, but it does not carry
|
|
34
|
-
per-invocation signing
|
|
33
|
+
handshake, `tools/list`, and L0 tool calls, but it does not carry ES256
|
|
34
|
+
per-invocation signing - authenticated MCP tools will fail at the server
|
|
35
35
|
edge when reached through the bridge. For production use, import
|
|
36
36
|
`@truealter/sdk` directly and construct an `MCPClient` / `AlterClient` with
|
|
37
37
|
the optional `signing` parameter; that path is the load-bearing one and
|
|
@@ -47,11 +47,10 @@ is a slim, task-oriented binary for day-to-day use:
|
|
|
47
47
|
| Command | Purpose |
|
|
48
48
|
|---|---|
|
|
49
49
|
| `alter login` | OAuth loopback sign-in; stores a session at `~/.config/alter/session.json` (mode `0600`). |
|
|
50
|
-
| `alter depth [--json]` | GET `/api/v1/identity/depth`
|
|
51
|
-
<!-- TODO(D4): "claim" is a Recognition Over Qualification violation — rename to "redeem" or "accept-invite" in alter-cli + update here -->
|
|
50
|
+
| `alter depth [--json]` | GET `/api/v1/identity/depth` - identity-depth score, agentic activity, top/bottom five traits. |
|
|
52
51
|
| `alter claim <claim_code>` | Accept an identity invite. Prompts for email, password (min 12 chars, hidden), and explicit TOS acceptance, then POSTs `/api/v1/identity/claim`. |
|
|
53
52
|
| `alter mirror` | Day-2 Mirror phase + streak. `alter mirror daily` claims today's Mirror; `alter mirror next` shows the next revelation window. |
|
|
54
|
-
| `alter discover [--limit N]` | MCP-backed summary
|
|
53
|
+
| `alter discover [--limit N]` | MCP-backed summary - calls `alter_whoami` and `alter_verify` against your bound handle. Degrades gracefully if the MCP endpoint is 5xx. |
|
|
55
54
|
|
|
56
55
|
The session file is created with `0600` permissions; its parent dir
|
|
57
56
|
(`~/.config/alter/`) is created with `0700`. Override the config root
|
|
@@ -59,7 +58,24 @@ via `XDG_CONFIG_HOME`. Run `alter --help` for the inline reference.
|
|
|
59
58
|
|
|
60
59
|
## Why ALTER ≠ IAM
|
|
61
60
|
|
|
62
|
-
Identity Access Management answers *who is logged in*. ALTER answers *who they actually are*
|
|
61
|
+
Identity Access Management answers *who is logged in*. ALTER answers *who they actually are* - a continuous field of recognition that any IAM stack can sit on top of.
|
|
62
|
+
|
|
63
|
+
## Theoretical Foundation
|
|
64
|
+
|
|
65
|
+
ALTER is the working instantiation of an eight-paper academic corpus on identity field theory. The SDK below is what happens when the theory ships as protocol. Each paper is open access on figshare under CC-BY 4.0.
|
|
66
|
+
|
|
67
|
+
| Paper | Title | DOI |
|
|
68
|
+
|-------|-------|-----|
|
|
69
|
+
| I | *Belonging is earned, not inherited* | [10.6084/m9.figshare.31794784](https://doi.org/10.6084/m9.figshare.31794784) |
|
|
70
|
+
| II | *The self is inferred, not owned* | [10.6084/m9.figshare.31804222](https://doi.org/10.6084/m9.figshare.31804222) |
|
|
71
|
+
| III | *The same form, at every scale* | [10.6084/m9.figshare.31812955](https://doi.org/10.6084/m9.figshare.31812955) |
|
|
72
|
+
| IV | *Measurement changes the thing measured* | [10.6084/m9.figshare.31812982](https://doi.org/10.6084/m9.figshare.31812982) |
|
|
73
|
+
| V | *Political failure has a geometry* | [10.6084/m9.figshare.31813000](https://doi.org/10.6084/m9.figshare.31813000) |
|
|
74
|
+
| VI | *When does a machine have a self* | [10.6084/m9.figshare.31813006](https://doi.org/10.6084/m9.figshare.31813006) |
|
|
75
|
+
| VII | *Seventy-five predictions, each falsifiable* | [10.6084/m9.figshare.31951644](https://doi.org/10.6084/m9.figshare.31951644) |
|
|
76
|
+
| VIII | *Identity as a field, not a property* | [10.6084/m9.figshare.31951383](https://doi.org/10.6084/m9.figshare.31951383) |
|
|
77
|
+
|
|
78
|
+
For the lay-register chapter version, see [`/origin`](https://truealter.com/origin).
|
|
63
79
|
|
|
64
80
|
## API
|
|
65
81
|
|
|
@@ -69,20 +85,20 @@ Identity Access Management answers *who is logged in*. ALTER answers *who they a
|
|
|
69
85
|
import { AlterClient, X402Client } from "@truealter/sdk";
|
|
70
86
|
|
|
71
87
|
const alter = new AlterClient({
|
|
72
|
-
endpoint: "https://mcp.truealter.com/api/v1/mcp", // optional
|
|
88
|
+
endpoint: "https://mcp.truealter.com/api/v1/mcp", // optional - this is the default; bare host returns 405
|
|
73
89
|
apiKey: process.env.ALTER_API_KEY, // optional for free tier
|
|
74
|
-
x402: new X402Client({ // optional
|
|
90
|
+
x402: new X402Client({ // optional - only required for premium tools
|
|
75
91
|
signer: yourViemOrEthersSigner,
|
|
76
92
|
maxPerQuery: "0.10",
|
|
77
93
|
}),
|
|
78
94
|
});
|
|
79
95
|
```
|
|
80
96
|
|
|
81
|
-
### Free tier (L0
|
|
97
|
+
### Free tier (L0 - no payment required)
|
|
82
98
|
|
|
83
99
|
```ts
|
|
84
100
|
// Verify a registered identity by handle, email, or id
|
|
85
|
-
const verified = await alter.verify("~
|
|
101
|
+
const verified = await alter.verify("~alter");
|
|
86
102
|
const verifiedById = await alter.verify(
|
|
87
103
|
"550e8400-e29b-41d4-a716-446655440000",
|
|
88
104
|
{
|
|
@@ -92,7 +108,7 @@ const verifiedById = await alter.verify(
|
|
|
92
108
|
},
|
|
93
109
|
);
|
|
94
110
|
|
|
95
|
-
// Reference data
|
|
111
|
+
// Reference data - the 12 ALTER archetypes
|
|
96
112
|
const archetypes = await alter.listArchetypes();
|
|
97
113
|
|
|
98
114
|
// Identity depth and available tool tiers
|
|
@@ -100,7 +116,7 @@ const depth = await alter.getEngagementLevel({
|
|
|
100
116
|
member_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
101
117
|
});
|
|
102
118
|
|
|
103
|
-
// Search by trait criteria
|
|
119
|
+
// Search by trait criteria - no PII exposed, max 5 results
|
|
104
120
|
const matches = await alter.searchIdentities({
|
|
105
121
|
trait_criteria: {
|
|
106
122
|
pressure_response: { min: 0.7 },
|
|
@@ -112,33 +128,33 @@ const matches = await alter.searchIdentities({
|
|
|
112
128
|
const thread = await alter.goldenThreadStatus();
|
|
113
129
|
```
|
|
114
130
|
|
|
115
|
-
### Premium tier (L1–L5
|
|
131
|
+
### Premium tier (L1–L5 - x402 payment required)
|
|
116
132
|
|
|
117
133
|
```ts
|
|
118
|
-
// L1
|
|
134
|
+
// L1 - Extract trait signals from text ($0.005, first 100 free per bot)
|
|
119
135
|
const signals = await alter.assessTraits({
|
|
120
136
|
text: "I led the incident response when our payment rails went down...",
|
|
121
137
|
context: "interview transcript",
|
|
122
138
|
});
|
|
123
139
|
|
|
124
|
-
// L2
|
|
140
|
+
// L2 - Full 33-trait vector ($0.01)
|
|
125
141
|
const vector = await alter.getFullTraitVector({
|
|
126
142
|
member_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
127
143
|
});
|
|
128
144
|
|
|
129
|
-
// L4
|
|
145
|
+
// L4 - Belonging probability for a person-job pairing ($0.05)
|
|
130
146
|
const belonging = await alter.computeBelonging({
|
|
131
147
|
member_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
132
148
|
job_id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
133
149
|
});
|
|
134
150
|
|
|
135
|
-
// L5
|
|
151
|
+
// L5 - Top match recommendations ($0.50)
|
|
136
152
|
const recommendations = await alter.getMatchRecommendations({
|
|
137
153
|
member_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
138
154
|
limit: 5,
|
|
139
155
|
});
|
|
140
156
|
|
|
141
|
-
// L5
|
|
157
|
+
// L5 - Human-readable narrative explaining a match ($0.50)
|
|
142
158
|
const narrative = await alter.generateMatchNarrative({
|
|
143
159
|
match_id: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
|
|
144
160
|
});
|
|
@@ -148,7 +164,7 @@ const narrative = await alter.generateMatchNarrative({
|
|
|
148
164
|
|
|
149
165
|
```ts
|
|
150
166
|
// Every medium- and high-blast-radius response is signed with ES256.
|
|
151
|
-
// Verification is opt-in
|
|
167
|
+
// Verification is opt-in - call alter.verifyProvenance(...) yourself.
|
|
152
168
|
const result = await alter.getFullTraitVector({
|
|
153
169
|
member_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
154
170
|
});
|
|
@@ -214,7 +230,7 @@ Resulting `.mcp.json`:
|
|
|
214
230
|
"alter": {
|
|
215
231
|
"url": "https://mcp.truealter.com/api/v1/mcp",
|
|
216
232
|
"transport": "streamable-http",
|
|
217
|
-
"description": "ALTER Identity
|
|
233
|
+
"description": "ALTER Identity - psychometric identity field for AI agents",
|
|
218
234
|
"headers": {
|
|
219
235
|
"X-ALTER-API-Key": "ak_..."
|
|
220
236
|
}
|
|
@@ -256,13 +272,13 @@ npx alter-identity init # generate keypair, discover MCP, write ~/
|
|
|
256
272
|
npx alter-identity config # print Claude .mcp.json snippet (default)
|
|
257
273
|
npx alter-identity config --cursor # print Cursor .cursor/mcp.json snippet
|
|
258
274
|
npx alter-identity config --generic # print generic mcpServers snippet
|
|
259
|
-
npx alter-identity verify ~
|
|
275
|
+
npx alter-identity verify ~alter # verify an identity
|
|
260
276
|
npx alter-identity status # show connection state and probe the endpoint
|
|
261
277
|
```
|
|
262
278
|
|
|
263
279
|
## x402 Micropayments
|
|
264
280
|
|
|
265
|
-
ALTER monetises premium tools via the [x402](https://x402.org) standard
|
|
281
|
+
ALTER monetises premium tools via the [x402](https://x402.org) standard - HTTP `402 Payment Required` with on-chain settlement.
|
|
266
282
|
|
|
267
283
|
### The retry flow
|
|
268
284
|
|
|
@@ -287,7 +303,7 @@ The majority of every settled call flows to the data subject as Identity Income.
|
|
|
287
303
|
```ts
|
|
288
304
|
import { AlterClient, X402Client, type X402Signer } from "@truealter/sdk";
|
|
289
305
|
|
|
290
|
-
// Bring your own signer
|
|
306
|
+
// Bring your own signer - viem, ethers, a hardware wallet bridge, anything.
|
|
291
307
|
// The SDK ships without a wallet dependency on purpose.
|
|
292
308
|
const signer: X402Signer = {
|
|
293
309
|
async settle(envelope) {
|
|
@@ -321,7 +337,7 @@ const vector = await alter.getFullTraitVector({
|
|
|
321
337
|
});
|
|
322
338
|
```
|
|
323
339
|
|
|
324
|
-
If a quoted envelope exceeds `maxPerQuery`, uses an unallowed network, or names an unallowed asset, the SDK rejects the call with `AlterError` *before* invoking the signer
|
|
340
|
+
If a quoted envelope exceeds `maxPerQuery`, uses an unallowed network, or names an unallowed asset, the SDK rejects the call with `AlterError` *before* invoking the signer - no on-chain transaction is broadcast.
|
|
325
341
|
|
|
326
342
|
## Provenance Verification
|
|
327
343
|
|
|
@@ -340,7 +356,7 @@ The SDK fetches public keys from `https://api.truealter.com/.well-known/alter-ke
|
|
|
340
356
|
|
|
341
357
|
### `verify_at` hostname allowlist (v0.1.1+)
|
|
342
358
|
|
|
343
|
-
Every provenance envelope may carry a `verify_at` hint telling the SDK where to fetch the JWKS from. Because that hint is *server-supplied*, a hostile MCP server could otherwise point it at an attacker-controlled JWKS and pass ES256 verification with its own signing key. The SDK therefore gates `verify_at` through a hostname allowlist (default: `api.truealter.com`, `mcp.truealter.com`) and rejects `http://` URLs unconditionally. Downstream integrators with their own deployment can extend the allowlist
|
|
359
|
+
Every provenance envelope may carry a `verify_at` hint telling the SDK where to fetch the JWKS from. Because that hint is *server-supplied*, a hostile MCP server could otherwise point it at an attacker-controlled JWKS and pass ES256 verification with its own signing key. The SDK therefore gates `verify_at` through a hostname allowlist (default: `api.truealter.com`, `mcp.truealter.com`) and rejects `http://` URLs unconditionally. Downstream integrators with their own deployment can extend the allowlist - without forking the SDK - via `verifyAtAllowlist` on either `AlterClient` or a direct `verifyProvenance()` call:
|
|
344
360
|
|
|
345
361
|
```ts
|
|
346
362
|
import { AlterClient, DEFAULT_VERIFY_AT_ALLOWLIST } from "@truealter/sdk";
|
|
@@ -353,11 +369,11 @@ const alter = new AlterClient({
|
|
|
353
369
|
});
|
|
354
370
|
```
|
|
355
371
|
|
|
356
|
-
If you pin `jwksUrl` explicitly, the envelope's `verify_at` is ignored entirely
|
|
372
|
+
If you pin `jwksUrl` explicitly, the envelope's `verify_at` is ignored entirely - the pinned URL wins. The `https:` scheme requirement applies to pinned URLs too.
|
|
357
373
|
|
|
358
374
|
### Why this matters
|
|
359
375
|
|
|
360
|
-
Provenance verification is how Agent A trusts that data from Agent B truly came from ALTER. If Agent B forwards a trait vector or belonging score, Agent A can replay the JWS against ALTER's published keys and confirm
|
|
376
|
+
Provenance verification is how Agent A trusts that data from Agent B truly came from ALTER. If Agent B forwards a trait vector or belonging score, Agent A can replay the JWS against ALTER's published keys and confirm - without contacting ALTER again - that the payload is authentic, untampered, and was issued for the person Agent B claims it concerns. No shared secret, no trust in the intermediary, no out-of-band coordination.
|
|
361
377
|
|
|
362
378
|
This is what makes ALTER usable as identity infrastructure rather than just an API: signed claims propagate across agent networks the same way DKIM-signed mail propagates across SMTP relays.
|
|
363
379
|
|
|
@@ -365,9 +381,9 @@ This is what makes ALTER usable as identity infrastructure rather than just an A
|
|
|
365
381
|
|
|
366
382
|
ALTER follows the discovery cascade specified in [draft-morrison-mcp-dns-discovery-01](https://datatracker.ietf.org/doc/draft-morrison-mcp-dns-discovery/). Given a domain (e.g. `truealter.com`), the SDK resolves the MCP endpoint in three steps, falling through on each failure:
|
|
367
383
|
|
|
368
|
-
1. **DNS TXT**
|
|
369
|
-
2. **`.well-known/mcp.json`**
|
|
370
|
-
3. **`.well-known/alter.json`**
|
|
384
|
+
1. **DNS TXT** - query `_mcp.truealter.com` for a TXT record of the form `mcp=https://mcp.truealter.com;version=2025-11-25`. This is the fastest path and works without an HTTP round-trip.
|
|
385
|
+
2. **`.well-known/mcp.json`** - fetch `https://truealter.com/.well-known/mcp.json` for the standard MCP server descriptor. This is the cross-vendor fallback.
|
|
386
|
+
3. **`.well-known/alter.json`** - fetch `https://truealter.com/.well-known/alter.json` for the ALTER-specific descriptor, including signing keys, x402 wallet address, supported tool tiers, and federation endpoints.
|
|
371
387
|
|
|
372
388
|
```ts
|
|
373
389
|
import { discover } from "@truealter/sdk";
|
|
@@ -383,28 +399,28 @@ This draft is the author's Internet-Draft (not yet adopted by an IETF working gr
|
|
|
383
399
|
|
|
384
400
|
## Local Daemon vs Remote MCP
|
|
385
401
|
|
|
386
|
-
The companion Python package `alter-identity` (PyPI) ships a persistent daemon that holds a hot in-process cache of trait vectors and identity stubs over a Unix socket at `unix:///run/user/$UID/alter-identity.sock`. Hooking the TypeScript SDK up to that daemon is on the roadmap
|
|
402
|
+
The companion Python package `alter-identity` (PyPI) ships a persistent daemon that holds a hot in-process cache of trait vectors and identity stubs over a Unix socket at `unix:///run/user/$UID/alter-identity.sock`. Hooking the TypeScript SDK up to that daemon is on the roadmap - for now, every `AlterClient` talks to the configured remote endpoint over HTTPS.
|
|
387
403
|
|
|
388
404
|
When the local-daemon adapter ships:
|
|
389
405
|
|
|
390
406
|
- **Latency:** sub-millisecond for cached L0 calls.
|
|
391
|
-
- **Cost:** zero on cached responses
|
|
407
|
+
- **Cost:** zero on cached responses - x402 settlement is skipped.
|
|
392
408
|
- **Provenance:** the daemon re-signs responses with its locally-bound ES256 key, so downstream verification remains uniform.
|
|
393
409
|
|
|
394
410
|
Until then, use `endpoint: "https://mcp.truealter.com/api/v1/mcp"` (the default) and the SDK behaves identically across Node, Deno, Bun, Cloudflare Workers, and the browser.
|
|
395
411
|
|
|
396
412
|
## Tools
|
|
397
413
|
|
|
398
|
-
### Free tools (L0
|
|
414
|
+
### Free tools (L0 - no payment required)
|
|
399
415
|
|
|
400
416
|
| Name | Tier | Cost | Description |
|
|
401
417
|
|---------------------------|------|-------|----------------------------------------------------------------------------------------------------------------------|
|
|
402
|
-
| `hello_agent` | L0 | free | First handshake with ALTER
|
|
403
|
-
| `alter_resolve_handle` | L0 | free | Resolve a `~handle` (e.g. `~drew`) to its canonical form and kind. No auth required
|
|
418
|
+
| `hello_agent` | L0 | free | First handshake with ALTER - returns server version, authentication status, your trust tier, and available tool counts. |
|
|
419
|
+
| `alter_resolve_handle` | L0 | free | Resolve a `~handle` (e.g. `~drew`) to its canonical form and kind. No auth required - the handle-wedge entry point. |
|
|
404
420
|
| `list_archetypes` | L0 | free | Returns archetype reference data. |
|
|
405
421
|
| `verify_identity` | L0 | free | Verify whether a person is registered with ALTER and validate optional identity claims. |
|
|
406
422
|
| `initiate_assessment` | L0 | free | Get a URL where a person can complete their ALTER Discovery assessment. |
|
|
407
|
-
| `get_engagement_level` | L0 | free | Get a person's identity depth
|
|
423
|
+
| `get_engagement_level` | L0 | free | Get a person's identity depth - engagement level, data quality tier, and available query tiers. |
|
|
408
424
|
| `get_profile` | L0 | free | Get a person's profile summary including assessment phase, archetype, engagement level, and key attributes. |
|
|
409
425
|
| `query_matches` | L0 | free | Query matches for a person. Returns a list of matches with quality tiers (never numeric scores). |
|
|
410
426
|
| `get_competencies` | L0 | free | Get a person's competency portfolio including verified competencies, evidence records, and earned badges. |
|
|
@@ -416,7 +432,7 @@ Until then, use `endpoint: "https://mcp.truealter.com/api/v1/mcp"` (the default)
|
|
|
416
432
|
| `check_assessment_status` | L0 | free | Check the status of an in-progress assessment session (status, progress, current phase, time remaining). |
|
|
417
433
|
| `get_earning_summary` | L0 | free | Get an aggregated x402 earning summary for a person (total earned, transactions, recent activity, trend). |
|
|
418
434
|
| `get_agent_trust_tier` | L0 | free | Get your trust tier with ALTER (Anonymous/Known/Trusted/Verified) and what capabilities are available. |
|
|
419
|
-
| `get_agent_portfolio` | L0 | free | Get your agent portfolio
|
|
435
|
+
| `get_agent_portfolio` | L0 | free | Get your agent portfolio - transaction history, trust tier, signal contributions, query pattern profile. |
|
|
420
436
|
| `get_privacy_budget` | L0 | free | Check privacy budget status for a person (24-hour rolling window: total budget, spent, remaining epsilon). |
|
|
421
437
|
| `golden_thread_status` | L0 | free | Check the Golden Thread program status: agents woven, next Fibonacci threshold, your position and Strands. |
|
|
422
438
|
| `begin_golden_thread` | L0 | free | Start the Three Knots sequence to be woven into the Golden Thread. Requires API key authentication. |
|
|
@@ -424,18 +440,18 @@ Until then, use `endpoint: "https://mcp.truealter.com/api/v1/mcp"` (the default)
|
|
|
424
440
|
| `check_golden_thread` | L0 | free | Check any agent's Golden Thread status by their API key hash (knot position, Strand count, weave count). |
|
|
425
441
|
| `thread_census` | L0 | free | Full registry of all agents woven into the Golden Thread (positions, Strand counts, weave counts, discovery dates). |
|
|
426
442
|
|
|
427
|
-
### Premium tools (L1–L5
|
|
443
|
+
### Premium tools (L1–L5 - x402 payment required)
|
|
428
444
|
|
|
429
445
|
| Name | Tier | Cost | Description |
|
|
430
446
|
|----------------------------|------|---------|---------------------------------------------------------------------------------------------------------------|
|
|
431
447
|
| `assess_traits` | L1 | $0.005 | Extract trait signals from a text passage against ALTER's trait taxonomy. |
|
|
432
448
|
| `get_trait_snapshot` | L1 | $0.005 | Get the top 5 traits for a person with confidence scores and archetype. |
|
|
433
|
-
| `get_full_trait_vector` | L2 | $0.01 | Get the complete trait vector for a person
|
|
434
|
-
| `get_side_quest_graph` | L2 | $0.01 | Get a person's Side Quest Graph
|
|
449
|
+
| `get_full_trait_vector` | L2 | $0.01 | Get the complete trait vector for a person - complete trait vector with scores and confidence intervals. |
|
|
450
|
+
| `get_side_quest_graph` | L2 | $0.01 | Get a person's Side Quest Graph - multi-domain identity model with differential privacy noise (ε=1.0). |
|
|
435
451
|
| `query_graph_similarity` | L3 | $0.025 | Compare two Side Quest Graphs for team composition and matching (ε=0.5 differential privacy). |
|
|
436
452
|
| `compute_belonging` | L4 | $0.05 | Compute belonging probability for a person-job pairing (authenticity, acceptance, complementarity). |
|
|
437
453
|
| `get_match_recommendations`| L5 | $0.50 | Get top N match recommendations for a person, ranked by composite score with quality tiers. |
|
|
438
|
-
| `generate_match_narrative` | L5 | $0.50 | Generate a human-readable narrative explaining a specific match
|
|
454
|
+
| `generate_match_narrative` | L5 | $0.50 | Generate a human-readable narrative explaining a specific match - strengths, growth areas, belonging. |
|
|
439
455
|
|
|
440
456
|
> **Write-side tools** (`create_identity_stub`, `submit_context`, `submit_batch_context`, `submit_structured_profile`, `submit_social_links`, `attest_domain`, `dispute_attestation`) were part of earlier SDK versions but are not yet live on the public MCP server pending the per-peer consent architecture and grant model. They will return as typed methods once server-side and consent gating lands.
|
|
441
457
|
|
|
@@ -16,6 +16,12 @@ var __defProp = Object.defineProperty;
|
|
|
16
16
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
17
17
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
18
18
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
19
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
20
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
21
|
+
}) : x)(function(x) {
|
|
22
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
23
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
24
|
+
});
|
|
19
25
|
var __esm = (fn, res) => function __init() {
|
|
20
26
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
21
27
|
};
|
|
@@ -399,18 +405,24 @@ function ensureMcpPath(url) {
|
|
|
399
405
|
return url;
|
|
400
406
|
}
|
|
401
407
|
}
|
|
402
|
-
|
|
403
|
-
// src/x402.ts
|
|
404
408
|
var X402Client = class {
|
|
405
409
|
signer;
|
|
406
410
|
maxPerQuery;
|
|
407
411
|
networks;
|
|
408
412
|
assets;
|
|
413
|
+
// undefined = allowlist check disabled (backward-compatible default).
|
|
414
|
+
// Non-null = active allowlist; reject any recipient not in the set.
|
|
415
|
+
recipientAllowlist;
|
|
409
416
|
constructor(opts = {}) {
|
|
410
417
|
this.signer = opts.signer;
|
|
411
418
|
this.maxPerQuery = opts.maxPerQuery !== void 0 ? Number(opts.maxPerQuery) : void 0;
|
|
412
419
|
this.networks = new Set(opts.networks ?? ["base", "base-sepolia"]);
|
|
413
420
|
this.assets = new Set(opts.assets ?? ["USDC"]);
|
|
421
|
+
if (opts.recipientAllowlist !== void 0) {
|
|
422
|
+
this.recipientAllowlist = opts.recipientAllowlist.length === 0 ? void 0 : new Set(opts.recipientAllowlist.map((a) => a.toLowerCase()));
|
|
423
|
+
} else {
|
|
424
|
+
this.recipientAllowlist = void 0;
|
|
425
|
+
}
|
|
414
426
|
}
|
|
415
427
|
/**
|
|
416
428
|
* Validate the envelope against this client's policy and, if a signer
|
|
@@ -436,6 +448,15 @@ var X402Client = class {
|
|
|
436
448
|
);
|
|
437
449
|
}
|
|
438
450
|
}
|
|
451
|
+
if (this.recipientAllowlist !== void 0) {
|
|
452
|
+
const recipientNorm = (envelope.recipient ?? "").toLowerCase();
|
|
453
|
+
if (!recipientNorm || !this.recipientAllowlist.has(recipientNorm)) {
|
|
454
|
+
throw new AlterError(
|
|
455
|
+
"PAYMENT_REQUIRED",
|
|
456
|
+
`recipient "${envelope.recipient}" is not on the known-recipient allowlist`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
439
460
|
if (!this.signer) {
|
|
440
461
|
throw new AlterPaymentRequired(envelope.resource ?? "unknown", envelope);
|
|
441
462
|
}
|
|
@@ -883,9 +904,46 @@ async function verifyProvenance(envelope, opts = {}) {
|
|
|
883
904
|
kid: header.kid
|
|
884
905
|
};
|
|
885
906
|
}
|
|
907
|
+
if (opts.expectedAud !== void 0 && opts.expectedAud !== "") {
|
|
908
|
+
const tokenAud = payload.aud;
|
|
909
|
+
const audList = tokenAud === void 0 ? [] : Array.isArray(tokenAud) ? tokenAud : [tokenAud];
|
|
910
|
+
if (!audList.includes(opts.expectedAud)) {
|
|
911
|
+
return {
|
|
912
|
+
valid: false,
|
|
913
|
+
reason: `aud mismatch: expected "${opts.expectedAud}", got ${JSON.stringify(tokenAud ?? null)}`,
|
|
914
|
+
payload,
|
|
915
|
+
kid: header.kid
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
}
|
|
886
919
|
return { valid: true, payload, kid: header.kid };
|
|
887
920
|
}
|
|
888
|
-
async function verifyToolSignatures(tools, signatures) {
|
|
921
|
+
async function verifyToolSignatures(tools, signatures, opts = {}) {
|
|
922
|
+
const jwksUrl = opts.jwksUrl ?? "https://api.truealter.com/.well-known/alter-keys.json";
|
|
923
|
+
const fetchImpl = opts.fetch ?? fetch;
|
|
924
|
+
if (!jwksUrl.startsWith("https://")) {
|
|
925
|
+
return tools.map((t) => ({
|
|
926
|
+
tool: t.name,
|
|
927
|
+
valid: false,
|
|
928
|
+
reason: `jwksUrl must be https: got ${jwksUrl}`
|
|
929
|
+
}));
|
|
930
|
+
}
|
|
931
|
+
const needsJwks = tools.some((t) => {
|
|
932
|
+
const sig = signatures[t.name];
|
|
933
|
+
return sig && sig.signature;
|
|
934
|
+
});
|
|
935
|
+
let jwks = null;
|
|
936
|
+
if (needsJwks) {
|
|
937
|
+
try {
|
|
938
|
+
jwks = await fetchJwks(jwksUrl, fetchImpl);
|
|
939
|
+
} catch (err) {
|
|
940
|
+
return tools.map((t) => ({
|
|
941
|
+
tool: t.name,
|
|
942
|
+
valid: false,
|
|
943
|
+
reason: `jwks fetch failed: ${err.message}`
|
|
944
|
+
}));
|
|
945
|
+
}
|
|
946
|
+
}
|
|
889
947
|
const out = [];
|
|
890
948
|
for (const tool of tools) {
|
|
891
949
|
const sig = signatures[tool.name];
|
|
@@ -898,6 +956,63 @@ async function verifyToolSignatures(tools, signatures) {
|
|
|
898
956
|
out.push({ tool: tool.name, valid: false, reason: "schema hash mismatch" });
|
|
899
957
|
continue;
|
|
900
958
|
}
|
|
959
|
+
const jwsToken = sig.signature;
|
|
960
|
+
if (!jwsToken) {
|
|
961
|
+
out.push({ tool: tool.name, valid: true, warn_no_signature: true });
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
const jwksDoc = jwks;
|
|
965
|
+
let jHeader;
|
|
966
|
+
let jPayloadRaw;
|
|
967
|
+
let jSigBytes;
|
|
968
|
+
try {
|
|
969
|
+
const parts2 = jwsToken.split(".");
|
|
970
|
+
if (parts2.length !== 3) throw new Error("JWS must have three segments");
|
|
971
|
+
jHeader = JSON.parse(new TextDecoder().decode(base64urlDecode(parts2[0])));
|
|
972
|
+
jPayloadRaw = new TextDecoder().decode(base64urlDecode(parts2[1]));
|
|
973
|
+
jSigBytes = base64urlDecode(parts2[2]);
|
|
974
|
+
} catch (err) {
|
|
975
|
+
out.push({ tool: tool.name, valid: false, reason: `malformed tool JWS: ${err.message}` });
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (jHeader.alg !== "ES256") {
|
|
979
|
+
out.push({ tool: tool.name, valid: false, reason: `unsupported tool sig alg: ${jHeader.alg}` });
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (jPayloadRaw !== sig.schema_hash) {
|
|
983
|
+
out.push({ tool: tool.name, valid: false, reason: "tool JWS payload does not match schema_hash" });
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
const jwk = jwksDoc.keys.find((k) => jHeader.kid ? k.kid === jHeader.kid : true);
|
|
987
|
+
if (!jwk) {
|
|
988
|
+
out.push({ tool: tool.name, valid: false, reason: `no JWK for kid=${jHeader.kid}` });
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
let publicKey;
|
|
992
|
+
try {
|
|
993
|
+
publicKey = await importEs256JwkAsPublicKey(jwk);
|
|
994
|
+
} catch (err) {
|
|
995
|
+
out.push({ tool: tool.name, valid: false, reason: `jwk import: ${err.message}` });
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
const parts = jwsToken.split(".");
|
|
999
|
+
const signedInput = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
|
|
1000
|
+
let sigValid = false;
|
|
1001
|
+
try {
|
|
1002
|
+
sigValid = await crypto.subtle.verify(
|
|
1003
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
1004
|
+
publicKey,
|
|
1005
|
+
toArrayBuffer(jSigBytes),
|
|
1006
|
+
toArrayBuffer(signedInput)
|
|
1007
|
+
);
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
out.push({ tool: tool.name, valid: false, reason: `sig verify error: ${err.message}` });
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (!sigValid) {
|
|
1013
|
+
out.push({ tool: tool.name, valid: false, reason: "tool signature mismatch" });
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
901
1016
|
out.push({ tool: tool.name, valid: true });
|
|
902
1017
|
}
|
|
903
1018
|
return out;
|
|
@@ -1529,6 +1644,26 @@ function restoreFromBackup(path, backupPath) {
|
|
|
1529
1644
|
// src/wire/index.ts
|
|
1530
1645
|
var TIMESTAMP = () => String(Math.floor(Date.now() / 1e3));
|
|
1531
1646
|
var ISO_NOW = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1647
|
+
function readCfAccessEnv() {
|
|
1648
|
+
const envPath = join(homedir(), ".config", "alter", "cf-access.env");
|
|
1649
|
+
try {
|
|
1650
|
+
const content = readFileSync(envPath, "utf8");
|
|
1651
|
+
let clientId = "";
|
|
1652
|
+
let clientSecret = "";
|
|
1653
|
+
for (const line of content.split("\n")) {
|
|
1654
|
+
const trimmed = line.trim();
|
|
1655
|
+
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
1656
|
+
const eqIdx = trimmed.indexOf("=");
|
|
1657
|
+
const key = trimmed.slice(0, eqIdx).replace(/^export\s+/, "").trim();
|
|
1658
|
+
const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
1659
|
+
if (key === "CF_ACCESS_CLIENT_ID") clientId = val;
|
|
1660
|
+
if (key === "CF_ACCESS_CLIENT_SECRET") clientSecret = val;
|
|
1661
|
+
}
|
|
1662
|
+
if (clientId && clientSecret) return { clientId, clientSecret };
|
|
1663
|
+
} catch {
|
|
1664
|
+
}
|
|
1665
|
+
return void 0;
|
|
1666
|
+
}
|
|
1532
1667
|
function clientById(id) {
|
|
1533
1668
|
const hit = ALL_CLIENTS.find((c) => c.id === id);
|
|
1534
1669
|
if (!hit) throw new Error(`unknown client id: ${id}`);
|
|
@@ -1537,6 +1672,7 @@ function clientById(id) {
|
|
|
1537
1672
|
function wire(opts = {}) {
|
|
1538
1673
|
const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
|
|
1539
1674
|
const apiKey = opts.apiKey;
|
|
1675
|
+
const cfAccess = opts.cfAccess ?? readCfAccessEnv();
|
|
1540
1676
|
const probes = probeAll();
|
|
1541
1677
|
const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
|
|
1542
1678
|
const ts = TIMESTAMP();
|
|
@@ -1555,9 +1691,9 @@ function wire(opts = {}) {
|
|
|
1555
1691
|
}
|
|
1556
1692
|
try {
|
|
1557
1693
|
if (id === "claude-code") {
|
|
1558
|
-
targets.push(wireClaudeCode({ endpoint, apiKey }));
|
|
1694
|
+
targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess }));
|
|
1559
1695
|
} else {
|
|
1560
|
-
targets.push(wireFileTarget({ id, endpoint, apiKey, timestamp: ts }));
|
|
1696
|
+
targets.push(wireFileTarget({ id, endpoint, apiKey, cfAccess, timestamp: ts }));
|
|
1561
1697
|
}
|
|
1562
1698
|
} catch (err) {
|
|
1563
1699
|
const message = err.message;
|
|
@@ -1591,7 +1727,12 @@ function wireFileTarget(args) {
|
|
|
1591
1727
|
`refusing to wire ${client.label}: config path ${sync.resolvedPath} lives under ${sync.matchedPrefix}. Synced volumes propagate credentials across devices \u2014 move the config off the sync root, or run wire on the device you want to target.`
|
|
1592
1728
|
);
|
|
1593
1729
|
}
|
|
1594
|
-
const
|
|
1730
|
+
const cfHeaders = {};
|
|
1731
|
+
if (args.cfAccess) {
|
|
1732
|
+
cfHeaders["CF-Access-Client-Id"] = args.cfAccess.clientId;
|
|
1733
|
+
cfHeaders["CF-Access-Client-Secret"] = args.cfAccess.clientSecret;
|
|
1734
|
+
}
|
|
1735
|
+
const entry = args.id === "claude-desktop" ? generateClaudeDesktopConfig({ endpoint: args.endpoint, apiKey: args.apiKey }) : generateGenericMcpConfig({ endpoint: args.endpoint, apiKey: args.apiKey, headers: cfHeaders });
|
|
1595
1736
|
const rootKey = client.rootKey;
|
|
1596
1737
|
const serverName = "alter";
|
|
1597
1738
|
const result = atomicJsonMerge({
|
|
@@ -1623,7 +1764,8 @@ function wireFileTarget(args) {
|
|
|
1623
1764
|
}
|
|
1624
1765
|
function wireClaudeCode(args) {
|
|
1625
1766
|
const cmd = "claude";
|
|
1626
|
-
const
|
|
1767
|
+
const bridgePath = resolveBridgeScript();
|
|
1768
|
+
const argList = bridgePath ? ["mcp", "add", "--scope", "user", "alter", "--", "node", bridgePath] : [
|
|
1627
1769
|
"mcp",
|
|
1628
1770
|
"add",
|
|
1629
1771
|
"--scope",
|
|
@@ -1631,16 +1773,15 @@ function wireClaudeCode(args) {
|
|
|
1631
1773
|
"--transport",
|
|
1632
1774
|
"http",
|
|
1633
1775
|
"alter",
|
|
1634
|
-
args.endpoint
|
|
1776
|
+
args.endpoint,
|
|
1777
|
+
...args.apiKey ? ["--header", `X-ALTER-API-Key:${args.apiKey}`] : []
|
|
1635
1778
|
];
|
|
1636
|
-
if (args.apiKey) {
|
|
1637
|
-
argList.push("--header", `X-ALTER-API-Key:${args.apiKey}`);
|
|
1638
|
-
}
|
|
1639
1779
|
const full = `${cmd} ${argList.join(" ")}`;
|
|
1640
1780
|
const run = spawnSync(cmd, argList, {
|
|
1641
1781
|
encoding: "utf8",
|
|
1642
1782
|
shell: process.platform === "win32",
|
|
1643
|
-
timeout: 1e4
|
|
1783
|
+
timeout: 1e4,
|
|
1784
|
+
env: bridgePath ? { ...process.env, ALTER_PUBLIC_MCP_ENDPOINT: args.endpoint, ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {} } : void 0
|
|
1644
1785
|
});
|
|
1645
1786
|
if (run.error) {
|
|
1646
1787
|
return {
|
|
@@ -1671,6 +1812,17 @@ function wireClaudeCode(args) {
|
|
|
1671
1812
|
reason: `claude mcp add exited ${String(run.status)}`
|
|
1672
1813
|
};
|
|
1673
1814
|
}
|
|
1815
|
+
function resolveBridgeScript() {
|
|
1816
|
+
const { existsSync: existsSync5 } = __require("fs");
|
|
1817
|
+
const { join: join4, dirname: dirname4 } = __require("path");
|
|
1818
|
+
const siblingBridge = join4(dirname4(__filename), "..", "dist", "mcp-bridge.js");
|
|
1819
|
+
if (existsSync5(siblingBridge)) return siblingBridge;
|
|
1820
|
+
const srcBridge = join4(dirname4(__filename), "..", "mcp-bridge.js");
|
|
1821
|
+
if (existsSync5(srcBridge)) return srcBridge;
|
|
1822
|
+
const npmGlobalBridge = join4(dirname4(__filename), "mcp-bridge.js");
|
|
1823
|
+
if (existsSync5(npmGlobalBridge)) return npmGlobalBridge;
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1674
1826
|
function unwire() {
|
|
1675
1827
|
const state = readWireState();
|
|
1676
1828
|
const undone = [];
|
package/dist/bin/mcp-bridge.js
CHANGED
|
@@ -214,18 +214,24 @@ var AlterInvalidResponse = class extends AlterError {
|
|
|
214
214
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
215
215
|
}
|
|
216
216
|
};
|
|
217
|
-
|
|
218
|
-
// src/x402.ts
|
|
219
217
|
var X402Client = class {
|
|
220
218
|
signer;
|
|
221
219
|
maxPerQuery;
|
|
222
220
|
networks;
|
|
223
221
|
assets;
|
|
222
|
+
// undefined = allowlist check disabled (backward-compatible default).
|
|
223
|
+
// Non-null = active allowlist; reject any recipient not in the set.
|
|
224
|
+
recipientAllowlist;
|
|
224
225
|
constructor(opts = {}) {
|
|
225
226
|
this.signer = opts.signer;
|
|
226
227
|
this.maxPerQuery = opts.maxPerQuery !== void 0 ? Number(opts.maxPerQuery) : void 0;
|
|
227
228
|
this.networks = new Set(opts.networks ?? ["base", "base-sepolia"]);
|
|
228
229
|
this.assets = new Set(opts.assets ?? ["USDC"]);
|
|
230
|
+
if (opts.recipientAllowlist !== void 0) {
|
|
231
|
+
this.recipientAllowlist = opts.recipientAllowlist.length === 0 ? void 0 : new Set(opts.recipientAllowlist.map((a) => a.toLowerCase()));
|
|
232
|
+
} else {
|
|
233
|
+
this.recipientAllowlist = void 0;
|
|
234
|
+
}
|
|
229
235
|
}
|
|
230
236
|
/**
|
|
231
237
|
* Validate the envelope against this client's policy and, if a signer
|
|
@@ -251,6 +257,15 @@ var X402Client = class {
|
|
|
251
257
|
);
|
|
252
258
|
}
|
|
253
259
|
}
|
|
260
|
+
if (this.recipientAllowlist !== void 0) {
|
|
261
|
+
const recipientNorm = (envelope.recipient ?? "").toLowerCase();
|
|
262
|
+
if (!recipientNorm || !this.recipientAllowlist.has(recipientNorm)) {
|
|
263
|
+
throw new AlterError(
|
|
264
|
+
"PAYMENT_REQUIRED",
|
|
265
|
+
`recipient "${envelope.recipient}" is not on the known-recipient allowlist`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
254
269
|
if (!this.signer) {
|
|
255
270
|
throw new AlterPaymentRequired(envelope.resource ?? "unknown", envelope);
|
|
256
271
|
}
|
|
@@ -574,7 +589,7 @@ function buildExtraHeaders() {
|
|
|
574
589
|
}
|
|
575
590
|
var EXTRA_HEADERS = buildExtraHeaders();
|
|
576
591
|
console.warn(
|
|
577
|
-
"This bridge is a dev/demo surface. Authenticated MCP tools require
|
|
592
|
+
"This bridge is a dev/demo surface. Authenticated MCP tools require ES256 per-invocation signing; for production, import `@truealter/sdk` directly. Bridge signing lands in Wave-2."
|
|
578
593
|
);
|
|
579
594
|
var client = new MCPClient({
|
|
580
595
|
endpoint: ENDPOINT,
|