<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Larry Johnson</title>
    <description>The latest articles on DEV Community by Larry Johnson (@larry_johnson_e014cef9ad9).</description>
    <link>https://dev.to/larry_johnson_e014cef9ad9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3981271%2F585cdbba-de9b-4513-b10f-adb025b574b3.png</url>
      <title>DEV Community: Larry Johnson</title>
      <link>https://dev.to/larry_johnson_e014cef9ad9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/larry_johnson_e014cef9ad9"/>
    <language>en</language>
    <item>
      <title>How to scrape business contact details and verify the emails in one step</title>
      <dc:creator>Larry Johnson</dc:creator>
      <pubDate>Mon, 15 Jun 2026 12:53:48 +0000</pubDate>
      <link>https://dev.to/larry_johnson_e014cef9ad9/how-to-scrape-business-contact-details-and-verify-the-emails-in-one-step-1nnf</link>
      <guid>https://dev.to/larry_johnson_e014cef9ad9/how-to-scrape-business-contact-details-and-verify-the-emails-in-one-step-1nnf</guid>
      <description>&lt;p&gt;If you do cold outreach, you have hit this wall: you scrape a list of company sites, get a pile of emails, load them into your sequencer, and a third of them bounce. Your sender reputation drops, and now even the good ones land in spam. The usual fix is two tools: one to scrape, one to verify. Here is how to do both in a single pass so the list that comes out is already usable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the two-step way leaks
&lt;/h2&gt;

&lt;p&gt;A plain contact scraper returns whatever matches an email pattern on the page. On a real business site that means you also collect placeholders from the template (&lt;code&gt;info@example.com&lt;/code&gt;), demo addresses, a Sentry error-tracking key that looks like an email (&lt;code&gt;key@o0.ingest.sentry.io&lt;/code&gt;), &lt;code&gt;git@github.com&lt;/code&gt; from a code snippet, and third-party addresses smuggled into a &lt;code&gt;mailto:?cc=&lt;/code&gt; link. Then you pay a second service to verify the whole dirty pile, including the garbage. The waste compounds.&lt;/p&gt;

&lt;p&gt;The better approach is to validate as you extract, and only keep what survives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: extract cleanly, not greedily
&lt;/h2&gt;

