@ttctl/mcp 0.0.0 → 0.1.0-rc.1

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.
Files changed (191) hide show
  1. package/README.md +72 -9
  2. package/dist/auth.d.ts +40 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +69 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/data-handling.d.ts +91 -0
  7. package/dist/data-handling.d.ts.map +1 -0
  8. package/dist/data-handling.js +129 -0
  9. package/dist/data-handling.js.map +1 -0
  10. package/dist/diagnostic.d.ts +262 -0
  11. package/dist/diagnostic.d.ts.map +1 -0
  12. package/dist/diagnostic.js +362 -0
  13. package/dist/diagnostic.js.map +1 -0
  14. package/dist/errors.d.ts +54 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +48 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +7 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/kill-switch-hook.d.ts +67 -0
  23. package/dist/kill-switch-hook.d.ts.map +1 -0
  24. package/dist/kill-switch-hook.js +61 -0
  25. package/dist/kill-switch-hook.js.map +1 -0
  26. package/dist/server.d.ts +100 -0
  27. package/dist/server.d.ts.map +1 -0
  28. package/dist/server.js +157 -0
  29. package/dist/server.js.map +1 -0
  30. package/dist/tools/_shared.d.ts +227 -0
  31. package/dist/tools/_shared.d.ts.map +1 -0
  32. package/dist/tools/_shared.js +238 -0
  33. package/dist/tools/_shared.js.map +1 -0
  34. package/dist/tools/applications.d.ts +27 -0
  35. package/dist/tools/applications.d.ts.map +1 -0
  36. package/dist/tools/applications.js +192 -0
  37. package/dist/tools/applications.js.map +1 -0
  38. package/dist/tools/availability.d.ts +33 -0
  39. package/dist/tools/availability.d.ts.map +1 -0
  40. package/dist/tools/availability.js +272 -0
  41. package/dist/tools/availability.js.map +1 -0
  42. package/dist/tools/contracts.d.ts +29 -0
  43. package/dist/tools/contracts.d.ts.map +1 -0
  44. package/dist/tools/contracts.js +157 -0
  45. package/dist/tools/contracts.js.map +1 -0
  46. package/dist/tools/engagements.d.ts +36 -0
  47. package/dist/tools/engagements.d.ts.map +1 -0
  48. package/dist/tools/engagements.js +408 -0
  49. package/dist/tools/engagements.js.map +1 -0
  50. package/dist/tools/file-upload.d.ts +133 -0
  51. package/dist/tools/file-upload.d.ts.map +1 -0
  52. package/dist/tools/file-upload.js +247 -0
  53. package/dist/tools/file-upload.js.map +1 -0
  54. package/dist/tools/index.d.ts +28 -0
  55. package/dist/tools/index.d.ts.map +1 -0
  56. package/dist/tools/index.js +131 -0
  57. package/dist/tools/index.js.map +1 -0
  58. package/dist/tools/jobs.d.ts +37 -0
  59. package/dist/tools/jobs.d.ts.map +1 -0
  60. package/dist/tools/jobs.js +505 -0
  61. package/dist/tools/jobs.js.map +1 -0
  62. package/dist/tools/output-schemas.d.ts +115 -0
  63. package/dist/tools/output-schemas.d.ts.map +1 -0
  64. package/dist/tools/output-schemas.js +130 -0
  65. package/dist/tools/output-schemas.js.map +1 -0
  66. package/dist/tools/payments.d.ts +36 -0
  67. package/dist/tools/payments.d.ts.map +1 -0
  68. package/dist/tools/payments.js +373 -0
  69. package/dist/tools/payments.js.map +1 -0
  70. package/dist/tools/profile/certifications.d.ts +18 -0
  71. package/dist/tools/profile/certifications.d.ts.map +1 -0
  72. package/dist/tools/profile/certifications.js +193 -0
  73. package/dist/tools/profile/certifications.js.map +1 -0
  74. package/dist/tools/profile/education.d.ts +23 -0
  75. package/dist/tools/profile/education.d.ts.map +1 -0
  76. package/dist/tools/profile/education.js +196 -0
  77. package/dist/tools/profile/education.js.map +1 -0
  78. package/dist/tools/profile/employment.d.ts +23 -0
  79. package/dist/tools/profile/employment.d.ts.map +1 -0
  80. package/dist/tools/profile/employment.js +228 -0
  81. package/dist/tools/profile/employment.js.map +1 -0
  82. package/dist/tools/profile/industries.d.ts +22 -0
  83. package/dist/tools/profile/industries.d.ts.map +1 -0
  84. package/dist/tools/profile/industries.js +168 -0
  85. package/dist/tools/profile/industries.js.map +1 -0
  86. package/dist/tools/profile/portfolio.d.ts +22 -0
  87. package/dist/tools/profile/portfolio.d.ts.map +1 -0
  88. package/dist/tools/profile/portfolio.js +341 -0
  89. package/dist/tools/profile/portfolio.js.map +1 -0
  90. package/dist/tools/profile/resume.d.ts +16 -0
  91. package/dist/tools/profile/resume.d.ts.map +1 -0
  92. package/dist/tools/profile/resume.js +107 -0
  93. package/dist/tools/profile/resume.js.map +1 -0
  94. package/dist/tools/profile/shared.d.ts +85 -0
  95. package/dist/tools/profile/shared.d.ts.map +1 -0
  96. package/dist/tools/profile/shared.js +128 -0
  97. package/dist/tools/profile/shared.js.map +1 -0
  98. package/dist/tools/profile/visas.d.ts +15 -0
  99. package/dist/tools/profile/visas.d.ts.map +1 -0
  100. package/dist/tools/profile/visas.js +170 -0
  101. package/dist/tools/profile/visas.js.map +1 -0
  102. package/dist/tools/profile_basic_photo_show.d.ts +14 -0
  103. package/dist/tools/profile_basic_photo_show.d.ts.map +1 -0
  104. package/dist/tools/profile_basic_photo_show.js +59 -0
  105. package/dist/tools/profile_basic_photo_show.js.map +1 -0
  106. package/dist/tools/profile_basic_photo_upload.d.ts +24 -0
  107. package/dist/tools/profile_basic_photo_upload.d.ts.map +1 -0
  108. package/dist/tools/profile_basic_photo_upload.js +90 -0
  109. package/dist/tools/profile_basic_photo_upload.js.map +1 -0
  110. package/dist/tools/profile_basic_show.d.ts +19 -0
  111. package/dist/tools/profile_basic_show.d.ts.map +1 -0
  112. package/dist/tools/profile_basic_show.js +64 -0
  113. package/dist/tools/profile_basic_show.js.map +1 -0
  114. package/dist/tools/profile_basic_update.d.ts +37 -0
  115. package/dist/tools/profile_basic_update.d.ts.map +1 -0
  116. package/dist/tools/profile_basic_update.js +97 -0
  117. package/dist/tools/profile_basic_update.js.map +1 -0
  118. package/dist/tools/profile_external_advanced_wizard_show.d.ts +14 -0
  119. package/dist/tools/profile_external_advanced_wizard_show.d.ts.map +1 -0
  120. package/dist/tools/profile_external_advanced_wizard_show.js +56 -0
  121. package/dist/tools/profile_external_advanced_wizard_show.js.map +1 -0
  122. package/dist/tools/profile_external_custom_requirements_set.d.ts +13 -0
  123. package/dist/tools/profile_external_custom_requirements_set.d.ts.map +1 -0
  124. package/dist/tools/profile_external_custom_requirements_set.js +75 -0
  125. package/dist/tools/profile_external_custom_requirements_set.js.map +1 -0
  126. package/dist/tools/profile_external_custom_requirements_show.d.ts +14 -0
  127. package/dist/tools/profile_external_custom_requirements_show.d.ts.map +1 -0
  128. package/dist/tools/profile_external_custom_requirements_show.js +56 -0
  129. package/dist/tools/profile_external_custom_requirements_show.js.map +1 -0
  130. package/dist/tools/profile_external_readiness.d.ts +12 -0
  131. package/dist/tools/profile_external_readiness.d.ts.map +1 -0
  132. package/dist/tools/profile_external_readiness.js +54 -0
  133. package/dist/tools/profile_external_readiness.js.map +1 -0
  134. package/dist/tools/profile_external_recommendations.d.ts +15 -0
  135. package/dist/tools/profile_external_recommendations.d.ts.map +1 -0
  136. package/dist/tools/profile_external_recommendations.js +57 -0
  137. package/dist/tools/profile_external_recommendations.js.map +1 -0
  138. package/dist/tools/profile_external_update.d.ts +14 -0
  139. package/dist/tools/profile_external_update.d.ts.map +1 -0
  140. package/dist/tools/profile_external_update.js +79 -0
  141. package/dist/tools/profile_external_update.js.map +1 -0
  142. package/dist/tools/profile_reviews_approve_item.d.ts +17 -0
  143. package/dist/tools/profile_reviews_approve_item.d.ts.map +1 -0
  144. package/dist/tools/profile_reviews_approve_item.js +77 -0
  145. package/dist/tools/profile_reviews_approve_item.js.map +1 -0
  146. package/dist/tools/profile_reviews_approve_section.d.ts +15 -0
  147. package/dist/tools/profile_reviews_approve_section.d.ts.map +1 -0
  148. package/dist/tools/profile_reviews_approve_section.js +70 -0
  149. package/dist/tools/profile_reviews_approve_section.js.map +1 -0
  150. package/dist/tools/profile_reviews_list.d.ts +16 -0
  151. package/dist/tools/profile_reviews_list.d.ts.map +1 -0
  152. package/dist/tools/profile_reviews_list.js +58 -0
  153. package/dist/tools/profile_reviews_list.js.map +1 -0
  154. package/dist/tools/profile_reviews_submit_for_review.d.ts +14 -0
  155. package/dist/tools/profile_reviews_submit_for_review.d.ts.map +1 -0
  156. package/dist/tools/profile_reviews_submit_for_review.js +56 -0
  157. package/dist/tools/profile_reviews_submit_for_review.js.map +1 -0
  158. package/dist/tools/profile_skills_add.d.ts +4 -0
  159. package/dist/tools/profile_skills_add.d.ts.map +1 -0
  160. package/dist/tools/profile_skills_add.js +52 -0
  161. package/dist/tools/profile_skills_add.js.map +1 -0
  162. package/dist/tools/profile_skills_autocomplete.d.ts +4 -0
  163. package/dist/tools/profile_skills_autocomplete.d.ts.map +1 -0
  164. package/dist/tools/profile_skills_autocomplete.js +78 -0
  165. package/dist/tools/profile_skills_autocomplete.js.map +1 -0
  166. package/dist/tools/profile_skills_list.d.ts +16 -0
  167. package/dist/tools/profile_skills_list.d.ts.map +1 -0
  168. package/dist/tools/profile_skills_list.js +65 -0
  169. package/dist/tools/profile_skills_list.js.map +1 -0
  170. package/dist/tools/profile_skills_readiness.d.ts +4 -0
  171. package/dist/tools/profile_skills_readiness.d.ts.map +1 -0
  172. package/dist/tools/profile_skills_readiness.js +53 -0
  173. package/dist/tools/profile_skills_readiness.js.map +1 -0
  174. package/dist/tools/profile_skills_remove.d.ts +4 -0
  175. package/dist/tools/profile_skills_remove.d.ts.map +1 -0
  176. package/dist/tools/profile_skills_remove.js +53 -0
  177. package/dist/tools/profile_skills_remove.js.map +1 -0
  178. package/dist/tools/profile_skills_show.d.ts +4 -0
  179. package/dist/tools/profile_skills_show.d.ts.map +1 -0
  180. package/dist/tools/profile_skills_show.js +51 -0
  181. package/dist/tools/profile_skills_show.js.map +1 -0
  182. package/dist/tools/profile_skills_update.d.ts +11 -0
  183. package/dist/tools/profile_skills_update.d.ts.map +1 -0
  184. package/dist/tools/profile_skills_update.js +97 -0
  185. package/dist/tools/profile_skills_update.js.map +1 -0
  186. package/dist/tools/timesheet.d.ts +29 -0
  187. package/dist/tools/timesheet.d.ts.map +1 -0
  188. package/dist/tools/timesheet.js +257 -0
  189. package/dist/tools/timesheet.js.map +1 -0
  190. package/package.json +33 -13
  191. package/index.js +0 -7
