@nimbus-dev/connectors 0.2.0 → 0.2.2
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/README.md +18 -4
- package/connectors/airflow/src/search-filter.ts +2 -15
- package/connectors/airflow/src/server.ts +2 -87
- package/connectors/airflow/src/tools.ts +74 -0
- package/connectors/apple/src/adapters.ts +326 -0
- package/connectors/apple/src/apple-mail-core.ts +4 -8
- package/connectors/apple/src/calendar-tools.ts +28 -8
- package/connectors/apple/src/server.ts +23 -512
- package/connectors/apple/src/tools.ts +18 -44
- package/connectors/argocd/src/server.ts +25 -38
- package/connectors/athena/src/tools.ts +15 -34
- package/connectors/aws/src/server.ts +2 -183
- package/connectors/aws/src/tools.ts +197 -0
- package/connectors/azure/src/server.ts +2 -131
- package/connectors/azure/src/tools.ts +133 -0
- package/connectors/bigeye/src/search-filter.ts +4 -15
- package/connectors/bigeye/src/server.ts +38 -38
- package/connectors/bigquery/src/tools.ts +1 -26
- package/connectors/bitbucket/src/server.ts +2 -244
- package/connectors/bitbucket/src/tools.ts +241 -0
- package/connectors/bitrise/src/server.ts +2 -79
- package/connectors/bitrise/src/tools.ts +76 -0
- package/connectors/canva/src/search-filter.ts +2 -11
- package/connectors/canva/src/server.ts +2 -73
- package/connectors/canva/src/tools.ts +66 -0
- package/connectors/circleci/src/server.ts +2 -176
- package/connectors/circleci/src/tools.ts +193 -0
- package/connectors/cloud-logging/src/tools.ts +14 -27
- package/connectors/cloudwatch/src/tools.ts +15 -34
- package/connectors/codemagic/src/server.ts +2 -70
- package/connectors/codemagic/src/tools.ts +71 -0
- package/connectors/confluence/src/server.ts +2 -301
- package/connectors/confluence/src/tools.ts +320 -0
- package/connectors/dagster/src/server.ts +2 -214
- package/connectors/dagster/src/tools.ts +187 -0
- package/connectors/databricks/src/search-filter.ts +7 -7
- package/connectors/databricks/src/server.ts +2 -77
- package/connectors/databricks/src/tools.ts +66 -0
- package/connectors/datadog/src/server.ts +2 -48
- package/connectors/datadog/src/tools.ts +48 -0
- package/connectors/dataprofile/src/profile.ts +16 -46
- package/connectors/dbt/src/server.ts +2 -92
- package/connectors/dbt/src/tools.ts +80 -0
- package/connectors/dependencytrack/src/server.ts +2 -76
- package/connectors/dependencytrack/src/tools.ts +65 -0
- package/connectors/discord/src/server.ts +2 -85
- package/connectors/discord/src/tools.ts +82 -0
- package/connectors/elasticsearch/src/tools.ts +7 -37
- package/connectors/fastmail/src/jmap-client.ts +202 -0
- package/connectors/fastmail/src/jmap-core.ts +8 -21
- package/connectors/fastmail/src/server.ts +2 -187
- package/connectors/fastmail/src/tools.ts +4 -23
- package/connectors/figma/src/search-filter.ts +2 -11
- package/connectors/figma/src/server.ts +2 -113
- package/connectors/figma/src/tools.ts +99 -0
- package/connectors/firebase/src/server.ts +2 -90
- package/connectors/firebase/src/token.ts +2 -5
- package/connectors/firebase/src/tools.ts +106 -0
- package/connectors/flagsmith/src/server.ts +2 -98
- package/connectors/flagsmith/src/tools.ts +95 -0
- package/connectors/flux/src/server.ts +52 -53
- package/connectors/gcp/src/server.ts +2 -132
- package/connectors/gcp/src/tools.ts +123 -0
- package/connectors/github/src/server.ts +2 -328
- package/connectors/github/src/tools.ts +311 -0
- package/connectors/github-actions/src/server.ts +2 -205
- package/connectors/github-actions/src/tools.ts +209 -0
- package/connectors/gitlab/src/server.ts +2 -384
- package/connectors/gitlab/src/tools.ts +392 -0
- package/connectors/gmail/src/server.ts +2 -270
- package/connectors/gmail/src/tools.ts +209 -0
- package/connectors/google-drive/src/server.ts +2 -524
- package/connectors/google-drive/src/tools.ts +529 -0
- package/connectors/google-meet/src/server.ts +2 -82
- package/connectors/google-meet/src/tools.ts +87 -0
- package/connectors/google-photos/src/server.ts +2 -132
- package/connectors/google-photos/src/tools.ts +138 -0
- package/connectors/grafana/src/server.ts +2 -53
- package/connectors/grafana/src/tools.ts +53 -0
- package/connectors/great-expectations/src/gx-parse.ts +14 -45
- package/connectors/greenhouse/src/server.ts +2 -63
- package/connectors/greenhouse/src/tools.ts +68 -0
- package/connectors/hubspot/src/server.ts +2 -77
- package/connectors/hubspot/src/tools.ts +70 -0
- package/connectors/iac/src/server.ts +2 -137
- package/connectors/iac/src/tools.ts +152 -0
- package/connectors/imap/src/imap-core.ts +29 -82
- package/connectors/imap/src/server.ts +33 -220
- package/connectors/intercom/src/server.ts +2 -61
- package/connectors/intercom/src/tools.ts +58 -0
- package/connectors/jenkins/src/jenkins-api.ts +2 -6
- package/connectors/jenkins/src/server.ts +2 -215
- package/connectors/jenkins/src/tools.ts +240 -0
- package/connectors/jira/src/server.ts +2 -301
- package/connectors/jira/src/tools.ts +325 -0
- package/connectors/kubernetes/src/server.ts +2 -155
- package/connectors/kubernetes/src/tools.ts +159 -0
- package/connectors/launchdarkly/src/server.ts +2 -75
- package/connectors/launchdarkly/src/tools.ts +72 -0
- package/connectors/lever/src/search-filter.ts +4 -12
- package/connectors/lever/src/server.ts +2 -60
- package/connectors/lever/src/tools.ts +61 -0
- package/connectors/linear/src/server.ts +2 -459
- package/connectors/linear/src/tools.ts +482 -0
- package/connectors/localdb/src/sql-scan.ts +13 -41
- package/connectors/localdb/src/tools.ts +1 -1
- package/connectors/looker/src/search-filter.ts +2 -15
- package/connectors/looker/src/server.ts +35 -70
- package/connectors/mendeley/src/server.ts +2 -55
- package/connectors/mendeley/src/tools.ts +54 -0
- package/connectors/mercury/src/server.ts +2 -57
- package/connectors/mercury/src/tools.ts +50 -0
- package/connectors/metabase/src/search-filter.ts +4 -15
- package/connectors/metabase/src/server.ts +2 -79
- package/connectors/metabase/src/tools.ts +64 -0
- package/connectors/miro/src/server.ts +2 -72
- package/connectors/miro/src/tools.ts +65 -0
- package/connectors/mlflow/src/search-filter.ts +3 -7
- package/connectors/mlflow/src/server.ts +20 -40
- package/connectors/monte-carlo/src/server.ts +50 -57
- package/connectors/netlify/src/search-filter.ts +6 -14
- package/connectors/netlify/src/server.ts +2 -60
- package/connectors/netlify/src/tools.ts +53 -0
- package/connectors/newrelic/src/server.ts +2 -40
- package/connectors/newrelic/src/tools.ts +37 -0
- package/connectors/notion/src/server.ts +2 -335
- package/connectors/notion/src/tools.ts +344 -0
- package/connectors/obsidian/src/server.ts +2 -425
- package/connectors/obsidian/src/tools.ts +466 -0
- package/connectors/onedrive/src/server.ts +2 -249
- package/connectors/onedrive/src/tools.ts +253 -0
- package/connectors/outlook/src/server.ts +2 -351
- package/connectors/outlook/src/tools.ts +357 -0
- package/connectors/pagerduty/src/server.ts +2 -132
- package/connectors/pagerduty/src/tools.ts +126 -0
- package/connectors/pipedrive/src/server.ts +2 -58
- package/connectors/pipedrive/src/tools.ts +63 -0
- package/connectors/powerbi/src/search-filter.ts +2 -15
- package/connectors/powerbi/src/server.ts +43 -68
- package/connectors/prefect/src/search-filter.ts +3 -16
- package/connectors/prefect/src/server.ts +2 -90
- package/connectors/prefect/src/tools.ts +83 -0
- package/connectors/protonmail/src/mail-core.ts +16 -75
- package/connectors/protonmail/src/server.ts +34 -214
- package/connectors/raindrop/src/server.ts +2 -103
- package/connectors/raindrop/src/tools.ts +103 -0
- package/connectors/ramp/src/server.ts +2 -105
- package/connectors/ramp/src/tools.ts +98 -0
- package/connectors/readwise/src/server.ts +2 -88
- package/connectors/readwise/src/tools.ts +81 -0
- package/connectors/sagemaker/src/tools.ts +19 -38
- package/connectors/salesforce/src/search-filter.ts +4 -11
- package/connectors/salesforce/src/server.ts +2 -90
- package/connectors/salesforce/src/tools.ts +75 -0
- package/connectors/semgrep/src/search-filter.ts +3 -10
- package/connectors/semgrep/src/server.ts +2 -104
- package/connectors/semgrep/src/tools.ts +92 -0
- package/connectors/sentry/src/server.ts +2 -58
- package/connectors/sentry/src/tools.ts +53 -0
- package/connectors/slack/src/server.ts +2 -352
- package/connectors/slack/src/tools.ts +376 -0
- package/connectors/snowflake/src/search-filter.ts +4 -19
- package/connectors/snowflake/src/server.ts +6 -25
- package/connectors/snyk/src/server.ts +2 -140
- package/connectors/snyk/src/tools.ts +136 -0
- package/connectors/sonarqube/src/server.ts +2 -111
- package/connectors/sonarqube/src/tools.ts +98 -0
- package/connectors/stackoverflow/src/server.ts +2 -77
- package/connectors/stackoverflow/src/tools.ts +66 -0
- package/connectors/storybook/src/storybook-parse.ts +3 -6
- package/connectors/stripe/src/server.ts +2 -61
- package/connectors/stripe/src/tools.ts +54 -0
- package/connectors/superset/src/search-filter.ts +4 -15
- package/connectors/superset/src/server.ts +2 -115
- package/connectors/superset/src/tools.ts +98 -0
- package/connectors/tableau/src/search-filter.ts +2 -15
- package/connectors/tableau/src/server.ts +13 -65
- package/connectors/teams/src/server.ts +2 -249
- package/connectors/teams/src/tools.ts +283 -0
- package/connectors/testflight/src/server.ts +2 -70
- package/connectors/testflight/src/tools.ts +79 -0
- package/connectors/vercel/src/search-filter.ts +2 -10
- package/connectors/vercel/src/server.ts +2 -75
- package/connectors/vercel/src/tools.ts +73 -0
- package/connectors/vertex-ai/src/tools.ts +23 -33
- package/connectors/wiz/src/search-filter.ts +6 -30
- package/connectors/wiz/src/server.ts +2 -188
- package/connectors/wiz/src/tools.ts +168 -0
- package/connectors/zendesk/src/server.ts +2 -73
- package/connectors/zendesk/src/tools.ts +60 -0
- package/connectors/zoom/src/search-filter.ts +1 -7
- package/connectors/zoom/src/server.ts +2 -94
- package/connectors/zoom/src/tools.ts +93 -0
- package/connectors/zotero/src/server.ts +2 -78
- package/connectors/zotero/src/tools.ts +67 -0
- package/package.json +15 -12
- package/shared/access-token-cache.ts +123 -0
- package/shared/atlassian-json-fetch.ts +6 -16
- package/shared/audit-chain.ts +13 -6
- package/shared/cli-json-kit.ts +150 -0
- package/shared/collection-tool-kit.ts +143 -0
- package/shared/connector-mode.ts +3 -2
- package/shared/cursor-list-tool.ts +29 -0
- package/shared/env-json-api.ts +209 -0
- package/shared/fetch-json-text.ts +27 -0
- package/shared/github-rest.ts +62 -0
- package/shared/graphql-json.ts +60 -0
- package/shared/imap-mail-core.ts +91 -0
- package/shared/imap-tool-kit.ts +57 -23
- package/shared/imapflow-adapter.ts +417 -0
- package/shared/rest-tool-kit.ts +77 -7
- package/shared/run-cli-json.ts +38 -16
- package/shared/search-filter.ts +58 -18
- package/shared/walk-files.ts +118 -0
- package/standalone/src/launcher.ts +19 -7
|
@@ -1,309 +1,10 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
fetchAtlassianBasicAuthJsonText,
|
|
6
|
-
normalizeRequiredSiteBaseUrl,
|
|
7
|
-
requireTrimmedEnv,
|
|
8
|
-
} from "../../../shared/atlassian-json-fetch.ts";
|
|
9
|
-
import { joinApiPath } from "../../../shared/join-api-path.ts";
|
|
10
|
-
import { parseCitationsJson } from "../../../shared/kb-markdown.ts";
|
|
11
|
-
import {
|
|
12
|
-
createRegisterSimpleTool,
|
|
13
|
-
createZodToolRegistrar,
|
|
14
|
-
mcpJsonResultFromTextIfOk,
|
|
15
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
16
|
-
import { buildConfluenceKbPageBody } from "./kb-append.ts";
|
|
17
|
-
|
|
18
|
-
function wikiRoot(siteBase: string): string {
|
|
19
|
-
const b = normalizeRequiredSiteBaseUrl(siteBase, "CONFLUENCE_BASE_URL is empty");
|
|
20
|
-
return b.endsWith("/wiki") ? b : `${b}/wiki`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function requireConfluenceConfig(): { wikiApi: string; email: string; token: string } {
|
|
24
|
-
const baseRaw = requireTrimmedEnv("CONFLUENCE_BASE_URL", "CONFLUENCE_BASE_URL is not set");
|
|
25
|
-
const email = requireTrimmedEnv("CONFLUENCE_EMAIL", "CONFLUENCE_EMAIL is not set");
|
|
26
|
-
const token = requireTrimmedEnv("CONFLUENCE_API_TOKEN", "CONFLUENCE_API_TOKEN is not set");
|
|
27
|
-
return {
|
|
28
|
-
wikiApi: `${wikiRoot(baseRaw)}/rest/api`,
|
|
29
|
-
email,
|
|
30
|
-
token,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function confFetch(
|
|
35
|
-
path: string,
|
|
36
|
-
init?: RequestInit,
|
|
37
|
-
): Promise<{ ok: boolean; status: number; text: string }> {
|
|
38
|
-
const { wikiApi, email, token } = requireConfluenceConfig();
|
|
39
|
-
const url = joinApiPath(wikiApi, path);
|
|
40
|
-
return fetchAtlassianBasicAuthJsonText(url, email, token, init);
|
|
41
|
-
}
|
|
3
|
+
import { registerConfluenceTools } from "./tools.ts";
|
|
42
4
|
|
|
43
5
|
const server = new McpServer({ name: "nimbus-confluence", version: "0.1.0" });
|
|
44
6
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
48
|
-
|
|
49
|
-
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Every MUTATING confluence tool goes through here. Outside the gateway this adds the
|
|
53
|
-
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
54
|
-
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
55
|
-
*/
|
|
56
|
-
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
57
|
-
connector: "confluence",
|
|
58
|
-
scopeEnv: "NIMBUS_MCP_CONFLUENCE_WRITE_SCOPE",
|
|
59
|
-
scopeKinds: ["space", "page"],
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const confluenceLimitStartSchema = z.object({
|
|
63
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
64
|
-
start: z.number().int().min(0).optional(),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
reg(
|
|
68
|
-
"confluence_space_list",
|
|
69
|
-
"List Confluence spaces (GET /space).",
|
|
70
|
-
confluenceLimitStartSchema,
|
|
71
|
-
async (parsed) => {
|
|
72
|
-
const qs = new URLSearchParams({
|
|
73
|
-
limit: String(parsed.limit ?? 25),
|
|
74
|
-
start: String(parsed.start ?? 0),
|
|
75
|
-
});
|
|
76
|
-
const res = await confFetch(`/space?${qs.toString()}`);
|
|
77
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
const confluenceSpaceContentSchema = z.object({
|
|
82
|
-
spaceKey: z.string().min(1),
|
|
83
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
84
|
-
start: z.number().int().min(0).optional(),
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
reg(
|
|
88
|
-
"confluence_page_list",
|
|
89
|
-
"List pages in a space (GET /content, type=page).",
|
|
90
|
-
confluenceSpaceContentSchema,
|
|
91
|
-
async (parsed) => {
|
|
92
|
-
const qs = new URLSearchParams({
|
|
93
|
-
type: "page",
|
|
94
|
-
spaceKey: parsed.spaceKey,
|
|
95
|
-
limit: String(parsed.limit ?? 50),
|
|
96
|
-
start: String(parsed.start ?? 0),
|
|
97
|
-
expand: "history.lastUpdated,version",
|
|
98
|
-
});
|
|
99
|
-
const res = await confFetch(`/content?${qs.toString()}`);
|
|
100
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
101
|
-
},
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const confluencePageIdSchema = z.object({ pageId: z.string().min(1) });
|
|
105
|
-
|
|
106
|
-
reg(
|
|
107
|
-
"confluence_page_get",
|
|
108
|
-
"Get a Confluence page with body.storage (GET /content/{id}).",
|
|
109
|
-
confluencePageIdSchema,
|
|
110
|
-
async (parsed) => {
|
|
111
|
-
const id = encodeURIComponent(parsed.pageId);
|
|
112
|
-
const res = await confFetch(
|
|
113
|
-
`/content/${id}?expand=body.storage,version,history.lastUpdated,space`,
|
|
114
|
-
);
|
|
115
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
116
|
-
},
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
reg(
|
|
120
|
-
"confluence_blogpost_list",
|
|
121
|
-
"List blog posts in a space (GET /content, type=blogpost).",
|
|
122
|
-
confluenceSpaceContentSchema,
|
|
123
|
-
async (parsed) => {
|
|
124
|
-
const qs = new URLSearchParams({
|
|
125
|
-
type: "blogpost",
|
|
126
|
-
spaceKey: parsed.spaceKey,
|
|
127
|
-
limit: String(parsed.limit ?? 25),
|
|
128
|
-
start: String(parsed.start ?? 0),
|
|
129
|
-
expand: "history.lastUpdated,version",
|
|
130
|
-
});
|
|
131
|
-
const res = await confFetch(`/content?${qs.toString()}`);
|
|
132
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
133
|
-
},
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const confluencePostIdSchema = z.object({ postId: z.string().min(1) });
|
|
137
|
-
|
|
138
|
-
reg(
|
|
139
|
-
"confluence_blogpost_get",
|
|
140
|
-
"Get a blog post by id (GET /content/{id}).",
|
|
141
|
-
confluencePostIdSchema,
|
|
142
|
-
async (parsed) => {
|
|
143
|
-
const id = encodeURIComponent(parsed.postId);
|
|
144
|
-
const res = await confFetch(
|
|
145
|
-
`/content/${id}?expand=body.storage,version,history.lastUpdated,space`,
|
|
146
|
-
);
|
|
147
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
148
|
-
},
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const confluenceCommentListSchema = z.object({
|
|
152
|
-
pageId: z.string().min(1),
|
|
153
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
154
|
-
start: z.number().int().min(0).optional(),
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
reg(
|
|
158
|
-
"confluence_comment_list",
|
|
159
|
-
"List footer comments on a page (GET /content/{id}/child/comment).",
|
|
160
|
-
confluenceCommentListSchema,
|
|
161
|
-
async (parsed) => {
|
|
162
|
-
const id = encodeURIComponent(parsed.pageId);
|
|
163
|
-
const qs = new URLSearchParams({
|
|
164
|
-
limit: String(parsed.limit ?? 50),
|
|
165
|
-
start: String(parsed.start ?? 0),
|
|
166
|
-
expand: "body.storage,version",
|
|
167
|
-
});
|
|
168
|
-
const res = await confFetch(`/content/${id}/child/comment?${qs.toString()}`);
|
|
169
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
170
|
-
},
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
const confluencePageCreateSchema = z.object({
|
|
174
|
-
spaceKey: z.string().min(1),
|
|
175
|
-
title: z.string().min(1),
|
|
176
|
-
storageHtml: z.string().min(1),
|
|
177
|
-
parentPageId: z.string().min(1).optional(),
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
registerWriteTool(
|
|
181
|
-
"confluence_page_create",
|
|
182
|
-
{
|
|
183
|
-
mutates: "confluence.page.create",
|
|
184
|
-
recoverable: true,
|
|
185
|
-
scopeTargetOf: (p) => ({ kind: "space", value: p.spaceKey }),
|
|
186
|
-
},
|
|
187
|
-
"Create a page in a space (POST /content). Optional parentPageId.",
|
|
188
|
-
confluencePageCreateSchema,
|
|
189
|
-
async (parsed) => {
|
|
190
|
-
const body: Record<string, unknown> = {
|
|
191
|
-
type: "page",
|
|
192
|
-
title: parsed.title,
|
|
193
|
-
space: { key: parsed.spaceKey },
|
|
194
|
-
body: {
|
|
195
|
-
storage: {
|
|
196
|
-
value: parsed.storageHtml,
|
|
197
|
-
representation: "storage",
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
};
|
|
201
|
-
if (parsed.parentPageId !== undefined) {
|
|
202
|
-
body["ancestors"] = [{ id: parsed.parentPageId }];
|
|
203
|
-
}
|
|
204
|
-
const res = await confFetch("/content", { method: "POST", body: JSON.stringify(body) });
|
|
205
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
206
|
-
},
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
const confluenceKbAppendSchema = z.object({
|
|
210
|
-
spaceKey: z.string().min(1),
|
|
211
|
-
parentPageId: z.string().min(1),
|
|
212
|
-
title: z.string().min(1),
|
|
213
|
-
bodyMarkdown: z.string().min(1),
|
|
214
|
-
citationsJson: z.string().optional(),
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
registerWriteTool(
|
|
218
|
-
"confluence_kb_append",
|
|
219
|
-
{
|
|
220
|
-
mutates: "confluence.knowledge.write",
|
|
221
|
-
recoverable: true,
|
|
222
|
-
scopeTargetOf: (p) => ({ kind: "space", value: p.spaceKey }),
|
|
223
|
-
},
|
|
224
|
-
"Create a knowledge-base page under a parent from simple markdown + citations (POST /content). " +
|
|
225
|
-
"Used by Nimbus tribal-knowledge capture; the destination space/parent is supplied by the gateway from local config only.",
|
|
226
|
-
confluenceKbAppendSchema,
|
|
227
|
-
async (parsed) => {
|
|
228
|
-
const body = buildConfluenceKbPageBody({
|
|
229
|
-
spaceKey: parsed.spaceKey,
|
|
230
|
-
parentPageId: parsed.parentPageId,
|
|
231
|
-
title: parsed.title,
|
|
232
|
-
bodyMarkdown: parsed.bodyMarkdown,
|
|
233
|
-
citations: parseCitationsJson(parsed.citationsJson ?? "[]"),
|
|
234
|
-
});
|
|
235
|
-
const res = await confFetch("/content", { method: "POST", body: JSON.stringify(body) });
|
|
236
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
237
|
-
},
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
const confluencePageUpdateSchema = z.object({
|
|
241
|
-
pageId: z.string().min(1),
|
|
242
|
-
versionNumber: z.number().int().min(1),
|
|
243
|
-
title: z.string().min(1),
|
|
244
|
-
storageHtml: z.string().min(1),
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
registerWriteTool(
|
|
248
|
-
"confluence_page_update",
|
|
249
|
-
{
|
|
250
|
-
mutates: "confluence.page.update",
|
|
251
|
-
recoverable: true,
|
|
252
|
-
scopeTargetOf: (p) => ({ kind: "page", value: p.pageId }),
|
|
253
|
-
},
|
|
254
|
-
"Update page body and bump version (PUT /content/{id}). Pass current version number and title.",
|
|
255
|
-
confluencePageUpdateSchema,
|
|
256
|
-
async (parsed) => {
|
|
257
|
-
const id = encodeURIComponent(parsed.pageId);
|
|
258
|
-
const body: Record<string, unknown> = {
|
|
259
|
-
type: "page",
|
|
260
|
-
title: parsed.title,
|
|
261
|
-
version: { number: parsed.versionNumber + 1, message: "nimbus" },
|
|
262
|
-
body: {
|
|
263
|
-
storage: {
|
|
264
|
-
value: parsed.storageHtml,
|
|
265
|
-
representation: "storage",
|
|
266
|
-
},
|
|
267
|
-
},
|
|
268
|
-
};
|
|
269
|
-
const res = await confFetch(`/content/${id}`, { method: "PUT", body: JSON.stringify(body) });
|
|
270
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
271
|
-
},
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
const confluenceCommentAddSchema = z.object({
|
|
275
|
-
pageId: z.string().min(1),
|
|
276
|
-
storageHtml: z.string().min(1),
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
registerWriteTool(
|
|
280
|
-
"confluence_comment_add",
|
|
281
|
-
{
|
|
282
|
-
mutates: "confluence.comment.add",
|
|
283
|
-
recoverable: true,
|
|
284
|
-
scopeTargetOf: (p) => ({ kind: "page", value: p.pageId }),
|
|
285
|
-
},
|
|
286
|
-
"Add a footer comment to a page (POST /content/{id}/child/comment).",
|
|
287
|
-
confluenceCommentAddSchema,
|
|
288
|
-
async (parsed) => {
|
|
289
|
-
const id = encodeURIComponent(parsed.pageId);
|
|
290
|
-
const body = {
|
|
291
|
-
type: "comment",
|
|
292
|
-
container: { id: parsed.pageId, type: "page" },
|
|
293
|
-
body: {
|
|
294
|
-
storage: {
|
|
295
|
-
value: parsed.storageHtml,
|
|
296
|
-
representation: "storage",
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
const res = await confFetch(`/content/${id}/child/comment`, {
|
|
301
|
-
method: "POST",
|
|
302
|
-
body: JSON.stringify(body),
|
|
303
|
-
});
|
|
304
|
-
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
305
|
-
},
|
|
306
|
-
);
|
|
7
|
+
registerConfluenceTools(server);
|
|
307
8
|
|
|
308
9
|
const transport = new StdioServerTransport();
|
|
309
10
|
await server.connect(transport);
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
fetchAtlassianBasicAuthJsonText,
|
|
4
|
+
normalizeRequiredSiteBaseUrl,
|
|
5
|
+
requireTrimmedEnv,
|
|
6
|
+
} from "../../../shared/atlassian-json-fetch.ts";
|
|
7
|
+
import type { ConsentServer } from "../../../shared/consent-kit.ts";
|
|
8
|
+
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
9
|
+
import { joinApiPath } from "../../../shared/join-api-path.ts";
|
|
10
|
+
import { parseCitationsJson } from "../../../shared/kb-markdown.ts";
|
|
11
|
+
import {
|
|
12
|
+
createRegisterSimpleTool,
|
|
13
|
+
createZodToolRegistrar,
|
|
14
|
+
mcpJsonResultFromTextIfOk,
|
|
15
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
16
|
+
import { buildConfluenceKbPageBody } from "./kb-append.ts";
|
|
17
|
+
|
|
18
|
+
function wikiRoot(siteBase: string): string {
|
|
19
|
+
const b = normalizeRequiredSiteBaseUrl(siteBase, "CONFLUENCE_BASE_URL is empty");
|
|
20
|
+
return b.endsWith("/wiki") ? b : `${b}/wiki`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function requireConfluenceConfig(): { wikiApi: string; email: string; token: string } {
|
|
24
|
+
const baseRaw = requireTrimmedEnv("CONFLUENCE_BASE_URL", "CONFLUENCE_BASE_URL is not set");
|
|
25
|
+
const email = requireTrimmedEnv("CONFLUENCE_EMAIL", "CONFLUENCE_EMAIL is not set");
|
|
26
|
+
const token = requireTrimmedEnv("CONFLUENCE_API_TOKEN", "CONFLUENCE_API_TOKEN is not set");
|
|
27
|
+
return {
|
|
28
|
+
wikiApi: `${wikiRoot(baseRaw)}/rest/api`,
|
|
29
|
+
email,
|
|
30
|
+
token,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function confFetch(
|
|
35
|
+
path: string,
|
|
36
|
+
init?: RequestInit,
|
|
37
|
+
): Promise<{ ok: boolean; status: number; text: string }> {
|
|
38
|
+
const { wikiApi, email, token } = requireConfluenceConfig();
|
|
39
|
+
const url = joinApiPath(wikiApi, path);
|
|
40
|
+
return fetchAtlassianBasicAuthJsonText(url, email, token, init);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
44
|
+
export const CONFLUENCE_TOOL_NAMES = [
|
|
45
|
+
"confluence_space_list",
|
|
46
|
+
"confluence_page_list",
|
|
47
|
+
"confluence_page_get",
|
|
48
|
+
"confluence_blogpost_list",
|
|
49
|
+
"confluence_blogpost_get",
|
|
50
|
+
"confluence_comment_list",
|
|
51
|
+
"confluence_page_create",
|
|
52
|
+
"confluence_kb_append",
|
|
53
|
+
"confluence_page_update",
|
|
54
|
+
"confluence_comment_add",
|
|
55
|
+
] as const;
|
|
56
|
+
|
|
57
|
+
export function registerConfluenceTools(
|
|
58
|
+
server: ConsentServer & { tool: (...args: never) => unknown },
|
|
59
|
+
): void {
|
|
60
|
+
const registerSimpleTool = createRegisterSimpleTool(server);
|
|
61
|
+
|
|
62
|
+
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Every MUTATING confluence tool goes through here. Outside the gateway this adds the
|
|
66
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
67
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
68
|
+
*/
|
|
69
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
70
|
+
connector: "confluence",
|
|
71
|
+
scopeEnv: "NIMBUS_MCP_CONFLUENCE_WRITE_SCOPE",
|
|
72
|
+
scopeKinds: ["space", "page"],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const confluenceLimitStartSchema = z.object({
|
|
76
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
77
|
+
start: z.number().int().min(0).optional(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
reg(
|
|
81
|
+
"confluence_space_list",
|
|
82
|
+
"List Confluence spaces (GET /space).",
|
|
83
|
+
confluenceLimitStartSchema,
|
|
84
|
+
async (parsed) => {
|
|
85
|
+
const qs = new URLSearchParams({
|
|
86
|
+
limit: String(parsed.limit ?? 25),
|
|
87
|
+
start: String(parsed.start ?? 0),
|
|
88
|
+
});
|
|
89
|
+
const res = await confFetch(`/space?${qs.toString()}`);
|
|
90
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const confluenceSpaceContentSchema = z.object({
|
|
95
|
+
spaceKey: z.string().min(1),
|
|
96
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
97
|
+
start: z.number().int().min(0).optional(),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
reg(
|
|
101
|
+
"confluence_page_list",
|
|
102
|
+
"List pages in a space (GET /content, type=page).",
|
|
103
|
+
confluenceSpaceContentSchema,
|
|
104
|
+
async (parsed) => {
|
|
105
|
+
const qs = new URLSearchParams({
|
|
106
|
+
type: "page",
|
|
107
|
+
spaceKey: parsed.spaceKey,
|
|
108
|
+
limit: String(parsed.limit ?? 50),
|
|
109
|
+
start: String(parsed.start ?? 0),
|
|
110
|
+
expand: "history.lastUpdated,version",
|
|
111
|
+
});
|
|
112
|
+
const res = await confFetch(`/content?${qs.toString()}`);
|
|
113
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const confluencePageIdSchema = z.object({ pageId: z.string().min(1) });
|
|
118
|
+
|
|
119
|
+
reg(
|
|
120
|
+
"confluence_page_get",
|
|
121
|
+
"Get a Confluence page with body.storage (GET /content/{id}).",
|
|
122
|
+
confluencePageIdSchema,
|
|
123
|
+
async (parsed) => {
|
|
124
|
+
const id = encodeURIComponent(parsed.pageId);
|
|
125
|
+
const res = await confFetch(
|
|
126
|
+
`/content/${id}?expand=body.storage,version,history.lastUpdated,space`,
|
|
127
|
+
);
|
|
128
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
reg(
|
|
133
|
+
"confluence_blogpost_list",
|
|
134
|
+
"List blog posts in a space (GET /content, type=blogpost).",
|
|
135
|
+
confluenceSpaceContentSchema,
|
|
136
|
+
async (parsed) => {
|
|
137
|
+
const qs = new URLSearchParams({
|
|
138
|
+
type: "blogpost",
|
|
139
|
+
spaceKey: parsed.spaceKey,
|
|
140
|
+
limit: String(parsed.limit ?? 25),
|
|
141
|
+
start: String(parsed.start ?? 0),
|
|
142
|
+
expand: "history.lastUpdated,version",
|
|
143
|
+
});
|
|
144
|
+
const res = await confFetch(`/content?${qs.toString()}`);
|
|
145
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const confluencePostIdSchema = z.object({ postId: z.string().min(1) });
|
|
150
|
+
|
|
151
|
+
reg(
|
|
152
|
+
"confluence_blogpost_get",
|
|
153
|
+
"Get a blog post by id (GET /content/{id}).",
|
|
154
|
+
confluencePostIdSchema,
|
|
155
|
+
async (parsed) => {
|
|
156
|
+
const id = encodeURIComponent(parsed.postId);
|
|
157
|
+
const res = await confFetch(
|
|
158
|
+
`/content/${id}?expand=body.storage,version,history.lastUpdated,space`,
|
|
159
|
+
);
|
|
160
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const confluenceCommentListSchema = z.object({
|
|
165
|
+
pageId: z.string().min(1),
|
|
166
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
167
|
+
start: z.number().int().min(0).optional(),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
reg(
|
|
171
|
+
"confluence_comment_list",
|
|
172
|
+
"List footer comments on a page (GET /content/{id}/child/comment).",
|
|
173
|
+
confluenceCommentListSchema,
|
|
174
|
+
async (parsed) => {
|
|
175
|
+
const id = encodeURIComponent(parsed.pageId);
|
|
176
|
+
const qs = new URLSearchParams({
|
|
177
|
+
limit: String(parsed.limit ?? 50),
|
|
178
|
+
start: String(parsed.start ?? 0),
|
|
179
|
+
expand: "body.storage,version",
|
|
180
|
+
});
|
|
181
|
+
const res = await confFetch(`/content/${id}/child/comment?${qs.toString()}`);
|
|
182
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const confluencePageCreateSchema = z.object({
|
|
187
|
+
spaceKey: z.string().min(1),
|
|
188
|
+
title: z.string().min(1),
|
|
189
|
+
storageHtml: z.string().min(1),
|
|
190
|
+
parentPageId: z.string().min(1).optional(),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
registerWriteTool(
|
|
194
|
+
"confluence_page_create",
|
|
195
|
+
{
|
|
196
|
+
mutates: "confluence.page.create",
|
|
197
|
+
recoverable: true,
|
|
198
|
+
scopeTargetOf: (p) => ({ kind: "space", value: p.spaceKey }),
|
|
199
|
+
},
|
|
200
|
+
"Create a page in a space (POST /content). Optional parentPageId.",
|
|
201
|
+
confluencePageCreateSchema,
|
|
202
|
+
async (parsed) => {
|
|
203
|
+
const body: Record<string, unknown> = {
|
|
204
|
+
type: "page",
|
|
205
|
+
title: parsed.title,
|
|
206
|
+
space: { key: parsed.spaceKey },
|
|
207
|
+
body: {
|
|
208
|
+
storage: {
|
|
209
|
+
value: parsed.storageHtml,
|
|
210
|
+
representation: "storage",
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
if (parsed.parentPageId !== undefined) {
|
|
215
|
+
body["ancestors"] = [{ id: parsed.parentPageId }];
|
|
216
|
+
}
|
|
217
|
+
const res = await confFetch("/content", { method: "POST", body: JSON.stringify(body) });
|
|
218
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const confluenceKbAppendSchema = z.object({
|
|
223
|
+
spaceKey: z.string().min(1),
|
|
224
|
+
parentPageId: z.string().min(1),
|
|
225
|
+
title: z.string().min(1),
|
|
226
|
+
bodyMarkdown: z.string().min(1),
|
|
227
|
+
citationsJson: z.string().optional(),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
registerWriteTool(
|
|
231
|
+
"confluence_kb_append",
|
|
232
|
+
{
|
|
233
|
+
mutates: "confluence.knowledge.write",
|
|
234
|
+
recoverable: true,
|
|
235
|
+
scopeTargetOf: (p) => ({ kind: "space", value: p.spaceKey }),
|
|
236
|
+
},
|
|
237
|
+
"Create a knowledge-base page under a parent from simple markdown + citations (POST /content). " +
|
|
238
|
+
"Used by Nimbus tribal-knowledge capture; the destination space/parent is supplied by the gateway from local config only.",
|
|
239
|
+
confluenceKbAppendSchema,
|
|
240
|
+
async (parsed) => {
|
|
241
|
+
const body = buildConfluenceKbPageBody({
|
|
242
|
+
spaceKey: parsed.spaceKey,
|
|
243
|
+
parentPageId: parsed.parentPageId,
|
|
244
|
+
title: parsed.title,
|
|
245
|
+
bodyMarkdown: parsed.bodyMarkdown,
|
|
246
|
+
citations: parseCitationsJson(parsed.citationsJson ?? "[]"),
|
|
247
|
+
});
|
|
248
|
+
const res = await confFetch("/content", { method: "POST", body: JSON.stringify(body) });
|
|
249
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
250
|
+
},
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const confluencePageUpdateSchema = z.object({
|
|
254
|
+
pageId: z.string().min(1),
|
|
255
|
+
versionNumber: z.number().int().min(1),
|
|
256
|
+
title: z.string().min(1),
|
|
257
|
+
storageHtml: z.string().min(1),
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
registerWriteTool(
|
|
261
|
+
"confluence_page_update",
|
|
262
|
+
{
|
|
263
|
+
mutates: "confluence.page.update",
|
|
264
|
+
recoverable: true,
|
|
265
|
+
scopeTargetOf: (p) => ({ kind: "page", value: p.pageId }),
|
|
266
|
+
},
|
|
267
|
+
"Update page body and bump version (PUT /content/{id}). Pass current version number and title.",
|
|
268
|
+
confluencePageUpdateSchema,
|
|
269
|
+
async (parsed) => {
|
|
270
|
+
const id = encodeURIComponent(parsed.pageId);
|
|
271
|
+
const body: Record<string, unknown> = {
|
|
272
|
+
type: "page",
|
|
273
|
+
title: parsed.title,
|
|
274
|
+
version: { number: parsed.versionNumber + 1, message: "nimbus" },
|
|
275
|
+
body: {
|
|
276
|
+
storage: {
|
|
277
|
+
value: parsed.storageHtml,
|
|
278
|
+
representation: "storage",
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
const res = await confFetch(`/content/${id}`, { method: "PUT", body: JSON.stringify(body) });
|
|
283
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const confluenceCommentAddSchema = z.object({
|
|
288
|
+
pageId: z.string().min(1),
|
|
289
|
+
storageHtml: z.string().min(1),
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
registerWriteTool(
|
|
293
|
+
"confluence_comment_add",
|
|
294
|
+
{
|
|
295
|
+
mutates: "confluence.comment.add",
|
|
296
|
+
recoverable: true,
|
|
297
|
+
scopeTargetOf: (p) => ({ kind: "page", value: p.pageId }),
|
|
298
|
+
},
|
|
299
|
+
"Add a footer comment to a page (POST /content/{id}/child/comment).",
|
|
300
|
+
confluenceCommentAddSchema,
|
|
301
|
+
async (parsed) => {
|
|
302
|
+
const id = encodeURIComponent(parsed.pageId);
|
|
303
|
+
const body = {
|
|
304
|
+
type: "comment",
|
|
305
|
+
container: { id: parsed.pageId, type: "page" },
|
|
306
|
+
body: {
|
|
307
|
+
storage: {
|
|
308
|
+
value: parsed.storageHtml,
|
|
309
|
+
representation: "storage",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
const res = await confFetch(`/content/${id}/child/comment`, {
|
|
314
|
+
method: "POST",
|
|
315
|
+
body: JSON.stringify(body),
|
|
316
|
+
});
|
|
317
|
+
return mcpJsonResultFromTextIfOk("Confluence", res);
|
|
318
|
+
},
|
|
319
|
+
);
|
|
320
|
+
}
|