Showing posts with label AI coding. Show all posts
Showing posts with label AI coding. Show all posts

Thursday, July 30, 2026

AI Breaks Developer Boundaries 3 - OS and Devices

Series · AI Breaks Developer Boundaries

Article series · Ongoing

Episode 3 · AI Breaks Developer Boundaries 3 - OS and Devices

This final article in the three-part series looks at how to implement across the boundary, with code and commands rather than abstractions. AI lowers this particular barrier well: it can connect the starting OS API, the callback that returns data, the log to open, and the sequence that joins a board to a server.

The examples form one implementation map: Windows UI input, Android BLE, and a Raspberry Pi toy car. The game-automation example discusses only the UI-input layer. Process memory, hooks, kernel drivers, and anti-cheat evasion are outside this article.

1. The UI layer of an auto mouse: turn coordinates into SendInput events

At the outermost Windows layer, an auto mouse is straightforward. Pick the screen pixel to click, normalize it to the 0–65535 absolute-coordinate range, then put move, left-button-down, and left-button-up records into an INPUT array for SendInput. Microsoft's SendInput reference describes this as serial insertion of keyboard and mouse events into the input stream.

CPP
// Win32 / User32.lib. Click one virtual-desktop pixel coordinate.
#include <windows.h>
#include <array>

bool clickAt(long pixelX, long pixelY) {
    const long left = GetSystemMetrics(SM_XVIRTUALSCREEN);
    const long top = GetSystemMetrics(SM_YVIRTUALSCREEN);
    const long width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    const long height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    const long normalizedX = (pixelX - left) * 65535L / (width - 1);
    const long normalizedY = (pixelY - top) * 65535L / (height - 1);

    std::array<INPUT, 3> input{};
    input[0].type = INPUT_MOUSE;
    input[0].mi.dx = normalizedX;
    input[0].mi.dy = normalizedY;
    input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE |
                          MOUSEEVENTF_VIRTUALDESK;
    input[1].type = INPUT_MOUSE;
    input[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    input[2].type = INPUT_MOUSE;
    input[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
    return SendInput(static_cast<UINT>(input.size()), input.data(), sizeof(INPUT))
           == input.size();
}

MOUSEEVENTF_ABSOLUTE accepts normalized rather than literal pixel coordinates. On a multi-monitor desktop, pair it with MOUSEEVENTF_VIRTUALDESK and the virtual desktop's left, top, width, and height. For a non-fixed button, the next pipeline is screen capture, template matching or OCR, button-center calculation, then clickAt(). That is the technical backbone of UI automation.

POWERSHELL
cl /std:c++20 /EHsc auto_click.cpp user32.lib
.\auto_click.exe

SendInput is a User32 input-layer API. This article stays at OS-provided UI-event generation and screen-coordinate handling. UIPI and application-specific input handling remain runtime conditions to check.

A Windows input flow from screen pixels through normalized coordinates to SendInput events

<Implementation flow from Windows UI input to an Android BLE connection 3.1>

2. Android Bluetooth LE: from scan callback to GATT notification

For headphones or a sensor, three Android classes carry the BLE implementation. BluetoothLeScanner discovers peripherals and returns results through ScanCallback. A discovered BluetoothDevice connects through connectGatt(). Connection, service discovery, reads, and notifications return through BluetoothGattCallback.

On Android 12 and newer, declare and request the following permissions at runtime. Android's Bluetooth permissions guide explains BLUETOOTH_SCAN and BLUETOOTH_CONNECT.

XML
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

This Kotlin example finds an owned BLE test device by advertised name, connects, then locates a documented service and characteristic UUID and enables notifications. Replace the sample UUIDs with the device's published GATT contract or values first observed in nRF Connect; do not invent them for a product.

KOTLIN
private val serviceUuid = UUID.fromString("12345678-1234-1234-1234-1234567890ab")
private val notifyUuid = UUID.fromString("12345678-1234-1234-1234-1234567890ac")
private val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")

private val scanCallback = object : ScanCallback() {
    override fun onScanResult(type: Int, result: ScanResult) {
        if (result.device.name == "LAB_SENSOR") {
            bluetoothAdapter.bluetoothLeScanner.stopScan(this)
            result.device.connectGatt(this@MainActivity, false, gattCallback)
        }
    }
}

private val gattCallback = object : BluetoothGattCallback() {
    override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, state: Int) {
        if (status == BluetoothGatt.GATT_SUCCESS && state == BluetoothProfile.STATE_CONNECTED) gatt.discoverServices()
        else if (state == BluetoothProfile.STATE_DISCONNECTED) gatt.close()
    }
    override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
        val characteristic = gatt.getService(serviceUuid)?.getCharacteristic(notifyUuid) ?: return
        gatt.setCharacteristicNotification(characteristic, true)
        val cccd = characteristic.getDescriptor(cccdUuid) ?: return
        cccd.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
        gatt.writeDescriptor(cccd)
    }
    override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
        Log.d("BLE", "${characteristic.uuid}: ${value.joinToString { "%02x".format(it) }}")
    }
}

