@ama-work/mcp-atlassian 1.0.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/client/adf.d.ts +17 -0
  4. package/dist/client/adf.js +137 -0
  5. package/dist/client/atlassian.d.ts +20908 -0
  6. package/dist/client/atlassian.js +28 -0
  7. package/dist/config.d.ts +2 -0
  8. package/dist/config.js +59 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +11 -0
  11. package/dist/tools/confluence-comment.d.ts +2 -0
  12. package/dist/tools/confluence-comment.js +19 -0
  13. package/dist/tools/confluence-create.d.ts +2 -0
  14. package/dist/tools/confluence-create.js +26 -0
  15. package/dist/tools/confluence-get.d.ts +2 -0
  16. package/dist/tools/confluence-get.js +33 -0
  17. package/dist/tools/confluence-search.d.ts +2 -0
  18. package/dist/tools/confluence-search.js +14 -0
  19. package/dist/tools/confluence-spaces.d.ts +2 -0
  20. package/dist/tools/confluence-spaces.js +17 -0
  21. package/dist/tools/confluence-update.d.ts +2 -0
  22. package/dist/tools/confluence-update.js +25 -0
  23. package/dist/tools/confluence.d.ts +2 -0
  24. package/dist/tools/confluence.js +14 -0
  25. package/dist/tools/jira-comment.d.ts +2 -0
  26. package/dist/tools/jira-comment.js +20 -0
  27. package/dist/tools/jira-create.d.ts +2 -0
  28. package/dist/tools/jira-create.js +39 -0
  29. package/dist/tools/jira-get.d.ts +2 -0
  30. package/dist/tools/jira-get.js +20 -0
  31. package/dist/tools/jira-projects.d.ts +2 -0
  32. package/dist/tools/jira-projects.js +23 -0
  33. package/dist/tools/jira-search.d.ts +2 -0
  34. package/dist/tools/jira-search.js +37 -0
  35. package/dist/tools/jira-transition.d.ts +2 -0
  36. package/dist/tools/jira-transition.js +30 -0
  37. package/dist/tools/jira-update.d.ts +2 -0
  38. package/dist/tools/jira-update.js +39 -0
  39. package/dist/tools/jira.d.ts +2 -0
  40. package/dist/tools/jira.js +16 -0
  41. package/dist/tools/meta.d.ts +2 -0
  42. package/dist/tools/meta.js +18 -0
  43. package/dist/types.d.ts +14 -0
  44. package/dist/types.js +1 -0
  45. package/package.json +52 -0