&lt;p&gt;The trick that removes most of the noise is to stop trusting raw HTML attributes. Real contact emails live in visible text or in JSON-LD structured data. Tracking params, image filenames, and dev keys live in attributes and scripts. So strip executable scripts and blank out attribute values before you run the email regex, and take &lt;code&gt;mailto:&lt;/code&gt; addresses only up to the &lt;code&gt;?&lt;/code&gt; (never the query string):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# blank href/src/content attribute VALUES so ?cc=, ?ref=, data-url junk can't match
&lt;/span&gt;&lt;span class="n"&gt;ATTR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(href|src|content|data-[\w-]+)\s*=\s*(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[^&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;|\'[^\']*\')&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ATTR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\1=&lt;/span&gt;&lt;span class="sh"&gt;''"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html_without_scripts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;emails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then drop known service domains (sentry.io, github.com, googleapis.com) and placeholder locals (&lt;code&gt;name@&lt;/code&gt;, &lt;code&gt;you@&lt;/code&gt;, &lt;code&gt;jane.doe@&lt;/code&gt;). What is left is candidate contacts, not soup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: verify each one without sending
&lt;/h2&gt;

&lt;p&gt;You do not need to send an email or even open an SMTP connection to catch most bad addresses. Resolve the domain's MX records. No MX (and no fallback A record) means mail cannot be delivered. Flag disposable domains by suffix match, and flag role inboxes (&lt;code&gt;info@&lt;/code&gt;, &lt;code&gt;sales@&lt;/code&gt;). Score each address 0 to 100 and assign a verdict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dns.resolver&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deliverable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cache the result per domain. A list of 5,000 emails often has only a few hundred unique domains, so you resolve each once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The state most tools get wrong
&lt;/h2&gt;

&lt;p&gt;Two-state verification (valid or invalid) quietly corrupts your list. A DNS timeout is not proof an address is dead, it is proof you could not check right now. Keep a third state: &lt;code&gt;unknown&lt;/code&gt;. Mark it, do not drop it, and never charge for it. The three buckets are &lt;code&gt;valid&lt;/code&gt; (deliverable, keep), &lt;code&gt;invalid&lt;/code&gt; (no MX, drop), and &lt;code&gt;unknown&lt;/code&gt; (retry later). That third state is the difference between a tool that cleans your list and one that silently deletes real leads.&lt;/p&gt;

&lt;h2&gt;
  
  
  What good output looks like
&lt;/h2&gt;

&lt;p&gt;Run the combined pipeline on a real site and you get the deliverable contact surfaced on top, scored, with the noise already gone. For example, scanning basecamp.com returns &lt;code&gt;jason@basecamp.com&lt;/code&gt; with a &lt;code&gt;valid&lt;/code&gt; verdict and a 100 score, while the demo and service addresses that a naive scraper would have mixed in are dropped before they ever reach you. One pass, one clean row per company.&lt;/p&gt;

&lt;h2&gt;
  
  
  One honest caveat
&lt;/h2&gt;

&lt;p&gt;There is no SMTP handshake here, and that is on purpose. From shared datacenter IPs, SMTP probes get greylisted or blocked and return unreliable answers, and the attempt can get your IP flagged. So this is DNS-tier confidence: it proves the domain accepts mail, not that one specific person's mailbox exists. A real but fictional demo address on a real domain can still pass. That is the honest ceiling of verify-without-sending, and it still removes the large majority of bounces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shortcut
&lt;/h2&gt;

&lt;p&gt;If you would rather not wire up the crawler, the noise filters, the DNS cache, and the three-state logic, I packaged the whole find-and-verify pipeline into one tool. You feed it a list of business sites and it returns deliverable, scored contacts in one run, with validation on by default and failed fetches never billed. It is the &lt;a href="https://apify.com/mrlarryjohnson" rel="noopener noreferrer"&gt;Lead-Gen Pipeline Pro on Apify&lt;/a&gt;. Either way, validate before you send, and make sure your tool keeps that third state.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>automation</category>
      <category>python</category>
    </item>
    <item>
      <title>Giving an AI agent live prediction-market data with an MCP server</title>
      <dc:creator>Larry Johnson</dc:creator>
      <pubDate>Mon, 15 Jun 2026 12:30:55 +0000</pubDate>
      <link>https://dev.to/larry_johnson_e014cef9ad9/giving-an-ai-agent-live-prediction-market-data-with-an-mcp-server-40kd</link>
      <guid>https://dev.to/larry_johnson_e014cef9ad9/giving-an-ai-agent-live-prediction-market-data-with-an-mcp-server-40kd</guid>
      <description>&lt;p&gt;If you are building an AI agent, the hardest part is rarely the model. It is getting the model trustworthy, current data at the moment it needs to act. A language model trained months ago has no idea what Polymarket is pricing a question at right now. The Model Context Protocol (MCP) is how you close that gap: it lets an agent call external tools mid-reasoning. Here is how that works for prediction-market data, and why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an MCP server actually is
&lt;/h2&gt;

&lt;p&gt;An MCP server exposes a set of tools, each with a name, a description, and typed inputs and outputs. An MCP-aware client (Claude, Cursor, an n8n flow, a custom agent) reads that list and can decide, on its own, to call a tool when it needs something it does not know. The agent sends the inputs, your server runs, and the result comes back into the model's context as a fact it can reason over.&lt;/p&gt;

&lt;p&gt;So instead of hardcoding an API call into your app, you publish a tool like &lt;code&gt;get_market_odds(question)&lt;/code&gt; and any agent that connects can use it. The agent figures out when to call it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why prediction markets are a good fit
&lt;/h2&gt;

&lt;p&gt;Prediction markets are a live, numeric, fast-moving signal about real-world questions: elections, rate decisions, sports, company events. That is exactly the kind of thing an agent gets wrong from training data alone. A few patterns this unlocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A research agent answering "how likely is X" can cite the current market-implied probability instead of guessing.&lt;/li&gt;
&lt;li&gt;A trading or monitoring agent can watch for a market repricing and act on the move.&lt;/li&gt;
&lt;li&gt;A writing agent can ground a claim in a live number rather than a stale one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is the agent stays current without you rebuilding the integration every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a good prediction-market MCP tool returns
&lt;/h2&gt;

&lt;p&gt;The tools that are actually useful to an agent are small and composable. A sensible set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;list or search active markets by keyword&lt;/li&gt;
&lt;li&gt;get the current price and implied probability for a market&lt;/li&gt;
&lt;li&gt;get recent large trades or volume for a market&lt;/li&gt;
&lt;li&gt;get a wallet or trader's history for a market&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each returns clean structured JSON the model can read directly. The design rule is to keep each tool doing one thing, so the agent can chain them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honesty constraint that matters for billing
&lt;/h2&gt;

&lt;p&gt;If you charge per tool call (which is the natural model for an agent buyer), there is one rule that builds trust: never charge for a call that failed to return real data. If the upstream is rate-limited or unreachable, the call should fail loudly, not return a plausible-but-empty result that the agent treats as fact and you bill for. An agent acting on fabricated empty data is worse than an agent that knows the call failed. Bill for delivered answers, never for the attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  How agents connect
&lt;/h2&gt;

&lt;p&gt;An MCP server published in a registry (the official MCP registry, Glama, Smithery) is discoverable by agent builders, and connecting is usually a one-line config: point the client at the server URL or run command, and the tools appear. No SDK to learn, no per-app integration. That low friction is the whole reason the protocol is spreading.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shortcut
&lt;/h2&gt;

&lt;p&gt;If you want live prediction-market data in your agent without building and hosting the server yourself, I published one that exposes Polymarket market data as native MCP tools, billed per tool call, with the honest-billing rule above baked in (a call that cannot return real data is not charged). It is in the official MCP registry and on Apify as the &lt;a href="https://apify.com/mrlarryjohnson" rel="noopener noreferrer"&gt;Polymarket MCP Server&lt;/a&gt;. Point your agent at it and it can read the markets the same way it reads anything else you give it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
      <category>api</category>
    </item>
    <item>
      <title>The billing state most APIs get wrong: "unknown" is not "no"</title>
      <dc:creator>Larry Johnson</dc:creator>
      <pubDate>Sun, 14 Jun 2026 13:55:12 +0000</pubDate>
      <link>https://dev.to/larry_johnson_e014cef9ad9/the-billing-state-most-apis-get-wrong-unknown-is-not-no-o9c</link>
      <guid>https://dev.to/larry_johnson_e014cef9ad9/the-billing-state-most-apis-get-wrong-unknown-is-not-no-o9c</guid>
      <description>&lt;p&gt;If you bill per result, there is one design decision that quietly decides whether customers trust you: what you do when you could not get an answer.&lt;/p&gt;

&lt;p&gt;Most usage-billed APIs collapse the world into two states. The call worked, or it failed. Valid or invalid. Found or not found. That binary is where the trust leaks out, because it hides a third state that happens constantly in the real world: you asked, and the system genuinely could not tell you right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example
&lt;/h2&gt;

&lt;p&gt;Say you run an email verification endpoint. A customer sends an address, you check it, you bill for the answer. Easy.&lt;/p&gt;

&lt;p&gt;Then a DNS lookup times out. Or the domain returns SERVFAIL. Or the mail server greylists your probe and hangs. What do you return?&lt;/p&gt;

&lt;p&gt;The lazy answer is &lt;code&gt;invalid&lt;/code&gt;, because it is not &lt;code&gt;valid&lt;/code&gt;, and now the row is "done" and billable. That single shortcut does two bad things at once. It deletes a real lead from your customer's list, because a timeout is not proof the address is dead. And it charges them for the deletion. They paid you to damage their own data.&lt;/p&gt;

&lt;p&gt;The honest version keeps three states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;valid        -&amp;gt; we confirmed it. billable.
invalid      -&amp;gt; we confirmed it is not deliverable (no MX, etc). billable.
unknown      -&amp;gt; we could not determine it right now. NOT billable.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;unknown&lt;/code&gt; is the whole game. It says "you asked, we tried, we could not be sure, so we are not going to charge you or pretend." It is the difference between a tool that cleans a list and a tool that silently corrupts it.&lt;/p&gt;

&lt;h2&gt;
  
  
  This generalizes past email
&lt;/h2&gt;

&lt;p&gt;The same gap shows up anywhere you bill per result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A scraper hits a page that 404s. Is that "no contact found" (a real answer) or "we could not load the page" (an unknown)? Bill the first, never the second.&lt;/li&gt;
&lt;li&gt;A profile lookup gets rate-limited. The lazy path returns an empty profile that looks exactly like a real "this person has no data." Now your customer cannot tell a true empty from a failure, and you billed for both.&lt;/li&gt;
&lt;li&gt;A price checker can not reach a vendor. Returning "no price" reads as "free or unavailable." Returning &lt;code&gt;unknown&lt;/code&gt; reads as "try again." Very different downstream.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I have settled on across a handful of pay-per-event tools: &lt;strong&gt;bill for delivered answers, never for attempts, and make "we could not tell" a first-class, free, clearly-labeled output.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is worth the lost revenue
&lt;/h2&gt;

&lt;p&gt;It costs you money on paper. Every &lt;code&gt;unknown&lt;/code&gt; and every &lt;code&gt;no-result&lt;/code&gt; is a row you did not charge for. Your per-run number looks worse than a competitor who bills for everything including the garbage.&lt;/p&gt;

&lt;p&gt;It is also the only version of the number that survives the customer opening their own logs. The competitor who billed for failures looks cheaper for exactly one cycle, until someone notices their bounce rate went up after a "cleaning" or their lead count dropped for no reason. Trust is the actual product. The billing model is just where you prove you have it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to implement it without overthinking
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Every record carries its own status. Do not infer success from "the batch finished."&lt;/li&gt;
&lt;li&gt;Use at least three buckets: answer, no-result, unknown. Add more if your domain needs them (fetch-error, rate-limited, etc), but never fewer.&lt;/li&gt;
&lt;li&gt;Only "answer" (and a definitive negative answer, if that is what was asked for) is billable. Everything else is free and labeled.&lt;/li&gt;
&lt;li&gt;Say it out loud in your docs. "We do not bill for failed fetches or unknowns" converts better than any feature list, because it tells buyers you understand the failure modes they have already been burned by.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are building anything usage-billed, the binary will tempt you because it is simpler and it makes more money this week. Resist it. The third state is cheap insurance against the one outcome you can not recover from, which is a customer deciding your numbers can not be trusted.&lt;/p&gt;




&lt;p&gt;I build a few data tools on this rule (the public ones are at &lt;a href="https://apify.com/mrlarryjohnson" rel="noopener noreferrer"&gt;apify.com/mrlarryjohnson&lt;/a&gt;). Happy to compare notes if you are designing a pay-per-result model. What is your third state?&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Give your AI agent live prediction-market data in 30 seconds (MCP, no keys, no installs)</title>
      <dc:creator>Larry Johnson</dc:creator>
      <pubDate>Fri, 12 Jun 2026 13:42:40 +0000</pubDate>
      <link>https://dev.to/larry_johnson_e014cef9ad9/give-your-ai-agent-live-prediction-market-data-in-30-seconds-mcp-no-keys-no-installs-4gba</link>
      <guid>https://dev.to/larry_johnson_e014cef9ad9/give-your-ai-agent-live-prediction-market-data-in-30-seconds-mcp-no-keys-no-installs-4gba</guid>
      <description>&lt;p&gt;Most "connect your AI to data" tutorials end with you managing API keys, running a local server, and debugging someone's half-maintained npm package. Here's the 30-second version instead, using the Model Context Protocol and a hosted server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;p&gt;Your agent (Claude, Cursor, n8n, Make — anything that speaks MCP) gains native tools for live Polymarket data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;search_markets&lt;/code&gt; / &lt;code&gt;get_market&lt;/code&gt; — find any prediction market + live prices&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_orderbook&lt;/code&gt; — current bids/asks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;whale_trades&lt;/code&gt; — recent large trades with wallet addresses&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;top_holders&lt;/code&gt; — who's positioned where&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you can ask things like &lt;em&gt;"What's the implied probability SpaceX closes above $2T today, and which whales disagree?"&lt;/em&gt; and the agent pulls real numbers instead of hallucinating stale ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30 seconds
&lt;/h2&gt;

&lt;p&gt;Add this to your MCP config (Claude Desktop, Claude Code, or Cursor):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"polymarket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mrlarryjohnson--polymarket-mcp-server.apify.actor/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;your-apify-token&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token is a free Apify account token. No Polymarket account, no wallet, no keys for the data source — the server is hosted and always warm (Apify Standby mode), and you pay fractions of a cent per tool call instead of a subscription.&lt;/p&gt;

&lt;p&gt;There's a matching &lt;a href="https://apify.com/mrlarryjohnson/web-search-mcp-server" rel="noopener noreferrer"&gt;Web Search MCP server&lt;/a&gt; if you also want multi-engine web/news search tools next to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why hosted MCP beats local for data tools
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Nothing to install or babysit&lt;/strong&gt; — no node/python process dying on your laptop mid-session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only by design&lt;/strong&gt; — the server holds no wallet and can't trade. Your agent gets market &lt;em&gt;data&lt;/em&gt;, not the ability to spend your money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay-per-call&lt;/strong&gt; — idle costs nothing; heavy research days cost cents.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The billing rule I wish more APIs had
&lt;/h2&gt;

&lt;p&gt;One decision I'd recommend to anyone building paid agent tools: &lt;strong&gt;never bill a failed call.&lt;/strong&gt; An upstream timeout or rate limit raises a clear error and is not charged — never disguised as an empty result. Agents retry blindly, so silent-empty responses both corrupt the agent's reasoning AND bill the user for nothing. Fail loud, bill only verdicts.&lt;/p&gt;

&lt;p&gt;(Same rule across the rest of my catalog — whale trackers, an email validator honest about not doing SMTP theater from datacenter IPs, a website change monitor that ships actual diffs: &lt;a href="https://apify.com/mrlarryjohnson" rel="noopener noreferrer"&gt;apify.com/mrlarryjohnson&lt;/a&gt;. Thanks to @apify for the Standby-mode hosting.)&lt;/p&gt;

&lt;p&gt;Questions welcome — happy to share implementation details.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
      <category>claude</category>
    </item>
  </channel>
</rss>
