<?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>Forem: Dany Paredes</title>
    <description>The latest articles on Forem by Dany Paredes (@danywalls).</description>
    <link>https://forem.com/danywalls</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F282011%2F419351ae-0ba9-4eb2-b2c4-91acd241f251.jpg</url>
      <title>Forem: Dany Paredes</title>
      <link>https://forem.com/danywalls</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danywalls"/>
    <language>en</language>
    <item>
      <title>How to Create One-Click MCP Installation Deep Links for Cursor and VS Code</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/how-to-create-one-click-mcp-installation-deep-links-for-cursor-and-vs-code-5g5k</link>
      <guid>https://forem.com/danywalls/how-to-create-one-click-mcp-installation-deep-links-for-cursor-and-vs-code-5g5k</guid>
      <description>&lt;p&gt;A few weeks ago, I built the &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/unsplashx" rel="noopener noreferrer"&gt;unsplashx&lt;/a&gt;&lt;/strong&gt; MCP. But to make it easier for my friends to use it, I decided to simplify the installation process. Today, I want to explain why using deep links is critical for reducing friction in the MCP ecosystem.&lt;/p&gt;

&lt;p&gt;Manual installation is a chore. Instead of asking users to hunt for JSON settings, fix syntax errors, and restart their IDE, we can use one-click deep links to make it invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Deep Links?
&lt;/h2&gt;

&lt;p&gt;Think of this as a "mailto:" link for your coding agent. If a user has Cursor or VS Code open, they click a link, and the editor handles the configuration automatically.&lt;/p&gt;

&lt;p&gt;Both &lt;strong&gt;Cursor&lt;/strong&gt; and &lt;strong&gt;VS Code&lt;/strong&gt; support these links, but they use different encoding formats. Manually creating these links every time you update your package is exactly the kind of task we should automate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal: From JSON to One-Click
&lt;/h2&gt;

&lt;p&gt;Let’s see how to transform a standard MCP configuration into a link that works in a real project. We'll use the config for my &lt;strong&gt;unsplashx&lt;/strong&gt; server:&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;"unsplashx"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unsplashx"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"UNSPLASH_ACCESS_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_KEY_HERE"&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;h3&gt;
  
  
  Step 1: Cursor Protocol (Base64)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt; requires the JSON configuration to be &lt;strong&gt;Base64 encoded&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Format:&lt;code&gt;cursor://anysphere.cursor-deeplink/mcp/install?name=$NAME&amp;amp;config=$BASE64_CONFIG&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Base64 encoding keeps the URL valid while preserving the entire JSON structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: VS Code Protocol (URL Encoded)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;VS Code&lt;/strong&gt; expects a &lt;strong&gt;URL-encoded JSON string&lt;/strong&gt; (usually via the official MCP extension).&lt;/p&gt;

&lt;p&gt;Format:&lt;code&gt;vscode://mcp/install?${JSON_STRING_URL_ENCODED}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since each editor has its own preference, our automation needs to handle both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: A Reusable Script
&lt;/h2&gt;

&lt;p&gt;Here is a simple TypeScript snippet to generate both links for any MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Automate MCP Installation Links
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateMcpLinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Cursor (Base64)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cursorBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configString&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cursorLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`cursor://anysphere.cursor-deeplink/mcp/install?name=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;config=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cursorBase64&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. VS Code (URL Encoded)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vscodeEncoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vscodeLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`vscode://mcp/install?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;vscodeEncoded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cursorLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vscodeLink&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example for unsplashx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stdio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unsplashx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;UNSPLASH_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateMcpLinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unsplashx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cursor:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursorLink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VS Code:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vscodeLink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it: One-Click unsplashx Installation
&lt;/h2&gt;

