@truealter/sdk 0.5.3 → 0.5.8

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/dist/index.cjs CHANGED
@@ -37,8 +37,13 @@ var __defProp = Object.defineProperty;
37
37
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
38
38
  var __getOwnPropNames = Object.getOwnPropertyNames;
39
39
  var __hasOwnProp = Object.prototype.hasOwnProperty;
40
- var __esm = (fn, res) => function __init() {
41
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
40
+ var __esm = (fn, res, err) => function __init() {
41
+ if (err) throw err[0];
42
+ try {
43
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
44
+ } catch (e) {
45
+ throw err = [e], e;
46
+ }
42
47
  };
43
48
  var __export = (target, all) => {
44
49
  for (var name in all)
@@ -450,7 +455,7 @@ init_cjs_shims();
450
455
  // src/meta.ts
451
456
  init_cjs_shims();
452
457
  var SDK_NAME = "@truealter/sdk";
453
- var SDK_VERSION = "0.5.3" ;
458
+ var SDK_VERSION = "0.5.8" ;
454
459
 
455
460
  // src/floor-preflight.ts
456
461
  var MIN_VERSION_ENDPOINT = "/v1/clients/min-version";
@@ -884,7 +889,7 @@ var MCPClient = class {
884
889
  this.preflightHook = opts.preflightHook;
885
890
  }
886
891
  /**
887
- * Run the lazy preflight hook (D-MIN-VERSION-FLOOR-1) exactly once.
892
+ * Run the lazy version-floor preflight hook exactly once.
888
893
  * Idempotent and serialised: concurrent callers share the same
889
894
  * promise. Throws from the hook propagate to every concurrent caller.
890
895
  */
@@ -1006,7 +1011,14 @@ var MCPClient = class {
1006
1011
  method: "POST",
1007
1012
  headers: this.buildHeaders(signatureHeader),
1008
1013
  body: JSON.stringify(payload),
1009
- signal: controller.signal
1014
+ signal: controller.signal,
1015
+ // Prevent fetch from silently following 3xx redirects. When
1016
+ // Cloudflare Access credentials are absent or expired the edge
1017
+ // returns HTTP 302 → CF Access login page (text/html). Without
1018
+ // this option undici follows the redirect, lands on a 200 HTML
1019
+ // body, and resp.json() throws the opaque "invalid JSON body"
1020
+ // error that was surfaced as "MCP <method>: invalid JSON body".
1021
+ redirect: "manual"
1010
1022
  });
1011
1023
  } catch (err) {
1012
1024
  clearTimeout(timer);
@@ -1023,6 +1035,19 @@ var MCPClient = class {
1023
1035
  clearTimeout(timer);
1024
1036
  const sessionHeader = resp.headers.get("Mcp-Session-Id");
1025
1037
  if (sessionHeader) this.sessionId = sessionHeader;
1038
+ if (resp.status >= 300 && resp.status < 400) {
1039
+ const location = resp.headers.get("Location") ?? "";
1040
+ const isAuthRedirect = location.includes("cloudflareaccess.com") || location.includes("/cdn-cgi/access/") || !location.startsWith("/") && !location.startsWith(new URL(this.endpoint).origin);
1041
+ if (isAuthRedirect) {
1042
+ throw new AlterAuthError(
1043
+ `MCP ${method}: Cloudflare Access blocked the request (session expired or credentials missing). Run \`alter login\` to re-authenticate.`,
1044
+ 302
1045
+ );
1046
+ }
1047
+ throw new AlterNetworkError(
1048
+ `MCP ${method}: unexpected redirect ${resp.status} to ${location || "(no Location)"}`
1049
+ );
1050
+ }
1026
1051
  if (resp.status === 401 || resp.status === 403) {
1027
1052
  throw new AlterAuthError(`HTTP ${resp.status} on ${method}`, resp.status);
1028
1053
  }
@@ -1047,11 +1072,56 @@ var MCPClient = class {
1047
1072
  const body2 = await safeText(resp);
1048
1073
  throw new AlterError("NETWORK", `HTTP ${resp.status} on ${method}: ${body2.slice(0, 200)}`);
1049
1074
  }
1075
+ const contentType = resp.headers.get("Content-Type") ?? "";
1076
+ const isHtml = contentType.includes("text/html");
1077
+ const isSse = contentType.includes("text/event-stream");
1078
+ if (isHtml || isSse) {
1079
+ if (isSse) {
1080
+ const rawText = await safeText(resp);
1081
+ const dataLine = rawText.split("\n").find((l) => l.startsWith("data:"));
1082
+ if (dataLine) {
1083
+ const jsonPart = dataLine.slice("data:".length).trim();
1084
+ try {
1085
+ const parsed = JSON.parse(jsonPart);
1086
+ if (parsed.error) {
1087
+ const code = parsed.error.code;
1088
+ const message = parsed.error.message ?? `MCP ${method} error`;
1089
+ throw new AlterToolError(this.guessToolName(payload), message, code);
1090
+ }
1091
+ return parsed.result;
1092
+ } catch (parseErr) {
1093
+ if (parseErr instanceof AlterError) throw parseErr;
1094
+ throw new AlterInvalidResponse(
1095
+ `MCP ${method}: could not parse SSE data frame as JSON`,
1096
+ parseErr
1097
+ );
1098
+ }
1099
+ }
1100
+ throw new AlterInvalidResponse(
1101
+ `MCP ${method}: received text/event-stream response with no data: frame`
1102
+ );
1103
+ }
1104
+ const excerpt = (await safeText(resp)).slice(0, 300);
1105
+ const looksLikeLoginPage = excerpt.toLowerCase().includes("cloudflareaccess") || excerpt.toLowerCase().includes("access denied") || excerpt.toLowerCase().includes("<title>");
1106
+ if (looksLikeLoginPage) {
1107
+ throw new AlterAuthError(
1108
+ `MCP ${method}: received an HTML login page instead of JSON (Content-Type: ${contentType}). Run \`alter login\` to re-authenticate.`,
1109
+ 200
1110
+ );
1111
+ }
1112
+ throw new AlterInvalidResponse(
1113
+ `MCP ${method}: unexpected Content-Type "${contentType}" (expected application/json). Body excerpt: ${excerpt.slice(0, 120)}`
1114
+ );
1115
+ }
1050
1116
  let body;
1051
1117
  try {
1052
1118
  body = await resp.json();
1053
1119
  } catch (err) {
1054
- throw new AlterInvalidResponse(`MCP ${method}: invalid JSON body`, err);
1120
+ const hint = contentType ? ` (Content-Type: ${contentType})` : "";
1121
+ throw new AlterInvalidResponse(
1122
+ `MCP ${method}: failed to parse JSON response${hint}. The server may have returned a non-JSON body. Run \`alter login\` if the session is expired.`,
1123
+ err
1124
+ );
1055
1125
  }
1056
1126
  if (body.error) {
1057
1127
  const code = body.error.code;
@@ -1072,7 +1142,7 @@ var MCPClient = class {
1072
1142
  const headers = {
1073
1143
  ...this.extraHeaders ?? {},
1074
1144
  "Content-Type": "application/json",
1075
- Accept: "application/json",
1145
+ Accept: "application/json, text/event-stream",
1076
1146
  "User-Agent": `${this.clientInfo.name}/${this.clientInfo.version}`,
1077
1147
  "X-Alter-Client-Id": "alter-identity",
1078
1148
  "X-Alter-Client-Version": SDK_VERSION,
@@ -1555,7 +1625,23 @@ function canonicalJson2(value) {
1555
1625
 
1556
1626
  // src/client.ts
1557
1627
  var DEFAULT_ENDPOINT = "https://mcp.truealter.com/api/v1/mcp";
1628
+ var MEMBER_BRIDGE_ENDPOINT = "https://api.truealter.com/api/v1/mcp";
1558
1629
  var DEFAULT_DOMAIN = "truealter.com";
1630
+ var CANONICAL_API_BASE = "https://api.truealter.com";
1631
+ var JWKS_WELL_KNOWN_PATH = "/.well-known/alter-keys.json";
1632
+ function resolveJwksUrl(opts = {}) {
1633
+ if (opts.jwksUrl) return opts.jwksUrl;
1634
+ const base = opts.apiBase ?? (typeof process !== "undefined" ? process.env?.ALTER_API : void 0) ?? originOf(opts.endpoint) ?? CANONICAL_API_BASE;
1635
+ return `${base.replace(/\/+$/, "")}${JWKS_WELL_KNOWN_PATH}`;
1636
+ }
1637
+ function originOf(url) {
1638
+ if (!url) return void 0;
1639
+ try {
1640
+ return new URL(url).origin;
1641
+ } catch {
1642
+ return void 0;
1643
+ }
1644
+ }
1559
1645
  var AlterClient = class {
1560
1646
  mcp;
1561
1647
  x402;
@@ -1714,7 +1800,7 @@ var AlterClient = class {
1714
1800
  // ── Alter-to-Alter Messaging ─────────────────────────────────────────
1715
1801
  // Wave 1: cross-handle direct messages between authenticated tilde
1716
1802
  // handles. Default closed: recipient must have granted the sender via
1717
- // alter_message_grant. Spec: docs/technical/Alter-to-Alter Messaging.md.
1803
+ // alter_message_grant. Spec: the ALTER Alter-to-Alter Messaging spec.
1718
1804
  /** Send a direct message to another tilde handle. */
1719
1805
  async messageSend(args) {
1720
1806
  return this.mcp.callTool("alter_message_send", args);
@@ -1753,6 +1839,10 @@ var AlterClient = class {
1753
1839
  if (!envelope) return { valid: false, reason: "no provenance envelope" };
1754
1840
  const inner = envelope.provenance ?? envelope;
1755
1841
  return verifyProvenance(inner, {
1842
+ // Pass an explicit jwksUrl only when the caller pinned one. Leaving it
1843
+ // undefined preserves the envelope `verify_at` (allowlist-gated)
1844
+ // resolution path inside verifyProvenance; the allowlist is the
1845
+ // trust-anchor gate there, not a bare hardcoded host.
1756
1846
  jwksUrl: this.options.jwksUrl,
1757
1847
  verifyAtAllowlist: this.options.verifyAtAllowlist
1758
1848
  });
@@ -1765,9 +1855,21 @@ var AlterClient = class {
1765
1855
  async verifyToolSignatures(tools, signatures) {
1766
1856
  return verifyToolSignatures(tools, signatures);
1767
1857
  }
1768
- /** Fetch the published JWKS for ALTER's signing key (cached 5 min). */
1858
+ /**
1859
+ * Fetch the published JWKS for ALTER's signing key (cached 5 min).
1860
+ *
1861
+ * The JWKS URL is derived from the configured API surface
1862
+ * ({@link resolveJwksUrl}: explicit `jwksUrl` > `apiBase` > `ALTER_API` >
1863
+ * the configured `endpoint` origin > the canonical host) rather than a
1864
+ * single hardcoded host, so the trust anchor tracks the surface the
1865
+ * client is actually pointed at.
1866
+ */
1769
1867
  async fetchPublicKeys() {
1770
- const url = this.options.jwksUrl ?? "https://api.truealter.com/.well-known/alter-keys.json";
1868
+ const url = resolveJwksUrl({
1869
+ jwksUrl: this.options.jwksUrl,
1870
+ apiBase: this.options.apiBase,
1871
+ endpoint: this.options.endpoint
1872
+ });
1771
1873
  return fetchPublicKeys(url);
1772
1874
  }
1773
1875
  };
@@ -1808,17 +1910,20 @@ function generateCursorConfig(opts = {}) {
1808
1910
  init_cjs_shims();
1809
1911
  function generateClaudeDesktopConfig(opts = {}) {
1810
1912
  const serverName = opts.serverName ?? "alter";
1811
- const bridgeCommand = opts.bridgeCommand ?? "alter-mcp-bridge";
1913
+ const bridgeCommand = opts.bridgeCommand ?? "alter";
1812
1914
  const env2 = {};
1813
- env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? DEFAULT_ENDPOINT;
1915
+ env2.ALTER_MCP_ENDPOINT = opts.endpoint ?? MEMBER_BRIDGE_ENDPOINT;
1814
1916
  if (opts.apiKey) env2.ALTER_API_KEY = opts.apiKey;
1815
1917
  const entry = {
1816
1918
  command: bridgeCommand,
1919
+ // The `mcp-bridge` subcommand is always the first arg now that the bridge
1920
+ // is invoked through the `alter` CLI, not a bare bridge binary.
1921
+ args: ["mcp-bridge"],
1817
1922
  env: env2,
1818
1923
  description: "ALTER Identity: psychometric identity field for AI agents"
1819
1924
  };
1820
1925
  if (opts.extraArgs && opts.extraArgs.length > 0) {
1821
- entry.args = [...opts.extraArgs];
1926
+ entry.args = [...entry.args, ...opts.extraArgs];
1822
1927
  }
1823
1928
  return { mcpServers: { [serverName]: entry } };
1824
1929
  }
@@ -2114,6 +2219,7 @@ function wire(opts = {}) {
2114
2219
  const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
2115
2220
  const apiKey = opts.apiKey;
2116
2221
  const cfAccess = opts.cfAccess ?? readCfAccessEnv();
2222
+ const launcherPath = opts.launcherPath;
2117
2223
  const probes = probeAll();
2118
2224
  const selection = opts.only ?? probes.filter((p) => p.installed).map((p) => p.client.id);
2119
2225
  const ts = TIMESTAMP();
@@ -2132,7 +2238,7 @@ function wire(opts = {}) {
2132
2238
  }
2133
2239
  try {
2134
2240
  if (id === "claude-code") {
2135
- targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess }));
2241
+ targets.push(wireClaudeCode({ endpoint, apiKey, cfAccess, launcherPath }));
2136
2242
  } else {
2137
2243
  targets.push(wireFileTarget({ id, endpoint, apiKey, cfAccess, timestamp: ts }));
2138
2244
  }
@@ -2203,10 +2309,28 @@ function wireFileTarget(args) {
2203
2309
  postSha256: result.postSha256
2204
2310
  };
2205
2311
  }
2206
- function wireClaudeCode(args) {
2207
- const cmd = "claude";
2208
- const bridgePath = resolveBridgeScript();
2209
- const argList = bridgePath ? ["mcp", "add", "--scope", "user", "alter", "--", "node", bridgePath] : [
2312
+ function redactSecret(text, secret) {
2313
+ if (!secret) return text;
2314
+ return text.split(secret).join("***redacted***");
2315
+ }
2316
+ function buildClaudeCodeAddArgs(args) {
2317
+ if (args.subprocessArgv) {
2318
+ return [
2319
+ "mcp",
2320
+ "add",
2321
+ "--scope",
2322
+ "user",
2323
+ "alter",
2324
+ "--env",
2325
+ `ALTER_MCP_ENDPOINT=${MEMBER_BRIDGE_ENDPOINT}`,
2326
+ "--env",
2327
+ `ALTER_PUBLIC_MCP_ENDPOINT=${MEMBER_BRIDGE_ENDPOINT}`,
2328
+ ...args.apiKey ? ["--env", `ALTER_API_KEY=${args.apiKey}`] : [],
2329
+ "--",
2330
+ ...args.subprocessArgv
2331
+ ];
2332
+ }
2333
+ return [
2210
2334
  "mcp",
2211
2335
  "add",
2212
2336
  "--scope",
@@ -2217,12 +2341,28 @@ function wireClaudeCode(args) {
2217
2341
  args.endpoint,
2218
2342
  ...args.apiKey ? ["--header", `X-ALTER-API-Key:${args.apiKey}`] : []
2219
2343
  ];
2220
- const full = `${cmd} ${argList.join(" ")}`;
2344
+ }
2345
+ function wireClaudeCode(args) {
2346
+ const cmd = "claude";
2347
+ const bridgePath = resolveBridgeScript();
2348
+ const launcher = args.launcherPath && fs.existsSync(args.launcherPath) ? args.launcherPath : null;
2349
+ const subprocessArgv = launcher ? ["node", launcher, "mcp-bridge"] : bridgePath ? ["node", bridgePath] : null;
2350
+ const argList = buildClaudeCodeAddArgs({
2351
+ apiKey: args.apiKey,
2352
+ subprocessArgv,
2353
+ endpoint: args.endpoint
2354
+ });
2355
+ const full = redactSecret(`${cmd} ${argList.join(" ")}`, args.apiKey);
2221
2356
  const run = child_process.spawnSync(cmd, argList, {
2222
2357
  encoding: "utf8",
2223
2358
  shell: process.platform === "win32",
2224
2359
  timeout: 1e4,
2225
- env: bridgePath ? { ...process.env, ALTER_PUBLIC_MCP_ENDPOINT: args.endpoint, ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {} } : void 0
2360
+ env: subprocessArgv ? {
2361
+ ...process.env,
2362
+ ALTER_MCP_ENDPOINT: MEMBER_BRIDGE_ENDPOINT,
2363
+ ALTER_PUBLIC_MCP_ENDPOINT: MEMBER_BRIDGE_ENDPOINT,
2364
+ ...args.apiKey ? { ALTER_API_KEY: args.apiKey } : {}
2365
+ } : void 0
2226
2366
  });
2227
2367
  if (run.error) {
2228
2368
  return {
@@ -2255,6 +2395,10 @@ function wireClaudeCode(args) {
2255
2395
  }
2256
2396
  function resolveBridgeScript() {
2257
2397
  const here = path.dirname(url.fileURLToPath(importMetaUrl));
2398
+ const distBinBridge = path.join(here, "bin", "mcp-bridge.js");
2399
+ if (fs.existsSync(distBinBridge)) return distBinBridge;
2400
+ const nestedDistBinBridge = path.join(here, "..", "dist", "bin", "mcp-bridge.js");
2401
+ if (fs.existsSync(nestedDistBinBridge)) return nestedDistBinBridge;
2258
2402
  const siblingBridge = path.join(here, "..", "dist", "mcp-bridge.js");
2259
2403
  if (fs.existsSync(siblingBridge)) return siblingBridge;
2260
2404
  const srcBridge = path.join(here, "..", "mcp-bridge.js");
@@ -2546,13 +2690,13 @@ var TIER_PRICES = {
2546
2690
  };
2547
2691
  var ADVERTISED_TOOL_COUNTS = {
2548
2692
  /** Free (L0) tools visible to anonymous / agent-class callers. */
2549
- free: 26,
2693
+ free: 27,
2550
2694
  /** Premium (L1-L5) tools requiring x402 payment. */
2551
- premium: 8,
2695
+ premium: 9,
2552
2696
  /** Messaging tools (member-self-only; excluded from external advertisement). */
2553
2697
  messaging: 0,
2554
2698
  /** Total publicly advertised (free + premium). */
2555
- total: 34
2699
+ total: 36
2556
2700
  };
2557
2701
  var REVENUE_SPLIT = {
2558
2702
  weaver: 0.75,
package/dist/index.d.cts CHANGED
@@ -437,7 +437,7 @@ interface MCPClientInfo {
437
437
  version: string;
438
438
  }
439
439
  interface MCPClientOptions {
440
- /** Streamable HTTP endpoint. Default: https://mcp.truealter.com */
440
+ /** Streamable HTTP endpoint. Default: https://mcp.truealter.com/api/v1/mcp (public discovery). Member bridge runtime sets api.truealter.com via env or explicit option. */
441
441
  endpoint?: string;
442
442
  /** Optional API key for the `X-ALTER-API-Key` header. */
443
443
  apiKey?: string;
@@ -474,7 +474,7 @@ interface MCPClientOptions {
474
474
  /**
475
475
  * Optional hook invoked once, lazily, before the first network call
476
476
  * (the first `initialize()` or any direct `rpc()`). Used by
477
- * {@link AlterClient} to run the D-MIN-VERSION-FLOOR-1 preflight on
477
+ * {@link AlterClient} to run the version-floor preflight on
478
478
  * first request: not on import, not in the constructor. A throw
479
479
  * from the hook propagates to the caller of the first `tools/call`
480
480
  * (or `initialize()`).
@@ -486,7 +486,7 @@ interface MCPSigningOptions {
486
486
  kid: string;
487
487
  /** ES256 P-256 private key: 32-byte scalar or PEM. */
488
488
  privateKey: Uint8Array | string;
489
- /** The caller's bound ~handle. */
489
+ /** The caller's bound handle. */
490
490
  handle: string;
491
491
  }
492
492
  interface MCPCallOptions {
@@ -545,7 +545,7 @@ declare class MCPClient {
545
545
  private initialised;
546
546
  constructor(opts?: MCPClientOptions);
547
547
  /**
548
- * Run the lazy preflight hook (D-MIN-VERSION-FLOOR-1) exactly once.
548
+ * Run the lazy version-floor preflight hook exactly once.
549
549
  * Idempotent and serialised: concurrent callers share the same
550
550
  * promise. Throws from the hook propagate to every concurrent caller.
551
551
  */
@@ -1212,7 +1212,8 @@ declare const TOOL_BLAST_RADIUS: Record<ToolName, "low" | "medium" | "high">;
1212
1212
  * This is the entry point most consumers will use. It bundles
1213
1213
  * {@link MCPClient}, {@link X402Client}, discovery, and provenance
1214
1214
  * verification into a single ergonomic surface that mirrors the 32
1215
- * tools exposed at https://mcp.truealter.com/api/v1/mcp.
1215
+ * tools exposed at https://mcp.truealter.com/api/v1/mcp (public/discovery)
1216
+ * and https://api.truealter.com/api/v1/mcp (member bearer-first runtime).
1216
1217
  *
1217
1218
  * Free tier methods require no authentication. Premium methods accept
1218
1219
  * an `X402Client` (or fall back to throwing {@link AlterPaymentRequired}
@@ -1237,8 +1238,13 @@ interface AlterClientOptions extends Omit<MCPClientOptions, 'x402'> {
1237
1238
  */
1238
1239
  skipDiscovery?: boolean;
1239
1240
  /**
1240
- * URL of the JWKS document used for provenance verification. Defaults
1241
- * to `https://api.truealter.com/.well-known/alter-keys.json`.
1241
+ * URL of the JWKS document used for provenance verification.
1242
+ *
1243
+ * When unset, the JWKS host is derived from the configured API surface
1244
+ * ({@link resolveJwksUrl}: `apiBase` > `ALTER_API` env > the `endpoint`
1245
+ * origin > the canonical `api.truealter.com`), so the trust anchor
1246
+ * tracks whatever surface the client is actually pointed at rather than
1247
+ * a single hardcoded host.
1242
1248
  *
1243
1249
  * When set, this URL is used verbatim for every `verifyProvenance`
1244
1250
  * call and *overrides* any `verify_at` hint on the server response:
@@ -1258,7 +1264,7 @@ interface AlterClientOptions extends Omit<MCPClientOptions, 'x402'> {
1258
1264
  */
1259
1265
  verifyAtAllowlist?: readonly string[];
1260
1266
  /**
1261
- * Skip the D-MIN-VERSION-FLOOR-1 client-side preflight (the lazy
1267
+ * Skip the client-side version-floor preflight (the lazy
1262
1268
  * `checkMinVersion()` invocation on first request). Deliberately named
1263
1269
  * to discourage casual use: the server-side floor gate (Phase 1
1264
1270
  * middleware) still rejects below-floor clients with HTTP 426
@@ -1402,7 +1408,15 @@ declare class AlterClient {
1402
1408
  valid: boolean;
1403
1409
  reason?: string;
1404
1410
  }[]>;
1405
- /** Fetch the published JWKS for ALTER's signing key (cached 5 min). */
1411
+ /**
1412
+ * Fetch the published JWKS for ALTER's signing key (cached 5 min).
1413
+ *
1414
+ * The JWKS URL is derived from the configured API surface
1415
+ * ({@link resolveJwksUrl}: explicit `jwksUrl` > `apiBase` > `ALTER_API` >
1416
+ * the configured `endpoint` origin > the canonical host) rather than a
1417
+ * single hardcoded host, so the trust anchor tracks the surface the
1418
+ * client is actually pointed at.
1419
+ */
1406
1420
  fetchPublicKeys(): Promise<unknown>;
1407
1421
  }
1408
1422
 
@@ -1477,9 +1491,9 @@ interface SignInvocationOptions {
1477
1491
  declare function signInvocation(toolName: string, toolArgs: Record<string, unknown>, options: SignInvocationOptions): string;
1478
1492
 
1479
1493
  /**
1480
- * floor-preflight: SDK-side D-MIN-VERSION-FLOOR-1 Phase 4 enforcement.
1494
+ * floor-preflight: SDK-side client version-floor enforcement.
1481
1495
  *
1482
- * Sister to `alter-cli`'s `src/lib/floor-preflight.ts` (Phase 2). This is
1496
+ * Sister to the CLI client's own floor-preflight. This is
1483
1497
  * the `@truealter/sdk` implementation: it fires lazily on the first
1484
1498
  * authenticated network call from {@link AlterClient}, hits the public
1485
1499
  * floor endpoint, verifies the Ed25519 signature against the published
@@ -1505,8 +1519,8 @@ declare function signInvocation(toolName: string, toolArgs: Record<string, unkno
1505
1519
  * corresponding public key (SPKI PEM) in {@link KNOWN_FLOOR_PUBLIC_KEYS},
1506
1520
  * keyed by `key_id`. No signing secret ships in the client: this is
1507
1521
  * asymmetric: the public key cannot forge signatures. MUST stay
1508
- * byte-compatible with `backend/app/core/floor_signing.py` and the
1509
- * canonical TypeScript client (`alter-cli` `src/lib/floor-preflight.ts`):
1522
+ * byte-compatible with the ALTER backend floor-signing implementation
1523
+ * and the canonical TypeScript CLI client:
1510
1524
  * - Algorithm: Ed25519 (RFC 8032). Deterministic: same key + payload
1511
1525
  * always yields the same signature.
1512
1526
  * - `key_id` = first 8 hex chars of SHA-256(raw 32-byte public key).
@@ -1534,8 +1548,8 @@ declare const CLIENT_CHANNEL = "npm";
1534
1548
  /**
1535
1549
  * Compute the 8-char `key_id` for an Ed25519 public key (SPKI PEM).
1536
1550
  *
1537
- * Matches the backend `key_id_for_public_key(pub)` in
1538
- * `backend/app/core/floor_signing.py`:
1551
+ * Matches the backend `key_id_for_public_key(pub)` in the ALTER
1552
+ * backend floor-signing implementation:
1539
1553
  * raw = pub.public_bytes(Encoding.Raw, PublicFormat.Raw) # 32 bytes
1540
1554
  * sha256(raw).hexdigest()[:8]
1541
1555
  *
@@ -1552,7 +1566,7 @@ declare function computeKeyId(publicKeyPem: string): string;
1552
1566
  * stripped via the `,`/`:` separators; non-ASCII passes through as raw UTF-8
1553
1567
  * (Node's `JSON.stringify` never `\uXXXX`-escapes non-ASCII, matching the
1554
1568
  * backend's `ensure_ascii=False`). The result is the byte-exact input to
1555
- * Ed25519 signing (backend) and verification (this SDK and alter-cli) on
1569
+ * Ed25519 signing (backend) and verification (this SDK and the CLI client) on
1556
1570
  * both sides.
1557
1571
  */
1558
1572
  declare function canonicalJson(obj: unknown): string;
@@ -1562,12 +1576,12 @@ declare function canonicalJson(obj: unknown): string;
1562
1576
  * `key_id` = first 8 hex chars of SHA-256(raw 32-byte Ed25519 public key).
1563
1577
  * Use `computeKeyId(spkiPem)` to re-derive and validate the map key.
1564
1578
  *
1565
- * Backend may rotate via D-MSG11 dual-key path: clients keep N keys and
1579
+ * Backend may rotate via a dual-key path: clients keep N keys and
1566
1580
  * select via `key_id`. Add new keys additively; remove old keys only after
1567
1581
  * all deployed clients have received the new key.
1568
1582
  *
1569
1583
  * Current keys (mirrors `KNOWN_FLOOR_PUBLIC_KEYS` in the canonical
1570
- * `alter-cli` client):
1584
+ * CLI client):
1571
1585
  * 8aa59e05: dev/non-prod key (local + e2e + staging). Safe to ship publicly;
1572
1586
  * this is the PUBLIC key only, never the private key.
1573
1587
  * 640f7d9a: prod key. Added 2026-06-05. key_id = first 8 hex of
@@ -1707,7 +1721,7 @@ declare function compareSemver(a: string, b: string): number;
1707
1721
  * canonical JSON of `{ floors, served_at }`: `signature` + `key_id` +
1708
1722
  * `cache_ttl_seconds` are excluded from the signed bytes.
1709
1723
  *
1710
- * MUST stay byte-compatible with the backend (`backend/app/core/floor_signing.py`):
1724
+ * MUST stay byte-compatible with the ALTER backend floor-signing implementation:
1711
1725
  * - Algorithm: Ed25519 (RFC 8032). Deterministic: same key + payload = same sig.
1712
1726
  * - Payload: `canonicalJson({floors, served_at})`: sorted-keys + compact.
1713
1727
  * - `key_id`: first 8 hex chars of SHA-256(raw 32-byte Ed25519 public key).
@@ -1715,7 +1729,7 @@ declare function compareSemver(a: string, b: string): number;
1715
1729
  *
1716
1730
  * The `keys` parameter maps `key_id -> SPKI PEM string`; defaults to
1717
1731
  * `KNOWN_FLOOR_PUBLIC_KEYS`. Returns false on unknown key_id (triggers refetch
1718
- * per D-MSG11 rotation path).
1732
+ * per the key-rotation path).
1719
1733
  */
1720
1734
  declare function verifyFloorSignature(doc: FloorDocument, keys?: Record<string, string>): boolean;
1721
1735
 
@@ -1747,8 +1761,8 @@ declare function generateGenericMcpConfig(opts?: GenerateMcpConfigOptions): Gene
1747
1761
  /**
1748
1762
  * Claude Code MCP config helper.
1749
1763
  *
1750
- * Claude Code reads MCP servers from `.mcp.json` (project) or
1751
- * `~/.claude/mcp.json` (user). The shape matches `mcpServers`.
1764
+ * Claude Code reads MCP servers from the project-level `.mcp.json` or
1765
+ * the user-level Claude Code config. The shape matches `mcpServers`.
1752
1766
  */
1753
1767
 
1754
1768
  declare function generateClaudeConfig(opts?: GenerateMcpConfigOptions): GenericMcpConfig;
@@ -1766,10 +1780,11 @@ declare function generateCursorConfig(opts?: GenerateMcpConfigOptions): GenericM
1766
1780
  * Claude Desktop MCP config helper.
1767
1781
  *
1768
1782
  * Claude Desktop speaks stdio only: it does not currently dial
1769
- * Streamable-HTTP MCP servers directly. The canonical bridge is our
1770
- * own `alter-mcp-bridge` binary, which is published alongside this CLI
1771
- * in the same npm package. Desktop hosts then spawn the bridge as a
1772
- * child process and read JSON-RPC over stdin/stdout.
1783
+ * Streamable-HTTP MCP servers directly. The canonical bridge is the
1784
+ * `alter` CLI's `mcp-bridge` subcommand. The SDK no longer publishes a
1785
+ * standalone bridge binary; the CLI launches the bridge by file path.
1786
+ * Desktop hosts spawn `alter mcp-bridge` as a child process and read
1787
+ * JSON-RPC over stdin/stdout.
1773
1788
  *
1774
1789
  * Config file path varies by platform and is resolved in
1775
1790
  * `src/wire/paths.ts`. This adapter only produces the config *shape*.
@@ -1790,9 +1805,9 @@ interface GenerateClaudeDesktopOptions {
1790
1805
  apiKey?: string;
1791
1806
  /** Identifier used by Claude Desktop for this server. Default: `alter`. */
1792
1807
  serverName?: string;
1793
- /** Override the bridge command (e.g. `npx alter-mcp-bridge`). Default: bare `alter-mcp-bridge`. */
1808
+ /** Override the bridge command (e.g. `npx`). Default: the `alter` CLI. */
1794
1809
  bridgeCommand?: string;
1795
- /** Extra args appended after the default bridge args. */
1810
+ /** Extra args appended after the default `mcp-bridge` subcommand arg. */
1796
1811
  extraArgs?: string[];
1797
1812
  }
1798
1813
  declare function generateClaudeDesktopConfig(opts?: GenerateClaudeDesktopOptions): ClaudeDesktopConfig;
@@ -1943,6 +1958,15 @@ interface WireOptions {
1943
1958
  only?: readonly ClientId[];
1944
1959
  /** Skip any client whose probe said "not installed" even if the caller passed it via `only`. */
1945
1960
  skipMissing?: boolean;
1961
+ /**
1962
+ * Absolute path to the `alter` CLI launcher entry (its `dist/index.js`).
1963
+ * When set, Claude Code is wired to run `node <launcherPath> mcp-bridge`,
1964
+ * which injects the ES256 signing credential (ALTER_SIGNING_KEY /
1965
+ * ALTER_SIGNING_KID) from the OS secure store before spawning the bridge,
1966
+ * so MCP tool calls are signed. When absent (standalone SDK use without the
1967
+ * CLI), wiring falls back to `node <bridge>`: anonymous / L0, no signing.
1968
+ */
1969
+ launcherPath?: string;
1946
1970
  }
1947
1971
  interface WireReport {
1948
1972
  state: WireState;
@@ -1980,13 +2004,13 @@ declare const TIER_PRICES: Record<string, number>;
1980
2004
  /** Publicly advertised tool counts. Mirrors canonical-facts.json public_advertised. */
1981
2005
  declare const ADVERTISED_TOOL_COUNTS: {
1982
2006
  /** Free (L0) tools visible to anonymous / agent-class callers. */
1983
- readonly free: 26;
2007
+ readonly free: 27;
1984
2008
  /** Premium (L1-L5) tools requiring x402 payment. */
1985
- readonly premium: 8;
2009
+ readonly premium: 9;
1986
2010
  /** Messaging tools (member-self-only; excluded from external advertisement). */
1987
2011
  readonly messaging: 0;
1988
2012
  /** Total publicly advertised (free + premium). */
1989
- readonly total: 34;
2013
+ readonly total: 36;
1990
2014
  };
1991
2015
  /** x402 settlement revenue split. Weaver = the data subject earning Identity Income. */
1992
2016
  declare const REVENUE_SPLIT: {
@@ -2164,8 +2188,8 @@ interface HomepageOutput {
2164
2188
  */
2165
2189
  type HomepageCallerVertical = "workplace" | "education" | "personal" | "civic" | "agent" | "unknown";
2166
2190
  /** Maximum sizes from the spec. SDK consumers can use these to validate
2167
- * input before sending. Mirrored from
2168
- * `docs/technical/alter-portfolio-manifest-v1.md` (forthcoming). */
2191
+ * input before sending. Mirrored from the ALTER portfolio manifest spec v1
2192
+ * (forthcoming). */
2169
2193
  declare const HOMEPAGE_LIMITS: {
2170
2194
  readonly whoami_max_chars: 240;
2171
2195
  readonly opener_max_chars: 280;
@@ -2178,8 +2202,7 @@ declare const HOMEPAGE_LIMITS: {
2178
2202
  * @truealter/sdk: theme pack types
2179
2203
  *
2180
2204
  * Wire-format types for ALTER theme packs and `themes.lock` composition
2181
- * manifests. The full specification lives in
2182
- * `docs/technical/alter-theme-pack-spec-v1.md`.
2205
+ * manifests. The full specification lives in the ALTER theme pack spec v1.
2183
2206
  *
2184
2207
  * These types describe the on-the-wire shape of theme manifests as they
2185
2208
  * are produced by `alter theme install`, persisted to `themes.lock`,
@@ -2205,7 +2228,7 @@ interface ThemeMeta {
2205
2228
  name: string;
2206
2229
  /** SemVer-shaped recommended; informational only. Resolution uses pack_id. */
2207
2230
  version: string;
2208
- /** MUST be a ~handle whose D-ID8 public key signs the pack. */
2231
+ /** MUST be a ~handle whose signing public key signs the pack. */
2209
2232
  author: string;
2210
2233
  /** ≤ 240 characters after NFC. */
2211
2234
  description: string;
@@ -2267,7 +2290,7 @@ interface ThemeManifestV1 {
2267
2290
  }
2268
2291
  /**
2269
2292
  * The `.sig` file accompanying every pack. Verification logic lives in
2270
- * `alter-cli/src/theme/sign.ts`; this is the wire-format type only.
2293
+ * the CLI client; this is the wire-format type only.
2271
2294
  */
2272
2295
  interface ThemeSignatureManifest {
2273
2296
  /** SHA-256 multihash of the canonical-form pack. */
@@ -2303,7 +2326,7 @@ interface ThemeLockEntry {
2303
2326
  */
2304
2327
  interface ThemesLockV1 {
2305
2328
  schema_version: 1;
2306
- /** e.g. "alter-cli/0.5.0", informational only. */
2329
+ /** Free-form producer tag, e.g. "@truealter/cli/1.0.0", informational only. */
2307
2330
  generated_by: string;
2308
2331
  /** RFC 3339 UTC timestamp at lockfile-write time. */
2309
2332
  generated_at: string;
@@ -2342,7 +2365,7 @@ interface ThemeShareOutput {
2342
2365
  message: string;
2343
2366
  };
2344
2367
  }
2345
- /** v1 schema constants. Mirror the spec at docs/technical/alter-theme-pack-spec-v1.md. */
2368
+ /** v1 schema constants. Mirror the ALTER theme pack spec v1. */
2346
2369
  declare const THEME_LIMITS: {
2347
2370
  readonly meta_name_pattern: RegExp;
2348
2371
  readonly meta_description_max_chars: 240;