@taiga-ai/mcp-server 0.26.0-dev.515 → 0.26.0-dev.516

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 (2) hide show
  1. package/dist/index.js +87 -74
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2688,7 +2688,7 @@ function setupNextStep(product) {
2688
2688
  if (product?.status !== "draft") return null;
2689
2689
  if (isImportedProduct(product)) switch (importStatusOf(product)) {
2690
2690
  case "analyzing":
2691
- case "generating_docs": return `The import is running and derives the documents from the code (taiga_wait_for_job follows its job). Then review them, and ${FINISH_DISCOVERY}`;
2691
+ case "generating_docs": return `The import is running and derives the documents from the code (taiga_wait (jobId) follows its job). Then review them, and ${FINISH_DISCOVERY}`;
2692
2692
  case "complete": return "The import is done: review the reality documents (taiga_get_product_document without type), then taiga_finish_discovery opens the product and starts planning its first initiatives (paid agent work: ask the person first).";
2693
2693
  case "failed":
2694
2694
  case "cancelled": return "The last import did not finish: taiga_start_import runs it again.";
@@ -2724,7 +2724,7 @@ const BY_ARGUMENT = {
2724
2724
  factoryId: "taiga_list_factories lists the factories the key can see.",
2725
2725
  findingId: "taiga_list_findings lists a product's findings.",
2726
2726
  violationId: "taiga_get_policy_standing lists a product's policy violations.",
2727
- environmentId: "taiga_list_environments lists the product's environments with their IDs; a product ID is not one.",
2727
+ environmentId: "taiga_get_deployments lists the product's environments with their IDs; a product ID is not one.",
2728
2728
  knowledgeId: "taiga_list_knowledge lists the knowledge files.",
2729
2729
  productId: "taiga_list_products lists the products the key can see; a name is not an ID (taiga_get_product takes a slug)."
2730
2730
  };
@@ -3308,7 +3308,7 @@ function nextStepFor(state) {
3308
3308
  if (state.linked === false) return "Link its repository (taiga_list_linkable_repositories, then taiga_link_repository), then taiga_start_import.";
3309
3309
  if (state.linked === null && state.importState === "not started") return setupNextStep(state.product);
3310
3310
  switch (state.importState) {
3311
- case "running": return "The import is running and derives the documents from the code; taiga_wait_for_job follows it.";
3311
+ case "running": return "The import is running and derives the documents from the code; taiga_wait (jobId) follows it.";
3312
3312
  case "failed": return "The last import did not finish: taiga_start_import runs it again.";
3313
3313
  case "complete":
3314
3314
  if (state.missing && state.missing.length > 0) return `The import finished but ${named(state.missing)} ${state.missing.length === 1 ? "is" : "are"} not published: taiga_get_product_document without type shows them, taiga_publish_product_document publishes a draft.`;
@@ -3787,7 +3787,7 @@ function registerProductTools(server, client) {
3787
3787
  }));
3788
3788
  server.registerTool("taiga_get_product", {
3789
3789
  title: "Get product",
3790
- description: "Read one product and its settings by ID, slug or git remote URL (git remote get-url origin; a URL linked to more than one product is refused with their IDs): name, factory, kind, autonomy, what Taiga builds (application, infrastructure, deployment) and network access, unsetMeans saying what a null setting follows. In setup, readiness says whether it can finish now and what is missing (repository, import, unpublished documents, unstamped checks), plus nextStep. For initiatives, runs and findings, use taiga_get_product_context.",
3790
+ description: "Read one product's settings only (for where it stands, use taiga_get_product_context), by ID, slug or git remote URL (git remote get-url origin; a URL linked to more than one product is refused with their IDs): name, factory, kind, autonomy, what Taiga builds (application, infrastructure, deployment) and network access, unsetMeans saying what a null setting follows. In setup, readiness says whether it can finish now and what is missing (repository, import, unpublished documents, unstamped checks), plus nextStep. For initiatives, runs and findings, use taiga_get_product_context.",
3791
3791
  inputSchema: z.object({
3792
3792
  productId: productId.optional(),
3793
3793
  slug: slug.optional(),
@@ -4139,7 +4139,7 @@ function registerJobTools(server, client) {
4139
4139
  if (!client.hasCapability("products:read")) return;
4140
4140
  server.registerTool("taiga_get_job", {
4141
4141
  title: "Get an agent job",
4142
- description: "Read one agent job, such as a taiga_request_initiatives request: status (pending, processing, completed, failed, cancelled), progress and any error. A finished initiative request lists the initiatives it added, or why none (already covered, not readable as work, outside the product, or not allowed by a policy, with the rule it conflicts with and how to decide otherwise). taiga_wait_for_job waits for it.",
4142
+ description: "Read one agent job, such as a taiga_request_initiatives request: status (pending, processing, completed, failed, cancelled), progress and any error. A finished initiative request lists the initiatives it added, or why none (already covered, not readable as work, outside the product, or not allowed by a policy, with the rule it conflicts with and how to decide otherwise). taiga_wait (jobId) waits for it.",
4143
4143
  inputSchema: z.object({ jobId: idInput("job", "the tool that started the job returned it") }),
4144
4144
  annotations: READ_ONLY
4145
4145
  }, (params) => runTool(async () => toolResult(jobReport((await client.getAgentJob(params.jobId)).data))));
@@ -5247,7 +5247,7 @@ function registerBacklogReads(server, client) {
5247
5247
  function registerRequest(server, client) {
5248
5248
  server.registerTool("taiga_request_initiatives", {
5249
5249
  title: "Request new initiatives",
5250
- description: "Ask an agent to turn described work into initiatives in the product's Backlog, and return the job. One piece of work becomes one initiative, a program an ordered set with dependencies. It may add none and say why (already covered, not work, outside the product, or a named policy forbids it). Omit title and description for the one next initiative the product most needs. taiga_wait_for_job gives the outcome. One request per product at a time (CONFLICT). Nothing goes on the line.",
5250
+ description: "Ask an agent to turn described work into initiatives in the product's Backlog, and return the job. One piece of work becomes one initiative, a program an ordered set with dependencies. It may add none and say why (already covered, not work, outside the product, or a named policy forbids it). Omit title and description for the one next initiative the product most needs. taiga_wait (jobId) gives the outcome. One request per product at a time (CONFLICT). Nothing goes on the line.",
5251
5251
  inputSchema: z.object({
5252
5252
  productId,
5253
5253
  title: z.string().max(500).optional().describe("What the work is, in a line"),
@@ -7073,7 +7073,7 @@ function deploymentLines(rows) {
7073
7073
  };
7074
7074
  });
7075
7075
  }
7076
- const environmentId = idInput("environment", "taiga_list_environments lists them; a product ID is not one");
7076
+ const environmentId = idInput("environment", "taiga_get_deployments lists them; a product ID is not one");
7077
7077
  /** The windows the pages offer, as the schema's own values rather than a number checked afterwards. */
7078
7078
  const windowDays = z.literal([
7079
7079
  30,
@@ -7083,11 +7083,11 @@ const windowDays = z.literal([
7083
7083
  /** Whether a failure is the API's (refused, missing, unreachable) rather than a bug here. */
7084
7084
  const isApiFailure = (error) => error instanceof TaigaApiError || isNetworkFailure(error);
7085
7085
  /** A part that may fail on its own without failing the read: null and the reason, or the value. */
7086
- async function part(read) {
7086
+ async function part(read, rethrow = () => false) {
7087
7087
  try {
7088
7088
  return { value: await read() };
7089
7089
  } catch (error) {
7090
- if (!isApiFailure(error)) throw error;
7090
+ if (!isApiFailure(error) || rethrow(error)) throw error;
7091
7091
  return { unavailable: error instanceof TaigaApiError ? `${error.status}: ${error.apiMessage ?? ""}`.trim() : "unreachable" };
7092
7092
  }
7093
7093
  }
@@ -7118,7 +7118,7 @@ function registerObserveTools(server, client) {
7118
7118
  if (!client.hasCapability("products:read")) return;
7119
7119
  server.registerTool("taiga_get_deployments", {
7120
7120
  title: "Get what runs where",
7121
- description: "Read what runs in each of a product's environments and its recent deployments, newest first: commit, pull request, status, deployer and, on failure, the provider's error. What runs is the latest successful deployment, so a newer failure shows beside it. History is mirrored from the provider, including deployments made outside Taiga; syncStatus says when it last synced. environmentId narrows to one environment. Taiga never deploys.",
7121
+ description: "Read a product's environments with their IDs, what runs in each, and its recent deployments, newest first: commit, pull request, status, deployer and, on failure, the provider's error. What runs is the latest successful deployment, so a newer failure shows beside it. History is mirrored from the provider, including deployments made outside Taiga; syncStatus says when it last synced. environmentId narrows to one environment. Taiga never deploys.",
7122
7122
  inputSchema: z.object({
7123
7123
  productId,
7124
7124
  environmentId: environmentId.optional(),
@@ -7126,25 +7126,31 @@ function registerObserveTools(server, client) {
7126
7126
  }),
7127
7127
  annotations: READ_ONLY
7128
7128
  }, (params) => runTool(async () => {
7129
- const [environments, current, history, sync] = await Promise.all([
7129
+ const [environments, currentRead, historyRead, sync] = await Promise.all([
7130
7130
  allEnvironments(client, params.productId),
7131
- client.getCurrentDeployments(params.productId),
7132
- client.listDeployments(params.productId, {
7131
+ part(() => client.getCurrentDeployments(params.productId)),
7132
+ part(() => client.listDeployments(params.productId, {
7133
7133
  environmentId: params.environmentId,
7134
7134
  limit: params.limit ?? 10,
7135
7135
  cursor: params.cursor
7136
- }),
7136
+ }), (error) => Boolean(params.environmentId) && error instanceof TaigaApiError && error.status === 404),
7137
7137
  part(() => client.getDeploymentSyncStatus(params.productId))
7138
7138
  ]);
7139
- const now = current.data;
7140
- const note = environments.rows.length === 0 ? "This product has no environments yet: an environment is defined with taiga_create_environment, and its deployments are mirrored from the provider once it is." : history.data.length === 0 && !params.cursor ? "No deployments recorded yet: none was mirrored from the provider (syncStatus says when it last synced), or nothing has been deployed." : null;
7141
- const live = new Set([...now.current ?? [], ...now.deploying ?? []].map((row) => row.id));
7139
+ const now = "value" in currentRead ? currentRead.value.data : null;
7140
+ const history = "value" in historyRead ? historyRead.value : null;
7141
+ const note = environments.rows.length === 0 ? "This product has no environments yet: an environment is defined with taiga_create_environment, and its deployments are mirrored from the provider once it is." : history && history.data.length === 0 && !params.cursor ? "No deployments recorded yet: none was mirrored from the provider (syncStatus says when it last synced), or nothing has been deployed." : null;
7142
+ const live = new Set([...now?.current ?? [], ...now?.deploying ?? []].map((row) => row.id));
7143
+ const unavailable = {
7144
+ ..."unavailable" in currentRead ? { running: currentRead.unavailable } : {},
7145
+ ..."unavailable" in historyRead ? { history: historyRead.unavailable } : {}
7146
+ };
7142
7147
  return toolResult({
7143
7148
  environments: pickFields(environments.rows, ENVIRONMENT_FIELDS),
7144
7149
  ...environments.more ? { moreEnvironments: true } : {},
7145
- running: deploymentLines(now.current ?? []),
7146
- deploying: deploymentLines(now.deploying ?? []),
7147
- history: page(deploymentLines(history.data.filter((row) => !live.has(row.id))), history.nextCursor),
7150
+ running: now ? deploymentLines(now.current ?? []) : null,
7151
+ deploying: now ? deploymentLines(now.deploying ?? []) : null,
7152
+ history: history ? page(deploymentLines(history.data.filter((row) => !live.has(row.id))), history.nextCursor) : null,
7153
+ ...Object.keys(unavailable).length > 0 ? { unavailable } : {},
7148
7154
  ..."value" in sync ? { syncStatus: sync.value.data } : {
7149
7155
  syncStatus: null,
7150
7156
  syncUnavailable: sync.unavailable
@@ -7152,19 +7158,6 @@ function registerObserveTools(server, client) {
7152
7158
  ...note ? { note } : {}
7153
7159
  });
7154
7160
  }));
7155
- server.registerTool("taiga_list_environments", {
7156
- title: "List a product's environments",
7157
- description: "List a product's environments with ID, type, order and App URL. These are the environment IDs taiga_get_delivery_metrics, taiga_get_monitoring and taiga_update_environment take. taiga_get_deployments lists them too, with what runs where.",
7158
- inputSchema: z.object({ productId }),
7159
- annotations: READ_ONLY
7160
- }, (params) => runTool(async () => {
7161
- const { rows, more } = await allEnvironments(client, params.productId);
7162
- return toolResult({
7163
- data: pickFields(rows, ENVIRONMENT_FIELDS),
7164
- ...more ? { more: "More environments than this answer reads; the rest were left out." } : {},
7165
- ...rows.length === 0 ? { note: "No environment is defined yet; taiga_create_environment adds one." } : {}
7166
- });
7167
- }));
7168
7161
  server.registerTool("taiga_get_delivery_metrics", {
7169
7162
  title: "Get delivery metrics",
7170
7163
  description: "Read DORA's five delivery metrics for one environment (the page shows production) over 30, 60 or 90 days: deployment frequency, lead time, failed deployment recovery time, change-failure rate and rework rate, each with its trend against the window before and a performance band (none for rework rate). Computed from deployments, not incidents; previews and checks do not count. Under ten deployments a trend is approximate; a metric with no data says so instead of a number. Without environmentId, the product's production environment when it has exactly one.",
@@ -7184,7 +7177,7 @@ function registerObserveTools(server, client) {
7184
7177
  note: "This product has no environments, so there is nothing to measure: an environment is defined with taiga_create_environment, and its deployments are mirrored from the provider once it is."
7185
7178
  });
7186
7179
  const production = all.filter((env) => env.type === "production");
7187
- if (production.length !== 1 || more) return toolError("VALIDATION_ERROR", `Pass environmentId: the product has ${more ? "more environments than one read holds, and " : ""}${production.length === 0 ? "no" : production.length} production environment${production.length === 1 ? "" : "s"}${more ? " among the first" : ""}. taiga_list_environments lists them with their IDs.`, { environments: all.map((env) => ({
7180
+ if (production.length !== 1 || more) return toolError("VALIDATION_ERROR", `Pass environmentId: the product has ${more ? "more environments than one read holds, and " : ""}${production.length === 0 ? "no" : production.length} production environment${production.length === 1 ? "" : "s"}${more ? " among the first" : ""}. taiga_get_deployments lists them with their IDs.`, { environments: all.map((env) => ({
7188
7181
  id: env.id,
7189
7182
  name: env.name,
7190
7183
  type: env.type
@@ -7313,7 +7306,7 @@ function registerProductDocumentWrites(server, client) {
7313
7306
  }, (params) => runTool(async () => toolResult((await client.markProductDocumentReviewed(params.productId, params.type)).data)));
7314
7307
  if (canRead) server.registerTool("taiga_regenerate_product_document", {
7315
7308
  title: "Regenerate a product document",
7316
- description: "Regenerate one product document from the documents it derives from, when it is outdated or needs another pass, and return the job. The result is published and documents derived from it are marked outdated. The Specification (from the Discovery conversation) and the Look & Feel (from its concepts) are made in the web. Documents Discovery requires regenerate only while Discovery is open; the optional Service Blueprint any time. taiga_wait_for_job says when it is done.",
7309
+ description: "Regenerate one product document from the documents it derives from, when it is outdated or needs another pass, and return the job. The result is published and documents derived from it are marked outdated. The Specification (from the Discovery conversation) and the Look & Feel (from its concepts) are made in the web. Documents Discovery requires regenerate only while Discovery is open; the optional Service Blueprint any time. taiga_wait (jobId) says when it is done.",
7317
7310
  inputSchema: z.object({
7318
7311
  productId,
7319
7312
  type: generatedType
@@ -22658,7 +22651,7 @@ function registerSetupTools(server, client) {
22658
22651
  }));
22659
22652
  server.registerTool("taiga_refresh_reality_documents", {
22660
22653
  title: "Refresh a product's documents from its code",
22661
- description: "Derive the reality documents again from the code on the branch Taiga works on (effectiveBranch): all, or the ones named. For a document the import misread, refresh it instead of importing again; unchanged sections are not republished. Returns the job; taiga_wait_for_job says when it is done.",
22654
+ description: "Derive the reality documents again from the code on the branch Taiga works on (effectiveBranch): all, or the ones named. For a document the import misread, refresh it instead of importing again; unchanged sections are not republished. Returns the job; taiga_wait (jobId) says when it is done.",
22662
22655
  inputSchema: z.object({
22663
22656
  productId,
22664
22657
  docTypes: z.array(z.enum(REALITY_DERIVED_DOCUMENT_TYPES)).min(1).max(REALITY_DERIVED_DOCUMENT_TYPES.length).optional().describe("The documents to refresh; omit for all")
@@ -22687,7 +22680,7 @@ function registerSetupTools(server, client) {
22687
22680
  if (!client.hasCapability("products:read")) return;
22688
22681
  server.registerTool("taiga_start_import", {
22689
22682
  title: "Import a codebase into a product",
22690
- description: "Start importing a product created for import, and return the job. Taiga reads the linked repository and derives the full document set from the code, citing the files. Then the scanners sweep it, the documents are assessed against policies, and the first initiatives are planned when Discovery finishes. Refused for a scratch product, without a linked repository, or while an import runs. taiga_wait_for_job says when it is done (it takes a while).",
22683
+ description: "Start importing a product created for import, and return the job. Taiga reads the linked repository and derives the full document set from the code, citing the files. Then the scanners sweep it, the documents are assessed against policies, and the first initiatives are planned when Discovery finishes. Refused for a scratch product, without a linked repository, or while an import runs. taiga_wait (jobId) says when it is done (it takes a while).",
22691
22684
  inputSchema: z.object({ productId }),
22692
22685
  annotations: OPERATE
22693
22686
  }, (params) => runTool(async () => {
@@ -22954,20 +22947,25 @@ function judgeBuild(until) {
22954
22947
  return null;
22955
22948
  };
22956
22949
  }
22950
+ /** What each target may be waited for. A job has one condition, finished, so it takes none. */
22951
+ const UNTIL_FOR = {
22952
+ initiative: ITEM_CONDITIONS,
22953
+ run: BUILD_CONDITIONS
22954
+ };
22955
+ const ALL_CONDITIONS = [.../* @__PURE__ */ new Set([...ITEM_CONDITIONS, ...BUILD_CONDITIONS])];
22956
+ /**
22957
+ * One tool for waiting (PRO-2941): three wait tools, one per thing waited on, were two more names in a catalogue
22958
+ * every review ranked too large. The ID says what is waited on; until says for what, refused when it does not
22959
+ * apply to that thing.
22960
+ */
22957
22961
  function registerWaitTools(server, client, options) {
22958
- if (client.hasCapability("backlog:read")) server.registerTool("taiga_wait_for_initiative", {
22959
- title: "Wait for an initiative",
22960
- description: "Wait until an initiative reaches a condition instead of polling: changed, planned, your_turn (waits on a person), pr_open (open now; merged or closed does not count), run_settled (latest run stopped moving) or done. Returns reached: true when it holds; early with stoppedBecause when only a person can move it (a flag, failed plan, plan or pull request awaiting review, a cancel); or reached: false at the timeout: call again. A condition already true answers at once. Planning takes minutes, a run up to an hour.",
22961
- inputSchema: z.object({
22962
- initiativeId: idInput("initiative", "taiga_list_initiatives (query finds one by title) gives it"),
22963
- until: z.enum(ITEM_CONDITIONS).describe("What to wait for"),
22964
- timeoutSeconds
22965
- }),
22966
- annotations: READ_ONLY
22967
- }, (params, ctx) => runTool(async () => {
22962
+ const items = client.hasCapability("backlog:read");
22963
+ const runs = client.hasCapability("products:read");
22964
+ if (!items && !runs) return;
22965
+ const waitInitiative = async (target, until, params, ctx) => {
22968
22966
  const gate = { product: void 0 };
22969
22967
  const read = async (signal) => {
22970
- const item = (await client.getBacklogItem(params.initiativeId, signal)).data;
22968
+ const item = (await client.getBacklogItem(target, signal)).data;
22971
22969
  if (gate.product === void 0 && typeof item.autoBuild !== "boolean") gate.product = client.hasCapability("products:read") ? await client.getProduct(String(item.productId), signal).then((p) => {
22972
22970
  const value = p.data.autoBuild;
22973
22971
  return typeof value === "boolean" ? value : true;
@@ -22977,48 +22975,31 @@ function registerWaitTools(server, client, options) {
22977
22975
  }) : null;
22978
22976
  return item;
22979
22977
  };
22980
- const { now, ...outcome } = await waitFor(read, judgeItem(params.until, gate), (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1e3, options, {
22978
+ const { now, ...outcome } = await waitFor(read, judgeItem(until, gate), (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1e3, options, {
22981
22979
  progressToken: ctx.mcpReq._meta?.progressToken,
22982
22980
  notify: ctx.mcpReq.notify,
22983
22981
  signal: ctx.mcpReq.signal
22984
22982
  });
22985
22983
  return toolResult({
22986
- until: params.until,
22984
+ until,
22987
22985
  ...outcome,
22988
22986
  item: now ? itemReport(now) : null
22989
22987
  });
22990
- }));
22991
- if (client.hasCapability("products:read")) server.registerTool("taiga_wait_for_run", {
22992
- title: "Wait for a run",
22993
- description: "Wait until a run reaches a condition: changed, step_done (one more step finished), pr_open (open now) or settled (in review, completed, failed, stopped). Returns reached: true when it holds; early with stoppedBecause if the run settled without it; or reached: false at the timeout: call again. A settled run answers settled at once; run is null if the first read timed out.",
22994
- inputSchema: z.object({
22995
- runId: idInput("run", "taiga_list_runs gives it"),
22996
- until: z.enum(BUILD_CONDITIONS).describe("What to wait for"),
22997
- timeoutSeconds
22998
- }),
22999
- annotations: READ_ONLY
23000
- }, (params, ctx) => runTool(async () => {
23001
- const { now, ...outcome } = await waitFor(async (signal) => (await client.getBuildRun(params.runId, signal)).data, judgeBuild(params.until), (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1e3, options, {
22988
+ };
22989
+ const waitRun = async (target, until, params, ctx) => {
22990
+ const { now, ...outcome } = await waitFor(async (signal) => (await client.getBuildRun(target, signal)).data, judgeBuild(until), (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1e3, options, {
23002
22991
  progressToken: ctx.mcpReq._meta?.progressToken,
23003
22992
  notify: ctx.mcpReq.notify,
23004
22993
  signal: ctx.mcpReq.signal
23005
22994
  });
23006
22995
  return toolResult({
23007
- until: params.until,
22996
+ until,
23008
22997
  ...outcome,
23009
22998
  run: now ? readableRow(pickFields([now], RUN_REPORT_FIELDS)[0]) : null
23010
22999
  });
23011
- }));
23012
- if (client.hasCapability("products:read")) server.registerTool("taiga_wait_for_job", {
23013
- title: "Wait for an agent job",
23014
- description: "Wait until an agent job finishes (completed, failed or cancelled). Returns the job as taiga_get_job reads it, with an initiative request's outcome, and reached: true; or reached: false at the timeout: call again to keep waiting. Most jobs take a few minutes.",
23015
- inputSchema: z.object({
23016
- jobId: idInput("job", "the tool that started the job returned it"),
23017
- timeoutSeconds
23018
- }),
23019
- annotations: READ_ONLY
23020
- }, (params, ctx) => runTool(async () => {
23021
- const { now, ...outcome } = await waitFor(async (signal) => (await client.getAgentJob(params.jobId, signal)).data, (job) => SETTLED_JOB_STATUSES.has(String(job.status)) ? { reached: true } : null, (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1e3, options, {
23000
+ };
23001
+ const waitJob = async (target, params, ctx) => {
23002
+ const { now, ...outcome } = await waitFor(async (signal) => (await client.getAgentJob(target, signal)).data, (job) => SETTLED_JOB_STATUSES.has(String(job.status)) ? { reached: true } : null, (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1e3, options, {
23022
23003
  progressToken: ctx.mcpReq._meta?.progressToken,
23023
23004
  notify: ctx.mcpReq.notify,
23024
23005
  signal: ctx.mcpReq.signal
@@ -23027,6 +23008,38 @@ function registerWaitTools(server, client, options) {
23027
23008
  ...outcome,
23028
23009
  job: now ? jobReport(now) : null
23029
23010
  });
23011
+ };
23012
+ server.registerTool("taiga_wait", {
23013
+ title: "Wait for an initiative, run or job",
23014
+ description: "Wait instead of polling, for one of: an initiative (initiativeId) to reach until: changed, planned, your_turn (waits on a person), pr_open (open now; merged or closed does not count), run_settled (latest run stopped moving) or done; a run (runId) to reach until: changed, step_done, pr_open or settled (in review, completed, failed, stopped); or an agent job (jobId) to finish, which takes no until. Returns reached: true when it holds; early with stoppedBecause when it cannot happen without a person; or reached: false at the timeout: call again. A condition already true answers at once. Planning takes minutes, a run up to an hour, most jobs a few minutes.",
23015
+ inputSchema: z.object({
23016
+ initiativeId: idInput("initiative", "taiga_list_initiatives (query finds one by title) gives it").optional(),
23017
+ runId: idInput("run", "taiga_list_runs gives it").optional(),
23018
+ jobId: idInput("job", "the tool that started the job returned it").optional(),
23019
+ until: z.enum(ALL_CONDITIONS).optional().describe("What to wait for: an initiative and a run need one"),
23020
+ timeoutSeconds
23021
+ }),
23022
+ annotations: READ_ONLY
23023
+ }, (params, ctx) => runTool(async () => {
23024
+ if ([
23025
+ "initiativeId",
23026
+ "runId",
23027
+ "jobId"
23028
+ ].filter((key) => params[key] !== void 0).length !== 1) return toolError("VALIDATION_ERROR", "Name exactly one of initiativeId, runId or jobId: the thing to wait on.");
23029
+ if (params.jobId !== void 0) {
23030
+ if (params.until !== void 0) return toolError("VALIDATION_ERROR", "A job is waited on until it finishes: leave until out.");
23031
+ if (!runs) return toolError("FORBIDDEN", "Waiting on a job needs products:read, which this key lacks.");
23032
+ return waitJob(params.jobId, params, ctx);
23033
+ }
23034
+ const kind = params.initiativeId !== void 0 ? "initiative" : "run";
23035
+ const allowed = UNTIL_FOR[kind];
23036
+ if (params.until === void 0 || !allowed.includes(params.until)) return toolError("VALIDATION_ERROR", `Waiting on ${kind === "initiative" ? "an initiative" : "a run"} takes until: ${allowed.join(", ")}.`);
23037
+ if (kind === "initiative") {
23038
+ if (!items) return toolError("FORBIDDEN", "Waiting on an initiative needs backlog:read, which this key lacks.");
23039
+ return waitInitiative(params.initiativeId, params.until, params, ctx);
23040
+ }
23041
+ if (!runs) return toolError("FORBIDDEN", "Waiting on a run needs products:read, which this key lacks.");
23042
+ return waitRun(params.runId, params.until, params, ctx);
23030
23043
  }));
23031
23044
  }
23032
23045
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ai/mcp-server",
3
- "version": "0.26.0-dev.515",
3
+ "version": "0.26.0-dev.516",
4
4
  "description": "Taiga for AI coding assistants: read products, policies, initiatives and runs, and operate the line, over MCP",
5
5
  "mcpName": "ga.tai/mcp-server",
6
6
  "type": "module",