@kaddo/mcp 3.87.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 +259 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11724,6 +11724,10 @@ function defaultMessageFor(code) {
|
|
|
11724
11724
|
function integrationError(code, message) {
|
|
11725
11725
|
return new IntegrationError(code, message ?? defaultMessageFor(code));
|
|
11726
11726
|
}
|
|
11727
|
+
function normalizeProviderError(err, fallback = "INTEGRATION_PROVIDER_ERROR") {
|
|
11728
|
+
if (err instanceof IntegrationError) return err;
|
|
11729
|
+
return new IntegrationError(fallback, defaultMessageFor(fallback));
|
|
11730
|
+
}
|
|
11727
11731
|
|
|
11728
11732
|
// ../integrations/src/config.ts
|
|
11729
11733
|
var ENV_SUFFIX = "_env";
|
|
@@ -11785,6 +11789,11 @@ function parseFilters(raw) {
|
|
|
11785
11789
|
const o2 = raw;
|
|
11786
11790
|
const filters = {};
|
|
11787
11791
|
let hasAny = false;
|
|
11792
|
+
const projects = toStringArray3(o2.projects);
|
|
11793
|
+
if (projects?.length) {
|
|
11794
|
+
filters.projects = projects;
|
|
11795
|
+
hasAny = true;
|
|
11796
|
+
}
|
|
11788
11797
|
const types = toStringArray3(o2.types);
|
|
11789
11798
|
if (types?.length) {
|
|
11790
11799
|
filters.types = types;
|
|
@@ -11879,6 +11888,7 @@ function mergeFilters(base, overlay) {
|
|
|
11879
11888
|
if (!base) return { ...overlay };
|
|
11880
11889
|
if (!overlay) return { ...base };
|
|
11881
11890
|
return {
|
|
11891
|
+
projects: overlay.projects?.length ? overlay.projects : base.projects,
|
|
11882
11892
|
types: overlay.types?.length ? overlay.types : base.types,
|
|
11883
11893
|
statuses: overlay.statuses?.length ? overlay.statuses : base.statuses,
|
|
11884
11894
|
labels: overlay.labels?.length ? overlay.labels : base.labels,
|
|
@@ -12217,10 +12227,259 @@ function createMockAdapter(opts = {}) {
|
|
|
12217
12227
|
};
|
|
12218
12228
|
}
|
|
12219
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
|
+
|
|
12220
12478
|
// ../integrations/src/index.ts
|
|
12221
12479
|
function createDefaultRegistry() {
|
|
12222
12480
|
const registry2 = new IntegrationRegistry();
|
|
12223
12481
|
registry2.register(createMockAdapter());
|
|
12482
|
+
registry2.register(createJiraAdapter());
|
|
12224
12483
|
return registry2;
|
|
12225
12484
|
}
|
|
12226
12485
|
|
package/package.json
CHANGED