onConnectionStateChange() receives link state, onServicesDiscovered() gets the GATT table, and onCharacteristicChanged() receives data pushed by the device. API 33 added the ByteArray value callback signature; Android's BluetoothGattCallback reference documents why it is preferable.

3. When BLE will not connect: where nRF Connect, HCI, ATT, and Wireshark fit

Separate the layers instead of reciting tool names. nRF Connect is a mobile test app that scans peripherals and explores, reads, and subscribes to GATT services and characteristics. Use it to verify the UUID contract before debugging application code. ATT, the Attribute Protocol, is the data protocol through which GATT reads and writes characteristic values. HCI, the Host Controller Interface, is the packet layer between Android's Bluetooth host and the physical controller. Wireshark opens an HCI snoop capture to show whether advertising, connection, ATT reads/writes, and notifications actually occurred.

Start with framework and service state:

BASH
adb shell dumpsys bluetooth_manager
adb logcat -v time | grep -E "BluetoothGatt|BtGatt|Bluetooth|BLE"

Then enable Enable Bluetooth HCI snoop log in Developer options and restart Bluetooth. AOSP's Bluetooth debugging guide documents dumpsys bluetooth_manager, btsnooz.py BUG_REPORT.txt > BTSNOOP.log, and HCI-snoop collection.

BASH
adb bugreport ble-report.zip
unzip ble-report.zip -d ble-report
btsnooz.py ble-report/bugreport-*.txt > BTSNOOP.log
# Open BTSNOOP.log in Wireshark and inspect HCI and ATT packets by time.

The diagnostic sequence becomes concrete. If nRF Connect cannot see the service, start with advertising, connection, or the peripheral GATT itself. If nRF works but the app does not, inspect permission, UUID, callback order, and descriptor write. If HCI contains a write but no notification, inspect peripheral firmware and characteristic subscription. Linking code, OS, and controller logs on one timeline is exactly where AI compresses discovery time.

4. Raspberry Pi car: join motor, sensor, and UART in one process

Before looking at code, separate the roles of the parts. This car has a Linux computer making decisions, GPIO carrying command signals, a motor driver switching current, and sensors or an MCU returning input.

Term Role in this stack What to verify at the connection
Raspberry Pi Main computer running Linux, Python, and networking GPIO pinout, camera, /dev/tty*, Wi-Fi/Ethernet
GPIO Low-voltage pins carrying digital or PWM command signals BCM versus physical pin number and 3.3 V logic level
H-bridge Power switch that reverses motor current for forward/reverse Pi GPIO inputs (IN1/IN2/PWM) and a separate motor supply
TB6612FNG / L298N Common module implementations of an H-bridge enable/STBY, motor supply, and common ground
UART TX/RX serial byte stream between devices port name, baud rate, and crossed TX/RX wiring
MCU or sensor Helper device reading field inputs such as distance, speed, or ADC message format (such as JSON), sample interval, and power

The minimum toy-car stack is therefore a Raspberry Pi, an H-bridge motor driver, a distance sensor, and a helper MCU or sensor over UART. When the Pi emits the logical command “forward at 45%” through GPIO, the H-bridge uses the separate motor supply to turn the wheels. The MCU returns sensor values over UART; the Pi combines those values with Python, network, and camera code. Do not attach a motor directly to GPIO; use an H-bridge such as a TB6612FNG or L298N. Raspberry Pi's GPIO hardware guide provides the same guidance and exposes pinout for local pin inspection.

