@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.
Files changed (2) hide show
  1. package/dist/index.js +262 -0
  2. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.86.0",
3
+ "version": "3.88.0",
4
4
  "description": "MCP server for Kaddo project knowledge: read-only resources/tools/prompts plus safe derived generation under .kaddo/.",
5
5
  "license": "MIT",
6
6
  "repository": {