@studiometa/productive-mcp 0.5.0 → 0.6.1

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.
Files changed (62) hide show
  1. package/Dockerfile +7 -0
  2. package/README.md +51 -43
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/crypto.js +1 -1
  6. package/dist/crypto.js.map +1 -1
  7. package/dist/formatters.d.ts +20 -0
  8. package/dist/formatters.d.ts.map +1 -1
  9. package/dist/handlers/bookings.d.ts +6 -0
  10. package/dist/handlers/bookings.d.ts.map +1 -0
  11. package/dist/handlers/comments.d.ts +6 -0
  12. package/dist/handlers/comments.d.ts.map +1 -0
  13. package/dist/handlers/companies.d.ts +6 -0
  14. package/dist/handlers/companies.d.ts.map +1 -0
  15. package/dist/handlers/deals.d.ts +6 -0
  16. package/dist/handlers/deals.d.ts.map +1 -0
  17. package/dist/handlers/index.d.ts +15 -0
  18. package/dist/handlers/index.d.ts.map +1 -0
  19. package/dist/handlers/people.d.ts +7 -0
  20. package/dist/handlers/people.d.ts.map +1 -0
  21. package/dist/handlers/projects.d.ts +6 -0
  22. package/dist/handlers/projects.d.ts.map +1 -0
  23. package/dist/handlers/services.d.ts +6 -0
  24. package/dist/handlers/services.d.ts.map +1 -0
  25. package/dist/handlers/tasks.d.ts +6 -0
  26. package/dist/handlers/tasks.d.ts.map +1 -0
  27. package/dist/handlers/time.d.ts +6 -0
  28. package/dist/handlers/time.d.ts.map +1 -0
  29. package/dist/handlers/timers.d.ts +6 -0
  30. package/dist/handlers/timers.d.ts.map +1 -0
  31. package/dist/handlers/types.d.ts +78 -0
  32. package/dist/handlers/types.d.ts.map +1 -0
  33. package/dist/handlers/utils.d.ts +17 -0
  34. package/dist/handlers/utils.d.ts.map +1 -0
  35. package/dist/handlers.d.ts +3 -10
  36. package/dist/handlers.d.ts.map +1 -1
  37. package/dist/handlers.js +2 -233
  38. package/dist/handlers.js.map +1 -1
  39. package/dist/http.js +3 -3
  40. package/dist/http.js.map +1 -1
  41. package/dist/index-CmTDkz-y.js +480 -0
  42. package/dist/index-CmTDkz-y.js.map +1 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/oauth.d.ts +1 -1
  47. package/dist/oauth.d.ts.map +1 -1
  48. package/dist/oauth.js +111 -12
  49. package/dist/oauth.js.map +1 -1
  50. package/dist/server.d.ts.map +1 -1
  51. package/dist/server.js +2 -2
  52. package/dist/server.js.map +1 -1
  53. package/dist/stdio.d.ts.map +1 -1
  54. package/dist/stdio.js +1 -1
  55. package/dist/stdio.js.map +1 -1
  56. package/dist/tools.d.ts.map +1 -1
  57. package/dist/tools.js +34 -5
  58. package/dist/tools.js.map +1 -1
  59. package/dist/version-BBHuTm1A.js +5 -0
  60. package/dist/{version-eQNCcjOb.js.map → version-BBHuTm1A.js.map} +1 -1
  61. package/package.json +46 -46
  62. package/dist/version-eQNCcjOb.js +0 -5
package/Dockerfile CHANGED
@@ -18,6 +18,10 @@ ENV NODE_ENV=production
18
18
  ENV PORT=3000
19
19
  ENV HOST=0.0.0.0
20
20
 
21
+ # Create non-root user for security
22
+ RUN addgroup -g 1001 -S nodejs && \
23
+ adduser -S nodejs -u 1001 -G nodejs
24
+
21
25
  # Expose port
22
26
  EXPOSE 3000
23
27
 
@@ -25,5 +29,8 @@ EXPOSE 3000
25
29
  HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
26
30
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
27
31
 
32
+ # Switch to non-root user
33
+ USER nodejs
34
+
28
35
  # Run the HTTP server
29
36
  CMD ["productive-mcp-server"]