The important question here is: when a sensor signal reaches the Pi, what actually wakes up? The answer depends on the sensor type. Connecting a sensor to a Raspberry Pi means choosing one of three paths.

Sensor output Path inside the Pi What the program sees
A digital line changing between 0 V and 3.3 V, such as a button, PIR, or sensor INT/DRDY pin GPIO pin → SoC GPIO controller → rising/falling edge → Linux GPIO event Event callback such as when_pressed, or a gpiomon wait
A sensor or MCU sending bytes over I2C, SPI, or UART I2C/SPI/UART peripheral → FIFO → hardware interrupt → Linux driver buffer Blocking read() or readline()
An analog sensor producing a continuous voltage, such as a potentiometer The Pi has no ADC, so an external ADC or MCU converts it to digital first Read the converted value through SPI, I2C, or UART

In other words, a changed voltage is detected first by the SoC GPIO block or a communications peripheral. GPIO inputs can be configured as rising/falling-edge or high/low-level interrupt sources. Linux passes the resulting event through a driver and file descriptor; a userspace program waits for the event or registers a library callback. Raspberry Pi's GPIO documentation states that GPIO inputs can be interrupt sources for the Arm, and its GPIO interrupt section distinguishes rising/falling edges from level interrupts.

Polling, blocking reads, and callbacks are different things. This distinction has to be clear before the sensor code can be read correctly.

Code shape Actual mechanism Role in the car
while True: sensor.distance; sleep(0.05) Poll current state every 50 ms An HC-SR04-style measurement where the Pi must send a trigger first
motor.forward(0.45) PWM output A command to turn a wheel, not input reception
uart.readline() Sleep until data arrives in the UART driver buffer Receive MCU telemetry without wasting CPU
sensor.when_pressed = handler Wait for a GPIO edge, then let a library run a userspace function Receive new-data notifications from a sensor with INT or DATA_READY

Strictly speaking, the Linux kernel does not directly call a Python function. The sequence is voltage change → GPIO hardware interrupt → kernel-driver event → a waiting userspace process or library thread wakes up → handler runs. gpiozero's when_pressed is an API that makes this final userspace callback convenient. Its input-event documentation separately demonstrates is_pressed polling, wait_for_press() blocking, and the when_pressed callback.

Step 1: identify pins and device files first

BASH
pinout
ls -l /dev/serial0 /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
sudo usermod -a -G gpio "$USER"
python3 -m pip show gpiozero pyserial requests

pinout maps physical pins to BCM numbers; /dev/ttyACM0 and /dev/ttyUSB0 are device files a USB/UART MCU creates in Linux. GPIO 5 means BCM number 5, not physical pin 5.

Step 2: poll sensors that must be asked, such as the HC-SR04

This example uses gpiozero.Motor for the H-bridge IN1/IN2 pins and DistanceSensor for an HC-SR04-style trigger/echo sensor. The HC-SR04 only returns a value after the Pi sends a trigger pulse and measures echo travel time, so 20 Hz polling is appropriate here. Pin numbers are BCM numbers; adapt them to real wiring, driver-board enable, and standby pins.

PYTHON
# car_control.py
from gpiozero import Motor, DistanceSensor
from time import sleep, monotonic

motor = Motor(forward=17, backward=27, enable=18)
range_sensor = DistanceSensor(echo=24, trigger=23, max_distance=2.0)

while True:
    distance_m = range_sensor.distance * 2.0
    command = "forward" if distance_m > 0.35 else "stop"
    if command == "forward": motor.forward(0.45)
    else: motor.stop()
    print(f"ts={monotonic():.3f} distance_m={distance_m:.3f} command={command}", flush=True)
    sleep(0.05)
BASH
python3 -u car_control.py | tee car-control.log
tail -f car-control.log

Step 3: receive sensors with an INT or DATA_READY pin through edge events

Accelerometers, ADCs, and some distance sensors expose an INT or DATA_READY pin to announce that a new measurement is ready. Connect that pin to Pi GPIO and there is no need to ask every 50 ms. For example, suppose a sensor emits an active-high DATA_READY pulse on BCM GPIO 5.

