@zysec-ai/cpod-sdk 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,96 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@zysec-ai/cpod-sdk` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.0.0] - 2026-06-11
9
+
10
+ ### Breaking Changes
11
+
12
+ #### Domain Renames
13
+
14
+ All 8 bare domain names have been renamed to qualified forms for clarity and EDM consistency:
15
+
16
+ | Old Path | New Path |
17
+ |----------|----------|
18
+ | `technology/` | `technology.assets/` |
19
+ | `assets/` | `assets.items/` |
20
+ | `relationships/` | `relationships.edges/` |
21
+ | `vulnerabilities/` | `vulnerabilities.items/` |
22
+ | `cloud-resources/` | `cloud-resources.items/` |
23
+ | `entitlements/` | `entitlements.items/` |
24
+ | `licenses/` | `licenses.items/` |
25
+ | `audit/` | `audit.records/` |
26
+
27
+ #### Import Path Updates
28
+
29
+ All imports from renamed domains must use qualified paths:
30
+
31
+ ```typescript
32
+ // Before (v1.x)
33
+ import { TechnologyService } from '@zysec-ai/cpod-sdk/dist/technology/service.js';
34
+ import { AssetService } from '@zysec-ai/cpod-sdk/dist/assets/service.js';
35
+
36
+ // After (v2.0.0)
37
+ import { TechnologyService } from '@zysec-ai/cpod-sdk/dist/technology.assets/service.js';
38
+ import { AssetService } from '@zysec-ai/cpod-sdk/dist/assets.items/service.js';
39
+ ```
40
+
41
+ #### Client Namespace Property Changes
42
+
43
+ All client namespace properties have been renamed to match qualified domain names:
44
+
45
+ | Old Property | New Property |
46
+ |--------------|--------------|
47
+ | `client.technology` | `client.technologyAssets` |
48
+ | `client.assets` | `client.assetsItems` |
49
+ | `client.relationships` | `client.relationshipsEdges` |
50
+ | `client.vulnerabilities` | `client.vulnerabilitiesItems` |
51
+ | `client.cloudResources` | `client.cloudResourcesItems` |
52
+ | `client.entitlements` | `client.entitlementsItems` |
53
+ | `client.licenses` | `client.licensesItems` |
54
+ | `client.audit` | `client.auditRecords` |
55
+
56
+ #### MCP Domain Updates
57
+
58
+ MCP tool domains now use qualified names:
59
+
60
+ - `'technology'` → `'technology.assets'`
61
+ - `'assets'` → `'assets.items'`
62
+ - `'relationships'` → `'relationships.edges'`
63
+ - `'vulnerabilities'` → `'vulnerabilities.items'`
64
+ - `'cloudResources'` → `'cloudResources.items'`
65
+ - `'entitlements'` → `'entitlements.items'`
66
+ - `'licenses'` → `'licenses.items'`
67
+
68
+ ### Migration
69
+
70
+ See [docs/MIGRATION_v2.md](../../docs/MIGRATION_v2.md) for detailed migration guide, codemod suggestions, and rollback instructions.
71
+
72
+ ### Added
73
+
74
+ - Domain naming linter (`scripts/lint-domain-naming.mjs`) to enforce qualified domain names
75
+ - Comprehensive migration guide with codemod examples
76
+ - Breaking change documentation
77
+
78
+ ### Changed
79
+
80
+ - All domain folders now use qualified names following EDM conventions
81
+ - Client namespace properties renamed for consistency
82
+ - MCP manifest domains use qualified names
83
+
84
+ ## [1.0.0] - 2026-05-20
85
+
86
+ ### Added
87
+ - Initial release of the cPod TypeScript/JavaScript SDK
88
+ - `CpodClient` main client class with configurable API key, base URL, and timeout
89
+ - `PodService` with full CRUD operations (`list`, `get`, `create`, `update`, `delete`)
90
+ - `EventService` with `subscribe` (EventEmitter pattern) and `getHistory`
91
+ - `TenantService` with full CRUD operations
92
+ - `HttpClient` with automatic retry (3 attempts, exponential backoff), auth header injection, and timeout support
93
+ - Typed error hierarchy: `CpodError`, `ApiError`, `AuthenticationError`, `NotFoundError`, `RateLimitError`
94
+ - Full TypeScript declarations and source maps
95
+ - ESM and CJS dual build output
96
+ - Tree-shakeable subpath exports (`@zysec-ai/cpod-sdk/pods`, `@zysec-ai/cpod-sdk/events`)
package/README.md ADDED
@@ -0,0 +1,315 @@
1
+ # @zysec-ai/cpod-sdk — TypeScript / JavaScript SDK
2
+
3
+ Official TypeScript SDK for the cPod Enterprise Data Model (EDM). Typed access to 60+ service namespaces covering people, technology, licenses, assets, risk & compliance, CRM, finance, HR, legal, marketing, customer success, process optimization, surveys, external integrations, and all cPod platform surfaces.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @zysec-ai/cpod-sdk
9
+ # or
10
+ pnpm add @zysec-ai/cpod-sdk
11
+ # or
12
+ yarn add @zysec-ai/cpod-sdk
13
+ ```
14
+
15
+ ## Requirements
16
+
17
+ - Node.js 18+
18
+ - TypeScript 5+ recommended
19
+ - A cPod backend URL and credentials (API key or OIDC token)
20
+
21
+ ## Quick Start
22
+
23
+ ```ts
24
+ import { CpodClient } from '@zysec-ai/cpod-sdk'
25
+
26
+ const cpod = new CpodClient({ apiKey: 'sk-...' })
27
+
28
+ const people = await cpod.people.persons.list({ pageSize: 10 })
29
+ const accounts = await cpod.customer.accounts.list({ pageSize: 10 })
30
+ const projects = await cpod.projects.project.list({ pageSize: 10 })
31
+
32
+ console.log({
33
+ people: people.items.length,
34
+ accounts: accounts.items.length,
35
+ projects: projects.items.length,
36
+ })
37
+ ```
38
+
39
+ ## Authentication Modes
40
+
41
+ ### API Key (service account / emulator)
42
+
43
+ ```ts
44
+ const cpod = new CpodClient({
45
+ apiKey: process.env.CPOD_API_KEY!,
46
+ baseUrl: process.env.CPOD_API_URL ?? 'https://api.cyberpod.app',
47
+ appId: process.env.CPOD_APP_ID,
48
+ })
49
+ ```
50
+
51
+ ### Environment-based (zero-config)
52
+
53
+ ```ts
54
+ const cpod = CpodClient.fromEnv()
55
+ ```
56
+
57
+ Reads `CPOD_API_KEY` (required), `CPOD_API_URL` / `CPOD_BASE_URL`, and `CPOD_APP_ID` from the environment.
58
+
59
+ ### Token-based (user-scoped, zero app secret)
60
+
61
+ ```ts
62
+ const cpod = CpodClient.fromToken(accessToken, {
63
+ refreshToken,
64
+ baseUrl: process.env.CPOD_API_URL,
65
+ appId: process.env.CPOD_APP_ID,
66
+ })
67
+
68
+ const me = await cpod.auth.me()
69
+ ```
70
+
71
+ ### Browser popup login
72
+
73
+ ```ts
74
+ const cpod = await CpodClient.login({ backendUrl: 'https://api.cyberpod.app' })
75
+ ```
76
+
77
+ ### Redirect login
78
+
79
+ ```ts
80
+ const { loginUrl } = CpodClient.beginLogin({
81
+ backendUrl: 'https://api.cyberpod.app',
82
+ returnTo: `${location.origin}/auth/callback`,
83
+ })
84
+ // redirect user, then on callback:
85
+ const { accessToken, refreshToken } = await CpodClient.exchangeCode({ backendUrl, code })
86
+ const cpod = CpodClient.fromToken(accessToken, { refreshToken })
87
+ ```
88
+
89
+ ## Environment Variables
90
+
91
+ | Variable | Required | Default | Description |
92
+ |---|---|---|---|
93
+ | `CPOD_API_KEY` | Yes (API key mode) | — | Service-account API key or emulator dev-token |
94
+ | `CPOD_API_URL` | No | `https://api.cyberpod.app` | Backend base URL. Takes precedence over `CPOD_BASE_URL` |
95
+ | `CPOD_BASE_URL` | No | `https://api.cyberpod.app` | Alias for `CPOD_API_URL` (fallback) |
96
+ | `CPOD_APP_ID` | For MCP/registry | — | App identifier for MCP tool registration and app storage scoping |
97
+
98
+ ## Service Index
99
+
100
+ The SDK exposes 60+ top-level namespaces. All are available on every `CpodClient` instance.
101
+
102
+ ### Business EDM Domains
103
+
104
+ | Namespace | Sub-services | Description |
105
+ |---|---|---|
106
+ | `people` | `persons`, `groups` | Person and Group records |
107
+ | `groups` | — | Group records (alias of `people.groups`) |
108
+ | `technology` | — | TechnologyAsset records |
109
+ | `entitlements` | — | AccessEntitlement grants |
110
+ | `licenses` | — | SoftwareLicense and LicenseAssignment |
111
+ | `assets` | — | PhysicalAsset records |
112
+ | `risk` | — | RiskItem records |
113
+ | `vulnerabilities` | — | CVE / finding records with severity scoring |
114
+ | `complianceControls` | — | SOC 2, ISO 27001, PCI, HIPAA, NIST controls |
115
+ | `cloudResources` | — | AWS / GCP / Azure / OCI resources |
116
+ | `relationships` | — | EDM graph relationship traversal |
117
+ | `dataSources` | — | External data source integrations |
118
+ | `contracts` | `vendors`, `contract`, `obligations` | Vendor, Contract, ContractObligation |
119
+ | `work` | `timeEntries`, `comments`, `capacity` | Time tracking, comments, capacity planning |
120
+ | `investments` | `portfolio`, `costCenters` | Technology investment portfolio, cost centers |
121
+ | `performance` | `reviews`, `goals`, `learningRecords` | Performance reviews, goals, learning records |
122
+ | `employee` | `skills`, `calendarEvents`, `leaveRequests`, `meetingNotes` | Employee experience |
123
+ | `okr` | `objectives`, `keyResults` | OKR management |
124
+ | `projects` | `project`, `tasks`, `sprints`, `features` | Project management |
125
+ | `customer` | `accounts`, `contacts`, `deals`, `activities`, `quotes`, `lineItems` | CRM |
126
+ | `grc` | `frameworks`, `controls`, `evidence`, `incidents`, `risks` | Governance, Risk & Compliance |
127
+ | `soc` | `alerts`, `investigations`, `playbooks` | Security Operations Center |
128
+ | `learning` | `cohorts`, `assessments` | Learning management |
129
+ | `rfp` | `records`, `questions`, `responses` | RFP management |
130
+ | `knowledge` | `documents`, `chunks`, `entities`, `templates`, `sops` | Knowledge base |
131
+ | `integration` | `applications`, `connectors`, `apiKeys`, `webhooks` | Integration platform |
132
+ | `helpdesk` | `tickets`, `slaPolicies` | Helpdesk / support tickets |
133
+ | `hr` | `jobPostings`, `applicants`, `onboardingTasks`, `offboardingTasks` | HR lifecycle |
134
+ | `notifications` | `notification`, `announcements` | Notifications and announcements |
135
+ | `approvals` | `requests`, `steps` | Approval workflows |
136
+ | `finance` | `invoices`, `purchaseOrders`, `expenses`, `budgets`, `budgetLines` | Finance |
137
+ | `policies` | `policy`, `acknowledgements`, `reviews` | Policy management |
138
+ | `org` | `locations`, `departments` | Organizational structure |
139
+ | `catalog` | `products`, `categories` | Product catalog |
140
+ | `procurement` | `suppliers` | Procurement / suppliers |
141
+ | `operations` | `accessRequests`, `hrRequests`, `pending`, `equipment`, `travel` | Cross-functional request workflows |
142
+ | `vendor` | `scorecards`, `insurance`, `certifications`, `contacts`, `onboarding` | Vendor management |
143
+ | `marketing` | `campaigns`, `leads`, `content`, `events`, `social`, `emails`, `analytics` | Marketing |
144
+ | `legal` | `contracts`, `ndas`, `cases`, `ip`, `compliance`, `reviews` | Legal |
145
+ | `clientops` | `healthScores`, `playbooks`, `onboardingPlans`, `retentionRisks`, `expansions` | Customer success and retention |
146
+ | `integrations` | `connections`, `dataSources`, `syncState` | External system connectivity and sync |
147
+ | `process` | `maps`, `bottlenecks`, `efficiencyScores` | Business process optimization |
148
+ | `surveys` | `surveys`, `responses`, `analytics` | Feedback collection and analysis |
149
+ | `completions` | `chat` | OpenAI-compatible completions gateway |
150
+
151
+ ### Platform Surfaces
152
+
153
+ | Namespace | Sub-services | Description |
154
+ |---|---|---|
155
+ | `storage` | `files`, `db`, `kv`, `objects`, `records`, `sqlite` | App-private sandboxed storage |
156
+ | `skills` | — | Platform skill listing and execution |
157
+ | `workflows` | — | Workflow management and triggering |
158
+ | `jobs` | — | Async job submission and monitoring |
159
+ | `flags` | — | Feature flag evaluation |
160
+ | `secrets` | — | Platform vault secret resolution |
161
+ | `mcp` | — | MCP tool registration and proxy |
162
+ | `registry` | — | Capability registry (app-tool discover/call) |
163
+ | `telemetry` | `audit`, `traces`, `events` | Audit events, LLM traces, analytics events |
164
+ | `audit` | — | Platform audit event emit/query |
165
+ | `events` | — | Event subscription and history |
166
+ | `analytics` | `dashboards`, `metrics`, `reports` | Dashboards, time-series metrics, reports |
167
+ | `graph` | `nodes`, `edges`, `queries` | Knowledge graph operations |
168
+ | `planner` | `dailyPlans`, `planItems` | User-scoped daily planning |
169
+ | `postmortems` | `timeline` | Incident timeline tracking |
170
+ | `masking` | — | PII and sensitive string masking |
171
+ | `workspaces` | — | Personal and shared file/folder containers |
172
+ | `apps` | — | App registration and MCP tool management |
173
+
174
+ ### Auth / Identity
175
+
176
+ | Namespace | Sub-services | Description |
177
+ |---|---|---|
178
+ | `auth` | — | Login, refresh, validate, /auth/me, change-password, invites |
179
+ | `organizations` | — | Organization context from JWT |
180
+ | `pods` | — | Pod resource CRUD and lifecycle |
181
+ | `credentials` | — | API keys and service account credentials |
182
+ | `userProfiles` | — | User profile resources |
183
+ | `agents` | — | Configured AI agents (model, skills, MCP, secrets) |
184
+ | `mcpServers` | — | External MCP server integrations |
185
+ | `memory` | — | Cognitive memory layer (facts, preferences, instructions) |
186
+ | `chat` | — | Human-in-the-Loop approval for agent tool calls |
187
+ | `tenants` | — | Tenant administration |
188
+
189
+ ## EDM CRUD Example
190
+
191
+ ```ts
192
+ // List
193
+ const people = await cpod.people.persons.list({ pageSize: 25 })
194
+
195
+ // Create
196
+ const account = await cpod.customer.accounts.create({
197
+ tenantId: 'tenant_dev',
198
+ appId: 'crm-lite',
199
+ ownerId: 'user_dev',
200
+ name: 'Acme Corp',
201
+ lifecycleStage: 'prospect',
202
+ })
203
+
204
+ // Update
205
+ const updated = await cpod.customer.accounts.update(account.id, {
206
+ healthScore: 'green',
207
+ })
208
+
209
+ // Get
210
+ const record = await cpod.customer.accounts.get(account.id)
211
+ ```
212
+
213
+ ## Storage
214
+
215
+ App-private storage for preferences, drafts, pins, cache metadata, and small files. Sandbox-scoped to tenant + app + user automatically.
216
+
217
+ ```ts
218
+ // Key-value
219
+ await cpod.storage.kv.set('crm-lite:last-filter', { owner: 'me' })
220
+ const value = await cpod.storage.kv.get('crm-lite:last-filter')
221
+
222
+ // Files (MinIO-backed, presigned URLs)
223
+ await cpod.storage.files.upload('report.pdf', blob)
224
+
225
+ // Structured documents (MongoDB-backed)
226
+ await cpod.storage.db.insert('drafts', { title: 'Q4 Plan', body: '...' })
227
+ ```
228
+
229
+ ## MCP Tools
230
+
231
+ Register tools for agent-callable workflows. Tools are proxied through a single audited endpoint.
232
+
233
+ ```ts
234
+ await cpod.mcp.register([
235
+ {
236
+ name: 'crm_lite_prepare_daily_brief',
237
+ description: 'Prepare a source-backed CRM follow-up brief.',
238
+ endpoint: '/api/mcp/crm_lite_prepare_daily_brief',
239
+ method: 'POST',
240
+ inputSchema: {
241
+ type: 'object',
242
+ properties: { focus: { type: 'string' } },
243
+ },
244
+ maskResponse: true,
245
+ },
246
+ ])
247
+ ```
248
+
249
+ Auto-derive tools from every service's MCP manifest:
250
+
251
+ ```ts
252
+ const tools = cpod.mcpTools() // → McpToolDefinition[]
253
+ ```
254
+
255
+ ## Registry
256
+
257
+ Dynamic app-tool discover and call through the capability registry:
258
+
259
+ ```ts
260
+ const tools = await cpod.registry.discover({ appId: 'my-app' })
261
+ const result = await cpod.registry.call('my-app', 'tool_name', { arg: 'value' })
262
+ ```
263
+
264
+ ## Error Handling
265
+
266
+ ```ts
267
+ import { AuthenticationError, NotFoundError, RateLimitError, ApiError } from '@zysec-ai/cpod-sdk'
268
+
269
+ try {
270
+ await cpod.customer.accounts.get('missing-id')
271
+ } catch (error) {
272
+ if (error instanceof NotFoundError) console.log('Record not found')
273
+ else if (error instanceof AuthenticationError) console.log('Bad or expired credentials')
274
+ else if (error instanceof RateLimitError) console.log('Rate limited')
275
+ else if (error instanceof ApiError) console.log(`API error: ${error.message}`)
276
+ else throw error
277
+ }
278
+ ```
279
+
280
+ ## Escape Hatch
281
+
282
+ Prefer typed services. For endpoints without a typed method yet:
283
+
284
+ ```ts
285
+ const result = await cpod.request('GET', '/api/v1/customer/accounts')
286
+ ```
287
+
288
+ ## Troubleshooting
289
+
290
+ | Symptom | Fix |
291
+ |---|---|
292
+ | `CPOD_API_KEY environment variable is required` | Set `CPOD_API_KEY` or use `CpodClient.fromToken(...)` |
293
+ | Calls go to production unexpectedly | Set `CPOD_API_URL=http://localhost:4000` for emulator/local backend |
294
+ | Browser popup blocked | Call `CpodClient.login()` from a click handler or use `beginLogin()` redirect flow |
295
+ | 401 after user login | Pass `refreshToken` to `fromToken()` so the SDK can refresh and retry |
296
+
297
+ ## Local Development
298
+
299
+ ```bash
300
+ pnpm install
301
+ pnpm build
302
+ pnpm typecheck
303
+ pnpm test
304
+ ```
305
+
306
+ From the monorepo root:
307
+
308
+ ```bash
309
+ pnpm --filter @zysec-ai/cpod-sdk build
310
+ pnpm --filter @zysec-ai/cpod-sdk test
311
+ ```
312
+
313
+ ## License
314
+
315
+ MIT
@@ -0,0 +1,21 @@
1
+ # cpod-mcp-server environment template.
2
+ #
3
+ # The MCP server (bin/cpod-mcp-server.mjs) auto-loads a .env file from
4
+ # either the current working directory or from this directory. Copy this
5
+ # file to .env, fill it in, and the MCP host config only needs to pass
6
+ # CPOD_API_KEY + CPOD_BASE_URL.
7
+ #
8
+ # cp .env.example .env
9
+
10
+ # ── Required ────────────────────────────────────────────────────────────────
11
+ CPOD_API_KEY=eyJ...
12
+ CPOD_BASE_URL=https://api.cpod.example
13
+
14
+ # ── EDM / storage direct-mode (required for those tools to work) ────────────
15
+ # Get the URI from your cpod admin or vault. Never commit a real one here.
16
+ CPOD_MONGO_URI=mongodb://USER:PASS@HOST:27017/?authSource=admin
17
+ CPOD_MONGO_DB=cpod-sdk
18
+
19
+ # ── Optional ────────────────────────────────────────────────────────────────
20
+ # CPOD_APP_ID=<app-id> # multi-app tenant scoping
21
+ # CPOD_TENANT_ID=<tenant-id> # pin a tenant explicitly
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ // ─────────────────────────────────────────────────────────────────────────────
3
+ // cpod-mcp-server — stdio MCP server that exposes every cpod-sdk method as
4
+ // an MCP tool. Walks client.mcpTools() once at startup; adding a new SDK
5
+ // method (with a manifest entry) auto-extends this server's tool list.
6
+ //
7
+ // Connects MCP hosts (Claude Desktop, Cursor, Continue, etc.) to the
8
+ // entire cpod platform without per-tool wiring.
9
+ //
10
+ // MCP host config — only API_KEY + BASE_URL needed:
11
+ // {
12
+ // "mcpServers": {
13
+ // "cpod": {
14
+ // "command": "node",
15
+ // "args": ["./node_modules/@cpod/sdk/bin/cpod-mcp-server.mjs"],
16
+ // "env": {
17
+ // "CPOD_API_KEY": "<your token>",
18
+ // "CPOD_BASE_URL": "https://api.example.com"
19
+ // }
20
+ // }
21
+ // }
22
+ // }
23
+ //
24
+ // Server-side config — Mongo URI for direct-mode EDM, etc. live in a
25
+ // `.env` file alongside the install or in the host process env. The
26
+ // server auto-loads `.env` from the current working directory and from
27
+ // the directory containing this script. Documented env vars:
28
+ //
29
+ // CPOD_API_KEY required — bearer token for backend calls
30
+ // CPOD_BASE_URL required — cpod-backend URL
31
+ // CPOD_MONGO_URI needed by EDM/storage direct-mode services
32
+ // CPOD_MONGO_DB defaults to "cpod-sdk"
33
+ // CPOD_APP_ID optional — multi-app tenant scoping
34
+ // CPOD_TENANT_ID optional — pin a tenant explicitly
35
+ // ─────────────────────────────────────────────────────────────────────────────
36
+
37
+ import { fileURLToPath } from 'node:url'
38
+ import { dirname, resolve } from 'node:path'
39
+ import { config as loadDotenv } from 'dotenv'
40
+
41
+ // Load .env from the cwd first, then from the script's own dir so a
42
+ // bundled install can ship its own defaults.
43
+ loadDotenv()
44
+ loadDotenv({ path: resolve(dirname(fileURLToPath(import.meta.url)), '.env') })
45
+
46
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
47
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
48
+ import {
49
+ CallToolRequestSchema,
50
+ ListToolsRequestSchema,
51
+ } from '@modelcontextprotocol/sdk/types.js'
52
+
53
+ import { CpodClient, invokeTool } from '../dist/index.mjs'
54
+
55
+ // ── Config from env ─────────────────────────────────────────────────────────
56
+ const apiKey = process.env.CPOD_API_KEY
57
+ const baseUrl = process.env.CPOD_BASE_URL || process.env.CPOD_BACKEND_URL
58
+ const mongoUri = process.env.CPOD_MONGO_URI
59
+ const mongoDb = process.env.CPOD_MONGO_DB
60
+ const appId = process.env.CPOD_APP_ID
61
+ const tenantId = process.env.CPOD_TENANT_ID
62
+
63
+ if (!apiKey || !baseUrl) {
64
+ process.stderr.write(
65
+ 'cpod-mcp-server: CPOD_API_KEY and CPOD_BASE_URL are required.\n' +
66
+ 'Pass them via the MCP host\'s env block.\n',
67
+ )
68
+ process.exit(2)
69
+ }
70
+
71
+ const client = new CpodClient({
72
+ apiKey,
73
+ baseUrl,
74
+ ...(mongoUri ? { mongoUri } : {}),
75
+ ...(mongoDb ? { mongoDb } : {}),
76
+ ...(appId ? { appId } : {}),
77
+ ...(tenantId ? { tenantId } : {}),
78
+ })
79
+
80
+ const tools = client.mcpTools()
81
+ process.stderr.write(`cpod-mcp-server: registered ${tools.length} tools.\n`)
82
+
83
+ // ── MCP server wiring ───────────────────────────────────────────────────────
84
+ const server = new Server(
85
+ {
86
+ name: 'cpod-sdk',
87
+ version: '1.0.0',
88
+ },
89
+ {
90
+ capabilities: { tools: {} },
91
+ },
92
+ )
93
+
94
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }))
95
+
96
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
97
+ const { name, arguments: args = {} } = request.params
98
+ try {
99
+ const result = await invokeTool(client, name, args)
100
+ return {
101
+ content: [
102
+ {
103
+ type: 'text',
104
+ text: typeof result === 'string'
105
+ ? result
106
+ : JSON.stringify(result, null, 2),
107
+ },
108
+ ],
109
+ }
110
+ } catch (err) {
111
+ return {
112
+ isError: true,
113
+ content: [
114
+ {
115
+ type: 'text',
116
+ text: `Error: ${err?.message || String(err)}`,
117
+ },
118
+ ],
119
+ }
120
+ }
121
+ })
122
+
123
+ // ── Start ────────────────────────────────────────────────────────────────────
124
+ const transport = new StdioServerTransport()
125
+ await server.connect(transport)
126
+
127
+ // Graceful shutdown — closes Mongo/MinIO connections cleanly.
128
+ async function shutdown(signal) {
129
+ process.stderr.write(`cpod-mcp-server: received ${signal}, closing...\n`)
130
+ try { await client.mongo.close() } catch {}
131
+ process.exit(0)
132
+ }
133
+ process.on('SIGINT', () => shutdown('SIGINT'))
134
+ process.on('SIGTERM', () => shutdown('SIGTERM'))