<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
  <title>Kuldeep Yadav — Writings</title>
  <link>https://kuldeeep.is-a.dev/writings</link>
  <description>Notes on how things work underneath: computers, MCP servers, side projects.</description>
  <atom:link href="https://kuldeeep.is-a.dev/rss.xml" rel="self" type="application/rss+xml" />
  <item>
    <title>Winking at my laptop to turn pages</title>
    <link>https://kuldeeep.is-a.dev/writings/winking-at-my-laptop-to-turn-pages</link>
    <guid>https://kuldeeep.is-a.dev/writings/winking-at-my-laptop-to-turn-pages</guid>
    <pubDate>Sun, 06 Sep 2026 00:00:00 GMT</pubDate>
    <description>I was tired of pressing page down while reading a pdf, so i made my webcam do it. Turns out nobody's eyes are symmetric, which is a problem when you're counting winks.</description>
    <content:encoded><![CDATA[<p>I was reading a book in my browser, its around 900 pages, and every 40 seconds i had to reach over and press page down. Very small problem. Extremely annoying problem.</p>
<p>So instead of just pressing the key like a normal person, i spent an evening building a thing that watches my face and does it for me.</p>
<h3>Eye tracking (no)</h3>
<p>My first idea was gaze tracking. Look at the right side of the screen, page turns. Look at the left, goes back. Sounds cool.</p>
<p>It doesn&#39;t work. Figuring out where your pupils are actually pointing needs way better hardware than the little camera above your screen. And even if it kinda worked, you&#39;d be turning pages every time you looked at the clock or someone walked into the room.</p>
<p>So gaze is out. What works instead is a <strong>gesture</strong>, something you clearly did on purpose. Not &quot;where is he looking&quot; but &quot;did he just do a thing with his face&quot;.</p>
<p>A wink is perfect for this. You never accidentally wink.</p>
<blockquote>
<p>wink right eye → next page
wink left eye → previous page</p>
</blockquote>
<h3>Blendshapes</h3>
<p>Now the question is how does a computer know i winked.</p>
<p>Google has a model called MediaPipe Face Landmarker. You give it one frame from your webcam and it gives you back a bunch of information about the face it found in it.</p>
<p>The part i needed is called <strong>blendshapes</strong>. These are around 52 numbers, and each number tells you how much one specific facial thing is happening right now, from 0 to 1. There&#39;s one for smiling, one for each eyebrow going up, one for puffing your cheeks.</p>
<p>And there are two called <code>eyeBlinkLeft</code> and <code>eyeBlinkRight</code>.</p>
<pre><code class="language-text">eyes wide open   →  0.02
normal           →  0.15
eye fully shut   →  0.95
</code></pre>
<p>That&#39;s basically the whole thing. Someone already did the hard part of looking at a face and turning it into numbers, i just have to read two of those numbers 30 times a second and decide if it counts as a wink.</p>
<h3>The naive approach</h3>
<p>So the first version was pretty much this:</p>
<pre><code class="language-text">if right eye is shut and left eye is open:
    press page down
</code></pre>
<p>Ship it, what could go wrong.</p>
<p>Well first it crashed. Not a normal error, an actual C++ stack trace:</p>
<pre><code class="language-text">Check failed: service_ Service is unavailable.
</code></pre>
<p>mediapipe 1.0.1 on an M4 goes looking for a Metal (gpu) thing that isn&#39;t there, and instead of falling back to the cpu it just dies. Forcing cpu didn&#39;t help either. Pinning <code>mediapipe&lt;1.0</code> fixed it, 0.10.35 works fine. ok, moving on.</p>
<h3>It worked, then it stopped</h3>
<p>Second run it was actually seeing my face. I winked, page turned. Winked again, page turned. Third time, page turned.</p>
<p>Then nothing. Completely dead. I could wink all i wanted and nothing happened.</p>
<p>The reason was something i added on purpose. After turning a page i didn&#39;t want one long wink to keep firing again and again, so i added a rule: after firing, don&#39;t fire again until both eyes are open.</p>
<p>Sounds reasonable. The problem is how i decided what &quot;open&quot; means. I just picked a number, below 0.3 is open.</p>
<p>But when you&#39;re reading, you&#39;re looking <strong>down</strong>, and your eyelids come down a bit with your eyes. So my fully open eyes were sitting at around 0.28 while reading, which is <em>just</em> under my line. One tiny movement and my eye was no longer officially open, so the script sat there waiting forever for me to open my eyes, which were already open.</p>
<pre><code class="language-text">    shut  1.0 ┤
              │
  my line     ┤ 0.30   ← &quot;below this = open&quot;
  me reading  ┤ 0.28   ← eyes fully open btw
              │
    open  0.0 ┤
</code></pre>
<p>Great margin lmao.</p>
<h3>Print the numbers</h3>
<p>At this point i stopped guessing and made it print what it was actually seeing, which honestly i should do earlier every single time.</p>
<pre><code class="language-text">R 0.05  L 0.15   ← both eyes open, just sitting there
R 0.22  L 0.64   ← left wink   → worked
R 0.57  L 0.34   ← right wink  → nothing happened??
</code></pre>
<p>Two things came out of this.</p>
<p><strong>First, my eyes are not the same.</strong> At rest my left eye reads 0.15 and my right reads 0.05. That&#39;s just my face. Nobody is perfectly symmetric.</p>
<p><strong>Second</strong>, because of that, every wink was being counted as a left wink. My left eye starts 0.10 higher than my right, so it wins the &quot;which eye is more shut&quot; question before i&#39;ve even done anything.</p>
<p>So when i winked with my right eye the difference between the two eyes came out to 0.23, and when i winked with my left it came out to 0.42. Same gesture, but one of them was above my threshold and one was below it, so right winks just got thrown away.</p>
<h3>Comparing each eye to itself</h3>
<p>The fix is to stop comparing my eyes to some number i made up, and compare each eye to <strong>its own normal</strong> instead.</p>
<p>So the script watches your face and learns what each of your eyes reads when you&#39;re just sitting there doing nothing. Then it only looks at how far each eye moved <em>from its own baseline</em>.</p>
<pre><code class="language-text">                   raw    baseline    moved
right eye     →   0.57  −   0.05   =   0.52
left eye      →   0.34  −   0.15   =   0.19
                                 difference = 0.33  ✅

left eye      →   0.64  −   0.15   =   0.49
right eye     →   0.22  −   0.05   =   0.17
                                 difference = 0.32  ✅
</code></pre>
<p>Now both winks come out around 0.32. Same gesture gives the same number no matter which eye you use.</p>
<p>And it accidentally fixed the reading problem too. When you look down, <strong>both</strong> eyes go up together, so the difference between them barely changes. A wink is one eye moving alone, looking down is both eyes moving together. So the difference between the two eyes tells them apart, and it doesn&#39;t need to know anything about where you&#39;re looking.</p>
<pre><code class="language-text">                  left   right   difference
just reading  →   0.16   0.22      0.06     → nothing
normal blink  →   0.34   0.32      0.02     → nothing
actual wink   →   0.49   0.17      0.32     → page turn
</code></pre>
<blockquote>
<p>One small thing, the baseline follows your face down quickly but up very slowly. Otherwise if you hold a wink for a while, the script would slowly decide that &quot;shut&quot; is your new normal and the wink would disappear on its own.</p>
</blockquote>
<h3>Stuff i&#39;d tell myself</h3>
<ul>
<li><strong>print the actual numbers first.</strong> every real fix here came from looking at real values, and every wrong turn came from me imagining what the values probably were.</li>
<li><strong>don&#39;t pick fixed thresholds when the thing you&#39;re measuring is a person.</strong> faces are different, and the same face is different depending on whether its looking at you or at a book. learn the normal instead of guessing it.</li>
</ul>
<h3>Two annoying things</h3>
<p>If you try it, macos needs <strong>Accessibility</strong> permission to let any program send fake key presses, and it doesn&#39;t ask you for it. Camera it asks for, accessibility you have to go add by hand. And until you do, everything looks like its working while nothing happens. Took me a while to figure out.</p>
<p>Also winking is more tiring than you&#39;d expect. Most people can&#39;t wink one eye without slightly squinting the other one, and that&#39;s exactly the confusing case i throw away on purpose. Ten minutes in you&#39;ll feel it.</p>
<h3>In a nutshell</h3>
<p>Its a menu bar app now, so its a toggle instead of a terminal window sitting open. 👁 means its watching and 😴 means its off, and the camera light actually goes off too.</p>
<p>Takes around 150ms from wink to page turn, which is fast enough that you don&#39;t feel like you&#39;re waiting for it.</p>
<p>Would pressing page down have been easier? yes. obviously. next question.</p>
<p>Code&#39;s here if you want to try it → <a href="https://github.com/kuldeeepy/winkscroll">winkscroll</a></p>
]]></content:encoded>
  </item>
  <item>
    <title>Hunting free ARM servers on OCI while I sleep</title>
    <link>https://kuldeeep.is-a.dev/writings/hunting-free-arm-server-on-oci-while-i-sleep</link>
    <guid>https://kuldeeep.is-a.dev/writings/hunting-free-arm-server-on-oci-while-i-sleep</guid>
    <pubDate>Fri, 04 Sep 2026 00:00:00 GMT</pubDate>
    <description>Oracle's free ARM servers are never in stock, at least in my region, so I let a script do the asking. Turns out it had quietly stopped.</description>
    <content:encoded><![CDATA[<p>Considering that i am broke as hell at every month-end but i also want my own cloud server where i can host my sloppy 0 rps apis and run some random background job without choking my local machine. Also solves the downtime issue.</p>
<p><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSx4RrFbfj6DlycjM6VSNsOMNrSyjsKfMMwXJrNEJkrsA&s=10" alt="gogle"></p>
<p>I got to know oracle has an always free tier which has two types of compute instances</p>
<pre><code>&gt; In AMD you can get 2 micro VMs (1/8 OCPU, 1 GB RAM each)
&gt; In ARM you can get 2 OCPUs and 12 GB of RAM (still pretty good)
</code></pre>
<p>I was able to get these two AMD instances and one of them i am using to hunt for the ARM one which is pretty hard to get, at least in my region on a free account.</p>
<blockquote>
<p>Btw, worth checking your account limits before you enter the never-ending begging loop!!</p>
</blockquote>
<pre><code>  oci limits resource-availability get --compartment-id &lt;tenancy&gt; \
    --service-name compute --limit-name standard-a1-core-count \
    --availability-domain &quot;&lt;your AD&gt;&quot;
</code></pre>
<h3>The Problem</h3>
<p>ARM shape is mostly always out of stock in popular / busiest regions and obviously there&#39;s no waiting list or free-up notification saying &quot;here you go with an always free instance&quot; so the best we can do is keep hitting (begging) their servers for a VM instance.</p>
<!-- ![gogle](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTmLDcg7ciAT3jI7kfm1YqAOXw8TziBz4x_-XG9vIAL3w&s=10) --><h3>The naive approach</h3>
<p>If you understood the problem, we can solve it by writing a simple POST request inside a loop which&#39;ll hit the oracle till eternity or until we get a VM. It might look something like this</p>
<pre><code>while (true) {
  const res = await fetch(ORACLE_API, { method: &quot;POST&quot;, body: serverConfig });
  
  if (res.ok) break;  // got a server, done
  
  await sleep(500); // nope, try again lmao
}
</code></pre>
<p>Looks fine, right? It isn&#39;t. There are two major issues you can get into with this: </p>
<ul>
<li><p><strong>Rate limit:</strong> considering this above script, let&#39;s say your one round-trip of request-response took 500ms to complete so in <strong>1 sec 1 api call</strong>, in <strong>1 min 60 api calls</strong> and in <strong>an hour 3600 api calls</strong>, you&#39;re ngmi.</p>
</li>
<li><p><strong>Error handling:</strong> It can get you into two kinds of error scenarios, <strong>retriable</strong> and <strong>non-retriable</strong>. </p>
<ul>
<li><strong>Retriable:</strong> error could be like no compute available, their server is down, some network issue. </li>
<li><strong>Non-retriable:</strong> error could be like requested more compute than your account is allowed to (eg. their policy changed) or a malformed request. waiting won&#39;t fix these, this is how you end up with a script that looks busy for two days and was never going to work.</li>
</ul>
</li>
</ul>
<p>And here&#39;s the annoying part. You&#39;d assume you can just check the status code, 4xx means i broke something, 5xx means they broke something.</p>
<p>but oracle sends &quot;out of host capacity&quot; as a <strong>500</strong> 🙃</p>
<pre><code>{
  &quot;status&quot;: 500,
  &quot;code&quot;: &quot;InternalError&quot;,
  &quot;message&quot;: &quot;Out of host capacity.&quot;
}
</code></pre>
<p>Nothing is actually broken, there just aren&#39;t any servers. But it shows up dressed as a server error, so you can&#39;t go by the status code, you have to read the message.</p>
<p>there&#39;s one more risk, make sure the <strong>serverConfig</strong> you&#39;ve passed to the request body has valid metadata for the vm you want. because once it initializes the instance, if you didn&#39;t add your public access key to it, you&#39;ll never be able to ssh into the vm hence you&#39;ll have to delete it and beg oracle again for an instance.</p>
<p>your payload should look something like this</p>
<pre><code>  {
    shape: &quot;VM.Standard.A1.Flex&quot;, // ARM machine
    shapeConfig: { ocpus: 1, memoryInGBs: 6 },
    imageId: &quot;...&quot;, // Ubuntu 24.04 ARM
    subnetId: &quot;...&quot;,  // which network to attach to
    assignPublicIp: true,
    metadata: {
      ssh_authorized_keys: &quot;ssh-ed12345 ABCDE3Nz... MrBean&quot; // ← this one ;)
    }
  }
</code></pre>
<h3>Why a 429 is worse than it looks</h3>
<p>This one took me a while to get. Your request doesn&#39;t go straight to the thing that knows about servers, there&#39;s a rate limiter sitting in front of it.</p>
<pre><code>your request
     │
     ▼
┌──────────────────┐
│ rate limiter     │  &quot;asking too often?&quot;
└────────┬─────────┘
    yes ─┴─ no
     │       │
     ▼       ▼
   429    ┌──────────────────┐
          │ capacity check   │  &quot;any servers free?&quot;
          └────────┬─────────┘
              ┌────┴────┐
              ▼         ▼
        out of      here you
        capacity      go
</code></pre>
<p>&quot;Out of host capacity&quot; and &quot;429&quot; feel like the same thing. both are failures, both mean no server for you. But they&#39;re not the same at all.</p>
<p>&quot;Out of capacity&quot; means somebody actually checked, and there was nothing.</p>
<p><strong>429 means nobody checked.</strong> you got stopped at the door.</p>
<p>So a rate limited request isn&#39;t a failed attempt, it&#39;s not an attempt at all. My script was running full speed, logs scrolling, and it wasn&#39;t even asking.</p>
<h3>Oracle&#39;s Retry Mechanism</h3>
<p>Which brings me to how i was hitting that limit without even realising.</p>
<p>So you send a POST, it fails, and you send another one. two requests, right?</p>
<p>Not really. Oracle&#39;s sdk/cli wrapper has its own retry mechanism baked in. When a request fails with something it thinks is temporary (their server choked, a network error, a 429) it quietly retries the same request in background until it&#39;s made <strong>7 attempts</strong>, and you won&#39;t even know about it.</p>
<p>You see one failure in your logs. Oracle saw seven requests.</p>
<pre><code>your script                 what Oracle receives
───────────                 ────────────────────
attempt #1  ─────────────►  request 1
                            request 2  ┐
                            request 3  │ SDK retrying,
                            ...        │ invisible to you
                            request 7  ┘
            ◄─────────────  one error

sleep 11s

attempt #2  ─────────────►  request 8
</code></pre>
<p>And remember the 500 thing? Their sdk retries any 5xx by default (except 501), because normally a 5xx is a blip, try again in a second and it&#39;ll probably work.</p>
<p>But &quot;out of host capacity&quot; is not a blip. there were no free servers a second ago and there are none now. might change in an hour, might change next week. So the sdk sees a 500, assumes it&#39;s temporary, and fires 7 more requests that ofcourse will fail.</p>
<p>It gets funnier. <strong>429 is also on their retry list.</strong> Oracle tells you you&#39;re sending too many requests, and the sdk&#39;s answer is to send more requests. Idk what they&#39;re smoking lmao.</p>
<p>My log said attempt #47. Oracle&#39;s log said request #329.</p>
<p>So you make sure to pass the <code>--no-retry</code> flag, which simply means don&#39;t handle retry for me, I am already doing the error handling so i&#39;ll retry myself. Which i think probably is the safer approach here, because my loop knows things the sdk doesn&#39;t. it knows out-of-capacity won&#39;t fix itself in one second, and that the right answer to a 429 is to back off, not to try harder.</p>
<h3>How I did it</h3>
<pre><code>┌────────────────────────────────────────────────┐
│ 1. SETUP — runs once                           │
└────────────────────────────────────────────────┘
   read public key  (~/.ssh/id_ed25519.pub)
   → build metadata  { ssh_authorized_keys: &quot;...&quot; }
   → do I already have an A1?
         yes → exit, nothing to do
         no  ↓

┌────────────────────────────────────────────────┐
│ 2. LOOP — until it lands                       │
└────────────────────────────────────────────────┘
   POST  /instances     (timeout 150s, --no-retry)
   ↓
   what came back?

   ├─ an instance id ─────────────────► SUCCESS
   │
   ├─ nothing / timed out ────────────► go and look:
   │                                      exists? → SUCCESS
   │                                      no?     → wait 165s
   │
   ├─ LimitExceeded, NotAuthorized ───► STOP. (un-retriable error).
   │  InvalidParameter                   
   │
   ├─ 429 Too Many Requests ──────────► wait 120s (retriable error)
   │
   ├─ 500 &quot;Out of host capacity&quot; ─────► wait 165s (retriable error)
   │
   └─ anything else ──────────────────► log it, wait 165s
                                          ↓
                                       loop again

┌────────────────────────────────────────────────┐
│ 3. SUCCESS                                     │
└────────────────────────────────────────────────┘
   poll for public IP   (up to 30 × 10s)
   → ping Discord
   → exit
</code></pre>
<p>In a nutshell, I tried to deep dive into why i am not getting VMs with my request loop and tried to make my approach optimal.</p>
<p>Its been running for 15 days now. 7,600 attempts. still nothing 🙂</p>
<p>But at least now its actually asking.</p>
<p>Code&#39;s here if you want to try it out → <a href="https://github.com/kuldeeepy/oci-a1-hunter">oci-a1-hunter</a></p>
]]></content:encoded>
  </item>
  <item>
    <title>Understanding MCP Servers</title>
    <link>https://kuldeeep.is-a.dev/writings/understanding-mcp-servers</link>
    <guid>https://kuldeeep.is-a.dev/writings/understanding-mcp-servers</guid>
    <pubDate>Sun, 30 Aug 2026 00:00:00 GMT</pubDate>
    <description>MCP sounds complex at first, but at its core, it's a standardized way for AI applications to discover and interact with external tools and data.</description>
    <content:encoded><![CDATA[<p>The first time I heard about MCP, It seemed like something really complex.</p>
<p><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTmLDcg7ciAT3jI7kfm1YqAOXw8TziBz4x_-XG9vIAL3w&s=10" alt="gogle"></p>
<p><strong>But it isn&#39;t.</strong></p>
<p>I remember the first mcp I used was the Figma MCP to replicate the design my fellow designer had created. And after using it, I realized that mcp is not really so complex.</p>
<p>btw one of my friend is really obsessed with concept of mcp, he wants mcp for almost everything i wonder when he&#39;s going to build an mcp server to connect claude with his gf lmao :)</p>
<p>But jokes apart, let&#39;s break it down into small pieces.</p>
<h3>Protocol</h3>
<p>As developers, we&#39;ve already been working with protocols like HTTP/HTTPS while building APIs all day.</p>
<p>A <strong>protocol</strong> is simply a shared set of rules that different systems follow when communicating with each other. </p>
<p>For example, with HTTP, we have methods like:</p>
<pre><code class="language-text">GET
POST
PATCH
DELETE
</code></pre>
<p>These are rules both the client and server understand. The client knows how to make a request, and the server knows how to respond to it.</p>
<p>MCP is also a protocol, but instead of defining how a browser talks to a web server, it defines how an AI application can communicate with external capabilities such as tools and data sources.</p>
<h3>Context</h3>
<p>Suppose you tell your LLM (ChatGPT, Claude, etc.) &quot;I am XYZ and I&#39;m from San Francisco.&quot; Then, after a few back-to-back messages, you ask the AI something about yourself.</p>
<p>It can still tell you that you&#39;re xyz and from sf, its because the conversation history is provided to the AI while generating its response as <strong>context</strong>.</p>
<p>context helps the model understand what you&#39;re talking about and generate a more relevant response. You can think of context as the AI&#39;s working memory for the conversation.</p>
<p>Now imagine that instead of just having your conversation, we could also provide the AI with information from the outside world like files, GitHub repositories, databases, APIs, Figma files, and so on.</p>
<p>That&#39;s where things start getting interesting...</p>
<h3>Model</h3>
<p>The model is the actual AI doing the reasoning like your Claude, ChatGPT, or whatever model you&#39;re using.</p>
<p>When you ask:</p>
<blockquote>
<p>What&#39;s the weather in Moscow?</p>
</blockquote>
<p>The ai models doesn&#39;t magically know the current weather they&#39;re just a dumb algorithm which predicts the next word. It needs some external capability that can fetch that information.</p>
<p>For example, imagine we have a tool called:</p>
<pre><code class="language-text">fetchWeatherOfCity(city)
</code></pre>
<p>The model can look at the tools available to it and decide that <code>fetchWeatherOfCity</code> is relevant to the question.</p>
<p>It can then request <code>fetchWeatherOfCity(&quot;Moscow&quot;)</code></p>
<p>The tool goes and gets the actual information, returns the result, and the model uses that result to generate the final answer.</p>
<p>This is the basic idea behind <strong>tool calling</strong>.</p>
<p>But where do these tools actually live? Obviously in an <strong>MCP Server</strong>.</p>
<p>An MCP server is a program that exposes capabilities to an AI application through the MCP protocol. For example, imagine we create an mcp server for a city information service.</p>
<p>It could expose tools like:</p>
<pre><code class="language-text">fetchWeatherOfCity(city)
fetchPopulationOfCity(city)
fetchAreaOfCity(city)
</code></pre>
<p>Now your AI can connect to this MCP server and discover these tools.</p>
<p>The important part is that the AI doesn&#39;t need to know how the tool is implemented internally.</p>
<p>It only needs to know what the tool does and what input it expects:</p>
<pre><code class="language-text">Tool:
fetchWeatherOfCity

Input:
city: string

Description:
Fetches the current weather for a city.
</code></pre>
<p>So you can think of an MCP server as a <strong>bridge between an AI application and some external capability</strong>.</p>
<h3>Host, Client and Server</h3>
<p>There is one more piece we need to understand.</p>
<p>When you use something like Claude Desktop or Cursor, the application you&#39;re interacting with is the <strong>host</strong>.</p>
<p>Inside that host is an <strong>MCP client</strong>, which is responsible for communicating with mcp servers.</p>
<p>The basic architecture looks like this:</p>
<pre><code class="language-text">You → AI Host → MCP Client → MCP Server → External System
       Claude / Cursor                 Figma / DB / API
</code></pre>
<p>The important distinction is:</p>
<ul>
<li><strong>Model</strong> → decides what it needs</li>
<li><strong>MCP Client</strong> → communicates with the MCP server</li>
<li><strong>MCP Server</strong> → exposes the tools</li>
<li><strong>External system</strong> → provides the data or performs the action</li>
</ul>
<p>The model doesn&#39;t directly communicate with the MCP server. The model just decides which capability it needs, while the mcp client handles the communication.</p>
<h3>Putting It Together</h3>
<p>Let&#39;s go back to our weather example.</p>
<p>You ask:</p>
<blockquote>
<p>What&#39;s the weather in Moscow?</p>
</blockquote>
<p>The model realizes it needs current weather information and chooses the <code>fetchWeatherOfCity</code> tool.</p>
<p>The rough flow looks like this:</p>
<pre><code class="language-text">┌──────┐ → ┌────────┐ → ┌───────┐ → ┌──────────┐ → ┌──────────┐
│ User │   │  Host  │   │ Model │   │  Client  │   │  Server  │
└──────┘   └────────┘   └───────┘   └──────────┘   └────┬─────┘
                                                        │
                                                        ↓
                                                   ┌─────────┐
                                                   │ Weather │
                                                   │   API   │
                                                   └────┬────┘
                                                        │
                                                        ↓
┌────────┐ ← ┌───────┐ ← ┌────────┐ ← ┌──────────┐ ← ┌───────┐
│ Answer │   │ Model │   │ Client │   │  Server  │   │ Result│
└────────┘   └───────┘   └────────┘   └──────────┘   └───────┘
</code></pre>
<h3>How Does the MCP Client Communicate with the Server?</h3>
<p>Your mcp server defines how this communication happens. The two main transport mechanisms are <strong>stdio</strong> and <strong>Streamable HTTP</strong>.</p>
<h3>Local MCP Server</h3>
<p>A local mcp server can run as a process on your machine. The AI host communicates with it through standard input and output (<code>stdin</code> / <code>stdout</code>).</p>
<pre><code class="language-text">AI Host ── stdin / stdout ──&gt; MCP Server
</code></pre>
<p>The messages use <strong>JSON-RPC</strong>.</p>
<p>This is useful for MCP servers that need access to things on your local machine, such as your filesystem.</p>
<h3>Remote MCP Server</h3>
<p>An mcp server can also run on another machine and communicate over HTTP / HTTPS network calls.</p>
<pre><code class="language-text">AI Host ── HTTP ──&gt; MCP Server ──&gt; External Service
</code></pre>
<p>This is useful when the server needs to be hosted remotely and accessed by multiple clients. </p>
<p>Instead of every AI application inventing its own way to talk to every service, an mcp server can expose those capabilities through a standardized interface.</p>
<h3>Enough Theory, Let&#39;s Build One</h3>
<p>So I wrote the smallest mcp server to learn about it. Only one tool, about 35 lines, no build step. It&#39;s literally the weather example we&#39;ve been using.</p>
<pre><code class="language-ts">server.registerTool(
  &quot;getCityWeather&quot;,
  {
    description: &quot;Fetch the current weather for a city&quot;,
    inputSchema: {
      name: z.string().describe(&quot;City name, e.g. Delhi&quot;),
    },
  },
  async ({ name }) =&gt; {
    const city = name.toLowerCase();
    return {
      content: [
        { type: &quot;text&quot;, text: weather[city] ?? &quot;Unknown city&quot; },
      ],
    };
  },
);
</code></pre>
<p>That&#39;s the whole tool. The weather itself is a hardcoded map of three cities, and
that&#39;s on purpose, swap it for a real API call and nothing else about the server
changes. The protocol doesn&#39;t care where your data comes from.</p>
<p>Two things here matter way more than they look: the <code>description</code> and the
<code>inputSchema</code>. That&#39;s the only thing the model reads when it&#39;s deciding whether
your tool is relevant to the question. Write a vague description and your tool
just never gets called. it sits there. nobody calls it. kinda sad tbh.</p>
<p>The rest is three lines to connect it over stdio, then you point your
<code>claude_desktop_config.json</code> at the file, restart, and ask it about the weather
in Delhi.</p>
<p>Code&#39;s here if you want to run it → <a href="https://github.com/kuldeeepy/first-mcp-server">first-mcp-server</a></p>
<p>In nutshell it&#39;s simply a protocol for connecting AI applications to external capabilities.</p>
]]></content:encoded>
  </item>
  <item>
    <title>How computers actually work</title>
    <link>https://kuldeeep.is-a.dev/writings/how-computers-actually-work</link>
    <guid>https://kuldeeep.is-a.dev/writings/how-computers-actually-work</guid>
    <pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate>
    <description>It's just bunch of electrical switches having either on or off state, literally.</description>
    <content:encoded><![CDATA[<p>I was always curious to understand how these tiny boxes (computers) work. Its insane to believe that they&#39;re just bunch of electrical switches, literally.</p>
<p><img src="https://cdn.wallpapersafari.com/20/53/jcCZWG.png" alt="gogle"></p>
<h2>Binary</h2>
<p>Computers can understand only two things either something is done or its not done, like i said they&#39;re just bunch of switches, a switch has only two states <strong>On</strong> or <strong>Off</strong> nothing in between. So computers only understand 0 (off) and 1 (on) which is called <strong>binary</strong></p>
<h2>Bits and bytes</h2>
<p>To represent a piece of information we use the word <strong>bit</strong> (a binary digit)</p>
<blockquote>
<p>0 or 1 would be considered a bit</p>
</blockquote>
<p>bit can only represent very small amount of information for example if a bulb is on or off, a single state. but if you want to represent big chunk of information like writing your pet&#39;s name, now you would need multiple bits to represent here comes bytes in the picture.</p>
<blockquote>
<p>01001011 -&gt; K</p>
</blockquote>
<p>as you can see grouping these 8 bits makes a byte which can represent numbers, letters, colors, even pictures which can be understood by humans.</p>
<blockquote>
<p>Example: the letter <strong>&quot;A&quot;</strong> is agreed (by a standard called ASCII) to be represented as the byte <code>01000001</code>.</p>
</blockquote>
<h2>The cpu</h2>
<p>Storing these bits / bytes in the computer won&#39;t help us in any way so there has to be something which can do things like adding, comparing and moving the numbers somehow, that doer is cpu.</p>
<p>cpu has a single job, to follow the instruction and execute a given task. It could be multiplying or adding two numbers. cpu follows a three step loop always to do something</p>
<p><strong>Fetch -&gt; Decode -&gt; Execute</strong></p>
<ul>
<li>Fetch the next instruction to be executed.</li>
<li>Decode the instruction to understand what exactly needs to be done.</li>
<li>Execute the instruction, actually do it.</li>
</ul>
<p>Then it moves to the next instruction and repeats . Forever. Billions of times per second.</p>
<h2>Memory</h2>
<p>for our computer to store the bits and bytes and make some operation on them it needs a place to sit somewhere, we call it memory.</p>
<p>a memory is of two types <strong>RAM</strong> and <strong>ROM</strong></p>
<p><strong>RAM (Random Access Memory)</strong></p>
<ul>
<li>short term</li>
<li>fast</li>
<li>wipes everything out when power is off.</li>
</ul>
<p><strong>ROM (Read Only Memory)</strong></p>
<ul>
<li>long term</li>
<li>slower</li>
<li>keeps things even when power is off.</li>
</ul>
<p><strong>But why two types, not one?</strong></p>
<ul>
<li>Computers need constant power to hold data and do any operation — which is expensive</li>
<li>RAM is fast but short-lived → good for active work</li>
<li>ROM is slower but long-lived → good for storage</li>
<li>You rarely need <em>all</em> your data at once — just the chunk you&#39;re working with right now</li>
<li>So keep the unused stuff in ROM, and load only what&#39;s needed into RAM</li>
</ul>
<blockquote>
<p>eg. chatGPT keeps all your chat histories (&quot;memory&quot;) but you only use one conversation at a time. Loading everything into RAM at once would be wasteful, better to keep unused history in ROM and pull in only what&#39;s needed right now.</p>
</blockquote>
<h2>Stack vs Heap</h2>
<p>computers can&#39;t just store anything anywhere, there has to be a structured way and different use cases right? So the RAM is organized in two structures with a different use cases:</p>
<p><strong>Stack</strong></p>
<ul>
<li>small data</li>
<li>short lived operation</li>
<li>ordered data</li>
</ul>
<pre><code>let age = 49; // small, static → STACK
</code></pre>
<p><strong>Heap</strong></p>
<ul>
<li>huge data</li>
<li>long lived operation</li>
<li>flexible data</li>
</ul>
<pre><code>let user = { name: &quot;James&quot; }; // bigger, flexible → HEAP
</code></pre>
<h2>Processes vs Threads</h2>
<p>Your computer&#39;s CPU has <strong>cores</strong> the parts that actually do the work.</p>
<p>Open Chrome, and the computer starts a <strong>process</strong> for it. Think of it as a separate box just for Chrome. Open Spotify, and it gets its own separate box too. One box can&#39;t see inside the other.</p>
<p>Inside each box, the work gets split into smaller pieces called <strong>threads</strong></p>
<p><strong>Process</strong></p>
<p>one app running, in its own isolated box / environment.</p>
<p><strong>Threads</strong></p>
<p>a smaller task inside that box, like 1 tab playing a video and 1 rendering a webpage in chrome</p>
<p>Just fyi: one core can do one task at a time and when you try to open multiple processes (programs) it tries to switch between the processes to handle them parallely (fake parallelism)</p>
]]></content:encoded>
  </item>
</channel>
</rss>
