@studiometa/productive-mcp 0.10.5 → 0.10.7

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/http.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as INSTRUCTIONS, t as VERSION } from "./version-4lpeoWqA.js";
2
- import { t as executeToolWithCredentials } from "./handlers-BvwBrRHp.js";
1
+ import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-zLvx5_bN.js";
2
+ import { t as executeToolWithCredentials } from "./handlers-CzOijI7B.js";
3
3
  import "./handlers.js";
4
4
  import { TOOLS } from "./tools.js";
5
5
  import { parseAuthHeader } from "./auth.js";
@@ -44,7 +44,10 @@ function handleInitialize() {
44
44
  name: "productive-mcp",
45
45
  version: VERSION
46
46
  },
47
- capabilities: { tools: {} },
47
+ capabilities: {
48
+ tools: {},
49
+ resources: {}
50
+ },
48
51
  instructions: INSTRUCTIONS
49
52
  };
50
53
  }
@@ -117,6 +120,13 @@ function createHttpApp() {
117
120
  const { name, arguments: args } = params;
118
121
  return jsonRpcSuccess(await executeToolWithCredentials(name, args || {}, credentials), id ?? null);
119
122
  }
123
+ if (method === "resources/list") return jsonRpcSuccess({ resources: listResources() }, id ?? null);
124
+ if (method === "resources/templates/list") return jsonRpcSuccess({ resourceTemplates: listResourceTemplates() }, id ?? null);
125
+ if (method === "resources/read") {
126
+ const { uri } = params ?? {};
127
+ if (!uri) return jsonRpcError(-32602, "Invalid params: uri is required", id ?? null);
128
+ return jsonRpcSuccess(await readResource(uri, credentials), id ?? null);
129
+ }
120
130
  return jsonRpcError(-32601, `Method not found: ${method}`, id ?? null);
