@kaddo/mcp 3.86.0 → 3.88.0
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 +262 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7646,6 +7646,7 @@ function parseWorkItemSource(frontmatter) {
|
|
|
7646
7646
|
integration: optStr(obj.integration) ?? optStr(frontmatter.source_integration),
|
|
7647
7647
|
url: optStr(obj.url) ?? optStr(frontmatter.source_url),
|
|
7648
7648
|
imported_at: optStr(obj.imported_at) ?? optStr(frontmatter.source_imported_at),
|
|
7649
|
+
external_updated_at: optStr(obj.external_updated_at) ?? optStr(frontmatter.source_external_updated_at),
|
|
7649
7650
|
synced_at: optStr(obj.synced_at) ?? optStr(frontmatter.source_synced_at),
|
|
7650
7651
|
inferred: obj.inferred === true || obj.inferred === "true",
|
|
7651
7652
|
reason: t && !isValidSource(t) ? `Invalid source type in object: "${t}".` : void 0
|
|
@@ -7661,6 +7662,7 @@ function parseWorkItemSource(frontmatter) {
|
|
|
7661
7662
|
provider: optStr(frontmatter.source_provider),
|
|
7662
7663
|
url: optStr(frontmatter.source_url),
|
|
7663
7664
|
imported_at: optStr(frontmatter.source_imported_at),
|
|
7665
|
+
external_updated_at: optStr(frontmatter.source_external_updated_at),
|
|
7664
7666
|
synced_at: optStr(frontmatter.source_synced_at),
|
|
7665
7667
|
inferred: false
|
|
7666
7668
|
};
|
|
@@ -7674,6 +7676,7 @@ function parseWorkItemSource(frontmatter) {
|
|
|
7674
7676
|
provider: optStr(frontmatter.source_provider),
|
|
7675
7677
|
url: optStr(frontmatter.source_url),
|
|
7676
7678
|
imported_at: optStr(frontmatter.source_imported_at),
|
|
7679
|
+
external_updated_at: optStr(frontmatter.source_external_updated_at),
|
|
7677
7680
|
synced_at: optStr(frontmatter.source_synced_at),
|
|
7678
7681
|
inferred: true,
|
|
7679
7682
|
reason: `Invalid source value: "${raw}".`
|
|
@@ -11721,6 +11724,10 @@ function defaultMessageFor(code) {
|
|
|
11721
11724
|
function integrationError(code, message) {
|
|
11722
11725
|
return new IntegrationError(code, message ?? defaultMessageFor(code));
|
|
11723
11726
|
}
|
|
11727
|
+
function normalizeProviderError(err, fallback = "INTEGRATION_PROVIDER_ERROR") {
|
|
11728
|
+
if (err instanceof IntegrationError) return err;
|
|
11729
|
+
return new IntegrationError(fallback, defaultMessageFor(fallback));
|
|
11730
|
+
}
|
|
11724
11731
|
|
|
11725
11732
|
// ../integrations/src/config.ts
|
|
11726
11733
|
var ENV_SUFFIX = "_env";
|
|
@@ -11782,6 +11789,11 @@ function parseFilters(raw) {
|
|
|
11782
11789
|
const o2 = raw;
|
|
11783
11790
|
const filters = {};
|
|
11784
11791
|
let hasAny = false;
|
|
11792
|
+
const projects = toStringArray3(o2.projects);
|
|
11793
|
+
if (projects?.length) {
|
|
11794
|
+
filters.projects = projects;
|
|
11795
|
+
hasAny = true;
|
|
11796
|
+
}
|
|
11785
11797
|
const types = toStringArray3(o2.types);
|
|
11786
11798
|
if (types?.length) {
|
|
11787
11799
|
filters.types = types;
|
|
@@ -11876,6 +11888,7 @@ function mergeFilters(base, overlay) {
|
|
|
11876
11888
|
if (!base) return { ...overlay };
|
|
11877
11889
|
if (!overlay) return { ...base };
|
|
11878
11890
|
return {
|
|
11891
|
+
projects: overlay.projects?.length ? overlay.projects : base.projects,
|
|
11879
11892
|
types: overlay.types?.length ? overlay.types : base.types,
|
|
11880
11893
|
statuses: overlay.statuses?.length ? overlay.statuses : base.statuses,
|
|
11881
11894
|
labels: overlay.labels?.length ? overlay.labels : base.labels,
|
|
@@ -12214,10 +12227,259 @@ function createMockAdapter(opts = {}) {
|
|
|
12214
12227
|
};
|
|
12215
12228
|
}
|
|
12216
12229
|
|
|
12230
|
+
// ../integrations/src/jira-adapter.ts
|
|
12231
|
+
var JIRA_ADAPTER_ID = "jira";
|
|
12232
|
+
function adfToMarkdown(node) {
|
|
12233
|
+
if (!node) return "";
|
|
12234
|
+
if (node.type === "text") {
|
|
12235
|
+
let t = node.text ?? "";
|
|
12236
|
+
for (const m2 of node.marks ?? []) {
|
|
12237
|
+
if (m2.type === "strong") t = `**${t}**`;
|
|
12238
|
+
else if (m2.type === "em") t = `*${t}*`;
|
|
12239
|
+
else if (m2.type === "code") t = `\`${t}\``;
|
|
12240
|
+
else if (m2.type === "strike") t = `~~${t}~~`;
|
|
12241
|
+
}
|
|
12242
|
+
return t;
|
|
12243
|
+
}
|
|
12244
|
+
const children = (node.content ?? []).map(adfToMarkdown).join("");
|
|
12245
|
+
switch (node.type) {
|
|
12246
|
+
case "doc":
|
|
12247
|
+
return children.trim();
|
|
12248
|
+
case "paragraph":
|
|
12249
|
+
return children + "\n\n";
|
|
12250
|
+
case "heading": {
|
|
12251
|
+
const level = Math.min(Number(node.attrs?.level) || 1, 6);
|
|
12252
|
+
return "#".repeat(level) + " " + children.trim() + "\n\n";
|
|
12253
|
+
}
|
|
12254
|
+
case "bulletList":
|
|
12255
|
+
return children;
|
|
12256
|
+
case "orderedList":
|
|
12257
|
+
return children;
|
|
12258
|
+
case "listItem":
|
|
12259
|
+
return "- " + children.trim() + "\n";
|
|
12260
|
+
case "blockquote":
|
|
12261
|
+
return children.split("\n").filter(Boolean).map((l2) => "> " + l2).join("\n") + "\n\n";
|
|
12262
|
+
case "codeBlock":
|
|
12263
|
+
return "```\n" + children + "```\n\n";
|
|
12264
|
+
case "rule":
|
|
12265
|
+
return "---\n\n";
|
|
12266
|
+
case "hardBreak":
|
|
12267
|
+
return "\n";
|
|
12268
|
+
case "mediaGroup":
|
|
12269
|
+
case "mediaSingle":
|
|
12270
|
+
case "media":
|
|
12271
|
+
return "";
|
|
12272
|
+
default:
|
|
12273
|
+
return children;
|
|
12274
|
+
}
|
|
12275
|
+
}
|
|
12276
|
+
function normalizeDescription(field) {
|
|
12277
|
+
if (!field) return void 0;
|
|
12278
|
+
if (typeof field === "string") return field || void 0;
|
|
12279
|
+
if (typeof field === "object" && field.type === "doc") {
|
|
12280
|
+
const md = adfToMarkdown(field);
|
|
12281
|
+
return md || void 0;
|
|
12282
|
+
}
|
|
12283
|
+
return void 0;
|
|
12284
|
+
}
|
|
12285
|
+
function escapeJql(value) {
|
|
12286
|
+
return '"' + value.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
|
|
12287
|
+
}
|
|
12288
|
+
function buildJql(filters) {
|
|
12289
|
+
const clauses = [];
|
|
12290
|
+
if (filters?.projects?.length) clauses.push(`project IN (${filters.projects.map(escapeJql).join(", ")})`);
|
|
12291
|
+
if (filters?.types?.length) clauses.push(`issuetype IN (${filters.types.map(escapeJql).join(", ")})`);
|
|
12292
|
+
if (filters?.statuses?.length) clauses.push(`status IN (${filters.statuses.map(escapeJql).join(", ")})`);
|
|
12293
|
+
if (filters?.labels?.length) clauses.push(`labels IN (${filters.labels.map(escapeJql).join(", ")})`);
|
|
12294
|
+
if (filters?.assignees?.length) clauses.push(`assignee IN (${filters.assignees.map(escapeJql).join(", ")})`);
|
|
12295
|
+
if (filters?.updatedAfter) clauses.push(`updated >= ${escapeJql(filters.updatedAfter)}`);
|
|
12296
|
+
if (filters?.search) clauses.push(`(summary ~ ${escapeJql(filters.search)} OR description ~ ${escapeJql(filters.search)})`);
|
|
12297
|
+
if (filters?.providerQuery) clauses.push(`(${filters.providerQuery})`);
|
|
12298
|
+
return clauses.join(" AND ") || "ORDER BY updated DESC";
|
|
12299
|
+
}
|
|
12300
|
+
function normalizeBaseUrl(raw) {
|
|
12301
|
+
let url = raw.trim();
|
|
12302
|
+
if (url.endsWith("/")) url = url.slice(0, -1);
|
|
12303
|
+
return url;
|
|
12304
|
+
}
|
|
12305
|
+
function authHeader(email, token) {
|
|
12306
|
+
const encoded = typeof Buffer !== "undefined" ? Buffer.from(`${email}:${token}`).toString("base64") : btoa(`${email}:${token}`);
|
|
12307
|
+
return `Basic ${encoded}`;
|
|
12308
|
+
}
|
|
12309
|
+
async function jiraFetch(baseUrl, path6, auth, timeoutMs, params) {
|
|
12310
|
+
const url = new URL(path6, baseUrl + "/");
|
|
12311
|
+
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
12312
|
+
const controller = new AbortController();
|
|
12313
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
12314
|
+
try {
|
|
12315
|
+
const res = await fetch(url.toString(), {
|
|
12316
|
+
method: "GET",
|
|
12317
|
+
headers: { Authorization: auth, Accept: "application/json" },
|
|
12318
|
+
signal: controller.signal
|
|
12319
|
+
});
|
|
12320
|
+
if (res.status === 401) throw integrationError("INTEGRATION_UNAUTHORIZED");
|
|
12321
|
+
if (res.status === 403) throw integrationError("INTEGRATION_FORBIDDEN");
|
|
12322
|
+
if (res.status === 404) throw integrationError("INTEGRATION_NOT_FOUND");
|
|
12323
|
+
if (res.status === 429) throw integrationError("INTEGRATION_RATE_LIMITED");
|
|
12324
|
+
if (res.status >= 500) throw integrationError("INTEGRATION_UNAVAILABLE");
|
|
12325
|
+
if (!res.ok) throw integrationError("INTEGRATION_PROVIDER_ERROR", `Jira responded with status ${res.status}.`);
|
|
12326
|
+
return await res.json();
|
|
12327
|
+
} catch (err) {
|
|
12328
|
+
if (err instanceof Error && err.name === "AbortError") throw integrationError("INTEGRATION_TIMEOUT");
|
|
12329
|
+
throw normalizeProviderError(err);
|
|
12330
|
+
} finally {
|
|
12331
|
+
clearTimeout(timer);
|
|
12332
|
+
}
|
|
12333
|
+
}
|
|
12334
|
+
function normalizeIssue(issue, baseUrl, integrationId) {
|
|
12335
|
+
const f3 = issue.fields;
|
|
12336
|
+
const issueType = f3.issuetype;
|
|
12337
|
+
const status = f3.status;
|
|
12338
|
+
const assignee = f3.assignee;
|
|
12339
|
+
const reporter = f3.reporter;
|
|
12340
|
+
const priority = f3.priority;
|
|
12341
|
+
const project = f3.project;
|
|
12342
|
+
const parent = f3.parent;
|
|
12343
|
+
const components = f3.components;
|
|
12344
|
+
const labels = f3.labels;
|
|
12345
|
+
return {
|
|
12346
|
+
externalId: issue.key,
|
|
12347
|
+
provider: JIRA_ADAPTER_ID,
|
|
12348
|
+
title: f3.summary ?? issue.key,
|
|
12349
|
+
description: normalizeDescription(f3.description),
|
|
12350
|
+
type: issueType?.name,
|
|
12351
|
+
status: status?.name,
|
|
12352
|
+
url: `${baseUrl}/browse/${issue.key}`,
|
|
12353
|
+
author: reporter ? { name: reporter.displayName, email: reporter.emailAddress, id: reporter.accountId } : void 0,
|
|
12354
|
+
assignees: assignee ? [{ name: assignee.displayName, email: assignee.emailAddress, id: assignee.accountId }] : void 0,
|
|
12355
|
+
labels,
|
|
12356
|
+
createdAt: f3.created,
|
|
12357
|
+
updatedAt: f3.updated,
|
|
12358
|
+
project: project ? { key: project.key, name: project.name, id: project.id } : void 0,
|
|
12359
|
+
rawMetadata: {
|
|
12360
|
+
...priority?.name ? { priority: priority.name } : {},
|
|
12361
|
+
...parent?.key ? { parent: parent.key } : {},
|
|
12362
|
+
...components?.length ? { components: components.map((c) => c.name).filter(Boolean) } : {}
|
|
12363
|
+
}
|
|
12364
|
+
};
|
|
12365
|
+
}
|
|
12366
|
+
var SEARCH_FIELDS = "summary,description,issuetype,status,assignee,reporter,priority,labels,project,parent,components,created,updated";
|
|
12367
|
+
var DEFAULT_PAGE_SIZE = 50;
|
|
12368
|
+
var metadata = {
|
|
12369
|
+
id: JIRA_ADAPTER_ID,
|
|
12370
|
+
displayName: "Jira",
|
|
12371
|
+
version: "1.0.0",
|
|
12372
|
+
description: "Connect Jira Cloud work items to Kaddo for discovery, import and refinement.",
|
|
12373
|
+
icon: "jira",
|
|
12374
|
+
configSchema: {
|
|
12375
|
+
baseUrl: {
|
|
12376
|
+
type: "url",
|
|
12377
|
+
required: true,
|
|
12378
|
+
label: "Jira Site URL",
|
|
12379
|
+
description: "The URL of the Jira Cloud site.",
|
|
12380
|
+
placeholder: "https://company.atlassian.net"
|
|
12381
|
+
},
|
|
12382
|
+
email: {
|
|
12383
|
+
type: "string",
|
|
12384
|
+
required: true,
|
|
12385
|
+
label: "Email",
|
|
12386
|
+
description: "Email associated with the Jira API token.",
|
|
12387
|
+
placeholder: "user@company.com"
|
|
12388
|
+
}
|
|
12389
|
+
},
|
|
12390
|
+
secretSchema: {
|
|
12391
|
+
apiToken: {
|
|
12392
|
+
type: "password",
|
|
12393
|
+
required: true,
|
|
12394
|
+
label: "API Token",
|
|
12395
|
+
description: "Jira API token for authentication. Generate one at id.atlassian.com."
|
|
12396
|
+
}
|
|
12397
|
+
},
|
|
12398
|
+
filterCapabilities: {
|
|
12399
|
+
projects: { supported: true, multiple: true },
|
|
12400
|
+
types: { supported: true, multiple: true },
|
|
12401
|
+
statuses: { supported: true, multiple: true },
|
|
12402
|
+
labels: { supported: true, multiple: true },
|
|
12403
|
+
assignees: { supported: true, multiple: true },
|
|
12404
|
+
updatedAfter: { supported: true },
|
|
12405
|
+
search: { supported: true },
|
|
12406
|
+
providerQuery: { supported: true, label: "JQL" }
|
|
12407
|
+
}
|
|
12408
|
+
};
|
|
12409
|
+
var capabilities = {
|
|
12410
|
+
workItems: { list: true, read: true, import: true, write: false, statusSync: false, comments: false, webhooks: false }
|
|
12411
|
+
};
|
|
12412
|
+
function resolveConfig(context) {
|
|
12413
|
+
const baseUrl = context.config.baseUrl;
|
|
12414
|
+
const email = context.config.email;
|
|
12415
|
+
const apiToken = context.credentials.apiToken;
|
|
12416
|
+
if (typeof baseUrl !== "string" || !baseUrl) throw integrationError("INTEGRATION_CONFIG_INVALID", "Jira Site URL is required.");
|
|
12417
|
+
if (typeof email !== "string" || !email) throw integrationError("INTEGRATION_CONFIG_INVALID", "Email is required.");
|
|
12418
|
+
if (!apiToken) throw integrationError("INTEGRATION_CONFIG_INVALID", "API Token is required.");
|
|
12419
|
+
return { baseUrl: normalizeBaseUrl(baseUrl), email, apiToken };
|
|
12420
|
+
}
|
|
12421
|
+
function createJiraAdapter() {
|
|
12422
|
+
return {
|
|
12423
|
+
id: JIRA_ADAPTER_ID,
|
|
12424
|
+
metadata,
|
|
12425
|
+
capabilities,
|
|
12426
|
+
async verifyConnection(context) {
|
|
12427
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12428
|
+
try {
|
|
12429
|
+
const { baseUrl, email, apiToken } = resolveConfig(context);
|
|
12430
|
+
const auth = authHeader(email, apiToken);
|
|
12431
|
+
await jiraFetch(baseUrl, "/rest/api/3/myself", auth, context.timeoutMs);
|
|
12432
|
+
return { status: "available", checkedAt };
|
|
12433
|
+
} catch (err) {
|
|
12434
|
+
const ie2 = normalizeProviderError(err);
|
|
12435
|
+
if (ie2.code === "INTEGRATION_UNAUTHORIZED") return { status: "unauthorized", message: "Check the configured email and API token.", checkedAt };
|
|
12436
|
+
if (ie2.code === "INTEGRATION_CONFIG_INVALID") return { status: "invalid-config", message: ie2.safeMessage, checkedAt };
|
|
12437
|
+
return { status: "unavailable", message: ie2.safeMessage, checkedAt };
|
|
12438
|
+
}
|
|
12439
|
+
},
|
|
12440
|
+
async listWorkItems(request) {
|
|
12441
|
+
const { baseUrl, email, apiToken } = resolveConfig(request.context);
|
|
12442
|
+
const auth = authHeader(email, apiToken);
|
|
12443
|
+
const jql = buildJql(request.filters);
|
|
12444
|
+
const pageSize = Math.min(Math.max(1, request.pageSize ?? DEFAULT_PAGE_SIZE), 100);
|
|
12445
|
+
const startAt = request.cursor ? Math.max(0, Number.parseInt(request.cursor, 10) || 0) : 0;
|
|
12446
|
+
try {
|
|
12447
|
+
const data = await jiraFetch(baseUrl, "/rest/api/3/search", auth, request.context.timeoutMs, {
|
|
12448
|
+
jql: jql.startsWith("ORDER BY") ? jql : jql + " ORDER BY updated DESC",
|
|
12449
|
+
startAt: String(startAt),
|
|
12450
|
+
maxResults: String(pageSize),
|
|
12451
|
+
fields: SEARCH_FIELDS
|
|
12452
|
+
});
|
|
12453
|
+
const items = data.issues.map((issue) => normalizeIssue(issue, baseUrl, request.context.integrationId));
|
|
12454
|
+
const nextStart = data.startAt + data.issues.length;
|
|
12455
|
+
const hasMore = nextStart < data.total;
|
|
12456
|
+
return { items, hasMore, ...hasMore ? { nextCursor: String(nextStart) } : {} };
|
|
12457
|
+
} catch (err) {
|
|
12458
|
+
throw normalizeProviderError(err);
|
|
12459
|
+
}
|
|
12460
|
+
},
|
|
12461
|
+
async getWorkItem(request) {
|
|
12462
|
+
const { baseUrl, email, apiToken } = resolveConfig(request.context);
|
|
12463
|
+
const auth = authHeader(email, apiToken);
|
|
12464
|
+
try {
|
|
12465
|
+
const issue = await jiraFetch(baseUrl, `/rest/api/3/issue/${encodeURIComponent(request.externalId)}`, auth, request.context.timeoutMs, {
|
|
12466
|
+
fields: SEARCH_FIELDS
|
|
12467
|
+
});
|
|
12468
|
+
return normalizeIssue(issue, baseUrl, request.context.integrationId);
|
|
12469
|
+
} catch (err) {
|
|
12470
|
+
const ie2 = normalizeProviderError(err);
|
|
12471
|
+
if (ie2.code === "INTEGRATION_NOT_FOUND") return null;
|
|
12472
|
+
throw ie2;
|
|
12473
|
+
}
|
|
12474
|
+
}
|
|
12475
|
+
};
|
|
12476
|
+
}
|
|
12477
|
+
|
|
12217
12478
|
// ../integrations/src/index.ts
|
|
12218
12479
|
function createDefaultRegistry() {
|
|
12219
12480
|
const registry2 = new IntegrationRegistry();
|
|
12220
12481
|
registry2.register(createMockAdapter());
|
|
12482
|
+
registry2.register(createJiraAdapter());
|
|
12221
12483
|
return registry2;
|
|
12222
12484
|
}
|
|
12223
12485
|
|
package/package.json
CHANGED