rightcard-mcp 0.1.4 → 0.1.6

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.
@@ -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 — from restaurants, gas, airfare, advertising, shipping & 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.",
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 — default gas.",
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 — pick the one you spend more on (grocery counts up to $25,000/year). Everything else earns 1×.",
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 — set the one you expect so RightCard recommends it.",
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 — set the category you expect to top so RightCard recommends it there. The 2% tier isn't modeled.",
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 — you set ${disp} for ${bonusStr}, the most in your wallet here.`;
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 — use the copy you set for ${disp}.`;
168
- lines.push(`${bonusStr} on ${disp} — your chosen category (the ${disp} copy)`);
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} — your chosen category`);
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} — the most in your wallet here.`;
179
- lines.push(`${valueStr} ${scope} — card benefit`);
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 — ${valueStr} this quarter, the most in your wallet.`;
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} — at ${cppStr}¢ each that's ${valueStr} per dollar, the most in your wallet.`;
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} — ${valueStr} per dollar.`;
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 — this is your best guaranteed card.`;
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 — this is your best guaranteed card.`;
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 — this is your best card that works in-store. Other networks work on Costco.com.";
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
@@ -56,8 +56,8 @@ export function inferProgram(issuer, id, currency) {
56
56
  return "southwest";
57
57
  if (has("jetblue", "trueblue"))
58
58
  return "jetBlue";
59
- if (has("alaska"))
60
- return "alaska";
59
+ if (has("alaska", "atmos"))
60
+ return "alaska"; // Atmos Ascent's id has no "alaska" (mirrors Swift)
61
61
  if (has("aeroplan", "air canada"))
62
62
  return "aeroplan";
63
63
  if (has("british airways", "avios"))
@@ -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"))
@@ -94,6 +98,10 @@ export function inferProgram(issuer, id, currency) {
94
98
  // GM — the generic 2¢ fallback read its flat 3x as "6%" (mirrors Swift).
95
99
  if (has("gm_rewards", "gm rewards"))
96
100
  return "fixedValue";
101
+ // Bank of America Premium Rewards + PenFed points: a set ~1¢, no transfer
102
+ // partners (mirrors RewardValuation.infer, Sep 26 2026).
103
+ if (has("bank of america", "bank_of_america", "penfed") || s.startsWith("bofa"))
104
+ return "fixedValue";
97
105
  return currency === "miles" ? "genericMiles" : "genericPoints";
98
106
  }
99
107
  export function makeValuation(mode = "cash") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rightcard-mcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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",