<?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: Phạm Tiến Thuận Phát</title>
    <description>The latest articles on Forem by Phạm Tiến Thuận Phát (@fuderrpham03).</description>
    <link>https://forem.com/fuderrpham03</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%2F1783474%2Fbf4f1bc2-5f9b-4e36-8d31-24410522f409.jpg</url>
      <title>Forem: Phạm Tiến Thuận Phát</title>
      <link>https://forem.com/fuderrpham03</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fuderrpham03"/>
    <language>en</language>
    <item>
      <title>Test any website with one URL—meet Scout (an AI quality companion) 🧠</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Tue, 07 Oct 2025 16:05:38 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/test-any-website-with-one-url-meet-scout-an-ai-quality-companion-44d5</link>
      <guid>https://forem.com/fuderrpham03/test-any-website-with-one-url-meet-scout-an-ai-quality-companion-44d5</guid>
      <description>&lt;h2&gt;
  
  
  Test any website with one URL—meet Scout
&lt;/h2&gt;

&lt;p&gt;🧠&lt;/p&gt;

&lt;p&gt;I'm super super excited to introduce with you guys to my AI Testing Companion &lt;/p&gt;

&lt;p&gt;In the era of AI Native App &lt;/p&gt;

&lt;p&gt;Building web apps is faster than ever with tools like Cursor and code assistants. But shipping quickly often turns testing into a pain point: you add a feature, and suddenly an older flow breaks or behaves weirdly.&lt;/p&gt;

&lt;p&gt;My teammates and I are working on Scout, an agent that helps you test entire websites with a single URL—then keeps testing the parts you care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Scout does
&lt;/h2&gt;

&lt;p&gt;🟢 First scan (site-wide): Get an overview of the whole website and a report of issues or bugs found on the first pass.&lt;/p&gt;

&lt;p&gt;🟢 Targeted follow-ups: Ask Scout to test specific features, user flows, or edge cases you care about.&lt;/p&gt;

&lt;p&gt;🟢 Actionable reports: Receive concise findings with concrete next steps. You can also trigger a fresh report any time.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Specially for Lovable and Replit apps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your product is built with Lovable or Replit, you don’t need any setup. Just insert scoutqa between lovable and .app in your domain and Scout will run automatically.&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
Original:&lt;br&gt;
&lt;a href="https://giggles-and-gizmos-12773.lovable.app/" rel="noopener noreferrer"&gt;https://giggles-and-gizmos-12773.lovable.app/&lt;/a&gt;&lt;br&gt;
With Scout:&lt;br&gt;
&lt;a href="https://giggles-and-gizmos-12773.lovable.scoutqa.app/" rel="noopener noreferrer"&gt;https://giggles-and-gizmos-12773.lovable.scoutqa.app/&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Speed: One URL to kick off testing—no heavy configs.&lt;/p&gt;

&lt;p&gt;Coverage: Catch regressions across pages while still drilling into critical paths.&lt;/p&gt;

&lt;p&gt;Clarity: Reports focus on root causes and suggested fixes, not just raw logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we’re exploring next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smarter test-case generation from user stories&lt;/p&gt;

&lt;p&gt;Visual diff + accessibility checks in the loop&lt;/p&gt;

&lt;p&gt;CI-friendly runs with per-PR summaries&lt;/p&gt;

&lt;p&gt;The most significant thing is that we will evolve along the way to make Scout more and more robust, a really helpful Testing Companion.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Try it here on Product Hunt&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://www.producthunt.com/products/scout-qa-ai-quality-companion?launch=scout-qa-ai-quality-companion" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/scout-qa-ai-quality-companion?launch=scout-qa-ai-quality-companion&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  We’d love your feedback
&lt;/h2&gt;

&lt;p&gt;What worked well? What didn’t?&lt;/p&gt;

&lt;p&gt;Which features should we add next?&lt;/p&gt;

&lt;p&gt;Any wild ideas you think Scout should try?&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your thoughts here
&lt;/h2&gt;

&lt;p&gt;(quick form):&lt;br&gt;
&lt;a href="https://docs.google.com/forms/d/e/1FAIpQLSdhkg-aC8T5d2zFVgPPVuZ1JHRaCkxvTMA69h0bWoXfdDh_Qw/viewform?usp=preview" rel="noopener noreferrer"&gt;https://docs.google.com/forms/d/e/1FAIpQLSdhkg-aC8T5d2zFVgPPVuZ1JHRaCkxvTMA69h0bWoXfdDh_Qw/viewform?usp=preview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you find Scout useful, an upvote means a lot:&lt;br&gt;
&lt;a href="https://www.producthunt.com/products/scout-qa-ai-quality-companion?utm_source=other&amp;amp;utm_medium=social&amp;amp;launch=scout-qa-ai-quality-companion" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/scout-qa-ai-quality-companion?utm_source=other&amp;amp;utm_medium=social&amp;amp;launch=scout-qa-ai-quality-companion&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>tooling</category>
      <category>coding</category>
    </item>
    <item>
      <title>🚀 Do you want to have your own MCP Server can be used anywhere from VSCode Copilot, n8n ?</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Sun, 29 Jun 2025 17:44:53 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/do-you-want-to-have-your-own-mcp-server-can-be-used-anywhere-from-vscode-copilot-n8n--59n4</link>
      <guid>https://forem.com/fuderrpham03/do-you-want-to-have-your-own-mcp-server-can-be-used-anywhere-from-vscode-copilot-n8n--59n4</guid>
      <description>&lt;h1&gt;
  
  
  🚀 Complete Guide: Deploying MCP Server on AWS EC2 with SSE Transport and HTTPS
&lt;/h1&gt;

&lt;p&gt;Model Context Protocol (MCP) is revolutionizing how AI applications interact with external data sources and tools. In this comprehensive guide, we'll walk through deploying a production-ready MCP server on AWS EC2 with Server-Sent Events (SSE) transport, complete with HTTPS SSL certificates and proper Nginx configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  📋 What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Understanding MCP and SSE transport protocol&lt;/li&gt;
&lt;li&gt;Building and pushing Docker images to AWS ECR&lt;/li&gt;
&lt;li&gt;Deploying MCP server on EC2 with proper security&lt;/li&gt;
&lt;li&gt;Configuring Nginx for SSE streaming with SSL&lt;/li&gt;
&lt;li&gt;Troubleshooting common MCP deployment issues&lt;/li&gt;
&lt;li&gt;Testing with n8n and other MCP clients&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔍 Understanding MCP and SSE Transport
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Model Context Protocol (MCP)?
&lt;/h3&gt;

&lt;p&gt;Model Context Protocol is an open standard that enables AI applications to securely connect to external data sources, databases, and tools. It provides a standardized way for Large Language Models (LLMs) to access real-time information and execute actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standardized Interface&lt;/strong&gt;: Consistent API across different tools and services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Controlled access to external resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Data&lt;/strong&gt;: Live connections to databases, APIs, and file systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt;: Easy to add new tools and capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why SSE (Server-Sent Events) Transport?
&lt;/h3&gt;

