@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 +49 -36
- package/dist/bin/alter-identity.js +19 -13
- package/dist/bin/mcp-bridge.js +30 -9
- package/dist/index.cjs +42 -13
- package/dist/index.d.cts +391 -27
- package/dist/index.d.ts +391 -27
- package/dist/index.js +38 -14
- package/package.json +4 -4
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:** **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 |
|
|
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
|
|
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
|
|
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 —
|
|
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
|
|
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("@") ? {
|
|
1077
|
-
// ~handle — server resolves these via the
|
|
1078
|
-
{
|
|
1079
|
-
) : {
|
|
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
|
}
|
package/dist/bin/mcp-bridge.js
CHANGED
|
@@ -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
|
|
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
|
|
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("@") ? {
|
|
1146
|
-
// ~handle — server resolves these via the
|
|
1147
|
-
{
|
|
1148
|
-
) : {
|
|
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;
|