askell-mcp 0.4.7 → 0.4.12
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 +1 -2
- package/package.json +1 -1
- package/spec/openapi-v2.json +43 -35
- package/src/client/paths.ts +1 -24
- package/src/openapi/registry.ts +14 -30
- package/src/resources/register.ts +7 -5
- package/src/server.ts +6 -10
- package/src/tools/analysis.ts +1 -1
- package/src/tools/discovery.ts +1 -1
package/README.md
CHANGED
|
@@ -126,9 +126,8 @@ Typical agent workflow:
|
|
|
126
126
|
## API notes (short)
|
|
127
127
|
|
|
128
128
|
- **v1** — legacy paths like `/customers/`, `/subscriptions/` (no `/v2` prefix)
|
|
129
|
-
- **v2** — current model: catalogs, quotes, checkouts, contracts, billing runs under `/v2/`
|
|
129
|
+
- **v2** — current model: catalogs, quotes, checkouts, contracts, billing runs, fulfillment orders under `/v2/`
|
|
130
130
|
- **v2 discounts** — coupons: `GET/POST /v2/subscription-contracts/{id}/discount|apply-code|remove-discount` (one active). Quotes take `promotion_code` and, for an existing buyer, `customer` (id) so combo discounts + promo restrictions apply. First-period totals already include coupon + combo; `quote.recurring_*` include combo but not the coupon (`discount.recurring_final_amount` while the coupon is active). Recurring `finalize` needs a verified payment method even when due-now is 0. Not the v1 `discount` 0–100 field.
|
|
131
|
-
- **v2 fulfilment** — British spelling: `GET /v2/fulfilment-orders/` (read-only). Path param is order `id`, not `number`. List `403` if shipping is off. Quote/checkout `shipping_fee` is already in totals.
|
|
132
131
|
- Paths use **trailing slashes**
|
|
133
132
|
- Prefer **v2** for new integrations; v1 remains for existing ones
|
|
134
133
|
- Docs: [docs.askell.is](https://docs.askell.is/) · OpenAPI: [v1](https://askell.is/api/swagger/swagger.json) · [v2](https://askell.is/api/swagger/v2/swagger.json)
|
package/package.json
CHANGED
package/spec/openapi-v2.json
CHANGED
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"description": "Subscription V2 billing-run read and retry APIs, requires secret api key."
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
|
-
"name": "V2
|
|
61
|
-
"description": "Subscription V2
|
|
60
|
+
"name": "V2 Fulfillment",
|
|
61
|
+
"description": "Subscription V2 fulfillment order read APIs for warehouse integrations, requires secret api key."
|
|
62
62
|
}
|
|
63
63
|
],
|
|
64
64
|
"paths": {
|
|
@@ -1677,13 +1677,13 @@
|
|
|
1677
1677
|
]
|
|
1678
1678
|
}
|
|
1679
1679
|
},
|
|
1680
|
-
"/v2/
|
|
1680
|
+
"/v2/fulfillment-orders/": {
|
|
1681
1681
|
"get": {
|
|
1682
1682
|
"tags": [
|
|
1683
|
-
"V2
|
|
1683
|
+
"V2 Fulfillment"
|
|
1684
1684
|
],
|
|
1685
|
-
"summary": "List
|
|
1686
|
-
"description": "Returns the authenticated account's
|
|
1685
|
+
"summary": "List fulfillment orders",
|
|
1686
|
+
"description": "Returns the authenticated account's fulfillment orders, newest first, with the same body the `fulfillment_order.*` webhooks carry. Intended for backfill and for polling-based reconciliation after a missed webhook delivery. Read-only. Requires a secret key, that the account uses subscription contracts, and that shipping is enabled for the account.",
|
|
1687
1687
|
"parameters": [
|
|
1688
1688
|
{
|
|
1689
1689
|
"in": "query",
|
|
@@ -1697,7 +1697,7 @@
|
|
|
1697
1697
|
"cancelled"
|
|
1698
1698
|
]
|
|
1699
1699
|
},
|
|
1700
|
-
"description": "Filter by
|
|
1700
|
+
"description": "Filter by fulfillment status."
|
|
1701
1701
|
},
|
|
1702
1702
|
{
|
|
1703
1703
|
"in": "query",
|
|
@@ -1747,7 +1747,7 @@
|
|
|
1747
1747
|
],
|
|
1748
1748
|
"responses": {
|
|
1749
1749
|
"200": {
|
|
1750
|
-
"$ref": "#/components/responses/
|
|
1750
|
+
"$ref": "#/components/responses/V2FulfillmentOrderList"
|
|
1751
1751
|
},
|
|
1752
1752
|
"403": {
|
|
1753
1753
|
"$ref": "#/components/responses/V2PermissionDenied"
|
|
@@ -1760,21 +1760,21 @@
|
|
|
1760
1760
|
]
|
|
1761
1761
|
}
|
|
1762
1762
|
},
|
|
1763
|
-
"/v2/
|
|
1763
|
+
"/v2/fulfillment-orders/{fulfillmentOrderId}/": {
|
|
1764
1764
|
"get": {
|
|
1765
1765
|
"tags": [
|
|
1766
|
-
"V2
|
|
1766
|
+
"V2 Fulfillment"
|
|
1767
1767
|
],
|
|
1768
|
-
"summary": "Get a
|
|
1769
|
-
"description": "Returns one
|
|
1768
|
+
"summary": "Get a fulfillment order",
|
|
1769
|
+
"description": "Returns one fulfillment order belonging to the authenticated account, with its lines, delivery address, shipping selection and booked shipments. Orders belonging to another account return `404`. Read-only. Requires a secret key.",
|
|
1770
1770
|
"parameters": [
|
|
1771
1771
|
{
|
|
1772
|
-
"$ref": "#/components/parameters/
|
|
1772
|
+
"$ref": "#/components/parameters/V2FulfillmentOrderId"
|
|
1773
1773
|
}
|
|
1774
1774
|
],
|
|
1775
1775
|
"responses": {
|
|
1776
1776
|
"200": {
|
|
1777
|
-
"$ref": "#/components/responses/
|
|
1777
|
+
"$ref": "#/components/responses/V2FulfillmentOrder"
|
|
1778
1778
|
},
|
|
1779
1779
|
"403": {
|
|
1780
1780
|
"$ref": "#/components/responses/V2PermissionDenied"
|
|
@@ -3222,7 +3222,7 @@
|
|
|
3222
3222
|
"properties": {
|
|
3223
3223
|
"option": {
|
|
3224
3224
|
"type": "integer",
|
|
3225
|
-
"description": "Id of one of the account's active shipping options."
|
|
3225
|
+
"description": "Id of one of the account's active shipping options. An option with a rate table is priced for the delivery address's zip code (the request's `delivery_address`, else the customer's address) and the weight of the goods; when the table has no rate for that delivery the request fails with `400`, `shipping` describing why and `shipping_code` `shipping_not_available`."
|
|
3226
3226
|
},
|
|
3227
3227
|
"location_id": {
|
|
3228
3228
|
"type": "string",
|
|
@@ -3281,6 +3281,14 @@
|
|
|
3281
3281
|
"currency": {
|
|
3282
3282
|
"type": "string"
|
|
3283
3283
|
},
|
|
3284
|
+
"zone_name": {
|
|
3285
|
+
"type": "string",
|
|
3286
|
+
"description": "The shipping zone that priced the selection, or an empty string when the option is flat-priced or the rate applied to every zip code."
|
|
3287
|
+
},
|
|
3288
|
+
"weight_band": {
|
|
3289
|
+
"type": "string",
|
|
3290
|
+
"description": "The weight band that priced the selection as `min-max` in grams (`max` empty for an open-ended band), or an empty string for a flat-priced option."
|
|
3291
|
+
},
|
|
3284
3292
|
"location_id": {
|
|
3285
3293
|
"type": "string"
|
|
3286
3294
|
},
|
|
@@ -3298,7 +3306,7 @@
|
|
|
3298
3306
|
"V2PickupLocation": {
|
|
3299
3307
|
"type": "object",
|
|
3300
3308
|
"nullable": true,
|
|
3301
|
-
"description": "The chosen pickup location as a structured block an external
|
|
3309
|
+
"description": "The chosen pickup location as a structured block an external fulfillment system can book against, or null when the selection has none (home delivery, freight, store pickup without an address). Derived from the shipping provider's own payload, with the flat `location_*` values as the fallback.",
|
|
3302
3310
|
"properties": {
|
|
3303
3311
|
"id": {
|
|
3304
3312
|
"type": "string",
|
|
@@ -4782,7 +4790,7 @@
|
|
|
4782
4790
|
}
|
|
4783
4791
|
}
|
|
4784
4792
|
},
|
|
4785
|
-
"
|
|
4793
|
+
"V2FulfillmentAddress": {
|
|
4786
4794
|
"type": "object",
|
|
4787
4795
|
"nullable": true,
|
|
4788
4796
|
"description": "The delivery address as it stood when the order was created. This is the order's own snapshot, so editing the contract's address afterwards does not change it.",
|
|
@@ -4822,7 +4830,7 @@
|
|
|
4822
4830
|
}
|
|
4823
4831
|
}
|
|
4824
4832
|
},
|
|
4825
|
-
"
|
|
4833
|
+
"V2FulfillmentOrderLine": {
|
|
4826
4834
|
"type": "object",
|
|
4827
4835
|
"properties": {
|
|
4828
4836
|
"id": {
|
|
@@ -4850,7 +4858,7 @@
|
|
|
4850
4858
|
}
|
|
4851
4859
|
}
|
|
4852
4860
|
},
|
|
4853
|
-
"
|
|
4861
|
+
"V2Fulfillment": {
|
|
4854
4862
|
"type": "object",
|
|
4855
4863
|
"description": "A shipment booked for (part of) an order.",
|
|
4856
4864
|
"properties": {
|
|
@@ -4910,9 +4918,9 @@
|
|
|
4910
4918
|
}
|
|
4911
4919
|
}
|
|
4912
4920
|
},
|
|
4913
|
-
"
|
|
4921
|
+
"V2FulfillmentOrder": {
|
|
4914
4922
|
"type": "object",
|
|
4915
|
-
"description": "A physical order generated by a paid billing run. This is also the body of the `
|
|
4923
|
+
"description": "A physical order generated by a paid billing run. This is also the body of the `fulfillment_order.*` webhooks.",
|
|
4916
4924
|
"properties": {
|
|
4917
4925
|
"id": {
|
|
4918
4926
|
"type": "integer"
|
|
@@ -4951,7 +4959,7 @@
|
|
|
4951
4959
|
"nullable": true
|
|
4952
4960
|
},
|
|
4953
4961
|
"delivery_address": {
|
|
4954
|
-
"$ref": "#/components/schemas/
|
|
4962
|
+
"$ref": "#/components/schemas/V2FulfillmentAddress"
|
|
4955
4963
|
},
|
|
4956
4964
|
"shipping_selection": {
|
|
4957
4965
|
"$ref": "#/components/schemas/V2ShippingSelection"
|
|
@@ -4959,14 +4967,14 @@
|
|
|
4959
4967
|
"lines": {
|
|
4960
4968
|
"type": "array",
|
|
4961
4969
|
"items": {
|
|
4962
|
-
"$ref": "#/components/schemas/
|
|
4970
|
+
"$ref": "#/components/schemas/V2FulfillmentOrderLine"
|
|
4963
4971
|
}
|
|
4964
4972
|
},
|
|
4965
|
-
"
|
|
4973
|
+
"fulfillments": {
|
|
4966
4974
|
"type": "array",
|
|
4967
4975
|
"description": "Shipments booked for this order. Empty until one is booked, and always empty for an account with no shipping providers configured.",
|
|
4968
4976
|
"items": {
|
|
4969
|
-
"$ref": "#/components/schemas/
|
|
4977
|
+
"$ref": "#/components/schemas/V2Fulfillment"
|
|
4970
4978
|
}
|
|
4971
4979
|
},
|
|
4972
4980
|
"estimated_weight_grams": {
|
|
@@ -5513,10 +5521,10 @@
|
|
|
5513
5521
|
"type": "integer"
|
|
5514
5522
|
}
|
|
5515
5523
|
},
|
|
5516
|
-
"
|
|
5517
|
-
"name": "
|
|
5524
|
+
"V2FulfillmentOrderId": {
|
|
5525
|
+
"name": "fulfillmentOrderId",
|
|
5518
5526
|
"in": "path",
|
|
5519
|
-
"description": "
|
|
5527
|
+
"description": "Fulfillment order id. This is the order's `id`, not its per-account `number`.",
|
|
5520
5528
|
"required": true,
|
|
5521
5529
|
"style": "simple",
|
|
5522
5530
|
"explode": false,
|
|
@@ -5901,18 +5909,18 @@
|
|
|
5901
5909
|
}
|
|
5902
5910
|
}
|
|
5903
5911
|
},
|
|
5904
|
-
"
|
|
5905
|
-
"description": "V2
|
|
5912
|
+
"V2FulfillmentOrder": {
|
|
5913
|
+
"description": "V2 fulfillment order",
|
|
5906
5914
|
"content": {
|
|
5907
5915
|
"application/json": {
|
|
5908
5916
|
"schema": {
|
|
5909
|
-
"$ref": "#/components/schemas/
|
|
5917
|
+
"$ref": "#/components/schemas/V2FulfillmentOrder"
|
|
5910
5918
|
}
|
|
5911
5919
|
}
|
|
5912
5920
|
}
|
|
5913
5921
|
},
|
|
5914
|
-
"
|
|
5915
|
-
"description": "V2
|
|
5922
|
+
"V2FulfillmentOrderList": {
|
|
5923
|
+
"description": "V2 fulfillment order list",
|
|
5916
5924
|
"content": {
|
|
5917
5925
|
"application/json": {
|
|
5918
5926
|
"schema": {
|
|
@@ -5920,7 +5928,7 @@
|
|
|
5920
5928
|
{
|
|
5921
5929
|
"type": "array",
|
|
5922
5930
|
"items": {
|
|
5923
|
-
"$ref": "#/components/schemas/
|
|
5931
|
+
"$ref": "#/components/schemas/V2FulfillmentOrder"
|
|
5924
5932
|
}
|
|
5925
5933
|
},
|
|
5926
5934
|
{
|
|
@@ -5940,7 +5948,7 @@
|
|
|
5940
5948
|
"results": {
|
|
5941
5949
|
"type": "array",
|
|
5942
5950
|
"items": {
|
|
5943
|
-
"$ref": "#/components/schemas/
|
|
5951
|
+
"$ref": "#/components/schemas/V2FulfillmentOrder"
|
|
5944
5952
|
}
|
|
5945
5953
|
}
|
|
5946
5954
|
}
|
package/src/client/paths.ts
CHANGED
|
@@ -1,29 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Askell OpenAPI uses British "fulfilment"; agents (and Shopify/Stripe training)
|
|
3
|
-
* type American "fulfillment". Fold to the British form Askell actually serves.
|
|
4
|
-
*/
|
|
5
|
-
export function foldFulfilmentSpelling(text: string): string {
|
|
6
|
-
return text.replace(/fulfillment/gi, (match) => {
|
|
7
|
-
const first = match[0];
|
|
8
|
-
if (match === match.toUpperCase()) {
|
|
9
|
-
return 'FULFILMENT';
|
|
10
|
-
}
|
|
11
|
-
if (first !== undefined && first === first.toUpperCase()) {
|
|
12
|
-
return 'Fulfilment';
|
|
13
|
-
}
|
|
14
|
-
return 'fulfilment';
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Lowercased text with American/British fulfilment spelling folded together. */
|
|
19
|
-
export function canonicalSearchText(text: string): string {
|
|
20
|
-
return foldFulfilmentSpelling(text).toLowerCase();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
1
|
/** Normalize Askell API paths: leading slash + trailing slash (OpenAPI convention). */
|
|
24
2
|
export function normalizeApiPath(path: string): string {
|
|
25
|
-
let normalized =
|
|
26
|
-
normalized = normalized.startsWith('/') ? normalized : `/${normalized}`;
|
|
3
|
+
let normalized = path.startsWith('/') ? path : `/${path}`;
|
|
27
4
|
|
|
28
5
|
if (normalized !== '/' && !normalized.endsWith('/')) {
|
|
29
6
|
normalized += '/';
|
package/src/openapi/registry.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import v1Spec from '../../spec/openapi-v1.json';
|
|
2
2
|
import v2Spec from '../../spec/openapi-v2.json';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
canonicalSearchText,
|
|
6
|
-
foldFulfilmentSpelling,
|
|
7
|
-
} from '../client/paths.ts';
|
|
8
4
|
import type {
|
|
9
5
|
ApiKeyKind,
|
|
10
6
|
ApiOperation,
|
|
@@ -188,10 +184,7 @@ export class OperationRegistry {
|
|
|
188
184
|
}
|
|
189
185
|
|
|
190
186
|
getById(id: string): ApiOperation | undefined {
|
|
191
|
-
|
|
192
|
-
return this.operations.find(
|
|
193
|
-
(operation) => operation.id === id || operation.id === folded,
|
|
194
|
-
);
|
|
187
|
+
return this.operations.find((operation) => operation.id === id);
|
|
195
188
|
}
|
|
196
189
|
|
|
197
190
|
find(filters: {
|
|
@@ -213,13 +206,7 @@ export class OperationRegistry {
|
|
|
213
206
|
return false;
|
|
214
207
|
}
|
|
215
208
|
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
tagFilter &&
|
|
219
|
-
!operation.tags.some(
|
|
220
|
-
(tag) => canonicalSearchText(tag) === canonicalSearchText(tagFilter),
|
|
221
|
-
)
|
|
222
|
-
) {
|
|
209
|
+
if (filters.tag && !operation.tags.includes(filters.tag)) {
|
|
223
210
|
return false;
|
|
224
211
|
}
|
|
225
212
|
|
|
@@ -229,9 +216,7 @@ export class OperationRegistry {
|
|
|
229
216
|
|
|
230
217
|
if (
|
|
231
218
|
filters.pathPrefix &&
|
|
232
|
-
!
|
|
233
|
-
canonicalSearchText(filters.pathPrefix),
|
|
234
|
-
)
|
|
219
|
+
!operation.path.startsWith(filters.pathPrefix)
|
|
235
220
|
) {
|
|
236
221
|
return false;
|
|
237
222
|
}
|
|
@@ -241,18 +226,17 @@ export class OperationRegistry {
|
|
|
241
226
|
}
|
|
242
227
|
|
|
243
228
|
if (search) {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (!haystack.includes(needle)) {
|
|
229
|
+
const haystack = [
|
|
230
|
+
operation.id,
|
|
231
|
+
operation.path,
|
|
232
|
+
operation.summary,
|
|
233
|
+
operation.description ?? '',
|
|
234
|
+
operation.tags.join(' '),
|
|
235
|
+
]
|
|
236
|
+
.join(' ')
|
|
237
|
+
.toLowerCase();
|
|
238
|
+
|
|
239
|
+
if (!haystack.includes(search)) {
|
|
256
240
|
return false;
|
|
257
241
|
}
|
|
258
242
|
}
|
|
@@ -9,7 +9,7 @@ Askell POSTs signed JSON to each URL you register. Verify \`Hook-HMAC\` before p
|
|
|
9
9
|
Headers:
|
|
10
10
|
- Hook-HMAC: base64 HMAC-SHA512 of the **raw body** (secret = \`hmac_secret\` from webhook create)
|
|
11
11
|
- Hook-Event: event type (\`subscription.renewed\`, \`payment.changed\`, or a family wildcard \`subscription.*\`)
|
|
12
|
-
- Hook-API-Version: \`v1\` for plan/subscription/customer/payment/checkout, \`v2\` for subscription_contract / billing_run /
|
|
12
|
+
- Hook-API-Version: \`v1\` for plan/subscription/customer/payment/checkout, \`v2\` for subscription_contract / billing_run / fulfillment_order
|
|
13
13
|
|
|
14
14
|
## Body shape
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ JSON body **is the event object**. It is **not** \`{ event, data }\`.
|
|
|
17
17
|
|
|
18
18
|
Upstream swagger used to document a dummy \`POST /your-webhook-url/\` with \`SubscriptionMultiLite\` (\`{ customer, subscriptions[] }\`). \`sync-specs\` strips that path.
|
|
19
19
|
|
|
20
|
-
Most inbound
|
|
20
|
+
Most inbound families are still undocumented in OpenAPI — this resource is the overlay. Exception: \`fulfillment_order.*\` body **is** \`V2FulfillmentOrder\` (same as \`GET /v2/fulfillment-orders/{id}/\`). Live https://docs.askell.is/en/api/webhooks.html does not list this family yet.
|
|
21
21
|
|
|
22
22
|
Rare historical payloads used \`{ event, data, ref?, sender? }\`. If both \`event\` and \`data\` are objects, use \`data\`.
|
|
23
23
|
|
|
@@ -62,10 +62,12 @@ V2 migration: \`subscription.*\` is **not** aliased onto the new contract (paylo
|
|
|
62
62
|
### checkout.* (v1)
|
|
63
63
|
\`created\`, \`changed\` — \`token\`, \`checkout_url\`, \`status\`.
|
|
64
64
|
|
|
65
|
-
###
|
|
66
|
-
Family wildcard
|
|
65
|
+
### fulfillment_order.* (v2)
|
|
66
|
+
Family wildcard \`fulfillment_order.*\`. Concrete event names beyond the family are not listed in swagger or live webhook docs — do not invent \`created\`/\`changed\`.
|
|
67
67
|
|
|
68
|
-
Body
|
|
68
|
+
Body = \`V2FulfillmentOrder\` = \`GET /v2/fulfillment-orders/{fulfillmentOrderId}/\` (list items are the same object). Physical order from a paid billing run (\`billing_run_id\`; at most one order per run). \`delivery_address\` is a snapshot (later contract address edits do not change it). \`shipping_selection\` is the checkout snapshot. \`fulfillments[]\` are booked shipments (empty until booked / if no shipping providers). Status: \`open\` | \`partially_fulfilled\` | \`fulfilled\` | \`cancelled\`.
|
|
69
|
+
|
|
70
|
+
Register \`fulfillment_order.*\` on \`POST /webhooks/\`. REST backfill: \`GET /v2/fulfillment-orders/?updated_since=\` (secret; newest first; \`403\` if the account has no subscription contracts or shipping is disabled). Read-only — no mark-shipped mutate.
|
|
69
71
|
`;
|
|
70
72
|
|
|
71
73
|
export function registerResources(server: McpServer): void {
|
package/src/server.ts
CHANGED
|
@@ -37,7 +37,7 @@ Workflow:
|
|
|
37
37
|
|
|
38
38
|
API models:
|
|
39
39
|
- v1 (legacy): PlanVariant + Subscription at paths like /subscriptions/, /customers/. Still supported for existing integrations.
|
|
40
|
-
- v2 (current): Catalog, bundles, quotes, checkouts, subscription contracts, billing runs under /v2/. Prefer v2 for new integrations.
|
|
40
|
+
- v2 (current): Catalog, bundles, quotes, checkouts, subscription contracts, billing runs, fulfillment orders under /v2/. Prefer v2 for new integrations.
|
|
41
41
|
- Prose docs at https://docs.askell.is/api/ may describe flows (embedded checkout, 3D Secure, wallet passes) not fully listed in OpenAPI.
|
|
42
42
|
|
|
43
43
|
API layout:
|
|
@@ -47,13 +47,6 @@ API layout:
|
|
|
47
47
|
- V2 list endpoints paginate only when page_size is provided (default 10, max 1000).
|
|
48
48
|
- GET /v2/customer-entitlements/ requires customer_reference query param.
|
|
49
49
|
|
|
50
|
-
V2 fulfilment (British spelling on the wire — not American fulfillment):
|
|
51
|
-
- Read-only GET /v2/fulfilment-orders/ and GET /v2/fulfilment-orders/{fulfilmentOrderId}/. No POST/PATCH in OpenAPI. Path param is the order's id, not per-account number.
|
|
52
|
-
- List 403 = account does not use V2 contracts or shipping is disabled (not a bad API key). GET 404 = missing or other-account.
|
|
53
|
-
- Reconciliation: poll list with updated_since = updated_at of the last order you processed. Filter by contract, customer, customer_reference, status.
|
|
54
|
-
- Webhook family fulfilment_order.* (not fulfillment_order.*). Body is V2FulfilmentOrder, same as GET. Live webhook docs omit this family; bundled spec is right. Do not invent event verbs beyond the wildcard until Askell lists them.
|
|
55
|
-
- Quote and checkout shipping_fee is already in totals; do not add it again (askell_describe_operation omits response schemas). shipping_selection.location is the structured pickup block; flat location_* are fallback.
|
|
56
|
-
|
|
57
50
|
V2 discounts — two systems, not v1 Subscription.discount (0-100 on a PlanVariant; never send that to v2):
|
|
58
51
|
- Coupons: one active per contract. GET /v2/subscription-contracts/{id}/discount/ (also nested as contract.discount). Apply with POST .../apply-code/ {promotion_code}. Remove with POST .../remove-discount/.
|
|
59
52
|
- Quotes (POST /v2/subscription-offer-quotes/): pass promotion_code for coupons. When quoting an existing customer, pass customer (numeric id) or combo discounts from their other active contracts and promo-code customer restrictions are skipped. First-period subtotal/tax/total already include coupon + combo. quote.recurring_* include combo, not the coupon — renewal-with-coupon is discount.recurring_final_amount, and only while duration still applies (once → after first payment use recurring_*). combo_discounts[] and discount.recurring_* are on the quote response (askell_describe_operation omits response schemas). Combo is automatic, not apply-code.
|
|
@@ -62,10 +55,13 @@ V2 checkout notes:
|
|
|
62
55
|
- checkout_url on V2 checkouts points to the API object URL, not a hosted payment page.
|
|
63
56
|
- GET contract.subscriber_page is the customer-facing subscription management URL (readOnly, nullable). Not checkout_url, not v1 /public/payments/{id}/ (hosted signup). Do not send it on create/patch.
|
|
64
57
|
- finalize: a recurring offer needs a verified payment method even when due-now/total is 0 (trial or fully discounted first period). Only a free one-time purchase finalizes without one. Live docs still say "unless 0 ISK" — ignore that; bundled OpenAPI is right.
|
|
65
|
-
- Hosted POST /v2/checkouts/: shipping {option, location_id?} is required when the offer has physical products and the account has active shipping options. No shipping-options list in OpenAPI (ids are account config). Pickup options need location_id. Snapshot is contract.shipping_selection, not on V2Checkout.
|
|
58
|
+
- Hosted POST /v2/checkouts/: shipping {option, location_id?} is required when the offer has physical products and the account has active shipping options. No shipping-options list in OpenAPI (ids are account config). Pickup options need location_id. Snapshot is contract.shipping_selection (plus location / zone_name / weight_band), not on V2Checkout. Rate-table option with no zip/weight rate: 400, shipping_code shipping_not_available. Quote/checkout totals already include shipping_fee when present.
|
|
66
59
|
- Hosted iframe (not askell.js): POST /v2/checkouts/ and POST .../payment-method-registrations/ take allowed_origin (one origin, no path; http only localhost/loopback). Replaces account-level frame-ancestors; GET empty string = account-level. Rejected on /v2/checkout-sessions/ (sales-channel allowed_origins[]).
|
|
67
60
|
- Embedded checkout uses POST /v2/checkout-sessions/ plus browser session-token sub-paths (widget collects address/shipping; see docs, not all in OpenAPI).
|
|
68
61
|
|
|
62
|
+
V2 fulfillment (warehouse, read-only):
|
|
63
|
+
- GET /v2/fulfillment-orders/ and GET /v2/fulfillment-orders/{id}/. Same body as fulfillment_order.* webhooks (V2FulfillmentOrder). Secret key. 403 if the account has no subscription contracts or shipping is disabled. Poll updated_since after a missed webhook (newest first). No POST/PATCH — cannot mark shipped via the API.
|
|
64
|
+
|
|
69
65
|
Auth:
|
|
70
66
|
- Most endpoints need the secret API key.
|
|
71
67
|
- Only temporary payment method and checkout status endpoints use the public key.
|
|
@@ -78,7 +74,7 @@ Safety:
|
|
|
78
74
|
|
|
79
75
|
Resources:
|
|
80
76
|
- askell://spec/v1 and askell://spec/v2 — bundled OpenAPI
|
|
81
|
-
- askell://docs/webhook-events — inbound webhook payloads (most not in OpenAPI;
|
|
77
|
+
- askell://docs/webhook-events — inbound webhook payloads (most families not in OpenAPI; fulfillment_order.* is V2FulfillmentOrder), HMAC-SHA512, /webhooks/ hmac_secret`;
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
export function createServer(config: AppConfig): McpServer {
|
package/src/tools/analysis.ts
CHANGED
|
@@ -141,7 +141,7 @@ export function registerAnalysisTools(
|
|
|
141
141
|
{
|
|
142
142
|
title: 'Subscription contract overview (v2)',
|
|
143
143
|
description:
|
|
144
|
-
'Fetch a v2 subscription contract and recent billing runs filtered by contract id. The contract payload includes `discount` (active coupon) when one is applied, `shipping_selection` when shipping was chosen at checkout, and `subscriber_page` (customer-facing management URL, read-only).
|
|
144
|
+
'Fetch a v2 subscription contract and recent billing runs filtered by contract id. The contract payload includes `discount` (active coupon) when one is applied, `shipping_selection` when shipping was chosen at checkout, and `subscriber_page` (customer-facing management URL, read-only).',
|
|
145
145
|
inputSchema: z.object({
|
|
146
146
|
contractId: z
|
|
147
147
|
.union([z.string().min(1), z.int()])
|
package/src/tools/discovery.ts
CHANGED
|
@@ -21,7 +21,7 @@ const listInputSchema = z.object({
|
|
|
21
21
|
.string()
|
|
22
22
|
.optional()
|
|
23
23
|
.describe(
|
|
24
|
-
'Case-insensitive search in id, path, summary, description, tags
|
|
24
|
+
'Case-insensitive search in id, path, summary, description, tags',
|
|
25
25
|
),
|
|
26
26
|
apiKeyKind: z
|
|
27
27
|
.enum(['secret', 'public'])
|