eassist-mcp 1.0.0

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.
@@ -0,0 +1,238 @@
1
+ import { apiGet, apiPost } from '../client.js';
2
+ import { fmtActionTable, fmtInitiativeTable, fmtDate, daysUntil } from '../format.js';
3
+ import { resolveUser, resolveInitiative } from '../resolve.js';
4
+ export const reportingToolSchemas = [
5
+ {
6
+ name: 'get_overdue_actions',
7
+ description: 'List all overdue actions across the organization. Returns a table with assignee, priority, how many days overdue, and which initiative.',
8
+ inputSchema: {
9
+ type: 'object',
10
+ properties: {
11
+ limit: { type: 'number', description: 'Max actions to return (default 50)' },
12
+ },
13
+ },
14
+ },
15
+ {
16
+ name: 'get_user_actions',
17
+ description: 'Get all open actions assigned to a specific person. Accepts a name or email (fuzzy match). Returns status, priority, due date.',
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ user: { type: 'string', description: 'Name or email of the person (fuzzy matched)' },
22
+ status: { type: 'string', description: 'Filter by status: open, completed, overdue, in_progress' },
23
+ },
24
+ required: ['user'],
25
+ },
26
+ },
27
+ {
28
+ name: 'get_executive_summary',
29
+ description: 'Full ELT-level dashboard summary: initiative health, action counts, overdue list, member workload, stale actions, priority distribution, on-time rate.',
30
+ inputSchema: { type: 'object', properties: {} },
31
+ },
32
+ {
33
+ name: 'get_executive_brief',
34
+ description: 'Concise executive brief with top risks and highlights. Shorter than the full ELT summary.',
35
+ inputSchema: { type: 'object', properties: {} },
36
+ },
37
+ {
38
+ name: 'search_actions',
39
+ description: 'Search actions by keyword. Returns matching actions with status, assignee, and due date.',
40
+ inputSchema: {
41
+ type: 'object',
42
+ properties: {
43
+ query: { type: 'string', description: 'Search keyword' },
44
+ status: { type: 'string', description: 'Filter by status' },
45
+ priority: { type: 'string', description: 'Filter by priority: urgent, high, medium, low' },
46
+ limit: { type: 'number', description: 'Max results (default 20)' },
47
+ },
48
+ required: ['query'],
49
+ },
50
+ },
51
+ {
52
+ name: 'get_action_detail',
53
+ description: 'Get full details of a single action including description, comments/updates, tags, and history.',
54
+ inputSchema: {
55
+ type: 'object',
56
+ properties: {
57
+ action_id: { type: 'string', description: 'Action ID' },
58
+ },
59
+ required: ['action_id'],
60
+ },
61
+ },
62
+ {
63
+ name: 'list_initiatives',
64
+ description: 'List all initiatives with status, priority, progress %, action count, and due date.',
65
+ inputSchema: {
66
+ type: 'object',
67
+ properties: {
68
+ status: { type: 'string', description: 'Filter by status: active, completed, paused, at_risk' },
69
+ },
70
+ },
71
+ },
72
+ {
73
+ name: 'get_initiative_report',
74
+ description: 'Detailed report for a single initiative: metadata + all its actions with status, assignees, due dates.',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ initiative: { type: 'string', description: 'Initiative name (fuzzy matched) or ID' },
79
+ },
80
+ required: ['initiative'],
81
+ },
82
+ },
83
+ {
84
+ name: 'notify_overdue',
85
+ description: 'Send an email to all assignees of overdue actions in an initiative, reminding them their actions are past due. Only works if you are the initiative owner or admin.',
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties: {
89
+ initiative: { type: 'string', description: 'Initiative name (fuzzy matched) or ID' },
90
+ },
91
+ required: ['initiative'],
92
+ },
93
+ },
94
+ ];
95
+ export async function handleReportingTool(name, args) {
96
+ switch (name) {
97
+ case 'get_overdue_actions': {
98
+ const limit = args.limit ?? 50;
99
+ const data = await apiGet('/command-center', { filter: 'overdue', limit });
100
+ if (data.actions.length === 0)
101
+ return '_No overdue actions. Great job!_';
102
+ const lines = [
103
+ `## Overdue Actions (${data.stats.overdue} total)\n`,
104
+ fmtActionTable(data.actions.slice(0, limit)),
105
+ ];
106
+ return lines.join('\n');
107
+ }
108
+ case 'get_user_actions': {
109
+ const resolved = await resolveUser(args.user);
110
+ if ('error' in resolved)
111
+ return resolved.error;
112
+ const params = { assigneeId: resolved.id };
113
+ if (args.status)
114
+ params.filter = args.status;
115
+ const data = await apiGet('/command-center', params);
116
+ if (data.actions.length === 0)
117
+ return `_No actions found for **${resolved.name}**._`;
118
+ return `## Actions for ${resolved.name} (${data.stats.total} total)\n\n${fmtActionTable(data.actions)}`;
119
+ }
120
+ case 'get_executive_summary': {
121
+ const summary = await apiGet('/elt/summary');
122
+ const lines = [];
123
+ lines.push(`## Executive Summary\n`);
124
+ lines.push(`**Actions:** ${summary.actionCounts.total} total | ${summary.actionCounts.open} open | ${summary.actionCounts.overdue} overdue | ${summary.actionCounts.completed} completed`);
125
+ if (summary.onTimeRate != null)
126
+ lines.push(`**On-time rate:** ${summary.onTimeRate.toFixed(1)}%`);
127
+ if (summary.unassignedHighPriority)
128
+ lines.push(`**Unassigned high-priority:** ${summary.unassignedHighPriority}`);
129
+ if (summary.dueSoon)
130
+ lines.push(`**Due within 7 days:** ${summary.dueSoon}`);
131
+ lines.push(`\n### Initiative Health`);
132
+ lines.push(fmtInitiativeTable(summary.initiatives.map(i => ({
133
+ id: i.id, title: i.title, status: i.status, priority: i.priority,
134
+ progress: i.progress, dueDate: i.dueDate, createdAt: '',
135
+ _count: { actions: i.actionCount, members: i.memberCount },
136
+ }))));
137
+ if (summary.overdueActions.length) {
138
+ lines.push(`\n### Top Overdue Actions`);
139
+ lines.push(fmtActionTable(summary.overdueActions.slice(0, 10)));
140
+ }
141
+ if (summary.memberWorkload.length) {
142
+ lines.push(`\n### Member Workload`);
143
+ lines.push('| Name | Assigned | Open | Overdue | Completed | Stale |');
144
+ lines.push('|------|----------|------|---------|-----------|-------|');
145
+ for (const m of summary.memberWorkload) {
146
+ lines.push(`| ${m.name} | ${m.assigned} | ${m.open} | ${m.overdue} | ${m.completed} | ${m.staleCount} |`);
147
+ }
148
+ }
149
+ if (summary.staleActions.length) {
150
+ lines.push(`\n### Stale Actions (no update in 14+ days)`);
151
+ lines.push(fmtActionTable(summary.staleActions.slice(0, 10)));
152
+ }
153
+ return lines.join('\n');
154
+ }
155
+ case 'get_executive_brief': {
156
+ const data = await apiGet('/executive-brief');
157
+ return data.brief ?? '_No brief available._';
158
+ }
159
+ case 'search_actions': {
160
+ const params = { search: args.query };
161
+ if (args.status)
162
+ params.filter = args.status;
163
+ if (args.priority)
164
+ params.priority = args.priority;
165
+ const limit = args.limit ?? 20;
166
+ const data = await apiGet('/command-center', params);
167
+ if (data.actions.length === 0)
168
+ return `_No actions found matching "${args.query}"._`;
169
+ return `## Search: "${args.query}" (${data.stats.total} results)\n\n${fmtActionTable(data.actions.slice(0, limit))}`;
170
+ }
171
+ case 'get_action_detail': {
172
+ const action = await apiGet(`/actions/${args.action_id}`);
173
+ const updates = await apiGet(`/actions/${args.action_id}/updates`).catch(() => ({ updates: [] }));
174
+ const lines = [
175
+ `## ${action.title}`,
176
+ `**Status:** ${action.status} | **Priority:** ${action.priority} | **Due:** ${fmtDate(action.dueDate)} (${daysUntil(action.dueDate)})`,
177
+ `**Assignees:** ${action.assignees.map(u => u.name).join(', ') || 'Unassigned'}`,
178
+ `**Initiative:** ${action.initiative?.title ?? 'Standalone'}`,
179
+ ];
180
+ if (action.description)
181
+ lines.push(`\n**Description:**\n${action.description}`);
182
+ if (action.tags?.length)
183
+ lines.push(`**Tags:** ${action.tags.map(t => t.name).join(', ')}`);
184
+ if (updates.updates.length) {
185
+ lines.push(`\n**Updates (${updates.updates.length}):**`);
186
+ for (const u of updates.updates.slice(0, 5)) {
187
+ lines.push(`- [${fmtDate(u.createdAt)}] **${u.user.name}**: ${u.content}`);
188
+ }
189
+ }
190
+ return lines.join('\n');
191
+ }
192
+ case 'list_initiatives': {
193
+ const params = {};
194
+ if (args.status)
195
+ params.status = args.status;
196
+ const data = await apiGet('/initiatives', params);
197
+ return `## Initiatives (${data.initiatives.length})\n\n${fmtInitiativeTable(data.initiatives)}`;
198
+ }
199
+ case 'get_initiative_report': {
200
+ const name = args.initiative;
201
+ let initiativeId = name;
202
+ if (!name.match(/^[a-z0-9]{20,}$/i)) {
203
+ const resolved = await resolveInitiative(name);
204
+ if ('error' in resolved)
205
+ return resolved.error;
206
+ initiativeId = resolved.id;
207
+ }
208
+ const [init, actionsData] = await Promise.all([
209
+ apiGet(`/initiatives/${initiativeId}`),
210
+ apiGet(`/initiatives/${initiativeId}/actions`),
211
+ ]);
212
+ const lines = [
213
+ `## ${init.title}`,
214
+ `**Status:** ${init.status} | **Priority:** ${init.priority} | **Progress:** ${init.progress}% | **Due:** ${fmtDate(init.dueDate)}`,
215
+ `**Actions:** ${init._count?.actions ?? actionsData.actions.length} total`,
216
+ `\n### Actions`,
217
+ fmtActionTable(actionsData.actions),
218
+ ];
219
+ return lines.join('\n');
220
+ }
221
+ case 'notify_overdue': {
222
+ const name = args.initiative;
223
+ let initiativeId = name;
224
+ if (!name.match(/^[a-z0-9]{20,}$/i)) {
225
+ const resolved = await resolveInitiative(name);
226
+ if ('error' in resolved)
227
+ return resolved.error;
228
+ initiativeId = resolved.id;
229
+ }
230
+ const result = await apiPost(`/initiatives/${initiativeId}/notify-overdue`);
231
+ if (result.notified === 0)
232
+ return result.message ?? 'No overdue actions found — no emails sent.';
233
+ return `Sent overdue reminder emails to **${result.notified} assignee${result.notified > 1 ? 's' : ''}** covering **${result.overdueCount} overdue action${result.overdueCount > 1 ? 's' : ''}**.`;
234
+ }
235
+ default:
236
+ return `Unknown tool: ${name}`;
237
+ }
238
+ }
@@ -0,0 +1,128 @@
1
+ export interface AuthData {
2
+ apiUrl: string;
3
+ accessToken: string;
4
+ refreshToken: string;
5
+ expiresAt: string;
6
+ name?: string;
7
+ email?: string;
8
+ }
9
+ export interface User {
10
+ id: string;
11
+ name: string;
12
+ email: string;
13
+ avatar: string | null;
14
+ role: string;
15
+ }
16
+ export interface Initiative {
17
+ id: string;
18
+ title: string;
19
+ status: string;
20
+ priority: string;
21
+ progress: number;
22
+ dueDate: string | null;
23
+ createdAt: string;
24
+ creator?: {
25
+ id: string;
26
+ name: string;
27
+ };
28
+ _count?: {
29
+ actions: number;
30
+ members: number;
31
+ };
32
+ actions?: Action[];
33
+ members?: Member[];
34
+ }
35
+ export interface Member {
36
+ id: string;
37
+ userId: string;
38
+ role: string;
39
+ department: string | null;
40
+ user: {
41
+ id: string;
42
+ name: string;
43
+ email: string;
44
+ avatar: string | null;
45
+ };
46
+ }
47
+ export interface Action {
48
+ id: string;
49
+ actionNumber: number;
50
+ title: string;
51
+ description: string | null;
52
+ status: string;
53
+ priority: string;
54
+ dueDate: string | null;
55
+ createdAt: string;
56
+ updatedAt: string;
57
+ initiative: {
58
+ id: string;
59
+ title: string;
60
+ } | null;
61
+ creator?: {
62
+ id: string;
63
+ name: string;
64
+ };
65
+ assignees: {
66
+ id: string;
67
+ name: string;
68
+ avatar: string | null;
69
+ }[];
70
+ tags?: {
71
+ id: string;
72
+ name: string;
73
+ color: string;
74
+ }[];
75
+ }
76
+ export interface CommandCenterResponse {
77
+ actions: Action[];
78
+ stats: {
79
+ total: number;
80
+ open: number;
81
+ completed: number;
82
+ overdue: number;
83
+ };
84
+ nextCursor?: string;
85
+ }
86
+ export interface EltSummary {
87
+ initiatives: {
88
+ id: string;
89
+ title: string;
90
+ status: string;
91
+ priority: string;
92
+ progress: number;
93
+ dueDate: string | null;
94
+ ownerName: string;
95
+ memberCount: number;
96
+ actionCount: number;
97
+ completedCount: number;
98
+ openCount: number;
99
+ overdueCount: number;
100
+ riskScore: number;
101
+ }[];
102
+ actionCounts: {
103
+ total: number;
104
+ open: number;
105
+ overdue: number;
106
+ completed: number;
107
+ };
108
+ overdueActions: Action[];
109
+ statusFunnel: Record<string, number>;
110
+ priorityDistribution: Record<string, number>;
111
+ memberWorkload: {
112
+ id: string;
113
+ name: string;
114
+ avatar: string | null;
115
+ department: string | null;
116
+ assigned: number;
117
+ open: number;
118
+ overdue: number;
119
+ completed: number;
120
+ avgAgeDays: number;
121
+ staleCount: number;
122
+ }[];
123
+ staleActions: Action[];
124
+ longRunningActions: Action[];
125
+ onTimeRate: number | null;
126
+ unassignedHighPriority: number;
127
+ dueSoon: number;
128
+ }
package/build/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "eassist-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for EAssist — query and manage actions & initiatives via Claude",
5
+ "type": "module",
6
+ "bin": {
7
+ "eassist-mcp": "./build/index.js"
8
+ },
9
+ "main": "./build/index.js",
10
+ "files": [
11
+ "build",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.9.0",
21
+ "axios": "^1.6.0",
22
+ "open": "^10.0.0",
23
+ "zod": "^3.22.0"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.3.3"
27
+ },
28
+ "keywords": [
29
+ "mcp",
30
+ "eassist",
31
+ "model-context-protocol",
32
+ "claude",
33
+ "actions",
34
+ "initiatives"
35
+ ],
36
+ "license": "MIT",
37
+ "engines": {
38
+ "node": ">=18"
39
+ }
40
+ }