<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Moksh Gupta</title>
    <description>The latest articles on DEV Community by Moksh Gupta (@moksh).</description>
    <link>https://dev.to/moksh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2457679%2F32485ee6-614e-4050-bd8e-e22536e1f2b5.png</url>
      <title>DEV Community: Moksh Gupta</title>
      <link>https://dev.to/moksh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/moksh"/>
    <language>en</language>
    <item>
      <title>Building a Python MCP Server from Scratch - A Practical GitHub API Guide</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Fri, 19 Jun 2026 08:53:46 +0000</pubDate>
      <link>https://dev.to/moksh/building-a-python-mcp-server-from-scratch-a-practical-github-api-guide-397k</link>
      <guid>https://dev.to/moksh/building-a-python-mcp-server-from-scratch-a-practical-github-api-guide-397k</guid>
      <description>&lt;p&gt;The Model Context Protocol has gone from a niche Anthropic project to industry-standard infrastructure in under two years - hitting 97 million monthly SDK downloads and earning a permanent home under the Linux Foundation. Every major AI coding tool now speaks MCP natively, yet most tutorials either list pre-built servers to install or recite the spec without building anything real.&lt;/p&gt;

&lt;p&gt;This guide walks you through writing a Python MCP server from zero: defining tools, resources, and prompts, testing with the MCP Inspector, and wiring it into Claude Desktop or Claude Code. The working example targets the GitHub API - a practical starting point you can extend for any project that needs live external data inside an AI assistant.&lt;/p&gt;

&lt;p&gt;Prerequisites: Python 3.10+, uv or pip, and Claude Desktop or Claude Code installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an MCP Server Actually Does
&lt;/h2&gt;

&lt;p&gt;MCP is a JSON-RPC protocol that gives an AI client a standardized way to call external services. The client - Claude, Cursor, or any compliant tool - sends a request and your server handles it, regardless of what language it is written in.&lt;/p&gt;

&lt;p&gt;Every MCP server exposes three core primitives. Tools are callable functions the AI can invoke to take action or fetch data. Resources are read-only data endpoints the AI can pull from - similar to files or database records. Prompts are reusable instruction templates stored on the server and referenced by name, useful for standardizing workflows across a team.&lt;/p&gt;

&lt;p&gt;For transport, this guide uses stdio - the server runs as a subprocess and communicates over stdin/stdout. This works out of the box with Claude Desktop and Claude Code. For production or remote deployments, Streamable HTTP is the alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;p&gt;Create a fresh directory and install the MCP SDK with the &lt;code&gt;[cli]&lt;/code&gt; extra, which includes the dev server and inspector launcher:&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;mkdir &lt;/span&gt;github-mcp-server
&lt;span class="nb"&gt;cd &lt;/span&gt;github-mcp-server
uv init &lt;span class="nb"&gt;.&lt;/span&gt;
uv add &lt;span class="s2"&gt;"mcp[cli]"&lt;/span&gt; httpx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer pip, run &lt;code&gt;pip install "mcp[cli]" httpx&lt;/code&gt; instead. Your project needs just three files: &lt;code&gt;server.py&lt;/code&gt;, &lt;code&gt;pyproject.toml&lt;/code&gt; (if using uv), and an optional &lt;code&gt;.env&lt;/code&gt; for your GitHub token.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal Tool in 10 Lines
&lt;/h2&gt;

&lt;p&gt;Before diving into the full server, start with the simplest possible working example. This lets you confirm the SDK is wired up correctly before adding any real logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello-mcp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return a personalized greeting. Use this when asked to greet someone.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;! Your MCP server is working.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stdio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;uv run mcp dev server.py&lt;/code&gt; to launch the MCP Inspector at &lt;code&gt;http://localhost:5173&lt;/code&gt;. Navigate to the Tools tab, call &lt;code&gt;greet&lt;/code&gt; with any name, and confirm the response. Three things matter here: &lt;code&gt;FastMCP&lt;/code&gt; handles all JSON-RPC plumbing, the &lt;code&gt;@mcp.tool()&lt;/code&gt; decorator auto-generates a schema from your type hints, and the docstring is what the AI reads to decide whether to call this tool - write it clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the GitHub Tools Server
&lt;/h2&gt;