&lt;p&gt;Server-Sent Events provide &lt;strong&gt;unidirectional, real-time communication&lt;/strong&gt; from server to client over HTTP. Unlike WebSockets, SSE is simpler and perfect for MCP's use case:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSE Advantages for MCP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP-based&lt;/strong&gt;: Works with existing infrastructure (load balancers, proxies)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-reconnection&lt;/strong&gt;: Built-in connection recovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler than WebSockets&lt;/strong&gt;: Less overhead for one-way communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall-friendly&lt;/strong&gt;: Uses standard HTTP/HTTPS ports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MCP Protocol Flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Client → GET /sse → Server (Establish SSE stream for responses)
2. Client → POST /messages → Server (Send MCP commands)  
3. Server → SSE Stream → Client (Send responses back)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏗️ Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐    HTTPS    ┌─────────────┐    HTTP    ┌─────────────┐
│   MCP       │ ──────────► │   Nginx     │ ─────────► │   Docker    │
│   Client    │             │   Proxy     │            │   Container │
│  (n8n/VSC)  │             │   + SSL     │            │ (MCP Server)│
└─────────────┘             └─────────────┘            └─────────────┘
                                   │
                            ┌─────────────┐
                            │ Let's       │
                            │ Encrypt     │
                            │ SSL Cert    │
                            └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AWS Account with CLI configured&lt;/li&gt;
&lt;li&gt;Docker installed locally&lt;/li&gt;
&lt;li&gt;Domain name with DNS management access&lt;/li&gt;
&lt;li&gt;Basic knowledge of Linux/Ubuntu commands&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Step 1: Build and Push Docker Image to ECR
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Create ECR Repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create ECR repository&lt;/span&gt;
aws ecr create-repository &lt;span class="nt"&gt;--repository-name&lt;/span&gt; mcp-server &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

&lt;span class="c"&gt;# Get login token&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &amp;lt;ACCOUNT_ID&amp;gt;.dkr.ecr.us-east-1.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Build and Tag Docker Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build your MCP server image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; mcp-server:latest &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Tag for ECR&lt;/span&gt;
docker tag mcp-server:latest &amp;lt;ACCOUNT_ID&amp;gt;.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest

