@studiometa/productive-mcp 0.10.14 → 0.10.15

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 (45) hide show
  1. package/dist/handlers/api-read.d.ts +2 -1
  2. package/dist/handlers/api-read.d.ts.map +1 -1
  3. package/dist/handlers/api-utils.d.ts +20 -0
  4. package/dist/handlers/api-utils.d.ts.map +1 -1
  5. package/dist/handlers/help.d.ts +20 -0
  6. package/dist/handlers/help.d.ts.map +1 -1
  7. package/dist/handlers/index.d.ts.map +1 -1
  8. package/dist/handlers/run.d.ts +24 -0
  9. package/dist/handlers/run.d.ts.map +1 -0
  10. package/dist/handlers/search-docs.d.ts +22 -0
  11. package/dist/handlers/search-docs.d.ts.map +1 -0
  12. package/dist/{handlers-BE96O-uy.js → handlers-BEeOjytC.js} +974 -10
  13. package/dist/{handlers-BE96O-uy.js.map → handlers-BEeOjytC.js.map} +1 -1
  14. package/dist/handlers.js +1 -1
  15. package/dist/{http-DF9K8Fr4.js → http-BXP2cZn1.js} +4 -4
  16. package/dist/{http-DF9K8Fr4.js.map → http-BXP2cZn1.js.map} +1 -1
  17. package/dist/http.js +1 -1
  18. package/dist/index.js +2 -2
  19. package/dist/run/bridge.d.ts +53 -0
  20. package/dist/run/bridge.d.ts.map +1 -0
  21. package/dist/run/docs.d.ts +23 -0
  22. package/dist/run/docs.d.ts.map +1 -0
  23. package/dist/run/engine.d.ts +60 -0
  24. package/dist/run/engine.d.ts.map +1 -0
  25. package/dist/run/limits.d.ts +33 -0
  26. package/dist/run/limits.d.ts.map +1 -0
  27. package/dist/run/prelude.d.ts +27 -0
  28. package/dist/run/prelude.d.ts.map +1 -0
  29. package/dist/run/render.d.ts +30 -0
  30. package/dist/run/render.d.ts.map +1 -0
  31. package/dist/run/strip.d.ts +20 -0
  32. package/dist/run/strip.d.ts.map +1 -0
  33. package/dist/schema.d.ts +13 -2
  34. package/dist/schema.d.ts.map +1 -1
  35. package/dist/server.js +2 -2
  36. package/dist/{stdio-BnfO285Q.js → stdio-pJj1QGos.js} +2 -2
  37. package/dist/{stdio-BnfO285Q.js.map → stdio-pJj1QGos.js.map} +1 -1
  38. package/dist/stdio.js +1 -1
  39. package/dist/tools.d.ts.map +1 -1
  40. package/dist/tools.js +93 -4
  41. package/dist/tools.js.map +1 -1
  42. package/dist/{version-XQYsroYk.js → version-CQgSvXUX.js} +3 -3
  43. package/dist/{version-XQYsroYk.js.map → version-CQgSvXUX.js.map} +1 -1
  44. package/package.json +5 -3
  45. package/skills/SKILL.md +71 -1
package/dist/http.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as handleToolsList, i as handleInitialize, n as createHttpMcpServer, o as jsonRpcError, r as createHttpMcpTransport, s as jsonRpcSuccess, t as createHttpApp } from "./http-DF9K8Fr4.js";
1
+ import { a as handleToolsList, i as handleInitialize, n as createHttpMcpServer, o as jsonRpcError, r as createHttpMcpTransport, s as jsonRpcSuccess, t as createHttpApp } from "./http-BXP2cZn1.js";
2
2
  export { createHttpApp, createHttpMcpServer, createHttpMcpTransport, handleInitialize, handleToolsList, jsonRpcError, jsonRpcSuccess };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-XQYsroYk.js";
