@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.
- package/dist/index.js +87 -74
- 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 (
|
|
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: "
|
|
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;
|
|
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
|
|
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).
|
|
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.
|
|
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", "
|
|
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
|
|
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,
|
|
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 =
|
|
7140
|
-
const
|
|
7141
|
-
const
|
|
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" : ""}.
|
|
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.
|
|
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;
|
|
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.
|
|
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
|
-
|
|
22959
|
-
|
|
22960
|
-
|
|
22961
|
-
|
|
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(
|
|
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(
|
|
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
|
|
22984
|
+
until,
|
|
22987
22985
|
...outcome,
|
|
22988
22986
|
item: now ? itemReport(now) : null
|
|
22989
22987
|
});
|
|
22990
|
-
}
|
|
22991
|
-
|
|
22992
|
-
|
|
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
|
|
22996
|
+
until,
|
|
23008
22997
|
...outcome,
|
|
23009
22998
|
run: now ? readableRow(pickFields([now], RUN_REPORT_FIELDS)[0]) : null
|
|
23010
22999
|
});
|
|
23011
|
-
}
|
|
23012
|
-
|
|
23013
|
-
|
|
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.
|
|
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",
|