rightcard-mcp 0.1.2 → 0.1.3
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/LICENSE +21 -0
- package/dist/data.js +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +16 -1
- package/dist/model.d.ts +6 -0
- package/dist/model.js +22 -2
- package/dist/tools.js +7 -2
- package/dist/valuation.d.ts +3 -1
- package/dist/valuation.js +8 -2
- package/package.json +3 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RightCard
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/data.js
CHANGED
|
@@ -25,7 +25,7 @@ async function getAll(path, fetchImpl) {
|
|
|
25
25
|
}
|
|
26
26
|
export async function loadCatalog(fetchImpl = fetch) {
|
|
27
27
|
const [cardRows, overrideRows, merchantRows] = await Promise.all([
|
|
28
|
-
getAll("cards?select=id,name,issuer,reward_currency,base_rate,standard_multipliers,annual_fee,verify_status&country=eq.US&order=id", fetchImpl),
|
|
28
|
+
getAll("cards?select=id,name,issuer,reward_currency,base_rate,standard_multipliers,annual_fee,verify_status,network&country=eq.US&order=id", fetchImpl),
|
|
29
29
|
getAll("active_overrides?select=*&order=id", fetchImpl),
|
|
30
30
|
getAll("merchants?select=id,display_name,category,sub_key,merchant_type,is_online,aliases&country=eq.US&order=id", fetchImpl),
|
|
31
31
|
]);
|
package/dist/engine.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface RecommendInput {
|
|
|
33
33
|
cardConfigs?: Record<string, Set<SpendCategory>>;
|
|
34
34
|
now: Date;
|
|
35
35
|
strictMerchantMatch?: boolean;
|
|
36
|
+
/** Register network lock (Costco = visa-only). null/undefined = no restriction. */
|
|
37
|
+
acceptedNetworks?: Set<string> | null;
|
|
36
38
|
}
|
|
37
39
|
export declare function formatPercent(v: number): string;
|
|
38
40
|
export declare function formatMultiplier(v: number): string;
|
package/dist/engine.js
CHANGED
|
@@ -241,9 +241,24 @@ function subcategoryBreakdown(cards, category, overrides, cardConfigs, v, now) {
|
|
|
241
241
|
}
|
|
242
242
|
/** MerchantYieldEngine.recommend — base-rate mode (offers: []) */
|
|
243
243
|
export function recommend(input) {
|
|
244
|
-
const {
|
|
244
|
+
const { category, overrides, now } = input;
|
|
245
|
+
let cards = input.cards;
|
|
245
246
|
if (cards.length === 0)
|
|
246
247
|
return null;
|
|
248
|
+
// Network-acceptance gate (Costco's Visa-only registers). Mirrors the Swift
|
|
249
|
+
// engine: a KNOWN-incompatible card can't win when a compatible (or
|
|
250
|
+
// unknown-network) card exists; if every card is known-incompatible, the
|
|
251
|
+
// full wallet answers (works on the merchant's site) and the warehouse
|
|
252
|
+
// caveat carries the register story. Unknown (null) network never gates.
|
|
253
|
+
const accepted = input.acceptedNetworks;
|
|
254
|
+
if (accepted && accepted.size > 0) {
|
|
255
|
+
const eligible = cards.filter((c) => {
|
|
256
|
+
const net = c.network?.toLowerCase();
|
|
257
|
+
return net == null || accepted.has(net);
|
|
258
|
+
});
|
|
259
|
+
if (eligible.length > 0)
|
|
260
|
+
cards = eligible;
|
|
261
|
+
}
|
|
247
262
|
const v = withPooling(input.valuation, cards);
|
|
248
263
|
const cardConfigs = input.cardConfigs ?? {};
|
|
249
264
|
const strict = input.strictMerchantMatch ?? false;
|
package/dist/model.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface CreditCard {
|
|
|
15
15
|
multipliers: Record<string, MultiplierField>;
|
|
16
16
|
annualFee?: number | null;
|
|
17
17
|
verifyStatus?: string | null;
|
|
18
|
+
/** Payment network (migration 0017): visa|mastercard|amex|discover; null = unknown, never gates. */
|
|
19
|
+
network?: string | null;
|
|
18
20
|
}
|
|
19
21
|
/** Supabase `cards` row → CreditCard (SupabaseCard.toCreditCard). */
|
|
20
22
|
export declare function cardFromRow(row: any): CreditCard;
|
|
@@ -51,5 +53,9 @@ export interface Merchant {
|
|
|
51
53
|
export declare function merchantFromRow(r: any): Merchant;
|
|
52
54
|
/** Merchant.exclusionCaveat */
|
|
53
55
|
export declare function exclusionCaveat(m: Merchant): string | null;
|
|
56
|
+
/** Merchant.acceptedNetworksAtRegister — Costco's Visa-only register lock.
|
|
57
|
+
* null for everything else (no restriction). Costco.com / online rows are
|
|
58
|
+
* never gated (they take other networks). Mirrors the Swift twin exactly. */
|
|
59
|
+
export declare function acceptedNetworksAtRegister(m: Merchant): Set<string> | null;
|
|
54
60
|
/** Merchant.isCuratedBrand */
|
|
55
61
|
export declare function isCuratedBrand(m: Merchant): boolean;
|
package/dist/model.js
CHANGED
|
@@ -27,6 +27,7 @@ export function cardFromRow(row) {
|
|
|
27
27
|
multipliers,
|
|
28
28
|
annualFee: row.annual_fee ?? null,
|
|
29
29
|
verifyStatus: row.verify_status ?? null,
|
|
30
|
+
network: row.network ?? null,
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
function fieldFromJSON(v) {
|
|
@@ -117,12 +118,31 @@ export function exclusionCaveat(m) {
|
|
|
117
118
|
switch (m.merchantType) {
|
|
118
119
|
case "superstore":
|
|
119
120
|
return `${m.displayName} usually codes as a superstore (not a supermarket), so grocery bonuses often don't post — this is your best guaranteed card.`;
|
|
120
|
-
case "warehouse_club":
|
|
121
|
-
|
|
121
|
+
case "warehouse_club": {
|
|
122
|
+
const base = `${m.displayName} codes as a warehouse club, so category bonuses usually don't post — this is your best guaranteed card.`;
|
|
123
|
+
// Costco alone is also NETWORK-locked: warehouse registers take Visa
|
|
124
|
+
// only (Costco.com takes other networks). Mirrors the Swift engine —
|
|
125
|
+
// keep both sides identical or the parity gate fails.
|
|
126
|
+
if (m.displayName.toLowerCase().includes("costco")) {
|
|
127
|
+
return base + " Note: Costco warehouses take Visa only at the register; other networks work on Costco.com.";
|
|
128
|
+
}
|
|
129
|
+
return base;
|
|
130
|
+
}
|
|
122
131
|
default:
|
|
123
132
|
return null;
|
|
124
133
|
}
|
|
125
134
|
}
|
|
135
|
+
/** Merchant.acceptedNetworksAtRegister — Costco's Visa-only register lock.
|
|
136
|
+
* null for everything else (no restriction). Costco.com / online rows are
|
|
137
|
+
* never gated (they take other networks). Mirrors the Swift twin exactly. */
|
|
138
|
+
export function acceptedNetworksAtRegister(m) {
|
|
139
|
+
const name = m.displayName.toLowerCase();
|
|
140
|
+
if (m.merchantType !== "warehouse_club" || !name.includes("costco"))
|
|
141
|
+
return null;
|
|
142
|
+
if (m.isOnline || name.includes(".com"))
|
|
143
|
+
return null;
|
|
144
|
+
return new Set(["visa"]);
|
|
145
|
+
}
|
|
126
146
|
/** Merchant.isCuratedBrand */
|
|
127
147
|
export function isCuratedBrand(m) {
|
|
128
148
|
if (m.aliases.length > 0)
|
package/dist/tools.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// and `caveats`; verified rows only in rankings; quarantined cards are NAMED, never ranked.
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { ALL_CATEGORIES, exclusionCaveat, hasAnyReward, multiplier, overrideIsActive, isPermanentMerchantBenefit } from "./model.js";
|
|
5
|
+
import { ALL_CATEGORIES, acceptedNetworksAtRegister, exclusionCaveat, hasAnyReward, multiplier, overrideIsActive, isPermanentMerchantBenefit } from "./model.js";
|
|
6
6
|
import { recommend, formatPercent, formatMultiplier } from "./engine.js";
|
|
7
7
|
import { rankedBrandMatches, searchNormalized } from "./search.js";
|
|
8
8
|
import { makeValuation, centsPerPoint, withPooling, effectiveProgram, PROGRAM_DISPLAY, TRAVEL_CPP } from "./valuation.js";
|
|
@@ -13,6 +13,10 @@ function cardSummary(c) {
|
|
|
13
13
|
return {
|
|
14
14
|
id: c.id, name: c.displayName, issuer: c.issuer, reward_currency: c.rewardCurrency,
|
|
15
15
|
verify_status: c.verifyStatus ?? "unknown", annual_fee: c.annualFee ?? null,
|
|
16
|
+
// Payment network (visa|mastercard|amex|discover) or null = unknown.
|
|
17
|
+
// Stated so the model never guesses it (Costco's registers are Visa-only;
|
|
18
|
+
// a guessed network in an answer is exactly the hallucination this bans).
|
|
19
|
+
network: c.network ?? null,
|
|
16
20
|
};
|
|
17
21
|
}
|
|
18
22
|
function resolveWallet(catalog, ids) {
|
|
@@ -92,7 +96,8 @@ export function buildServer(getCatalog) {
|
|
|
92
96
|
configs[k] = new Set(v);
|
|
93
97
|
const now = on ? new Date(on + "T00:00:00Z") : new Date();
|
|
94
98
|
const r = recommend({ cards, category: cat, subKey, merchantName: merchant ?? null, overrides: catalog.overrides,
|
|
95
|
-
valuation: makeValuation(mode), cardConfigs: configs, now
|
|
99
|
+
valuation: makeValuation(mode), cardConfigs: configs, now,
|
|
100
|
+
acceptedNetworks: dir ? acceptedNetworksAtRegister(dir) : null });
|
|
96
101
|
if (!r)
|
|
97
102
|
return err("No recommendation could be computed.");
|
|
98
103
|
const answer = {
|
package/dist/valuation.d.ts
CHANGED
|
@@ -20,5 +20,7 @@ export declare function centsPerPoint(v: Valuation, card: CreditCard): number;
|
|
|
20
20
|
export declare function valuesPointsAboveCash(v: Valuation, card: CreditCard): boolean;
|
|
21
21
|
export declare function earnUnitLabel(v: Valuation, card: CreditCard): string;
|
|
22
22
|
export declare function basisLabel(v: Valuation, card: CreditCard): string;
|
|
23
|
-
/** Swift String(format: "%.1f")
|
|
23
|
+
/** Swift String(format: "%.1f") — printf rounds EXACT binary ties to even
|
|
24
|
+
* (1.25 → "1.2"), where Math.round rounds them up ("1.3"). Surfaced Aug 29
|
|
25
|
+
* 2026 when a live card hit a 1.25x earn and 27 golden cases split. */
|
|
24
26
|
export declare function fmt1(x: number): string;
|
package/dist/valuation.js
CHANGED
|
@@ -154,7 +154,13 @@ export function basisLabel(v, card) {
|
|
|
154
154
|
const pooled = v.pooledPrograms[card.id] !== undefined ? " (pooled with your wallet)" : "";
|
|
155
155
|
return `${PROGRAM_DISPLAY[effectiveProgram(v, card)]} · ${fmt1(cpp)}¢ travel value${pooled}`;
|
|
156
156
|
}
|
|
157
|
-
/** Swift String(format: "%.1f")
|
|
157
|
+
/** Swift String(format: "%.1f") — printf rounds EXACT binary ties to even
|
|
158
|
+
* (1.25 → "1.2"), where Math.round rounds them up ("1.3"). Surfaced Aug 29
|
|
159
|
+
* 2026 when a live card hit a 1.25x earn and 27 golden cases split. */
|
|
158
160
|
export function fmt1(x) {
|
|
159
|
-
|
|
161
|
+
const scaled = x * 10;
|
|
162
|
+
let r = Math.round(scaled);
|
|
163
|
+
if (scaled - Math.floor(scaled) === 0.5 && r % 2 !== 0)
|
|
164
|
+
r -= 1;
|
|
165
|
+
return (r / 10).toFixed(1);
|
|
160
166
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rightcard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"mcpName": "ai.rightcard/mcp",
|
|
5
|
-
"description": "RightCard MCP
|
|
5
|
+
"description": "RightCard MCP — the card-selection oracle for agents. Which of your credit cards to use at any store, from verified data. No bank login.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"bin": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/
|
|
28
|
+
"url": "git+https://github.com/rightcard/rightcard-mcp.git"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"dist",
|