@kaddo/mcp 3.87.0 → 3.88.1

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 +259 -0
  2. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.87.0",
3
+ "version": "3.88.1",
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": {