Other formats agents might prefer:
markdownjsonllms.txt

Agent? You probably want markdown or json, or Pod over MCP.

Reported issues for ttctl

Pod holds 22 of 219 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to ttctl.

Most discussed

(feat) applications interviews notes update: UpdateInterviewTalentNotes mutation wrapper (CLI + MCP)

[!WARNING] BLOCKED on vendor (2026-06-12) — the talent-notes write surface is broken or unexposed in the Toptal UI in every checked state (scheduled interview 2026-06-03; during the live 2026-06-05 interview), and the live-probed API write model is append-only with undeletable guide-section notes — unsafe to ship from inference. Do NOT test-write against a real account. Implementation preserved on branch feat/441-interview-notes-update (HEAD d115661; closed draft PR #676). Unbl

Read the thread · 2026-05-20 · open · 6 comments

surveys submit: OPEN_TEXT question with a sentinel option rejects free text

ttctl_surveys_submit (and the surveys submit CLI path) cannot answer free-text questions on ENGAGEMENT_ENDED surveys. Every OPEN_TEXT question on that survey kind is returned by the API carrying a single sentinel option in answers[], and buildSurveyAnswers dispatches question type on answers.length rather than on inputType — so the question takes the multiple-choice path and real prose is validated against a one-item enum.

Environment

Detail Value

Read the thread · 2026-08-11 · closed · 4 comments

(wire-broke) external_update — twitter field rejected, whole call fails

TTCtl version

v0.1.0-rc.7

Exact command that failed

MCP tool call via Claude Code:

{
  "tool": "ttctl_profile_external_update",
  "args": {
    "linkedin": "https://www.linkedin.com/in/<handle>",
    "github": "https://github.com/<handle>",
    "website": "https://<personal-domain>",
    "twitter": "https://x.com/<handle>"
  }
}

Error output (stderr + stdout)

Error: ttctl_profile_external_update failed (GRAPHQL_ERROR): External profile update failed: Variable $

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/526) · 2026-05-23 · closed · 3 comments

### (feat) profile.employment.add: custom (non-catalog) workplace via employerId:null

# Feature: support custom (non-catalog) workplace on `employment.add`

> **Updated 2026-05-19**: original framing ("create a new employer in Toptal's catalog") was based on an incorrect premise — there is no `CreateEmployer` mutation. The real mechanism is the existing `CreateEmployment` with `employerId: null`. Decision: **Option C** (see [decision comment](#) and § Decision below). Original Options A/B and their ACs are superseded.

## Use case

I need to add an employment entry to my Toptal p

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/401) · 2026-05-19 · closed · 3 comments

### (wire-broke) basic_update / employment_update / employment_add send null for server-required non-null fields

# Bug: write mutations send `null` for server-required non-null fields

## Environment

- **TTCtl version**: `v0.1.0-rc.3` (2026-05-19) — the release that closed #379
- **Invocation**: MCP server (via Claude Code MCP host); not CLI
- **Node.js**: v26.0.0
- **OS**: macOS (Darwin 25.3.0)
- **Toptal surface**: `talent-profile` (per dry-run preview output)
- **`auth status`**: valid (read tools work — `basic_show`, `employment_show`, `industries_list`, `skills_list` all return data)

This is a **wir

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/392) · 2026-05-19 · closed · 3 comments

### (feat) MCP list tools: add pagination to jobs/applications/viewed surfaces

## Problem / Motivation

The MCP list tools — `ttctl_jobs_list`, `ttctl_applications_list`, and `ttctl_jobs_viewed` — return only the first page of results (≤20 items) with no exposed pagination parameter. The Toptal portal paginates through the same data source, so the wire likely supports pagination — the MCP wrapper just doesn't surface it.

In practice this means callers cannot iterate beyond the first 20 items:

- A user with 37+ eligible jobs sees only ~20 via `ttctl_jobs_list`.
- A user w

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/369) · 2026-05-18 · closed · 3 comments

### MVP MCP tools: profile_show + profile_update

## Summary

Register the first two MCP tools on the (currently empty) MCP server: `profile_show` (read) and `profile_update` (write). Both call the same code paths as their CLI counterparts.

## Context

Per `CLAUDE.md` § MCP Integration: `ttctl mcp` starts an MCP server on stdio. Currently the server is built but registers no tools. This issue is the MVP MCP surface — two tools that mirror the read/write profile vertical.

The tools must be demonstrable from a Claude Desktop config — once regis

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/10) · 2026-05-05 · closed · 3 comments

### Expose timesheet approval status, not just submission state

## Problem / Motivation

TTCtl's timesheet tools surface only talent-side submission state. There's no way to answer a routine question: have any of my submitted timesheets been approved, or are they still waiting on someone else?

Across `ttctl_timesheet_list`, `ttctl_timesheet_pending_list`, and `ttctl_timesheet_show`, the only status-bearing fields returned are:

