@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 +96 -0
- package/README.md +315 -0
- package/bin/.env.example +21 -0
- package/bin/cpod-mcp-server.mjs +134 -0
- package/dist/client-BRW4z8Ls.d.mts +1073 -0
- package/dist/client-BRW4z8Ls.d.ts +1073 -0
- package/dist/events/index.d.mts +139 -0
- package/dist/events/index.d.ts +139 -0
- package/dist/events/index.js +112 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/index.mjs +109 -0
- package/dist/events/index.mjs.map +1 -0
- package/dist/index.d.mts +6224 -0
- package/dist/index.d.ts +6224 -0
- package/dist/index.js +8272 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8081 -0
- package/dist/index.mjs.map +1 -0
- package/dist/pods/index.d.mts +116 -0
- package/dist/pods/index.d.ts +116 -0
- package/dist/pods/index.js +108 -0
- package/dist/pods/index.js.map +1 -0
- package/dist/pods/index.mjs +106 -0
- package/dist/pods/index.mjs.map +1 -0
- package/dist/tenants/index.d.mts +69 -0
- package/dist/tenants/index.d.ts +69 -0
- package/dist/tenants/index.js +85 -0
- package/dist/tenants/index.js.map +1 -0
- package/dist/tenants/index.mjs +82 -0
- package/dist/tenants/index.mjs.map +1 -0
- package/package.json +75 -0
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
|
package/bin/.env.example
ADDED
|
@@ -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'))
|