@iwas/mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,135 +1,406 @@
1
1
  # @iwas/mcp
2
2
 
3
- Intelligent WiFi Access & Presence Service (IWAS) Model Context Protocol (MCP) workspace package.
4
-
5
- > **Status: Standalone Distribution Package.** This package is bundled via `tsup` into a self-contained
6
- > binary (`dist/bin.js`) with zero monorepo dependencies, published to the NPM Registry under
7
- > `@iwas/mcp`. It exposes the `iwas` (and `iwas-mcp`) CLI supporting RFC 8628 Device Authorization,
8
- > native OS Keychain storage, and tool catalog definitions.
9
-
10
- ## Subpath Exports
11
-
12
- - `@iwas/mcp` — root entrypoint (version plus re-exports).
13
- - `@iwas/mcp/catalog` — re-exports the single permission vocabulary from `@iwas/auth/permissions`
14
- (FR-011). The MCP capability census is generated by the API drift gate (INV-MCP-14) and is
15
- deliberately not pinned here.
16
- - `@iwas/mcp/client` — programmatic MCP client interfaces.
17
- - `@iwas/mcp/auth` — MCP credential vault (`CredentialVault`), OS Keychain integration (`KeychainService`), profile normalization, and token redaction helpers (DEV-08).
18
-
19
- ## Credential Vault & Storage Security
20
-
21
- `@iwas/mcp/auth` provides secure credential storage for RFC 8628 Device Flow:
22
-
23
- - **Keychain Backend (Default on macOS & Linux):**
24
- - **Service:** `iwas-cli` (default)
25
- - **Account:** Profile key in canonical format `<origin>::<clientId>` (e.g. `https://getiwas.com::iwas-cli`).
26
- - macOS: Uses `/usr/bin/security` (`add-generic-password -U`, `find-generic-password -w`, `delete-generic-password`).
27
- - Linux: Uses `secret-tool` (`store`, `lookup`, `clear`) with secrets passed strictly via `stdin` (never exposed in `argv` or `ps`).
28
- - **Manual Verification on macOS:**
29
- ```bash
30
- # Check stored secret in macOS Keychain:
31
- security find-generic-password -s iwas-cli -a "https://getiwas.com::iwas-cli" -w
32
- ```
33
- - **File Fallback (`~/.config/iwas/credentials.json`):**
34
- - Active in headless CI, Docker containers, and on Windows (`%USERPROFILE%\.config\iwas\credentials.json`).
35
- - Respects `$XDG_CONFIG_HOME/iwas/credentials.json`.
36
- - Enforces strict POSIX permissions: directory `0700` (`rwx------`), file `0600` (`rw-------`).
37
- - Atomic write strategy: writes to temporary file, syncs to disk (`fsync`), and renames atomically.
38
- - Protects corrupt files: if the JSON is malformed or missing fields, the vault fails closed and refuses to overwrite.
39
- - **Security Considerations & Trade-offs:**
40
- > [!WARNING]
41
- > The file fallback stores serialized credentials in plaintext JSON protected only by OS file permissions (`0600`). On systems where native OS keychain utilities are available, the keychain backend is strongly preferred.
42
- > On macOS workstations, `/usr/bin/security` accepts `-w <secret>` via arguments which is transiently visible in the process table `ps` to local processes running under the same user UID.
43
- - **Conflict Resolution & Precedence:**
44
- - Credentials include an internal `updatedAt` timestamp (`Date.now()`). When operating in `auto` mode with both keychain and file present, the vault compares timestamps (`updatedAt ?? expiresAt`) and automatically migrates newer file credentials into the keychain while purging stale copies (resolving split-backend desync).
45
- - **Environment Override:**
46
- - Set `IWAS_MCP_VAULT=auto` (default: keychain if available, fallback to file).
47
- - Set `IWAS_MCP_VAULT=keychain` (strict keychain; throws if unavailable or locked).
48
- - Set `IWAS_MCP_VAULT=file` (forces file storage; useful for headless CI).
49
- - Set `IWAS_MCP_REAL_KEYCHAIN=1` (opt-in to execute real host macOS Keychain tests).
50
-
51
- ## Local CLI
52
-
53
- Executable binary is `iwas` (with `iwas-mcp` provided as a backward-compatible alias):
3
+ # IWAS MCP server
4
+
5
+ [`@iwas/mcp`](https://www.npmjs.com/package/@iwas/mcp) is the official MCP server for [IWAS](https://getiwas.com) — the enterprise WiFi billing, captive portal, FreeRADIUS control, and network presence intelligence platform.
6
+
7
+ It operates over **stdio** as a secure bridge: it signs in via OAuth 2.0 Device Authorization (RFC 8628), persists credentials to native OS Keychains, transparently rotates expired tokens behind a cross-process lock (`VaultLock`), and proxies MCP tool calls to the IWAS API with authorized Bearer tokens.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@iwas/mcp.svg)](https://www.npmjs.com/package/@iwas/mcp)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
11
+ [![Node: 18+](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org)
12
+
13
+ > 💡 **Dual-Transport Architecture:** Every IWAS API deployment also exposes a native Streamable HTTP MCP endpoint at `/mcp` (e.g. `https://getiwas.com/mcp` or `http://localhost:3001/mcp`) supporting RFC 9728 Protected Resource Metadata and RFC 9449 DPoP proofs. If your client supports direct Streamable HTTP (such as cloud agents or webhooks), you can connect directly. For desktop AI IDEs that communicate over stdio, use this `@iwas/mcp` bridge.
14
+
15
+ ---
16
+
17
+ ## Highlights
18
+
19
+ - 🚀 **Zero Setup**: Run instantly via `npx -y @iwas/mcp` without cloning the repository or pre-installing dependencies.
20
+ - 🔐 **RFC 8628 Device Authorization**: Browser-based interactive login supporting Passkeys (Touch ID/Face ID), Google OAuth, and 2FA.
21
+ - 🛡️ **Native OS Keychain Security**: Stores tokens directly in **Apple Keychain Services** (macOS) and **Secret Service** (Linux), with atomic POSIX `0600` file fallback.
22
+ - 🔄 **Silent Token Rotation**: Seamless background refresh before expiration (`TokenRefreshManager`) and automatic recovery on HTTP 401.
23
+ - 🔒 **Concurrency-Safe (`VaultLock`)**: Cross-process file locking ensures multiple IDE windows or concurrent subagents never corrupt token families.
24
+ - 🤖 **Plug-and-Play AI Assistant Support**: Instant integration for Claude Desktop, Cursor, Antigravity IDE, Windsurf, and VS Code (Cline / Roo Code).
25
+ - 📦 **Dual Executable**: Provides both `iwas` and backward-compatible `iwas-mcp` CLI commands.
26
+
27
+ ---
28
+
29
+ ## Prerequisites
30
+
31
+ - **Node.js 18+** (Node.js 20+ recommended)
32
+ - An accessible IWAS API instance (defaults to production `https://getiwas.com`, or local development `http://localhost:3001`).
33
+
34
+ ---
35
+
36
+ ## Quickstart
37
+
38
+ ### 1. Authenticate Workstation
39
+
40
+ Run the login command once to authenticate your machine via OAuth 2.0 Device Flow:
54
41
 
55
42
  ```bash
56
- # Display help & version
57
- iwas --help
58
- iwas --version
43
+ # Interactive login (automatically opens browser)
44
+ npx -y @iwas/mcp login
45
+
46
+ # Connect to a self-hosted or local development instance
47
+ npx -y @iwas/mcp login --server http://localhost:3001
48
+
49
+ # Headless / remote server login (prints verification URL & user code)
50
+ npx -y @iwas/mcp login --no-browser
51
+ ```
52
+
53
+ ### 2. Verify Session Status
54
+
55
+ Inspect your active profile and token remaining duration:
56
+
57
+ ```bash
58
+ # Check default production profile (https://getiwas.com)
59
+ npx -y @iwas/mcp status
60
+
61
+ # Check a specific server or local development instance
62
+ npx -y @iwas/mcp status --server http://localhost:3001
63
+ ```
64
+
65
+ _Example Output:_
66
+
67
+ ```text
68
+ ============================================================
69
+ IWAS MCP Auth Status
70
+ ============================================================
71
+
72
+ - Profile: https://getiwas.com::iwas-cli
73
+ - Server: https://getiwas.com
74
+ - Client ID: iwas-cli
75
+ - Status: 🟢 Active
76
+ - Organization: org_123 (or platform scope)
77
+ - Resource: https://getiwas.com/mcp
78
+ - Expires: 2026-10-19T10:43:18.314Z (48m 51s remaining)
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Integrating with AI Assistants
84
+
85
+ Wire IWAS tools into your AI coding assistant using standard MCP configuration.
86
+
87
+ > ⚠️ **Important:** Ensure you have completed `npx -y @iwas/mcp login` once before launching your MCP client.
88
+
89
+ ### Claude Desktop (`claude_desktop_config.json`)
90
+
91
+ On macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
92
+ On Windows: `%APPDATA%\Claude\claude_desktop_config.json`
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "iwas": {
98
+ "command": "npx",
99
+ "args": ["-y", "@iwas/mcp", "mcp"],
100
+ "env": {
101
+ "IWAS_SERVER_URL": "https://getiwas.com"
102
+ }
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Cursor (`.cursor/mcp.json` or Global Features > MCP)
109
+
110
+ ```json
111
+ {
112
+ "mcpServers": {
113
+ "iwas": {
114
+ "command": "npx",
115
+ "args": ["-y", "@iwas/mcp", "mcp"],
116
+ "env": {
117
+ "IWAS_SERVER_URL": "https://getiwas.com"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### Antigravity IDE (`mcp_config.json`)
125
+
126
+ In `~/.gemini/config/mcp_config.json` or `.agents/mcp_config.json`:
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "iwas": {
132
+ "command": "npx",
133
+ "args": ["-y", "@iwas/mcp", "mcp"],
134
+ "env": {
135
+ "IWAS_SERVER_URL": "https://getiwas.com"
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ### VS Code (Cline / Roo Code)
143
+
144
+ In Cline MCP settings (`cline_mcp_settings.json`):
145
+
146
+ ```json
147
+ {
148
+ "mcpServers": {
149
+ "iwas": {
150
+ "command": "npx",
151
+ "args": ["-y", "@iwas/mcp", "mcp"],
152
+ "env": {
153
+ "IWAS_SERVER_URL": "https://getiwas.com"
154
+ }
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ > 💡 **Local Development:** When pairing against a local NestJS backend, set `"IWAS_SERVER_URL": "http://localhost:3001"`.
161
+
162
+ ---
163
+
164
+ ## Environment Variables
165
+
166
+ | Variable | Default | Description |
167
+ | :---------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
168
+ | `IWAS_SERVER_URL` | `https://getiwas.com` | Base origin of the IWAS API & Auth server (e.g. `http://localhost:3001` for local dev). |
169
+ | `IWAS_MCP_VAULT` | `auto` | Credential storage mode: `auto` (OS Keychain with file fallback), `keychain` (strict OS Keychain only), or `file` (strict POSIX file only). |
170
+ | `XDG_CONFIG_HOME` | `~/.config` | Custom base directory for credentials file fallback (`$XDG_CONFIG_HOME/iwas/credentials.json`). |
171
+
172
+ _Note: Custom OAuth Client IDs and Resource endpoints can be configured via `--client-id <id>` and `--resource <url>` CLI options._
173
+
174
+ ---
175
+
176
+ ## CLI Reference
177
+
178
+ The package provides two identical executable binaries: `iwas` and `iwas-mcp`.
179
+
180
+ ```text
181
+ USAGE:
182
+ iwas <command> [options]
183
+ iwas-mcp <command> [options]
184
+
185
+ COMMANDS:
186
+ login Authenticate using OAuth 2.0 Device Authorization Grant (RFC 8628)
187
+ status Display current authentication status for a server profile
188
+ logout Remove stored credentials for a server profile
189
+ mcp Run a stdio MCP bridge using the stored device-flow credentials
190
+ help Display help information
191
+ ```
192
+
193
+ ### `iwas login`
59
194
 
60
- # Log in using OAuth 2.0 Device Authorization Grant (RFC 8628)
195
+ Initiate an OAuth 2.0 Device Authorization Grant flow:
196
+
197
+ ```bash
198
+ # Standard browser login
61
199
  iwas login
62
- iwas login --server https://getiwas.com --no-browser
63
- iwas login --scopes "analytics:read session:read offline_access" --json
64
200
 
65
- # Inspect active authentication status for a server profile
201
+ # Specify target server
202
+ iwas login --server http://localhost:3001
203
+
204
+ # Headless / remote SSH session (no browser)
205
+ iwas login --no-browser
206
+
207
+ # Custom least-privilege OAuth scopes
208
+ iwas login --scopes "analytics:read session:read offline_access"
209
+
210
+ # Machine-readable JSON output for automated scripting
211
+ iwas login --json
212
+ ```
213
+
214
+ ### `iwas status`
215
+
216
+ Display authentication status, active profile, and human-readable expiration countdown:
217
+
218
+ ```bash
219
+ # Check default profile
66
220
  iwas status
67
- iwas status --server https://getiwas.com --json
68
221
 
69
- # Log out and remove credentials from vault & keychain
222
+ # Check status for a specific server instance
223
+ iwas status --server http://localhost:3001
224
+
225
+ # Machine-readable JSON output
226
+ iwas status --json
227
+ ```
228
+
229
+ ### `iwas logout`
230
+
231
+ Revoke active session grants on the server (RFC 7009) and purge stored credentials from the local vault:
232
+
233
+ ```bash
234
+ # Logout from default server
70
235
  iwas logout
71
- iwas logout --server https://getiwas.com --json
236
+
237
+ # Logout from a specific server instance
238
+ iwas logout --server http://localhost:3001
239
+
240
+ # Force local cleanup even if the authentication server is unreachable
241
+ iwas logout --force
242
+ ```
243
+
244
+ ### `iwas mcp`
245
+
246
+ Launch the stdio-to-HTTP MCP bridge used by AI coding assistants. Reads tokens automatically from native OS Keychain and routes JSON-RPC frames:
247
+
248
+ ```bash
249
+ iwas mcp
250
+ iwas mcp --server http://localhost:3001
72
251
  ```
73
252
 
74
- Auth server origin can also be set via the `IWAS_SERVER_URL` environment variable.
253
+ _Note: The `mcp` command strictly reserves stdout for JSON-RPC frames; passing `--json` will error._
254
+
255
+ ---
256
+
257
+ ## 🛠️ Complete MCP Capabilities Catalog (26 Tools + 2 Prompts)
258
+
259
+ ### 1. Analytics & Revenue Intelligence
75
260
 
76
- ### OAuth Scopes & Least-Privilege Defaults
261
+ | Tool | Description |
262
+ | :--- | :--- |
263
+ | `get_dashboard_stats` | Real-time network telemetry, active WiFi guests, revenue today, and router connectivity. Aligned with `/api/admin/stats`. |
264
+ | `get_revenue_breakdown` | Granular revenue breakdown by time period, package tier, venue, and payment provider. Aligned with `/api/admin/revenue`. |
265
+ | `list_packages` | Paginated listing of hotspot billing packages, duration, bandwidth limits, and pricing. |
266
+ | `get_package` | Retrieve full configuration and tax category for a specific package ID. |
77
267
 
78
- Default scopes requested during `iwas login` follow **strict least-privilege**: only read-only capabilities (`*:read`) plus `offline_access` are requested. Mutating or administrative actions (such as `:manage`, `:delete`, `:control`, `:issue`, `:write`) require explicitly passing the required permissions via `--scopes`.
268
+ ### 2. Sessions & FreeRADIUS Accounting
79
269
 
80
- > [!NOTE]
81
- > Platform-only read scopes (such as `diagnostics:read`) are included in `DEFAULT_READ_ONLY_SCOPES` for platform administrators, while tenant callers have access strictly evaluated and gated by the server's RBAC engine at execution time.
270
+ | Tool | Description |
271
+ | :--- | :--- |
272
+ | `list_active_sessions` | Inspect currently connected captive portal and hotspot guest sessions. |
273
+ | `list_today_sessions` | Query all guest sessions recorded for the current day with `revenueToday` summary. |
274
+ | `get_session_accounting` | Granular byte count, duration, and FreeRADIUS RFC 2866 accounting usage per session ID. |
82
275
 
83
- ### Machine-Readable JSON Mode (`--json`)
276
+ ### 3. Business Insights & Optimization
84
277
 
85
- When `--json` is passed, machine consumers receive newline-delimited JSON events on `stdout`:
278
+ | Tool | Description |
279
+ | :--- | :--- |
280
+ | `summarize_package_performance` | Statistical duration, ARPU, and conversion metrics across billing plans (default 7-day lookback). |
281
+ | `suggest_promo_windows` | AI-driven revenue timing analysis to identify optimal discount and marketing windows. |
86
282
 
87
- 1. **Authorization initiated (`device_authorization` event):** Emitted immediately after obtaining the device code, providing headless runners and IDE extensions with the verification details without waiting for token approval:
88
- ```json
89
- {"event":"device_authorization","user_code":"WDJB-MJHT","verification_uri":"https://getiwas.com/device","verification_uri_complete":"https://getiwas.com/device?user_code=WDJB-MJHT","expires_in":600,"interval":5}
90
- ```
91
- *Security:* per RFC 8628 §3.2, `device_code` is strictly kept internal to the CLI and never included in this payload.
283
+ ### 4. Diagnostics & Network Observability
92
284
 
93
- 2. **Authorization completed (`login_success` event):**
94
- ```json
95
- {"event":"login_success","success":true,"profileKey":"https://getiwas.com::iwas-cli","serverUrl":"https://getiwas.com","clientId":"iwas-cli","organizationId":"org_123","scope":"...","resource":"...","expiresAt":1792376265000}
96
- ```
285
+ | Tool | Description |
286
+ | :--- | :--- |
287
+ | `read_service_health` | Real-time liveness, readiness, and database connectivity checks. Valid authentication required. |
288
+ | `get_mcp_span` | Retrieve end-to-end execution span telemetry for an MCP request by `requestId` or numeric ID. |
289
+ | `get_error_by_request_id` _(Platform Staff)_ | Deep diagnostic trace and error detail for a specific failed `requestId`. |
290
+ | `list_error_events` _(Platform Staff)_ | Fetch recent unhandled exceptions, paths, and sanitized stack traces. |
291
+ | `list_flow_events` _(Platform Staff)_ | Interactive multi-stage journey lifecycle traces (OAuth, login, checkout). |
292
+ | `list_issues` _(Platform Staff)_ | Recurring incident clusters, severity badges, and automated triage statuses. |
293
+ | `update_issue_status` _(Platform Staff)_ | Update diagnostic issue triage state (`open`, `investigating`, `resolved`, `ignored`). |
294
+ | `list_audit_logs` | Query tamper-evident administrative action history. |
295
+ | `query_system_logs` _(Platform Staff)_ | Execute structured queries against system JSONL infrastructure logs. |
97
296
 
98
- 3. **Error event:**
99
- ```json
100
- {"event":"error","error":"Invalid OAuth client: client not found"}
101
- ```
297
+ ### 5. Captive Portal Content & Announcements
102
298
 
103
- *Stream Isolation:* In `--json` mode, `stdout` is strictly reserved for structured JSON events. Human-readable interactive prompts, code highlights, and browser warnings are routed to `stderr`.
299
+ | Tool | Description |
300
+ | :--- | :--- |
301
+ | `blog_list_posts` | List captive portal venue announcements, promotions, and articles with filters. |
302
+ | `blog_get_post` | Retrieve full content and metadata of a specific portal post by ID or slug. |
303
+ | `blog_create_post` | Author a new captive portal announcement with Markdown, SEO tags, and translation pairing. |
304
+ | `blog_update_post` | Update title, content, published status, or cover image. |
305
+ | `blog_delete_post` | Soft-delete an obsolete portal announcement. |
306
+ | `blog_import_media` | Download an external image from a public URL and store it in IWAS media CDN storage. |
307
+ | `blog_request_upload_url` | Request a presigned URL for direct client-side media uploads. |
308
+ | `blog_upload_media_base64` | Direct Base64 media asset upload (max 10MB decoded) for portal banners. |
104
309
 
105
- ## Automatic Token Refresh & Concurrency Control (DEV-10)
310
+ ### 6. MCP Prompts (Pre-Configured Workflows)
106
311
 
107
- `@iwas/mcp` provides automatic, transparent token refresh and concurrency-safe credential management:
312
+ | Prompt | Description | Parameters |
313
+ | :--- | :--- | :--- |
314
+ | `morning_revenue_digest` | Multi-step workflow to analyze 24-hour financial metrics, peak hours, and underperforming billing packages. | `lookbackDays` (1..30, optional) |
315
+ | `investigate_api_failure` | Diagnostic workflow to trace an error by `requestId`, inspect span telemetry, and correlate recurring issues. | `requestId` (string, optional) |
316
+
317
+ ---
318
+
319
+ ## Security & Credential Storage
320
+
321
+ `@iwas/mcp` applies defense-in-depth principles to protect developer credentials:
322
+
323
+ 1. **Native OS Keychains**:
324
+ - **macOS**: Stored securely in **Apple Keychain Services** under service name `iwas-cli` with account key `<serverUrl>::<clientId>`.
325
+ - **Linux**: Stored via standard **Secret Service API / libsecret**.
326
+ 2. **Hardened File Fallback**:
327
+ - On headless environments, Docker containers, or Windows, credentials fall back to `~/.config/iwas/credentials.json` with strict POSIX `0700` directory and `0600` file permissions.
328
+ 3. **Cross-Process Concurrency (`VaultLock`)**:
329
+ - Acquires an atomic file lock (`~/.config/iwas/credentials.lock`) during token refresh. Prevents race conditions and token family invalidation when multiple IDE windows or subagents make concurrent requests.
330
+ 4. **Zero Token Leakage**:
331
+ - Tokens and secrets are automatically redacted from stdout, stderr, logs, and error traces.
332
+
333
+ ---
334
+
335
+ ## Troubleshooting
336
+
337
+ ### 1. `No IWAS credentials for profile "..."`
338
+
339
+ - **Cause:** The machine has not been authenticated yet.
340
+ - **Solution:** Run `npx -y @iwas/mcp login` (or `npx -y @iwas/mcp login --server http://localhost:3001` for local development).
341
+
342
+ ### 2. `fetch failed: connection closed` or `ECONNREFUSED`
343
+
344
+ - **Cause:** The IWAS backend server specified by `IWAS_SERVER_URL` is offline or unreachable.
345
+ - **Solution:** Verify the API server is running (e.g. `pnpm dev:api` on `http://localhost:3001`) or check internet connectivity to `https://getiwas.com`.
346
+
347
+ ### 3. macOS Keychain Permission Prompt
348
+
349
+ - **Cause:** macOS prompts once when a new Node.js binary accesses Keychain Services items created under `iwas-cli`.
350
+ - **Solution:** Click **"Always Allow"** in the macOS dialog. Alternatively, set `IWAS_MCP_VAULT=file` in your environment to use file-based storage.
351
+
352
+ ### 4. `The mcp command reserves stdout for MCP frames; --json is not supported`
353
+
354
+ - **Cause:** Passing `--json` to `iwas mcp`.
355
+ - **Solution:** The MCP stdio protocol requires raw JSON-RPC on stdout. Do not pass `--json` to the `mcp` command.
356
+
357
+ ---
358
+
359
+ ## Programmatic TypeScript SDK Usage
360
+
361
+ `@iwas/mcp` exports programmatic primitives for custom agent developers:
362
+
363
+ ```bash
364
+ pnpm add @iwas/mcp
365
+ ```
108
366
 
109
367
  ```typescript
110
- import { CredentialVault, TokenRefreshManager } from '@iwas/mcp/auth';
368
+ import { CredentialVault, TokenRefreshManager } from '@iwas/mcp/auth'
111
369
 
112
- const vault = new CredentialVault();
113
- const refreshManager = new TokenRefreshManager({ vault });
370
+ const vault = new CredentialVault()
371
+ const refreshManager = new TokenRefreshManager({ vault })
114
372
 
115
- // Automatically returns a valid access token, refreshing if within safety margin (60s)
116
- const accessToken = await refreshManager.getValidAccessToken('https://getiwas.com::iwas-cli');
373
+ // Automatically retrieves a valid token, refreshing via OAuth family rotation if near expiration:
374
+ const credentials = await refreshManager.getValidCredentials('https://getiwas.com::iwas-cli')
375
+ console.log('Access token:', credentials.accessToken)
117
376
  ```
118
377
 
119
- ### Concurrency Guarantees
120
- - **In-process Single-flight:** Multiple concurrent asynchronous calls (both automatic and explicit force `refresh()`) for the same profile collapse to exactly one HTTP refresh request.
121
- - **Cross-process Mutual Exclusion (`VaultLock`):** Protects `<config-dir>/iwas/credentials.lock` via atomic `wx` creation (mode `0600`), dedicated app directory `0700` verification, and default 60s bounded timeout.
122
- - **Double-Checked Locking:** After acquiring the cross-process lock, the vault is re-read. If another concurrent process has already rotated the token family, the refreshed token is used immediately without issuing a redundant request.
123
- - **Active Process Immunity & Dead PID Reclaim (M1):** On the same host, a lock owned by a running process (`process.kill(pid, 0)`) is **never** reclaimed or stolen regardless of age, preventing token family destruction during slow network operations. Stale lock recovery occurs only when the holding PID is confirmed dead. *Cross-host/network FS tradeoff:* Across different machines or on shared network mounts (e.g. NFS/SMB), remote PIDs cannot be probed, so locks older than `staleTimeoutMs = 60s` are reclaimed based on lease age; configure a higher `staleTimeoutMs` for multi-host setups with operations exceeding 60s.
124
- - **TOCTOU-Safe Reclamation & Inode Verification:** Stale locks are verified via hardlink (`linkSync`) + `nlink` checks and inode matching before unlinking, ensuring racing processes acquiring new locks are never unlinked or overwritten. If the filesystem does not support hardlinks, reclamation fails closed (the caller times out) rather than risk deleting a live lock; remove the stale lock manually in that environment.
125
- - **Strictly Scoped Terminal Errors (M2):** ONLY `invalid_grant` (revoked/expired refresh token) and `invalid_client` purge local vault credentials and raise `ReauthenticationRequiredError`. Non-terminal 4xx errors (e.g. `invalid_request`, `invalid_scope`) throw sanitized errors while safely preserving credentials in the vault.
126
- - **Transient Retries & 30s Reuse Grace Window:** Network errors and 5xx server errors retry with bounded exponential backoff and never wipe stored credentials. Single retry on POST is protected by Better Auth's 30s refresh token reuse grace window (`MCP-OAUTH-04`).
127
- - **Atomic Persistence Failure Handling:** If saving rotated credentials to the vault fails, the profile is purged and raises `ReauthenticationRequiredError` ('persistence_failure') to avoid repeated replay of stale refresh tokens. Secret tokens are strictly redacted from diagnostic logs and error messages.
378
+ ### Subpath Exports:
379
+
380
+ - `@iwas/mcp` — Core entrypoint and metadata.
381
+ - `@iwas/mcp/auth` — `CredentialVault`, `TokenRefreshManager`, `VaultLock`, and Device Flow utilities.
382
+ - `@iwas/mcp/catalog` — Typed permission vocabulary, tool schemas, and prompt definitions.
383
+ - `@iwas/mcp/client` — MCP client transport and Streamable HTTP utilities.
128
384
 
129
- ## Development
385
+ ---
386
+
387
+ ## Develop from Source
388
+
389
+ From the monorepo root:
130
390
 
131
391
  ```bash
132
- pnpm --filter @iwas/mcp build # emits dist/*.js and dist/*.d.ts
133
- pnpm --filter @iwas/mcp test # compiles, then runs test suite
392
+ # Build the package
393
+ pnpm --filter @iwas/mcp build
394
+
395
+ # Run unit and integration tests (126 tests)
396
+ pnpm --filter @iwas/mcp test
397
+
398
+ # Run type check
134
399
  pnpm --filter @iwas/mcp exec tsc --noEmit
135
400
  ```
401
+
402
+ ---
403
+
404
+ ## License
405
+
406
+ MIT © [9xlayer](https://getiwas.com)
@@ -395,6 +395,10 @@ declare function pollDeviceToken(serverUrl: string, params: {
395
395
  * Orchestrates the full login flow, saves credentials in vault, and formats output.
396
396
  */
397
397
  declare function executeLogin(options?: DeviceLoginOptions): Promise<DeviceLoginResult>;
398
+ /**
399
+ * Formats a duration in seconds into human-readable units (e.g. "1d 2h 30m 15s", "52m 1s", or "45s").
400
+ */
401
+ declare function formatRemainingDuration(totalSeconds: number): string;
398
402
  /**
399
403
  * Checks and outputs stored authentication status for the given profile.
400
404
  */
@@ -627,5 +631,5 @@ declare class TokenRefreshManager {
627
631
  private performTokenExchange;
628
632
  }
629
633
 
630
- export { CredentialVault, DEFAULT_CLI_CLIENT_ID, DEFAULT_KEYCHAIN_SERVICE, DEFAULT_READ_ONLY_SCOPES, DEFAULT_SCOPES, DEFAULT_SERVER_URL, DEVICE_CODE_GRANT_TYPE, DefaultKeychainRunner, HttpError, KeychainService, ReauthenticationRequiredError, TokenRefreshManager, VaultLock, VaultLockError, VaultLockTimeoutError, decodeJwtPayload, defaultIsProcessAlive, executeLogin, executeLogout, executeStatus, isCredentialExpired, isLocalhost, isValidStoredCredentials, normalizeProfileKey, openBrowser, pollDeviceToken, redactCredentials, requestDeviceCode, resolveAuthServerUrl, resolveMcpResourceUrl, sanitizeSecretString, sendRequest, validateScopes, withVaultLock };
634
+ export { CredentialVault, DEFAULT_CLI_CLIENT_ID, DEFAULT_KEYCHAIN_SERVICE, DEFAULT_READ_ONLY_SCOPES, DEFAULT_SCOPES, DEFAULT_SERVER_URL, DEVICE_CODE_GRANT_TYPE, DefaultKeychainRunner, HttpError, KeychainService, ReauthenticationRequiredError, TokenRefreshManager, VaultLock, VaultLockError, VaultLockTimeoutError, decodeJwtPayload, defaultIsProcessAlive, executeLogin, executeLogout, executeStatus, formatRemainingDuration, isCredentialExpired, isLocalhost, isValidStoredCredentials, normalizeProfileKey, openBrowser, pollDeviceToken, redactCredentials, requestDeviceCode, resolveAuthServerUrl, resolveMcpResourceUrl, sanitizeSecretString, sendRequest, validateScopes, withVaultLock };
631
635
  export type { CredentialVaultOptions, DeviceAuthorizationResponse, DeviceLoginOptions, DeviceLoginResult, HttpResponse, KeychainCommandRunner, KeychainOptions, KeychainRunResult, LogoutOptions, LogoutResult, McpCredentials, OAuthTokenErrorResponse, OAuthTokenSuccessResponse, RequestOptions, SpawnFunction, StoredMcpCredentials, TokenRefreshManagerOptions, VaultBackendType, VaultLockData, VaultLockOptions };