PYTHON
# data_ready.py: mark the GPIO edge quickly; perform real I2C/SPI reads in the normal loop.
from gpiozero import Button
from threading import Event

data_ready = Button(5, pull_up=False, bounce_time=0.002)
sample_ready = Event()

def mark_sample_ready():
    sample_ready.set()  # Do not perform long I2C or network work in the callback.

data_ready.when_pressed = mark_sample_ready

while True:
    sample_ready.wait()       # Wait for a Linux GPIO event without consuming CPU.
    sample_ready.clear()
    # Read the sensor registers with smbus2/spidev and calculate a control value here.
    print("new sample: read the sensor over I2C/SPI here")

when_pressed is not the hardware ISR itself; it is a userspace callback. From the program's point of view, it is still a push model: the code runs when the sensor announces new data. The Linux GPIO character-device API sends rising/falling edge events to userspace with timestamps and sequence numbers. To inspect the lower layer first, observe real edges with libgpiod tools.

BASH
# First identify the real gpiochip name and line information.
gpiodetect
gpioinfo

# Replace the chip name with gpiodetect output. Confirm the line number with gpioinfo; do not assume it is a BCM number.
gpiomon -r -f gpiochip0 5

Step 4: receive UART through a blocking read

For an Arduino or sensor board on UART, send a line of JSON from the MCU and read it on the Pi. This is the simplest telemetry path between a board and a Linux application. readline() here is not busy polling that checks an empty buffer every 50 ms. The UART peripheral reports an arriving byte by interrupt; the Linux TTY driver puts it in a kernel buffer and wakes the sleeping readline() call.

CPP
void setup() { Serial.begin(115200); }
void loop() {
  int raw = analogRead(A0);
  Serial.print("{\"raw\":"); Serial.print(raw);
  Serial.println(",\"source\":\"mcu\"}");
  delay(100);
}
PYTHON
# timeout=None blocks until a complete line arrives.
# Replace /dev/ttyACM0 with the real port shown by ls /dev/ttyACM*.
import json, serial
with serial.Serial("/dev/ttyACM0", 115200, timeout=None) as uart:
    while True:
        line = uart.readline().decode(errors="replace").strip()
        if line: print(json.loads(line))

Polling is therefore not the only choice. Use a GPIO edge event when a sensor has DATA_READY; use a blocking readline() when it streams over UART; use polling at an appropriate interval when an I2C sensor responds only after being asked. In a sensor datasheet, look not only for the measurement register but also for INT, DRDY, DATA_READY, and FIFO. That one line determines how the sensor, operating system, and Python control loop fit together.

A Raspberry Pi car flow connecting sensor and UART input to motor control, camera capture, and a network server

<Data flow from Raspberry Pi sensors and UART through motor, camera, and server paths 3.2>

5. Send camera frames to a server with Picamera2 and HTTP

Adding a camera lets the car send frames as well as scalar sensor data. On current Raspberry Pi OS, Picamera2 runs on the libcamera/rpicam stack. Raspberry Pi's camera documentation and the Picamera2 manual document the capture_array() path to a NumPy frame.

BASH
rpicam-hello -t 3000
sudo apt install -y python3-picamera2 python3-opencv python3-requests
PYTHON
# camera_uploader.py
from picamera2 import Picamera2
import cv2, requests, time

SERVER = "http://192.168.0.10:8080/telemetry/frame"
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={"size": (640, 480), "format": "RGB888"}))
camera.start()