3
- import { a as handlePrompt, n as getAvailableTools, s as handleToolCall, t as getAvailablePrompts } from "./stdio-BnfO285Q.js";
2
+ import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-CQgSvXUX.js";
3
+ import { a as handlePrompt, n as getAvailableTools, s as handleToolCall, t as getAvailablePrompts } from "./stdio-pJj1QGos.js";
4
4
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Host-side capability bridge for sandboxed scripts.
3
+ *
4
+ * The QuickJS sandbox has no direct network, filesystem, or process access.
5
+ * The only way a script reaches the Productive API is through this bridge,
6
+ * which re-enters the exact same `executeToolWithCredentials` pipeline used by
7
+ * every other MCP tool call — the real HTTP request happens here, on the host. That means scripts automatically inherit credential scoping, rate
8
+ * limiting, include validation, and formatting — and egress is constrained to
9
+ * the Productive API by construction.
10
+ *
11
+ * The bridge also enforces the per-run API-call budget, honours the run's
12
+ * abort signal (wall-clock timeout), and implements dry-run by classifying
13
+ * mutating operations and recording them instead of executing.
14
+ */
15
+ import type { ProductiveCredentials } from '../auth.js';
16
+ import type { ToolResult } from '../handlers/types.js';
17
+ import type { RunLimits } from './limits.js';
18
+ /** The subset of `executeToolWithCredentials` the bridge depends on. */
19
+ export type ToolExecutor = (name: string, args: Record<string, unknown>, credentials: ProductiveCredentials) => Promise<ToolResult>;
20
+ /** Logical channels a script may call through. */
21
+ export type BridgeChannel = 'productive' | 'api_read' | 'api_write';
22
+ /** A mutating call recorded (not executed) during a dry run. */
23
+ export interface RecordedCall {
24
+ channel: BridgeChannel;
25
+ payload: Record<string, unknown>;
26
+ }
27
+ export interface BridgeOptions {
28
+ credentials: ProductiveCredentials;
29
+ exec: ToolExecutor;
30
+ limits: RunLimits;
31
+ dryRun: boolean;
32
+ signal: AbortSignal;
33
+ }
34
+ export interface Bridge {
35
+ /**
36
+ * Execute a single bridged call, returning the result as a JSON **string**
37
+ * (the text the tool already produced). Returning text rather than a parsed
38
+ * value lets the engine hand it straight to the guest without an extra
39
+ * parse-then-restringify round trip. Throws on error results so guest-side
40
+ * `try/catch` works naturally.
41
+ */
42
+ call(channel: BridgeChannel, payload: Record<string, unknown>): Promise<string>;
43
+ /** Snapshot of usage so the handler can report it. */
44
+ getStats(): {
45
+ apiCalls: number;
46
+ recorded: RecordedCall[];
47
+ };
48
+ }
49
+ /**
50
+ * Create a bridge bound to a set of credentials and limits.
51
+ */
52
+ export declare function createBridge(opts: BridgeOptions): Bridge;
53
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/run/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,EAAE,qBAAqB,KAC/B,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,qBAAqB,CAAC;IACnC,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,MAAM;IACrB;;;;;;OAMG;IACH,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChF,sDAAsD;IACtD,QAAQ,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAC5D;AA4CD;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAgCxD"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Scripting-API reference content for `run_script`, surfaced through the global
3
+ * `search_docs` tool (resources / endpoints / scripting all discoverable from
4
+ * one place).
5
+ *
6
+ * This module owns the content as structured sections; `search_docs` is the
7
+ * only consumer (it lists section titles in its table of contents and returns
8
+ * matching section bodies on a query). The resource list is derived from
9
+ * {@link SCRIPT_RESOURCES} so it can't drift from what the prelude exposes.
10
+ */
11
+ export interface DocSection {
12
+ title: string;
13
+ /** One-line description shown in the table of contents. */
14
+ summary: string;
15
+ keywords: string[];
16
+ body: string;
17
+ }
18
+ export declare const DOC_SECTIONS: DocSection[];
19
+ /** Section titles, for the documentation table of contents. */
20
+ export declare function docSectionTitles(): string[];
21
+ /** Find scripting-doc sections matching a query (title, keywords, or body). */
22
+ export declare function findDocSections(query: string): DocSection[];
23
+ //# sourceMappingURL=docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/run/docs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,YAAY,EAAE,UAAU,EAwHpC,CAAC;AAEF,+DAA+D;AAC/D,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAS3D"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * QuickJS-WASM execution engine for sandboxed scripts.
3
+ *
4
+ * Each run gets a fresh QuickJS context (isolated globals + heap) created from
5
+ * a single, process-wide cached WASM module. The sandbox has no ambient
6
+ * capabilities: the only host functions exposed are `__hostCall` (bridged API
7
+ * access, returns a real guest Promise so scripts can `await` naturally) and
8
+ * `__emit` (output buffering).
9
+ *
10
+ * Limits enforced here:
11
+ * - memory — `runtime.setMemoryLimit`
12
+ * - CPU/loop — an interrupt handler with a wall-clock deadline (fires even
13
+ * while the host event loop is blocked by a synchronous loop)
14
+ * - hang — an abort-signal race around the async completion wait
15
+ * - output — buffered output is capped and flagged as truncated
16
+ */
17
+ import { type QuickJSWASMModule } from 'quickjs-emscripten-core';
18
+ import type { RunLimits } from './limits.js';
19
+ /** A single buffered output entry produced by `output.*`. */
20
+ export interface OutputEntry {
21
+ type: string;
22
+ data: unknown;
23
+ }
24
+ /** Successful engine result. */
25
+ export interface EngineResult {
26
+ result: unknown;
27
+ output: OutputEntry[];
28
+ truncated: boolean;
29
+ }
30
+ /**
31
+ * Host call performed on behalf of the guest. Returns the result as a JSON
32
+ * **string** (the text the tool already produced), which is handed straight to
33
+ * the guest without a re-serialization round trip.
34
+ */
35
+ export type HostCall = (channel: string, payload: Record<string, unknown>) => Promise<string>;
36
+ export interface RunScriptInput {
37
+ code: string;
38
+ args: string[];
39
+ flags: Record<string, unknown>;
40
+ limits: RunLimits;
41
+ signal: AbortSignal;
42
+ hostCall: HostCall;
43
+ }
44
+ /** Error raised when a script fails to compile, throws, or times out. */
45
+ export declare class ScriptError extends Error {
46
+ readonly stackTrace?: string;
47
+ constructor(message: string, stackTrace?: string);
48
+ }
49
+ /**
50
+ * Lazily create and cache the QuickJS WASM module (decision: cache the
51
+ * compiled module across runs; each run still gets a fresh context).
52
+ */
53
+ export declare function getQuickJSModule(): Promise<QuickJSWASMModule>;
54
+ /**
55
+ * Run a script to completion in a sandboxed QuickJS context.
56
+ *
57
+ * @throws {ScriptError} on compile error, uncaught guest error, or timeout.
58
+ */
59
+ export declare function runScript(input: RunScriptInput): Promise<EngineResult>;
60
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/run/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,6DAA6D;AAC7D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAED,gCAAgC;AAChC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,yEAAyE;AACzE,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpC,YAAY,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAI/C;CACF;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAK7D;AAyJD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CA6E5E"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Resource limits and feature gating for the sandboxed `run_script` tool.
3
+ *
4
+ * The tool is disabled by default and must be explicitly enabled via
5
+ * `PRODUCTIVE_MCP_ENABLE_RUN=true` — same gating model as `api_write`. This
6
+ * keeps hosted HTTP deployments safe until an operator opts in.
7
+ *
8
+ * Every limit is configurable via an environment variable so operators can
9
+ * tune the sandbox for their deployment. Invalid values fall back to the
10
+ * default rather than throwing.
11
+ */
12
+ /** Resolved resource limits for a single script run. */
13
+ export interface RunLimits {
14
+ /** Wall-clock budget for the whole run, in milliseconds. */
15
+ timeoutMs: number;
16
+ /** Maximum heap the QuickJS runtime may allocate, in bytes. */
17
+ memoryBytes: number;
18
+ /** Maximum number of bridged API calls a single run may make. */
19
+ maxApiCalls: number;
20
+ /** Maximum serialized size of buffered output + result, in bytes. */
21
+ maxOutputBytes: number;
22
+ /** Maximum size of the submitted script source, in bytes. */
23
+ maxCodeBytes: number;
24
+ }
25
+ /**
26
+ * Whether the `run_script` tool is enabled. Off unless explicitly turned on.
27
+ */
28
+ export declare function isRunScriptEnabled(env?: NodeJS.ProcessEnv): boolean;
29
+ /**
30
+ * Resolve the active resource limits from the environment.
31
+ */
32
+ export declare function resolveRunLimits(env?: NodeJS.ProcessEnv): RunLimits;
33
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/run/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wDAAwD;AACxD,MAAM,WAAW,SAAS;IACxB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEhF;AAWD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAUhF"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Guest-side JavaScript prelude injected into the sandbox before user code.
3
+ *
4
+ * It builds the `productive`, `api`, `output`, `args`, and `flags` globals on
5
+ * top of two host primitives provided by the engine:
6
+ *
7
+ * - `__hostCall(channel, payloadJson)` → Promise<resultJson> — bridged API call
8
+ * - `__emit(entryJson)` — synchronous output buffering
9
+ *
10
+ * The surface deliberately mirrors the `productive` tool's resource/action
11
+ * model (what agents already know) rather than the fluent SDK, since no SDK
12
+ * code runs inside the sandbox.
13
+ */
14
+ /** Resources that get a `productive.<resource>` convenience accessor. */
15
+ export declare const SCRIPT_RESOURCES: ("activities" | "attachments" | "batch" | "bookings" | "comments" | "companies" | "custom_fields" | "deals" | "discussions" | "pages" | "people" | "projects" | "reports" | "search" | "services" | "summaries" | "tasks" | "time" | "timers" | "workflows")[];
16
+ export interface PreludeOptions {
17
+ args: string[];
18
+ flags: Record<string, unknown>;
19
+ }
20
+ /**
21
+ * Build the prelude source for a run.
22
+ *
23
+ * `args` and `flags` are embedded as JSON literals (safe — JSON is a subset of
24
+ * JS expression syntax).
25
+ */
26
+ export declare function buildPrelude(opts: PreludeOptions): string;
27
+ //# sourceMappingURL=prelude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prelude.d.ts","sourceRoot":"","sources":["../../src/run/prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,gQAAsD,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CA2DzD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Markdown rendering for `run_script` results.
3
+ *
4
+ * The tool returns two things (per the MCP structured-output convention):
5
+ * - `structuredContent` — the raw `{ result, output, _run }` object for hosts
6
+ * that consume structured tool output;
7
+ * - a `text` content block — this human-facing Markdown rendering, so clients
8
+ * that only display text still get formatted tables/JSON/logs rather than a
9
+ * single opaque JSON blob.
10
+ *
11
+ * MCP has no "table" content type, so "rendering" here means producing Markdown
12
+ * (tables, fenced code blocks, labelled lines).
13
+ */
14
+ import type { OutputEntry } from './engine.js';
15
+ export interface RunStats {
16
+ apiCalls: number;
17
+ dryRun: boolean;
18
+ outputTruncated?: boolean;
19
+ recorded?: unknown[];
20
+ }
21
+ export interface RunRenderInput {
22
+ result: unknown;
23
+ output: OutputEntry[];
24
+ run: RunStats;
25
+ }
26
+ /**
27
+ * Render a run result as human-facing Markdown.
28
+ */
29
+ export declare function renderRunResult(input: RunRenderInput): string;
30
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/run/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,GAAG,EAAE,QAAQ,CAAC;CACf;AA4FD;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAqB7D"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * TypeScript type-stripping for submitted scripts.
3
+ *
4
+ * QuickJS executes JavaScript, so any TypeScript syntax in a submitted script
5
+ * must be transformed away first. We use Node's built-in
6
+ * {@link stripTypeScriptTypes} in `transform` mode, which also lowers `enum`
7
+ * and parameter properties (not just erasing annotations).
8
+ *
9
+ * Plain JavaScript passes through unchanged. If the source cannot be parsed as
10
+ * TypeScript at all, the original is returned untouched so the sandbox can
11
+ * surface the real syntax error to the caller.
12
+ */
13
+ /**
14
+ * Strip TypeScript types from a script, returning runnable JavaScript.
15
+ *
16
+ * @param code - The submitted script source (TypeScript or JavaScript).
17
+ * @returns JavaScript with type syntax removed.
18
+ */
19
+ export declare function stripTypes(code: string): string;
20
+ //# sourceMappingURL=strip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip.d.ts","sourceRoot":"","sources":["../../src/run/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ/C"}
package/dist/schema.d.ts CHANGED
@@ -265,7 +265,8 @@ export type ProductiveToolInput = z.infer<typeof ProductiveToolInputSchema>;
265
265
  * Full input schema for the raw api_read tool.
