@goforgeit/mcp-asana 0.4.12 → 0.4.19

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/index.js CHANGED
@@ -17,7 +17,9 @@ var ForgeOAuthTokensSchema = z.object({
17
17
  connectedAt: z.string(),
18
18
  scope: z.string().optional(),
19
19
  email: z.string().optional(),
20
- accountId: z.string().optional()
20
+ accountId: z.string().optional(),
21
+ /** User-level access token (Slack xoxp-* token for search operations) */
22
+ userAccessToken: z.string().optional()
21
23
  });
22
24
  var DEFAULT_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
23
25
  function createForgeTokenManager(options) {
@@ -99,6 +101,12 @@ function createForgeTokenManager(options) {
99
101
  }
100
102
  return cachedTokens.accessToken;
101
103
  },
104
+ async getUserAccessToken() {
105
+ if (!cachedTokens) {
106
+ await this.getAccessToken();
107
+ }
108
+ return cachedTokens?.userAccessToken ?? null;
109
+ },
102
110
  getTokenInfo() {
103
111
  return cachedTokens;
104
112
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../shared/src/mcp-auth/forge-token-manager.ts"],"sourcesContent":["/**\n * @goforgeit/mcp-asana — MCP server for Asana\n *\n * Provides task, project, and workspace tools via stdio transport.\n * Reads Forge's OAuth tokens and auto-refreshes them.\n * Uses Asana REST API directly (no SDK dependency).\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { createForgeTokenManager } from '@forge/shared/mcp-auth';\n\nconst ASANA_TOKEN_URL = 'https://app.asana.com/-/oauth_token';\nconst ASANA_BASE = 'https://app.asana.com/api/1.0';\n\nexport interface AsanaMCPOptions {\n tokenPath: string;\n clientId: string;\n clientSecret: string;\n}\n\ninterface ToolHandler {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (args: any) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;\n}\n\ninterface AsanaMCPServer {\n server: McpServer;\n getRegisteredTools(): Record<string, ToolHandler>;\n start(): Promise<void>;\n}\n\nexport function createAsanaMCPServer(options: AsanaMCPOptions): AsanaMCPServer {\n const tokenManager = createForgeTokenManager({\n tokenPath: options.tokenPath,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n tokenUrl: ASANA_TOKEN_URL,\n });\n\n const server = new McpServer({\n name: '@goforgeit/mcp-asana',\n version: '0.1.0',\n });\n\n const registeredTools: Record<string, ToolHandler> = {};\n\n function registerTool(\n name: string,\n description: string,\n schema: Record<string, z.ZodType>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (args: any) => Promise<{ content: Array<{ type: 'text'; text: string }> }>,\n ) {\n registeredTools[name] = { handler };\n server.tool(name, description, schema, handler);\n }\n\n async function asanaRequest(\n path: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',\n body?: unknown,\n ): Promise<unknown> {\n const token = await tokenManager.getAccessToken();\n const options: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n };\n if (body) options.body = JSON.stringify({ data: body });\n\n const response = await fetch(`${ASANA_BASE}${path}`, options);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Asana API error (${response.status}): ${errorText}`);\n }\n\n if (response.status === 204) return {};\n const json = (await response.json()) as { data: unknown };\n return json.data;\n }\n\n // --- Workspace Tools ---\n\n registerTool('list_workspaces', 'List all Asana workspaces', {}, async () => {\n const data = (await asanaRequest('/workspaces')) as unknown[];\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ workspaces: data }) }],\n };\n });\n\n // --- Project Tools ---\n\n registerTool(\n 'search_projects',\n 'Search Asana projects in a workspace',\n {\n workspaceGid: z.string().describe('Workspace GID'),\n query: z.string().optional().describe('Search query to filter projects by name'),\n },\n async (args: { workspaceGid: string; query?: string }) => {\n const path = `/workspaces/${args.workspaceGid}/projects?opt_fields=name,archived,color,created_at,current_status,due_on,owner,public,start_on`;\n if (args.query) {\n // Asana doesn't have native search for projects, so we fetch all and filter\n }\n const data = (await asanaRequest(path)) as unknown[];\n let results = data;\n if (args.query) {\n const q = args.query.toLowerCase();\n results = (data as Array<{ name?: string }>).filter(\n (p) => p.name && p.name.toLowerCase().includes(q),\n );\n }\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ projects: results }) }],\n };\n },\n );\n\n registerTool(\n 'get_project',\n 'Get Asana project details by GID',\n {\n projectGid: z.string().describe('Project GID'),\n },\n async (args: { projectGid: string }) => {\n const data = await asanaRequest(\n `/projects/${args.projectGid}?opt_fields=name,notes,archived,color,created_at,current_status,due_on,owner,public,start_on,members,custom_fields`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'create_project',\n 'Create a new Asana project. For organization workspaces, teamGid is required.',\n {\n workspaceGid: z.string().describe('Workspace GID'),\n name: z.string().describe('Project name'),\n teamGid: z.string().optional().describe('Team GID (required for organization workspaces)'),\n notes: z.string().optional().describe('Project description'),\n color: z.string().optional().describe('Project color (e.g. \"light-green\")'),\n isPublic: z.boolean().optional().describe('Whether the project is public'),\n },\n async (args: {\n workspaceGid: string;\n name: string;\n teamGid?: string;\n notes?: string;\n color?: string;\n isPublic?: boolean;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = {\n workspace: args.workspaceGid,\n name: args.name,\n };\n if (args.teamGid) body.team = args.teamGid;\n if (args.notes) body.notes = args.notes;\n if (args.color) body.color = args.color;\n if (args.isPublic !== undefined) body.public = args.isPublic;\n\n const data = await asanaRequest('/projects', 'POST', body);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n // --- Section Tools ---\n\n registerTool(\n 'get_project_sections',\n 'List sections in an Asana project',\n {\n projectGid: z.string().describe('Project GID'),\n },\n async (args: { projectGid: string }) => {\n const data = await asanaRequest(`/projects/${args.projectGid}/sections`);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ sections: data }) }],\n };\n },\n );\n\n registerTool(\n 'create_section',\n 'Create a section in an Asana project',\n {\n projectGid: z.string().describe('Project GID'),\n name: z.string().describe('Section name'),\n },\n async (args: { projectGid: string; name: string }) => {\n const data = await asanaRequest(`/projects/${args.projectGid}/sections`, 'POST', {\n name: args.name,\n });\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n // --- Task Tools ---\n\n registerTool(\n 'search_tasks',\n 'Search Asana tasks in a workspace with filters',\n {\n workspaceGid: z.string().describe('Workspace GID'),\n query: z.string().optional().describe('Full-text search query'),\n assigneeGid: z.string().optional().describe('Filter by assignee GID (use \"me\" for self)'),\n projectGid: z.string().optional().describe('Filter by project GID'),\n completed: z.boolean().optional().describe('Filter by completion status'),\n limit: z.number().optional().default(25).describe('Maximum results (default 25)'),\n },\n async (args: {\n workspaceGid: string;\n query?: string;\n assigneeGid?: string;\n projectGid?: string;\n completed?: boolean;\n limit?: number;\n }) => {\n const params = new URLSearchParams();\n if (args.query) params.set('text', args.query);\n if (args.assigneeGid) params.set('assignee.any', args.assigneeGid);\n if (args.projectGid) params.set('projects.any', args.projectGid);\n if (args.completed !== undefined) params.set('completed', String(args.completed));\n params.set('limit', String(args.limit || 25));\n params.set(\n 'opt_fields',\n 'name,completed,assignee,due_on,due_at,created_at,modified_at,notes,projects,tags,parent',\n );\n\n const data = await asanaRequest(\n `/workspaces/${args.workspaceGid}/tasks/search?${params.toString()}`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ tasks: data }) }],\n };\n },\n );\n\n registerTool(\n 'get_task',\n 'Get full Asana task details by GID',\n {\n taskGid: z.string().describe('Task GID'),\n },\n async (args: { taskGid: string }) => {\n const data = await asanaRequest(\n `/tasks/${args.taskGid}?opt_fields=name,notes,completed,assignee,due_on,due_at,created_at,modified_at,projects,tags,parent,subtasks,custom_fields,followers`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'create_task',\n 'Create a new Asana task',\n {\n projectGid: z.string().describe('Project GID to add the task to'),\n name: z.string().describe('Task name'),\n notes: z.string().optional().describe('Task description'),\n assigneeGid: z.string().optional().describe('Assignee GID (use \"me\" for self)'),\n dueOn: z.string().optional().describe('Due date (YYYY-MM-DD)'),\n sectionGid: z.string().optional().describe('Section GID to place the task in'),\n },\n async (args: {\n projectGid: string;\n name: string;\n notes?: string;\n assigneeGid?: string;\n dueOn?: string;\n sectionGid?: string;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = {\n projects: [args.projectGid],\n name: args.name,\n };\n if (args.notes) body.notes = args.notes;\n if (args.assigneeGid) body.assignee = args.assigneeGid;\n if (args.dueOn) body.due_on = args.dueOn;\n\n const data = await asanaRequest('/tasks', 'POST', body);\n\n // If a section was specified, add the task to it\n if (args.sectionGid && data && typeof data === 'object' && 'gid' in data) {\n await asanaRequest(`/sections/${args.sectionGid}/addTask`, 'POST', {\n task: (data as { gid: string }).gid,\n });\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'update_task',\n 'Update an existing Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n name: z.string().optional().describe('New task name'),\n notes: z.string().optional().describe('New task description'),\n completed: z.boolean().optional().describe('Mark complete/incomplete'),\n assigneeGid: z.string().optional().describe('New assignee GID'),\n dueOn: z.string().optional().describe('New due date (YYYY-MM-DD)'),\n },\n async (args: {\n taskGid: string;\n name?: string;\n notes?: string;\n completed?: boolean;\n assigneeGid?: string;\n dueOn?: string;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = {};\n if (args.name !== undefined) body.name = args.name;\n if (args.notes !== undefined) body.notes = args.notes;\n if (args.completed !== undefined) body.completed = args.completed;\n if (args.assigneeGid !== undefined) body.assignee = args.assigneeGid;\n if (args.dueOn !== undefined) body.due_on = args.dueOn;\n\n const data = await asanaRequest(`/tasks/${args.taskGid}`, 'PUT', body);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'create_subtask',\n 'Create a subtask under a parent Asana task',\n {\n parentTaskGid: z.string().describe('Parent task GID'),\n name: z.string().describe('Subtask name'),\n notes: z.string().optional().describe('Subtask description'),\n assigneeGid: z.string().optional().describe('Assignee GID'),\n dueOn: z.string().optional().describe('Due date (YYYY-MM-DD)'),\n },\n async (args: {\n parentTaskGid: string;\n name: string;\n notes?: string;\n assigneeGid?: string;\n dueOn?: string;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = { name: args.name };\n if (args.notes) body.notes = args.notes;\n if (args.assigneeGid) body.assignee = args.assigneeGid;\n if (args.dueOn) body.due_on = args.dueOn;\n\n const data = await asanaRequest(`/tasks/${args.parentTaskGid}/subtasks`, 'POST', body);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'delete_task',\n 'Delete an Asana task',\n {\n taskGid: z.string().describe('Task GID to delete'),\n },\n async (args: { taskGid: string }) => {\n await asanaRequest(`/tasks/${args.taskGid}`, 'DELETE');\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ deleted: true, taskGid: args.taskGid }),\n },\n ],\n };\n },\n );\n\n // --- Story / Comment Tools ---\n\n registerTool(\n 'get_task_stories',\n 'Get comments and activity on an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n },\n async (args: { taskGid: string }) => {\n const data = await asanaRequest(\n `/tasks/${args.taskGid}/stories?opt_fields=text,type,created_at,created_by`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ stories: data }) }],\n };\n },\n );\n\n registerTool(\n 'create_task_story',\n 'Add a comment to an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n text: z.string().describe('Comment text'),\n },\n async (args: { taskGid: string; text: string }) => {\n const data = await asanaRequest(`/tasks/${args.taskGid}/stories`, 'POST', {\n text: args.text,\n });\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n // --- Tag Tools ---\n\n registerTool(\n 'get_tags_for_task',\n 'List tags on an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n },\n async (args: { taskGid: string }) => {\n const data = await asanaRequest(`/tasks/${args.taskGid}/tags`);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ tags: data }) }],\n };\n },\n );\n\n registerTool(\n 'add_tag_to_task',\n 'Add a tag to an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n tagGid: z.string().describe('Tag GID to add'),\n },\n async (args: { taskGid: string; tagGid: string }) => {\n await asanaRequest(`/tasks/${args.taskGid}/addTag`, 'POST', { tag: args.tagGid });\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ added: true, taskGid: args.taskGid, tagGid: args.tagGid }),\n },\n ],\n };\n },\n );\n\n // --- User Tools ---\n\n registerTool('get_me', 'Get the authenticated Asana user', {}, async () => {\n const data = await asanaRequest('/users/me?opt_fields=name,email,workspaces');\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n });\n\n return {\n server,\n getRegisteredTools() {\n return registeredTools;\n },\n async start() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n\n// CLI entry point\nconst isMainModule =\n process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\\\/g, '/'));\nif (isMainModule || process.env.FORGE_MCP_START === 'true') {\n const tokenPath = process.env.FORGE_TOKEN_PATH;\n const clientId = process.env.FORGE_CLIENT_ID;\n const clientSecret = process.env.FORGE_CLIENT_SECRET;\n\n if (!tokenPath || !clientId || !clientSecret) {\n console.error(\n 'Required environment variables: FORGE_TOKEN_PATH, FORGE_CLIENT_ID, FORGE_CLIENT_SECRET',\n );\n process.exit(1);\n }\n\n const mcpServer = createAsanaMCPServer({ tokenPath, clientId, clientSecret });\n mcpServer.start().catch((err) => {\n console.error('Failed to start MCP server:', err);\n process.exit(1);\n });\n}\n","/**\n * Forge Token Manager — reads Forge's OAuthTokens format and handles refresh.\n *\n * Used by @forge/mcp-* packages to get valid access tokens for API calls.\n * Reads from FORGE_TOKEN_PATH, validates with Zod, and refreshes when\n * the token is expired or within 5 minutes of expiry.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\n/** Zod schema for Forge's OAuth token file format */\nexport const ForgeOAuthTokensSchema = z.object({\n accessToken: z.string(),\n refreshToken: z.string().optional(),\n expiresAt: z.number(),\n providerId: z.string(),\n connectedAt: z.string(),\n scope: z.string().optional(),\n email: z.string().optional(),\n accountId: z.string().optional(),\n});\n\nexport type ForgeOAuthTokens = z.infer<typeof ForgeOAuthTokensSchema>;\n\nexport interface ForgeTokenManagerOptions {\n /** Path to the Forge OAuth token JSON file */\n tokenPath: string;\n /** OAuth client ID for token refresh */\n clientId: string;\n /** OAuth client secret for token refresh */\n clientSecret: string;\n /** OAuth token endpoint URL for refresh requests */\n tokenUrl: string;\n /** Buffer in ms before expiry to trigger refresh (default: 5 minutes) */\n refreshBufferMs?: number;\n}\n\nexport interface ForgeTokenManager {\n /** Returns a valid access token, refreshing if needed */\n getAccessToken(): Promise<string>;\n /** Returns the current token data, or null if not yet loaded */\n getTokenInfo(): ForgeOAuthTokens | null;\n /** Register a callback for when tokens are refreshed */\n onRefresh(callback: (tokens: ForgeOAuthTokens) => void): void;\n}\n\nconst DEFAULT_REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes\n\nexport function createForgeTokenManager(options: ForgeTokenManagerOptions): ForgeTokenManager {\n const {\n tokenPath,\n clientId,\n clientSecret,\n tokenUrl,\n refreshBufferMs = DEFAULT_REFRESH_BUFFER_MS,\n } = options;\n\n let cachedTokens: ForgeOAuthTokens | null = null;\n const refreshCallbacks: Array<(tokens: ForgeOAuthTokens) => void> = [];\n\n function isExpiredOrNearExpiry(tokens: ForgeOAuthTokens): boolean {\n return Date.now() >= tokens.expiresAt - refreshBufferMs;\n }\n\n async function readTokenFile(): Promise<ForgeOAuthTokens> {\n let raw: string;\n try {\n raw = await fs.readFile(tokenPath, 'utf-8');\n } catch (err: unknown) {\n if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {\n throw new Error(`Token file not found: ${tokenPath}`);\n }\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON in token file: ${tokenPath}`);\n }\n\n const result = ForgeOAuthTokensSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(`Invalid token file format: ${result.error.message}`);\n }\n\n return result.data;\n }\n\n async function refreshToken(tokens: ForgeOAuthTokens): Promise<ForgeOAuthTokens> {\n if (!tokens.refreshToken) {\n throw new Error('No refresh token available — cannot refresh expired token');\n }\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: tokens.refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = await response.json();\n\n const refreshedTokens: ForgeOAuthTokens = {\n ...tokens,\n accessToken: data.access_token,\n refreshToken: data.refresh_token || tokens.refreshToken,\n expiresAt: Date.now() + data.expires_in * 1000,\n };\n\n // Write refreshed tokens back to file\n await fs.mkdir(path.dirname(tokenPath), { recursive: true });\n await fs.writeFile(tokenPath, JSON.stringify(refreshedTokens), { mode: 0o600 });\n\n // Notify listeners\n for (const cb of refreshCallbacks) {\n cb(refreshedTokens);\n }\n\n return refreshedTokens;\n }\n\n return {\n async getAccessToken(): Promise<string> {\n if (cachedTokens && !isExpiredOrNearExpiry(cachedTokens)) {\n return cachedTokens.accessToken;\n }\n\n const tokens = cachedTokens || (await readTokenFile());\n\n if (isExpiredOrNearExpiry(tokens)) {\n if (tokens.refreshToken) {\n cachedTokens = await refreshToken(tokens);\n } else {\n // No refresh token — return the access token as-is.\n // Some providers (e.g. Slack bot tokens) issue non-expiring tokens\n // without refresh tokens, so the expiresAt may be unreliable.\n cachedTokens = tokens;\n }\n } else {\n cachedTokens = tokens;\n }\n\n return cachedTokens.accessToken;\n },\n\n getTokenInfo(): ForgeOAuthTokens | null {\n return cachedTokens;\n },\n\n onRefresh(callback: (tokens: ForgeOAuthTokens) => void): void {\n refreshCallbacks.push(callback);\n },\n };\n}\n\n/**\n * Create a ForgeTokenManager from standard environment variables.\n *\n * Expected env vars:\n * - FORGE_TOKEN_PATH: path to the OAuth token JSON file\n * - FORGE_CLIENT_ID: OAuth client ID\n * - FORGE_CLIENT_SECRET: OAuth client secret\n *\n * @param tokenUrl The provider's token endpoint URL\n */\nexport function createForgeTokenManagerFromEnv(tokenUrl: string): ForgeTokenManager {\n const tokenPath = process.env.FORGE_TOKEN_PATH;\n const clientId = process.env.FORGE_CLIENT_ID;\n const clientSecret = process.env.FORGE_CLIENT_SECRET;\n\n if (!tokenPath) throw new Error('FORGE_TOKEN_PATH environment variable is required');\n if (!clientId) throw new Error('FORGE_CLIENT_ID environment variable is required');\n if (!clientSecret) throw new Error('FORGE_CLIENT_SECRET environment variable is required');\n\n return createForgeTokenManager({ tokenPath, clientId, clientSecret, tokenUrl });\n}\n"],"mappings":"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","names":["z","path","options","z"]}
1
+ {"version":3,"sources":["../src/index.ts","../../shared/src/mcp-auth/forge-token-manager.ts"],"sourcesContent":["/**\n * @goforgeit/mcp-asana — MCP server for Asana\n *\n * Provides task, project, and workspace tools via stdio transport.\n * Reads Forge's OAuth tokens and auto-refreshes them.\n * Uses Asana REST API directly (no SDK dependency).\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { createForgeTokenManager } from '@forge/shared/mcp-auth';\n\nconst ASANA_TOKEN_URL = 'https://app.asana.com/-/oauth_token';\nconst ASANA_BASE = 'https://app.asana.com/api/1.0';\n\nexport interface AsanaMCPOptions {\n tokenPath: string;\n clientId: string;\n clientSecret: string;\n}\n\ninterface ToolHandler {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (args: any) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;\n}\n\ninterface AsanaMCPServer {\n server: McpServer;\n getRegisteredTools(): Record<string, ToolHandler>;\n start(): Promise<void>;\n}\n\nexport function createAsanaMCPServer(options: AsanaMCPOptions): AsanaMCPServer {\n const tokenManager = createForgeTokenManager({\n tokenPath: options.tokenPath,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n tokenUrl: ASANA_TOKEN_URL,\n });\n\n const server = new McpServer({\n name: '@goforgeit/mcp-asana',\n version: '0.1.0',\n });\n\n const registeredTools: Record<string, ToolHandler> = {};\n\n function registerTool(\n name: string,\n description: string,\n schema: Record<string, z.ZodType>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (args: any) => Promise<{ content: Array<{ type: 'text'; text: string }> }>,\n ) {\n registeredTools[name] = { handler };\n server.tool(name, description, schema, handler);\n }\n\n async function asanaRequest(\n path: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',\n body?: unknown,\n ): Promise<unknown> {\n const token = await tokenManager.getAccessToken();\n const options: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n };\n if (body) options.body = JSON.stringify({ data: body });\n\n const response = await fetch(`${ASANA_BASE}${path}`, options);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Asana API error (${response.status}): ${errorText}`);\n }\n\n if (response.status === 204) return {};\n const json = (await response.json()) as { data: unknown };\n return json.data;\n }\n\n // --- Workspace Tools ---\n\n registerTool('list_workspaces', 'List all Asana workspaces', {}, async () => {\n const data = (await asanaRequest('/workspaces')) as unknown[];\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ workspaces: data }) }],\n };\n });\n\n // --- Project Tools ---\n\n registerTool(\n 'search_projects',\n 'Search Asana projects in a workspace',\n {\n workspaceGid: z.string().describe('Workspace GID'),\n query: z.string().optional().describe('Search query to filter projects by name'),\n },\n async (args: { workspaceGid: string; query?: string }) => {\n const path = `/workspaces/${args.workspaceGid}/projects?opt_fields=name,archived,color,created_at,current_status,due_on,owner,public,start_on`;\n if (args.query) {\n // Asana doesn't have native search for projects, so we fetch all and filter\n }\n const data = (await asanaRequest(path)) as unknown[];\n let results = data;\n if (args.query) {\n const q = args.query.toLowerCase();\n results = (data as Array<{ name?: string }>).filter(\n (p) => p.name && p.name.toLowerCase().includes(q),\n );\n }\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ projects: results }) }],\n };\n },\n );\n\n registerTool(\n 'get_project',\n 'Get Asana project details by GID',\n {\n projectGid: z.string().describe('Project GID'),\n },\n async (args: { projectGid: string }) => {\n const data = await asanaRequest(\n `/projects/${args.projectGid}?opt_fields=name,notes,archived,color,created_at,current_status,due_on,owner,public,start_on,members,custom_fields`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'create_project',\n 'Create a new Asana project. For organization workspaces, teamGid is required.',\n {\n workspaceGid: z.string().describe('Workspace GID'),\n name: z.string().describe('Project name'),\n teamGid: z.string().optional().describe('Team GID (required for organization workspaces)'),\n notes: z.string().optional().describe('Project description'),\n color: z.string().optional().describe('Project color (e.g. \"light-green\")'),\n isPublic: z.boolean().optional().describe('Whether the project is public'),\n },\n async (args: {\n workspaceGid: string;\n name: string;\n teamGid?: string;\n notes?: string;\n color?: string;\n isPublic?: boolean;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = {\n workspace: args.workspaceGid,\n name: args.name,\n };\n if (args.teamGid) body.team = args.teamGid;\n if (args.notes) body.notes = args.notes;\n if (args.color) body.color = args.color;\n if (args.isPublic !== undefined) body.public = args.isPublic;\n\n const data = await asanaRequest('/projects', 'POST', body);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n // --- Section Tools ---\n\n registerTool(\n 'get_project_sections',\n 'List sections in an Asana project',\n {\n projectGid: z.string().describe('Project GID'),\n },\n async (args: { projectGid: string }) => {\n const data = await asanaRequest(`/projects/${args.projectGid}/sections`);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ sections: data }) }],\n };\n },\n );\n\n registerTool(\n 'create_section',\n 'Create a section in an Asana project',\n {\n projectGid: z.string().describe('Project GID'),\n name: z.string().describe('Section name'),\n },\n async (args: { projectGid: string; name: string }) => {\n const data = await asanaRequest(`/projects/${args.projectGid}/sections`, 'POST', {\n name: args.name,\n });\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n // --- Task Tools ---\n\n registerTool(\n 'search_tasks',\n 'Search Asana tasks in a workspace with filters',\n {\n workspaceGid: z.string().describe('Workspace GID'),\n query: z.string().optional().describe('Full-text search query'),\n assigneeGid: z.string().optional().describe('Filter by assignee GID (use \"me\" for self)'),\n projectGid: z.string().optional().describe('Filter by project GID'),\n completed: z.boolean().optional().describe('Filter by completion status'),\n limit: z.number().optional().default(25).describe('Maximum results (default 25)'),\n },\n async (args: {\n workspaceGid: string;\n query?: string;\n assigneeGid?: string;\n projectGid?: string;\n completed?: boolean;\n limit?: number;\n }) => {\n const params = new URLSearchParams();\n if (args.query) params.set('text', args.query);\n if (args.assigneeGid) params.set('assignee.any', args.assigneeGid);\n if (args.projectGid) params.set('projects.any', args.projectGid);\n if (args.completed !== undefined) params.set('completed', String(args.completed));\n params.set('limit', String(args.limit || 25));\n params.set(\n 'opt_fields',\n 'name,completed,assignee,due_on,due_at,created_at,modified_at,notes,projects,tags,parent',\n );\n\n const data = await asanaRequest(\n `/workspaces/${args.workspaceGid}/tasks/search?${params.toString()}`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ tasks: data }) }],\n };\n },\n );\n\n registerTool(\n 'get_task',\n 'Get full Asana task details by GID',\n {\n taskGid: z.string().describe('Task GID'),\n },\n async (args: { taskGid: string }) => {\n const data = await asanaRequest(\n `/tasks/${args.taskGid}?opt_fields=name,notes,completed,assignee,due_on,due_at,created_at,modified_at,projects,tags,parent,subtasks,custom_fields,followers`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'create_task',\n 'Create a new Asana task',\n {\n projectGid: z.string().describe('Project GID to add the task to'),\n name: z.string().describe('Task name'),\n notes: z.string().optional().describe('Task description'),\n assigneeGid: z.string().optional().describe('Assignee GID (use \"me\" for self)'),\n dueOn: z.string().optional().describe('Due date (YYYY-MM-DD)'),\n sectionGid: z.string().optional().describe('Section GID to place the task in'),\n },\n async (args: {\n projectGid: string;\n name: string;\n notes?: string;\n assigneeGid?: string;\n dueOn?: string;\n sectionGid?: string;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = {\n projects: [args.projectGid],\n name: args.name,\n };\n if (args.notes) body.notes = args.notes;\n if (args.assigneeGid) body.assignee = args.assigneeGid;\n if (args.dueOn) body.due_on = args.dueOn;\n\n const data = await asanaRequest('/tasks', 'POST', body);\n\n // If a section was specified, add the task to it\n if (args.sectionGid && data && typeof data === 'object' && 'gid' in data) {\n await asanaRequest(`/sections/${args.sectionGid}/addTask`, 'POST', {\n task: (data as { gid: string }).gid,\n });\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'update_task',\n 'Update an existing Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n name: z.string().optional().describe('New task name'),\n notes: z.string().optional().describe('New task description'),\n completed: z.boolean().optional().describe('Mark complete/incomplete'),\n assigneeGid: z.string().optional().describe('New assignee GID'),\n dueOn: z.string().optional().describe('New due date (YYYY-MM-DD)'),\n },\n async (args: {\n taskGid: string;\n name?: string;\n notes?: string;\n completed?: boolean;\n assigneeGid?: string;\n dueOn?: string;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = {};\n if (args.name !== undefined) body.name = args.name;\n if (args.notes !== undefined) body.notes = args.notes;\n if (args.completed !== undefined) body.completed = args.completed;\n if (args.assigneeGid !== undefined) body.assignee = args.assigneeGid;\n if (args.dueOn !== undefined) body.due_on = args.dueOn;\n\n const data = await asanaRequest(`/tasks/${args.taskGid}`, 'PUT', body);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'create_subtask',\n 'Create a subtask under a parent Asana task',\n {\n parentTaskGid: z.string().describe('Parent task GID'),\n name: z.string().describe('Subtask name'),\n notes: z.string().optional().describe('Subtask description'),\n assigneeGid: z.string().optional().describe('Assignee GID'),\n dueOn: z.string().optional().describe('Due date (YYYY-MM-DD)'),\n },\n async (args: {\n parentTaskGid: string;\n name: string;\n notes?: string;\n assigneeGid?: string;\n dueOn?: string;\n }) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: Record<string, any> = { name: args.name };\n if (args.notes) body.notes = args.notes;\n if (args.assigneeGid) body.assignee = args.assigneeGid;\n if (args.dueOn) body.due_on = args.dueOn;\n\n const data = await asanaRequest(`/tasks/${args.parentTaskGid}/subtasks`, 'POST', body);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n registerTool(\n 'delete_task',\n 'Delete an Asana task',\n {\n taskGid: z.string().describe('Task GID to delete'),\n },\n async (args: { taskGid: string }) => {\n await asanaRequest(`/tasks/${args.taskGid}`, 'DELETE');\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ deleted: true, taskGid: args.taskGid }),\n },\n ],\n };\n },\n );\n\n // --- Story / Comment Tools ---\n\n registerTool(\n 'get_task_stories',\n 'Get comments and activity on an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n },\n async (args: { taskGid: string }) => {\n const data = await asanaRequest(\n `/tasks/${args.taskGid}/stories?opt_fields=text,type,created_at,created_by`,\n );\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ stories: data }) }],\n };\n },\n );\n\n registerTool(\n 'create_task_story',\n 'Add a comment to an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n text: z.string().describe('Comment text'),\n },\n async (args: { taskGid: string; text: string }) => {\n const data = await asanaRequest(`/tasks/${args.taskGid}/stories`, 'POST', {\n text: args.text,\n });\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n },\n );\n\n // --- Tag Tools ---\n\n registerTool(\n 'get_tags_for_task',\n 'List tags on an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n },\n async (args: { taskGid: string }) => {\n const data = await asanaRequest(`/tasks/${args.taskGid}/tags`);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ tags: data }) }],\n };\n },\n );\n\n registerTool(\n 'add_tag_to_task',\n 'Add a tag to an Asana task',\n {\n taskGid: z.string().describe('Task GID'),\n tagGid: z.string().describe('Tag GID to add'),\n },\n async (args: { taskGid: string; tagGid: string }) => {\n await asanaRequest(`/tasks/${args.taskGid}/addTag`, 'POST', { tag: args.tagGid });\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ added: true, taskGid: args.taskGid, tagGid: args.tagGid }),\n },\n ],\n };\n },\n );\n\n // --- User Tools ---\n\n registerTool('get_me', 'Get the authenticated Asana user', {}, async () => {\n const data = await asanaRequest('/users/me?opt_fields=name,email,workspaces');\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data) }],\n };\n });\n\n return {\n server,\n getRegisteredTools() {\n return registeredTools;\n },\n async start() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n\n// CLI entry point\nconst isMainModule =\n process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\\\/g, '/'));\nif (isMainModule || process.env.FORGE_MCP_START === 'true') {\n const tokenPath = process.env.FORGE_TOKEN_PATH;\n const clientId = process.env.FORGE_CLIENT_ID;\n const clientSecret = process.env.FORGE_CLIENT_SECRET;\n\n if (!tokenPath || !clientId || !clientSecret) {\n console.error(\n 'Required environment variables: FORGE_TOKEN_PATH, FORGE_CLIENT_ID, FORGE_CLIENT_SECRET',\n );\n process.exit(1);\n }\n\n const mcpServer = createAsanaMCPServer({ tokenPath, clientId, clientSecret });\n mcpServer.start().catch((err) => {\n console.error('Failed to start MCP server:', err);\n process.exit(1);\n });\n}\n","/**\n * Forge Token Manager — reads Forge's OAuthTokens format and handles refresh.\n *\n * Used by @goforgeit/mcp-* packages to get valid access tokens for API calls.\n * Reads from FORGE_TOKEN_PATH, validates with Zod, and refreshes when\n * the token is expired or within 5 minutes of expiry.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\n/** Zod schema for Forge's OAuth token file format */\nexport const ForgeOAuthTokensSchema = z.object({\n accessToken: z.string(),\n refreshToken: z.string().optional(),\n expiresAt: z.number(),\n providerId: z.string(),\n connectedAt: z.string(),\n scope: z.string().optional(),\n email: z.string().optional(),\n accountId: z.string().optional(),\n /** User-level access token (Slack xoxp-* token for search operations) */\n userAccessToken: z.string().optional(),\n});\n\nexport type ForgeOAuthTokens = z.infer<typeof ForgeOAuthTokensSchema>;\n\nexport interface ForgeTokenManagerOptions {\n /** Path to the Forge OAuth token JSON file */\n tokenPath: string;\n /** OAuth client ID for token refresh */\n clientId: string;\n /** OAuth client secret for token refresh */\n clientSecret: string;\n /** OAuth token endpoint URL for refresh requests */\n tokenUrl: string;\n /** Buffer in ms before expiry to trigger refresh (default: 5 minutes) */\n refreshBufferMs?: number;\n}\n\nexport interface ForgeTokenManager {\n /** Returns a valid access token, refreshing if needed */\n getAccessToken(): Promise<string>;\n /** Returns the user-level access token if available (e.g., Slack xoxp-* for search) */\n getUserAccessToken(): Promise<string | null>;\n /** Returns the current token data, or null if not yet loaded */\n getTokenInfo(): ForgeOAuthTokens | null;\n /** Register a callback for when tokens are refreshed */\n onRefresh(callback: (tokens: ForgeOAuthTokens) => void): void;\n}\n\nconst DEFAULT_REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes\n\nexport function createForgeTokenManager(options: ForgeTokenManagerOptions): ForgeTokenManager {\n const {\n tokenPath,\n clientId,\n clientSecret,\n tokenUrl,\n refreshBufferMs = DEFAULT_REFRESH_BUFFER_MS,\n } = options;\n\n let cachedTokens: ForgeOAuthTokens | null = null;\n const refreshCallbacks: Array<(tokens: ForgeOAuthTokens) => void> = [];\n\n function isExpiredOrNearExpiry(tokens: ForgeOAuthTokens): boolean {\n return Date.now() >= tokens.expiresAt - refreshBufferMs;\n }\n\n async function readTokenFile(): Promise<ForgeOAuthTokens> {\n let raw: string;\n try {\n raw = await fs.readFile(tokenPath, 'utf-8');\n } catch (err: unknown) {\n if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {\n throw new Error(`Token file not found: ${tokenPath}`);\n }\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON in token file: ${tokenPath}`);\n }\n\n const result = ForgeOAuthTokensSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(`Invalid token file format: ${result.error.message}`);\n }\n\n return result.data;\n }\n\n async function refreshToken(tokens: ForgeOAuthTokens): Promise<ForgeOAuthTokens> {\n if (!tokens.refreshToken) {\n throw new Error('No refresh token available — cannot refresh expired token');\n }\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: tokens.refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = await response.json();\n\n const refreshedTokens: ForgeOAuthTokens = {\n ...tokens,\n accessToken: data.access_token,\n refreshToken: data.refresh_token || tokens.refreshToken,\n expiresAt: Date.now() + data.expires_in * 1000,\n };\n\n // Write refreshed tokens back to file\n await fs.mkdir(path.dirname(tokenPath), { recursive: true });\n await fs.writeFile(tokenPath, JSON.stringify(refreshedTokens), { mode: 0o600 });\n\n // Notify listeners\n for (const cb of refreshCallbacks) {\n cb(refreshedTokens);\n }\n\n return refreshedTokens;\n }\n\n return {\n async getAccessToken(): Promise<string> {\n if (cachedTokens && !isExpiredOrNearExpiry(cachedTokens)) {\n return cachedTokens.accessToken;\n }\n\n const tokens = cachedTokens || (await readTokenFile());\n\n if (isExpiredOrNearExpiry(tokens)) {\n if (tokens.refreshToken) {\n cachedTokens = await refreshToken(tokens);\n } else {\n // No refresh token — return the access token as-is.\n // Some providers (e.g. Slack bot tokens) issue non-expiring tokens\n // without refresh tokens, so the expiresAt may be unreliable.\n cachedTokens = tokens;\n }\n } else {\n cachedTokens = tokens;\n }\n\n return cachedTokens.accessToken;\n },\n\n async getUserAccessToken(): Promise<string | null> {\n // Ensure tokens are loaded\n if (!cachedTokens) {\n await this.getAccessToken();\n }\n return cachedTokens?.userAccessToken ?? null;\n },\n\n getTokenInfo(): ForgeOAuthTokens | null {\n return cachedTokens;\n },\n\n onRefresh(callback: (tokens: ForgeOAuthTokens) => void): void {\n refreshCallbacks.push(callback);\n },\n };\n}\n\n/**\n * Create a ForgeTokenManager from standard environment variables.\n *\n * Expected env vars:\n * - FORGE_TOKEN_PATH: path to the OAuth token JSON file\n * - FORGE_CLIENT_ID: OAuth client ID\n * - FORGE_CLIENT_SECRET: OAuth client secret\n *\n * @param tokenUrl The provider's token endpoint URL\n */\nexport function createForgeTokenManagerFromEnv(tokenUrl: string): ForgeTokenManager {\n const tokenPath = process.env.FORGE_TOKEN_PATH;\n const clientId = process.env.FORGE_CLIENT_ID;\n const clientSecret = process.env.FORGE_CLIENT_SECRET;\n\n if (!tokenPath) throw new Error('FORGE_TOKEN_PATH environment variable is required');\n if (!clientId) throw new Error('FORGE_CLIENT_ID environment variable is required');\n if (!clientSecret) throw new Error('FORGE_CLIENT_SECRET environment variable is required');\n\n return createForgeTokenManager({ tokenPath, clientId, clientSecret, tokenUrl });\n}\n"],"mappings":"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","names":["z","path","options","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goforgeit/mcp-asana",
3
- "version": "0.4.12",
3
+ "version": "0.4.19",
4
4
  "type": "module",
5
5
  "description": "Forge MCP server for Asana (tasks, projects, workspaces)",
6
6
  "bin": {