while True:
    frame = camera.capture_array("main")
    ok, jpeg = cv2.imencode(".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, 75])
    if ok:
        response = requests.post(SERVER,
            files={"frame": ("frame.jpg", jpeg.tobytes(), "image/jpeg")},
            data={"source": "toy-car", "captured_at": str(time.time())}, timeout=2)
        print("upload", response.status_code, len(jpeg))
    time.sleep(0.2)

At first the server only needs to receive the frame and return a status code. Later it can separate motor commands through MQTT or return object-detection results for the Pi to consume. AI is particularly useful here because it joins rules scattered across GPIO pins, UART baud rates, camera formats, HTTP payloads, and system-service logs into one executable first draft.

Does embedded work feel difficult? I feel the same way. To implement the intended signal interpretation and behavior, one has to know the specifications of hardware sensors and their callback structures well enough to distinguish an appropriate polling structure from the shape of the sensed values. But seeing the boundary clearly is enough for the next step. Once we can see it, we can ask AI about it, question its answer again, and dive deeper.

The point of Part 3 is to see the thresholds between fields. Then we can finally ask AI about what those thresholds really are. AI already knows at least part of the answer. We have entered a time when people who did not even know where the threshold was can use AI to identify it, learn how to cross it, and begin to aim for a complete implementation.

Conclusion

In the past, taking on each field alone required a large amount of work and an enormous body of knowledge. Companies that build businesses around these skills must turn all of that expertise into revenue, which makes the industrial version of the problem harder still. Then AI arrived. Trained on extensive operating-system foundations, embedded code, virtualization techniques, open-source projects, languages, long-lived Python and scripting ecosystems, and design patterns, it can extend a developer's practical range dramatically. It can surface the Windows API sets needed to create a window, identify OS components and hardware-control interfaces, and propose a systematic path through permissions and firewall conditions. Keyboard and mouse HID control, screen capture, image processing, and real-time image analysis become far more approachable when those pieces are brought together.

The same is true when the hardware target is Android rather than Windows. AOSP, the Android Open Source Project, makes the foundation of the Android operating system publicly available. The AOSP architecture overview describes it as publicly available and modifiable Android source. That gives an AI-assisted developer a rich starting point for understanding mobile execution, driver structures, and their underlying principles. Mobile Bluetooth, USB-C interfaces, and NFC therefore become technical areas whose first principles can be reached much faster.

Raspberry Pi and Arduino systems built on Linux are no different. The embedded-driver work that connects Linux and physical boards once felt like an open secret; now the code, test environments, and tools are far easier to find and interrogate with AI. Python's once-distant ecosystem is likewise available to developers who previously worked mainly in Java or scripting environments, opening a practical route to C, C++, and Python. The AI vibe-coding market is still early, and developers may not yet have noticed how far their working range has expanded. Before that expanded capability is fully understood—or before AI replaces more of the work—the more useful question is how far we can push this unfamiliar technology. It is time to look more closely.

As the examples in this article show, a developer CLI workflow and AI coding tools let us ask and act in the order which class → which callback → which log → which port → which test, even in a new field. Once that flow is executed and corrected, the walls built by languages and platforms become substantially lower.

AI Breaks Developer Boundaries 2 - Open Code and Interfaces

Series · AI Breaks Developer Boundaries

Article series · Ongoing

Episode 2 · AI Breaks Developer Boundaries 2 - Open Code and Interfaces

This is the second of a three-part series on the boundaries among web, game, and embedded development, and on how AI tools are reshaping them. The first article identified the boundaries; this one asks what AI can read when it crosses them and what it cannot. Public-code ecosystems are a vastly friendlier landscape for AI. When code, change history, and usage context are closed—or survive only inside UI gestures—the observable surface becomes smaller.

This matters for more than the claim that an AI has “seen lots of code.” Development is the process in which requirements become code, configuration, assets, tests, and incident records. The public collaboration culture represented by Git and GitHub preserves those traces as source, commits, issues, pull requests, reviews, and licenses. Java itself is not synonymous with open source, but an openly developed implementation and ecosystem such as OpenJDK gains the same advantages.

There are unambiguous public examples of code-model training on public-source data. BigCode's The Stack is a 6.4 TB dataset of permissively licensed source code across 358 languages, published for GitHub-based code-model research and training. BigCode also states that StarCoder-family models were trained on GitHub data. That does not prove that every commercial model trained on every GitHub repository. Some providers, including OpenAI, disclose broad data categories rather than repository-level lists. The uncertainty about a particular proprietary model does not erase the structural advantage of public code culture for AI.

Public code still matters enormously. Code is an executable example; documentation explains names; issues retain failure conditions; pull requests and reviews record why a design changed. Together they are development knowledge infrastructure. AI works best not as isolated recall but when connected to search, repositories, tests, linters, shells, and documentation.

Public code is verifiable context before it is training material

Large language models generate code by learning patterns from large-scale text and code. The advantage of public code is not only volume: multiple implementations, failed issues, corrective diffs, tests, and documentation form a foundation for checking a generated answer. A useful answer still requires more than generation: reproduce the issue, locate the API contract, inspect dependencies, pass tests, and review license and security impact.

git status --short
rg -n "timeout|retry|ConnectionError" src test
npm test -- --runInBand

These commands do not solve a failure. They join change state, relevant code, and the current test result. When an AI can also read a README, issue history, CI output, and type definitions, it can form a narrower hypothesis than “there should probably be a function for this.” That is why a GitHub Code Quality workflow that retains evidence in a PR matters.

GitHub's Copilot code-referencing documentation explains a feature that compares proposed-code context with a public-repository index to surface references. That is provenance assistance, not disclosure of a model training corpus. The developer's role is not to paste output unquestioned; it is to verify the contract, license, and test evidence behind the change.

Public does not mean unrestricted training permission. Licenses, copyright, secrets, security vulnerabilities, and low-quality AI-generated code flowing back into repositories are real costs of a public ecosystem. An AI-friendly environment is not the same thing as data that can be used without legal or ethical review.

A developer workflow showing code, tests, issues, and documentation converging into an AI-assisted change

<The public-code context loop that makes an AI-assisted change reviewable 2.1>

Code-native products are easier for an AI to observe

This is why UI-based platforms belong in the argument. AI most easily reads traces left in source repositories and tool logs. In Unity or Unreal, a person may create objects in an editor, bind Prefabs, and connect assets and events through UI. Some development context can then sit outside the code. This does not make UI inferior, nor does it mean AI can never learn editor work. It means that when a model learns from, or reads, code, documents, and diffs, information retained only as UI operations and visual judgment is more likely to be missing.

The standard in this article is therefore how much of the development process is open to people and tools. Scene data, asset references, automation APIs, build logs, and screenshot comparison can make an editor-based platform richer in AI-readable context. Conversely, if the crucial decisions live only in a closed binary, an internal-only tool, or an irreproducible sequence of clicks, the boundary is expensive for a new human developer as well as for AI.

HTML, CSS, and JavaScript illustrate the advantage. Structure, layout rules, and state transitions are textual, so much intent can be inferred before rendering a screen. Earlier code-driven UI systems such as Swing and JavaFX had the same property. But textual representation does not guarantee a beautiful or accessible result: browsers, screen sizes, keyboard navigation, and contrast still need to be run and checked.

export function SaveButton({ pending, onSave }: {
  pending: boolean; onSave(): Promise<void>;
}) {
  return <button disabled={pending} onClick={() => void onSave()}>
    {pending ? 'Saving…' : 'Save'}
  </button>;
}

It is also too quick to call editor-centric engines an AI-proof zone. The more an environment provides asset data, tool APIs, build logs, screenshot comparisons, and execution traces alongside source, the more observable the work becomes. That does not predict every platform will become code-only. It does suggest that platform competition will increasingly include a way to transfer the whole development context to AI by connecting code, declarative data, UI state, and tool calls.

AOSP and MCP represent two different kinds of openness

AOSP's architecture overview describes Android Open Source Project as publicly available, modifiable Android source and lays out framework, HAL, native-library, and kernel layers. It gives both people and AI a broad starting point for understanding OS structure, platform APIs, builds, and tests.

It is not a complete blueprint for every phone. Vendor images, SoC, graphics and radio drivers, OEM extensions, and carrier constraints vary and may not be public or portable. The VNDK overview notes that vendors may extend libraries for function or performance. A broad public base does not replace device-specific validation.

Model Context Protocol, or MCP, is a different kind of openness: an open standard for connecting an AI application to files, databases, search, and workflows. It is not a way to make a model remember everything; it gives a model explicit, inspectable paths to the required data and tools.

{
  "server": "project-docs",
  "capabilities": ["read_repository", "search_docs"],
  "policy": "read-only"
}

This is an illustrative contract, not a universal MCP configuration. MCP's essential contribution is connectivity between legacy systems and AI. When an old server's APIs, data model, operating commands, and permission boundary are exposed as tools, an AI can not only call a function but also better read what it does and in which context it is risky. MCP is not a device for covert data collection; it is a limited contract through which a provider offers function and context, lowering the discovery barrier.

Least privilege remains the central rule. A tool that reads files must not be treated like a tool that deploys. MCP's security best practices keep server selection, consent, authentication, and access control with the operator. Providers still have a clear incentive to offer MCP: the service context once supplied to people through a web screen and manual can be supplied to AI through searchable tools, schemas, and guidance, making that service selectable inside an AI workflow.

Code-native configuration and tool APIs expanding an AI agent's observable workspace under explicit permissions

<How code, configuration, and tool APIs expand an observable workspace under explicit permission 2.2>

A CLI agent shows a direction, not a license

Google's Gemini CLI is an open-source terminal agent that presents file work, shell commands, web fetching, and MCP support. API plus MCP is a window through which AI can understand a legacy server's functions and context; CLI plus MCP can become the equivalent window for desktop programs, local files, and command-line environments. That is evidence that a development experience connecting models to files, commands, search, and tool calls is becoming a real product category. Gemini Managed Agents and remote MCP follow the same direction.

How to assess Microsoft's historical closedness

The author views Microsoft's historic platform strategy as having imposed a substantial interoperability cost on computing. That does not deny the productivity and tooling value created by Office and Windows. The history in which Internet Explorer and Windows-centric technologies bound web developers to one platform should not disappear either. The U.S. Department of Justice's court findings in United States v. Microsoft record Microsoft's efforts to induce ISVs to depend on Windows-specific browsing technologies and to deter reliance on APIs exposed by a competing browser.

It would be inaccurate to reduce that critique to “MSDN was private.” MSDN and today's Microsoft Learn were publicly readable, but they are naturally vendor documentation centered on Microsoft's APIs, tools, and deployment paths. That is different in character from a public knowledge infrastructure of reusable source, discussion, and change history that outsiders can independently implement and verify. Microsoft's later relationship with Linux and open source also changed materially; “Microsoft Loves Linux” appears in a 2016 Ignite session and current WSL documentation.

The AI-era test is simpler. The more that old proprietary APIs, closed formats, vendor-only tools, and license constraints dominate, the less public context is available for training, retrieval, reproduction, and independent verification. Proprietary platforms are not worthless—security, privacy, quality control, and customer-data protection require some context to stay closed. The failure is when necessary closure also removes standards, documentation, and tool contracts that would let users connect to other platforms.

“AI is definitely worse at C, C++, and C#” needs a benchmark

The hypothesis can arise from real engineering experience, but the currently public evidence does not support a categorical claim that AI writes C, C++, or C# definitively worse than Java, Node.js, JavaScript, or Python. HumanEval-X includes C++, Java, JavaScript, and Go, while MultiPL-E translates code-generation benchmarks into 18 languages. The need for such tools reflects the fact that language-level results vary by model, version, prompt, library, and task.

A stronger claim is that AI may fail more often on work such as Windows-specific C++, COM, drivers, or older C# frameworks when public examples and current test environments are scarce and vendor or version coupling is high. Turning that into a fact requires matched, task-relevant measurement.

# Keep generation, build, and tests separate for the same requirements.
for lang in python javascript java cpp csharp; do
  ./run-task-suite --language "$lang" --tasks ./tasks --samples 20
done
Measure Question to ask
Build success Does the output compile in the target toolchain?
Test pass rate Does it meet the same functional contract?
Repair count How many human corrections are needed?
Dependency error Does it assume an obsolete package, SDK, or OS feature?
Security and performance Does it pass separate memory, authority, and bottleneck checks?

Tool connection makes automation more powerful; it does not remove approval and testing. Gemini CLI's tool documentation describes approvals and sandboxing options for file changes and shell commands.

Public code and MCP are not a bypass for permission, security, or quality. License review, secrets, data access, deployment approval, and real tests are contracts to verify before trusting AI output.

AI's apparent capacity is therefore a result of observability, not magic. The more code, documentation, examples, tests, issues, and tool APIs connect, the faster a first map of an unfamiliar field can be drawn. The last article follows that map into operating systems and physical devices.

404 Dev Room 30 - Taming

Series · 404 Dev Room Webtoon · Ongoing Episode 30 · 404 Dev Room 30 - Taming The trainer in the AI coding room has changed. <...