package/README.md CHANGED
@@ -20,9 +20,9 @@ MCP (Model Context Protocol) server for [Productive.io](https://productive.io) A
20
20
 
21
21
  This package supports two modes:
22
22
 
23
- | Mode | Command | Use Case |
24
- |------|---------|----------|
25
- | **Local (stdio)** | `productive-mcp` | Personal use via Claude Desktop config |
23
+ | Mode | Command | Use Case |
24
+ | ----------------- | ----------------------- | -------------------------------------------- |
25
+ | **Local (stdio)** | `productive-mcp` | Personal use via Claude Desktop config |
26
26
  | **Remote (HTTP)** | `productive-mcp-server` | Team use via Claude Desktop custom connector |
27
27
 
28
28
  ---
@@ -40,6 +40,7 @@ npm install -g @studiometa/productive-mcp
40
40
  ### Claude Desktop Configuration
41
41
 
42
42
  Edit your Claude Desktop config:
43
+
43
44
  - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
44
45
  - **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
45
46
  - **Linux**: `~/.config/Claude/claude_desktop_config.json`
@@ -131,22 +132,23 @@ services:
131
132
  dockerfile: packages/productive-mcp/Dockerfile
132
133
  restart: unless-stopped
133
134
  ports:
134
- - "3000:3000"
135
+ - '3000:3000'
135
136
  environment:
136
137
  PORT: 3000
137
138
  HOST: 0.0.0.0
138
- OAUTH_SECRET: "your-random-secret-here" # Required for production!
139
+ OAUTH_SECRET: 'your-random-secret-here' # Required for production!
139
140
  ```
140
141
 
141
142
  ### Environment Variables
142
143
 
143
- | Variable | Required | Description |
144
- |----------|----------|-------------|
145
- | `PORT` | No | Server port (default: 3000) |
146
- | `HOST` | No | Bind address (default: 0.0.0.0) |
144
+ | Variable | Required | Description |
145
+ | -------------- | -------------------- | -------------------------------------- |
146
+ | `PORT` | No | Server port (default: 3000) |
147
+ | `HOST` | No | Bind address (default: 0.0.0.0) |
147
148
  | `OAUTH_SECRET` | **Yes (production)** | Secret key for encrypting OAuth tokens |
148
149
 
149
150
  > ⚠️ **Important**: Always set `OAUTH_SECRET` in production. Generate a random secret:
151
+ >
150
152
  > ```bash
151
153
  > openssl rand -base64 32
152
154
  > ```
@@ -174,6 +176,7 @@ echo -n "YOUR_ORG_ID:YOUR_API_TOKEN:YOUR_USER_ID" | base64
174
176
  ```
175
177
 
176
178
  Example:
179
+
177
180
  ```bash
178
181
  echo -n "12345:pk_abc123xyz:67890" | base64
179
182
  # Output: MTIzNDU6cGtfYWJjMTIzeHl6OjY3ODkw
@@ -181,15 +184,15 @@ echo -n "12345:pk_abc123xyz:67890" | base64
181
184
 
182
185
  ### Server Endpoints
183
186
 
184
- | Endpoint | Method | Description |
185
- |----------|--------|-------------|
186
- | `/mcp` | POST | MCP JSON-RPC endpoint |
187
- | `/health` | GET | Health check |
188
- | `/` | GET | Server info |
189
- | `/authorize` | GET | OAuth authorization (login form) |
190
- | `/authorize` | POST | OAuth authorization (process login) |
191
- | `/token` | POST | OAuth token exchange |
192
- | `/.well-known/oauth-authorization-server` | GET | OAuth metadata |
187
+ | Endpoint | Method | Description |
188
+ | ----------------------------------------- | ------ | ----------------------------------- |
189
+ | `/mcp` | POST | MCP JSON-RPC endpoint |
190
+ | `/health` | GET | Health check |
191
+ | `/` | GET | Server info |
192
+ | `/authorize` | GET | OAuth authorization (login form) |
193
+ | `/authorize` | POST | OAuth authorization (process login) |
194
+ | `/token` | POST | OAuth token exchange |
195
+ | `/.well-known/oauth-authorization-server` | GET | OAuth metadata |
193
196
 
194
197
  ---
195
198
 
@@ -203,38 +206,40 @@ productive(resource, action, ...)
203
206
 
204
207
  ### Resources & Actions
205
208
 
206
- | Resource | Actions | Description |
207
- |----------|---------|-------------|
208
- | `projects` | `list`, `get` | Project management |
209
- | `time` | `list`, `get`, `create`, `update`, `delete` | Time tracking |
210
- | `tasks` | `list`, `get` | Task management |
211
- | `services` | `list` | Budget line items |
212
- | `people` | `list`, `get`, `me` | Team members |
209
+ | Resource | Actions | Description |
210
+ | ---------- | ------------------------------------------- | ------------------ |
211
+ | `projects` | `list`, `get` | Project management |
212
+ | `time` | `list`, `get`, `create`, `update`, `delete` | Time tracking |
213
+ | `tasks` | `list`, `get` | Task management |
214
+ | `services` | `list` | Budget line items |
215
+ | `people` | `list`, `get`, `me` | Team members |
213
216
 
214
217
  ### Parameters
215
218
 
216
- | Parameter | Type | Description |
217
- |-----------|------|-------------|
218
- | `resource` | string | **Required**. One of: `projects`, `time`, `tasks`, `services`, `people` |
219
- | `action` | string | **Required**. Action to perform (see table above) |
220
- | `id` | string | Resource ID (required for `get`, `update`, `delete`) |
221
- | `filter` | object | Filter criteria for `list` actions |
222
- | `page` | number | Page number for pagination |
223
- | `per_page` | number | Items per page (default: 20, max: 200) |
224
- | `compact` | boolean | Compact output mode (default: true) |
225
- | `person_id` | string | Person ID (for time entry creation) |
226
- | `service_id` | string | Service ID (for time entry creation) |
227
- | `time` | number | Time in minutes (for time entries) |
228
- | `date` | string | Date in YYYY-MM-DD format |
229
- | `note` | string | Note/description |
219
+ | Parameter | Type | Description |
220
+ | ------------ | ------- | ----------------------------------------------------------------------- |
221
+ | `resource` | string | **Required**. One of: `projects`, `time`, `tasks`, `services`, `people` |
222
+ | `action` | string | **Required**. Action to perform (see table above) |
223
+ | `id` | string | Resource ID (required for `get`, `update`, `delete`) |
224
+ | `filter` | object | Filter criteria for `list` actions |
225
+ | `page` | number | Page number for pagination |
226
+ | `per_page` | number | Items per page (default: 20, max: 200) |
227
+ | `compact` | boolean | Compact output mode (default: true) |
228
+ | `person_id` | string | Person ID (for time entry creation) |
229
+ | `service_id` | string | Service ID (for time entry creation) |
230
+ | `time` | number | Time in minutes (for time entries) |
231
+ | `date` | string | Date in YYYY-MM-DD format |
232
+ | `note` | string | Note/description |
230
233
 
231
234
  ### Filter Options
232
235
 
233
236
  #### Projects
237
+
234
238
  - `company_id` - Filter by company
235
239
  - `project_manager_id` - Filter by project manager
236
240
 
237
241
  #### Time Entries
242
+
238
243
  - `person_id` - Filter by person
239
244
  - `project_id` - Filter by project
240
245
  - `service_id` - Filter by service
@@ -242,23 +247,26 @@ productive(resource, action, ...)
242
247
  - `before` - Before date (YYYY-MM-DD)
243
248
 
244
249
  #### Tasks
250
+
245
251
  - `project_id` - Filter by project
246
252
  - `assignee_id` - Filter by assignee
247
253
  - `task_list_id` - Filter by task list
248
254
 
249
255
  #### Services
256
+
250
257
  - `project_id` - Filter by project
251
258
  - `deal_id` - Filter by deal
252
259
 
253
260
  #### People
261
+
254
262
  - `archived` - Include archived (boolean)
255
263
 
256
264
  ### Configuration Tools (Local mode only)
257
265
 
258
- | Tool | Description |
259
- |------|-------------|
260
- | `productive_configure` | Configure credentials (organizationId, apiToken, userId) |
261
- | `productive_get_config` | View current configuration (token masked) |
266
+ | Tool | Description |
267
+ | ----------------------- | -------------------------------------------------------- |
268
+ | `productive_configure` | Configure credentials (organizationId, apiToken, userId) |
269
+ | `productive_get_config` | View current configuration (token masked) |
262
270
 
263
271
  ---
264
272
 
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,qBAAqB,GAAG,IAAI,CAkCnG;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,qBAAqB,GAAG,MAAM,CAM1E"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GACpC,qBAAqB,GAAG,IAAI,CAkC9B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,qBAAqB,GAAG,MAAM,CAM1E"}
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["../src/auth.ts"],"sourcesContent":["/**\n * Authentication utilities for Productive MCP server\n */\n\nexport interface ProductiveCredentials {\n organizationId: string;\n apiToken: string;\n userId?: string;\n}\n\n/**\n * Parse Bearer token containing Productive credentials\n * Token format: base64(organizationId:apiToken) or base64(organizationId:apiToken:userId)\n *\n * @param authHeader - Authorization header value (e.g., \"Bearer base64...\")\n * @returns Parsed credentials or null if invalid\n */\nexport function parseAuthHeader(authHeader: string | undefined | null): ProductiveCredentials | null {\n if (!authHeader) {\n return null;\n }\n\n const match = authHeader.match(/^Bearer\\s+(.+)$/i);\n if (!match) {\n return null;\n }\n\n const token = match[1];\n\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf-8');\n const parts = decoded.split(':');\n\n if (parts.length < 2) {\n return null;\n }\n\n const [organizationId, apiToken, userId] = parts;\n\n if (!organizationId || !apiToken) {\n return null;\n }\n\n return {\n organizationId,\n apiToken,\n userId: userId || undefined,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Create a Bearer token from Productive credentials\n * Useful for documentation and testing\n *\n * @param credentials - Productive credentials\n * @returns Base64 encoded token (without \"Bearer \" prefix)\n */\nexport function createAuthToken(credentials: ProductiveCredentials): string {\n const parts = [credentials.organizationId, credentials.apiToken];\n if (credentials.userId) {\n parts.push(credentials.userId);\n }\n return Buffer.from(parts.join(':')).toString('base64');\n}\n"],"names":[],"mappings":"AAiBO,SAAS,gBAAgB,YAAqE;AACnG,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,WAAW,MAAM,kBAAkB;AACjD,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,CAAC;AAErB,MAAI;AACF,UAAM,UAAU,OAAO,KAAK,OAAO,QAAQ,EAAE,SAAS,OAAO;AAC7D,UAAM,QAAQ,QAAQ,MAAM,GAAG;AAE/B,QAAI,MAAM,SAAS,GAAG;AACpB,aAAO;AAAA,IACT;AAEA,UAAM,CAAC,gBAAgB,UAAU,MAAM,IAAI;AAE3C,QAAI,CAAC,kBAAkB,CAAC,UAAU;AAChC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ,UAAU;AAAA,IAAA;AAAA,EAEtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASO,SAAS,gBAAgB,aAA4C;AAC1E,QAAM,QAAQ,CAAC,YAAY,gBAAgB,YAAY,QAAQ;AAC/D,MAAI,YAAY,QAAQ;AACtB,UAAM,KAAK,YAAY,MAAM;AAAA,EAC/B;AACA,SAAO,OAAO,KAAK,MAAM,KAAK,GAAG,CAAC,EAAE,SAAS,QAAQ;AACvD;"}
1
+ {"version":3,"file":"auth.js","sources":["../src/auth.ts"],"sourcesContent":["/**\n * Authentication utilities for Productive MCP server\n */\n\nexport interface ProductiveCredentials {\n organizationId: string;\n apiToken: string;\n userId?: string;\n}\n\n/**\n * Parse Bearer token containing Productive credentials\n * Token format: base64(organizationId:apiToken) or base64(organizationId:apiToken:userId)\n *\n * @param authHeader - Authorization header value (e.g., \"Bearer base64...\")\n * @returns Parsed credentials or null if invalid\n */\nexport function parseAuthHeader(\n authHeader: string | undefined | null,\n): ProductiveCredentials | null {\n if (!authHeader) {\n return null;\n }\n\n const match = authHeader.match(/^Bearer\\s+(.+)$/i);\n if (!match) {\n return null;\n }\n\n const token = match[1];\n\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf-8');\n const parts = decoded.split(':');\n\n if (parts.length < 2) {\n return null;\n }\n\n const [organizationId, apiToken, userId] = parts;\n\n if (!organizationId || !apiToken) {\n return null;\n }\n\n return {\n organizationId,\n apiToken,\n userId: userId || undefined,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Create a Bearer token from Productive credentials\n * Useful for documentation and testing\n *\n * @param credentials - Productive credentials\n * @returns Base64 encoded token (without \"Bearer \" prefix)\n */\nexport function createAuthToken(credentials: ProductiveCredentials): string {\n const parts = [credentials.organizationId, credentials.apiToken];\n if (credentials.userId) {\n parts.push(credentials.userId);\n }\n return Buffer.from(parts.join(':')).toString('base64');\n}\n"],"names":[],"mappings":"AAiBO,SAAS,gBACd,YAC8B;AAC9B,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,WAAW,MAAM,kBAAkB;AACjD,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,CAAC;AAErB,MAAI;AACF,UAAM,UAAU,OAAO,KAAK,OAAO,QAAQ,EAAE,SAAS,OAAO;AAC7D,UAAM,QAAQ,QAAQ,MAAM,GAAG;AAE/B,QAAI,MAAM,SAAS,GAAG;AACpB,aAAO;AAAA,IACT;AAEA,UAAM,CAAC,gBAAgB,UAAU,MAAM,IAAI;AAE3C,QAAI,CAAC,kBAAkB,CAAC,UAAU;AAChC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ,UAAU;AAAA,IAAA;AAAA,EAEtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASO,SAAS,gBAAgB,aAA4C;AAC1E,QAAM,QAAQ,CAAC,YAAY,gBAAgB,YAAY,QAAQ;AAC/D,MAAI,YAAY,QAAQ;AACtB,UAAM,KAAK,YAAY,MAAM;AAAA,EAC/B;AACA,SAAO,OAAO,KAAK,MAAM,KAAK,GAAG,CAAC,EAAE,SAAS,QAAQ;AACvD;"}
package/dist/crypto.js CHANGED
@@ -37,7 +37,7 @@ function decrypt(ciphertext, secret = getSecret()) {
37
37
  );
38
38
  const encrypted = combined.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
39
39
  const key = deriveKey(secret, salt);
40
- const decipher = createDecipheriv(ALGORITHM, key, iv);
40
+ const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
41
41
  decipher.setAuthTag(authTag);
42
42
  const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
43
43
  return decrypted.toString("utf8");
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.js","sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Cryptographic utilities for stateless OAuth tokens\n *\n * Uses AES-256-GCM for authenticated encryption.\n * The authorization code contains encrypted credentials that can be\n * decrypted without server-side storage.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12; // GCM recommended IV length\nconst AUTH_TAG_LENGTH = 16;\nconst SALT_LENGTH = 16;\n\n/**\n * Derive a 256-bit key from a password using scrypt\n */\nfunction deriveKey(password: string, salt: Buffer): Buffer {\n return scryptSync(password, salt, 32);\n}\n\n/**\n * Get the encryption secret from environment or generate a default\n * In production, OAUTH_SECRET should always be set\n */\nexport function getSecret(): string {\n const secret = process.env.OAUTH_SECRET;\n if (!secret) {\n console.warn(\n 'WARNING: OAUTH_SECRET not set. Using default secret. Set OAUTH_SECRET in production!'\n );\n return 'productive-mcp-default-secret-change-me';\n }\n return secret;\n}\n\n/**\n * Encrypt data using AES-256-GCM\n *\n * Output format: base64(salt + iv + authTag + ciphertext)\n *\n * @param plaintext - Data to encrypt\n * @param secret - Encryption secret (defaults to OAUTH_SECRET env var)\n * @returns Base64-encoded encrypted data\n */\nexport function encrypt(plaintext: string, secret: string = getSecret()): string {\n const salt = randomBytes(SALT_LENGTH);\n const key = deriveKey(secret, salt);\n const iv = randomBytes(IV_LENGTH);\n\n const cipher = createCipheriv(ALGORITHM, key, iv);\n const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n // Combine: salt + iv + authTag + ciphertext\n const combined = Buffer.concat([salt, iv, authTag, encrypted]);\n\n return combined.toString('base64url');\n}\n\n/**\n * Decrypt data encrypted with encrypt()\n *\n * @param ciphertext - Base64-encoded encrypted data\n * @param secret - Encryption secret (defaults to OAUTH_SECRET env var)\n * @returns Decrypted plaintext\n * @throws Error if decryption fails (invalid data or wrong secret)\n */\nexport function decrypt(ciphertext: string, secret: string = getSecret()): string {\n try {\n const combined = Buffer.from(ciphertext, 'base64url');\n\n // Extract components\n const salt = combined.subarray(0, SALT_LENGTH);\n const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const authTag = combined.subarray(\n SALT_LENGTH + IV_LENGTH,\n SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH\n );\n const encrypted = combined.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n\n const key = deriveKey(secret, salt);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv);\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);\n\n return decrypted.toString('utf8');\n } catch {\n throw new Error('Decryption failed: invalid token or secret');\n }\n}\n\n/**\n * Authorization code payload structure\n */\nexport interface AuthCodePayload {\n orgId: string;\n apiToken: string;\n userId?: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n}\n\n/**\n * Create an encrypted authorization code containing credentials and PKCE challenge\n *\n * @param credentials - Object with orgId, apiToken, userId, and optional PKCE params\n * @param expiresInSeconds - Code expiration time (default: 5 minutes)\n * @returns Encrypted authorization code\n */\nexport function createAuthCode(\n credentials: AuthCodePayload,\n expiresInSeconds: number = 300\n): string {\n const payload = {\n ...credentials,\n exp: Date.now() + expiresInSeconds * 1000,\n };\n return encrypt(JSON.stringify(payload));\n}\n\n/**\n * Decode and validate an authorization code\n *\n * @param code - Encrypted authorization code\n * @returns Decoded payload with credentials and PKCE challenge\n * @throws Error if code is invalid or expired\n */\nexport function decodeAuthCode(code: string): AuthCodePayload {\n const payload = JSON.parse(decrypt(code));\n\n if (payload.exp && Date.now() > payload.exp) {\n throw new Error('Authorization code expired');\n }\n\n const { orgId, apiToken, userId, codeChallenge, codeChallengeMethod } = payload;\n\n if (!orgId || !apiToken) {\n throw new Error('Invalid authorization code: missing credentials');\n }\n\n return { orgId, apiToken, userId, codeChallenge, codeChallengeMethod };\n}\n"],"names":[],"mappings":";AAUA,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,cAAc;AAKpB,SAAS,UAAU,UAAkB,MAAsB;AACzD,SAAO,WAAW,UAAU,MAAM,EAAE;AACtC;AAMO,SAAS,YAAoB;AAClC,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,YAAQ;AAAA,MACN;AAAA,IAAA;AAEF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAWO,SAAS,QAAQ,WAAmB,SAAiB,aAAqB;AAC/E,QAAM,OAAO,YAAY,WAAW;AACpC,QAAM,MAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,KAAK,YAAY,SAAS;AAEhC,QAAM,SAAS,eAAe,WAAW,KAAK,EAAE;AAChD,QAAM,YAAY,OAAO,OAAO,CAAC,OAAO,OAAO,WAAW,MAAM,GAAG,OAAO,MAAA,CAAO,CAAC;AAClF,QAAM,UAAU,OAAO,WAAA;AAGvB,QAAM,WAAW,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,SAAS,CAAC;AAE7D,SAAO,SAAS,SAAS,WAAW;AACtC;AAUO,SAAS,QAAQ,YAAoB,SAAiB,aAAqB;AAChF,MAAI;AACF,UAAM,WAAW,OAAO,KAAK,YAAY,WAAW;AAGpD,UAAM,OAAO,SAAS,SAAS,GAAG,WAAW;AAC7C,UAAM,KAAK,SAAS,SAAS,aAAa,cAAc,SAAS;AACjE,UAAM,UAAU,SAAS;AAAA,MACvB,cAAc;AAAA,MACd,cAAc,YAAY;AAAA,IAAA;AAE5B,UAAM,YAAY,SAAS,SAAS,cAAc,YAAY,eAAe;AAE7E,UAAM,MAAM,UAAU,QAAQ,IAAI;AAElC,UAAM,WAAW,iBAAiB,WAAW,KAAK,EAAE;AACpD,aAAS,WAAW,OAAO;AAE3B,UAAM,YAAY,OAAO,OAAO,CAAC,SAAS,OAAO,SAAS,GAAG,SAAS,MAAA,CAAO,CAAC;AAE9E,WAAO,UAAU,SAAS,MAAM;AAAA,EAClC,QAAQ;AACN,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACF;AAoBO,SAAS,eACd,aACA,mBAA2B,KACnB;AACR,QAAM,UAAU;AAAA,IACd,GAAG;AAAA,IACH,KAAK,KAAK,IAAA,IAAQ,mBAAmB;AAAA,EAAA;AAEvC,SAAO,QAAQ,KAAK,UAAU,OAAO,CAAC;AACxC;AASO,SAAS,eAAe,MAA+B;AAC5D,QAAM,UAAU,KAAK,MAAM,QAAQ,IAAI,CAAC;AAExC,MAAI,QAAQ,OAAO,KAAK,IAAA,IAAQ,QAAQ,KAAK;AAC3C,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,QAAM,EAAE,OAAO,UAAU,QAAQ,eAAe,wBAAwB;AAExE,MAAI,CAAC,SAAS,CAAC,UAAU;AACvB,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,SAAO,EAAE,OAAO,UAAU,QAAQ,eAAe,oBAAA;AACnD;"}
1
+ {"version":3,"file":"crypto.js","sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Cryptographic utilities for stateless OAuth tokens\n *\n * Uses AES-256-GCM for authenticated encryption.\n * The authorization code contains encrypted credentials that can be\n * decrypted without server-side storage.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12; // GCM recommended IV length\nconst AUTH_TAG_LENGTH = 16;\nconst SALT_LENGTH = 16;\n\n/**\n * Derive a 256-bit key from a password using scrypt\n */\nfunction deriveKey(password: string, salt: Buffer): Buffer {\n return scryptSync(password, salt, 32);\n}\n\n/**\n * Get the encryption secret from environment or generate a default\n * In production, OAUTH_SECRET should always be set\n */\nexport function getSecret(): string {\n const secret = process.env.OAUTH_SECRET;\n if (!secret) {\n console.warn(\n 'WARNING: OAUTH_SECRET not set. Using default secret. Set OAUTH_SECRET in production!',\n );\n return 'productive-mcp-default-secret-change-me';\n }\n return secret;\n}\n\n/**\n * Encrypt data using AES-256-GCM\n *\n * Output format: base64(salt + iv + authTag + ciphertext)\n *\n * @param plaintext - Data to encrypt\n * @param secret - Encryption secret (defaults to OAUTH_SECRET env var)\n * @returns Base64-encoded encrypted data\n */\nexport function encrypt(plaintext: string, secret: string = getSecret()): string {\n const salt = randomBytes(SALT_LENGTH);\n const key = deriveKey(secret, salt);\n const iv = randomBytes(IV_LENGTH);\n\n const cipher = createCipheriv(ALGORITHM, key, iv);\n const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n // Combine: salt + iv + authTag + ciphertext\n const combined = Buffer.concat([salt, iv, authTag, encrypted]);\n\n return combined.toString('base64url');\n}\n\n/**\n * Decrypt data encrypted with encrypt()\n *\n * @param ciphertext - Base64-encoded encrypted data\n * @param secret - Encryption secret (defaults to OAUTH_SECRET env var)\n * @returns Decrypted plaintext\n * @throws Error if decryption fails (invalid data or wrong secret)\n */\nexport function decrypt(ciphertext: string, secret: string = getSecret()): string {\n try {\n const combined = Buffer.from(ciphertext, 'base64url');\n\n // Extract components\n const salt = combined.subarray(0, SALT_LENGTH);\n const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const authTag = combined.subarray(\n SALT_LENGTH + IV_LENGTH,\n SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH,\n );\n const encrypted = combined.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n\n const key = deriveKey(secret, salt);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);\n\n return decrypted.toString('utf8');\n } catch {\n throw new Error('Decryption failed: invalid token or secret');\n }\n}\n\n/**\n * Authorization code payload structure\n */\nexport interface AuthCodePayload {\n orgId: string;\n apiToken: string;\n userId?: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n}\n\n/**\n * Create an encrypted authorization code containing credentials and PKCE challenge\n *\n * @param credentials - Object with orgId, apiToken, userId, and optional PKCE params\n * @param expiresInSeconds - Code expiration time (default: 5 minutes)\n * @returns Encrypted authorization code\n */\nexport function createAuthCode(\n credentials: AuthCodePayload,\n expiresInSeconds: number = 300,\n): string {\n const payload = {\n ...credentials,\n exp: Date.now() + expiresInSeconds * 1000,\n };\n return encrypt(JSON.stringify(payload));\n}\n\n/**\n * Decode and validate an authorization code\n *\n * @param code - Encrypted authorization code\n * @returns Decoded payload with credentials and PKCE challenge\n * @throws Error if code is invalid or expired\n */\nexport function decodeAuthCode(code: string): AuthCodePayload {\n const payload = JSON.parse(decrypt(code));\n\n if (payload.exp && Date.now() > payload.exp) {\n throw new Error('Authorization code expired');\n }\n\n const { orgId, apiToken, userId, codeChallenge, codeChallengeMethod } = payload;\n\n if (!orgId || !apiToken) {\n throw new Error('Invalid authorization code: missing credentials');\n }\n\n return { orgId, apiToken, userId, codeChallenge, codeChallengeMethod };\n}\n"],"names":[],"mappings":";AAUA,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,cAAc;AAKpB,SAAS,UAAU,UAAkB,MAAsB;AACzD,SAAO,WAAW,UAAU,MAAM,EAAE;AACtC;AAMO,SAAS,YAAoB;AAClC,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,YAAQ;AAAA,MACN;AAAA,IAAA;AAEF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAWO,SAAS,QAAQ,WAAmB,SAAiB,aAAqB;AAC/E,QAAM,OAAO,YAAY,WAAW;AACpC,QAAM,MAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,KAAK,YAAY,SAAS;AAEhC,QAAM,SAAS,eAAe,WAAW,KAAK,EAAE;AAChD,QAAM,YAAY,OAAO,OAAO,CAAC,OAAO,OAAO,WAAW,MAAM,GAAG,OAAO,MAAA,CAAO,CAAC;AAClF,QAAM,UAAU,OAAO,WAAA;AAGvB,QAAM,WAAW,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,SAAS,CAAC;AAE7D,SAAO,SAAS,SAAS,WAAW;AACtC;AAUO,SAAS,QAAQ,YAAoB,SAAiB,aAAqB;AAChF,MAAI;AACF,UAAM,WAAW,OAAO,KAAK,YAAY,WAAW;AAGpD,UAAM,OAAO,SAAS,SAAS,GAAG,WAAW;AAC7C,UAAM,KAAK,SAAS,SAAS,aAAa,cAAc,SAAS;AACjE,UAAM,UAAU,SAAS;AAAA,MACvB,cAAc;AAAA,MACd,cAAc,YAAY;AAAA,IAAA;AAE5B,UAAM,YAAY,SAAS,SAAS,cAAc,YAAY,eAAe;AAE7E,UAAM,MAAM,UAAU,QAAQ,IAAI;AAElC,UAAM,WAAW,iBAAiB,WAAW,KAAK,IAAI,EAAE,eAAe,iBAAiB;AACxF,aAAS,WAAW,OAAO;AAE3B,UAAM,YAAY,OAAO,OAAO,CAAC,SAAS,OAAO,SAAS,GAAG,SAAS,MAAA,CAAO,CAAC;AAE9E,WAAO,UAAU,SAAS,MAAM;AAAA,EAClC,QAAQ;AACN,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACF;AAoBO,SAAS,eACd,aACA,mBAA2B,KACnB;AACR,QAAM,UAAU;AAAA,IACd,GAAG;AAAA,IACH,KAAK,KAAK,IAAA,IAAQ,mBAAmB;AAAA,EAAA;AAEvC,SAAO,QAAQ,KAAK,UAAU,OAAO,CAAC;AACxC;AASO,SAAS,eAAe,MAA+B;AAC5D,QAAM,UAAU,KAAK,MAAM,QAAQ,IAAI,CAAC;AAExC,MAAI,QAAQ,OAAO,KAAK,IAAA,IAAQ,QAAQ,KAAK;AAC3C,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,QAAM,EAAE,OAAO,UAAU,QAAQ,eAAe,wBAAwB;AAExE,MAAI,CAAC,SAAS,CAAC,UAAU;AACvB,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,SAAO,EAAE,OAAO,UAAU,QAAQ,eAAe,oBAAA;AACnD;"}
@@ -37,6 +37,26 @@ export declare function formatPerson(person: JsonApiResource, options?: McpForma
37
37
  * Format service for agent consumption
38
38
  */
39
39
  export declare function formatService(service: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
40
+ /**
41
+ * Format company for agent consumption
42
+ */
43
+ export declare function formatCompany(company: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
44
+ /**
45
+ * Format comment for agent consumption
46
+ */
47
+ export declare function formatComment(comment: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
48
+ /**
49
+ * Format timer for agent consumption
50
+ */
51
+ export declare function formatTimer(timer: JsonApiResource, _options?: McpFormatOptions): Record<string, unknown>;
52
+ /**
53
+ * Format deal for agent consumption
54
+ */
55
+ export declare function formatDeal(deal: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
56
+ /**
57
+ * Format booking for agent consumption
58
+ */
59
+ export declare function formatBooking(booking: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
40
60
  /**
41
61
  * Format list response with pagination
42
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAOL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AAcjF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B;AAgBD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,IAAI,EAAE,eAAe,EAAE,EACvB,SAAS,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,CAAC,EACnE,IAAI,CAAC,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,gBAAgB,GACzB;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAY3C"}
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAYL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AAcjF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B;AAaD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EACtB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,IAAI,EAAE,eAAe,EAAE,EACvB,SAAS,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,CAAC,EACnE,IAAI,CAAC,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,gBAAgB,GACzB;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAY3C"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Bookings resource handler
3
+ */
4
+ import type { HandlerContext, BookingArgs, ToolResult } from './types.js';
5
+ export declare function handleBookings(action: string, args: BookingArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=bookings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookings.d.ts","sourceRoot":"","sources":["../../src/handlers/bookings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK1E,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CAmDrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Comments resource handler
3
+ */
4
+ import type { HandlerContext, CommentArgs, ToolResult } from './types.js';
5
+ export declare function handleComments(action: string, args: CommentArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/handlers/comments.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK1E,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CA0CrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Companies resource handler
3
+ */
4
+ import type { HandlerContext, CompanyArgs, ToolResult } from './types.js';
5
+ export declare function handleCompanies(action: string, args: CompanyArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=companies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"companies.d.ts","sourceRoot":"","sources":["../../src/handlers/companies.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK1E,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CA8BrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Deals resource handler
3
+ */
4
+ import type { HandlerContext, DealArgs, ToolResult } from './types.js';
5
+ export declare function handleDeals(action: string, args: DealArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=deals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deals.d.ts","sourceRoot":"","sources":["../../src/handlers/deals.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKvE,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CA0CrB"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Tool execution handlers for Productive MCP server
3
+ * These are shared between stdio and HTTP transports
4
+ *
5
+ * Single consolidated tool for minimal token overhead:
6
+ * - productive: resource + action based API
7
+ */
8
+ import type { ProductiveCredentials } from '../auth.js';
9
+ import type { ToolResult } from './types.js';
10
+ export type { ToolResult } from './types.js';
11
+ /**
12
+ * Execute a tool with the given credentials and arguments
13
+ */
14
+ export declare function executeToolWithCredentials(name: string, args: Record<string, unknown>, credentials: ProductiveCredentials): Promise<ToolResult>;
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,YAAY,CAAC;AAgB7D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA2C7C;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,EAAE,qBAAqB,GACjC,OAAO,CAAC,UAAU,CAAC,CA4ErB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * People resource handler
3
+ */
4
+ import type { ProductiveCredentials } from '../auth.js';
5
+ import type { HandlerContext, CommonArgs, ToolResult } from './types.js';
6
+ export declare function handlePeople(action: string, args: CommonArgs, ctx: HandlerContext, credentials: ProductiveCredentials): Promise<ToolResult>;
7
+ //# sourceMappingURL=people.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"people.d.ts","sourceRoot":"","sources":["../../src/handlers/people.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKzE,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,cAAc,EACnB,WAAW,EAAE,qBAAqB,GACjC,OAAO,CAAC,UAAU,CAAC,CA2BrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Projects resource handler
3
+ */
4
+ import type { HandlerContext, CommonArgs, ToolResult } from './types.js';
5
+ export declare function handleProjects(action: string, args: CommonArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/handlers/projects.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKzE,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CAgBrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Services resource handler
3
+ */
4
+ import type { HandlerContext, CommonArgs, ToolResult } from './types.js';
5
+ export declare function handleServices(action: string, _args: CommonArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/handlers/services.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKzE,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CASrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tasks resource handler
3
+ */
4
+ import type { HandlerContext, TaskArgs, ToolResult } from './types.js';
5
+ export declare function handleTasks(action: string, args: TaskArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/handlers/tasks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKvE,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CA8CrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Time entries resource handler
3
+ */
4
+ import type { HandlerContext, CommonArgs, ToolResult } from './types.js';
5
+ export declare function handleTime(action: string, args: CommonArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/handlers/time.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKzE,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CAyCrB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Timers resource handler
3
+ */
4
+ import type { HandlerContext, TimerArgs, ToolResult } from './types.js';
5
+ export declare function handleTimers(action: string, args: TimerArgs, ctx: HandlerContext): Promise<ToolResult>;
6
+ //# sourceMappingURL=timers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timers.d.ts","sourceRoot":"","sources":["../../src/handlers/timers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKxE,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,UAAU,CAAC,CA8BrB"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Shared types for resource handlers
3
+ */
4
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
5
+ import type { ProductiveApi } from '@studiometa/productive-cli';
6
+ import type { McpFormatOptions } from '../formatters.js';
7
+ export type ToolResult = CallToolResult;
8
+ /**
9
+ * Context passed to each resource handler
10
+ */
11
+ export interface HandlerContext {
12
+ api: ProductiveApi;
13
+ formatOptions: McpFormatOptions;
14
+ filter?: Record<string, string>;
15
+ page?: number;
16
+ perPage: number;
17
+ }
18
+ /**
19
+ * Common args shared across resources
20
+ */
21
+ export interface CommonArgs {
22
+ id?: string;
23
+ person_id?: string;
24
+ service_id?: string;
25
+ task_id?: string;
26
+ company_id?: string;
27
+ time?: number;
28
+ date?: string;
29
+ note?: string;
30
+ }
31
+ /**
32
+ * Task-specific args
33
+ */
34
+ export interface TaskArgs extends CommonArgs {
35
+ title?: string;
36
+ project_id?: string;
37
+ task_list_id?: string;
38
+ description?: string;
39
+ assignee_id?: string;
40
+ }
41
+ /**
42
+ * Comment-specific args
43
+ */
44
+ export interface CommentArgs extends CommonArgs {
45
+ body?: string;
46
+ deal_id?: string;
47
+ }
48
+ /**
49
+ * Timer-specific args
50
+ */
51
+ export interface TimerArgs extends CommonArgs {
52
+ time_entry_id?: string;
53
+ }
54
+ /**
55
+ * Deal-specific args
56
+ */
57
+ export interface DealArgs extends CommonArgs {
58
+ name?: string;
59
+ }
60
+ /**
61
+ * Booking-specific args
62
+ */
63
+ export interface BookingArgs extends CommonArgs {
64
+ started_on?: string;
65
+ ended_on?: string;
66
+ event_id?: string;
67
+ }
68
+ /**
69
+ * Company-specific args
70
+ */
71
+ export interface CompanyArgs extends CommonArgs {
72
+ name?: string;
73
+ }
74
+ /**
75
+ * Resource handler function signature
76
+ */
77
+ export type ResourceHandler<T extends CommonArgs = CommonArgs> = (action: string, args: T, ctx: HandlerContext) => Promise<ToolResult>;
78
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEhE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,aAAa,CAAC;IACnB,aAAa,EAAE,gBAAgB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI,CAC/D,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,cAAc,KAChB,OAAO,CAAC,UAAU,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Utility functions for resource handlers
3
+ */
4
+ import type { ToolResult } from './types.js';
5
+ /**
6
+ * Helper to create a successful JSON response
7
+ */
8
+ export declare function jsonResult(data: unknown): ToolResult;
9
+ /**
10
+ * Helper to create an error response
11
+ */
12
+ export declare function errorResult(message: string): ToolResult;
13
+ /**
14
+ * Convert unknown filter to string filter for API
15
+ */
16
+ export declare function toStringFilter(filter?: Record<string, unknown>): Record<string, string> | undefined;
17
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/handlers/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAIpD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAKvD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CASpC"}
@@ -1,15 +1,8 @@
1
1
  /**
2
2
  * Tool execution handlers for Productive MCP server
3
- * These are shared between stdio and HTTP transports
4
3
  *
5
- * Single consolidated tool for minimal token overhead:
6
- * - productive: resource + action based API
4
+ * This module re-exports from the handlers/ directory for backwards compatibility.
5
+ * The handlers have been refactored into separate modules for better maintainability.
7
6
  */
8
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
9
- import type { ProductiveCredentials } from './auth.js';
10
- export type ToolResult = CallToolResult;
11
- /**
12
- * Execute a tool with the given credentials and arguments
13
- */
14
- export declare function executeToolWithCredentials(name: string, args: Record<string, unknown>, credentials: ProductiveCredentials): Promise<ToolResult>;
7
+ export { executeToolWithCredentials, type ToolResult } from './handlers/index.js';
15
8
  //# sourceMappingURL=handlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAWvD,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC;AA0DxC;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,EAAE,qBAAqB,GACjC,OAAO,CAAC,UAAU,CAAC,CAoLrB"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,0BAA0B,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC"}