tempo-api-mcp 2.0.0 → 2.0.2
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/bundle.js +14696 -5766
- package/dist/client.js +20 -2
- package/dist/index.js +12 -47
- package/dist/tools/accounts.js +97 -154
- package/dist/tools/plans.js +81 -134
- package/dist/tools/projects.js +89 -164
- package/dist/tools/teams.js +94 -147
- package/dist/tools/worklogs.js +163 -245
- package/package.json +9 -6
- package/server.json +29 -0
package/dist/tools/worklogs.js
CHANGED
|
@@ -1,290 +1,208 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function register(server, client) {
|
|
3
|
+
server.registerTool('tempo_get_worklogs', {
|
|
4
4
|
description: 'Retrieve a list of Tempo worklogs matching the given search parameters. Supports filtering by project, issue, date range, and more.',
|
|
5
5
|
annotations: { readOnlyHint: true },
|
|
6
6
|
inputSchema: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
16
|
-
orderBy: { type: 'string', enum: ['ID', 'START_DATE_TIME', 'UPDATED'], description: 'Sort order (descending)' },
|
|
17
|
-
},
|
|
18
|
-
required: [],
|
|
7
|
+
projectId: z.array(z.number().int()).optional().describe('Filter by project ids'),
|
|
8
|
+
issueId: z.array(z.number().int()).optional().describe('Filter by issue ids'),
|
|
9
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
10
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
11
|
+
updatedFrom: z.string().optional().describe('Filter by update date/time (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ssZ)'),
|
|
12
|
+
offset: z.number().int().optional().describe('Pagination offset (default 0)'),
|
|
13
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
14
|
+
orderBy: z.enum(['ID', 'START_DATE_TIME', 'UPDATED']).optional().describe('Sort order (descending)'),
|
|
19
15
|
},
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
}, async ({ projectId, issueId, from, to, updatedFrom, offset, limit, orderBy }) => {
|
|
17
|
+
const data = await client.request('GET', '/4/worklogs', undefined, {
|
|
18
|
+
projectId, issueId, from, to, updatedFrom, offset, limit, orderBy,
|
|
19
|
+
});
|
|
20
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
21
|
+
});
|
|
22
|
+
server.registerTool('tempo_get_worklog', {
|
|
23
23
|
description: 'Retrieve a single Tempo worklog by its id.',
|
|
24
24
|
annotations: { readOnlyHint: true },
|
|
25
25
|
inputSchema: {
|
|
26
|
-
|
|
27
|
-
properties: {
|
|
28
|
-
id: { type: 'string', description: 'Worklog id' },
|
|
29
|
-
},
|
|
30
|
-
required: ['id'],
|
|
26
|
+
id: z.string().describe('Worklog id'),
|
|
31
27
|
},
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
}, async ({ id }) => {
|
|
29
|
+
const data = await client.request('GET', `/4/worklogs/${id}`);
|
|
30
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
31
|
+
});
|
|
32
|
+
server.registerTool('tempo_create_worklog', {
|
|
35
33
|
description: 'Create a new Tempo worklog.',
|
|
36
34
|
annotations: { readOnlyHint: false },
|
|
37
35
|
inputSchema: {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
billableSeconds: { type: 'integer', description: 'Billable seconds (defaults to timeSpentSeconds)' },
|
|
47
|
-
remainingEstimateSeconds: { type: 'integer', description: 'Remaining estimate in seconds' },
|
|
48
|
-
},
|
|
49
|
-
required: ['authorAccountId', 'issueId', 'startDate', 'timeSpentSeconds'],
|
|
36
|
+
authorAccountId: z.string().describe('Atlassian account id of the worklog author'),
|
|
37
|
+
issueId: z.number().int().describe('Jira issue id to log time against'),
|
|
38
|
+
startDate: z.string().describe('Work date (YYYY-MM-DD)'),
|
|
39
|
+
timeSpentSeconds: z.number().int().describe('Time spent in seconds (e.g. 3600 = 1 hour)'),
|
|
40
|
+
startTime: z.string().regex(/^([0-1]?[0-9]|2[0-3])(:[0-5][0-9])(:[0-5][0-9])$/).optional().describe('Start time (HH:mm:ss)'),
|
|
41
|
+
description: z.string().optional().describe('Description of work done'),
|
|
42
|
+
billableSeconds: z.number().int().optional().describe('Billable seconds (defaults to timeSpentSeconds)'),
|
|
43
|
+
remainingEstimateSeconds: z.number().int().optional().describe('Remaining estimate in seconds'),
|
|
50
44
|
},
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
}, async ({ authorAccountId, issueId, startDate, timeSpentSeconds, startTime, description, billableSeconds, remainingEstimateSeconds }) => {
|
|
46
|
+
const body = { authorAccountId, issueId, startDate, timeSpentSeconds };
|
|
47
|
+
if (startTime !== undefined)
|
|
48
|
+
body.startTime = startTime;
|
|
49
|
+
if (description !== undefined)
|
|
50
|
+
body.description = description;
|
|
51
|
+
if (billableSeconds !== undefined)
|
|
52
|
+
body.billableSeconds = billableSeconds;
|
|
53
|
+
if (remainingEstimateSeconds !== undefined)
|
|
54
|
+
body.remainingEstimateSeconds = remainingEstimateSeconds;
|
|
55
|
+
const data = await client.request('POST', '/4/worklogs', body);
|
|
56
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
57
|
+
});
|
|
58
|
+
server.registerTool('tempo_update_worklog', {
|
|
54
59
|
description: 'Update an existing Tempo worklog by id.',
|
|
55
60
|
annotations: { readOnlyHint: false },
|
|
56
61
|
inputSchema: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
billableSeconds: { type: 'integer', description: 'Billable seconds' },
|
|
66
|
-
remainingEstimateSeconds: { type: 'integer', description: 'Remaining estimate in seconds' },
|
|
67
|
-
},
|
|
68
|
-
required: ['id', 'authorAccountId', 'startDate', 'timeSpentSeconds'],
|
|
62
|
+
id: z.string().describe('Worklog id'),
|
|
63
|
+
authorAccountId: z.string().describe('Atlassian account id of the worklog author'),
|
|
64
|
+
startDate: z.string().describe('Work date (YYYY-MM-DD)'),
|
|
65
|
+
timeSpentSeconds: z.number().int().describe('Time spent in seconds'),
|
|
66
|
+
startTime: z.string().optional().describe('Start time (HH:mm:ss)'),
|
|
67
|
+
description: z.string().optional().describe('Description of work done'),
|
|
68
|
+
billableSeconds: z.number().int().optional().describe('Billable seconds'),
|
|
69
|
+
remainingEstimateSeconds: z.number().int().optional().describe('Remaining estimate in seconds'),
|
|
69
70
|
},
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
}, async ({ id, authorAccountId, startDate, timeSpentSeconds, startTime, description, billableSeconds, remainingEstimateSeconds }) => {
|
|
72
|
+
const body = { authorAccountId, startDate, timeSpentSeconds };
|
|
73
|
+
if (startTime !== undefined)
|
|
74
|
+
body.startTime = startTime;
|
|
75
|
+
if (description !== undefined)
|
|
76
|
+
body.description = description;
|
|
77
|
+
if (billableSeconds !== undefined)
|
|
78
|
+
body.billableSeconds = billableSeconds;
|
|
79
|
+
if (remainingEstimateSeconds !== undefined)
|
|
80
|
+
body.remainingEstimateSeconds = remainingEstimateSeconds;
|
|
81
|
+
const data = await client.request('PUT', `/4/worklogs/${id}`, body);
|
|
82
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
83
|
+
});
|
|
84
|
+
server.registerTool('tempo_delete_worklog', {
|
|
73
85
|
description: 'Delete a Tempo worklog by id.',
|
|
74
86
|
annotations: { readOnlyHint: false },
|
|
75
87
|
inputSchema: {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
id: { type: 'string', description: 'Worklog id' },
|
|
79
|
-
bypassPeriodClosuresAndApprovals: { type: 'boolean', description: 'Bypass period closures/approvals (requires Tempo Admin + Override Mode)' },
|
|
80
|
-
},
|
|
81
|
-
required: ['id'],
|
|
88
|
+
id: z.string().describe('Worklog id'),
|
|
89
|
+
bypassPeriodClosuresAndApprovals: z.boolean().optional().describe('Bypass period closures/approvals (requires Tempo Admin + Override Mode)'),
|
|
82
90
|
},
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
}, async ({ id, bypassPeriodClosuresAndApprovals }) => {
|
|
92
|
+
await client.request('DELETE', `/4/worklogs/${id}`, undefined, {
|
|
93
|
+
bypassPeriodClosuresAndApprovals,
|
|
94
|
+
});
|
|
95
|
+
return { content: [{ type: 'text', text: `Worklog ${id} deleted successfully` }] };
|
|
96
|
+
});
|
|
97
|
+
server.registerTool('tempo_search_worklogs', {
|
|
86
98
|
description: 'Search Tempo worklogs using a POST body with advanced filters (author ids, issue ids, project ids, date range).',
|
|
87
99
|
annotations: { readOnlyHint: true },
|
|
88
100
|
inputSchema: {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
offset: { type: 'integer', description: 'Pagination offset' },
|
|
100
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
101
|
-
},
|
|
102
|
-
required: [],
|
|
101
|
+
authorIds: z.array(z.string()).optional().describe('Atlassian account ids of worklog authors'),
|
|
102
|
+
issueIds: z.array(z.number().int()).optional().describe('Jira issue ids'),
|
|
103
|
+
projectIds: z.array(z.number().int()).optional().describe('Jira project ids'),
|
|
104
|
+
teamIds: z.array(z.number().int()).optional().describe('Tempo team ids'),
|
|
105
|
+
accountIds: z.array(z.string()).optional().describe('Tempo account keys'),
|
|
106
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
107
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
108
|
+
updatedFrom: z.string().optional().describe('Filter by update date'),
|
|
109
|
+
offset: z.number().int().optional().describe('Pagination offset'),
|
|
110
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
103
111
|
},
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
}, async ({ authorIds, issueIds, projectIds, teamIds, accountIds, from, to, updatedFrom, offset, limit }) => {
|
|
113
|
+
const query = {};
|
|
114
|
+
if (offset !== undefined)
|
|
115
|
+
query.offset = offset;
|
|
116
|
+
if (limit !== undefined)
|
|
117
|
+
query.limit = limit;
|
|
118
|
+
const body = {};
|
|
119
|
+
if (authorIds)
|
|
120
|
+
body.authorIds = authorIds;
|
|
121
|
+
if (issueIds)
|
|
122
|
+
body.issueIds = issueIds;
|
|
123
|
+
if (projectIds)
|
|
124
|
+
body.projectIds = projectIds;
|
|
125
|
+
if (teamIds)
|
|
126
|
+
body.teamIds = teamIds;
|
|
127
|
+
if (accountIds)
|
|
128
|
+
body.accountIds = accountIds;
|
|
129
|
+
if (from)
|
|
130
|
+
body.from = from;
|
|
131
|
+
if (to)
|
|
132
|
+
body.to = to;
|
|
133
|
+
if (updatedFrom)
|
|
134
|
+
body.updatedFrom = updatedFrom;
|
|
135
|
+
const data = await client.request('POST', '/4/worklogs/search', body, query);
|
|
136
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
137
|
+
});
|
|
138
|
+
server.registerTool('tempo_get_worklogs_by_user', {
|
|
107
139
|
description: 'Retrieve all Tempo worklogs for a specific user (Atlassian account id).',
|
|
108
140
|
annotations: { readOnlyHint: true },
|
|
109
141
|
inputSchema: {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
offset: { type: 'integer', description: 'Pagination offset' },
|
|
116
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
117
|
-
},
|
|
118
|
-
required: ['accountId'],
|
|
142
|
+
accountId: z.string().describe('Atlassian account id of the user'),
|
|
143
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
144
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
145
|
+
offset: z.number().int().optional().describe('Pagination offset'),
|
|
146
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
119
147
|
},
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
|
|
148
|
+
}, async ({ accountId, from, to, offset, limit }) => {
|
|
149
|
+
const data = await client.request('GET', `/4/worklogs/user/${accountId}`, undefined, { from, to, offset, limit });
|
|
150
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
151
|
+
});
|
|
152
|
+
server.registerTool('tempo_get_worklogs_by_project', {
|
|
123
153
|
description: 'Retrieve all Tempo worklogs for a specific Jira project.',
|
|
124
154
|
annotations: { readOnlyHint: true },
|
|
125
155
|
inputSchema: {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
offset: { type: 'integer', description: 'Pagination offset' },
|
|
132
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
133
|
-
},
|
|
134
|
-
required: ['projectId'],
|
|
156
|
+
projectId: z.number().int().describe('Jira project id'),
|
|
157
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
158
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
159
|
+
offset: z.number().int().optional().describe('Pagination offset'),
|
|
160
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
135
161
|
},
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
|
|
162
|
+
}, async ({ projectId, from, to, offset, limit }) => {
|
|
163
|
+
const data = await client.request('GET', `/4/worklogs/project/${projectId}`, undefined, { from, to, offset, limit });
|
|
164
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
165
|
+
});
|
|
166
|
+
server.registerTool('tempo_get_worklogs_by_issue', {
|
|
139
167
|
description: 'Retrieve all Tempo worklogs for a specific Jira issue.',
|
|
140
168
|
annotations: { readOnlyHint: true },
|
|
141
169
|
inputSchema: {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
offset: { type: 'integer', description: 'Pagination offset' },
|
|
148
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
149
|
-
},
|
|
150
|
-
required: ['issueId'],
|
|
170
|
+
issueId: z.number().int().describe('Jira issue id'),
|
|
171
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
172
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
173
|
+
offset: z.number().int().optional().describe('Pagination offset'),
|
|
174
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
151
175
|
},
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
}, async ({ issueId, from, to, offset, limit }) => {
|
|
177
|
+
const data = await client.request('GET', `/4/worklogs/issue/${issueId}`, undefined, { from, to, offset, limit });
|
|
178
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
179
|
+
});
|
|
180
|
+
server.registerTool('tempo_get_worklogs_by_team', {
|
|
155
181
|
description: 'Retrieve all Tempo worklogs for a specific Tempo team.',
|
|
156
182
|
annotations: { readOnlyHint: true },
|
|
157
183
|
inputSchema: {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
offset: { type: 'integer', description: 'Pagination offset' },
|
|
164
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
165
|
-
},
|
|
166
|
-
required: ['teamId'],
|
|
184
|
+
teamId: z.number().int().describe('Tempo team id'),
|
|
185
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
186
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
187
|
+
offset: z.number().int().optional().describe('Pagination offset'),
|
|
188
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
167
189
|
},
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
}, async ({ teamId, from, to, offset, limit }) => {
|
|
191
|
+
const data = await client.request('GET', `/4/worklogs/team/${teamId}`, undefined, { from, to, offset, limit });
|
|
192
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
193
|
+
});
|
|
194
|
+
server.registerTool('tempo_get_worklogs_by_account', {
|
|
171
195
|
description: 'Retrieve all Tempo worklogs associated to a Tempo account key.',
|
|
172
196
|
annotations: { readOnlyHint: true },
|
|
173
197
|
inputSchema: {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
offset: { type: 'integer', description: 'Pagination offset' },
|
|
180
|
-
limit: { type: 'integer', description: 'Max results (default 50)' },
|
|
181
|
-
},
|
|
182
|
-
required: ['accountKey'],
|
|
198
|
+
accountKey: z.string().describe('Tempo account key (e.g. ACCOUNT-123)'),
|
|
199
|
+
from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
200
|
+
to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
201
|
+
offset: z.number().int().optional().describe('Pagination offset'),
|
|
202
|
+
limit: z.number().int().optional().describe('Max results (default 50)'),
|
|
183
203
|
},
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
case 'tempo_get_worklogs': {
|
|
189
|
-
const { projectId, issueId, from, to, updatedFrom, offset, limit, orderBy } = args;
|
|
190
|
-
const data = await client.request('GET', '/4/worklogs', undefined, {
|
|
191
|
-
projectId, issueId, from, to, updatedFrom, offset, limit, orderBy,
|
|
192
|
-
});
|
|
193
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
194
|
-
}
|
|
195
|
-
case 'tempo_get_worklog': {
|
|
196
|
-
const { id } = args;
|
|
197
|
-
const data = await client.request('GET', `/4/worklogs/${id}`);
|
|
198
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
199
|
-
}
|
|
200
|
-
case 'tempo_create_worklog': {
|
|
201
|
-
const { authorAccountId, issueId, startDate, timeSpentSeconds, startTime, description, billableSeconds, remainingEstimateSeconds } = args;
|
|
202
|
-
const body = { authorAccountId, issueId, startDate, timeSpentSeconds };
|
|
203
|
-
if (startTime !== undefined)
|
|
204
|
-
body.startTime = startTime;
|
|
205
|
-
if (description !== undefined)
|
|
206
|
-
body.description = description;
|
|
207
|
-
if (billableSeconds !== undefined)
|
|
208
|
-
body.billableSeconds = billableSeconds;
|
|
209
|
-
if (remainingEstimateSeconds !== undefined)
|
|
210
|
-
body.remainingEstimateSeconds = remainingEstimateSeconds;
|
|
211
|
-
const data = await client.request('POST', '/4/worklogs', body);
|
|
212
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
213
|
-
}
|
|
214
|
-
case 'tempo_update_worklog': {
|
|
215
|
-
const { id, authorAccountId, startDate, timeSpentSeconds, startTime, description, billableSeconds, remainingEstimateSeconds } = args;
|
|
216
|
-
const body = { authorAccountId, startDate, timeSpentSeconds };
|
|
217
|
-
if (startTime !== undefined)
|
|
218
|
-
body.startTime = startTime;
|
|
219
|
-
if (description !== undefined)
|
|
220
|
-
body.description = description;
|
|
221
|
-
if (billableSeconds !== undefined)
|
|
222
|
-
body.billableSeconds = billableSeconds;
|
|
223
|
-
if (remainingEstimateSeconds !== undefined)
|
|
224
|
-
body.remainingEstimateSeconds = remainingEstimateSeconds;
|
|
225
|
-
const data = await client.request('PUT', `/4/worklogs/${id}`, body);
|
|
226
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
227
|
-
}
|
|
228
|
-
case 'tempo_delete_worklog': {
|
|
229
|
-
const { id, bypassPeriodClosuresAndApprovals } = args;
|
|
230
|
-
await client.request('DELETE', `/4/worklogs/${id}`, undefined, {
|
|
231
|
-
bypassPeriodClosuresAndApprovals,
|
|
232
|
-
});
|
|
233
|
-
return { content: [{ type: 'text', text: `Worklog ${id} deleted successfully` }] };
|
|
234
|
-
}
|
|
235
|
-
case 'tempo_search_worklogs': {
|
|
236
|
-
const { authorIds, issueIds, projectIds, teamIds, accountIds, from, to, updatedFrom, offset, limit } = args;
|
|
237
|
-
const query = {};
|
|
238
|
-
if (offset !== undefined)
|
|
239
|
-
query.offset = offset;
|
|
240
|
-
if (limit !== undefined)
|
|
241
|
-
query.limit = limit;
|
|
242
|
-
const body = {};
|
|
243
|
-
if (authorIds)
|
|
244
|
-
body.authorIds = authorIds;
|
|
245
|
-
if (issueIds)
|
|
246
|
-
body.issueIds = issueIds;
|
|
247
|
-
if (projectIds)
|
|
248
|
-
body.projectIds = projectIds;
|
|
249
|
-
if (teamIds)
|
|
250
|
-
body.teamIds = teamIds;
|
|
251
|
-
if (accountIds)
|
|
252
|
-
body.accountIds = accountIds;
|
|
253
|
-
if (from)
|
|
254
|
-
body.from = from;
|
|
255
|
-
if (to)
|
|
256
|
-
body.to = to;
|
|
257
|
-
if (updatedFrom)
|
|
258
|
-
body.updatedFrom = updatedFrom;
|
|
259
|
-
const data = await client.request('POST', '/4/worklogs/search', body, query);
|
|
260
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
261
|
-
}
|
|
262
|
-
case 'tempo_get_worklogs_by_user': {
|
|
263
|
-
const { accountId, from, to, offset, limit } = args;
|
|
264
|
-
const data = await client.request('GET', `/4/worklogs/user/${accountId}`, undefined, { from, to, offset, limit });
|
|
265
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
266
|
-
}
|
|
267
|
-
case 'tempo_get_worklogs_by_project': {
|
|
268
|
-
const { projectId, from, to, offset, limit } = args;
|
|
269
|
-
const data = await client.request('GET', `/4/worklogs/project/${projectId}`, undefined, { from, to, offset, limit });
|
|
270
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
271
|
-
}
|
|
272
|
-
case 'tempo_get_worklogs_by_issue': {
|
|
273
|
-
const { issueId, from, to, offset, limit } = args;
|
|
274
|
-
const data = await client.request('GET', `/4/worklogs/issue/${issueId}`, undefined, { from, to, offset, limit });
|
|
275
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
276
|
-
}
|
|
277
|
-
case 'tempo_get_worklogs_by_team': {
|
|
278
|
-
const { teamId, from, to, offset, limit } = args;
|
|
279
|
-
const data = await client.request('GET', `/4/worklogs/team/${teamId}`, undefined, { from, to, offset, limit });
|
|
280
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
281
|
-
}
|
|
282
|
-
case 'tempo_get_worklogs_by_account': {
|
|
283
|
-
const { accountKey, from, to, offset, limit } = args;
|
|
284
|
-
const data = await client.request('GET', `/4/worklogs/account/${accountKey}`, undefined, { from, to, offset, limit });
|
|
285
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
286
|
-
}
|
|
287
|
-
default:
|
|
288
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
289
|
-
}
|
|
204
|
+
}, async ({ accountKey, from, to, offset, limit }) => {
|
|
205
|
+
const data = await client.request('GET', `/4/worklogs/account/${accountKey}`, undefined, { from, to, offset, limit });
|
|
206
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
207
|
+
});
|
|
290
208
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tempo-api-mcp",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"mcpName": "io.github.chrischall/tempo-api-mcp",
|
|
5
|
+
"description": "Tempo API MCP server for Claude — developed and maintained by AI (Claude Code)",
|
|
6
|
+
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/chrischall/tempo-api-mcp.git"
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"dist",
|
|
16
|
-
"SKILL.md"
|
|
17
|
+
"SKILL.md",
|
|
18
|
+
"server.json"
|
|
17
19
|
],
|
|
18
20
|
"scripts": {
|
|
19
21
|
"build": "tsc && npm run bundle",
|
|
@@ -25,12 +27,13 @@
|
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
29
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"dotenv": "^17.4.0"
|
|
30
|
+
"dotenv": "^17.4.0",
|
|
31
|
+
"zod": "^4.3.6"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {
|
|
31
34
|
"@types/node": "^25.5.2",
|
|
32
35
|
"@vitest/coverage-v8": "^4.1.2",
|
|
33
|
-
"esbuild": "^0.
|
|
36
|
+
"esbuild": "^0.28.0",
|
|
34
37
|
"typescript": "^6.0.2",
|
|
35
38
|
"vitest": "^4.1.2"
|
|
36
39
|
}
|
package/server.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.chrischall/tempo-api-mcp",
|
|
4
|
+
"description": "Tempo time-tracking for Claude — worklogs, plans, and timesheet approvals",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/chrischall/tempo-api-mcp",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "2.0.2",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "tempo-api-mcp",
|
|
14
|
+
"version": "2.0.2",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
},
|
|
18
|
+
"environmentVariables": [
|
|
19
|
+
{
|
|
20
|
+
"name": "TEMPO_API_TOKEN",
|
|
21
|
+
"description": "Your Tempo API token (Settings → API integration in your Tempo workspace)",
|
|
22
|
+
"isRequired": true,
|
|
23
|
+
"format": "string",
|
|
24
|
+
"isSecret": true
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|