@studiometa/productive-mcp 0.10.3 → 0.10.5
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/handlers/comments.d.ts.map +1 -1
- package/dist/handlers/help.d.ts.map +1 -1
- package/dist/handlers/schema.d.ts.map +1 -1
- package/dist/handlers/types.d.ts +1 -0
- package/dist/handlers/types.d.ts.map +1 -1
- package/dist/{handlers-CaOBYauF.js → handlers-BvwBrRHp.js} +41 -18
- package/dist/handlers-BvwBrRHp.js.map +1 -0
- package/dist/handlers-Cha6_ulB.js +256 -0
- package/dist/handlers-Cha6_ulB.js.map +1 -0
- package/dist/handlers.js +1 -1
- package/dist/http.js +2 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/prompts/definitions.d.ts +18 -0
- package/dist/prompts/definitions.d.ts.map +1 -0
- package/dist/prompts/handlers.d.ts +22 -0
- package/dist/prompts/handlers.d.ts.map +1 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/server.js +2 -2
- package/dist/stdio.d.ts +2 -6
- package/dist/stdio.d.ts.map +1 -1
- package/dist/stdio.js +5 -4
- package/dist/stdio.js.map +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +4 -0
- package/dist/tools.js.map +1 -1
- package/dist/{version-BRTaTnUG.js → version-4lpeoWqA.js} +2 -2
- package/dist/{version-BRTaTnUG.js.map → version-4lpeoWqA.js.map} +1 -1
- package/package.json +3 -3
- package/skills/SKILL.md +46 -0
- package/dist/handlers-CaOBYauF.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers-BvwBrRHp.js","names":[],"sources":["../src/errors.ts","../src/formatters.ts","../src/suggestions.ts","../src/handlers/utils.ts","../src/handlers/resolve.ts","../src/handlers/factory.ts","../src/handlers/activities.ts","../src/hints.ts","../src/handlers/attachments.ts","../src/handlers/batch.ts","../src/handlers/bookings.ts","../src/handlers/comments.ts","../src/handlers/companies.ts","../src/handlers/deals.ts","../src/handlers/discussions.ts","../src/handlers/help.ts","../src/handlers/pages.ts","../src/handlers/people.ts","../src/handlers/projects.ts","../src/handlers/reports.ts","../src/handlers/schema.ts","../src/handlers/search.ts","../src/handlers/services.ts","../src/handlers/summaries.ts","../src/handlers/tasks.ts","../src/handlers/time.ts","../src/handlers/timers.ts","../src/handlers/valid-includes.ts","../src/handlers/workflows.ts","../src/handlers/index.ts"],"sourcesContent":["/**\n * Custom error classes for MCP server\n *\n * These provide structured error handling with LLM-friendly messages\n * that include guidance on how to resolve issues.\n */\n\n/**\n * Error thrown when user input validation fails.\n * These errors should be returned to the user directly.\n *\n * Includes optional hints for how to resolve the issue.\n */\nexport class UserInputError extends Error {\n public readonly hints?: string[];\n\n constructor(message: string, hints?: string[]) {\n super(message);\n this.name = 'UserInputError';\n this.hints = hints;\n }\n\n /**\n * Format error message with hints for LLM consumption\n */\n toFormattedMessage(): string {\n let msg = `**Input Error:** ${this.message}`;\n if (this.hints && this.hints.length > 0) {\n msg += '\\n\\n**Hints:**\\n' + this.hints.map((h) => `- ${h}`).join('\\n');\n }\n return msg;\n }\n}\n\n/**\n * Error messages with guidance for common validation failures\n */\nexport const ErrorMessages = {\n // Required field errors\n missingId: (action: string) =>\n new UserInputError(`id is required for ${action} action`, [\n `Use action=\"list\" first to find the resource ID`,\n `Then use action=\"${action}\" with the id parameter`,\n ]),\n\n missingRequiredFields: (resource: string, fields: string[]) =>\n new UserInputError(\n `${fields.join(', ')} ${fields.length === 1 ? 'is' : 'are'} required for creating ${resource}`,\n [\n `Provide all required fields: ${fields.join(', ')}`,\n `Use action=\"help\" for detailed documentation on ${resource}`,\n ],\n ),\n\n // Invalid action errors\n invalidAction: (action: string, resource: string, validActions: string[]) =>\n new UserInputError(`Invalid action \"${action}\" for ${resource}`, [\n `Valid actions are: ${validActions.join(', ')}`,\n `Use action=\"help\" with resource=\"${resource}\" for detailed documentation`,\n ]),\n\n // Unknown resource errors\n unknownResource: (resource: string, validResources: string[]) =>\n new UserInputError(`Unknown resource: ${resource}`, [\n `Valid resources are: ${validResources.join(', ')}`,\n `Use action=\"help\" without a resource for an overview of all resources`,\n ]),\n\n // Report-specific errors\n missingReportType: () =>\n new UserInputError('report_type is required for reports', [\n 'Specify report_type parameter (e.g., \"time_reports\", \"project_reports\")',\n 'Use action=\"help\" with resource=\"reports\" for available report types',\n ]),\n\n invalidReportType: (reportType: string, validTypes: string[]) =>\n new UserInputError(`Invalid report_type: ${reportType}`, [\n `Valid report types are: ${validTypes.join(', ')}`,\n 'Use action=\"help\" with resource=\"reports\" for detailed documentation',\n ]),\n\n // Timer-specific errors\n missingServiceForTimer: () =>\n new UserInputError('service_id is required to start a timer', [\n 'First find a service using resource=\"services\" action=\"list\"',\n 'Then start the timer with the service_id',\n ]),\n\n // People-specific errors\n noUserIdConfigured: () =>\n new UserInputError('User ID not configured', [\n 'The \"me\" action requires a user ID to be configured',\n 'Use action=\"list\" to find people, or configure the user ID',\n ]),\n\n // Comment-specific errors\n missingCommentTarget: () =>\n new UserInputError('A target is required for creating a comment', [\n 'Provide one of: task_id, deal_id, or company_id',\n 'Find targets using resource=\"tasks\", \"deals\", or \"companies\" with action=\"list\"',\n ]),\n\n // Booking-specific errors\n missingBookingTarget: () =>\n new UserInputError('A service or event is required for creating a booking', [\n 'Provide either: service_id or event_id',\n 'Find services using resource=\"services\" with action=\"list\"',\n ]),\n\n // Update-specific errors\n noUpdateFieldsSpecified: (allowedFields: string[]) =>\n new UserInputError(\n `No updates specified. Provide at least one of: ${allowedFields.join(', ')}`,\n ['Specify at least one field to update', `Updatable fields are: ${allowedFields.join(', ')}`],\n ),\n\n // API errors\n apiError: (statusCode: number, message: string) => {\n const hints: string[] = [];\n\n if (statusCode === 401) {\n hints.push('Check that your API token is valid and not expired');\n hints.push('Verify the organization ID is correct');\n } else if (statusCode === 403) {\n hints.push('You may not have permission to access this resource');\n hints.push('Check your API token permissions');\n } else if (statusCode === 404) {\n hints.push('The resource may not exist or you may not have access');\n hints.push('Verify the resource ID is correct');\n hints.push('Use action=\"list\" to find valid resource IDs');\n } else if (statusCode === 422) {\n hints.push('The request data may be invalid');\n hints.push('Check the field values and types');\n hints.push('Use action=\"help\" for field documentation');\n } else if (statusCode >= 500) {\n hints.push('This is a server error - try again later');\n }\n\n return new UserInputError(`API error (${statusCode}): ${message}`, hints);\n },\n} as const;\n\n/**\n * Check if an error is a UserInputError\n */\nexport function isUserInputError(error: unknown): error is UserInputError {\n return error instanceof UserInputError;\n}\n","/**\n * Response formatters for agent-friendly output\n *\n * This module re-exports formatters from @studiometa/productive-api\n * with MCP-specific defaults (no relationship IDs, no timestamps).\n *\n * Supports compact mode to reduce token usage by omitting verbose fields\n * like descriptions and notes from list responses.\n */\n\nimport {\n formatTimeEntry as cliFormatTimeEntry,\n formatProject as cliFormatProject,\n formatTask as cliFormatTask,\n formatPerson as cliFormatPerson,\n formatService as cliFormatService,\n formatCompany as cliFormatCompany,\n formatComment as cliFormatComment,\n formatTimer as cliFormatTimer,\n formatDeal as cliFormatDeal,\n formatBooking as cliFormatBooking,\n formatAttachment as cliFormatAttachment,\n formatPage as cliFormatPage,\n formatDiscussion as cliFormatDiscussion,\n formatActivity as cliFormatActivity,\n formatListResponse as cliFormatListResponse,\n type JsonApiResource,\n type JsonApiMeta,\n type FormatOptions,\n type FormattedPagination,\n} from '@studiometa/productive-api';\n\n// Re-export types\nexport type { JsonApiResource, JsonApiMeta, FormatOptions, FormattedPagination };\n\n/**\n * MCP-specific format options\n * - No relationship IDs (cleaner output for agents)\n * - No timestamps (reduce noise)\n * - HTML stripping enabled\n */\nconst MCP_FORMAT_OPTIONS: FormatOptions = {\n includeRelationshipIds: false,\n includeTimestamps: false,\n stripHtml: true,\n};\n\n/**\n * Extended format options for MCP with compact mode\n */\nexport interface McpFormatOptions {\n compact?: boolean;\n included?: JsonApiResource[];\n}\n\n/**\n * Remove verbose fields from an object for compact output\n */\nfunction compactify<T extends Record<string, unknown>>(obj: T, fieldsToRemove: string[]): T {\n const result = { ...obj };\n for (const field of fieldsToRemove) {\n delete result[field];\n }\n return result;\n}\n\n/**\n * Format time entry for agent consumption\n */\nexport function formatTimeEntry(\n entry: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatTimeEntry(entry, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['note', 'billable_time', 'approved']);\n }\n return result;\n}\n\n/**\n * Format project for agent consumption\n */\nexport function formatProject(\n project: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatProject(project, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['budget']);\n }\n return result;\n}\n\n/**\n * Format task for agent consumption\n * Tasks use included resources to resolve project/company names\n */\nexport function formatTask(\n task: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatTask(task, { ...MCP_FORMAT_OPTIONS, included: options?.included });\n if (options?.compact) {\n return compactify(result, [\n 'description',\n 'initial_estimate',\n 'worked_time',\n 'remaining_time',\n 'project', // Keep project_name but remove nested object\n 'company', // Keep company name inline if needed\n ]);\n }\n return result;\n}\n\n/**\n * Format person for agent consumption\n */\nexport function formatPerson(\n person: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatPerson(person, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['title', 'first_name', 'last_name']); // Keep 'name' which combines them\n }\n return result;\n}\n\n/**\n * Format service for agent consumption\n */\nexport function formatService(\n service: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatService(service, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['budgeted_time', 'worked_time']);\n }\n return result;\n}\n\n/**\n * Format company for agent consumption\n */\nexport function formatCompany(\n company: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatCompany(company, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['billing_name', 'domain', 'due_days']);\n }\n return result;\n}\n\n/**\n * Format comment for agent consumption\n */\nexport function formatComment(\n comment: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatComment(comment, { ...MCP_FORMAT_OPTIONS, included: options?.included });\n return result;\n}\n\n/**\n * Format timer for agent consumption\n */\nexport function formatTimer(\n timer: JsonApiResource,\n _options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatTimer(timer, MCP_FORMAT_OPTIONS);\n return result;\n}\n\n/**\n * Format deal for agent consumption\n */\nexport function formatDeal(\n deal: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatDeal(deal, { ...MCP_FORMAT_OPTIONS, included: options?.included });\n if (options?.compact) {\n return compactify(result, ['won_at', 'lost_at']);\n }\n return result;\n}\n\n/**\n * Format booking for agent consumption\n */\nexport function formatBooking(\n booking: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatBooking(booking, { ...MCP_FORMAT_OPTIONS, included: options?.included });\n if (options?.compact) {\n return compactify(result, ['approved_at', 'rejected_at', 'rejected_reason']);\n }\n return result;\n}\n\n/**\n * Format attachment for agent consumption\n */\nexport function formatAttachment(\n attachment: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatAttachment(attachment, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['url']);\n }\n return result;\n}\n\n/**\n * Format page for agent consumption\n */\nexport function formatPage(\n page: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatPage(page, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['body', 'version_number']);\n }\n return result;\n}\n\n/**\n * Format discussion for agent consumption\n */\nexport function formatDiscussion(\n discussion: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n const result = cliFormatDiscussion(discussion, MCP_FORMAT_OPTIONS);\n if (options?.compact) {\n return compactify(result, ['body']);\n }\n return result;\n}\n\nexport function formatActivity(\n activity: JsonApiResource,\n options?: McpFormatOptions,\n): Record<string, unknown> {\n return cliFormatActivity(activity, {\n ...MCP_FORMAT_OPTIONS,\n included: options?.included,\n });\n}\n\n/**\n * Format list response with pagination\n *\n * @param data - Array of JSON:API resources\n * @param formatter - Formatter function (item, options?) => T\n * @param meta - Pagination metadata\n * @param options - MCP format options (compact, included)\n */\nexport function formatListResponse<T>(\n data: JsonApiResource[],\n formatter: (item: JsonApiResource, options?: McpFormatOptions) => T,\n meta?: JsonApiMeta,\n options?: McpFormatOptions,\n): { data: T[]; meta?: FormattedPagination } {\n // Create a wrapper that passes MCP options to the formatter\n const wrappedFormatter = (item: JsonApiResource, _cliOptions?: FormatOptions) => {\n return formatter(item, options);\n };\n\n const result = cliFormatListResponse(data, wrappedFormatter, meta, {\n ...MCP_FORMAT_OPTIONS,\n included: options?.included,\n });\n\n return result as { data: T[]; meta?: FormattedPagination };\n}\n","/**\n * Proactive suggestion generators for MCP responses.\n *\n * Data-aware warnings and recommendations computed from response data.\n * Different from `_hints` (which show related resources/actions):\n * - `_hints` tell the agent *what to fetch next*\n * - `_suggestions` tell the agent *what to pay attention to*\n *\n * Key design constraints:\n * - No extra API calls — compute only from data already in the response.\n * - Return `string[]` (human-readable messages).\n * - Emoji prefixes: ⚠️ warnings, ℹ️ info, 📊 stats, ⏱️ timers.\n */\n\nimport type { JsonApiResource } from '@studiometa/productive-api';\nimport type { MyDaySummaryResult } from '@studiometa/productive-core';\n\n/**\n * Get today's date string in YYYY-MM-DD format\n */\nfunction getToday(): string {\n return new Date().toISOString().split('T')[0];\n}\n\n/**\n * Get suggestions for tasks.list response.\n *\n * - Warns about overdue tasks (due_date < today and not closed)\n * - Informs about unassigned tasks\n */\nexport function getTaskListSuggestions(tasks: JsonApiResource[]): string[] {\n const suggestions: string[] = [];\n if (!tasks || tasks.length === 0) return suggestions;\n\n const today = getToday();\n\n let overdueCount = 0;\n let unassignedCount = 0;\n\n for (const task of tasks) {\n const attrs = task.attributes as Record<string, unknown>;\n const relationships = task.relationships as Record<string, unknown> | undefined;\n\n // Check overdue: due_date exists, is before today, and task is not closed\n const dueDate = attrs.due_date as string | undefined;\n const closed = attrs.closed as boolean | undefined;\n const closedAt = attrs.closed_at as string | undefined;\n if (dueDate && dueDate < today && !closed && !closedAt) {\n overdueCount++;\n }\n\n // Check unassigned: assignee relationship is absent or null\n const assignee = (relationships?.assignee as { data?: unknown } | undefined)?.data;\n if (!assignee) {\n unassignedCount++;\n }\n }\n\n if (overdueCount > 0) {\n suggestions.push(`⚠️ ${overdueCount} task(s) are overdue`);\n }\n\n if (unassignedCount > 0) {\n suggestions.push(`ℹ️ ${unassignedCount} task(s) have no assignee`);\n }\n\n return suggestions;\n}\n\n/**\n * Get suggestions for tasks.get response.\n *\n * - Warns if the single task is overdue (and by how many days)\n * - Informs if no time has been logged (only when time_entries are included)\n */\nexport function getTaskGetSuggestions(\n task: JsonApiResource,\n included?: JsonApiResource[],\n): string[] {\n const suggestions: string[] = [];\n if (!task) return suggestions;\n\n const attrs = task.attributes as Record<string, unknown>;\n const today = getToday();\n\n // Check overdue\n const dueDate = attrs.due_date as string | undefined;\n const closed = attrs.closed as boolean | undefined;\n const closedAt = attrs.closed_at as string | undefined;\n if (dueDate && dueDate < today && !closed && !closedAt) {\n const due = new Date(dueDate);\n const now = new Date(today);\n const diffDays = Math.round((now.getTime() - due.getTime()) / (1000 * 60 * 60 * 24));\n suggestions.push(`⚠️ Task is ${diffDays} day(s) overdue`);\n }\n\n // Check no time logged — only if time_entries were included in the response\n if (included && included.length > 0) {\n const taskId = task.id;\n const timeEntries = included.filter((r) => {\n if (r.type !== 'time_entries') return false;\n const rels = r.relationships as Record<string, unknown> | undefined;\n const taskRel = (rels?.task as { data?: { id?: string } } | undefined)?.data;\n return taskRel?.id === taskId;\n });\n\n if (timeEntries.length === 0) {\n suggestions.push('ℹ️ No time entries on this task');\n }\n }\n\n return suggestions;\n}\n\n/**\n * Get suggestions for time.list response.\n *\n * - Shows total hours logged across all entries in the response.\n * - If filtered by today, shows hours vs 8h target.\n */\nexport function getTimeListSuggestions(\n entries: JsonApiResource[],\n filter?: Record<string, string>,\n): string[] {\n const suggestions: string[] = [];\n if (!entries || entries.length === 0) return suggestions;\n\n // Sum all durations (stored in minutes)\n const totalMinutes = entries.reduce((sum, entry) => {\n const attrs = entry.attributes as Record<string, unknown>;\n return sum + ((attrs.time as number) || 0);\n }, 0);\n\n const totalHours = +(totalMinutes / 60).toFixed(1);\n\n // Detect if this is a \"today\" filter: after === today AND before === today\n const today = getToday();\n const isToday = filter?.after === today && filter?.before === today;\n\n if (isToday) {\n const targetHours = 8;\n suggestions.push(`📊 ${totalHours}h/${targetHours}h logged today`);\n } else if (totalMinutes > 0) {\n suggestions.push(`📊 Total: ${totalHours}h logged`);\n }\n\n return suggestions;\n}\n\n/**\n * Get suggestions for summaries.my_day response.\n *\n * - Warns if no time has been logged today.\n * - Warns if a timer has been running for more than 2 hours.\n */\nexport function getMyDaySuggestions(data: MyDaySummaryResult): string[] {\n const suggestions: string[] = [];\n if (!data) return suggestions;\n\n // No time logged today\n if (data.time.logged_today_minutes === 0 && data.time.entries_today === 0) {\n suggestions.push('⚠️ No time logged today');\n }\n\n // Timer running long (> 2 hours = 120 minutes)\n if (data.timers && data.timers.length > 0) {\n for (const timer of data.timers) {\n if (timer.total_time > 120) {\n const hours = +(timer.total_time / 60).toFixed(1);\n suggestions.push(`⏱️ Timer running for ${hours}h — remember to stop it`);\n }\n }\n }\n\n return suggestions;\n}\n","/**\n * Utility functions for resource handlers\n */\n\nimport type { ToolResult } from './types.js';\n\nimport { UserInputError, isUserInputError } from '../errors.js';\n\n/**\n * Helper to create a successful JSON response\n */\nexport function jsonResult(data: unknown): ToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n}\n\n/**\n * Helper to create an error response from a string message\n */\nexport function errorResult(message: string): ToolResult {\n return {\n content: [{ type: 'text', text: `**Error:** ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Helper to create an error response from a UserInputError\n * Includes formatted hints for LLM consumption\n */\nexport function inputErrorResult(error: UserInputError): ToolResult {\n return {\n content: [{ type: 'text', text: error.toFormattedMessage() }],\n isError: true,\n };\n}\n\n/**\n * Helper to create an error response from any error type\n * Automatically formats UserInputError with hints\n */\nexport function formatError(error: unknown): ToolResult {\n if (isUserInputError(error)) {\n return inputErrorResult(error);\n }\n\n const message = error instanceof Error ? error.message : String(error);\n return errorResult(message);\n}\n\n/**\n * Convert unknown filter to string filter for API\n */\nexport function toStringFilter(\n filter?: Record<string, unknown>,\n): Record<string, string> | undefined {\n if (!filter) return undefined;\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(filter)) {\n if (value !== undefined && value !== null) {\n result[key] = String(value);\n }\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n","/**\n * Resolve handler for MCP.\n *\n * Thin wrapper around core's resource resolver.\n * Provides handleResolve for the MCP 'resolve' action.\n */\n\nimport { resolveResource, ResolveError, type ResolveResult } from '@studiometa/productive-core';\n\nimport type { HandlerContext, ToolResult } from './types.js';\n\nimport { UserInputError } from '../errors.js';\nimport { errorResult, inputErrorResult, jsonResult } from './utils.js';\n\n// Re-export types used by MCP handlers\nexport type { ResolvableResourceType, ResolveResult } from '@studiometa/productive-core';\n\n/**\n * Arguments for resolve action\n */\ninterface ResolveArgs {\n query?: string;\n type?: 'person' | 'project' | 'company' | 'deal' | 'service';\n project_id?: string;\n}\n\n/**\n * Handle resolve action for a resource.\n *\n * Delegates to core's resolveResource function and wraps\n * errors in MCP-friendly format.\n */\nexport async function handleResolve(args: ResolveArgs, ctx: HandlerContext): Promise<ToolResult> {\n const { query, type, project_id } = args;\n\n if (!query) {\n return errorResult('query is required for resolve action');\n }\n\n try {\n const execCtx = ctx.executor();\n const results: ResolveResult[] = await resolveResource(execCtx.api, query, {\n type,\n projectId: project_id,\n });\n\n return jsonResult({\n query,\n matches: results,\n exact: results.length === 1 && results[0].exact,\n });\n } catch (error) {\n if (error instanceof ResolveError) {\n return inputErrorResult(\n new UserInputError(error.message, [\n `Query: \"${error.query}\"`,\n ...(error.type ? [`Type: ${error.type}`] : []),\n ]),\n );\n }\n throw error;\n }\n}\n","/**\n * Factory for creating resource handlers.\n *\n * Encapsulates the repetitive list/get/create/update/delete/resolve pattern\n * shared by all MCP resource handlers.\n */\n\nimport type { JsonApiResource } from '@studiometa/productive-api';\nimport type { ExecutorContext, ResolvedInfo } from '@studiometa/productive-core';\n\nimport type { McpFormatOptions } from '../formatters.js';\nimport type { ContextualHints } from '../hints.js';\nimport type { CommonArgs, HandlerContext, ToolResult } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatListResponse } from '../formatters.js';\nimport {\n getTaskGetSuggestions,\n getTaskListSuggestions,\n getTimeListSuggestions,\n} from '../suggestions.js';\nimport { handleResolve, type ResolvableResourceType } from './resolve.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\n// Re-export for use in customActions\nexport type { ExecutorContext };\n\n/**\n * Result type from executors (list operations)\n */\ninterface ListExecutorResult {\n data: JsonApiResource[];\n meta?: { total_count?: number; total_pages?: number; current_page?: number };\n included?: JsonApiResource[];\n resolved?: Record<string, ResolvedInfo>;\n}\n\n/**\n * Result type from executors (single resource operations)\n */\ninterface SingleExecutorResult {\n data: JsonApiResource;\n included?: JsonApiResource[];\n}\n\n/**\n * Configuration for the create action\n */\ninterface CreateConfig<TArgs> {\n /** Fields that must be present for create */\n required: (keyof TArgs)[];\n /** Custom validation that returns a ToolResult on error, or undefined if valid */\n validateArgs?: (args: TArgs) => ToolResult | undefined;\n /** Map args to executor options */\n mapOptions: (args: TArgs) => Record<string, unknown>;\n}\n\n/**\n * Configuration for the update action\n */\ninterface UpdateConfig<TArgs> {\n /** Fields that can be updated - if none provided, error is returned */\n allowedFields?: (keyof TArgs)[];\n /** Map args to executor options (id is handled automatically) */\n mapOptions: (args: TArgs) => Record<string, unknown>;\n}\n\n/**\n * Configuration for a resource handler\n */\nexport interface ResourceHandlerConfig<TArgs extends CommonArgs = CommonArgs> {\n /** Resource name (e.g., 'projects', 'tasks') */\n resource: string;\n\n /** Display name for error messages (defaults to resource) */\n displayName?: string;\n\n /** Valid actions for this resource */\n actions: string[];\n\n /** Formatter function for this resource */\n formatter: (item: JsonApiResource, options?: McpFormatOptions) => Record<string, unknown>;\n\n /** Optional hints generator for get action */\n hints?: (data: JsonApiResource, id: string) => ContextualHints;\n\n /** Default include for list/get operations */\n defaultInclude?: {\n list?: string[];\n get?: string[];\n };\n\n /** Whether this resource supports the resolve action */\n supportsResolve?: boolean;\n\n /** Extract additional filters from args for list operations */\n listFilterFromArgs?: (args: TArgs) => Record<string, string>;\n\n /** Extract extra args to pass to handleResolve (e.g., project_id for tasks/time) */\n resolveArgsFromArgs?: (args: TArgs) => Record<string, string | undefined>;\n\n /** Custom action handlers for non-CRUD actions (e.g., resolve/reopen for discussions, start/stop for timers) */\n customActions?: Record<\n string,\n (args: TArgs, ctx: HandlerContext, execCtx: ExecutorContext) => Promise<ToolResult>\n >;\n\n /** Create action configuration (if supported) */\n create?: CreateConfig<TArgs>;\n\n /** Update action configuration (if supported) */\n update?: UpdateConfig<TArgs>;\n\n /** Executors for each action */\n executors: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n list: (options: any, ctx: ExecutorContext) => Promise<ListExecutorResult>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n get?: (options: any, ctx: ExecutorContext) => Promise<SingleExecutorResult>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n create?: (options: any, ctx: ExecutorContext) => Promise<SingleExecutorResult>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n update?: (options: any, ctx: ExecutorContext) => Promise<SingleExecutorResult>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n delete?: (options: any, ctx: ExecutorContext) => Promise<unknown>;\n };\n}\n\n/**\n * Merge user includes with defaults, ensuring no duplicates\n */\nfunction mergeIncludes(userInclude?: string[], defaults?: string[]): string[] | undefined {\n if (!userInclude?.length && !defaults?.length) return undefined;\n if (!userInclude?.length) return defaults;\n if (!defaults?.length) return userInclude;\n return [...new Set([...defaults, ...userInclude])];\n}\n\n/**\n * Create a resource handler function from configuration.\n *\n * @example\n * ```typescript\n * export const handleProjects = createResourceHandler({\n * resource: 'projects',\n * actions: ['list', 'get', 'resolve'],\n * formatter: formatProject,\n * hints: (data, id) => getProjectHints(id),\n * supportsResolve: true,\n * executors: {\n * list: listProjects,\n * get: getProject,\n * },\n * });\n * ```\n */\nexport function createResourceHandler<TArgs extends CommonArgs = CommonArgs>(\n config: ResourceHandlerConfig<TArgs>,\n): (\n action: string,\n args: TArgs & { query?: string; type?: ResolvableResourceType },\n ctx: HandlerContext,\n) => Promise<ToolResult> {\n const {\n resource,\n displayName = resource,\n actions,\n formatter,\n hints,\n defaultInclude,\n supportsResolve,\n listFilterFromArgs,\n resolveArgsFromArgs,\n customActions,\n create: createConfig,\n update: updateConfig,\n executors,\n } = config;\n\n return async (\n action: string,\n args: TArgs & { query?: string; type?: ResolvableResourceType },\n ctx: HandlerContext,\n ): Promise<ToolResult> => {\n const { formatOptions, filter, page, perPage, include: userInclude } = ctx;\n const { id, query, type } = args;\n\n const execCtx = ctx.executor();\n\n // Handle custom actions first (before built-in resolve, to allow overriding)\n if (customActions?.[action]) {\n return customActions[action](args, ctx, execCtx);\n }\n\n // Handle resolve action (for query resolution, not domain-specific resolve)\n if (action === 'resolve') {\n if (!supportsResolve) {\n return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));\n }\n return handleResolve({ query, type, ...resolveArgsFromArgs?.(args) }, ctx);\n }\n\n // Handle get action\n if (action === 'get') {\n if (!executors.get) {\n return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));\n }\n if (!id) return inputErrorResult(ErrorMessages.missingId('get'));\n\n const include = mergeIncludes(userInclude, defaultInclude?.get);\n const result = await executors.get({ id, include }, execCtx);\n const formatted = formatter(result.data, { ...formatOptions, included: result.included });\n\n const getResponseData: Record<string, unknown> = { ...formatted };\n\n if (ctx.includeHints) {\n if (hints) {\n getResponseData._hints = hints(result.data, id);\n }\n }\n\n // Add resource-specific suggestions (controlled separately from hints)\n if (ctx.includeSuggestions !== false) {\n let getSuggestions: string[] = [];\n if (resource === 'tasks') {\n getSuggestions = getTaskGetSuggestions(result.data, result.included);\n }\n if (getSuggestions.length > 0) {\n getResponseData._suggestions = getSuggestions;\n }\n }\n\n return jsonResult(getResponseData);\n }\n\n // Handle create action\n if (action === 'create') {\n if (!executors.create || !createConfig) {\n return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));\n }\n\n // Check required fields first\n const missingFields = createConfig.required.filter((field) => !args[field]);\n if (missingFields.length > 0) {\n return inputErrorResult(\n ErrorMessages.missingRequiredFields(displayName, missingFields as string[]),\n );\n }\n\n // Run custom args validation if provided (returns ToolResult on error)\n if (createConfig.validateArgs) {\n const errorResult = createConfig.validateArgs(args);\n if (errorResult) return errorResult;\n }\n\n const options = createConfig.mapOptions(args);\n const result = await executors.create(options, execCtx);\n return jsonResult({ success: true, ...formatter(result.data, formatOptions) });\n }\n\n // Handle update action\n if (action === 'update') {\n if (!executors.update || !updateConfig) {\n return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));\n }\n if (!id) return inputErrorResult(ErrorMessages.missingId('update'));\n\n // Validate at least one allowed field is provided\n if (updateConfig.allowedFields && updateConfig.allowedFields.length > 0) {\n const hasAnyField = updateConfig.allowedFields.some((field) => args[field] !== undefined);\n if (!hasAnyField) {\n return inputErrorResult(\n ErrorMessages.noUpdateFieldsSpecified(updateConfig.allowedFields as string[]),\n );\n }\n }\n\n const options = { id, ...updateConfig.mapOptions(args) };\n const result = await executors.update(options, execCtx);\n return jsonResult({ success: true, ...formatter(result.data, formatOptions) });\n }\n\n // Handle delete action\n if (action === 'delete') {\n if (!executors.delete) {\n return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));\n }\n if (!id) return inputErrorResult(ErrorMessages.missingId('delete'));\n\n await executors.delete({ id }, execCtx);\n return jsonResult({ success: true, deleted: id });\n }\n\n // Handle list action\n if (action === 'list') {\n const include = mergeIncludes(userInclude, defaultInclude?.list);\n const additionalFilters = {\n ...filter,\n ...listFilterFromArgs?.(args),\n };\n const result = await executors.list({ page, perPage, additionalFilters, include }, execCtx);\n\n const response = formatListResponse(result.data, formatter, result.meta, {\n ...formatOptions,\n included: result.included,\n });\n\n const listResponseData: Record<string, unknown> = { ...response };\n\n // Include resolution metadata if any resolutions occurred\n if (result.resolved && Object.keys(result.resolved).length > 0) {\n listResponseData._resolved = result.resolved;\n }\n\n // Add resource-specific suggestions\n if (ctx.includeSuggestions !== false) {\n let listSuggestions: string[] = [];\n if (resource === 'tasks') {\n listSuggestions = getTaskListSuggestions(result.data);\n } else if (resource === 'time') {\n listSuggestions = getTimeListSuggestions(result.data, additionalFilters);\n }\n if (listSuggestions.length > 0) {\n listResponseData._suggestions = listSuggestions;\n }\n }\n\n return jsonResult(listResponseData);\n }\n\n // Invalid action\n return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));\n };\n}\n","/**\n * Activities MCP handler.\n *\n * Uses the createResourceHandler factory for the common list pattern.\n * Activities are read-only — only `list` is supported.\n */\n\nimport { listActivities } from '@studiometa/productive-core';\n\nimport type { CommonArgs } from './types.js';\n\nimport { formatActivity } from '../formatters.js';\nimport { createResourceHandler } from './factory.js';\n\n/**\n * Args for activity list requests.\n */\nexport interface ActivityArgs extends CommonArgs {\n /** ISO timestamp — return activities after this date */\n after?: string;\n /** Filter by event type: create | update | delete */\n event?: string;\n}\n\n/**\n * Handle activities resource.\n *\n * Supports: list\n */\nexport const handleActivities = createResourceHandler<ActivityArgs>({\n resource: 'activities',\n actions: ['list'],\n formatter: formatActivity,\n executors: {\n list: listActivities,\n },\n defaultInclude: { list: ['creator'] },\n listFilterFromArgs: (args) => {\n const filter: Record<string, string> = {};\n if (args.after) filter.after = args.after;\n if (args.event) filter.event = args.event;\n return filter;\n },\n});\n","/**\n * Contextual hints for AI agents\n *\n * Provides suggestions for related resources and common actions\n * to help agents discover how to fetch additional context.\n */\n\nexport interface ResourceHint {\n resource: string;\n description: string;\n example: Record<string, unknown>;\n}\n\nexport interface ActionHint {\n action: string;\n example: Record<string, unknown>;\n}\n\nexport interface ContextualHints {\n related_resources?: ResourceHint[];\n common_actions?: ActionHint[];\n}\n\n/**\n * Generate hints for a task\n */\nexport function getTaskHints(taskId: string, serviceId?: string): ContextualHints {\n const hints: ContextualHints = {\n related_resources: [\n {\n resource: 'comments',\n description: 'Get comments on this task',\n example: {\n resource: 'comments',\n action: 'list',\n filter: { task_id: taskId },\n },\n },\n {\n resource: 'time',\n description: 'Get time entries logged on this task',\n example: {\n resource: 'time',\n action: 'list',\n filter: { task_id: taskId },\n },\n },\n {\n resource: 'tasks',\n description: 'Get subtasks of this task',\n example: {\n resource: 'tasks',\n action: 'list',\n filter: { parent_task_id: taskId },\n },\n },\n ],\n common_actions: [\n {\n action: 'Add a comment',\n example: {\n resource: 'comments',\n action: 'create',\n task_id: taskId,\n body: '<your comment>',\n },\n },\n ],\n };\n\n // Add time logging hint if we have a service ID\n if (serviceId) {\n hints.common_actions!.push({\n action: 'Log time on this task',\n example: {\n resource: 'time',\n action: 'create',\n service_id: serviceId,\n task_id: taskId,\n date: new Date().toISOString().split('T')[0],\n time: 60,\n note: '<description of work>',\n },\n });\n }\n\n return hints;\n}\n\n/**\n * Generate hints for a project\n */\nexport function getProjectHints(projectId: string): ContextualHints {\n return {\n related_resources: [\n {\n resource: 'tasks',\n description: 'Get tasks in this project',\n example: {\n resource: 'tasks',\n action: 'list',\n filter: { project_id: projectId },\n },\n },\n {\n resource: 'services',\n description: 'Get services (budget lines) for this project',\n example: {\n resource: 'services',\n action: 'list',\n filter: { project_id: projectId },\n },\n },\n {\n resource: 'time',\n description: 'Get time entries for this project',\n example: {\n resource: 'time',\n action: 'list',\n filter: { project_id: projectId },\n },\n },\n {\n resource: 'comments',\n description: 'Get comments on this project',\n example: {\n resource: 'comments',\n action: 'list',\n filter: { project_id: projectId },\n },\n },\n {\n resource: 'deals',\n description: 'Get deals/budgets for this project',\n example: {\n resource: 'deals',\n action: 'list',\n filter: { project_id: projectId },\n },\n },\n ],\n common_actions: [\n {\n action: 'Create a task',\n example: {\n resource: 'tasks',\n action: 'create',\n project_id: projectId,\n task_list_id: '<task_list_id>',\n title: '<task title>',\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a deal/budget\n */\nexport function getDealHints(dealId: string): ContextualHints {\n return {\n related_resources: [\n {\n resource: 'comments',\n description: 'Get comments on this deal/budget',\n example: {\n resource: 'comments',\n action: 'list',\n filter: { deal_id: dealId },\n },\n },\n {\n resource: 'services',\n description: 'Get services (budget lines) for this deal',\n example: {\n resource: 'services',\n action: 'list',\n filter: { deal_id: dealId },\n },\n },\n {\n resource: 'time',\n description: 'Get time entries for this deal/budget',\n example: {\n resource: 'time',\n action: 'list',\n filter: { deal_id: dealId },\n },\n },\n {\n resource: 'bookings',\n description: 'Get resource bookings for this deal',\n example: {\n resource: 'bookings',\n action: 'list',\n filter: { deal_id: dealId },\n },\n },\n ],\n common_actions: [\n {\n action: 'Add a comment',\n example: {\n resource: 'comments',\n action: 'create',\n deal_id: dealId,\n body: '<your comment>',\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a person\n */\nexport function getPersonHints(personId: string): ContextualHints {\n return {\n related_resources: [\n {\n resource: 'tasks',\n description: 'Get tasks assigned to this person',\n example: {\n resource: 'tasks',\n action: 'list',\n filter: { assignee_id: personId },\n },\n },\n {\n resource: 'time',\n description: 'Get time entries by this person',\n example: {\n resource: 'time',\n action: 'list',\n filter: { person_id: personId },\n },\n },\n {\n resource: 'bookings',\n description: 'Get bookings for this person',\n example: {\n resource: 'bookings',\n action: 'list',\n filter: { person_id: personId },\n },\n },\n {\n resource: 'timers',\n description: 'Get active timers for this person',\n example: {\n resource: 'timers',\n action: 'list',\n filter: { person_id: personId },\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a services list response.\n *\n * When the query doesn't already filter by deal_id, suggests filtering\n * by deal_id to scope services to a specific budget/deal.\n *\n * @param currentFilter - The filter object currently applied to the list query\n */\nexport function getServiceListHints(\n currentFilter?: Record<string, string>,\n): ContextualHints | null {\n // No hint needed if already filtering by deal_id\n if (currentFilter?.deal_id) {\n return null;\n }\n\n return {\n common_actions: [\n {\n action: 'Filter services by deal to see budget line items for a specific deal',\n example: {\n resource: 'services',\n action: 'list',\n filter: { deal_id: '<deal_id>' },\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a service\n */\nexport function getServiceHints(serviceId: string): ContextualHints {\n return {\n related_resources: [\n {\n resource: 'time',\n description: 'Get time entries for this service',\n example: {\n resource: 'time',\n action: 'list',\n filter: { service_id: serviceId },\n },\n },\n {\n resource: 'tasks',\n description: 'Get tasks linked to this service',\n example: {\n resource: 'tasks',\n action: 'list',\n filter: { service_id: serviceId },\n },\n },\n {\n resource: 'bookings',\n description: 'Get bookings for this service',\n example: {\n resource: 'bookings',\n action: 'list',\n filter: { service_id: serviceId },\n },\n },\n ],\n common_actions: [\n {\n action: 'Log time on this service',\n example: {\n resource: 'time',\n action: 'create',\n service_id: serviceId,\n date: new Date().toISOString().split('T')[0],\n time: 60,\n note: '<description of work>',\n },\n },\n {\n action: 'Start a timer',\n example: {\n resource: 'timers',\n action: 'start',\n service_id: serviceId,\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a company\n */\nexport function getCompanyHints(companyId: string): ContextualHints {\n return {\n related_resources: [\n {\n resource: 'projects',\n description: 'Get projects for this company',\n example: {\n resource: 'projects',\n action: 'list',\n filter: { company_id: companyId },\n },\n },\n {\n resource: 'deals',\n description: 'Get deals for this company',\n example: {\n resource: 'deals',\n action: 'list',\n filter: { company_id: companyId },\n },\n },\n {\n resource: 'tasks',\n description: 'Get tasks for this company',\n example: {\n resource: 'tasks',\n action: 'list',\n filter: { company_id: companyId },\n },\n },\n {\n resource: 'people',\n description: 'Get contacts at this company',\n example: {\n resource: 'people',\n action: 'list',\n filter: { company_id: companyId },\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a time entry\n */\nexport function getTimeEntryHints(\n timeEntryId: string,\n taskId?: string,\n serviceId?: string,\n): ContextualHints {\n const hints: ContextualHints = {\n related_resources: [],\n common_actions: [\n {\n action: 'Update this time entry',\n example: {\n resource: 'time',\n action: 'update',\n id: timeEntryId,\n time: 120,\n note: '<updated note>',\n },\n },\n ],\n };\n\n if (taskId) {\n hints.related_resources!.push({\n resource: 'tasks',\n description: 'Get the associated task',\n example: {\n resource: 'tasks',\n action: 'get',\n id: taskId,\n },\n });\n }\n\n if (serviceId) {\n hints.related_resources!.push({\n resource: 'services',\n description: 'Get the associated service',\n example: {\n resource: 'services',\n action: 'get',\n id: serviceId,\n },\n });\n }\n\n return hints;\n}\n\n/**\n * Generate hints for a comment\n */\nexport function getCommentHints(\n _commentId: string,\n commentableType?: string,\n commentableId?: string,\n): ContextualHints {\n const hints: ContextualHints = {\n related_resources: [],\n };\n\n if (commentableType && commentableId) {\n const resourceMap: Record<string, string> = {\n task: 'tasks',\n deal: 'deals',\n project: 'projects',\n company: 'companies',\n };\n\n const resource = resourceMap[commentableType];\n if (resource) {\n hints.related_resources!.push({\n resource,\n description: `Get the ${commentableType} this comment is on`,\n example: {\n resource,\n action: 'get',\n id: commentableId,\n },\n });\n }\n }\n\n return hints;\n}\n\n/**\n * Generate hints for an attachment\n */\nexport function getAttachmentHints(\n _attachmentId: string,\n attachableType?: string,\n): ContextualHints {\n const hints: ContextualHints = {\n related_resources: [],\n common_actions: [\n {\n action: 'Delete this attachment',\n example: {\n resource: 'attachments',\n action: 'delete',\n id: _attachmentId,\n },\n },\n ],\n };\n\n if (attachableType) {\n const resourceMap: Record<string, string> = {\n Task: 'tasks',\n Comment: 'comments',\n Deal: 'deals',\n Page: 'projects',\n };\n\n const resource = resourceMap[attachableType];\n if (resource) {\n hints.related_resources!.push({\n resource,\n description: `View the ${attachableType.toLowerCase()} this attachment belongs to`,\n example: {\n resource,\n action: 'list',\n },\n });\n }\n }\n\n return hints;\n}\n\n/**\n * Generate hints for a booking\n */\nexport function getBookingHints(bookingId: string, personId?: string): ContextualHints {\n const hints: ContextualHints = {\n related_resources: [],\n common_actions: [\n {\n action: 'Update this booking',\n example: {\n resource: 'bookings',\n action: 'update',\n id: bookingId,\n time: 480,\n },\n },\n ],\n };\n\n if (personId) {\n hints.related_resources!.push({\n resource: 'people',\n description: 'Get the person this booking is for',\n example: {\n resource: 'people',\n action: 'get',\n id: personId,\n },\n });\n }\n\n return hints;\n}\n\n/**\n * Generate hints for a page\n */\nexport function getPageHints(pageId: string): ContextualHints {\n return {\n related_resources: [\n {\n resource: 'discussions',\n description: 'Get discussions on this page',\n example: {\n resource: 'discussions',\n action: 'list',\n filter: { page_id: pageId },\n },\n },\n {\n resource: 'comments',\n description: 'Get comments on this page',\n example: {\n resource: 'comments',\n action: 'list',\n filter: { page_id: pageId },\n },\n },\n {\n resource: 'pages',\n description: 'Get sub-pages of this page',\n example: {\n resource: 'pages',\n action: 'list',\n filter: { parent_page_id: pageId },\n },\n },\n ],\n common_actions: [\n {\n action: 'Create a discussion',\n example: {\n resource: 'discussions',\n action: 'create',\n page_id: pageId,\n body: '<your discussion>',\n },\n },\n {\n action: 'Create a sub-page',\n example: {\n resource: 'pages',\n action: 'create',\n parent_page_id: pageId,\n title: '<sub-page title>',\n project_id: '<project_id>',\n },\n },\n ],\n };\n}\n\n/**\n * Generate hints for a discussion\n */\nexport function getDiscussionHints(discussionId: string, pageId?: string): ContextualHints {\n const hints: ContextualHints = {\n related_resources: [\n {\n resource: 'comments',\n description: 'Get comments on this discussion',\n example: {\n resource: 'comments',\n action: 'list',\n filter: { discussion_id: discussionId },\n },\n },\n ],\n common_actions: [\n {\n action: 'Resolve this discussion',\n example: {\n resource: 'discussions',\n action: 'resolve',\n id: discussionId,\n },\n },\n {\n action: 'Add a comment',\n example: {\n resource: 'comments',\n action: 'create',\n discussion_id: discussionId,\n body: '<your comment>',\n },\n },\n ],\n };\n\n if (pageId) {\n hints.related_resources!.push({\n resource: 'pages',\n description: 'Get the page this discussion is on',\n example: {\n resource: 'pages',\n action: 'get',\n id: pageId,\n },\n });\n }\n\n return hints;\n}\n\n/**\n * Generate hints for a timer\n */\nexport function getTimerHints(timerId: string, serviceId?: string): ContextualHints {\n const hints: ContextualHints = {\n common_actions: [\n {\n action: 'Stop this timer',\n example: {\n resource: 'timers',\n action: 'stop',\n id: timerId,\n },\n },\n ],\n related_resources: [],\n };\n\n if (serviceId) {\n hints.related_resources!.push({\n resource: 'services',\n description: 'Get the service this timer is running on',\n example: {\n resource: 'services',\n action: 'get',\n id: serviceId,\n },\n });\n }\n\n return hints;\n}\n","/**\n * Attachments MCP handler.\n */\n\nimport { listAttachments, getAttachment, deleteAttachment } from '@studiometa/productive-core';\n\nimport type { AttachmentArgs } from './types.js';\n\nimport { formatAttachment } from '../formatters.js';\nimport { getAttachmentHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\n\nexport const handleAttachments = createResourceHandler<AttachmentArgs>({\n resource: 'attachments',\n actions: ['list', 'get', 'delete'],\n formatter: formatAttachment,\n hints: (data, id) => {\n const attachableType = data.attributes?.attachable_type as string | undefined;\n return getAttachmentHints(id, attachableType);\n },\n listFilterFromArgs: (args) => {\n const filters: Record<string, string> = {};\n if (args.task_id) filters.task_id = args.task_id;\n if (args.comment_id) filters.comment_id = args.comment_id;\n if (args.deal_id) filters.deal_id = args.deal_id;\n return filters;\n },\n executors: {\n list: listAttachments,\n get: getAttachment,\n delete: deleteAttachment,\n },\n});\n","/**\n * Batch handler - execute multiple operations in a single call\n *\n * Enables AI agents to reduce round-trips by batching multiple\n * Productive.io operations into one MCP tool call.\n */\n\nimport type { ProductiveCredentials } from '../auth.js';\nimport type { ToolResult } from './types.js';\n\nimport { UserInputError } from '../errors.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\n/** Maximum number of operations allowed in a single batch */\nexport const MAX_BATCH_SIZE = 10;\n\n/**\n * A single operation within a batch request\n */\nexport interface BatchOperation {\n resource: string;\n action: string;\n [key: string]: unknown;\n}\n\n/**\n * Result of a single batch operation\n */\nexport interface BatchOperationResult {\n resource: string;\n action: string;\n index: number;\n data?: unknown;\n error?: string;\n}\n\n/**\n * Summary of batch execution\n */\nexport interface BatchSummary {\n total: number;\n succeeded: number;\n failed: number;\n}\n\n/**\n * Full batch response\n */\nexport interface BatchResponse {\n _batch: BatchSummary;\n results: BatchOperationResult[];\n}\n\n/**\n * Function signature for executing a single tool operation\n * Injected for testability - in production this is executeToolWithCredentials\n */\nexport type ExecuteFunction = (\n name: string,\n args: Record<string, unknown>,\n credentials: ProductiveCredentials,\n) => Promise<ToolResult>;\n\n/**\n * Validate batch operations array\n */\nfunction validateOperations(operations: unknown): BatchOperation[] {\n // Check if operations is an array\n if (!Array.isArray(operations)) {\n throw new UserInputError('operations must be an array', [\n 'Provide an array of operation objects',\n 'Each operation needs: { resource: \"...\", action: \"...\", ...params }',\n ]);\n }\n\n // Check if array is empty\n if (operations.length === 0) {\n throw new UserInputError('operations array cannot be empty', [\n 'Provide at least one operation',\n 'Example: operations: [{ resource: \"projects\", action: \"list\" }]',\n ]);\n }\n\n // Check max size\n if (operations.length > MAX_BATCH_SIZE) {\n throw new UserInputError(`operations array exceeds maximum size of ${MAX_BATCH_SIZE}`, [\n `Split your batch into chunks of ${MAX_BATCH_SIZE} or fewer operations`,\n `You provided ${operations.length} operations`,\n ]);\n }\n\n // Validate each operation\n const validatedOps: BatchOperation[] = [];\n const errors: string[] = [];\n\n for (let i = 0; i < operations.length; i++) {\n const op = operations[i];\n\n if (typeof op !== 'object' || op === null) {\n errors.push(`Operation at index ${i}: must be an object`);\n continue;\n }\n\n const { resource, action } = op as Record<string, unknown>;\n\n if (typeof resource !== 'string' || resource.trim() === '') {\n errors.push(`Operation at index ${i}: missing or invalid \"resource\" field`);\n }\n\n if (typeof action !== 'string' || action.trim() === '') {\n errors.push(`Operation at index ${i}: missing or invalid \"action\" field`);\n }\n\n if (errors.length === 0) {\n validatedOps.push(op as BatchOperation);\n }\n }\n\n if (errors.length > 0) {\n throw new UserInputError('Invalid operations in batch', errors);\n }\n\n return validatedOps;\n}\n\n/**\n * Execute a single operation and capture result\n */\nasync function executeOperation(\n operation: BatchOperation,\n index: number,\n credentials: ProductiveCredentials,\n execute: ExecuteFunction,\n): Promise<BatchOperationResult> {\n const { resource, action, ...params } = operation;\n\n try {\n const result = await execute('productive', { resource, action, ...params }, credentials);\n\n // Parse the result content to extract data\n const content = result.content[0];\n if (content?.type === 'text') {\n try {\n const data = JSON.parse(content.text);\n if (result.isError) {\n return { resource, action, index, error: content.text };\n }\n return { resource, action, index, data };\n } catch {\n // If JSON parsing fails, treat text as error message if isError, otherwise as data\n if (result.isError) {\n return { resource, action, index, error: content.text };\n }\n return { resource, action, index, data: content.text };\n }\n }\n\n return { resource, action, index, data: null };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { resource, action, index, error: message };\n }\n}\n\n/**\n * Handle batch operation - execute multiple operations in parallel\n *\n * @param operations - Array of operations to execute\n * @param credentials - API credentials\n * @param execute - Function to execute individual operations (injected for testability)\n * @returns Batch response with summary and individual results\n */\nexport async function handleBatch(\n operations: unknown,\n credentials: ProductiveCredentials,\n execute: ExecuteFunction,\n): Promise<ToolResult> {\n // Validate operations\n let validatedOps: BatchOperation[];\n try {\n validatedOps = validateOperations(operations);\n } catch (err) {\n if (err instanceof UserInputError) {\n return inputErrorResult(err);\n }\n throw err;\n }\n\n // Execute all operations in parallel\n const results = await Promise.all(\n validatedOps.map((op, index) => executeOperation(op, index, credentials, execute)),\n );\n\n // Calculate summary\n const succeeded = results.filter((r) => r.data !== undefined && r.error === undefined).length;\n const failed = results.filter((r) => r.error !== undefined).length;\n\n const response: BatchResponse = {\n _batch: {\n total: results.length,\n succeeded,\n failed,\n },\n results,\n };\n\n return jsonResult(response);\n}\n","/**\n * Bookings MCP handler.\n */\n\nimport {\n listBookings,\n getBooking,\n createBooking,\n updateBooking,\n} from '@studiometa/productive-core';\n\nimport type { BookingArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatBooking } from '../formatters.js';\nimport { getBookingHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult } from './utils.js';\n\nexport const handleBookings = createResourceHandler<BookingArgs>({\n resource: 'bookings',\n displayName: 'booking',\n actions: ['list', 'get', 'create', 'update'],\n formatter: formatBooking,\n hints: (data, id) => {\n const personId = data.relationships?.person?.data?.id;\n return getBookingHints(id, personId);\n },\n defaultInclude: {\n list: ['person', 'service'],\n get: ['person', 'service'],\n },\n create: {\n required: ['person_id', 'started_on', 'ended_on'],\n validateArgs: (args) => {\n if (!args.service_id && !args.event_id) {\n return inputErrorResult(ErrorMessages.missingBookingTarget());\n }\n return undefined;\n },\n mapOptions: (args) => ({\n personId: args.person_id,\n serviceId: args.service_id ?? '',\n startedOn: args.started_on,\n endedOn: args.ended_on,\n time: args.time,\n note: args.note,\n eventId: args.event_id,\n }),\n },\n update: {\n mapOptions: (args) => ({\n startedOn: args.started_on,\n endedOn: args.ended_on,\n time: args.time,\n note: args.note,\n }),\n },\n executors: {\n list: listBookings,\n get: getBooking,\n create: createBooking,\n update: updateBooking,\n },\n});\n","/**\n * Comments MCP handler.\n */\n\nimport {\n listComments,\n getComment,\n createComment,\n updateComment,\n} from '@studiometa/productive-core';\n\nimport type { CommentArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatComment } from '../formatters.js';\nimport { getCommentHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult } from './utils.js';\n\nexport const handleComments = createResourceHandler<CommentArgs>({\n resource: 'comments',\n actions: ['list', 'get', 'create', 'update'],\n formatter: formatComment,\n hints: (data, id) => {\n const commentableType = data.attributes?.commentable_type as string | undefined;\n let commentableId: string | undefined;\n if (commentableType === 'task') {\n commentableId = data.relationships?.task?.data?.id;\n } else if (commentableType === 'deal') {\n commentableId = data.relationships?.deal?.data?.id;\n } else if (commentableType === 'company') {\n commentableId = data.relationships?.company?.data?.id;\n }\n return getCommentHints(id, commentableType, commentableId);\n },\n defaultInclude: {\n list: ['creator'],\n get: ['creator'],\n },\n create: {\n required: ['body'],\n validateArgs: (args) => {\n if (!args.task_id && !args.deal_id && !args.company_id) {\n return inputErrorResult(ErrorMessages.missingCommentTarget());\n }\n return undefined;\n },\n mapOptions: (args) => ({\n body: args.body,\n hidden: args.hidden,\n taskId: args.task_id,\n dealId: args.deal_id,\n companyId: args.company_id,\n }),\n },\n update: {\n allowedFields: ['body', 'hidden'],\n mapOptions: (args) => ({ body: args.body, hidden: args.hidden }),\n },\n executors: {\n list: listComments,\n get: getComment,\n create: createComment,\n update: updateComment,\n },\n});\n","/**\n * Companies MCP handler.\n *\n * Uses the createResourceHandler factory for the common list/get/create/update/resolve pattern.\n */\n\nimport {\n listCompanies,\n getCompany,\n createCompany,\n updateCompany,\n} from '@studiometa/productive-core';\n\nimport type { CompanyArgs } from './types.js';\n\nimport { formatCompany } from '../formatters.js';\nimport { getCompanyHints } from '../hints.js';\nimport { createResourceHandler, type ResourceHandlerConfig } from './factory.js';\n\n// Type alias for cleaner casts\ntype Executors = ResourceHandlerConfig<CompanyArgs>['executors'];\n\n/**\n * Handle companies resource.\n *\n * Supports: list, get, create, update, resolve\n */\nexport const handleCompanies = createResourceHandler<CompanyArgs>({\n resource: 'companies',\n actions: ['list', 'get', 'create', 'update', 'resolve'],\n formatter: formatCompany,\n hints: (_data, id) => getCompanyHints(id),\n supportsResolve: true,\n create: {\n required: ['name'] as (keyof CompanyArgs)[],\n mapOptions: (args) => ({ name: args.name }),\n },\n update: {\n mapOptions: (args) => ({ name: args.name }),\n },\n executors: {\n list: listCompanies,\n get: getCompany,\n create: createCompany as unknown as Executors['create'],\n update: updateCompany as unknown as Executors['update'],\n },\n});\n","/**\n * Deals MCP handler.\n */\n\nimport {\n listDeals,\n getDeal,\n getDealContext,\n createDeal,\n updateDeal,\n} from '@studiometa/productive-core';\n\nimport type { DealArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatDeal, formatService, formatComment, formatTimeEntry } from '../formatters.js';\nimport { getDealHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nexport const handleDeals = createResourceHandler<DealArgs>({\n resource: 'deals',\n displayName: 'deal',\n actions: ['list', 'get', 'create', 'update', 'resolve', 'context'],\n formatter: formatDeal,\n hints: (_data, id) => getDealHints(id),\n supportsResolve: true,\n defaultInclude: {\n list: ['company', 'deal_status'],\n get: ['company', 'deal_status', 'responsible'],\n },\n create: {\n required: ['name', 'company_id'],\n mapOptions: (args) => ({ name: args.name, companyId: args.company_id }),\n },\n update: {\n mapOptions: (args) => ({ name: args.name }),\n },\n customActions: {\n context: async (args, ctx, execCtx) => {\n if (!args.id) return inputErrorResult(ErrorMessages.missingId('context'));\n const result = await getDealContext({ id: args.id }, execCtx);\n const formatOptions = { ...ctx.formatOptions, included: result.included };\n\n return jsonResult({\n ...formatDeal(result.data.deal, formatOptions),\n services: result.data.services.map((s) => formatService(s, { compact: true })),\n comments: result.data.comments.map((c) => formatComment(c, { compact: true })),\n time_entries: result.data.time_entries.map((t) => formatTimeEntry(t, { compact: true })),\n });\n },\n },\n executors: {\n list: listDeals,\n get: getDeal,\n create: createDeal,\n update: updateDeal,\n },\n});\n","/**\n * Discussions MCP handler.\n */\n\nimport {\n listDiscussions,\n getDiscussion,\n createDiscussion,\n updateDiscussion,\n deleteDiscussion,\n resolveDiscussion,\n reopenDiscussion,\n} from '@studiometa/productive-core';\n\nimport type { DiscussionArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatDiscussion } from '../formatters.js';\nimport { getDiscussionHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\n// Status mapping for discussion list filter\nconst STATUS_MAP: Record<string, string> = {\n active: '1',\n resolved: '2',\n};\n\nexport const handleDiscussions = createResourceHandler<DiscussionArgs>({\n resource: 'discussions',\n actions: ['list', 'get', 'create', 'update', 'delete', 'resolve', 'reopen'],\n formatter: formatDiscussion,\n hints: (data, id) => {\n const pageId = data.relationships?.page?.data?.id;\n return getDiscussionHints(id, pageId);\n },\n listFilterFromArgs: (args) => {\n const filters: Record<string, string> = {};\n if (args.status) {\n // Map user-friendly status to API value\n const mapped = STATUS_MAP[args.status.toLowerCase()];\n if (mapped) filters.status = mapped;\n }\n return filters;\n },\n create: {\n required: ['body', 'page_id'],\n mapOptions: (args) => ({\n body: args.body,\n pageId: args.page_id,\n title: args.title,\n }),\n },\n update: {\n mapOptions: (args) => ({ title: args.title, body: args.body }),\n },\n customActions: {\n resolve: async (args, ctx, execCtx) => {\n if (!args.id) return inputErrorResult(ErrorMessages.missingId('resolve'));\n const result = await resolveDiscussion({ id: args.id }, execCtx);\n return jsonResult({ success: true, ...formatDiscussion(result.data, ctx.formatOptions) });\n },\n reopen: async (args, ctx, execCtx) => {\n if (!args.id) return inputErrorResult(ErrorMessages.missingId('reopen'));\n const result = await reopenDiscussion({ id: args.id }, execCtx);\n return jsonResult({ success: true, ...formatDiscussion(result.data, ctx.formatOptions) });\n },\n },\n executors: {\n list: listDiscussions,\n get: getDiscussion,\n create: createDiscussion,\n update: updateDiscussion,\n delete: deleteDiscussion,\n },\n});\n","/**\n * Help handler - provides detailed documentation for each resource\n */\n\nimport type { ToolResult } from './types.js';\n\nimport { jsonResult } from './utils.js';\n\ninterface ResourceHelp {\n description: string;\n actions: Record<string, string>;\n filters?: Record<string, string>;\n includes?: string[];\n fields?: Record<string, string>;\n examples?: Array<{ description: string; params: Record<string, unknown> }>;\n}\n\nconst RESOURCE_HELP: Record<string, ResourceHelp> = {\n batch: {\n description:\n 'Execute multiple operations in a single call. Operations run in parallel via Promise.all, reducing round-trips for AI agents.',\n actions: {\n run: 'Execute a batch of operations (max 10)',\n },\n fields: {\n operations:\n 'Array of operation objects. Each must have \"resource\" and \"action\", plus any additional params for that resource.',\n },\n examples: [\n {\n description: 'Batch multiple queries',\n params: {\n resource: 'batch',\n action: 'run',\n operations: [\n { resource: 'projects', action: 'get', id: '123' },\n { resource: 'time', action: 'list', filter: { project_id: '123' } },\n { resource: 'services', action: 'list', filter: { project_id: '123' } },\n ],\n },\n },\n {\n description: 'Batch create time entries',\n params: {\n resource: 'batch',\n action: 'run',\n operations: [\n {\n resource: 'time',\n action: 'create',\n service_id: '111',\n date: '2024-01-15',\n time: 60,\n note: 'Morning work',\n },\n {\n resource: 'time',\n action: 'create',\n service_id: '111',\n date: '2024-01-15',\n time: 120,\n note: 'Afternoon work',\n },\n ],\n },\n },\n ],\n },\n\n projects: {\n description: 'Manage projects in Productive.io',\n actions: {\n list: 'List all projects with optional filters',\n get: 'Get a single project by ID (supports PRJ-123, P-123 format)',\n resolve: 'Resolve by project number (PRJ-123, P-123)',\n context:\n 'Get full project context in one call: project details + open tasks + services + recent time entries',\n },\n filters: {\n query: 'Text search on project name',\n project_type: 'Filter by project type: 1=internal, 2=client',\n company_id: 'Filter by company',\n responsible_id: 'Filter by project manager',\n person_id: 'Filter by team member',\n status: 'Filter by status: 1=active, 2=archived',\n },\n fields: {\n id: 'Unique project identifier',\n name: 'Project name',\n project_number: 'Project reference number',\n archived: 'Whether the project is archived',\n budget: 'Project budget amount',\n },\n examples: [\n {\n description: 'Search projects by name',\n params: { resource: 'projects', action: 'list', query: 'website' },\n },\n {\n description: 'List active projects',\n params: { resource: 'projects', action: 'list', filter: { archived: 'false' } },\n },\n {\n description: 'Get project details',\n params: { resource: 'projects', action: 'get', id: '12345' },\n },\n {\n description: 'Get full project context',\n params: { resource: 'projects', action: 'context', id: '12345' },\n },\n ],\n },\n\n tasks: {\n description: 'Manage tasks within projects',\n actions: {\n list: 'List tasks with optional filters',\n get: 'Get a single task by ID with full details (description, comments, etc.)',\n create: 'Create a new task (requires title, project_id, task_list_id)',\n update: 'Update an existing task',\n resolve: 'Resolve by text search',\n context:\n 'Get full task context in one call: task details + comments + time entries + subtasks',\n },\n filters: {\n query: 'Text search on task title',\n project_id: 'Filter by project',\n company_id: 'Filter by company',\n assignee_id: 'Filter by assigned person',\n creator_id: 'Filter by task creator',\n status: 'Filter by status: 1=open, 2=closed (or \"open\", \"closed\", \"all\")',\n task_list_id: 'Filter by task list',\n board_id: 'Filter by board',\n workflow_status_id: 'Filter by workflow status (kanban column)',\n parent_task_id: 'Filter by parent task (for subtasks)',\n overdue_status: 'Filter by overdue: 1=not overdue, 2=overdue',\n due_date_on: 'Filter by exact due date (YYYY-MM-DD)',\n due_date_before: 'Filter by due date before (YYYY-MM-DD)',\n due_date_after: 'Filter by due date after (YYYY-MM-DD)',\n },\n includes: [\n 'project',\n 'project.company',\n 'assignee',\n 'workflow_status',\n 'comments',\n 'attachments',\n 'subtasks',\n ],\n fields: {\n id: 'Unique task identifier',\n title: 'Task title',\n description: 'Full task description (HTML)',\n number: 'Task number within project',\n due_date: 'Due date (YYYY-MM-DD)',\n initial_estimate: 'Estimated time in minutes',\n worked_time: 'Logged time in minutes',\n remaining_time: 'Remaining time in minutes',\n closed: 'Whether the task is closed',\n },\n examples: [\n {\n description: 'Search tasks by title',\n params: { resource: 'tasks', action: 'list', query: 'bug fix' },\n },\n {\n description: 'List open tasks for a project',\n params: {\n resource: 'tasks',\n action: 'list',\n filter: { project_id: '12345', status: 'open' },\n },\n },\n {\n description: 'Get task with comments',\n params: {\n resource: 'tasks',\n action: 'get',\n id: '67890',\n include: ['comments', 'assignee'],\n },\n },\n {\n description: 'Create a task',\n params: {\n resource: 'tasks',\n action: 'create',\n title: 'New task',\n project_id: '12345',\n task_list_id: '111',\n },\n },\n {\n description: 'Get full task context',\n params: { resource: 'tasks', action: 'context', id: '67890' },\n },\n ],\n },\n\n time: {\n description: 'Track time entries against services/tasks',\n actions: {\n list: 'List time entries with optional filters',\n get: 'Get a single time entry by ID',\n create: 'Create a new time entry (requires person_id, service_id, date, time)',\n update: 'Update an existing time entry',\n delete: 'Delete a time entry',\n resolve: 'Resolve related resources (person, project, service)',\n },\n filters: {\n person_id: 'Filter by person (use \"me\" for current user)',\n service_id: 'Filter by service',\n project_id: 'Filter by project',\n task_id: 'Filter by task',\n company_id: 'Filter by company',\n deal_id: 'Filter by deal',\n budget_id: 'Filter by budget',\n after: 'Filter entries after date (YYYY-MM-DD)',\n before: 'Filter entries before date (YYYY-MM-DD)',\n status: 'Filter by approval status: 1=approved, 2=unapproved, 3=rejected',\n billing_type_id: 'Filter by billing type: 1=fixed, 2=actuals, 3=non_billable',\n invoicing_status: 'Filter by invoicing: 1=not_invoiced, 2=drafted, 3=finalized',\n },\n fields: {\n id: 'Unique time entry identifier',\n date: 'Date of the entry (YYYY-MM-DD)',\n time: 'Time in minutes',\n note: 'Description of work done',\n billable_time: 'Billable time in minutes',\n approved: 'Whether the entry is approved',\n },\n examples: [\n {\n description: 'List my time entries this week',\n params: {\n resource: 'time',\n action: 'list',\n filter: { person_id: 'me', after: '2024-01-15', before: '2024-01-21' },\n },\n },\n {\n description: 'Log 2 hours',\n params: {\n resource: 'time',\n action: 'create',\n service_id: '12345',\n date: '2024-01-16',\n time: 120,\n note: 'Development work',\n },\n },\n ],\n },\n\n services: {\n description: 'Budget line items within projects',\n actions: {\n list: 'List services with optional filters',\n get: 'Get a single service by ID',\n },\n filters: {\n project_id: 'Filter by project',\n deal_id: 'Filter by deal',\n task_id: 'Filter by task',\n person_id: 'Filter by person (trackable by)',\n budget_status: 'Filter by budget status: 1=open, 2=delivered',\n billing_type: 'Filter by billing type: 1=fixed, 2=actuals, 3=none',\n time_tracking_enabled: 'Filter by time tracking: true/false',\n },\n fields: {\n id: 'Unique service identifier',\n name: 'Service name',\n budgeted_time: 'Budgeted time in minutes',\n worked_time: 'Logged time in minutes',\n },\n examples: [\n {\n description: 'List services for a deal (budget line items)',\n params: { resource: 'services', action: 'list', filter: { deal_id: '12345' } },\n },\n {\n description: 'List services for a project',\n params: { resource: 'services', action: 'list', filter: { project_id: '12345' } },\n },\n ],\n },\n\n people: {\n description: 'Team members and contacts',\n actions: {\n list: 'List people with optional filters',\n get: 'Get a single person by ID (supports email address)',\n me: 'Get the currently authenticated user',\n resolve: 'Resolve by email address',\n },\n filters: {\n query: 'Text search on name or email',\n status: 'Filter by status: 1=active, 2=deactivated',\n person_type: 'Filter by type: 1=user, 2=contact, 3=placeholder',\n company_id: 'Filter by company',\n project_id: 'Filter by project',\n role_id: 'Filter by role',\n team: 'Filter by team name',\n },\n fields: {\n id: 'Unique person identifier',\n name: 'Full name',\n first_name: 'First name',\n last_name: 'Last name',\n email: 'Email address',\n title: 'Job title',\n active: 'Whether the person is active',\n },\n examples: [\n { description: 'Get current user', params: { resource: 'people', action: 'me' } },\n {\n description: 'Search people by name',\n params: { resource: 'people', action: 'list', query: 'john' },\n },\n {\n description: 'List active team members',\n params: { resource: 'people', action: 'list', filter: { status: 'active' } },\n },\n ],\n },\n\n companies: {\n description: 'Client companies and organizations',\n actions: {\n list: 'List companies with optional filters',\n get: 'Get a single company by ID (supports company name)',\n create: 'Create a new company (requires name)',\n update: 'Update an existing company',\n resolve: 'Resolve by company name',\n },\n filters: {\n query: 'Text search on company name',\n archived: 'Filter by archived status (true/false)',\n },\n fields: {\n id: 'Unique company identifier',\n name: 'Company name',\n billing_name: 'Legal/billing name',\n domain: 'Website domain',\n vat: 'VAT number',\n },\n examples: [\n {\n description: 'Search companies',\n params: { resource: 'companies', action: 'list', query: 'acme' },\n },\n {\n description: 'List active companies',\n params: { resource: 'companies', action: 'list', filter: { archived: 'false' } },\n },\n ],\n },\n\n attachments: {\n description: 'File attachments on tasks, comments, deals, and pages',\n actions: {\n list: 'List attachments with optional filters',\n get: 'Get a single attachment by ID',\n delete: 'Delete an attachment by ID',\n },\n filters: {\n task_id: 'Filter by task',\n comment_id: 'Filter by comment',\n deal_id: 'Filter by deal',\n page_id: 'Filter by page',\n },\n fields: {\n id: 'Unique attachment identifier',\n name: 'File name',\n content_type: 'MIME type (e.g., image/png, application/pdf)',\n size: 'File size in bytes',\n size_human: 'Human-readable file size (e.g., 1.5 MB)',\n url: 'Download URL',\n attachable_type: 'Parent resource type (Task, Comment, Deal, Page)',\n },\n examples: [\n {\n description: 'List attachments on a task',\n params: { resource: 'attachments', action: 'list', filter: { task_id: '12345' } },\n },\n {\n description: 'Get attachment details',\n params: { resource: 'attachments', action: 'get', id: '67890' },\n },\n {\n description: 'Delete an attachment',\n params: { resource: 'attachments', action: 'delete', id: '67890' },\n },\n ],\n },\n\n comments: {\n description: 'Comments on tasks, deals, and other resources',\n actions: {\n list: 'List comments with optional filters',\n get: 'Get a single comment by ID',\n create: 'Create a new comment (requires body and one of: task_id, deal_id, company_id)',\n update: 'Update an existing comment',\n },\n filters: {\n task_id: 'Filter by task',\n deal_id: 'Filter by deal',\n project_id: 'Filter by project',\n page_id: 'Filter by page',\n discussion_id: 'Filter by discussion',\n },\n includes: ['creator', 'task', 'deal'],\n fields: {\n id: 'Unique comment identifier',\n body: 'Comment text (may contain HTML)',\n hidden: 'Boolean — true if hidden from client (default: false)',\n creator: 'Person who created the comment',\n },\n examples: [\n {\n description: 'List comments on a task',\n params: { resource: 'comments', action: 'list', filter: { task_id: '12345' } },\n },\n {\n description: 'Add a comment',\n params: { resource: 'comments', action: 'create', task_id: '12345', body: 'Looking good!' },\n },\n {\n description: 'Add a hidden comment (hidden from client)',\n params: {\n resource: 'comments',\n action: 'create',\n task_id: '12345',\n body: 'Internal note',\n hidden: true,\n },\n },\n ],\n },\n\n timers: {\n description: 'Active time tracking timers',\n actions: {\n list: 'List active timers',\n get: 'Get a single timer by ID',\n start: 'Start a new timer (requires service_id or time_entry_id)',\n stop: 'Stop an active timer by ID',\n },\n filters: {\n person_id: 'Filter by person',\n time_entry_id: 'Filter by time entry',\n },\n fields: {\n id: 'Unique timer identifier',\n started_at: 'When the timer started (ISO 8601)',\n total_time: 'Elapsed time in seconds',\n },\n examples: [\n { description: 'List active timers', params: { resource: 'timers', action: 'list' } },\n {\n description: 'Start timer on service',\n params: { resource: 'timers', action: 'start', service_id: '12345' },\n },\n { description: 'Stop timer', params: { resource: 'timers', action: 'stop', id: '67890' } },\n ],\n },\n\n deals: {\n description:\n 'Sales deals, opportunities, and budgets. Budgets are deals with budget=true — use filter[type]=2 to list only budgets.',\n actions: {\n list: 'List deals with optional filters',\n get: 'Get a single deal by ID (supports D-123, DEAL-123 format)',\n create: 'Create a new deal (requires name, company_id)',\n update: 'Update an existing deal',\n resolve: 'Resolve by deal number (D-123, DEAL-123)',\n context:\n 'Get full deal context in one call: deal details + services + comments + time entries',\n },\n filters: {\n query: 'Text search on deal name',\n company_id: 'Filter by company',\n project_id: 'Filter by project',\n responsible_id: 'Filter by responsible person',\n pipeline_id: 'Filter by pipeline',\n stage_status_id: 'Filter by stage: 1=open, 2=won, 3=lost',\n type: 'Filter by type: 1=deal, 2=budget',\n budget_status: 'Filter by budget status: 1=open, 2=closed',\n },\n includes: ['company', 'deal_status', 'responsible', 'project'],\n fields: {\n id: 'Unique deal identifier',\n name: 'Deal name',\n number: 'Deal number',\n date: 'Deal date',\n budget: 'Whether this deal is a budget (true/false)',\n status: 'Current status (from deal_status)',\n },\n examples: [\n {\n description: 'Search deals',\n params: { resource: 'deals', action: 'list', query: 'website redesign' },\n },\n {\n description: 'List deals for a company',\n params: { resource: 'deals', action: 'list', filter: { company_id: '12345' } },\n },\n {\n description: 'List only budgets',\n params: { resource: 'deals', action: 'list', filter: { type: '2' } },\n },\n {\n description: 'Get full deal context',\n params: { resource: 'deals', action: 'context', id: '12345' },\n },\n ],\n },\n\n bookings: {\n description: 'Resource scheduling and capacity planning',\n actions: {\n list: 'List bookings with optional filters',\n get: 'Get a single booking by ID',\n create:\n 'Create a new booking (requires person_id, started_on, ended_on, and service_id or event_id)',\n update: 'Update an existing booking',\n },\n filters: {\n person_id: 'Filter by person',\n service_id: 'Filter by service',\n project_id: 'Filter by project',\n company_id: 'Filter by company',\n event_id: 'Filter by event',\n after: 'Filter bookings after date (YYYY-MM-DD)',\n before: 'Filter bookings before date (YYYY-MM-DD)',\n booking_type: 'Filter by type: event (absence) or service (budget)',\n draft: 'Filter by tentative status: true/false',\n },\n includes: ['person', 'service', 'event'],\n fields: {\n id: 'Unique booking identifier',\n started_on: 'Start date (YYYY-MM-DD)',\n ended_on: 'End date (YYYY-MM-DD)',\n time: 'Time per day in minutes',\n total_time: 'Total booked time in minutes',\n note: 'Booking note',\n },\n examples: [\n {\n description: 'List my bookings',\n params: { resource: 'bookings', action: 'list', filter: { person_id: 'me' } },\n },\n ],\n },\n\n pages: {\n description: 'Manage pages (wiki/docs) within projects',\n actions: {\n list: 'List pages with optional filters',\n get: 'Get a single page by ID with full details',\n create: 'Create a new page (requires title, project_id)',\n update: 'Update an existing page',\n delete: 'Delete a page',\n },\n filters: {\n project_id: 'Filter by project',\n creator_id: 'Filter by creator',\n parent_page_id: 'Filter by parent page (for sub-pages)',\n },\n fields: {\n id: 'Unique page identifier',\n title: 'Page title',\n body: 'Page body content (HTML)',\n public: 'Whether the page is publicly accessible',\n version_number: 'Current version number',\n parent_page_id: 'Parent page ID (for sub-pages)',\n },\n examples: [\n {\n description: 'List pages for a project',\n params: { resource: 'pages', action: 'list', filter: { project_id: '12345' } },\n },\n {\n description: 'Get page details',\n params: { resource: 'pages', action: 'get', id: '67890' },\n },\n {\n description: 'Create a page',\n params: {\n resource: 'pages',\n action: 'create',\n title: 'Getting Started',\n project_id: '12345',\n },\n },\n {\n description: 'Create a sub-page',\n params: {\n resource: 'pages',\n action: 'create',\n title: 'Sub-section',\n project_id: '12345',\n parent_page_id: '67890',\n },\n },\n {\n description: 'Delete a page',\n params: { resource: 'pages', action: 'delete', id: '67890' },\n },\n ],\n },\n\n discussions: {\n description: 'Manage discussions (comment threads on highlighted page content)',\n actions: {\n list: 'List discussions with optional filters',\n get: 'Get a single discussion by ID',\n create: 'Create a new discussion (requires body, page_id)',\n update: 'Update an existing discussion',\n delete: 'Delete a discussion',\n resolve: 'Resolve a discussion (mark as resolved)',\n reopen: 'Reopen a resolved discussion',\n },\n filters: {\n page_id: 'Filter by page',\n status: 'Filter by status: 1=active, 2=resolved',\n },\n fields: {\n id: 'Unique discussion identifier',\n title: 'Discussion title',\n body: 'Discussion body (HTML)',\n status: 'Status: active or resolved',\n resolved_at: 'When the discussion was resolved',\n },\n examples: [\n {\n description: 'List discussions on a page',\n params: { resource: 'discussions', action: 'list', filter: { page_id: '12345' } },\n },\n {\n description: 'List active discussions',\n params: { resource: 'discussions', action: 'list', status: 'active' },\n },\n {\n description: 'Create a discussion',\n params: {\n resource: 'discussions',\n action: 'create',\n page_id: '12345',\n body: 'Review this section',\n },\n },\n {\n description: 'Resolve a discussion',\n params: { resource: 'discussions', action: 'resolve', id: '67890' },\n },\n {\n description: 'Reopen a discussion',\n params: { resource: 'discussions', action: 'reopen', id: '67890' },\n },\n ],\n },\n\n workflows: {\n description:\n 'Compound workflows that chain multiple resource operations into a single tool call. Use these for common multi-step patterns.',\n actions: {\n complete_task: 'Mark a task closed, optionally post a comment, and stop running timers',\n log_day: 'Create multiple time entries in parallel from a structured list',\n weekly_standup: 'Aggregate completed tasks, time logged, and upcoming deadlines for a week',\n },\n fields: {\n task_id: '(complete_task) Required. Task ID to mark as complete',\n comment: '(complete_task) Optional. Completion comment text to post',\n stop_timer: '(complete_task) Optional. Whether to stop running timers (default: true)',\n entries:\n '(log_day) Required. Array of { project_id, service_id, duration_minutes, note?, date? }',\n date: '(log_day) Optional. Default date for all entries (YYYY-MM-DD, defaults to today)',\n person_id: '(log_day / weekly_standup) Optional. Person to act on (defaults to current user)',\n week_start:\n '(weekly_standup) Optional. Monday date of the target week (defaults to this Monday)',\n },\n examples: [\n {\n description: 'Complete a task with a comment',\n params: {\n resource: 'workflows',\n action: 'complete_task',\n task_id: '12345',\n comment: 'Done! All tests passing.',\n },\n },\n {\n description: 'Complete a task without stopping timers',\n params: {\n resource: 'workflows',\n action: 'complete_task',\n task_id: '12345',\n stop_timer: false,\n },\n },\n {\n description: 'Log a full day across multiple projects',\n params: {\n resource: 'workflows',\n action: 'log_day',\n date: '2024-01-16',\n entries: [\n { project_id: '100', service_id: '111', duration_minutes: 240, note: 'Frontend dev' },\n { project_id: '200', service_id: '222', duration_minutes: 120, note: 'Code review' },\n { project_id: '100', service_id: '333', duration_minutes: 60, note: 'Meetings' },\n ],\n },\n },\n {\n description: 'Get this week standup',\n params: {\n resource: 'workflows',\n action: 'weekly_standup',\n },\n },\n {\n description: 'Get standup for a specific past week',\n params: {\n resource: 'workflows',\n action: 'weekly_standup',\n week_start: '2024-01-15',\n },\n },\n ],\n },\n\n activities: {\n description:\n 'Read-only activity feed — audit log of create/update/delete events across the organization',\n actions: {\n list: 'List recent activities with optional filters',\n },\n filters: {\n event: 'Filter by event type: create, update, delete',\n after: 'Filter to activities after this ISO 8601 timestamp (e.g. 2026-01-01T00:00:00Z)',\n person_id: 'Filter by creator person ID',\n project_id: 'Filter by project ID',\n },\n includes: ['creator'],\n fields: {\n id: 'Unique activity identifier',\n event: 'Event type: create, update, or delete',\n changeset: 'Human-readable summary of field changes (e.g. \"name: null → My Project\")',\n created_at: 'When the activity occurred (ISO 8601)',\n creator_name: 'Full name of the person who triggered the activity (when creator included)',\n },\n examples: [\n {\n description: 'List recent activities',\n params: { resource: 'activities', action: 'list' },\n },\n {\n description: 'List only create events',\n params: { resource: 'activities', action: 'list', filter: { event: 'create' } },\n },\n {\n description: 'List activities after a date',\n params: {\n resource: 'activities',\n action: 'list',\n filter: { after: '2026-02-01T00:00:00Z' },\n },\n },\n {\n description: 'List activities by a specific person',\n params: {\n resource: 'activities',\n action: 'list',\n filter: { person_id: '12345' },\n },\n },\n ],\n },\n\n reports: {\n description: 'Generate various reports (time, budget, project, etc.)',\n actions: {\n get: 'Generate a report (requires report_type)',\n },\n filters: {\n person_id: 'Filter by person',\n project_id: 'Filter by project',\n company_id: 'Filter by company',\n after: 'Filter from date (YYYY-MM-DD)',\n before: 'Filter to date (YYYY-MM-DD)',\n },\n fields: {\n report_type:\n 'Type of report: time_reports, project_reports, budget_reports, person_reports, invoice_reports, payment_reports, service_reports, task_reports, company_reports, deal_reports, timesheet_reports',\n group: 'Grouping dimension (varies by report type)',\n from: 'Start date for date range',\n to: 'End date for date range',\n },\n examples: [\n {\n description: 'Time report by person',\n params: {\n resource: 'reports',\n action: 'get',\n report_type: 'time_reports',\n group: 'person',\n from: '2024-01-01',\n to: '2024-01-31',\n },\n },\n {\n description: 'Project budget report',\n params: {\n resource: 'reports',\n action: 'get',\n report_type: 'budget_reports',\n filter: { project_id: '12345' },\n },\n },\n ],\n },\n};\n\n/**\n * Handle help action - returns documentation for a specific resource\n */\nexport function handleHelp(resource: string): ToolResult {\n const help = RESOURCE_HELP[resource];\n\n if (!help) {\n return jsonResult({\n error: `Unknown resource: ${resource}`,\n available_resources: Object.keys(RESOURCE_HELP),\n _tip: \"Call { action: 'help' } without a resource to see all available resources.\",\n });\n }\n\n return jsonResult({\n resource,\n ...help,\n });\n}\n\n/**\n * Get help for all resources (overview)\n */\nexport function handleHelpOverview(): ToolResult {\n const overview = Object.entries(RESOURCE_HELP).map(([resource, help]) => ({\n resource,\n description: help.description,\n actions: Object.keys(help.actions),\n }));\n\n return jsonResult({\n message: 'Use action=\"help\" with a specific resource for detailed documentation',\n resources: overview,\n _tip: \"Always call { action: 'help', resource: '<name>' } before your first interaction with any resource to learn valid filters, required fields, and examples.\",\n });\n}\n","/**\n * Pages MCP handler.\n *\n * Uses the createResourceHandler factory for the common list/get/create/update/delete pattern.\n */\n\nimport {\n listPages,\n getPage,\n createPage,\n updatePage,\n deletePage,\n} from '@studiometa/productive-core';\n\nimport type { PageArgs } from './types.js';\n\nimport { formatPage } from '../formatters.js';\nimport { getPageHints } from '../hints.js';\nimport { createResourceHandler, type ResourceHandlerConfig } from './factory.js';\n\n// Type alias for cleaner casts\ntype Executors = ResourceHandlerConfig<PageArgs>['executors'];\n\n/**\n * Handle pages resource.\n *\n * Supports: list, get, create, update, delete\n */\nexport const handlePages = createResourceHandler<PageArgs>({\n resource: 'pages',\n actions: ['list', 'get', 'create', 'update', 'delete'],\n formatter: formatPage,\n hints: (_data, id) => getPageHints(id),\n supportsResolve: false,\n create: {\n required: ['title', 'project_id'] as (keyof PageArgs)[],\n mapOptions: (args) => ({\n title: args.title,\n projectId: args.project_id,\n body: args.body,\n parentPageId: args.parent_page_id,\n }),\n },\n update: {\n mapOptions: (args) => ({ title: args.title, body: args.body }),\n },\n executors: {\n list: listPages,\n get: getPage,\n create: createPage as unknown as Executors['create'],\n update: updatePage as unknown as Executors['update'],\n delete: deletePage as unknown as Executors['delete'],\n },\n});\n","/**\n * People MCP handler.\n */\n\nimport { listPeople, getPerson } from '@studiometa/productive-core';\n\nimport type { ProductiveCredentials } from '../auth.js';\nimport type { CommonArgs, HandlerContext, ToolResult } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatListResponse, formatPerson } from '../formatters.js';\nimport { getPersonHints } from '../hints.js';\nimport { handleResolve, type ResolvableResourceType } from './resolve.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nconst VALID_ACTIONS = ['list', 'get', 'me', 'resolve'];\n\nexport async function handlePeople(\n action: string,\n args: CommonArgs & { query?: string; type?: ResolvableResourceType },\n ctx: HandlerContext,\n credentials: ProductiveCredentials,\n): Promise<ToolResult> {\n const { formatOptions, filter, page, perPage } = ctx;\n const { id, query, type } = args;\n\n if (action === 'resolve') {\n return handleResolve({ query, type }, ctx);\n }\n\n const execCtx = ctx.executor();\n\n if (action === 'get') {\n if (!id) return inputErrorResult(ErrorMessages.missingId('get'));\n\n const result = await getPerson({ id }, execCtx);\n const formatted = formatPerson(result.data, formatOptions);\n\n if (ctx.includeHints !== false) {\n return jsonResult({ ...formatted, _hints: getPersonHints(id) });\n }\n return jsonResult(formatted);\n }\n\n if (action === 'me') {\n if (credentials.userId) {\n const result = await getPerson({ id: credentials.userId }, execCtx);\n const formatted = formatPerson(result.data, formatOptions);\n\n if (ctx.includeHints !== false) {\n return jsonResult({ ...formatted, _hints: getPersonHints(credentials.userId) });\n }\n return jsonResult(formatted);\n }\n return jsonResult({\n message: 'User ID not configured. Set userId in credentials to use this action.',\n hint: 'Use action=\"list\" to find people, or configure the user ID in your credentials.',\n organizationId: credentials.organizationId,\n });\n }\n\n if (action === 'list') {\n const result = await listPeople({ page, perPage, additionalFilters: filter }, execCtx);\n\n const response = formatListResponse(result.data, formatPerson, result.meta, formatOptions);\n\n if (result.resolved && Object.keys(result.resolved).length > 0) {\n return jsonResult({ ...response, _resolved: result.resolved });\n }\n return jsonResult(response);\n }\n\n return inputErrorResult(ErrorMessages.invalidAction(action, 'people', VALID_ACTIONS));\n}\n","/**\n * Projects MCP handler.\n *\n * Uses the createResourceHandler factory for the common list/get/resolve pattern.\n */\n\nimport { listProjects, getProject, getProjectContext } from '@studiometa/productive-core';\n\nimport type { CommonArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatProject, formatTask, formatService, formatTimeEntry } from '../formatters.js';\nimport { getProjectHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\n/**\n * Handle projects resource.\n *\n * Supports: list, get, resolve, context\n */\nexport const handleProjects = createResourceHandler<CommonArgs>({\n resource: 'projects',\n actions: ['list', 'get', 'resolve', 'context'],\n formatter: formatProject,\n hints: (_data, id) => getProjectHints(id),\n supportsResolve: true,\n customActions: {\n context: async (args, ctx, execCtx) => {\n if (!args.id) return inputErrorResult(ErrorMessages.missingId('context'));\n const result = await getProjectContext({ id: args.id }, execCtx);\n const formatOptions = { ...ctx.formatOptions, included: result.included };\n\n return jsonResult({\n ...formatProject(result.data.project, ctx.formatOptions),\n tasks: result.data.tasks.map((t) => formatTask(t, { ...formatOptions, compact: true })),\n services: result.data.services.map((s) => formatService(s, { compact: true })),\n time_entries: result.data.time_entries.map((t) => formatTimeEntry(t, { compact: true })),\n });\n },\n },\n executors: {\n list: listProjects,\n get: getProject,\n },\n});\n","/**\n * Reports MCP handler.\n */\n\nimport { getReport, VALID_REPORT_TYPES, type ReportType } from '@studiometa/productive-core';\n\nimport type { CommonArgs, HandlerContext, ToolResult } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\ninterface ReportArgs extends CommonArgs {\n report_type?: string;\n group?: string;\n from?: string;\n to?: string;\n person_id?: string;\n project_id?: string;\n company_id?: string;\n deal_id?: string;\n status?: string;\n}\n\nfunction formatReportData(data: unknown[]): unknown[] {\n return data.map((item: unknown) => {\n const record = item as { id: string; type: string; attributes: Record<string, unknown> };\n return {\n id: record.id,\n type: record.type,\n ...record.attributes,\n };\n });\n}\n\nconst VALID_ACTIONS = ['get'];\n\nexport async function handleReports(\n action: string,\n args: ReportArgs,\n ctx: HandlerContext,\n): Promise<ToolResult> {\n const { filter, page, perPage } = ctx;\n const { report_type, group, from, to, person_id, project_id, company_id, deal_id, status } = args;\n\n if (action !== 'get') {\n return inputErrorResult(ErrorMessages.invalidAction(action, 'reports', VALID_ACTIONS));\n }\n\n if (!report_type) {\n return inputErrorResult(ErrorMessages.missingReportType());\n }\n\n if (!VALID_REPORT_TYPES.includes(report_type as ReportType)) {\n return inputErrorResult(ErrorMessages.invalidReportType(report_type, [...VALID_REPORT_TYPES]));\n }\n\n const execCtx = ctx.executor();\n\n const result = await getReport(\n {\n reportType: report_type as ReportType,\n page,\n perPage,\n group,\n from,\n to,\n personId: person_id,\n projectId: project_id,\n companyId: company_id,\n dealId: deal_id,\n status,\n additionalFilters: filter,\n },\n execCtx,\n );\n\n const formattedData = formatReportData(result.data);\n\n return jsonResult({\n data: formattedData,\n meta: result.meta,\n });\n}\n","/**\n * Schema handler - provides compact, machine-readable resource specifications\n *\n * More concise than action=help, optimized for LLM consumption when only\n * field metadata is needed (filters, create fields, includes).\n */\n\nimport type { ToolResult } from './types.js';\n\nimport { errorResult, jsonResult } from './utils.js';\n\n/**\n * Field specification for create/update operations\n */\nexport interface ResourceFieldSpec {\n required: boolean;\n type: string;\n}\n\n/**\n * Compact schema data for a resource\n */\nexport interface ResourceSchemaData {\n actions: string[];\n filters: Record<string, string>;\n create?: Record<string, ResourceFieldSpec>;\n update?: string[];\n includes?: string[];\n}\n\n/**\n * Schema definitions for all resources.\n *\n * This provides a compact, machine-readable specification of each resource's\n * capabilities. For detailed documentation with examples, use action=help.\n */\nconst RESOURCE_SCHEMAS: Record<string, ResourceSchemaData> = {\n projects: {\n actions: ['list', 'get', 'resolve'],\n filters: {\n query: 'string — text search on project name',\n project_type: '1=internal|2=client',\n company_id: 'string',\n responsible_id: 'string',\n person_id: 'string',\n status: '1=active|2=archived',\n },\n },\n\n time: {\n actions: ['list', 'get', 'create', 'update', 'delete'],\n filters: {\n person_id: \"string — use 'me' for current user\",\n after: 'date YYYY-MM-DD',\n before: 'date YYYY-MM-DD',\n project_id: 'string',\n service_id: 'string',\n task_id: 'string',\n status: '1=approved|2=unapproved|3=rejected',\n },\n create: {\n person_id: { required: true, type: 'string' },\n service_id: { required: true, type: 'string' },\n date: { required: true, type: 'date YYYY-MM-DD' },\n time: { required: true, type: 'minutes integer' },\n note: { required: false, type: 'string' },\n task_id: { required: false, type: 'string' },\n },\n includes: ['person', 'service', 'task'],\n },\n\n tasks: {\n actions: ['list', 'get', 'create', 'update', 'resolve'],\n filters: {\n query: 'string — text search on task title',\n project_id: 'string',\n assignee_id: 'string',\n status: '1=open|2=closed (or \"open\", \"closed\", \"all\")',\n task_list_id: 'string',\n workflow_status_id: 'string — kanban column',\n },\n create: {\n title: { required: true, type: 'string' },\n project_id: { required: true, type: 'string' },\n task_list_id: { required: true, type: 'string' },\n description: { required: false, type: 'string' },\n assignee_id: { required: false, type: 'string' },\n },\n includes: ['project', 'assignee', 'comments', 'subtasks', 'workflow_status'],\n },\n\n services: {\n actions: ['list', 'get'],\n filters: {\n project_id: 'string',\n deal_id: 'string',\n task_id: 'string',\n budget_status: '1=open|2=delivered',\n },\n },\n\n people: {\n actions: ['list', 'get', 'me', 'resolve'],\n filters: {\n query: 'string — text search on name or email',\n status: '1=active|2=deactivated',\n company_id: 'string',\n },\n },\n\n companies: {\n actions: ['list', 'get', 'create', 'update', 'resolve'],\n filters: {\n query: 'string — text search on company name',\n archived: 'boolean',\n },\n create: {\n name: { required: true, type: 'string' },\n },\n },\n\n comments: {\n actions: ['list', 'get', 'create', 'update'],\n filters: {\n task_id: 'string',\n deal_id: 'string',\n page_id: 'string',\n discussion_id: 'string',\n },\n create: {\n body: { required: true, type: 'string' },\n hidden: { required: false, type: 'boolean — true to hide from client' },\n task_id: { required: false, type: 'string — one of task_id, deal_id required' },\n deal_id: { required: false, type: 'string — one of task_id, deal_id required' },\n },\n update: ['body', 'hidden'],\n includes: ['creator'],\n },\n\n attachments: {\n actions: ['list', 'get', 'delete'],\n filters: {\n task_id: 'string',\n comment_id: 'string',\n deal_id: 'string',\n page_id: 'string',\n },\n },\n\n timers: {\n actions: ['list', 'get', 'start', 'stop'],\n filters: {\n person_id: 'string',\n time_entry_id: 'string',\n },\n },\n\n deals: {\n actions: ['list', 'get', 'create', 'update', 'resolve'],\n filters: {\n query: 'string — text search on deal name',\n company_id: 'string',\n type: '1=deal|2=budget',\n stage_status_id: '1=open|2=won|3=lost',\n },\n create: {\n name: { required: true, type: 'string' },\n company_id: { required: true, type: 'string' },\n },\n includes: ['company', 'deal_status'],\n },\n\n bookings: {\n actions: ['list', 'get', 'create', 'update'],\n filters: {\n person_id: 'string',\n after: 'date YYYY-MM-DD',\n before: 'date YYYY-MM-DD',\n service_id: 'string',\n },\n create: {\n person_id: { required: true, type: 'string' },\n started_on: { required: true, type: 'date YYYY-MM-DD' },\n ended_on: { required: true, type: 'date YYYY-MM-DD' },\n service_id: { required: false, type: 'string — one of service_id, event_id required' },\n event_id: { required: false, type: 'string — one of service_id, event_id required' },\n },\n },\n\n pages: {\n actions: ['list', 'get', 'create', 'update', 'delete'],\n filters: {\n project_id: 'string',\n },\n create: {\n title: { required: true, type: 'string' },\n project_id: { required: true, type: 'string' },\n body: { required: false, type: 'string' },\n parent_page_id: { required: false, type: 'string' },\n },\n },\n\n discussions: {\n actions: ['list', 'get', 'create', 'update', 'delete', 'resolve', 'reopen'],\n filters: {\n page_id: 'string',\n status: '1=active|2=resolved',\n },\n create: {\n body: { required: true, type: 'string' },\n page_id: { required: true, type: 'string' },\n },\n },\n\n reports: {\n actions: ['get'],\n filters: {\n person_id: 'string',\n project_id: 'string',\n company_id: 'string',\n after: 'date YYYY-MM-DD',\n before: 'date YYYY-MM-DD',\n },\n create: {\n report_type: { required: true, type: 'time_reports|project_reports|budget_reports|...' },\n from: { required: false, type: 'date YYYY-MM-DD' },\n to: { required: false, type: 'date YYYY-MM-DD' },\n group: { required: false, type: 'string — grouping dimension' },\n },\n },\n};\n\n/**\n * Handle schema action - returns compact specification for a specific resource\n */\nexport function handleSchema(resource: string): ToolResult {\n const schema = RESOURCE_SCHEMAS[resource];\n\n if (!schema) {\n return errorResult(\n `Unknown resource: ${resource}. Valid resources: ${Object.keys(RESOURCE_SCHEMAS).join(', ')}`,\n );\n }\n\n return jsonResult({\n resource,\n ...schema,\n });\n}\n\n/**\n * Get schema overview for all resources\n */\nexport function handleSchemaOverview(): ToolResult {\n const overview = Object.entries(RESOURCE_SCHEMAS).map(([resource, schema]) => ({\n resource,\n actions: schema.actions,\n }));\n\n return jsonResult({\n _tip: 'Use action=\"schema\" with a specific resource for full filter/create/includes spec',\n resources: overview,\n });\n}\n","/**\n * Cross-resource search handler for MCP.\n *\n * Fans out a text query across multiple resource types simultaneously,\n * returning grouped results in a single tool call.\n */\n\nimport type { ProductiveCredentials } from '../auth.js';\nimport type { ToolResult } from './types.js';\n\nimport { errorResult, jsonResult } from './utils.js';\n\n/**\n * Resources that support the query filter for text search\n */\nexport const SEARCHABLE_RESOURCES = ['projects', 'companies', 'people', 'tasks', 'deals'] as const;\nexport type SearchableResource = (typeof SEARCHABLE_RESOURCES)[number];\n\n/**\n * Default resources to search when not specified\n */\nconst DEFAULT_SEARCH_RESOURCES: SearchableResource[] = ['projects', 'companies', 'people', 'tasks'];\n\n/**\n * Type for the execute function injected for delegation\n */\nexport type ExecuteFunction = (\n name: string,\n args: Record<string, unknown>,\n credentials: ProductiveCredentials,\n) => Promise<ToolResult>;\n\n/**\n * Search result for a single resource\n */\ninterface ResourceSearchResult {\n items?: unknown[];\n error?: string;\n}\n\n/**\n * Handle cross-resource search.\n *\n * @param query - Search query text (required)\n * @param resources - Resource types to search (optional, defaults to DEFAULT_SEARCH_RESOURCES)\n * @param credentials - Productive API credentials\n * @param execute - Function to execute tool calls (injected for delegation and testing)\n * @returns Grouped search results across all requested resources\n */\nexport async function handleSearch(\n query: string | undefined,\n resources: string[] | undefined,\n credentials: ProductiveCredentials,\n execute: ExecuteFunction,\n): Promise<ToolResult> {\n // Validate query is non-empty\n if (!query || query.trim() === '') {\n return errorResult('Missing required parameter: query. Provide a non-empty search string.');\n }\n\n const trimmedQuery = query.trim();\n\n // Resolve resources to default if not provided\n const resourcesToSearch =\n resources && resources.length > 0 ? resources : DEFAULT_SEARCH_RESOURCES;\n\n // Validate all resources are searchable\n const invalidResources = resourcesToSearch.filter(\n (r) => !SEARCHABLE_RESOURCES.includes(r as SearchableResource),\n );\n\n if (invalidResources.length > 0) {\n return errorResult(\n `Invalid searchable resources: ${invalidResources.join(', ')}. ` +\n `Valid searchable resources: ${SEARCHABLE_RESOURCES.join(', ')}.`,\n );\n }\n\n // Run all searches in parallel\n const searchPromises = resourcesToSearch.map(\n async (resource): Promise<[string, ResourceSearchResult]> => {\n try {\n const result = await execute(\n 'productive',\n {\n resource,\n action: 'list',\n query: trimmedQuery,\n compact: true,\n per_page: 10,\n },\n credentials,\n );\n\n // Parse JSON from result content\n const textContent = result.content.find((c) => c.type === 'text');\n if (!textContent || textContent.type !== 'text') {\n return [resource, { error: 'No content in response' }];\n }\n\n try {\n const parsed = JSON.parse(textContent.text);\n // Extract items array from the response (formatListResponse uses 'items' key)\n const items = parsed.items ?? parsed.data ?? [];\n return [resource, { items: Array.isArray(items) ? items : [] }];\n } catch {\n return [resource, { error: 'Failed to parse response JSON' }];\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return [resource, { error: message }];\n }\n },\n );\n\n const searchResults = await Promise.all(searchPromises);\n\n // Build results object and count total\n const results: Record<string, unknown[] | { error: string }> = {};\n let totalResults = 0;\n\n for (const [resource, result] of searchResults) {\n if (result.error) {\n results[resource] = { error: result.error };\n } else {\n const items = result.items ?? [];\n results[resource] = items;\n totalResults += items.length;\n }\n }\n\n return jsonResult({\n query: trimmedQuery,\n resources_searched: resourcesToSearch,\n results,\n total_results: totalResults,\n });\n}\n","/**\n * Services MCP handler.\n *\n * Uses the createResourceHandler factory for the common list pattern.\n */\n\nimport { listServices } from '@studiometa/productive-core';\n\nimport type { CommonArgs } from './types.js';\n\nimport { formatService, formatListResponse } from '../formatters.js';\nimport { getServiceListHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { jsonResult } from './utils.js';\n\n/**\n * Handle services resource.\n *\n * Supports: list\n */\nexport const handleServices = createResourceHandler<CommonArgs>({\n resource: 'services',\n actions: ['list'],\n formatter: formatService,\n executors: {\n list: listServices,\n },\n customActions: {\n list: async (args, ctx, execCtx) => {\n const { formatOptions, filter, page, perPage } = ctx;\n const additionalFilters = { ...filter };\n\n const result = await listServices({ page, perPage, additionalFilters }, execCtx);\n\n const response = formatListResponse(result.data, formatService, result.meta, {\n ...formatOptions,\n included: result.included,\n });\n\n // Append hint when not filtering by deal_id\n // Note: ctx.includeHints is only true for get actions, but list-level\n // hints are lightweight suggestions that should always be included\n const hints = getServiceListHints(additionalFilters);\n if (hints) {\n return jsonResult({ ...response, _hints: hints });\n }\n\n return jsonResult(response);\n },\n },\n});\n","/**\n * Summaries MCP handler.\n *\n * Custom handler for dashboard-style summaries (not using createResourceHandler).\n * Routes actions to the appropriate summary executor.\n */\n\nimport {\n getMyDaySummary,\n getProjectHealthSummary,\n getTeamPulseSummary,\n} from '@studiometa/productive-core';\n\nimport type { HandlerContext, ToolResult } from './types.js';\n\nimport { ErrorMessages, UserInputError } from '../errors.js';\nimport { getMyDaySuggestions } from '../suggestions.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nconst VALID_ACTIONS = ['my_day', 'project_health', 'team_pulse', 'help'];\n\ninterface SummaryArgs {\n project_id?: string;\n}\n\n/**\n * Handle summaries resource.\n *\n * Supports: my_day, project_health, team_pulse\n */\nexport async function handleSummaries(\n action: string,\n args: SummaryArgs,\n ctx: HandlerContext,\n): Promise<ToolResult> {\n if (!VALID_ACTIONS.includes(action)) {\n return inputErrorResult(ErrorMessages.invalidAction(action, 'summaries', VALID_ACTIONS));\n }\n\n const execCtx = ctx.executor();\n\n switch (action) {\n case 'my_day': {\n const result = await getMyDaySummary({}, execCtx);\n if (ctx.includeSuggestions !== false) {\n const suggestions = getMyDaySuggestions(result.data);\n if (suggestions.length > 0) {\n return jsonResult({ ...result.data, _suggestions: suggestions });\n }\n }\n return jsonResult(result.data);\n }\n\n case 'project_health': {\n if (!args.project_id) {\n return inputErrorResult(\n new UserInputError('project_id is required for project_health summary', [\n 'Provide the project_id parameter',\n 'You can find project IDs using resource=\"projects\" action=\"list\"',\n 'Or use a project number like \"PRJ-123\"',\n ]),\n );\n }\n\n const result = await getProjectHealthSummary({ projectId: args.project_id }, execCtx);\n return jsonResult(result.data);\n }\n\n case 'team_pulse': {\n const result = await getTeamPulseSummary({}, execCtx);\n return jsonResult(result.data);\n }\n\n case 'help': {\n return jsonResult({\n resource: 'summaries',\n description: 'Dashboard-style summaries that aggregate data from multiple resources',\n actions: {\n my_day: {\n description: 'Personal dashboard for the current user',\n parameters: {},\n returns: {\n tasks: 'Open and overdue tasks assigned to you',\n time: 'Time entries logged today',\n timers: 'Currently running timers',\n },\n },\n project_health: {\n description: 'Project status with budget burn and task stats',\n parameters: {\n project_id: 'Required. Project ID or project number (e.g., PRJ-123)',\n },\n returns: {\n project: 'Project details',\n tasks: 'Open and overdue task counts',\n budget: 'Budget burn rate by service',\n recent_activity: 'Time tracking activity in last 7 days',\n },\n },\n team_pulse: {\n description: 'Team-wide time tracking activity for today',\n parameters: {},\n returns: {\n team: 'Counts of active users, those tracking time, and with timers',\n people: 'Per-person breakdown of time logged and active timers',\n },\n },\n },\n });\n }\n\n default: {\n return inputErrorResult(ErrorMessages.invalidAction(action, 'summaries', VALID_ACTIONS));\n }\n }\n}\n","/**\n * Tasks MCP handler.\n */\n\nimport {\n listTasks,\n getTask,\n getTaskContext,\n createTask,\n updateTask,\n} from '@studiometa/productive-core';\n\nimport type { TaskArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatTask, formatComment, formatTimeEntry } from '../formatters.js';\nimport { getTaskHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nexport const handleTasks = createResourceHandler<TaskArgs>({\n resource: 'tasks',\n displayName: 'task',\n actions: ['list', 'get', 'create', 'update', 'resolve', 'context'],\n formatter: formatTask,\n hints: (data, id) => {\n const serviceId = data.relationships?.service?.data?.id;\n return getTaskHints(id, serviceId);\n },\n supportsResolve: true,\n resolveArgsFromArgs: (args) => ({ project_id: args.project_id }),\n defaultInclude: {\n list: ['project', 'project.company'],\n get: ['project', 'project.company'],\n },\n create: {\n required: ['title', 'project_id', 'task_list_id'],\n mapOptions: (args) => ({\n title: args.title,\n projectId: args.project_id,\n taskListId: args.task_list_id,\n assigneeId: args.assignee_id,\n description: args.description,\n }),\n },\n update: {\n mapOptions: (args) => ({\n title: args.title,\n description: args.description,\n assigneeId: args.assignee_id,\n }),\n },\n customActions: {\n context: async (args, ctx, execCtx) => {\n if (!args.id) return inputErrorResult(ErrorMessages.missingId('context'));\n const result = await getTaskContext({ id: args.id }, execCtx);\n const formatOptions = { ...ctx.formatOptions, included: result.included };\n\n return jsonResult({\n ...formatTask(result.data.task, formatOptions),\n comments: result.data.comments.map((c) => formatComment(c, { compact: true })),\n time_entries: result.data.time_entries.map((t) => formatTimeEntry(t, { compact: true })),\n subtasks: result.data.subtasks.map((s) =>\n formatTask(s, { ...formatOptions, compact: true }),\n ),\n });\n },\n },\n executors: {\n list: listTasks,\n get: getTask,\n create: createTask,\n update: updateTask,\n },\n});\n","/**\n * Time entries MCP handler.\n *\n * Thin adapter that delegates business logic to core executors\n * and handles MCP-specific concerns (hints, error formatting, JSON results).\n */\n\nimport {\n listTimeEntries,\n getTimeEntry,\n createTimeEntry,\n updateTimeEntry,\n deleteTimeEntry,\n} from '@studiometa/productive-core';\n\nimport type { TimeArgs } from './types.js';\n\nimport { ErrorMessages, UserInputError } from '../errors.js';\nimport { formatTimeEntry } from '../formatters.js';\nimport { getTimeEntryHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nexport const handleTime = createResourceHandler<TimeArgs>({\n resource: 'time',\n displayName: 'time entry',\n actions: ['list', 'get', 'create', 'update', 'delete', 'resolve'],\n formatter: formatTimeEntry,\n hints: (data, id) => {\n const serviceId = data.relationships?.service?.data?.id;\n return getTimeEntryHints(id, undefined, serviceId);\n },\n supportsResolve: true,\n resolveArgsFromArgs: (args) => ({ project_id: args.project_id }),\n customActions: {\n create: async (args, ctx, execCtx) => {\n // Validate required fields (person_id is optional — defaults to current user)\n const missingFields = (['service_id', 'time', 'date'] as (keyof TimeArgs)[]).filter(\n (field) => !args[field],\n );\n if (missingFields.length > 0) {\n return inputErrorResult(\n ErrorMessages.missingRequiredFields('time entry', missingFields as string[]),\n );\n }\n\n // Default person_id to the current user when not provided\n const personId = args.person_id ?? execCtx.config.userId;\n if (!personId) {\n return inputErrorResult(\n new UserInputError(\n 'person_id is required (could not auto-resolve: userId not configured)',\n ['Provide person_id explicitly', 'Or configure userId in your credentials'],\n ),\n );\n }\n\n const result = await createTimeEntry(\n {\n personId,\n serviceId: args.service_id as string,\n time: args.time as number,\n date: args.date as string,\n note: args.note ?? undefined,\n taskId: args.task_id,\n projectId: args.project_id,\n },\n execCtx,\n );\n\n return jsonResult({ success: true, ...formatTimeEntry(result.data, ctx.formatOptions) });\n },\n },\n update: {\n mapOptions: (args) => ({\n time: args.time ?? undefined,\n date: args.date ?? undefined,\n note: args.note ?? undefined,\n }),\n },\n executors: {\n list: listTimeEntries,\n get: getTimeEntry,\n create: createTimeEntry,\n update: updateTimeEntry,\n delete: deleteTimeEntry,\n },\n});\n","/**\n * Timers MCP handler.\n */\n\nimport { listTimers, getTimer, startTimer, stopTimer } from '@studiometa/productive-core';\n\nimport type { TimerArgs } from './types.js';\n\nimport { ErrorMessages } from '../errors.js';\nimport { formatTimer } from '../formatters.js';\nimport { getTimerHints } from '../hints.js';\nimport { createResourceHandler } from './factory.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nexport const handleTimers = createResourceHandler<TimerArgs>({\n resource: 'timers',\n actions: ['list', 'get', 'start', 'stop'],\n formatter: formatTimer,\n hints: (_data, id) => getTimerHints(id),\n customActions: {\n start: async (args, ctx, execCtx) => {\n if (!args.service_id && !args.time_entry_id) {\n return inputErrorResult(ErrorMessages.missingServiceForTimer());\n }\n const result = await startTimer(\n { serviceId: args.service_id, timeEntryId: args.time_entry_id },\n execCtx,\n );\n return jsonResult({ success: true, ...formatTimer(result.data, ctx.formatOptions) });\n },\n create: async (args, ctx, execCtx) => {\n // 'create' is an alias for 'start'\n if (!args.service_id && !args.time_entry_id) {\n return inputErrorResult(ErrorMessages.missingServiceForTimer());\n }\n const result = await startTimer(\n { serviceId: args.service_id, timeEntryId: args.time_entry_id },\n execCtx,\n );\n return jsonResult({ success: true, ...formatTimer(result.data, ctx.formatOptions) });\n },\n stop: async (args, ctx, execCtx) => {\n if (!args.id) return inputErrorResult(ErrorMessages.missingId('stop'));\n const result = await stopTimer({ id: args.id }, execCtx);\n return jsonResult({ success: true, ...formatTimer(result.data, ctx.formatOptions) });\n },\n },\n executors: {\n list: listTimers,\n get: getTimer,\n },\n});\n","/**\n * Valid include values per resource.\n *\n * Sourced from help.ts and schema.ts include lists.\n * Resources not listed here have no include validation (pass-through).\n */\n\nexport const VALID_INCLUDES: Record<string, string[]> = {\n activities: ['creator'],\n tasks: [\n 'project',\n 'project.company',\n 'assignee',\n 'workflow_status',\n 'comments',\n 'attachments',\n 'subtasks',\n ],\n comments: ['creator', 'task', 'deal'],\n deals: ['company', 'deal_status', 'responsible', 'project'],\n bookings: ['person', 'service', 'event'],\n time: ['person', 'service', 'task'],\n};\n\nexport interface ValidateIncludesResult {\n valid: string[];\n invalid: string[];\n suggestions: Record<string, string>;\n}\n\n/**\n * Known misleading include values and their suggestions.\n * These are values that agents commonly try that don't exist.\n */\nconst KNOWN_SUGGESTIONS: Record<string, string> = {\n notes: 'Use resource=comments to fetch comments on a resource',\n services: 'Use resource=services with filter.deal_id or filter.project_id to list services',\n time_entries:\n 'Use resource=time with a filter (e.g. filter.task_id, filter.project_id) to list time entries',\n time: 'Use resource=time with a filter (e.g. filter.task_id) to list time entries',\n user: 'Use \"assignee\" or \"person\" instead',\n author: 'Use \"creator\" instead',\n owner: 'Use \"responsible\" or \"assignee\" instead',\n company: 'Use \"project.company\" to include the project\\'s company on tasks',\n status: 'Use \"workflow_status\" to include the workflow/kanban status on tasks',\n};\n\n/**\n * Validate include values for a given resource.\n *\n * Returns the valid and invalid values, plus suggestions for invalid values.\n * If the resource is not in VALID_INCLUDES, skips validation (returns all as valid).\n */\nexport function validateIncludes(\n resource: string,\n includes: string[],\n): ValidateIncludesResult | null {\n const validSet = VALID_INCLUDES[resource];\n\n // Resource has no include validation — skip\n if (!validSet) {\n return null;\n }\n\n const valid: string[] = [];\n const invalid: string[] = [];\n const suggestions: Record<string, string> = {};\n\n for (const inc of includes) {\n if (validSet.includes(inc)) {\n valid.push(inc);\n } else {\n invalid.push(inc);\n if (KNOWN_SUGGESTIONS[inc]) {\n suggestions[inc] = KNOWN_SUGGESTIONS[inc];\n } else {\n suggestions[inc] = `Valid includes for ${resource}: ${validSet.join(', ')}`;\n }\n }\n }\n\n return { valid, invalid, suggestions };\n}\n","/**\n * Workflows MCP handler.\n *\n * Custom handler for compound workflows that chain multiple executors.\n * NOT using createResourceHandler — standalone routing like summaries.ts.\n *\n * Supported actions:\n * - complete_task: Mark task closed, optionally comment and stop timers\n * - log_day: Create multiple time entries in parallel from a structured list\n * - weekly_standup: Aggregate completed tasks, time logged, and upcoming deadlines\n */\n\nimport { completeTask, logDay, weeklyStandup } from '@studiometa/productive-core';\n\nimport type { HandlerContext, ToolResult } from './types.js';\n\nimport { ErrorMessages, UserInputError } from '../errors.js';\nimport { inputErrorResult, jsonResult } from './utils.js';\n\nconst VALID_ACTIONS = ['complete_task', 'log_day', 'weekly_standup', 'help'];\n\ninterface CompleteTaskArgs {\n task_id?: string;\n comment?: string;\n stop_timer?: boolean;\n}\n\ninterface LogDayEntryArg {\n project_id: string;\n service_id: string;\n duration_minutes: number;\n note?: string;\n date?: string;\n}\n\ninterface LogDayArgs {\n entries?: Array<Record<string, unknown>>;\n date?: string;\n person_id?: string;\n}\n\ninterface WeeklyStandupArgs {\n person_id?: string;\n week_start?: string;\n}\n\ntype WorkflowArgs = CompleteTaskArgs & LogDayArgs & WeeklyStandupArgs;\n\n/**\n * Handle workflows resource.\n *\n * Supports: complete_task, log_day, weekly_standup\n */\nexport async function handleWorkflows(\n action: string,\n args: WorkflowArgs,\n ctx: HandlerContext,\n): Promise<ToolResult> {\n if (!VALID_ACTIONS.includes(action)) {\n return inputErrorResult(ErrorMessages.invalidAction(action, 'workflows', VALID_ACTIONS));\n }\n\n const execCtx = ctx.executor();\n\n switch (action) {\n case 'complete_task': {\n if (!args.task_id) {\n return inputErrorResult(\n new UserInputError('task_id is required for complete_task workflow', [\n 'Provide the task_id parameter (numeric task ID)',\n 'You can find task IDs using resource=\"tasks\" action=\"list\"',\n ]),\n );\n }\n\n const result = await completeTask(\n {\n taskId: args.task_id,\n comment: args.comment,\n stopTimer: args.stop_timer,\n },\n execCtx,\n );\n return jsonResult(result.data);\n }\n\n case 'log_day': {\n if (!args.entries || !Array.isArray(args.entries) || args.entries.length === 0) {\n return inputErrorResult(\n new UserInputError(\n 'entries is required and must be a non-empty array for log_day workflow',\n [\n 'Provide entries as an array of { project_id, service_id, duration_minutes, note?, date? }',\n 'Example: { \"entries\": [{ \"project_id\": \"123\", \"service_id\": \"456\", \"duration_minutes\": 120, \"note\": \"Development\" }] }',\n 'You can find service IDs using resource=\"services\" action=\"list\" with filter.project_id',\n ],\n ),\n );\n }\n\n const mappedEntries = (args.entries as Array<Record<string, unknown>>).map((e) => {\n const entry = e as unknown as LogDayEntryArg;\n return {\n project_id: String(entry.project_id),\n service_id: String(entry.service_id),\n duration_minutes: Number(entry.duration_minutes),\n note: entry.note != null ? String(entry.note) : undefined,\n date: entry.date != null ? String(entry.date) : undefined,\n };\n });\n\n const result = await logDay(\n {\n entries: mappedEntries,\n date: args.date,\n personId: args.person_id,\n },\n execCtx,\n );\n return jsonResult(result.data);\n }\n\n case 'weekly_standup': {\n const result = await weeklyStandup(\n {\n personId: args.person_id,\n weekStart: args.week_start,\n },\n execCtx,\n );\n return jsonResult(result.data);\n }\n\n case 'help': {\n return jsonResult({\n resource: 'workflows',\n description:\n 'Compound workflows that chain multiple resource operations into a single tool call',\n actions: {\n complete_task: {\n description:\n 'Mark a task as complete, optionally post a comment and stop running timers',\n parameters: {\n task_id: 'Required. The task ID to complete',\n comment: 'Optional. A completion comment to post on the task',\n stop_timer: 'Optional. Whether to stop running timers (default: true)',\n },\n returns: {\n task: 'Updated task info (id, title, closed status)',\n comment_posted: 'Whether the comment was posted',\n comment_id: 'ID of the created comment (if posted)',\n timers_stopped: 'Number of timers stopped',\n errors: 'Any sub-step errors (partial results are still returned)',\n },\n },\n log_day: {\n description: 'Create multiple time entries in parallel from a structured list',\n parameters: {\n entries:\n 'Required. Array of { project_id, service_id, duration_minutes, note?, date? }',\n date: 'Optional. Default date for all entries (YYYY-MM-DD, defaults to today)',\n person_id: 'Optional. Person to log for (defaults to current user)',\n },\n returns: {\n entries: 'Per-entry results with success/failure status',\n succeeded: 'Number of entries successfully created',\n failed: 'Number of entries that failed',\n total_minutes_logged: 'Sum of minutes for successful entries',\n },\n },\n weekly_standup: {\n description:\n 'Aggregate a weekly standup: completed tasks, time logged, and upcoming deadlines',\n parameters: {\n person_id: 'Optional. Person to generate standup for (defaults to current user)',\n week_start: 'Optional. ISO date for Monday of the week (defaults to this Monday)',\n },\n returns: {\n completed_tasks: 'Tasks closed this week (count + list)',\n time_logged: 'Total minutes and breakdown by project',\n upcoming_deadlines: 'Open tasks due in the next 7 days',\n },\n },\n },\n });\n }\n\n default: {\n return inputErrorResult(ErrorMessages.invalidAction(action, 'workflows', VALID_ACTIONS));\n }\n }\n}\n","/**\n * Tool execution handlers for Productive MCP server\n * These are shared between stdio and HTTP transports\n *\n * Single consolidated tool for minimal token overhead:\n * - productive: resource + action based API\n */\n\nimport { ProductiveApi } from '@studiometa/productive-api';\nimport { fromHandlerContext, RESOURCES } from '@studiometa/productive-core';\n\nimport type { ProductiveCredentials } from '../auth.js';\nimport type { McpFormatOptions } from '../formatters.js';\nimport type { HandlerContext, ToolResult } from './types.js';\n\nimport { ErrorMessages, UserInputError, isUserInputError } from '../errors.js';\nimport { handleActivities } from './activities.js';\nimport { handleAttachments } from './attachments.js';\nimport { handleBatch } from './batch.js';\nimport { handleBookings } from './bookings.js';\nimport { handleComments } from './comments.js';\nimport { handleCompanies } from './companies.js';\nimport { handleDeals } from './deals.js';\nimport { handleDiscussions } from './discussions.js';\nimport { handleHelp, handleHelpOverview } from './help.js';\nimport { handlePages } from './pages.js';\nimport { handlePeople } from './people.js';\n// Resource handlers\nimport { handleProjects } from './projects.js';\nimport { handleReports } from './reports.js';\nimport { type ResolvableResourceType } from './resolve.js';\nimport { handleSchema, handleSchemaOverview } from './schema.js';\nimport { handleSearch } from './search.js';\nimport { handleServices } from './services.js';\nimport { handleSummaries } from './summaries.js';\nimport { handleTasks } from './tasks.js';\nimport { handleTime } from './time.js';\nimport { handleTimers } from './timers.js';\nimport { errorResult, formatError, inputErrorResult, toStringFilter } from './utils.js';\nimport { VALID_INCLUDES, validateIncludes } from './valid-includes.js';\nimport { handleWorkflows } from './workflows.js';\n\n// Re-export types\nexport type { ToolResult } from './types.js';\n\n/** Valid resources for the productive tool (derived from core constants) */\nconst VALID_RESOURCES = [...RESOURCES];\n\n/** Default page size for MCP (smaller than CLI to reduce token usage) */\nconst DEFAULT_PER_PAGE = 20;\n\n/**\n * Args interface for the consolidated tool\n */\ninterface ProductiveArgs {\n resource: string;\n action: string;\n id?: string;\n filter?: Record<string, unknown>;\n page?: number;\n per_page?: number;\n compact?: boolean;\n include?: string[];\n query?: string;\n resources?: string[];\n // Common fields\n person_id?: string;\n service_id?: string;\n task_id?: string;\n company_id?: string;\n time?: number;\n date?: string;\n note?: string;\n // Task fields\n title?: string;\n project_id?: string;\n task_list_id?: string;\n description?: string;\n assignee_id?: string;\n // Company fields\n name?: string;\n // Comment fields\n body?: string;\n deal_id?: string;\n // Attachment fields\n comment_id?: string;\n // Timer fields\n time_entry_id?: string;\n // Page fields\n parent_page_id?: string;\n page_id?: string;\n // Booking fields\n started_on?: string;\n ended_on?: string;\n event_id?: string;\n // Report fields\n report_type?: string;\n group?: string;\n from?: string;\n to?: string;\n status?: string;\n // Batch fields\n operations?: Array<Record<string, unknown>>;\n // Workflow fields\n entries?: Array<Record<string, unknown>>;\n comment?: string;\n stop_timer?: boolean;\n week_start?: string;\n}\n\n/**\n * Route to the appropriate resource handler.\n * Extracted from executeToolWithCredentials to keep cyclomatic complexity manageable.\n */\nasync function routeToHandler(\n resource: string,\n action: string,\n restArgs: Record<string, unknown>,\n resolveArgs: { query?: string; type?: ResolvableResourceType },\n ctx: HandlerContext,\n credentials: ProductiveCredentials,\n): Promise<ToolResult> {\n switch (resource) {\n case 'projects':\n return await handleProjects(action, { ...restArgs, ...resolveArgs }, ctx);\n\n case 'time':\n return await handleTime(action, { ...restArgs, ...resolveArgs }, ctx);\n\n case 'tasks':\n return await handleTasks(action, { ...restArgs, ...resolveArgs }, ctx);\n\n case 'services':\n return await handleServices(action, restArgs, ctx);\n\n case 'people':\n return await handlePeople(action, { ...restArgs, ...resolveArgs }, ctx, credentials);\n\n case 'companies':\n return await handleCompanies(action, { ...restArgs, ...resolveArgs }, ctx);\n\n case 'comments':\n return await handleComments(action, restArgs, ctx);\n\n case 'attachments':\n return await handleAttachments(action, restArgs, ctx);\n\n case 'timers':\n return await handleTimers(action, restArgs, ctx);\n\n case 'deals':\n return await handleDeals(action, { ...restArgs, ...resolveArgs }, ctx);\n\n case 'bookings':\n return await handleBookings(action, restArgs, ctx);\n\n case 'pages':\n return await handlePages(action, restArgs, ctx);\n\n case 'discussions':\n return await handleDiscussions(action, restArgs, ctx);\n\n case 'activities':\n return await handleActivities(action, restArgs, ctx);\n\n case 'reports':\n return await handleReports(action, restArgs, ctx);\n\n case 'summaries':\n return await handleSummaries(action, restArgs, ctx);\n\n case 'workflows':\n return await handleWorkflows(action, restArgs, ctx);\n\n case 'budgets':\n return inputErrorResult(\n new UserInputError(\n 'The \"budgets\" resource has been removed. Budgets are deals with type=2.',\n [\n 'Use resource=\"deals\" with filter[type]=\"2\" to list only budgets',\n 'To create a budget: resource=\"deals\" action=\"create\" with budget=true',\n 'Use action=\"help\" resource=\"deals\" for full documentation',\n ],\n ),\n );\n\n case 'docs':\n return inputErrorResult(\n new UserInputError('Unknown resource \"docs\". Did you mean \"pages\"?', [\n 'Use resource=\"pages\" to access Productive pages/documents',\n 'Use action=\"list\" to list all pages',\n 'Use action=\"help\" resource=\"pages\" for full documentation',\n ]),\n );\n\n default:\n return inputErrorResult(ErrorMessages.unknownResource(resource, VALID_RESOURCES));\n }\n}\n\n/**\n * Execute a tool with the given credentials and arguments\n */\nexport async function executeToolWithCredentials(\n name: string,\n args: Record<string, unknown>,\n credentials: ProductiveCredentials,\n): Promise<ToolResult> {\n // Handle the single consolidated tool\n if (name !== 'productive') {\n return errorResult(`Unknown tool: ${name}`);\n }\n\n // Handle batch resource BEFORE initializing API client\n // Batch delegates back to executeToolWithCredentials for each operation\n const typedArgs = args as unknown as ProductiveArgs;\n if (typedArgs.resource === 'batch') {\n return handleBatch(typedArgs.operations, credentials, executeToolWithCredentials);\n }\n\n // Detect common mistake: passing \"params\" instead of \"filter\"\n if ((args as Record<string, unknown>).params !== undefined) {\n return inputErrorResult(\n new UserInputError('Unknown field \"params\". Use \"filter\" instead.', [\n 'Example: { \"filter\": { \"assignee_id\": \"me\" } }',\n 'The MCP tool uses \"filter\" for query parameters, not \"params\"',\n ]),\n );\n }\n\n const {\n resource,\n action,\n filter,\n page,\n per_page,\n compact,\n include,\n query,\n resources,\n no_hints,\n type,\n ...restArgs\n } = typedArgs as ProductiveArgs & { no_hints?: boolean; type?: ResolvableResourceType };\n\n // Handle cross-resource search BEFORE API client initialization\n // (search delegates to executeToolWithCredentials for each resource)\n if (resource === 'search') {\n return await handleSearch(query, resources, credentials, executeToolWithCredentials);\n }\n\n // Initialize API client with provided credentials\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 // Default compact to false for 'get' action (single resource), true for 'list'\n const isCompact = compact ?? action !== 'get';\n const formatOptions: McpFormatOptions = { compact: isCompact };\n let stringFilter = toStringFilter(filter);\n const perPage = per_page ?? DEFAULT_PER_PAGE;\n\n // Add query to filter if provided (for text search)\n if (query) {\n stringFilter = { ...stringFilter, query };\n }\n\n // Hints are included for 'get' actions (not compact), suggestions for any action.\n // Both are disabled with no_hints: true.\n const includeHints = no_hints !== true && action === 'get' && !isCompact;\n const includeSuggestions = no_hints !== true;\n\n // Validate include values against known-valid includes for this resource.\n // Do this before building the handler context so we can return early.\n if (include && include.length > 0) {\n const includeValidation = validateIncludes(resource, include);\n if (includeValidation && includeValidation.invalid.length > 0) {\n const { invalid, valid, suggestions } = includeValidation;\n const hintLines: string[] = [\n `Invalid include value${invalid.length > 1 ? 's' : ''}: ${invalid.join(', ')}`,\n `Valid includes for ${resource}: ${(VALID_INCLUDES[resource] ?? []).join(', ')}`,\n ];\n for (const [value, suggestion] of Object.entries(suggestions)) {\n hintLines.push(`\"${value}\": ${suggestion}`);\n }\n if (valid.length > 0) {\n hintLines.push(\n `The following includes are valid and will be used if you remove the invalid ones: ${valid.join(', ')}`,\n );\n }\n return inputErrorResult(\n new UserInputError(\n `Invalid include value${invalid.length > 1 ? 's' : ''} for resource \"${resource}\": ${invalid.join(', ')}`,\n hintLines,\n ),\n );\n }\n }\n\n // Build handler context — api is not exposed directly.\n // Handlers access executors via ctx.executor() which creates an ExecutorContext.\n const execCtx = fromHandlerContext({ api }, { userId: credentials.userId });\n const ctx: HandlerContext = {\n formatOptions,\n filter: stringFilter,\n page,\n perPage,\n include,\n includeHints,\n includeSuggestions,\n executor: () => execCtx,\n };\n\n try {\n // Intercept common wrong action patterns and return helpful guidance\n\n // action=\"search\" on a specific resource — agents should use action=\"list\" with query, or resource=\"search\"\n if (action === 'search' && resource !== 'search') {\n return inputErrorResult(\n new UserInputError(\n `action=\"search\" is not supported on resource=\"${resource}\". Use action=\"list\" with a query parameter for text filtering, or use resource=\"search\" for cross-resource search.`,\n [\n `Use resource=\"${resource}\" action=\"list\" with query=\"<your search terms>\" to filter ${resource}`,\n 'Use resource=\"search\" action=\"run\" with query=\"<your search terms>\" to search across all resources',\n `Use action=\"help\" resource=\"${resource}\" to see all supported actions and filters`,\n ],\n ),\n );\n }\n\n // action starts with \"get_\" — agents using snake_case function-style naming\n if (action.startsWith('get_')) {\n const suggestedResource = action.replace(/^get_/, '').replace(/_/g, ' ');\n return inputErrorResult(\n new UserInputError(\n `action=\"${action}\" is not valid. Actions use simple verbs like \"list\", \"get\", \"create\", not function-style names.`,\n [\n `To retrieve a single item, use action=\"get\" with an id parameter`,\n `To retrieve multiple items, use action=\"list\" (e.g. resource=\"${resource || suggestedResource}\" action=\"list\")`,\n `Use action=\"help\" resource=\"${resource || 'tasks'}\" to see all supported actions for a resource`,\n ],\n ),\n );\n }\n\n // Handle help action first (doesn't need API)\n // Exception: summaries has its own help handler\n if (action === 'help' && resource !== 'summaries') {\n return resource ? handleHelp(resource) : handleHelpOverview();\n }\n\n // Handle schema action (doesn't need API)\n if (action === 'schema') {\n return resource ? handleSchema(resource) : handleSchemaOverview();\n }\n\n // Route to appropriate resource handler\n const resolveArgs = { query, type };\n return await routeToHandler(resource, action, restArgs, resolveArgs, ctx, credentials);\n } catch (error) {\n // Handle UserInputError with formatted hints\n if (isUserInputError(error)) {\n return formatError(error);\n }\n\n // Handle API errors with status codes\n const message = error instanceof Error ? error.message : String(error);\n const statusMatch = message.match(/(\\d{3})/);\n if (statusMatch) {\n const statusCode = Number.parseInt(statusMatch[1], 10);\n return inputErrorResult(ErrorMessages.apiError(statusCode, message));\n }\n\n return errorResult(message);\n }\n}\n"],"mappings":"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"}
|