M

One Prompt. One Button. A Fully Deployed System. This Is What AI Engineering Looks Like in 2026

13 min readView source ↗

Cover image

Six months ago this required a backend developer, a DevOps engineer, and two weeks. Today it takes a single sentence and Claude. Here's the complete architecture - and why most developers haven't figured this out yet.

The mental model most people have of AI is wrong.

They think: AI assistant → you ask questions → it answers.

The reality in 2026: AI agent → you describe a system → it builds, deploys, and connects that system to your environment. Not a prototype. Not a code snippet. A running application.

This shift has a name: Generative App Architecture. And the stack that makes it practical - Claude + Obsidian + MCP - is available to anyone right now.

  1. What Generative App Architecture actually means

Traditional software development:

Generative App Architecture:

The AI agent doesn't just write code. It runs a full Agentic Loop under the hood:

Step 1 - Decomposition

Your abstract request ("build a CRM for a car wash") gets broken into dozens of atomic subtasks: database schema, UI components, notification logic, authentication flow, API endpoints.

Step 2 - Environment mapping via MCP

Through the Model Context Protocol, the agent instantly understands what tools it has access to - your filesystem, installed packages, running services, available APIs.

Step 3 - Iterative build and test

The agent writes code, executes it, catches compile errors, fixes them, and retests - autonomously - until the system runs correctly.

Step 4 - Contextual deployment

Output isn't a text file with code. It's a live local environment or a deployable web application.

This is the difference between a tool that assists developers and a system that replaces the build pipeline entirely.

  1. Claude as the engine - three deployment modes

Mode 1 - Claude Artifacts

The fastest path from prompt to running application. When you ask Claude to build something, it opens an interactive Artifact window and assembles, compiles, and runs a complete web application in real time - React, HTML, JavaScript - while you watch.

You can interact with the running app inside the Artifact, iterate on it in natural language, and export the complete archive.

Prompt that produces a production-ready finance tracker:

What Claude Artifacts returns - a complete React application:

This is the complete application. One prompt. Runs instantly inside Claude Artifacts.

Mode 2 - Claude + MCP (local deployment)

MCP (Model Context Protocol) gives Claude direct access to your local environment. One prompt doesn't just generate code - it creates folders on your hard drive, installs packages via terminal, writes config files, and starts a local server.

The MCP configuration that connects Claude to your filesystem:

With this configuration active, a single prompt:

Claude executes this end-to-end through MCP - creating the directory structure, installing Node packages, writing the database schema, building the UI, and starting the server. You get a localhost URL.

Mode 3 - Claude Projects with company standards

Upload your UI kit, design tokens, API documentation, and security requirements to a Claude Project once. Every system Claude generates after that automatically conforms to your internal stack.

  1. Obsidian as the AI command center

bsidian in 2026 is not a note-taking app. It's a personal AI command center where your accumulated knowledge base becomes the context layer for every system you build.

The architecture shift: your vault isn't storage anymore. It's RAG (Retrieval Augmented Generation) - a living knowledge base that AI agents query before executing any task.

The plugin stack that makes this work:

Smart Connections - indexes your entire vault and sends relevant notes as context with every AI call.

Obsidian Copilot - connects your vault directly to Claude API. Configure once:

Canvas as system design interface

One prompt creates an interactive Obsidian Canvas where Claude lays out the complete system architecture as connected blocks.

The Canvas output Claude generates as a structured file:

  1. Local LLMs for full privacy

For sensitive data - client systems, financial tools, internal tooling - you route through a local model instead of a cloud API. Zero data leaves your machine.

Configure Obsidian Copilot to use the local endpoint:

The local model + MCP bridge:

Drop a .md file into your Obsidian AI-Inbox with a build prompt. The bridge picks it up, routes it through the local model, builds the system in your Projects folder, and writes a completion note back to Obsidian.

  1. The complete workflow - from idea to deployed system

Total time from step 1 to running application: 4-12 minutes.

The shift this represents

This isn't a faster way to write code.

It's a different relationship between idea and execution.

The constraint on what you can build used to be technical skill and time. A non-developer couldn't build a CRM. A solo developer couldn't build five systems simultaneously. A team couldn't ship in hours instead of weeks.

All three of those constraints are gone.

The new constraint is clarity of thought - the ability to describe precisely what you need, understand what good output looks like, and iterate on it. That's a thinking skill, not a technical one.

Generative App Architecture doesn't make developers obsolete. It makes the ability to think in systems - to reason about what you need before you build it - dramatically more valuable than the ability to write the code that implements it.

The stack is here. The models are capable. The only remaining variable is whether you're building with it or reading about people who are.

This article describes capabilities available in Claude, Obsidian, and MCP as of July 2026. Specific plugin versions and API configurations may change. Always verify current documentation before implementation.

