3. Project Architecture & Directory Roles
Top-Level Directory Structure
Layer Responsibilities
src/common/ — Shared Kit (Internal Only, Never Published Separately)
Not a separate package — just a module in the same source tree. tsup inlines it via noExternal, so it only exists inside the build output.
agent/is deliberately not re-exported fromindex.ts. The build inlines every dependency vianoExternal, so re-exporting it would drag langchain and deepagents into the published MCP server bundle. Consumers reach it through the separate@/common/agentpath, and it stays out ofdist/.This keeps the layering one-way:
src/common/agentis the only place that assembles an agent — everything else ships MCP tools and nothing else. Proving the tools work under an LLM happens from a test that hands@/common/agentan MCP server and a prompt (seesrc/agent.test.ts).
src/tools/ — MCP Tool Definitions
src/exchange/ — Exchange-Rate Scraping Domain
Opens Naver, Google, and Daum with Playwright and reads the rates directly. playwright resolves browser drivers from its own package directory at runtime, so it is left in dependencies and kept out of the bundle (marked external in tsup.config.ts).
src/tmdb/ — TMDB Movie Lookup Domain
Calls the TMDB v3 REST API over fetch. No browser and no extra dependency, so the whole domain is bundled like the rest of the source.
Authentication comes from the environment: TMDB_API_KEY (v3 API key, sent as a query parameter) or TMDB_ACCESS_TOKEN (read access token, sent as a bearer header). When both are set the token wins, so the secret never lands in a URL.
src/system/ — Host Metrics Domain
Reads the machine the server runs on through systeminformation. No network, no credential, and no browser — the package shells out to platform commands (ioreg, vm_stat, df, …), so every read is wrapped in a timeout.
Three decisions in this domain exist because the raw numbers answer the wrong question:
- CPU load is sampled twice.
currentLoad()reports the delta since the previous call, so the first call in a process is the average since boot.readCpu()primes it, waitssampleMs(at least 200ms — below thatsysteminformationreturns its cached reading), then reads again. - Memory counts
active, notused. On macOS and Linuxusedincludes cache and buffers, which puts an idle machine in the 90s. The reclaimable part is reported separately ascachedBytes. - One disk is chosen, not all of them. macOS splits a single APFS container into several volumes and mounts a read-only system snapshot at
/, which reports about 3% usage.pickPrimaryDisk()prefers/System/Volumes/Dataon darwin, the working directory's drive on Windows, and/elsewhere.allDisksreturns every mount instead.
readSnapshot() reads the requested sections concurrently, so asking for all four costs about as much as asking for CPU alone. A section that fails becomes null and the reason lands in errors, which is what separates "could not read the battery" from "this machine has no battery".
docs/ + rspress.config.ts — Rspress Documentation Site
readme-docs-plugin renders the hand-written root README.md at the /api (and /ko/api) routes.
End-to-End Data Flow
tsconfig path alias: @/* → ./src/* (root tsconfig.json; rstest.config.ts mirrors the same alias)
Architecture Diagrams
Module & Dependency Structure
src/commonis never published as its own package — it is inlined into the bundle at build time.
Internal Source Structure
One tools definition is consumed three ways: the MCP server (stdio), the CLI runner, and the library entry. Documentation is a fourth consumer, written by hand.
Build Flow
Release Pipeline (.github/workflows/release.yml)
Separation of Concerns
- Tool definitions live in
src/tools/— the MCP-facing interface belongs only here - Domain logic lives in
src/exchange/,src/tmdb/,src/system/— scraping, parsing, timeout handling - Shared MCP server/CLI logic lives in
src/common/kit/— server creation, CLI parsing, error handling src/server.tsonly passes the tool objects tocreateMcpServer()(a very thin layer)src/cli.tsonly passes the tool objects torunCli()(a very thin layer)
Where to Add New Features
- New tool: add a file under
src/tools/(or extend an existing one) and aggregate it insrc/tools/index.ts - New portal/currency: add a scraper under
src/exchange/providers/and the constant insrc/exchange/types.ts - New TMDB endpoint: add the call in
src/tmdb/movies.tsand the response type insrc/tmdb/types.ts - New host metric: add the section name to
SECTIONSinsrc/system/types.ts, the conversion insrc/system/normalize.ts, and the reader insrc/system/collect.ts - New shared capability: add a module under
src/common/kit/ - Build config changes: edit the root
tsup.config.ts