@studiometa/productive-mcp 0.10.17 → 0.10.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/{handlers-Dk4AQQ3K.js → handlers-DEpVjmEe.js} +40 -15
- package/dist/{handlers-Dk4AQQ3K.js.map → handlers-DEpVjmEe.js.map} +1 -1
- package/dist/handlers.js +1 -1
- package/dist/{http-B7M9meAw.js → http-BV2yXyCc.js} +4 -4
- package/dist/{http-B7M9meAw.js.map → http-BV2yXyCc.js.map} +1 -1
- package/dist/http.js +1 -1
- package/dist/index.js +2 -2
- package/dist/run/bridge.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/{stdio-DlulXg7G.js → stdio-BvCJVE3e.js} +2 -2
- package/dist/{stdio-DlulXg7G.js.map → stdio-BvCJVE3e.js.map} +1 -1
- package/dist/stdio.js +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +159 -109
- package/dist/tools.js.map +1 -1
- package/dist/{version-C8z9HRGX.js → version-oVuraOuX.js} +3 -3
- package/dist/{version-C8z9HRGX.js.map → version-oVuraOuX.js.map} +1 -1
- package/package.json +3 -3
- package/skills/SKILL.md +5 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ MCP (Model Context Protocol) server for [Productive.io](https://productive.io).
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- Single unified `productive` tool — minimal token overhead (~170 tokens)
|
|
12
|
+
- Read-only `productive_read` sibling tool (`readOnlyHint: true`) for clients that only allow read-only tools
|
|
12
13
|
- Smart ID resolution — use emails and project numbers instead of numeric IDs
|
|
13
14
|
- Rich context — `action=context` fetches a resource with all related data in one call
|
|
14
15
|
- Proactive suggestions — data-aware warnings (overdue tasks, long-running timers, etc.)
|
|
@@ -132,6 +133,17 @@ productive(resource, action, ...)
|
|
|
132
133
|
|
|
133
134
|
Use `action="help"` with any resource for detailed documentation on available parameters and filters.
|
|
134
135
|
|
|
136
|
+
### Read-only Tool
|
|
137
|
+
|
|
138
|
+
Some MCP clients only expose tools that have the `readOnlyHint: true` annotation. For these clients, the server also exposes `productive_read`.
|
|
139
|
+
|
|
140
|
+
| Tool | Description |
|
|
141
|
+
| ----------------- | ---------------------------------------------------------------------------------------- |
|
|
142
|
+
| `productive` | All resources and actions, reads and writes |
|
|
143
|
+
| `productive_read` | Same routing and read parameters as `productive`, but only read actions (`readOnlyHint`) |
|
|
144
|
+
|
|
145
|
+
`productive_read` rejects the actions that change data (`create`, `update`, `delete`, `start`, `stop`, `reopen`, `complete_task`, `log_day`, and `resolve` on `discussions`). It also rejects a `batch` that contains one of these actions. Use the `productive` tool for writes.
|
|
146
|
+
|
|
135
147
|
### Common Parameters
|
|
136
148
|
|
|
137
149
|
| Parameter | Type | Description |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,YAAY,CAAC;AA0C7D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,YAAY,CAAC;AA0C7D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAkK7C;;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,CAqNrB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProductiveApi, formatActivity, formatAttachment, formatBooking, formatComment, formatCompany, formatCustomField, formatDeal, formatDiscussion, formatListResponse, formatPage, formatPerson, formatProject, formatService, formatTask, formatTimeEntry, formatTimer } from "@studiometa/productive-api";
|
|
2
|
-
import { ACTIONS, REPORT_TYPES, RESOURCES, ResolveError, VALID_REPORT_TYPES, completeTask, createBooking, createComment, createCompany, createDeal, createDiscussion, createPage, createTask, createTimeEntry, deleteAttachment, deleteDiscussion, deletePage, deleteTimeEntry, fromHandlerContext, getAttachment, getBooking, getComment, getCompany, getCustomField, getDeal, getDealContext, getDiscussion, getMyDaySummary, getPage, getPerson, getProject, getProjectContext, getProjectHealthSummary, getReport, getService, getTask, getTaskContext, getTeamPulseSummary, getTimeEntry, getTimer, listActivities, listAttachments, listBookings, listComments, listCompanies, listCustomFields, listDeals, listDiscussions, listPages, listPeople, listProjects, listServices, listTasks, listTimeEntries, listTimers, logDay, readApi, reopenDiscussion, resolveDiscussion, resolveResource, startTimer, stopTimer, updateBooking, updateComment, updateCompany, updateDeal, updateDiscussion, updatePage, updateTask, updateTimeEntry, weeklyStandup, writeApi } from "@studiometa/productive-core";
|
|
2
|
+
import { ACTIONS, REPORT_TYPES, RESOURCES, ResolveError, VALID_REPORT_TYPES, completeTask, createBooking, createComment, createCompany, createDeal, createDiscussion, createPage, createTask, createTimeEntry, deleteAttachment, deleteDiscussion, deletePage, deleteTimeEntry, fromHandlerContext, getAttachment, getBooking, getComment, getCompany, getCustomField, getDeal, getDealContext, getDiscussion, getMyDaySummary, getPage, getPerson, getProject, getProjectContext, getProjectHealthSummary, getReport, getService, getTask, getTaskContext, getTeamPulseSummary, getTimeEntry, getTimer, isReadCall, listActivities, listAttachments, listBookings, listComments, listCompanies, listCustomFields, listDeals, listDiscussions, listPages, listPeople, listProjects, listServices, listTasks, listTimeEntries, listTimers, logDay, readApi, reopenDiscussion, resolveDiscussion, resolveResource, startTimer, stopTimer, updateBooking, updateComment, updateCompany, updateDeal, updateDiscussion, updatePage, updateTask, updateTimeEntry, weeklyStandup, writeApi } from "@studiometa/productive-core";
|
|
3
3
|
import variant from "@jitl/quickjs-singlefile-cjs-release-sync";
|
|
4
4
|
import { newQuickJSWASMModuleFromVariant } from "quickjs-emscripten-core";
|
|
5
5
|
import { timingSafeEqual } from "node:crypto";
|
|
@@ -40870,21 +40870,27 @@ async function handleReports(action, args, ctx) {
|
|
|
40870
40870
|
}
|
|
40871
40871
|
//#endregion
|
|
40872
40872
|
//#region src/run/bridge.ts
|
|
40873
|
-
/**
|
|
40874
|
-
|
|
40875
|
-
|
|
40876
|
-
|
|
40877
|
-
|
|
40878
|
-
|
|
40879
|
-
|
|
40880
|
-
|
|
40881
|
-
|
|
40882
|
-
|
|
40883
|
-
|
|
40884
|
-
|
|
40873
|
+
/**
|
|
40874
|
+
* Host-side capability bridge for sandboxed scripts.
|
|
40875
|
+
*
|
|
40876
|
+
* The QuickJS sandbox has no direct network, filesystem, or process access.
|
|
40877
|
+
* The only way a script reaches the Productive API is through this bridge,
|
|
40878
|
+
* which re-enters the exact same `executeToolWithCredentials` pipeline used by
|
|
40879
|
+
* every other MCP tool call — the real HTTP request happens here, on the host. That means scripts automatically inherit credential scoping, rate
|
|
40880
|
+
* limiting, include validation, and formatting — and egress is constrained to
|
|
40881
|
+
* the Productive API by construction.
|
|
40882
|
+
*
|
|
40883
|
+
* The bridge also enforces the per-run API-call budget, honours the run's
|
|
40884
|
+
* abort signal (wall-clock timeout), and implements dry-run by classifying
|
|
40885
|
+
* mutating operations and recording them instead of executing.
|
|
40886
|
+
*/
|
|
40887
|
+
/**
|
|
40888
|
+
* Whether a call may mutate data (used for dry-run classification). Fails
|
|
40889
|
+
* closed: a `productive` call that is not a known read is recorded.
|
|
40890
|
+
*/
|
|
40885
40891
|
function isMutating(channel, payload) {
|
|
40886
40892
|
if (channel === "api_write") return true;
|
|
40887
|
-
if (channel === "productive") return
|
|
40893
|
+
if (channel === "productive") return !isReadCall(payload.resource, payload.action);
|
|
40888
40894
|
return false;
|
|
40889
40895
|
}
|
|
40890
40896
|
/** Map a channel to its tool name. */
|
|
@@ -42268,6 +42274,20 @@ async function routeToHandler(resource, action, restArgs, resolveArgs, ctx, cred
|
|
|
42268
42274
|
}
|
|
42269
42275
|
}
|
|
42270
42276
|
/**
|
|
42277
|
+
* Find the first call in a `productive_read` request that is not a read,
|
|
42278
|
+
* including the operations of a batch (recursively, since a batch operation
|
|
42279
|
+
* may itself be a batch). This is an allowlist (see `isReadCall`): unknown or
|
|
42280
|
+
* non-string actions are rejected too. Returns a label for the rejected
|
|
42281
|
+
* action, or undefined when the whole request only reads.
|
|
42282
|
+
*/
|
|
42283
|
+
function findNonReadAction(args) {
|
|
42284
|
+
if (!isReadCall(args.resource, args.action)) return JSON.stringify(args.action) ?? "undefined";
|
|
42285
|
+
if (args.resource === "batch" && Array.isArray(args.operations)) for (const op of args.operations) {
|
|
42286
|
+
const found = findNonReadAction(op && typeof op === "object" ? op : {});
|
|
42287
|
+
if (found) return found;
|
|
42288
|
+
}
|
|
42289
|
+
}
|
|
42290
|
+
/**
|
|
42271
42291
|
* Execute a tool with the given credentials and arguments
|
|
42272
42292
|
*/
|
|
42273
42293
|
async function executeToolWithCredentials(name, args, credentials) {
|
|
@@ -42311,6 +42331,11 @@ async function executeToolWithCredentials(name, args, credentials) {
|
|
|
42311
42331
|
if (!parsed.success) return inputErrorResult(new UserInputError(formatValidationErrors(parsed.error)));
|
|
42312
42332
|
return handleRunScript(parsed.data, credentials, executeToolWithCredentials);
|
|
42313
42333
|
}
|
|
42334
|
+
if (name === "productive_read") {
|
|
42335
|
+
const rejectedAction = findNonReadAction(args);
|
|
42336
|
+
if (rejectedAction) return inputErrorResult(new UserInputError(`Action ${rejectedAction} is not allowed on the read-only tool`, ["productive_read only supports read actions (list, get, resolve, context, help, schema, ...)", `Use the \`productive\` tool to run ${rejectedAction}`]));
|
|
42337
|
+
return executeToolWithCredentials("productive", args, credentials);
|
|
42338
|
+
}
|
|
42314
42339
|
if (name !== "productive") return errorResult(`Unknown tool: ${name}`);
|
|
42315
42340
|
const typedArgs = args;
|
|
42316
42341
|
if (typedArgs.resource === "batch") return handleBatch(typedArgs.operations, credentials, executeToolWithCredentials);
|
|
@@ -42379,4 +42404,4 @@ async function executeToolWithCredentials(name, args, credentials) {
|
|
|
42379
42404
|
//#endregion
|
|
42380
42405
|
export { handleSummaries as C, handlePeople as D, handleProjects as E, handleDeals as O, handleTasks as S, handleSchemaOverview as T, record as _, _null as a, unknown as b, custom as c, literal as d, looseObject as f, preprocess as g, optional as h, _enum as i, discriminatedUnion as l, object as m, resolveRunnerConfig as n, array as o, number as p, runnerTokenMatches as r, boolean as s, executeToolWithCredentials as t, intersection as u, string as v, handleServices as w, datetime as x, union as y };
|
|
42381
42406
|
|
|
42382
|
-
//# sourceMappingURL=handlers-
|
|
42407
|
+
//# sourceMappingURL=handlers-DEpVjmEe.js.map
|