&lt;p&gt;Now replace that minimal example with a real server. This version exposes two tools: one that fetches repository metadata and one that lists open issues, both backed by live GitHub API calls via httpx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This server provides tools to interact with the GitHub API. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use get_repo_info to fetch repository metadata. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use list_open_issues to retrieve open issues for a repository.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_github_headers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/vnd.github+json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-GitHub-Api-Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2022-11-28&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RepoInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;stars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;forks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;open_issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_repo_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RepoInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Fetch metadata for a GitHub repository including stars, forks, and open issue count.
    Args:
        owner: GitHub username or organization (e.g. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;anthropics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
        repo: Repository name (e.g. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;claude-code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.github.com/repos/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;_github_headers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RepoInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;full_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;stars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stargazers_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;forks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forks_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;open_issues&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open_issues_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;language&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_open_issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    List open issues for a GitHub repository, ordered by most recently updated.
    Args:
        owner: GitHub username or organization
        repo: Repository name
        limit: Max issues to return (1-30, default 10)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.github.com/repos/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/issues&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;_github_headers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;per_page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;updated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;issues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No open issues found for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Open issues in **&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;** (showing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;):&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- #&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stdio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few deliberate design choices are worth noting. Returning a Pydantic model from a tool gives the AI a typed, structured response it can reference field by field - far more reliable than parsing a formatted string. For string-return tools, catching exceptions and returning an error message is safer than letting them propagate, since an unhandled exception under stdio can kill the entire connection. Always clamp numeric inputs like &lt;code&gt;limit&lt;/code&gt; - the AI will occasionally send &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;100&lt;/code&gt;, or a string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Resources and Prompts
&lt;/h2&gt;

&lt;p&gt;Resources let the AI read data passively. Here is one that reports whether a GitHub token is configured, and a dynamic one that fetches a repo's README:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@mcp.resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config://github-tools/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;server_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Report whether the server has a GitHub token configured.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;auth_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authenticated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unauthenticated (rate-limited to 60 req/hr)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GitHub Tools MCP Server&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth_status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github://repos/{owner}/{repo}/readme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_readme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetch the raw README content for a repository.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.github.com/repos/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/readme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nf"&gt;_github_headers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/vnd.github.raw+json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.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;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No README found for this repository.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prompts are stored instruction templates any MCP client can call by name. This one structures a code review request around the GitHub tools we defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@mcp.prompt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;review_pull_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Prompt template for reviewing a GitHub pull request.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please review pull request #&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Start by fetching the repository info with get_repo_info, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;then list the open issues to understand the project context. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Focus your review on correctness, performance, and adherence to the project&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s patterns.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing with the MCP Inspector
&lt;/h2&gt;

&lt;p&gt;The fastest way to validate your server is with the built-in inspector - no Claude required. Run &lt;code&gt;uv run mcp dev server.py&lt;/code&gt; and open &lt;code&gt;http://localhost:5173&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Set your &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; in the Environment Variables section before connecting. Then test tools in the Tools tab, verify resources in the Resources tab, and confirm prompts show up in the Prompts tab. If anything fails, the Logs panel shows the raw JSON-RPC exchange, which is the most direct way to pinpoint the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to Claude Desktop
&lt;/h2&gt;

&lt;p&gt;Open the Claude Desktop config file - on macOS at &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;, on Windows at &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;. Add your server under &lt;code&gt;mcpServers&lt;/code&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github-tools"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp[cli]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"httpx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/absolute/path/to/github-mcp-server/server.py"&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;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"PYTHONUNBUFFERED"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITHUB_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_github_token_here"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;Use &lt;code&gt;uv run&lt;/code&gt; rather than a bare &lt;code&gt;python&lt;/code&gt; command - Claude Desktop spawns its own shell environment without your PATH, so bare &lt;code&gt;python&lt;/code&gt; will often fail. Fully quit and restart Claude Desktop after saving. A plug icon in the input box confirms the server connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to Claude Code
&lt;/h2&gt;

&lt;p&gt;For Claude Code, use the &lt;code&gt;claude mcp add&lt;/code&gt; CLI command. The &lt;code&gt;--&lt;/code&gt; separator is required to separate the server name from the launch command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add github-tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_token &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;PYTHONUNBUFFERED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; uv run &lt;span class="nt"&gt;--with&lt;/span&gt; &lt;span class="s2"&gt;"mcp[cli]"&lt;/span&gt; &lt;span class="nt"&gt;--with&lt;/span&gt; httpx python /absolute/path/to/server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To share the server config with your team, use &lt;code&gt;--scope project&lt;/code&gt;. This writes an &lt;code&gt;.mcp.json&lt;/code&gt; file to the repository root and prompts team members to activate it when they open the project. Run &lt;code&gt;claude mcp list&lt;/code&gt; to confirm registration.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/build-mcp-server-python" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/build-mcp-server-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MCP Python SDK: &lt;a href="https://github.com/modelcontextprotocol/python-sdk" rel="noopener noreferrer"&gt;https://github.com/modelcontextprotocol/python-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;FastMCP documentation: &lt;a href="https://gofastmcp.com" rel="noopener noreferrer"&gt;https://gofastmcp.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MCP specification (Linux Foundation): &lt;a href="https://spec.modelcontextprotocol.io" rel="noopener noreferrer"&gt;https://spec.modelcontextprotocol.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;httpx documentation: &lt;a href="https://www.python-httpx.org" rel="noopener noreferrer"&gt;https://www.python-httpx.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub REST API reference: &lt;a href="https://docs.github.com/en/rest" rel="noopener noreferrer"&gt;https://docs.github.com/en/rest&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>mcp</category>
      <category>developertools</category>
    </item>
    <item>
      <title>Node.js vs Bun vs Deno 2 in 2026: Which JavaScript Runtime Should You Actually Use?</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Thu, 18 Jun 2026 05:34:55 +0000</pubDate>
      <link>https://dev.to/moksh/nodejs-vs-bun-vs-deno-2-in-2026-which-javascript-runtime-should-you-actually-use-260e</link>
      <guid>https://dev.to/moksh/nodejs-vs-bun-vs-deno-2-in-2026-which-javascript-runtime-should-you-actually-use-260e</guid>
      <description>&lt;p&gt;In 2026, the JavaScript runtime landscape looks very different from just two years ago. Trigger.dev switched to Bun and reported a 5x throughput improvement on identical hardware. Anthropic acquired Bun and uses it in Claude Code's CLI. Deno 2 shipped real Node.js compatibility, bringing enterprise teams on board. And Node.js 24 quietly added native TypeScript execution alongside a production-ready built-in test runner.&lt;/p&gt;

&lt;p&gt;The question developers now face is not "should I switch?" - it is "which runtime fits which job?" This guide covers where each runtime actually wins in 2026, backed by numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Each Runtime Shipped Recently
&lt;/h2&gt;

&lt;p&gt;Node.js 24 reached Active LTS status with a major quality-of-life upgrade: &lt;code&gt;--experimental-strip-types&lt;/code&gt; is now stable, letting you run &lt;code&gt;.ts&lt;/code&gt; files directly without a separate build step. The &lt;code&gt;require(esm)&lt;/code&gt; interop is also stable in v24, ending years of CommonJS and ESM friction. The built-in &lt;code&gt;node:test&lt;/code&gt; module gained coverage reporting, mocking support, and &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;it&lt;/code&gt; APIs. V8 13.6 makes JSON serialization up to 2x faster on large objects.&lt;/p&gt;

&lt;p&gt;Bun 1.3 is still the all-in-one runtime - it packs a package manager, bundler, and test runner into a single binary, running on JavaScriptCore (Safari's engine) for lower memory and faster cold starts than V8. It added a built-in Redis client in 1.3, joining a native SQLite driver. The &lt;code&gt;bun compile&lt;/code&gt; command produces single-file executables, making it the cleanest option for distributing CLI tools. Anthropic's acquisition signals long-term institutional backing.&lt;/p&gt;

&lt;p&gt;Deno 2.8 completed the Node.js compatibility work started in v2.0. It now handles &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;node_modules&lt;/code&gt;, npm workspaces, and most CommonJS packages with little friction. New additions include &lt;code&gt;deno audit&lt;/code&gt;, a &lt;code&gt;dx&lt;/code&gt; command similar to npx, self-extracting compiled binaries, and stabilization of the Temporal API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Benchmarks
&lt;/h2&gt;

&lt;p&gt;On a plain REST endpoint with no database, Bun with Hono handles around 110,000 requests per second. Deno reaches roughly 85,000 req/s, while Node.js with Express lands near 50,000 req/s. In realistic apps with database calls and middleware, the gap narrows but Bun still leads at around 14,320 req/s versus Node's 5,240.&lt;/p&gt;

&lt;p&gt;Cold start times tell an equally clear story. A hello-world process starts in 8-15ms with Bun, 40-60ms with Deno, and 60-120ms with Node.js. On AWS Lambda, Bun averages a 156ms cold start compared to Node's 245ms - a 35% difference that adds up quickly in pay-per-invocation billing.&lt;/p&gt;

&lt;p&gt;Memory usage follows the same pattern. An idle Bun process uses around 18MB; Deno uses roughly 30MB; Node.js around 40MB. At 5,000 concurrent WebSocket connections, Bun uses 620MB versus Node's 890MB - a meaningful difference when running many processes per server.&lt;/p&gt;

&lt;p&gt;Package install speed is where Bun is the clear standout. An 847-package monorepo installs in 1.2 seconds with &lt;code&gt;bun install&lt;/code&gt; versus 32 seconds with npm. On a 1,847-dependency project, the gap grows to 47 seconds versus 17-20 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript Support - The Real Practical Difference
&lt;/h2&gt;

&lt;p&gt;All three runtimes can execute TypeScript without a separate compile step in 2026, but their handling of TypeScript features differs significantly.&lt;/p&gt;

&lt;p&gt;Bun and Deno support enums, decorators, and TypeScript namespaces natively out of the box. Node.js 24's strip-types mode only removes type annotations - it does not transform TypeScript syntax, so enums and decorators still require a bundler like tsx or esbuild.&lt;/p&gt;

&lt;p&gt;Deno also offers something Bun and Node.js do not: &lt;code&gt;deno check server.ts&lt;/code&gt; runs a full tsc pass and surfaces type errors before runtime. Both Bun and Node.js strip types silently, meaning type errors go undetected until they crash at runtime - unless you run a separate &lt;code&gt;tsc&lt;/code&gt; step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in Tooling Comparison
&lt;/h2&gt;

&lt;p&gt;Bun and Deno both cut down the toolchain setup required for new projects. A fresh Bun project comes with TypeScript support, a test runner, and a bundler - no installs needed. Deno ships a linter, formatter, test runner, doc generator, and compiler as built-in subcommands.&lt;/p&gt;

&lt;p&gt;Node.js 24 has been closing this gap. Its built-in &lt;code&gt;node:test&lt;/code&gt; module now supports coverage, mocking, and &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;it&lt;/code&gt; APIs - making it a practical Jest replacement. But there is still no built-in bundler or linter, so tools like esbuild and ESLint remain part of the standard workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing an HTTP Server in Each Runtime
&lt;/h2&gt;

&lt;p&gt;Bun and Deno both use the web-standard &lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt; API, making code portable to Cloudflare Workers, edge functions, and browser Service Workers. Node.js uses its own HTTP module by default.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js 24:&lt;/strong&gt; &lt;code&gt;createServer()&lt;/code&gt; from &lt;code&gt;node:http&lt;/code&gt;, run with &lt;code&gt;node --experimental-strip-types server.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bun:&lt;/strong&gt; &lt;code&gt;Bun.serve({ fetch(req) {...} })&lt;/code&gt;, run with &lt;code&gt;bun run server.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deno:&lt;/strong&gt; &lt;code&gt;Deno.serve((req) =&amp;gt; {...})&lt;/code&gt;, run with &lt;code&gt;deno run --allow-net server.ts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Bun and Deno APIs align with how you write handlers in edge environments, which makes code more portable across deployment targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Readiness - Known Gaps
&lt;/h2&gt;

&lt;p&gt;Bun's main production concern is stability in long-running services. Memory leaks have been reported and partially patched through Bun 1.1.x, but developers continue to flag instability in processes running for days. There is no formal LTS program, so breaking changes can ship at any version. The ongoing Zig-to-Rust rewrite adds short-term uncertainty.&lt;/p&gt;

&lt;p&gt;Deno's main challenge is ecosystem coverage. Developer adoption sits at roughly 2.4% versus Node.js's 42.65%. npm compatibility covers about 90-95% of packages, but native addons and Node-internal-dependent packages remain hit-or-miss. Next.js and Remix are not fully supported.&lt;/p&gt;

&lt;p&gt;Node.js's limitations are more familiar: slower cold starts, no built-in bundler, and slower package installs. ESM/CommonJS migration friction, while improved in v24, still causes headaches on large codebases. TypeScript enum and decorator support still requires a build tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Which Runtime
&lt;/h2&gt;

&lt;p&gt;Here is a practical decision guide based on use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless and edge functions&lt;/strong&gt; - Bun (8ms cold start, 35% faster Lambda)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-throughput REST APIs&lt;/strong&gt; - Bun (2-3x req/s in real apps, lower memory per process)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI tools and scripts&lt;/strong&gt; - Bun for single-file compiled binaries; Deno for sandboxed execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise and regulated workloads&lt;/strong&gt; - Node.js (30-month LTS, OpenJS Foundation governance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing Node.js codebases&lt;/strong&gt; - Node.js (ecosystem lock-in is real; switch only with measurable benefit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript-first greenfield projects&lt;/strong&gt; - Deno (built-in type checking, web standards first)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security-sensitive applications&lt;/strong&gt; - Deno (default-deny permissions; Bun has no sandbox; Node's model is opt-in)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time and WebSocket servers&lt;/strong&gt; - Bun (2.8x more messages/sec at 5,000 connections)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo tooling&lt;/strong&gt; - Bun (1.2s vs 32s install on an 847-package repo)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge CDN deployment&lt;/strong&gt; - Deno (Deno Deploy is first-class; no Bun equivalent)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Install and Try Each Runtime Today
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Node.js via nvm&lt;/span&gt;
curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--lts&lt;/span&gt;

&lt;span class="c"&gt;# Bun&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://bun.sh/install | bash

&lt;span class="c"&gt;# Deno&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://deno.land/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running a TypeScript file in each:&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;# Node.js 24 - strips types only, no enums or decorators&lt;/span&gt;
node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; script.ts

&lt;span class="c"&gt;# Bun - full TypeScript support&lt;/span&gt;
bun run script.ts

&lt;span class="c"&gt;# Deno - runs without type checking&lt;/span&gt;
deno run script.ts

&lt;span class="c"&gt;# Deno - with full type checking before run&lt;/span&gt;
deno check script.ts &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; deno run script.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To benchmark your actual server, install &lt;code&gt;autocannon&lt;/code&gt; and test under real load before committing to a runtime.&lt;/p&gt;

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

&lt;p&gt;The JavaScript runtime space in 2026 is no longer a single-winner conversation. Bun, Node.js, and Deno have each carved out distinct strengths - and the ecosystem is better for it. Bun pushed the industry to take cold start times and install speed seriously. Deno proved that TypeScript-first and security-by-default were viable design choices. Node.js responded by shipping a native test runner, built-in fetch, and TypeScript stripping.&lt;/p&gt;

&lt;p&gt;Pick the runtime that removes the most friction for your specific workflow. If you are running a high-throughput API that starts frequently, Bun's numbers are hard to argue with. If you are maintaining a large enterprise codebase with strict audit requirements, Node.js LTS is the path of least resistance. If you are writing TypeScript-heavy serverless functions or CLIs, Deno's zero-config type checking is genuinely pleasant.&lt;/p&gt;

&lt;p&gt;Run the benchmarks that matter for your actual application - five minutes of &lt;code&gt;autocannon&lt;/code&gt; on your real endpoint is worth more than any comparison table.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/javascript-runtime-comparison" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/javascript-runtime-comparison&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Node.js official documentation: &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;https://nodejs.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Bun official documentation: &lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;https://bun.sh/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deno official documentation: &lt;a href="https://deno.com/" rel="noopener noreferrer"&gt;https://deno.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;nvm (Node Version Manager): &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;https://github.com/nvm-sh/nvm&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>DevToolLab Launches a Free Discord Community for Developers</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Wed, 17 Jun 2026 10:05:25 +0000</pubDate>
      <link>https://dev.to/moksh/devtoollab-launches-a-free-discord-community-for-developers-5ggl</link>
      <guid>https://dev.to/moksh/devtoollab-launches-a-free-discord-community-for-developers-5ggl</guid>
      <description>&lt;p&gt;Building a developer toolkit is one thing, but creating a community around it is a different challenge. DevToolLab has expanded to 500+ browser-based utilities, and now there is a dedicated space where developers can connect, ask questions, and share what they are working on. The DevToolLab Discord server is open and free to join today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Discord Server Is Actually For
&lt;/h2&gt;

&lt;p&gt;The server has four core purposes. You can get direct help when a tool behaves unexpectedly. You can suggest new tools or request features - the best ideas have always come from developers who hit a real workflow gap. You can share what you are building and get feedback from peers. And you get early announcements: updates get posted in the server before they appear anywhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Join the DevToolLab Discord Server
&lt;/h2&gt;

&lt;p&gt;Joining takes under a minute. Go to discord.gg/zzNzd9ssG in your browser. If you do not yet have a Discord account, creating one is free and does not require email verification to start reading. Once inside, accept the server rules and post a brief intro in the introductions channel. All channels are open to every member with no paid tiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Discord Works Well for a Developer Community
&lt;/h2&gt;

&lt;p&gt;Discord was chosen over Slack and forums for practical reasons. Threaded replies keep technical discussions organized without cluttering the main channel. Search is fully open with no login required, so useful answers from months ago remain accessible. And Discord is already part of the daily routine for a large percentage of developers, which reduces friction to participate.&lt;/p&gt;

&lt;h2&gt;
  
  
  About DevToolLab
&lt;/h2&gt;

&lt;p&gt;DevToolLab is a free collection of 500+ browser-based utilities built for developers who need quick results without installing anything. The toolset covers JSON formatting, base64 encoding, URL tracking, password generation, webhook inspection, and dozens of other everyday tasks. The Discord server extends the platform into a community - a place where users and the team can actually communicate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools Worth Bookmarking Before You Join
&lt;/h2&gt;

&lt;p&gt;Several DevToolLab tools come up regularly in community help threads. The Webhook Receiver generates a live URL for inspecting HTTP payloads without writing handling code. The Pastebin tool creates shareable links with syntax highlighting for 30+ languages. The Code to Image Converter produces clean PNGs from snippets for posting in Discord. The JSON Formatter cleans up API responses before you paste them into a help request.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/devtoollab-discord-community" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/devtoollab-discord-community&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DevToolLab Discord Server invite: &lt;a href="https://discord.gg/zzNzd9ssG" rel="noopener noreferrer"&gt;https://discord.gg/zzNzd9ssG&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DevToolLab X Account: &lt;a href="https://x.com/devtoollab" rel="noopener noreferrer"&gt;https://x.com/devtoollab&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DevToolLab GitHub Account: &lt;a href="https://github.com/DevToolLab" rel="noopener noreferrer"&gt;https://github.com/DevToolLab&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DevToolLab LinkedIn Account: &lt;a href="https://www.linkedin.com/company/devtoollab" rel="noopener noreferrer"&gt;https://www.linkedin.com/company/devtoollab&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>community</category>
      <category>discord</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Claude Fable 5 Went Offline in 72 Hours: What Developers Need to Know About the US Export Control Shutdown</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Mon, 15 Jun 2026 17:14:31 +0000</pubDate>
      <link>https://dev.to/moksh/claude-fable-5-went-offline-in-72-hours-what-developers-need-to-know-about-the-us-export-control-4pcn</link>
      <guid>https://dev.to/moksh/claude-fable-5-went-offline-in-72-hours-what-developers-need-to-know-about-the-us-export-control-4pcn</guid>
      <description>&lt;p&gt;Anthropic launched Claude Fable 5 on June 9, 2026. Three days later, on June 12, a US government export control directive arrived at 5:21 p.m. ET ordering Anthropic to suspend access to the model. By that evening, every API call to &lt;code&gt;claude-fable-5&lt;/code&gt; and &lt;code&gt;claude-mythos-5&lt;/code&gt; returned an error. No grace period. No migration window. No workaround at the API layer.&lt;/p&gt;

&lt;p&gt;This is what happened, what drove it, and what you need to do if your application was running on either model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Claude Fable 5 and Mythos 5 Were
&lt;/h2&gt;

&lt;p&gt;Claude Fable 5 was Anthropic's most capable publicly available model at launch - designed for complex reasoning, code analysis, and research tasks. Claude Mythos 5 is the underlying architecture that Fable 5 is built on, minus an additional safety classifier layer. Mythos 5 was already limited to a narrower enterprise and research audience before the shutdown happened.&lt;/p&gt;

&lt;p&gt;The safety architecture in Fable 5 is worth understanding. In high-risk domains like cybersecurity and biology, an extra classifier layer monitors active sessions. If it detects potential misuse, it automatically reroutes that session to Claude Opus 4.8 instead of the full Fable 5 model. Anthropic said this fallback triggers in fewer than 5% of sessions. Mythos 5 has no equivalent fallback.&lt;/p&gt;

&lt;p&gt;Both models had been live for exactly three days when the shutdown order arrived.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Jailbreak Claim That Set Off the Chain of Events
&lt;/h2&gt;

&lt;p&gt;Shortly after Fable 5 launched, AI researcher Pliny the Liberator published a claimed jailbreak using a coordinated multi-agent strategy. The approach used multiple AI instances working in combination to probe the edges of Fable 5's classifier - something a single session could not do effectively on its own. The claim was that this technique produced output useful for developing cyberattack tooling and stack exploits.&lt;/p&gt;

&lt;p&gt;Anthropic disputed the framing publicly. In a statement covered by SecurityWeek, the company said the technique does not satisfy its definition of a true jailbreak - which requires bypassing core safeguards and delivering meaningful capability uplift toward high-risk activities, not just producing tangentially related output.&lt;/p&gt;

&lt;p&gt;Amazon's internal security team reached a different conclusion. Amazon CEO Andy Jassy - who is simultaneously Anthropic's largest investor, a board participant, and the operator of the cloud infrastructure running both models - personally escalated the findings to Treasury Secretary Scott Bessent. That escalation triggered the regulatory review that produced the June 12 directive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Export Control Directive That Forced a Global Shutdown
&lt;/h2&gt;

&lt;p&gt;The directive Anthropic received on June 12 was unusually broad in scope. It ordered suspension of access to both models for any foreign national - whether they were located inside or outside the United States. That definition extended to Anthropic's own foreign national employees working in its US offices.&lt;/p&gt;

&lt;p&gt;The compliance problem was immediate and structural. Anthropic does not have real-time nationality verification in its API pipeline. Reliably determining which of millions of active API sessions belonged to foreign nationals was not feasible within the directive's window. The only path to compliance was to shut both models down for all users, everywhere.&lt;/p&gt;

&lt;p&gt;CNBC confirmed the shutdown happened the same evening the directive arrived. Anthropic published an official statement at anthropic.com/news/fable-mythos-access, describing the situation as stemming from a "misunderstanding" and confirming it is working with the government to restore access. The company also confirmed it had not received specific technical details about the national security concerns underpinning the directive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Anthropic Models Are Still Available
&lt;/h2&gt;

&lt;p&gt;The export control directive named only &lt;code&gt;claude-fable-5&lt;/code&gt; and &lt;code&gt;claude-mythos-5&lt;/code&gt;. All other Anthropic models are unaffected and operating normally.&lt;/p&gt;

&lt;p&gt;Claude Opus 4.8, Sonnet 4.6, and Haiku 4.5 are all accepting requests. The Anthropic models overview page at platform.claude.com/docs/en/about-claude/models/overview reflects live model status and is the authoritative reference for current availability.&lt;/p&gt;

&lt;p&gt;If your production traffic runs on any model identifier other than the two named endpoints, your application is unaffected by this shutdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Migrate from Claude Fable 5 to Opus 4.8
&lt;/h2&gt;

&lt;p&gt;Any application calling &lt;code&gt;claude-fable-5&lt;/code&gt; or &lt;code&gt;claude-mythos-5&lt;/code&gt; will receive errors at the infrastructure level. Retry logic and timeout handling will not resolve this - the models are disabled on Anthropic's side, not yours.&lt;/p&gt;

&lt;p&gt;The direct replacement is &lt;code&gt;claude-opus-4-8&lt;/code&gt;. This is the same model Fable 5 itself used as its internal safety fallback for flagged sessions, which means output quality for the large majority of production use cases will be comparable.&lt;/p&gt;

&lt;p&gt;For applications where Opus 4.8 costs or latency are a concern and Fable 5 was primarily used for general reasoning - not top-end capability - &lt;code&gt;claude-sonnet-4-6&lt;/code&gt; is a viable intermediate option.&lt;/p&gt;

&lt;p&gt;If your model identifier is spread across a codebase, a targeted search will surface all locations before errors reach users:&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;# Find all claude-fable-5 and claude-mythos-5 references&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"claude-fable-5&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;claude-mythos-5"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.py"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.ts"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.env"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you store your model identifier in a config file or environment variable rather than hardcoded literals, this is a one-line change. If it is scattered across files, the grep output gives you a complete list to work through.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structural Tension This Incident Exposed
&lt;/h2&gt;

&lt;p&gt;Several things about this sequence are worth noting beyond the immediate operational impact.&lt;/p&gt;

&lt;p&gt;The compliance problem Anthropic faced is structurally difficult to solve. Export control law was designed around physical goods with trackable supply chains. Applying it to API endpoints serving millions of requests daily from users across every country - with no reliable real-time nationality signal in the authentication layer - creates an enforcement problem that "just verify users" does not cleanly solve. The mechanism Anthropic will use to restore access while remaining compliant has not been made public yet.&lt;/p&gt;

&lt;p&gt;The Amazon escalation path deserves attention. Amazon is simultaneously Anthropic's primary cloud infrastructure provider, its largest investor, a board participant, and a direct competitor in the AI model market through Amazon Bedrock. That overlap of roles puts Amazon in a position where it has responsibility for reporting security concerns, financial interest in Anthropic's performance, and a competitive stake in Anthropic's market position. The June 12 events are the first time that combination became visible as a public incident with direct operational consequences for developers.&lt;/p&gt;

&lt;p&gt;Anthropic's "misunderstanding" framing is technically defensible based on its own definitions. But the government issued the directive regardless, and the result for developers was the same: both models went dark three days after launch with no grandfathered access period.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Will Claude Fable 5 Return
&lt;/h2&gt;

&lt;p&gt;Anthropic has confirmed it is working with the government toward restoring access. The most plausible resolution paths are either Anthropic deploying nationality verification at the API layer - technically achievable but requiring engineering work and likely terms-of-service changes - or the government narrowing the directive to a scope that does not require global shutdown.&lt;/p&gt;

&lt;p&gt;Neither path is fast. Export control negotiations typically move on a timeline of weeks to months. The practical recommendation: migrate to Opus 4.8 now, treat Fable 5's return as an upgrade opportunity rather than something to block on, and monitor status.anthropic.com for live updates.&lt;/p&gt;

&lt;p&gt;For teams that specifically need Fable 5's top-end reasoning capability and cannot substitute, there is no workaround at present. The shutdown is at Anthropic's infrastructure level and cannot be circumvented with a different API key or region header.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline of Events
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;June 9, 2026&lt;/strong&gt; - Claude Fable 5 and Mythos 5 launch publicly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 12, 2026 (morning)&lt;/strong&gt; - Pliny the Liberator publishes claimed Fable 5 jailbreak&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 12, 2026 (afternoon)&lt;/strong&gt; - Amazon researchers report findings; Andy Jassy escalates to Treasury Secretary Bessent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 12, 2026 (5:21 p.m. ET)&lt;/strong&gt; - US export control directive arrives at Anthropic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 12, 2026 (evening)&lt;/strong&gt; - Anthropic disables both models globally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 13, 2026&lt;/strong&gt; - Anthropic publishes official statement; disputes jailbreak characterization&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Migrate to &lt;code&gt;claude-opus-4-8&lt;/code&gt; now. It was already Fable 5's internal fallback and covers the overwhelming majority of production use cases without significant quality degradation. If Fable 5 returns, switching back is a one-line change.&lt;/p&gt;

&lt;p&gt;Monitor status.anthropic.com for live updates, and keep an eye on the official Anthropic news page for any announcements about the path to restored access.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source article: &lt;a href="https://devtoollab.com/blog/claude-fable-5-shutdown" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/claude-fable-5-shutdown&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic official statement: &lt;a href="https://www.anthropic.com/news/fable-mythos-access" rel="noopener noreferrer"&gt;https://www.anthropic.com/news/fable-mythos-access&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic model status: &lt;a href="https://status.anthropic.com/" rel="noopener noreferrer"&gt;https://status.anthropic.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic models overview: &lt;a href="https://platform.claude.com/docs/en/about-claude/models/overview" rel="noopener noreferrer"&gt;https://platform.claude.com/docs/en/about-claude/models/overview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CNBC coverage: &lt;a href="https://www.cnbc.com/2026/06/12/anthropic-disables-access-to-fable-5-and-mythos-5-to-comply-with-government-directive.html" rel="noopener noreferrer"&gt;https://www.cnbc.com/2026/06/12/anthropic-disables-access-to-fable-5-and-mythos-5-to-comply-with-government-directive.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SecurityWeek - Anthropic disputes jailbreak: &lt;a href="https://www.securityweek.com/anthropic-disputes-fable-5-ai-jailbreak/" rel="noopener noreferrer"&gt;https://www.securityweek.com/anthropic-disputes-fable-5-ai-jailbreak/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>devops</category>
      <category>news</category>
    </item>
    <item>
      <title>Cognitive Debt: The Hidden Cost of Letting AI Write Your Code</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Sun, 14 Jun 2026 18:19:21 +0000</pubDate>
      <link>https://dev.to/moksh/cognitive-debt-the-hidden-cost-of-letting-ai-write-your-code-5d5c</link>
      <guid>https://dev.to/moksh/cognitive-debt-the-hidden-cost-of-letting-ai-write-your-code-5d5c</guid>
      <description>&lt;p&gt;In early 2026, Anthropic researchers ran an experiment with 52 junior developers. Half used an AI assistant to learn an unfamiliar Python library. The other half worked without one. Both groups finished the task. But when tested on how well they understood the code they had just written, the AI-assisted group scored 50% on a comprehension quiz - versus 67% for the unassisted group.&lt;/p&gt;

&lt;p&gt;That 17-percentage-point gap has a name: cognitive debt. It is one of the most important concepts in software engineering right now, and most developers are not paying enough attention to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Cognitive Debt?
&lt;/h2&gt;

&lt;p&gt;Cognitive debt describes the growing gap between the volume of code that exists in a system and the amount that any developer genuinely understands. It is not a new term, but it crystallized across multiple research streams in early 2026.&lt;/p&gt;

&lt;p&gt;Addy Osmani (Google Chrome) described it as "comprehension debt" - the hidden cost that accumulates when code becomes cheap to generate but understanding still requires deliberate effort. Margaret-Anne Storey (University of Victoria) formalized the concept in a March 2026 arXiv paper, framing it as a team-level problem and extending it into a Triple Debt Model: technical debt in the code, cognitive debt in the people, and intent debt - the missing rationale that both humans and AI agents need to safely work with code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cognitive Debt vs. Technical Debt
&lt;/h2&gt;

&lt;p&gt;These two ideas are easy to conflate, but they are fundamentally different problems.&lt;/p&gt;

&lt;p&gt;Technical debt lives in the code - it shows up as slow builds, tangled dependencies, and failing tests. Cognitive debt lives in people - it surfaces as an inability to explain, debug, or extend code that the team themselves wrote.&lt;/p&gt;

&lt;p&gt;The critical difference: technical debt announces itself through friction. Cognitive debt breeds false confidence. Your tests are green, velocity looks fine, and nobody realizes the system is fragile until something breaks in production and the team cannot reason through why.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Research Shows
&lt;/h2&gt;

&lt;p&gt;The Anthropic study found more than just a comprehension gap. Developers who delegated fully to AI - asking it to write code - scored below 40% on comprehension. Developers who used AI as a learning tool - asking it to explain concepts - scored above 65%, matching or beating the no-AI group. The tool is not the problem. The usage pattern is.&lt;/p&gt;

&lt;p&gt;A METR study added another dimension: experienced developers working on their own large codebases took 19% longer to complete tasks when using AI-assisted tools versus working without them. Before the study, those same developers expected AI would speed them up by 24%. After the study, they still believed it had sped them up by 20%. The confidence that AI tools produce appears to be partially disconnected from actual performance.&lt;/p&gt;

&lt;p&gt;A June 2025 MIT Media Lab study used EEG brain scans to compare LLM-assisted writing versus search-assisted writing versus unassisted writing. Brain connectivity - the measure of how actively and broadly neural networks are engaged - scaled down as tool support increased. LLM-assisted work produced the output but not the neural engagement behind it.&lt;/p&gt;

&lt;p&gt;Finally, a February 2026 study by Sankaranarayanan introduced an "Explanation Gate" - requiring developers to explain AI-generated code before integrating it. The unrestricted AI group had a 77% failure rate on a maintenance task after a 30-minute AI blackout. The Explanation Gate group had a 39% failure rate. One simple intervention cut the failure rate nearly in half.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Cognitive Debt Accumulates
&lt;/h2&gt;

&lt;p&gt;There are three core mechanisms.&lt;/p&gt;

&lt;p&gt;First, AI eliminates productive struggle. Learning science shows that difficulty during study - retrieval practice, working through confusion - is what drives long-term retention. When you paste an error message into a chat interface and receive a fix, the bug is resolved but the learning moment is bypassed.&lt;/p&gt;

&lt;p&gt;Second, there is a generation-comprehension gap. AI can produce 200 lines of working code in 30 seconds. Building a genuine mental model of those 200 lines and how they interact with the rest of your system takes considerably longer. Most developers skip that step. GitClear's analysis of 211 million changed lines found code duplication increased eightfold in 2024, while refactoring - the activity most closely linked to deep code understanding - dropped from 25% of changed lines in 2021 to under 10% in 2024.&lt;/p&gt;

&lt;p&gt;Third, automation complacency is a well-documented failure mode in aviation and nuclear power. Sustained automation use erodes the ability to catch what the automation gets wrong. A 2026 study found developers accept faulty AI reasoning 73.2% of the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Warning Signs You Are Accumulating Cognitive Debt
&lt;/h2&gt;

&lt;p&gt;Here are signals worth watching for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You cannot explain a design decision in code review without saying "the AI suggested it."&lt;/li&gt;
&lt;li&gt;Your productivity drops sharply when AI tools are unavailable due to rate limits or outages.&lt;/li&gt;
&lt;li&gt;There are files in your own codebase you mentally route around because you do not fully follow them.&lt;/li&gt;
&lt;li&gt;You paste error messages into chat without first forming a hypothesis about the cause.&lt;/li&gt;
&lt;li&gt;You can build new features easily but struggle to debug code that is six months old.&lt;/li&gt;
&lt;li&gt;You accept AI solutions without being able to articulate why they work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Counter-Arguments Are Worth Taking Seriously
&lt;/h2&gt;

&lt;p&gt;Not everyone agrees that AI-induced skill erosion is a real problem, and the pushback deserves honest engagement.&lt;/p&gt;

&lt;p&gt;Every abstraction layer in history - assembly to C, C to Python, Python to frameworks - was accused of de-skilling developers. Each one expanded the developer population and enabled new categories of software. AI may follow the same pattern.&lt;/p&gt;

&lt;p&gt;The Anthropic study also contains its own counter-argument: developers who used AI for learning scored as well as the no-AI group. The problem is passive delegation, not AI tools themselves. Used as a Socratic tutor, AI may actually accelerate skill development. And Stack Overflow's 2026 data shows 64% of developers now use AI specifically to learn - up from 37% in 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Protocol for Fighting Cognitive Debt
&lt;/h2&gt;

&lt;p&gt;The research literature and practitioner experience converge on a few high-leverage interventions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apply the Explanation Gate.&lt;/strong&gt; Before integrating any AI-generated code, explain it - why it works this way, and what would break if a specific part changed. Sankaranarayanan's study showed this cuts maintenance failure rates dramatically with no measurable impact on initial productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt problems before consulting AI.&lt;/strong&gt; Spend 15 to 30 minutes working through a problem independently. The wrong hypotheses and partial approaches during that time are where schema formation happens. When you then consult AI, you arrive with a framework for evaluating its answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask "why?" more than "write this."&lt;/strong&gt; Prompts like "explain the time complexity of this approach" or "what are the tradeoffs between these two implementations" build understanding. The code AI produces is the output. The understanding you build by interrogating it is the asset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schedule no-AI days.&lt;/strong&gt; Reserve one day per week for unassisted work. The goal is calibration - measuring your actual skill level and identifying the gaps that have opened since last time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design before you generate.&lt;/strong&gt; Use AI to reason through architecture and tradeoffs before writing any code. Then generate implementation to fill in a design you already understand, rather than receiving a design embedded in code you do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Audit Your Team's Cognitive Debt
&lt;/h2&gt;

&lt;p&gt;Run this exercise quarterly. No special tooling required.&lt;/p&gt;

&lt;p&gt;Ask three engineers to independently whiteboard the architecture of a shared system. Where the diagrams diverge is where cognitive debt has accumulated.&lt;/p&gt;

&lt;p&gt;Pick a 50-line function that was AI-generated and committed more than two weeks ago. Ask the author to explain it without looking at it. Track whether they can recover the reasoning.&lt;/p&gt;

&lt;p&gt;Give an engineer a bug report for AI-generated code they have not touched before, with AI access removed, for 20 minutes. Observe whether they form and test hypotheses independently or stall immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Industry Picture
&lt;/h2&gt;

&lt;p&gt;The individual-level concern is real, but the structural concern may be larger. A codebase where developers have deep understanding is fundamentally different from one where working code was generated and accepted without thorough review. The first can be extended and maintained. The second accumulates brittleness that is only visible during incidents, migrations, and onboarding.&lt;/p&gt;

&lt;p&gt;Stack Overflow's 2025 survey (49,009 respondents across 166 countries) found developer trust in AI accuracy fell from 40% to 29% year-over-year, and overall favorability dropped from 72% to 60%, even as adoption continued rising. Developers are noticing something. Cognitive debt is part of what they are noticing.&lt;/p&gt;

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

&lt;p&gt;AI coding tools offer genuine productivity gains. They also carry a measurable comprehension cost that does not appear in standard metrics until it becomes a crisis. The gap between those two outcomes comes down to one thing: whether you keep the explanation work as your own responsibility.&lt;/p&gt;

&lt;p&gt;Code you can generate but not explain is a liability shaped like an asset. Attempt before delegating. Explain before integrating. Ask "why?" more than "write this." The source code is what the AI produces. The mental model is what only you can build.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source article: &lt;a href="https://devtoollab.com/blog/cognitive-debt-ai-coding" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/cognitive-debt-ai-coding&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic study (Shen and Tamkin, 2026): &lt;a href="https://arxiv.org/abs/2601.20245" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2601.20245&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Margaret-Anne Storey cognitive debt paper: &lt;a href="https://arxiv.org/abs/2603.22106" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2603.22106&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Addy Osmani on comprehension debt: &lt;a href="https://addyosmani.com/blog/comprehension-debt/" rel="noopener noreferrer"&gt;https://addyosmani.com/blog/comprehension-debt/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MIT Media Lab brain study: &lt;a href="https://www.media.mit.edu/publications/your-brain-on-chatgpt/" rel="noopener noreferrer"&gt;https://www.media.mit.edu/publications/your-brain-on-chatgpt/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitClear code quality analysis: &lt;a href="https://www.gitclear.com/ai_assistant_code_quality_2025_research" rel="noopener noreferrer"&gt;https://www.gitclear.com/ai_assistant_code_quality_2025_research&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stack Overflow Developer Survey 2025: &lt;a href="https://survey.stackoverflow.co/2025" rel="noopener noreferrer"&gt;https://survey.stackoverflow.co/2025&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>career</category>
    </item>
    <item>
      <title>WebAssembly in 2026: A Practical Guide to Wasm and WASI for Modern Developers</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Sun, 14 Jun 2026 18:01:05 +0000</pubDate>
      <link>https://dev.to/moksh/webassembly-in-2026-a-practical-guide-to-wasm-and-wasi-for-modern-developers-3ogm</link>
      <guid>https://dev.to/moksh/webassembly-in-2026-a-practical-guide-to-wasm-and-wasi-for-modern-developers-3ogm</guid>
      <description>&lt;p&gt;Google Sheets recalculates cells twice as fast after shifting its compute engine to WebAssembly. Figma cut initial load time by 3x. Shopify executes custom checkout rules compiled from Rust to Wasm at the CDN edge - all in production, serving millions of users every day.&lt;/p&gt;

&lt;p&gt;WebAssembly started as a way to bring C++ into the browser. Today, in 2026, it is a universal binary runtime that executes in browsers, edge networks, serverless functions, and even AI inference pipelines. The W3C ratified Wasm 3.0 as a formal standard in September 2025, and WASI Preview 2 is now stable. The tooling has matured to a point where it genuinely works.&lt;/p&gt;

&lt;p&gt;Over 70% of developers now evaluate or actively use Wasm outside the browser, per CNCF survey data. This is no longer a niche experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is WebAssembly?
&lt;/h2&gt;

&lt;p&gt;WebAssembly is a compact binary instruction format designed to run inside a sandboxed virtual machine. That VM is embedded in every major browser, in standalone runtimes like Wasmtime, and on cloud platforms from Cloudflare Workers to AWS Lambda.&lt;/p&gt;

&lt;p&gt;Key properties that make it worth your attention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Near-native speed&lt;/strong&gt; - Wasm runs at 80-95% of native performance with no JIT warmup delay. The binary arrives structured for fast parsing, unlike JavaScript that must be parsed and compiled on every load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language-agnostic&lt;/strong&gt; - Rust, C/C++, Go, Python, AssemblyScript, C#, and Kotlin all compile to Wasm. Your team picks the language; the runtime only sees the &lt;code&gt;.wasm&lt;/code&gt; binary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic execution&lt;/strong&gt; - The same Wasm binary produces identical results whether it runs in Chrome, Firefox, Node.js, or Wasmtime on ARM or x86.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxed by default&lt;/strong&gt; - A Wasm module starts with zero permissions. No filesystem, no network, no system calls unless the host runtime explicitly grants them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wasm does not replace JavaScript. It runs alongside it. JavaScript manages the DOM, events, and most application logic. Wasm handles the parts where JavaScript hits a performance ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Look at Wasm's History (2017-2026)
&lt;/h2&gt;

&lt;p&gt;Wasm shipped in all four major browsers in 2017 as a W3C MVP. In 2019 it became an official W3C standard. The real transformation happened in 2020 with the announcement of WASI, which let Wasm escape the browser entirely.&lt;/p&gt;

&lt;p&gt;WasmGC shipped in all major browsers in 2024, enabling garbage-collected languages like Kotlin and Java to target Wasm without bringing their own GC. In September 2025, Wasm 3.0 was ratified. February 2026 brought WASI 0.3.0 with native async I/O using futures and streams - the last major gap between Wasm and conventional server runtimes.&lt;/p&gt;

&lt;p&gt;The ecosystem feels like it "arrived" recently because WASI and the Component Model are what turned Wasm from a browser trick into a production server runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Adoption: Companies Already Running Wasm in Production
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Figma&lt;/strong&gt; compiled its C++ rendering engine to Wasm using Emscripten. The result: 3x faster load times across all document sizes. This was a full production rewrite, not a proof of concept.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Sheets&lt;/strong&gt; migrated its calculation engine to WasmGC, achieving 2x faster recalculation. Sheets with millions of cells now finish in under a second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shopify Functions&lt;/strong&gt; allows merchants to write custom discount and checkout logic in Rust, compiled to Wasm, executed at the edge inside a strict 10ms time budget. No Node.js function could reliably meet that ceiling at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adobe Premiere Rush&lt;/strong&gt; brings video editing to the browser via Wasm-compiled media pipelines. No plugin, no Electron wrapper - just a browser tab running compute-heavy codecs at near-native speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  WASI: Taking Wasm Beyond the Browser
&lt;/h2&gt;

&lt;p&gt;A plain Wasm module running in the browser has no access to the filesystem, sockets, clocks, or any OS resource. That works fine for in-browser computation but rules out server applications and CLI tools.&lt;/p&gt;

&lt;p&gt;WASI - the WebAssembly System Interface - solves this. It is a standardized interface that gives Wasm modules portable, capability-gated access to OS resources. A Wasm binary compiled for WASI runs on any WASI-compatible runtime without modification.&lt;/p&gt;

&lt;p&gt;As Solomon Hykes, co-founder of Docker, noted: if WASM+WASI had existed in 2008, Docker might not have needed to be invented. The portability problem that containers solve is the same one WASI addresses - with a much smaller footprint.&lt;/p&gt;

&lt;p&gt;WASI 0.3.0 (February 2026) added native async I/O with futures and streams, making Wasm viable for real-world servers handling concurrent connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Component Model: Polyglot Composition Without Glue Code
&lt;/h2&gt;

&lt;p&gt;Before the Component Model, connecting a Rust module to a Python module meant manually writing type conversion glue across the memory boundary. In practice, cross-language Wasm compositions were too painful to maintain.&lt;/p&gt;

&lt;p&gt;The Component Model introduces WIT (WebAssembly Interface Types) - a language-neutral interface definition format. A Rust library and a Python library, both compiled to Wasm components, can call each other's functions through automatically generated, type-safe bindings. No manual marshaling.&lt;/p&gt;

&lt;p&gt;This enables genuinely polyglot applications. Your crypto library can be Rust, your data pipeline can be Python, your business logic can be TypeScript via Javy - all composing without a translation penalty. Tools like &lt;code&gt;cargo-component&lt;/code&gt; and &lt;code&gt;wit-bindgen&lt;/code&gt; ship today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge Computing: Why Wasm Wins on Cold Start
&lt;/h2&gt;

&lt;p&gt;The performance advantage of Wasm amplifies at the edge, where cold start latency is often the binding constraint.&lt;/p&gt;

&lt;p&gt;A container starts in 50-500ms. A Node.js Lambda in 200-400ms. A Wasm module in 1-5ms. Memory per instance is roughly 1MB for Wasm vs 10MB+ for a container. At the scale of thousands of isolated tenant functions, that gap is enormous.&lt;/p&gt;

&lt;p&gt;Cloudflare Workers, Fastly Compute@Edge, and Vercel Edge Functions all support Wasm today. AWS Lambda Wasm functions show 10-40x improvement in cold-start times compared to container equivalents.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebAssembly vs JavaScript: A Practical Decision Framework
&lt;/h2&gt;

&lt;p&gt;The choice is not ideological. It is about where JavaScript's speed ceiling becomes your bottleneck.&lt;/p&gt;

&lt;p&gt;Use Wasm for: image and video processing, cryptographic operations, AI/ML inference in the browser, physics simulations, game engines, and serverless functions with strict latency budgets.&lt;/p&gt;

&lt;p&gt;Use JavaScript for: DOM manipulation, event handling, JSON parsing, calling APIs, server-side rendering, and standard CRUD application logic.&lt;/p&gt;

&lt;p&gt;In practice, 5-10% of an application's code - the hot paths - benefits from Wasm. The rest should stay in JavaScript where the ecosystem, tooling, and debugging experience are stronger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building with Wasm: Three Practical Starting Points
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rust to Wasm - The Production Path
&lt;/h3&gt;

&lt;p&gt;Rust is the dominant choice for production Wasm. It has no garbage collector (no GC pauses inside your module), zero-overhead abstractions, and the &lt;code&gt;wasm-pack&lt;/code&gt; toolchain that handles compilation, JS bindings, and npm packaging in one command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown
cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-pack
cargo new &lt;span class="nt"&gt;--lib&lt;/span&gt; wasm-hello &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;wasm-hello
wasm-pack build &lt;span class="nt"&gt;--target&lt;/span&gt; web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output &lt;code&gt;pkg/&lt;/code&gt; directory contains the &lt;code&gt;.wasm&lt;/code&gt; binary, a JS glue file, and TypeScript types ready for import.&lt;/p&gt;

&lt;h3&gt;
  
  
  AssemblyScript - For TypeScript Developers
&lt;/h3&gt;

&lt;p&gt;AssemblyScript is a TypeScript-like language that compiles directly to Wasm. If your team is JavaScript-native and wants Wasm without learning a new paradigm, this is the lowest-friction entry point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; assemblyscript
npx asinit &lt;span class="nb"&gt;.&lt;/span&gt;
npm run asbuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Performance and safety guarantees are lower than Rust, but for moving a hot path out of JavaScript quickly, AssemblyScript is a solid pragmatic choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Side Wasm with Wasmtime
&lt;/h3&gt;

&lt;p&gt;For running Wasm outside the browser with WASI, Wasmtime is the reference runtime from the Bytecode Alliance. It implements WASI Preview 2 and the Component Model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://wasmtime.dev/install.sh &lt;span class="nt"&gt;-sSf&lt;/span&gt; | bash
rustup target add wasm32-wasip2
cargo build &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasip2 &lt;span class="nt"&gt;--release&lt;/span&gt;
wasmtime target/wasm32-wasip2/release/my-app.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same &lt;code&gt;.wasm&lt;/code&gt; binary you run locally in Wasmtime runs unchanged on Cloudflare Workers or AWS Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language Support in 2026
&lt;/h2&gt;

&lt;p&gt;Rust and C/C++ are production-ready. AssemblyScript and Go (via TinyGo) are stable. C# via Blazor is production-ready and used by 43% of .NET developers. Kotlin/Wasm is stable with WasmGC. Python (via Pyodide) is emerging for browser-based data science. JavaScript/TypeScript via Javy is experimental but useful for embedding a tiny JS runtime inside Wasm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations Developers Should Know About
&lt;/h2&gt;

&lt;p&gt;Most Wasm guides skip over these. Here is an honest look at the gaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No native multithreading in WASI.&lt;/strong&gt; Server-side Wasm still lacks a proper parallel compute model. High-throughput servers and CPU-parallel workloads are not good Wasm candidates today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory overhead at scale.&lt;/strong&gt; Each Wasm instance has a minimum memory footprint. Running thousands of isolated modules can use more total memory than shared-runtime alternatives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOM access requires a JS bridge.&lt;/strong&gt; In the browser, Wasm cannot touch the DOM directly. All DOM operations go through JavaScript. If DOM manipulation is your bottleneck, Wasm will not help.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging experience lags.&lt;/strong&gt; Production builds strip debug info. Source maps exist but lag behind native tooling. The practical workaround is running logic through &lt;code&gt;cargo test&lt;/code&gt; in a native binary for unit testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large module startup cost.&lt;/strong&gt; Very large Wasm binaries - ML models, for example - have real instantiation overhead. Streaming compilation and &lt;code&gt;wasm-opt&lt;/code&gt; mitigate this, but it is not zero.&lt;/p&gt;

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

&lt;p&gt;WebAssembly in 2026 is a mature production runtime, not a browser experiment. Wasm 3.0 is a W3C standard. WASI Preview 2 is stable. Figma, Google, Shopify, and Adobe run it at scale.&lt;/p&gt;

&lt;p&gt;Most developers do not need to rewrite anything today. The practical approach: identify the 5-10% of your codebase that is genuinely performance-constrained, and evaluate whether Wasm closes that gap. Start with &lt;code&gt;wasm-pack&lt;/code&gt; and the Rust quickstart - the round-trip from source to a callable JavaScript function is shorter than you expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/webassembly-guide" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/webassembly-guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wasmtime runtime: &lt;a href="https://wasmtime.dev/" rel="noopener noreferrer"&gt;https://wasmtime.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;wasm-pack documentation: &lt;a href="https://rustwasm.github.io/docs/wasm-pack/" rel="noopener noreferrer"&gt;https://rustwasm.github.io/docs/wasm-pack/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AssemblyScript: &lt;a href="https://www.assemblyscript.org/" rel="noopener noreferrer"&gt;https://www.assemblyscript.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Bytecode Alliance (WASI/Component Model): &lt;a href="https://bytecodealliance.org/" rel="noopener noreferrer"&gt;https://bytecodealliance.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare Workers Wasm support: &lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;https://workers.cloudflare.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Shopify Functions: &lt;a href="https://shopify.dev/docs/apps/functions" rel="noopener noreferrer"&gt;https://shopify.dev/docs/apps/functions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webassembly</category>
      <category>rust</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>JSONata Explained: Query and Transform JSON Without the Boilerplate</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Sat, 13 Jun 2026 18:26:41 +0000</pubDate>
      <link>https://dev.to/moksh/jsonata-explained-query-and-transform-json-without-the-boilerplate-50g8</link>
      <guid>https://dev.to/moksh/jsonata-explained-query-and-transform-json-without-the-boilerplate-50g8</guid>
      <description>&lt;p&gt;Working with complex JSON payloads can quickly become a nightmare. You end up chaining &lt;code&gt;.map()&lt;/code&gt;, &lt;code&gt;.filter()&lt;/code&gt;, and &lt;code&gt;.reduce()&lt;/code&gt; calls across multiple lines just to pull out a few nested values. Add optional chaining to avoid crashes and the code becomes nearly unreadable.&lt;/p&gt;

&lt;p&gt;There is a cleaner way - &lt;strong&gt;JSONata&lt;/strong&gt;. It is a compact, purpose-built query and transformation language for JSON data. Think of it as XPath for XML, but designed from the ground up to work with JSON objects and arrays.&lt;/p&gt;

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

&lt;p&gt;JSONata is an open-source project originally created by Andrew Coleman at IBM. It gives developers a declarative syntax to extract and reshape JSON data without writing procedural JavaScript loops. Where vanilla JS might take 15 lines, a JSONata expression often takes one.&lt;/p&gt;

&lt;p&gt;It is available as an npm package and integrates naturally into Node.js and TypeScript projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Path Navigation
&lt;/h2&gt;

&lt;p&gt;The foundation of JSONata is its dot-notation path traversal. Given a nested JSON object, you simply trace the path to the value you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer.address.city
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the city value without any need for null checks or defensive coding. JSONata handles missing properties gracefully by returning &lt;code&gt;undefined&lt;/code&gt; rather than throwing errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Array Mapping
&lt;/h2&gt;

&lt;p&gt;When JSONata encounters an array during path traversal, it automatically maps across all items. There is no need to write an explicit &lt;code&gt;.map()&lt;/code&gt; call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer.orders.product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns an array of all product names from every order in one clean expression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inline Filtering
&lt;/h2&gt;

&lt;p&gt;You can filter arrays directly using bracket notation with a condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer.orders[price &amp;gt; 1000].product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns only the products from orders where the price exceeds 1000. No &lt;code&gt;.filter()&lt;/code&gt; callback required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in Aggregation Functions
&lt;/h2&gt;

&lt;p&gt;JSONata ships with a solid set of built-in functions for math, strings, and arrays. Aggregating a set of values is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$sum(customer.orders.price)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other useful functions include &lt;code&gt;$count()&lt;/code&gt;, &lt;code&gt;$average()&lt;/code&gt;, &lt;code&gt;$string()&lt;/code&gt;, &lt;code&gt;$round()&lt;/code&gt;, and many more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restructuring JSON Output
&lt;/h2&gt;

&lt;p&gt;One of JSONata's most powerful features is the ability to declare an entirely new output shape. You define the target structure and map source values into it:&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;"customerName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;customer.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalSpent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$sum(customer.orders.price)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$count(customer.orders)&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;This is especially useful in Backend-For-Frontend (BFF) patterns where you need to slim down a bloated API response before it reaches the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSONata vs Vanilla JavaScript
&lt;/h2&gt;

&lt;p&gt;JavaScript can do everything JSONata does - but at a cost. JSONata wins on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conciseness&lt;/strong&gt;: Data reshaping expressions shrink dramatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative style&lt;/strong&gt;: You describe the output shape, not the iteration steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety&lt;/strong&gt;: JSONata expressions are sandboxed, making them safer to expose to non-engineers for custom data extraction.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  JSONata vs jq
&lt;/h2&gt;

&lt;p&gt;If you live in the terminal, &lt;code&gt;jq&lt;/code&gt; is a great tool. But for Node.js and React applications where you need embedded transformation logic, JSONata offers syntax that feels more natural to JavaScript developers and integrates directly into application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started in Node.js
&lt;/h2&gt;

&lt;p&gt;Installing JSONata takes one command:&lt;br&gt;
&lt;/p&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;jsonata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a basic usage example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsonata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;orders&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Laptop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jsonata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.orders[price &amp;gt; 1000]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Use Case - API Response Formatting
&lt;/h2&gt;

&lt;p&gt;A common pattern is using JSONata in a BFF layer to strip unnecessary fields from a third-party API response before forwarding it to the frontend. Instead of writing custom mapping functions for every endpoint, you define a single JSONata expression that reshapes the response declaratively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Case - Sales Data Aggregation
&lt;/h2&gt;

&lt;p&gt;JSONata handles grouped aggregations well. You can compute totals and averages across nested transaction arrays using &lt;code&gt;$sum()&lt;/code&gt; and &lt;code&gt;$average()&lt;/code&gt; in a single expression, without writing complex &lt;code&gt;reduce()&lt;/code&gt; logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Case - Dynamic Config Parsing
&lt;/h2&gt;

&lt;p&gt;JSONata can dynamically resolve environment-specific values from a shared configuration object. By injecting the environment name into the expression, you get clean, environment-specific output without branching logic in your application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Feature - Custom Functions
&lt;/h2&gt;

&lt;p&gt;JSONata lets you define lambda functions directly inside your expression payload. This is useful for tasks like formatting currency values or applying custom business logic to each item in an array, all within a single self-contained expression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Feature - Conditional Fallbacks
&lt;/h2&gt;

&lt;p&gt;Ternary expressions in JSONata make it easy to handle missing or unexpected values from external APIs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zipCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zipCode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No ZIP code provided&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps your transformation logic clean and avoids runtime failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Feature - Recursive Processing
&lt;/h2&gt;

&lt;p&gt;JSONata supports recursive self-calling functions, which makes it possible to traverse and process tree structures like file directory hierarchies of arbitrary depth - without any imperative loop logic.&lt;/p&gt;

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

&lt;p&gt;If your work involves API integration, data pipelines, or payload manipulation, JSONata is a tool worth adding to your toolkit. It replaces verbose imperative JavaScript with concise, readable, declarative expressions that are easier to maintain and reason about.&lt;/p&gt;

&lt;p&gt;Start with simple path expressions and work up to custom functions and recursive transforms - the learning curve is gentle and the payoff is significant.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/jsonata-intro" rel="noopener noreferrer"&gt;What Is JSONata? Learn How to Query JSON Like a Pro - DevToolLab&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JSONata official site: &lt;a href="https://jsonata.org" rel="noopener noreferrer"&gt;https://jsonata.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JSONata npm package: &lt;a href="https://www.npmjs.com/package/jsonata" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/jsonata&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JSONata documentation: &lt;a href="https://docs.jsonata.org" rel="noopener noreferrer"&gt;https://docs.jsonata.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>json</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Top Ngrok Alternatives for Developers in 2026 - Free, Open-Source, and Self-Hosted</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Sat, 13 Jun 2026 17:42:46 +0000</pubDate>
      <link>https://dev.to/moksh/top-ngrok-alternatives-for-developers-in-2026-free-open-source-and-self-hosted-1fjc</link>
      <guid>https://dev.to/moksh/top-ngrok-alternatives-for-developers-in-2026-free-open-source-and-self-hosted-1fjc</guid>
      <description>&lt;p&gt;If you're building APIs, testing webhooks, or working with third-party integrations, you've probably needed to expose a local server to the internet. Ngrok has long been the go-to tool for this, but rising costs, session limits, and the demand for self-hosted control have pushed many developers to seek out alternatives. This guide covers the most reliable Ngrok replacements available in 2026 - from managed services with free tiers to fully self-hosted open-source tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Look for in a Tunneling Tool
&lt;/h2&gt;

&lt;p&gt;Not all tunneling tools are created equal. Before picking one, consider these key criteria: connection reliability, latency and throughput, support for protocols beyond HTTP (TCP, UDP, WebSocket), custom domain support, request inspection capabilities, and whether a self-hosted or open-source option is available. Your specific workflow and infrastructure constraints should drive the final decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Comparison: Ngrok Alternatives at a Glance
&lt;/h2&gt;

&lt;p&gt;Here is a summary of how the main alternatives stack up across important features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pinggy&lt;/strong&gt; - No install, SSH-based, free tier, custom domains, TCP + WebSocket support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; - Enterprise-grade security, no bandwidth limits on free plan, Cloudflare ecosystem integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;inlets&lt;/strong&gt; - Open core, Kubernetes-friendly, self-hostable, automatic SSL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagekite&lt;/strong&gt; - Mature and simple, flexible pricing, self-hosted option&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tunnelto&lt;/strong&gt; - Team-focused, real-time inspector, API integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serveo&lt;/strong&gt; - Zero install via SSH, quick and free, limited features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localtunnel&lt;/strong&gt; - Fully open-source, easy CLI, WebSocket support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;frp&lt;/strong&gt; - Feature-rich, multi-protocol, load balancing, dashboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expose&lt;/strong&gt; - Open-source, Laravel-friendly, real-time dashboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telebit&lt;/strong&gt; - AGPL-licensed, auto HTTPS, SSH tunnels, self-hostable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bore&lt;/strong&gt; - Rust-based, minimal, very fast, self-hosted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sish&lt;/strong&gt; - SSH authentication, HTTP/TCP tunneling, custom domains&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Commercial Services with Free Tiers
&lt;/h2&gt;

&lt;p&gt;These managed tools handle infrastructure for you and offer free plans suitable for most development use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Pinggy
&lt;/h3&gt;

&lt;p&gt;Pinggy is a strong Ngrok competitor that works entirely over SSH - no client installation needed. It offers a generous free tier with HTTPS, TCP, and WebSocket tunneling, plus an in-browser request inspector with filtering. Custom domains and end-to-end encryption round out a well-designed package. Ideal for developers who want a zero-setup workflow with solid debugging tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cloudflare Tunnel
&lt;/h3&gt;

&lt;p&gt;Formerly known as Argo Tunnel, Cloudflare Tunnel integrates deeply with the Cloudflare platform - giving you DDoS protection, automatic SSL, built-in analytics, and a zero-trust security model. The free plan has no bandwidth cap, making it a compelling option for developers already using Cloudflare for DNS or CDN. Best suited for production-grade or security-sensitive environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. inlets
&lt;/h3&gt;

&lt;p&gt;inlets offers an open-source core under the MIT license alongside a commercial Pro version for advanced use cases. It supports HTTP and TCP tunneling, integrates with Kubernetes, and handles automatic SSL via Let's Encrypt. It works well even behind restrictive firewalls and can be self-hosted. A natural fit for cloud-native teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Pagekite
&lt;/h3&gt;

&lt;p&gt;Pagekite is one of the older tunneling solutions, and its maturity shows. Configuration is straightforward, HTTPS is supported, and it follows an open-core model that allows self-hosting. The pay-what-you-want pricing is a nice touch for indie developers and small teams who need a predictable, no-surprises tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Tunnelto
&lt;/h3&gt;

&lt;p&gt;Tunnelto targets developer teams with features like real-time traffic inspection, custom subdomains, TCP tunneling, and built-in API integration. Collaboration features make it more useful in multi-developer environments where multiple people need visibility into request flows.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Serveo
&lt;/h3&gt;

&lt;p&gt;Serveo has a narrow but useful appeal: it requires absolutely no client installation and operates purely over SSH. It supports HTTP and TCP forwarding with custom subdomain options. Limitations apply on the free tier, but for a quick one-off tunnel session, it is hard to beat for simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open-Source and Self-Hosted Options
&lt;/h2&gt;

&lt;p&gt;If you need full control over your infrastructure, these fully open-source tools are worth considering.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Localtunnel
&lt;/h3&gt;

&lt;p&gt;Localtunnel is a lightweight, MIT-licensed option with a simple CLI and WebSocket support. It can be self-hosted and is quick to set up:&lt;br&gt;
&lt;/p&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; localtunnel
lt &lt;span class="nt"&gt;--port&lt;/span&gt; 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A solid pick for developers who want a no-frills, open-source tunneling solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. frp - Fast Reverse Proxy
&lt;/h3&gt;

&lt;p&gt;frp (Apache-2.0) is a powerful self-hosted proxy that supports TCP, UDP, HTTP, HTTPS, and WebSocket. It includes load balancing, custom domains, encryption, a monitoring dashboard, and a plugin system. If you need advanced networking control, frp is likely the most feature-complete open-source option on this list.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Expose
&lt;/h3&gt;

&lt;p&gt;Expose is a PHP/Laravel-friendly open-source tool (MIT) that also works with any web framework. It supports custom subdomains, request logging and replay, and a real-time dashboard. Self-hosting is Docker-based and well-documented. A good fit for PHP developers who want observability built in.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Telebit
&lt;/h3&gt;

&lt;p&gt;Telebit (AGPL-3.0) is a security-oriented tunneling tool with automatic HTTPS via Let's Encrypt, SSH tunnel support, and custom domain handling. It has a clean CLI and can be self-hosted. Well-suited for developers who prioritize encrypted tunnels and straightforward configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Bore
&lt;/h3&gt;

&lt;p&gt;Bore is a minimal, Rust-powered tunneling tool designed for speed and simplicity. Setting up a server and connecting a client takes just two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;bore-cli
bore server
&lt;span class="c"&gt;# client side:&lt;/span&gt;
bore &lt;span class="nb"&gt;local &lt;/span&gt;8000 &lt;span class="nt"&gt;--to&lt;/span&gt; your-bore-server:7835
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its MIT license and low resource footprint make it ideal for self-hosted setups where performance matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  12. sish
&lt;/h3&gt;

&lt;p&gt;sish is an SSH-based tunneling server that supports HTTP, HTTPS, and TCP with custom domain routing and WebSocket traffic. Authentication is handled entirely over SSH, keeping the setup simple and secure. A clean choice for developers comfortable in the terminal who want self-hosted tunneling.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Pick the Right Tool for Your Setup
&lt;/h2&gt;

&lt;p&gt;Choosing the right Ngrok alternative depends on a few practical questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do you want managed hosting or full self-hosted control?&lt;/li&gt;
&lt;li&gt;What protocols do you need beyond HTTP?&lt;/li&gt;
&lt;li&gt;How much traffic will you be routing?&lt;/li&gt;
&lt;li&gt;Do you have existing infrastructure (Cloudflare, Kubernetes, Docker) to integrate with?&lt;/li&gt;
&lt;li&gt;What is your budget - free tier, subscription, or one-time self-host cost?&lt;/li&gt;
&lt;li&gt;How important is request inspection and debugging?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answering these will quickly narrow the list to two or three strong candidates.&lt;/p&gt;

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

&lt;p&gt;In 2026, developers have a wider choice of tunneling tools than ever. For a managed, zero-config experience, Pinggy and Cloudflare Tunnel lead the pack. For open-source flexibility, frp and sish are the most capable self-hosted options. And for lightweight use cases where you want something minimal and fast, Bore and Localtunnel are reliable standbys.&lt;/p&gt;

&lt;p&gt;The right tool ultimately depends on your project requirements, infrastructure preferences, and how much management overhead you are willing to accept.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/best-ngrok-alternatives" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/best-ngrok-alternatives&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ngrok official site: &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;https://ngrok.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pinggy: &lt;a href="https://pinggy.io/" rel="noopener noreferrer"&gt;https://pinggy.io/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare Tunnel: &lt;a href="https://www.cloudflare.com/products/tunnel/" rel="noopener noreferrer"&gt;https://www.cloudflare.com/products/tunnel/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;inlets: &lt;a href="https://inlets.dev/" rel="noopener noreferrer"&gt;https://inlets.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pagekite: &lt;a href="https://pagekite.net/" rel="noopener noreferrer"&gt;https://pagekite.net/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Localtunnel on GitHub: &lt;a href="https://github.com/localtunnel/localtunnel" rel="noopener noreferrer"&gt;https://github.com/localtunnel/localtunnel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;frp on GitHub: &lt;a href="https://github.com/fatedier/frp" rel="noopener noreferrer"&gt;https://github.com/fatedier/frp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Expose on GitHub: &lt;a href="https://github.com/beyondcode/expose" rel="noopener noreferrer"&gt;https://github.com/beyondcode/expose&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Bore on GitHub: &lt;a href="https://github.com/ekzhang/bore" rel="noopener noreferrer"&gt;https://github.com/ekzhang/bore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;sish on GitHub: &lt;a href="https://github.com/antoniomika/sish" rel="noopener noreferrer"&gt;https://github.com/antoniomika/sish&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>devtools</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Understanding XML Structure: A Practical Guide for Developers</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Fri, 12 Jun 2026 18:49:14 +0000</pubDate>
      <link>https://dev.to/moksh/understanding-xml-structure-a-practical-guide-for-developers-35gm</link>
      <guid>https://dev.to/moksh/understanding-xml-structure-a-practical-guide-for-developers-35gm</guid>
      <description>&lt;p&gt;JSON and GraphQL dominate modern web development, but XML (eXtensible Markup Language) is far from obsolete. Enterprise integrations, legacy systems, healthcare standards, and financial protocols still rely heavily on XML. If you work across diverse stacks, understanding XML is a skill that pays dividends.&lt;/p&gt;

&lt;p&gt;This guide covers the core syntax, validation techniques, parsing approaches, and best practices - with code you can put to work right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why XML Still Matters in 2026
&lt;/h2&gt;

&lt;p&gt;XML has been around since 1996 and continues to thrive in specific domains. It handles deeply nested hierarchical data well, supports robust native schema validation, and manages mixed document-oriented content better than most alternatives. If you're dealing with SOAP APIs, Android layouts, SVG, DOCX/XLSX files, HL7 healthcare records, or FIX financial protocols, you're already in XML territory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Building Blocks of an XML Document
&lt;/h2&gt;

&lt;p&gt;At its core, XML is a tree of nodes serialized as text. Every well-formed document starts with a declaration that tells the parser the version and character encoding - UTF-8 is the standard choice. From there, the document is composed of nested elements, attributes, and optionally text content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elements - The Tree Nodes
&lt;/h2&gt;

&lt;p&gt;Elements are the primary structural unit in XML. They wrap your data in opening and closing tags. XML is case-sensitive, so a tag and a tag are treated as two completely different elements. Every opened element must have a corresponding closing tag to keep the document well-formed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attributes - Metadata on Elements
&lt;/h2&gt;

&lt;p&gt;Attributes sit inside an opening tag and carry metadata about the element rather than the primary data itself. A good rule of thumb: use attributes for identifiers, types, or units (like currency), and use child elements for the actual payload data. This separation keeps your parsers predictable and your document structure clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Closing Elements
&lt;/h2&gt;

&lt;p&gt;When an element has no content or child nodes, you can collapse the open and close tag into a single self-closing form. This reduces verbosity without sacrificing clarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  CDATA Sections - Handling Special Characters
&lt;/h2&gt;

&lt;p&gt;When you need to embed raw content - such as HTML snippets, JSON blobs, or code fragments - inside an XML node, CDATA sections let you do it safely. Everything between the CDATA delimiters is treated as raw text by the parser, so characters like &amp;lt; and &amp;amp; don't need escaping and won't break the document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Validation - A Real Advantage Over JSON
&lt;/h2&gt;

&lt;p&gt;One of XML's strongest selling points is first-class schema validation without external libraries. DTDs (Document Type Definitions) are the older approach, letting you declare which elements and attributes are valid in a document. XSD (XML Schema Definition) is the modern standard - more verbose, but it supports proper data types including strings, dates, integers, and regex patterns. Enterprise systems use XSDs to validate incoming payloads before they ever reach application logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Namespaces - Avoiding Naming Conflicts
&lt;/h2&gt;

&lt;p&gt;When combining XML outputs from multiple sources or APIs, element name collisions become a real problem. Namespaces solve this by associating elements with a unique URI prefix. Each element belongs to a specific namespace context, so two different info elements from two different schemas can coexist in the same document without conflict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing XML in Practice
&lt;/h2&gt;

&lt;p&gt;Parsing XML as raw strings is a recipe for bugs. Use proper parser APIs instead. In browser and Node.js environments, the DOMParser API converts XML text into a queryable document object. You can then use querySelector-style methods to locate specific elements and extract their content or attributes. For more complex querying, XPath is the right tool - it lets you write powerful expressions to target elements by structure, attribute values, or content, similar to how CSS selectors and SQL work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for XML Development
&lt;/h2&gt;

&lt;p&gt;Keep element names descriptive and consistent - pick camelCase or kebab-case and stick to it throughout the document. Structure nesting to mirror the real-world relationships in your data. On the security side, always disable external entity processing to block XXE (XML External Entity) injection attacks. Validate all input against a strict schema, set resource limits on parser memory consumption, and sanitize any user-supplied data before it gets written into XML output.&lt;/p&gt;

&lt;h2&gt;
  
  
  XML vs. JSON - When to Choose What
&lt;/h2&gt;

&lt;p&gt;JSON wins for web API responses and lightweight configuration. XML wins when you need native schema validation, namespace support, mixed content (text and markup together), or when integrating with enterprise systems and industry-standard protocols. If the system on the other end speaks XML, XML is the right choice - no abstraction layer needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Domains That Rely on XML
&lt;/h2&gt;

&lt;p&gt;XML is the foundation of SOAP-based enterprise integrations, healthcare data exchange via HL7 and FHIR, securities trading with the FIX protocol, and modern office file formats like DOCX and XLSX. Android developers work with XML daily for layout files and manifests. SVG graphics are XML under the hood.&lt;/p&gt;

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

&lt;p&gt;XML's longevity comes from solving real problems that JSON and newer formats don't fully address - strict validation, namespace management, and mixed content support. Understanding its structure and tooling makes you a more effective developer when working across enterprise, healthcare, or any legacy-adjacent domain.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source article: &lt;a href="https://devtoollab.com/blog/xml-structure-explained" rel="noopener noreferrer"&gt;XML Structure Explained - DevToolLab&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devtoollab.com/tools/xml-to-json" rel="noopener noreferrer"&gt;XML to JSON Converter - DevToolLab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/XML_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP XML Security Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.json.org/json-en.html" rel="noopener noreferrer"&gt;JSON.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>xml</category>
      <category>webdev</category>
      <category>programming</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Top 12 Playit.gg Alternatives for Low-Latency Game Server Hosting in 2026</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Fri, 12 Jun 2026 18:21:18 +0000</pubDate>
      <link>https://dev.to/moksh/top-12-playitgg-alternatives-for-low-latency-game-server-hosting-in-2026-2jla</link>
      <guid>https://dev.to/moksh/top-12-playitgg-alternatives-for-low-latency-game-server-hosting-in-2026-2jla</guid>
      <description>&lt;p&gt;Multiplayer gaming has come a long way, and so have the tools that keep your servers accessible. Playit.gg has served gamers well as a no-port-forwarding proxy solution, but it is not the only option. Whether you are running a Minecraft world for your friend group, a Terraria server, or any other multiplayer environment, there are smarter and sometimes faster solutions available in 2026.&lt;/p&gt;

&lt;p&gt;This guide covers 12 tested alternatives across four categories: tunneling services, dedicated game hosting, virtual LAN solutions, and remote play platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Free Tier&lt;/th&gt;
&lt;th&gt;Custom Domains&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pinggy&lt;/td&gt;
&lt;td&gt;Tunneling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ngrok&lt;/td&gt;
&lt;td&gt;Tunneling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portmap.io&lt;/td&gt;
&lt;td&gt;Tunneling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PageKite&lt;/td&gt;
&lt;td&gt;Tunneling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LocalXpose&lt;/td&gt;
&lt;td&gt;Tunneling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aternos&lt;/td&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailscale&lt;/td&gt;
&lt;td&gt;VPN&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZeroTier&lt;/td&gt;
&lt;td&gt;VPN&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SoftEther VPN&lt;/td&gt;
&lt;td&gt;VPN&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Radmin VPN&lt;/td&gt;
&lt;td&gt;VPN&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parsec&lt;/td&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shadow&lt;/td&gt;
&lt;td&gt;Cloud PC&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why Consider Moving Away from Playit.gg?
&lt;/h2&gt;

&lt;p&gt;Playit.gg works as a global proxy that bridges your local server to the internet without any router configuration. That said, developers and gamers look for alternatives when they need lower baseline latency, custom domains, better UDP protocol support, or higher uptime guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 1 - Tunneling and Port-Forwarding Tools
&lt;/h2&gt;

&lt;p&gt;These solutions punch a secure tunnel from your local machine to the internet, giving remote players a public address to connect to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pinggy
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pinggy.io/" rel="noopener noreferrer"&gt;Pinggy&lt;/a&gt; is a minimal-footprint tunneling tool that runs comfortably on low-powered hardware like a Raspberry Pi. There is no daemon to maintain - just a single terminal command. It supports custom domains on paid plans and keeps CPU overhead extremely low.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Hosting game servers on constrained or embedded hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ngrok
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt; remains the gold standard for quick tunnel setup. A single command exposes your local server, and a built-in dashboard lets you inspect all incoming traffic in real time. The free tier uses randomly generated URLs, but paid plans unlock persistent custom subdomains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Temporary sessions, developer testing, and one-off gaming events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portmap.io
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://portmap.io/" rel="noopener noreferrer"&gt;Portmap.io&lt;/a&gt; is built on OpenVPN infrastructure and lets you route traffic through geographically distributed relay servers. Crucially, it handles both TCP and UDP traffic - a hard requirement for most fast-paced shooters and survival games.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Competitive gaming where low ping and protocol flexibility matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  PageKite
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pagekite.net/" rel="noopener noreferrer"&gt;PageKite&lt;/a&gt; is an open-source tunneling tool that provides persistent named tunnels. Your server address stays the same between restarts. It also supports failover across multiple backends, which keeps sessions alive if your primary connection drops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Servers that need a stable, permanent address over long periods.&lt;/p&gt;

&lt;h3&gt;
  
  
  LocalXpose
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://localxpose.io/" rel="noopener noreferrer"&gt;LocalXpose&lt;/a&gt; differentiates itself with end-to-end encryption and DDoS protection built directly into the free tier. Free users can claim custom subdomains rather than dealing with randomly assigned URLs each session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Security-conscious communities that want persistent subdomains without upgrading to a paid plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 2 - Dedicated Game Server Hosting
&lt;/h2&gt;

&lt;p&gt;Instead of tunneling through your local machine, these services host your game server on dedicated infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aternos
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://aternos.org/" rel="noopener noreferrer"&gt;Aternos&lt;/a&gt; provides completely free, ad-supported Minecraft servers with one-click modpack and plugin installation. The trade-off is that servers shut down automatically when all players leave, and available RAM is limited to casual use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Small friend groups who play Minecraft occasionally and do not want to maintain a machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paid Game Server Hosts
&lt;/h3&gt;

&lt;p&gt;Services like &lt;a href="https://apexminecrafthosting.com/" rel="noopener noreferrer"&gt;Apex Hosting&lt;/a&gt; and &lt;a href="https://server.nitrado.net/" rel="noopener noreferrer"&gt;Nitrado&lt;/a&gt; offer 24/7 uptime, DDoS mitigation, and substantial resource allocations starting around $5 to $15 per month. For growing gaming communities, eliminating home hardware limitations is well worth the subscription cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Established communities that need reliable, always-on servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 3 - VPN and Virtual LAN Solutions
&lt;/h2&gt;

&lt;p&gt;These tools simulate a local area network across the internet, so multiplayer works exactly as it would on a home LAN.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailscale
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://tailscale.com/" rel="noopener noreferrer"&gt;Tailscale&lt;/a&gt; uses the WireGuard protocol to build a peer-to-peer mesh network between all connected devices. Traffic goes directly between players rather than through a central server, producing the lowest possible latency. Setup requires only a Google or Microsoft login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Small friend groups who want enterprise-grade security with near-LAN latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  ZeroTier
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.zerotier.com/" rel="noopener noreferrer"&gt;ZeroTier&lt;/a&gt; acts like a global virtual switch. It handles complex NAT traversal automatically and supports up to 100 devices on the free tier, making it well suited for large clans playing older titles that expect a LAN connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Large groups and LAN-style games that do not natively support internet play.&lt;/p&gt;

&lt;h3&gt;
  
  
  SoftEther VPN
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.softether.org/" rel="noopener noreferrer"&gt;SoftEther&lt;/a&gt; is a full-featured, open-source VPN platform that supports nearly every major protocol including OpenVPN and L2TP. It is capable of tunneling through restrictive firewalls found in corporate or university environments. The configuration process is complex and requires meaningful technical knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Advanced users who need to bypass strict network-level restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Radmin VPN
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.radmin-vpn.com/" rel="noopener noreferrer"&gt;Radmin VPN&lt;/a&gt; is a free, Windows-only virtual LAN tool with minimal background overhead and no device limits on the free plan. It is the practical modern replacement for LogMeIn Hamachi for classic PC LAN gaming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Large Windows-only groups playing classic or legacy LAN titles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 4 - Remote Play and Game Streaming
&lt;/h2&gt;

&lt;p&gt;These tools take a different approach - rather than hosting a server, they stream the game screen directly to participants.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsec
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://parsec.app/" rel="noopener noreferrer"&gt;Parsec&lt;/a&gt; lets one person run the game locally while friends connect to stream the display and send controller inputs over the internet. It supports up to 60 FPS at 4K resolution with very low input latency, making it viable for couch co-op titles that lack native online multiplayer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Local co-op games played remotely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shadow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://shadow.tech/" rel="noopener noreferrer"&gt;Shadow&lt;/a&gt; provides a full high-end Windows PC in the cloud, connected via a 1 Gbps enterprise uplink. You run your game server and client from their infrastructure, completely bypassing local hardware limits and ISP throttling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Gamers with underpowered machines or heavily restricted home internet connections.&lt;/p&gt;

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

&lt;p&gt;In 2026, the ecosystem of game server connectivity tools is broader and more capable than ever. For quick throwaway sessions, Ngrok or Pinggy are the fastest paths to getting friends connected. For permanent low-latency setups, Tailscale and ZeroTier are the engineering-first choices. If you want a persistent world running around the clock, a paid dedicated host is the most reliable path.&lt;/p&gt;

&lt;p&gt;Most of these tools offer usable free tiers, so the best approach is to test a couple and benchmark the actual ping your players experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devtoollab.com/blog/best-playit-gg-alternatives" rel="noopener noreferrer"&gt;Original Article: 12 Best Playit.gg Alternatives in 2026 - DevToolLab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pinggy.io/" rel="noopener noreferrer"&gt;Pinggy Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;Ngrok Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portmap.io/" rel="noopener noreferrer"&gt;Portmap.io Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pagekite.net/" rel="noopener noreferrer"&gt;PageKite Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://localxpose.io/" rel="noopener noreferrer"&gt;LocalXpose Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aternos.org/" rel="noopener noreferrer"&gt;Aternos Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/" rel="noopener noreferrer"&gt;Tailscale Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.zerotier.com/" rel="noopener noreferrer"&gt;ZeroTier Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.softether.org/" rel="noopener noreferrer"&gt;SoftEther VPN Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.radmin-vpn.com/" rel="noopener noreferrer"&gt;Radmin VPN Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://parsec.app/" rel="noopener noreferrer"&gt;Parsec Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://shadow.tech/" rel="noopener noreferrer"&gt;Shadow Official Site&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gaming</category>
      <category>networking</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Convert JSON to XML Without Breaking Your Integration</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Thu, 11 Jun 2026 18:45:51 +0000</pubDate>
      <link>https://dev.to/moksh/how-to-convert-json-to-xml-without-breaking-your-integration-1gpb</link>
      <guid>https://dev.to/moksh/how-to-convert-json-to-xml-without-breaking-your-integration-1gpb</guid>
      <description>&lt;p&gt;Working with modern APIs means living in JSON. But the moment your project touches a legacy enterprise system - a bank, a government service, or a SOAP endpoint that hasn't changed in a decade - you're suddenly dealing with XML. The challenge isn't just swapping syntax; it's understanding where the two formats are structurally incompatible, and what breaks silently when you ignore that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JSON and XML Don't Simply Map to Each Other
&lt;/h2&gt;

&lt;p&gt;JSON is compact and type-aware - it distinguishes between numbers, booleans, strings, and arrays natively. XML is verbose, treats all content as text, and has no concept of arrays. It only has repeated sibling elements. This gap is where most conversion bugs are born. A JSON array with just one item can silently become a plain object if your converter doesn't handle the edge case explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Biggest Conversion Pitfalls
&lt;/h2&gt;

&lt;p&gt;First is array ambiguity - XML has no array type, so a JSON array becomes repeated sibling elements. A single-item array is indistinguishable from a plain object unless your converter explicitly preserves the list context. Second is type erasure - XML flattens numbers, booleans, and strings into plain text, destroying the type information that many downstream systems depend on. Third is the single root element rule - JSON can have multiple top-level keys, but every valid XML document must have exactly one root element wrapping everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Arrays the Right Way
&lt;/h2&gt;

&lt;p&gt;Always nest array items inside a named parent element. A JSON users array should produce a parent element containing individual child elements. This structure makes the list unambiguous to any downstream XML parser and prevents silent data loss during round-trips.&lt;/p&gt;

&lt;h2&gt;
  
  
  Escaping Special Characters
&lt;/h2&gt;

&lt;p&gt;Characters that are perfectly valid inside a JSON string will break an XML parser immediately. Your conversion logic must escape these four: less-than becomes &amp;lt;, greater-than becomes &amp;gt;, ampersand becomes &amp;amp;, and double-quote becomes ". Skipping even one of these is one of the most common causes of cryptic integration failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanitizing JSON Keys for XML Element Names
&lt;/h2&gt;

&lt;p&gt;JSON allows keys that are illegal as XML element names - ones that start with a digit, contain spaces, or use special characters. Your conversion logic must sanitize keys before they become tags. A standard strategy is prefixing digit-starting names with an underscore, so "1st" becomes "_1st", and stripping or replacing any other disallowed characters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example - SOAP API Integration
&lt;/h2&gt;

&lt;p&gt;When your application needs to talk to a SOAP service, your JSON payload must be wrapped inside an XML envelope shaped to match the service WSDL schema. Map each JSON field to the correct XML element name, and use the repeating-child pattern for any arrays inside. SOAP services validate the envelope structure strictly before processing any data, so getting this structure right is not optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Libraries by Language
&lt;/h2&gt;

&lt;p&gt;Avoid building an XML serializer from scratch. In Node.js, xml2js provides a Builder API that manages encoding and root elements cleanly. In Python, dicttoxml converts a dictionary directly to XML with a configurable root tag. For Java enterprise applications, Jackson XmlMapper is the standard choice. For quick browser-based conversions during development, DevToolLab JSON to XML Converter (&lt;a href="https://devtoollab.com/tools/json-to-xml" rel="noopener noreferrer"&gt;https://devtoollab.com/tools/json-to-xml&lt;/a&gt;) is fully client-side - no data is ever sent to a server.&lt;/p&gt;

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

&lt;p&gt;JSON to XML conversion is an unavoidable reality for any team working with enterprise integrations. The key rules are straightforward: always wrap output in a single root element, handle arrays explicitly using named parent tags, escape reserved XML characters, and sanitize JSON keys before they become element names. Apply these consistently and your integration will hold up in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: JSON to XML Conversion Complete Developer Guide 2026 - &lt;a href="https://devtoollab.com/blog/json-to-xml-conversion-guide" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/json-to-xml-conversion-guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;XML to JSON Conversion Best Practices - &lt;a href="https://devtoollab.com/blog/xml-to-json-conversion-best-practices" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/xml-to-json-conversion-best-practices&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Essential Developer Tools 2026 - &lt;a href="https://devtoollab.com/blog/essential-developer-tools" rel="noopener noreferrer"&gt;https://devtoollab.com/blog/essential-developer-tools&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;xml2js on npm - &lt;a href="https://www.npmjs.com/package/xml2js" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/xml2js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jackson XmlMapper - &lt;a href="https://github.com/FasterXML/jackson-dataformat-xml" rel="noopener noreferrer"&gt;https://github.com/FasterXML/jackson-dataformat-xml&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>api</category>
      <category>devex</category>
    </item>
    <item>
      <title>XML to JSON Conversion - Avoiding the Pitfalls That Actually Bite You</title>
      <dc:creator>Moksh Gupta</dc:creator>
      <pubDate>Thu, 11 Jun 2026 17:37:41 +0000</pubDate>
      <link>https://dev.to/moksh/xml-to-json-conversion-avoiding-the-pitfalls-that-actually-bite-you-oo7</link>
      <guid>https://dev.to/moksh/xml-to-json-conversion-avoiding-the-pitfalls-that-actually-bite-you-oo7</guid>
      <description>&lt;p&gt;Converting XML to JSON looks deceptively simple - until your pipeline silently drops attribute data, crashes on single-item responses, or passes "true" as a string into a boolean check. The structural gap between XML and JSON is wider than most developers expect. This guide covers the real conversion challenges that matter in production code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why XML and JSON Don't Map Cleanly
&lt;/h2&gt;

&lt;p&gt;XML carries concepts that JSON simply doesn't support natively - attributes, mixed-content nodes, namespaces, and implicit ordering. A direct parse without any configuration will technically produce output, but that output will be inconsistent and fragile. Understanding where the model breaks down is the first step toward writing a conversion that actually holds up.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;XML Concept&lt;/th&gt;
&lt;th&gt;JSON Equivalent&lt;/th&gt;
&lt;th&gt;The Problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Attributes (id="123")&lt;/td&gt;
&lt;td&gt;Properties&lt;/td&gt;
&lt;td&gt;No native attribute concept&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single vs. multiple child elements&lt;/td&gt;
&lt;td&gt;Value vs. Array&lt;/td&gt;
&lt;td&gt;Single item becomes a string, multiple become an array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All text is a string&lt;/td&gt;
&lt;td&gt;Typed values&lt;/td&gt;
&lt;td&gt;"true" and "42" need real type conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Namespaces (soap:Body)&lt;/td&gt;
&lt;td&gt;No equivalent&lt;/td&gt;
&lt;td&gt;Naming collisions without careful handling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Handling XML Attributes Without Losing Data
&lt;/h2&gt;

&lt;p&gt;XML attributes - things like &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;role&lt;/code&gt;, or &lt;code&gt;currency&lt;/code&gt; - encode real business data, yet most XML parsers drop them by default. The standard convention is to prefix attribute keys with &lt;code&gt;@&lt;/code&gt; so they land as regular JSON properties alongside text content.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;fast-xml-parser&lt;/code&gt; for Node.js, set &lt;code&gt;ignoreAttributes: false&lt;/code&gt; and &lt;code&gt;attributeNamePrefix: "@"&lt;/code&gt;. The default behavior silently discards attribute data, which is a common source of hard-to-debug data loss in API migrations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;XMLParser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fast-xml-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XMLParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;ignoreAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attributeNamePrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;textNodeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parseAttributeValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xmlString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Array Problem - The Bug That Catches Everyone
&lt;/h2&gt;

&lt;p&gt;XML doesn't distinguish between a single child element and a collection of child elements. This means a one-item list parses to a plain string, while a two-item list parses to an array - and your &lt;code&gt;array.map()&lt;/code&gt; call works fine in testing and fails in production when a single-item edge case arrives.&lt;/p&gt;

&lt;p&gt;The fix is to declare known collection tags explicitly using the &lt;code&gt;isArray&lt;/code&gt; callback in &lt;code&gt;fast-xml-parser&lt;/code&gt;, or to write a post-processing normalization step that enforces array types on known plural keys like &lt;code&gt;products&lt;/code&gt;, &lt;code&gt;items&lt;/code&gt;, &lt;code&gt;users&lt;/code&gt;, and &lt;code&gt;orders&lt;/code&gt;. Pick one approach and apply it consistently across your pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XMLParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;ignoreAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attributeNamePrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tagName&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alwaysArrayTags&lt;/span&gt; &lt;span class="o"&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;product&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;item&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;user&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;order&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;role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;alwaysArrayTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tagName&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;h2&gt;
  
  
  Type Inference - Don't Let "42" Stay a String
&lt;/h2&gt;

&lt;p&gt;XML stores everything as text. Without explicit type parsing, boolean flags come through as &lt;code&gt;"true"&lt;/code&gt; strings, numeric IDs come through as &lt;code&gt;"123"&lt;/code&gt; strings, and your downstream code has to compensate - or silently misbehaves. Most parsers offer a &lt;code&gt;parseTagValue: true&lt;/code&gt; option that handles the common cases automatically.&lt;/p&gt;

&lt;p&gt;For custom logic, a simple helper that checks for &lt;code&gt;"true"&lt;/code&gt;, &lt;code&gt;"false"&lt;/code&gt;, numeric strings, and empty values covers most real-world needs. Pair it with &lt;code&gt;trimValues: true&lt;/code&gt; to strip whitespace from element text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XMLParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;ignoreAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attributeNamePrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parseAttributeValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parseTagValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trimValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Case - Stripping a SOAP Envelope
&lt;/h2&gt;

&lt;p&gt;A common XML-to-JSON task is migrating from a SOAP-based service to a REST JSON API. The SOAP response wraps the actual payload in &lt;code&gt;soap:Envelope&lt;/code&gt; and &lt;code&gt;soap:Body&lt;/code&gt; containers that you need to navigate past before extracting your data.&lt;/p&gt;

&lt;p&gt;The pattern is straightforward: parse the full XML with attribute support enabled, drill into the envelope wrapper using optional chaining, and remap the inner payload to a clean flat JSON structure. Always declare collection tags like &lt;code&gt;Role&lt;/code&gt; as arrays during the parse step - not as a post-processing fix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;soapToRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;soapXml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XMLParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;ignoreAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;attributeNamePrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parseAttributeValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parseTagValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;soapXml&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;rawUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;soap:Envelope&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;soap:Body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;User&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rawUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not locate User in SOAP body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawUser&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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;h2&gt;
  
  
  Real-World Case - Migrating XML Config to JSON Config
&lt;/h2&gt;

&lt;p&gt;When migrating apps from XML-based configuration (Spring, Maven, legacy enterprise apps) to JSON config, type inference becomes critical. A &lt;code&gt;port&lt;/code&gt; value of &lt;code&gt;5432&lt;/code&gt; stored as XML text must become a JSON number, not the string &lt;code&gt;"5432"&lt;/code&gt;, or your app will reject it at startup.&lt;/p&gt;

&lt;p&gt;Enable &lt;code&gt;parseTagValue: true&lt;/code&gt; during parsing and validate the output schema before replacing your config files in production. Type mismatches in config migrations are easy to introduce and annoying to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Libraries and Tools
&lt;/h2&gt;

&lt;p&gt;For Node.js, &lt;code&gt;fast-xml-parser&lt;/code&gt; gives you fine-grained control over attribute handling, array normalization, and type inference. For Python, &lt;code&gt;xmltodict&lt;/code&gt; paired with &lt;code&gt;json.dumps()&lt;/code&gt; handles most straightforward cases.&lt;/p&gt;

&lt;p&gt;For quick one-off conversions without writing code, the &lt;a href="https://devtoollab.com/tools/xml-to-json" rel="noopener noreferrer"&gt;DevToolLab XML to JSON converter&lt;/a&gt; processes files locally in the browser - no data is sent to a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original article: &lt;a href="https://devtoollab.com/blog/xml-to-json-conversion-best-practices" rel="noopener noreferrer"&gt;XML to JSON Conversion: Best Practices and Common Pitfalls - DevToolLab&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NaturalIntelligence/fast-xml-parser" rel="noopener noreferrer"&gt;fast-xml-parser documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/martinblech/xmltodict" rel="noopener noreferrer"&gt;xmltodict - Python library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devtoollab.com/blog/json-to-xml-conversion-guide" rel="noopener noreferrer"&gt;JSON to XML Conversion Guide - DevToolLab&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>api</category>
      <category>devex</category>
    </item>
  </channel>
</rss>
