Quincy Labs LogoQuincy Labs
Join ourFollow on

Building an Apple Stocks Exporter in One Session: MCP Tools + Swift 6.2 + Vibe Coding

Exporting Your Apple Stocks WatchlistIn one coding session.

Substack
3 min read

Building an Apple Stocks Exporter in One Session: MCP Tools + Swift 6.2 + Vibe Coding

Henry
via Substack
View original

Exporting Your Apple Stocks Watchlist

In one coding session. Using AI agents, MCP tools, and Swift 6.2’s latest features I built this tool.

How I Reverse-Engineered a Hidden macOS Feature Using MCP, Parallel Agents & Swift 6.2


The Problem

I wanted a simple CSV of my Apple Stocks watchlist — symbol, name, price, change percentage.

Should be easy, right?

Wrong.

Apple sandboxes everything. The data lives in a private CloudKit container.
There’s no public API. Forums are full of people asking the same question with no answers.

The only options:

  1. Poke the filesystem — maybe there’s a plist or SQLite DB somewhere

  2. Screen-scrape the UI using the Accessibility API

So I built both, with automatic fallback


MCP-Powered Research Workflow

Exa.ai for Web Research

Query:

macOS Sequoia Apple Stocks app data location plist sqlite database 2024 2025

Exa surfaced:

  • Sandboxed app container:
    ~/Library/Containers/com.apple.stocks/

  • Group containers for widgets

  • Old forensic references to plist locations

Then I searched:

Swift macOS Accessibility API AXUIElement screen scrape application window extract text rows table 2024

This surfaced code patterns from AXSwift and AXorcist.

Firecrawl for Deep Dives

When I needed detailed docs, firecrawl.dev MCP endpoints let me scrape pages instantly.

Filesystem Exploration

I inspected my machine:

find ~/Library/Containers/com.apple.stocks.widget -type f

Found the jackpot:

  • stock-record-source — binary record of all tracked symbols

  • fsCachedData/* — JSON files with cached prices

I could see all my tickers — AAPL, MSFT, GLTR, GOLD, TLT…
413 symbols total.


Planning With Parallel Agents

I launched two independent planning agents:

  1. Accessibility API approach
    How to scrape the running Stocks.app

  2. Filesystem cache approach
    How to parse Apple’s widget cache format

Each agent explored references, returned patterns, and produced a spec.
Result: a complete blueprint for a dual-extraction CLI with fallback.


Swift 6.2 on macOS 26

Building this gave me space to try Swift 6.2’s newest features.

Strict Sendable Concurrency

struct StockItem: Codable, Sendable {
    let symbol: String
    var name: String?
    var price: String?
    var change: String?
    var changePercent: String?
}

The compiler forced me to fix non-Sendable types crossing actor boundaries — exactly the kind of concurrency bug that causes random crashes.

Global Actor Isolation Fixes

In Swift 6.2, this raised warnings:

let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true]

The fix:

private let axTrustedPromptKey = “AXTrustedCheckOptionPrompt”

ArgumentParser 1.6

@main
struct StocksExportCLI: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: “stocks-export”,
        abstract: “Export Apple Stocks watchlist to CSV”
    )

    @Option(name: .shortAndLong, help: “Output file path”)
    var output: String?

    @Flag(name: .long, help: “Output as JSON instead of CSV”)
    var json: Bool = false

    @Option(name: .long, help: “Extraction method”)
    var method: ExtractionMethod = .auto
}

No custom argument parsing. Just clean declarative Swift.


Deep Dive: Using the Accessibility API

The Accessibility API exposes every UI as a tree of AXUIElements.

Traversal pattern:

func dfs(_ root: AXUIElement, where predicate: (AXUIElement) -> Bool) -> [AXUIElement] {
    var result: [AXUIElement] = []
    var stack: [AXUIElement] = [root]

    while let node = stack.popLast() {
        if predicate(node) { result.append(node) }
        stack.append(contentsOf: axChildren(of: node))
    }
    return result
}

To extract rows from Stocks.app:

  1. Locate process via bundle ID

  2. Get main window AXUIElement

  3. DFS into AXTable / AXOutline

  4. Extract AXStaticText children

  5. Parse fields by position/heuristics

This can break if Apple adjusts its UI hierarchy — which is why the filesystem fallback exists.


Parsing Apple’s Widget Cache Format

Example cache JSON:

{
  “GLTR”: {
    “query”: {
      “results”: {
        “item”: {
          “response”: {
            “data-series”: {
              “series”: {
                “p”: [
                  {
                    “v”: [
                      “138.14”,
                      “138.50”,
                      “137.80”,
                      “138.00”,
                      “1234567”
                    ]
                  }
                ]
              }
            }
          }
        }
      }
    }
  }
}

v = [close, high, low, open, volume].

To avoid confusing timestamps for prices:

if strValue.count >= 10, Int64(strValue) != nil {
    continue
}

The Result

A 1.6MB native macOS binary that:

  • Extracts 413 stocks in under 1 second

  • Works offline using filesystem cache

  • Works live using Accessibility API

  • Exports CSV or JSON

  • Includes debugging modes

Example:

$ stocks-export -o watchlist.csv
$ head -5 watchlist.csv
Symbol,Name,Price,Change,Change%
AAPL,Apple Inc.,203.96,+1.23,+0.61%
MSFT,Microsoft Corporation,378.91,-1.45,-0.38%
AMZN,Amazon.com Inc.,211.92,+2.15,+1.02%
GLTR,abrdn Physical Precious Metals Basket,138.14,+0.56,+0.41%

My Vibe Coding Workflow

  1. Research first — exa.ai + firecrawl MCP

  2. Plan in parallel — competing agent approaches

  3. Iterate fast — Swift 6.2 caught every race condition

  4. Dual extraction — UI scrape + filesystem cache

  5. Ship — README, repo, binary, all in one sitting

Total time: one coding session.


Try It Yourself

Open source:

https://github.com/zetsuchan/apple-stocks-export

Commands:

git clone https://github.com/zetsuchan/apple-stocks-export.git
cd apple-stocks-export
swift build -c release
.build/release/stocks-export -o ~/Desktop/watchlist.csv

What’s Next?

This whole pattern — MCP-powered research, parallel agents, modern Swift — generalizes.

What other data in your apps is locked away behind sandboxes and forgotten APIs?

Time to free it.


Enjoyed this post?

Get our latest research insights and technical deep dives delivered to your inbox.

Subscribe on Substack