@themkn/clockify-mcp 0.1.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.
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/SECURITY.md +20 -0
- package/config.example.json +4 -0
- package/dist/clockify/client.d.ts +53 -0
- package/dist/clockify/client.js +181 -0
- package/dist/clockify/client.js.map +1 -0
- package/dist/clockify/errors.d.ts +7 -0
- package/dist/clockify/errors.js +15 -0
- package/dist/clockify/errors.js.map +1 -0
- package/dist/clockify/types.d.ts +111 -0
- package/dist/clockify/types.js +2 -0
- package/dist/clockify/types.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +49 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.js +63 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/clients.d.ts +2 -0
- package/dist/tools/clients.js +60 -0
- package/dist/tools/clients.js.map +1 -0
- package/dist/tools/coerce.d.ts +11 -0
- package/dist/tools/coerce.js +43 -0
- package/dist/tools/coerce.js.map +1 -0
- package/dist/tools/meta.d.ts +2 -0
- package/dist/tools/meta.js +21 -0
- package/dist/tools/meta.js.map +1 -0
- package/dist/tools/projects.d.ts +2 -0
- package/dist/tools/projects.js +101 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/shape.d.ts +92 -0
- package/dist/tools/shape.js +50 -0
- package/dist/tools/shape.js.map +1 -0
- package/dist/tools/tags.d.ts +2 -0
- package/dist/tools/tags.js +54 -0
- package/dist/tools/tags.js.map +1 -0
- package/dist/tools/tasks.d.ts +2 -0
- package/dist/tools/tasks.js +78 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/timeEntries.d.ts +2 -0
- package/dist/tools/timeEntries.js +161 -0
- package/dist/tools/timeEntries.js.map +1 -0
- package/package.json +39 -0
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { type ZodTypeAny } from "zod";
|
|
3
|
+
import { ClockifyClient } from "./clockify/client.js";
|
|
4
|
+
import type { Config } from "./config.js";
|
|
5
|
+
import type { ClockifyUser } from "./clockify/types.js";
|
|
6
|
+
export interface ToolDefinition<Input> {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
schema: ZodTypeAny;
|
|
10
|
+
handler: (input: Input, ctx: ToolContext) => Promise<unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolContext {
|
|
13
|
+
client: ClockifyClient;
|
|
14
|
+
config: Config;
|
|
15
|
+
user: ClockifyUser;
|
|
16
|
+
}
|
|
17
|
+
export interface ServerBootstrap {
|
|
18
|
+
config: Config;
|
|
19
|
+
user: ClockifyUser;
|
|
20
|
+
client: ClockifyClient;
|
|
21
|
+
tools: ToolDefinition<unknown>[];
|
|
22
|
+
}
|
|
23
|
+
export declare function buildServer(bootstrap: ServerBootstrap): Server;
|
|
24
|
+
export declare function runServer(bootstrap: ServerBootstrap): Promise<void>;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { ClockifyError } from "./clockify/errors.js";
|
|
6
|
+
export function buildServer(bootstrap) {
|
|
7
|
+
const { config, user, client, tools } = bootstrap;
|
|
8
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
9
|
+
const ctx = { client, config, user };
|
|
10
|
+
const mcp = new Server({ name: "clockify-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
11
|
+
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
12
|
+
tools: tools.map((t) => ({
|
|
13
|
+
name: t.name,
|
|
14
|
+
description: t.description,
|
|
15
|
+
inputSchema: toInputSchema(t.schema),
|
|
16
|
+
})),
|
|
17
|
+
}));
|
|
18
|
+
mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
19
|
+
const tool = byName.get(req.params.name);
|
|
20
|
+
if (!tool) {
|
|
21
|
+
return {
|
|
22
|
+
isError: true,
|
|
23
|
+
content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const parsed = tool.schema.safeParse(req.params.arguments ?? {});
|
|
27
|
+
if (!parsed.success) {
|
|
28
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
29
|
+
return {
|
|
30
|
+
isError: true,
|
|
31
|
+
content: [{ type: "text", text: `Invalid input: ${msg}` }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const result = await tool.handler(parsed.data, ctx);
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const text = err instanceof ClockifyError
|
|
42
|
+
? err.toUserMessage()
|
|
43
|
+
: `Unexpected error: ${err.message}`;
|
|
44
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return mcp;
|
|
48
|
+
}
|
|
49
|
+
export async function runServer(bootstrap) {
|
|
50
|
+
const server = buildServer(bootstrap);
|
|
51
|
+
const transport = new StdioServerTransport();
|
|
52
|
+
await server.connect(transport);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Project a zod schema to the JSON-Schema shape the MCP SDK expects for
|
|
56
|
+
* `Tool.inputSchema`. Delegates to zod 4's built-in `z.toJSONSchema`, then
|
|
57
|
+
* casts to the MCP SDK's narrower type (MCP requires `type: "object"` at the
|
|
58
|
+
* top level; our tool schemas always start with `z.object(...)`).
|
|
59
|
+
*/
|
|
60
|
+
function toInputSchema(schema) {
|
|
61
|
+
return z.toJSONSchema(schema, { target: "draft-7" });
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { shapeClient } from "./shape.js";
|
|
3
|
+
import { zBoolean, zPositiveInt } from "./coerce.js";
|
|
4
|
+
const idString = z.string().min(1).refine((s) => !s.includes("/"), "must not contain '/'");
|
|
5
|
+
const ListInput = z
|
|
6
|
+
.object({
|
|
7
|
+
name: z.string().min(1).optional(),
|
|
8
|
+
archived: zBoolean().optional(),
|
|
9
|
+
page: zPositiveInt().optional(),
|
|
10
|
+
pageSize: zPositiveInt(200).optional(),
|
|
11
|
+
})
|
|
12
|
+
.strict();
|
|
13
|
+
const CreateInput = z.object({ name: z.string().min(1), note: z.string().optional() }).strict();
|
|
14
|
+
const UpdateInput = z
|
|
15
|
+
.object({
|
|
16
|
+
id: idString,
|
|
17
|
+
name: z.string().min(1).optional(),
|
|
18
|
+
note: z.string().optional(),
|
|
19
|
+
archived: zBoolean().optional(),
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
const DeleteInput = z.object({ id: idString }).strict();
|
|
23
|
+
const c = (ctx) => ctx.client;
|
|
24
|
+
export const clientTools = [
|
|
25
|
+
{
|
|
26
|
+
name: "list_clients",
|
|
27
|
+
description: "List Clockify clients in the workspace.",
|
|
28
|
+
schema: ListInput,
|
|
29
|
+
handler: async (input, ctx) => {
|
|
30
|
+
const raw = await c(ctx).listClients(ctx.config.workspaceId, input);
|
|
31
|
+
return raw.map(shapeClient);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "create_client",
|
|
36
|
+
description: "Create a Clockify client.",
|
|
37
|
+
schema: CreateInput,
|
|
38
|
+
handler: async (input, ctx) => shapeClient(await c(ctx).createClient(ctx.config.workspaceId, input)),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "update_client",
|
|
42
|
+
description: "Patch a Clockify client. Any field omitted is left unchanged.",
|
|
43
|
+
schema: UpdateInput,
|
|
44
|
+
handler: async (input, ctx) => {
|
|
45
|
+
const { id, ...rest } = input;
|
|
46
|
+
return shapeClient(await c(ctx).updateClient(ctx.config.workspaceId, id, rest));
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "delete_client",
|
|
51
|
+
description: "Delete a Clockify client.",
|
|
52
|
+
schema: DeleteInput,
|
|
53
|
+
handler: async (input, ctx) => {
|
|
54
|
+
const { id } = input;
|
|
55
|
+
await c(ctx).deleteClient(ctx.config.workspaceId, id);
|
|
56
|
+
return { deleted: true, id };
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
//# sourceMappingURL=clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/tools/clients.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Accepts a boolean, or the strings "true"/"false" (case-insensitive).
|
|
4
|
+
* Rejects everything else. Used because some MCP clients stringify tool
|
|
5
|
+
* arguments before sending them.
|
|
6
|
+
*/
|
|
7
|
+
export declare function zBoolean(): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>;
|
|
8
|
+
/** Use this when you don't need chained number constraints. */
|
|
9
|
+
export declare function zNumber(): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>;
|
|
10
|
+
/** Use this for pagination-style numbers: positive int, optional max. */
|
|
11
|
+
export declare function zPositiveInt(max?: number): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const coerceBoolean = (v) => {
|
|
3
|
+
if (typeof v === "boolean")
|
|
4
|
+
return v;
|
|
5
|
+
if (typeof v === "string") {
|
|
6
|
+
const s = v.trim().toLowerCase();
|
|
7
|
+
if (s === "true")
|
|
8
|
+
return true;
|
|
9
|
+
if (s === "false")
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return v; // leave for zod to reject
|
|
13
|
+
};
|
|
14
|
+
const coerceNumber = (v) => {
|
|
15
|
+
if (typeof v === "number")
|
|
16
|
+
return v;
|
|
17
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
18
|
+
const n = Number(v);
|
|
19
|
+
if (Number.isFinite(n))
|
|
20
|
+
return n;
|
|
21
|
+
}
|
|
22
|
+
return v; // leave for zod to reject
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Accepts a boolean, or the strings "true"/"false" (case-insensitive).
|
|
26
|
+
* Rejects everything else. Used because some MCP clients stringify tool
|
|
27
|
+
* arguments before sending them.
|
|
28
|
+
*/
|
|
29
|
+
export function zBoolean() {
|
|
30
|
+
return z.preprocess(coerceBoolean, z.boolean());
|
|
31
|
+
}
|
|
32
|
+
/** Use this when you don't need chained number constraints. */
|
|
33
|
+
export function zNumber() {
|
|
34
|
+
return z.preprocess(coerceNumber, z.number());
|
|
35
|
+
}
|
|
36
|
+
/** Use this for pagination-style numbers: positive int, optional max. */
|
|
37
|
+
export function zPositiveInt(max) {
|
|
38
|
+
const base = max === undefined
|
|
39
|
+
? z.number().int().positive()
|
|
40
|
+
: z.number().int().positive().max(max);
|
|
41
|
+
return z.preprocess(coerceNumber, base);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=coerce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coerce.js","sourceRoot":"","sources":["../../src/tools/coerce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,aAAa,GAAG,CAAC,CAAU,EAAW,EAAE;IAC5C,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAC9B,IAAI,CAAC,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC;IAClC,CAAC;IACD,OAAO,CAAC,CAAC,CAAC,0BAA0B;AACtC,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,CAAU,EAAW,EAAE;IAC3C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,CAAC,CAAC,CAAC,0BAA0B;AACtC,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,CAAC,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,OAAO;IACrB,OAAO,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,IAAI,GACR,GAAG,KAAK,SAAS;QACf,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QAC7B,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3C,OAAO,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const Empty = z.object({}).strict();
|
|
3
|
+
export const metaTools = [
|
|
4
|
+
{
|
|
5
|
+
name: "get_current_user",
|
|
6
|
+
description: "Return the Clockify user this server is acting as (resolved from the configured API key at startup).",
|
|
7
|
+
schema: Empty,
|
|
8
|
+
handler: async (_input, ctx) => ({
|
|
9
|
+
id: ctx.user.id,
|
|
10
|
+
email: ctx.user.email,
|
|
11
|
+
name: ctx.user.name,
|
|
12
|
+
}),
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "get_workspace",
|
|
16
|
+
description: "Return the workspace id this server operates against.",
|
|
17
|
+
schema: Empty,
|
|
18
|
+
handler: async (_input, ctx) => ({ workspaceId: ctx.config.workspaceId }),
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/tools/meta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AAEpC,MAAM,CAAC,MAAM,SAAS,GAA8B;IAClD;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,sGAAsG;QACxG,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YAC/B,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE;YACf,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK;YACrB,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI;SACpB,CAAC;KACH;IACD;QACE,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,uDAAuD;QACpE,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;KAC1E;CACF,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ClockifyError } from "../clockify/errors.js";
|
|
3
|
+
import { shapeProject } from "./shape.js";
|
|
4
|
+
import { zBoolean, zPositiveInt } from "./coerce.js";
|
|
5
|
+
const idString = z.string().min(1).refine((s) => !s.includes("/"), "must not contain '/'");
|
|
6
|
+
const ListInput = z
|
|
7
|
+
.object({
|
|
8
|
+
name: z.string().min(1).optional(),
|
|
9
|
+
clientId: idString.optional(),
|
|
10
|
+
archived: zBoolean().optional(),
|
|
11
|
+
page: zPositiveInt().optional(),
|
|
12
|
+
pageSize: zPositiveInt(200).optional(),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
const GetInput = z.object({ id: idString }).strict();
|
|
16
|
+
const CreateInput = z
|
|
17
|
+
.object({
|
|
18
|
+
name: z.string().min(1),
|
|
19
|
+
clientId: idString.optional(),
|
|
20
|
+
color: z.string().min(1).optional(),
|
|
21
|
+
billable: zBoolean().optional(),
|
|
22
|
+
note: z.string().optional(),
|
|
23
|
+
})
|
|
24
|
+
.strict();
|
|
25
|
+
const UpdateInput = z
|
|
26
|
+
.object({
|
|
27
|
+
id: idString,
|
|
28
|
+
name: z.string().min(1).optional(),
|
|
29
|
+
clientId: idString.optional(),
|
|
30
|
+
color: z.string().min(1).optional(),
|
|
31
|
+
billable: zBoolean().optional(),
|
|
32
|
+
note: z.string().optional(),
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
35
|
+
const DeleteInput = z.object({ id: idString }).strict();
|
|
36
|
+
const c = (ctx) => ctx.client;
|
|
37
|
+
export const projectTools = [
|
|
38
|
+
{
|
|
39
|
+
name: "list_projects",
|
|
40
|
+
description: "List projects in the workspace. Filter by name substring, clientId, or archived state.",
|
|
41
|
+
schema: ListInput,
|
|
42
|
+
handler: async (input, ctx) => {
|
|
43
|
+
const i = input;
|
|
44
|
+
const raw = await c(ctx).listProjects(ctx.config.workspaceId, {
|
|
45
|
+
name: i.name,
|
|
46
|
+
clientIds: i.clientId ? [i.clientId] : undefined,
|
|
47
|
+
archived: i.archived,
|
|
48
|
+
page: i.page,
|
|
49
|
+
pageSize: i.pageSize,
|
|
50
|
+
});
|
|
51
|
+
return raw.map(shapeProject);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "get_project",
|
|
56
|
+
description: "Fetch a project by id.",
|
|
57
|
+
schema: GetInput,
|
|
58
|
+
handler: async (input, ctx) => {
|
|
59
|
+
const { id } = input;
|
|
60
|
+
return shapeProject(await c(ctx).getProject(ctx.config.workspaceId, id));
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "create_project",
|
|
65
|
+
description: "Create a project.",
|
|
66
|
+
schema: CreateInput,
|
|
67
|
+
handler: async (input, ctx) => {
|
|
68
|
+
const body = input;
|
|
69
|
+
return shapeProject(await c(ctx).createProject(ctx.config.workspaceId, body));
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "update_project",
|
|
74
|
+
description: "Patch a project. Any field omitted is left unchanged.",
|
|
75
|
+
schema: UpdateInput,
|
|
76
|
+
handler: async (input, ctx) => {
|
|
77
|
+
const { id, ...rest } = input;
|
|
78
|
+
return shapeProject(await c(ctx).updateProject(ctx.config.workspaceId, id, rest));
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "delete_project",
|
|
83
|
+
description: "Attempt to delete a project. If Clockify refuses because the project has time entries, the project is archived instead. The response reports which action ran: { action: 'deleted' } or { action: 'archived' }.",
|
|
84
|
+
schema: DeleteInput,
|
|
85
|
+
handler: async (input, ctx) => {
|
|
86
|
+
const { id } = input;
|
|
87
|
+
try {
|
|
88
|
+
await c(ctx).deleteProject(ctx.config.workspaceId, id);
|
|
89
|
+
return { action: "deleted", id };
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err instanceof ClockifyError && (err.status === 400 || err.status === 403 || err.status === 409)) {
|
|
93
|
+
await c(ctx).archiveProject(ctx.config.workspaceId, id);
|
|
94
|
+
return { action: "archived", id };
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
interface RawTimeEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
timeInterval?: {
|
|
5
|
+
start?: string;
|
|
6
|
+
end?: string | null;
|
|
7
|
+
duration?: string | null;
|
|
8
|
+
};
|
|
9
|
+
projectId?: string | null;
|
|
10
|
+
project?: {
|
|
11
|
+
id?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
} | null;
|
|
14
|
+
taskId?: string | null;
|
|
15
|
+
tagIds?: string[] | null;
|
|
16
|
+
billable?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface TimeEntrySummary {
|
|
19
|
+
id: string;
|
|
20
|
+
description: string;
|
|
21
|
+
start: string | null;
|
|
22
|
+
end: string | null;
|
|
23
|
+
durationSeconds: number | null;
|
|
24
|
+
projectId: string | null;
|
|
25
|
+
projectName: string | null;
|
|
26
|
+
taskId: string | null;
|
|
27
|
+
tagIds: string[];
|
|
28
|
+
billable: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function shapeTimeEntry(e: RawTimeEntry): TimeEntrySummary;
|
|
31
|
+
interface RawProject {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
clientId?: string | null;
|
|
35
|
+
clientName?: string | null;
|
|
36
|
+
archived?: boolean;
|
|
37
|
+
billable?: boolean;
|
|
38
|
+
color?: string | null;
|
|
39
|
+
note?: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface ProjectSummary {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
clientId: string | null;
|
|
45
|
+
clientName: string | null;
|
|
46
|
+
archived: boolean;
|
|
47
|
+
billable: boolean;
|
|
48
|
+
color: string | null;
|
|
49
|
+
}
|
|
50
|
+
export declare function shapeProject(p: RawProject): ProjectSummary;
|
|
51
|
+
interface RawTask {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
projectId: string;
|
|
55
|
+
status?: string;
|
|
56
|
+
assigneeIds?: string[];
|
|
57
|
+
estimate?: string | null;
|
|
58
|
+
}
|
|
59
|
+
export interface TaskSummary {
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
projectId: string;
|
|
63
|
+
status: string | null;
|
|
64
|
+
assigneeIds: string[];
|
|
65
|
+
estimate: string | null;
|
|
66
|
+
}
|
|
67
|
+
export declare function shapeTask(t: RawTask): TaskSummary;
|
|
68
|
+
interface RawTag {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
archived?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface TagSummary {
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
archived: boolean;
|
|
77
|
+
}
|
|
78
|
+
export declare function shapeTag(t: RawTag): TagSummary;
|
|
79
|
+
interface RawClient {
|
|
80
|
+
id: string;
|
|
81
|
+
name: string;
|
|
82
|
+
archived?: boolean;
|
|
83
|
+
note?: string | null;
|
|
84
|
+
}
|
|
85
|
+
export interface ClientSummary {
|
|
86
|
+
id: string;
|
|
87
|
+
name: string;
|
|
88
|
+
archived: boolean;
|
|
89
|
+
note: string | null;
|
|
90
|
+
}
|
|
91
|
+
export declare function shapeClient(c: RawClient): ClientSummary;
|
|
92
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export function shapeTimeEntry(e) {
|
|
2
|
+
const start = e.timeInterval?.start ?? null;
|
|
3
|
+
const end = e.timeInterval?.end ?? null;
|
|
4
|
+
return {
|
|
5
|
+
id: e.id,
|
|
6
|
+
description: e.description ?? "",
|
|
7
|
+
start,
|
|
8
|
+
end,
|
|
9
|
+
durationSeconds: computeDurationSeconds(start, end),
|
|
10
|
+
projectId: e.projectId ?? e.project?.id ?? null,
|
|
11
|
+
projectName: e.project?.name ?? null,
|
|
12
|
+
taskId: e.taskId ?? null,
|
|
13
|
+
tagIds: e.tagIds ?? [],
|
|
14
|
+
billable: e.billable ?? false,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function computeDurationSeconds(start, end) {
|
|
18
|
+
if (!start || !end)
|
|
19
|
+
return null;
|
|
20
|
+
const ms = Date.parse(end) - Date.parse(start);
|
|
21
|
+
return Number.isFinite(ms) ? Math.round(ms / 1000) : null;
|
|
22
|
+
}
|
|
23
|
+
export function shapeProject(p) {
|
|
24
|
+
return {
|
|
25
|
+
id: p.id,
|
|
26
|
+
name: p.name,
|
|
27
|
+
clientId: p.clientId ?? null,
|
|
28
|
+
clientName: p.clientName ?? null,
|
|
29
|
+
archived: p.archived ?? false,
|
|
30
|
+
billable: p.billable ?? false,
|
|
31
|
+
color: p.color ?? null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function shapeTask(t) {
|
|
35
|
+
return {
|
|
36
|
+
id: t.id,
|
|
37
|
+
name: t.name,
|
|
38
|
+
projectId: t.projectId,
|
|
39
|
+
status: t.status ?? null,
|
|
40
|
+
assigneeIds: t.assigneeIds ?? [],
|
|
41
|
+
estimate: t.estimate ?? null,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function shapeTag(t) {
|
|
45
|
+
return { id: t.id, name: t.name, archived: t.archived ?? false };
|
|
46
|
+
}
|
|
47
|
+
export function shapeClient(c) {
|
|
48
|
+
return { id: c.id, name: c.name, archived: c.archived ?? false, note: c.note ?? null };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../src/tools/shape.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,cAAc,CAAC,CAAe;IAC5C,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,EAAE,KAAK,IAAI,IAAI,CAAC;IAC5C,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,EAAE,GAAG,IAAI,IAAI,CAAC;IACxC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;QAChC,KAAK;QACL,GAAG;QACH,eAAe,EAAE,sBAAsB,CAAC,KAAK,EAAE,GAAG,CAAC;QACnD,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,IAAI;QAC/C,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,IAAI;QACpC,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI;QACxB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;QACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK;KAC9B,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAoB,EAAE,GAAkB;IACtE,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5D,CAAC;AAuBD,MAAM,UAAU,YAAY,CAAC,CAAa;IACxC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI;QAC5B,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI;QAChC,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK;QAC7B,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK;QAC7B,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI;KACvB,CAAC;AACJ,CAAC;AAoBD,MAAM,UAAU,SAAS,CAAC,CAAU;IAClC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI;QACxB,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;QAChC,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI;KAC7B,CAAC;AACJ,CAAC;AAcD,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC;AACnE,CAAC;AAgBD,MAAM,UAAU,WAAW,CAAC,CAAY;IACtC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { shapeTag } from "./shape.js";
|
|
3
|
+
import { zBoolean, zPositiveInt } from "./coerce.js";
|
|
4
|
+
const idString = z.string().min(1).refine((s) => !s.includes("/"), "must not contain '/'");
|
|
5
|
+
const ListInput = z
|
|
6
|
+
.object({
|
|
7
|
+
name: z.string().min(1).optional(),
|
|
8
|
+
archived: zBoolean().optional(),
|
|
9
|
+
page: zPositiveInt().optional(),
|
|
10
|
+
pageSize: zPositiveInt(200).optional(),
|
|
11
|
+
})
|
|
12
|
+
.strict();
|
|
13
|
+
const CreateInput = z.object({ name: z.string().min(1) }).strict();
|
|
14
|
+
const UpdateInput = z.object({ id: idString, name: z.string().min(1).optional(), archived: zBoolean().optional() }).strict();
|
|
15
|
+
const DeleteInput = z.object({ id: idString }).strict();
|
|
16
|
+
const c = (ctx) => ctx.client;
|
|
17
|
+
export const tagTools = [
|
|
18
|
+
{
|
|
19
|
+
name: "list_tags",
|
|
20
|
+
description: "List tags in the workspace. Filter by name or archived state.",
|
|
21
|
+
schema: ListInput,
|
|
22
|
+
handler: async (input, ctx) => {
|
|
23
|
+
const i = input;
|
|
24
|
+
const raw = await c(ctx).listTags(ctx.config.workspaceId, i);
|
|
25
|
+
return raw.map(shapeTag);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "create_tag",
|
|
30
|
+
description: "Create a tag.",
|
|
31
|
+
schema: CreateInput,
|
|
32
|
+
handler: async (input, ctx) => shapeTag(await c(ctx).createTag(ctx.config.workspaceId, input)),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "update_tag",
|
|
36
|
+
description: "Rename or archive a tag.",
|
|
37
|
+
schema: UpdateInput,
|
|
38
|
+
handler: async (input, ctx) => {
|
|
39
|
+
const { id, ...rest } = input;
|
|
40
|
+
return shapeTag(await c(ctx).updateTag(ctx.config.workspaceId, id, rest));
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "delete_tag",
|
|
45
|
+
description: "Delete a tag.",
|
|
46
|
+
schema: DeleteInput,
|
|
47
|
+
handler: async (input, ctx) => {
|
|
48
|
+
const { id } = input;
|
|
49
|
+
await c(ctx).deleteTag(ctx.config.workspaceId, id);
|
|
50
|
+
return { deleted: true, id };
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
//# sourceMappingURL=tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/tools/tags.ts"],"names":[],"mappings":"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"}
|