@@ -0,0 +1,133 @@
1
+ import { z } from "zod";
2
+ import type { ToolErrorResponse } from "../errors.js";
3
+ /**
4
+ * Reusable input-schema fragment for tools that accept a file via either
5
+ * a server-relative `filePath` (host has filesystem access) or
6
+ * base64-encoded `content` (web-host without filesystem access).
7
+ *
8
+ * Tool descriptions should clarify which option the host prefers per the
9
+ * issue spec:
10
+ *
11
+ * - **Claude Desktop / Claude Code**: filesystem access; prefer `filePath`.
12
+ * - **Web-hosted clients (no filesystem)**: only `content` (base64) works.
13
+ *
14
+ * Exactly one of the two MUST be supplied. Schema accepts both as
15
+ * optional; the runtime validation in `decodeFileUploadInput` enforces
16
+ * the mutual-exclusion constraint. If we marked one of the two required,
17
+ * Zod would reject calls that supplied only the other.
18
+ */
19
+ export declare const fileUploadInputSchema: {
20
+ filePath: z.ZodOptional<z.ZodString>;
21
+ filename: z.ZodOptional<z.ZodString>;
22
+ content: z.ZodOptional<z.ZodString>;
23
+ contentType: z.ZodOptional<z.ZodString>;
24
+ };
25
+ export type FileUploadInput = {
26
+ filePath?: string | undefined;
27
+ filename?: string | undefined;
28
+ content?: string | undefined;
29
+ contentType?: string | undefined;
30
+ };
31
+ /**
32
+ * Resolution variant matching the service-layer `FileSource` shape (so
33
+ * the caller can pass the result directly to `profile.portfolio.upload*`
34
+ * / `profile.resume.upload`). Buffer mode populates `contentType` only
35
+ * when supplied — `exactOptionalPropertyTypes: true` rejects the
36
+ * `string | undefined` form, so we omit the key in the absence case.
37
+ */
38
+ export type FileSourceResolution = {
39
+ kind: "buffer";
40
+ filename: string;
41
+ content: Buffer;
42
+ contentType?: string;
43
+ } | {
44
+ kind: "path";
45
+ path: string;
46
+ };
47
+ /**
48
+ * Per-tool upload category — used to drive the extension allowlist
49
+ * (defense-in-depth gate per issue #221, audit CRIT-007). Each MCP tool
50
+ * that accepts a file upload picks the category matching the wire-level
51
+ * resource it pushes to Toptal.
52
+ *
53
+ * The allowlists are deliberately curated by category:
54
+ *
55
+ * - `basicPhoto` / `portfolioCover` — images only (jpg/png/gif/webp).
56
+ * Cover images and profile photos render directly on the public
57
+ * profile; non-image content has no legitimate use here.
58
+ * - `resume` — common resume document formats (pdf / docx / doc / txt
59
+ * / rtf / odt). Excludes images and archives.
60
+ * - `portfolioFile` — broad attachment allowlist (docs, images, media,
61
+ * archives) but explicitly excludes extensions correlated with
62
+ * credential exfiltration (no `.pub`, `.env`, `.db`, `.sqlite`, no
63
+ * no-extension files like `id_rsa` / `id_ed25519`).
64
+ *
65
+ * The category serves as the defense-in-depth layer that catches the
66
+ * prompt-injection variant described in `SECURITY.md` § Prompt Injection
67
+ * Risk (file-exfiltration variant): an injected prompt that tries to
68
+ * push `~/.ssh/id_rsa`, `~/.aws/credentials`, or a cookie database to
69
+ * Toptal will fail the extension check even if path-sandbox bypass is
70
+ * enabled.
71
+ */
72
+ export interface UploadCategory {
73
+ /** Stable identifier used verbatim in error messages. */
74
+ readonly name: string;
75
+ /**
76
+ * Allowlist of accepted extensions, lowercased and including the
77
+ * leading dot (e.g. `.pdf`). Match is case-insensitive against the
78
+ * lowercased {@link path.extname} of the supplied `filePath` or
79
+ * `filename`. Empty extension (no dot in basename) is NEVER allowed —
80
+ * a deliberate refusal of extensionless secret files (`id_rsa`,
81
+ * `credentials`, `config`).
82
+ */
83
+ readonly allowedExtensions: readonly string[];
84
+ }
85
+ export declare const UPLOAD_CATEGORIES: {
86
+ readonly basicPhoto: {
87
+ readonly name: "basic-photo";
88
+ readonly allowedExtensions: readonly [".jpg", ".jpeg", ".png", ".gif", ".webp"];
89
+ };
90
+ readonly resume: {
91
+ readonly name: "resume";
92
+ readonly allowedExtensions: readonly [".pdf", ".docx", ".doc", ".txt", ".rtf", ".odt"];
93
+ };
94
+ readonly portfolioCover: {
95
+ readonly name: "portfolio-cover";
96
+ readonly allowedExtensions: readonly [".jpg", ".jpeg", ".png", ".gif", ".webp"];
97
+ };
98
+ readonly portfolioFile: {
99
+ readonly name: "portfolio-file";
100
+ readonly allowedExtensions: readonly [".pdf", ".docx", ".doc", ".txt", ".rtf", ".odt", ".xlsx", ".xls", ".pptx", ".ppt", ".csv", ".md", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".heic", ".zip", ".mp4", ".mov", ".webm", ".mp3", ".m4a", ".wav"];
101
+ };
102
+ };
103
+ /**
104
+ * Validate a file-upload path against both gates: extension allowlist +
105
+ * path-prefix sandbox. Returns `null` on accept, {@link ToolErrorResponse}
106
+ * on reject.
107
+ *
108
+ * Exposed so `profile_basic_photo_upload.ts` can call it directly — that
109
+ * tool registers a bare `file` field (not via {@link fileUploadInputSchema})
110
+ * and therefore can't go through {@link decodeFileUploadInput}.
111
+ *
112
+ * Order: extension first (cheap, clearer signal), then sandbox.
113
+ */
114
+ export declare function validateUploadPath(filePath: string, category: UploadCategory): ToolErrorResponse | null;
115
+ /**
116
+ * Translate the typed input fragment into the {@link FileSource}
117
+ * variant the service layer expects. Validates the mutual-exclusion
118
+ * constraint and the per-mode requirements.
119
+ *
120
+ * Defense-in-depth (issue #221, audit CRIT-007): the path branch
121
+ * additionally enforces an extension allowlist and a path-prefix
122
+ * sandbox per the supplied {@link UploadCategory}; the buffer branch
123
+ * enforces the extension allowlist against the supplied `filename`.
124
+ *
125
+ * Returns either:
126
+ * - A resolution object the caller passes to the service function, OR
127
+ * - A `ToolErrorResponse` carrying a `(VALIDATION_ERROR)` block for the
128
+ * tool callback to return directly.
129
+ */
130
+ export declare function decodeFileUploadInput(input: FileUploadInput, category: UploadCategory): FileSourceResolution | ToolErrorResponse;
131
+ declare function validationError(message: string): ToolErrorResponse;
132
+ export { validationError };
133
+ //# sourceMappingURL=file-upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-upload.d.ts","sourceRoot":"","sources":["../../src/tools/file-upload.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB;;;;;CAuBjC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;CA+CqB,CAAC;AAmFpD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,iBAAiB,GAAG,IAAI,CAIvG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,cAAc,GACvB,oBAAoB,GAAG,iBAAiB,CAkC1C;AAED,iBAAS,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAgB3D;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,247 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { z } from "zod";
6
+ /**
7
+ * Reusable input-schema fragment for tools that accept a file via either
8
+ * a server-relative `filePath` (host has filesystem access) or
9
+ * base64-encoded `content` (web-host without filesystem access).
10
+ *
11
+ * Tool descriptions should clarify which option the host prefers per the
12
+ * issue spec:
13
+ *
14
+ * - **Claude Desktop / Claude Code**: filesystem access; prefer `filePath`.
15
+ * - **Web-hosted clients (no filesystem)**: only `content` (base64) works.
16
+ *
17
+ * Exactly one of the two MUST be supplied. Schema accepts both as
18
+ * optional; the runtime validation in `decodeFileUploadInput` enforces
19
+ * the mutual-exclusion constraint. If we marked one of the two required,
20
+ * Zod would reject calls that supplied only the other.
21
+ */
22
+ export const fileUploadInputSchema = {
23
+ filePath: z
24
+ .string()
25
+ .optional()
26
+ .describe("Server-relative path to the file. Preferred when the host has filesystem access (Claude Desktop, Claude Code). Mutually exclusive with `content`."),
27
+ filename: z
28
+ .string()
29
+ .optional()
30
+ .describe("Filename to send to Toptal. Required when using `content` (no path to derive from). Optional when using `filePath` — the basename of the path is used by default."),
31
+ content: z
32
+ .string()
33
+ .optional()
34
+ .describe("Base64-encoded file content. Use when the host lacks filesystem access (web-hosted clients). Mutually exclusive with `filePath`."),
35
+ contentType: z
36
+ .string()
37
+ .optional()
38
+ .describe("Content-Type for the file (e.g., `image/png`, `application/pdf`). Optional; server applies a default."),
39
+ };
40
+ export const UPLOAD_CATEGORIES = {
41
+ basicPhoto: {
42
+ name: "basic-photo",
43
+ allowedExtensions: [".jpg", ".jpeg", ".png", ".gif", ".webp"],
44
+ },
45
+ resume: {
46
+ name: "resume",
47
+ allowedExtensions: [".pdf", ".docx", ".doc", ".txt", ".rtf", ".odt"],
48
+ },
49
+ portfolioCover: {
50
+ name: "portfolio-cover",
51
+ allowedExtensions: [".jpg", ".jpeg", ".png", ".gif", ".webp"],
52
+ },
53
+ portfolioFile: {
54
+ name: "portfolio-file",
55
+ allowedExtensions: [
56
+ // Documents
57
+ ".pdf",
58
+ ".docx",
59
+ ".doc",
60
+ ".txt",
61
+ ".rtf",
62
+ ".odt",
63
+ ".xlsx",
64
+ ".xls",
65
+ ".pptx",
66
+ ".ppt",
67
+ ".csv",
68
+ ".md",
69
+ // Images
70
+ ".jpg",
71
+ ".jpeg",
72
+ ".png",
73
+ ".gif",
74
+ ".webp",
75
+ ".svg",
76
+ ".heic",
77
+ // Archives / media
78
+ ".zip",
79
+ ".mp4",
80
+ ".mov",
81
+ ".webm",
82
+ ".mp3",
83
+ ".m4a",
84
+ ".wav",
85
+ ],
86
+ },
87
+ };
88
+ /**
89
+ * Directory names (relative to `$HOME`) that the path-prefix sandbox
90
+ * allows. Picked to cover the common locations a user would intentionally
91
+ * stage a file for upload from an MCP client. Sensitive directories
92
+ * (`~/.ssh`, `~/.aws`, `~/Library`, browser profile dirs) are NOT here
93
+ * and therefore refused unless the env override is set.
94
+ */
95
+ const SAFE_PATH_PREFIX_DIRS = ["Documents", "Downloads", "Desktop"];
96
+ /**
97
+ * Env var that disables the path-prefix sandbox (extension allowlist
98
+ * always remains in force). Set to `"1"` in the MCP server's process
99
+ * environment to allow uploads from arbitrary paths — for users who
100
+ * routinely stage upload candidates outside the default safe
101
+ * directories. Any other value (including empty string, `"0"`, `"true"`)
102
+ * keeps the sandbox enforced.
103
+ */
104
+ const SANDBOX_BYPASS_ENV = "TTCTL_MCP_FILE_UPLOAD_ALLOW_ANY";
105
+ function isSandboxBypassed() {
106
+ return process.env[SANDBOX_BYPASS_ENV] === "1";
107
+ }
108
+ function safePathPrefixes() {
109
+ const home = os.homedir();
110
+ return SAFE_PATH_PREFIX_DIRS.map((dir) => path.join(home, dir));
111
+ }
112
+ /**
113
+ * Validate the extension of the supplied filename or path against the
114
+ * category's allowlist. Case-insensitive (`Foo.PDF` matches `.pdf`).
115
+ * Extensionless inputs (`id_rsa`, `credentials`) are NEVER accepted —
116
+ * the empty-extension case is explicit since no allowlist contains the
117
+ * empty string.
118
+ */
119
+ function validateExtension(filenameOrPath, category) {
120
+ const ext = path.extname(filenameOrPath).toLowerCase();
121
+ if (ext !== "" && category.allowedExtensions.includes(ext)) {
122
+ return null;
123
+ }
124
+ const displayExt = ext === "" ? "<none>" : ext;
125
+ return validationError([
126
+ `File extension "${displayExt}" is not allowed for ${category.name} uploads.`,
127
+ `Allowed: ${category.allowedExtensions.join(", ")}.`,
128
+ ].join(" "));
129
+ }
130
+ /**
131
+ * Validate `filePath` against the path-prefix sandbox. Resolves the
132
+ * path first (normalises `..`, makes it absolute) so traversal attempts
133
+ * like `~/Documents/../.ssh/id_rsa` collapse before the prefix check.
134
+ *
135
+ * Sibling-name guard: the comparison requires `resolved === prefix` OR
136
+ * `resolved.startsWith(prefix + path.sep)`, so `~/Documents_secret/`
137
+ * does NOT match `~/Documents`.
138
+ *
139
+ * When `TTCTL_MCP_FILE_UPLOAD_ALLOW_ANY=1`, the sandbox is skipped
140
+ * entirely. Extension allowlist remains in force in that mode.
141
+ */
142
+ function validateSandbox(filePath) {
143
+ if (isSandboxBypassed()) {
144
+ return null;
145
+ }
146
+ const resolved = path.resolve(filePath);
147
+ const prefixes = safePathPrefixes();
148
+ const isAllowed = prefixes.some((prefix) => resolved === prefix || resolved.startsWith(prefix + path.sep));
149
+ if (isAllowed) {
150
+ return null;
151
+ }
152
+ return validationError([
153
+ `Refusing to read file outside of the MCP upload sandbox.`,
154
+ `Resolved path: "${resolved}".`,
155
+ `Allowed prefixes: ${prefixes.join(", ")}.`,
156
+ `Set ${SANDBOX_BYPASS_ENV}=1 in the MCP server's environment to override (extension allowlist still applies).`,
157
+ ].join(" "));
158
+ }
159
+ /**
160
+ * Validate a file-upload path against both gates: extension allowlist +
161
+ * path-prefix sandbox. Returns `null` on accept, {@link ToolErrorResponse}
162
+ * on reject.
163
+ *
164
+ * Exposed so `profile_basic_photo_upload.ts` can call it directly — that
165
+ * tool registers a bare `file` field (not via {@link fileUploadInputSchema})
166
+ * and therefore can't go through {@link decodeFileUploadInput}.
167
+ *
168
+ * Order: extension first (cheap, clearer signal), then sandbox.
169
+ */
170
+ export function validateUploadPath(filePath, category) {
171
+ const extError = validateExtension(filePath, category);
172
+ if (extError !== null)
173
+ return extError;
174
+ return validateSandbox(filePath);
175
+ }
176
+ /**
177
+ * Translate the typed input fragment into the {@link FileSource}
178
+ * variant the service layer expects. Validates the mutual-exclusion
179
+ * constraint and the per-mode requirements.
180
+ *
181
+ * Defense-in-depth (issue #221, audit CRIT-007): the path branch
182
+ * additionally enforces an extension allowlist and a path-prefix
183
+ * sandbox per the supplied {@link UploadCategory}; the buffer branch
184
+ * enforces the extension allowlist against the supplied `filename`.
185
+ *
186
+ * Returns either:
187
+ * - A resolution object the caller passes to the service function, OR
188
+ * - A `ToolErrorResponse` carrying a `(VALIDATION_ERROR)` block for the
189
+ * tool callback to return directly.
190
+ */
191
+ export function decodeFileUploadInput(input, category) {
192
+ const hasPath = input.filePath !== undefined && input.filePath !== "";
193
+ const hasContent = input.content !== undefined && input.content !== "";
194
+ if (hasPath && hasContent) {
195
+ return validationError("`filePath` and `content` are mutually exclusive — supply exactly one.");
196
+ }
197
+ if (!hasPath && !hasContent) {
198
+ return validationError("Supply exactly one of `filePath` or `content` (base64).");
199
+ }
200
+ if (hasPath) {
201
+ const pathError = validateUploadPath(input.filePath, category);
202
+ if (pathError !== null)
203
+ return pathError;
204
+ return { kind: "path", path: input.filePath };
205
+ }
206
+ // base64 branch
207
+ if (input.filename === undefined || input.filename === "") {
208
+ return validationError("`filename` is required when uploading via `content` (base64).");
209
+ }
210
+ const filenameError = validateExtension(input.filename, category);
211
+ if (filenameError !== null)
212
+ return filenameError;
213
+ let buffer;
214
+ try {
215
+ buffer = Buffer.from(input.content, "base64");
216
+ }
217
+ catch {
218
+ return validationError("`content` is not valid base64.");
219
+ }
220
+ if (buffer.length === 0) {
221
+ return validationError("`content` decoded to zero bytes.");
222
+ }
223
+ // `contentType` is omitted entirely when not supplied — assigning
224
+ // `undefined` would violate `exactOptionalPropertyTypes`.
225
+ return input.contentType !== undefined
226
+ ? { kind: "buffer", filename: input.filename, content: buffer, contentType: input.contentType }
227
+ : { kind: "buffer", filename: input.filename, content: buffer };
228
+ }
229
+ function validationError(message) {
230
+ return {
231
+ isError: true,
232
+ content: [
233
+ {
234
+ type: "text",
235
+ text: [
236
+ `Error: ${message}`,
237
+ "",
238
+ "Recovery: Adjust the tool input and call again.",
239
+ "",
240
+ "(Code: VALIDATION_ERROR)",
241
+ ].join("\n"),
242
+ },
243
+ ],
244
+ };
245
+ }
246
+ export { validationError };
247
+ //# sourceMappingURL=file-upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-upload.js","sourceRoot":"","sources":["../../src/tools/file-upload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ToolRegistrationContext } from "./_shared.js";
3
+ export type { ToolRegistrationContext } from "./_shared.js";
4
+ /**
5
+ * Register every tool TTCtl exposes via MCP. Called once at server
6
+ * construction time. Tool names are the CLI path joined with `_`
7
+ * (`ttctl_profile_basic_show`, `ttctl_profile_industries_add`, …) per
8
+ * project policy — MCP names use ONLY the canonical sub-domain spelling
9
+ * (no aliases like `certs` / `experience` / `rm`; those are CLI-only
10
+ * affordances).
11
+ *
12
+ * Today's surface (Wave 3): 4 `profile.basic` + 7 `profile.skills` (#73,
13
+ * one tool per file) + 5 `profile.industries` + 5 `profile.education` +
14
+ * 5 `profile.certifications` + 6 `profile.employment` (#74, one file per
15
+ * sub-domain registering its full leaf set) + 8 `profile.portfolio` +
16
+ * 4 `profile.visas` + 2 `profile.resume` (#75) + 6 `profile.external` +
17
+ * 4 `profile.reviews` (#76, one tool per file) = 56 profile tools, plus
18
+ * 3 `applications` (#15) + 8 `engagements` (#147 + #155 + #156) + 5 `availability`
19
+ * (#146 amended) + 13 `jobs` (#148) + 3 `timesheet` (#13) = 88 tools total.
20
+ *
21
+ * Post-#113: takes a `ToolRegistrationContext` carrying the per-session
22
+ * auth resolvers bound to the config path captured at `buildServer()`
23
+ * time. Tools call `ctx.resolveToolAuth()` / `ctx.loadTokenForTool()` on
24
+ * each invocation; both target the captured path, so env-var shifts
25
+ * mid-session do NOT retarget reads or writes.
26
+ */
27
+ export declare function registerAllTools(server: McpServer, ctx: ToolRegistrationContext): void;
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAqC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAiFtF"}
@@ -0,0 +1,131 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { registerApplicationsTools } from "./applications.js";
4
+ import { registerAvailabilityTools } from "./availability.js";
5
+ import { registerContractsTools } from "./contracts.js";
6
+ import { registerEngagementsTools } from "./engagements.js";
7
+ import { registerJobsTools } from "./jobs.js";
8
+ import { registerPaymentsTools } from "./payments.js";
9
+ import { registerTimesheetTools } from "./timesheet.js";
10
+ import { registerCertificationsTools } from "./profile/certifications.js";
11
+ import { registerEducationTools } from "./profile/education.js";
12
+ import { registerEmploymentTools } from "./profile/employment.js";
13
+ import { registerIndustriesTools } from "./profile/industries.js";
14
+ import { registerPortfolioTools } from "./profile/portfolio.js";
15
+ import { registerResumeTools } from "./profile/resume.js";
16
+ import { registerVisasTools } from "./profile/visas.js";
17
+ import { registerProfileBasicPhotoShowTool } from "./profile_basic_photo_show.js";
18
+ import { registerProfileBasicPhotoUploadTool } from "./profile_basic_photo_upload.js";
19
+ import { registerProfileBasicShowTool } from "./profile_basic_show.js";
20
+ import { registerProfileBasicUpdateTool } from "./profile_basic_update.js";
21
+ import { registerProfileExternalAdvancedWizardShowTool } from "./profile_external_advanced_wizard_show.js";
22
+ import { registerProfileExternalCustomRequirementsSetTool } from "./profile_external_custom_requirements_set.js";
23
+ import { registerProfileExternalCustomRequirementsShowTool } from "./profile_external_custom_requirements_show.js";
24
+ import { registerProfileExternalReadinessTool } from "./profile_external_readiness.js";
25
+ import { registerProfileExternalRecommendationsTool } from "./profile_external_recommendations.js";
26
+ import { registerProfileExternalUpdateTool } from "./profile_external_update.js";
27
+ import { registerProfileReviewsApproveItemTool } from "./profile_reviews_approve_item.js";
28
+ import { registerProfileReviewsApproveSectionTool } from "./profile_reviews_approve_section.js";
29
+ import { registerProfileReviewsListTool } from "./profile_reviews_list.js";
30
+ import { registerProfileReviewsSubmitForReviewTool } from "./profile_reviews_submit_for_review.js";
31
+ import { registerProfileSkillsAddTool } from "./profile_skills_add.js";
32
+ import { registerProfileSkillsAutocompleteTool } from "./profile_skills_autocomplete.js";
33
+ import { registerProfileSkillsListTool } from "./profile_skills_list.js";
34
+ import { registerProfileSkillsReadinessTool } from "./profile_skills_readiness.js";
35
+ import { registerProfileSkillsRemoveTool } from "./profile_skills_remove.js";
36
+ import { registerProfileSkillsShowTool } from "./profile_skills_show.js";
37
+ import { registerProfileSkillsUpdateTool } from "./profile_skills_update.js";
38
+ /**
39
+ * Register every tool TTCtl exposes via MCP. Called once at server
40
+ * construction time. Tool names are the CLI path joined with `_`
41
+ * (`ttctl_profile_basic_show`, `ttctl_profile_industries_add`, …) per
42
+ * project policy — MCP names use ONLY the canonical sub-domain spelling
43
+ * (no aliases like `certs` / `experience` / `rm`; those are CLI-only
44
+ * affordances).
45
+ *
46
+ * Today's surface (Wave 3): 4 `profile.basic` + 7 `profile.skills` (#73,
47
+ * one tool per file) + 5 `profile.industries` + 5 `profile.education` +
48
+ * 5 `profile.certifications` + 6 `profile.employment` (#74, one file per
49
+ * sub-domain registering its full leaf set) + 8 `profile.portfolio` +
50
+ * 4 `profile.visas` + 2 `profile.resume` (#75) + 6 `profile.external` +
51
+ * 4 `profile.reviews` (#76, one tool per file) = 56 profile tools, plus
52
+ * 3 `applications` (#15) + 8 `engagements` (#147 + #155 + #156) + 5 `availability`
53
+ * (#146 amended) + 13 `jobs` (#148) + 3 `timesheet` (#13) = 88 tools total.
54
+ *
55
+ * Post-#113: takes a `ToolRegistrationContext` carrying the per-session
56
+ * auth resolvers bound to the config path captured at `buildServer()`
57
+ * time. Tools call `ctx.resolveToolAuth()` / `ctx.loadTokenForTool()` on
58
+ * each invocation; both target the captured path, so env-var shifts
59
+ * mid-session do NOT retarget reads or writes.
60
+ */
61
+ export function registerAllTools(server, ctx) {
62
+ // profile.basic — 4 leaves (#73)
63
+ registerProfileBasicShowTool(server, ctx);
64
+ registerProfileBasicUpdateTool(server, ctx);
65
+ registerProfileBasicPhotoShowTool(server, ctx);
66
+ registerProfileBasicPhotoUploadTool(server, ctx);
67
+ // profile.skills — 7 leaves (#73, cardinality-collapsed from 18 raw operations)
68
+ registerProfileSkillsAddTool(server, ctx);
69
+ registerProfileSkillsRemoveTool(server, ctx);
70
+ registerProfileSkillsUpdateTool(server, ctx);
71
+ registerProfileSkillsShowTool(server, ctx);
72
+ registerProfileSkillsListTool(server, ctx);
73
+ registerProfileSkillsAutocompleteTool(server, ctx);
74
+ registerProfileSkillsReadinessTool(server, ctx);
75
+ // profile.industries / education / certifications / employment — 21 leaves (#74)
76
+ registerIndustriesTools(server, ctx);
77
+ registerEducationTools(server, ctx);
78
+ registerCertificationsTools(server, ctx);
79
+ registerEmploymentTools(server, ctx);
80
+ // profile.portfolio (8), profile.visas (4), profile.resume (2) — #75
81
+ registerPortfolioTools(server, ctx);
82
+ registerVisasTools(server, ctx);
83
+ registerResumeTools(server, ctx);
84
+ // profile.external — 6 leaves (#76)
85
+ registerProfileExternalUpdateTool(server, ctx);
86
+ registerProfileExternalCustomRequirementsShowTool(server, ctx);
87
+ registerProfileExternalCustomRequirementsSetTool(server, ctx);
88
+ registerProfileExternalReadinessTool(server, ctx);
89
+ registerProfileExternalRecommendationsTool(server, ctx);
90
+ registerProfileExternalAdvancedWizardShowTool(server, ctx);
91
+ // profile.reviews — 4 leaves (#76)
92
+ registerProfileReviewsListTool(server, ctx);
93
+ registerProfileReviewsApproveItemTool(server, ctx);
94
+ registerProfileReviewsApproveSectionTool(server, ctx);
95
+ registerProfileReviewsSubmitForReviewTool(server, ctx);
96
+ // applications — 3 leaves (#15). Read-only access to the user's
97
+ // Toptal Talent Activity view (TalentJobActivityItem rows). Per
98
+ // project non-goals, no apply / withdraw / edit tools are exposed.
99
+ registerApplicationsTools(server, ctx);
100
+ // contracts — 2 leaves (#195). Read-only talent-level legal documents
101
+ // (Toptal Direct, MSA, etc.) via `viewer.contracts` on the portal
102
+ // surface. Engagement-attached commercial agreements stay in
103
+ // `engagements_show` (different surface, different domain type).
104
+ registerContractsTools(server, ctx);
105
+ // engagements — 6 leaves (#147). View current and past engagements;
106
+ // manage engagement breaks (list / add / remove). `allocated-hours`
107
+ // moved to availability (#146) per scope amendment.
108
+ registerEngagementsTools(server, ctx);
109
+ // availability — 5 leaves (#146 amended). Top-level snapshot + working-hours
110
+ // show/set + allocated-hours show/set. Per-engagement time-off (= engagement
111
+ // breaks) stays on `engagements` and is NOT mirrored here.
112
+ registerAvailabilityTools(server, ctx);
113
+ // jobs — 13 leaves (#148). Browse opportunities (list/show/saved/viewed/
114
+ // not-interested-list) + interest mutations (save/unsave/mark-viewed/
115
+ // not-interested/clear-interest) + search-subscription management
116
+ // (list/save/remove — single-subscription model per R2). Per the AC, MCP
117
+ // tool names use canonical `jobs_*` prefix only (no `opportunities_*`
118
+ // aliasing).
119
+ registerJobsTools(server, ctx);
120
+ // payments — 7 leaves (#149). Read-only payouts (list/show) and
121
+ // configured payment methods (list/show); rate sub-namespace mixes a
122
+ // unified read projection (`rate_show`), discovery (`rate_questions`),
123
+ // and a state-changing mutation (`rate_change`, INFERRED input, gated
124
+ // by `--confirm` at the CLI and the LLM-client confirmation at MCP).
125
+ registerPaymentsTools(server, ctx);
126
+ // timesheet — 3 leaves (#13). list (viewer-wide pending OR scoped) +
127
+ // show (BillingCycle.id detail) + submit (destructive: enters Toptal's
128
+ // billing pipeline; LLM clients must confirm with the user first).
129
+ registerTimesheetTools(server, ctx);
130
+ }
131
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type ToolRegistrationContext } from "./_shared.js";
3
+ /**
4
+ * Register the `ttctl_jobs_*` MCP tools per #148. Tool names use the
5
+ * `ttctl_` prefix and the canonical CLI path joined with `_`. Per the
6
+ * AC, MCP tools use the canonical `jobs_*` prefix only — no
7
+ * `opportunities_*` aliasing (MCP tool names must be deterministic for
8
+ * LLM clients).
9
+ *
10
+ * Tool surface (13 tools):
11
+ * - `ttctl_jobs_list`
12
+ * - `ttctl_jobs_show`
13
+ * - `ttctl_jobs_save`
14
+ * - `ttctl_jobs_unsave`
15
+ * - `ttctl_jobs_saved`
16
+ * - `ttctl_jobs_viewed`
17
+ * - `ttctl_jobs_mark_viewed`
18
+ * - `ttctl_jobs_not_interested`
19
+ * - `ttctl_jobs_not_interested_list`
20
+ * - `ttctl_jobs_clear_interest`
21
+ * - `ttctl_jobs_search_list`
22
+ * - `ttctl_jobs_search_save`
23
+ * - `ttctl_jobs_search_remove`
24
+ *
25
+ * **Wire-shape notes** (R1 / R2): `jobs_viewed` is scoped to the first
26
+ * page (≤20 jobs, client-side filter); `jobs_search_*` operates on a
27
+ * single subscription per user. The tool descriptions surface these.
28
+ *
29
+ * Dry-run path (issue #165): every tool accepts `dryRun?: boolean`. Read
30
+ * tools build the preview at the MCP layer; the 7 mutations (save,
31
+ * unsave, mark_viewed, not_interested, clear_interest, search_save,
32
+ * search_remove) passthrough-forward `{ dryRun: true }` to the core
33
+ * (which already supports dryRun per #162) and reformat the
34
+ * `{ kind: "preview", preview }` outcome as the uniform envelope.
35
+ */
36
+ export declare function registerJobsTools(server: McpServer, ctx: ToolRegistrationContext): void;
37
+ //# sourceMappingURL=jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/tools/jobs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,OAAO,EAAyC,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAmCnG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAyavF"}