@studiometa/productive-mcp 0.10.6 → 0.10.8
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/dist/formatters.d.ts +4 -0
- package/dist/formatters.d.ts.map +1 -1
- package/dist/handlers/custom-fields.d.ts +26 -0
- package/dist/handlers/custom-fields.d.ts.map +1 -0
- package/dist/handlers/help.d.ts.map +1 -1
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/handlers/pre-validation-guards.d.ts +64 -0
- package/dist/handlers/pre-validation-guards.d.ts.map +1 -0
- package/dist/handlers/schema.d.ts.map +1 -1
- package/dist/handlers/valid-includes.d.ts.map +1 -1
- package/dist/{handlers-BvwBrRHp.js → handlers-t95fhdps.js} +2844 -2349
- package/dist/handlers-t95fhdps.js.map +1 -0
- package/dist/handlers.js +1 -1
- package/dist/hints.d.ts +4 -0
- package/dist/hints.d.ts.map +1 -1
- package/dist/http.d.ts +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +13 -4
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -5
- package/dist/index.js.map +1 -1
- package/dist/resources.d.ts +81 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/schema.d.ts +4 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/{handlers-Cha6_ulB.js → stdio-Bi1Lvp8O.js} +97 -2
- package/dist/stdio-Bi1Lvp8O.js.map +1 -0
- package/dist/stdio.js +2 -99
- package/dist/version-DpBFJ7eV.js +236 -0
- package/dist/version-DpBFJ7eV.js.map +1 -0
- package/package.json +3 -3
- package/skills/SKILL.md +168 -59
- package/dist/handlers-BvwBrRHp.js.map +0 -1
- package/dist/handlers-Cha6_ulB.js.map +0 -1
- package/dist/stdio.js.map +0 -1
- package/dist/version-KdH6s_ty.js +0 -29
- package/dist/version-KdH6s_ty.js.map +0 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { a as handleSchemaOverview, c as handleDeals, i as handleServices, n as handleTasks, o as handleProjects, r as handleSummaries, s as handlePeople } from "./handlers-t95fhdps.js";
|
|
2
|
+
import { ProductiveApi } from "@studiometa/productive-api";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { fromHandlerContext } from "@studiometa/productive-core";
|
|
7
|
+
/**
|
|
8
|
+
* MCP Server Instructions
|
|
9
|
+
*
|
|
10
|
+
* These instructions are sent to Claude Desktop during initialization
|
|
11
|
+
* and used as context/hints for the LLM. This ensures the AI agent
|
|
12
|
+
* knows how to properly use the Productive.io MCP server.
|
|
13
|
+
*
|
|
14
|
+
* The content is derived from skills/SKILL.md (without YAML frontmatter).
|
|
15
|
+
*/
|
|
16
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
/**
|
|
18
|
+
* Load instructions from SKILL.md file
|
|
19
|
+
* Removes YAML frontmatter (content between --- markers)
|
|
20
|
+
*/
|
|
21
|
+
function loadInstructions() {
|
|
22
|
+
try {
|
|
23
|
+
return readFileSync(join(__dirname, "..", "skills", "SKILL.md"), "utf-8").replace(/^---\n[\s\S]*?\n---\n+/, "").trim();
|
|
24
|
+
} catch {
|
|
25
|
+
return "Productive.io MCP Server - Use the productive tool with resource and action parameters.";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const INSTRUCTIONS = loadInstructions();
|
|
29
|
+
/** MIME type used for all resource content */
|
|
30
|
+
var MIME_TYPE = "application/json";
|
|
31
|
+
/**
|
|
32
|
+
* Static resources that are always available without API credentials
|
|
33
|
+
*/
|
|
34
|
+
const STATIC_RESOURCES = [{
|
|
35
|
+
uri: "productive://schema",
|
|
36
|
+
name: "Schema",
|
|
37
|
+
description: "Overview of all Productive.io resources, their actions and filters",
|
|
38
|
+
mimeType: MIME_TYPE
|
|
39
|
+
}, {
|
|
40
|
+
uri: "productive://instructions",
|
|
41
|
+
name: "Instructions",
|
|
42
|
+
description: "Server instructions and usage guide for the Productive.io MCP server",
|
|
43
|
+
mimeType: MIME_TYPE
|
|
44
|
+
}];
|
|
45
|
+
/**
|
|
46
|
+
* Dynamic resources that are computed at read time (require credentials)
|
|
47
|
+
*/
|
|
48
|
+
const DYNAMIC_RESOURCES = [{
|
|
49
|
+
uri: "productive://summaries/my_day",
|
|
50
|
+
name: "My Day",
|
|
51
|
+
description: "Personal dashboard: open tasks, today's time entries, active timers",
|
|
52
|
+
mimeType: MIME_TYPE
|
|
53
|
+
}, {
|
|
54
|
+
uri: "productive://summaries/team_pulse",
|
|
55
|
+
name: "Team Pulse",
|
|
56
|
+
description: "Team-wide time tracking activity for today",
|
|
57
|
+
mimeType: MIME_TYPE
|
|
58
|
+
}];
|
|
59
|
+
/**
|
|
60
|
+
* Resource templates that accept URI parameters (require credentials)
|
|
61
|
+
*/
|
|
62
|
+
const RESOURCE_TEMPLATES = [
|
|
63
|
+
{
|
|
64
|
+
uriTemplate: "productive://projects/{id}",
|
|
65
|
+
name: "Project",
|
|
66
|
+
description: "Details of a specific project by ID",
|
|
67
|
+
mimeType: MIME_TYPE
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
uriTemplate: "productive://tasks/{id}",
|
|
71
|
+
name: "Task",
|
|
72
|
+
description: "Details of a specific task by ID",
|
|
73
|
+
mimeType: MIME_TYPE
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
uriTemplate: "productive://people/{id}",
|
|
77
|
+
name: "Person",
|
|
78
|
+
description: "Details of a specific person by ID",
|
|
79
|
+
mimeType: MIME_TYPE
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
uriTemplate: "productive://deals/{id}",
|
|
83
|
+
name: "Deal",
|
|
84
|
+
description: "Details of a specific deal or budget by ID",
|
|
85
|
+
mimeType: MIME_TYPE
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
uriTemplate: "productive://projects/{id}/tasks",
|
|
89
|
+
name: "Project Tasks",
|
|
90
|
+
description: "All tasks belonging to a specific project",
|
|
91
|
+
mimeType: MIME_TYPE
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
uriTemplate: "productive://projects/{id}/services",
|
|
95
|
+
name: "Project Services",
|
|
96
|
+
description: "All services belonging to a specific project",
|
|
97
|
+
mimeType: MIME_TYPE
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
/** Route patterns and their handler factories */
|
|
101
|
+
var URI_PATTERNS = [
|
|
102
|
+
{
|
|
103
|
+
pattern: /^productive:\/\/schema$/,
|
|
104
|
+
handler: async () => {
|
|
105
|
+
const text = handleSchemaOverview().content[0];
|
|
106
|
+
return JSON.parse(text.text);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
pattern: /^productive:\/\/instructions$/,
|
|
111
|
+
handler: async () => ({ instructions: INSTRUCTIONS })
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
pattern: /^productive:\/\/summaries\/my_day$/,
|
|
115
|
+
handler: async (_, credentials) => {
|
|
116
|
+
return extractJsonFromResult(await handleSummaries("my_day", {}, buildHandlerContext(credentials)));
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
pattern: /^productive:\/\/summaries\/team_pulse$/,
|
|
121
|
+
handler: async (_, credentials) => {
|
|
122
|
+
return extractJsonFromResult(await handleSummaries("team_pulse", {}, buildHandlerContext(credentials)));
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
pattern: /^productive:\/\/projects\/([^/]+)\/tasks$/,
|
|
127
|
+
handler: async ([, id], credentials) => {
|
|
128
|
+
const ctx = buildHandlerContext(credentials, { filter: { project_id: id } });
|
|
129
|
+
return extractJsonFromResult(await handleTasks("list", { project_id: id }, ctx));
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
pattern: /^productive:\/\/projects\/([^/]+)\/services$/,
|
|
134
|
+
handler: async ([, id], credentials) => {
|
|
135
|
+
return extractJsonFromResult(await handleServices("list", {}, buildHandlerContext(credentials, { filter: { project_id: id } })));
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
pattern: /^productive:\/\/projects\/([^/]+)$/,
|
|
140
|
+
handler: async ([, id], credentials) => {
|
|
141
|
+
const ctx = buildHandlerContext(credentials);
|
|
142
|
+
return extractJsonFromResult(await handleProjects("get", { id }, ctx));
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
pattern: /^productive:\/\/tasks\/([^/]+)$/,
|
|
147
|
+
handler: async ([, id], credentials) => {
|
|
148
|
+
const ctx = buildHandlerContext(credentials);
|
|
149
|
+
return extractJsonFromResult(await handleTasks("get", { id }, ctx));
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
pattern: /^productive:\/\/people\/([^/]+)$/,
|
|
154
|
+
handler: async ([, id], credentials) => {
|
|
155
|
+
const ctx = buildHandlerContext(credentials);
|
|
156
|
+
return extractJsonFromResult(await handlePeople("get", { id }, ctx, credentials));
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
pattern: /^productive:\/\/deals\/([^/]+)$/,
|
|
161
|
+
handler: async ([, id], credentials) => {
|
|
162
|
+
const ctx = buildHandlerContext(credentials);
|
|
163
|
+
return extractJsonFromResult(await handleDeals("get", { id }, ctx));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
/**
|
|
168
|
+
* Build a HandlerContext from credentials, with optional filter overrides.
|
|
169
|
+
*/
|
|
170
|
+
function buildHandlerContext(credentials, overrides = {}) {
|
|
171
|
+
const execCtx = fromHandlerContext({ api: new ProductiveApi({ config: {
|
|
172
|
+
apiToken: credentials.apiToken,
|
|
173
|
+
organizationId: credentials.organizationId,
|
|
174
|
+
userId: credentials.userId,
|
|
175
|
+
baseUrl: process.env.PRODUCTIVE_BASE_URL
|
|
176
|
+
} }) }, { userId: credentials.userId });
|
|
177
|
+
return {
|
|
178
|
+
formatOptions: { compact: false },
|
|
179
|
+
filter: overrides.filter,
|
|
180
|
+
perPage: 50,
|
|
181
|
+
includeHints: false,
|
|
182
|
+
includeSuggestions: false,
|
|
183
|
+
executor: () => execCtx
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Extract the parsed JSON data from a ToolResult.
|
|
188
|
+
* Throws if the result is an error or not parseable.
|
|
189
|
+
*/
|
|
190
|
+
function extractJsonFromResult(result) {
|
|
191
|
+
if (result.isError) {
|
|
192
|
+
const text = result.content[0].text;
|
|
193
|
+
throw new Error(text);
|
|
194
|
+
}
|
|
195
|
+
const text = result.content[0].text;
|
|
196
|
+
try {
|
|
197
|
+
return JSON.parse(text);
|
|
198
|
+
} catch {
|
|
199
|
+
return { text };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* List all static and dynamic resources (no credentials needed for static).
|
|
204
|
+
*/
|
|
205
|
+
function listResources() {
|
|
206
|
+
return [...STATIC_RESOURCES, ...DYNAMIC_RESOURCES];
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* List all resource templates.
|
|
210
|
+
*/
|
|
211
|
+
function listResourceTemplates() {
|
|
212
|
+
return RESOURCE_TEMPLATES;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Read a resource by URI.
|
|
216
|
+
*
|
|
217
|
+
* Routes the URI to the appropriate handler. Throws on unknown URI.
|
|
218
|
+
*/
|
|
219
|
+
async function readResource(uri, credentials) {
|
|
220
|
+
for (const { pattern, handler } of URI_PATTERNS) {
|
|
221
|
+
const match = uri.match(pattern);
|
|
222
|
+
if (match) {
|
|
223
|
+
const data = await handler(match, credentials);
|
|
224
|
+
return { contents: [{
|
|
225
|
+
uri,
|
|
226
|
+
mimeType: MIME_TYPE,
|
|
227
|
+
text: JSON.stringify(data, null, 2)
|
|
228
|
+
}] };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
throw new Error(`Unknown resource URI: ${uri}. Available static resources: ${STATIC_RESOURCES.map((r) => r.uri).join(", ")}. Available dynamic resources: ${DYNAMIC_RESOURCES.map((r) => r.uri).join(", ")}. Resource templates: ${RESOURCE_TEMPLATES.map((t) => t.uriTemplate).join(", ")}.`);
|
|
232
|
+
}
|
|
233
|
+
const VERSION = "0.10.8";
|
|
234
|
+
export { INSTRUCTIONS as a, readResource as i, listResourceTemplates as n, listResources as r, VERSION as t };
|
|
235
|
+
|
|
236
|
+
//# sourceMappingURL=version-DpBFJ7eV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-DpBFJ7eV.js","names":[],"sources":["../src/instructions.ts","../src/resources.ts","../src/version.ts"],"sourcesContent":["/**\n * MCP Server Instructions\n *\n * These instructions are sent to Claude Desktop during initialization\n * and used as context/hints for the LLM. This ensures the AI agent\n * knows how to properly use the Productive.io MCP server.\n *\n * The content is derived from skills/SKILL.md (without YAML frontmatter).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Load instructions from SKILL.md file\n * Removes YAML frontmatter (content between --- markers)\n */\nfunction loadInstructions(): string {\n try {\n // In dist/, go up to package root, then to skills/\n const skillPath = join(__dirname, '..', 'skills', 'SKILL.md');\n const content = readFileSync(skillPath, 'utf-8');\n\n // Remove YAML frontmatter (between --- markers at start of file)\n const withoutFrontmatter = content.replace(/^---\\n[\\s\\S]*?\\n---\\n+/, '');\n\n return withoutFrontmatter.trim();\n } catch {\n // Fallback if file not found (shouldn't happen in production)\n return 'Productive.io MCP Server - Use the productive tool with resource and action parameters.';\n }\n}\n\nexport const INSTRUCTIONS = loadInstructions();\n","/**\n * MCP Resources handlers for Productive MCP Server\n *\n * Exposes Productive data via MCP resources/ capability so clients can browse\n * and read data without tool calls.\n *\n * Static resources (always available):\n * productive://schema — full resource schema overview\n * productive://instructions — server instructions / SKILL.md content\n *\n * Resource templates (parameterized, require API calls):\n * productive://projects/{id} — project details\n * productive://tasks/{id} — task details\n * productive://people/{id} — person details\n * productive://deals/{id} — deal details\n * productive://projects/{id}/tasks — tasks for a project\n * productive://projects/{id}/services — services for a project\n *\n * Dynamic resources (computed):\n * productive://summaries/my_day — personal dashboard\n * productive://summaries/team_pulse — team activity\n */\n\nimport type { ReadResourceResult as McpReadResourceResult } from '@modelcontextprotocol/sdk/types.js';\n\nimport { ProductiveApi } from '@studiometa/productive-api';\nimport { fromHandlerContext } from '@studiometa/productive-core';\n\nimport type { ProductiveCredentials } from './auth.js';\nimport type { HandlerContext } from './handlers/types.js';\n\nimport { handleDeals } from './handlers/deals.js';\nimport { handlePeople } from './handlers/people.js';\nimport { handleProjects } from './handlers/projects.js';\nimport { handleSchemaOverview } from './handlers/schema.js';\nimport { handleServices } from './handlers/services.js';\nimport { handleSummaries } from './handlers/summaries.js';\nimport { handleTasks } from './handlers/tasks.js';\nimport { INSTRUCTIONS } from './instructions.js';\n\n/** MIME type used for all resource content */\nconst MIME_TYPE = 'application/json';\n\n/**\n * A single resource content item returned in resources/read responses\n */\nexport interface ResourceContent {\n uri: string;\n mimeType: string;\n text: string;\n}\n\n/**\n * Shape of a resources/read response (re-export of SDK type for consumers)\n */\nexport type ReadResourceResult = McpReadResourceResult;\n\n/**\n * Shape of a static resource descriptor (resources/list)\n */\nexport interface StaticResource {\n uri: string;\n name: string;\n description: string;\n mimeType: string;\n}\n\n/**\n * Shape of a resource template descriptor (resources/templates/list)\n */\nexport interface ResourceTemplate {\n uriTemplate: string;\n name: string;\n description: string;\n mimeType: string;\n}\n\n// ---------------------------------------------------------------------------\n// Static resource definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Static resources that are always available without API credentials\n */\nexport const STATIC_RESOURCES: StaticResource[] = [\n {\n uri: 'productive://schema',\n name: 'Schema',\n description: 'Overview of all Productive.io resources, their actions and filters',\n mimeType: MIME_TYPE,\n },\n {\n uri: 'productive://instructions',\n name: 'Instructions',\n description: 'Server instructions and usage guide for the Productive.io MCP server',\n mimeType: MIME_TYPE,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Dynamic resource definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Dynamic resources that are computed at read time (require credentials)\n */\nexport const DYNAMIC_RESOURCES: StaticResource[] = [\n {\n uri: 'productive://summaries/my_day',\n name: 'My Day',\n description: \"Personal dashboard: open tasks, today's time entries, active timers\",\n mimeType: MIME_TYPE,\n },\n {\n uri: 'productive://summaries/team_pulse',\n name: 'Team Pulse',\n description: 'Team-wide time tracking activity for today',\n mimeType: MIME_TYPE,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Resource template definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Resource templates that accept URI parameters (require credentials)\n */\nexport const RESOURCE_TEMPLATES: ResourceTemplate[] = [\n {\n uriTemplate: 'productive://projects/{id}',\n name: 'Project',\n description: 'Details of a specific project by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://tasks/{id}',\n name: 'Task',\n description: 'Details of a specific task by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://people/{id}',\n name: 'Person',\n description: 'Details of a specific person by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://deals/{id}',\n name: 'Deal',\n description: 'Details of a specific deal or budget by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://projects/{id}/tasks',\n name: 'Project Tasks',\n description: 'All tasks belonging to a specific project',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://projects/{id}/services',\n name: 'Project Services',\n description: 'All services belonging to a specific project',\n mimeType: MIME_TYPE,\n },\n];\n\n// ---------------------------------------------------------------------------\n// URI pattern matching\n// ---------------------------------------------------------------------------\n\n/** Route patterns and their handler factories */\nconst URI_PATTERNS: Array<{\n pattern: RegExp;\n handler: (match: RegExpMatchArray, credentials: ProductiveCredentials) => Promise<unknown>;\n}> = [\n // Static resources (no credentials needed)\n {\n pattern: /^productive:\\/\\/schema$/,\n handler: async () => {\n const result = handleSchemaOverview();\n const text = result.content[0] as { text: string };\n return JSON.parse(text.text);\n },\n },\n {\n pattern: /^productive:\\/\\/instructions$/,\n handler: async () => ({ instructions: INSTRUCTIONS }),\n },\n\n // Dynamic summaries\n {\n pattern: /^productive:\\/\\/summaries\\/my_day$/,\n handler: async (_, credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleSummaries('my_day', {}, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/summaries\\/team_pulse$/,\n handler: async (_, credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleSummaries('team_pulse', {}, ctx);\n return extractJsonFromResult(result);\n },\n },\n\n // Project nested resources (before single project to avoid conflict)\n {\n pattern: /^productive:\\/\\/projects\\/([^/]+)\\/tasks$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials, { filter: { project_id: id } });\n const result = await handleTasks('list', { project_id: id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/projects\\/([^/]+)\\/services$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials, { filter: { project_id: id } });\n // handleServices uses CommonArgs; project_id is passed via ctx.filter\n const result = await handleServices('list', {}, ctx);\n return extractJsonFromResult(result);\n },\n },\n\n // Single entity resources\n {\n pattern: /^productive:\\/\\/projects\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleProjects('get', { id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/tasks\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleTasks('get', { id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/people\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handlePeople('get', { id }, ctx, credentials);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/deals\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleDeals('get', { id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build a HandlerContext from credentials, with optional filter overrides.\n */\nfunction buildHandlerContext(\n credentials: ProductiveCredentials,\n overrides: { filter?: Record<string, string> } = {},\n): HandlerContext {\n const api = new ProductiveApi({\n config: {\n apiToken: credentials.apiToken,\n organizationId: credentials.organizationId,\n userId: credentials.userId,\n baseUrl: process.env.PRODUCTIVE_BASE_URL,\n },\n });\n\n const execCtx = fromHandlerContext({ api }, { userId: credentials.userId });\n\n return {\n formatOptions: { compact: false },\n filter: overrides.filter,\n perPage: 50,\n includeHints: false,\n includeSuggestions: false,\n executor: () => execCtx,\n };\n}\n\n/**\n * Extract the parsed JSON data from a ToolResult.\n * Throws if the result is an error or not parseable.\n */\nfunction extractJsonFromResult(result: { content: unknown[]; isError?: boolean }): unknown {\n if (result.isError) {\n const text = (result.content[0] as { text: string }).text;\n throw new Error(text);\n }\n const text = (result.content[0] as { text: string }).text;\n try {\n return JSON.parse(text);\n } catch {\n return { text };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * List all static and dynamic resources (no credentials needed for static).\n */\nexport function listResources(): StaticResource[] {\n return [...STATIC_RESOURCES, ...DYNAMIC_RESOURCES];\n}\n\n/**\n * List all resource templates.\n */\nexport function listResourceTemplates(): ResourceTemplate[] {\n return RESOURCE_TEMPLATES;\n}\n\n/**\n * Read a resource by URI.\n *\n * Routes the URI to the appropriate handler. Throws on unknown URI.\n */\nexport async function readResource(\n uri: string,\n credentials: ProductiveCredentials,\n): Promise<ReadResourceResult> {\n for (const { pattern, handler } of URI_PATTERNS) {\n const match = uri.match(pattern);\n if (match) {\n const data = await handler(match, credentials);\n return {\n contents: [\n {\n uri,\n mimeType: MIME_TYPE,\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n }\n\n throw new Error(\n `Unknown resource URI: ${uri}. ` +\n `Available static resources: ${STATIC_RESOURCES.map((r) => r.uri).join(', ')}. ` +\n `Available dynamic resources: ${DYNAMIC_RESOURCES.map((r) => r.uri).join(', ')}. ` +\n `Resource templates: ${RESOURCE_TEMPLATES.map((t) => t.uriTemplate).join(', ')}.`,\n );\n}\n","/**\n * Package version - injected from package.json at build time\n */\ndeclare const __VERSION__: string;\nexport const VERSION = __VERSION__;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studiometa/productive-mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.8",
|
|
4
4
|
"description": "MCP server for Productive.io API - Model Context Protocol integration for Claude Desktop",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
90
|
-
"@studiometa/productive-api": "0.10.
|
|
91
|
-
"@studiometa/productive-core": "0.10.
|
|
90
|
+
"@studiometa/productive-api": "0.10.8",
|
|
91
|
+
"@studiometa/productive-core": "0.10.8",
|
|
92
92
|
"h3": "^1.15.1",
|
|
93
93
|
"zod": "4.3.6"
|
|
94
94
|
},
|
package/skills/SKILL.md
CHANGED
|
@@ -21,24 +21,25 @@ productive(resource, action, [parameters...])
|
|
|
21
21
|
|
|
22
22
|
### Resources & Actions
|
|
23
23
|
|
|
24
|
-
| Resource
|
|
25
|
-
|
|
|
26
|
-
| `projects`
|
|
27
|
-
| `time`
|
|
28
|
-
| `tasks`
|
|
29
|
-
| `services`
|
|
30
|
-
| `people`
|
|
31
|
-
| `companies`
|
|
32
|
-
| `comments`
|
|
33
|
-
| `attachments`
|
|
34
|
-
| `timers`
|
|
35
|
-
| `deals`
|
|
36
|
-
| `bookings`
|
|
37
|
-
| `pages`
|
|
38
|
-
| `discussions`
|
|
39
|
-
| `activities`
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
24
|
+
| Resource | Actions | Description |
|
|
25
|
+
| --------------- | ------------------------------------------------------------------------ | -------------------------------------------------------- |
|
|
26
|
+
| `projects` | `list`, `get`, `resolve`, `context`, `help` | Project management |
|
|
27
|
+
| `time` | `list`, `get`, `create`, `update`, `resolve`, `help` | Time tracking |
|
|
28
|
+
| `tasks` | `list`, `get`, `create`, `update`, `resolve`, `context`, `help` | Task management |
|
|
29
|
+
| `services` | `list`, `get`, `resolve`, `help` | Budget line items |
|
|
30
|
+
| `people` | `list`, `get`, `me`, `resolve`, `help` | Team members |
|
|
31
|
+
| `companies` | `list`, `get`, `create`, `update`, `resolve`, `help` | Client companies |
|
|
32
|
+
| `comments` | `list`, `get`, `create`, `update`, `help` | Comments on tasks/deals |
|
|
33
|
+
| `attachments` | `list`, `get`, `delete`, `help` | File attachments |
|
|
34
|
+
| `timers` | `list`, `get`, `start`, `stop`, `help` | Active timers |
|
|
35
|
+
| `deals` | `list`, `get`, `create`, `update`, `resolve`, `context`, `help` | Sales deals & budgets (use `filter[type]=2` for budgets) |
|
|
36
|
+
| `bookings` | `list`, `get`, `create`, `update`, `help` | Resource scheduling |
|
|
37
|
+
| `pages` | `list`, `get`, `create`, `update`, `delete`, `help` | Wiki/docs pages |
|
|
38
|
+
| `discussions` | `list`, `get`, `create`, `update`, `delete`, `resolve`, `reopen`, `help` | Discussions on pages |
|
|
39
|
+
| `activities` | `list`, `help` | Activity feed (audit log of create/update/delete events) |
|
|
40
|
+
| `custom_fields` | `list`, `get`, `help` | Custom field definitions and option values |
|
|
41
|
+
| `reports` | `get`, `help` | Generate reports |
|
|
42
|
+
| `workflows` | `complete_task`, `log_day`, `weekly_standup`, `help` | Compound workflows chaining multiple operations |
|
|
42
43
|
|
|
43
44
|
### Getting Help
|
|
44
45
|
|
|
@@ -472,89 +473,166 @@ Compound workflows that chain multiple operations into a single tool call.
|
|
|
472
473
|
|
|
473
474
|
## Filters Reference
|
|
474
475
|
|
|
476
|
+
> **Tip:** Use `action: "help"` on any resource to see the full, up-to-date list of filters, fields, and examples. Use `action: "schema"` for a compact machine-readable spec.
|
|
477
|
+
>
|
|
478
|
+
> ```json
|
|
479
|
+
> { "resource": "tasks", "action": "help" }
|
|
480
|
+
> { "resource": "tasks", "action": "schema" }
|
|
481
|
+
> ```
|
|
482
|
+
|
|
483
|
+
### Text Search with `query`
|
|
484
|
+
|
|
485
|
+
Many resources support a `query` filter for full-text search. You can pass it either as a top-level shorthand or via the `filter` object (passthrough pattern):
|
|
486
|
+
|
|
487
|
+
```json
|
|
488
|
+
// Shorthand (top-level)
|
|
489
|
+
{ "resource": "projects", "action": "list", "query": "website" }
|
|
490
|
+
|
|
491
|
+
// Filter passthrough (explicit)
|
|
492
|
+
{ "resource": "projects", "action": "list", "filter": { "query": "website" } }
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Resources that support `query`: **projects**, **tasks**, **people**, **companies**, **deals**
|
|
496
|
+
|
|
475
497
|
### Time Entries
|
|
476
498
|
|
|
477
|
-
- `person_id` - Filter by person (use "me" for current user)
|
|
478
|
-
- `project_id` - Filter by project
|
|
479
|
-
- `service_id` - Filter by service
|
|
480
|
-
- `task_id` - Filter by task
|
|
481
|
-
- `company_id` - Filter by company
|
|
482
|
-
- `deal_id` / `budget_id` - Filter by deal/budget
|
|
483
|
-
- `after` -
|
|
484
|
-
- `
|
|
499
|
+
- `person_id` - Filter by person (use "me" for current user) (array)
|
|
500
|
+
- `project_id` - Filter by project (array)
|
|
501
|
+
- `service_id` - Filter by service (array)
|
|
502
|
+
- `task_id` - Filter by task (array)
|
|
503
|
+
- `company_id` - Filter by company (array)
|
|
504
|
+
- `deal_id` / `budget_id` - Filter by deal/budget (array)
|
|
505
|
+
- `after` / `before` - Date range (YYYY-MM-DD)
|
|
506
|
+
- `date` - Exact date (YYYY-MM-DD)
|
|
485
507
|
- `status` - Approval status: `1`=approved, `2`=unapproved, `3`=rejected
|
|
486
508
|
- `billing_type_id` - Billing type: `1`=fixed, `2`=actuals, `3`=non_billable
|
|
487
509
|
- `invoicing_status` - Invoicing: `1`=not_invoiced, `2`=drafted, `3`=finalized
|
|
510
|
+
- `invoiced` - Invoiced status (boolean)
|
|
511
|
+
- `creator_id` / `approver_id` - Filter by creator or approver (array)
|
|
512
|
+
- `booking_id` - Filter by booking (array)
|
|
513
|
+
- `autotracked` - Auto-tracked entries (boolean)
|
|
488
514
|
|
|
489
515
|
### Tasks
|
|
490
516
|
|
|
491
|
-
- `
|
|
492
|
-
- `
|
|
493
|
-
- `
|
|
494
|
-
- `
|
|
517
|
+
- `query` - Text search on task title
|
|
518
|
+
- `project_id` - Filter by project (array)
|
|
519
|
+
- `company_id` - Filter by company (array)
|
|
520
|
+
- `assignee_id` - Filter by assigned person (array)
|
|
521
|
+
- `creator_id` - Filter by task creator (array)
|
|
495
522
|
- `status` - Status: `1`=open, `2`=closed (or "open", "closed", "all")
|
|
496
|
-
- `task_list_id` - Filter by task list
|
|
497
|
-
- `
|
|
498
|
-
- `
|
|
499
|
-
- `
|
|
523
|
+
- `task_list_id` - Filter by task list (array)
|
|
524
|
+
- `task_list_status` - Task list status: `1`=open, `2`=closed
|
|
525
|
+
- `board_id` - Filter by board (array)
|
|
526
|
+
- `workflow_status_id` - Filter by workflow status/kanban column (array)
|
|
527
|
+
- `workflow_status_category_id` - Workflow category: `1`=not started, `2`=started, `3`=closed
|
|
528
|
+
- `workflow_id` - Filter by workflow (array)
|
|
529
|
+
- `parent_task_id` - Filter by parent task (for subtasks) (array)
|
|
530
|
+
- `task_type` - Task type: `1`=parent task, `2`=subtask
|
|
500
531
|
- `overdue_status` - Overdue: `1`=not overdue, `2`=overdue
|
|
501
532
|
- `due_date_on` / `due_date_before` / `due_date_after` - Due date filters
|
|
533
|
+
- `start_date_before` / `start_date_after` - Start date filters
|
|
534
|
+
- `after` / `before` - Created date range
|
|
535
|
+
- `closed_after` / `closed_before` - Closed date range
|
|
536
|
+
- `project_manager_id` - Filter by project manager (array)
|
|
537
|
+
- `subscriber_id` - Filter by subscriber/watcher (array)
|
|
538
|
+
- `tags` - Filter by tags
|
|
502
539
|
|
|
503
540
|
### Projects
|
|
504
541
|
|
|
505
|
-
- `
|
|
542
|
+
- `query` - Text search on project name
|
|
543
|
+
- `company_id` - Filter by company (array)
|
|
506
544
|
- `project_type` - Type: `1`=internal, `2`=client
|
|
507
|
-
- `responsible_id` - Filter by project manager
|
|
508
|
-
- `person_id` - Filter by team member
|
|
545
|
+
- `responsible_id` - Filter by project manager (array)
|
|
546
|
+
- `person_id` - Filter by team member (array)
|
|
509
547
|
- `status` - Status: `1`=active, `2`=archived
|
|
510
548
|
|
|
511
549
|
### Services
|
|
512
550
|
|
|
513
|
-
- `project_id` - Filter by project
|
|
514
|
-
- `deal_id` - Filter by deal
|
|
515
|
-
- `task_id` - Filter by task
|
|
516
|
-
- `person_id` - Filter by person
|
|
551
|
+
- `project_id` - Filter by project (array)
|
|
552
|
+
- `deal_id` - Filter by deal (array)
|
|
553
|
+
- `task_id` - Filter by task (array)
|
|
554
|
+
- `person_id` - Filter by person/trackable by (array)
|
|
555
|
+
- `name` - Filter by service name (text match)
|
|
517
556
|
- `budget_status` - Status: `1`=open, `2`=delivered
|
|
557
|
+
- `stage_status_id` - Stage: `1`=open, `2`=won, `3`=lost, `4`=delivered (array)
|
|
518
558
|
- `billing_type` - Type: `1`=fixed, `2`=actuals, `3`=none
|
|
519
|
-
- `
|
|
559
|
+
- `unit` - Unit: `1`=hour, `2`=piece, `3`=day
|
|
560
|
+
- `time_tracking_enabled` / `expense_tracking_enabled` - Boolean
|
|
561
|
+
- `trackable_by_person_id` - Services trackable by a specific person
|
|
562
|
+
- `after` / `before` - Date range
|
|
520
563
|
|
|
521
564
|
### People
|
|
522
565
|
|
|
566
|
+
- `query` - Text search on name or email
|
|
567
|
+
- `email` - Filter by exact email address
|
|
523
568
|
- `status` - Status: `1`=active, `2`=deactivated
|
|
524
569
|
- `person_type` - Type: `1`=user, `2`=contact, `3`=placeholder
|
|
525
|
-
- `company_id` - Filter by company
|
|
570
|
+
- `company_id` - Filter by company (array)
|
|
526
571
|
- `project_id` - Filter by project
|
|
527
|
-
- `role_id` - Filter by role
|
|
572
|
+
- `role_id` - Filter by role (array)
|
|
528
573
|
- `team` - Filter by team name
|
|
574
|
+
- `manager_id` - Filter by manager
|
|
575
|
+
- `custom_role_id` - Filter by custom role
|
|
576
|
+
- `tags` - Filter by tags
|
|
577
|
+
|
|
578
|
+
### Companies
|
|
579
|
+
|
|
580
|
+
- `query` - Text search on company name
|
|
581
|
+
- `name` - Exact name match
|
|
582
|
+
- `company_code` / `billing_name` / `vat` - Filter by specific fields
|
|
583
|
+
- `status` - Status (integer)
|
|
584
|
+
- `archived` - Archived status (boolean)
|
|
585
|
+
- `project_id` - Filter by project (array)
|
|
586
|
+
- `subsidiary_id` - Filter by subsidiary (array)
|
|
587
|
+
- `default_currency` - Filter by currency code (e.g. USD, EUR)
|
|
529
588
|
|
|
530
589
|
### Deals
|
|
531
590
|
|
|
532
|
-
- `
|
|
533
|
-
- `
|
|
534
|
-
- `
|
|
535
|
-
- `
|
|
536
|
-
- `
|
|
591
|
+
- `query` - Text search on deal name
|
|
592
|
+
- `number` - Filter by deal number
|
|
593
|
+
- `company_id` - Filter by company (array)
|
|
594
|
+
- `project_id` - Filter by project (array)
|
|
595
|
+
- `responsible_id` - Filter by responsible person (array)
|
|
596
|
+
- `creator_id` - Filter by creator (array)
|
|
597
|
+
- `pipeline_id` - Filter by pipeline (array)
|
|
598
|
+
- `stage_status_id` - Stage: `1`=open, `2`=won, `3`=lost (array)
|
|
599
|
+
- `status_id` - Filter by deal status (array)
|
|
537
600
|
- `type` - Type: `1`=deal, `2`=budget
|
|
601
|
+
- `deal_type_id` - Deal type: `1`=internal, `2`=client
|
|
538
602
|
- `budget_status` - Budget status: `1`=open, `2`=closed
|
|
603
|
+
- `project_type` - Project type: `1`=internal, `2`=client
|
|
604
|
+
- `subsidiary_id` - Filter by subsidiary (array)
|
|
605
|
+
- `tags` - Filter by tags
|
|
606
|
+
- `recurring` - Recurring deals (boolean)
|
|
607
|
+
- `needs_invoicing` / `time_approval` - Boolean filters
|
|
539
608
|
|
|
540
609
|
> **Note:** Budgets are deals with `budget=true`. There is no separate `/budgets` endpoint. Use `filter[type]=2` to list only budgets.
|
|
541
610
|
|
|
542
611
|
### Bookings
|
|
543
612
|
|
|
544
|
-
- `person_id` - Filter by person
|
|
613
|
+
- `person_id` - Filter by person (array)
|
|
545
614
|
- `service_id` - Filter by service
|
|
546
|
-
- `project_id` - Filter by project
|
|
547
|
-
- `company_id` - Filter by company
|
|
548
|
-
- `event_id` - Filter by event
|
|
549
|
-
- `
|
|
615
|
+
- `project_id` - Filter by project (array)
|
|
616
|
+
- `company_id` - Filter by company (array)
|
|
617
|
+
- `event_id` - Filter by event/absence (array)
|
|
618
|
+
- `task_id` - Filter by task (array)
|
|
619
|
+
- `approver_id` - Filter by approver (array)
|
|
620
|
+
- `after` / `before` - Date range (YYYY-MM-DD)
|
|
621
|
+
- `started_on` / `ended_on` - Exact start/end date
|
|
550
622
|
- `booking_type` - Type: `event` (absence) or `service` (budget)
|
|
551
|
-
- `draft` - Tentative
|
|
623
|
+
- `draft` - Tentative bookings only: `true`/`false`
|
|
624
|
+
- `with_draft` - Include tentative bookings: `true`/`false`
|
|
625
|
+
- `status` / `approval_status` - Approval status (array)
|
|
626
|
+
- `billing_type_id` - Billing type: `1`=fixed, `2`=actuals, `3`=none (array)
|
|
627
|
+
- `person_type` - Person type: `1`=user, `2`=contact, `3`=placeholder
|
|
628
|
+
- `canceled` - Canceled bookings (boolean)
|
|
552
629
|
|
|
553
630
|
### Pages
|
|
554
631
|
|
|
555
|
-
- `project_id` - Filter by project
|
|
632
|
+
- `project_id` - Filter by project (array)
|
|
556
633
|
- `creator_id` - Filter by creator
|
|
557
634
|
- `parent_page_id` - Filter by parent page (for sub-pages)
|
|
635
|
+
- `edited_at` - Filter by last edited date (ISO 8601)
|
|
558
636
|
|
|
559
637
|
### Discussions
|
|
560
638
|
|
|
@@ -564,15 +642,46 @@ Compound workflows that chain multiple operations into a single tool call.
|
|
|
564
642
|
### Comments
|
|
565
643
|
|
|
566
644
|
- `task_id` - Filter by task
|
|
567
|
-
- `
|
|
568
|
-
- `
|
|
569
|
-
- `page_id` - Filter by page
|
|
645
|
+
- `project_id` - Filter by project (array)
|
|
646
|
+
- `page_id` - Filter by page (array)
|
|
570
647
|
- `discussion_id` - Filter by discussion
|
|
648
|
+
- `draft` - Draft comments: `true`/`false`
|
|
649
|
+
- `workflow_status_category_id` - Filter by workflow status category (array)
|
|
650
|
+
|
|
651
|
+
### Activities
|
|
652
|
+
|
|
653
|
+
- `event` - Event type: create, copy, update, delete, etc.
|
|
654
|
+
- `type` - Activity type: `1`=Comment, `2`=Changeset, `3`=Email
|
|
655
|
+
- `after` / `before` - ISO 8601 timestamp range
|
|
656
|
+
- `person_id` - Filter by person (array)
|
|
657
|
+
- `project_id` - Filter by project (array)
|
|
658
|
+
- `company_id` / `task_id` / `deal_id` / `discussion_id` - Filter by resource (array)
|
|
659
|
+
- `item_type` - Resource type (e.g. Task, Page, Deal, Workspace)
|
|
660
|
+
- `parent_type` / `root_type` - Parent/root resource type
|
|
661
|
+
- `has_attachments` / `pinned` - Boolean filters
|
|
662
|
+
|
|
663
|
+
### Custom Fields
|
|
664
|
+
|
|
665
|
+
- `customizable_type` - Resource type: Task, Deal, Company, Project, Booking, Service, etc.
|
|
666
|
+
- `archived` - Archived status: `true`/`false`
|
|
667
|
+
- `name` - Filter by field name
|
|
668
|
+
- `project_id` - Filter by project
|
|
669
|
+
- `global` - Global custom fields: `true`/`false`
|
|
670
|
+
|
|
671
|
+
**Workflow to resolve custom field values:**
|
|
672
|
+
|
|
673
|
+
1. Fetch a task/deal with `custom_fields` attribute (raw `{field_id: value}` hash)
|
|
674
|
+
2. List definitions: `resource=custom_fields, action=list, filter={customizable_type: "Task"}`
|
|
675
|
+
3. For select/multi-select: get field with options: `resource=custom_fields, action=get, id=<field_id>, include=["options"]`
|
|
676
|
+
4. Map field IDs to names, option IDs to values
|
|
677
|
+
|
|
678
|
+
**Data types:** 1=Text, 2=Number, 3=Select, 4=Date, 5=Multi-select, 6=Person, 7=Attachment
|
|
571
679
|
|
|
572
680
|
### Timers
|
|
573
681
|
|
|
574
682
|
- `person_id` - Filter by person
|
|
575
683
|
- `time_entry_id` - Filter by time entry
|
|
684
|
+
- `started_at` / `stopped_at` - Filter by start/stop time (ISO 8601)
|
|
576
685
|
|
|
577
686
|
## Include (Related Resources)
|
|
578
687
|
|