@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/README.md +92 -80
- package/dist/bin/mcp-bridge.js +141 -12
- package/dist/index.cjs +167 -23
- package/dist/index.d.cts +62 -39
- package/dist/index.d.ts +62 -39
- package/dist/index.js +167 -23
- package/package.json +3 -6
- package/dist/bin/alter-identity.js +0 -2641
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
/**
|
|
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 =
|
|
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
|
|
1913
|
+
const bridgeCommand = opts.bridgeCommand ?? "alter";
|
|
1812
1914
|
const env2 = {};
|
|
1813
|
-
env2.ALTER_MCP_ENDPOINT = opts.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
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
2693
|
+
free: 27,
|
|
2550
2694
|
/** Premium (L1-L5) tools requiring x402 payment. */
|
|
2551
|
-
premium:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
1241
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
1494
|
+
* floor-preflight: SDK-side client version-floor enforcement.
|
|
1481
1495
|
*
|
|
1482
|
-
* Sister to
|
|
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
|
|
1509
|
-
* canonical TypeScript client
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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`
|
|
1751
|
-
*
|
|
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
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
* child process and read
|
|
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
|
|
1808
|
+
/** Override the bridge command (e.g. `npx`). Default: the `alter` CLI. */
|
|
1794
1809
|
bridgeCommand?: string;
|
|
1795
|
-
/** Extra args appended after the default bridge
|
|
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:
|
|
2007
|
+
readonly free: 27;
|
|
1984
2008
|
/** Premium (L1-L5) tools requiring x402 payment. */
|
|
1985
|
-
readonly premium:
|
|
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:
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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. "
|
|
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
|
|
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;
|