121
131
  } catch (error) {
122
132
  return jsonRpcError(-32603, `Internal error: ${error instanceof Error ? error.message : String(error)}`, id ?? null);
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","names":[],"sources":["../src/http.ts"],"sourcesContent":["/**\n * HTTP transport handlers for Productive MCP Server\n *\n * This module contains the app/router creation logic for the HTTP transport.\n * The actual server startup is in server.ts.\n */\n\nimport {\n createApp,\n createRouter,\n defineEventHandler,\n readBody,\n getHeader,\n setResponseHeader,\n type App,\n} from 'h3';\n\nimport { parseAuthHeader } from './auth.js';\nimport { executeToolWithCredentials } from './handlers.js';\nimport { INSTRUCTIONS } from './instructions.js';\nimport {\n oauthMetadataHandler,\n registerHandler,\n authorizeGetHandler,\n authorizePostHandler,\n tokenHandler,\n} from './oauth.js';\nimport { TOOLS } from './tools.js';\nimport { VERSION } from './version.js';\n\n/**\n * JSON-RPC error response\n */\nexport function jsonRpcError(code: number, message: string, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n error: { code, message },\n id,\n };\n}\n\n/**\n * JSON-RPC success response\n */\nexport function jsonRpcSuccess(result: unknown, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n result,\n id,\n };\n}\n\n/**\n * Handle the initialize JSON-RPC method\n */\nexport function handleInitialize() {\n return {\n protocolVersion: '2024-11-05',\n serverInfo: {\n name: 'productive-mcp',\n version: VERSION,\n },\n capabilities: {\n tools: {},\n },\n instructions: INSTRUCTIONS,\n };\n}\n\n/**\n * Handle the tools/list JSON-RPC method\n */\nexport function handleToolsList() {\n return { tools: TOOLS };\n}\n\n/**\n * Create the h3 application with all routes\n */\nexport function createHttpApp(): App {\n const app = createApp();\n const router = createRouter();\n\n // OAuth 2.0 endpoints for Claude Desktop integration (MCP auth spec)\n router.get('/.well-known/oauth-authorization-server', oauthMetadataHandler);\n router.post('/register', registerHandler); // Dynamic Client Registration (RFC 7591)\n router.get('/authorize', authorizeGetHandler);\n router.post('/authorize', authorizePostHandler);\n router.post('/token', tokenHandler);\n\n // OAuth Protected Resource Metadata (RFC 9728 / MCP spec 2025-03-26)\n // This endpoint tells clients where to find the authorization server\n router.get(\n '/.well-known/oauth-protected-resource',\n defineEventHandler((event) => {\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n setResponseHeader(event, 'Cache-Control', 'public, max-age=3600');\n\n return {\n resource: `${baseUrl}/mcp`,\n authorization_servers: [baseUrl],\n scopes_supported: ['productive'],\n bearer_methods_supported: ['header'],\n };\n }),\n );\n\n // Health check endpoint\n router.get(\n '/',\n defineEventHandler(() => {\n return { status: 'ok', service: 'productive-mcp', version: VERSION };\n }),\n );\n\n router.get(\n '/health',\n defineEventHandler(() => {\n return { status: 'ok' };\n }),\n );\n\n // MCP endpoint - handles JSON-RPC over HTTP\n router.post(\n '/mcp',\n defineEventHandler(async (event) => {\n // Parse authorization header\n const authHeader = getHeader(event, 'authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n setResponseHeader(\n event,\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.node.res.statusCode = 401;\n return jsonRpcError(\n -32001,\n 'Authentication required. Provide Bearer token with base64(organizationId:apiToken:userId)',\n );\n }\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n\n // Parse JSON-RPC request\n let body: { method?: string; params?: unknown; id?: string | number };\n try {\n body = await readBody(event);\n } catch {\n event.node.res.statusCode = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n if (!body || typeof body !== 'object') {\n event.node.res.statusCode = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n const { method, params, id } = body;\n\n try {\n if (method === 'initialize') {\n return jsonRpcSuccess(handleInitialize(), id ?? null);\n }\n\n if (method === 'tools/list') {\n return jsonRpcSuccess(handleToolsList(), id ?? null);\n }\n\n if (method === 'tools/call') {\n const { name, arguments: args } = params as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n const result = await executeToolWithCredentials(name, args || {}, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n // Unknown method\n return jsonRpcError(-32601, `Method not found: ${method}`, id ?? null);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return jsonRpcError(-32603, `Internal error: ${message}`, id ?? null);\n }\n }),\n );\n\n // SSE endpoint for server-sent events (optional, for streaming responses)\n router.get(\n '/mcp/sse',\n defineEventHandler(async (event) => {\n const authHeader = getHeader(event, 'authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(\n event,\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.node.res.statusCode = 401;\n return { error: 'Authentication required' };\n }\n\n // Set SSE headers\n setResponseHeader(event, 'Content-Type', 'text/event-stream');\n setResponseHeader(event, 'Cache-Control', 'no-cache');\n setResponseHeader(event, 'Connection', 'keep-alive');\n\n // Generate session ID and send it\n const sessionId = crypto.randomUUID();\n\n // Send initial session event\n event.node.res.write(`event: session\\ndata: ${JSON.stringify({ sessionId })}\\n\\n`);\n\n // Keep connection alive\n const keepAlive = setInterval(() => {\n event.node.res.write(': keepalive\\n\\n');\n }, 30000);\n\n // Clean up on close\n event.node.req.on('close', () => {\n clearInterval(keepAlive);\n });\n\n // Don't end the response - keep it open for SSE\n return new Promise(() => {});\n }),\n );\n\n app.use(router);\n return app;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http.js","names":[],"sources":["../src/http.ts"],"sourcesContent":["/**\n * HTTP transport handlers for Productive MCP Server\n *\n * This module contains the app/router creation logic for the HTTP transport.\n * The actual server startup is in server.ts.\n */\n\nimport {\n createApp,\n createRouter,\n defineEventHandler,\n readBody,\n getHeader,\n setResponseHeader,\n type App,\n} from 'h3';\n\nimport { parseAuthHeader } from './auth.js';\nimport { executeToolWithCredentials } from './handlers.js';\nimport { INSTRUCTIONS } from './instructions.js';\nimport {\n oauthMetadataHandler,\n registerHandler,\n authorizeGetHandler,\n authorizePostHandler,\n tokenHandler,\n} from './oauth.js';\nimport { listResources, listResourceTemplates, readResource } from './resources.js';\nimport { TOOLS } from './tools.js';\nimport { VERSION } from './version.js';\n\n/**\n * JSON-RPC error response\n */\nexport function jsonRpcError(code: number, message: string, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n error: { code, message },\n id,\n };\n}\n\n/**\n * JSON-RPC success response\n */\nexport function jsonRpcSuccess(result: unknown, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n result,\n id,\n };\n}\n\n/**\n * Handle the initialize JSON-RPC method\n */\nexport function handleInitialize() {\n return {\n protocolVersion: '2024-11-05',\n serverInfo: {\n name: 'productive-mcp',\n version: VERSION,\n },\n capabilities: {\n tools: {},\n resources: {},\n },\n instructions: INSTRUCTIONS,\n };\n}\n\n/**\n * Handle the tools/list JSON-RPC method\n */\nexport function handleToolsList() {\n return { tools: TOOLS };\n}\n\n/**\n * Create the h3 application with all routes\n */\nexport function createHttpApp(): App {\n const app = createApp();\n const router = createRouter();\n\n // OAuth 2.0 endpoints for Claude Desktop integration (MCP auth spec)\n router.get('/.well-known/oauth-authorization-server', oauthMetadataHandler);\n router.post('/register', registerHandler); // Dynamic Client Registration (RFC 7591)\n router.get('/authorize', authorizeGetHandler);\n router.post('/authorize', authorizePostHandler);\n router.post('/token', tokenHandler);\n\n // OAuth Protected Resource Metadata (RFC 9728 / MCP spec 2025-03-26)\n // This endpoint tells clients where to find the authorization server\n router.get(\n '/.well-known/oauth-protected-resource',\n defineEventHandler((event) => {\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n setResponseHeader(event, 'Cache-Control', 'public, max-age=3600');\n\n return {\n resource: `${baseUrl}/mcp`,\n authorization_servers: [baseUrl],\n scopes_supported: ['productive'],\n bearer_methods_supported: ['header'],\n };\n }),\n );\n\n // Health check endpoint\n router.get(\n '/',\n defineEventHandler(() => {\n return { status: 'ok', service: 'productive-mcp', version: VERSION };\n }),\n );\n\n router.get(\n '/health',\n defineEventHandler(() => {\n return { status: 'ok' };\n }),\n );\n\n // MCP endpoint - handles JSON-RPC over HTTP\n router.post(\n '/mcp',\n defineEventHandler(async (event) => {\n // Parse authorization header\n const authHeader = getHeader(event, 'authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n setResponseHeader(\n event,\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.node.res.statusCode = 401;\n return jsonRpcError(\n -32001,\n 'Authentication required. Provide Bearer token with base64(organizationId:apiToken:userId)',\n );\n }\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n\n // Parse JSON-RPC request\n let body: { method?: string; params?: unknown; id?: string | number };\n try {\n body = await readBody(event);\n } catch {\n event.node.res.statusCode = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n if (!body || typeof body !== 'object') {\n event.node.res.statusCode = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n const { method, params, id } = body;\n\n try {\n if (method === 'initialize') {\n return jsonRpcSuccess(handleInitialize(), id ?? null);\n }\n\n if (method === 'tools/list') {\n return jsonRpcSuccess(handleToolsList(), id ?? null);\n }\n\n if (method === 'tools/call') {\n const { name, arguments: args } = params as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n const result = await executeToolWithCredentials(name, args || {}, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n if (method === 'resources/list') {\n return jsonRpcSuccess({ resources: listResources() }, id ?? null);\n }\n\n if (method === 'resources/templates/list') {\n return jsonRpcSuccess({ resourceTemplates: listResourceTemplates() }, id ?? null);\n }\n\n if (method === 'resources/read') {\n const { uri } = (params as { uri: string }) ?? {};\n if (!uri) {\n return jsonRpcError(-32602, 'Invalid params: uri is required', id ?? null);\n }\n const result = await readResource(uri, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n // Unknown method\n return jsonRpcError(-32601, `Method not found: ${method}`, id ?? null);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return jsonRpcError(-32603, `Internal error: ${message}`, id ?? null);\n }\n }),\n );\n\n // SSE endpoint for server-sent events (optional, for streaming responses)\n router.get(\n '/mcp/sse',\n defineEventHandler(async (event) => {\n const authHeader = getHeader(event, 'authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(\n event,\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.node.res.statusCode = 401;\n return { error: 'Authentication required' };\n }\n\n // Set SSE headers\n setResponseHeader(event, 'Content-Type', 'text/event-stream');\n setResponseHeader(event, 'Cache-Control', 'no-cache');\n setResponseHeader(event, 'Connection', 'keep-alive');\n\n // Generate session ID and send it\n const sessionId = crypto.randomUUID();\n\n // Send initial session event\n event.node.res.write(`event: session\\ndata: ${JSON.stringify({ sessionId })}\\n\\n`);\n\n // Keep connection alive\n const keepAlive = setInterval(() => {\n event.node.res.write(': keepalive\\n\\n');\n }, 30000);\n\n // Clean up on close\n event.node.req.on('close', () => {\n clearInterval(keepAlive);\n });\n\n // Don't end the response - keep it open for SSE\n return new Promise(() => {});\n }),\n );\n\n app.use(router);\n return app;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAanE;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA8C1C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAkBnE;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA4E1C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKtD"}
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { n as INSTRUCTIONS, t as VERSION } from "./version-4lpeoWqA.js";
3
- import "./handlers-BvwBrRHp.js";
2
+ import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-zLvx5_bN.js";
3
+ import "./handlers-CzOijI7B.js";
4
4
  import { getAvailablePrompts, getAvailableTools, handlePrompt, handleToolCall } from "./stdio.js";
5
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
- import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
7
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
8
+ import { getConfig } from "@studiometa/productive-api";
8
9
  /**
9
10
  * Productive MCP Server - Stdio Transport
10
11
  *
@@ -34,7 +35,8 @@ function createStdioServer() {
34
35
  }, {
35
36
  capabilities: {
36
37
  tools: {},
37
- prompts: {}
38
+ prompts: {},
39
+ resources: {}
38
40
  },
39
41
  instructions: INSTRUCTIONS
40
42
  });
@@ -47,6 +49,22 @@ function createStdioServer() {
47
49
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
48
50
  return handlePrompt(request.params.name, request.params.arguments);
49
51
  });
52
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
53
+ return { resources: listResources() };
54
+ });
55
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
56
+ return { resourceTemplates: listResourceTemplates() };
57
+ });
58
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
59
+ const { uri } = request.params;
60
+ const config = await getConfig();
61
+ if (!config.organizationId || !config.apiToken) throw new Error("Productive.io credentials not configured. Use the \"productive_configure\" tool or set PRODUCTIVE_ORG_ID and PRODUCTIVE_API_TOKEN environment variables.");
62
+ return readResource(uri, {
63
+ organizationId: config.organizationId,
64
+ apiToken: config.apiToken,
65
+ userId: config.userId
66
+ });
67
+ });
50
68
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
51
69
  const { name, arguments: args } = request.params;
52
70
  try {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Productive MCP Server - Stdio Transport\n *\n * This is the local execution mode using stdio transport.\n * For remote HTTP deployment, use server.ts instead.\n *\n * Usage:\n * npx @studiometa/productive-mcp\n *\n * Or in Claude Desktop config:\n * {\n * \"mcpServers\": {\n * \"productive\": {\n * \"command\": \"npx\",\n * \"args\": [\"@studiometa/productive-mcp\"]\n * }\n * }\n * }\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListPromptsRequestSchema,\n GetPromptRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { INSTRUCTIONS } from './instructions.js';\nimport { getAvailableTools, getAvailablePrompts, handleToolCall, handlePrompt } from './stdio.js';\nimport { VERSION } from './version.js';\n\n/**\n * Create and configure the MCP server\n */\nexport function createStdioServer(): Server {\n const server = new Server(\n {\n name: 'productive-mcp',\n version: VERSION,\n },\n {\n capabilities: {\n tools: {},\n prompts: {},\n },\n instructions: INSTRUCTIONS,\n },\n );\n\n // List available tools (including stdio-only configuration tools)\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: getAvailableTools() };\n });\n\n // List available prompts\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return { prompts: getAvailablePrompts() };\n });\n\n // Get prompt\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n return handlePrompt(request.params.name, request.params.arguments as Record<string, string>);\n });\n\n // Handle tool calls\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n return await handleToolCall(name, (args as Record<string, unknown>) || {});\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text', text: `Error: ${message}` }],\n isError: true,\n };\n }\n });\n\n return server;\n}\n\n/**\n * Start the stdio server\n */\nexport async function startStdioServer(): Promise<void> {\n const server = createStdioServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`Productive MCP server v${VERSION} running on stdio`);\n}\n\n// Start server when run directly\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith('/productive-mcp') ||\n process.argv[1]?.endsWith('\\\\productive-mcp');\n\nif (isMainModule) {\n startStdioServer().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,oBAA4B;CAC1C,MAAM,SAAS,IAAI,OACjB;EACE,MAAM;EACN,SAAS;EACV,EACD;EACE,cAAc;GACZ,OAAO,EAAE;GACT,SAAS,EAAE;GACZ;EACD,cAAc;EACf,CACF;AAGD,QAAO,kBAAkB,wBAAwB,YAAY;AAC3D,SAAO,EAAE,OAAO,mBAAmB,EAAE;GACrC;AAGF,QAAO,kBAAkB,0BAA0B,YAAY;AAC7D,SAAO,EAAE,SAAS,qBAAqB,EAAE;GACzC;AAGF,QAAO,kBAAkB,wBAAwB,OAAO,YAAY;AAClE,SAAO,aAAa,QAAQ,OAAO,MAAM,QAAQ,OAAO,UAAoC;GAC5F;AAGF,QAAO,kBAAkB,uBAAuB,OAAO,YAAY;EACjE,MAAM,EAAE,MAAM,WAAW,SAAS,QAAQ;AAE1C,MAAI;AACF,UAAO,MAAM,eAAe,MAAO,QAAoC,EAAE,CAAC;WACnE,OAAO;AAEd,UAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM,UAFlB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;KAEf,CAAC;IACtD,SAAS;IACV;;GAEH;AAEF,QAAO;;;;;AAMT,eAAsB,mBAAkC;CACtD,MAAM,SAAS,mBAAmB;CAClC,MAAM,YAAY,IAAI,sBAAsB;AAC5C,OAAM,OAAO,QAAQ,UAAU;AAC/B,SAAQ,MAAM,0BAA0B,QAAQ,mBAAmB;;AASrE,IAJE,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,QAC3C,QAAQ,KAAK,IAAI,SAAS,kBAAkB,IAC5C,QAAQ,KAAK,IAAI,SAAS,mBAAmB,CAG7C,mBAAkB,CAAC,OAAO,UAAU;AAClC,SAAQ,MAAM,gBAAgB,MAAM;AACpC,SAAQ,KAAK,EAAE;EACf"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Productive MCP Server - Stdio Transport\n *\n * This is the local execution mode using stdio transport.\n * For remote HTTP deployment, use server.ts instead.\n *\n * Usage:\n * npx @studiometa/productive-mcp\n *\n * Or in Claude Desktop config:\n * {\n * \"mcpServers\": {\n * \"productive\": {\n * \"command\": \"npx\",\n * \"args\": [\"@studiometa/productive-mcp\"]\n * }\n * }\n * }\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListPromptsRequestSchema,\n GetPromptRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ReadResourceRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { getConfig } from '@studiometa/productive-api';\n\nimport { INSTRUCTIONS } from './instructions.js';\nimport { listResources, listResourceTemplates, readResource } from './resources.js';\nimport { getAvailableTools, getAvailablePrompts, handleToolCall, handlePrompt } from './stdio.js';\nimport { VERSION } from './version.js';\n\n/**\n * Create and configure the MCP server\n */\nexport function createStdioServer(): Server {\n const server = new Server(\n {\n name: 'productive-mcp',\n version: VERSION,\n },\n {\n capabilities: {\n tools: {},\n prompts: {},\n resources: {},\n },\n instructions: INSTRUCTIONS,\n },\n );\n\n // List available tools (including stdio-only configuration tools)\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: getAvailableTools() };\n });\n\n // List available prompts\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return { prompts: getAvailablePrompts() };\n });\n\n // Get prompt\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n return handlePrompt(request.params.name, request.params.arguments as Record<string, string>);\n });\n\n // List available resources (static + dynamic)\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return { resources: listResources() };\n });\n\n // List resource templates (parameterized URIs)\n server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {\n return { resourceTemplates: listResourceTemplates() };\n });\n\n // Read a resource by URI\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n const { uri } = request.params;\n\n // Get credentials from config (same as tool calls)\n const config = await getConfig();\n if (!config.organizationId || !config.apiToken) {\n throw new Error(\n 'Productive.io credentials not configured. Use the \"productive_configure\" tool or set PRODUCTIVE_ORG_ID and PRODUCTIVE_API_TOKEN environment variables.',\n );\n }\n\n return readResource(uri, {\n organizationId: config.organizationId,\n apiToken: config.apiToken,\n userId: config.userId,\n });\n });\n\n // Handle tool calls\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n return await handleToolCall(name, (args as Record<string, unknown>) || {});\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text', text: `Error: ${message}` }],\n isError: true,\n };\n }\n });\n\n return server;\n}\n\n/**\n * Start the stdio server\n */\nexport async function startStdioServer(): Promise<void> {\n const server = createStdioServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`Productive MCP server v${VERSION} running on stdio`);\n}\n\n// Start server when run directly\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith('/productive-mcp') ||\n process.argv[1]?.endsWith('\\\\productive-mcp');\n\nif (isMainModule) {\n startStdioServer().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,oBAA4B;CAC1C,MAAM,SAAS,IAAI,OACjB;EACE,MAAM;EACN,SAAS;EACV,EACD;EACE,cAAc;GACZ,OAAO,EAAE;GACT,SAAS,EAAE;GACX,WAAW,EAAE;GACd;EACD,cAAc;EACf,CACF;AAGD,QAAO,kBAAkB,wBAAwB,YAAY;AAC3D,SAAO,EAAE,OAAO,mBAAmB,EAAE;GACrC;AAGF,QAAO,kBAAkB,0BAA0B,YAAY;AAC7D,SAAO,EAAE,SAAS,qBAAqB,EAAE;GACzC;AAGF,QAAO,kBAAkB,wBAAwB,OAAO,YAAY;AAClE,SAAO,aAAa,QAAQ,OAAO,MAAM,QAAQ,OAAO,UAAoC;GAC5F;AAGF,QAAO,kBAAkB,4BAA4B,YAAY;AAC/D,SAAO,EAAE,WAAW,eAAe,EAAE;GACrC;AAGF,QAAO,kBAAkB,oCAAoC,YAAY;AACvE,SAAO,EAAE,mBAAmB,uBAAuB,EAAE;GACrD;AAGF,QAAO,kBAAkB,2BAA2B,OAAO,YAAY;EACrE,MAAM,EAAE,QAAQ,QAAQ;EAGxB,MAAM,SAAS,MAAM,WAAW;AAChC,MAAI,CAAC,OAAO,kBAAkB,CAAC,OAAO,SACpC,OAAM,IAAI,MACR,2JACD;AAGH,SAAO,aAAa,KAAK;GACvB,gBAAgB,OAAO;GACvB,UAAU,OAAO;GACjB,QAAQ,OAAO;GAChB,CAAC;GACF;AAGF,QAAO,kBAAkB,uBAAuB,OAAO,YAAY;EACjE,MAAM,EAAE,MAAM,WAAW,SAAS,QAAQ;AAE1C,MAAI;AACF,UAAO,MAAM,eAAe,MAAO,QAAoC,EAAE,CAAC;WACnE,OAAO;AAEd,UAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM,UAFlB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;KAEf,CAAC;IACtD,SAAS;IACV;;GAEH;AAEF,QAAO;;;;;AAMT,eAAsB,mBAAkC;CACtD,MAAM,SAAS,mBAAmB;CAClC,MAAM,YAAY,IAAI,sBAAsB;AAC5C,OAAM,OAAO,QAAQ,UAAU;AAC/B,SAAQ,MAAM,0BAA0B,QAAQ,mBAAmB;;AASrE,IAJE,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,QAC3C,QAAQ,KAAK,IAAI,SAAS,kBAAkB,IAC5C,QAAQ,KAAK,IAAI,SAAS,mBAAmB,CAG7C,mBAAkB,CAAC,OAAO,UAAU;AAClC,SAAQ,MAAM,gBAAgB,MAAM;AACpC,SAAQ,KAAK,EAAE;EACf"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * MCP Resources handlers for Productive MCP Server
3
+ *
4
+ * Exposes Productive data via MCP resources/ capability so clients can browse
5
+ * and read data without tool calls.
6
+ *
7
+ * Static resources (always available):
8
+ * productive://schema — full resource schema overview
9
+ * productive://instructions — server instructions / SKILL.md content
10
+ *
11
+ * Resource templates (parameterized, require API calls):
12
+ * productive://projects/{id} — project details
13
+ * productive://tasks/{id} — task details
14
+ * productive://people/{id} — person details
15
+ * productive://deals/{id} — deal details
16
+ * productive://projects/{id}/tasks — tasks for a project
17
+ * productive://projects/{id}/services — services for a project
18
+ *
19
+ * Dynamic resources (computed):
20
+ * productive://summaries/my_day — personal dashboard
21
+ * productive://summaries/team_pulse — team activity
22
+ */
23
+ import type { ReadResourceResult as McpReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
24
+ import type { ProductiveCredentials } from './auth.js';
25
+ /**
26
+ * A single resource content item returned in resources/read responses
27
+ */
28
+ export interface ResourceContent {
29
+ uri: string;
30
+ mimeType: string;
31
+ text: string;
32
+ }
33
+ /**
34
+ * Shape of a resources/read response (re-export of SDK type for consumers)
35
+ */
36
+ export type ReadResourceResult = McpReadResourceResult;
37
+ /**
38
+ * Shape of a static resource descriptor (resources/list)
39
+ */
40
+ export interface StaticResource {
41
+ uri: string;
42
+ name: string;
43
+ description: string;
44
+ mimeType: string;
45
+ }
46
+ /**
47
+ * Shape of a resource template descriptor (resources/templates/list)
48
+ */
49
+ export interface ResourceTemplate {
50
+ uriTemplate: string;
51
+ name: string;
52
+ description: string;
53
+ mimeType: string;
54
+ }
55
+ /**
56
+ * Static resources that are always available without API credentials
57
+ */
58
+ export declare const STATIC_RESOURCES: StaticResource[];
59
+ /**
60
+ * Dynamic resources that are computed at read time (require credentials)
61
+ */
62
+ export declare const DYNAMIC_RESOURCES: StaticResource[];
63
+ /**
64
+ * Resource templates that accept URI parameters (require credentials)
65
+ */
66
+ export declare const RESOURCE_TEMPLATES: ResourceTemplate[];
67
+ /**
68
+ * List all static and dynamic resources (no credentials needed for static).
69
+ */
70
+ export declare function listResources(): StaticResource[];
71
+ /**
72
+ * List all resource templates.
73
+ */
74
+ export declare function listResourceTemplates(): ResourceTemplate[];
75
+ /**
76
+ * Read a resource by URI.
77
+ *
78
+ * Routes the URI to the appropriate handler. Throws on unknown URI.
79
+ */
80
+ export declare function readResource(uri: string, credentials: ProductiveCredentials): Promise<ReadResourceResult>;
81
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,IAAI,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAKtG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAevD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAa5C,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,EAa7C,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBAAgB,EAqChD,CAAC;AAsJF;;GAEG;AACH,wBAAgB,aAAa,IAAI,cAAc,EAAE,CAEhD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,EAAE,CAE1D;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,qBAAqB,GACjC,OAAO,CAAC,kBAAkB,CAAC,CAuB7B"}
package/dist/schema.d.ts CHANGED
@@ -31,8 +31,8 @@ export declare const ResourceSchema: z.ZodEnum<{
31
31
  batch: "batch";
32
32
  workflows: "workflows";
33
33
  reports: "reports";
34
- summaries: "summaries";
35
34
  search: "search";
35
+ summaries: "summaries";
36
36
  }>;
37
37
  /**
38
38
  * Actions available for resources
@@ -180,8 +180,8 @@ export declare const ProductiveToolInputSchema: z.ZodObject<{
180
180
  batch: "batch";
181
181
  workflows: "workflows";
182
182
  reports: "reports";
183
- summaries: "summaries";
184
183
  search: "search";
184
+ summaries: "summaries";
185
185
  }>;
186
186
  action: z.ZodEnum<{
187
187
  list: "list";
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as VERSION } from "./version-4lpeoWqA.js";
3
- import "./handlers-BvwBrRHp.js";
2
+ import { t as VERSION } from "./version-zLvx5_bN.js";
3
+ import "./handlers-CzOijI7B.js";
4
4
  import { createHttpApp } from "./http.js";
5
5
  import { toNodeListener } from "h3";
6
6
  import { createServer } from "node:http";
package/dist/stdio.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as executeToolWithCredentials } from "./handlers-BvwBrRHp.js";
1
+ import { t as executeToolWithCredentials } from "./handlers-CzOijI7B.js";
2
2
  import "./handlers.js";
3
3
  import { n as PROMPT_DEFINITIONS, t as getPromptMessages } from "./handlers-Cha6_ulB.js";
4
4
  import { STDIO_ONLY_TOOLS, TOOLS } from "./tools.js";
@@ -0,0 +1,236 @@
1
+ import { a as handleSchemaOverview, c as handleDeals, i as handleServices, n as handleTasks, o as handleProjects, r as handleSummaries, s as handlePeople } from "./handlers-CzOijI7B.js";
2
+ import { ProductiveApi } from "@studiometa/productive-api";
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { fromHandlerContext } from "@studiometa/productive-core";
7
+ /**
8
+ * MCP Server Instructions
9
+ *
10
+ * These instructions are sent to Claude Desktop during initialization
11
+ * and used as context/hints for the LLM. This ensures the AI agent
12
+ * knows how to properly use the Productive.io MCP server.
13
+ *
14
+ * The content is derived from skills/SKILL.md (without YAML frontmatter).
15
+ */
16
+ var __dirname = dirname(fileURLToPath(import.meta.url));
17
+ /**
18
+ * Load instructions from SKILL.md file
19
+ * Removes YAML frontmatter (content between --- markers)
20
+ */
21
+ function loadInstructions() {
22
+ try {
23
+ return readFileSync(join(__dirname, "..", "skills", "SKILL.md"), "utf-8").replace(/^---\n[\s\S]*?\n---\n+/, "").trim();
24
+ } catch {
25
+ return "Productive.io MCP Server - Use the productive tool with resource and action parameters.";
26
+ }
27
+ }
28
+ const INSTRUCTIONS = loadInstructions();
29
+ /** MIME type used for all resource content */
30
+ var MIME_TYPE = "application/json";
31
+ /**
32
+ * Static resources that are always available without API credentials
33
+ */
34
+ const STATIC_RESOURCES = [{
35
+ uri: "productive://schema",
36
+ name: "Schema",
37
+ description: "Overview of all Productive.io resources, their actions and filters",
38
+ mimeType: MIME_TYPE
39
+ }, {
40
+ uri: "productive://instructions",
41
+ name: "Instructions",
42
+ description: "Server instructions and usage guide for the Productive.io MCP server",
43
+ mimeType: MIME_TYPE
44
+ }];
45
+ /**
46
+ * Dynamic resources that are computed at read time (require credentials)
47
+ */
48
+ const DYNAMIC_RESOURCES = [{
49
+ uri: "productive://summaries/my_day",
50
+ name: "My Day",
51
+ description: "Personal dashboard: open tasks, today's time entries, active timers",
52
+ mimeType: MIME_TYPE
53
+ }, {
54
+ uri: "productive://summaries/team_pulse",
55
+ name: "Team Pulse",
56
+ description: "Team-wide time tracking activity for today",
57
+ mimeType: MIME_TYPE
58
+ }];
59
+ /**
60
+ * Resource templates that accept URI parameters (require credentials)
61
+ */
62
+ const RESOURCE_TEMPLATES = [
63
+ {
64
+ uriTemplate: "productive://projects/{id}",
65
+ name: "Project",
66
+ description: "Details of a specific project by ID",
67
+ mimeType: MIME_TYPE
68
+ },
69
+ {
70
+ uriTemplate: "productive://tasks/{id}",
71
+ name: "Task",
72
+ description: "Details of a specific task by ID",
73
+ mimeType: MIME_TYPE
74
+ },
75
+ {
76
+ uriTemplate: "productive://people/{id}",
77
+ name: "Person",
78
+ description: "Details of a specific person by ID",
79
+ mimeType: MIME_TYPE
80
+ },
81
+ {
82
+ uriTemplate: "productive://deals/{id}",
83
+ name: "Deal",
84
+ description: "Details of a specific deal or budget by ID",
85
+ mimeType: MIME_TYPE
86
+ },
87
+ {
88
+ uriTemplate: "productive://projects/{id}/tasks",
89
+ name: "Project Tasks",
90
+ description: "All tasks belonging to a specific project",
91
+ mimeType: MIME_TYPE
92
+ },
93
+ {
94
+ uriTemplate: "productive://projects/{id}/services",
95
+ name: "Project Services",
96
+ description: "All services belonging to a specific project",
97
+ mimeType: MIME_TYPE
98
+ }
99
+ ];
100
+ /** Route patterns and their handler factories */
101
+ var URI_PATTERNS = [
102
+ {
103
+ pattern: /^productive:\/\/schema$/,
104
+ handler: async () => {
105
+ const text = handleSchemaOverview().content[0];
106
+ return JSON.parse(text.text);
107
+ }
108
+ },
109
+ {
110
+ pattern: /^productive:\/\/instructions$/,
111
+ handler: async () => ({ instructions: INSTRUCTIONS })
112
+ },
113
+ {
114
+ pattern: /^productive:\/\/summaries\/my_day$/,
115
+ handler: async (_, credentials) => {
116
+ return extractJsonFromResult(await handleSummaries("my_day", {}, buildHandlerContext(credentials)));
117
+ }
118
+ },
119
+ {
120
+ pattern: /^productive:\/\/summaries\/team_pulse$/,
121
+ handler: async (_, credentials) => {
122
+ return extractJsonFromResult(await handleSummaries("team_pulse", {}, buildHandlerContext(credentials)));
123
+ }
124
+ },
125
+ {
126
+ pattern: /^productive:\/\/projects\/([^/]+)\/tasks$/,
127
+ handler: async ([, id], credentials) => {
128
+ const ctx = buildHandlerContext(credentials, { filter: { project_id: id } });
129
+ return extractJsonFromResult(await handleTasks("list", { project_id: id }, ctx));
130
+ }
131
+ },
132
+ {
133
+ pattern: /^productive:\/\/projects\/([^/]+)\/services$/,
134
+ handler: async ([, id], credentials) => {
135
+ return extractJsonFromResult(await handleServices("list", {}, buildHandlerContext(credentials, { filter: { project_id: id } })));
136
+ }
137
+ },
138
+ {
139
+ pattern: /^productive:\/\/projects\/([^/]+)$/,
140
+ handler: async ([, id], credentials) => {
141
+ const ctx = buildHandlerContext(credentials);
142
+ return extractJsonFromResult(await handleProjects("get", { id }, ctx));
143
+ }
144
+ },
145
+ {
146
+ pattern: /^productive:\/\/tasks\/([^/]+)$/,
147
+ handler: async ([, id], credentials) => {
148
+ const ctx = buildHandlerContext(credentials);
149
+ return extractJsonFromResult(await handleTasks("get", { id }, ctx));
150
+ }
151
+ },
152
+ {
153
+ pattern: /^productive:\/\/people\/([^/]+)$/,
154
+ handler: async ([, id], credentials) => {
155
+ const ctx = buildHandlerContext(credentials);
156
+ return extractJsonFromResult(await handlePeople("get", { id }, ctx, credentials));
157
+ }
158
+ },
159
+ {
160
+ pattern: /^productive:\/\/deals\/([^/]+)$/,
161
+ handler: async ([, id], credentials) => {
162
+ const ctx = buildHandlerContext(credentials);
163
+ return extractJsonFromResult(await handleDeals("get", { id }, ctx));
164
+ }
165
+ }
166
+ ];
167
+ /**
168
+ * Build a HandlerContext from credentials, with optional filter overrides.
169
+ */
170
+ function buildHandlerContext(credentials, overrides = {}) {
171
+ const execCtx = fromHandlerContext({ api: new ProductiveApi({ config: {
172
+ apiToken: credentials.apiToken,
173
+ organizationId: credentials.organizationId,
174
+ userId: credentials.userId,
175
+ baseUrl: process.env.PRODUCTIVE_BASE_URL
176
+ } }) }, { userId: credentials.userId });
177
+ return {
178
+ formatOptions: { compact: false },
179
+ filter: overrides.filter,
180
+ perPage: 50,
181
+ includeHints: false,
182
+ includeSuggestions: false,
183
+ executor: () => execCtx
184
+ };
185
+ }
186
+ /**
187
+ * Extract the parsed JSON data from a ToolResult.
188
+ * Throws if the result is an error or not parseable.
189
+ */
190
+ function extractJsonFromResult(result) {
191
+ if (result.isError) {
192
+ const text = result.content[0].text;
193
+ throw new Error(text);
194
+ }
195
+ const text = result.content[0].text;
196
+ try {
197
+ return JSON.parse(text);
198
+ } catch {
199
+ return { text };
200
+ }
201
+ }
202
+ /**
203
+ * List all static and dynamic resources (no credentials needed for static).
204
+ */
205
+ function listResources() {
206
+ return [...STATIC_RESOURCES, ...DYNAMIC_RESOURCES];
207
+ }
208
+ /**
209
+ * List all resource templates.
210
+ */
211
+ function listResourceTemplates() {
212
+ return RESOURCE_TEMPLATES;
213
+ }
214
+ /**
215
+ * Read a resource by URI.
216
+ *
217
+ * Routes the URI to the appropriate handler. Throws on unknown URI.
218
+ */
219
+ async function readResource(uri, credentials) {
220
+ for (const { pattern, handler } of URI_PATTERNS) {
221
+ const match = uri.match(pattern);
222
+ if (match) {
223
+ const data = await handler(match, credentials);
224
+ return { contents: [{
225
+ uri,
226
+ mimeType: MIME_TYPE,
227
+ text: JSON.stringify(data, null, 2)
228
+ }] };
229
+ }
230
+ }
231
+ throw new Error(`Unknown resource URI: ${uri}. Available static resources: ${STATIC_RESOURCES.map((r) => r.uri).join(", ")}. Available dynamic resources: ${DYNAMIC_RESOURCES.map((r) => r.uri).join(", ")}. Resource templates: ${RESOURCE_TEMPLATES.map((t) => t.uriTemplate).join(", ")}.`);
232
+ }
233
+ const VERSION = "0.10.7";
234
+ export { INSTRUCTIONS as a, readResource as i, listResourceTemplates as n, listResources as r, VERSION as t };
235
+
236
+ //# sourceMappingURL=version-zLvx5_bN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-zLvx5_bN.js","names":[],"sources":["../src/instructions.ts","../src/resources.ts","../src/version.ts"],"sourcesContent":["/**\n * MCP Server Instructions\n *\n * These instructions are sent to Claude Desktop during initialization\n * and used as context/hints for the LLM. This ensures the AI agent\n * knows how to properly use the Productive.io MCP server.\n *\n * The content is derived from skills/SKILL.md (without YAML frontmatter).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Load instructions from SKILL.md file\n * Removes YAML frontmatter (content between --- markers)\n */\nfunction loadInstructions(): string {\n try {\n // In dist/, go up to package root, then to skills/\n const skillPath = join(__dirname, '..', 'skills', 'SKILL.md');\n const content = readFileSync(skillPath, 'utf-8');\n\n // Remove YAML frontmatter (between --- markers at start of file)\n const withoutFrontmatter = content.replace(/^---\\n[\\s\\S]*?\\n---\\n+/, '');\n\n return withoutFrontmatter.trim();\n } catch {\n // Fallback if file not found (shouldn't happen in production)\n return 'Productive.io MCP Server - Use the productive tool with resource and action parameters.';\n }\n}\n\nexport const INSTRUCTIONS = loadInstructions();\n","/**\n * MCP Resources handlers for Productive MCP Server\n *\n * Exposes Productive data via MCP resources/ capability so clients can browse\n * and read data without tool calls.\n *\n * Static resources (always available):\n * productive://schema — full resource schema overview\n * productive://instructions — server instructions / SKILL.md content\n *\n * Resource templates (parameterized, require API calls):\n * productive://projects/{id} — project details\n * productive://tasks/{id} — task details\n * productive://people/{id} — person details\n * productive://deals/{id} — deal details\n * productive://projects/{id}/tasks — tasks for a project\n * productive://projects/{id}/services — services for a project\n *\n * Dynamic resources (computed):\n * productive://summaries/my_day — personal dashboard\n * productive://summaries/team_pulse — team activity\n */\n\nimport type { ReadResourceResult as McpReadResourceResult } from '@modelcontextprotocol/sdk/types.js';\n\nimport { ProductiveApi } from '@studiometa/productive-api';\nimport { fromHandlerContext } from '@studiometa/productive-core';\n\nimport type { ProductiveCredentials } from './auth.js';\nimport type { HandlerContext } from './handlers/types.js';\n\nimport { handleDeals } from './handlers/deals.js';\nimport { handlePeople } from './handlers/people.js';\nimport { handleProjects } from './handlers/projects.js';\nimport { handleSchemaOverview } from './handlers/schema.js';\nimport { handleServices } from './handlers/services.js';\nimport { handleSummaries } from './handlers/summaries.js';\nimport { handleTasks } from './handlers/tasks.js';\nimport { INSTRUCTIONS } from './instructions.js';\n\n/** MIME type used for all resource content */\nconst MIME_TYPE = 'application/json';\n\n/**\n * A single resource content item returned in resources/read responses\n */\nexport interface ResourceContent {\n uri: string;\n mimeType: string;\n text: string;\n}\n\n/**\n * Shape of a resources/read response (re-export of SDK type for consumers)\n */\nexport type ReadResourceResult = McpReadResourceResult;\n\n/**\n * Shape of a static resource descriptor (resources/list)\n */\nexport interface StaticResource {\n uri: string;\n name: string;\n description: string;\n mimeType: string;\n}\n\n/**\n * Shape of a resource template descriptor (resources/templates/list)\n */\nexport interface ResourceTemplate {\n uriTemplate: string;\n name: string;\n description: string;\n mimeType: string;\n}\n\n// ---------------------------------------------------------------------------\n// Static resource definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Static resources that are always available without API credentials\n */\nexport const STATIC_RESOURCES: StaticResource[] = [\n {\n uri: 'productive://schema',\n name: 'Schema',\n description: 'Overview of all Productive.io resources, their actions and filters',\n mimeType: MIME_TYPE,\n },\n {\n uri: 'productive://instructions',\n name: 'Instructions',\n description: 'Server instructions and usage guide for the Productive.io MCP server',\n mimeType: MIME_TYPE,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Dynamic resource definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Dynamic resources that are computed at read time (require credentials)\n */\nexport const DYNAMIC_RESOURCES: StaticResource[] = [\n {\n uri: 'productive://summaries/my_day',\n name: 'My Day',\n description: \"Personal dashboard: open tasks, today's time entries, active timers\",\n mimeType: MIME_TYPE,\n },\n {\n uri: 'productive://summaries/team_pulse',\n name: 'Team Pulse',\n description: 'Team-wide time tracking activity for today',\n mimeType: MIME_TYPE,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Resource template definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Resource templates that accept URI parameters (require credentials)\n */\nexport const RESOURCE_TEMPLATES: ResourceTemplate[] = [\n {\n uriTemplate: 'productive://projects/{id}',\n name: 'Project',\n description: 'Details of a specific project by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://tasks/{id}',\n name: 'Task',\n description: 'Details of a specific task by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://people/{id}',\n name: 'Person',\n description: 'Details of a specific person by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://deals/{id}',\n name: 'Deal',\n description: 'Details of a specific deal or budget by ID',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://projects/{id}/tasks',\n name: 'Project Tasks',\n description: 'All tasks belonging to a specific project',\n mimeType: MIME_TYPE,\n },\n {\n uriTemplate: 'productive://projects/{id}/services',\n name: 'Project Services',\n description: 'All services belonging to a specific project',\n mimeType: MIME_TYPE,\n },\n];\n\n// ---------------------------------------------------------------------------\n// URI pattern matching\n// ---------------------------------------------------------------------------\n\n/** Route patterns and their handler factories */\nconst URI_PATTERNS: Array<{\n pattern: RegExp;\n handler: (match: RegExpMatchArray, credentials: ProductiveCredentials) => Promise<unknown>;\n}> = [\n // Static resources (no credentials needed)\n {\n pattern: /^productive:\\/\\/schema$/,\n handler: async () => {\n const result = handleSchemaOverview();\n const text = result.content[0] as { text: string };\n return JSON.parse(text.text);\n },\n },\n {\n pattern: /^productive:\\/\\/instructions$/,\n handler: async () => ({ instructions: INSTRUCTIONS }),\n },\n\n // Dynamic summaries\n {\n pattern: /^productive:\\/\\/summaries\\/my_day$/,\n handler: async (_, credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleSummaries('my_day', {}, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/summaries\\/team_pulse$/,\n handler: async (_, credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleSummaries('team_pulse', {}, ctx);\n return extractJsonFromResult(result);\n },\n },\n\n // Project nested resources (before single project to avoid conflict)\n {\n pattern: /^productive:\\/\\/projects\\/([^/]+)\\/tasks$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials, { filter: { project_id: id } });\n const result = await handleTasks('list', { project_id: id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/projects\\/([^/]+)\\/services$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials, { filter: { project_id: id } });\n // handleServices uses CommonArgs; project_id is passed via ctx.filter\n const result = await handleServices('list', {}, ctx);\n return extractJsonFromResult(result);\n },\n },\n\n // Single entity resources\n {\n pattern: /^productive:\\/\\/projects\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleProjects('get', { id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/tasks\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleTasks('get', { id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/people\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handlePeople('get', { id }, ctx, credentials);\n return extractJsonFromResult(result);\n },\n },\n {\n pattern: /^productive:\\/\\/deals\\/([^/]+)$/,\n handler: async ([, id], credentials) => {\n const ctx = buildHandlerContext(credentials);\n const result = await handleDeals('get', { id }, ctx);\n return extractJsonFromResult(result);\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build a HandlerContext from credentials, with optional filter overrides.\n */\nfunction buildHandlerContext(\n credentials: ProductiveCredentials,\n overrides: { filter?: Record<string, string> } = {},\n): HandlerContext {\n const api = new ProductiveApi({\n config: {\n apiToken: credentials.apiToken,\n organizationId: credentials.organizationId,\n userId: credentials.userId,\n baseUrl: process.env.PRODUCTIVE_BASE_URL,\n },\n });\n\n const execCtx = fromHandlerContext({ api }, { userId: credentials.userId });\n\n return {\n formatOptions: { compact: false },\n filter: overrides.filter,\n perPage: 50,\n includeHints: false,\n includeSuggestions: false,\n executor: () => execCtx,\n };\n}\n\n/**\n * Extract the parsed JSON data from a ToolResult.\n * Throws if the result is an error or not parseable.\n */\nfunction extractJsonFromResult(result: { content: unknown[]; isError?: boolean }): unknown {\n if (result.isError) {\n const text = (result.content[0] as { text: string }).text;\n throw new Error(text);\n }\n const text = (result.content[0] as { text: string }).text;\n try {\n return JSON.parse(text);\n } catch {\n return { text };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * List all static and dynamic resources (no credentials needed for static).\n */\nexport function listResources(): StaticResource[] {\n return [...STATIC_RESOURCES, ...DYNAMIC_RESOURCES];\n}\n\n/**\n * List all resource templates.\n */\nexport function listResourceTemplates(): ResourceTemplate[] {\n return RESOURCE_TEMPLATES;\n}\n\n/**\n * Read a resource by URI.\n *\n * Routes the URI to the appropriate handler. Throws on unknown URI.\n */\nexport async function readResource(\n uri: string,\n credentials: ProductiveCredentials,\n): Promise<ReadResourceResult> {\n for (const { pattern, handler } of URI_PATTERNS) {\n const match = uri.match(pattern);\n if (match) {\n const data = await handler(match, credentials);\n return {\n contents: [\n {\n uri,\n mimeType: MIME_TYPE,\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n }\n\n throw new Error(\n `Unknown resource URI: ${uri}. ` +\n `Available static resources: ${STATIC_RESOURCES.map((r) => r.uri).join(', ')}. ` +\n `Available dynamic resources: ${DYNAMIC_RESOURCES.map((r) => r.uri).join(', ')}. ` +\n `Resource templates: ${RESOURCE_TEMPLATES.map((t) => t.uriTemplate).join(', ')}.`,\n );\n}\n","/**\n * Package version - injected from package.json at build time\n */\ndeclare const __VERSION__: string;\nexport const VERSION = __VERSION__;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiometa/productive-mcp",
3
- "version": "0.10.5",
3
+ "version": "0.10.7",
4
4
  "description": "MCP server for Productive.io API - Model Context Protocol integration for Claude Desktop",
5
5
  "keywords": [
6
6
  "ai",
@@ -87,8 +87,8 @@
87
87
  },
88
88
  "dependencies": {
89
89
  "@modelcontextprotocol/sdk": "^1.26.0",
90
- "@studiometa/productive-api": "0.10.5",
91
- "@studiometa/productive-core": "0.10.5",
90
+ "@studiometa/productive-api": "0.10.7",
91
+ "@studiometa/productive-core": "0.10.7",
92
92
  "h3": "^1.15.1",
93
93
  "zod": "4.3.6"
94
94
  },