# How Environment Configuration Is Loaded and Used in castrozan/tcc

> Learn how castrozan/tcc loads environment configuration using dotenv and yargs. Discover how typed config objects power server components for efficient development.

- Repository: [Lucas Zanoni⠀⠀⠀⠀⠀ ⠀╱|、 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ (˚ˎ 。7 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ |、˜〵 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ じしˍ,)ノ/tcc](https://github.com/castrozan/tcc)
- Tags: internals
- Published: 2026-02-27

---

**Environment configuration in the castrozan/tcc repository is loaded via `dotenv` for dummy applications and through a merged CLI-arguments-and-environment-variables approach using `yargs` for the MCP OpenAPI server, then propagated through typed config objects to server components.**

The castrozan/tcc repository implements a dual-strategy approach to environment configuration management, separating simple dotenv-based loading for mock services from a sophisticated command-line-and-environment merger for the core MCP OpenAPI server. Understanding how these configuration patterns initialize runtime behavior is essential for deploying and extending the system.

## Configuration Loading Architectures

The repository employs two distinct patterns for loading environment configuration based on the component's complexity requirements.

### Dummy Applications: Direct dotenv Initialization

For the **professionals-dummy-app** and **equipments-dummy-app** services, configuration loading follows a straightforward pattern. The [`src/config/index.ts`](https://github.com/castrozan/tcc/blob/main/src/config/index.ts) file invokes `dotenv.config()` at the module level to parse local `.env` files, then exports a `Config` object selected by `process.env.NODE_ENV`.

Individual values such as `process.env.PORT` are read directly when the configuration map is built. The resulting configuration object is then imported by the HTTP server in [`src/infrastructure/web/open-api/server.ts`](https://github.com/castrozan/tcc/blob/main/src/infrastructure/web/open-api/server.ts), which passes the `port` value to the Hono server instance.

### MCP OpenAPI Server: Merged CLI and Environment Variables

The **mcp-openapi-server** package employs a more complex resolution strategy defined in [`src/config.ts`](https://github.com/castrozan/tcc/blob/main/src/config.ts). The `loadConfig()` function parses command-line arguments using **yargs**, then merges these with `process.env` fallbacks to construct an `OpenAPIMCPServerConfig` object.

This configuration resolution handles:
- **Transport type** (`TRANSPORT_TYPE` or `--transport`): Determines whether to use HTTP or stdio transport
- **HTTP settings** (`HTTP_PORT`, `HTTP_HOST`, `ENDPOINT_PATH`): Server binding parameters
- **OpenAPI spec source**: Exactly one of `OPENAPI_SPEC_PATH`, `OPENAPI_SPEC_FROM_STDIN`, or `OPENAPI_SPEC_INLINE` must be provided
- **API connectivity** (`API_BASE_URL`, `API_HEADERS`): Downstream service endpoints and authentication
- **Feature flags** (`DISABLE_ABBREVIATION`, `TOOLS_MODE`): Optional behavioral toggles

## Configuration Resolution Flow

The MCP OpenAPI server follows a strict initialization sequence when loading environment configuration:

1. **Process initialization** — [`src/index.ts`](https://github.com/castrozan/tcc/blob/main/src/index.ts) invokes `loadConfig()`
2. **Argument parsing** — `yargs` builds the `argv` object from process arguments
3. **Environment fallback** — For each configuration option, the code applies `argv.xxx ?? process.env.XXX` precedence
4. **Transport determination** — If `argv.transport === "http"` or `process.env.TRANSPORT_TYPE === "http"`, the transport type is set to `"http"`; otherwise defaults to `"stdio"`
5. **Spec source validation** — The function ensures exactly one of `--openapi-spec`, `--spec-from-stdin`, or `--spec-inline` is provided, recording the method as `url`, `file`, `stdin`, or `inline`
6. **Header parsing** — The `parseHeaders()` function transforms the string `"Key:Value,Another:Value"` into a `Record<string,string>`
7. **Object assembly** — All derived values are assembled into the immutable `OpenAPIMCPServerConfig` object

## Propagation to Runtime Components

Once loaded, the configuration object propagates through the application stack according to a strict dependency injection pattern.

The [`src/index.ts`](https://github.com/castrozan/tcc/blob/main/src/index.ts) entry point passes the config to `new OpenAPIServer(config)`, which stores the configuration internally. Inside the `OpenAPIServer` constructor located in [`src/server.ts`](https://github.com/castrozan/tcc/blob/main/src/server.ts), the configuration drives:

- **Server metadata**: `name` and `version` fields exposed to clients
- **ToolsManager construction**: Receives the config to determine which OpenAPI tools to load, which tools to filter, and whether abbreviation is disabled via `disableAbbreviation`
- **ApiClient creation**: Uses `config.apiBaseUrl` and `config.headers` to enable downstream API calls with correct base URLs and authentication headers
- **Transport selection**: The [`src/index.ts`](https://github.com/castrozan/tcc/blob/main/src/index.ts) file selects between `StreamableHttpServerTransport` (for HTTP) or `StdioServerTransport` (for stdio) based on `config.transportType`

## Code Implementation Examples

### Loading Configuration with CLI and Environment Merge

```typescript
// mcp-openapi-server/src/config.ts
export function loadConfig(): OpenAPIMCPServerConfig {
  const argv = yargs(hideBin(process.argv))
    .option("transport", { choices: ["stdio", "http"] })
    .option("port", { type: "number" })
    // …other options…
    .parseSync();

  // Transport resolution with environment fallback
  const transportType =
    argv.transport === "http" || process.env.TRANSPORT_TYPE === "http"
      ? "http"
      : "stdio";

  // HTTP settings fallback to env vars
  const httpPort = argv.port ?? (process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3000);
  const httpHost = argv.host || process.env.HTTP_HOST || "127.0.0.1";
  const endpointPath = argv.path || process.env.ENDPOINT_PATH || "/mcp";

  // Spec source handling (exactly one required)
  const specFromStdin = argv["spec-from-stdin"] || process.env.OPENAPI_SPEC_FROM_STDIN === "true";
  const specInline = argv["spec-inline"] || process.env.OPENAPI_SPEC_INLINE;
  const openApiSpec = argv["openapi-spec"] || process.env.OPENAPI_SPEC_PATH;
  
  const apiBaseUrl = argv["api-base-url"] || process.env.API_BASE_URL;
  const headers = parseHeaders(argv.headers || process.env.API_HEADERS);
  
  return {
    name: argv.name || process.env.SERVER_NAME || "mcp-openapi-server",
    version: argv["server-version"] || process.env.SERVER_VERSION || "1.0.0",
    apiBaseUrl,
    openApiSpec,
    specInputMethod,
    inlineSpecContent: specInline,
    headers,
    transportType,
    httpPort,
    httpHost,
    endpointPath,
    toolsMode: (argv.tools as "all" | "dynamic") || process.env.TOOLS_MODE || "all",
    disableAbbreviation: argv["disable-abbreviation"] ?? (process.env.DISABLE_ABBREVIATION === "true"),
  };
}

```

### Consuming Configuration to Initialize the Server

```typescript
// mcp-openapi-server/src/index.ts
import { loadConfig } from "./config";
import { OpenAPIServer } from "./server";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StreamableHttpServerTransport } from "./transport/StreamableHttpServerTransport";

async function main() {
  const config = loadConfig();                     // ← pulls from CLI & env
  const server = new OpenAPIServer(config);       // ← config drives server setup

  let transport;
  if (config.transportType === "http") {
    transport = new StreamableHttpServerTransport(
      config.httpPort!,
      config.httpHost,
      config.endpointPath,
    );
  } else {
    transport = new StdioServerTransport();
  }

  await server.start(transport);                  // ← server uses config internally
}
main();

```

### Dummy Application Configuration Pattern

```typescript
// professionals-dummy-app/src/config/index.ts
import dotenv from "dotenv";
import { Config } from "./types.js";

dotenv.config();                                   // ← reads .env file

const configs: Record<string, Config> = {
  development: { port: Number(process.env.PORT) || 3003 },
  test:        { port: Number(process.env.PORT) || 3003 },
  production:  { port: Number(process.env.PORT) || 3003 },
};

const environment = process.env.NODE_ENV ?? "development";
export default configs[environment] ?? configs.development;

```

## Summary

- **Dual strategies**: Dummy apps use simple `dotenv` loading in [`src/config/index.ts`](https://github.com/castrozan/tcc/blob/main/src/config/index.ts), while the MCP server uses `yargs` + `process.env` merging in [`src/config.ts`](https://github.com/castrozan/tcc/blob/main/src/config.ts)
- **Precedence rules**: CLI arguments take priority over environment variables using the `argv.xxx ?? process.env.XXX` pattern
- **Typed propagation**: The `OpenAPIMCPServerConfig` object provides a single source of truth passed from `loadConfig()` through `OpenAPIServer` to `ToolsManager` and `ApiClient`
- **Transport selection**: The `TRANSPORT_TYPE` environment variable or `--transport` CLI flag determines whether the server uses `StreamableHttpServerTransport` or `StdioServerTransport`
- **Validation requirements**: The MCP server requires exactly one OpenAPI spec source to be specified among file path, stdin, or inline options

## Frequently Asked Questions

### How does the MCP OpenAPI server prioritize CLI arguments over environment variables?

The `loadConfig()` function in [`mcp-openapi-server/src/config.ts`](https://github.com/castrozan/tcc/blob/main/mcp-openapi-server/src/config.ts) uses the nullish coalescing operator (`??`) and logical OR (`||`) to implement precedence. For most options, the pattern `argv.xxx ?? process.env.XXX` ensures that explicitly provided CLI arguments override environment variables, while environment variables provide defaults when CLI arguments are absent. For boolean flags like `disableAbbreviation`, the code checks both the argv property and the environment variable separately.

### What configuration options control the OpenAPI specification source?

The server accepts exactly one of three mutually exclusive options: `OPENAPI_SPEC_PATH` (or `--openapi-spec`) for file/URL paths, `OPENAPI_SPEC_FROM_STDIN` (or `--spec-from-stdin`) for piping specs via standard input, or `OPENAPI_SPEC_INLINE` (or `--spec-inline`) for direct string embedding. The `loadConfig()` function validates that only one method is specified and records this choice in the `specInputMethod` field of the configuration object.

### How are HTTP server parameters configured in the dummy applications?

The dummy applications read `process.env.PORT` after `dotenv.config()` initializes the environment. The [`src/config/index.ts`](https://github.com/castrozan/tcc/blob/main/src/config/index.ts) file maps `NODE_ENV` values to specific configuration objects, defaulting to port 3003 if `PORT` is undefined. This configuration is then consumed by [`src/infrastructure/web/open-api/server.ts`](https://github.com/castrozan/tcc/blob/main/src/infrastructure/web/open-api/server.ts) to bind the Hono server to the specified port.

### Which file is responsible for transport type selection?

Transport type selection occurs in two locations. The `loadConfig()` function in [`mcp-openapi-server/src/config.ts`](https://github.com/castrozan/tcc/blob/main/mcp-openapi-server/src/config.ts) determines the transport type by checking `argv.transport` and `process.env.TRANSPORT_TYPE`, defaulting to `"stdio"` if neither specifies `"http"`. Subsequently, [`src/index.ts`](https://github.com/castrozan/tcc/blob/main/src/index.ts) instantiates the appropriate transport class—either `StreamableHttpServerTransport` for HTTP mode or `StdioServerTransport` for stdio mode—based on the `config.transportType` value.