&lt;span class="c"&gt;# Push to ECR&lt;/span&gt;
docker push &amp;lt;ACCOUNT_ID&amp;gt;.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🖥️ Step 2: Launch and Configure EC2 Instance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Launch EC2 Instance
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Via AWS Console:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AMI&lt;/strong&gt;: Ubuntu Server 22.04 LTS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance Type&lt;/strong&gt;: t3.small (or t2.micro for testing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Pair&lt;/strong&gt;: Create or select existing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Group&lt;/strong&gt;: Create with these ports:

&lt;ul&gt;
&lt;li&gt;SSH (22): Your IP&lt;/li&gt;
&lt;li&gt;HTTP (80): 0.0.0.0/0&lt;/li&gt;
&lt;li&gt;HTTPS (443): 0.0.0.0/0&lt;/li&gt;
&lt;li&gt;Custom (8000): 0.0.0.0/0 (for testing)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.2 Create IAM Role for ECR Access
&lt;/h3&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec2.amazonaws.com"&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;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Attach Policy:&lt;/strong&gt;&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"ecr:GetAuthorizationToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchCheckLayerAvailability"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:GetDownloadUrlForLayer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchGetImage"&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;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🐳 Step 3: Setup Docker and Deploy Container
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 SSH into EC2 and Install Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH into your instance&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; your-key.pem ubuntu@&amp;lt;EC2_PUBLIC_IP&amp;gt;

&lt;span class="c"&gt;# Update system&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker.io
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; docker ubuntu

&lt;span class="c"&gt;# Apply docker group (avoid logout)&lt;/span&gt;
newgrp docker

&lt;span class="c"&gt;# Install AWS CLI&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; awscli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Pull and Run MCP Server Container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login to ECR&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &amp;lt;ACCOUNT_ID&amp;gt;.dkr.ecr.us-east-1.amazonaws.com

&lt;span class="c"&gt;# Pull image&lt;/span&gt;
docker pull &amp;lt;ACCOUNT_ID&amp;gt;.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest

&lt;span class="c"&gt;# Create logs directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/mcp-logs

&lt;span class="c"&gt;# Run container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mcp-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/mcp-logs:/app/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;ACCOUNT_ID&amp;gt;.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest

&lt;span class="c"&gt;# Verify container is running&lt;/span&gt;
docker ps
docker logs mcp-server

&lt;span class="c"&gt;# Test endpoint&lt;/span&gt;
curl http://localhost:8000/sse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🌐 Step 4: Configure DNS
&lt;/h2&gt;

&lt;p&gt;Add an A record in your DNS provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type: A
Name: mcp-server
Value: &amp;lt;EC2_PUBLIC_IP&amp;gt;
TTL: 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify DNS propagation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup mcp-server.yourdomain.com
dig mcp-server.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔧 Step 5: Install and Configure Nginx
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Install Nginx
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 Configure Nginx for MCP SSE Transport
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Critical Configuration for MCP Protocol:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/nginx/sites-available/mcp-server.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
server {
    listen 80;
    server_name mcp-server.yourdomain.com;
    return 301 https://&lt;/span&gt;&lt;span class="nv"&gt;$server_name$request_uri&lt;/span&gt;&lt;span class="sh"&gt;;
}

server {
    listen 443 ssl;
    server_name mcp-server.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/mcp-server.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mcp-server.yourdomain.com/privkey.pem;

    # SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    # CRITICAL: SSE endpoint - ONLY GET requests
    location = /sse {
        if (&lt;/span&gt;&lt;span class="nv"&gt;$request_method&lt;/span&gt;&lt;span class="sh"&gt; != GET) {
            return 405;
        }

        proxy_pass http://127.0.0.1:8000/sse;

        # Essential for SSE streaming
        proxy_http_version 1.1;
        proxy_set_header Upgrade &lt;/span&gt;&lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header Connection "upgrade";

        # Standard proxy headers
        proxy_set_header Host &lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header X-Real-IP &lt;/span&gt;&lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header X-Forwarded-For &lt;/span&gt;&lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header X-Forwarded-Proto &lt;/span&gt;&lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="sh"&gt;;

        # SSE specific - NO BUFFERING
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        # CORS headers
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, OPTIONS';
        add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
    }

    # CRITICAL: Messages endpoint for POST requests
    location /messages {
        proxy_pass http://127.0.0.1:8000;

        proxy_http_version 1.1;
        proxy_set_header Host &lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header X-Real-IP &lt;/span&gt;&lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header X-Forwarded-For &lt;/span&gt;&lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header X-Forwarded-Proto &lt;/span&gt;&lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="sh"&gt;;
        proxy_set_header Content-Type &lt;/span&gt;&lt;span class="nv"&gt;$content_type&lt;/span&gt;&lt;span class="sh"&gt;;

        # CORS headers
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
    }

    # Handle CORS preflight requests
    location / {
        if (&lt;/span&gt;&lt;span class="nv"&gt;$request_method&lt;/span&gt;&lt;span class="sh"&gt; = 'OPTIONS') {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
            add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 200;
        }

        return 301 /sse;
    }
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Enable the configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/mcp-server.conf /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nginx/sites-enabled/default

&lt;span class="c"&gt;# Test configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;

&lt;span class="c"&gt;# Reload Nginx&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔒 Step 6: Setup SSL Certificate with Let's Encrypt
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Install Certbot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; certbot python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 Obtain SSL Certificate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; mcp-server.yourdomain.com &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com &lt;span class="nt"&gt;--agree-tos&lt;/span&gt; &lt;span class="nt"&gt;--non-interactive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.3 Setup Auto-renewal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"0 12 * * * /usr/bin/certbot renew --quiet"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/crontab &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.4 Verify SSL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot certificates
curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://mcp-server.yourdomain.com/sse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚨 Critical: Understanding the Nginx Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why This Configuration is Essential for MCP
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;❌ Common Mistake - Single Endpoint:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This WILL NOT WORK for MCP&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/sse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8000/sse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;# Tries to handle both GET and POST on same endpoint&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;✅ Correct Configuration - Separate Endpoints:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GET /sse - For SSE streaming (responses)&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/sse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request_method&lt;/span&gt; &lt;span class="s"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;GET)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;405&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8000/sse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# POST /messages - For MCP commands&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/messages&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8000&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;h3&gt;
  
  
  Why MCP Needs Two Endpoints
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;MCP Protocol Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSE Stream (GET /sse)&lt;/strong&gt;: Persistent connection for receiving responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command Channel (POST /messages)&lt;/strong&gt;: Send MCP commands and requests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What Happens:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client establishes: GET /sse → Long-lived SSE connection
Client sends command: POST /messages/?session_id=xxx → JSON-RPC request
Server responds via: SSE stream → Real-time response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Errors and Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;RuntimeError: Expected ASGI message 'http.response.body'&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cause&lt;/strong&gt;: POST requests sent to SSE endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Separate GET and POST endpoints as shown above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;405 Method Not Allowed&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cause&lt;/strong&gt;: Wrong HTTP method for endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Ensure GET for /sse, POST for /messages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ Step 7: Testing Your MCP Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 Basic Connectivity Tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test SSE endpoint&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: text/event-stream"&lt;/span&gt; https://mcp-server.yourdomain.com/sse

&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# event: endpoint&lt;/span&gt;
&lt;span class="c"&gt;# data: /messages/?session_id=xxx&lt;/span&gt;
&lt;span class="c"&gt;# : ping - 2024-01-01T12:00:00+00:00&lt;/span&gt;

&lt;span class="c"&gt;# Test HTTPS redirect&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://mcp-server.yourdomain.com/sse
&lt;span class="c"&gt;# Should redirect to HTTPS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.2 Monitor Server Logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Watch container logs&lt;/span&gt;
docker logs mcp-server &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Expected successful logs:&lt;/span&gt;
&lt;span class="c"&gt;# INFO: 172.17.0.1:40138 - "GET /sse HTTP/1.1" 200 OK&lt;/span&gt;
&lt;span class="c"&gt;# INFO: 172.17.0.1:40152 - "POST /messages/?session_id=xxx HTTP/1.1" 202 Accepted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.3 Test with VSCode Copilot, n8n
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43gluxlmoponm60tpng0.png" 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%2F43gluxlmoponm60tpng0.png" alt="Image description" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add MCP Tool in n8n:&lt;/strong&gt;&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp-server.yourdomain.com/sse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SSE"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Successful Connection Indicators:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;n8n shows "Connected" status&lt;/li&gt;
&lt;li&gt;Server logs show both GET and POST requests&lt;/li&gt;
&lt;li&gt;Tools are listed and callable&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔧 Troubleshooting Common Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: DNS Not Resolving
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup mcp-server.yourdomain.com
&lt;span class="c"&gt;# Returns NXDOMAIN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify A record is created correctly&lt;/li&gt;
&lt;li&gt;Wait for DNS propagation (up to 48 hours)&lt;/li&gt;
&lt;li&gt;Use online DNS checkers to verify propagation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 2: SSL Certificate Fails
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certbot failed to authenticate some domains
DNS problem: NXDOMAIN looking up A for mcp-server.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure DNS is fully propagated before running certbot&lt;/li&gt;
&lt;li&gt;Verify domain points to correct IP&lt;/li&gt;
&lt;li&gt;Check firewall allows HTTP (port 80) for validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 3: MCP Client Can't Connect
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client shows "Connection failed"&lt;/li&gt;
&lt;li&gt;405 Method Not Allowed errors&lt;/li&gt;
&lt;li&gt;ASGI protocol errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify Nginx configuration separates GET and POST endpoints&lt;/li&gt;
&lt;li&gt;Check CORS headers are present&lt;/li&gt;
&lt;li&gt;Ensure container is running and accessible on port 8000&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 4: SSE Stream Disconnects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connection drops after few seconds&lt;/li&gt;
&lt;li&gt;Client keeps reconnecting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify &lt;code&gt;proxy_buffering off&lt;/code&gt; in Nginx&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;proxy_read_timeout&lt;/code&gt; is set high (3600s)&lt;/li&gt;
&lt;li&gt;Ensure container handles SSE properly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Production Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security Hardening
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Restrict SSH access&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable
sudo &lt;/span&gt;ufw allow ssh
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow &lt;span class="s1"&gt;'Nginx Full'&lt;/span&gt;

&lt;span class="c"&gt;# Update security group to restrict SSH to your IP only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitoring and Logging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Setup log rotation&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/logrotate.d/mcp-server &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
~/mcp-logs/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Monitor with CloudWatch (optional)&lt;/span&gt;
&lt;span class="c"&gt;# Install CloudWatch agent for detailed monitoring&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backup Strategy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Backup container logs&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; mcp-logs-backup-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz ~/mcp-logs/

&lt;span class="c"&gt;# Backup Nginx configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; /etc/nginx/sites-available/mcp-server.conf ~/nginx-backup.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 Final Verification Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Container running and accessible on port 8000&lt;/li&gt;
&lt;li&gt;[ ] DNS resolves to correct IP address&lt;/li&gt;
&lt;li&gt;[ ] HTTPS certificate valid and auto-renewing&lt;/li&gt;
&lt;li&gt;[ ] GET /sse returns SSE stream&lt;/li&gt;
&lt;li&gt;[ ] POST /messages accepts JSON requests&lt;/li&gt;
&lt;li&gt;[ ] MCP client (n8n/VS Code) connects successfully&lt;/li&gt;
&lt;li&gt;[ ] Server logs show both GET and POST requests&lt;/li&gt;
&lt;li&gt;[ ] Tools are discoverable and executable&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎉 Conclusion
&lt;/h2&gt;

&lt;p&gt;You now have a production-ready MCP server running on AWS EC2 with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secure HTTPS&lt;/strong&gt; with automatic SSL certificate renewal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proper SSE transport&lt;/strong&gt; configuration for real-time communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable architecture&lt;/strong&gt; ready for production workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive monitoring&lt;/strong&gt; and troubleshooting capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight from this deployment is understanding that &lt;strong&gt;MCP requires two separate endpoints&lt;/strong&gt;: one for SSE streaming (GET /sse) and another for command messages (POST /messages). This separation is crucial for the protocol to work correctly with clients like n8n, VS Code, and other MCP-compatible tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale horizontally&lt;/strong&gt;: Use Application Load Balancer for multiple instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add monitoring&lt;/strong&gt;: Implement CloudWatch dashboards and alerts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhance security&lt;/strong&gt;: Add API authentication and rate limiting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize performance&lt;/strong&gt;: Implement caching and connection pooling&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📚 Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" rel="noopener noreferrer"&gt;Server-Sent Events MDN Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.n8n.io/" rel="noopener noreferrer"&gt;n8n MCP Integration Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;AWS EC2 Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Happy deploying! 🚀 If you found this guide helpful, please share it with others building MCP servers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>mcp</category>
      <category>n8n</category>
      <category>ai</category>
    </item>
    <item>
      <title>🐦 VibeCoding - AmazonQ CLI Building Flappy Bird with Phaser 3 + TypeScript: A Journey from Bugs to Victory</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Sun, 08 Jun 2025 14:10:12 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/amazonq-cli-building-flappy-bird-with-phaser-3-typescript-a-journey-from-bugs-to-victory-49a5</link>
      <guid>https://forem.com/fuderrpham03/amazonq-cli-building-flappy-bird-with-phaser-3-typescript-a-journey-from-bugs-to-victory-49a5</guid>
      <description>&lt;p&gt;&lt;em&gt;How I created a fully functional Flappy Bird game and learned valuable lessons about game development, debugging, and problem-solving along the way pair with AmazonQ CLI&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎮 The Challenge
&lt;/h2&gt;

&lt;p&gt;Ever wondered what it takes to recreate one of the most addictive mobile games of all time? I recently embarked on a journey to build &lt;strong&gt;Flappy Bird&lt;/strong&gt; from scratch using modern web technologies, and let me tell you - it was quite an adventure!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎯 &lt;strong&gt;Phaser 3&lt;/strong&gt; - Game engine&lt;/li&gt;
&lt;li&gt;📝 &lt;strong&gt;TypeScript&lt;/strong&gt; - Type safety and better DX&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Rollup&lt;/strong&gt; - Module bundling&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;HTML5 Canvas&lt;/strong&gt; - Rendering&lt;/li&gt;
&lt;li&gt;🔊 &lt;strong&gt;Web Audio API&lt;/strong&gt; - Sound effects&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46lrtl84po3r1pmz9d8q.png" 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%2F46lrtl84po3r1pmz9d8q.png" alt="Image description" width="640" height="1034"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 The Development Journey
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1: Setting Up the Foundation
&lt;/h3&gt;

&lt;p&gt;The project started with a clean architecture approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flappy-bird-game/
├── src/
│   ├── scenes/          # Game scenes (Menu, Game, GameOver)
│   ├── objects/         # Game entities (Bird, PipeManager, Background)
│   ├── config/          # Game configuration
│   └── assets/          # Images and sounds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; for better code maintainability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scene-based architecture&lt;/strong&gt; for clean separation of concerns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component-based game objects&lt;/strong&gt; for reusability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 2: The Physics and Animation
&lt;/h3&gt;

&lt;p&gt;Creating the bird was surprisingly fun! Here's what made it special:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Phaser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Physics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arcade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;flap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setVelocityY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GameConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flapStrength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -350 px/s&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flapSound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Realistic rotation based on velocity&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Upward tilt&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.05&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;The magic details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✨ &lt;strong&gt;Smooth rotation&lt;/strong&gt; that follows physics&lt;/li&gt;
&lt;li&gt;🎵 &lt;strong&gt;Sound feedback&lt;/strong&gt; on every flap&lt;/li&gt;
&lt;li&gt;🎭 &lt;strong&gt;3-frame animation&lt;/strong&gt; for wing flapping&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 3: The Great Scoring System Debugging Saga 🐛
&lt;/h3&gt;

&lt;p&gt;This is where things got &lt;strong&gt;really interesting&lt;/strong&gt;. What seemed like a simple feature turned into a fascinating debugging adventure!&lt;/p&gt;

&lt;h4&gt;
  
  
  The Problem: Score Wasn't Working! 😱
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ This approach FAILED miserably&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;physics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pipes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;object&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isScoreTrigger&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="nx"&gt;score&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Never executed!&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;The symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Game loaded perfectly ✅&lt;/li&gt;
&lt;li&gt;Bird physics worked ✅
&lt;/li&gt;
&lt;li&gt;Pipes generated correctly ✅&lt;/li&gt;
&lt;li&gt;Score remained stubbornly at 0 ❌&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqksrb4nq3htr2jph6pf0.png" 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%2Fqksrb4nq3htr2jph6pf0.png" alt="Image description" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The Investigation 🔍
&lt;/h4&gt;

&lt;p&gt;Through extensive console logging, I discovered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Overlap detection wasn't firing&lt;/strong&gt; for invisible score triggers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixed object types&lt;/strong&gt; in physics groups caused conflicts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phaser's collision system&lt;/strong&gt; was more complex than expected
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Debug logs revealed the truth:&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="s1"&gt;Bird position:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;birdX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;birdY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// ✅ Working&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="s1"&gt;Score trigger created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;triggerX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// ✅ Working  &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="s1"&gt;Overlap detected:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c1"&gt;// ❌ Never appeared!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Breakthrough: Manual Position Tracking 💡
&lt;/h4&gt;

&lt;p&gt;Instead of fighting with Phaser's physics system, I implemented a &lt;strong&gt;custom scoring algorithm&lt;/strong&gt;:&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="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;checkScoreManually&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;birdX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Bird stays at fixed X position&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scoreTriggers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trigger&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;// Pipes move left: X(t) = X₀ + velocity × deltaTime&lt;/span&gt;
        &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;GameConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// -200 px/s&lt;/span&gt;

        &lt;span class="c1"&gt;// Score when pipe passes bird (left movement)&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scored&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;birdX&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;birdX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passedPipes&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;score-updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passedPipes&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;The key insight:&lt;/strong&gt; In Flappy Bird, the &lt;strong&gt;bird doesn't move horizontally&lt;/strong&gt; - the &lt;strong&gt;pipes move toward the bird&lt;/strong&gt;! 🤯&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Technical Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Smart Asset Management&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Automated asset copying during build&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copyAssets&lt;/span&gt; &lt;span class="o"&gt;=&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="nf"&gt;copyDir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/assets&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="s1"&gt;./dist/src/assets&lt;/span&gt;&lt;span class="dl"&gt;'&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="s1"&gt;Assets copied successfully!&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;h3&gt;
  
  
  2. &lt;strong&gt;Persistent High Score System&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple but effective localStorage implementation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;highScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flappyHighScore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;highScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flappyHighScore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentScore&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;showNewRecordAnimation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ✨ Visual feedback&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Performance Optimizations&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Object pooling&lt;/strong&gt; for pipes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual cleanup&lt;/strong&gt; of off-screen objects
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient collision detection&lt;/strong&gt; only where needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimized sprite animations&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏆 The Final Result
&lt;/h2&gt;

&lt;p&gt;After solving the scoring system puzzle, everything clicked into place:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Game Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Smooth 60fps gameplay&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Accurate collision detection&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Persistent high scores&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Sound effects and animations&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Responsive controls&lt;/strong&gt; (mouse + keyboard)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Mobile-friendly design&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Sometimes Simple Solutions Win&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Complex framework features aren't always the answer. My manual position tracking turned out to be more reliable than Phaser's built-in collision system.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Debug Logging is Your Best Friend&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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="s1"&gt;🎯 Manual score detection! Pipe passed bird at X:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;triggerX&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="s1"&gt;✅ New score:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passedPipes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those emoji-filled logs made debugging actually enjoyable!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Understanding Game Mechanics Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I initially misunderstood how Flappy Bird works. The bird doesn't move horizontally - this insight was crucial for fixing the scoring system.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;TypeScript + Game Development = ❤️&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Type safety caught numerous bugs before runtime and made refactoring much safer.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 The Architecture That Worked
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TB
    A[GameScene] --&amp;gt; B[Bird]
    A --&amp;gt; C[PipeManager]  
    A --&amp;gt; D[Background]
    C --&amp;gt; E[Manual Score Detection]
    B --&amp;gt; F[Physics &amp;amp; Animation]
    A --&amp;gt; G[Collision System]
    H[GameConfig] --&amp;gt; A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://gist.github.com/delee03/0d7919352b40fc034f78200a9c49d710" rel="noopener noreferrer"&gt;Detailed Class Diagram&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GameScene&lt;/strong&gt;: Orchestrates everything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bird&lt;/strong&gt;: Physics-based player character&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PipeManager&lt;/strong&gt;: Obstacle generation + custom scoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background&lt;/strong&gt;: Scrolling environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GameConfig&lt;/strong&gt;: Centralized configuration&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Want to build your own version? Here's the quick start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone and setup&lt;/span&gt;
git clone &amp;lt;your-repo&amp;gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;flappy-bird-game
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Build and run&lt;/span&gt;
npm run build
npm run serve

&lt;span class="c"&gt;# Open http://localhost:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pro tips for aspiring game developers:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with a simple game loop&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add one feature at a time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debug with extensive logging&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don't be afraid to try different approaches&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test frequently on different devices&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🎮 What's Next?
&lt;/h2&gt;

&lt;p&gt;The game is fully playable, but there's always room for improvement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📱 &lt;strong&gt;Mobile touch controls&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Particle effects&lt;/strong&gt; for enhanced visuals&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Online leaderboards&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🎵 &lt;strong&gt;Background music&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Level editor&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Building Flappy Bird taught me that &lt;strong&gt;game development is as much about problem-solving as it is about coding&lt;/strong&gt;. The scoring system bug that initially frustrated me became the most educational part of the entire project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real victory wasn't just creating a working game&lt;/strong&gt; - it was learning to debug systematically, think creatively about solutions, and persist through challenging problems.&lt;/p&gt;

&lt;p&gt;Whether you're a seasoned developer or just starting out, I encourage you to try building a simple game. You'll be surprised by how much you learn about programming, problem-solving, and the satisfaction of creating something interactive and fun!&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🎮 &lt;strong&gt;&lt;a href="https://github.com/delee03/flappyBirdGamesWithAmazonQ" rel="noopener noreferrer"&gt;Play the Game&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;&lt;a href="https://github.com/delee03/flappyBirdGamesWithAmazonQ" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line.html" rel="noopener noreferrer"&gt;AmazonQ CLI Docs&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;&lt;a href="https://community.aws/content/2xIoduO0xhkhUApQpVUIqBFGmAc/build-games-with-amazon-q-cli-and-score-a-t-shirt" rel="noopener noreferrer"&gt;AmazonQ CLI Challenges and T-shirts&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;&lt;a href="https://photonstorm.github.io/phaser3-docs/" rel="noopener noreferrer"&gt;Phaser 3 Documentation&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;&lt;a href="https://www.typescriptlang.org/docs/" rel="noopener noreferrer"&gt;TypeScript Handbook&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's your experience with game development? Have you faced similar debugging challenges? Share your stories in the comments below!&lt;/strong&gt; 👇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding, and may your birds always flap smoothly! 🐦✨&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #AmazonQCLI #gamedev #typescript #phaser #javascript #webdev #debugging #flappybird #html5games&lt;/p&gt;

</description>
      <category>amazonqcli</category>
      <category>aws</category>
      <category>gamedev</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Build a Python Bot to Automate Your Expense Tracking with Discord and Google Sheets [Part1]</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Sun, 08 Jun 2025 12:48:25 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/build-a-python-bot-to-automate-your-expense-tracking-with-discord-and-google-sheets-53ck</link>
      <guid>https://forem.com/fuderrpham03/build-a-python-bot-to-automate-your-expense-tracking-with-discord-and-google-sheets-53ck</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/delee03/bot_management_wallet" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt; &lt;br&gt;
&lt;a href="https://www.linkedin.com/in/tienthuanphat/" rel="noopener noreferrer"&gt;LinkedIn &lt;/a&gt; &lt;br&gt;
Tired of manually entering every single coffee purchase or subscription fee into a clunky spreadsheet? Me too. Manual expense tracking is tedious and prone to errors. What if you could just send a message to a personal bot and have everything logged, categorized, and visualized automatically?&lt;/p&gt;

&lt;p&gt;In this step-by-step guide, we'll build exactly that. We'll create a personal Discord bot that listens for your expense messages, processes them, and pushes the data to a Google Sheet. To top it off, we'll build an interactive dashboard right within Google Sheets to see where your money is going.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The results image to easier visualization our goal:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏷️ All WE NEED IS OPEN OUR PHONE AND CHAT WITH BOT&lt;/strong&gt;&lt;br&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%2Fu2dyu6ntjueln2xp2ka0.png" 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%2Fu2dyu6ntjueln2xp2ka0.png" alt="Image description" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a sneak peek of the final result:&lt;/p&gt;
&lt;h2&gt;
  
  
  The Bot in Action:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Interactive Dashboard:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ready to build your own financial assistant? Let's dive in.&lt;/p&gt;

&lt;p&gt;The Architecture: How It Works&lt;br&gt;
Before we write any code, let's understand the flow of data. Our system has a simple but effective architecture:&lt;br&gt;
&lt;a href="https://gist.github.com/delee03/3d749ab271a107ec94761080a10853f7" rel="noopener noreferrer"&gt;Architecture_Diagram&lt;/a&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gwtg8plerflbwip2qvk.png" 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%2F1gwtg8plerflbwip2qvk.png" alt="Image description" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You send a message, the bot picks it up, and the Google Sheet acts as both our database and our business intelligence tool.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 1: Setting Up the Foundations
&lt;/h2&gt;

&lt;p&gt;This part involves configuring Discord and Google Cloud. It's a one-time setup that gives our bot the permissions it needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: The Discord Bot&lt;/strong&gt;&lt;br&gt;
First, we need to create a bot user in Discord.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create an Application:&lt;/strong&gt; Go to the &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord Developer Portal&lt;/a&gt;, click "New Application," and give it a name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create a Bot:&lt;/strong&gt; Navigate to the "Bot" tab and click "Add Bot."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Enable Intents:&lt;/strong&gt; This is crucial. You must enable the &lt;strong&gt;MESSAGE CONTENT INTENT&lt;/strong&gt;. This allows your bot to read the content of messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Get Your Token:&lt;/strong&gt; On the same "Bot" tab, click "Reset Token" to get your secret bot token. Copy it and save it somewhere safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Invite the Bot:&lt;/strong&gt; Go to the "OAuth2" -&amp;gt; "URL Generator" tab. Select the bot scope, and then grant it Send Messages and Read Message History permissions. Copy the generated URL and paste it into your browser to invite the bot to your server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: The Google Sheets "Database"&lt;/strong&gt;&lt;br&gt;
Next, we'll set up the Google Sheets API so our Python script can write data to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a Google Cloud Project:&lt;/strong&gt; Go to the &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;, create a new project, and give it a name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Enable APIs:&lt;/strong&gt; In your project, search for and enable two APIs: Google Drive API and Google Sheets API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Create a Service Account:&lt;/strong&gt; In "APIs &amp;amp; Services" -&amp;gt; "Credentials," create a new Service account. Give it a name and grant it the Editor role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Generate a JSON Key:&lt;/strong&gt; After creating the service account, go to its "Keys" tab, add a new key, select JSON, and create it. A credentials.json file will be downloaded. Place this file in your project folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Share Your Sheet:&lt;/strong&gt; Create a new Google Sheet. Open the credentials.json file, copy the client_email value, and share your Google Sheet with this email address, giving it Editor access.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 2: The Brain - The Python Code
&lt;/h2&gt;

&lt;p&gt;Now for the fun part. Here is the complete Python script for our bot. Save it as main.py.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import sys
import discord
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import re
from datetime import datetime
import os
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv() 

# --- Bot Setup ---
# This code block fixes an EventLoop issue on Windows
if sys.platform == 'win32':
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

# --- Configuration ---
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
GOOGLE_SHEET_NAME = os.getenv('GOOGLE_SHEET_NAME')
ALLOWED_CHANNEL = os.getenv('ALLOWED_CHANNEL')

# Expense categories dictionary
CATEGORIES = {
    'Food &amp;amp; Drink': ['ăn', 'uống', 'cà phê', 'cf', 'nhà hàng', 'quán', 'trà sữa', 'bữa trưa', 'bữa tối'],
    'Transportation': ['xăng', 'grab', 'be', 'bus', 'taxi', 'vé xe', 'gửi xe'],
    'Utilities': ['điện', 'nước', 'internet', 'net', 'thuê nhà', '4G'],
    'Shopping': ['quần áo', 'giày', 'mỹ phẩm', 'sách', 'mua sắm', 'shopee', 'lazada'],
    'Entertainment': ['xem phim', 'game', 'du lịch', 'concert'],
    'Self-development': ['thi certification', 'khóa học', 'English', 'cert', 'IELTS', 'gym'],
    'IT-Industry': ['GPT', 'Cursor', 'AWS', 'AI'],
    'Networking': ['Mời nước', 'mời cơm trưa', 'network']
}

# --- Core Logic Functions ---
def get_gsheet_client():
    """Authorizes the gspread client and returns it."""
    scope = ["https://spreadsheets.google.com/feeds", 'https://www.googleapis.com/auth/spreadsheets',
             "https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive"]
    creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
    client = gspread.authorize(creds)
    return client

def parse_message(text):
    """
    Parses a message to extract description and amount.
    Handles 'k' suffix for thousands (e.g., 10k -&amp;gt; 10000).
    """
    match = re.search(r'(.*):\s*([\d.,]+)\s*([kK]?)', text.strip())
    if match:
        description = match.group(1).strip()
        amount_str = match.group(2).replace('.', '').replace(',', '')
        suffix = match.group(3).lower()
        try:
            amount = float(amount_str)
            if suffix == 'k':
                amount *= 1000
            return description, int(amount)
        except ValueError:
            return None, None
    return None, None

def categorize_expense(description):
    """Automatically categorizes an expense based on keywords."""
    description_lower = description.lower()
    for category, keywords in CATEGORIES.items():
        for keyword in keywords:
            if keyword.lower() in description_lower:
                return category
    return 'Other'

# --- Discord Bot Events ---
gsheet_client = get_gsheet_client()
worksheet = gsheet_client.open(GOOGLE_SHEET_NAME).sheet1

intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

@client.event
async def on_ready():
    """Called when the bot successfully connects."""
    print(f'Bot has logged in as {client.user}')
    print('Bot is ready!')

@client.event
async def on_message(message):
    """Called for every new message."""
    if message.author == client.user: return
    if message.channel.name != ALLOWED_CHANNEL: return

    description, amount = parse_message(message.content)

    if description and amount:
        category = categorize_expense(description)
        now = datetime.now()
        date_today = now.strftime('%d/%m/%Y')
        time_now = now.strftime('%H:%M:%S')
        new_row = [date_today, time_now, category, description, amount]

        try:
            worksheet.append_row(new_row)
            await message.channel.send(
                f'✅ **Successfully Logged!**\n\n'
                f'📅 **Date:** {date_today}\n'
                f'⏰ **Time:** {time_now}\n'
                f'🏷️ **Category:** {category}\n'
                f'📝 **Item:** {description}\n'
                f'💰 **Amount:** {amount:,} VND'
            )
        except Exception as e:
            print(f"Error writing to Google Sheet: {e}")
            await message.channel.send('Sorry, there was an error saving to Google Sheets.')

# --- Main function to run the bot ---
if __name__ == '__main__':
    # Setup environment variables in a .env file
    # DISCORD_TOKEN=...
    # GOOGLE_SHEET_NAME=...
    # ALLOWED_CHANNEL=...
    client.run(DISCORD_TOKEN)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 3: The Interactive Dashboard in Google Sheets
&lt;/h2&gt;

&lt;p&gt;This is where we turn our raw data into valuable insights without writing any more code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a "Month" Helper Column:&lt;/strong&gt; In your data sheet (WalletManagement), go to the next empty column (e.g., F1), title it "Month," and paste this formula into cell F2. It will automatically populate the month for every entry.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=ARRAYFORMULA(IF(A2:A&amp;lt;&amp;gt;"", IFERROR(TEXT(A2:A, "MM/YYYY")), ""))&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Note: Depending on your region, you may need to replace the comma , with a semicolon ;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create a Pivot Table:&lt;/strong&gt; In a new sheet (call it Dashboard), go to Insert -&amp;gt; Pivot table. Use your WalletManagement sheet as the data source.&lt;/p&gt;

&lt;p&gt;Set Rows to Hạng mục (Category).&lt;br&gt;
Set Values to Số tiền (Amount), summarized by SUM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Add a Slicer:&lt;/strong&gt; This is our "remote control." Go to Data -&amp;gt; Add a slicer. In the slicer settings, choose the "Month" column as your filter. Now you can click the slicer and select any month to filter your report!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Add the Pie Chart:&lt;/strong&gt; Select the data in your Pivot Table (the categories and their totals). Go to Insert -&amp;gt; Chart and choose a Pie Chart. It will automatically link to the pivot table and update whenever you use the slicer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Next Steps
&lt;/h2&gt;

&lt;p&gt;And there you have it! A fully automated expense tracker tailored to your needs. You've connected a Discord bot to Google's powerful spreadsheet and visualization tools, creating a system that's both practical and fun.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From here, the possibilities are endless:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deploy the Bot: Deploy the Python script to a free cloud service like Render or a Raspberry Pi so it runs 24/7.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add More Commands: Create commands like !summary or !last5 to get insights directly within Discord.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deeper Analysis: Use more advanced Google Sheets functions or connect your sheet to Google Looker Studio for even more powerful BI dashboards.&lt;br&gt;
Happy coding, and may your budgets be ever in your favor!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>discord</category>
      <category>googlecloud</category>
      <category>programming</category>
      <category>automaton</category>
    </item>
    <item>
      <title>Avoiding Common Pitfalls: Deploying Node.js Backends to AWS Lambda with Docker &amp; Serverless on macOS</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Fri, 09 May 2025 06:13:07 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/avoiding-common-pitfalls-deploying-nodejs-backends-to-aws-lambda-with-docker-serverless-on-macos-1k0c</link>
      <guid>https://forem.com/fuderrpham03/avoiding-common-pitfalls-deploying-nodejs-backends-to-aws-lambda-with-docker-serverless-on-macos-1k0c</guid>
      <description>&lt;h1&gt;
  
  
  Avoiding Common Pitfalls: Deploying Node.js Backends to AWS Lambda with Docker &amp;amp; Serverless on macOS
&lt;/h1&gt;

&lt;p&gt;Deploying a Node.js (NestJS) backend to AWS Lambda using Docker and the Serverless Framework can be a smooth experience—if you know what to watch out for. On macOS, especially with Apple Silicon, there are unique challenges that can trip up even experienced developers. Here's a practical guide to the most common pitfalls and how to avoid them, based on real-world troubleshooting.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. &lt;strong&gt;Global Package Permissions: Never Use &lt;code&gt;sudo&lt;/code&gt; with Serverless&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Installing Serverless Framework globally with &lt;code&gt;sudo&lt;/code&gt; (&lt;code&gt;sudo npm i -g serverless&lt;/code&gt;) can cause permission errors, making it impossible to update or remove packages later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always install Serverless globally &lt;strong&gt;without&lt;/strong&gt; &lt;code&gt;sudo&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Or use a Node version manager (like &lt;code&gt;nvm&lt;/code&gt;) for even more control.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. &lt;strong&gt;Base Image Confusion: Use the AWS Lambda Base Image&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Using a standard Node.js image (e.g., &lt;code&gt;node:20-alpine&lt;/code&gt;) instead of the AWS Lambda base image leads to entrypoint errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: fork/exec /usr/local/bin/docker-entrypoint.sh: exec format error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always use the AWS Lambda base image:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  FROM public.ecr.aws/lambda/nodejs:20
  ...
  CMD [ "dist/lambda.handler" ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This ensures Lambda can find and invoke your handler correctly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. &lt;strong&gt;Handler Syntax: CMD Must Match Lambda's Expectations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Using the wrong CMD in your Dockerfile, such as &lt;code&gt;CMD ["node", "dist/lambda.js"]&lt;/code&gt;, will break Lambda's invocation process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the format &lt;code&gt;CMD [ "&amp;lt;file&amp;gt;.&amp;lt;exported function&amp;gt;" ]&lt;/code&gt; (e.g., &lt;code&gt;CMD [ "dist/lambda.handler" ]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Make sure your built JS file exports the handler as &lt;code&gt;exports.handler&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. &lt;strong&gt;Architecture Mismatches: Build for &lt;code&gt;linux/amd64&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; On Apple Silicon (M1/M2), Docker may build images for &lt;code&gt;arm64&lt;/code&gt; by default, but AWS Lambda requires &lt;code&gt;amd64&lt;/code&gt; (x86_64). This leads to manifest errors or runtime failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always build with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  docker buildx build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; lambda-img:latest &lt;span class="nt"&gt;--load&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Check your image manifest to ensure only &lt;code&gt;amd64&lt;/code&gt; is present.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. &lt;strong&gt;CORS Configuration: Place It in the Right Spot&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Defining CORS under each function event in &lt;code&gt;serverless.yml&lt;/code&gt; (as you might with REST API) will cause configuration errors with HTTP API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Place CORS configuration under &lt;code&gt;provider.httpApi&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;httpApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;allowedOrigins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
        &lt;span class="na"&gt;allowedHeaders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
        &lt;span class="na"&gt;allowedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. &lt;strong&gt;Manifest Bloat: Clean Up Before Rebuilding&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Docker image manifests may accumulate unwanted architectures (&lt;code&gt;unknown&lt;/code&gt;, &lt;code&gt;arm64&lt;/code&gt;) if you don't clean up before rebuilding, leading to Lambda deployment failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regularly prune Docker images and cache:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt;
  docker rmi &lt;span class="si"&gt;$(&lt;/span&gt;docker images &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. &lt;strong&gt;Debugging: Always Check CloudWatch Logs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Many Lambda errors (handler not found, runtime errors) are only visible in CloudWatch logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After each deployment, check CloudWatch logs for your Lambda function to quickly identify and resolve issues.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Deploying with Docker and Serverless on macOS is powerful, but requires attention to detail. By following these best practices and learning from common mistakes, you'll save hours of frustration and ensure your backend runs smoothly on AWS Lambda.&lt;/p&gt;

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

</description>
      <category>ai</category>
      <category>aws</category>
      <category>serverless</category>
      <category>devops</category>
    </item>
    <item>
      <title>Troubleshoot: Update Boto3 'BedrockRuntime' object has no attribute 'start_async_invoke' 🚀</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Mon, 10 Mar 2025 15:28:12 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/troubleshoot-update-boto3-bedrockruntime-object-has-no-attribute-startasyncinvoke-3of7</link>
      <guid>https://forem.com/fuderrpham03/troubleshoot-update-boto3-bedrockruntime-object-has-no-attribute-startasyncinvoke-3of7</guid>
      <description>&lt;p&gt;Today, I tried using a new feature from AWS—Amazon Nova Model—to generate videos. However, when integrating it with AWS Lambda using Boto3 SDK, I encountered an error related to the method start_async_invoke. The error message stated that the object has no attribute "start_async_invoke", and I wasn’t sure why exactly this happened.&lt;/p&gt;

&lt;p&gt;After some investigation, I suspected that when AWS releases a new feature, it sometimes takes a little time for Lambda runtime environments to update accordingly. As a result, I ran into this issue because my Lambda function was using an outdated version of Boto3.&lt;/p&gt;

&lt;p&gt;The solution? Upgrading the Boto3 layer to the latest version! After applying the update, I was able to use the method successfully to generate videos using a provided prompt.&lt;/p&gt;

&lt;p&gt;If you're facing the same issue, here’s the exact process you can follow to update your Boto3 layer in AWS Lambda 😊:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step-by-Step Guide to Upgrading Boto3 in AWS Lambda&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Open AWS Cloud Shell on the AWS Management Console&lt;/strong&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhmdb6t3bkkga8rmpqqg.png" 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%2Frhmdb6t3bkkga8rmpqqg.png" alt="Image description" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create a library directory&lt;/strong&gt;&lt;br&gt;
Run the following command to create a new directory for your custom Boto3 layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LIB_DIR=boto3-mylayer/python
mkdir -p $LIB_DIR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Install the latest Boto3 version in the library directory&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install boto3 -t $LIB_DIR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Package the dependencies into a ZIP file&lt;/strong&gt;&lt;br&gt;
Navigate into the boto3-mylayer directory and create a ZIP file containing the updated Boto3 package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd boto3-mylayer
zip -r /tmp/boto3-mylayer.zip .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Publish the updated layer to AWS Lambda&lt;/strong&gt;&lt;br&gt;
Run this command to publish the new Lambda layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws lambda publish-layer-version --layer-name boto3-mylayer --zip-file fileb:///tmp/boto3-mylayer.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tj34tcw39whshzoyei4.png" 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%2F6tj34tcw39whshzoyei4.png" alt="Image description" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this step is completed, AWS will return a confirmation dictionary that includes the ARN (Amazon Resource Name) for the new layer like in the below image. You’ll need this ARN for the final step.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvxcrbusuk7ylydrcduy.png" 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%2Fjvxcrbusuk7ylydrcduy.png" alt="Image description" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Update your Lambda function to use the new Boto3 layer&lt;/strong&gt;&lt;br&gt;
Replace MY_FUNCTION with your actual Lambda function name and LAYER_ARN with the ARN you obtained in the previous step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws lambda update-function-configuration --function-name MY_FUNCTION --layers LAYER_ARN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example (Replacing MY_FUNCTION &amp;amp; LAYER_ARN):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws lambda update-function-configuration --function-name genVideoAI --layers arn:aws:lambda:us-east-1:123456789:layer:boto3-mylayer:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxs6sgsli0kkwvx4lak4.png" 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%2Fqxs6sgsli0kkwvx4lak4.png" alt="Image description" width="800" height="302"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;7. Test Your Lambda Function&lt;/strong&gt;&lt;br&gt;
Now, go back to your AWS Lambda function and test it out. If everything is set up correctly, your function should work without the previous error!&lt;/p&gt;

&lt;p&gt;That’s it! This method worked for me, and I hope it works for you as well. Let me know in the comments if this helped or if you faced any issues. Thanks for reading, and happy coding! 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fact:&lt;/strong&gt; This is a video model Amazon Nova generated for me after troubleshoot method "start_async_invoke" &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24a7knj3g5l4e05ojklm.png" 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%2F24a7knj3g5l4e05ojklm.png" alt="Image description" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect me on LinkedIn:&lt;a href="https://www.linkedin.com/in/tienthuanphat/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/tienthuanphat/&lt;/a&gt;&lt;br&gt;
Repo Github: &lt;a href="https://github.com/delee03/genVideo_NovaReels" rel="noopener noreferrer"&gt;https://github.com/delee03/genVideo_NovaReels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>boto3</category>
      <category>lambda</category>
      <category>bedrock</category>
    </item>
    <item>
      <title>DeepSeek R1 vs. OpenAI o1 vs. Anthropic Claude: AI Coding Battle of 2025 – Who Reigns Supreme? 🚀</title>
      <dc:creator>Phạm Tiến Thuận Phát</dc:creator>
      <pubDate>Thu, 06 Feb 2025 15:23:36 +0000</pubDate>
      <link>https://forem.com/fuderrpham03/deepseek-r1-vs-openai-o1-vs-anthropic-claude-ai-coding-battle-of-2025-who-reigns-supreme-f</link>
      <guid>https://forem.com/fuderrpham03/deepseek-r1-vs-openai-o1-vs-anthropic-claude-ai-coding-battle-of-2025-who-reigns-supreme-f</guid>
      <description>&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction: The AI Showdown of 2025
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
In the rapidly evolving landscape of artificial intelligence (AI) in 2025, developers are presented with a variety of models that excel in logical reasoning and coding capabilities. The recent introduction of DeepSeek’s R1 model has intensified discussions about the comparative strengths of AI models from industry leaders such as OpenAI and Anthropic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which model is the ultimate coding assistant for developers? Let’s break it down.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Contenders: A Quick Overview&lt;/strong&gt;&lt;br&gt;
🔥 &lt;em&gt;&lt;strong&gt;DeepSeek R1: The New Disruptor&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
DeepSeek, a Chinese AI company, has developed R1, which emphasizes logical inference, mathematical reasoning, and real-time problem-solving. Notably, DeepSeek claims that R1 surpasses OpenAI’s o1 model in benchmarks like the American Invitational Mathematics Examination (AIME) and MATH.&lt;/p&gt;

&lt;p&gt;🧠 &lt;em&gt;&lt;strong&gt;OpenAI o1: The Reasoning Giant&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
OpenAI’s latest model, o1, represents a major shift towards advanced reasoning capabilities, moving beyond statistical predictions to true logical thinking. This makes it an invaluable tool for developers looking for intelligent coding assistance.&lt;/p&gt;

&lt;p&gt;🛡️ &lt;em&gt;&lt;strong&gt;Anthropic’s Claude Series: The Safety-Conscious AI&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Anthropic has developed the Claude series, focusing on alignment, safety, and responsible AI behavior. Claude models are designed to minimize hallucinations and biased outputs, ensuring safe and ethical AI-assisted development.&lt;/p&gt;

&lt;p&gt;**Comparison Table: DeepSeek R1 vs. OpenAI o1 for easily illustration&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5c3fv0cs9pont5mojngw.png" 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%2F5c3fv0cs9pont5mojngw.png" alt="Comparing AI Features for Developers" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion: Which AI Model Should You Use?&lt;/strong&gt;&lt;br&gt;
Each model brings unique strengths to the table, and the best choice depends on your specific needs:&lt;/p&gt;

&lt;p&gt;If you need raw logic power, mathematical reasoning, and a cost-effective AI, DeepSeek R1 is a game-changer. 🚀&lt;/p&gt;

&lt;p&gt;If you prefer a well-rounded, reasoning-driven AI for general coding tasks, OpenAI o1 is your best bet. 🔥&lt;/p&gt;

&lt;p&gt;If you prioritize safety, reliability, and ethical AI use, then Anthropic Claude is the way to go. 🛡️&lt;/p&gt;

&lt;p&gt;2025 is shaping up to be the most competitive year for AI coding assistants. Which model will dominate? Only time will tell! ⏳&lt;/p&gt;

&lt;p&gt;💡 What do you think? Which AI model do you prefer for coding tasks? Drop your thoughts in the comments! 🚀&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