- `timesheetSubmitted` — whether *I* submitted it
- `timesheetOverdue`

No `approved`, `approvalStatus`, `approvedAt`, `approver`, 

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/849) · 2026-07-10 · closed · 2 comments

## Most recent

### (chore) wire-routing manifest gate: enforce docs/wire-validation-routing.md against invoked ops

Follow-up from #883 (PR #883 body, § *Known gap, unowned — flagged for a maintainer call*). The second of the two ungated cross-domain doc surfaces. The maintainer call has been taken: build the mechanical check.

## The gap

CLAUDE.md § Track 1 vs Track 2 disposition states the rule outright: *"The manifest must be updated in the same PR as any new op invocation."* The manifest is [`docs/wire-validation-routing.md`](../blob/main/docs/wire-validation-routing.md), the authoritative per-op `T1` / 

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/887) · 2026-08-12 · closed · 1 comment

### (chore) mcp tool-catalog gate: enforce packages/mcp/README.md against the EXPECTED_TOOLS roster

Follow-up from #883 (PR #883 body, § *Known gap, unowned — flagged for a maintainer call*), which widened `schema-contract-disposition` but deliberately left `doc-surface` narrow, on the grounds that *"an attestation is not detection"* — and named two cross-domain doc surfaces that are genuinely ungated. This is the first of the two. The maintainer call has been taken: build the mechanical check.

## The gap

`packages/mcp/README.md` carries a tool catalog — a stated total plus a per-domain brea

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/886) · 2026-08-12 · closed · 1 comment

### (feat) MCP-layer sliding-window rate limit for bulk job-signal mutations (anti-automation)

## Problem / Motivation

[ADR-009 § "What We're NOT Solving"](hq/engineering/adr/ADR-009-per-domain-consent-vocabulary.md)
explicitly carved this out as a **separate work item** to be created when #475
is picked up:

> **Anti-automation enforcement for #475** — the ≤20-id batch bound is
> README-discipline; the council's security panelist flagged that an agent can
> loop 50×20 (1000-id mass-view, schema-compliant). This needs an MCP-layer
> sliding-window rate limit, tracked as a **separate work

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/818) · 2026-06-16 · open · 0 comments

### (chore) disposition transitive npm-audit advisories (hono/ip-address/qs via MCP SDK — unreachable on stdio)

## Problem / Motivation

`pnpm audit --prod` reports **9 advisories** (1 low, 8 moderate) ahead of the v0.1.0 stable cut. A security-aware Toptal engineer runs `npm audit` right after `npm i -g ttctl` — for a credential-handling, Cloudflare-TLS-impersonating tool, "9 vulnerabilities" with no explanation on day one is a first-contact trust ding.

All 9 are **transitive through `@modelcontextprotocol/sdk`** and sit in its optional HTTP/SSE transport stack:

| Severity | Package | Advisory (abbrev.

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/770) · 2026-06-13 · closed · 1 comment

### (docs) packages/mcp/README.md: recount tool catalog for v0.1.0 stable (88 → actual, add surveys)

## Problem / Motivation

`packages/mcp/README.md:34` states the MCP server "registers **88 tools** (at time of writing)" and gives a per-domain breakdown. Both are stale on the published surface — and `packages/mcp/README.md` ships inside the `@ttctl/mcp` npm tarball, so this is the permanent first-impression catalog a v0.1.0-stable user reads.

Evidence (2026-06-13, against `main`):

- **Count undercount (~47%)**: README says 88; two independent counts of `"ttctl_*"` literals put the real total

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/769) · 2026-06-13 · closed · 1 comment

### (feat) readme-verbs gate: check ttctl_* MCP tool-name claims against MCP tool registrations

Follow-up to #762 (PR #764), from the post-submit review's frame-level finding.

The README "What It Does" section carries MCP tool-name claims (e.g. the Jobs bullet ships `ttctl_jobs_apply_similar_answers`). `scripts/check-readme-verbs.ts` reports these as unchecked by design — its comparison universe is the CLI command tree only. But MCP tool names are mechanically checkable in principle against the tool registrations under `packages/mcp/src/tools/**`, so the #751 drift class has an MCP-parity

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/765) · 2026-06-12 · closed · 2 comments

### (wire-broke) surveys submit: INTERVIEW_ENDED occurred checkbox unanswerable

### TTCtl version

`0.1.0-rc.13` (MCP server launched as `npx ttctl@next mcp`)

### Exact command that failed

MCP tool `ttctl_surveys_submit` (host: Claude Code), against a pending `INTERVIEW_ENDED` survey:

