mech.app

The mech.app newsletter

Agentic AI, minus the noise.

Get practical field notes on AI agents, automation, developer tools and security delivered to your inbox.

No spam. Unsubscribe anytime.

AI Agents

FinBridge MCP: How Korean Stock Market Data Becomes Agent-Readable Without Breaking Exchange Rate Limits

Exposing KOSPI and NASDAQ data through MCP requires rate-limit buffering, schema normalization, and timezone handling. Here's the plumbing.

Source: gronox.kr
FinBridge MCP: How Korean Stock Market Data Becomes Agent-Readable Without Breaking Exchange Rate Limits

AI agents need structured market data, but exchange APIs were built for human-driven dashboards. FinBridge MCP bridges Korean (KOSPI/KOSDAQ) and U.S. markets through the Model Context Protocol, exposing 33 tools that agents can call without understanding exchange-specific quirks. The interesting part is not the data itself but the plumbing required to make real-time financial feeds agent-safe.

The Rate Limit Problem

Korean exchange APIs enforce strict per-minute quotas. An agent running a multi-step reasoning loop can easily trigger 20+ data requests in seconds. The MCP server sits between the agent and the exchange, so it must absorb bursts without propagating them upstream.

Buffering strategies:

  • Request coalescing: If three agents ask for the same ticker within a 500ms window, the server issues one upstream call and fans out the result.
  • Stale-while-revalidate cache: Market data older than 15 seconds gets served from cache while a background refresh runs. Agents get instant responses; the exchange sees predictable load.
  • Circuit breaker: After hitting 80% of the rate limit, the server queues requests and drains them at a controlled pace. Agents see latency instead of errors.

This approach trades freshness for reliability. During market hours, a 15-second delay is acceptable for most agent workflows. During after-hours, cached data can be hours old without breaking use cases like screening or historical analysis.

Schema Normalization Across Exchanges

KOSPI uses six-digit numeric tickers (e.g., 005930 for Samsung). NASDAQ uses alphabetic symbols (e.g., NVDA). Agents should not need to know which exchange a ticker belongs to or how to format queries differently.

FinBridge solves this by ingesting both markets into a single SQLite database every night. The schema unifies:

  • Ticker format: All identifiers are stored with exchange suffix (005930.KOSPI, NVDA.NASDAQ).
  • Currency: Korean won values are converted to USD at the daily close rate. Agents query in dollars.
  • Timezone: All timestamps are UTC. Trading hours, earnings dates, and filing timestamps are normalized so agents don’t need market-specific calendars.
  • Field names: Revenue is revenue, whether it came from DART (Korean filings) or EDGAR (U.S. filings). No translation layer in the agent.

The nightly batch process handles the heavy lifting. The MCP server exposes a read-only view, so agents never hit the raw exchange APIs directly.

Authentication Boundaries

Multiple agents sharing one MCP server instance create a credential management problem. FinBridge uses API keys scoped to the MCP server, not individual agents. The server holds exchange credentials and rate-limit state internally.

Key rotation flow:

  1. Admin rotates the exchange API key in the server config.
  2. Server reloads credentials without restart (SIGHUP handler).
  3. Agents continue calling MCP tools without changes.

This keeps exchange credentials out of agent memory and logs. Agents authenticate to the MCP server using short-lived tokens, but those tokens never touch the exchange APIs.

Tool Design for LLM Discoverability

The MCP protocol requires each tool to expose a JSON schema that describes its parameters. Financial data has dozens of optional filters (date range, sector, market cap, technical indicators). Exposing all of them makes the schema unreadable to an LLM.

FinBridge splits tools by use case instead of by parameter count:

Tool NamePurposeRequired ParamsOptional Params
get_quoteReal-time pricetickerNone
get_technicalsRS, trend templatetickerperiod
screen_minerviniTrend-following scanmarketrs_min, volume_threshold
dart_get_insider_tradesKorean insider activitytickerdays_back
get_financialsIncome statementtickerperiod, statement_type

Each tool does one thing. Agents compose them in multi-step workflows. The alternative (one query_market_data tool with 30 parameters) produces hallucinated parameter combinations and requires extensive prompt engineering.

Caching Strategy for Multi-Step Reasoning

An agent screening 2,000 KOSDAQ tickers will call get_technicals thousands of times. Without caching, this hammers the upstream API and burns through rate limits in seconds.

Three-tier cache:

  1. In-memory (Redis): Hot tickers (top 200 by volume) stay in RAM. TTL is 10 seconds during market hours, 1 hour after close.
  2. SQLite snapshot: Nightly batch writes a full market snapshot. Agents doing historical analysis or backtests read from this without touching live APIs.
  3. Upstream API: Cache miss triggers a live call, which then populates Redis and gets written to the next nightly snapshot.

The MCP server tracks cache hit rate per tool. If screen_minervini shows a 40% hit rate, the server pre-warms the cache for the top 500 tickers at market open.

