rightcard-mcp 0.1.5 → 0.1.7
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/dist/configurable.js +5 -5
- package/dist/engine.js +9 -9
- package/dist/model.js +3 -3
- package/dist/valuation.js +4 -0
- package/package.json +1 -1
package/dist/configurable.js
CHANGED
|
@@ -2,25 +2,25 @@ export const CONFIGURABLE_SPECS = [
|
|
|
2
2
|
{
|
|
3
3
|
key: "amex_business_gold", displayName: "Amex Business Gold", bonusRate: 4, slots: 2,
|
|
4
4
|
eligible: ["dining", "gas", "travel"],
|
|
5
|
-
note: "4× on the 2 categories your business spends the most on each billing cycle
|
|
5
|
+
note: "4× on the 2 categories your business spends the most on each billing cycle (restaurants, gas, airfare, advertising, shipping and select tech), on up to $150,000/year (1× after). Of the categories RightCard tracks, pick the up-to-2 you expect: restaurants, gas, or airfare.",
|
|
6
6
|
matchNeedles: ["businessgold"], multiCardMax: 1,
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
key: "bofa_business_customized", displayName: "BofA Business Advantage Customized Cash", bonusRate: 3, slots: 1,
|
|
10
10
|
eligible: ["gas", "travel"],
|
|
11
|
-
note: "3% in your chosen business category (plus an automatic 2% at restaurants), on up to $50,000 in combined choice + dining purchases each year (1% after). Of the categories RightCard tracks, the choice is gas or travel
|
|
11
|
+
note: "3% in your chosen business category (plus an automatic 2% at restaurants), on up to $50,000 in combined choice + dining purchases each year (1% after). Of the categories RightCard tracks, the choice is gas or travel. Gas is the default.",
|
|
12
12
|
matchNeedles: ["businessadvantagecustomized"], multiCardMax: 1,
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
key: "bilt_obsidian", displayName: "Bilt Obsidian Card", bonusRate: 3, slots: 1,
|
|
16
16
|
eligible: ["dining", "grocery"],
|
|
17
|
-
note: "3× on your choice of dining OR grocery, set for the calendar year
|
|
17
|
+
note: "3× on your choice of dining OR grocery, set for the calendar year. Pick the one you spend more on (grocery counts up to $25,000/year). Everything else earns 1×.",
|
|
18
18
|
matchNeedles: ["obsidian"], multiCardMax: 1,
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
key: "citi_custom_cash", displayName: "Citi Custom Cash", bonusRate: 5, slots: 1,
|
|
22
22
|
eligible: ["dining", "grocery", "gas", "travel", "drugstore", "transit", "streaming"],
|
|
23
|
-
note: "5% on your top eligible category, up to $500 each billing cycle (1% after). Citi applies it to your highest-spend category automatically
|
|
23
|
+
note: "5% on your top eligible category, up to $500 each billing cycle (1% after). Citi applies it to your highest-spend category automatically. Set the one you expect so RightCard recommends it.",
|
|
24
24
|
matchNeedles: ["customcash"], multiCardMax: 3,
|
|
25
25
|
},
|
|
26
26
|
{
|
|
@@ -32,7 +32,7 @@ export const CONFIGURABLE_SPECS = [
|
|
|
32
32
|
{
|
|
33
33
|
key: "venmo_credit_card", displayName: "Venmo Credit Card", bonusRate: 3, slots: 1,
|
|
34
34
|
eligible: ["dining", "grocery", "gas", "travel", "transit"],
|
|
35
|
-
note: "3% back on the category you spend most in each month (2% on your second, 1% everywhere else). Venmo applies the tiers automatically from your actual spending
|
|
35
|
+
note: "3% back on the category you spend most in each month (2% on your second, 1% everywhere else). Venmo applies the tiers automatically from your actual spending. Set the category you expect to top so RightCard recommends it there. The 2% tier isn't modeled.",
|
|
36
36
|
matchNeedles: ["venmo"], multiCardMax: 1,
|
|
37
37
|
},
|
|
38
38
|
];
|
package/dist/engine.js
CHANGED
|
@@ -162,25 +162,25 @@ function rateContext(card, earn, valueCents, override, configured, v, category,
|
|
|
162
162
|
const pointsAbove = valuesPointsAboveCash(v, card) && card.rewardCurrency !== null;
|
|
163
163
|
if (configured) {
|
|
164
164
|
const bonusStr = formatPercent(configured.bonusRate);
|
|
165
|
-
reason = `${card.displayName} is your choose-your-category card
|
|
165
|
+
reason = `${card.displayName} is your choose-your-category card. You set ${disp} for ${bonusStr}, the most in your wallet here.`;
|
|
166
166
|
if (chosenCategoryCount > configured.slots) {
|
|
167
|
-
reason += ` You hold more than one
|
|
168
|
-
lines.push(`${bonusStr} on ${disp}
|
|
167
|
+
reason += ` You hold more than one. Use the copy you set for ${disp}.`;
|
|
168
|
+
lines.push(`${bonusStr} on ${disp} · your chosen category (the ${disp} copy)`);
|
|
169
169
|
}
|
|
170
170
|
else {
|
|
171
|
-
lines.push(`${bonusStr} on ${disp}
|
|
171
|
+
lines.push(`${bonusStr} on ${disp} · your chosen category`);
|
|
172
172
|
}
|
|
173
173
|
lines.push(configured.note);
|
|
174
174
|
}
|
|
175
175
|
else if (override) {
|
|
176
176
|
const scope = override.merchant ? `at ${override.merchant}` : cat;
|
|
177
177
|
if (isPermanentMerchantBenefit(override, now)) {
|
|
178
|
-
reason = `${card.displayName} earns ${valueStr} ${scope}
|
|
179
|
-
lines.push(`${valueStr} ${scope}
|
|
178
|
+
reason = `${card.displayName} earns ${valueStr} ${scope}, the most in your wallet here.`;
|
|
179
|
+
lines.push(`${valueStr} ${scope} · card benefit`);
|
|
180
180
|
}
|
|
181
181
|
else {
|
|
182
182
|
const what = override.label ?? `${formatPercent(override.bonusRate)} rotating ${scope} bonus`;
|
|
183
|
-
reason = `${card.displayName} has a rotating bonus ${scope} active right now
|
|
183
|
+
reason = `${card.displayName} has a rotating bonus ${scope} active right now: ${valueStr} this quarter, the most in your wallet.`;
|
|
184
184
|
lines.push(`Rotating bonus: ${what}`);
|
|
185
185
|
}
|
|
186
186
|
if (pointsAbove) {
|
|
@@ -196,12 +196,12 @@ function rateContext(card, earn, valueCents, override, configured, v, category,
|
|
|
196
196
|
const earnStr = formatMultiplier(earn);
|
|
197
197
|
const unit = earnUnitLabel(v, card);
|
|
198
198
|
const cppStr = fmt1(centsPerPoint(v, card));
|
|
199
|
-
reason = `${card.displayName} earns ${earnStr} ${unit} on ${cat}
|
|
199
|
+
reason = `${card.displayName} earns ${earnStr} ${unit} on ${cat}. At ${cppStr}¢ each, that's ${valueStr} per dollar, the most in your wallet.`;
|
|
200
200
|
lines.push(`Earns ${earnStr} ${unit}`);
|
|
201
201
|
lines.push(`Valued at ${cppStr}¢/pt → ${valueStr} per $1`);
|
|
202
202
|
}
|
|
203
203
|
else {
|
|
204
|
-
reason = `${card.displayName} earns the most for ${cat}
|
|
204
|
+
reason = `${card.displayName} earns the most for ${cat}: ${valueStr} per dollar.`;
|
|
205
205
|
// Mirrors the Swift engine (Sep 2026): a points/miles card in cash mode
|
|
206
206
|
// still EARNS points — never blur the earn (4x points) with its value (4%).
|
|
207
207
|
if (card.rewardCurrency !== null && card.rewardCurrency !== "cashback") {
|
package/dist/model.js
CHANGED
|
@@ -123,9 +123,9 @@ export function merchantFromRow(r) {
|
|
|
123
123
|
export function exclusionCaveat(m) {
|
|
124
124
|
switch (m.merchantType) {
|
|
125
125
|
case "superstore":
|
|
126
|
-
return `${m.displayName} usually codes as a superstore (not a supermarket), so grocery bonuses often don't post
|
|
126
|
+
return `${m.displayName} usually codes as a superstore (not a supermarket), so grocery bonuses often don't post. This is your best guaranteed card.`;
|
|
127
127
|
case "warehouse_club": {
|
|
128
|
-
const base = `${m.displayName} codes as a warehouse club, so category bonuses usually don't post
|
|
128
|
+
const base = `${m.displayName} codes as a warehouse club, so category bonuses usually don't post. This is your best guaranteed card.`;
|
|
129
129
|
// Costco alone is also NETWORK-locked: warehouse registers take Visa
|
|
130
130
|
// only (Costco.com takes other networks). Mirrors the Swift engine —
|
|
131
131
|
// keep both sides identical or the parity gate fails.
|
|
@@ -139,7 +139,7 @@ export function exclusionCaveat(m) {
|
|
|
139
139
|
// warehouse's Visa-only registers; their own MCC stays honest (no
|
|
140
140
|
// "bonuses don't post" clause). Mirrors the Swift engine (Sep 2026).
|
|
141
141
|
if (acceptedNetworksAtRegister(m) !== null) {
|
|
142
|
-
return "Costco takes Visa only at the register
|
|
142
|
+
return "Costco takes Visa only at the register. This is your best card that works in-store. Other networks work on Costco.com.";
|
|
143
143
|
}
|
|
144
144
|
return null;
|
|
145
145
|
}
|
package/dist/valuation.js
CHANGED
|
@@ -72,6 +72,10 @@ export function inferProgram(issuer, id, currency) {
|
|
|
72
72
|
return "rbcAvion";
|
|
73
73
|
if (has("bmo"))
|
|
74
74
|
return "bmoRewards";
|
|
75
|
+
// Navy Federal points: ~1¢, no transfer partners. Before the Amex needle:
|
|
76
|
+
// "More Rewards American Express" is a Navy Federal card (mirrors Swift, Oct 5 2026).
|
|
77
|
+
if (has("navy"))
|
|
78
|
+
return "fixedValue";
|
|
75
79
|
if (has("amex", "american express", "membership"))
|
|
76
80
|
return "amexMR";
|
|
77
81
|
if (has("chase", "sapphire", "freedom", "ink"))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rightcard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"mcpName": "ai.rightcard/mcp",
|
|
5
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",
|