266
266
  */
267
267
  export declare const ApiReadToolInputSchema: z.ZodObject<{
268
- path: z.ZodString;
268
+ path: z.ZodOptional<z.ZodString>;
269
+ search: z.ZodOptional<z.ZodString>;
269
270
  describe: z.ZodOptional<z.ZodBoolean>;
270
271
  filter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
271
272
  include: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -292,6 +293,16 @@ export declare const ApiWriteToolInputSchema: z.ZodObject<{
292
293
  dry_run: z.ZodOptional<z.ZodBoolean>;
293
294
  }, z.core.$strip>;
294
295
  export type ApiWriteToolInput = z.infer<typeof ApiWriteToolInputSchema>;
296
+ /**
297
+ * Full input schema for the sandboxed run_script tool.
298
+ */
299
+ export declare const RunScriptToolInputSchema: z.ZodObject<{
300
+ code: z.ZodString;
301
+ args: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
302
+ flags: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
303
+ dry_run: z.ZodOptional<z.ZodBoolean>;
304
+ }, z.core.$strip>;
305
+ export type RunScriptToolInput = z.infer<typeof RunScriptToolInputSchema>;
295
306
  /**
296
307
  * Validate and parse tool input
297
308
  * Returns parsed data or throws with validation errors
@@ -305,5 +316,5 @@ export declare function safeValidateToolInput(input: unknown): ZodSafeParseResul
305
316
  /**
306
317
  * Format Zod validation errors for LLM consumption
307
318
  */
308
- export declare function formatValidationErrors(error: ZodError<ProductiveToolInput | ApiReadToolInput | ApiWriteToolInput>): string;
319
+ export declare function formatValidationErrors(error: ZodError<ProductiveToolInput | ApiReadToolInput | ApiWriteToolInput | RunScriptToolInput>): string;
309
320
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,CAAC,EAAoB,KAAK,kBAAkB,EAAE,KAAK,QAAQ,EAAE,MAAM,KAAK,CAAC;AAGlF,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAMhF;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;EAAoB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;EAAkB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAAuB,CAAC;AAMrD;;GAEG;AACH,eAAO,MAAM,OAAO,aAIgC,CAAC;AAErD;;GAEG;AACH,eAAO,MAAM,aAAa,aAKvB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,cAAc,aAKxB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,cAAc,aAGqD,CAAC;AAEjF;;GAEG;AACH,eAAO,MAAM,WAAW,aAG+C,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,cAAc,aAGuD,CAAC;AAEnF;;GAEG;AACH,eAAO,MAAM,WAAW,aAG+C,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,SAAS,aAIuC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,gBAAgB,aAKiE,CAAC;AAE/F;;GAEG;AACH,eAAO,MAAM,SAAS,2BAKiC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,YAAY,2BAMsC,CAAC;AAEhE;;GAEG;AACH,eAAO,MAAM,YAAY,6BAKtB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,UAAU,aAMpB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,cAAc,wCAKxB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,YAAY,6BAKtB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,YAAY,yBAItB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,SAAS,aAAkD,CAAC;AAEzE;;GAEG;AACH,eAAO,MAAM,UAAU,aAIqB,CAAC;AAE7C;;GAEG;AACH,eAAO,MAAM,SAAS,aAGgB,CAAC;AAMvC;;GAEG;AACH,eAAO,MAAM,YAAY,uDAGsB,CAAC;AAMhD;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqFpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;iBAUjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAMlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAMxE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAErE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAAC,mBAAmB,CAAC,CAE7F;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,QAAQ,CAAC,mBAAmB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC,GAC1E,MAAM,CAOR"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as VERSION } from "./version-XQYsroYk.js";
3
- import { t as createHttpApp } from "./http-DF9K8Fr4.js";
2
+ import { t as VERSION } from "./version-CQgSvXUX.js";
3
+ import { t as createHttpApp } from "./http-BXP2cZn1.js";
4
4
  import { toNodeHandler } from "h3";
5
5
  import { createServer } from "node:http";
6
6
  //#region src/server.ts
@@ -1,4 +1,4 @@
1
- import { t as executeToolWithCredentials } from "./handlers-BE96O-uy.js";
1
+ import { t as executeToolWithCredentials } from "./handlers-BEeOjytC.js";
2
2
  import { STDIO_ONLY_TOOLS, TOOLS } from "./tools.js";
3
3
  import { getConfig, setConfig } from "@studiometa/productive-api";
4
4
  //#region src/prompts/definitions.ts
@@ -354,4 +354,4 @@ async function handlePrompt(name, args) {
354
354
  //#endregion
355
355
  export { handlePrompt as a, handleGetConfigTool as i, getAvailableTools as n, handleSetupPrompt as o, handleConfigureTool as r, handleToolCall as s, getAvailablePrompts as t };
356
356
 
357
- //# sourceMappingURL=stdio-BnfO285Q.js.map
357
+ //# sourceMappingURL=stdio-pJj1QGos.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio-BnfO285Q.js","names":[],"sources":["../src/prompts/definitions.ts","../src/prompts/handlers.ts","../src/stdio.ts"],"sourcesContent":["/**\n * MCP Prompt Template Definitions\n *\n * Prompt templates serve as guided conversation starters that instruct the LLM\n * which productive tool calls to make and how to format the output.\n */\n\nexport interface PromptArgument {\n name: string;\n description: string;\n required: boolean;\n}\n\nexport interface PromptDefinition {\n name: string;\n description: string;\n arguments: PromptArgument[];\n}\n\nexport const PROMPT_DEFINITIONS: PromptDefinition[] = [\n {\n name: 'end-of-day',\n description: 'Compose an end-of-day standup message based on your activity today',\n arguments: [\n {\n name: 'format',\n description: 'Output format: slack, email, or plain (default: plain)',\n required: false,\n },\n ],\n },\n {\n name: 'project-review',\n description: 'Analyze project health and status with budget, tasks, and timeline overview',\n arguments: [\n {\n name: 'project',\n description: 'Project ID or project number (e.g. PRJ-123)',\n required: true,\n },\n ],\n },\n {\n name: 'plan-sprint',\n description: 'Help prioritize tasks for the next sprint based on open tasks and budget',\n arguments: [\n {\n name: 'project',\n description: 'Project ID or project number (e.g. PRJ-123)',\n required: true,\n },\n ],\n },\n {\n name: 'weekly-report',\n description: 'Generate a polished weekly progress report',\n arguments: [\n {\n name: 'person',\n description: 'Person email or ID to report on (default: current user)',\n required: false,\n },\n {\n name: 'format',\n description: 'Output format: slack, email, or plain (default: plain)',\n required: false,\n },\n ],\n },\n {\n name: 'invoice-prep',\n description: 'Prepare a billing summary for a project within a date range',\n arguments: [\n {\n name: 'project',\n description: 'Project ID or project number (e.g. PRJ-123)',\n required: true,\n },\n {\n name: 'from',\n description: 'Start date in YYYY-MM-DD format',\n required: true,\n },\n {\n name: 'to',\n description: 'End date in YYYY-MM-DD format',\n required: true,\n },\n ],\n },\n];\n","/**\n * MCP Prompt Template Handlers\n *\n * Returns messages arrays that instruct the LLM which productive tool calls to\n * make and how to format and present the output to the user.\n */\n\ntype PromptMessage = {\n role: string;\n content: { type: string; text: string };\n};\n\ntype PromptResult = {\n messages: PromptMessage[];\n};\n\n/**\n * Build the end-of-day standup prompt messages\n */\nfunction endOfDay(args?: Record<string, string>): PromptResult {\n const format = args?.format ?? 'plain';\n\n const formatInstruction =\n format === 'slack'\n ? 'Format the output as a Slack message using emoji bullets and bold text with *asterisks*.'\n : format === 'email'\n ? 'Format the output as a professional email with a subject line and proper greeting/closing.'\n : 'Format the output as a plain-text standup message with clear sections.';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please compose my end-of-day standup message.\n\nFirst, call the productive tool to gather today's activity:\n\\`\\`\\`json\n{ \"resource\": \"summaries\", \"action\": \"my_day\" }\n\\`\\`\\`\n\nThen compose a standup message with these sections:\n- **What I did today**: Summarize completed tasks and logged time by project\n- **What I'm working on tomorrow**: Any open/in-progress tasks from today\n- **Blockers**: Note any overdue tasks or items needing attention\n\n${formatInstruction}\n\nKeep it concise — this is for a standup, not a novel.`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the project-review prompt messages\n */\nfunction projectReview(args?: Record<string, string>): PromptResult {\n const project = args?.project ?? '';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please analyze the health and status of project ${project ? `\"${project}\"` : '(project not specified)'}.\n\nFetch all project context in one call:\n\\`\\`\\`json\n{ \"resource\": \"projects\", \"action\": \"context\", \"id\": \"${project}\" }\n\\`\\`\\`\n\nAlso check the project's tasks for a fuller picture:\n\\`\\`\\`json\n{ \"resource\": \"tasks\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\", \"status\": 1 }, \"per_page\": 50 }\n\\`\\`\\`\n\nThen provide a structured project health review covering:\n1. **Overview**: Project name, client, current status\n2. **Budget**: Budget used vs total, burn rate, projected overage risk\n3. **Tasks**: Open vs completed task counts, overdue tasks, upcoming deadlines\n4. **Recent activity**: What has been worked on recently\n5. **Health assessment**: Overall RAG status (🟢 On track / 🟡 At risk / 🔴 Critical) with reasoning\n6. **Recommendations**: Concrete next steps or concerns to address`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the plan-sprint prompt messages\n */\nfunction planSprint(args?: Record<string, string>): PromptResult {\n const project = args?.project ?? '';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please help me plan the next sprint for project ${project ? `\"${project}\"` : '(project not specified)'}.\n\nFetch all open tasks for the project:\n\\`\\`\\`json\n{ \"resource\": \"tasks\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\", \"status\": 1 }, \"per_page\": 100 }\n\\`\\`\\`\n\nFetch the project's budget services to understand available capacity:\n\\`\\`\\`json\n{ \"resource\": \"services\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\" }, \"per_page\": 50 }\n\\`\\`\\`\n\nThen provide sprint planning recommendations:\n1. **Backlog summary**: Total open tasks, categories/task-lists breakdown\n2. **Priority candidates**: Top tasks to tackle next sprint based on:\n - Due dates and overdue items\n - Task dependencies (if visible)\n - Effort estimates\n3. **Budget check**: Remaining budget per service — flag if any service is near its limit\n4. **Suggested sprint scope**: A realistic list of tasks for a 1-2 week sprint\n5. **Risks**: Tasks that are blocked, unassigned, or missing estimates`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the weekly-report prompt messages\n */\nfunction weeklyReport(args?: Record<string, string>): PromptResult {\n const person = args?.person;\n const format = args?.format ?? 'plain';\n\n const personInstruction = person\n ? `Focus on person \"${person}\". Resolve the person ID if needed using: { \"resource\": \"people\", \"action\": \"resolve\", \"query\": \"${person}\" } first.`\n : 'Focus on the current authenticated user (use the default person from summaries).';\n\n const formatInstruction =\n format === 'slack'\n ? 'Format as a Slack message with emoji bullets, bold project names, and a summary header.'\n : format === 'email'\n ? 'Format as a professional email with subject line, greeting, bulleted highlights per project, and a closing.'\n : 'Format as a clean plain-text weekly report with clear section headers.';\n\n const standupCall = person\n ? `First resolve the person ID, then fetch weekly standup data:\n\\`\\`\\`json\n{ \"resource\": \"people\", \"action\": \"resolve\", \"query\": \"${person}\" }\n\\`\\`\\`\n\\`\\`\\`json\n{ \"resource\": \"workflows\", \"action\": \"weekly_standup\", \"person_id\": \"<resolved_person_id>\" }\n\\`\\`\\``\n : `Fetch weekly standup data:\n\\`\\`\\`json\n{ \"resource\": \"workflows\", \"action\": \"weekly_standup\" }\n\\`\\`\\``;\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please generate a weekly progress report.\n\n${personInstruction}\n\n${standupCall}\n\nThen compose a polished weekly report with:\n1. **This week's accomplishments**: Completed tasks grouped by project\n2. **Time logged**: Hours per project this week, total hours\n3. **In progress**: Tasks still open that were worked on\n4. **Upcoming**: Any tasks with deadlines in the next 7 days\n5. **Highlights**: Notable wins or milestones reached\n\n${formatInstruction}`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the invoice-prep prompt messages\n */\nfunction invoicePrep(args?: Record<string, string>): PromptResult {\n const project = args?.project ?? '';\n const from = args?.from ?? '';\n const to = args?.to ?? '';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please prepare a billing summary for project ${project ? `\"${project}\"` : '(project not specified)'} from ${from || '(start date not specified)'} to ${to || '(end date not specified)'}.\n\nFetch time entries for the date range:\n\\`\\`\\`json\n{\n \"resource\": \"time\",\n \"action\": \"list\",\n \"filter\": {\n \"project_id\": \"${project}\",\n \"after\": \"${from}\",\n \"before\": \"${to}\"\n },\n \"include\": [\"person\", \"service\"],\n \"per_page\": 200\n}\n\\`\\`\\`\n\nFetch project services/budget lines:\n\\`\\`\\`json\n{ \"resource\": \"services\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\" }, \"per_page\": 50 }\n\\`\\`\\`\n\nFetch the project deal/budget details:\n\\`\\`\\`json\n{ \"resource\": \"deals\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\", \"type\": 2 }, \"per_page\": 10 }\n\\`\\`\\`\n\nThen produce a billing summary:\n1. **Period**: ${from} → ${to}\n2. **Time entries by service**: Hours logged per service/budget line with team member breakdown\n3. **Billable totals**: If rates are available, calculate amounts per service\n4. **Budget consumed**: Planned vs actual hours per service\n5. **Invoice line items**: Suggested line items ready to copy into an invoice\n6. **Notes**: Any unbilled items, write-offs, or adjustments to flag`,\n },\n },\n ],\n };\n}\n\n/**\n * Get messages for a named prompt template\n */\nexport function getPromptMessages(name: string, args?: Record<string, string>): PromptResult {\n switch (name) {\n case 'end-of-day':\n return endOfDay(args);\n case 'project-review':\n return projectReview(args);\n case 'plan-sprint':\n return planSprint(args);\n case 'weekly-report':\n return weeklyReport(args);\n case 'invoice-prep':\n return invoicePrep(args);\n default:\n throw new Error(`Unknown prompt: ${name}`);\n }\n}\n","/**\n * Stdio transport handlers for Productive MCP Server\n *\n * This module contains the handler logic for the stdio transport.\n * The actual server startup is in index.ts.\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\nimport { getConfig, setConfig } from '@studiometa/productive-api';\n\nimport { executeToolWithCredentials } from './handlers.js';\nimport { PROMPT_DEFINITIONS, getPromptMessages } from './prompts/index.js';\nimport { TOOLS, STDIO_ONLY_TOOLS } from './tools.js';\n\nexport type ToolResult = CallToolResult;\n\n/**\n * Get all available tools (including stdio-only configuration tools)\n */\nexport function getAvailableTools() {\n return [...TOOLS, ...STDIO_ONLY_TOOLS];\n}\n\n/**\n * Get available prompts\n */\nexport function getAvailablePrompts() {\n return [\n {\n name: 'setup_productive',\n description: 'Interactive setup for Productive.io credentials',\n arguments: [],\n },\n ...PROMPT_DEFINITIONS,\n ];\n}\n\n/**\n * Handle the setup_productive prompt\n */\nexport async function handleSetupPrompt(): Promise<{\n messages: Array<{ role: string; content: { type: string; text: string } }>;\n}> {\n const config = await getConfig();\n const hasConfig = !!(config.organizationId && config.apiToken);\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: hasConfig\n ? 'I have already configured Productive.io credentials. Would you like to update them?'\n : 'I need to configure my Productive.io credentials. Please help me set up:\\n1. Organization ID\\n2. API Token\\n3. User ID (optional)',\n },\n },\n ],\n };\n}\n\n/**\n * Handle the productive_configure tool\n */\nexport async function handleConfigureTool(args: {\n organizationId: string;\n apiToken: string;\n userId?: string;\n}): Promise<ToolResult> {\n const { organizationId, apiToken, userId } = args;\n\n await setConfig('organizationId', organizationId);\n await setConfig('apiToken', apiToken);\n if (userId) {\n await setConfig('userId', userId);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n success: true,\n message: 'Productive.io credentials configured successfully',\n configured: {\n organizationId,\n userId: userId || 'not set',\n apiToken: '***' + apiToken.slice(-4),\n },\n },\n null,\n 2,\n ),\n },\n ],\n };\n}\n\n/**\n * Handle the productive_get_config tool\n */\nexport async function handleGetConfigTool(): Promise<ToolResult> {\n const currentConfig = await getConfig();\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n organizationId: currentConfig.organizationId || 'not configured',\n userId: currentConfig.userId || 'not configured',\n apiToken: currentConfig.apiToken\n ? '***' + currentConfig.apiToken.slice(-4)\n : 'not configured',\n configured: !!(currentConfig.organizationId && currentConfig.apiToken),\n },\n null,\n 2,\n ),\n },\n ],\n };\n}\n\n/**\n * Handle a tool call request\n */\nexport async function handleToolCall(\n name: string,\n args: Record<string, unknown>,\n): Promise<ToolResult> {\n // Handle stdio-only configuration tools\n if (name === 'productive_configure') {\n return handleConfigureTool(args as Parameters<typeof handleConfigureTool>[0]);\n }\n\n if (name === 'productive_get_config') {\n return handleGetConfigTool();\n }\n\n // Get config for API tools\n const config = await getConfig();\n if (!config.organizationId || !config.apiToken) {\n return {\n content: [\n {\n type: 'text',\n text: 'Error: Productive.io credentials not configured. Please use the \"productive_configure\" tool to set your credentials, or set PRODUCTIVE_ORG_ID and PRODUCTIVE_API_TOKEN environment variables.',\n },\n ],\n isError: true,\n };\n }\n\n // Execute tool with credentials from config\n return executeToolWithCredentials(name, args, {\n organizationId: config.organizationId,\n apiToken: config.apiToken,\n userId: config.userId,\n });\n}\n\n/**\n * Handle a prompt request\n */\nexport async function handlePrompt(\n name: string,\n args?: Record<string, string>,\n): Promise<{\n messages: Array<{ role: string; content: { type: string; text: string } }>;\n}> {\n if (name === 'setup_productive') {\n return handleSetupPrompt();\n }\n\n return getPromptMessages(name, args);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stdio-pJj1QGos.js","names":[],"sources":["../src/prompts/definitions.ts","../src/prompts/handlers.ts","../src/stdio.ts"],"sourcesContent":["/**\n * MCP Prompt Template Definitions\n *\n * Prompt templates serve as guided conversation starters that instruct the LLM\n * which productive tool calls to make and how to format the output.\n */\n\nexport interface PromptArgument {\n name: string;\n description: string;\n required: boolean;\n}\n\nexport interface PromptDefinition {\n name: string;\n description: string;\n arguments: PromptArgument[];\n}\n\nexport const PROMPT_DEFINITIONS: PromptDefinition[] = [\n {\n name: 'end-of-day',\n description: 'Compose an end-of-day standup message based on your activity today',\n arguments: [\n {\n name: 'format',\n description: 'Output format: slack, email, or plain (default: plain)',\n required: false,\n },\n ],\n },\n {\n name: 'project-review',\n description: 'Analyze project health and status with budget, tasks, and timeline overview',\n arguments: [\n {\n name: 'project',\n description: 'Project ID or project number (e.g. PRJ-123)',\n required: true,\n },\n ],\n },\n {\n name: 'plan-sprint',\n description: 'Help prioritize tasks for the next sprint based on open tasks and budget',\n arguments: [\n {\n name: 'project',\n description: 'Project ID or project number (e.g. PRJ-123)',\n required: true,\n },\n ],\n },\n {\n name: 'weekly-report',\n description: 'Generate a polished weekly progress report',\n arguments: [\n {\n name: 'person',\n description: 'Person email or ID to report on (default: current user)',\n required: false,\n },\n {\n name: 'format',\n description: 'Output format: slack, email, or plain (default: plain)',\n required: false,\n },\n ],\n },\n {\n name: 'invoice-prep',\n description: 'Prepare a billing summary for a project within a date range',\n arguments: [\n {\n name: 'project',\n description: 'Project ID or project number (e.g. PRJ-123)',\n required: true,\n },\n {\n name: 'from',\n description: 'Start date in YYYY-MM-DD format',\n required: true,\n },\n {\n name: 'to',\n description: 'End date in YYYY-MM-DD format',\n required: true,\n },\n ],\n },\n];\n","/**\n * MCP Prompt Template Handlers\n *\n * Returns messages arrays that instruct the LLM which productive tool calls to\n * make and how to format and present the output to the user.\n */\n\ntype PromptMessage = {\n role: string;\n content: { type: string; text: string };\n};\n\ntype PromptResult = {\n messages: PromptMessage[];\n};\n\n/**\n * Build the end-of-day standup prompt messages\n */\nfunction endOfDay(args?: Record<string, string>): PromptResult {\n const format = args?.format ?? 'plain';\n\n const formatInstruction =\n format === 'slack'\n ? 'Format the output as a Slack message using emoji bullets and bold text with *asterisks*.'\n : format === 'email'\n ? 'Format the output as a professional email with a subject line and proper greeting/closing.'\n : 'Format the output as a plain-text standup message with clear sections.';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please compose my end-of-day standup message.\n\nFirst, call the productive tool to gather today's activity:\n\\`\\`\\`json\n{ \"resource\": \"summaries\", \"action\": \"my_day\" }\n\\`\\`\\`\n\nThen compose a standup message with these sections:\n- **What I did today**: Summarize completed tasks and logged time by project\n- **What I'm working on tomorrow**: Any open/in-progress tasks from today\n- **Blockers**: Note any overdue tasks or items needing attention\n\n${formatInstruction}\n\nKeep it concise — this is for a standup, not a novel.`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the project-review prompt messages\n */\nfunction projectReview(args?: Record<string, string>): PromptResult {\n const project = args?.project ?? '';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please analyze the health and status of project ${project ? `\"${project}\"` : '(project not specified)'}.\n\nFetch all project context in one call:\n\\`\\`\\`json\n{ \"resource\": \"projects\", \"action\": \"context\", \"id\": \"${project}\" }\n\\`\\`\\`\n\nAlso check the project's tasks for a fuller picture:\n\\`\\`\\`json\n{ \"resource\": \"tasks\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\", \"status\": 1 }, \"per_page\": 50 }\n\\`\\`\\`\n\nThen provide a structured project health review covering:\n1. **Overview**: Project name, client, current status\n2. **Budget**: Budget used vs total, burn rate, projected overage risk\n3. **Tasks**: Open vs completed task counts, overdue tasks, upcoming deadlines\n4. **Recent activity**: What has been worked on recently\n5. **Health assessment**: Overall RAG status (🟢 On track / 🟡 At risk / 🔴 Critical) with reasoning\n6. **Recommendations**: Concrete next steps or concerns to address`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the plan-sprint prompt messages\n */\nfunction planSprint(args?: Record<string, string>): PromptResult {\n const project = args?.project ?? '';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please help me plan the next sprint for project ${project ? `\"${project}\"` : '(project not specified)'}.\n\nFetch all open tasks for the project:\n\\`\\`\\`json\n{ \"resource\": \"tasks\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\", \"status\": 1 }, \"per_page\": 100 }\n\\`\\`\\`\n\nFetch the project's budget services to understand available capacity:\n\\`\\`\\`json\n{ \"resource\": \"services\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\" }, \"per_page\": 50 }\n\\`\\`\\`\n\nThen provide sprint planning recommendations:\n1. **Backlog summary**: Total open tasks, categories/task-lists breakdown\n2. **Priority candidates**: Top tasks to tackle next sprint based on:\n - Due dates and overdue items\n - Task dependencies (if visible)\n - Effort estimates\n3. **Budget check**: Remaining budget per service — flag if any service is near its limit\n4. **Suggested sprint scope**: A realistic list of tasks for a 1-2 week sprint\n5. **Risks**: Tasks that are blocked, unassigned, or missing estimates`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the weekly-report prompt messages\n */\nfunction weeklyReport(args?: Record<string, string>): PromptResult {\n const person = args?.person;\n const format = args?.format ?? 'plain';\n\n const personInstruction = person\n ? `Focus on person \"${person}\". Resolve the person ID if needed using: { \"resource\": \"people\", \"action\": \"resolve\", \"query\": \"${person}\" } first.`\n : 'Focus on the current authenticated user (use the default person from summaries).';\n\n const formatInstruction =\n format === 'slack'\n ? 'Format as a Slack message with emoji bullets, bold project names, and a summary header.'\n : format === 'email'\n ? 'Format as a professional email with subject line, greeting, bulleted highlights per project, and a closing.'\n : 'Format as a clean plain-text weekly report with clear section headers.';\n\n const standupCall = person\n ? `First resolve the person ID, then fetch weekly standup data:\n\\`\\`\\`json\n{ \"resource\": \"people\", \"action\": \"resolve\", \"query\": \"${person}\" }\n\\`\\`\\`\n\\`\\`\\`json\n{ \"resource\": \"workflows\", \"action\": \"weekly_standup\", \"person_id\": \"<resolved_person_id>\" }\n\\`\\`\\``\n : `Fetch weekly standup data:\n\\`\\`\\`json\n{ \"resource\": \"workflows\", \"action\": \"weekly_standup\" }\n\\`\\`\\``;\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please generate a weekly progress report.\n\n${personInstruction}\n\n${standupCall}\n\nThen compose a polished weekly report with:\n1. **This week's accomplishments**: Completed tasks grouped by project\n2. **Time logged**: Hours per project this week, total hours\n3. **In progress**: Tasks still open that were worked on\n4. **Upcoming**: Any tasks with deadlines in the next 7 days\n5. **Highlights**: Notable wins or milestones reached\n\n${formatInstruction}`,\n },\n },\n ],\n };\n}\n\n/**\n * Build the invoice-prep prompt messages\n */\nfunction invoicePrep(args?: Record<string, string>): PromptResult {\n const project = args?.project ?? '';\n const from = args?.from ?? '';\n const to = args?.to ?? '';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Please prepare a billing summary for project ${project ? `\"${project}\"` : '(project not specified)'} from ${from || '(start date not specified)'} to ${to || '(end date not specified)'}.\n\nFetch time entries for the date range:\n\\`\\`\\`json\n{\n \"resource\": \"time\",\n \"action\": \"list\",\n \"filter\": {\n \"project_id\": \"${project}\",\n \"after\": \"${from}\",\n \"before\": \"${to}\"\n },\n \"include\": [\"person\", \"service\"],\n \"per_page\": 200\n}\n\\`\\`\\`\n\nFetch project services/budget lines:\n\\`\\`\\`json\n{ \"resource\": \"services\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\" }, \"per_page\": 50 }\n\\`\\`\\`\n\nFetch the project deal/budget details:\n\\`\\`\\`json\n{ \"resource\": \"deals\", \"action\": \"list\", \"filter\": { \"project_id\": \"${project}\", \"type\": 2 }, \"per_page\": 10 }\n\\`\\`\\`\n\nThen produce a billing summary:\n1. **Period**: ${from} → ${to}\n2. **Time entries by service**: Hours logged per service/budget line with team member breakdown\n3. **Billable totals**: If rates are available, calculate amounts per service\n4. **Budget consumed**: Planned vs actual hours per service\n5. **Invoice line items**: Suggested line items ready to copy into an invoice\n6. **Notes**: Any unbilled items, write-offs, or adjustments to flag`,\n },\n },\n ],\n };\n}\n\n/**\n * Get messages for a named prompt template\n */\nexport function getPromptMessages(name: string, args?: Record<string, string>): PromptResult {\n switch (name) {\n case 'end-of-day':\n return endOfDay(args);\n case 'project-review':\n return projectReview(args);\n case 'plan-sprint':\n return planSprint(args);\n case 'weekly-report':\n return weeklyReport(args);\n case 'invoice-prep':\n return invoicePrep(args);\n default:\n throw new Error(`Unknown prompt: ${name}`);\n }\n}\n","/**\n * Stdio transport handlers for Productive MCP Server\n *\n * This module contains the handler logic for the stdio transport.\n * The actual server startup is in index.ts.\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\nimport { getConfig, setConfig } from '@studiometa/productive-api';\n\nimport { executeToolWithCredentials } from './handlers.js';\nimport { PROMPT_DEFINITIONS, getPromptMessages } from './prompts/index.js';\nimport { TOOLS, STDIO_ONLY_TOOLS } from './tools.js';\n\nexport type ToolResult = CallToolResult;\n\n/**\n * Get all available tools (including stdio-only configuration tools)\n */\nexport function getAvailableTools() {\n return [...TOOLS, ...STDIO_ONLY_TOOLS];\n}\n\n/**\n * Get available prompts\n */\nexport function getAvailablePrompts() {\n return [\n {\n name: 'setup_productive',\n description: 'Interactive setup for Productive.io credentials',\n arguments: [],\n },\n ...PROMPT_DEFINITIONS,\n ];\n}\n\n/**\n * Handle the setup_productive prompt\n */\nexport async function handleSetupPrompt(): Promise<{\n messages: Array<{ role: string; content: { type: string; text: string } }>;\n}> {\n const config = await getConfig();\n const hasConfig = !!(config.organizationId && config.apiToken);\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: hasConfig\n ? 'I have already configured Productive.io credentials. Would you like to update them?'\n : 'I need to configure my Productive.io credentials. Please help me set up:\\n1. Organization ID\\n2. API Token\\n3. User ID (optional)',\n },\n },\n ],\n };\n}\n\n/**\n * Handle the productive_configure tool\n */\nexport async function handleConfigureTool(args: {\n organizationId: string;\n apiToken: string;\n userId?: string;\n}): Promise<ToolResult> {\n const { organizationId, apiToken, userId } = args;\n\n await setConfig('organizationId', organizationId);\n await setConfig('apiToken', apiToken);\n if (userId) {\n await setConfig('userId', userId);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n success: true,\n message: 'Productive.io credentials configured successfully',\n configured: {\n organizationId,\n userId: userId || 'not set',\n apiToken: '***' + apiToken.slice(-4),\n },\n },\n null,\n 2,\n ),\n },\n ],\n };\n}\n\n/**\n * Handle the productive_get_config tool\n */\nexport async function handleGetConfigTool(): Promise<ToolResult> {\n const currentConfig = await getConfig();\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n organizationId: currentConfig.organizationId || 'not configured',\n userId: currentConfig.userId || 'not configured',\n apiToken: currentConfig.apiToken\n ? '***' + currentConfig.apiToken.slice(-4)\n : 'not configured',\n configured: !!(currentConfig.organizationId && currentConfig.apiToken),\n },\n null,\n 2,\n ),\n },\n ],\n };\n}\n\n/**\n * Handle a tool call request\n */\nexport async function handleToolCall(\n name: string,\n args: Record<string, unknown>,\n): Promise<ToolResult> {\n // Handle stdio-only configuration tools\n if (name === 'productive_configure') {\n return handleConfigureTool(args as Parameters<typeof handleConfigureTool>[0]);\n }\n\n if (name === 'productive_get_config') {\n return handleGetConfigTool();\n }\n\n // Get config for API tools\n const config = await getConfig();\n if (!config.organizationId || !config.apiToken) {\n return {\n content: [\n {\n type: 'text',\n text: 'Error: Productive.io credentials not configured. Please use the \"productive_configure\" tool to set your credentials, or set PRODUCTIVE_ORG_ID and PRODUCTIVE_API_TOKEN environment variables.',\n },\n ],\n isError: true,\n };\n }\n\n // Execute tool with credentials from config\n return executeToolWithCredentials(name, args, {\n organizationId: config.organizationId,\n apiToken: config.apiToken,\n userId: config.userId,\n });\n}\n\n/**\n * Handle a prompt request\n */\nexport async function handlePrompt(\n name: string,\n args?: Record<string, string>,\n): Promise<{\n messages: Array<{ role: string; content: { type: string; text: string } }>;\n}> {\n if (name === 'setup_productive') {\n return handleSetupPrompt();\n }\n\n return getPromptMessages(name, args);\n}\n"],"mappings":"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"}
package/dist/stdio.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as handlePrompt, i as handleGetConfigTool, n as getAvailableTools, o as handleSetupPrompt, r as handleConfigureTool, s as handleToolCall, t as getAvailablePrompts } from "./stdio-BnfO285Q.js";
1
+ import { a as handlePrompt, i as handleGetConfigTool, n as getAvailableTools, o as handleSetupPrompt, r as handleConfigureTool, s as handleToolCall, t as getAvailablePrompts } from "./stdio-pJj1QGos.js";
2
2
  export { getAvailablePrompts, getAvailableTools, handleConfigureTool, handleGetConfigTool, handlePrompt, handleSetupPrompt, handleToolCall };
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAwB/D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,KAAK,EAAE,IAAI,EA+JvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAoClC,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAwB/D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,KAAK,EAAE,IAAI,EA+PvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAoClC,CAAC"}
package/dist/tools.js CHANGED
@@ -9,7 +9,7 @@ function generateDescription() {
9
9
  "Productive.io API.",
10
10
  `Resources: ${RESOURCES.join(", ")}.`,
11
11
  `Actions: ${ACTIONS.join(", ")} (varies by resource).`,
12
- "Discovery: action=help with any resource for filters, fields, examples. action=schema for compact machine-readable spec.",
12
+ "Discovery: action=help with a resource for filters/fields/examples, action=help with query to search across resources, action=schema for a compact spec. Or use the search_docs tool to discover across resources, raw endpoints, and scripting.",
13
13
  "Filters: filter:{key:value}. Common: project_id, person_id, after/before (YYYY-MM-DD).",
14
14
  "Includes: include:[...] for related data (e.g. [\"project\",\"assignee\"]).",
15
15
  "Output: compact=false for full detail (default for get; list defaults true).",
@@ -160,7 +160,7 @@ var TOOLS = [
160
160
  },
161
161
  {
162
162
  name: "api_read",
163
- description: "Read-only raw Productive API access for documented GET endpoints. Supports describe=true for endpoint docs, filter/include/sort, and optional safe pagination.",
163
+ description: "Read-only raw Productive API access for documented GET endpoints. Use search=\"<term>\" to discover endpoints, describe=true for an endpoint spec, or path + filter/include/sort with optional safe pagination.",
164
164
  annotations: {
165
165
  title: "Productive API Read",
166
166
  readOnlyHint: true,
@@ -175,6 +175,10 @@ var TOOLS = [
175
175
  type: "string",
176
176
  description: "Relative Productive API path, e.g. /invoices"
177
177
  },
178
+ search: {
179
+ type: "string",
180
+ description: "Find documented endpoints by keyword (returns matching paths). Use instead of path."
181
+ },
178
182
  describe: {
179
183
  type: "boolean",
180
184
  description: "Return endpoint documentation instead of calling the API"
@@ -192,8 +196,7 @@ var TOOLS = [
192
196
  per_page: { type: "number" },
193
197
  paginate: { type: "boolean" },
194
198
  max_pages: { type: "number" }
195
- },
196
- required: ["path"]
199
+ }
197
200
  }
198
201
  },
199
202
  {
@@ -229,6 +232,92 @@ var TOOLS = [
229
232
  "confirm"
230
233
  ]
231
234
  }
235
+ },
236
+ {
237
+ name: "run_script",
238
+ description: "Run a sandboxed JavaScript/TypeScript script for advanced multi-step logic in one call. Globals: productive(resource, action, params), api.read/write, output.*, args, flags; `return` a value for the result. Call `search_docs` (e.g. query=\"output\", \"productive\") for the scripting API before writing a script. dry_run=true previews mutations. No direct network/filesystem access (API calls run host-side). Disabled by default, requires PRODUCTIVE_MCP_ENABLE_RUN=true.",
239
+ annotations: {
240
+ title: "Run Script",
241
+ readOnlyHint: false,
242
+ destructiveHint: false,
243
+ idempotentHint: false,
244
+ openWorldHint: true
245
+ },
246
+ inputSchema: {
247
+ type: "object",
248
+ properties: {
249
+ code: {
250
+ type: "string",
251
+ description: "JavaScript/TypeScript source to execute in the sandbox."
252
+ },
253
+ args: {
254
+ type: "array",
255
+ items: { type: "string" },
256
+ description: "Positional arguments exposed to the script as `args`."
257
+ },
258
+ flags: {
259
+ type: "object",
260
+ description: "Named values exposed to the script as `flags`."
261
+ },
262
+ dry_run: {
263
+ type: "boolean",
264
+ description: "Record mutating calls (create/update/delete/...) instead of executing them."
265
+ }
266
+ },
267
+ required: ["code"]
268
+ },
269
+ outputSchema: {
270
+ type: "object",
271
+ properties: {
272
+ result: { description: "The value the script returned (any JSON)." },
273
+ output: {
274
+ type: "array",
275
+ description: "Buffered output.* entries, in order.",
276
+ items: {
277
+ type: "object",
278
+ properties: {
279
+ type: { type: "string" },
280
+ data: {}
281
+ },
282
+ required: ["type"]
283
+ }
284
+ },
285
+ _run: {
286
+ type: "object",
287
+ description: "Run metadata.",
288
+ properties: {
289
+ apiCalls: { type: "number" },
290
+ dryRun: { type: "boolean" },
291
+ outputTruncated: { type: "boolean" },
292
+ recorded: { type: "array" }
293
+ },
294
+ required: ["apiCalls", "dryRun"]
295
+ }
296
+ },
297
+ required: [
298
+ "result",
299
+ "output",
300
+ "_run"
301
+ ]
302
+ }
303
+ },
304
+ {
305
+ name: "search_docs",
306
+ description: "Discover documentation across everything this server exposes — Productive resources, raw API endpoints, and the run_script scripting API. Call with no query for a table of contents, or a query (e.g. \"invoices\", \"billing\", \"output\") for ranked cross-domain matches: resources/endpoints point at the tool to drill in, run_script sections are returned in full. Good first call when unsure where to look.",
307
+ annotations: {
308
+ title: "Search Docs",
309
+ readOnlyHint: true,
310
+ destructiveHint: false,
311
+ idempotentHint: true,
312
+ openWorldHint: false
313
+ },
314
+ inputSchema: {
315
+ type: "object",
316
+ properties: { query: {
317
+ type: "string",
318
+ description: "Optional keyword to search across resources, endpoints, and scripting docs."
319
+ } }
320
+ }
232
321
  }
233
322
  ];
234
323
  /**