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.
@@ -1,290 +1,208 @@
1
- export const toolDefinitions = [
2
- {
3
- name: 'tempo_get_worklogs',
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
- type: 'object',
8
- properties: {
9
- projectId: { type: 'array', items: { type: 'integer' }, description: 'Filter by project ids' },
10
- issueId: { type: 'array', items: { type: 'integer' }, description: 'Filter by issue ids' },
11
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
12
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
13
- updatedFrom: { type: 'string', description: 'Filter by update date/time (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ssZ)' },
14
- offset: { type: 'integer', description: 'Pagination offset (default 0)' },
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
- name: 'tempo_get_worklog',
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
- type: 'object',
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
- name: 'tempo_create_worklog',
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
- type: 'object',
39
- properties: {
40
- authorAccountId: { type: 'string', description: 'Atlassian account id of the worklog author' },
41
- issueId: { type: 'integer', description: 'Jira issue id to log time against' },
42
- startDate: { type: 'string', description: 'Work date (YYYY-MM-DD)' },
43
- timeSpentSeconds: { type: 'integer', description: 'Time spent in seconds (e.g. 3600 = 1 hour)' },
44
- startTime: { type: 'string', description: 'Start time (HH:mm:ss)', pattern: '^([0-1]?[0-9]|2[0-3])(:[0-5][0-9])(:[0-5][0-9])$' },
45
- description: { type: 'string', description: 'Description of work done' },
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
- name: 'tempo_update_worklog',
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
- type: 'object',
58
- properties: {
59
- id: { type: 'string', description: 'Worklog id' },
60
- authorAccountId: { type: 'string', description: 'Atlassian account id of the worklog author' },
61
- startDate: { type: 'string', description: 'Work date (YYYY-MM-DD)' },
62
- timeSpentSeconds: { type: 'integer', description: 'Time spent in seconds' },
63
- startTime: { type: 'string', description: 'Start time (HH:mm:ss)' },
64
- description: { type: 'string', description: 'Description of work done' },
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
- name: 'tempo_delete_worklog',
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
- type: 'object',
77
- properties: {
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
- name: 'tempo_search_worklogs',
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
- type: 'object',
90
- properties: {
91
- authorIds: { type: 'array', items: { type: 'string' }, description: 'Atlassian account ids of worklog authors' },
92
- issueIds: { type: 'array', items: { type: 'integer' }, description: 'Jira issue ids' },
93
- projectIds: { type: 'array', items: { type: 'integer' }, description: 'Jira project ids' },
94
- teamIds: { type: 'array', items: { type: 'integer' }, description: 'Tempo team ids' },
95
- accountIds: { type: 'array', items: { type: 'string' }, description: 'Tempo account keys' },
96
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
97
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
98
- updatedFrom: { type: 'string', description: 'Filter by update date' },
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
- name: 'tempo_get_worklogs_by_user',
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
- type: 'object',
111
- properties: {
112
- accountId: { type: 'string', description: 'Atlassian account id of the user' },
113
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
114
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
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
- name: 'tempo_get_worklogs_by_project',
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
- type: 'object',
127
- properties: {
128
- projectId: { type: 'integer', description: 'Jira project id' },
129
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
130
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
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
- name: 'tempo_get_worklogs_by_issue',
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
- type: 'object',
143
- properties: {
144
- issueId: { type: 'integer', description: 'Jira issue id' },
145
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
146
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
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
- name: 'tempo_get_worklogs_by_team',
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
- type: 'object',
159
- properties: {
160
- teamId: { type: 'integer', description: 'Tempo team id' },
161
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
162
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
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
- name: 'tempo_get_worklogs_by_account',
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
- type: 'object',
175
- properties: {
176
- accountKey: { type: 'string', description: 'Tempo account key (e.g. ACCOUNT-123)' },
177
- from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
178
- to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
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
- export async function handleTool(name, args, client) {
187
- switch (name) {
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.0",
4
- "description": "Tempo API MCP server for Claude — developed and maintained by AI (Claude Sonnet 4.6)",
5
- "author": "Claude Sonnet 4.6 (AI) <https://www.anthropic.com/claude>",
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.27.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
+ }