Category: AI-Assisted Development

  • Before we AI: can you trust the numbers?

    Before we AI: can you trust the numbers?

    Working around finance, reporting, data and large transformation projects, I have seen technology move a long way.

    AI now promises the next step: instead of building another report, people can simply ask questions of company data and get an answer.

    The demos are impressive.

    But there is one thing I keep coming back to:

    Can we trust the numbers?

    Not because the AI cannot calculate.

    Because many of the decisions behind a business number were never really part of the data in the first place.

    Which of three systems counts when they disagree?

    Is that export in Controlling the source of truth or simply a copy somebody kept?

    At what level do we eliminate intercompany?

    What exactly does “last year” mean in this company?

    And yet a system can produce a number without having explicit answers to any of those questions.

    That is what bothers me.

    It worked without those answers. So somebody — or something — supplied them. Not you.

    And that can be surprisingly difficult to detect.

    If the wrong table is selected, you do not get an error. You get a number.

    The SQL is correct. The arithmetic is correct. The result looks completely normal.

    Your fiscal year runs May to April, but “last year” is interpreted as January to December? You still get twelve perfectly valid months.

    Just not the twelve months you meant.

    And this can easily survive testing. A test may confirm that the system computes what it was told to compute — without establishing that the underlying business interpretation was the right one.

    That is the problem I started working on with before-we-ai.

    The idea is to put a control point in front of the AI answer.

    For a business question such as “show me revenue by entity and month”, before-we-ai makes visible:

    • what that answer depends on;
    • which sources and business rules are being used;
    • what can actually be verified against the data;
    • what somebody in the business still needs to confirm.

    Where something can be tested, it is tested against the data.

    Where it is a business decision — for example which source is authoritative — somebody has to own that decision, with a defined scope.

    And unresolved points stay unresolved.

    The result is deliberately simple:

    Ready.
    Ready with limitations.
    Blocked.

    And if it is blocked, you see why.

    That is what I would want to know before putting an AI-generated number into a management report, a board pack or another business-critical decision.


    The idea behind before-we-ai

    The important part is not another semantic model or another layer of metadata.

    It is making the reasoning behind a business answer visible.

    Diagram showing that revenue by entity and month depends on ledger, entity, reporting period, revenue definition, and intercompany treatment, with readiness status.

    Suppose the system believes:

    This table is the general ledger used for the revenue calculation.

    In before-we-ai, that statement is not silently treated as fact.

    It starts as a claim.

    And a claim can have very different kinds of support.

    The AI may have proposed it because the table name, columns and relationships look plausible.

    A policy document may contain a relevant definition.

    A deterministic test may show that the table balances correctly.

    A finance expert may confirm that this is in fact the ledger used for this reporting purpose.

    These are different things, and before-we-ai keeps them separate.

    That distinction is the core of the project.

    Diagram separating AI proposal, data checks, document evidence, and human authority before a claim is supported for a defined purpose.

    From a claim back to the evidence

    Instead of starting with a number and explaining it afterwards, the system works backwards through what must be true.

    For example:

    Revenue by entity and month depends on the correct ledger.

    That becomes:

    Which source represents the ledger?

    There may be several candidates.

    One could be the actual ERP journal.

    Another could be a reporting export that looks almost identical.

    A third may be an old copy.

    The AI is useful here. It can inspect the available structure and propose likely candidates.

    But it is not allowed to decide that its own proposal is correct.

    The candidates are tested where testing is possible.

    If a supposed journal does not balance, that is strong evidence against it.

    The query that ran, the population tested and the exceptions remain attached to that result.

    If two candidates both survive, the system does not simply choose the one with the nicer table name.

    It leaves the question open.

    Someone has to decide.

    That is deliberately different from asking an LLM:

    Which of these tables is probably the journal?

    The model can help find the candidates.

    It cannot create the authority.


    Not every business rule lives in the data

    Some questions cannot be settled by SQL at all.

    Which price should be used for expected costing?

    Which system is authoritative when two systems disagree?

    Which customers count as intercompany?

    What constitutes revenue for this management report?

    These are not properties waiting to be discovered in a table.

    They are organisational decisions.

    A document may provide the answer. If it does, before-we-ai keeps the connection to the source and the relevant passage.

    If a person has to decide, the confirmation should have a scope.

    Not simply:

    Revenue definition approved.

    But something closer to:

    This definition applies to these entities, for this reporting purpose.

    The idea is to make business knowledge traceable in the same way we already expect technical lineage to be traceable.

    Technical lineage tells us where the data came from.

    This adds another question:

    Why are we entitled to interpret it this way?


    The AI has a deliberately limited role

    I am not trying to solve this by using less AI.

    Quite the opposite.

    AI is very useful for reading documentation, inspecting unfamiliar data, finding relationships, proposing mappings and identifying what should be checked.

    That work can save a huge amount of manual investigation.

    But there is one boundary in before-we-ai:

    The AI can propose. It cannot promote its own proposal into a trusted fact.

    Something that came from the model stays a proposal.

    A deterministic check can provide test evidence.

    A human can confirm organisational meaning.

    A document can provide a traceable source.

    And if none of those settles the point, it remains unresolved.

    The model cannot make the red light disappear by producing a more convincing explanation.


    What the user should see

    Most of this should not look like an AI architecture to the person using it.

    The intended experience is much simpler.

    You ask a business question.

    For example:

    Show me actual P&L by entity and month.

    The system then shows the few things that matter for that answer:

    Ledger of record
    One candidate survives the accounting checks.

    Entity
    Two possible fields remain. Needs confirmation.

    Reporting period
    Confirmed from the reporting policy.

    Revenue definition
    Documented and confirmed.

    Intercompany treatment
    Required, but currently unsupported.

    And at the top:

    Blocked

    Reason:

    Intercompany treatment has not yet been established for this scope.

    Upload another document, answer the question or correct the underlying data, run again, and the status changes.

    If a source changes later, evidence based on the old version becomes stale rather than quietly remaining valid.

    That is the kind of loop I want before-we-ai to provide:

    ask → inspect → resolve → rerun → see what changed

    Not another black box that immediately returns a number.


    Why I think this matters

    The interesting risk with AI in enterprise data is not only hallucination.

    A much quieter problem is a system making a perfectly reasonable interpretation that nobody in the organisation consciously made.

    A semantic layer can make the result consistent.

    A knowledge graph can make relationships easier to navigate.

    A more capable model can produce better SQL.

    All of these are useful.

    But none of them automatically answers:

    Who decided that this interpretation is the one our business intends?

    And consistency can actually make mistakes harder to notice.

    If several teams calculate revenue differently, the disagreement itself creates a reason to investigate.

    If every answer now uses the same hidden assumption, everything may suddenly agree.

    Consistency is valuable.

    It is not the same thing as correctness.


    What before-we-ai looks like today

    before-we-ai is currently a Python project, deliberately kept small.

    It runs locally and works with data and documents such as databases, Excel, CSV and PDF sources.

    The current engine can already:

    • inspect and profile the available data;
    • propose possible relationships and mappings;
    • run deterministic checks;
    • retain the SQL and exceptions behind those checks;
    • attach evidence from documents;
    • record human clarification;
    • determine which parts of a business question are supported or unresolved;
    • detect when underlying data or evidence has changed;
    • derive a final readiness status.

    The current test environment is a synthetic finance company built with deliberately seeded traps: plausible duplicate sources, ambiguous mappings, document-only rules and accounting inconsistencies.

    That is useful for testing the mechanism.

    It is not yet a claim that the approach has been proven across real enterprise landscapes.


    What is still missing

    The engine is ahead of the product experience.

    The next major step is the consumer-facing interface.

    The intention is that somebody can run a simple Python application locally, point it at a set of sources, state a question and work through the unresolved points without seeing internal claim IDs or YAML files.

    There is also remaining work around automatically building the initial business knowledge from existing documentation and presenting document tables reliably.

    And there is one deliberate boundary in the current roadmap:

    before-we-ai does not yet compute the final business answer.

    Today the system answers the preceding question:

    Do the data, business rules and organisational decisions currently support this answer?

    Generating and executing the final business query comes later.

    I actually like that separation.

    It forces the project to prove the control layer before turning it into another answering machine.


    Try it, challenge it

    before-we-ai is open source and still early.

    The repository contains the current engine, the test landscape, the design documentation and the roadmap.

    👉 https://github.com/happychriss/before-we-ai

    What I am particularly interested in now are cases that challenge the idea:

    • business questions where the required knowledge is difficult to make explicit;
    • situations where data evidence and organisational authority conflict;
    • examples where the readiness model would give the wrong conclusion;
    • real-world data landscapes that break the assumptions behind the current prototype.

    Before asking whether AI can answer the question, I think we should first know whether we have earned the right to trust the answer.

  • Claude Code in a Dev Container: From Coding Help to System Thinking

    Claude Code in a Dev Container: From Coding Help to System Thinking

    Something shifted in how I program. Not just in the tools I use — but in how I think about the work itself.

    It’s Not Really an Agent. But It Feels Like One.

    I recently wrote about why context is the missing layer in AI-driven work — the idea that the sequence is Data → Analytics → Context → Action, and that without context, even smart systems stay limited. You can read that post here.

    Claude Code is a good example of this in practice. It’s not a true autonomous agent. It doesn’t plan multi-step strategies on its own. It doesn’t have memory across sessions by default. But when you give it the right context — the codebase, the requirements, the error logs, the hardware specs — something interesting happens. It starts to act rather than just answer. It writes, runs, reads the output, adjusts, and iterates. That feedback loop — code, execute, observe, fix — is what makes it feel agentic, even though technically it isn’t.

    The intelligence was always there in the model. What unlocks it is context. And building the right container for that context — literally and figuratively — is what this post is about.

    The Old Way Was Frustrating

    I started experimenting with Claude Code early on. The concept is great: give an AI a task, let it write code, run it, see what breaks, fix it, repeat. Fully autonomous.

    But the default experience was painful. Constant confirmation prompts. “Should I proceed?” — yes. “Are you sure?” — yes, just do it. That kills any sense of flow.

    Then I heard about --dangerouslySkipPermissions. That removes all the guardrails. Claude just executes. Which sounds great — until you realize you’re running an autonomous AI agent with full access to your actual PC. Your home directory. Your SSH keys. Everything.

    I also tried running Claude on an isolated machine — an old Raspberry Pi — which worked but was awkward and had no real development environment. Not a long-term solution.

    A Swiss Guy on YouTube Had the Right Idea

    I stumbled across this video. Andreas Spiess — strange accent, great ideas — who simply installed Claude Code inside a virtual machine and ran it with full permissions there. Clean isolation. No risk to the host.

    The direction was right. But a full VM is heavy. I wanted something lighter, faster to spin up, and more developer-friendly.

    After a bit of research I found VS Code Dev Containers — and they turned out to be a perfect fit.

    What Is a Dev Container?

    A Dev Container is just a Docker container — but with a special .devcontainer/ configuration folder in your project. When you open the project in VS Code, it detects this config and asks: “Reopen in Container?”

    You click yes. VS Code reopens. Same editor. Same extensions. Same terminal. But now everything runs inside Docker. The container is isolated from your host. Your actual filesystem, your home directory, your other projects — invisible and unreachable from inside.

    You don’t even notice you’re in a container. That’s the point.

    This is exactly the sandbox Claude Code needs to run with full permissions safely.

    The Template I Built

    I created a reusable template on GitHub — a ready-to-use Dev Container setup for Claude Code with a clear security model and a practical project structure.

    Security model — what’s protected:

    • Host filesystem: only the ./project folder is mounted — nothing else on your PC is visible inside the container
    • No privileged mode: the container cannot escape to the host kernel
    • SSH keys: never copied in — the host SSH agent socket is forwarded, keys stay on your machine
    • GitHub token: passed as env var, never stored in the container
    • USB access: hot-plug support for ESP32 and Arduino via device_cgroup_rules — no full /dev access needed

    There is also an optional outbound firewall that restricts Claude to allowlisted domains only (Anthropic API, GitHub, npm, VS Code). I usually leave it off and just watch what Claude does — but it’s there if you want it.

    The Project Structure — Context Is Everything

    The container gives Claude a clean, isolated workspace. But the real insight is what’s inside the project/ folder. This is where context lives — and context is what makes Claude Code go from impressive demo to actual tool.

    project/
      CLAUDE.md          ← session bootstrap: tells Claude what to read at startup
      requirements.md    ← what you're building — written by you, for Claude
      memory.md          ← live session memory — Claude writes here
      skills/            ← working conventions, loaded every session
      knowledge/         ← confirmed config and integration notes
      external-docs/     ← raw reference material (API docs, specs)
      src/               ← the actual source code

    Every session, Claude reads CLAUDE.md first. That file tells it to load requirements.md, all skill files, and all knowledge files before doing anything else. Claude knows the full project context before writing a single line of code.

    requirements.md is the key file — and writing it is now your main job. You don’t write code anymore. You write what you want. You describe the feature. You think through the architecture. You define constraints. You specify the behavior. Claude reads it and implements it.

    This is the shift I mentioned at the start. You move from coder to architect. The work changes — it’s not about syntax anymore, it’s about clarity of thought. If you can describe what you want precisely, Claude can build it. If your requirements are vague, the output will be vague. Good thinking produces good code.

    Skills are reusable context. A skill file can define how to structure code for a specific hardware board, which GPIO pins are available, what libraries to use, what pitfalls to avoid. You write the skill once — and swap it in for every project that uses that board. Claude immediately works more efficiently because it already knows the environment before you start.

    Knowledge files accumulate truth. When Claude figures out a tricky integration — a working config for a library, a confirmed pin mapping, a tested setup — it writes it into knowledge/. Next session, it starts with that already loaded. No re-discovery.

    Starting a New Project: Three Steps

    The template is designed to be copied per project. Starting fresh is fast:

    1. Copy the template folder, rename it to your project name
    2. Edit .env: set COMPOSE_PROJECT_NAME to something unique
    3. Open the folder in VS Code → “Reopen in Container” → enter API key → start Claude with --dangerouslySkipPermissions

    Each project gets its own isolated container, its own Claude config volume, its own shell history. No cross-contamination between projects.

    The ESP32 Feedback Loop — Hardware Gets Interesting

    The USB passthrough is what makes hardware projects genuinely exciting.

    Claude Code doesn’t just write firmware — it flashes it to the ESP32 via USB, reads the serial debug log, and reacts to errors automatically. The loop looks like this:

    Write code → flash → read serial output → fix error → flash again.

    All without me touching anything. Claude drives the entire cycle.

    I was building a touch display project. Claude couldn’t see the screen, obviously. So it just asked me: “I can’t see the display. Does it look correct? Just say yes or no.” Otherwise it was perfectly happy reading the debug port and iterating on its own.

    That feedback loop is the concrete, hardware version of the same principle from my other post. The model is smart. But what makes it act is context: the code, the error log, the serial output, the confirmed hardware config in knowledge/. Remove any of that and the loop breaks.

    Real Projects Done This Way

    This isn’t theoretical. I’ve used this setup to build several actual projects:

    • Soundbox — hardware audio project with ESP32: Claude wrote the firmware, flashed it via USB, read the serial log, debugged autonomously
    • PyLearn — a Python learning app, posted here on 44-2.de — built in a few evenings
    • A Firefox extension that expands the Perplexity chat sidebar to full screen width — one evening, done

    Projects that would have taken me weeks now take two or three evenings. And the code quality? Honestly, at least as good as mine. Often better structured. Sometimes better documented than I would have bothered to do.

    Watch Out

    A few real risks to know about:

    • API costs: Claude calls the Anthropic API freely from inside the container. Set a spend limit at console.anthropic.com — seriously, do it before you start.
    • Git push risk: Claude has access to your forwarded SSH key and can push or delete remote branches. Protect your important branches on GitHub, or set ENABLE_GIT=false in .env.
    • USB security: The /dev filesystem is bind-mounted for USB support. Only ttyACM and ttyUSB device classes are accessible via cgroup rules — but understand what you’re enabling.
    • Firewall bypass: If you enable NET_ADMIN for the optional firewall, Claude could theoretically flush its own iptables rules. Acceptable if you trust the model. Remove the capability if you don’t.

    Container isolation is very good. It is not magic. Know the tradeoffs.

    What’s Next

    A few things I want to explore:

    • More skill files for different hardware boards — ESP32-S3, RP2040, different Arduino variants — each with pinouts, known-good libraries, and gotchas already loaded
    • Better firewall profiles: project-specific allowlists rather than one global list

    The deeper idea here is that skill files could become a community resource. Share the skill for your board, your framework, your setup — and everyone’s Claude sessions get smarter about that environment immediately.


    The template is on GitHub: happychriss/claude-code-container

    Copy it. Edit requirements.md , change the .env file with your project name, and if needed update your USB device. Describe what you want to build (and if you like – which platform or language). Watch what happens.

  • ESP32 Soundbox in an IKEA Frame: A Weekend Hardware Build with AI Support

    ESP32 Soundbox in an IKEA Frame: A Weekend Hardware Build with AI Support

    A friend of mine has a very particular taste in music. So when our group decided to give gifts that all fit into a common frame — literally — I had an idea. Why not build an actual sound machine inside an IKEA picture frame? Total budget: under €30, one weekend, and one AI coding assistant. Let’s go.

     

    The Hardware

    I didn’t have much time, so I just ordered everything from AliExpress in one shot:

    • Ai-Thinker ESP32-Audio-Kit (ESP32-A1S V2.2) — ESP32 with onboard ES8388 audio codec, two NS4150 class-D amplifiers, SD card slot, Bluetooth, six hardware buttons, all in one board. About €17.
    • WS2812B LED strip, 30 LEDs — individually addressable RGB, wired to GPIO22. Around €4.
    • Two small speakers from my parts bin.
    • A 5×7″ IKEA picture frame — €5.
    • A USB power cable for 5V supply.

    That’s it. No custom PCB, no breadboard rats’ nest. The board has two USB connectors — one for flashing, one for power — which makes things even cleaner.

    The speakers sit inside the frame glued to the plastic backing. If I were to rebuild this, I’d attach them directly to the wooden frame itself so it acts as a resonance body — the 6W amplifier is there, the 4Ω drivers are there, but right now the sound escapes through plastic. It still sounds surprisingly good for five-euro speakers. With the frame as resonator it would be genuinely excellent.

    The Build

    Everything fits together with double-sided tape and a hot glue gun. The only soldering needed is:

    1. Speaker wires to the board’s JST connector pads
    2. LED strip power + signal wire to the board

    The board is recessed slightly into the back of the frame so nothing sticks out beyond the frame depth. You can hang this directly on a wall.

    One small thing I’d add next time: an external power switch. Right now you unplug it. Works fine, but still.

    What It Does

    The feature set ended up being pretty complete:

    • Plays all .mp3 files from the SD card root in alphabetical filename order, looping forever
    • _welcome.mp3 always plays first on boot (underscore sorts before letters) — the box greets you with a purple light frame
    • KEY1 short-press: pause/resume
    • KEY1 long-press (2 s): toggle Bluetooth A2DP sink mode — LEDs pulse blue during pairing, then your phone streams audio through the same codec and the same LED show
    • KEY3/KEY4: previous/next track
    • KEY5/KEY6: volume down/up
    • Bluetooth device name: Dietmars-Soundbox (personalized gift, obviously)

    The LED Show

    This is where it gets technically interesting. The LED task runs a 5-layer composited show on Core 0:

    1. Ambient — slow color breathing based on overall volume
    2. Beat burst — flash on kick detection
    3. Bass blob — low-frequency energy mapped to brightness
    4. Sparkles — random high-frequency glints
    5. Picture frame — the four sides of the frame each get a distinct hue, driven by volume tier

    The audio pipeline works like this: PCM samples are tapped from the I2S write callback (both in SD and Bluetooth mode), converted from stereo int16 to mono float, and pushed into a stream buffer. The LED task drains that buffer, runs a 512-point FFT with a Hann window, splits the result into 8 logarithmic bands from 60 Hz to 20 kHz, applies per-band AGC with a ~10 s time constant, and does beat detection via a dual-EMA sub-bass ratio: kick_fast / kick_slow > 1.3, with a 200 ms minimum gap between beats.

    At quiet passages the frame breathes in calm colors at low saturation. Loud and fast music triggers full-saturation disco snaps where each side of the frame gets a randomly assigned hue with ~80–120° forced separation — so you never get two sides the same color by accident. Volume tiers (quiet / mid / loud) control how many frame sides are lit at once.

    It genuinely sets the mood of a room. That part was satisfying to see work.

    Building It With Claude Code

    I had a Docker container from a previous project — Ubuntu base, Node.js, @anthropic-ai/claude-code installed, USB serial device passthrough configured via docker-compose.yml. I dropped all the AliExpress datasheets and board schematics I could find into an external-docs/ folder, wrote a rough requirements.md, and let Claude loose.

    The setup is worth describing: the repo has a CLAUDE.md that tells Claude to read requirements.md, all skills/ files (working conventions per concern), and all knowledge/ files (validated config notes per component) at the start of every session. This way Claude has context persistence across sessions without relying on conversation history. Each time Claude confirms a working configuration, it writes a note into knowledge/ — so the next session starts with what was already proven to work.

    The first challenge was getting audio out at all. The ES8388 codec is sensitive to init order: SD card must mount before I2S init (GPIO25/26 conflict), MCLK must be stable for 100 ms before codec init, and you must use I2S_CLK_SRC_DEFAULT — APLL causes broadband white noise on ESP32. Claude worked through all of that by trying, failing, reading the error, and adjusting. The right path was to use espressif/esp_codec_dev and never write ES8388 registers directly.

    There was also a nasty bug where every song with embedded cover art crackled at the start. The cause: the ID3v2 tag can contain a several-hundred-KB JPEG, and the Helix MP3 decoder was scanning through it byte-by-byte looking for a sync frame, starving the I2S DMA buffer. The fix was to parse the syncsafe size from the ID3v2 header and fseek() past the entire tag before handing the file to the decoder. Claude found this independently after I described the crackling symptom.

    Bluetooth required a custom 3 MB partition table because the BT stack pushes the binary to ~1.1 MB. The A2DP data callback must be strictly non-blocking — push to a StreamBufferHandle_t, drain on a separate task on Core 1 — otherwise you drop the BT link under any load.

    I’m a decent programmer. I would have needed two or three weekends to get all this working on my own. With Claude Code it was one.

    Bill of Materials

    PartCost
    ESP32-Audio-Kit (A1S V2.2, ES8388)€17
    WS2812B LED strip (30 LEDs)€4
    IKEA picture frame€5
    Misc (USB cable, tape, glue)~€3
    Total~€29

    Speakers from the parts bin, SD card from a drawer. Add another €5 if you’re buying those.

    What’s Next

    The natural upgrade is a small 5V LiPo under the frame for wireless wall-hanging. The LED strip needs 5V anyway, so no boost converter required — just a tiny LiPo with a TP4056 charger board under the frame. And a proper external power switch.

    Full source and the devcontainer setup are on GitHub.


    A few things I took away from this: cheap AliExpress hardware is genuinely capable if you understand what you’re buying. The ES8388 codec is a real audio chip — it sounds good. And Claude Code is a legitimately useful tool for embedded work, not just web stuff, as long as you give it structured context. The knowledge/ folder approach — where confirmed hardware facts accumulate across sessions — made a real difference. It’s worth setting up properly.


    GitHub: happychriss/ESP32-A1S-sound-machine

  • Publishing with AI: A WordPress MCP Workflow

    Publishing with AI: A WordPress MCP Workflow

    Tonight I connected Perplexity directly to my self-hosted WordPress site at 44-2.de — using the WordPress.org MCP connector via Pipedream. The idea: talk to an AI, end up with a published post. No copy-pasting, no switching tabs.

    How to set it up

    1. In WordPress admin, go to Users → Add New. Create a user with the role Editor. Don’t use your admin account.
    2. Open the new user’s profile, scroll down to Application Passwords. Enter a name (e.g. “Perplexity MCP”) and click Add New Application Password.
    3. WordPress shows you the password once. Copy it immediately — you won’t see it again. The name is just a label; the long generated string is the actual password.
    4. Go to the Perplexity add-on page and connect the WordPress integration. Enter your site URL (with https://, not http://), the username, and the application password.
    5. The “connection authenticated” confirmation does not verify your credentials actually work — it only checks that a connection was made. The real test comes when you try to use it.
    6. In the Perplexity chat, press +, add the WordPress MCP, and you’re ready to go.

    What tripped me up

    Wrong user — I started with the admin account. Admin should stay for admin things.

    Pipedream doesn’t auto-refresh — After switching users in WordPress, Pipedream kept using the old credentials. You have to disconnect and reconnect manually for changes to take effect.

    The Application Password UI — I was copying the name instead of the generated password. WordPress shows the actual password once, right next to the name field. Easy to miss if you’re not looking for it.

    What’s next

    GitHub is next. The plan is that progress on projects — commits, notes, decisions — flows into posts through conversations. This post was written and published entirely through the MCP connection. That’s the point.