```json
{
  "surveyId": "<survey-id>",
  "answers": [
    { "questionId": "VjEtSW50ZXJ2aWV3RW5kZWRTdXJ2ZXlRdWVzdGlvbi0w", "value": "5" },
    { "questionId": "VjEtSW50ZXJ2aWV3RW5kZWRTdXJ2ZXlRdWVzdGlvbi0z", "value": "unsure" },
    { "questionId": "VjEtSW50ZXJ2aWV3RW5kZWRTdXJ2ZXlRdWVzdGlvbi0

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/754) · 2026-06-12 · closed · 2 comments

### (fix) README: drop unshipped timesheet update verb until #458 lands

## Problem / Motivation

`README.md:38` reads "**Timesheets** — list, view, submit, and update timesheet billing cycles", but `UpdateTimesheet` has no invocation in core and `packages/cli/src/commands/timesheet/` registers only list / show / submit / pending. The README ships in the npm package — users are invited to invoke a verb that does not exist.

How it happened: docs issue #431 ("append update verb") was closed completed on 2026-05-22 via e8b25dc while its prerequisite #458 (the `timeshee

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/751) · 2026-06-12 · closed · 1 comment

### (docs) security: classify surveys_* MCP tools in MCP-leakage threat model

Discovered during #704 (surfacing the shipped surveys domain in the storefront READMEs) — out of that issue's scope, deferred here.

## Gap

The `surveys` domain shipped in rc.13 (#671–#674) with three MCP tools:

- `ttctl_surveys_list` — read (pending surveys)
- `ttctl_surveys_submit` — destructive write (IRREVERSIBLE, consent-gated)
- `ttctl_surveys_feedback` — destructive write (IRREVERSIBLE, consent-gated)

`docs/security/mcp-leakage-threat-model.md` has **zero** `survey` mentions (`grep -i 

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/725) · 2026-06-04 · closed · 1 comment

### (fix) MCP upload sandbox: resolve symlinks to close traversal/exfil bypass

Surfaced by the 0.1.0 release-readiness audit (2026-06-03). **Defense-in-depth bypass of a documented control; localized fix.**

## Problem
The MCP file-upload path-prefix sandbox does not resolve symlinks. `validateSandbox` (`packages/mcp/src/tools/file-upload.ts:228`) uses `path.resolve(filePath)` (logical `..` normalization only) and compares against `~/Documents | ~/Downloads | ~/Desktop`; there is no `realpath` / `lstat` / `readlink` anywhere in the upload path. The service then reads the t

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/707) · 2026-06-03 · closed · 1 comment

### (docs) README: surface shipped surveys domain; drop coming note

Surfaced by the 0.1.0 release-readiness audit (2026-06-03). **Storefront accuracy — fix before tagging.**

## Problem
The `surveys` domain shipped (rc.13; #671-#674: `ttctl surveys list/submit/feedback`, `core` `export * as surveys` at `packages/core/src/index.ts:89`, MCP `ttctl_surveys_*`) but the storefront still hides it:
- `README.md:34-42` "What It Does" — no surveys bullet.
- `README.md:64` — still reads "the `surveys` commands will appear under What It Does as they ship" (future framing).

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/704) · 2026-06-03 · closed · 1 comment

### (docs) README: align Node minimum to >=22.19.0 across package READMEs

Surfaced by the 0.1.0 release-readiness audit (2026-06-03). **Storefront accuracy — fix before tagging.**

## Problem
Four package READMEs state "Requires Node.js ≥ 24" but the published `engines.node` is `>=22.19.0`:
- `packages/{cli,core,mcp,ttctl}/README.md:22` → "Node.js ≥ 24"
- `packages/{cli,core,mcp,ttctl}/package.json` → `"engines": { "node": ">=22.19.0" }`
- Root `README.md:70` + root `package.json:9` → `>=22.19.0` (correct). CLAUDE.md frames Node 24 only as the release-action default L

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/703) · 2026-06-03 · closed · 1 comment

### (feat) applications interview show: inline job.title (Approach B of #694)

## Problem

Split from #694 (**Approach B**). `applications interview show` carries `job` only as `{ id, activityItemId }` — no human-readable **title**. A user can't tell *which job* an interview is for without a second command. #694 Approach A added a discovery footer pointing at `applications show <activityItemId>`; Approach B surfaces the title **inline** on the interview detail.

## Scope

Add `job.title` to the `Interview` op selection + projection + renderer:

- `packages/core/src/service

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/696) · 2026-06-03 · closed · 1 comment

### (feat) extend CLI/MCP output on interview + availability-request ops for better UX

## Problem

The interview + availability-request read ops (`applications interview show`, `applications interview notes show`, `applications interview guide show`, `applications availability-request show`) intentionally trim heavy job-context sub-trees from the wire — now documented as **BY-DESIGN wire trims** (#685). The trimmed context is reachable via a sibling command (`applications show <activityId>`), but:

1. **The end user isn't told.** Only the JSDoc (contributor-facing) names the sibli

[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/694) · 2026-06-03 · closed · 1 comment

The remaining reports are on [the project's issue tracker](https://github.com/alexey-pelykh/ttctl/issues).