protogrid

Search / io.github.cwasley/earnrate

earnrate

R2· OAuth, once

Which credit card earns the most at a merchant, from your cards or every card, and why.

v0.2.28 · active · repository · website · descriptor JSON · versions

An agent can connect after a one-time OAuth consent by a human.Remote endpoint behind OAuth 2. Tokens stay with the agent; the registry only exposes the authorization metadata.
Quality94strong
Trust91of 100
Uptime, 30 days94%
Latency p50478 ms

Connect

8 clients · secrets stay placeholders
{
  "mcpServers": {
    "earnrate": {
      "type": "http",
      "url": "https://mcp.earnrate.dev"
    }
  }
}
claude mcp add --transport http earnrate https://mcp.earnrate.dev
[mcp_servers.earnrate]
url = "https://mcp.earnrate.dev"
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "earnrate": {
      "type": "remote",
      "url": "https://mcp.earnrate.dev",
      "enabled": true
    }
  }
}
{
  "mcpServers": {
    "earnrate": {
      "type": "http",
      "url": "https://mcp.earnrate.dev"
    }
  },
  "deeplink": "cursor://anysphere.cursor-deeplink/mcp/install?name=earnrate&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vbWNwLmVhcm5yYXRlLmRldiJ9"
}
{
  "servers": {
    "earnrate": {
      "type": "http",
      "url": "https://mcp.earnrate.dev"
    }
  }
}
{
  "mcpServers": {
    "earnrate": {
      "httpUrl": "https://mcp.earnrate.dev"
    }
  }
}
extensions:
  "earnrate":
    enabled: true
    name: "earnrate"
    type: streamable_http
    uri: "https://mcp.earnrate.dev"
    timeout: 300

Quality 94 / 100strong

protocoln/a · weight 25
  • n/a
    protocol.modern, protocol.stateless, protocol.transport, protocol.list_ttlno remote answered a protocol handshake (not probed yet, unreachable, or local-only)
authorization100 · weight 15
  • pass
    auth.prmProtected Resource Metadata (RFC 9728) published
  • pass
    auth.as_metadataauthorization server metadata reachable
  • pass
    auth.cimdClient ID Metadata Documents supported
  • n/a
    auth.secret_in_urlno templated URL
tool hygiene88 · weight 30
  • pass
    tools.descriptionsevery tool has a description
  • pass
    tools.description_lengthdescriptions are concise
  • pass
    tools.schemasevery tool has an object input schema
  • pass
    tools.annotations16 of 16 tools declare readOnlyHint or destructiveHint
  • warn
    tools.directory_hints7 of 16 tools declare all four hints; missing: best_card_for_purchase (destructiveHint, idempotentHint); resolve_merchant (destructiveHint, idempotentHint); list_wallet (destructiveHint, idempotentHint); …
  • warn
    tools.token_costabout 8,469 tokens to load every tool
  • pass
    tools.api_dumptools are not a one-to-one API dump
  • pass
    tools.hidden_textno hidden text in tool names, descriptions, schemas or annotations
stability100 · weight 15
  • pass
    stability.changesno tool changes in 30 days
  • pass
    stability.rug_pullno tool changed its meaning under the same name
  • pass
    stability.new_linksno tool change named a new site
dependenciesn/a · weight 15
  • n/a
    deps.known_vulns, deps.mcp_sdk_version, deps.resolvabledependencies not resolved yet

16 tools, about 8,469 tokens to load them all · tool set df9e6a280957 · checked 2026-11-14 07:03 UTC

Tool changes

No tool definition changes recorded. A server's first observation is its baseline; later probes record what changes.

Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed. How it is computed.

Trust 91 / 100

multi-version-spam
provenance90
liveness98
freshness100
hygiene75

+repository +namespace-matches-repo +website +reachable +uptime-94% +updated-0d-ago -multi-version-spam(34)

Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed. How it is computed.

Dependencies

[email protected] npmnot resolved yet; dependencies are read once a day

Dependency graphs from deps.dev; advisories from OSV.dev, including the GitHub Advisory Database and the PyPI Advisory Database; all CC BY 4.0. The graph is what a clean install of this version resolves today; nothing was installed or run. How it is checked.

Endpoints

remoteauthreachableuptime 30dp50protocollast ok
https://mcp.earnrate.dev streamable-httpoauth2 AS: https://mcp.earnrate.devyes94%478 ms–2026-11-14
packageregistryversionruntimesecrets
earnratenpm0.2.28npxANTHROPIC_API_KEY

Tools (16)

