@ttctl/mcp 0.0.0 → 0.1.0-rc.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/README.md +72 -9
- package/dist/auth.d.ts +40 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +69 -0
- package/dist/auth.js.map +1 -0
- package/dist/data-handling.d.ts +91 -0
- package/dist/data-handling.d.ts.map +1 -0
- package/dist/data-handling.js +129 -0
- package/dist/data-handling.js.map +1 -0
- package/dist/diagnostic.d.ts +262 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +362 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/errors.d.ts +54 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +48 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/kill-switch-hook.d.ts +67 -0
- package/dist/kill-switch-hook.d.ts.map +1 -0
- package/dist/kill-switch-hook.js +61 -0
- package/dist/kill-switch-hook.js.map +1 -0
- package/dist/server.d.ts +100 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +157 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/_shared.d.ts +227 -0
- package/dist/tools/_shared.d.ts.map +1 -0
- package/dist/tools/_shared.js +238 -0
- package/dist/tools/_shared.js.map +1 -0
- package/dist/tools/applications.d.ts +27 -0
- package/dist/tools/applications.d.ts.map +1 -0
- package/dist/tools/applications.js +192 -0
- package/dist/tools/applications.js.map +1 -0
- package/dist/tools/availability.d.ts +33 -0
- package/dist/tools/availability.d.ts.map +1 -0
- package/dist/tools/availability.js +272 -0
- package/dist/tools/availability.js.map +1 -0
- package/dist/tools/contracts.d.ts +29 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +157 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/engagements.d.ts +36 -0
- package/dist/tools/engagements.d.ts.map +1 -0
- package/dist/tools/engagements.js +408 -0
- package/dist/tools/engagements.js.map +1 -0
- package/dist/tools/file-upload.d.ts +133 -0
- package/dist/tools/file-upload.d.ts.map +1 -0
- package/dist/tools/file-upload.js +247 -0
- package/dist/tools/file-upload.js.map +1 -0
- package/dist/tools/index.d.ts +28 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +133 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/jobs.d.ts +37 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +505 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/output-schemas.d.ts +129 -0
- package/dist/tools/output-schemas.d.ts.map +1 -0
- package/dist/tools/output-schemas.js +138 -0
- package/dist/tools/output-schemas.js.map +1 -0
- package/dist/tools/payments.d.ts +36 -0
- package/dist/tools/payments.d.ts.map +1 -0
- package/dist/tools/payments.js +373 -0
- package/dist/tools/payments.js.map +1 -0
- package/dist/tools/profile/certifications.d.ts +18 -0
- package/dist/tools/profile/certifications.d.ts.map +1 -0
- package/dist/tools/profile/certifications.js +219 -0
- package/dist/tools/profile/certifications.js.map +1 -0
- package/dist/tools/profile/education.d.ts +23 -0
- package/dist/tools/profile/education.d.ts.map +1 -0
- package/dist/tools/profile/education.js +222 -0
- package/dist/tools/profile/education.js.map +1 -0
- package/dist/tools/profile/employment.d.ts +23 -0
- package/dist/tools/profile/employment.d.ts.map +1 -0
- package/dist/tools/profile/employment.js +254 -0
- package/dist/tools/profile/employment.js.map +1 -0
- package/dist/tools/profile/industries.d.ts +30 -0
- package/dist/tools/profile/industries.d.ts.map +1 -0
- package/dist/tools/profile/industries.js +196 -0
- package/dist/tools/profile/industries.js.map +1 -0
- package/dist/tools/profile/portfolio.d.ts +22 -0
- package/dist/tools/profile/portfolio.d.ts.map +1 -0
- package/dist/tools/profile/portfolio.js +341 -0
- package/dist/tools/profile/portfolio.js.map +1 -0
- package/dist/tools/profile/resume.d.ts +16 -0
- package/dist/tools/profile/resume.d.ts.map +1 -0
- package/dist/tools/profile/resume.js +107 -0
- package/dist/tools/profile/resume.js.map +1 -0
- package/dist/tools/profile/shared.d.ts +85 -0
- package/dist/tools/profile/shared.d.ts.map +1 -0
- package/dist/tools/profile/shared.js +128 -0
- package/dist/tools/profile/shared.js.map +1 -0
- package/dist/tools/profile/visas.d.ts +15 -0
- package/dist/tools/profile/visas.d.ts.map +1 -0
- package/dist/tools/profile/visas.js +170 -0
- package/dist/tools/profile/visas.js.map +1 -0
- package/dist/tools/profile_basic_photo_show.d.ts +14 -0
- package/dist/tools/profile_basic_photo_show.d.ts.map +1 -0
- package/dist/tools/profile_basic_photo_show.js +59 -0
- package/dist/tools/profile_basic_photo_show.js.map +1 -0
- package/dist/tools/profile_basic_photo_upload.d.ts +24 -0
- package/dist/tools/profile_basic_photo_upload.d.ts.map +1 -0
- package/dist/tools/profile_basic_photo_upload.js +90 -0
- package/dist/tools/profile_basic_photo_upload.js.map +1 -0
- package/dist/tools/profile_basic_show.d.ts +64 -0
- package/dist/tools/profile_basic_show.d.ts.map +1 -0
- package/dist/tools/profile_basic_show.js +108 -0
- package/dist/tools/profile_basic_show.js.map +1 -0
- package/dist/tools/profile_basic_update.d.ts +37 -0
- package/dist/tools/profile_basic_update.d.ts.map +1 -0
- package/dist/tools/profile_basic_update.js +97 -0
- package/dist/tools/profile_basic_update.js.map +1 -0
- package/dist/tools/profile_external_advanced_wizard_show.d.ts +14 -0
- package/dist/tools/profile_external_advanced_wizard_show.d.ts.map +1 -0
- package/dist/tools/profile_external_advanced_wizard_show.js +56 -0
- package/dist/tools/profile_external_advanced_wizard_show.js.map +1 -0
- package/dist/tools/profile_external_custom_requirements_set.d.ts +13 -0
- package/dist/tools/profile_external_custom_requirements_set.d.ts.map +1 -0
- package/dist/tools/profile_external_custom_requirements_set.js +75 -0
- package/dist/tools/profile_external_custom_requirements_set.js.map +1 -0
- package/dist/tools/profile_external_custom_requirements_show.d.ts +14 -0
- package/dist/tools/profile_external_custom_requirements_show.d.ts.map +1 -0
- package/dist/tools/profile_external_custom_requirements_show.js +56 -0
- package/dist/tools/profile_external_custom_requirements_show.js.map +1 -0
- package/dist/tools/profile_external_readiness.d.ts +12 -0
- package/dist/tools/profile_external_readiness.d.ts.map +1 -0
- package/dist/tools/profile_external_readiness.js +54 -0
- package/dist/tools/profile_external_readiness.js.map +1 -0
- package/dist/tools/profile_external_recommendations.d.ts +15 -0
- package/dist/tools/profile_external_recommendations.d.ts.map +1 -0
- package/dist/tools/profile_external_recommendations.js +57 -0
- package/dist/tools/profile_external_recommendations.js.map +1 -0
- package/dist/tools/profile_external_show.d.ts +15 -0
- package/dist/tools/profile_external_show.d.ts.map +1 -0
- package/dist/tools/profile_external_show.js +59 -0
- package/dist/tools/profile_external_show.js.map +1 -0
- package/dist/tools/profile_external_update.d.ts +14 -0
- package/dist/tools/profile_external_update.d.ts.map +1 -0
- package/dist/tools/profile_external_update.js +79 -0
- package/dist/tools/profile_external_update.js.map +1 -0
- package/dist/tools/profile_reviews_approve_item.d.ts +17 -0
- package/dist/tools/profile_reviews_approve_item.d.ts.map +1 -0
- package/dist/tools/profile_reviews_approve_item.js +77 -0
- package/dist/tools/profile_reviews_approve_item.js.map +1 -0
- package/dist/tools/profile_reviews_approve_section.d.ts +15 -0
- package/dist/tools/profile_reviews_approve_section.d.ts.map +1 -0
- package/dist/tools/profile_reviews_approve_section.js +70 -0
- package/dist/tools/profile_reviews_approve_section.js.map +1 -0
- package/dist/tools/profile_reviews_list.d.ts +16 -0
- package/dist/tools/profile_reviews_list.d.ts.map +1 -0
- package/dist/tools/profile_reviews_list.js +58 -0
- package/dist/tools/profile_reviews_list.js.map +1 -0
- package/dist/tools/profile_reviews_submit_for_review.d.ts +14 -0
- package/dist/tools/profile_reviews_submit_for_review.d.ts.map +1 -0
- package/dist/tools/profile_reviews_submit_for_review.js +56 -0
- package/dist/tools/profile_reviews_submit_for_review.js.map +1 -0
- package/dist/tools/profile_skills_add.d.ts +4 -0
- package/dist/tools/profile_skills_add.d.ts.map +1 -0
- package/dist/tools/profile_skills_add.js +52 -0
- package/dist/tools/profile_skills_add.js.map +1 -0
- package/dist/tools/profile_skills_autocomplete.d.ts +4 -0
- package/dist/tools/profile_skills_autocomplete.d.ts.map +1 -0
- package/dist/tools/profile_skills_autocomplete.js +78 -0
- package/dist/tools/profile_skills_autocomplete.js.map +1 -0
- package/dist/tools/profile_skills_list.d.ts +16 -0
- package/dist/tools/profile_skills_list.d.ts.map +1 -0
- package/dist/tools/profile_skills_list.js +65 -0
- package/dist/tools/profile_skills_list.js.map +1 -0
- package/dist/tools/profile_skills_readiness.d.ts +4 -0
- package/dist/tools/profile_skills_readiness.d.ts.map +1 -0
- package/dist/tools/profile_skills_readiness.js +53 -0
- package/dist/tools/profile_skills_readiness.js.map +1 -0
- package/dist/tools/profile_skills_remove.d.ts +4 -0
- package/dist/tools/profile_skills_remove.d.ts.map +1 -0
- package/dist/tools/profile_skills_remove.js +53 -0
- package/dist/tools/profile_skills_remove.js.map +1 -0
- package/dist/tools/profile_skills_show.d.ts +4 -0
- package/dist/tools/profile_skills_show.d.ts.map +1 -0
- package/dist/tools/profile_skills_show.js +51 -0
- package/dist/tools/profile_skills_show.js.map +1 -0
- package/dist/tools/profile_skills_update.d.ts +11 -0
- package/dist/tools/profile_skills_update.d.ts.map +1 -0
- package/dist/tools/profile_skills_update.js +97 -0
- package/dist/tools/profile_skills_update.js.map +1 -0
- package/dist/tools/timesheet.d.ts +29 -0
- package/dist/tools/timesheet.d.ts.map +1 -0
- package/dist/tools/timesheet.js +257 -0
- package/dist/tools/timesheet.js.map +1 -0
- package/package.json +33 -13
- package/index.js +0 -7
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { timesheet } from "@ttctl/core";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { ttctlErrorToToolResponseOrNull } from "../errors.js";
|
|
6
|
+
import { buildMcpDryRunPreview, dryRunResponse } from "./_shared.js";
|
|
7
|
+
const DRY_RUN_FIELD = z
|
|
8
|
+
.boolean()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Preview the request without executing. Returns `{ ok: true, dryRun: true, preview }` with operationName + variables + redacted bearer header. Default: false.");
|
|
11
|
+
/**
|
|
12
|
+
* Placeholder cycle id used in the `timesheet_submit` dry-run preview
|
|
13
|
+
* when the caller omits `id` (auto-resolve at apply time). Apply path
|
|
14
|
+
* resolves the real id via {@link timesheet.resolveCurrentCycle};
|
|
15
|
+
* dry-run skips that prefetch and stamps this sentinel value into the
|
|
16
|
+
* preview's `variables.id` so the wire-shape is honest about the
|
|
17
|
+
* pending resolution.
|
|
18
|
+
*/
|
|
19
|
+
const SUBMIT_AUTO_RESOLVE_PLACEHOLDER = "<auto-resolved-at-apply-time>";
|
|
20
|
+
/**
|
|
21
|
+
* Register the `ttctl_timesheet_*` MCP tools per the #13 spec. Tool
|
|
22
|
+
* names use the `ttctl_` prefix and the canonical CLI path joined
|
|
23
|
+
* with `_`:
|
|
24
|
+
*
|
|
25
|
+
* - `ttctl_timesheet_list`
|
|
26
|
+
* - `ttctl_timesheet_show`
|
|
27
|
+
* - `ttctl_timesheet_submit`
|
|
28
|
+
*
|
|
29
|
+
* Each tool maps 1:1 to a CLI leaf — schemas describe the same set of
|
|
30
|
+
* fields. Identity model:
|
|
31
|
+
*
|
|
32
|
+
* - `BillingCycle.id` — the "timesheet id" returned by
|
|
33
|
+
* `timesheet_list` and consumed by
|
|
34
|
+
* `timesheet_show` / `timesheet_submit`.
|
|
35
|
+
* - `JobActivityItem.id` — the "engagement id" exposed by
|
|
36
|
+
* `engagements_list`. Passed via
|
|
37
|
+
* `engagement` to scope.
|
|
38
|
+
*
|
|
39
|
+
* Submit is destructive — its tool description explicitly warns
|
|
40
|
+
* humans, and per #13 the CLI gates on `--confirm` / TTY interactive
|
|
41
|
+
* confirm. The MCP side has no analogous gate (LLM clients are
|
|
42
|
+
* expected to confirm with the human before invoking write tools)
|
|
43
|
+
* but the description and rationale are surfaced verbatim.
|
|
44
|
+
*/
|
|
45
|
+
export function registerTimesheetTools(server, ctx) {
|
|
46
|
+
server.registerTool("ttctl_timesheet_list", {
|
|
47
|
+
title: "List timesheet billing cycles",
|
|
48
|
+
description: [
|
|
49
|
+
"List the signed-in user's Toptal Talent timesheet billing cycles.",
|
|
50
|
+
"A timesheet is a per-billing-cycle bucket of time entries that the",
|
|
51
|
+
"talent submits for client billing.",
|
|
52
|
+
"",
|
|
53
|
+
"Default scope: viewer-wide pending timesheets (what currently needs",
|
|
54
|
+
"submission). Pass `engagement` (jobActivityItem.id from",
|
|
55
|
+
"`engagements_list`) to scope to one engagement (returns ALL cycles for",
|
|
56
|
+
"that engagement, regardless of submission state).",
|
|
57
|
+
"",
|
|
58
|
+
"Example user prompts:",
|
|
59
|
+
' - "Show my pending Toptal timesheets."',
|
|
60
|
+
' - "What timesheets do I need to submit?"',
|
|
61
|
+
' - "List all timesheets for engagement act_xyz."',
|
|
62
|
+
].join("\n"),
|
|
63
|
+
inputSchema: {
|
|
64
|
+
engagement: z
|
|
65
|
+
.string()
|
|
66
|
+
.optional()
|
|
67
|
+
.describe("Scope to one engagement (jobActivityItem.id from `engagements_list`). Omit for viewer-wide pending."),
|
|
68
|
+
dryRun: DRY_RUN_FIELD,
|
|
69
|
+
},
|
|
70
|
+
}, async (args) => {
|
|
71
|
+
const auth = await ctx.resolveToolAuth();
|
|
72
|
+
if (!auth.ok)
|
|
73
|
+
return auth.response;
|
|
74
|
+
const opts = {};
|
|
75
|
+
if (args.engagement !== undefined)
|
|
76
|
+
opts.engagement = args.engagement;
|
|
77
|
+
if (args.dryRun === true) {
|
|
78
|
+
// Apply path picks PendingTimesheets (no engagement) or
|
|
79
|
+
// Timesheets($jobActivityItemId) (engagement scoped); mirror
|
|
80
|
+
// that branch in the preview so callers see the exact wire
|
|
81
|
+
// operation that would fire.
|
|
82
|
+
if (opts.engagement === undefined) {
|
|
83
|
+
return dryRunResponse(buildMcpDryRunPreview("PendingTimesheets", "mobile-gateway", {}, auth.token));
|
|
84
|
+
}
|
|
85
|
+
return dryRunResponse(buildMcpDryRunPreview("Timesheets", "mobile-gateway", { jobActivityItemId: opts.engagement }, auth.token));
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const items = await timesheet.list(auth.token, opts);
|
|
89
|
+
return successResponse(items);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return mapTimesheetError(err);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
server.registerTool("ttctl_timesheet_show", {
|
|
96
|
+
title: "Show one timesheet by id",
|
|
97
|
+
description: [
|
|
98
|
+
"Fetch a single timesheet's full detail by BillingCycle.id (the id",
|
|
99
|
+
"returned by `ttctl_timesheet_list`). Returns the cycle metadata",
|
|
100
|
+
"(week range, hours, submission state), the engagement+job reference,",
|
|
101
|
+
"the rate agreement snapshot, the comment (if any), and the per-day",
|
|
102
|
+
"time entries (`timesheetRecords`).",
|
|
103
|
+
"",
|
|
104
|
+
"Example user prompts:",
|
|
105
|
+
' - "Show me the details of timesheet bc_abc123."',
|
|
106
|
+
' - "What did I log for the May 1-15 cycle on engagement act_xyz?"',
|
|
107
|
+
].join("\n"),
|
|
108
|
+
inputSchema: {
|
|
109
|
+
id: z.string().describe("Timesheet id (BillingCycle.id from `timesheet_list`)"),
|
|
110
|
+
dryRun: DRY_RUN_FIELD,
|
|
111
|
+
},
|
|
112
|
+
}, async (args) => {
|
|
113
|
+
const auth = await ctx.resolveToolAuth();
|
|
114
|
+
if (!auth.ok)
|
|
115
|
+
return auth.response;
|
|
116
|
+
if (args.dryRun === true) {
|
|
117
|
+
return dryRunResponse(buildMcpDryRunPreview("TimesheetDetails", "mobile-gateway", { id: args.id }, auth.token));
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const item = await timesheet.show(auth.token, args.id);
|
|
121
|
+
return successResponse(item);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
return mapTimesheetError(err);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
server.registerTool("ttctl_timesheet_submit", {
|
|
128
|
+
title: "Submit a timesheet for billing (DESTRUCTIVE — one-way)",
|
|
129
|
+
description: [
|
|
130
|
+
"Submit a timesheet (BillingCycle.id) for billing. The submission is",
|
|
131
|
+
"one-way at the wire level — once submitted, the timesheet enters",
|
|
132
|
+
"Toptal's billing pipeline and cannot be retracted from this CLI.",
|
|
133
|
+
"",
|
|
134
|
+
"**This is a write operation**. Confirm with the user BEFORE invoking,",
|
|
135
|
+
"including the cycle id, week range, and hours about to be submitted.",
|
|
136
|
+
"",
|
|
137
|
+
"`id` is optional: omitted, the tool resolves the single currently-pending",
|
|
138
|
+
"cycle whose submission window contains 'now'. If zero or multiple",
|
|
139
|
+
"cycles match, the tool returns a structured error so the LLM can",
|
|
140
|
+
"disambiguate before retrying with an explicit id.",
|
|
141
|
+
"",
|
|
142
|
+
"Optional `engagement` (jobActivityItem.id) scopes the auto-resolve to",
|
|
143
|
+
"one engagement — useful when the user has multiple parallel",
|
|
144
|
+
"engagements with overlapping current cycles.",
|
|
145
|
+
"",
|
|
146
|
+
"**`dryRun` caveat**: when `id` is omitted AND `dryRun: true`, the preview",
|
|
147
|
+
"stamps the literal placeholder string `<auto-resolved-at-apply-time>`",
|
|
148
|
+
"into `variables.id` — the apply path's auto-resolve read is SKIPPED on",
|
|
149
|
+
"dry-run (no wire call). Operation name, surface, header redaction, and",
|
|
150
|
+
"variable shape are verbatim; only the cycle-id VALUE is deferred. Do NOT",
|
|
151
|
+
"treat the placeholder as a real BillingCycle id.",
|
|
152
|
+
"",
|
|
153
|
+
"Example user prompts:",
|
|
154
|
+
' - "Submit my current Toptal timesheet."',
|
|
155
|
+
' - "Submit timesheet bc_abc123."',
|
|
156
|
+
' - "Submit my pending timesheet for engagement act_xyz."',
|
|
157
|
+
].join("\n"),
|
|
158
|
+
inputSchema: {
|
|
159
|
+
id: z
|
|
160
|
+
.string()
|
|
161
|
+
.optional()
|
|
162
|
+
.describe("Timesheet id (BillingCycle.id) to submit. Omit for current-pending auto-resolve."),
|
|
163
|
+
engagement: z
|
|
164
|
+
.string()
|
|
165
|
+
.optional()
|
|
166
|
+
.describe("Scope auto-resolve to one engagement (jobActivityItem.id). Ignored when `id` is supplied."),
|
|
167
|
+
dryRun: DRY_RUN_FIELD,
|
|
168
|
+
},
|
|
169
|
+
}, async (args) => {
|
|
170
|
+
const auth = await ctx.resolveToolAuth();
|
|
171
|
+
if (!auth.ok)
|
|
172
|
+
return auth.response;
|
|
173
|
+
if (args.dryRun === true) {
|
|
174
|
+
// Apply path optionally pre-fetches PendingTimesheets to auto-resolve
|
|
175
|
+
// the cycle id when none is supplied. Dry-run SKIPS that prefetch
|
|
176
|
+
// (per the same "no wire call" semantics engagements.breaks.add
|
|
177
|
+
// applies for its engagement-id translation) and stamps a placeholder
|
|
178
|
+
// string into the SubmitTimesheet preview's `variables.id`. The
|
|
179
|
+
// wire SHAPE (operationName, surface, variables key names, redacted
|
|
180
|
+
// headers) is verbatim; only the cycle-id VALUE is deferred to apply.
|
|
181
|
+
const variables = { id: args.id ?? SUBMIT_AUTO_RESOLVE_PLACEHOLDER };
|
|
182
|
+
return dryRunResponse(buildMcpDryRunPreview("SubmitTimesheet", "mobile-gateway", variables, auth.token));
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
let cycleId;
|
|
186
|
+
if (args.id !== undefined) {
|
|
187
|
+
cycleId = args.id;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const resolveOpts = {};
|
|
191
|
+
if (args.engagement !== undefined)
|
|
192
|
+
resolveOpts.engagement = args.engagement;
|
|
193
|
+
const resolution = await timesheet.resolveCurrentCycle(auth.token, resolveOpts);
|
|
194
|
+
if (resolution.kind === "none") {
|
|
195
|
+
return errorResponse("NO_CURRENT_CYCLE", "No billing cycle is currently in its submission window.", "Run `ttctl_timesheet_list` to see what's pending, or specify an id explicitly.");
|
|
196
|
+
}
|
|
197
|
+
if (resolution.kind === "multiple") {
|
|
198
|
+
const lines = resolution.candidates.map((c) => {
|
|
199
|
+
const client = c.engagement.job.client?.fullName ?? "(no client)";
|
|
200
|
+
const title = c.engagement.job.title ?? "(untitled)";
|
|
201
|
+
return ` - ${c.id} (${client} — ${title}, ${c.startDate} → ${c.endDate})`;
|
|
202
|
+
});
|
|
203
|
+
return errorResponse("MULTIPLE_CURRENT_CYCLES", `${resolution.candidates.length.toString()} cycles match the current submission window — specify one explicitly.`, `Candidates:\n${lines.join("\n")}`);
|
|
204
|
+
}
|
|
205
|
+
cycleId = resolution.cycle.id;
|
|
206
|
+
}
|
|
207
|
+
const updated = await timesheet.submit(auth.token, cycleId);
|
|
208
|
+
return successResponse(updated);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
return mapTimesheetError(err);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function successResponse(data) {
|
|
216
|
+
return {
|
|
217
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function errorResponse(code, message, recovery) {
|
|
221
|
+
return {
|
|
222
|
+
isError: true,
|
|
223
|
+
content: [
|
|
224
|
+
{
|
|
225
|
+
type: "text",
|
|
226
|
+
text: [`Error: ${message}`, "", `Recovery: ${recovery}`, "", `(Code: ${code})`].join("\n"),
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function mapTimesheetError(err) {
|
|
232
|
+
const typed = ttctlErrorToToolResponseOrNull(err);
|
|
233
|
+
if (typed !== null)
|
|
234
|
+
return typed;
|
|
235
|
+
if (err instanceof timesheet.TimesheetError) {
|
|
236
|
+
return errorResponse(err.code, err.message, recoveryForCode(err.code));
|
|
237
|
+
}
|
|
238
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
239
|
+
return errorResponse("UNKNOWN", `timesheet request failed: ${message}`, "Retry; if the failure persists, file an issue.");
|
|
240
|
+
}
|
|
241
|
+
function recoveryForCode(code) {
|
|
242
|
+
switch (code) {
|
|
243
|
+
case "NOT_FOUND":
|
|
244
|
+
return "Verify the id (use `ttctl_timesheet_list` to discover billing-cycle ids).";
|
|
245
|
+
case "NO_ENGAGEMENT":
|
|
246
|
+
return "The activity item exists but isn't an engagement — only engagement-bearing rows have timesheets.";
|
|
247
|
+
case "NO_CURRENT_CYCLE":
|
|
248
|
+
return "No cycle is currently in its submission window. Run `ttctl_timesheet_list` to see what's pending.";
|
|
249
|
+
case "MULTIPLE_CURRENT_CYCLES":
|
|
250
|
+
return "Multiple cycles overlap — specify the cycle id explicitly via `id`.";
|
|
251
|
+
case "MUTATION_ERROR":
|
|
252
|
+
return "The server rejected the submission (often: missing required hours, deadline passed, or already submitted). Inspect the message above.";
|
|
253
|
+
default:
|
|
254
|
+
return "Adjust the tool input or retry; see the code below.";
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=timesheet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timesheet.js","sourceRoot":"","sources":["../../src/tools/timesheet.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttctl/mcp",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "TTCtl MCP server exposing Toptal Talent operations to AI assistants
|
|
3
|
+
"version": "0.1.0-rc.2",
|
|
4
|
+
"description": "TTCtl MCP server exposing Toptal Talent operations to AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.19.0"
|
|
8
|
+
},
|
|
7
9
|
"license": "AGPL-3.0-only",
|
|
8
10
|
"author": "Alexey Pelykh (https://github.com/alexey-pelykh)",
|
|
9
11
|
"homepage": "https://github.com/alexey-pelykh/ttctl/tree/main/packages/mcp",
|
|
@@ -11,20 +13,38 @@
|
|
|
11
13
|
"funding": "https://github.com/sponsors/alexey-pelykh",
|
|
12
14
|
"repository": {
|
|
13
15
|
"type": "git",
|
|
14
|
-
"url": "
|
|
16
|
+
"url": "https://github.com/alexey-pelykh/ttctl.git",
|
|
15
17
|
"directory": "packages/mcp"
|
|
16
18
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
22
25
|
"files": [
|
|
23
|
-
"
|
|
24
|
-
"README.md",
|
|
25
|
-
"LICENSE"
|
|
26
|
+
"dist"
|
|
26
27
|
],
|
|
27
28
|
"publishConfig": {
|
|
28
29
|
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^25",
|
|
33
|
+
"eslint": "^10.3.0",
|
|
34
|
+
"typescript": "~6.0.3",
|
|
35
|
+
"vitest": "^4.1.1"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
39
|
+
"zod": "^4.4.3",
|
|
40
|
+
"@ttctl/core": "^0.1.0-rc.2"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"test": "vitest run --passWithNoTests",
|
|
47
|
+
"lint": "eslint src/",
|
|
48
|
+
"dev": "tsc --watch"
|
|
29
49
|
}
|
|
30
|
-
}
|
|
50
|
+
}
|
package/index.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// @ttctl/mcp placeholder — name reservation only.
|
|
2
|
-
// The real release is in development at https://github.com/alexey-pelykh/ttctl
|
|
3
|
-
console.log(
|
|
4
|
-
"@ttctl/mcp: this is a placeholder reservation, not a functional release.\n" +
|
|
5
|
-
"The real MCP server is in development. See:\n" +
|
|
6
|
-
" https://github.com/alexey-pelykh/ttctl",
|
|
7
|
-
);
|