@studiometa/productive-mcp 0.4.6 → 0.6.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/Dockerfile +7 -0
- package/README.md +160 -61
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/crypto.js +1 -1
- package/dist/crypto.js.map +1 -1
- package/dist/formatters.d.ts +38 -8
- package/dist/formatters.d.ts.map +1 -1
- package/dist/handlers/bookings.d.ts +6 -0
- package/dist/handlers/bookings.d.ts.map +1 -0
- package/dist/handlers/comments.d.ts +6 -0
- package/dist/handlers/comments.d.ts.map +1 -0
- package/dist/handlers/companies.d.ts +6 -0
- package/dist/handlers/companies.d.ts.map +1 -0
- package/dist/handlers/deals.d.ts +6 -0
- package/dist/handlers/deals.d.ts.map +1 -0
- package/dist/handlers/index.d.ts +15 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/people.d.ts +7 -0
- package/dist/handlers/people.d.ts.map +1 -0
- package/dist/handlers/projects.d.ts +6 -0
- package/dist/handlers/projects.d.ts.map +1 -0
- package/dist/handlers/services.d.ts +6 -0
- package/dist/handlers/services.d.ts.map +1 -0
- package/dist/handlers/tasks.d.ts +6 -0
- package/dist/handlers/tasks.d.ts.map +1 -0
- package/dist/handlers/time.d.ts +6 -0
- package/dist/handlers/time.d.ts.map +1 -0
- package/dist/handlers/timers.d.ts +6 -0
- package/dist/handlers/timers.d.ts.map +1 -0
- package/dist/handlers/types.d.ts +78 -0
- package/dist/handlers/types.d.ts.map +1 -0
- package/dist/handlers/utils.d.ts +17 -0
- package/dist/handlers/utils.d.ts.map +1 -0
- package/dist/handlers.d.ts +3 -12
- package/dist/handlers.d.ts.map +1 -1
- package/dist/handlers.js +2 -135
- package/dist/handlers.js.map +1 -1
- package/dist/http.js +3 -3
- package/dist/http.js.map +1 -1
- package/dist/index-CmTDkz-y.js +480 -0
- package/dist/index-CmTDkz-y.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +1 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +111 -12
- package/dist/oauth.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/stdio.d.ts.map +1 -1
- package/dist/stdio.js +1 -1
- package/dist/stdio.js.map +1 -1
- package/dist/tools.d.ts +3 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +55 -193
- package/dist/tools.js.map +1 -1
- package/dist/version-BmTJXDFu.js +5 -0
- package/dist/{version-uWLfG4Z0.js.map → version-BmTJXDFu.js.map} +1 -1
- package/package.json +45 -45
- package/dist/version-uWLfG4Z0.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
|
@@ -13,15 +13,16 @@ MCP (Model Context Protocol) server for [Productive.io](https://productive.io) A
|
|
|
13
13
|
- 🔑 **OAuth 2.0 support** for Claude Desktop custom connectors
|
|
14
14
|
- 🌐 Deploy once, share with your team via Claude Desktop custom connectors
|
|
15
15
|
- 🐳 Docker-ready for easy deployment
|
|
16
|
+
- ⚡ **Token-optimized** - Single tool design minimizes context usage (~180 tokens)
|
|
16
17
|
- 📦 Built on [@studiometa/productive-cli](../productive-cli)
|
|
17
18
|
|
|
18
19
|
## Usage Modes
|
|
19
20
|
|
|
20
21
|
This package supports two modes:
|
|
21
22
|
|
|
22
|
-
| Mode
|
|
23
|
-
|
|
24
|
-
| **Local (stdio)** | `productive-mcp`
|
|
23
|
+
| Mode | Command | Use Case |
|
|
24
|
+
| ----------------- | ----------------------- | -------------------------------------------- |
|
|
25
|
+
| **Local (stdio)** | `productive-mcp` | Personal use via Claude Desktop config |
|
|
25
26
|
| **Remote (HTTP)** | `productive-mcp-server` | Team use via Claude Desktop custom connector |
|
|
26
27
|
|
|
27
28
|
---
|
|
@@ -39,6 +40,7 @@ npm install -g @studiometa/productive-mcp
|
|
|
39
40
|
### Claude Desktop Configuration
|
|
40
41
|
|
|
41
42
|
Edit your Claude Desktop config:
|
|
43
|
+
|
|
42
44
|
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
43
45
|
- **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
44
46
|
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
|
@@ -130,22 +132,23 @@ services:
|
|
|
130
132
|
dockerfile: packages/productive-mcp/Dockerfile
|
|
131
133
|
restart: unless-stopped
|
|
132
134
|
ports:
|
|
133
|
-
-
|
|
135
|
+
- '3000:3000'
|
|
134
136
|
environment:
|
|
135
137
|
PORT: 3000
|
|
136
138
|
HOST: 0.0.0.0
|
|
137
|
-
OAUTH_SECRET:
|
|
139
|
+
OAUTH_SECRET: 'your-random-secret-here' # Required for production!
|
|
138
140
|
```
|
|
139
141
|
|
|
140
142
|
### Environment Variables
|
|
141
143
|
|
|
142
|
-
| Variable
|
|
143
|
-
|
|
144
|
-
| `PORT`
|
|
145
|
-
| `HOST`
|
|
144
|
+
| Variable | Required | Description |
|
|
145
|
+
| -------------- | -------------------- | -------------------------------------- |
|
|
146
|
+
| `PORT` | No | Server port (default: 3000) |
|
|
147
|
+
| `HOST` | No | Bind address (default: 0.0.0.0) |
|
|
146
148
|
| `OAUTH_SECRET` | **Yes (production)** | Secret key for encrypting OAuth tokens |
|
|
147
149
|
|
|
148
150
|
> ⚠️ **Important**: Always set `OAUTH_SECRET` in production. Generate a random secret:
|
|
151
|
+
>
|
|
149
152
|
> ```bash
|
|
150
153
|
> openssl rand -base64 32
|
|
151
154
|
> ```
|
|
@@ -173,6 +176,7 @@ echo -n "YOUR_ORG_ID:YOUR_API_TOKEN:YOUR_USER_ID" | base64
|
|
|
173
176
|
```
|
|
174
177
|
|
|
175
178
|
Example:
|
|
179
|
+
|
|
176
180
|
```bash
|
|
177
181
|
echo -n "12345:pk_abc123xyz:67890" | base64
|
|
178
182
|
# Output: MTIzNDU6cGtfYWJjMTIzeHl6OjY3ODkw
|
|
@@ -180,56 +184,89 @@ echo -n "12345:pk_abc123xyz:67890" | base64
|
|
|
180
184
|
|
|
181
185
|
### Server Endpoints
|
|
182
186
|
|
|
183
|
-
| Endpoint
|
|
184
|
-
|
|
185
|
-
| `/mcp`
|
|
186
|
-
| `/health`
|
|
187
|
-
| `/`
|
|
188
|
-
| `/authorize`
|
|
189
|
-
| `/authorize`
|
|
190
|
-
| `/token`
|
|
191
|
-
| `/.well-known/oauth-authorization-server` | GET
|
|
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 |
|
|
192
196
|
|
|
193
197
|
---
|
|
194
198
|
|
|
195
|
-
##
|
|
199
|
+
## The `productive` Tool
|
|
196
200
|
|
|
197
|
-
|
|
198
|
-
- `productive_list_projects` - List projects with optional filters
|
|
199
|
-
- `productive_get_project` - Get project details by ID
|
|
201
|
+
The MCP server exposes a single unified tool optimized for minimal token usage:
|
|
200
202
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
```
|
|
204
|
+
productive(resource, action, ...)
|
|
205
|
+
```
|
|
204
206
|
|
|
205
|
-
###
|
|
206
|
-
- `productive_list_time_entries` - List time entries with filters
|
|
207
|
-
- `productive_get_time_entry` - Get time entry details by ID
|
|
208
|
-
- `productive_create_time_entry` - Create a new time entry
|
|
209
|
-
- `productive_update_time_entry` - Update an existing time entry
|
|
210
|
-
- `productive_delete_time_entry` - Delete a time entry
|
|
207
|
+
### Resources & Actions
|
|
211
208
|
|
|
212
|
-
|
|
213
|
-
|
|
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 |
|
|
214
216
|
|
|
215
|
-
###
|
|
216
|
-
- `productive_list_people` - List people in the organization
|
|
217
|
-
- `productive_get_person` - Get person details by ID
|
|
218
|
-
- `productive_get_current_user` - Get current authenticated user
|
|
217
|
+
### Parameters
|
|
219
218
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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 |
|
|
223
233
|
|
|
224
|
-
|
|
234
|
+
### Filter Options
|
|
225
235
|
|
|
226
|
-
|
|
236
|
+
#### Projects
|
|
227
237
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
- `company_id` - Filter by company
|
|
239
|
+
- `project_manager_id` - Filter by project manager
|
|
240
|
+
|
|
241
|
+
#### Time Entries
|
|
242
|
+
|
|
243
|
+
- `person_id` - Filter by person
|
|
244
|
+
- `project_id` - Filter by project
|
|
245
|
+
- `service_id` - Filter by service
|
|
246
|
+
- `after` - After date (YYYY-MM-DD)
|
|
247
|
+
- `before` - Before date (YYYY-MM-DD)
|
|
248
|
+
|
|
249
|
+
#### Tasks
|
|
250
|
+
|
|
251
|
+
- `project_id` - Filter by project
|
|
252
|
+
- `assignee_id` - Filter by assignee
|
|
253
|
+
- `task_list_id` - Filter by task list
|
|
254
|
+
|
|
255
|
+
#### Services
|
|
256
|
+
|
|
257
|
+
- `project_id` - Filter by project
|
|
258
|
+
- `deal_id` - Filter by deal
|
|
259
|
+
|
|
260
|
+
#### People
|
|
261
|
+
|
|
262
|
+
- `archived` - Include archived (boolean)
|
|
263
|
+
|
|
264
|
+
### Configuration Tools (Local mode only)
|
|
265
|
+
|
|
266
|
+
| Tool | Description |
|
|
267
|
+
| ----------------------- | -------------------------------------------------------- |
|
|
268
|
+
| `productive_configure` | Configure credentials (organizationId, apiToken, userId) |
|
|
269
|
+
| `productive_get_config` | View current configuration (token masked) |
|
|
233
270
|
|
|
234
271
|
---
|
|
235
272
|
|
|
@@ -242,13 +279,57 @@ You: "Configure my Productive.io credentials"
|
|
|
242
279
|
Claude: "I'll help you set up. Please provide your Organization ID and API Token..."
|
|
243
280
|
```
|
|
244
281
|
|
|
245
|
-
###
|
|
282
|
+
### List Projects
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
You: "Show me all projects"
|
|
286
|
+
Claude uses: productive(resource="projects", action="list")
|
|
287
|
+
```
|
|
246
288
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
289
|
+
### Get Project Details
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
You: "Get details for project 12345"
|
|
293
|
+
Claude uses: productive(resource="projects", action="get", id="12345")
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Create Time Entry
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
You: "Log 2 hours today on service 456"
|
|
300
|
+
Claude uses: productive(resource="time", action="create", person_id="...", service_id="456", time=120, date="2024-01-15")
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### List My Time Entries
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
You: "What did I work on last week?"
|
|
307
|
+
Claude uses: productive(resource="time", action="list", filter={person_id: "...", after: "2024-01-08", before: "2024-01-14"})
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Get Current User
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
You: "Who am I logged in as?"
|
|
314
|
+
Claude uses: productive(resource="people", action="me")
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### List Tasks for a Project
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
You: "Show tasks for project 789"
|
|
321
|
+
Claude uses: productive(resource="tasks", action="list", filter={project_id: "789"})
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Get Your Productive.io Credentials
|
|
327
|
+
|
|
328
|
+
1. Log into [Productive.io](https://productive.io)
|
|
329
|
+
2. Go to **Settings → Integrations → API**
|
|
330
|
+
3. Generate an API token
|
|
331
|
+
4. Note your Organization ID (visible in URL or API settings)
|
|
332
|
+
5. Note your User ID (click your profile, visible in URL)
|
|
252
333
|
|
|
253
334
|
---
|
|
254
335
|
|
|
@@ -256,8 +337,8 @@ Claude: "I'll help you set up. Please provide your Organization ID and API Token
|
|
|
256
337
|
|
|
257
338
|
```bash
|
|
258
339
|
# Clone the repository
|
|
259
|
-
git clone https://github.com/studiometa/productive-
|
|
260
|
-
cd productive-
|
|
340
|
+
git clone https://github.com/studiometa/productive-tools
|
|
341
|
+
cd productive-tools
|
|
261
342
|
|
|
262
343
|
# Install dependencies
|
|
263
344
|
npm install
|
|
@@ -271,6 +352,9 @@ npm run build -w @studiometa/productive-mcp
|
|
|
271
352
|
# Development mode (watch)
|
|
272
353
|
npm run dev -w @studiometa/productive-mcp
|
|
273
354
|
|
|
355
|
+
# Run tests
|
|
356
|
+
npm test -w @studiometa/productive-mcp
|
|
357
|
+
|
|
274
358
|
# Test local server
|
|
275
359
|
node packages/productive-mcp/dist/index.js
|
|
276
360
|
|
|
@@ -290,7 +374,7 @@ TOKEN=$(echo -n "YOUR_ORG_ID:YOUR_API_TOKEN:YOUR_USER_ID" | base64)
|
|
|
290
374
|
# Test health endpoint
|
|
291
375
|
curl http://localhost:3000/health
|
|
292
376
|
|
|
293
|
-
# Test MCP endpoint
|
|
377
|
+
# Test MCP endpoint - list tools
|
|
294
378
|
curl -X POST http://localhost:3000/mcp \
|
|
295
379
|
-H "Authorization: Bearer $TOKEN" \
|
|
296
380
|
-H "Content-Type: application/json" \
|
|
@@ -300,7 +384,13 @@ curl -X POST http://localhost:3000/mcp \
|
|
|
300
384
|
curl -X POST http://localhost:3000/mcp \
|
|
301
385
|
-H "Authorization: Bearer $TOKEN" \
|
|
302
386
|
-H "Content-Type: application/json" \
|
|
303
|
-
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"
|
|
387
|
+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"productive","arguments":{"resource":"projects","action":"list"}},"id":2}'
|
|
388
|
+
|
|
389
|
+
# Get a specific project
|
|
390
|
+
curl -X POST http://localhost:3000/mcp \
|
|
391
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
392
|
+
-H "Content-Type: application/json" \
|
|
393
|
+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"productive","arguments":{"resource":"projects","action":"get","id":"12345"}},"id":3}'
|
|
304
394
|
```
|
|
305
395
|
|
|
306
396
|
---
|
|
@@ -353,13 +443,22 @@ productive-mcp/
|
|
|
353
443
|
│ ├── http.ts # HTTP routes and MCP endpoint
|
|
354
444
|
│ ├── oauth.ts # OAuth 2.0 endpoints
|
|
355
445
|
│ ├── crypto.ts # Encryption for stateless OAuth tokens
|
|
356
|
-
│ ├── tools.ts # Tool definitions (
|
|
357
|
-
│ ├── handlers.ts # Tool execution
|
|
446
|
+
│ ├── tools.ts # Tool definitions (single unified tool)
|
|
447
|
+
│ ├── handlers.ts # Tool execution logic
|
|
448
|
+
│ ├── formatters.ts # Response formatting with compact mode
|
|
358
449
|
│ └── auth.ts # Bearer token parsing
|
|
359
450
|
├── Dockerfile
|
|
360
451
|
└── README.md
|
|
361
452
|
```
|
|
362
453
|
|
|
454
|
+
### Token Optimization
|
|
455
|
+
|
|
456
|
+
The server uses a single unified `productive` tool instead of multiple individual tools. This reduces the tool schema from ~1300 tokens to ~180 tokens (86% reduction), which:
|
|
457
|
+
|
|
458
|
+
- Reduces context window usage
|
|
459
|
+
- Minimizes compaction frequency
|
|
460
|
+
- Improves response times
|
|
461
|
+
|
|
363
462
|
### OAuth Flow (Stateless)
|
|
364
463
|
|
|
365
464
|
The OAuth implementation is **stateless** - no database or session storage required:
|
|
@@ -386,8 +485,8 @@ MIT © [Studio Meta](https://www.studiometa.fr)
|
|
|
386
485
|
|
|
387
486
|
## Links
|
|
388
487
|
|
|
389
|
-
- [GitHub Repository](https://github.com/studiometa/productive-
|
|
488
|
+
- [GitHub Repository](https://github.com/studiometa/productive-tools)
|
|
390
489
|
- [Productive.io API Docs](https://developer.productive.io)
|
|
391
490
|
- [MCP Documentation](https://modelcontextprotocol.io)
|
|
392
491
|
- [Claude Desktop Custom Connectors](https://docs.anthropic.com)
|
|
393
|
-
- [Issues](https://github.com/studiometa/productive-
|
|
492
|
+
- [Issues](https://github.com/studiometa/productive-tools/issues)
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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");
|
package/dist/crypto.js.map
CHANGED
|
@@ -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!'
|
|
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;"}
|
package/dist/formatters.d.ts
CHANGED
|
@@ -3,39 +3,69 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module re-exports formatters from @studiometa/productive-cli
|
|
5
5
|
* with MCP-specific defaults (no relationship IDs, no timestamps).
|
|
6
|
+
*
|
|
7
|
+
* Supports compact mode to reduce token usage by omitting verbose fields
|
|
8
|
+
* like descriptions and notes from list responses.
|
|
6
9
|
*/
|
|
7
10
|
import { type JsonApiResource, type JsonApiMeta, type FormatOptions, type FormattedPagination } from '@studiometa/productive-cli';
|
|
8
11
|
export type { JsonApiResource, JsonApiMeta, FormatOptions, FormattedPagination };
|
|
12
|
+
/**
|
|
13
|
+
* Extended format options for MCP with compact mode
|
|
14
|
+
*/
|
|
15
|
+
export interface McpFormatOptions {
|
|
16
|
+
compact?: boolean;
|
|
17
|
+
included?: JsonApiResource[];
|
|
18
|
+
}
|
|
9
19
|
/**
|
|
10
20
|
* Format time entry for agent consumption
|
|
11
21
|
*/
|
|
12
|
-
export declare function formatTimeEntry(entry: JsonApiResource,
|
|
22
|
+
export declare function formatTimeEntry(entry: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
|
|
13
23
|
/**
|
|
14
24
|
* Format project for agent consumption
|
|
15
25
|
*/
|
|
16
|
-
export declare function formatProject(project: JsonApiResource,
|
|
26
|
+
export declare function formatProject(project: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
|
|
17
27
|
/**
|
|
18
28
|
* Format task for agent consumption
|
|
19
29
|
* Tasks use included resources to resolve project/company names
|
|
20
30
|
*/
|
|
21
|
-
export declare function formatTask(task: JsonApiResource,
|
|
31
|
+
export declare function formatTask(task: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
|
|
22
32
|
/**
|
|
23
33
|
* Format person for agent consumption
|
|
24
34
|
*/
|
|
25
|
-
export declare function formatPerson(person: JsonApiResource,
|
|
35
|
+
export declare function formatPerson(person: JsonApiResource, options?: McpFormatOptions): Record<string, unknown>;
|
|
26
36
|
/**
|
|
27
37
|
* Format service for agent consumption
|
|
28
38
|
*/
|
|
29
|
-
export declare function formatService(service: JsonApiResource,
|
|
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>;
|
|
30
60
|
/**
|
|
31
61
|
* Format list response with pagination
|
|
32
62
|
*
|
|
33
63
|
* @param data - Array of JSON:API resources
|
|
34
|
-
* @param formatter - Formatter function (item,
|
|
64
|
+
* @param formatter - Formatter function (item, options?) => T
|
|
35
65
|
* @param meta - Pagination metadata
|
|
36
|
-
* @param
|
|
66
|
+
* @param options - MCP format options (compact, included)
|
|
37
67
|
*/
|
|
38
|
-
export declare function formatListResponse<T>(data: JsonApiResource[], formatter: (item: JsonApiResource,
|
|
68
|
+
export declare function formatListResponse<T>(data: JsonApiResource[], formatter: (item: JsonApiResource, options?: McpFormatOptions) => T, meta?: JsonApiMeta, options?: McpFormatOptions): {
|
|
39
69
|
data: T[];
|
|
40
70
|
meta?: FormattedPagination;
|
|
41
71
|
};
|
package/dist/formatters.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|