@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
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { TtctlError } from "@ttctl/core";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tool-error response shape. Mirrors the Model Context Protocol's
|
|
4
|
+
* `CallToolResult` for error cases (`isError: true` + `content: [TextContent]`).
|
|
5
|
+
*
|
|
6
|
+
* Defined locally rather than re-exporting from `@modelcontextprotocol/sdk`
|
|
7
|
+
* to keep the typing focused on what TTCtl emits — the upstream SDK type is
|
|
8
|
+
* a wide union covering all content types we don't return. This shape is
|
|
9
|
+
* the SUBSET we promise.
|
|
10
|
+
*
|
|
11
|
+
* The `[key: string]: unknown` index signature keeps the type structurally
|
|
12
|
+
* compatible with the SDK's `CallToolResult` (whose own type carries the
|
|
13
|
+
* same signature for forward-compatibility with new optional fields).
|
|
14
|
+
* Without it, returning this shape from a `registerTool` callback fails
|
|
15
|
+
* strict type-checking even though the runtime values are identical.
|
|
16
|
+
*/
|
|
17
|
+
export interface ToolErrorResponse {
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
isError: true;
|
|
20
|
+
content: [{
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Render a `TtctlError` as an MCP tool-error response (issue #77 § MCP
|
|
27
|
+
* error presentation).
|
|
28
|
+
*
|
|
29
|
+
* The text payload follows the same Error/Recovery/Code blocks the CLI
|
|
30
|
+
* surface emits, with the `code` echoed as a structured tag MCP-aware LLM
|
|
31
|
+
* clients can detect programmatically without parsing prose. Multi-line
|
|
32
|
+
* content stays in `text` since MCP's `TextContent` is a free-form string.
|
|
33
|
+
*/
|
|
34
|
+
export declare function ttctlErrorToToolResponse(err: TtctlError): ToolErrorResponse;
|
|
35
|
+
/**
|
|
36
|
+
* Convenience guard for tool implementations: if `err` is a `TtctlError`,
|
|
37
|
+
* return the structured tool response; otherwise fall through (return
|
|
38
|
+
* `null`) so the caller can apply its own non-typed-error handling.
|
|
39
|
+
*
|
|
40
|
+
* Tool implementations consume this as:
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* try {
|
|
44
|
+
* const value = await someService(...);
|
|
45
|
+
* return successResponse(value);
|
|
46
|
+
* } catch (err) {
|
|
47
|
+
* const typed = ttctlErrorToToolResponseOrNull(err);
|
|
48
|
+
* if (typed !== null) return typed;
|
|
49
|
+
* throw err; // or generic-error response
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function ttctlErrorToToolResponseOrNull(err: unknown): ToolErrorResponse | null;
|
|
54
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,CAU3E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAKrF"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { TtctlError } from "@ttctl/core";
|
|
4
|
+
/**
|
|
5
|
+
* Render a `TtctlError` as an MCP tool-error response (issue #77 § MCP
|
|
6
|
+
* error presentation).
|
|
7
|
+
*
|
|
8
|
+
* The text payload follows the same Error/Recovery/Code blocks the CLI
|
|
9
|
+
* surface emits, with the `code` echoed as a structured tag MCP-aware LLM
|
|
10
|
+
* clients can detect programmatically without parsing prose. Multi-line
|
|
11
|
+
* content stays in `text` since MCP's `TextContent` is a free-form string.
|
|
12
|
+
*/
|
|
13
|
+
export function ttctlErrorToToolResponse(err) {
|
|
14
|
+
return {
|
|
15
|
+
isError: true,
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: [`Error: ${err.message}`, "", `Recovery: ${err.recovery}`, "", `(Code: ${err.code})`].join("\n"),
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convenience guard for tool implementations: if `err` is a `TtctlError`,
|
|
26
|
+
* return the structured tool response; otherwise fall through (return
|
|
27
|
+
* `null`) so the caller can apply its own non-typed-error handling.
|
|
28
|
+
*
|
|
29
|
+
* Tool implementations consume this as:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* try {
|
|
33
|
+
* const value = await someService(...);
|
|
34
|
+
* return successResponse(value);
|
|
35
|
+
* } catch (err) {
|
|
36
|
+
* const typed = ttctlErrorToToolResponseOrNull(err);
|
|
37
|
+
* if (typed !== null) return typed;
|
|
38
|
+
* throw err; // or generic-error response
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function ttctlErrorToToolResponseOrNull(err) {
|
|
43
|
+
if (err instanceof TtctlError) {
|
|
44
|
+
return ttctlErrorToToolResponse(err);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAuBzC;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAe;IACtD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,aAAa,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;aACvG;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,8BAA8B,CAAC,GAAY;IACzD,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;QAC9B,OAAO,wBAAwB,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { buildServer, runMcpStdio } from "./server.js";
|
|
2
|
+
export type { BuildServerOptions } from "./server.js";
|
|
3
|
+
export { DATA_HANDLING_FOOTER, HIGH_RISK_TOOLS, THIRDPARTY_FREETEXT_FOOTER, composeDescription, } from "./data-handling.js";
|
|
4
|
+
export { ttctlErrorToToolResponse, ttctlErrorToToolResponseOrNull } from "./errors.js";
|
|
5
|
+
export type { ToolErrorResponse } from "./errors.js";
|
|
6
|
+
export { emitMcpAuthResolve, emitMcpDebug, extractTransportStatus, extractTransportSurface, getMcpDiagnosticLogger, isTransportError, redactToolArgs, resetMcpDiagnosticLogger, setMcpDiagnosticLogger, wrapToolHandler, } from "./diagnostic.js";
|
|
7
|
+
export type { McpAuthResolveRecord, McpDebugRecord, McpDiagnosticLogger, McpToolInvokeEndRecord, McpToolInvokeStartRecord, McpToolInvokeStatus, McpTransportErrorRecord, } from "./diagnostic.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AACvF,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
export { buildServer, runMcpStdio } from "./server.js";
|
|
4
|
+
export { DATA_HANDLING_FOOTER, HIGH_RISK_TOOLS, THIRDPARTY_FREETEXT_FOOTER, composeDescription, } from "./data-handling.js";
|
|
5
|
+
export { ttctlErrorToToolResponse, ttctlErrorToToolResponseOrNull } from "./errors.js";
|
|
6
|
+
export { emitMcpAuthResolve, emitMcpDebug, extractTransportStatus, extractTransportSurface, getMcpDiagnosticLogger, isTransportError, redactToolArgs, resetMcpDiagnosticLogger, setMcpDiagnosticLogger, wrapToolHandler, } from "./diagnostic.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAGvF,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,GAChB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP-side wire-up for the remote version-killed manifest (#312).
|
|
3
|
+
*
|
|
4
|
+
* Invoked from `buildServer` at construction time (AC 4). Two
|
|
5
|
+
* differences from the CLI hook:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Fire-and-forget**: the server constructor must stay sync
|
|
8
|
+
* (the MCP SDK's `McpServer` doesn't support an async build path,
|
|
9
|
+
* and a long blocking startup would interrupt the JSON-RPC
|
|
10
|
+
* handshake with the client). The check runs as a detached
|
|
11
|
+
* Promise; the warning lands on stderr whenever the fetch
|
|
12
|
+
* resolves.
|
|
13
|
+
* 2. **Always warn, never refuse**: refusing a long-lived MCP
|
|
14
|
+
* server has no usable semantics — mid-flight tool calls would
|
|
15
|
+
* be interrupted, the Claude Desktop / Cursor session would
|
|
16
|
+
* receive a partial response, and the operator may not see the
|
|
17
|
+
* stderr trail. The runbook documents this asymmetry.
|
|
18
|
+
*
|
|
19
|
+
* Also schedules a recurring refetch every ~24h per the issue's
|
|
20
|
+
* frequency spec, with `.unref()` so the timer never holds the
|
|
21
|
+
* process alive — natural Node exit semantics remain intact.
|
|
22
|
+
*
|
|
23
|
+
* **Fail-silent contract**: every error path is swallowed. Even an
|
|
24
|
+
* unexpected throw inside the fire-and-forget Promise is caught (via
|
|
25
|
+
* the `.catch()` on the returned Promise) so no `unhandledRejection`
|
|
26
|
+
* fires.
|
|
27
|
+
*/
|
|
28
|
+
export interface McpKillSwitchHookOptions {
|
|
29
|
+
/** Override stderr writer. Default: `process.stderr.write`. */
|
|
30
|
+
writeStderr?: (chunk: string) => void;
|
|
31
|
+
/** Override the running version (default: read from this package's package.json). */
|
|
32
|
+
version?: string;
|
|
33
|
+
/** Override the manifest URL. */
|
|
34
|
+
url?: string;
|
|
35
|
+
/** Override the per-fetch timeout (ms). */
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
/** Override the refetch interval (ms). Default: ~24h. */
|
|
38
|
+
refetchIntervalMs?: number;
|
|
39
|
+
/** Injected fetch. Tests pass a mock. */
|
|
40
|
+
fetchFn?: typeof globalThis.fetch;
|
|
41
|
+
/**
|
|
42
|
+
* Override `setInterval`. Tests inject a controllable timer to verify
|
|
43
|
+
* the refetch loop without waiting 24h. The returned handle must
|
|
44
|
+
* carry an `unref` method (Node's `Timeout`). When omitted, the real
|
|
45
|
+
* `setInterval` is used.
|
|
46
|
+
*/
|
|
47
|
+
setIntervalFn?: typeof globalThis.setInterval;
|
|
48
|
+
}
|
|
49
|
+
/** Returned handle for callers that want to cancel the refetch loop. */
|
|
50
|
+
export interface McpKillSwitchHandle {
|
|
51
|
+
/** Stop the refetch timer. After this, no further checks fire. */
|
|
52
|
+
stop: () => void;
|
|
53
|
+
/**
|
|
54
|
+
* The Promise from the initial at-startup check. Exposed for tests
|
|
55
|
+
* that need to await the first-fetch completion. Production callers
|
|
56
|
+
* do NOT await this — buildServer is sync.
|
|
57
|
+
*/
|
|
58
|
+
initialCheck: Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Schedule the startup + recurring kill-switch checks for the MCP
|
|
62
|
+
* server. Returns a handle the caller can use to stop the refetch
|
|
63
|
+
* timer (mostly useful for tests; production callers ignore it — the
|
|
64
|
+
* `.unref()` timer dies naturally on process exit).
|
|
65
|
+
*/
|
|
66
|
+
export declare function scheduleMcpKillSwitch(opts?: McpKillSwitchHookOptions): McpKillSwitchHandle;
|
|
67
|
+
//# sourceMappingURL=kill-switch-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-switch-hook.d.ts","sourceRoot":"","sources":["../src/kill-switch-hook.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,WAAW,wBAAwB;IACvC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAClC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,UAAU,CAAC,WAAW,CAAC;CAC/C;AAED,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAmCD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,wBAA6B,GAAG,mBAAmB,CAwB9F"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { checkKillSwitch, formatKillSwitchMessage, KILL_SWITCH_DEFAULT_REFETCH_INTERVAL_MS, KILL_SWITCH_DEFAULT_TIMEOUT_MS, readPackageVersion, } from "@ttctl/core";
|
|
4
|
+
async function runOnce(opts, version) {
|
|
5
|
+
try {
|
|
6
|
+
const result = await checkKillSwitch({
|
|
7
|
+
version,
|
|
8
|
+
...(opts.url !== undefined ? { url: opts.url } : {}),
|
|
9
|
+
timeoutMs: opts.timeoutMs ?? KILL_SWITCH_DEFAULT_TIMEOUT_MS,
|
|
10
|
+
...(opts.fetchFn !== undefined ? { fetchFn: opts.fetchFn } : {}),
|
|
11
|
+
});
|
|
12
|
+
if (result.status !== "match")
|
|
13
|
+
return;
|
|
14
|
+
const writeStderr = opts.writeStderr ??
|
|
15
|
+
((chunk) => {
|
|
16
|
+
process.stderr.write(chunk);
|
|
17
|
+
});
|
|
18
|
+
writeStderr(formatKillSwitchMessage({
|
|
19
|
+
toolName: "ttctl mcp",
|
|
20
|
+
version,
|
|
21
|
+
entry: result.entry,
|
|
22
|
+
}));
|
|
23
|
+
// Per docstring: MCP always warns even when entry.action === "refuse".
|
|
24
|
+
// No process.exit, no server-side enforcement of refuse.
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Outer envelope: even if checkKillSwitch were to throw (it shouldn't —
|
|
28
|
+
// it returns a discriminated result for every failure path), swallow
|
|
29
|
+
// here so the fire-and-forget Promise never surfaces as an
|
|
30
|
+
// unhandledRejection.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Schedule the startup + recurring kill-switch checks for the MCP
|
|
35
|
+
* server. Returns a handle the caller can use to stop the refetch
|
|
36
|
+
* timer (mostly useful for tests; production callers ignore it — the
|
|
37
|
+
* `.unref()` timer dies naturally on process exit).
|
|
38
|
+
*/
|
|
39
|
+
export function scheduleMcpKillSwitch(opts = {}) {
|
|
40
|
+
const version = opts.version ?? readPackageVersion(import.meta.url);
|
|
41
|
+
const refetchIntervalMs = opts.refetchIntervalMs ?? KILL_SWITCH_DEFAULT_REFETCH_INTERVAL_MS;
|
|
42
|
+
const setIntervalImpl = opts.setIntervalFn ?? globalThis.setInterval;
|
|
43
|
+
const initialCheck = runOnce(opts, version);
|
|
44
|
+
const timer = setIntervalImpl(() => {
|
|
45
|
+
void runOnce(opts, version);
|
|
46
|
+
}, refetchIntervalMs);
|
|
47
|
+
// Don't hold the process alive solely for this timer — natural Node
|
|
48
|
+
// exit semantics (e.g., stdin EOF terminating the MCP transport)
|
|
49
|
+
// must still terminate the daemon. The injected setIntervalFn type
|
|
50
|
+
// is `typeof globalThis.setInterval` which returns NodeJS.Timeout —
|
|
51
|
+
// .unref() is always present on the contract; tests supply a stub
|
|
52
|
+
// that includes the method.
|
|
53
|
+
timer.unref();
|
|
54
|
+
return {
|
|
55
|
+
stop: () => {
|
|
56
|
+
clearInterval(timer);
|
|
57
|
+
},
|
|
58
|
+
initialCheck,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=kill-switch-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-switch-hook.js","sourceRoot":"","sources":["../src/kill-switch-hook.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,uCAAuC,EACvC,8BAA8B,EAC9B,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAgErB,KAAK,UAAU,OAAO,CAAC,IAA8B,EAAE,OAAe;IACpE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;YACnC,OAAO;YACP,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,8BAA8B;YAC3D,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO;QAEtC,MAAM,WAAW,GACf,IAAI,CAAC,WAAW;YAChB,CAAC,CAAC,KAAa,EAAQ,EAAE;gBACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;QACL,WAAW,CACT,uBAAuB,CAAC;YACtB,QAAQ,EAAE,WAAW;YACrB,OAAO;YACP,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC,CACH,CAAC;QACF,uEAAuE;QACvE,yDAAyD;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,qEAAqE;QACrE,2DAA2D;QAC3D,sBAAsB;IACxB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAiC,EAAE;IACvE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,uCAAuC,CAAC;IAC5F,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,WAAW,CAAC;IAErE,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE5C,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,EAAE;QACjC,KAAK,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACtB,oEAAoE;IACpE,iEAAiE;IACjE,mEAAmE;IACnE,oEAAoE;IACpE,kEAAkE;IAClE,4BAA4B;IAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;IAEd,OAAO;QACL,IAAI,EAAE,GAAS,EAAE;YACf,aAAa,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,YAAY;KACb,CAAC;AACJ,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type McpDiagnosticLogger } from "./diagnostic.js";
|
|
3
|
+
import { type McpKillSwitchHookOptions } from "./kill-switch-hook.js";
|
|
4
|
+
/**
|
|
5
|
+
* Options accepted by `buildServer` and `runMcpStdio`.
|
|
6
|
+
*
|
|
7
|
+
* - `configPath` — explicit config-file path captured by the CLI's
|
|
8
|
+
* `--config <path>` flag (or any future SSE/HTTP transport entry).
|
|
9
|
+
* When undefined, `resolveConfig` falls through to the
|
|
10
|
+
* `TTCTL_CONFIG_FILE` env var, then to `~/.ttctl.yaml` (the canonical
|
|
11
|
+
* 3-step chain documented in CLAUDE.md § Config File Resolution).
|
|
12
|
+
* - `logger` — optional MCP diagnostic logger override (issue #224).
|
|
13
|
+
* When supplied to `runMcpStdio`, the logger is installed BEFORE
|
|
14
|
+
* `buildServer` runs, so every tool-registration instrumentation +
|
|
15
|
+
* every per-call emission routes through the injected logger.
|
|
16
|
+
* Production callers omit this field; tests inject a capturing
|
|
17
|
+
* logger to assert emission shape without manipulating
|
|
18
|
+
* `process.env`. When `runMcpStdio` is called with no `logger`, the
|
|
19
|
+
* module-default env-gated stderr emitter (`TTCTL_DEBUG_MCP=1`) is
|
|
20
|
+
* used. `buildServer` itself does NOT install the logger — it
|
|
21
|
+
* only consumes the currently-installed one when wrapping tool
|
|
22
|
+
* handlers — so callers that bypass `runMcpStdio` (e.g. tests
|
|
23
|
+
* building a server directly) must call
|
|
24
|
+
* `setMcpDiagnosticLogger` themselves if they want custom emission.
|
|
25
|
+
*/
|
|
26
|
+
export interface BuildServerOptions {
|
|
27
|
+
configPath?: string;
|
|
28
|
+
logger?: McpDiagnosticLogger;
|
|
29
|
+
/**
|
|
30
|
+
* Remote version-killed manifest check (#312, AC 4). When omitted,
|
|
31
|
+
* `buildServer` fires the default at-startup check and schedules a
|
|
32
|
+
* recurring ~24h refetch. Production callers omit this; tests inject
|
|
33
|
+
* a mocked `fetchFn` + `setIntervalFn` + `writeStderr` to assert
|
|
34
|
+
* the wiring without hitting the network or polluting stderr.
|
|
35
|
+
*
|
|
36
|
+
* Set to `null` to opt out entirely (used by some test setups where
|
|
37
|
+
* the kill-switch is exercised separately or is irrelevant to the
|
|
38
|
+
* scenario under test).
|
|
39
|
+
*/
|
|
40
|
+
killSwitch?: McpKillSwitchHookOptions | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the TTCtl MCP server. Tools are wired in via `registerAllTools` —
|
|
44
|
+
* a single registration site that pulls in every per-tool module so each
|
|
45
|
+
* tool file stays focused on its own input shape.
|
|
46
|
+
*
|
|
47
|
+
* Trust model: process-level — any process that can spawn `ttctl mcp` gets
|
|
48
|
+
* full access to the user's Toptal Talent session. See SECURITY.md.
|
|
49
|
+
*
|
|
50
|
+
* Tools are registered in `tools/index.ts`. Today's surface: 4
|
|
51
|
+
* `profile.basic` + 7 `profile.skills` (#73) + 5 `profile.industries` +
|
|
52
|
+
* 5 `profile.education` + 5 `profile.certifications` +
|
|
53
|
+
* 6 `profile.employment` (#74) = 32 tools. MCP tool names use ONLY the
|
|
54
|
+
* canonical sub-domain names per project policy (#72) — CLI aliases like
|
|
55
|
+
* `certs` and `experience` are CLI-only and never appear in the MCP catalog.
|
|
56
|
+
*
|
|
57
|
+
* Path capture (#113): `resolveConfig` is invoked ONCE here at server-
|
|
58
|
+
* construction time. The resolved absolute path is captured into closures
|
|
59
|
+
* for each per-tool auth resolver (`resolveToolAuth`, `loadTokenForTool`,
|
|
60
|
+
* `resolveTokenForTool`) so subsequent tool invocations read AND write
|
|
61
|
+
* the path that was canonical at startup. Mid-session env-var shifts
|
|
62
|
+
* (e.g., parent shell re-exporting `TTCTL_CONFIG_FILE`) are intentionally
|
|
63
|
+
* ignored — long-running MCP sessions need read/write symmetry.
|
|
64
|
+
*
|
|
65
|
+
* Diagnostic instrumentation (#224): every tool registered through this
|
|
66
|
+
* server has its callback wrapped with `wrapToolHandler` so the active
|
|
67
|
+
* MCP diagnostic logger sees `mcp_tool_invoke_start` BEFORE the callback
|
|
68
|
+
* runs, `mcp_tool_invoke_end` AFTER it resolves (or throws), and
|
|
69
|
+
* `mcp_transport_error` when the throw is a transport-class error
|
|
70
|
+
* (`Cf403Error`, `Cf403PersistentError`, `SchedulerBearerExpired`). The
|
|
71
|
+
* wrap is applied transparently by monkey-patching `server.registerTool`
|
|
72
|
+
* BEFORE `registerAllTools` runs, so per-tool files (~30+ registrars)
|
|
73
|
+
* are not aware of the instrumentation — the wiring lives in this one
|
|
74
|
+
* place.
|
|
75
|
+
*
|
|
76
|
+
* Fail-fast: any `ConfigError` thrown by the startup-time `resolveConfig`
|
|
77
|
+
* (e.g., `NO_CREDS` when no candidate file exists) propagates verbatim —
|
|
78
|
+
* the server does NOT start in a half-initialized state.
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildServer(opts?: BuildServerOptions): McpServer;
|
|
81
|
+
/**
|
|
82
|
+
* Run the MCP server over stdio (the canonical transport for Claude Desktop /
|
|
83
|
+
* Claude Code / Cursor / Windsurf).
|
|
84
|
+
*
|
|
85
|
+
* Accepts the same `configPath` knob as `buildServer`. Threading the path
|
|
86
|
+
* through here keeps the umbrella `ttctl mcp [--config <path>]` entry
|
|
87
|
+
* surface as the single point of CLI-flag parsing, with no per-tool
|
|
88
|
+
* config knowledge needed inside the server module.
|
|
89
|
+
*
|
|
90
|
+
* Diagnostic logger wiring (issue #224): when `opts.logger` is supplied,
|
|
91
|
+
* `setMcpDiagnosticLogger(opts.logger)` is called BEFORE `buildServer`
|
|
92
|
+
* so the tool-registration wrapping in `buildServer` already routes
|
|
93
|
+
* through the injected logger. When omitted, the module-scoped default
|
|
94
|
+
* (env-gated stderr emitter for `TTCTL_DEBUG_MCP=1`) remains active.
|
|
95
|
+
* Tests calling `runMcpStdio` with a fake logger thus get full
|
|
96
|
+
* tool-start / tool-end / transport-error visibility without needing
|
|
97
|
+
* to manipulate `process.env`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function runMcpStdio(opts?: BuildServerOptions): Promise<void>;
|
|
100
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOpE,OAAO,EAAE,KAAK,mBAAmB,EAA2C,MAAM,iBAAiB,CAAC;AACpG,OAAO,EAAyB,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAK7F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,SAAS,CAsFpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9E"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { resolveConfig } from "@ttctl/core";
|
|
6
|
+
import { createToolAuthResolver } from "./auth.js";
|
|
7
|
+
import { composeDescription } from "./data-handling.js";
|
|
8
|
+
import { setMcpDiagnosticLogger, wrapToolHandler } from "./diagnostic.js";
|
|
9
|
+
import { scheduleMcpKillSwitch } from "./kill-switch-hook.js";
|
|
10
|
+
import { createTokenLoader } from "./tools/_shared.js";
|
|
11
|
+
import { createTokenResolver } from "./tools/profile/shared.js";
|
|
12
|
+
import { registerAllTools } from "./tools/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Build the TTCtl MCP server. Tools are wired in via `registerAllTools` —
|
|
15
|
+
* a single registration site that pulls in every per-tool module so each
|
|
16
|
+
* tool file stays focused on its own input shape.
|
|
17
|
+
*
|
|
18
|
+
* Trust model: process-level — any process that can spawn `ttctl mcp` gets
|
|
19
|
+
* full access to the user's Toptal Talent session. See SECURITY.md.
|
|
20
|
+
*
|
|
21
|
+
* Tools are registered in `tools/index.ts`. Today's surface: 4
|
|
22
|
+
* `profile.basic` + 7 `profile.skills` (#73) + 5 `profile.industries` +
|
|
23
|
+
* 5 `profile.education` + 5 `profile.certifications` +
|
|
24
|
+
* 6 `profile.employment` (#74) = 32 tools. MCP tool names use ONLY the
|
|
25
|
+
* canonical sub-domain names per project policy (#72) — CLI aliases like
|
|
26
|
+
* `certs` and `experience` are CLI-only and never appear in the MCP catalog.
|
|
27
|
+
*
|
|
28
|
+
* Path capture (#113): `resolveConfig` is invoked ONCE here at server-
|
|
29
|
+
* construction time. The resolved absolute path is captured into closures
|
|
30
|
+
* for each per-tool auth resolver (`resolveToolAuth`, `loadTokenForTool`,
|
|
31
|
+
* `resolveTokenForTool`) so subsequent tool invocations read AND write
|
|
32
|
+
* the path that was canonical at startup. Mid-session env-var shifts
|
|
33
|
+
* (e.g., parent shell re-exporting `TTCTL_CONFIG_FILE`) are intentionally
|
|
34
|
+
* ignored — long-running MCP sessions need read/write symmetry.
|
|
35
|
+
*
|
|
36
|
+
* Diagnostic instrumentation (#224): every tool registered through this
|
|
37
|
+
* server has its callback wrapped with `wrapToolHandler` so the active
|
|
38
|
+
* MCP diagnostic logger sees `mcp_tool_invoke_start` BEFORE the callback
|
|
39
|
+
* runs, `mcp_tool_invoke_end` AFTER it resolves (or throws), and
|
|
40
|
+
* `mcp_transport_error` when the throw is a transport-class error
|
|
41
|
+
* (`Cf403Error`, `Cf403PersistentError`, `SchedulerBearerExpired`). The
|
|
42
|
+
* wrap is applied transparently by monkey-patching `server.registerTool`
|
|
43
|
+
* BEFORE `registerAllTools` runs, so per-tool files (~30+ registrars)
|
|
44
|
+
* are not aware of the instrumentation — the wiring lives in this one
|
|
45
|
+
* place.
|
|
46
|
+
*
|
|
47
|
+
* Fail-fast: any `ConfigError` thrown by the startup-time `resolveConfig`
|
|
48
|
+
* (e.g., `NO_CREDS` when no candidate file exists) propagates verbatim —
|
|
49
|
+
* the server does NOT start in a half-initialized state.
|
|
50
|
+
*/
|
|
51
|
+
export function buildServer(opts = {}) {
|
|
52
|
+
// resolveConfig honors `path` when provided, else falls through to the
|
|
53
|
+
// env→home chain. We always read the canonical absolute path off the
|
|
54
|
+
// returned `path` field; that's what the resolvers close over.
|
|
55
|
+
const resolved = opts.configPath !== undefined ? resolveConfig({ path: opts.configPath }) : resolveConfig();
|
|
56
|
+
const capturedPath = resolved.path;
|
|
57
|
+
const server = new McpServer({
|
|
58
|
+
name: "ttctl",
|
|
59
|
+
version: "0.0.0",
|
|
60
|
+
});
|
|
61
|
+
// Monkey-patch `server.registerTool` BEFORE `registerAllTools` runs so
|
|
62
|
+
// every per-tool registrar's callback is transparently wrapped with the
|
|
63
|
+
// diagnostic-instrumentation contract (#224) AND every per-tool
|
|
64
|
+
// description is augmented with response-side data-handling guidance
|
|
65
|
+
// (#265). The patch:
|
|
66
|
+
// 1. Captures `originalRegisterTool` bound to the McpServer instance
|
|
67
|
+
// so the SDK's internal `_registeredTools` bookkeeping fires normally.
|
|
68
|
+
// 2. Replaces `server.registerTool` with a wrapper that (a) augments
|
|
69
|
+
// the config's `description` field via `composeDescription` (#265),
|
|
70
|
+
// and (b) wraps the callback with `wrapToolHandler(name, cb)` (#224).
|
|
71
|
+
// The wrapper's return is the same `RegisteredTool` handle as the
|
|
72
|
+
// original — `.enable()` / `.disable()` / `.update()` work unchanged
|
|
73
|
+
// on the registered tool.
|
|
74
|
+
// 3. Uses an `unknown` cast on the patch because `registerTool`'s
|
|
75
|
+
// generic signature (with `ZodRawShapeCompat | AnySchema` + the
|
|
76
|
+
// output schema variant) is hostile to a single typed override.
|
|
77
|
+
// The wrapper preserves runtime behavior; type safety at the
|
|
78
|
+
// callsites (each registrar) is unaffected.
|
|
79
|
+
// The signature of McpServer.registerTool is an overload set that
|
|
80
|
+
// resolves to `never` under `Parameters<...>`, so we type the patch
|
|
81
|
+
// as a permissive `(...args: unknown[]) => unknown` and lean on the
|
|
82
|
+
// outer cast to restore the McpServer's view. Runtime semantics are
|
|
83
|
+
// unchanged — name is always args[0]: string, config args[1]: object,
|
|
84
|
+
// cb args[2]: function — and the original registerTool reapplies the
|
|
85
|
+
// overload-correct type check on its side.
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
const originalRegisterTool = server.registerTool.bind(server);
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
const patchedRegisterTool = (...args) => {
|
|
90
|
+
const name = args[0];
|
|
91
|
+
const config = args[1];
|
|
92
|
+
const cb = args[2];
|
|
93
|
+
// Augment the tool's description with response-side data-handling
|
|
94
|
+
// guidance (#265). The augmentation is at registration time so every
|
|
95
|
+
// tool — current and future — inherits the footer without per-tool
|
|
96
|
+
// boilerplate. High-risk tools (per the threat-model § 5 audit, set
|
|
97
|
+
// membership lives in `data-handling.ts`) additionally get a
|
|
98
|
+
// third-party-content notice.
|
|
99
|
+
let patchedConfig = config;
|
|
100
|
+
if (typeof config === "object" && config !== null) {
|
|
101
|
+
const cfg = config;
|
|
102
|
+
patchedConfig = {
|
|
103
|
+
...cfg,
|
|
104
|
+
description: composeDescription(name, cfg.description),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const wrappedCb = wrapToolHandler(name, cb);
|
|
108
|
+
return originalRegisterTool(name, patchedConfig, wrappedCb);
|
|
109
|
+
};
|
|
110
|
+
server.registerTool = patchedRegisterTool;
|
|
111
|
+
registerAllTools(server, {
|
|
112
|
+
resolveToolAuth: createToolAuthResolver(capturedPath),
|
|
113
|
+
loadTokenForTool: createTokenLoader(capturedPath),
|
|
114
|
+
resolveTokenForTool: createTokenResolver(capturedPath),
|
|
115
|
+
});
|
|
116
|
+
// Fire-and-forget kill-switch check (#312, AC 4). Detached on
|
|
117
|
+
// purpose — buildServer is sync (the MCP SDK has no async-build
|
|
118
|
+
// path), and a synchronous block here would delay the JSON-RPC
|
|
119
|
+
// handshake. The check writes to stderr when the running version
|
|
120
|
+
// matches an entry; never throws (fail-silent contract); never
|
|
121
|
+
// refuses (refusing a long-lived MCP server has no usable
|
|
122
|
+
// semantics — see kill-switch-hook.ts docstring).
|
|
123
|
+
//
|
|
124
|
+
// Schedules a ~24h refetch with `.unref()` so the timer doesn't
|
|
125
|
+
// keep the process alive solely for the recurring fetch.
|
|
126
|
+
if (opts.killSwitch !== null) {
|
|
127
|
+
scheduleMcpKillSwitch(opts.killSwitch ?? {});
|
|
128
|
+
}
|
|
129
|
+
return server;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Run the MCP server over stdio (the canonical transport for Claude Desktop /
|
|
133
|
+
* Claude Code / Cursor / Windsurf).
|
|
134
|
+
*
|
|
135
|
+
* Accepts the same `configPath` knob as `buildServer`. Threading the path
|
|
136
|
+
* through here keeps the umbrella `ttctl mcp [--config <path>]` entry
|
|
137
|
+
* surface as the single point of CLI-flag parsing, with no per-tool
|
|
138
|
+
* config knowledge needed inside the server module.
|
|
139
|
+
*
|
|
140
|
+
* Diagnostic logger wiring (issue #224): when `opts.logger` is supplied,
|
|
141
|
+
* `setMcpDiagnosticLogger(opts.logger)` is called BEFORE `buildServer`
|
|
142
|
+
* so the tool-registration wrapping in `buildServer` already routes
|
|
143
|
+
* through the injected logger. When omitted, the module-scoped default
|
|
144
|
+
* (env-gated stderr emitter for `TTCTL_DEBUG_MCP=1`) remains active.
|
|
145
|
+
* Tests calling `runMcpStdio` with a fake logger thus get full
|
|
146
|
+
* tool-start / tool-end / transport-error visibility without needing
|
|
147
|
+
* to manipulate `process.env`.
|
|
148
|
+
*/
|
|
149
|
+
export async function runMcpStdio(opts = {}) {
|
|
150
|
+
if (opts.logger !== undefined) {
|
|
151
|
+
setMcpDiagnosticLogger(opts.logger);
|
|
152
|
+
}
|
|
153
|
+
const server = buildServer(opts);
|
|
154
|
+
const transport = new StdioServerTransport();
|
|
155
|
+
await server.connect(transport);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|