add_cardAdds a card the user holds to their wallet by its id from search_cards, creating the wallet if there isn't one. The reply says what the card still needs (category picks, memberships, activations); ask the user about those. Stores only the card's product id and settings, never card numbers.
best_card_for_purchaseread-onlyWhich card earns the most rewards for a purchase, and why. With no cards given and no saved wallet, it ranks every card earnrate knows (in `allCards`): a complete answer for anyone. With cards named in the conversation (`cards`) or a saved wallet, it picks the best of the user's own: `decision.cardId` is the pick and `decision.checkoutLabel` is how the card appears at checkout, with every usable card ranked with reasons, cards the merchant won't take, assumptions, warnings, and when a different point valuation would change the pick. Never needs or returns card numbers.
best_cards_forread-onlyRanks every card earnrate knows, not only the user's, for one purchase or category, each set up at its best (the right category pick, activated, cap unused), with its annual fee and what earning the rate takes. Use it for questions like "what's the best card for groceries?" or "which card should I get for Amazon?". It needs no wallet. For which of the user's own cards to use, call best_card_for_purchase instead. Mention the annual fee and any conditions alongside each card.
cheat_sheetread-onlyThe card to use for each everyday kind of purchase right now: groceries, dining, gas, Amazon, Costco, travel, streaming, drugstores and everything else. With no cards given and no saved wallet, it picks from every card earnrate knows (`scope: all cards`, each row noting the card's fee and conditions). With cards named in the conversation or a saved wallet, it picks from the user's own cards, with caps, picks and activations as they are. For one specific store, call best_card_for_purchase instead. Pass on the notes.
compare_welcome_offersread-onlyRanks the public welcome (sign-up) offers on cards the user doesn't have by first-year value: the bonus at the user's point values minus the first year's annual fee. Use it when the user asks which new card to get or which has the best sign-up bonus. It covers only the cards earnrate knows, offers vary by person, and each offer's note gives the issuer's eligibility rules; pass those caveats on. It doesn't account for what a card would earn on the user's spending.
delete_walletdestructiveDeletes the saved wallet now and for good, and signs out every app using it. Only when the user asks to delete it; confirm first, and offer export_wallet to keep a copy.
export_walletread-onlyThe saved wallet as a wallet.yaml file the earnrate command line and earnrate.dev can import: card ids, picks, activations, memberships, cap usage and bonus progress. Never card numbers.
import_walletdestructiveReplaces the saved wallet with a wallet.yaml from the earnrate command line or earnrate.dev, when the user pastes or attaches one. Wallet files only: never statements or card numbers. Confirm with the user first if they already have cards saved.
list_walletread-onlyLists the user's cards: checkout label, network, rewards currency and its value, spending caps with how much is left this period, chosen bonus categories (and whether they're current), welcome-bonus progress, which stores a store card works at, and when each card's data was last verified.
record_purchaseRecords a purchase the user has already made, so spending caps and welcome-bonus progress stay accurate for future recommendations. It updates the user's saved wallet. Only call it after the purchase has happened, and only once per purchase: calling it twice counts the spend twice. Purchases with no cap and no open welcome bonus are acknowledged and nothing is written.
remove_carddestructiveRemoves a card from the wallet, with its tracked cap usage and bonus progress. Only when the user says they no longer have it or don't want it considered.
resolve_merchantread-onlyShows how earnrate identifies a merchant: its spend category, likely MCC, which card networks it takes, confidence, and whether that came from curated data, Claude, or nowhere. Use it to explain or double-check a recommendation.
search_cardsread-onlyFinds credit cards in earnrate's catalog by name or issuer, e.g. "sapphire", "amex gold" or "bofa". Returns each card's id (for add_card), network, annual fee, whether it's already in the wallet, and what it needs set up: category picks and their options, memberships, activations. Leave the query out to list every card. US cards only.
set_membershipsdestructiveSets the memberships that unlock some cards' rates, such as amazon-prime for Prime Visa's 5% or a Bank of America Preferred Rewards tier. Replaces the whole list, so include the ones to keep.
simulate_spendingread-onlySpends monthly amounts for a year across every card earnrate knows, and ranks single cards and two-card pairs by rewards minus annual fees. Spending is optional: without it, typical U.S. household amounts are used and the answer says so. With `cards` (named in the conversation) or compareToWallet (the saved wallet), it ranks the cards that would add the most to those cards instead. Use it for "which card fits my spending?" or "is it worth adding a card?". It leaves out welcome bonuses.
update_carddestructiveChanges settings on a card already in the wallet: its category picks for this period, checkout label, anniversary or activated categories. Picks for the current period are merged with the ones already saved. Use it when the user picks new categories, such as each quarter on U.S. Bank Cash+.

Schemas: list_tools.

Similar MCP servers

Official server.json
{
  "name": "io.github.cwasley/earnrate",
  "title": "earnrate",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  "remotes": [
    {
      "url": "https://mcp.earnrate.dev",
      "type": "streamable-http"
    }
  ],
  "version": "0.2.28",
  "packages": [
    {
      "version": "0.2.28",
      "transport": {
        "type": "stdio"
      },
      "identifier": "earnrate",
      "runtimeHint": "npx",
      "registryType": "npm",
      "registryBaseUrl": "https://registry.npmjs.org",
      "packageArguments": [
        {
          "type": "positional",
          "value": "mcp"
        }
      ],
      "environmentVariables": [
        {
          "name": "EARNRATE_WALLET",
          "description": "Path to your wallet file. Default: ~/.earnrate/wallet.yaml"
        },
        {
          "name": "EARNRATE_LLM",
          "description": "Set to on to let Claude classify merchants earnrate doesn't know. Off by default."
        },
        {
          "name": "EARNRATE_LLM_MODEL",
          "description": "The Claude model for merchant lookups, when EARNRATE_LLM is on."
        },
        {
          "name": "EARNRATE_LLM_MAX_CALLS",
          "description": "Most Claude merchant lookups per session, when EARNRATE_LLM is on."
        },
        {
          "name": "EARNRATE_MERCHANT_CACHE",
          "description": "Where Claude's merchant answers are saved. Default: ~/.earnrate/merchant-cache.json"
        },
        {
          "name": "EARNRATE_MISSED_MERCHANTS",
          "description": "Where stores earnrate didn't recognize are listed. Default: ~/.earnrate/missed-merchants.json; off keeps no list."
        },
        {
          "name": "ANTHROPIC_API_KEY",
          "isSecret": true,
          "description": "Your own Anthropic key, used only when EARNRATE_LLM is on."
        }
      ]
    }
  ],
  "repository": {
    "url": "https://github.com/cwasley/earnrate",
    "source": "github"
  },
  "websiteUrl": "https://earnrate.dev",
  "description": "Which credit card earns the most at a merchant, from your cards or every card, and why."
}