@truealter/sdk 0.4.1 → 0.5.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/README.md CHANGED
@@ -12,7 +12,7 @@ A thin client over the ALTER MCP server (Streamable HTTP, JSON-RPC 2.0, MCP spec
12
12
  - **Branded host:** `https://mcp.truealter.com` (serves `.well-known/mcp.json` for discovery)
13
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:** **32 typed and wired** — 24 free (L0) + 8 premium (L1–L5). Mirrors the live server's `tools/list` response byte-for-byte; every name in `FREE_TOOL_NAMES` / `PREMIUM_TOOL_NAMES` has a matching server handler at `mcp.truealter.com/api/v1/mcp`.
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
@@ -25,6 +25,38 @@ npx alter-identity init
25
25
  npx alter-identity verify ~truealter
26
26
  ```
27
27
 
28
+ ## Bridge vs SDK
29
+
30
+ The `alter-mcp-bridge` binary shipped in this package (`bin/mcp-bridge.ts`)
31
+ is a **dev/demo surface** for dropping ALTER into MCP hosts that speak the
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 Q5c
34
+ per-invocation signing — authenticated MCP tools will fail at the server
35
+ edge when reached through the bridge. For production use, import
36
+ `@truealter/sdk` directly and construct an `MCPClient` / `AlterClient` with
37
+ the optional `signing` parameter; that path is the load-bearing one and
38
+ carries the provenance envelope end-to-end. Bridge signing lands in Wave-2
39
+ alongside the CLI wallet/consent verbs.
40
+
41
+ ## CLI
42
+
43
+ The package ships two binaries. `alter-identity` is the full SDK-feature
44
+ binary (`init`, `verify`, `whoami`, wire/unwire, signing, etc). `alter`
45
+ is a slim, task-oriented binary for day-to-day use:
46
+
47
+ | Command | Purpose |
48
+ |---|---|
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` — identity-depth score, agentic activity, top/bottom five traits. |
51
+ <!-- TODO(D4): "claim" is a Recognition Over Qualification violation — rename to "redeem" or "accept-invite" in alter-cli + update here -->
52
+ | `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
+ | `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 — calls `alter_whoami` and `alter_verify` against your bound handle. Degrades gracefully if the MCP endpoint is 5xx. |
55
+
56
+ The session file is created with `0600` permissions; its parent dir
57
+ (`~/.config/alter/`) is created with `0700`. Override the config root
58
+ via `XDG_CONFIG_HOME`. Run `alter --help` for the inline reference.
59
+
28
60
  ## Why ALTER ≠ IAM
29
61
 
30
62
  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.
@@ -65,7 +97,7 @@ const archetypes = await alter.listArchetypes();
65
97
 
66
98
  // Identity depth and available tool tiers
67
99
  const depth = await alter.getEngagementLevel({
68
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
100
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
69
101
  });
70
102
 
71
103
  // Search by trait criteria — no PII exposed, max 5 results
@@ -91,18 +123,18 @@ const signals = await alter.assessTraits({
91
123
 
92
124
  // L2 — Full 33-trait vector ($0.01)
93
125
  const vector = await alter.getFullTraitVector({
94
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
126
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
95
127
  });
96
128
 
97
129
  // L4 — Belonging probability for a person-job pairing ($0.05)
98
130
  const belonging = await alter.computeBelonging({
99
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
131
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
100
132
  job_id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
101
133
  });
102
134
 
103
135
  // L5 — Top match recommendations ($0.50)
104
136
  const recommendations = await alter.getMatchRecommendations({
105
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
137
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
106
138
  limit: 5,
107
139
  });
108
140
 
@@ -118,7 +150,7 @@ const narrative = await alter.generateMatchNarrative({
118
150
  // Every medium- and high-blast-radius response is signed with ES256.
119
151
  // Verification is opt-in — call alter.verifyProvenance(...) yourself.
120
152
  const result = await alter.getFullTraitVector({
121
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
153
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
122
154
  });
123
155
 
124
156
  const check = await alter.verifyProvenance(result._meta?.provenance);
@@ -152,7 +184,7 @@ const mcp = new MCPClient({ endpoint: "https://mcp.truealter.com/api/v1/mcp" });
152
184
  await mcp.initialize();
153
185
  const tools = await mcp.listTools();
154
186
  const response = await mcp.callTool("verify_identity", {
155
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
187
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
156
188
  });
157
189
  ```
158
190
 
@@ -238,34 +270,17 @@ ALTER monetises premium tools via the [x402](https://x402.org) standard — HTTP
238
270
  2. Server replies `402 Payment Required` with a payment requirement (amount, recipient, asset, network).
239
271
  3. Client signs and broadcasts a USDC transfer on Base L2, attaches the proof, retries.
240
272
  4. Server validates the proof, executes the tool, signs the response with ES256, returns it.
241
- 5. The treasury splits the payment within seconds.
273
+ 5. AlterRouter executes the split on-chain in the same transaction. The data subject receives Identity Income directly; ALTER receives only its protocol cut. No custodian, no broker.
242
274
 
243
275
  The SDK handles steps 2–4 automatically when an `X402Client` with a configured `signer` is passed in.
244
276
 
245
277
  ### Tier structure
246
278
 
247
- | Tier | Cost | Tools |
248
- |------|----------|-----------------------------------------------------|
249
- | L1 | $0.005 | `assess_traits`, `get_trait_snapshot` |
250
- | L2 | $0.01 | `get_full_trait_vector`, `get_side_quest_graph` |
251
- | L3 | $0.025 | `query_graph_similarity` |
252
- | L4 | $0.05 | `compute_belonging` |
253
- | L5 | $0.50 | `get_match_recommendations`, `generate_match_narrative` |
254
-
255
- The first **100 calls per bot are free** before x402 settlement engages — enough to evaluate the network without spending a cent.
279
+ x402 micropayments at L0–L5 trust tiers. Per-call pricing visible after `alter login`.
256
280
 
257
281
  ### Identity income split
258
282
 
259
- Every settled call is split four ways:
260
-
261
- | Recipient | Share |
262
- |----------------------|-------|
263
- | Data subject | 75% |
264
- | Facilitator agent | 5% |
265
- | ALTER (protocol) | 15% |
266
- | Cooperative treasury | 5% |
267
-
268
- The 75% to the data subject is the foundation of *Identity Income* — humans earn from queries against their own identity field, automatically, without intermediation.
283
+ The majority of every settled call flows to the data subject as Identity Income. Split details available post-authentication via `alter status`.
269
284
 
270
285
  ### Code example
271
286
 
@@ -302,7 +317,7 @@ const alter = new AlterClient({
302
317
 
303
318
  // Auto-retries with payment when the server returns 402
304
319
  const vector = await alter.getFullTraitVector({
305
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
320
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
306
321
  });
307
322
  ```
308
323
 
@@ -314,7 +329,7 @@ Every response from a medium- or high-blast-radius tool ships with an ES256 JWS
314
329
 
315
330
  ```ts
316
331
  const result = await alter.getFullTraitVector({
317
- candidate_id: "550e8400-e29b-41d4-a716-446655440000",
332
+ member_id: "550e8400-e29b-41d4-a716-446655440000",
318
333
  });
319
334
 
320
335
  const check = await alter.verifyProvenance(result._meta?.provenance);
@@ -364,7 +379,7 @@ const descriptor = await discover("truealter.com");
364
379
  const httpsOnly = await discover("truealter.com", { skipDns: true });
365
380
  ```
366
381
 
367
- The IETF draft is being progressed through the IETF; until adoption, the cascade order may change. Pin the SDK version to a specific minor release if you depend on this behaviour.
382
+ This draft is the author's Internet-Draft (not yet adopted by an IETF working group); until adoption, the cascade order may change. Pin the SDK version to a specific minor release if you depend on this behaviour.
368
383
 
369
384
  ## Local Daemon vs Remote MCP
370
385
 
@@ -386,12 +401,12 @@ Until then, use `endpoint: "https://mcp.truealter.com/api/v1/mcp"` (the default)
386
401
  |---------------------------|------|-------|----------------------------------------------------------------------------------------------------------------------|
387
402
  | `hello_agent` | L0 | free | First handshake with ALTER — returns server version, authentication status, your trust tier, and available tool counts. |
388
403
  | `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. |
389
- | `list_archetypes` | L0 | free | List all 12 ALTER identity archetypes with names, descriptions, and protective equations. |
404
+ | `list_archetypes` | L0 | free | Returns archetype reference data. |
390
405
  | `verify_identity` | L0 | free | Verify whether a person is registered with ALTER and validate optional identity claims. |
391
406
  | `initiate_assessment` | L0 | free | Get a URL where a person can complete their ALTER Discovery assessment. |
392
407
  | `get_engagement_level` | L0 | free | Get a person's identity depth — engagement level, data quality tier, and available query tiers. |
393
408
  | `get_profile` | L0 | free | Get a person's profile summary including assessment phase, archetype, engagement level, and key attributes. |
394
- | `query_matches` | L0 | free | Query matches for a person. Returns a list of job matches with quality tiers (never numeric scores). |
409
+ | `query_matches` | L0 | free | Query matches for a person. Returns a list of matches with quality tiers (never numeric scores). |
395
410
  | `get_competencies` | L0 | free | Get a person's competency portfolio including verified competencies, evidence records, and earned badges. |
396
411
  | `search_identities` | L0 | free | Search identity stubs and profiles by trait criteria. Returns up to 5 matches with no PII. |
397
412
  | `get_identity_earnings` | L0 | free | Get accrued Identity Income earnings for a person (75% of every x402 transaction goes to the data subject). |
@@ -413,9 +428,9 @@ Until then, use `endpoint: "https://mcp.truealter.com/api/v1/mcp"` (the default)
413
428
 
414
429
  | Name | Tier | Cost | Description |
415
430
  |----------------------------|------|---------|---------------------------------------------------------------------------------------------------------------|
416
- | `assess_traits` | L1 | $0.005 | Extract trait signals from a text passage against ALTER's 33-trait taxonomy (first 100 free per bot). |
431
+ | `assess_traits` | L1 | $0.005 | Extract trait signals from a text passage against ALTER's trait taxonomy. |
417
432
  | `get_trait_snapshot` | L1 | $0.005 | Get the top 5 traits for a person with confidence scores and archetype. |
418
- | `get_full_trait_vector` | L2 | $0.01 | Get the complete trait vector for a person — all 33 traits (29 continuous + 4 categorical) with scores, intervals, and category groupings. |
433
+ | `get_full_trait_vector` | L2 | $0.01 | Get the complete trait vector for a person — complete trait vector with scores and confidence intervals. |
419
434
  | `get_side_quest_graph` | L2 | $0.01 | Get a person's Side Quest Graph — multi-domain identity model with differential privacy noise (ε=1.0). |
420
435
  | `query_graph_similarity` | L3 | $0.025 | Compare two Side Quest Graphs for team composition and matching (ε=0.5 differential privacy). |
421
436
  | `compute_belonging` | L4 | $0.05 | Compute belonging probability for a person-job pairing (authenticity, acceptance, complementarity). |
@@ -429,5 +444,3 @@ Until then, use `endpoint: "https://mcp.truealter.com/api/v1/mcp"` (the default)
429
444
  Apache License 2.0. See [LICENSE](./LICENSE) for the full text.
430
445
 
431
446
  Copyright 2026 Alter Meridian Pty Ltd (ABN 54 696 662 049).
432
-
433
- ALTER, the Trill (`~`), and the Golden Thread are trademarks of Alter Meridian Pty Ltd.
@@ -2,6 +2,7 @@
2
2
  import { p256 } from '@noble/curves/p256';
3
3
  import { sha256 } from '@noble/hashes/sha256';
4
4
  import { hexToBytes, bytesToHex as bytesToHex$1, randomBytes } from '@noble/hashes/utils';
5
+ import { createHash, createPrivateKey } from 'crypto';
5
6
  import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, renameSync, copyFileSync } from 'fs';
6
7
  import { homedir, platform } from 'os';
7
8
  import { join, dirname, resolve } from 'path';
@@ -10,18 +11,11 @@ import { createInterface } from 'readline';
10
11
  import * as ed25519 from '@noble/ed25519';
11
12
  import { sha512 } from '@noble/hashes/sha512';
12
13
  import { spawnSync } from 'child_process';
13
- import { createHash } from 'crypto';
14
14
 
15
15
  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
- });
25
19
  var __esm = (fn, res) => function __init() {
26
20
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
27
21
  };
@@ -106,8 +100,7 @@ function loadPrivateKey(key) {
106
100
  return key;
107
101
  }
108
102
  if (typeof key === "string" && key.includes("-----BEGIN")) {
109
- const nodeCrypto = __require("crypto");
110
- const keyObj = nodeCrypto.createPrivateKey({ key, format: "pem" });
103
+ const keyObj = createPrivateKey({ key, format: "pem" });
111
104
  const jwk = keyObj.export({ format: "jwk" });
112
105
  if (jwk.crv !== "P-256" || !jwk.d) {
113
106
  throw new TypeError("PEM is not a P-256 private key.");
@@ -500,6 +493,7 @@ var MCPClient = class {
500
493
  clientInfo;
501
494
  x402;
502
495
  signing;
496
+ extraHeaders;
503
497
  requestCounter = 0;
504
498
  initialised = false;
505
499
  constructor(opts = {}) {
@@ -511,6 +505,7 @@ var MCPClient = class {
511
505
  this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
512
506
  this.x402 = opts.x402;
513
507
  this.signing = opts.signing;
508
+ this.extraHeaders = opts.extraHeaders;
514
509
  }
515
510
  /**
516
511
  * Send the MCP `initialize` handshake and capture the resulting session
@@ -674,6 +669,7 @@ var MCPClient = class {
674
669
  }
675
670
  buildHeaders(extra) {
676
671
  const headers = {
672
+ ...this.extraHeaders ?? {},
677
673
  "Content-Type": "application/json",
678
674
  Accept: "application/json",
679
675
  "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
@@ -795,6 +791,7 @@ var DEFAULT_VERIFY_AT_ALLOWLIST = Object.freeze([
795
791
  "api.truealter.com",
796
792
  "mcp.truealter.com"
797
793
  ]);
794
+ var ALTER_PLATFORM_ISS = "did:alter:platform";
798
795
  async function verifyProvenance(envelope, opts = {}) {
799
796
  const token = typeof envelope === "string" ? envelope : envelope.token;
800
797
  if (!token) return { valid: false, reason: "empty token" };
@@ -877,6 +874,15 @@ async function verifyProvenance(envelope, opts = {}) {
877
874
  if (typeof payload.iat === "number" && payload.iat > now + 300) {
878
875
  return { valid: false, reason: "issued in the future", payload, kid: header.kid };
879
876
  }
877
+ const expectedIss = opts.expectedIss !== void 0 ? opts.expectedIss : ALTER_PLATFORM_ISS;
878
+ if (expectedIss !== "" && payload.iss !== expectedIss) {
879
+ return {
880
+ valid: false,
881
+ reason: `iss mismatch: expected "${expectedIss}", got "${payload.iss}"`,
882
+ payload,
883
+ kid: header.kid
884
+ };
885
+ }
880
886
  return { valid: true, payload, kid: header.kid };
881
887
  }
882
888
  async function verifyToolSignatures(tools, signatures) {
@@ -1073,10 +1079,10 @@ var AlterClient = class {
1073
1079
  }
1074
1080
  /** Verify a person is registered with ALTER (handle or id). */
1075
1081
  async verify(handleOrId, claims) {
1076
- const args = handleOrId.includes("@") ? { candidate_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1077
- // ~handle — server resolves these via the candidate_id field
1078
- { candidate_id: handleOrId }
1079
- ) : { candidate_id: handleOrId };
1082
+ const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1083
+ // ~handle — server resolves these via the member_id field
1084
+ { member_id: handleOrId }
1085
+ ) : { member_id: handleOrId };
1080
1086
  if (claims) args.claims = claims;
1081
1087
  return this.mcp.callTool("verify_identity", args);
1082
1088
  }
@@ -2,6 +2,7 @@
2
2
  import { p256 } from '@noble/curves/p256';
3
3
  import { sha256 } from '@noble/hashes/sha256';
4
4
  import { randomBytes } from '@noble/hashes/utils';
5
+ import { createPrivateKey } from 'crypto';
5
6
  import { createInterface } from 'readline';
6
7
  import { env, stderr, exit, stdin, stdout } from 'process';
7
8
 
@@ -9,12 +10,6 @@ var __defProp = Object.defineProperty;
9
10
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
11
  var __getOwnPropNames = Object.getOwnPropertyNames;
11
12
  var __hasOwnProp = Object.prototype.hasOwnProperty;
12
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
13
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
14
- }) : x)(function(x) {
15
- if (typeof require !== "undefined") return require.apply(this, arguments);
16
- throw Error('Dynamic require of "' + x + '" is not supported');
17
- });
18
13
  var __esm = (fn, res) => function __init() {
19
14
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
20
15
  };
@@ -99,8 +94,7 @@ function loadPrivateKey(key) {
99
94
  return key;
100
95
  }
101
96
  if (typeof key === "string" && key.includes("-----BEGIN")) {
102
- const nodeCrypto = __require("crypto");
103
- const keyObj = nodeCrypto.createPrivateKey({ key, format: "pem" });
97
+ const keyObj = createPrivateKey({ key, format: "pem" });
104
98
  const jwk = keyObj.export({ format: "jwk" });
105
99
  if (jwk.crv !== "P-256" || !jwk.d) {
106
100
  throw new TypeError("PEM is not a P-256 private key.");
@@ -314,6 +308,7 @@ var MCPClient = class {
314
308
  clientInfo;
315
309
  x402;
316
310
  signing;
311
+ extraHeaders;
317
312
  requestCounter = 0;
318
313
  initialised = false;
319
314
  constructor(opts = {}) {
@@ -325,6 +320,7 @@ var MCPClient = class {
325
320
  this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
326
321
  this.x402 = opts.x402;
327
322
  this.signing = opts.signing;
323
+ this.extraHeaders = opts.extraHeaders;
328
324
  }
329
325
  /**
330
326
  * Send the MCP `initialize` handshake and capture the resulting session
@@ -488,6 +484,7 @@ var MCPClient = class {
488
484
  }
489
485
  buildHeaders(extra) {
490
486
  const headers = {
487
+ ...this.extraHeaders ?? {},
491
488
  "Content-Type": "application/json",
492
489
  Accept: "application/json",
493
490
  "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
@@ -556,10 +553,34 @@ async function safeText(resp) {
556
553
  // bin/mcp-bridge.ts
557
554
  var ENDPOINT = env.ALTER_MCP_ENDPOINT ?? "https://mcp.truealter.com/api/v1/mcp";
558
555
  var API_KEY = env.ALTER_API_KEY ?? void 0;
556
+ function buildExtraHeaders() {
557
+ const headers = {};
558
+ if (env.CF_ACCESS_CLIENT_ID && env.CF_ACCESS_CLIENT_SECRET) {
559
+ headers["CF-Access-Client-Id"] = env.CF_ACCESS_CLIENT_ID;
560
+ headers["CF-Access-Client-Secret"] = env.CF_ACCESS_CLIENT_SECRET;
561
+ }
562
+ if (env.ALTER_BRIDGE_HEADERS) {
563
+ try {
564
+ const parsed = JSON.parse(env.ALTER_BRIDGE_HEADERS);
565
+ Object.assign(headers, parsed);
566
+ } catch (err) {
567
+ stderr.write(
568
+ `[alter-bridge] warning: ALTER_BRIDGE_HEADERS is not valid JSON; ignored (${err.message})
569
+ `
570
+ );
571
+ }
572
+ }
573
+ return Object.keys(headers).length ? headers : void 0;
574
+ }
575
+ var EXTRA_HEADERS = buildExtraHeaders();
576
+ console.warn(
577
+ "This bridge is a dev/demo surface. Authenticated MCP tools require Q5c signing; for production, import `@truealter/sdk` directly. Bridge signing lands in Wave-2."
578
+ );
559
579
  var client = new MCPClient({
560
580
  endpoint: ENDPOINT,
561
581
  apiKey: API_KEY,
562
- clientInfo: { name: "@truealter/sdk-mcp-bridge", version: "0.2.0" }
582
+ clientInfo: { name: "@truealter/sdk-mcp-bridge", version: "0.2.0" },
583
+ extraHeaders: EXTRA_HEADERS
563
584
  });
564
585
  function send(response) {
565
586
  stdout.write(JSON.stringify(response) + "\n");
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var p256 = require('@noble/curves/p256');
4
4
  var sha256 = require('@noble/hashes/sha256');
5
5
  var utils = require('@noble/hashes/utils');
6
+ var crypto$1 = require('crypto');
6
7
  var ed25519 = require('@noble/ed25519');
7
8
  var sha512 = require('@noble/hashes/sha512');
8
9
  var child_process = require('child_process');
@@ -10,7 +11,6 @@ var os = require('os');
10
11
  var path = require('path');
11
12
  var process$1 = require('process');
12
13
  var fs = require('fs');
13
- var crypto$1 = require('crypto');
14
14
 
15
15
  function _interopNamespace(e) {
16
16
  if (e && e.__esModule) return e;
@@ -36,12 +36,6 @@ var __defProp = Object.defineProperty;
36
36
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
37
37
  var __getOwnPropNames = Object.getOwnPropertyNames;
38
38
  var __hasOwnProp = Object.prototype.hasOwnProperty;
39
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
40
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
41
- }) : x)(function(x) {
42
- if (typeof require !== "undefined") return require.apply(this, arguments);
43
- throw Error('Dynamic require of "' + x + '" is not supported');
44
- });
45
39
  var __esm = (fn, res) => function __init() {
46
40
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
47
41
  };
@@ -132,8 +126,7 @@ function loadPrivateKey(key) {
132
126
  return key;
133
127
  }
134
128
  if (typeof key === "string" && key.includes("-----BEGIN")) {
135
- const nodeCrypto = __require("crypto");
136
- const keyObj = nodeCrypto.createPrivateKey({ key, format: "pem" });
129
+ const keyObj = crypto$1.createPrivateKey({ key, format: "pem" });
137
130
  const jwk = keyObj.export({ format: "jwk" });
138
131
  if (jwk.crv !== "P-256" || !jwk.d) {
139
132
  throw new TypeError("PEM is not a P-256 private key.");
@@ -544,6 +537,7 @@ var MCPClient = class {
544
537
  clientInfo;
545
538
  x402;
546
539
  signing;
540
+ extraHeaders;
547
541
  requestCounter = 0;
548
542
  initialised = false;
549
543
  constructor(opts = {}) {
@@ -555,6 +549,7 @@ var MCPClient = class {
555
549
  this.clientInfo = opts.clientInfo ?? { name: "@truealter/sdk", version: "0.2.0" };
556
550
  this.x402 = opts.x402;
557
551
  this.signing = opts.signing;
552
+ this.extraHeaders = opts.extraHeaders;
558
553
  }
559
554
  /**
560
555
  * Send the MCP `initialize` handshake and capture the resulting session
@@ -718,6 +713,7 @@ var MCPClient = class {
718
713
  }
719
714
  buildHeaders(extra) {
720
715
  const headers = {
716
+ ...this.extraHeaders ?? {},
721
717
  "Content-Type": "application/json",
722
718
  Accept: "application/json",
723
719
  "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`
@@ -864,6 +860,7 @@ var DEFAULT_VERIFY_AT_ALLOWLIST = Object.freeze([
864
860
  "api.truealter.com",
865
861
  "mcp.truealter.com"
866
862
  ]);
863
+ var ALTER_PLATFORM_ISS = "did:alter:platform";
867
864
  async function verifyProvenance(envelope, opts = {}) {
868
865
  const token = typeof envelope === "string" ? envelope : envelope.token;
869
866
  if (!token) return { valid: false, reason: "empty token" };
@@ -946,6 +943,15 @@ async function verifyProvenance(envelope, opts = {}) {
946
943
  if (typeof payload.iat === "number" && payload.iat > now + 300) {
947
944
  return { valid: false, reason: "issued in the future", payload, kid: header.kid };
948
945
  }
946
+ const expectedIss = opts.expectedIss !== void 0 ? opts.expectedIss : ALTER_PLATFORM_ISS;
947
+ if (expectedIss !== "" && payload.iss !== expectedIss) {
948
+ return {
949
+ valid: false,
950
+ reason: `iss mismatch: expected "${expectedIss}", got "${payload.iss}"`,
951
+ payload,
952
+ kid: header.kid
953
+ };
954
+ }
949
955
  return { valid: true, payload, kid: header.kid };
950
956
  }
951
957
  async function verifyToolSignatures(tools, signatures) {
@@ -1142,10 +1148,10 @@ var AlterClient = class {
1142
1148
  }
1143
1149
  /** Verify a person is registered with ALTER (handle or id). */
1144
1150
  async verify(handleOrId, claims) {
1145
- const args = handleOrId.includes("@") ? { candidate_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1146
- // ~handle — server resolves these via the candidate_id field
1147
- { candidate_id: handleOrId }
1148
- ) : { candidate_id: handleOrId };
1151
+ const args = handleOrId.includes("@") ? { member_id: "", email: handleOrId } : handleOrId.startsWith("~") ? (
1152
+ // ~handle — server resolves these via the member_id field
1153
+ { member_id: handleOrId }
1154
+ ) : { member_id: handleOrId };
1149
1155
  if (claims) args.claims = claims;
1150
1156
  return this.mcp.callTool("verify_identity", args);
1151
1157
  }
@@ -1965,6 +1971,26 @@ var TOOL_BLAST_RADIUS = {
1965
1971
  query_graph_similarity: "high"
1966
1972
  };
1967
1973
 
1974
+ // src/homepage.ts
1975
+ init_cjs_shims();
1976
+ var HOMEPAGE_LIMITS = {
1977
+ whoami_max_chars: 240,
1978
+ opener_max_chars: 280,
1979
+ pronouns_max_chars: 32,
1980
+ attunement_glyph_max_chars: 16
1981
+ };
1982
+
1983
+ // src/themes.ts
1984
+ init_cjs_shims();
1985
+ var THEME_LIMITS = {
1986
+ meta_name_pattern: /^[a-z][a-z0-9-]{0,63}$/,
1987
+ meta_description_max_chars: 240,
1988
+ opener_library_max_entries: 32,
1989
+ opener_entry_max_chars: 240,
1990
+ share_note_max_chars: 280
1991
+ };
1992
+ var OSC8_ALLOWED_SCHEMES = ["https:", "mailto:"];
1993
+
1968
1994
  exports.ALL_CLIENTS = ALL_CLIENTS;
1969
1995
  exports.AlterAuthError = AlterAuthError;
1970
1996
  exports.AlterClient = AlterClient;
@@ -1984,11 +2010,14 @@ exports.DEFAULT_DOMAIN = DEFAULT_DOMAIN;
1984
2010
  exports.DEFAULT_ENDPOINT = DEFAULT_ENDPOINT;
1985
2011
  exports.DEFAULT_VERIFY_AT_ALLOWLIST = DEFAULT_VERIFY_AT_ALLOWLIST;
1986
2012
  exports.FREE_TOOL_NAMES = FREE_TOOL_NAMES;
2013
+ exports.HOMEPAGE_LIMITS = HOMEPAGE_LIMITS;
1987
2014
  exports.MCPClient = MCPClient;
1988
2015
  exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
2016
+ exports.OSC8_ALLOWED_SCHEMES = OSC8_ALLOWED_SCHEMES;
1989
2017
  exports.PREMIUM_TOOL_NAMES = PREMIUM_TOOL_NAMES;
1990
2018
  exports.SDK_NAME = SDK_NAME;
1991
2019
  exports.SDK_VERSION = SDK_VERSION;
2020
+ exports.THEME_LIMITS = THEME_LIMITS;
1992
2021
  exports.TOOL_BLAST_RADIUS = TOOL_BLAST_RADIUS;
1993
2022
  exports.TOOL_COSTS = TOOL_COSTS;
1994
2023
  exports.TOOL_TIERS = TOOL_TIERS;