0nmcp 2.2.0 → 2.4.0

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
@@ -19,21 +19,21 @@
19
19
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen?style=flat-square)](https://nodejs.org)
20
20
  [![MCP](https://img.shields.io/badge/MCP-Compatible-blueviolet?style=flat-square)](https://modelcontextprotocol.io)
21
21
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
22
- [![Services](https://img.shields.io/badge/services-48+-blue?style=flat-square)](#-supported-services)
23
- [![Tools](https://img.shields.io/badge/tools-819-orange?style=flat-square)](#-all-tools)
22
+ [![Services](https://img.shields.io/badge/services-53+-blue?style=flat-square)](#-supported-services)
23
+ [![Tools](https://img.shields.io/badge/tools-850-orange?style=flat-square)](#-all-tools)
24
24
  [![Community](https://img.shields.io/badge/community-1000%2B_devs-ff6600?style=flat-square)](#-community)
25
25
  [![Website](https://img.shields.io/badge/Website-0nmcp.com-ff6b35?style=flat-square)](https://0nmcp.com)
26
26
  [![GitHub Discussions](https://img.shields.io/github/discussions/0nork/0nMCP?style=flat-square&label=discussions)](https://github.com/0nork/0nMCP/discussions)
27
27
 
28
- **819 tools. 48 services. Zero configuration. One natural language interface.**
28
+ **850 tools. 53 services. Zero configuration. One natural language interface.**
29
29
 
30
- [Website](https://0nmcp.com) · [Quick Start](#-installation) · [Services](#-supported-services) · [819 Tools](#-all-tools) · [.0n Standard](#-the-0n-standard) · [Unlocks](#-unlocks) · [Community](https://0nmcp.com/community)
30
+ [Website](https://0nmcp.com) · [Quick Start](#-installation) · [Services](#-supported-services) · [850 Tools](#-all-tools) · [.0n Standard](#-the-0n-standard) · [Unlocks](#-unlocks) · [Community](https://0nmcp.com/community)
31
31
 
32
32
  </div>
33
33
 
34
34
  ---
35
35
 
36
- > **v2.2.0** — 819 tools across 48 services in 19 categories. 958 total capabilities. Now with **22 new services** (QuickBooks, Asana, Intercom, Dropbox, WhatsApp, Instagram, X/Twitter, TikTok, Google Ads, Facebook Ads, Plaid, Square, TikTok Ads, X Ads, LinkedIn Ads, Instagram Ads, Smartlead, Zapier, MuleSoft, Azure, Pipedrive, LinkedIn), **Business Deed Transfer**, **0nVault Containers** (Patent Pending #63/990,046), **Application Engine**, **Workflow Runtime** + **HTTP Server**, and **portable AI Brain bundles**. [See what's new](#-whats-new-in-v22).
36
+ > **v2.3.0** — 850 tools across 53 services in 23 categories. 1,142 total capabilities. New in v2.3: **5 new services** (Cloudflare, GoDaddy, n8n, Pabbly, Make), **Resend expanded** (3→67 endpoints), **ACTION_ALIASES conversion layer** for intuitive .0n workflow authoring, **connection auto-enrichment** (locationId, pipelineId injected from .0n files), and **enhanced API validation** (CRM, Anthropic, Vercel). [See what's new](#-whats-new-in-v23).
37
37
 
38
38
  ---
39
39
 
@@ -180,11 +180,16 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) o
180
180
  | **Microsoft Azure** | Cloud | Resources, resource groups, storage, VMs via ARM |
181
181
  | **Pipedrive** | CRM | Deals, persons, organizations, activities, pipelines, notes |
182
182
  | **LinkedIn** | Social | Posts, profile, connections, organization pages |
183
+ | **Cloudflare** | Cloud | DNS zones, records, workers, KV storage, page rules, firewall |
184
+ | **GoDaddy** | Cloud | Domains, DNS, availability, agreements, renewals |
185
+ | **n8n** | Automation | Workflows, executions, credentials — self-hosted automation |
186
+ | **Pabbly** | Automation | Workflows, triggers, actions, connections |
187
+ | **Make** | Automation | Scenarios, organizations, data stores, connections |
183
188
  | **CRM** | CRM | **245 tools** — contacts, conversations, calendars, invoices, payments, products, pipelines, social media, custom objects, and more |
184
189
 
185
- **48 services. 819 tools. 19 categories. One interface.**
190
+ **53 services. 850 tools. 23 categories. One interface.**
186
191
 
187
- > **More coming:** AWS S3, Vercel, Cloudflare, Firebase, Figma, DocuSign, Twilio Flex...
192
+ > **More coming:** AWS S3, Vercel, Firebase, Figma, DocuSign, Twilio Flex...
188
193
 
189
194
  ---
190
195
 
@@ -269,7 +274,7 @@ One tool call. Everything deployed:
269
274
  | `connect_service` | Connect a new service with credentials |
270
275
  | `disconnect_service` | Remove a connected service |
271
276
  | `list_connections` | See what's connected and capability counts |
272
- | `list_available_services` | Browse all 48 services grouped by category |
277
+ | `list_available_services` | Browse all 53 services grouped by category |
273
278
  | `get_service_info` | Deep dive on a specific service — endpoints, auth, capabilities |
274
279
  | `api_call` | Direct API call to any connected service endpoint |
275
280
 
@@ -292,17 +297,27 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
292
297
  | **Users** | 24 | Users, forms, surveys, funnels, media, companies, businesses |
293
298
  | **Objects** | 34 | Custom objects, associations, email, workflows, snapshots, links, campaigns, courses, SaaS |
294
299
 
295
- **819 total tools.** Universal orchestration (545 catalog tools across 48 services) + the most comprehensive CRM integration in the MCP ecosystem (245 dedicated tools) + Vault (4 tools) + Vault Containers (8 tools) + Business Deed Transfer (6 tools) + Engine (6 tools) + App Builder (5 tools).
300
+ **850 total tools.** Universal orchestration (576 catalog tools across 53 services) + the most comprehensive CRM integration in the MCP ecosystem (245 dedicated tools) + Vault (4 tools) + Vault Containers (8 tools) + Business Deed Transfer (6 tools) + Engine (6 tools) + App Builder (5 tools).
296
301
 
297
302
  > Every CRM tool is data-driven — defined as configuration, not code. Adding new endpoints takes minutes, not hours. See [CONTRIBUTING.md](CONTRIBUTING.md).
298
303
 
299
304
  ---
300
305
 
301
- ## What's New in v2.2
306
+ ## What's New in v2.3
307
+
308
+ ### v2.3.0 — Conversion Layer + 5 New Services
309
+
310
+ - **5 new services**: Cloudflare (DNS, Workers, KV), GoDaddy (domains), n8n (self-hosted automation), Pabbly (workflows), Make (scenarios)
311
+ - **68 new catalog endpoints** — from 508 to 576 catalog tools
312
+ - **Resend expanded**: 3→67 endpoints — full Resend API coverage (domains, contacts, audiences, broadcasts)
313
+ - **ACTION_ALIASES conversion layer** — 150+ intuitive action mappings (e.g., `contacts.create` → `create_contact`) so .0n SWITCH files use natural action names
314
+ - **Connection auto-enrichment** — workflow runner automatically injects `locationId`, `pipelineId`, `projectRef` from .0n connection metadata
315
+ - **Enhanced API validation** — CRM, Anthropic, and Vercel verification endpoints added to engine validator
316
+ - Total: **850 tools across 53 services in 23 categories**
302
317
 
303
318
  ### v2.2.0 — 22 New Services Expansion
304
319
 
305
- - **255 new catalog endpoints** across 22 new services — from 290 to 545 catalog tools
320
+ - **255 new catalog endpoints** across 22 new services — from 290 to 576 catalog tools
306
321
  - **6 new categories**: Accounting, Advertising, Finance, Cloud, Integration, Automation
307
322
  - **Advertising suite**: Google Ads, Facebook Ads, TikTok Ads, X Ads, LinkedIn Ads, Instagram Ads
308
323
  - **Social expansion**: Instagram, X (Twitter), TikTok Business, LinkedIn
@@ -310,7 +325,7 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
310
325
  - **Infrastructure**: Dropbox, MuleSoft, Microsoft Azure, Zapier
311
326
  - **Communication**: WhatsApp Business
312
327
  - **Marketing**: Smartlead cold email outreach
313
- - Total: **819 tools across 48 services in 19 categories**
328
+ - Total: **850 tools across 53 services in 23 categories**
314
329
 
315
330
  ### v2.1.0 — Business Deed Transfer System
316
331
 
@@ -333,13 +348,13 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
333
348
 
334
349
  ### v1.7.0 — Foundation
335
350
 
336
- - 550 tools across 26 services in 13 categories
337
- - **.0n Conversion Engine** — import credentials, auto-map to 48 services, generate configs for 7 AI platforms
351
+ - 550 tools across 53 services in 13 categories
352
+ - **.0n Conversion Engine** — import credentials, auto-map to 53 services, generate configs for 7 AI platforms
338
353
  - **Vault** — machine-bound encrypted credential storage (AES-256-GCM + PBKDF2-SHA512 + hardware fingerprint)
339
354
  - **Workflow Runtime** + **HTTP Server** + **CLI with named runs**
340
355
  - **Three-Level Execution** (Patent Pending) — Pipeline → Assembly Line → Radial Burst
341
356
 
342
- > **819 tools. 48 services. 958 total capabilities.** See [CHANGELOG.md](CHANGELOG.md) for full version history.
357
+ > **850 tools. 53 services. 1,142 total capabilities.** See [CHANGELOG.md](CHANGELOG.md) for full version history and the [What's New in v2.3](#-whats-new-in-v23) section above.
343
358
 
344
359
  ---
345
360
 
@@ -353,7 +368,7 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
353
368
  │ for $500 and │ │ 2. Plan steps │ │ SendGrid │
354
369
  │ notify #sales" │ │ 3. Execute APIs │ │ Slack │
355
370
  │ │◀────│ 4. Chain data │◀────│ CRM │
356
- │ │ │ 5. Summarize │ │ + 44 more... │
371
+ │ │ │ 5. Summarize │ │ + 49 more... │
357
372
  └─────────────────┘ └──────────────────┘ └─────────────────┘
358
373
  ```
359
374
 
@@ -378,7 +393,7 @@ The orchestrator uses keyword matching to route tasks to the right service. Less
378
393
  | **Flexibility** | Say what you want | Triggers/actions only | Unlimited but complex |
379
394
  | **Maintenance** | Zero | Update broken zaps | Fix API changes |
380
395
  | **Open source** | Yes (MIT) | No | Depends |
381
- | **Tools available** | 819 | Varies | Whatever you build |
396
+ | **Tools available** | 850 | Varies | Whatever you build |
382
397
 
383
398
  ---
384
399
 
@@ -430,7 +445,7 @@ Every task execution is logged to `~/.0n/history/` as JSONL — full audit trail
430
445
  ```
431
446
  0nMCP/
432
447
  ├── index.js # Entry point — MCP server startup
433
- ├── catalog.js # Service catalog — 48 integrations with endpoints
448
+ ├── catalog.js # Service catalog — 53 integrations with endpoints
434
449
  ├── connections.js # Connection manager — ~/.0n/connections/*.0n
435
450
  ├── orchestrator.js # AI execution planner — the brain
436
451
  ├── workflow.js # WorkflowRunner — .0n file execution
@@ -471,7 +486,7 @@ Every task execution is logged to `~/.0n/history/` as JSONL — full audit trail
471
486
  ├── engine/ # .0n Conversion Engine + App Builder
472
487
  │ ├── index.js # Engine entry — 6 tools
473
488
  │ ├── parser.js # Multi-format credential parser
474
- │ ├── mapper.js # Auto-map credentials to 26 services
489
+ │ ├── mapper.js # Auto-map credentials to 53 services
475
490
  │ ├── validator.js # API key verification
476
491
  │ ├── platforms.js # 7 AI platform config generators
477
492
  │ ├── bundler.js # Portable .0n bundle creator
@@ -490,14 +505,14 @@ Every task execution is logged to `~/.0n/history/` as JSONL — full audit trail
490
505
 
491
506
  | Component | What It Does |
492
507
  |-----------|-------------|
493
- | **Service Catalog** | Defines all 48 services — their base URLs, endpoints, auth patterns, and capabilities |
508
+ | **Service Catalog** | Defines all 53 services — their base URLs, endpoints, auth patterns, and capabilities |
494
509
  | **Connection Manager** | Stores credentials as `.0n` files in `~/.0n/connections/` per the .0n standard |
495
510
  | **Orchestrator** | The brain — parses natural language, plans multi-step execution, calls APIs, chains data |
496
511
  | **CRM Modules** | 245 tools across 12 modules — data-driven, every tool is config not code |
497
512
  | **Vault** | Machine-bound encrypted credential storage — AES-256-GCM + PBKDF2-SHA512 |
498
513
  | **Vault Container System** | Patent Pending #63/990,046 — 7 semantic layers, multi-party escrow, Seal of Truth, binary .0nv format |
499
514
  | **Business Deed Transfer** | Package + escrow + transfer entire digital businesses in encrypted containers |
500
- | **Conversion Engine** | Import credentials from .env/CSV/JSON, auto-map to 48 services, generate 7 AI platform configs |
515
+ | **Conversion Engine** | Import credentials from .env/CSV/JSON, auto-map to 53 services, generate 7 AI platform configs |
501
516
  | **Application Engine** | Build, distribute, schedule .0n applications with CronScheduler + HTTP middleware |
502
517
  | **Workflow Runtime** | Load and execute `.0n` workflow files with template engine, conditions, and step chaining |
503
518
  | **HTTP Server** | Express-based REST API, MCP over HTTP, and webhook receivers |
@@ -607,7 +622,7 @@ The tool factory handles registration, validation, API calls, error handling —
607
622
 
608
623
  ### Phase 0 — Foundation (Current)
609
624
 
610
- - [x] **48 services, 819 tools, 19 categories**
625
+ - [x] **53 services, 850 tools, 23 categories**
611
626
  - [x] Core orchestration engine with AI planning
612
627
  - [x] **245 CRM tools** — full API coverage across 12 modules
613
628
  - [x] Gmail, Google Sheets, Google Drive, Jira, Zendesk, Mailchimp, Zoom, Microsoft 365, MongoDB
@@ -625,7 +640,7 @@ The tool factory handles registration, validation, API calls, error handling —
625
640
  - [x] **Asana** — project and task management
626
641
  - [x] **Intercom** — customer messaging
627
642
  - [x] **22 new services** — advertising, social, finance, cloud, automation
628
- - [x] Target: 48 services, 819 tools (exceeded)
643
+ - [x] Target: 53 services, 850 tools (exceeded)
629
644
 
630
645
  ### Phase 2 — Full Stack (500 stars / $2K MRR)
631
646
 
@@ -691,9 +706,9 @@ We ship weekly. The codebase is active. The community is real. If you're buildin
691
706
 
692
707
  | Metric | |
693
708
  |--------|---|
694
- | **Tools shipped** | 819 |
695
- | **Services integrated** | 48 |
696
- | **Categories** | 19 |
709
+ | **Tools shipped** | 850 |
710
+ | **Services integrated** | 53 |
711
+ | **Categories** | 23 |
697
712
  | **CRM endpoints covered** | 245 / 245 (100%) |
698
713
  | **npm packages** | 3 ([0nmcp](https://www.npmjs.com/package/0nmcp), [0nork](https://www.npmjs.com/package/0nork), [0n-spec](https://www.npmjs.com/package/0n-spec)) |
699
714
  | **Open source repos** | 3 |
@@ -742,7 +757,7 @@ node index.js
742
757
 
743
758
  | Project | Description |
744
759
  |---------|-------------|
745
- | **[0nMCP](https://0nmcp.com)** | Universal AI API Orchestrator — 819 tools, 48 services, Vault encryption, Business Deed transfer ([source](https://github.com/0nork/0nMCP)) |
760
+ | **[0nMCP](https://0nmcp.com)** | Universal AI API Orchestrator — 850 tools, 53 services, Vault encryption, Business Deed transfer ([source](https://github.com/0nork/0nMCP)) |
746
761
  | **[0n-spec](https://github.com/0nork/0n-spec)** | The .0n Standard — universal configuration format for AI orchestration |
747
762
  | **[0nork](https://github.com/0nork/0nork)** | The parent org — AI orchestration infrastructure |
748
763
 
@@ -757,7 +772,7 @@ node index.js
757
772
 
758
773
  <div align="center">
759
774
 
760
- **[Sponsor on GitHub](https://github.com/sponsors/0nork)** · **[Star the repo](https://github.com/0nork/0nMCP)** · **[Tell a friend](https://twitter.com/intent/tweet?text=0nMCP%20-%20819%20tools,%2048%20services,%20zero%20config.%20The%20universal%20AI%20API%20orchestrator.%20Free%20and%20open%20source.&url=https://github.com/0nork/0nMCP)**
775
+ **[Sponsor on GitHub](https://github.com/sponsors/0nork)** · **[Star the repo](https://github.com/0nork/0nMCP)** · **[Tell a friend](https://twitter.com/intent/tweet?text=0nMCP%20-%20850%20tools,%2053%20services,%20zero%20config.%20The%20universal%20AI%20API%20orchestrator.%20Free%20and%20open%20source.&url=https://github.com/0nork/0nMCP)**
761
776
 
762
777
  </div>
763
778
 
@@ -767,7 +782,7 @@ node index.js
767
782
 
768
783
  ### Stop building workflows. Start describing outcomes.
769
784
 
770
- **819 tools. 48 services. Zero config. MIT licensed. Community driven.**
785
+ **850 tools. 53 services. Zero config. MIT licensed. Community driven.**
771
786
 
772
787
  **[Get Started](https://0nmcp.com)** · **[Join the Community](https://0nmcp.com/community)** · **[Unlock Schedule](https://0nmcp.com/sponsor)** · **[View Source](https://github.com/0nork/0nMCP)** · **[Read the Spec](https://github.com/0nork/0n-spec)**
773
788
 
@@ -0,0 +1,199 @@
1
+ // ============================================================
2
+ // 0nMCP — Capability Proxy
3
+ // ============================================================
4
+ // Zero-knowledge API execution layer. Tools call capabilities,
5
+ // not accounts. Credentials are borrowed from vault/connections,
6
+ // held in memory only for the duration of the call, then wiped.
7
+ //
8
+ // Wires in rate limiting (ratelimit.js) and audit logging.
9
+ // Credentials NEVER appear in logs, errors, or tool responses.
10
+ // ============================================================
11
+
12
+ import { appendFileSync, existsSync, mkdirSync } from "fs";
13
+ import { join } from "path";
14
+ import { homedir } from "os";
15
+ import { getLimiter, retryWithBackoff } from "./ratelimit.js";
16
+
17
+ const AUDIT_DIR = join(homedir(), ".0n", "history");
18
+ const AUDIT_FILE = join(AUDIT_DIR, "audit.jsonl");
19
+
20
+ export class CapabilityProxy {
21
+ /**
22
+ * @param {import("./connections.js").ConnectionManager} connectionManager
23
+ * @param {Record<string, any>} catalog - SERVICE_CATALOG from catalog.js
24
+ */
25
+ constructor(connectionManager, catalog) {
26
+ this.connections = connectionManager;
27
+ this.catalog = catalog;
28
+
29
+ // Ensure audit directory exists
30
+ if (!existsSync(AUDIT_DIR)) {
31
+ mkdirSync(AUDIT_DIR, { recursive: true });
32
+ }
33
+ }
34
+
35
+ // ── Primary method — tools call this instead of fetch() ──────
36
+ /**
37
+ * Execute an API call through the proxy.
38
+ * Borrows credentials, enforces rate limits, executes, wipes credentials, logs audit.
39
+ *
40
+ * @param {string} service - Service key (e.g., "stripe", "sendgrid")
41
+ * @param {string} endpoint - Endpoint name from service catalog
42
+ * @param {Object} params - Request parameters
43
+ * @returns {Promise<{response: Response, data: any}>}
44
+ */
45
+ async call(service, endpoint, params = {}) {
46
+ const start = Date.now();
47
+ let creds = null;
48
+ let status = 0;
49
+
50
+ try {
51
+ // 1. Borrow credentials (in-memory only)
52
+ creds = this._borrowCredentials(service);
53
+ if (!creds) {
54
+ throw new Error(`Service ${service} not connected`);
55
+ }
56
+
57
+ // 2. Resolve catalog entry
58
+ const svc = this.catalog[service];
59
+ if (!svc) throw new Error(`Unknown service: ${service}`);
60
+
61
+ const ep = svc.endpoints?.[endpoint];
62
+ if (!ep) throw new Error(`Unknown endpoint: ${service}.${endpoint}`);
63
+
64
+ // 3. Build request from catalog
65
+ const { url, method, headers, body } = this._buildRequest(svc, ep, params, creds);
66
+
67
+ // 4. Enforce rate limit
68
+ await this._enforceRateLimit(service);
69
+
70
+ // 5. Execute with retry on 429/5xx
71
+ const response = await retryWithBackoff(
72
+ () => fetch(url, { method, headers, body }),
73
+ { maxRetries: 2, initialDelayMs: 500 }
74
+ );
75
+
76
+ status = response.status;
77
+ const data = await response.json().catch(() => ({
78
+ status: response.status,
79
+ statusText: response.statusText,
80
+ }));
81
+
82
+ return { response, data };
83
+ } finally {
84
+ // 6. Wipe credentials from local scope
85
+ creds = null;
86
+
87
+ // 7. Log audit entry (NO credentials)
88
+ this._logAudit(service, endpoint, status, Date.now() - start);
89
+ }
90
+ }
91
+
92
+ // ── CRM variant — credentials provided per-call ──────────────
93
+ /**
94
+ * Execute with externally-provided credentials (CRM pattern).
95
+ * Used when access_token comes from the MCP tool argument, not ConnectionManager.
96
+ * Still provides rate limiting and audit logging.
97
+ *
98
+ * @param {string} service - Service key (typically "crm")
99
+ * @param {string} endpoint - Endpoint/tool name
100
+ * @param {string} url - Pre-built URL
101
+ * @param {Object} options - Fetch options (method, headers, body)
102
+ * @returns {Promise<{response: Response, data: any}>}
103
+ */
104
+ async callWithCredentials(service, endpoint, url, options) {
105
+ const start = Date.now();
106
+ let status = 0;
107
+
108
+ try {
109
+ // Enforce rate limit
110
+ await this._enforceRateLimit(service);
111
+
112
+ // Execute with retry
113
+ const response = await retryWithBackoff(
114
+ () => fetch(url, options),
115
+ { maxRetries: 2, initialDelayMs: 500 }
116
+ );
117
+
118
+ status = response.status;
119
+ const data = await response.json().catch(() => ({
120
+ status: response.status,
121
+ statusText: response.statusText,
122
+ }));
123
+
124
+ return { response, data };
125
+ } finally {
126
+ this._logAudit(service, endpoint, status, Date.now() - start);
127
+ }
128
+ }
129
+
130
+ // ── Internal: borrow credentials ─────────────────────────────
131
+ _borrowCredentials(service) {
132
+ const creds = this.connections.getCredentials(service);
133
+ // Return a shallow copy; original ref released after call
134
+ return creds ? { ...creds } : null;
135
+ }
136
+
137
+ // ── Internal: enforce rate limit ─────────────────────────────
138
+ async _enforceRateLimit(service) {
139
+ const limiter = getLimiter(service);
140
+ await limiter.acquire();
141
+ }
142
+
143
+ // ── Internal: build request from catalog entry ───────────────
144
+ _buildRequest(svc, ep, params, creds) {
145
+ // URL with path param substitution
146
+ const allParams = { ...creds, ...params };
147
+ let url = svc.baseUrl + ep.path;
148
+ url = url.replace(/\{(\w+)\}/g, (_, key) => allParams[key] || `{${key}}`);
149
+
150
+ // Headers from catalog auth
151
+ const headers = svc.authHeader(creds);
152
+ const method = ep.method;
153
+ let body = undefined;
154
+
155
+ // Body for non-GET
156
+ if (method !== "GET" && params && Object.keys(params).length > 0) {
157
+ const contentType = ep.contentType || "application/json";
158
+ if (contentType === "application/x-www-form-urlencoded") {
159
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
160
+ const flat = {};
161
+ for (const [k, v] of Object.entries(params)) {
162
+ if (typeof v !== "object") flat[k] = String(v);
163
+ }
164
+ body = new URLSearchParams(flat).toString();
165
+ } else {
166
+ headers["Content-Type"] = "application/json";
167
+ body = JSON.stringify(params);
168
+ }
169
+ }
170
+
171
+ // Query string for GET
172
+ if (method === "GET" && params && Object.keys(params).length > 0) {
173
+ const flat = {};
174
+ for (const [k, v] of Object.entries(params)) {
175
+ if (typeof v !== "object") flat[k] = String(v);
176
+ }
177
+ const qs = new URLSearchParams(flat).toString();
178
+ if (qs) url += (url.includes("?") ? "&" : "?") + qs;
179
+ }
180
+
181
+ return { url, method, headers, body };
182
+ }
183
+
184
+ // ── Internal: audit log (NEVER contains credentials) ─────────
185
+ _logAudit(service, endpoint, status, durationMs) {
186
+ try {
187
+ const entry = {
188
+ ts: new Date().toISOString(),
189
+ service,
190
+ endpoint,
191
+ status,
192
+ durationMs,
193
+ };
194
+ appendFileSync(AUDIT_FILE, JSON.stringify(entry) + "\n");
195
+ } catch {
196
+ // Audit logging should never break execution
197
+ }
198
+ }
199
+ }