@studiometa/productive-mcp 0.10.8 → 0.10.9

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/errors.d.ts.map +1 -1
  2. package/dist/handlers/activities.d.ts +98 -3
  3. package/dist/handlers/activities.d.ts.map +1 -1
  4. package/dist/handlers/attachments.d.ts +98 -3
  5. package/dist/handlers/attachments.d.ts.map +1 -1
  6. package/dist/handlers/bookings.d.ts +98 -3
  7. package/dist/handlers/bookings.d.ts.map +1 -1
  8. package/dist/handlers/comments.d.ts +98 -3
  9. package/dist/handlers/comments.d.ts.map +1 -1
  10. package/dist/handlers/companies.d.ts +98 -3
  11. package/dist/handlers/companies.d.ts.map +1 -1
  12. package/dist/handlers/custom-fields.d.ts +98 -3
  13. package/dist/handlers/custom-fields.d.ts.map +1 -1
  14. package/dist/handlers/deals.d.ts +98 -3
  15. package/dist/handlers/deals.d.ts.map +1 -1
  16. package/dist/handlers/discussions.d.ts +98 -3
  17. package/dist/handlers/discussions.d.ts.map +1 -1
  18. package/dist/handlers/pages.d.ts +98 -3
  19. package/dist/handlers/pages.d.ts.map +1 -1
  20. package/dist/handlers/projects.d.ts +98 -3
  21. package/dist/handlers/projects.d.ts.map +1 -1
  22. package/dist/handlers/services.d.ts +98 -3
  23. package/dist/handlers/services.d.ts.map +1 -1
  24. package/dist/handlers/tasks.d.ts +98 -3
  25. package/dist/handlers/tasks.d.ts.map +1 -1
  26. package/dist/handlers/time.d.ts +98 -3
  27. package/dist/handlers/time.d.ts.map +1 -1
  28. package/dist/handlers/timers.d.ts +98 -3
  29. package/dist/handlers/timers.d.ts.map +1 -1
  30. package/dist/http.d.ts +7 -7
  31. package/dist/http.d.ts.map +1 -1
  32. package/dist/http.js +51 -40
  33. package/dist/http.js.map +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/oauth.d.ts +9 -9
  36. package/dist/oauth.d.ts.map +1 -1
  37. package/dist/oauth.js +39 -39
  38. package/dist/oauth.js.map +1 -1
  39. package/dist/schema.d.ts +62 -62
  40. package/dist/server.js +3 -3
  41. package/dist/server.js.map +1 -1
  42. package/dist/stdio.d.ts +4 -4
  43. package/dist/{version-DpBFJ7eV.js → version-BFw4junA.js} +2 -2
  44. package/dist/{version-DpBFJ7eV.js.map → version-BFw4junA.js.map} +1 -1
  45. package/package.json +10 -18
@@ -1 +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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"}
1
+ {"version":3,"file":"version-BFw4junA.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.8",
3
+ "version": "0.10.9",
4
4
  "description": "MCP server for Productive.io API - Model Context Protocol integration for Claude Desktop",
5
5
  "keywords": [
6
6
  "ai",
@@ -72,35 +72,27 @@
72
72
  "access": "public"
73
73
  },
74
74
  "scripts": {
75
- "dev": "vite build --watch",
76
75
  "dev:server": "node --watch dist/server.js",
77
- "build": "vite build && tsc --emitDeclarationOnly && node scripts/postbuild.js",
78
- "prepublishOnly": "npm run build",
76
+ "build": "vite build && tsgo --emitDeclarationOnly && node scripts/postbuild.js",
79
77
  "start": "node dist/server.js",
80
78
  "test": "vitest run",
81
79
  "test:watch": "vitest",
82
80
  "test:ci": "vitest run --coverage",
83
- "lint": "oxlint src",
84
- "format": "oxfmt --write src",
85
- "format:check": "oxfmt --check src",
86
81
  "typecheck": "tsgo --noEmit"
87
82
  },
88
83
  "dependencies": {
89
- "@modelcontextprotocol/sdk": "^1.26.0",
90
- "@studiometa/productive-api": "0.10.8",
91
- "@studiometa/productive-core": "0.10.8",
92
- "h3": "^1.15.1",
84
+ "@modelcontextprotocol/sdk": "^1.27.1",
85
+ "@studiometa/productive-api": "0.10.9",
86
+ "@studiometa/productive-core": "0.10.9",
87
+ "h3": "^2.0.1-rc.14",
93
88
  "zod": "4.3.6"
94
89
  },
95
90
  "devDependencies": {
96
- "@types/node": "^22.10.5",
97
- "@vitest/coverage-v8": "^4.1.0-beta.4",
98
- "oxlint": "^1.43.0",
99
- "typescript": "^5.7.3",
100
- "vite": "^8.0.0-beta.14",
101
- "vitest": "^4.1.0-beta.4"
91
+ "@vitest/coverage-v8": "^4.1.0-beta.5",
92
+ "vite": "^8.0.0-beta.16",
93
+ "vitest": "^4.1.0-beta.5"
102
94
  },
103
95
  "engines": {
104
- "node": ">=20.0.0"
96
+ "node": ">=24.0.0"
105
97
  }
106
98
  }