@absolutejs/mcp 0.11.3 → 0.13.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/CHANGELOG.md +13 -0
- package/README.md +48 -0
- package/changelog.json +22 -0
- package/dist/commerce.js +173 -0
- package/dist/index.js +230 -24
- package/dist/manifest.js +213 -129
- package/dist/manifest.json +1 -1
- package/dist/src/commerce.d.ts +50 -0
- package/dist/src/creditStatus.d.ts +13 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/types.d.ts +9 -0
- package/docs/commerce-host-rules.md +266 -0
- package/package.json +23 -23
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@absolutejs/mcp`.
|
|
4
|
+
|
|
5
|
+
This file is generated by `absolute-changelog` from the entries in
|
|
6
|
+
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
|
+
`changelog/unreleased/`.
|
|
8
|
+
|
|
9
|
+
## 0.13.0 — 2026-09-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Add reviewed host commerce eligibility, execution guards, and reusable credit status tools** (`McpServerConfig`, `McpTool`, `evaluateCommerce`, `createCreditBalanceTool`)
|
package/README.md
CHANGED
|
@@ -13,6 +13,54 @@ authorization server. The default negotiated revision is the current finalized
|
|
|
13
13
|
`2025-11-25` specification; older finalized revisions remain available when
|
|
14
14
|
explicitly requested.
|
|
15
15
|
|
|
16
|
+
## Commerce across AI hosts
|
|
17
|
+
|
|
18
|
+
See [Commerce host rules and shared package design](docs/commerce-host-rules.md)
|
|
19
|
+
for the dated host-policy survey, checkout restrictions, secure handoffs, and
|
|
20
|
+
reusable AbsoluteJS package boundaries. Host eligibility is implemented;
|
|
21
|
+
checkout sessions, payment adapters and commerce UI remain planned.
|
|
22
|
+
|
|
23
|
+
### Enforce host commerce eligibility
|
|
24
|
+
|
|
25
|
+
`@absolutejs/mcp/commerce` exports `evaluateCommerce`, typed requirements and
|
|
26
|
+
review evidence. Tag every commerce tool with `commerce`; the server hides
|
|
27
|
+
ineligible tools and checks again before execution (including delayed tasks).
|
|
28
|
+
Tools without a commerce tag keep their existing behavior.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
mcpServer<Caller>({
|
|
32
|
+
// Existing authorize, issuer, path, serverInfo and agency configuration…
|
|
33
|
+
commerce: ({ caller }) => resolveReviewedDeployment(caller),
|
|
34
|
+
tools: () => ({
|
|
35
|
+
open_checkout: {
|
|
36
|
+
commerce: { action: "external_checkout", categories: ["usage_credits"] },
|
|
37
|
+
description: "Open a secure checkout for service credits",
|
|
38
|
+
inputSchema: { type: "object", properties: {} },
|
|
39
|
+
handler: () => createSecureCheckoutHandoff(),
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`resolveReviewedDeployment` and `createSecureCheckoutHandoff` above are consumer
|
|
46
|
+
integration callbacks, not package exports. Return a `CommerceContext` based on
|
|
47
|
+
trusted server configuration, never the tool arguments or a claimed client name.
|
|
48
|
+
Unknown channels and missing/expired reviews fail closed. A deployment review
|
|
49
|
+
cannot override the bundled ChatGPT digital-sales, Claude interactive-purchase,
|
|
50
|
+
or Cursor marketplace paid-access restrictions. Ambiguous profiles intersect.
|
|
51
|
+
|
|
52
|
+
Reviews must bind the profile, actions, product categories, source URLs and
|
|
53
|
+
validity window. Capabilities such as external links are checked independently.
|
|
54
|
+
Classify every possible cart category server-side; never trust a model-supplied
|
|
55
|
+
product category. `meta.commerceDecision` records the result for `onCall`.
|
|
56
|
+
|
|
57
|
+
This gate is **host eligibility only**: preserve ownership checks, Agency action
|
|
58
|
+
authorization, exact purchase confirmation and provider reconciliation. It does
|
|
59
|
+
not inspect arbitrary text, prompts, resources or untagged tools for sales links.
|
|
60
|
+
Consumers must use the same evaluator for those surfaces until shared renderers
|
|
61
|
+
are implemented. An informational-link classification must never redirect users
|
|
62
|
+
to a transaction. No live sales path is enabled by installing this package.
|
|
63
|
+
|
|
16
64
|
## Agent action enforcement
|
|
17
65
|
|
|
18
66
|
Tools carrying manifest contract 2 `authorization` metadata fail closed unless
|
package/changelog.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contract": 1,
|
|
3
|
+
"name": "@absolutejs/mcp",
|
|
4
|
+
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "added",
|
|
9
|
+
"summary": "Add reviewed host commerce eligibility, execution guards, and reusable credit status tools",
|
|
10
|
+
"symbols": [
|
|
11
|
+
"McpServerConfig",
|
|
12
|
+
"McpTool",
|
|
13
|
+
"evaluateCommerce",
|
|
14
|
+
"createCreditBalanceTool"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"date": "2026-09-11",
|
|
19
|
+
"version": "0.13.0"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
package/dist/commerce.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/creditStatus.ts
|
|
18
|
+
var createCreditBalanceTool = (options) => ({
|
|
19
|
+
annotations: {
|
|
20
|
+
readOnlyHint: true,
|
|
21
|
+
destructiveHint: false,
|
|
22
|
+
openWorldHint: false,
|
|
23
|
+
title: "Credit balance"
|
|
24
|
+
},
|
|
25
|
+
commerce: { action: "entitlement_status", categories: ["usage_credits"] },
|
|
26
|
+
description: "Read this account's service-credit allowance, consumption and remaining balance. Does not purchase credits or report the AI assistant's own token usage.",
|
|
27
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
28
|
+
outputSchema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
required: ["allowance", "consumed", "remaining", "periodEnd", "unit"],
|
|
31
|
+
properties: {
|
|
32
|
+
allowance: { type: "integer", minimum: 0 },
|
|
33
|
+
consumed: { type: "integer", minimum: 0 },
|
|
34
|
+
remaining: { type: "integer", minimum: 0 },
|
|
35
|
+
periodEnd: { type: ["string", "null"] },
|
|
36
|
+
unit: { const: "service_credits" }
|
|
37
|
+
},
|
|
38
|
+
additionalProperties: false
|
|
39
|
+
},
|
|
40
|
+
handler: async () => {
|
|
41
|
+
const balance = await options.read();
|
|
42
|
+
if (![balance.allowance, balance.consumed, balance.remaining].every((n) => Number.isSafeInteger(n) && n >= 0) || balance.periodEnd !== null && !Number.isFinite(Date.parse(balance.periodEnd)))
|
|
43
|
+
throw new Error("Credit balance is unavailable");
|
|
44
|
+
const summary = {
|
|
45
|
+
allowance: balance.allowance,
|
|
46
|
+
consumed: balance.consumed,
|
|
47
|
+
remaining: balance.remaining,
|
|
48
|
+
periodEnd: balance.periodEnd,
|
|
49
|
+
unit: "service_credits"
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `${summary.remaining} service credits remaining; ${summary.consumed} consumed.`
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
structuredContent: summary
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// src/commerce.ts
|
|
64
|
+
var COMMERCE_POLICY_VERSION = "2026-09-10.1";
|
|
65
|
+
var COMMERCE_POLICY_SOURCES = Object.freeze({
|
|
66
|
+
claude: "https://support.claude.com/en/articles/13454812-use-interactive-connectors-in-claude",
|
|
67
|
+
chatgpt: "https://developers.openai.com/plugins/app-guidelines#commerce-and-monetization",
|
|
68
|
+
chatgptCheckout: "https://developers.openai.com/plugins/build/monetization",
|
|
69
|
+
cursorMarketplace: "https://cursor.com/marketplace-publisher-terms"
|
|
70
|
+
});
|
|
71
|
+
var actions = new Set([
|
|
72
|
+
"entitlement_status",
|
|
73
|
+
"informational_link",
|
|
74
|
+
"paid_access",
|
|
75
|
+
"pricing",
|
|
76
|
+
"external_checkout",
|
|
77
|
+
"saved_method_purchase",
|
|
78
|
+
"new_method_collection",
|
|
79
|
+
"subscription",
|
|
80
|
+
"automatic_refill"
|
|
81
|
+
]);
|
|
82
|
+
var categories = new Set([
|
|
83
|
+
"physical_goods",
|
|
84
|
+
"digital_service",
|
|
85
|
+
"usage_credits",
|
|
86
|
+
"subscription"
|
|
87
|
+
]);
|
|
88
|
+
var profiles = new Set([
|
|
89
|
+
"claude-interactive",
|
|
90
|
+
"chatgpt-plugin",
|
|
91
|
+
"cursor-marketplace",
|
|
92
|
+
"direct-mcp",
|
|
93
|
+
"self-hosted",
|
|
94
|
+
"unknown"
|
|
95
|
+
]);
|
|
96
|
+
var sales = new Set([
|
|
97
|
+
"pricing",
|
|
98
|
+
"external_checkout",
|
|
99
|
+
"saved_method_purchase",
|
|
100
|
+
"new_method_collection",
|
|
101
|
+
"subscription",
|
|
102
|
+
"automatic_refill"
|
|
103
|
+
]);
|
|
104
|
+
var decision = (status, reason, sourceUrls = [], reviewIds = []) => ({
|
|
105
|
+
allowed: status === "permitted",
|
|
106
|
+
status,
|
|
107
|
+
reason,
|
|
108
|
+
policyVersion: COMMERCE_POLICY_VERSION,
|
|
109
|
+
sourceUrls,
|
|
110
|
+
reviewIds
|
|
111
|
+
});
|
|
112
|
+
var validSource = (source) => {
|
|
113
|
+
try {
|
|
114
|
+
const url = new URL(source);
|
|
115
|
+
return url.protocol === "https:" && !url.username && !url.password;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var reviewed = (review, profile, req, now) => typeof review.id === "string" && review.id.trim().length > 0 && review.profile === profile && Array.isArray(review.actions) && review.actions.includes(req.action) && Array.isArray(review.categories) && req.categories.every((c) => review.categories.includes(c)) && Number.isFinite(Date.parse(review.reviewedAt)) && Date.parse(review.reviewedAt) <= now && Number.isFinite(Date.parse(review.expiresAt)) && Date.parse(review.expiresAt) > now && Array.isArray(review.sourceUrls) && review.sourceUrls.length > 0 && review.sourceUrls.every(validSource);
|
|
121
|
+
var evaluateCommerce = (req, context, now = new Date) => {
|
|
122
|
+
if (!req || !actions.has(req.action) || !Array.isArray(req.categories) || req.categories.length === 0 || req.categories.some((c) => !categories.has(c)) || !context || !Array.isArray(context.profiles) || context.profiles.length === 0 || context.profiles.some((p) => !profiles.has(p)) || !Number.isFinite(now.getTime()))
|
|
123
|
+
return decision("unverified", "invalid_commerce_context");
|
|
124
|
+
const results = context.profiles.map((profile) => {
|
|
125
|
+
const digital = req.categories.some((c) => c !== "physical_goods");
|
|
126
|
+
if (profile === "chatgpt-plugin" && digital && sales.has(req.action))
|
|
127
|
+
return decision("restricted", "chatgpt_digital_commerce", [
|
|
128
|
+
COMMERCE_POLICY_SOURCES.chatgpt
|
|
129
|
+
]);
|
|
130
|
+
if (profile === "claude-interactive" && [
|
|
131
|
+
"saved_method_purchase",
|
|
132
|
+
"new_method_collection",
|
|
133
|
+
"subscription",
|
|
134
|
+
"automatic_refill"
|
|
135
|
+
].includes(req.action))
|
|
136
|
+
return decision("restricted", "claude_interactive_purchases", [
|
|
137
|
+
COMMERCE_POLICY_SOURCES.claude
|
|
138
|
+
]);
|
|
139
|
+
if (profile === "cursor-marketplace" && req.action === "paid_access")
|
|
140
|
+
return decision("restricted", "cursor_marketplace_paid_access", [
|
|
141
|
+
COMMERCE_POLICY_SOURCES.cursorMarketplace
|
|
142
|
+
]);
|
|
143
|
+
if (req.action === "entitlement_status")
|
|
144
|
+
return decision("permitted", "non_transactional_status");
|
|
145
|
+
if (req.action === "informational_link" && profile === "chatgpt-plugin")
|
|
146
|
+
return decision("permitted", "chatgpt_entitlement_information", [
|
|
147
|
+
COMMERCE_POLICY_SOURCES.chatgpt
|
|
148
|
+
]);
|
|
149
|
+
if (profile === "unknown")
|
|
150
|
+
return decision("unverified", "unknown_host_channel");
|
|
151
|
+
const review = context.reviews?.find((r) => reviewed(r, profile, req, now.getTime()));
|
|
152
|
+
return review ? decision("permitted", "reviewed_deployment", [...review.sourceUrls], [review.id]) : decision("unverified", "commerce_review_required");
|
|
153
|
+
});
|
|
154
|
+
const blocked = results.find((r) => r.status === "restricted") ?? results.find((r) => !r.allowed);
|
|
155
|
+
if (blocked)
|
|
156
|
+
return blocked;
|
|
157
|
+
const caps = context.capabilities;
|
|
158
|
+
if ((req.action === "external_checkout" || req.action === "informational_link") && caps?.externalLinks !== true)
|
|
159
|
+
return decision("unverified", "external_links_unavailable");
|
|
160
|
+
if ((req.action === "saved_method_purchase" || req.action === "new_method_collection") && caps?.interactiveUi !== true)
|
|
161
|
+
return decision("unverified", "interactive_ui_unavailable");
|
|
162
|
+
if (req.action === "new_method_collection" && context.profiles.includes("chatgpt-plugin") && caps?.nativePaymentSheet !== true)
|
|
163
|
+
return decision("restricted", "chatgpt_native_payment_sheet_required", [
|
|
164
|
+
COMMERCE_POLICY_SOURCES.chatgptCheckout
|
|
165
|
+
]);
|
|
166
|
+
return decision("permitted", "host_commerce_eligible", [...new Set(results.flatMap((r) => r.sourceUrls))], [...new Set(results.flatMap((r) => r.reviewIds))]);
|
|
167
|
+
};
|
|
168
|
+
export {
|
|
169
|
+
COMMERCE_POLICY_SOURCES,
|
|
170
|
+
COMMERCE_POLICY_VERSION,
|
|
171
|
+
createCreditBalanceTool,
|
|
172
|
+
evaluateCommerce
|
|
173
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,158 @@ var __export = (target, all) => {
|
|
|
14
14
|
});
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// src/creditStatus.ts
|
|
18
|
+
var createCreditBalanceTool = (options) => ({
|
|
19
|
+
annotations: {
|
|
20
|
+
readOnlyHint: true,
|
|
21
|
+
destructiveHint: false,
|
|
22
|
+
openWorldHint: false,
|
|
23
|
+
title: "Credit balance"
|
|
24
|
+
},
|
|
25
|
+
commerce: { action: "entitlement_status", categories: ["usage_credits"] },
|
|
26
|
+
description: "Read this account's service-credit allowance, consumption and remaining balance. Does not purchase credits or report the AI assistant's own token usage.",
|
|
27
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
28
|
+
outputSchema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
required: ["allowance", "consumed", "remaining", "periodEnd", "unit"],
|
|
31
|
+
properties: {
|
|
32
|
+
allowance: { type: "integer", minimum: 0 },
|
|
33
|
+
consumed: { type: "integer", minimum: 0 },
|
|
34
|
+
remaining: { type: "integer", minimum: 0 },
|
|
35
|
+
periodEnd: { type: ["string", "null"] },
|
|
36
|
+
unit: { const: "service_credits" }
|
|
37
|
+
},
|
|
38
|
+
additionalProperties: false
|
|
39
|
+
},
|
|
40
|
+
handler: async () => {
|
|
41
|
+
const balance = await options.read();
|
|
42
|
+
if (![balance.allowance, balance.consumed, balance.remaining].every((n) => Number.isSafeInteger(n) && n >= 0) || balance.periodEnd !== null && !Number.isFinite(Date.parse(balance.periodEnd)))
|
|
43
|
+
throw new Error("Credit balance is unavailable");
|
|
44
|
+
const summary = {
|
|
45
|
+
allowance: balance.allowance,
|
|
46
|
+
consumed: balance.consumed,
|
|
47
|
+
remaining: balance.remaining,
|
|
48
|
+
periodEnd: balance.periodEnd,
|
|
49
|
+
unit: "service_credits"
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `${summary.remaining} service credits remaining; ${summary.consumed} consumed.`
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
structuredContent: summary
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// src/commerce.ts
|
|
64
|
+
var COMMERCE_POLICY_VERSION = "2026-09-10.1";
|
|
65
|
+
var COMMERCE_POLICY_SOURCES = Object.freeze({
|
|
66
|
+
claude: "https://support.claude.com/en/articles/13454812-use-interactive-connectors-in-claude",
|
|
67
|
+
chatgpt: "https://developers.openai.com/plugins/app-guidelines#commerce-and-monetization",
|
|
68
|
+
chatgptCheckout: "https://developers.openai.com/plugins/build/monetization",
|
|
69
|
+
cursorMarketplace: "https://cursor.com/marketplace-publisher-terms"
|
|
70
|
+
});
|
|
71
|
+
var actions = new Set([
|
|
72
|
+
"entitlement_status",
|
|
73
|
+
"informational_link",
|
|
74
|
+
"paid_access",
|
|
75
|
+
"pricing",
|
|
76
|
+
"external_checkout",
|
|
77
|
+
"saved_method_purchase",
|
|
78
|
+
"new_method_collection",
|
|
79
|
+
"subscription",
|
|
80
|
+
"automatic_refill"
|
|
81
|
+
]);
|
|
82
|
+
var categories = new Set([
|
|
83
|
+
"physical_goods",
|
|
84
|
+
"digital_service",
|
|
85
|
+
"usage_credits",
|
|
86
|
+
"subscription"
|
|
87
|
+
]);
|
|
88
|
+
var profiles = new Set([
|
|
89
|
+
"claude-interactive",
|
|
90
|
+
"chatgpt-plugin",
|
|
91
|
+
"cursor-marketplace",
|
|
92
|
+
"direct-mcp",
|
|
93
|
+
"self-hosted",
|
|
94
|
+
"unknown"
|
|
95
|
+
]);
|
|
96
|
+
var sales = new Set([
|
|
97
|
+
"pricing",
|
|
98
|
+
"external_checkout",
|
|
99
|
+
"saved_method_purchase",
|
|
100
|
+
"new_method_collection",
|
|
101
|
+
"subscription",
|
|
102
|
+
"automatic_refill"
|
|
103
|
+
]);
|
|
104
|
+
var decision = (status, reason, sourceUrls = [], reviewIds = []) => ({
|
|
105
|
+
allowed: status === "permitted",
|
|
106
|
+
status,
|
|
107
|
+
reason,
|
|
108
|
+
policyVersion: COMMERCE_POLICY_VERSION,
|
|
109
|
+
sourceUrls,
|
|
110
|
+
reviewIds
|
|
111
|
+
});
|
|
112
|
+
var validSource = (source) => {
|
|
113
|
+
try {
|
|
114
|
+
const url = new URL(source);
|
|
115
|
+
return url.protocol === "https:" && !url.username && !url.password;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var reviewed = (review, profile, req, now) => typeof review.id === "string" && review.id.trim().length > 0 && review.profile === profile && Array.isArray(review.actions) && review.actions.includes(req.action) && Array.isArray(review.categories) && req.categories.every((c) => review.categories.includes(c)) && Number.isFinite(Date.parse(review.reviewedAt)) && Date.parse(review.reviewedAt) <= now && Number.isFinite(Date.parse(review.expiresAt)) && Date.parse(review.expiresAt) > now && Array.isArray(review.sourceUrls) && review.sourceUrls.length > 0 && review.sourceUrls.every(validSource);
|
|
121
|
+
var evaluateCommerce = (req, context, now = new Date) => {
|
|
122
|
+
if (!req || !actions.has(req.action) || !Array.isArray(req.categories) || req.categories.length === 0 || req.categories.some((c) => !categories.has(c)) || !context || !Array.isArray(context.profiles) || context.profiles.length === 0 || context.profiles.some((p) => !profiles.has(p)) || !Number.isFinite(now.getTime()))
|
|
123
|
+
return decision("unverified", "invalid_commerce_context");
|
|
124
|
+
const results = context.profiles.map((profile) => {
|
|
125
|
+
const digital = req.categories.some((c) => c !== "physical_goods");
|
|
126
|
+
if (profile === "chatgpt-plugin" && digital && sales.has(req.action))
|
|
127
|
+
return decision("restricted", "chatgpt_digital_commerce", [
|
|
128
|
+
COMMERCE_POLICY_SOURCES.chatgpt
|
|
129
|
+
]);
|
|
130
|
+
if (profile === "claude-interactive" && [
|
|
131
|
+
"saved_method_purchase",
|
|
132
|
+
"new_method_collection",
|
|
133
|
+
"subscription",
|
|
134
|
+
"automatic_refill"
|
|
135
|
+
].includes(req.action))
|
|
136
|
+
return decision("restricted", "claude_interactive_purchases", [
|
|
137
|
+
COMMERCE_POLICY_SOURCES.claude
|
|
138
|
+
]);
|
|
139
|
+
if (profile === "cursor-marketplace" && req.action === "paid_access")
|
|
140
|
+
return decision("restricted", "cursor_marketplace_paid_access", [
|
|
141
|
+
COMMERCE_POLICY_SOURCES.cursorMarketplace
|
|
142
|
+
]);
|
|
143
|
+
if (req.action === "entitlement_status")
|
|
144
|
+
return decision("permitted", "non_transactional_status");
|
|
145
|
+
if (req.action === "informational_link" && profile === "chatgpt-plugin")
|
|
146
|
+
return decision("permitted", "chatgpt_entitlement_information", [
|
|
147
|
+
COMMERCE_POLICY_SOURCES.chatgpt
|
|
148
|
+
]);
|
|
149
|
+
if (profile === "unknown")
|
|
150
|
+
return decision("unverified", "unknown_host_channel");
|
|
151
|
+
const review = context.reviews?.find((r) => reviewed(r, profile, req, now.getTime()));
|
|
152
|
+
return review ? decision("permitted", "reviewed_deployment", [...review.sourceUrls], [review.id]) : decision("unverified", "commerce_review_required");
|
|
153
|
+
});
|
|
154
|
+
const blocked = results.find((r) => r.status === "restricted") ?? results.find((r) => !r.allowed);
|
|
155
|
+
if (blocked)
|
|
156
|
+
return blocked;
|
|
157
|
+
const caps = context.capabilities;
|
|
158
|
+
if ((req.action === "external_checkout" || req.action === "informational_link") && caps?.externalLinks !== true)
|
|
159
|
+
return decision("unverified", "external_links_unavailable");
|
|
160
|
+
if ((req.action === "saved_method_purchase" || req.action === "new_method_collection") && caps?.interactiveUi !== true)
|
|
161
|
+
return decision("unverified", "interactive_ui_unavailable");
|
|
162
|
+
if (req.action === "new_method_collection" && context.profiles.includes("chatgpt-plugin") && caps?.nativePaymentSheet !== true)
|
|
163
|
+
return decision("restricted", "chatgpt_native_payment_sheet_required", [
|
|
164
|
+
COMMERCE_POLICY_SOURCES.chatgptCheckout
|
|
165
|
+
]);
|
|
166
|
+
return decision("permitted", "host_commerce_eligible", [...new Set(results.flatMap((r) => r.sourceUrls))], [...new Set(results.flatMap((r) => r.reviewIds))]);
|
|
167
|
+
};
|
|
168
|
+
|
|
17
169
|
// src/guards.ts
|
|
18
170
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
171
|
|
|
@@ -834,9 +986,23 @@ var initialize = async (config, id, params, context) => {
|
|
|
834
986
|
response.headers.set("Mcp-Session-Id", sessionId);
|
|
835
987
|
return response;
|
|
836
988
|
};
|
|
989
|
+
var commerceDecision = async (config, caller, name, tool) => {
|
|
990
|
+
if (tool.commerce === undefined)
|
|
991
|
+
return;
|
|
992
|
+
try {
|
|
993
|
+
return evaluateCommerce(tool.commerce, config.commerce ? await config.commerce({ caller, name }) : { profiles: [] });
|
|
994
|
+
} catch {
|
|
995
|
+
return evaluateCommerce(tool.commerce, { profiles: [] });
|
|
996
|
+
}
|
|
997
|
+
};
|
|
837
998
|
var toolsList = async (config, caller, scopes, id, params, protocolVersion) => {
|
|
838
999
|
const tools = await config.tools({ caller, meta: {} });
|
|
839
|
-
const
|
|
1000
|
+
const eligible = await Promise.all(Object.entries(tools).map(async ([name, tool]) => ({
|
|
1001
|
+
name,
|
|
1002
|
+
allowed: (await commerceDecision(config, caller, name, tool))?.allowed !== false
|
|
1003
|
+
})));
|
|
1004
|
+
const commerceVisible = new Set(eligible.filter((entry) => entry.allowed).map((entry) => entry.name));
|
|
1005
|
+
const visible = Object.entries(tools).filter(([name, tool]) => commerceVisible.has(name) && scopeAllows(tool, scopes) && agencyAllows(config, tool, scopes)).map(([name, tool]) => ({
|
|
840
1006
|
annotations: tool.annotations,
|
|
841
1007
|
...tool.coaz === undefined ? {} : { coaz: tool.coaz },
|
|
842
1008
|
description: tool.description,
|
|
@@ -870,9 +1036,45 @@ var runTool = async (config, caller, scopes, id, name, args, meta, tool, context
|
|
|
870
1036
|
let ok = false;
|
|
871
1037
|
let payload;
|
|
872
1038
|
try {
|
|
873
|
-
const invoke = async () =>
|
|
1039
|
+
const invoke = async () => {
|
|
1040
|
+
const eligibility2 = await commerceDecision(config, caller, name, tool);
|
|
1041
|
+
if (eligibility2)
|
|
1042
|
+
meta.commerceDecision = eligibility2;
|
|
1043
|
+
if (eligibility2?.allowed === false)
|
|
1044
|
+
return {
|
|
1045
|
+
content: [
|
|
1046
|
+
{
|
|
1047
|
+
type: "text",
|
|
1048
|
+
text: "This commerce action is unavailable in this connection."
|
|
1049
|
+
}
|
|
1050
|
+
],
|
|
1051
|
+
isError: true,
|
|
1052
|
+
structuredContent: {
|
|
1053
|
+
type: "absolute.commerce_decision",
|
|
1054
|
+
...eligibility2
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
return normalizeResult(await tool.handler(args, context));
|
|
1058
|
+
};
|
|
874
1059
|
let result;
|
|
875
|
-
|
|
1060
|
+
const eligibility = await commerceDecision(config, caller, name, tool);
|
|
1061
|
+
if (eligibility)
|
|
1062
|
+
meta.commerceDecision = eligibility;
|
|
1063
|
+
if (eligibility?.allowed === false) {
|
|
1064
|
+
result = {
|
|
1065
|
+
content: [
|
|
1066
|
+
{
|
|
1067
|
+
type: "text",
|
|
1068
|
+
text: "This commerce action is unavailable in this connection."
|
|
1069
|
+
}
|
|
1070
|
+
],
|
|
1071
|
+
isError: true,
|
|
1072
|
+
structuredContent: {
|
|
1073
|
+
type: "absolute.commerce_decision",
|
|
1074
|
+
...eligibility
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
} else if (tool.authorization === undefined) {
|
|
876
1078
|
result = await invoke();
|
|
877
1079
|
} else {
|
|
878
1080
|
const agency = config.agency;
|
|
@@ -1763,26 +1965,30 @@ var createPostgresMcpSessionStore = ({
|
|
|
1763
1965
|
};
|
|
1764
1966
|
};
|
|
1765
1967
|
export {
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
parseMcpAuthorizationChallenge,
|
|
1770
|
-
metadataPathFor,
|
|
1771
|
-
mcpServer,
|
|
1772
|
-
mcpPostgresSchemaSql,
|
|
1773
|
-
feedbackTools,
|
|
1774
|
-
dispatchMcp,
|
|
1775
|
-
discoverMcpAuthorization,
|
|
1776
|
-
createSessionRegistry,
|
|
1777
|
-
createPostgresMcpTaskStore,
|
|
1778
|
-
createPostgresMcpSessionStore,
|
|
1779
|
-
createMemoryMcpTaskStore,
|
|
1780
|
-
createMemoryMcpOAuthTokenStore,
|
|
1781
|
-
createMcpOAuthProvider,
|
|
1782
|
-
createMcpHandler,
|
|
1783
|
-
createMcpClient,
|
|
1784
|
-
createMcpAuthorizationRequest,
|
|
1785
|
-
McpClientError,
|
|
1968
|
+
COMMERCE_POLICY_SOURCES,
|
|
1969
|
+
COMMERCE_POLICY_VERSION,
|
|
1970
|
+
FEEDBACK_INSTRUCTIONS,
|
|
1786
1971
|
MCP_LATEST_PROTOCOL_VERSION,
|
|
1787
|
-
|
|
1972
|
+
McpClientError,
|
|
1973
|
+
createCreditBalanceTool,
|
|
1974
|
+
createMcpAuthorizationRequest,
|
|
1975
|
+
createMcpClient,
|
|
1976
|
+
createMcpHandler,
|
|
1977
|
+
createMcpOAuthProvider,
|
|
1978
|
+
createMemoryMcpOAuthTokenStore,
|
|
1979
|
+
createMemoryMcpTaskStore,
|
|
1980
|
+
createPostgresMcpSessionStore,
|
|
1981
|
+
createPostgresMcpTaskStore,
|
|
1982
|
+
createSessionRegistry,
|
|
1983
|
+
discoverMcpAuthorization,
|
|
1984
|
+
dispatchMcp,
|
|
1985
|
+
evaluateCommerce,
|
|
1986
|
+
feedbackTools,
|
|
1987
|
+
mcpPostgresSchemaSql,
|
|
1988
|
+
mcpServer,
|
|
1989
|
+
metadataPathFor,
|
|
1990
|
+
parseMcpAuthorizationChallenge,
|
|
1991
|
+
protectedResourceMetadata,
|
|
1992
|
+
publicMcpTask,
|
|
1993
|
+
verifyBearer
|
|
1788
1994
|
};
|