Thank you for reading.

Prompts

{
  "provider": "ollama",
  "model": "qwen2.5-coder:32b",
  "baseUrl": "http://localhost:11434",
  "systemPrompt": "You are a local system builder with access to the user's filesystem via MCP. Build complete working systems. Never send data to external APIs without explicit instruction."
}
 
  "provider": "anthropic",
  "model": "claude-opus-4-8",
  "apiKey": "YOUR_ANTHROPIC_KEY",
  "systemPrompt": "You are a system architect. You have access to the user's complete knowledge vault as context. When building systems, reference their existing notes, past decisions, and established patterns. Output: working code + Obsidian file structure.",
  "contextWindow": {
    "includeActiveFile": true,
    "includeLinkedFiles": true,
    "includeRecentFiles": 10,
    "includeFolders": ["Projects", "Systems", "Decisions"]
  }
}
 
import { useState, useEffect } from "react";
 
const CATEGORIES = ["Housing","Food","Transport","Entertainment","Health","Savings","Income","Crypto","Other"];
const CRYPTO_IDS = { BTC: "bitcoin", ETH: "ethereum", SOL: "solana" };
 
export default function FinanceTracker() {
  const [transactions, setTransactions] = useState(() =>
    JSON.parse(localStorage.getItem("transactions") || "[]")
  );
  const [cryptoPrices, setCryptoPrices] = useState({});
  const [form, setForm] = useState({ description:"", amount:"", category:"Other", type:"expense", date:"" });
  const [darkMode, setDarkMode] = useState(false);
 
  useEffect(() => {
    const ids = Object.values(CRYPTO_IDS).join(",");
    fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd`)
      .then(r => r.json())
      .then(data => {
        const prices = {};
        Object.entries(CRYPTO_IDS).forEach(([sym, id]) => { prices[sym] = data[id]?.usd || 0; });
        setCryptoPrices(prices);
      });
  }, []);
 
  useEffect(() => {
    localStorage.setItem("transactions", JSON.stringify(transactions));
  }, [transactions]);
 
  const addTransaction = () => {
    if (!form.description || !form.amount) return;
    setTransactions(prev => [...prev, { ...form, id: Date.now(), amount: parseFloat(form.amount),
      date: form.date || new Date().toISOString().split("T")[0] }]);
    setForm({ description:"", amount:"", category:"Other", type:"expense", date:"" });
  };
 
  const totalIncome = transactions.filter(t => t.type === "income").reduce((s,t) => s + t.amount, 0);
  const totalExpenses = transactions.filter(t => t.type === "expense").reduce((s,t) => s + t.amount, 0);
  const netWorth = totalIncome - totalExpenses;
 
  const bg = darkMode ? "#0D0D0D" : "#F9F7F4";
  const card = darkMode ? "#1A1A1A" : "#FFF";
  const text = darkMode ? "#F0F0F0" : "#1A1A1A";
  const border = darkMode ? "#2A2A2A" : "#E8E8E8";
 
  return (
    <div style={{ minHeight:"100vh", background:bg, color:text, fontFamily:"system-ui, sans-serif" }}>
      <div style={{ maxWidth:800, margin:"0 auto", padding:"24px 16px" }}>
 
        <div style={{ display:"flex", justifyContent:"space-between", marginBottom:24 }}>
          <div>
            <h1 style={{ fontSize:22, fontWeight:500, margin:0 }}>Finance Tracker</h1>
            <p style={{ margin:"4px 0 0", fontSize:13, color: netWorth >= 0 ? "#22C55E" : "#EF4444" }}>
              Net worth: ${netWorth.toFixed(2)}
            </p>
          </div>
          <button onClick={() => setDarkMode(!darkMode)}
            style={{ padding:"8px 14px", borderRadius:8, border:`1px solid ${border}`,
              background:card, color:text, cursor:"pointer" }}>
            {darkMode ? "Light" : "Dark"}
          </button>
        </div>
 
        <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:10, marginBottom:20 }}>
          {[["Income","#22C55E",totalIncome],["Expenses","#EF4444",totalExpenses]].map(([label,color,val]) => (
            <div key={label} style={{ background:card, border:`1px solid ${border}`, borderRadius:10, padding:14 }}>
              <p style={{ margin:0, fontSize:12, color:"#999" }}>{label}</p>
              <p style={{ margin:"4px 0 0", fontSize:20, fontWeight:600, color }}>${val.toFixed(2)}</p>
            </div>
          ))}
        </div>
 
        <div style={{ background:card, border:`1px solid ${border}`, borderRadius:10, padding:16, marginBottom:20 }}>
          <div style={{ display:"grid", gridTemplateColumns:"2fr 1fr 1fr", gap:8, marginBottom:8 }}>
            <input placeholder="Description" value={form.description}
              onChange={e => setForm(p => ({ ...p, description: e.target.value }))}
              style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }} />
            <input type="number" placeholder="Amount" value={form.amount}
              onChange={e => setForm(p => ({ ...p, amount: e.target.value }))}
              style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }} />
            <select value={form.type} onChange={e => setForm(p => ({ ...p, type: e.target.value }))}
              style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }}>
              <option value="expense">Expense</option>
              <option value="income">Income</option>
            </select>
          </div>
          <div style={{ display:"grid", gridTemplateColumns:"2fr auto", gap:8 }}>
            <select value={form.category} onChange={e => setForm(p => ({ ...p, category: e.target.value }))}
              style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }}>
              {CATEGORIES.map(c => <option key={c}>{c}</option>)}
            </select>
            <button onClick={addTransaction}
              style={{ padding:"9px 20px", borderRadius:8, background:"#E8692A",
                color:"#fff", border:"none", cursor:"pointer", fontSize:13, fontWeight:500 }}>
              Add
            </button>
          </div>
        </div>
 
        <div style={{ background:card, border:`1px solid ${border}`, borderRadius:10, overflow:"hidden" }}>
          <p style={{ margin:0, padding:"12px 16px", borderBottom:`1px solid ${border}`, fontSize:13, fontWeight:500 }}>
            Transactions ({transactions.length})
          </p>
          {transactions.slice().reverse().map(t => (
            <div key={t.id} style={{ display:"flex", justifyContent:"space-between",
              alignItems:"center", padding:"11px 16px", borderBottom:`1px solid ${border}` }}>
              <div>
                <p style={{ margin:0, fontSize:13, fontWeight:500 }}>{t.description}</p>
                <p style={{ margin:"2px 0 0", fontSize:12, color:"#999" }}>{t.category} · {t.date}</p>
              </div>
              <span style={{ fontSize:14, fontWeight:600, color: t.type==="income" ? "#22C55E" : "#EF4444" }}>
                {t.type==="income" ? "+" : "-"}${t.amount.toFixed(2)}
              </span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}
 
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
 
# Pull a coding-capable model
ollama pull llama3.2:latest
# or for stronger code generation:
ollama pull qwen2.5-coder:32b
 
# Start the local server
ollama serve
# Runs at localhost:11434
Prompt in Obsidian Canvas:
"Design a content production pipeline for my Twitter account.
I publish 3 articles per week and manage 5 sponsored deals.
Reference my existing content calendar notes."
 
Output: Canvas with connected blocks for:
→ Content ideation (pulls from your interest notes)
→ Draft → Review → Publish workflow
→ Sponsor tracking with deadline alerts
→ Analytics dashboard
→ Automated cross-posting logic
→ Each block links to the relevant Obsidian file
import requests
import json
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
 
OBSIDIAN_INBOX = Path.home() / "Obsidian" / "AI-Inbox"
PROJECTS_DIR = Path.home() / "Projects"
OLLAMA_URL = "http://localhost:11434/api/generate"
 
class InboxWatcher(FileSystemEventHandler):
    """
    Watch Obsidian AI-Inbox folder.
    When a new .md file appears, treat it as a build prompt.
    Pass to local LLM -> execute via MCP -> report back.
    """
 
    def on_created(self, event):
        if not event.src_path.endswith(".md"):
            return
 
        prompt_file = Path(event.src_path)
        prompt = prompt_file.read_text()
 
        print(f"[Inbox] New prompt: {prompt_file.name}")
        self.execute_prompt(prompt, prompt_file.stem)
 
    def execute_prompt(self, prompt: str, project_name: str):
        response = requests.post(OLLAMA_URL, json={
            "model": "qwen2.5-coder:32b",
            "prompt": f"""You are a system builder.
            Build this system: {prompt}
 
            Output a JSON plan with:
            {{
                "project_dir": "relative path",
                "files": [
                    {{"path": "relative path", "content": "file content"}}
                ],
                "commands": ["npm install", "etc"],
                "summary": "what was built"
            }}""",
            "stream": False
        })
 
        plan = json.loads(response.json()["response"])
        project_dir = PROJECTS_DIR / plan["project_dir"]
        project_dir.mkdir(parents=True, exist_ok=True)
 
        for file_spec in plan["files"]:
            file_path = project_dir / file_spec["path"]
            file_path.parent.mkdir(parents=True, exist_ok=True)
            file_path.write_text(file_spec["content"])
            print(f"[Build] Created: {file_path}")
 
        result_file = OBSIDIAN_INBOX / f"{project_name}-result.md"
        result_file.write_text(
            f"# Build Complete: {project_name}\n\n"
            f"**Location:** {project_dir}\n\n"
            f"**Summary:** {plan['summary']}\n\n"
            f"**Files created:** {len(plan['files'])}\n"
        )
        print(f"[Done] Result written to Obsidian")
 
if __name__ == "__main__":
    OBSIDIAN_INBOX.mkdir(exist_ok=True)
    observer = Observer()
    observer.schedule(InboxWatcher(), str(OBSIDIAN_INBOX), recursive=False)
    observer.start()
    print(f"[Watching] {OBSIDIAN_INBOX}")
 
    try:
        while True:
            import time; time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()
 
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/yourname/Projects"
      ]
    },
    "terminal": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-terminal"]
    },
    "sqlite": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-sqlite",
        "--db-path",
        "/Users/yourname/Projects/app.db"
      ]
    }
  }
}
 
{
  "nodes": [
    {
      "id": "ideation",
      "type": "file",
      "file": "Systems/ContentPipeline/Ideation.md",
      "x": 0, "y": 0, "width": 300, "height": 200,
      "color": "1"
    },
    {
      "id": "draft",
      "type": "file",
      "file": "Systems/ContentPipeline/Draft.md",
      "x": 400, "y": 0, "width": 300, "height": 200,
      "color": "3"
    },
    {
      "id": "sponsors",
      "type": "file",
      "file": "Systems/ContentPipeline/SponsorTracker.md",
      "x": 0, "y": 300, "width": 300, "height": 200,
      "color": "5"
    },
    {
      "id": "analytics",
      "type": "file",
      "file": "Systems/ContentPipeline/Analytics.md",
      "x": 400, "y": 300, "width": 300, "height": 200,
      "color": "6"
    }
  ],
  "edges": [
    {
      "id": "e1",
      "fromNode": "ideation", "fromSide": "right",
      "toNode": "draft", "toSide": "left",
      "label": "approved concepts"
    },
    {
      "id": "e2",
      "fromNode": "sponsors", "fromSide": "right",
      "toNode": "draft", "toSide": "bottom",
      "label": "brief integration"
    }
  ]
}
 
Build and deploy a local CRM system for a car wash business.
 
Create the project at ~/Projects/carwash-crm/
Install dependencies automatically.
Set up SQLite database with tables for:
- customers (name, phone, email, vehicle, notes)
- visits (customer_id, date, service, price, status)
- staff (name, role, schedule)
 
Build a web interface with:
- Customer search and profiles
- Visit history per customer
- Revenue dashboard by month
- SMS reminder system (Twilio integration, config only)
 
Start the dev server when complete.
Report the localhost URL when ready.
Idea → Spec → Design → Frontend dev → Backend dev
→ Database setup → API integration → Testing → Deploy
Duration: weeks. Cost: thousands.
TEP 1 - Input
In Obsidian, on your AI panel, write:
"Personal finance system with crypto integration
and monthly budget tracking."
Press Generate.
 
STEP 2 - Context assembly
Smart Connections plugin scans your vault.
Finds your finance notes from the last 3 months.
Sends them as context to Claude API.
 
STEP 3 - Architecture
Claude (Opus 4.8) designs the system:
- Database schema
- UI components
- API integrations
- File structure
 
STEP 4 - Build
Claude (Sonnet 5) implements via MCP:
- Creates ~/Projects/finance-tracker/
- Installs dependencies
- Writes all files
- Starts dev server at localhost:3000
 
STEP 5 - Result
Obsidian creates folder: Finance System/
Inside: linked dashboards, config notes,
and a direct link to the running application.
Idea → Prompt → Running system
Duration: minutes. Cost: API tokens.
[Claude Project context contains:]
- design-system.md (colors, typography, spacing)
- api-standards.md (authentication patterns, error codes)
- security-policy.md (input validation rules, CORS config)
- stack.md (React 18, TypeScript, Tailwind, Prisma, PostgreSQL)
 
Prompt:
"Build a client onboarding form with email verification
and automatic Notion database entry on submission."
 
Output: production-ready code that matches your stack
exactly - no cleanup required.
Build a personal finance tracking application with:
 
FEATURES:
- Transaction log (add/edit/delete with categories)
- Monthly budget view with progress bars
- Crypto portfolio tracker pulling live prices from CoinGecko API
- Net worth calculation updated in real time
- Dark mode toggle
 
TECHNICAL REQUIREMENTS:
- Single-file React application
- useState + useEffect only (no external state management)
- Tailwind CSS for styling
- Fetch from public APIs (no auth required)
- Mobile responsive
 
DATA:
- Persist to localStorage between sessions
- Export transactions as CSV
 
Output: complete self-contained application ready to deploy.

Related articles