&lt;p&gt;See it in action. These buttons will install the &lt;strong&gt;unsplashx&lt;/strong&gt; MCP directly into your editor (you'll just need to provide your API key in the prompt):&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Friction kills adoption.&lt;/strong&gt; Most people love new tools but hate configuration. By automating these links, you remove the barrier to entry.&lt;/p&gt;

&lt;p&gt;If you build an MCP server, don't just ship the code— &lt;strong&gt;ship the installation experience.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@shauste?utm_source=unsplashx&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Sven&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplashx&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Being a Writer in the Era of Writer/AI Slop</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Fri, 20 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/being-a-writer-in-the-era-of-writerai-slop-5c6m</link>
      <guid>https://forem.com/danywalls/being-a-writer-in-the-era-of-writerai-slop-5c6m</guid>
      <description>&lt;p&gt;After seven years of writing technical articles, during this time I have loved share my headaches faced while building a solution; doing so made it easy to re-read my thoughts before an interview or when I had to solve the same problem again months later.&lt;/p&gt;

&lt;p&gt;My articles were, and still are, based entirely on real work experience but nowadays, it looks like we need to write just for SEO or positioning or ChatGPT Position 😭, not for returning real user value. The worst part is that we get more AI visitors than humans, and when a real human reads the article, they feel the difference.&lt;/p&gt;

&lt;p&gt;Today, it often feels like we're trapped in a strange loop. Everyone is writing but increasing amount of "empty content" or "AI/Writer Slop." It has become a cycle where robots write for robots, and humans simply press "publish."&lt;/p&gt;

&lt;p&gt;When we rely too much on these tools, we lose our unique voices. We stop being developers sharing real fixes and become just more noise in the crowd. To stand out today, I believe we must be more human than ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Experience is My Best Asset
&lt;/h2&gt;

&lt;p&gt;While AI can explain a library's API better than me, reading the full source-code and writing examples, it cannot tell you how it felt to debug a production issue at 2 AM or the scare of running a schema migration. The AI cannot create a unique analogy from scratch that connects with the reader.&lt;/p&gt;

&lt;p&gt;I felt happy when, a few days ago, a colleague shared how much she loved my comparison of AI without MCP to a smart friend on the phone. It clicked because it was a direct reflection of my own experience—something I lived, rather than something a machine generated from a template.&lt;/p&gt;

&lt;p&gt;Being writer doesn't require inflated phrasing like "delve into this transformative landscape." Instead, you just need to be yourself. My aim is to write for you as if we were having a coffee, telling you directly: "This is what I did, and this is how it worked for me."&lt;/p&gt;

&lt;p&gt;Think of your article as a repository. You wouldn't add unnecessary dependencies, right? To keep my voice authentic, I always check my drafts for these points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Is this my story?&lt;/strong&gt; : Did I actually try this code myself?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is my language simple?&lt;/strong&gt; : Can a junior developer from any country understand me?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Am I using real world examples?&lt;/strong&gt; : This scenario case a real pain of my readers?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are my sentences short and direct?&lt;/strong&gt; : Am I hiding a lack of clarity behind long words?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did I use a real-world analogy?&lt;/strong&gt; : Can I explain this like I would to a friend?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Refactoring to a Human Tone
&lt;/h2&gt;

&lt;p&gt;I know the AI is incredible, but it lacks the human touch. When I read some AI generated articles, I feel that they are empty; they don't have the soul of a real developer. Let's look at how I would refactor a typical AI-generated paragraph into my own voice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How a Robot Writes:&lt;/strong&gt;"Signals in Angular are pivotal to delve into the transformative capabilities of the new Angular signals to unleash the full potential of your application's reactivity."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I Write:&lt;/strong&gt;"Signals in Angular make my apps faster. They simplify how I handle data changes without the complexity of RxJS. Let's see how they work in a real project."&lt;/p&gt;

&lt;p&gt;Did you feel the difference? The first line is boring without sharing experiences but with fancy, nice words, but they are empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  But I love the AI
&lt;/h2&gt;

&lt;p&gt;Yes, I use AI to help me write, but I use it to help me write better, not to write for me. I use it to help me find the right words, not to write the article for me.&lt;/p&gt;

&lt;p&gt;I do a lot of typos and use incorrect verb tenses, so I use AI to correct them, but I always re-read the article to make sure it sounds like me.&lt;/p&gt;

&lt;p&gt;The AI is perfect to help me with the boilerplate code, fix bugs, or suggest better approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Reflections
&lt;/h2&gt;

&lt;p&gt;This is not technical article, its just my feeling writing is about more than just data; it's about sharing a journey. Our real-world "headaches" are our most valuable lessons.&lt;/p&gt;

&lt;p&gt;Please, if you like to write, just write.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write for your future self&lt;/strong&gt; : Detail the difficult parts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn from others&lt;/strong&gt; : As Mandy showed me, connection is essential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay authentic&lt;/strong&gt; : Lived experience is something AI cannot replicate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take a paragraph you wrote recently and read it out loud. Ask yourself: "Does this sound like me?" If it doesn't, start cutting the fluff until your own voice begins to shine through.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1638537690617-ebc561143de0%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w5MDEwMDl8MHwxfGFsbHx8fHx8fHx8fDE3NzQwMTM2MDh8%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1638537690617-ebc561143de0%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w5MDEwMDl8MHwxfGFsbHx8fHx8fHx8fDE3NzQwMTM2MDh8%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Hand writing with a pen in a book" width="1080" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@vdphotography?utm_source=unsplashx&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;VD Photography&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplashx&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>discuss</category>
      <category>writing</category>
    </item>
    <item>
      <title>Moving from Vercel to Google Cloud: Why and How I Migrated My Next.js Blog</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/moving-from-vercel-to-google-cloud-why-and-how-i-migrated-my-nextjs-blog-194e</link>
      <guid>https://forem.com/danywalls/moving-from-vercel-to-google-cloud-why-and-how-i-migrated-my-nextjs-blog-194e</guid>
      <description>&lt;p&gt;Let’s be honest: &lt;strong&gt;I love Vercel.&lt;/strong&gt; It is an amazing platform. The developer experience is probably the best you can find. If you are starting a new project or need to move fast, I still recommend it.&lt;/p&gt;

&lt;p&gt;But as I added more projects like &lt;strong&gt;zum360&lt;/strong&gt; , &lt;strong&gt;LearningCool&lt;/strong&gt; , and my sister’s website, my monthly bill started to grow. Those "Edge Function Execution" charges can be a surprise at the end of the month.&lt;/p&gt;

&lt;p&gt;Since I use &lt;strong&gt;Google Cloud Platform (GCP)&lt;/strong&gt; at my current job, I decided it was time to move my personal blog and projects there. I wanted total control and lower costs.&lt;/p&gt;

&lt;p&gt;Here is the technical journey of how I did it, developer to developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing for Docker
&lt;/h2&gt;

&lt;p&gt;First, you need to package your app. I already wrote a guide on &lt;a href="https://dev.to/deploying-nextjs-outside-vercel"&gt;how to deploy Next.js outside Vercel&lt;/a&gt; which covers the basics, but here is the specific "secret sauce" for GCP.&lt;/p&gt;

&lt;p&gt;Next.js has a built-in &lt;strong&gt;standalone&lt;/strong&gt; mode. This is critical because it creates a very small production build. You don't need to ship your massive &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Update your &lt;code&gt;next.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;standalone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!TIP] You can find more details in the &lt;a href="https://nextjs.org/docs/app/api-reference/next-config-js/output" rel="noopener noreferrer"&gt;official Next.js documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Moving to Google Cloud Run
&lt;/h2&gt;

&lt;p&gt;For the hosting, I chose &lt;strong&gt;Google Cloud Run&lt;/strong&gt;. It is a "serverless container" service. This means your app only runs when someone visits your site. It scales to zero when there is no traffic, so you pay almost nothing for a personal blog.&lt;/p&gt;

&lt;h3&gt;
  
  
  The CLI Setup
&lt;/h3&gt;

&lt;p&gt;First, I installed the &lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;Google Cloud CLI&lt;/a&gt;. After logging in with &lt;code&gt;gcloud auth login&lt;/code&gt;, I was ready to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building and Deploying
&lt;/h3&gt;

&lt;p&gt;Instead of building the Docker image on my laptop (which is slow), I used &lt;strong&gt;Cloud Build&lt;/strong&gt;. This command sends your code to Google’s servers, builds the image, and saves it in a private registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud builds submit &lt;span class="nt"&gt;--tag&lt;/span&gt; gcr.io/YOUR_PROJECT_ID/danywalls-blog &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, to make it live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run deploy danywalls-blog &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt; gcr.io/YOUR_PROJECT_ID/danywalls-blog &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; managed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing Secrets (The Professional Way)
&lt;/h2&gt;

&lt;p&gt;On Vercel, you just paste your API keys into a web panel. In GCP, we use &lt;strong&gt;Secret Manager&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I stored my ConvertKit API key and Google Analytics ID there and granted my Cloud Run service the &lt;code&gt;Secret Manager Secret Accessor&lt;/code&gt; role. Then, I updated the service to mount these secrets as environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run services update danywalls-blog &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;CONVERTKIT_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CONVERTKIT_API_KEY:latest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s more steps than Vercel, but it’s much more secure. No secrets are ever stored in my code or build logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting the Domains (Double Trouble)
&lt;/h2&gt;

&lt;p&gt;My blog uses both &lt;code&gt;danywalls.dev&lt;/code&gt; and &lt;code&gt;danywalls.com&lt;/code&gt;. To link them, I used &lt;strong&gt;Domain Mappings&lt;/strong&gt; in Cloud Run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud beta run domain-mappings create &lt;span class="nt"&gt;--service&lt;/span&gt; danywalls-blog &lt;span class="nt"&gt;--domain&lt;/span&gt; danywalls.com

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google gave me a list of &lt;strong&gt;A&lt;/strong&gt; and &lt;strong&gt;AAAA&lt;/strong&gt; records. I use &lt;strong&gt;Cloudflare&lt;/strong&gt; for DNS, so I added those records there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Tip for Cloudflare users:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set your SSL/TLS mode to &lt;strong&gt;"Full (Strict)"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;At first, leave the Proxy status to "DNS Only" (Grey cloud) until Google finishes issuing the SSL certificate. Once it's "Ready", you can turn the proxy back on.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  "Git Push to Deploy" with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;I didn't want to lose that Vercel feeling where you just push code and it updates the site. I recreated this using &lt;strong&gt;GitHub Actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; file. This workflow authenticates with Google Cloud using a Service Account key (stored in GitHub Secrets) and runs the build and deploy commands automatically every time I push to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my next article, I’ll dive deeper into exactly how I configured this. I’ll share how my AI assistant helped me set up everything to make the workflow feel as natural and smooth as it did on Vercel. Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Leaving Vercel was a big step, but I learned so much:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost Control&lt;/strong&gt; : I now pay a few cents instead of $20/month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Skills&lt;/strong&gt; : I now manage my own containers and security policies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portability&lt;/strong&gt; : My app is now a standard Docker image. I can move it to any other cloud in 5 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re feeling restricted by Vercel’s pricing or just want to level up your engineering skills, &lt;strong&gt;building your own "nest" in the cloud is a great experience.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Are you thinking about migrating? Let me know your thoughts on Twitter/X!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>googlecloud</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dockerizing Next.js: The Hidden Challenges</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/dockerizing-nextjs-the-hidden-challenges-4h0a</link>
      <guid>https://forem.com/danywalls/dockerizing-nextjs-the-hidden-challenges-4h0a</guid>
      <description>&lt;p&gt;I wanted to write this article for a month. There is a big difference between a small project on Vercel and a real production app. In Vercel, everything is easy and feels "pink-colored" because the complexity is hidden.&lt;/p&gt;

&lt;p&gt;When your project grows, you need more control. You move from a simple setup to a large infrastructure with multiple pods and scaling rules. I use &lt;strong&gt;Google Cloud&lt;/strong&gt; for my projects, but these challenges are the same if you use AWS or Azure.&lt;/p&gt;

&lt;p&gt;This move is a big change in architecture. In Vercel, the system is managed for you. Outside of Vercel, you are the architect. In this article, I want to share the first stumble I had when dockerizing Next.js. I never thought it would give me problems, but it did: &lt;strong&gt;Environment Variables&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vercel vs. Self-Managed Architecture
&lt;/h2&gt;

&lt;p&gt;When you use Vercel, they provide a "black box" that handles your build, your deployment, your SSL, and your CDN. When you move to your own infrastructure (like Docker on GCP), you need to replace each piece of that box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="k"&gt;graph&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
    &lt;span class="k"&gt;subgraph&lt;/span&gt; &lt;span class="s2"&gt;"The Vercel Way (Managed)"&lt;/span&gt;
        &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Git&lt;/span&gt; &lt;span class="nv"&gt;Push&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Vercel&lt;/span&gt; &lt;span class="nv"&gt;Build&lt;/span&gt; &lt;span class="nv"&gt;Engine&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Vercel&lt;/span&gt; &lt;span class="nv"&gt;Edge&lt;/span&gt; &lt;span class="nv"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;subgraph&lt;/span&gt; &lt;span class="s2"&gt;"The Self-Managed Way (Docker)"&lt;/span&gt;
        &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Git&lt;/span&gt; &lt;span class="nv"&gt;Push&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;CI&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;CD&lt;/span&gt; &lt;span class="nv"&gt;Pipeline&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;nGitHub&lt;/span&gt; &lt;span class="nv"&gt;Actions&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;GitLab&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="nv"&gt;Build&lt;/span&gt; &lt;span class="nv"&gt;Image&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Container&lt;/span&gt; &lt;span class="nv"&gt;Registry&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;nArtifact&lt;/span&gt; &lt;span class="nv"&gt;Registry&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ECR&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;G&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="nv"&gt;Deploy&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Cloud&lt;/span&gt; &lt;span class="nv"&gt;Hosting&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;nCloud&lt;/span&gt; &lt;span class="nv"&gt;Run&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;App&lt;/span&gt; &lt;span class="nv"&gt;Runner&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Secret&lt;/span&gt; &lt;span class="nv"&gt;Manager&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="err"&gt;-.&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Runtime&lt;/span&gt; &lt;span class="nv"&gt;Secrets&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;H&lt;/span&gt;
        &lt;span class="nv"&gt;H&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;J&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Load&lt;/span&gt; &lt;span class="nv"&gt;Balancer&lt;/span&gt; &lt;span class="err"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;CDN&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;J&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;K&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this architecture work, the most critical part is the &lt;strong&gt;Build Pipeline&lt;/strong&gt;. This is where your code is converted into a Docker image, and it is exactly where most developers face their first big problem: &lt;strong&gt;Environment Variables&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Wall: Environment Variables
&lt;/h2&gt;

&lt;p&gt;Before we build our architecture, we must understand how Next.js treats environment variables. It puts them into two separate groups: &lt;strong&gt;Build-Time Variables&lt;/strong&gt; and &lt;strong&gt;Runtime Variables&lt;/strong&gt;. In Vercel, this connection is seamless. You add your variables to the dashboard and they work in both cases. In your own architecture, you must handle this manually in your &lt;code&gt;Dockerfile&lt;/code&gt; and your CI/CD.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build-Time Variables (&lt;code&gt;NEXT_PUBLIC_&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Variables starting with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; are baked into the final JavaScript bundle when you run &lt;code&gt;npm run build&lt;/code&gt;. They are visible to the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; API URLs or analytics IDs.&lt;/p&gt;

&lt;p&gt;If you forget to add these variables during the build step, the final JavaScript will have &lt;code&gt;undefined&lt;/code&gt; or empty strings. This happens even if you try to add them later when starting the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime Variables (Private)
&lt;/h3&gt;

&lt;p&gt;Variables without the prefix are private and only exist on the server. They are not bundled during build; instead, they are accessed during request time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Database URLs, API tokens, or payment keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;Imagine an app that needs to fetch data from an external API. In local development, everything works with no effort. You create a &lt;code&gt;.env.local&lt;/code&gt; file and Next.js picks up the values automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env.local
&lt;/span&gt;&lt;span class="py"&gt;NEXT_PUBLIC_API_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://dummyjson.com/products&lt;/span&gt;
&lt;span class="py"&gt;API_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;my-super-secret-key-123&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NEXT_PUBLIC_API_URL&lt;/code&gt;&lt;/strong&gt; : This is for the frontend. It tells your app where to fetch information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;API_SECRET_KEY&lt;/code&gt;&lt;/strong&gt; : This is a private key for your backend logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you run &lt;code&gt;npm run dev&lt;/code&gt;, Next.js reads these variables and your app works perfectly. In your code, you access them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Frontend: This will be part of the JS bundle&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_API_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Backend: This will stay only on the server&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;API_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This "magic" happens because Next.js is designed to make local development simple. But as soon as you move to Docker, this magic disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Docker
&lt;/h2&gt;

&lt;p&gt;After testing the app locally, you might think that moving to Docker is simple. If &lt;code&gt;npm run dev&lt;/code&gt; and &lt;code&gt;npm run build&lt;/code&gt; work on your machine, the Docker image should work too.&lt;/p&gt;

&lt;p&gt;I made my first &lt;code&gt;Dockerfile&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build &lt;span class="c"&gt;# ❌ NEXT_PUBLIC_ variables are missing!&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I built the image, started the container, and found my first stumble. The app was running, but the frontend variables were missing. This is because Next.js needs &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; variables &lt;strong&gt;during&lt;/strong&gt; the build process. In my simple &lt;code&gt;Dockerfile&lt;/code&gt;, the &lt;code&gt;npm run build&lt;/code&gt; command had no access to these variables.&lt;/p&gt;

&lt;p&gt;The result? The compiled files were created with &lt;code&gt;undefined&lt;/code&gt; values.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: ARG and ENV
&lt;/h2&gt;

&lt;p&gt;To fix this, we need to pass these variables during the build phase using Docker &lt;code&gt;ARG&lt;/code&gt; and &lt;code&gt;ENV&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ARG&lt;/code&gt; (Build Argument)&lt;/strong&gt;: This is a temporary variable used only while the image is being built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ENV&lt;/code&gt; (Environment Variable)&lt;/strong&gt;: This is a permanent variable inside the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the correct way to write the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="c"&gt;# 1. Capture build-time args&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NEXT_PUBLIC_API_URL&lt;/span&gt;
&lt;span class="c"&gt;# 2. Assign to ENV for the build process&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can build the image with the correct values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;NEXT_PUBLIC_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.com"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; my-app &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What about private variables?
&lt;/h3&gt;

&lt;p&gt;Private variables (without the &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix) are different. You should &lt;strong&gt;never&lt;/strong&gt; put them in the &lt;code&gt;Dockerfile&lt;/code&gt;. These variables are used when the server is running, not when you build the image.&lt;/p&gt;

&lt;p&gt;You can inject them when you start the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;API_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-secret"&lt;/span&gt; my-app

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Concerns
&lt;/h2&gt;

&lt;p&gt;When you move your app to a production environment, security is very important. In Vercel, you add your secrets in the dashboard and they are encrypted automatically.&lt;/p&gt;

&lt;p&gt;In your own infrastructure, you might think about using the &lt;code&gt;-e API_SECRET_KEY=...&lt;/code&gt; flag when you start Docker. This is not a good idea for a real project. It is not secure and it is not scalable. Typing your passwords in plain text in the terminal is a big risk.&lt;/p&gt;

&lt;p&gt;Instead, you should use a &lt;strong&gt;Secret Manager&lt;/strong&gt;. Big cloud providers like Google Cloud, AWS, and Azure provide services to manage your secrets safely.&lt;/p&gt;

&lt;p&gt;When your container starts, the cloud provider injects these secrets directly into the environment. This is a much better way to work because your secrets are never saved in your CI/CD logs, your terminal history, or your Docker image layers. This keeps your application safe as it grows to multiple pods and complex setups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Now that our Docker image is ready, we can move it to any cloud provider like &lt;strong&gt;Google Cloud&lt;/strong&gt; , &lt;strong&gt;AWS&lt;/strong&gt; , or &lt;strong&gt;DigitalOcean&lt;/strong&gt;. Choosing the right architecture is a big step when you move beyond Vercel or your local machine.&lt;/p&gt;

&lt;p&gt;In a real production environment, you might use a more complex setup with many pods and &lt;strong&gt;Kubernetes&lt;/strong&gt;. I wanted to focus on this first "stumble" because it is the most common reason why Next.js deployments fail.&lt;/p&gt;

&lt;p&gt;In a future article, I will explain the next step: how to upload this image to &lt;strong&gt;Google Cloud&lt;/strong&gt; and how to use their &lt;strong&gt;Secret Manager&lt;/strong&gt; to handle your enterprise secrets.&lt;/p&gt;

&lt;p&gt;I hope this guide helps you when you move your application from Vercel to a real Cloud environment.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>infrastructure</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>WebMCP: AI Agents are your new web visitors</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/webmcp-ai-agents-are-your-new-web-visitors-4k2n</link>
      <guid>https://forem.com/danywalls/webmcp-ai-agents-are-your-new-web-visitors-4k2n</guid>
      <description>&lt;p&gt;This weekend, I decided to play with something that seems to have a lot of potential. As you probably know, I've been experimenting a lot with MCP lately (I even created &lt;a href="https://mcpclick.com" rel="noopener noreferrer"&gt;mcpclick.com&lt;/a&gt;), and this discovery feels like a game-changer.&lt;/p&gt;

&lt;p&gt;The web is changing. Until now, we built websites only for humans. But today, a new user is visiting our pages: &lt;strong&gt;AI Agents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Have you ever tried to make an AI enroll in university courses or search for a specific class schedule on a student portal? It is slow and often fails. &lt;strong&gt;Why?&lt;/strong&gt; Because the agent has to "read" the website like a human. It looks at the HTML and guesses which fields to fill. If you change your CSS even a little bit, the agent gets lost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What problem does this solve for us as developers?&lt;/strong&gt; It gives us a simple, fast way to tell the AI: &lt;em&gt;"Don't guess. Use these tools instead."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;WebMCP (Web Model Context Protocol)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is WebMCP?
&lt;/h2&gt;

&lt;p&gt;WebMCP is a new standard &lt;a href="https://github.com/webmachinelearning/webmcp/blob/main/docs/proposal.md" rel="noopener noreferrer"&gt;proposed by Microsoft and Google&lt;/a&gt;. Its goal is to make your website &lt;strong&gt;ready for agents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of this as a Sitemap for actions.&lt;/strong&gt; A &lt;code&gt;sitemap.xml&lt;/code&gt; helps Google find your pages. WebMCP involves telling AI models (like Gemini, GPT, or Claude) what they can &lt;em&gt;do&lt;/em&gt; on your site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two ways to use it:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The HTML Way (Declarative):&lt;/strong&gt; Great for forms. You add two simple attributes, and the browser handles the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The JavaScript Way (Imperative):&lt;/strong&gt; For complex tasks, like calculations or real-time searching.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting Up Your Lab
&lt;/h2&gt;

&lt;p&gt;This is new technology and has not yet arrived in standard browsers. To interact with it, you will need a special version of Chrome that includes the latest experimental features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't worry, you can install it alongside your normal Chrome.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;strong&gt;&lt;a href="https://www.google.com/chrome/canary/" rel="noopener noreferrer"&gt;Chrome Canary&lt;/a&gt;&lt;/strong&gt; or &lt;strong&gt;Chrome Dev&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Note:&lt;/em&gt; You need version &lt;strong&gt;146&lt;/strong&gt; or higher.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Open Chrome Canary and go to &lt;code&gt;chrome://flags&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search for &lt;code&gt;#web-mcp&lt;/code&gt; (Web Model Context Protocol) and fully enable it.&lt;/li&gt;
&lt;li&gt;Restart the browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This is experimental software. Features may change or break without notice. It is designed for developers, so expect some rough edges!&lt;/p&gt;

&lt;h2&gt;
  
  
  The HTML Way
&lt;/h2&gt;

&lt;p&gt;Imagine you have a blog. You want a user to say to their browser: &lt;em&gt;"Search Dany’s blog for React articles."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Computers are bad at guessing. With WebMCP, we tell them exactly what to do using HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; 
  &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/search"&lt;/span&gt; 
  &lt;span class="na"&gt;toolname=&lt;/span&gt;&lt;span class="s"&gt;"search_articles"&lt;/span&gt; 
  &lt;span class="na"&gt;tooldescription=&lt;/span&gt;&lt;span class="s"&gt;"Search for tech articles by topic in the blog."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"What to learn?"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"category"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"react"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;React&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"typescript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;TypeScript&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ai"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;AI&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search Now&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What did we just do?&lt;/strong&gt; We added &lt;code&gt;toolname&lt;/code&gt; and &lt;code&gt;tooldescription&lt;/code&gt;. Now, the browser reads this form and creates a manual for the IA. The agent knows exactly how to use your search bar. It does not need to guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  The JavaScript Way
&lt;/h2&gt;

&lt;p&gt;Sometimes a form is not enough. Maybe you need to verify if a course has open seats without reloading the page. For this, we use JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check if the browser supports this&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modelContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check_course_availability&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Checks if a university course has open seats.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;courseId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The course code (e.g., CS101)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;semester&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Target semester (e.g., Fall 2026)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;courseId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;semester&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fetch real-time data from your API&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/courses/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;courseId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/status?sem=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;semester&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seats&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;remainingSeats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;professor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;professorName&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why is this cool?&lt;/strong&gt; Now, an AI (like a browser Copilot) can call &lt;code&gt;check_course_availability&lt;/code&gt; directly. It doesn't need to navigate complex tables. It just asks your code for the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it Safe?
&lt;/h2&gt;

&lt;p&gt;Yes. Security is built-in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You know who called:&lt;/strong&gt; You can check if a human or an AI sent the form (the proposal adds an &lt;code&gt;agentInvoked&lt;/code&gt; property to &lt;code&gt;SubmitEvent&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private:&lt;/strong&gt; The agent only sees the tools you list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consent:&lt;/strong&gt; The browser usually asks the user before the AI takes action (like buying something).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Go Explore!
&lt;/h2&gt;

&lt;p&gt;This is just the beginning. We are moving to a web where sites are APIs for AI.&lt;/p&gt;

&lt;p&gt;I encourage you to &lt;strong&gt;try it out&lt;/strong&gt;. It is fun to be an early adopter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/ai/join-epp" rel="noopener noreferrer"&gt;Join the Chrome AI Early Preview Program&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/webmachinelearning/webmcp" rel="noopener noreferrer"&gt;Read the official docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't just read about it. &lt;strong&gt;Experiment&lt;/strong&gt;. Try to make your own "Agent-Ready" website.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>mcp</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Moving to Cybersecurity from a Software Developer Role</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Tue, 10 Feb 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/moving-to-cybersecurity-from-a-software-developer-role-meo</link>
      <guid>https://forem.com/danywalls/moving-to-cybersecurity-from-a-software-developer-role-meo</guid>
      <description>&lt;p&gt;Moving to a new field is always a challenge. But when I joined &lt;a href="https://neuraltrust.ai/" rel="noopener noreferrer"&gt;Neuraltrust&lt;/a&gt;, a cybersecurity company, I realized that my "standard" developer security skills were simply not enough. 😅&lt;/p&gt;

&lt;p&gt;I have been a developer for years. I thought I understood security because I knew the standard practices: hash passwords, use HTTPS, sanitize database inputs, and never commit secrets to GitHub.&lt;/p&gt;

&lt;p&gt;But during my first week of meetings, the heavy use of acronyms like &lt;strong&gt;SIEM. IPA. IDP. PII. SPII.&lt;/strong&gt; made me feel completely lost. It felt like I was learning a new language.&lt;/p&gt;

&lt;p&gt;After about a month, I started to understand. I realized these are not just complex business words—they are the foundation of trust. As developers, we focus on building features. Cybersecurity focuses on protecting those features.&lt;/p&gt;

&lt;p&gt;If you are moving to Cybersecurity from a developer role, this post is for you. It is the guide I wish I had on my first day. Here is a simple explanation of these terms from a developer's perspective. Instead of seeing them as hurdles, I began to see them as layers of a defense strategy.&lt;/p&gt;

&lt;p&gt;Let’s start with how we handle the "noise" and security events.&lt;/p&gt;

&lt;h2&gt;
  
  
  SIEM: More Than Just Logs
&lt;/h2&gt;

&lt;p&gt;We love logs, right? Logging is for fixing bugs. If the app crashes, we check the logs to see the error.&lt;/p&gt;

&lt;p&gt;But in Cybersecurity, logs are much more than that, you will hear the term &lt;strong&gt;SIEM&lt;/strong&gt; very often. &lt;strong&gt;But what SIEM is?&lt;/strong&gt; To make short, it is a Security Information and Event Management, but what does it mean?&lt;/p&gt;

&lt;p&gt;Imagine your application is a house. You check the locks (authentication) and maybe install a camera (logging).A &lt;strong&gt;SIEM&lt;/strong&gt; is like a central security station that watches &lt;em&gt;every&lt;/em&gt; camera in the neighborhood at the same time. It collects logs from your app, but also from firewalls, servers, databases, and routers. It uses logic and AI to find connections between events.&lt;/p&gt;

&lt;p&gt;If a user fails to login 5 times in your app, that is a simple log. If that same IP address tried to access the database server 2 seconds earlier, the SIEM connects these two events and alerts the team of an attack.&lt;/p&gt;

&lt;p&gt;Your logs are not just for you; they are data for the SIEM. If you create bad logs (like &lt;code&gt;console.log("error here")&lt;/code&gt;), the SIEM cannot understand them. To fix this, you should structure your logs using JSON format and include key context like the User ID, IP address, Action, and Timestamp. Just be careful not to log sensitive data, which leads us to the critical topic of &lt;strong&gt;Data Protection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The most common tools you will encounter are &lt;strong&gt;Splunk&lt;/strong&gt; , which is popular for analyzing massive amounts of log data, and &lt;strong&gt;Google Chronicle&lt;/strong&gt; , which is built for speed and feels like searching Google for your logs.&lt;/p&gt;

&lt;p&gt;However, knowing &lt;em&gt;what&lt;/em&gt; happened in the logs is only half the battle. You also need to know &lt;em&gt;who&lt;/em&gt; did it, which brings us to identity and compliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  IdP &amp;amp; HIPAA: Identity vs. Compliance
&lt;/h2&gt;

&lt;p&gt;If I need to create a feature for user authentication, like a login page, I will create a &lt;code&gt;users&lt;/code&gt; table and I am done, but in the real world it is much more complex.&lt;/p&gt;

&lt;h3&gt;
  
  
  What IdP ?
&lt;/h3&gt;

&lt;p&gt;In a large system, you do not want every app to manage its own passwords. You want one central place to manage users. An &lt;strong&gt;IdP&lt;/strong&gt; (like Okta, Auth0, or Microsoft Entra ID) is that central place. It confirms who the user is and gives your app a token.&lt;/p&gt;

&lt;h3&gt;
  
  
  Break Glass or The Emergency Key
&lt;/h3&gt;

&lt;p&gt;What happens if the login system (IdP) stops working? Or if the admin loses their phone and cannot use 2FA? This is why we need a &lt;strong&gt;Break Glass&lt;/strong&gt; account. In simple terms, it is a special account with full power that we &lt;em&gt;never&lt;/em&gt; use for normal work. It is kept in a safe place and only used during a real emergency. It is like an emergency key to enter your house. It ensures that if everything else fails, we are not locked out of our own system.&lt;/p&gt;

&lt;h3&gt;
  
  
  What HIPAA ?
&lt;/h3&gt;

&lt;p&gt;I thought this was "HIPPA" or just some medical rule. It stands for &lt;strong&gt;Health Insurance Portability and Accountability Act&lt;/strong&gt;. If you touch &lt;em&gt;any&lt;/em&gt; health-related data, you must follow this. It dictates how data is encrypted, who can access it, and how you audit it.&lt;/p&gt;

&lt;p&gt;Recognizing &lt;em&gt;who&lt;/em&gt; is accessing the system and &lt;em&gt;what&lt;/em&gt; regulations apply is just the first step. The next is understanding the &lt;em&gt;nature&lt;/em&gt; of the data itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  PII vs. SPII: Data Types Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What PII (Personally Identifiable Information) is
&lt;/h3&gt;

&lt;p&gt;Any data that can identify a specific person, such as their name, email, phone number, or IP address.&lt;/p&gt;

&lt;h3&gt;
  
  
  What SPII (Sensitive PII) ?
&lt;/h3&gt;

&lt;p&gt;This is even more critical. If stolen, it can cause severe harm. We are talking about Social Security Numbers, passport details, biometric data like fingerprints, medical records, or financial account numbers.&lt;/p&gt;

&lt;p&gt;I used to treat a "Notes" field as a simple text box. But if a user types their credit card number into that "Notes" field and I save it to the database, I have created a serious security problem. The key is to know what you are storing. SPII must be encrypted (database passwords aren't enough), and PII must be masked in logs so you never expose a user's email or phone number in plain text.&lt;/p&gt;

&lt;p&gt;Once you understand the sensitivity of the data you're handling, you need a philosophy to protect it. This is where the core philosophy of security comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CIA Triad: The Core Principles
&lt;/h2&gt;

&lt;p&gt;Before this job, my security checklist was random. Now I know that everything connects to three main concepts: &lt;strong&gt;C-I-A&lt;/strong&gt;. It stands for &lt;strong&gt;Confidentiality&lt;/strong&gt; (only authorized people see data), &lt;strong&gt;Integrity&lt;/strong&gt; (data hasn't been tampered with), and &lt;strong&gt;Availability&lt;/strong&gt; (the system is actually online).&lt;/p&gt;

&lt;p&gt;When you build a feature, ask yourself: &lt;em&gt;Does this affect Confidentiality, Integrity, or Availability?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The CIA triad gives us our goals, but how do we actually achieve them at scale? We use established frameworks to guide our processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Frameworks: NIST CSF &amp;amp; Security Controls
&lt;/h2&gt;

&lt;p&gt;My mindset as a developer was that security policies were just boring documents. Since I come from frameworks like Angular, I thought, "Hey, this is going to be some cool code!" — well, it turns out it's more about structure, processes, and strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  What NIST CSF (Cybersecurity Framework) is
&lt;/h3&gt;

&lt;p&gt;It is a guide that breaks security down into five simple steps. I realized my job involves the whole cycle: we start by &lt;strong&gt;Identifying&lt;/strong&gt; what we have and &lt;strong&gt;Protecting&lt;/strong&gt; it with standard measures like auth and encryption. But we also need to &lt;strong&gt;Detect&lt;/strong&gt; when something goes wrong, &lt;strong&gt;Respond&lt;/strong&gt; with a plan, and &lt;strong&gt;Recover&lt;/strong&gt; the system after an attack.&lt;/p&gt;

&lt;h3&gt;
  
  
  What CISSP ?
&lt;/h3&gt;

&lt;p&gt;This is a major certification for security professionals. For developers, &lt;strong&gt;Domain 8: Software Development Security&lt;/strong&gt; is the most important. It teaches that security must be part of the design, not added at the end.&lt;/p&gt;

&lt;p&gt;This fundamentally changed how I view typical security requests. When a security engineer asks for a "Software Bill of Materials" (SBOM), they are simply following the &lt;strong&gt;Identify&lt;/strong&gt; step of the framework. When they require &lt;strong&gt;Two-Factor Authentication&lt;/strong&gt; , they are implementing a standard &lt;strong&gt;Security Control&lt;/strong&gt;. Seeing the bigger picture turns these from annoying tasks into necessary architecture.&lt;/p&gt;

&lt;p&gt;But even the best architecture can fail, and when it does, you need a plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Playbook: Response Plans
&lt;/h2&gt;

&lt;p&gt;My mindset as a developer was: If we get hacked, everyone just starts fixing things randomly.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a Playbook ?
&lt;/h3&gt;

&lt;p&gt;A playbook is a recipe for handling a specific type of attack. For example, a &lt;strong&gt;Phishing Playbook&lt;/strong&gt; might instruct the team to isolate the affected computer and reset the user's password immediately. Then, they check the logs (via Splunk or Chronicle) to see what was accessed and notify the legal team.&lt;/p&gt;

&lt;p&gt;As a developer, your job is often to provide the tools or logs that make these playbooks possible. And when the playbook says "Access the backup server," you might be the one reaching for the &lt;strong&gt;Break Glass&lt;/strong&gt; credentials to save the day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Recap
&lt;/h2&gt;

&lt;p&gt;If you are in the middle of an interview process or just stepping into the world of Cybersecurity, here is a quick summary of the key concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SIEM:&lt;/strong&gt; The "security camera system" for your logs (like Splunk or Chronicle).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IdP &amp;amp; Break Glass:&lt;/strong&gt; Your centralized login system and your emergency backup keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII vs. SPII:&lt;/strong&gt; Distinguish between personal data (to protect) and sensitive data (to encrypt).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HIPAA:&lt;/strong&gt; The strict rulebook you must follow if you touch medical data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CIA Triad:&lt;/strong&gt; The core principles of Confidentiality, Integrity, and Availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NIST CSF:&lt;/strong&gt; The primary framework used to Identify, Protect, Detect, Respond, and Recover.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CISSP &amp;amp; Playbooks:&lt;/strong&gt; The gold standard certification and your step-by-step recipes for responding to attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest change for me was realizing that &lt;strong&gt;security is a process, not a feature.&lt;/strong&gt; As developers, we are naturally optimists; we build for the "Happy Path" where everything works perfectly. In contrast, Cybersecurity looks at the "Unhappy Path"—where someone is actively trying to break the system.&lt;/p&gt;

&lt;p&gt;My first two months I'm not fighting or hacking but I'm building systems that are easy to monitor (SIEM), have secure users (IdP), and protect data properly (PII). And honestly? It changes the way I think and solve my coding challenges.&lt;/p&gt;

</description>
      <category>career</category>
      <category>cybersecurity</category>
      <category>learning</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>MCP One-Click Installers: From Pain to One-Click Magic</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Sun, 18 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/mcp-one-click-installers-from-pain-to-one-click-magic-nh8</link>
      <guid>https://forem.com/danywalls/mcp-one-click-installers-from-pain-to-one-click-magic-nh8</guid>
      <description>&lt;p&gt;Let's be honest: setting up MCP (Model Context Protocol) servers is usually a "copy-paste and hope" experience.&lt;/p&gt;

&lt;p&gt;You find a cool tool, like the &lt;strong&gt;Kendo Telerik MCP&lt;/strong&gt; (which I use daily for UI scaffolding), and then the dance begins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open editor settings.&lt;/li&gt;
&lt;li&gt;Find the JSON config.&lt;/li&gt;
&lt;li&gt;Paste the snippet.&lt;/li&gt;
&lt;li&gt;Check the commas.&lt;/li&gt;
&lt;li&gt;Restart.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s manual. It’s friction. And for someone trying your tool for the first time, it’s often where they give up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "A-ha!" Moment
&lt;/h2&gt;

&lt;p&gt;While building my first MCP server, I realized that both &lt;strong&gt;Cursor&lt;/strong&gt; and &lt;strong&gt;VS Code&lt;/strong&gt; support deep links. They have a secret way of saying: &lt;em&gt;"Hey, if you click this link, I'll install the server for you."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But there was a catch. The link formats are... let's just say "developer-unfriendly":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; requires Base64 encoding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code&lt;/strong&gt; requires specific URL encoding patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spent more time encoding JSON strings than actually coding features. That’s when the idea for &lt;strong&gt;&lt;a href="https://mcpclick.app" rel="noopener noreferrer"&gt;mcpclick.app&lt;/a&gt;&lt;/strong&gt; was born.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Installation Invisible
&lt;/h2&gt;

&lt;p&gt;I wanted a way to share the &lt;strong&gt;Kendo Telerik MCP&lt;/strong&gt; with my friends and team without explaining the technical details. I wanted them to just click a button.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;mcpclick.app&lt;/strong&gt; to handle the "boring stuff."&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works (The 10-second version):
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You go to &lt;a href="https://mcpclick.app/generate" rel="noopener noreferrer"&gt;mcpclick.app/generate&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You paste your raw JSON config.&lt;/li&gt;
&lt;li&gt;You get professional buttons for &lt;strong&gt;Cursor&lt;/strong&gt; , &lt;strong&gt;VS Code&lt;/strong&gt; , and &lt;strong&gt;VS Code Insiders&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more &lt;code&gt;btoa()&lt;/code&gt; in the console. No more broken URL parameters. Just professional, standard badges ready for your GitHub README or blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result: One-Click Joy
&lt;/h2&gt;

&lt;p&gt;Now, when I want to share the Kendo Telerik setup, I don't send a block of JSON. I send this:&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;If you are a developer building an MCP or a team lead trying to standardize tools, installation is your first point of &lt;strong&gt;Developer Experience (DX)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By using magic links, you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove all friction&lt;/strong&gt; : One click vs. Five steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid typos&lt;/strong&gt; : The config is baked into the link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look professional&lt;/strong&gt; : Official-looking badges build trust.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://mcpclick.app" rel="noopener noreferrer"&gt;mcpclick.app&lt;/a&gt;&lt;/strong&gt; as a gift to the community. It’s free, it’s fast, and it hosts over 500+ curated servers if you're looking for inspiration.&lt;/p&gt;

&lt;p&gt;Go make your MCPs one-click ready! 🚀&lt;/p&gt;

</description>
      <category>automation</category>
      <category>mcp</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How I Improved My Blog with Vercel Agent Skills and React Best Practices</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Fri, 16 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/how-i-improved-my-blog-with-vercel-agent-skills-and-react-best-practices-3jja</link>
      <guid>https://forem.com/danywalls/how-i-improved-my-blog-with-vercel-agent-skills-and-react-best-practices-3jja</guid>
      <description>&lt;p&gt;Early this morning, I found that Vercel had released a new &lt;strong&gt;Agent Skill&lt;/strong&gt; about React Best Practices.&lt;/p&gt;

&lt;p&gt;I thought: &lt;em&gt;Who better than Vercel? They have taken React to the next level.&lt;/em&gt; I wondered how the experience of their entire team of developers could help me improve my blog.&lt;/p&gt;

&lt;p&gt;So I ran one command, and here is what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Good Code That Could Be Great
&lt;/h2&gt;

&lt;p&gt;My blog was already using Next.js 16 and React 19. The build was passing, linting was clean, and users seemed happy. But as developers, we know that "working" and "optimized" are two different things.&lt;/p&gt;

&lt;p&gt;I had questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was I using React hooks correctly?&lt;/li&gt;
&lt;li&gt;Were my components re-rendering too much?&lt;/li&gt;
&lt;li&gt;Could my images load faster?&lt;/li&gt;
&lt;li&gt;Was my code accessible for keyboard users?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't the kind of problems that break your app. They're the silent performance killers that add up over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Vercel's Experience Matters
&lt;/h2&gt;

&lt;p&gt;If there is one company that truly understands React and Next.js at scale, it is &lt;strong&gt;Vercel&lt;/strong&gt;. The sheer volume of products and solutions they have developed is brutal. They have seen it all: the performance bottlenecks, the complex edge cases, and the headaches that keep us up at night.&lt;/p&gt;

&lt;p&gt;For years, that knowledge was largely locked inside their engineering team. But now, thanks to &lt;strong&gt;Agent Skills&lt;/strong&gt; , we can access that collective wisdom directly in our editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: One Command, Instant Expertise
&lt;/h2&gt;

&lt;p&gt;Vercel recently released &lt;a href="https://github.com/vercel-labs/agent-skills/tree/main/skills/react-best-practices" rel="noopener noreferrer"&gt;react-best-practices&lt;/a&gt;, a collection of React optimization patterns packaged as &lt;strong&gt;Agent Skills&lt;/strong&gt;. These skills work with AI coding assistants like Cursor, Claude Code, and others.&lt;/p&gt;

&lt;p&gt;The best part? Installing them is ridiculously simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx add-skill vercel-labs/agent-skills

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One command, and your AI assistant now has access to 40+ React optimization rules, ordered by impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Agent Uncovered
&lt;/h2&gt;

&lt;p&gt;We all miss things. Sometimes we're in a rush to ship, or we simply overlook details assuming they "don't matter much."&lt;/p&gt;

&lt;p&gt;The agent caught several things I had either ignored or discarded due to time constraints. It wasn't just "fixing bugs"—it was highlighting standard practices I had drifted away from. Now, thanks to the &lt;strong&gt;Vercel Agent Skill&lt;/strong&gt; , I feel much more confident and calm about the code I'm shipping.&lt;/p&gt;

&lt;p&gt;These aren't micro-optimizations. They're the kind of improvements that show up as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster perceived performance&lt;/strong&gt; (users feel the difference)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better accessibility scores&lt;/strong&gt; (more users can use your site)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower maintenance costs&lt;/strong&gt; (consistent patterns across the codebase)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent skills framework prioritizes fixes by impact:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CRITICAL&lt;/strong&gt; : Eliminate waterfalls, reduce bundle size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HIGH&lt;/strong&gt; : Server-side performance, client-side fetching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MEDIUM&lt;/strong&gt; : Re-render optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LOW&lt;/strong&gt; : Advanced patterns&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This ordering is brilliant. It prevents you from optimizing the wrong thing. No point in shaving microseconds off a loop if you have a 600ms request waterfall.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use This in Your Projects
&lt;/h2&gt;

&lt;p&gt;Here's my recommendation:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install the Skills
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx add-skill vercel-labs/agent-skills

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Ask Your AI Assistant
&lt;/h3&gt;

&lt;p&gt;Tell your coding assistant:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Review this project using React best practices from Vercel"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Prioritize by Impact
&lt;/h3&gt;

&lt;p&gt;Don't try to fix everything at once. Start with CRITICAL issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are you blocking async work unnecessarily?&lt;/li&gt;
&lt;li&gt;Is your bundle size growing?&lt;/li&gt;
&lt;li&gt;Are components re-rendering too much?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Verify the Changes
&lt;/h3&gt;

&lt;p&gt;Always run your tests and build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run lint
npm run build

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure nothing broke. Performance improvements shouldn't introduce bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;The most valuable lesson? &lt;strong&gt;Performance work compounds&lt;/strong&gt;. Every small regression you ship today becomes a long-term tax on every user session.&lt;/p&gt;

&lt;p&gt;By applying these best practices early, you're not just making your app faster today. You're preventing future slowdowns.&lt;/p&gt;

&lt;p&gt;Think of it like this: Would you rather fix 40 performance issues in one focused session, or discover them one by one over the next year when users complain?&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;If you maintain a React or Next.js project, I highly recommend trying this. The setup takes 30 seconds, and the insights are invaluable.&lt;/p&gt;

&lt;p&gt;Here's what to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the agent skills: &lt;code&gt;npx add-skill vercel-labs/agent-skills&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ask your AI assistant to review your code&lt;/li&gt;
&lt;li&gt;Start with the CRITICAL fixes&lt;/li&gt;
&lt;li&gt;Verify everything still works&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You'll be surprised at what you find. I know I was.&lt;/p&gt;

&lt;p&gt;Have you tried agent skills in your projects?&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/agent-skills/tree/main/skills/react-best-practices" rel="noopener noreferrer"&gt;Vercel's React Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vercel.com/blog/introducing-react-best-practices" rel="noopener noreferrer"&gt;Introducing React Best Practices (Vercel Blog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/agent-skills" rel="noopener noreferrer"&gt;Agent Skills Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>codequality</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>Protect Your API: Why You Need Rate Limiting (and How to Add It)</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/protect-your-api-why-you-need-rate-limiting-and-how-to-add-it-4220</link>
      <guid>https://forem.com/danywalls/protect-your-api-why-you-need-rate-limiting-and-how-to-add-it-4220</guid>
      <description>&lt;p&gt;Yesterday, I was talking to a friend about the importance of security when building applications. We were discussing a classic feature: &lt;strong&gt;a newsletter subscription form&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You know the drill. You create an endpoint, say &lt;code&gt;/api/subscribe&lt;/code&gt;, and you add the usual validations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the email field empty?&lt;/li&gt;
&lt;li&gt;Does it look like a real email (regex)?&lt;/li&gt;
&lt;li&gt;Is this person already subscribed?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"That's enough, right?" he asked.&lt;/p&gt;

&lt;p&gt;"Well," I said, "it's enough to keep your database clean, but it's not enough to keep your server alive."&lt;/p&gt;

&lt;p&gt;The problem isn't just bad data—it's &lt;strong&gt;volume&lt;/strong&gt;. Without protection, a simple bot script could hit your endpoint 10,000 times in a minute. Even if you reject the bad emails, your database is still working hard to check if they exist. Your server is still parsing JSON. You are paying for those cycles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What problem does this solve for us as developers?&lt;/strong&gt; Rate limiting ensures that no single user (or bot) can degrade the experience for everyone else or bankrupt us with server costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept
&lt;/h2&gt;

&lt;p&gt;Think of Rate Limiting as a &lt;strong&gt;bouncer at a club&lt;/strong&gt;. The bouncer doesn't care if your ID is valid (that's the data validation); he cares about how many people are trying to push through the door at once. He keeps the flow manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Do It Yourself" Approach (And Why It Fails)
&lt;/h2&gt;

&lt;p&gt;Let's say we want to limit users to &lt;strong&gt;5 requests every 60 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Your first instinct might be: &lt;em&gt;"I don't need a fancy library. I'll just use a Javascript Map in memory."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It might look something like this in a Next.js API route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const rateLimitMap = new Map();

export async function POST(request: Request) {
  const ip = request.headers.get("x-forwarded-for") || "127.0.0.1";
  const now = Date.now();
  const windowSize = 60 * 1000; // 60 seconds

  // Get user's recent request timestamps
  const userHistory = rateLimitMap.get(ip) || [];

  // Filter out timestamps older than 60 seconds
  const recentRequests = userHistory.filter(timestamp =&amp;gt; now - timestamp &amp;lt; windowSize);

  if (recentRequests.length &amp;gt;= 5) {
    return Response.json(
      { error: "Too many requests, slow down!" }, 
      { status: 429 }
    );
  }

  // Record this request
  recentRequests.push(now);
  rateLimitMap.set(ip, recentRequests);

  // ... proceed with subscription logic ...
  return Response.json({ message: "Subscribed!" });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem with "In-Memory"
&lt;/h3&gt;

&lt;p&gt;This code works perfectly... &lt;strong&gt;on your local machine&lt;/strong&gt;. But the moment you deploy this to a serverless environment like Vercel or AWS Lambda, it breaks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Statelessness&lt;/strong&gt; : Serverless functions spin up and die. Next time the function runs, your &lt;code&gt;rateLimitMap&lt;/code&gt; is empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Islands&lt;/strong&gt; : If your app scales to handle traffic, you might have 50 different instances running at once. Instance A doesn't know that the user just hit Instance B 500 times.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, how do we share this "memory" across thousands of short-lived server instances?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Redis (Upstash)
&lt;/h2&gt;

&lt;p&gt;To solve this, we need an external memory—a database that is fast enough to check "has this IP made 5 requests?" in milliseconds. &lt;strong&gt;Redis&lt;/strong&gt; is the standard tool for this.&lt;/p&gt;

&lt;p&gt;For serverless apps, I love using &lt;strong&gt;Upstash&lt;/strong&gt;. It's Redis over HTTP, so you don't need to manage persistent connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Let's clean up our naive code. First, install the specialized library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @upstash/ratelimit @upstash/redis

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab your &lt;code&gt;UPSTASH_REDIS_REST_URL&lt;/code&gt; and &lt;code&gt;UPSTASH_REDIS_REST_TOKEN&lt;/code&gt; from the &lt;a href="https://console.upstash.com/" rel="noopener noreferrer"&gt;Upstash Console&lt;/a&gt; and add them to your &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Now, protecting our &lt;code&gt;/api/subscribe&lt;/code&gt; endpoint is incredibly simple. We don't need to write logic for sliding windows or timestamps; the library handles it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/api/subscribe/route.ts
import { NextResponse } from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { headers } from "next/headers";

// Create a unified limiter instance
const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(5, "60 s"), // 5 requests per 60s
  analytics: true,
});

export async function POST(request: Request) {
  // Use IP as key (or user ID if logged in)
  const ip = headers().get("x-forwarded-for") || "127.0.0.1";

  // Ask Redis: "Can this guy pass?"
  const { success } = await ratelimit.limit(ip);

  if (!success) {
    return NextResponse.json(
      { error: "Too many requests. Please try again later." },
      { status: 429 }
    );
  }

  // ✅ Safe to process the subscription
  const body = await request.json();

  // Example subscription logic
  console.log(`Subscribing email: ${body.email}`);

  return NextResponse.json({ message: "Successfully subscribed!" });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far we've set up the limiter and applied it to a route. This moves the state to Upstash, so it doesn't matter if your API is running on one server or a thousand Lambda functions across the globe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Rate Limiting is not just a "nice to have" feature; it's a fundamental security practice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;We learned&lt;/strong&gt; that functional validation (regex) isn't enough to protect resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We saw&lt;/strong&gt; why in-memory solutions fail in modern serverless architectures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We built&lt;/strong&gt; a robust, distributed rate limiter with Redis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenge
&lt;/h3&gt;

&lt;p&gt;Now that you have this working, try to extend it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can you create different limits for free vs. premium users? ($user.plan === 'pro')&lt;/li&gt;
&lt;li&gt;Try blocking specific IPs permanently if they exceed a "ban" threshold.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My Newsletter Went to Spam (Here is How I Fixed It for Free)</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/my-newsletter-went-to-spam-here-is-how-i-fixed-it-for-free-dmg</link>
      <guid>https://forem.com/danywalls/my-newsletter-went-to-spam-here-is-how-i-fixed-it-for-free-dmg</guid>
      <description>&lt;p&gt;It started with a WhatsApp message from a friend: &lt;em&gt;"Hey checking your blog, I subscribed but I never got the confirmation email."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I told him to check his Spam folder. "Yep, it's there," he replied.&lt;/p&gt;

&lt;p&gt;That was a red flag. I rushed to check my &lt;strong&gt;Kit (ConvertKit)&lt;/strong&gt; dashboard and saw the trend: my open rates had dropped significantly since I migrated. I was sending emails from my personal &lt;code&gt;my-personal-email@gmail.com&lt;/code&gt; address, which I thought was fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It turns out, I was breaking the rules of the internet.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: DMARC and "Fake" Senders
&lt;/h2&gt;

&lt;p&gt;Gmail and Yahoo recently updated their spam rules. Basically, they say: &lt;em&gt;"If an email claims to be from @gmail.com but comes from a marketing server (like Kit/Mailchimp), it's fake."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To fix this, you need two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;Custom Domain&lt;/strong&gt; (like &lt;code&gt;danywalls.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; (DKIM/SPF) to prove you own that domain.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The catch?&lt;/strong&gt; I didn't want to pay $6/month for Google Workspace just to have &lt;code&gt;hola@danywalls.com&lt;/code&gt;. I already pay for my domain and hosting; I wanted a free solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Cloudflare Email Routing
&lt;/h2&gt;

&lt;p&gt;Since I host my blog on &lt;strong&gt;Vercel&lt;/strong&gt; but manage my DNS with &lt;strong&gt;Cloudflare&lt;/strong&gt; , I discovered a hidden gem: &lt;strong&gt;Cloudflare Email Routing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This allows you to create professional email addresses (aliases) that forward automatically to your personal Gmail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inbound&lt;/strong&gt; : People email &lt;code&gt;hola@danywalls.com&lt;/code&gt; -&amp;gt; Cloudflare forwards to &lt;code&gt;my-personal-email@gmail.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound&lt;/strong&gt; : Kit sends as &lt;code&gt;hola@danywalls.com&lt;/code&gt; -&amp;gt; We verify the DNS so Gmail trusts it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What problem does this solve for us?&lt;/strong&gt; We get a professional, deliverable email setup for $0/month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Guide
&lt;/h2&gt;

&lt;p&gt;Here is how I set it up in 10 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Enable Email Routing in Cloudflare
&lt;/h3&gt;

&lt;p&gt;Go to your Cloudflare Dashboard -&amp;gt; &lt;strong&gt;Email&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Email Routing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Click "Get Started" and define your custom address.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Address&lt;/strong&gt; : &lt;code&gt;hola&lt;/code&gt; (creates &lt;code&gt;hola@danywalls.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination&lt;/strong&gt; : &lt;code&gt;your-personal@gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloudflare will send you a verification email to your personal Gmail. Click the link, and you are verified.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Cloudflare will ask to add some DNS records (MX and TXT) automatically. Let it do it.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Verify Your Domain in Kit
&lt;/h3&gt;

&lt;p&gt;Now we need to tell Kit that it's allowed to send emails as this new address.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Kit &lt;strong&gt;Settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Email&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add your Verified Sending Domain (&lt;code&gt;danywalls.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Kit will give you 3 &lt;strong&gt;CNAME&lt;/strong&gt; records.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Pro Tip: If you are already using Cloudflare, Kit has a button to configure this automatically. It opens a pop-up, you log in to Cloudflare, and acts as a "One-Click install". It saves you from copy-pasting manually.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The "Gotcha" with Cloudflare Proxy
&lt;/h3&gt;

&lt;p&gt;This is where I got stuck. When adding these CNAME records to Cloudflare:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MAKE SURE TO TURN OFF THE ORANGE CLOUD (Proxy).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set them to "DNS Only" (Grey Cloud). If you leave the proxy on, Kit cannot verify the records because Cloudflare hides the real values.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Update Your Sender Identity
&lt;/h3&gt;

&lt;p&gt;Once the domain is verified (it takes about 5 minutes), go back to Kit and add &lt;code&gt;hola@danywalls.com&lt;/code&gt; as your sender.&lt;/p&gt;

&lt;p&gt;Now, when you send a newsletter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It comes from a professional domain.&lt;/li&gt;
&lt;li&gt;It creates a "pass" on DMARC checks.&lt;/li&gt;
&lt;li&gt;It lands in the Inbox, not Spam.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Deliverability is "invisible" tech debt. You don't see it until your open rates crash.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;We learned&lt;/strong&gt; that sending marketing emails from &lt;code&gt;@gmail.com&lt;/code&gt; is a bad practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We built&lt;/strong&gt; a forwarding system using Cloudflare Email Routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We saved&lt;/strong&gt; money by avoiding a paid rigorous email hosting plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far we've fixed the technical setup. Now, go double-check your own open rates—you might be surprised.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>marketing</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Make GitHub Work for You: GitHub MCP and Dependabot</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/make-github-work-for-you-github-mcp-and-dependabot-e5l</link>
      <guid>https://forem.com/danywalls/make-github-work-for-you-github-mcp-and-dependabot-e5l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnvymtpwzf7zj7mpgeaa.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnvymtpwzf7zj7mpgeaa.webp" alt="upload funny" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lately, I wanted to automate my daily work. I need to see my tasks quickly, report bugs with good details, and stop worrying about security holes in my code.&lt;/p&gt;

&lt;p&gt;The problem? I was always switching between my code editor and the GitHub website. Creating Pull Requests (PRs), adding labels, writing bug reports, checking for updates—it felt like boring work that stopped my flow.&lt;/p&gt;

&lt;p&gt;Then I found &lt;strong&gt;GitHub MCP&lt;/strong&gt; and &lt;strong&gt;Dependabot&lt;/strong&gt;. Now my work is automatic, and I almost never use the GitHub website.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Wanted to Automate
&lt;/h2&gt;

&lt;p&gt;I needed three things: easy interaction with GitHub (PRs, comments, tags, issues), automated security monitoring, and control over when and how updates happen. GitHub MCP handles the first part, Dependabot handles security, and together they've transformed how I work.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub MCP: Easy GitHub Interaction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub MCP&lt;/strong&gt; lets me work with GitHub easily. I can create PRs, write comments, add tags, and open issues—all without leaving my code editor.&lt;/p&gt;

&lt;p&gt;Think of GitHub MCP as a bridge. It connects your AI-powered editor (like &lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;) directly to &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of opening Chrome or Safari, you just ask your AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Create a PR with these labels"&lt;/li&gt;
&lt;li&gt;"Add a comment to issue #42"&lt;/li&gt;
&lt;li&gt;"Open a bug report for this error"&lt;/li&gt;
&lt;li&gt;"What tasks do I have left?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything happens right inside your editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up GitHub MCP in Cursor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Get Your Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/settings/personal-access-tokens/new" rel="noopener noreferrer"&gt;GitHub's token settings&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate new token (classic)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select these permissions (scopes): &lt;code&gt;repo&lt;/code&gt;, &lt;code&gt;workflow&lt;/code&gt;, &lt;code&gt;read:org&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy the token.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Configure Cursor
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mcpServers": {
    "github": {
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": {
        "Authorization": "Bearer YOUR_GITHUB_PAT"
      }
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_GITHUB_PAT&lt;/code&gt; with your token, save, and restart Cursor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Verify
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Tools &amp;amp; Integrations&lt;/strong&gt; → &lt;strong&gt;MCP Tools&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Look for a green dot using "github".&lt;/li&gt;
&lt;li&gt;Test it by asking: "List my GitHub repositories".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; Now your editor can talk to GitHub. You can create PRs and issues just by chatting.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Use This Every Day
&lt;/h2&gt;

&lt;p&gt;Once it is set up, I use it all the time. I stay in my editor while the AI does the boring GitHub work.&lt;/p&gt;

&lt;h2&gt;
  
  
  But How Do I Automate Security?
&lt;/h2&gt;

&lt;p&gt;GitHub MCP helps with my tasks, but what about security? That is where &lt;strong&gt;Dependabot&lt;/strong&gt; helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependabot&lt;/strong&gt; is a tool that watches your project libraries (dependencies) for security problems. It does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finds security issues.&lt;/li&gt;
&lt;li&gt;Creates PRs to fix them.&lt;/li&gt;
&lt;li&gt;Keeps your libraries up to date.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the trick: by default, Dependabot sends PRs to the &lt;code&gt;main&lt;/code&gt; branch. If you use a &lt;code&gt;develop&lt;/code&gt; branch for your work, this breaks your flow.&lt;/p&gt;

&lt;p&gt;But we can fix this. We can tell Dependabot to use &lt;code&gt;develop&lt;/code&gt;. We can also tell it to run only once a week, so it doesn't bother us every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Dependabot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable Dependabot
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your repository on GitHub.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Code security and analysis&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Dependabot alerts&lt;/strong&gt; , &lt;strong&gt;security updates&lt;/strong&gt; , and &lt;strong&gt;version updates&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configure Dependabot
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.github/dependabot.yml&lt;/code&gt; with this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    open-pull-requests-limit: 5
    target-branch: "develop"
    commit-message:
      prefix: "chore"
    labels:
      - "dependencies"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why these settings?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;target-branch: "develop"&lt;/code&gt;: Sends updates to your development branch, not production.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open-pull-requests-limit: 5&lt;/code&gt;: Prevents too many PRs at once.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;schedule: weekly&lt;/code&gt;: Checks only on Mondays, so you don't get notifications every day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Commit and push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add .github/dependabot.yml
git commit -m "chore: configure dependabot"
git push

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Dependabot PRs
&lt;/h3&gt;

&lt;p&gt;When Dependabot creates a PR:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the description.&lt;/li&gt;
&lt;li&gt;Check the changes (diff).&lt;/li&gt;
&lt;li&gt;Make sure tests pass.&lt;/li&gt;
&lt;li&gt;Merge or close it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I usually merge security fixes fast, but I wait until the end of the week for normal updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;p&gt;Automation is key. Simplifying tasks improves your speed and reduces mistakes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub MCP&lt;/strong&gt; handles your daily work—creating PRs, comments, and issues from your editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependabot&lt;/strong&gt; handles security—watching your libraries and fixing problems automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they give you what every developer needs: &lt;strong&gt;less boring work&lt;/strong&gt; and &lt;strong&gt;better security&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;Ready to try it?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect MCP&lt;/strong&gt; : Set up the GitHub MCP server in your editor today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Dependabot&lt;/strong&gt; : Add the &lt;code&gt;.github/dependabot.yml&lt;/code&gt; file to one of your projects using the &lt;code&gt;develop&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try a command&lt;/strong&gt; : Ask your AI to "List my open issues" and see what happens.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let the tools do the work for you!&lt;/p&gt;

</description>
      <category>github</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Why I Switched from Loom to Cap</title>
      <dc:creator>Dany Paredes</dc:creator>
      <pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/danywalls/why-i-switched-from-loom-to-cap-j36</link>
      <guid>https://forem.com/danywalls/why-i-switched-from-loom-to-cap-j36</guid>
      <description>&lt;p&gt;For years, Loom was my go-to tool for screen recordings. Whether I was explaining a bug to my team, creating quick tutorials, or sharing feedback on a pull request, Loom made it easy. But recently, I found myself looking for something better.&lt;/p&gt;

&lt;p&gt;What problem does this solve? We need tools that are fast, reliable, and don't break the bank. We also value tools that respect our privacy and give us control over our data.&lt;/p&gt;

&lt;p&gt;After trying &lt;a href="https://go.cap.so/dany-paredes" rel="noopener noreferrer"&gt;Cap.so&lt;/a&gt;, I made the switch. Let me tell you why.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cap?
&lt;/h2&gt;

&lt;p&gt;Think of Cap as the open-source alternative to Loom. It's lightweight, powerful, and works across platforms. But what makes it special isn't just that it's open source—it's how it fits into a developer's workflow.&lt;/p&gt;

&lt;p&gt;Cap gives you two modes that fit different workflows. When I need something quick, I use &lt;strong&gt;Instant Mode&lt;/strong&gt; —just record, stop, and share. Your video is live in seconds with auto-generated captions and summaries. But when I need that extra polish for a client presentation or a tutorial, &lt;strong&gt;Studio Mode&lt;/strong&gt; gives me full editing capabilities to make everything perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Made the Switch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Screenshots and Screen Recording in One Tool
&lt;/h3&gt;

&lt;p&gt;One thing that immediately caught my attention was Cap's ability to handle both screenshots and screen recordings. We often need to quickly capture what's on our screen—sometimes a static image is enough, sometimes we need a video.&lt;/p&gt;

&lt;p&gt;Before Cap, I was juggling multiple tools. I used &lt;a href="https://gifox.app/" rel="noopener noreferrer"&gt;&lt;strong&gt;Gifox&lt;/strong&gt;&lt;/a&gt; for quick GIF captures and &lt;a href="https://shottr.cc/" rel="noopener noreferrer"&gt;&lt;strong&gt;Shottr&lt;/strong&gt;&lt;/a&gt; for screenshots with annotations. Both are great tools, but switching between them broke my flow. With Cap, I don't need to switch between different tools anymore—everything is in one place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshot Mode&lt;/strong&gt; isn't just a basic grabber. It includes a dedicated editor that I use all the time. I can add shapes and text annotations to highlight what matters, apply sensitive masking when I need to hide API keys or passwords, adjust backgrounds and padding to make screenshots look professional, and then export or share everything with a single click.&lt;/p&gt;

&lt;p&gt;I can take a screenshot when I need to show a quick UI state, or record a video when I need to explain a complex flow. This might seem small, but it saves me time throughout the day.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Easy Sharing That Just Works
&lt;/h3&gt;

&lt;p&gt;Sharing videos should be simple. With Cap, you get shareable links that work instantly. What I love is that the tool automatically generates captions, titles, summaries, and even chapters for longer videos. This means when I share a recording with my team, they can quickly understand what it's about without watching the entire video. For async collaboration, this is a game-changer.&lt;/p&gt;

&lt;p&gt;Another feature I absolutely love is the &lt;strong&gt;auto-zoom cursor&lt;/strong&gt;. When you're recording tutorials or demos, Cap automatically zooms in on your cursor when you click, making it crystal clear what you're doing. This is perfect for creating professional-looking tutorials without any post-production work. Here's a quick demo I recorded:&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Price That Makes Sense
&lt;/h3&gt;

&lt;p&gt;Here's the deal: Loom Business costs $12.50/user/month. Cap's Desktop License? $29/year or $58 lifetime. You do the math.&lt;/p&gt;

&lt;p&gt;Cap has three tiers: &lt;strong&gt;Free&lt;/strong&gt; (unlimited local recording, 5-min shareable links), &lt;strong&gt;Desktop License&lt;/strong&gt; ($29/year for commercial use), and &lt;strong&gt;Cap Pro&lt;/strong&gt; ($8.16/month for unlimited sharing and AI features). For solo developers, the lifetime license is a no-brainer. For teams, Cap Pro is still way cheaper than Loom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;I use Cap daily for bug reports (show, don't tell), code reviews (walk through changes with auto-generated summaries), quick tutorials for new team members, and client demos. The auto-zoom cursor and captions make everything look professional without any editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About the Free Version?
&lt;/h2&gt;

&lt;p&gt;The free version of Cap is quite generous. You get unlimited local recordings, shareable links up to 5 minutes, export to MP4 or GIF, and even Studio Mode with the full editor. For many developers, this is enough. I used the free version for weeks before upgrading. You only need to upgrade if you need unlimited cloud storage, longer shareable links, AI features like auto titles and transcriptions, custom domain support, or team spaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Recommendation
&lt;/h2&gt;

&lt;p&gt;If you're looking for a screen recording tool, give Cap a try. The free version is generous enough to test it out, and the Desktop License at $29/year is incredible value. For teams needing AI features and unlimited sharing, Cap Pro at $8.16/month per user is significantly cheaper than Loom.&lt;/p&gt;

&lt;p&gt;The only reason to stick with Loom? If your entire team is already locked in and migration feels too painful. Otherwise, &lt;a href="https://go.cap.so/dany-paredes" rel="noopener noreferrer"&gt;download Cap using my referral link&lt;/a&gt; and see the difference for yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Ready to make the switch? Here's what to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Download Cap&lt;/strong&gt; : Visit &lt;a href="https://go.cap.so/dany-paredes" rel="noopener noreferrer"&gt;cap.so&lt;/a&gt; and download for your platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try the free version&lt;/strong&gt; : Record a few videos and see how it feels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare with your current tool&lt;/strong&gt; : Use both for a week and see which fits your workflow better&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade if needed&lt;/strong&gt; : If you like it, the Desktop License is a no-brainer at $29/year&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;After years of using Loom, switching to Cap was one of the best decisions I made. Screenshots and screen recording in one tool, incredible pricing, and the freedom to own my data—it just works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://go.cap.so/dany-paredes" rel="noopener noreferrer"&gt;Give Cap a try&lt;/a&gt; and let me know what you think. I'm curious to hear if it fits your workflow as well as it fits mine.&lt;/p&gt;

</description>
      <category>developertools</category>
    </item>
  </channel>
</rss>
