@solvapay/mcp-core 0.1.0 → 0.2.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 +33 -14
- package/dist/index.cjs +63 -78
- package/dist/index.d.cts +141 -126
- package/dist/index.d.ts +141 -126
- package/dist/index.js +62 -78
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -17,9 +17,7 @@ var MCP_TOOL_NAMES = {
|
|
|
17
17
|
var SOLVAPAY_MCP_VIEW_KINDS = [
|
|
18
18
|
"checkout",
|
|
19
19
|
"account",
|
|
20
|
-
"topup"
|
|
21
|
-
"paywall",
|
|
22
|
-
"nudge"
|
|
20
|
+
"topup"
|
|
23
21
|
];
|
|
24
22
|
var TOOL_FOR_VIEW = {
|
|
25
23
|
checkout: "upgrade",
|
|
@@ -365,41 +363,19 @@ function previewJson(value, max = 400) {
|
|
|
365
363
|
}
|
|
366
364
|
}
|
|
367
365
|
|
|
368
|
-
// src/paywall-meta.ts
|
|
369
|
-
function buildPaywallUiMeta(input) {
|
|
370
|
-
return {
|
|
371
|
-
ui: {
|
|
372
|
-
resourceUri: input.resourceUri
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
|
|
377
366
|
// src/paywallToolResult.ts
|
|
378
367
|
import { PaywallError } from "@solvapay/server";
|
|
379
|
-
async function paywallToolResult(errOrGate,
|
|
368
|
+
async function paywallToolResult(errOrGate, _ctx = {}) {
|
|
380
369
|
const paywallContent = errOrGate instanceof PaywallError ? errOrGate.structuredContent : errOrGate;
|
|
381
370
|
const narrationText = errOrGate instanceof PaywallError ? errOrGate.message : paywallContent.message;
|
|
382
|
-
let structuredContent;
|
|
383
|
-
if (ctx.buildBootstrap) {
|
|
384
|
-
const bootstrap = await ctx.buildBootstrap("paywall", ctx.extra, {
|
|
385
|
-
paywall: paywallContent
|
|
386
|
-
});
|
|
387
|
-
structuredContent = bootstrap;
|
|
388
|
-
} else {
|
|
389
|
-
structuredContent = paywallContent;
|
|
390
|
-
}
|
|
391
371
|
return {
|
|
392
372
|
// Deliberately `false`: paywall is a user-actionable gate, not a
|
|
393
|
-
// tool failure.
|
|
394
|
-
//
|
|
395
|
-
// `
|
|
396
|
-
// MCPJam / Claude Desktop / ChatGPT Apps. The `structuredContent`
|
|
397
|
-
// and `content[0].text` still carry the gate reason so the LLM
|
|
398
|
-
// can narrate it, while `_meta.ui` triggers the widget.
|
|
373
|
+
// tool failure. The LLM narrates the recovery from
|
|
374
|
+
// `content[0].text` and the structured gate content on
|
|
375
|
+
// `structuredContent` is available for programmatic consumers.
|
|
399
376
|
isError: false,
|
|
400
377
|
content: [{ type: "text", text: narrationText }],
|
|
401
|
-
structuredContent
|
|
402
|
-
_meta: buildPaywallUiMeta({ resourceUri: ctx.resourceUri })
|
|
378
|
+
structuredContent: paywallContent
|
|
403
379
|
};
|
|
404
380
|
}
|
|
405
381
|
|
|
@@ -429,6 +405,30 @@ function mergeCsp(overrides) {
|
|
|
429
405
|
};
|
|
430
406
|
}
|
|
431
407
|
|
|
408
|
+
// src/hideToolsByAudience.ts
|
|
409
|
+
function applyHideToolsByAudience(server, audiences) {
|
|
410
|
+
if (!audiences || audiences.length === 0) return;
|
|
411
|
+
const hidden = new Set(audiences);
|
|
412
|
+
const inner = server.server;
|
|
413
|
+
if (!inner || typeof inner !== "object" || !(inner._requestHandlers instanceof Map)) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const handlers = inner._requestHandlers;
|
|
417
|
+
const original = handlers.get("tools/list");
|
|
418
|
+
if (!original) return;
|
|
419
|
+
handlers.set("tools/list", async (req, extra) => {
|
|
420
|
+
const res = await original(req, extra);
|
|
421
|
+
const tools = Array.isArray(res?.tools) ? res.tools : [];
|
|
422
|
+
return {
|
|
423
|
+
...res,
|
|
424
|
+
tools: tools.filter((t) => {
|
|
425
|
+
const audience = t?._meta?.audience;
|
|
426
|
+
return !hidden.has(typeof audience === "string" ? audience : "");
|
|
427
|
+
})
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
432
|
// src/descriptors.ts
|
|
433
433
|
import {
|
|
434
434
|
activatePlanCore,
|
|
@@ -472,7 +472,7 @@ function createBuildBootstrapPayload(options) {
|
|
|
472
472
|
};
|
|
473
473
|
const buildRequest = (extra) => buildSolvaPayRequest(extra, { getCustomerRef });
|
|
474
474
|
const productQueryRequest = () => buildSolvaPayRequest(void 0, { query: { productRef }, getCustomerRef: () => null });
|
|
475
|
-
return async (view, extra
|
|
475
|
+
return async (view, extra) => {
|
|
476
476
|
const customerRef = getCustomerRef(extra);
|
|
477
477
|
const wrapError = (promise) => promise.catch((err) => ({
|
|
478
478
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -519,7 +519,7 @@ function createBuildBootstrapPayload(options) {
|
|
|
519
519
|
balance: okOrNull(balanceResult),
|
|
520
520
|
usage: okOrNull(usageResult)
|
|
521
521
|
} : null;
|
|
522
|
-
|
|
522
|
+
return {
|
|
523
523
|
view,
|
|
524
524
|
productRef,
|
|
525
525
|
stripePublishableKey,
|
|
@@ -529,8 +529,6 @@ function createBuildBootstrapPayload(options) {
|
|
|
529
529
|
plans,
|
|
530
530
|
customer
|
|
531
531
|
};
|
|
532
|
-
if (extras.paywall) payload.paywall = extras.paywall;
|
|
533
|
-
return payload;
|
|
534
532
|
};
|
|
535
533
|
}
|
|
536
534
|
|
|
@@ -564,9 +562,13 @@ with clickable URLs for text-only hosts.
|
|
|
564
562
|
|
|
565
563
|
Each intent tool returns a full \`BootstrapPayload\` (merchant + product + plans
|
|
566
564
|
+ customer snapshot) so the embedded UI never fires per-view read calls. When
|
|
567
|
-
a paywalled data tool hits the usage limit, its response
|
|
568
|
-
|
|
569
|
-
|
|
565
|
+
a paywalled data tool hits the usage limit, its response is plain text: the
|
|
566
|
+
\`content[0].text\` narration names the recovery intent tool
|
|
567
|
+
(\`upgrade\` / \`topup\` / \`activate_plan\`) and inlines \`gate.checkoutUrl\` for
|
|
568
|
+
terminal-first hosts. The structured gate rides on \`structuredContent\` for
|
|
569
|
+
programmatic consumers. No widget iframe opens for a gate \u2014 the LLM reads the
|
|
570
|
+
narration and, if the user agrees, calls the named intent tool which mounts
|
|
571
|
+
the checkout / topup / account surface.
|
|
570
572
|
|
|
571
573
|
Auth is handled by the SolvaPay OAuth bridge (see \`createMcpOAuthBridge\`). The
|
|
572
574
|
bridge injects \`customer_ref\` onto every authenticated request; tools that
|
|
@@ -974,7 +976,7 @@ function buildSolvaPayPrompts(options = {}) {
|
|
|
974
976
|
}
|
|
975
977
|
|
|
976
978
|
// src/payable-handler.ts
|
|
977
|
-
import { isPaywallStructuredContent } from "@solvapay/server";
|
|
979
|
+
import { buildNudgeMessage, isPaywallStructuredContent } from "@solvapay/server";
|
|
978
980
|
|
|
979
981
|
// src/response-context.ts
|
|
980
982
|
import { PaywallError as PaywallError2 } from "@solvapay/server";
|
|
@@ -1079,7 +1081,7 @@ function buildResponseContext(params) {
|
|
|
1079
1081
|
|
|
1080
1082
|
// src/payable-handler.ts
|
|
1081
1083
|
function buildPayableHandler(solvaPay, ctx, handler) {
|
|
1082
|
-
const { product,
|
|
1084
|
+
const { product, getCustomerRef } = ctx;
|
|
1083
1085
|
const wrappedBusinessLogic = async (args, handlerContext) => {
|
|
1084
1086
|
const limits = handlerContext?.limits ?? null;
|
|
1085
1087
|
const customerRef = handlerContext?.customerRef ?? "";
|
|
@@ -1095,60 +1097,44 @@ function buildPayableHandler(solvaPay, ctx, handler) {
|
|
|
1095
1097
|
return async (args, extra) => {
|
|
1096
1098
|
const result = await protectedHandler(args, extra);
|
|
1097
1099
|
if (isPaywallStructuredContent(result.structuredContent)) {
|
|
1098
|
-
const existingMeta = typeof result._meta === "object" && result._meta !== null ? result._meta : {};
|
|
1099
|
-
const gateContent = result.structuredContent;
|
|
1100
|
-
const structuredContent = buildBootstrap ? await buildBootstrap("paywall", extra, {
|
|
1101
|
-
paywall: gateContent
|
|
1102
|
-
}) : result.structuredContent;
|
|
1103
1100
|
return {
|
|
1104
1101
|
...result,
|
|
1105
1102
|
isError: false,
|
|
1106
|
-
structuredContent
|
|
1107
|
-
_meta: {
|
|
1108
|
-
...existingMeta,
|
|
1109
|
-
...buildPaywallUiMeta({ resourceUri })
|
|
1110
|
-
}
|
|
1103
|
+
structuredContent: result.structuredContent
|
|
1111
1104
|
};
|
|
1112
1105
|
}
|
|
1113
1106
|
if (result.isError) {
|
|
1114
1107
|
return result;
|
|
1115
1108
|
}
|
|
1116
1109
|
const envelope = assertResponseResult(result.structuredContent);
|
|
1117
|
-
return unwrapResponseEnvelope(
|
|
1118
|
-
result,
|
|
1119
|
-
envelope,
|
|
1120
|
-
resourceUri,
|
|
1121
|
-
buildBootstrap,
|
|
1122
|
-
extra
|
|
1123
|
-
);
|
|
1110
|
+
return unwrapResponseEnvelope(result, envelope, extra);
|
|
1124
1111
|
};
|
|
1125
1112
|
}
|
|
1126
|
-
async function unwrapResponseEnvelope(adapterResult, envelope,
|
|
1113
|
+
async function unwrapResponseEnvelope(adapterResult, envelope, _extra) {
|
|
1127
1114
|
const { data, options, emittedBlocks } = envelope;
|
|
1128
1115
|
const textOverride = options?.text;
|
|
1129
1116
|
const nudge = options?.nudge;
|
|
1130
|
-
const
|
|
1117
|
+
const baseText = typeof textOverride === "string" ? textOverride : JSON.stringify(data);
|
|
1118
|
+
let primaryText = baseText;
|
|
1119
|
+
if (nudge) {
|
|
1120
|
+
const nudgeText = nudge.message && nudge.message.length > 0 ? nudge.message : buildNudgeMessage(
|
|
1121
|
+
// `buildNudgeMessage` only reads the state kind to pick
|
|
1122
|
+
// copy; for merchant-supplied nudges we don't have a
|
|
1123
|
+
// `LimitResponseWithPlan` in hand here, so we defer to
|
|
1124
|
+
// the nudge's own kind → state mapping. `low-balance` →
|
|
1125
|
+
// topup, everything else → upgrade.
|
|
1126
|
+
nudge.kind === "low-balance" ? { kind: "topup_required" } : { kind: "upgrade_required" },
|
|
1127
|
+
null
|
|
1128
|
+
);
|
|
1129
|
+
primaryText = baseText.length > 0 ? `${baseText}
|
|
1130
|
+
|
|
1131
|
+
${nudgeText}` : nudgeText;
|
|
1132
|
+
}
|
|
1131
1133
|
const content = [
|
|
1132
1134
|
...emittedBlocks ?? [],
|
|
1133
1135
|
{ type: "text", text: primaryText }
|
|
1134
1136
|
];
|
|
1135
1137
|
const existingMeta = typeof adapterResult._meta === "object" && adapterResult._meta !== null ? adapterResult._meta : {};
|
|
1136
|
-
if (nudge) {
|
|
1137
|
-
const structuredContent = buildBootstrap ? {
|
|
1138
|
-
...await buildBootstrap("nudge", extra),
|
|
1139
|
-
nudge,
|
|
1140
|
-
data
|
|
1141
|
-
} : data;
|
|
1142
|
-
return {
|
|
1143
|
-
...adapterResult,
|
|
1144
|
-
content,
|
|
1145
|
-
structuredContent,
|
|
1146
|
-
_meta: {
|
|
1147
|
-
...existingMeta,
|
|
1148
|
-
...buildNudgeUiMeta({ resourceUri, nudge })
|
|
1149
|
-
}
|
|
1150
|
-
};
|
|
1151
|
-
}
|
|
1152
1138
|
return {
|
|
1153
1139
|
...adapterResult,
|
|
1154
1140
|
content,
|
|
@@ -1156,9 +1142,6 @@ async function unwrapResponseEnvelope(adapterResult, envelope, resourceUri, buil
|
|
|
1156
1142
|
...Object.keys(existingMeta).length > 0 ? { _meta: existingMeta } : {}
|
|
1157
1143
|
};
|
|
1158
1144
|
}
|
|
1159
|
-
function buildNudgeUiMeta(input) {
|
|
1160
|
-
return { ui: { resourceUri: input.resourceUri, nudge: input.nudge } };
|
|
1161
|
-
}
|
|
1162
1145
|
|
|
1163
1146
|
// src/oauth-discovery.ts
|
|
1164
1147
|
var DEFAULT_OAUTH_PATHS = {
|
|
@@ -1304,16 +1287,17 @@ export {
|
|
|
1304
1287
|
TOOL_FOR_VIEW,
|
|
1305
1288
|
VIEW_FOR_OPEN_TOOL,
|
|
1306
1289
|
VIEW_FOR_TOOL,
|
|
1290
|
+
applyHideToolsByAudience,
|
|
1307
1291
|
balanceSummary,
|
|
1308
1292
|
buildAuthInfoFromBearer,
|
|
1309
1293
|
buildPayableHandler,
|
|
1310
|
-
buildPaywallUiMeta,
|
|
1311
1294
|
buildSolvaPayDescriptors,
|
|
1312
1295
|
buildSolvaPayPrompts,
|
|
1313
1296
|
buildSolvaPayRequest,
|
|
1314
1297
|
createBuildBootstrapPayload,
|
|
1315
1298
|
decodeJwtPayload,
|
|
1316
1299
|
defaultGetCustomerRef,
|
|
1300
|
+
deriveIcons,
|
|
1317
1301
|
enrichPurchase,
|
|
1318
1302
|
extractBearerToken,
|
|
1319
1303
|
getCustomerRefFromBearerAuthHeader,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solvapay/mcp-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Framework-neutral MCP contracts for the SolvaPay SDK (tool names, descriptors, payable handler, paywall meta, CSP, bootstrap payload, OAuth discovery JSON builders, bearer/JWT helpers).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"zod": "^3.25.0 || ^4.0.0",
|
|
37
|
-
"@solvapay/server": "1.0.8
|
|
37
|
+
"@solvapay/server": "^1.0.8"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"tsup": "^8.5.1",
|
|
41
41
|
"typescript": "^5.9.3",
|
|
42
42
|
"vitest": "^4.1.2",
|
|
43
43
|
"zod": "^4.3.6",
|
|
44
|
-
"@solvapay/server": "1.0.8
|
|
44
|
+
"@solvapay/server": "1.0.8",
|
|
45
45
|
"@solvapay/test-utils": "^0.0.0"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|