Currency Conversion and Timezone Handling

Korean won to USD conversion happens at the nightly batch, not at query time. The server uses the previous day’s close rate from the Bank of Korea API. This introduces a 24-hour lag in exchange rates, but it keeps the MCP server stateless and prevents agents from needing to understand FX mechanics.

Timezone normalization:

  • KOSPI trading hours: 09:00–15:30 KST (00:00–06:30 UTC)
  • NASDAQ trading hours: 09:30–16:00 EST (14:30–21:00 UTC)

All timestamps in the database are UTC. The MCP server exposes a get_market_status tool that returns open, closed, or pre_market for a given exchange at the current UTC time. Agents use this to decide whether to request live quotes or fall back to cached data.

Failure Modes

Exchange API outage: The MCP server serves stale data from the SQLite snapshot and sets a data_age_seconds field in every response. Agents can decide whether to proceed or abort based on staleness tolerance.

Rate limit exhaustion: Requests queue with a 30-second timeout. If the queue depth exceeds 100, the server returns HTTP 429 with a retry_after header. Agents that ignore this will see their requests dropped.

Schema drift: Korean exchanges occasionally change DART filing formats. The nightly batch includes a schema validator that compares incoming fields to the expected schema. Mismatches trigger an alert but do not block ingestion. Unknown fields are stored as JSON blobs and excluded from the normalized schema until a human reviews them.

Concurrent agent access: SQLite supports multiple readers but one writer. The nightly batch acquires an exclusive lock for 2–5 minutes. During this window, the MCP server serves from the previous snapshot and sets a snapshot_in_progress flag. Agents see slightly older data but no downtime.

Deployment Shape

The reference deployment runs on a single 4-core VPS with 8GB RAM. Redis and SQLite colocate with the MCP server process. The nightly batch runs as a cron job at 02:00 UTC (after both markets close).

Resource usage during market hours:

  • CPU: 15–25% (mostly JSON serialization)
  • RAM: 2.3 GB (Redis cache + SQLite page cache)
  • Disk I/O: <5 MB/s (reads from SQLite snapshot)
  • Network: 200–400 req/min to upstream APIs (rate-limited)

Horizontal scaling is not required for up to 50 concurrent agents. Beyond that, the bottleneck is Redis memory. The next step is to shard the cache by ticker prefix (0–4, 5–9, A–M, N–Z) and run four Redis instances.

Code: MCP Tool Registration

from mcp import Tool, ToolParameter

tools = [
    Tool(
        name="get_quote",
        description="Fetch real-time or cached quote for a ticker",
        parameters=[
            ToolParameter(
                name="ticker",
                type="string",
                description="Ticker with exchange suffix (e.g., 005930.KOSPI, NVDA.NASDAQ)",
                required=True
            )
        ],
        handler=lambda params: quote_handler(params["ticker"])
    ),
    Tool(
        name="screen_minervini",
        description="Screen for stocks passing Minervini trend template",
        parameters=[
            ToolParameter(name="market", type="string", required=True),
            ToolParameter(name="rs_min", type="integer", required=False, default=70),
            ToolParameter(name="volume_threshold", type="number", required=False, default=1.5)
        ],
        handler=lambda params: screen_handler(
            market=params["market"],
            rs_min=params.get("rs_min", 70),
            volume_threshold=params.get("volume_threshold", 1.5)
        )
    )
]

def quote_handler(ticker: str) -> dict:
    # Check Redis cache
    cached = redis_client.get(f"quote:{ticker}")
    if cached:
        return json.loads(cached)
    
    # Fetch from upstream API with rate limit check
    if rate_limiter.should_throttle():
        return {"error": "rate_limit", "retry_after": 15}
    
    quote = exchange_api.get_quote(ticker)
    redis_client.setex(f"quote:{ticker}", 10, json.dumps(quote))
    return quote

The handler checks cache first, respects rate limits, and returns structured errors that agents can parse. No exceptions bubble up to the MCP layer.

Technical Verdict

Use FinBridge MCP when:

  • You need agents to screen or analyze both Korean and U.S. markets without writing exchange-specific code.
  • Your agent workflows involve multi-step reasoning that would otherwise hammer live APIs.
  • You want schema normalization (currency, timezone, field names) handled server-side.

Avoid it when:

  • You need sub-second quote latency (the 15-second cache is a dealbreaker for HFT or real-time arbitrage).
  • Your agents require tick-by-tick data or order book depth (the schema focuses on OHLCV and fundamentals).
  • You need to support more than 50 concurrent agents without adding infrastructure (Redis sharding and load balancing are not included).

The core value is not the data itself but the rate-limit buffering and schema unification. If you are building agents that need to reason about cross-border equities, this plumbing pattern applies beyond Korea and the U.S.

Tags

agentic-ai orchestration infrastructure mcp financial-data

Primary Source

gronox.kr