@iwas/mcp 1.0.1 → 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,173 +1,403 @@
1
1
  # @iwas/mcp
2
2
 
3
- > **Intelligent WiFi Access & Presence Service (IWAS) Model Context Protocol (MCP) CLI & SDK**
3
+ # IWAS MCP server
4
4
 
5
- Official command-line tool and developer SDK for connecting AI coding assistants (Claude Desktop, Cursor, Antigravity, Cline) and enterprise applications to the **IWAS** WiFi billing, captive portal, and network presence infrastructure.
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.
6
8
 
7
9
  [![npm version](https://img.shields.io/npm/v/@iwas/mcp.svg)](https://www.npmjs.com/package/@iwas/mcp)
8
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.
9
14
 
10
15
  ---
11
16
 
12
17
  ## Highlights
13
18
 
14
- - 🚀 **Zero Setup**: Run instantly via `npx @iwas/mcp` without cloning or pre-installing dependencies.
15
- - 🔐 **RFC 8628 Device Authorization**: Secure, headless browser-based login with standard OAuth 2.0 device flow.
16
- - 🛡️ **Enterprise Credential Security**: Integrates with native OS Keychains (macOS Keychain, Linux Secret Service) with atomic, permissions-hardened file fallbacks.
17
- - 🤖 **Native AI Assistant Support**: Plug-and-play Model Context Protocol (MCP) server for Claude, Cursor, and custom agentic frameworks.
18
- - 📦 **Standalone Zero-Dependency Binary**: Self-contained runtime bundle engineered for maximum reliability.
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`).
19
33
 
20
34
  ---
21
35
 
22
36
  ## Quickstart
23
37
 
24
- ### Run with `npx` (No installation needed)
38
+ ### 1. Authenticate Workstation
39
+
40
+ Run the login command once to authenticate your machine via OAuth 2.0 Device Flow:
25
41
 
26
42
  ```bash
27
- # Log in via interactive browser
28
- npx @iwas/mcp login --browser
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
29
48
 
30
- # Check session & authentication status
31
- npx @iwas/mcp status
49
+ # Headless / remote server login (prints verification URL & user code)
50
+ npx -y @iwas/mcp login --no-browser
32
51
  ```
33
52
 
34
- ### Install Globally
53
+ ### 2. Verify Session Status
54
+
55
+ Inspect your active profile and token remaining duration:
35
56
 
36
57
  ```bash
37
- npm install -g @iwas/mcp
58
+ # Check default production profile (https://getiwas.com)
59
+ npx -y @iwas/mcp status
38
60
 
39
- # Verify installation
40
- iwas --version
41
- iwas --help
61
+ # Check a specific server or local development instance
62
+ npx -y @iwas/mcp status --server http://localhost:3001
42
63
  ```
43
64
 
44
- *(Note: `iwas-mcp` is also provided as a backward-compatible command alias).*
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
+ ```
45
80
 
46
81
  ---
47
82
 
48
83
  ## Integrating with AI Assistants
49
84
 
50
- You can wire IWAS tools directly into your favorite AI environments via standard MCP configuration:
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.
51
88
 
52
89
  ### Claude Desktop (`claude_desktop_config.json`)
53
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
+
54
146
  ```json
55
147
  {
56
148
  "mcpServers": {
57
149
  "iwas": {
58
150
  "command": "npx",
59
- "args": ["-y", "@iwas/mcp"]
151
+ "args": ["-y", "@iwas/mcp", "mcp"],
152
+ "env": {
153
+ "IWAS_SERVER_URL": "https://getiwas.com"
154
+ }
60
155
  }
61
156
  }
62
157
  }
63
158
  ```
64
159
 
65
- ### Cursor / Cline / Antigravity
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`). |
66
171
 
67
- Configure an MCP server with command `npx` and arguments `["-y", "@iwas/mcp"]`. The CLI will automatically use your stored credentials from `iwas login`.
172
+ _Note: Custom OAuth Client IDs and Resource endpoints can be configured via `--client-id <id>` and `--resource <url>` CLI options._
68
173
 
69
174
  ---
70
175
 
71
- ## CLI Commands
176
+ ## CLI Reference
72
177
 
73
- ### 1. `iwas login`
74
- Authenticate your workstation or development environment using OAuth 2.0 Device Flow:
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`
194
+
195
+ Initiate an OAuth 2.0 Device Authorization Grant flow:
75
196
 
76
197
  ```bash
77
- # Interactive login with automatic browser opening
78
- iwas login --browser
198
+ # Standard browser login
199
+ iwas login
79
200
 
80
- # Connect to a custom or self-hosted IWAS gateway
81
- iwas login --server https://getiwas.com --browser
201
+ # Specify target server
202
+ iwas login --server http://localhost:3001
82
203
 
83
- # Headless / remote server login (shows verification URL & code)
204
+ # Headless / remote SSH session (no browser)
84
205
  iwas login --no-browser
85
206
 
86
- # Request custom scopes
207
+ # Custom least-privilege OAuth scopes
87
208
  iwas login --scopes "analytics:read session:read offline_access"
209
+
210
+ # Machine-readable JSON output for automated scripting
211
+ iwas login --json
88
212
  ```
89
213
 
90
- ### 2. `iwas status`
91
- Inspect the active authenticated profile, expiration status, and authorized scopes:
214
+ ### `iwas status`
215
+
216
+ Display authentication status, active profile, and human-readable expiration countdown:
92
217
 
93
218
  ```bash
219
+ # Check default profile
94
220
  iwas status
95
221
 
96
- # JSON output for automated scripting & CI/CD
222
+ # Check status for a specific server instance
223
+ iwas status --server http://localhost:3001
224
+
225
+ # Machine-readable JSON output
97
226
  iwas status --json
98
227
  ```
99
228
 
100
- ### 3. `iwas logout`
101
- Revoke active session grants on the server and remove stored credentials from the local vault:
229
+ ### `iwas logout`
230
+
231
+ Revoke active session grants on the server (RFC 7009) and purge stored credentials from the local vault:
102
232
 
103
233
  ```bash
234
+ # Logout from default server
104
235
  iwas logout
105
236
 
237
+ # Logout from a specific server instance
238
+ iwas logout --server http://localhost:3001
239
+
106
240
  # Force local cleanup even if the authentication server is unreachable
107
241
  iwas logout --force
108
242
  ```
109
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
251
+ ```
252
+
253
+ _Note: The `mcp` command strictly reserves stdout for JSON-RPC frames; passing `--json` will error._
254
+
110
255
  ---
111
256
 
112
- ## Machine-Readable JSON Mode (`--json`)
257
+ ## 🛠️ Complete MCP Capabilities Catalog (26 Tools + 2 Prompts)
258
+
259
+ ### 1. Analytics & Revenue Intelligence
260
+
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. |
267
+
268
+ ### 2. Sessions & FreeRADIUS Accounting
269
+
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. |
275
+
276
+ ### 3. Business Insights & Optimization
277
+
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. |
282
+
283
+ ### 4. Diagnostics & Network Observability
284
+
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. |
296
+
297
+ ### 5. Captive Portal Content & Announcements
298
+
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. |
309
+
310
+ ### 6. MCP Prompts (Pre-Configured Workflows)
311
+
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) |
113
316
 
114
- When passed the `--json` flag, all commands output structured, newline-delimited JSON events to `stdout`, while human-readable prompts and notices are redirected to `stderr`.
317
+ ---
115
318
 
116
- **Example: Initiating device login**
117
- ```json
118
- {"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}
119
- ```
319
+ ## Security & Credential Storage
120
320
 
121
- **Example: Successful login**
122
- ```json
123
- {"event":"login_success","success":true,"profileKey":"https://getiwas.com::iwas-cli","serverUrl":"https://getiwas.com","clientId":"iwas-cli","organizationId":"org_123","scope":"...","expiresAt":1792376265000}
124
- ```
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.
125
332
 
126
333
  ---
127
334
 
128
- ## TypeScript SDK & Programmatic Usage
335
+ ## Troubleshooting
129
336
 
130
- `@iwas/mcp` provides first-class TypeScript library exports for building custom agents and administrative tools:
337
+ ### 1. `No IWAS credentials for profile "..."`
131
338
 
132
- ```bash
133
- pnpm add @iwas/mcp
134
- # or
135
- npm install @iwas/mcp
136
- ```
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`.
137
346
 
138
- ### Subpath Exports
347
+ ### 3. macOS Keychain Permission Prompt
139
348
 
140
- * `@iwas/mcp` — Core entrypoint and package metadata.
141
- * `@iwas/mcp/client` — Programmatic MCP client transport and client interfaces.
142
- * `@iwas/mcp/auth` — Credential vault (`CredentialVault`), Token refresh manager (`TokenRefreshManager`), and OS Keychain utilities.
143
- * `@iwas/mcp/catalog` — Typed permission vocabulary and tool definition catalog.
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.
144
351
 
145
- ### Example: Automated Token Refresh in Node.js
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
+ ```
146
366
 
147
367
  ```typescript
148
- import { CredentialVault, TokenRefreshManager } from '@iwas/mcp/auth';
368
+ import { CredentialVault, TokenRefreshManager } from '@iwas/mcp/auth'
149
369
 
150
- const vault = new CredentialVault();
151
- const refreshManager = new TokenRefreshManager({ vault });
370
+ const vault = new CredentialVault()
371
+ const refreshManager = new TokenRefreshManager({ vault })
152
372
 
153
- // Automatically retrieves a valid token, refreshing via OAuth family rotation if near expiration
154
- 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)
155
376
  ```
156
377
 
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.
384
+
157
385
  ---
158
386
 
159
- ## Credential Storage & Security
387
+ ## Develop from Source
160
388
 
161
- Credentials are protected using defense-in-depth:
389
+ From the monorepo root:
162
390
 
163
- * **macOS**: Stored securely in the **macOS Keychain Services** under service `iwas-cli`.
164
- * **Linux**: Stored via standard **Secret Service API / libsecret**.
165
- * **Headless / Windows / Container Fallback**: Stored in `~/.config/iwas/credentials.json` with strict POSIX `0600` permissions and atomic write guarantees.
391
+ ```bash
392
+ # Build the package
393
+ pnpm --filter @iwas/mcp build
166
394
 
167
- You can explicitly enforce storage mode via the `IWAS_MCP_VAULT` environment variable:
168
- - `IWAS_MCP_VAULT=auto` (default: Keychain if available, fallback to file)
169
- - `IWAS_MCP_VAULT=keychain` (strict Keychain only)
170
- - `IWAS_MCP_VAULT=file` (strict file storage only)
395
+ # Run unit and integration tests (126 tests)
396
+ pnpm --filter @iwas/mcp test
397
+
398
+ # Run type check
399
+ pnpm --filter @iwas/mcp exec tsc --noEmit
400
+ ```
171
401
 
172
402
  ---
173
403
 
@@ -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 };