agentic-wallet-mcp 0.3.1 → 0.4.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 +21 -12
- package/dist/server-bundle.cjs +180 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,15 +20,23 @@ VC issuance → identity proof → pay-per-use).
|
|
|
20
20
|
|
|
21
21
|
| Tool | Does | Input | Output (shape) |
|
|
22
22
|
|---|---|---|---|
|
|
23
|
-
| `wallet_status` | Report holder DID/address/network + client-supplied
|
|
24
|
-
| `prove_identity` | Answer an x401 `PROOF-REQUEST` → return the `PROOF-RESPONSE` header to replay | `{ proofRequest, vc
|
|
23
|
+
| `wallet_status` | Report holder DID/address/network + held VCs (client-supplied, or the local cache) | `{ heldCredentials? }` | `{ holderDid, zetrixAddress, network, credentials }` |
|
|
24
|
+
| `prove_identity` | Answer an x401 `PROOF-REQUEST` → return the `PROOF-RESPONSE` header to replay | `{ proofRequest, vc?, revealAttribute?, issuerKeys? }` | `{ proofResponseHeader, verified, presentationId }` |
|
|
25
25
|
| `pay_and_fetch` | Fetch a URL, auto-pay with x402 (self-pay via Wallet BE) on `402` | `{ url, method?, headers?, body? }` | `{ status, body, paymentMade, amountPaid, amountPaidHuman, asset }` |
|
|
26
|
-
| `subscribe_and_issue` |
|
|
26
|
+
| `subscribe_and_issue` | Reuse a cached VC if still valid, else pay x402 → MBI issues → return the VC | `{ templateId, attributes, expirationDate?, dryRun?, forceReissue? }` | `{ issued, vcId, vc, txHash, fromCache? }` |
|
|
27
27
|
| `create_holder_account` | Onboarding: manually mint an additional/replacement HSM account (the MCP already auto-creates one at startup if `ZETRIX_ADDRESS` is omitted — see Environment below) | `{ password, label?, purpose? }` | `{ zetrixAddress, holderDid, publicKeyHex, message }` |
|
|
28
28
|
|
|
29
|
-
>
|
|
30
|
-
>
|
|
31
|
-
>
|
|
29
|
+
> **VCs are cached locally**, keyed by `templateId`, under `~/.agentic-wallet-mcp/vc-cache/`
|
|
30
|
+
> (scoped per network + holder — different identities or networks never share a cache).
|
|
31
|
+
> `subscribe_and_issue` checks the cache before paying: a still-valid cached VC is returned
|
|
32
|
+
> immediately with `fromCache: true` and **no payment made**; pass `forceReissue: true` to pay
|
|
33
|
+
> and issue fresh regardless. `wallet_status`/`prove_identity` fall back to the cache
|
|
34
|
+
> automatically when you don't pass `heldCredentials`/`vc` explicitly — `prove_identity` only
|
|
35
|
+
> auto-selects when there's exactly one valid cached VC; with zero or several, it errors and
|
|
36
|
+
> asks you to pass `vc` explicitly. Explicitly passing `vc`/`heldCredentials` (including `[]`)
|
|
37
|
+
> always overrides the cache. Validity is read from the VC's own `validUntil` field, falling
|
|
38
|
+
> back to the `expirationDate` requested at issuance; a VC with neither is cached indefinitely.
|
|
39
|
+
> All Ed25519 signing still goes through Wallet BE HSM; no plaintext private keys.
|
|
32
40
|
|
|
33
41
|
> `create_holder_account` mints a **brand-new** keypair — Wallet BE's `/account/create` has no
|
|
34
42
|
> way to provision a pre-chosen address. It returns the new `zetrixAddress`/`holderDid` for you
|
|
@@ -126,8 +134,9 @@ network:
|
|
|
126
134
|
|
|
127
135
|
Only set any of the four explicitly if you run your own instance of that service instead of the
|
|
128
136
|
default one — an explicit value always wins over the network default. The `*.myegdev.com`
|
|
129
|
-
(testnet) hosts are
|
|
130
|
-
|
|
137
|
+
(testnet) hosts are public endpoints — no VPN needed; if you're on a corporate VPN and one of
|
|
138
|
+
them times out, disconnecting it is more likely to fix that than connecting it. See "Network
|
|
139
|
+
reachability & troubleshooting" below.
|
|
131
140
|
|
|
132
141
|
That's the complete list — no VC-MCP subprocess, no BaaS gateway key, no manually-configured
|
|
133
142
|
BBS+ key to set up.
|
|
@@ -220,13 +229,13 @@ Independent of Phases 2/3 — no VC or identity proof involved, just a fresh pay
|
|
|
220
229
|
|
|
221
230
|
## Network reachability & troubleshooting
|
|
222
231
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
232
|
+
The `*.myegdev.com` testnet endpoints are public — no VPN needed to reach them. One dependency
|
|
233
|
+
sits behind a CDN edge (see the ZID resolver row below). When something that worked before
|
|
234
|
+
suddenly times out or 403s, check this table before assuming a code regression.
|
|
226
235
|
|
|
227
236
|
| Symptom | Cause | Fix / status |
|
|
228
237
|
|---|---|---|
|
|
229
|
-
| `Wallet BE /wallet/hsm/sign-blob request failed <- fetch failed <- UND_ERR_CONNECT_TIMEOUT` (or same for `mbi-vc.myegdev.com`) |
|
|
238
|
+
| `Wallet BE /wallet/hsm/sign-blob request failed <- fetch failed <- UND_ERR_CONNECT_TIMEOUT` (or same for `mbi-vc.myegdev.com`) | Transient network issue, or (if you're on a corporate VPN) the VPN routing away from the public internet | If you're on a VPN, try disconnecting it and retrying. Verify reachability directly: `curl -I https://wallet-api.myegdev.com/server` |
|
|
230
239
|
| `VP derivation failed <- ZID resolver HTTP 403 ... cf-mitigated: challenge` | ZID resolver (`zid-resolver-sandbox.zetrix.com`) sitting behind a Cloudflare **managed challenge** that blocks plain server-to-server `fetch` | Server-to-server access to the resolver must be allowlisted so it returns `200` directly. If the challenge is active, `prove_identity`'s `issuerKeys` input is the fallback — fetch the DID document via a real browser (it clears the JS challenge) and pass its `verificationMethod` entries' `publicKeyMultibase` (BBS+) / `publicKeyHex` (Ed25519) directly. |
|
|
231
240
|
| `OID4VP backend returned a malformed presentation definition` | Historical SDK bug: the live sandbox's `GET /v1/presentation/{id}` response has no `expires_at` field, but the SDK guard required one | **Fixed** in `x401-zetrix-client` — `expiresAt` is now optional on `PresentationDefinition`. If you see this, you're on a stale cached `npx` install — clear it (`npx clear-npx-cache` or bump the version) to pick up the current published `agentic-wallet-mcp`. |
|
|
232
241
|
| `SUBMIT_FAILED: OID4VP backend returned 401 ... Missing X-Wallet-Public-Key header` | Historical SDK gap: `POST /v1/presentation/submit` requires wallet-auth headers (`X-Wallet-Public-Key` / `X-Wallet-Signed-Data`, holder signs their own address) that the SDK didn't send | **Fixed** — `X401Wallet` now accepts an injected `submitAuth` provider and the wallet wires it automatically; nothing to configure. |
|
package/dist/server-bundle.cjs
CHANGED
|
@@ -2233,8 +2233,8 @@ var require_resolve = __commonJS({
|
|
|
2233
2233
|
}
|
|
2234
2234
|
return count;
|
|
2235
2235
|
}
|
|
2236
|
-
function getFullPath(resolver, id = "",
|
|
2237
|
-
if (
|
|
2236
|
+
function getFullPath(resolver, id = "", normalize2) {
|
|
2237
|
+
if (normalize2 !== false)
|
|
2238
2238
|
id = normalizeId(id);
|
|
2239
2239
|
const p = resolver.parse(id);
|
|
2240
2240
|
return _getFullPath(resolver, p);
|
|
@@ -3630,7 +3630,7 @@ var require_fast_uri = __commonJS({
|
|
|
3630
3630
|
"use strict";
|
|
3631
3631
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3632
3632
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3633
|
-
function
|
|
3633
|
+
function normalize2(uri, options) {
|
|
3634
3634
|
if (typeof uri === "string") {
|
|
3635
3635
|
uri = /** @type {T} */
|
|
3636
3636
|
normalizeString(uri, options);
|
|
@@ -3897,7 +3897,7 @@ var require_fast_uri = __commonJS({
|
|
|
3897
3897
|
}
|
|
3898
3898
|
var fastUri = {
|
|
3899
3899
|
SCHEMES,
|
|
3900
|
-
normalize,
|
|
3900
|
+
normalize: normalize2,
|
|
3901
3901
|
resolve,
|
|
3902
3902
|
resolveComponent,
|
|
3903
3903
|
equal,
|
|
@@ -8541,13 +8541,13 @@ var require_path = __commonJS({
|
|
|
8541
8541
|
return /^(?:\/|\w+:)/.test(path2);
|
|
8542
8542
|
}
|
|
8543
8543
|
);
|
|
8544
|
-
var
|
|
8544
|
+
var normalize2 = (
|
|
8545
8545
|
/**
|
|
8546
8546
|
* Normalizes the specified path.
|
|
8547
8547
|
* @param {string} path Path to normalize
|
|
8548
8548
|
* @returns {string} Normalized path
|
|
8549
8549
|
*/
|
|
8550
|
-
path.normalize = function
|
|
8550
|
+
path.normalize = function normalize3(path2) {
|
|
8551
8551
|
path2 = path2.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
|
|
8552
8552
|
var parts = path2.split("/"), absolute = isAbsolute(path2), prefix = "";
|
|
8553
8553
|
if (absolute)
|
|
@@ -8570,12 +8570,12 @@ var require_path = __commonJS({
|
|
|
8570
8570
|
);
|
|
8571
8571
|
path.resolve = function resolve(originPath, includePath, alreadyNormalized) {
|
|
8572
8572
|
if (!alreadyNormalized)
|
|
8573
|
-
includePath =
|
|
8573
|
+
includePath = normalize2(includePath);
|
|
8574
8574
|
if (isAbsolute(includePath))
|
|
8575
8575
|
return includePath;
|
|
8576
8576
|
if (!alreadyNormalized)
|
|
8577
|
-
originPath =
|
|
8578
|
-
return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ?
|
|
8577
|
+
originPath = normalize2(originPath);
|
|
8578
|
+
return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ? normalize2(originPath + "/" + includePath) : includePath;
|
|
8579
8579
|
};
|
|
8580
8580
|
}
|
|
8581
8581
|
});
|
|
@@ -12540,11 +12540,14 @@ __export(index_exports, {
|
|
|
12540
12540
|
buildToolList: () => buildToolList
|
|
12541
12541
|
});
|
|
12542
12542
|
module.exports = __toCommonJS(index_exports);
|
|
12543
|
+
var import_node_crypto2 = require("node:crypto");
|
|
12544
|
+
var import_node_os = require("node:os");
|
|
12545
|
+
var import_node_path2 = require("node:path");
|
|
12543
12546
|
|
|
12544
12547
|
// package.json
|
|
12545
12548
|
var package_default = {
|
|
12546
12549
|
name: "agentic-wallet-mcp",
|
|
12547
|
-
version: "0.
|
|
12550
|
+
version: "0.4.0",
|
|
12548
12551
|
description: "Agent-facing MCP wallet for Zetrix \u2014 orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
12549
12552
|
keywords: ["mcp", "model-context-protocol", "zetrix", "wallet", "x401", "x402", "blockchain"],
|
|
12550
12553
|
license: "MIT",
|
|
@@ -21607,7 +21610,7 @@ async function resolveAssetSymbol(asset, query) {
|
|
|
21607
21610
|
}
|
|
21608
21611
|
|
|
21609
21612
|
// src/clients/template-info-client.ts
|
|
21610
|
-
async function
|
|
21613
|
+
async function fetchTemplateFields(templateId, registryAddress, nodeBaseUrl, query) {
|
|
21611
21614
|
const key = `template__${templateId}`;
|
|
21612
21615
|
const url = `${nodeBaseUrl.replace(/\/+$/, "")}/getAccountMetaData?address=${encodeURIComponent(registryAddress)}&key=${encodeURIComponent(key)}`;
|
|
21613
21616
|
let res;
|
|
@@ -21621,10 +21624,16 @@ async function fetchTemplateRequiredFields(templateId, registryAddress, nodeBase
|
|
|
21621
21624
|
if (!value) return null;
|
|
21622
21625
|
try {
|
|
21623
21626
|
const parsed = JSON.parse(value);
|
|
21624
|
-
if (parsed.applyFormat === void 0 || parsed.applyFormat === null) return [];
|
|
21627
|
+
if (parsed.applyFormat === void 0 || parsed.applyFormat === null) return { required: [], allKeys: [] };
|
|
21625
21628
|
const format = typeof parsed.applyFormat === "string" ? JSON.parse(parsed.applyFormat) : parsed.applyFormat;
|
|
21626
21629
|
if (!Array.isArray(format)) return null;
|
|
21627
|
-
|
|
21630
|
+
const entries = format.filter(
|
|
21631
|
+
(e) => !!e && typeof e.key === "string" && e.key !== ""
|
|
21632
|
+
);
|
|
21633
|
+
return {
|
|
21634
|
+
required: entries.filter((e) => e.mandatory === 1).map((e) => e.key),
|
|
21635
|
+
allKeys: entries.map((e) => e.key)
|
|
21636
|
+
};
|
|
21628
21637
|
} catch {
|
|
21629
21638
|
return null;
|
|
21630
21639
|
}
|
|
@@ -21721,6 +21730,10 @@ function findLeafPath(obj, key) {
|
|
|
21721
21730
|
}
|
|
21722
21731
|
return null;
|
|
21723
21732
|
}
|
|
21733
|
+
function flattenLeafPaths(obj, prefix = []) {
|
|
21734
|
+
if (!isRecord2(obj)) return prefix.length ? [prefix.join(".")] : [];
|
|
21735
|
+
return Object.entries(obj).flatMap(([k, v]) => flattenLeafPaths(v, [...prefix, k]));
|
|
21736
|
+
}
|
|
21724
21737
|
function dcqlToRevealAttributes(credentialQuery, vc) {
|
|
21725
21738
|
if (!isRecord2(credentialQuery) || !Array.isArray(credentialQuery.credentials)) return [];
|
|
21726
21739
|
const subject = isRecord2(vc) && isRecord2(vc.credentialSubject) ? vc.credentialSubject : void 0;
|
|
@@ -21743,7 +21756,9 @@ function dcqlToRevealAttributes(credentialQuery, vc) {
|
|
|
21743
21756
|
}
|
|
21744
21757
|
}
|
|
21745
21758
|
}
|
|
21746
|
-
return out;
|
|
21759
|
+
if (!subject) return out;
|
|
21760
|
+
const canonicalIndex = new Map(flattenLeafPaths(subject).map((path, i) => [path, i]));
|
|
21761
|
+
return out.map((path, i) => ({ path, key: canonicalIndex.get(path) ?? Number.MAX_SAFE_INTEGER, i })).sort((a, b) => a.key - b.key || a.i - b.i).map((entry) => entry.path);
|
|
21747
21762
|
}
|
|
21748
21763
|
var MbiVpAdapter = class {
|
|
21749
21764
|
constructor(mbi, signHexBlob, signMessage, holderAddress, resolveIssuerKeys, present) {
|
|
@@ -21950,17 +21965,92 @@ function zetrixHexStringToBytes(s) {
|
|
|
21950
21965
|
return out;
|
|
21951
21966
|
}
|
|
21952
21967
|
|
|
21968
|
+
// src/clients/vc-cache.ts
|
|
21969
|
+
var import_node_crypto = require("node:crypto");
|
|
21970
|
+
var import_promises = require("node:fs/promises");
|
|
21971
|
+
var import_node_path = require("node:path");
|
|
21972
|
+
function cacheFileName(templateId) {
|
|
21973
|
+
return `${(0, import_node_crypto.createHash)("sha256").update(templateId).digest("hex")}.json`;
|
|
21974
|
+
}
|
|
21975
|
+
function isCachedVcShape(value) {
|
|
21976
|
+
return typeof value === "object" && value !== null && typeof value.templateId === "string" && "vc" in value && typeof value.issuedAt === "string";
|
|
21977
|
+
}
|
|
21978
|
+
function createFsVcCache(baseDir) {
|
|
21979
|
+
return {
|
|
21980
|
+
async get(templateId) {
|
|
21981
|
+
try {
|
|
21982
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path.join)(baseDir, cacheFileName(templateId)), "utf8");
|
|
21983
|
+
const parsed = JSON.parse(raw);
|
|
21984
|
+
return isCachedVcShape(parsed) ? parsed : null;
|
|
21985
|
+
} catch {
|
|
21986
|
+
return null;
|
|
21987
|
+
}
|
|
21988
|
+
},
|
|
21989
|
+
async set(templateId, entry) {
|
|
21990
|
+
await (0, import_promises.mkdir)(baseDir, { recursive: true, mode: 448 });
|
|
21991
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(baseDir, cacheFileName(templateId)), JSON.stringify(entry), { encoding: "utf8", mode: 384 });
|
|
21992
|
+
},
|
|
21993
|
+
async list() {
|
|
21994
|
+
let files;
|
|
21995
|
+
try {
|
|
21996
|
+
files = await (0, import_promises.readdir)(baseDir);
|
|
21997
|
+
} catch {
|
|
21998
|
+
return [];
|
|
21999
|
+
}
|
|
22000
|
+
const entries = await Promise.all(
|
|
22001
|
+
files.filter((f) => f.endsWith(".json")).map(async (f) => {
|
|
22002
|
+
try {
|
|
22003
|
+
const parsed = JSON.parse(await (0, import_promises.readFile)((0, import_node_path.join)(baseDir, f), "utf8"));
|
|
22004
|
+
return isCachedVcShape(parsed) ? parsed : null;
|
|
22005
|
+
} catch {
|
|
22006
|
+
return null;
|
|
22007
|
+
}
|
|
22008
|
+
})
|
|
22009
|
+
);
|
|
22010
|
+
return entries.filter((e) => e !== null);
|
|
22011
|
+
}
|
|
22012
|
+
};
|
|
22013
|
+
}
|
|
22014
|
+
function isVcValid(entry, now = /* @__PURE__ */ new Date()) {
|
|
22015
|
+
if (!entry.validUntil) return true;
|
|
22016
|
+
const expiry = new Date(entry.validUntil);
|
|
22017
|
+
if (Number.isNaN(expiry.getTime())) return false;
|
|
22018
|
+
return expiry.getTime() > now.getTime();
|
|
22019
|
+
}
|
|
22020
|
+
function extractValidUntil(vc, fallback) {
|
|
22021
|
+
if (typeof vc === "object" && vc !== null && "validUntil" in vc) {
|
|
22022
|
+
const v = vc.validUntil;
|
|
22023
|
+
if (typeof v === "string") return v;
|
|
22024
|
+
}
|
|
22025
|
+
return fallback;
|
|
22026
|
+
}
|
|
22027
|
+
|
|
21953
22028
|
// src/orchestrator/subscribe.ts
|
|
21954
22029
|
async function subscribeAndIssue(deps, opts) {
|
|
21955
22030
|
if (!/^did:zid:/.test(opts.templateId)) {
|
|
21956
22031
|
return { issued: false, reason: `templateId must be a did:zid:... credential-definition id, got "${opts.templateId}"` };
|
|
21957
22032
|
}
|
|
22033
|
+
if (deps.cache && !opts.forceReissue && !opts.dryRun) {
|
|
22034
|
+
const cached2 = await deps.cache.get(opts.templateId);
|
|
22035
|
+
if (cached2 && isVcValid(cached2)) {
|
|
22036
|
+
return {
|
|
22037
|
+
issued: true,
|
|
22038
|
+
vcId: cached2.vcId,
|
|
22039
|
+
vc: cached2.vc,
|
|
22040
|
+
txHash: cached2.txHash,
|
|
22041
|
+
paidAsset: cached2.paidAsset,
|
|
22042
|
+
amountPaid: cached2.amountPaid,
|
|
22043
|
+
fromCache: true
|
|
22044
|
+
};
|
|
22045
|
+
}
|
|
22046
|
+
}
|
|
22047
|
+
const fields = deps.resolveTemplateFields ? await deps.resolveTemplateFields(opts.templateId) : null;
|
|
21958
22048
|
const { agentDid, ...rest } = opts.attributes ?? {};
|
|
21959
|
-
const
|
|
21960
|
-
const
|
|
21961
|
-
if (!opts.dryRun &&
|
|
22049
|
+
const shouldAutoFillAgentDid = !agentDid && fields !== null && fields.allKeys.includes("agentDid");
|
|
22050
|
+
const attributes = shouldAutoFillAgentDid ? { agentDid: deps.holderDid, ...rest } : opts.attributes ?? {};
|
|
22051
|
+
if (!opts.dryRun && fields) {
|
|
21962
22052
|
const attrs = attributes;
|
|
21963
|
-
const missing =
|
|
22053
|
+
const missing = fields.required.filter((k) => attrs[k] === void 0 || attrs[k] === null || attrs[k] === "");
|
|
21964
22054
|
if (missing.length > 0) {
|
|
21965
22055
|
return {
|
|
21966
22056
|
issued: false,
|
|
@@ -21986,7 +22076,7 @@ async function subscribeAndIssue(deps, opts) {
|
|
|
21986
22076
|
asset: quotedAsset,
|
|
21987
22077
|
maxAmountRequired: accept.maxAmountRequired,
|
|
21988
22078
|
payTo: accept.payTo,
|
|
21989
|
-
...
|
|
22079
|
+
...fields ? { requiredAttributes: fields.required } : {}
|
|
21990
22080
|
}
|
|
21991
22081
|
};
|
|
21992
22082
|
}
|
|
@@ -21994,13 +22084,26 @@ async function subscribeAndIssue(deps, opts) {
|
|
|
21994
22084
|
const issued = await deps.mbi.applySettle({ ...body, paymentId: challenge.paymentId }, xPayment);
|
|
21995
22085
|
const rawAsset = String(accept.asset ?? "");
|
|
21996
22086
|
const paidAsset = deps.resolveSymbol ? await deps.resolveSymbol(rawAsset) : rawAsset;
|
|
22087
|
+
const amountPaid = String(accept.maxAmountRequired ?? "");
|
|
22088
|
+
if (deps.cache) {
|
|
22089
|
+
await deps.cache.set(opts.templateId, {
|
|
22090
|
+
templateId: opts.templateId,
|
|
22091
|
+
vc: issued.verifiableCredential,
|
|
22092
|
+
vcId: issued.vcId,
|
|
22093
|
+
txHash: issued.txHash,
|
|
22094
|
+
paidAsset,
|
|
22095
|
+
amountPaid,
|
|
22096
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22097
|
+
validUntil: extractValidUntil(issued.verifiableCredential, opts.expirationDate)
|
|
22098
|
+
});
|
|
22099
|
+
}
|
|
21997
22100
|
return {
|
|
21998
22101
|
issued: true,
|
|
21999
22102
|
vcId: issued.vcId,
|
|
22000
22103
|
vc: issued.verifiableCredential,
|
|
22001
22104
|
txHash: issued.txHash,
|
|
22002
22105
|
paidAsset,
|
|
22003
|
-
amountPaid
|
|
22106
|
+
amountPaid
|
|
22004
22107
|
};
|
|
22005
22108
|
}
|
|
22006
22109
|
|
|
@@ -22018,22 +22121,59 @@ async function createHolderAccount(create, input) {
|
|
|
22018
22121
|
return { zetrixAddress, holderDid, publicKeyHex, message };
|
|
22019
22122
|
}
|
|
22020
22123
|
|
|
22124
|
+
// src/template-aliases.ts
|
|
22125
|
+
var TEMPLATE_ALIASES = [
|
|
22126
|
+
{
|
|
22127
|
+
match: "birthcert",
|
|
22128
|
+
testnet: "did:zid:d6b783559acf6ba0f7ef6e1365bdaf0774d622d8d22728ca6323677f49ee94f8",
|
|
22129
|
+
mainnet: "did:zid:032cb99be3577beccfc6252783c49c83673af38f8456d73462043654d7764e83"
|
|
22130
|
+
}
|
|
22131
|
+
];
|
|
22132
|
+
function normalize(s) {
|
|
22133
|
+
return s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
22134
|
+
}
|
|
22135
|
+
function resolveTemplateAlias(input, network) {
|
|
22136
|
+
if (/^did:zid:/.test(input)) return void 0;
|
|
22137
|
+
const normalized = normalize(input);
|
|
22138
|
+
const entry = TEMPLATE_ALIASES.find((e) => normalized.includes(e.match));
|
|
22139
|
+
if (!entry) return void 0;
|
|
22140
|
+
return network.includes("testnet") ? entry.testnet : entry.mainnet;
|
|
22141
|
+
}
|
|
22142
|
+
|
|
22021
22143
|
// src/mcp-tools.ts
|
|
22144
|
+
async function loadValidCachedCredentials(cache) {
|
|
22145
|
+
if (!cache) return [];
|
|
22146
|
+
const all = await cache.list();
|
|
22147
|
+
return all.filter((entry) => isVcValid(entry));
|
|
22148
|
+
}
|
|
22022
22149
|
function createTools(deps) {
|
|
22023
22150
|
return {
|
|
22024
22151
|
async wallet_status(input = {}) {
|
|
22025
22152
|
const balances = deps.getBalances ? await deps.getBalances() : void 0;
|
|
22153
|
+
const credentials = input.heldCredentials ?? (await loadValidCachedCredentials(deps.cache)).map((entry) => entry.vc);
|
|
22026
22154
|
return {
|
|
22027
22155
|
holderDid: deps.config.holderDid,
|
|
22028
22156
|
zetrixAddress: deps.config.zetrixAddress,
|
|
22029
22157
|
network: deps.config.network,
|
|
22030
|
-
credentials
|
|
22158
|
+
credentials,
|
|
22031
22159
|
...balances !== void 0 ? { balances } : {}
|
|
22032
22160
|
};
|
|
22033
22161
|
},
|
|
22034
|
-
prove_identity(input) {
|
|
22162
|
+
async prove_identity(input) {
|
|
22163
|
+
let vc = input.vc;
|
|
22164
|
+
if (vc === void 0) {
|
|
22165
|
+
const cached2 = await loadValidCachedCredentials(deps.cache);
|
|
22166
|
+
if (cached2.length === 0) {
|
|
22167
|
+
throw new Error("prove_identity: no vc supplied and no valid credential is cached \u2014 call subscribe_and_issue first, or pass vc explicitly.");
|
|
22168
|
+
}
|
|
22169
|
+
if (cached2.length > 1) {
|
|
22170
|
+
const ids = cached2.map((entry) => entry.templateId).join(", ");
|
|
22171
|
+
throw new Error(`prove_identity: no vc supplied and multiple credentials are cached (templateIds: ${ids}) \u2014 pass vc explicitly to select one.`);
|
|
22172
|
+
}
|
|
22173
|
+
vc = cached2[0].vc;
|
|
22174
|
+
}
|
|
22035
22175
|
const wallet = deps.makeWallet({
|
|
22036
|
-
vc
|
|
22176
|
+
vc,
|
|
22037
22177
|
revealAttribute: input.revealAttribute,
|
|
22038
22178
|
issuerKeys: input.issuerKeys
|
|
22039
22179
|
});
|
|
@@ -22043,7 +22183,8 @@ function createTools(deps) {
|
|
|
22043
22183
|
return payAndFetch(deps.payer, input);
|
|
22044
22184
|
},
|
|
22045
22185
|
subscribe_and_issue(input) {
|
|
22046
|
-
|
|
22186
|
+
const resolved = resolveTemplateAlias(input.templateId, deps.config.network);
|
|
22187
|
+
return subscribeAndIssue(deps.subscribeDeps, resolved ? { ...input, templateId: resolved } : input);
|
|
22047
22188
|
},
|
|
22048
22189
|
create_holder_account(input) {
|
|
22049
22190
|
return createHolderAccount(deps.createAccount, input);
|
|
@@ -22073,7 +22214,7 @@ function buildToolList() {
|
|
|
22073
22214
|
inputSchema: {
|
|
22074
22215
|
type: "object",
|
|
22075
22216
|
properties: {
|
|
22076
|
-
heldCredentials: { type: "array", items: { type: "object" }, description: "VCs the client holds
|
|
22217
|
+
heldCredentials: { type: "array", items: { type: "object" }, description: "VCs the client holds. Omit to report whatever the wallet has cached locally from prior subscribe_and_issue calls instead." }
|
|
22077
22218
|
}
|
|
22078
22219
|
}
|
|
22079
22220
|
},
|
|
@@ -22084,7 +22225,7 @@ function buildToolList() {
|
|
|
22084
22225
|
type: "object",
|
|
22085
22226
|
properties: {
|
|
22086
22227
|
proofRequest: { type: "string", description: "The PROOF-REQUEST header value from the 401 challenge." },
|
|
22087
|
-
vc: { type: "object", description: "The
|
|
22228
|
+
vc: { type: "object", description: "The VerifiableCredential to present. Omit to use the wallet's single locally-cached credential, if there is exactly one \u2014 the call fails with a clear error if none or several are cached." },
|
|
22088
22229
|
revealAttribute: { type: "array", items: { type: "string" }, description: "Dotted disclosure paths to reveal. Omit to reveal exactly the claims the challenge (DCQL) requests; a challenge naming no claims reveals all." },
|
|
22089
22230
|
issuerKeys: {
|
|
22090
22231
|
type: "object",
|
|
@@ -22095,7 +22236,7 @@ function buildToolList() {
|
|
|
22095
22236
|
description: "Optional issuer verification keys to bypass the ZID resolver when it is unreachable (e.g. Cloudflare-gated). When set, resolution is skipped."
|
|
22096
22237
|
}
|
|
22097
22238
|
},
|
|
22098
|
-
required: ["proofRequest"
|
|
22239
|
+
required: ["proofRequest"]
|
|
22099
22240
|
}
|
|
22100
22241
|
},
|
|
22101
22242
|
{
|
|
@@ -22114,13 +22255,13 @@ function buildToolList() {
|
|
|
22114
22255
|
},
|
|
22115
22256
|
{
|
|
22116
22257
|
name: "subscribe_and_issue",
|
|
22117
|
-
description: "Obtain a VC from MBI: build the signed payload, pay x402, and return the issued credential. Payment is asset-agnostic \u2014 MBI's 402 challenge may quote the native ZETRIX token or a ZTP20 token (e.g. JMYR); pass dryRun:true first to see the quoted asset/amount for free before committing to pay.",
|
|
22258
|
+
description: "Obtain a VC from MBI: build the signed payload, pay x402, and return the issued credential. If a still-valid credential for this templateId is already cached locally, it is returned directly with no payment (fromCache: true) \u2014 pass forceReissue:true to pay and issue fresh regardless. Payment is asset-agnostic \u2014 MBI's 402 challenge may quote the native ZETRIX token or a ZTP20 token (e.g. JMYR); pass dryRun:true first to see the quoted asset/amount for free before committing to pay.",
|
|
22118
22259
|
inputSchema: {
|
|
22119
22260
|
type: "object",
|
|
22120
22261
|
properties: {
|
|
22121
22262
|
templateId: {
|
|
22122
22263
|
type: "string",
|
|
22123
|
-
description: `The MBI credential-definition id to issue, e.g. "did:zid:...". Take this from the x401 challenge's credential_requirements.query.credentials[].id \u2014 NOT from requirementsId (that's just a label for the requirement set, e.g. "agent-identity").`
|
|
22264
|
+
description: `The MBI credential-definition id to issue, e.g. "did:zid:...". Take this from the x401 challenge's credential_requirements.query.credentials[].id \u2014 NOT from requirementsId (that's just a label for the requirement set, e.g. "agent-identity"). A known template's natural-language name (e.g. "AI Birthcert") is also accepted and resolved to the right did:zid:... for the configured network.`
|
|
22124
22265
|
},
|
|
22125
22266
|
attributes: {
|
|
22126
22267
|
type: "object",
|
|
@@ -22130,6 +22271,10 @@ function buildToolList() {
|
|
|
22130
22271
|
dryRun: {
|
|
22131
22272
|
type: "boolean",
|
|
22132
22273
|
description: "Stop after MBI's free phase-1 quote and return { quote: { asset, maxAmountRequired, payTo, requiredAttributes? } } without paying or issuing \u2014 use this to check the payment requirement AND the template's required attributes (read from chain) before spending funds."
|
|
22274
|
+
},
|
|
22275
|
+
forceReissue: {
|
|
22276
|
+
type: "boolean",
|
|
22277
|
+
description: "Skip the local cache and pay + issue a fresh credential regardless of what is already cached."
|
|
22133
22278
|
}
|
|
22134
22279
|
},
|
|
22135
22280
|
required: ["templateId", "attributes"]
|
|
@@ -22186,7 +22331,9 @@ async function main() {
|
|
|
22186
22331
|
const resolveSymbol = (asset) => resolveAssetSymbol(asset, contractQuery);
|
|
22187
22332
|
const nodeBaseUrl = `https://${config2.nodeHost}${config2.nodePort ? `:${config2.nodePort}` : ""}`;
|
|
22188
22333
|
const nodeMetaQuery = (url) => fetch(url, { headers: { Accept: "application/json" } }).then((r) => r.json());
|
|
22189
|
-
const
|
|
22334
|
+
const resolveTemplateFields = (templateId) => fetchTemplateFields(templateId, config2.templateRegistryAddress, nodeBaseUrl, nodeMetaQuery);
|
|
22335
|
+
const cacheScope = (0, import_node_crypto2.createHash)("sha256").update(`${config2.network}:${zetrixAddress}`).digest("hex");
|
|
22336
|
+
const vcCache = createFsVcCache((0, import_node_path2.join)((0, import_node_os.homedir)(), ".agentic-wallet-mcp", "vc-cache", cacheScope));
|
|
22190
22337
|
const pay = (accept) => {
|
|
22191
22338
|
assertWithinPaymentCap(accept, config2.maxPaymentAmount);
|
|
22192
22339
|
return import_x402_zetrix_client.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn);
|
|
@@ -22229,8 +22376,9 @@ async function main() {
|
|
|
22229
22376
|
config: { holderDid, zetrixAddress, network: config2.network },
|
|
22230
22377
|
makeWallet,
|
|
22231
22378
|
payer,
|
|
22232
|
-
subscribeDeps: { mbi, sign: subscribeSign, pay, resolveSymbol, holderDid,
|
|
22233
|
-
createAccount: (password, label, purpose) => be.createAccount(password, label, purpose)
|
|
22379
|
+
subscribeDeps: { mbi, sign: subscribeSign, pay, resolveSymbol, holderDid, resolveTemplateFields, cache: vcCache },
|
|
22380
|
+
createAccount: (password, label, purpose) => be.createAccount(password, label, purpose),
|
|
22381
|
+
cache: vcCache
|
|
22234
22382
|
};
|
|
22235
22383
|
const tools = createTools(deps);
|
|
22236
22384
|
const server = new Server({ name: "agentic-wallet-mcp", version: packageVersion }, { capabilities: { tools: {} } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-wallet-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Agent-facing MCP wallet for Zetrix — orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
5
5
|
"keywords": ["mcp", "model-context-protocol", "zetrix", "wallet", "x401", "x402", "blockchain"],
|
|
6
6
|
"license": "MIT",
|