@@ -0,0 +1,28 @@
1
+ import { createCloudClient } from "jira.js";
2
+ import { createV1Client, createV2Client } from "confluence.js";
3
+ import { getToken, getCloudId } from "../config.js";
4
+ export async function createJiraClient() {
5
+ const [token, cloudId] = await Promise.all([getToken(), getCloudId()]);
6
+ return createCloudClient({
7
+ host: "https://api.atlassian.com",
8
+ auth: { type: "oauth2", accessToken: token, cloudId },
9
+ getAuthOn401: async () => {
10
+ const fresh = await getToken();
11
+ return { type: "oauth2", accessToken: fresh, cloudId };
12
+ },
13
+ });
14
+ }
15
+ export async function createConfluenceClient() {
16
+ const [token, cloudId] = await Promise.all([getToken(), getCloudId()]);
17
+ return createV2Client({
18
+ host: "https://api.atlassian.com",
19
+ auth: { type: "oauth2", accessToken: token, cloudId },
20
+ });
21
+ }
22
+ export async function createConfluenceV1Client() {
23
+ const [token, cloudId] = await Promise.all([getToken(), getCloudId()]);
24
+ return createV1Client({
25
+ host: "https://api.atlassian.com",
26
+ auth: { type: "oauth2", accessToken: token, cloudId },
27
+ });
28
+ }
@@ -0,0 +1,2 @@
1
+ export declare function getToken(): Promise<string>;
2
+ export declare function getCloudId(): Promise<string>;
package/dist/config.js ADDED
@@ -0,0 +1,59 @@
1
+ const REFRESH_BUFFER_MS = 5 * 60 * 1000;
2
+ let cache = null;
3
+ function env(name) {
4
+ return process.env[name];
5
+ }
6
+ async function fetchFromRegistry() {
7
+ const registryUrl = env("REGISTRY_URL");
8
+ const instanceId = env("INSTANCE_ID");
9
+ const gatewayPsk = env("GATEWAY_PSK");
10
+ if (!registryUrl || !instanceId || !gatewayPsk) {
11
+ throw new Error("Registry mode requires REGISTRY_URL, INSTANCE_ID, and GATEWAY_PSK");
12
+ }
13
+ const url = `${registryUrl}/instances/${instanceId}/secrets`;
14
+ const res = await fetch(url, {
15
+ headers: { Authorization: `Bearer ${gatewayPsk}` },
16
+ });
17
+ if (!res.ok) {
18
+ const body = await res.text();
19
+ throw new Error(`Registry fetch failed (${res.status}): ${body}`);
20
+ }
21
+ const data = (await res.json());
22
+ return {
23
+ token: data.oauth.access_token,
24
+ cloudId: data.oauth.cloud_id,
25
+ expiresAt: data.oauth.expires_at,
26
+ };
27
+ }
28
+ function resolveStandalone() {
29
+ const token = env("ATLASSIAN_ACCESS_TOKEN");
30
+ const cloudId = env("ATLASSIAN_CLOUD_ID");
31
+ if (!token || !cloudId)
32
+ return null;
33
+ return {
34
+ token,
35
+ cloudId,
36
+ expiresAt: Number.MAX_SAFE_INTEGER,
37
+ };
38
+ }
39
+ function isExpired(c) {
40
+ return Date.now() >= c.expiresAt - REFRESH_BUFFER_MS;
41
+ }
42
+ async function load() {
43
+ const standalone = resolveStandalone();
44
+ if (standalone)
45
+ return standalone;
46
+ return fetchFromRegistry();
47
+ }
48
+ export async function getToken() {
49
+ if (!cache || isExpired(cache)) {
50
+ cache = await load();
51
+ }
52
+ return cache.token;
53
+ }
54
+ export async function getCloudId() {
55
+ if (!cache || isExpired(cache)) {
56
+ cache = await load();
57
+ }
58
+ return cache.cloudId;
59
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { registerJiraTools } from "./tools/jira.js";
4
+ import { registerConfluenceTools } from "./tools/confluence.js";
5
+ import { registerMetaTools } from "./tools/meta.js";
6
+ const server = new McpServer({ name: "ama-mcp-atlassian", version: "0.1.0" });
7
+ registerJiraTools(server);
8
+ registerConfluenceTools(server);
9
+ registerMetaTools(server);
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceCommentTools(server: McpServer): void;
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+ import { createConfluenceClient } from "../client/atlassian.js";
3
+ import { toADF } from "../client/adf.js";
4
+ export function registerConfluenceCommentTools(server) {
5
+ server.tool("confluence_add_comment", "Add a footer comment to a Confluence page.", {
6
+ pageId: z.string().describe("Numeric page ID"),
7
+ body: z.string().describe("Comment text as plain markdown. Converted to ADF before sending."),
8
+ }, async ({ pageId, body }) => {
9
+ const client = await createConfluenceClient();
10
+ const adfDoc = toADF(body);
11
+ const comment = await client.comment.createFooterComment({
12
+ pageId,
13
+ body: { representation: "atlas_doc_format", value: adfDoc },
14
+ });
15
+ return {
16
+ content: [{ type: "text", text: JSON.stringify(comment, null, 2) }],
17
+ };
18
+ });
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceCreateTools(server: McpServer): void;
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ import { createConfluenceClient } from "../client/atlassian.js";
3
+ import { toADF } from "../client/adf.js";
4
+ export function registerConfluenceCreateTools(server) {
5
+ server.tool("confluence_create_page", "Create a new Confluence page in the specified space.", {
6
+ spaceId: z.string().describe("Numeric space ID. Use confluence_get_spaces to look up IDs by name or key."),
7
+ title: z.string().describe("Page title"),
8
+ body: z.string().describe("Page content as plain markdown. Converted to ADF before sending."),
9
+ parentId: z.string().optional().describe("Numeric ID of parent page. If omitted, page is created at space root."),
10
+ status: z.enum(["current", "draft"]).optional().describe('"current" (published, default) or "draft"'),
11
+ }, async ({ spaceId, title, body, parentId, status }) => {
12
+ const client = await createConfluenceClient();
13
+ const adfDoc = toADF(body);
14
+ const params = {
15
+ spaceId,
16
+ title,
17
+ body: { representation: "atlas_doc_format", value: adfDoc },
18
+ ...(parentId ? { parentId } : {}),
19
+ status: status ?? "current",
20
+ };
21
+ const page = await client.page.createPage(params);
22
+ return {
23
+ content: [{ type: "text", text: JSON.stringify(page, null, 2) }],
24
+ };
25
+ });
26
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceGetTools(server: McpServer): void;
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ import { createConfluenceClient } from "../client/atlassian.js";
3
+ import { fromADF } from "../client/adf.js";
4
+ export function registerConfluenceGetTools(server) {
5
+ server.tool("confluence_get_page", "Get the full content of a Confluence page by its numeric page ID. Returns title, spaceKey, body as markdown, version number, and last-modified date.", {
6
+ pageId: z.string().describe('Numeric page ID (e.g. "98383"). Find IDs via confluence_search.'),
7
+ }, async ({ pageId }) => {
8
+ const client = await createConfluenceClient();
9
+ const page = await client.page.getPageById({
10
+ id: Number(pageId),
11
+ bodyFormat: "atlas_doc_format",
12
+ includeVersion: true,
13
+ });
14
+ const body = page.body;
15
+ const adfBody = body?.atlas_doc_format;
16
+ const markdown = adfBody?.value ? fromADF(JSON.parse(adfBody.value)) : "";
17
+ return {
18
+ content: [{
19
+ type: "text",
20
+ text: JSON.stringify({
21
+ pageId: page.id,
22
+ title: page.title,
23
+ spaceId: page.spaceId,
24
+ parentId: page.parentId,
25
+ status: page.status,
26
+ body: markdown,
27
+ version: page.version?.number,
28
+ lastModified: page.version?.createdAt,
29
+ }, null, 2),
30
+ }],
31
+ };
32
+ });
33
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceSearchTools(server: McpServer): void;
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ import { createConfluenceV1Client } from "../client/atlassian.js";
3
+ export function registerConfluenceSearchTools(server) {
4
+ server.tool("confluence_search", "Search Confluence content using CQL (Confluence Query Language). Returns matching pages, blog posts, and spaces with title, spaceKey, URL, and a short excerpt.", {
5
+ cql: z.string().describe('CQL query string. Examples: space = "ENG" AND type = page AND text ~ "deploy", title = "Architecture Overview"'),
6
+ limit: z.number().int().min(1).max(50).default(10).describe("Max results (1-50, default 10)"),
7
+ }, async ({ cql, limit }) => {
8
+ const client = await createConfluenceV1Client();
9
+ const result = await client.search.searchByCQL({ cql, limit });
10
+ return {
11
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
12
+ };
13
+ });
14
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceSpaceTools(server: McpServer): void;
@@ -0,0 +1,17 @@
1
+ import { z } from "zod";
2
+ import { createConfluenceClient } from "../client/atlassian.js";
3
+ export function registerConfluenceSpaceTools(server) {
4
+ server.tool("confluence_get_spaces", "List Confluence spaces accessible to the authenticated user. Returns space ID, key, name, type, and homepage ID.", {
5
+ limit: z.number().int().min(1).max(50).default(25).describe("Max results (1-50, default 25)"),
6
+ type: z.enum(["global", "personal"]).optional().describe('Filter by type: "global" or "personal"'),
7
+ }, async ({ limit, type }) => {
8
+ const client = await createConfluenceClient();
9
+ const result = await client.space.getSpaces({
10
+ limit,
11
+ type: type ?? undefined,
12
+ });
13
+ return {
14
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
15
+ };
16
+ });
17
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceUpdateTools(server: McpServer): void;
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import { createConfluenceClient } from "../client/atlassian.js";
3
+ import { toADF } from "../client/adf.js";
4
+ export function registerConfluenceUpdateTools(server) {
5
+ server.tool("confluence_update_page", "Update the title and/or content of an existing Confluence page. Requires the current version number — Confluence rejects updates that omit it.", {
6
+ pageId: z.string().describe("Numeric page ID"),
7
+ title: z.string().describe("New title (required even if unchanged)"),
8
+ body: z.string().describe("New content as plain markdown. Converted to ADF before sending."),
9
+ versionNumber: z.number().int().describe("Current version number. Fetch with confluence_get_page first. Must increment by 1."),
10
+ }, async ({ pageId, title, body, versionNumber }) => {
11
+ const client = await createConfluenceClient();
12
+ const adfDoc = toADF(body);
13
+ const params = {
14
+ id: Number(pageId),
15
+ title,
16
+ body: { representation: "atlas_doc_format", value: adfDoc },
17
+ version: { number: versionNumber + 1 },
18
+ status: "current",
19
+ };
20
+ const page = await client.page.updatePage(params);
21
+ return {
22
+ content: [{ type: "text", text: JSON.stringify(page, null, 2) }],
23
+ };
24
+ });
25
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConfluenceTools(server: McpServer): void;
@@ -0,0 +1,14 @@
1
+ import { registerConfluenceSearchTools } from "./confluence-search.js";
2
+ import { registerConfluenceGetTools } from "./confluence-get.js";
3
+ import { registerConfluenceCreateTools } from "./confluence-create.js";
4
+ import { registerConfluenceUpdateTools } from "./confluence-update.js";
5
+ import { registerConfluenceCommentTools } from "./confluence-comment.js";
6
+ import { registerConfluenceSpaceTools } from "./confluence-spaces.js";
7
+ export function registerConfluenceTools(server) {
8
+ registerConfluenceSearchTools(server);
9
+ registerConfluenceGetTools(server);
10
+ registerConfluenceCreateTools(server);
11
+ registerConfluenceUpdateTools(server);
12
+ registerConfluenceCommentTools(server);
13
+ registerConfluenceSpaceTools(server);
14
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraCommentTools(server: McpServer): void;
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ import { toADF } from "../client/adf.js";
4
+ export function registerJiraCommentTools(server) {
5
+ server.tool("jira_add_comment", "Add a comment to a Jira issue.", {
6
+ issueKey: z.string().describe("Issue key"),
7
+ body: z
8
+ .string()
9
+ .describe("Comment text (plain text, auto-converted to ADF)"),
10
+ }, async ({ issueKey, body }) => {
11
+ const client = await createJiraClient();
12
+ const comment = await client.issueComments.addComment({
13
+ issueIdOrKey: issueKey,
14
+ body: toADF(body),
15
+ });
16
+ return {
17
+ content: [{ type: "text", text: JSON.stringify(comment, null, 2) }],
18
+ };
19
+ });
20
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraCreateTools(server: McpServer): void;
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ import { toADF } from "../client/adf.js";
4
+ function buildCreateFields(input) {
5
+ const fields = {
6
+ project: { key: input.projectKey },
7
+ summary: input.summary,
8
+ issuetype: { name: input.issueType },
9
+ };
10
+ if (input.description)
11
+ fields.description = toADF(input.description);
12
+ if (input.assigneeAccountId)
13
+ fields.assignee = { id: input.assigneeAccountId };
14
+ if (input.priority)
15
+ fields.priority = { name: input.priority };
16
+ if (input.labels)
17
+ fields.labels = input.labels;
18
+ if (input.parentKey)
19
+ fields.parent = { key: input.parentKey };
20
+ return fields;
21
+ }
22
+ export function registerJiraCreateTools(server) {
23
+ server.tool("jira_create_issue", "Create a new Jira issue in the specified project.", {
24
+ projectKey: z.string().describe("Project key (e.g. PROJ)"),
25
+ summary: z.string().describe("Issue title/summary"),
26
+ issueType: z.string().describe("Issue type name, e.g. Task, Bug, Story"),
27
+ description: z.string().optional().describe("Issue body text (plain text, auto-converted to ADF)"),
28
+ assigneeAccountId: z.string().optional().describe("Atlassian account ID of assignee"),
29
+ priority: z.string().optional().describe("Priority name: Highest, High, Medium, Low, Lowest"),
30
+ labels: z.array(z.string()).optional().describe("Array of label strings"),
31
+ parentKey: z.string().optional().describe("Parent issue key (for subtasks)"),
32
+ }, async (input) => {
33
+ const client = await createJiraClient();
34
+ const created = await client.issues.createIssue({ fields: buildCreateFields(input) });
35
+ return {
36
+ content: [{ type: "text", text: JSON.stringify(created, null, 2) }],
37
+ };
38
+ });
39
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraGetTools(server: McpServer): void;
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ export function registerJiraGetTools(server) {
4
+ server.tool("jira_get_issue", "Get full details of a single Jira issue by its key (e.g. PROJ-123). Returns fields including summary, description, status, assignee, comments, and subtasks.", {
5
+ issueKey: z.string().describe("Issue key (e.g. PROJ-42)"),
6
+ fields: z
7
+ .array(z.string())
8
+ .optional()
9
+ .describe("Fields to return. Omit for all fields."),
10
+ }, async ({ issueKey, fields }) => {
11
+ const client = await createJiraClient();
12
+ const issue = await client.issues.getIssue({
13
+ issueIdOrKey: issueKey,
14
+ fields,
15
+ });
16
+ return {
17
+ content: [{ type: "text", text: JSON.stringify(issue, null, 2) }],
18
+ };
19
+ });
20
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraProjectTools(server: McpServer): void;
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ export function registerJiraProjectTools(server) {
4
+ server.tool("jira_get_projects", "List all Jira projects accessible to the authenticated user. Returns project key, name, type, and lead.", {
5
+ maxResults: z
6
+ .number()
7
+ .int()
8
+ .min(1)
9
+ .max(100)
10
+ .default(50)
11
+ .describe("Max projects to return (default 50)"),
12
+ query: z.string().optional().describe("Optional name filter string"),
13
+ }, async ({ maxResults, query }) => {
14
+ const client = await createJiraClient();
15
+ const result = await client.projects.searchProjects({
16
+ maxResults,
17
+ query,
18
+ });
19
+ return {
20
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
21
+ };
22
+ });
23
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraSearchTools(server: McpServer): void;
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ const DEFAULT_FIELDS = [
4
+ "summary",
5
+ "status",
6
+ "assignee",
7
+ "priority",
8
+ "issuetype",
9
+ "created",
10
+ "updated",
11
+ ];
12
+ export function registerJiraSearchTools(server) {
13
+ server.tool("jira_search_issues", "Search Jira issues using JQL. Returns a list of matching issues with key, summary, status, assignee, and priority.", {
14
+ jql: z.string().describe("JQL query string"),
15
+ maxResults: z
16
+ .number()
17
+ .int()
18
+ .min(1)
19
+ .max(100)
20
+ .default(20)
21
+ .describe("Max issues to return (1-100, default 20)"),
22
+ fields: z
23
+ .array(z.string())
24
+ .optional()
25
+ .describe("Fields to include in results"),
26
+ }, async ({ jql, maxResults, fields }) => {
27
+ const client = await createJiraClient();
28
+ const result = await client.issueSearch.searchAndReconsileIssuesUsingJql({
29
+ jql,
30
+ maxResults,
31
+ fields: fields ?? DEFAULT_FIELDS,
32
+ });
33
+ return {
34
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
35
+ };
36
+ });
37
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraTransitionTools(server: McpServer): void;
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ function textResponse(data) {
4
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
5
+ }
6
+ async function handleTransition(issueKey, transitionName) {
7
+ const client = await createJiraClient();
8
+ const { transitions } = await client.issues.getTransitions({ issueIdOrKey: issueKey });
9
+ if (!transitions || transitions.length === 0) {
10
+ return textResponse({ error: "No transitions available for this issue" });
11
+ }
12
+ const match = transitions.find((t) => t.name?.toLowerCase() === transitionName.toLowerCase());
13
+ if (!match) {
14
+ return textResponse({
15
+ error: `No transition named "${transitionName}" found`,
16
+ available: transitions.map((t) => t.name),
17
+ });
18
+ }
19
+ await client.issues.doTransition({
20
+ issueIdOrKey: issueKey,
21
+ transition: { id: match.id },
22
+ });
23
+ return textResponse({ success: true, transitionedTo: transitionName });
24
+ }
25
+ export function registerJiraTransitionTools(server) {
26
+ server.tool("jira_transition_issue", "Move a Jira issue to a new status by triggering a workflow transition (e.g. In Progress, Done).", {
27
+ issueKey: z.string().describe("Issue key"),
28
+ transitionName: z.string().describe('Target status name, e.g. "In Progress", "Done", "Closed"'),
29
+ }, ({ issueKey, transitionName }) => handleTransition(issueKey, transitionName));
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraUpdateTools(server: McpServer): void;
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { createJiraClient } from "../client/atlassian.js";
3
+ import { toADF } from "../client/adf.js";
4
+ export function registerJiraUpdateTools(server) {
5
+ server.tool("jira_update_issue", "Update fields on an existing Jira issue.", {
6
+ issueKey: z.string().describe("Issue key (e.g. PROJ-42)"),
7
+ summary: z.string().optional().describe("New summary"),
8
+ description: z
9
+ .string()
10
+ .optional()
11
+ .describe("New description (plain text, auto-converted to ADF)"),
12
+ assigneeAccountId: z
13
+ .string()
14
+ .optional()
15
+ .describe("New assignee's account ID"),
16
+ priority: z.string().optional().describe("New priority name"),
17
+ labels: z
18
+ .array(z.string())
19
+ .optional()
20
+ .describe("Replace labels with this array"),
21
+ }, async ({ issueKey, summary, description, assigneeAccountId, priority, labels }) => {
22
+ const client = await createJiraClient();
23
+ const fields = {};
24
+ if (summary)
25
+ fields.summary = summary;
26
+ if (description)
27
+ fields.description = toADF(description);
28
+ if (assigneeAccountId)
29
+ fields.assignee = { id: assigneeAccountId };
30
+ if (priority)
31
+ fields.priority = { name: priority };
32
+ if (labels)
33
+ fields.labels = labels;
34
+ await client.issues.editIssue({ issueIdOrKey: issueKey, fields });
35
+ return {
36
+ content: [{ type: "text", text: `Updated ${issueKey}` }],
37
+ };
38
+ });
39
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerJiraTools(server: McpServer): void;
@@ -0,0 +1,16 @@
1
+ import { registerJiraSearchTools } from "./jira-search.js";
2
+ import { registerJiraGetTools } from "./jira-get.js";
3
+ import { registerJiraCreateTools } from "./jira-create.js";
4
+ import { registerJiraUpdateTools } from "./jira-update.js";
5
+ import { registerJiraTransitionTools } from "./jira-transition.js";
6
+ import { registerJiraCommentTools } from "./jira-comment.js";
7
+ import { registerJiraProjectTools } from "./jira-projects.js";
8
+ export function registerJiraTools(server) {
9
+ registerJiraSearchTools(server);
10
+ registerJiraGetTools(server);
11
+ registerJiraCreateTools(server);
12
+ registerJiraUpdateTools(server);
13
+ registerJiraTransitionTools(server);
14
+ registerJiraCommentTools(server);
15
+ registerJiraProjectTools(server);
16
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMetaTools(server: McpServer): void;
@@ -0,0 +1,18 @@
1
+ import { getToken } from "../config.js";
2
+ export function registerMetaTools(server) {
3
+ server.tool("atlassian_whoami", "Returns the identity of the currently authenticated Atlassian user — account ID, display name, email, and accessible site URLs.", {}, async () => {
4
+ const token = await getToken();
5
+ const res = await fetch("https://api.atlassian.com/me", {
6
+ headers: { Authorization: `Bearer ${token}` },
7
+ });
8
+ if (!res.ok) {
9
+ return {
10
+ content: [{ type: "text", text: JSON.stringify({ error: true, status: res.status, message: `Failed to fetch user info: ${res.statusText}` }) }],
11
+ };
12
+ }
13
+ const data = await res.json();
14
+ return {
15
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
16
+ };
17
+ });
18
+ }
@@ -0,0 +1,14 @@
1
+ export interface Secrets {
2
+ oauth: {
3
+ access_token: string;
4
+ refresh_token: string;
5
+ expires_at: number;
6
+ scope: string;
7
+ cloud_id: string;
8
+ };
9
+ }
10
+ export interface TokenCache {
11
+ token: string;
12
+ cloudId: string;
13
+ expiresAt: number;
14
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};