@nimbus-dev/connectors 0.2.1 → 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 +14 -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 +14 -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,184 +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 { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
5
|
-
import {
|
|
6
|
-
createRegisterSimpleTool,
|
|
7
|
-
createZodToolRegistrar,
|
|
8
|
-
mcpJsonResult as jsonResult,
|
|
9
|
-
requireProcessEnv,
|
|
10
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
11
|
-
import { makeRestToolRegistrar } from "../../../shared/rest-tool-kit.ts";
|
|
12
|
-
|
|
13
|
-
const CCI_API = "https://circleci.com/api/v2";
|
|
14
|
-
|
|
15
|
-
function projectPathSegments(projectSlug: string): string {
|
|
16
|
-
return projectSlug
|
|
17
|
-
.split("/")
|
|
18
|
-
.map((s) => s.trim())
|
|
19
|
-
.filter((s) => s.length > 0)
|
|
20
|
-
.map((s) => encodeURIComponent(s))
|
|
21
|
-
.join("/");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function circleciFetch(
|
|
25
|
-
token: string,
|
|
26
|
-
path: string,
|
|
27
|
-
init?: RequestInit,
|
|
28
|
-
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
29
|
-
const url = path.startsWith("http") ? path : `${CCI_API}${path}`;
|
|
30
|
-
const res = await fetch(url, {
|
|
31
|
-
...init,
|
|
32
|
-
headers: {
|
|
33
|
-
Accept: "application/json",
|
|
34
|
-
"Circle-Token": token,
|
|
35
|
-
...(init?.headers as Record<string, string> | undefined),
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
const text = await res.text();
|
|
39
|
-
let json: unknown;
|
|
40
|
-
try {
|
|
41
|
-
json = JSON.parse(text) as unknown;
|
|
42
|
-
} catch {
|
|
43
|
-
json = null;
|
|
44
|
-
}
|
|
45
|
-
return { ok: res.ok, status: res.status, json, text };
|
|
46
|
-
}
|
|
3
|
+
import { registerCircleciTools } from "./tools.ts";
|
|
47
4
|
|
|
48
5
|
const mcp = new McpServer({ name: "nimbus-circleci", version: "0.1.0" });
|
|
49
|
-
const reg = createZodToolRegistrar(createRegisterSimpleTool(mcp));
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Every MUTATING circleci 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(mcp, {
|
|
57
|
-
connector: "circleci",
|
|
58
|
-
scopeEnv: "NIMBUS_MCP_CIRCLECI_WRITE_SCOPE",
|
|
59
|
-
scopeKinds: ["project"],
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
/** Standard CircleCI read tool: token → circleciFetch(buildPath) → mcpJsonResultIfOk("CircleCI"). */
|
|
63
|
-
const registerCciTool = makeRestToolRegistrar({
|
|
64
|
-
registrar: reg,
|
|
65
|
-
tokenEnv: "CIRCLECI_API_TOKEN",
|
|
66
|
-
serviceLabel: "CircleCI",
|
|
67
|
-
fetch: circleciFetch,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const projectSlugSchema = z.object({
|
|
71
|
-
projectSlug: z
|
|
72
|
-
.string()
|
|
73
|
-
.min(3)
|
|
74
|
-
.describe("CircleCI project slug, e.g. gh/org/repo or bb/workspace/repo"),
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
registerCciTool(
|
|
78
|
-
"circleci_pipeline_list",
|
|
79
|
-
"List pipelines for a CircleCI project.",
|
|
80
|
-
projectSlugSchema.extend({
|
|
81
|
-
pageToken: z.string().min(1).optional(),
|
|
82
|
-
}),
|
|
83
|
-
(parsed) => {
|
|
84
|
-
const base = `/project/${projectPathSegments(parsed.projectSlug)}/pipeline`;
|
|
85
|
-
const u = new URL(`${CCI_API}${base}`);
|
|
86
|
-
if (parsed.pageToken !== undefined) {
|
|
87
|
-
u.searchParams.set("page-token", parsed.pageToken);
|
|
88
|
-
}
|
|
89
|
-
return `${u.pathname}${u.search}`;
|
|
90
|
-
},
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
registerCciTool(
|
|
94
|
-
"circleci_pipeline_get",
|
|
95
|
-
"Get a pipeline by UUID.",
|
|
96
|
-
z.object({ pipelineId: z.uuid() }),
|
|
97
|
-
(parsed) => `/pipeline/${encodeURIComponent(parsed.pipelineId)}`,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
registerCciTool(
|
|
101
|
-
"circleci_workflow_list",
|
|
102
|
-
"List workflows for a pipeline.",
|
|
103
|
-
z.object({ pipelineId: z.uuid() }),
|
|
104
|
-
(parsed) => `/pipeline/${encodeURIComponent(parsed.pipelineId)}/workflow`,
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
registerCciTool(
|
|
108
|
-
"circleci_job_list",
|
|
109
|
-
"List jobs for a workflow.",
|
|
110
|
-
z.object({ workflowId: z.uuid() }),
|
|
111
|
-
(parsed) => `/workflow/${encodeURIComponent(parsed.workflowId)}/job`,
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
registerCciTool(
|
|
115
|
-
"circleci_job_artifacts",
|
|
116
|
-
"List artifact metadata for a job number under a project.",
|
|
117
|
-
projectSlugSchema.extend({
|
|
118
|
-
jobNumber: z.number().int().min(1),
|
|
119
|
-
}),
|
|
120
|
-
(parsed) =>
|
|
121
|
-
`/project/${projectPathSegments(parsed.projectSlug)}/job/${String(parsed.jobNumber)}/artifacts`,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
registerWriteTool(
|
|
125
|
-
"circleci_pipeline_trigger",
|
|
126
|
-
{
|
|
127
|
-
mutates: "circleci.pipeline.trigger",
|
|
128
|
-
recoverable: true,
|
|
129
|
-
scopeTargetOf: (p) => ({ kind: "project", value: p.projectSlug }),
|
|
130
|
-
},
|
|
131
|
-
"Trigger a new pipeline on a branch (or tag).",
|
|
132
|
-
projectSlugSchema.extend({
|
|
133
|
-
branch: z.string().min(1).optional(),
|
|
134
|
-
tag: z.string().min(1).optional(),
|
|
135
|
-
parameters: z.record(z.string(), z.string()).optional(),
|
|
136
|
-
}),
|
|
137
|
-
async (parsed) => {
|
|
138
|
-
const token = requireProcessEnv("CIRCLECI_API_TOKEN");
|
|
139
|
-
const path = `/project/${projectPathSegments(parsed.projectSlug)}/pipeline`;
|
|
140
|
-
const body: Record<string, unknown> = {};
|
|
141
|
-
if (parsed.tag !== undefined && parsed.tag !== "") {
|
|
142
|
-
body["tag"] = parsed.tag;
|
|
143
|
-
} else {
|
|
144
|
-
body["branch"] = parsed.branch ?? "main";
|
|
145
|
-
}
|
|
146
|
-
if (parsed.parameters !== undefined && Object.keys(parsed.parameters).length > 0) {
|
|
147
|
-
body["parameters"] = parsed.parameters;
|
|
148
|
-
}
|
|
149
|
-
const res = await circleciFetch(token, path, {
|
|
150
|
-
method: "POST",
|
|
151
|
-
headers: { "Content-Type": "application/json" },
|
|
152
|
-
body: JSON.stringify(body),
|
|
153
|
-
});
|
|
154
|
-
if (!res.ok) {
|
|
155
|
-
throw new Error(`CircleCI trigger ${String(res.status)}: ${res.text.slice(0, 400)}`);
|
|
156
|
-
}
|
|
157
|
-
return jsonResult(res.json ?? { ok: true, raw: res.text });
|
|
158
|
-
},
|
|
159
|
-
);
|
|
160
6
|
|
|
161
|
-
|
|
162
|
-
"circleci_job_cancel",
|
|
163
|
-
{
|
|
164
|
-
mutates: "circleci.job.cancel",
|
|
165
|
-
recoverable: true,
|
|
166
|
-
scopeTargetOf: (p) => ({ kind: "project", value: p.projectSlug }),
|
|
167
|
-
},
|
|
168
|
-
"Cancel a running job by project slug and job number.",
|
|
169
|
-
projectSlugSchema.extend({
|
|
170
|
-
jobNumber: z.number().int().min(1),
|
|
171
|
-
}),
|
|
172
|
-
async (parsed) => {
|
|
173
|
-
const token = requireProcessEnv("CIRCLECI_API_TOKEN");
|
|
174
|
-
const path = `/project/${projectPathSegments(parsed.projectSlug)}/job/${String(parsed.jobNumber)}/cancel`;
|
|
175
|
-
const res = await circleciFetch(token, path, { method: "POST" });
|
|
176
|
-
if (!res.ok) {
|
|
177
|
-
throw new Error(`CircleCI job cancel ${String(res.status)}: ${res.text.slice(0, 400)}`);
|
|
178
|
-
}
|
|
179
|
-
return jsonResult(res.json ?? { ok: true });
|
|
180
|
-
},
|
|
181
|
-
);
|
|
7
|
+
registerCircleciTools(mcp);
|
|
182
8
|
|
|
183
9
|
const transport = new StdioServerTransport();
|
|
184
10
|
await mcp.connect(transport);
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ConsentServer } from "../../../shared/consent-kit.ts";
|
|
3
|
+
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
4
|
+
import {
|
|
5
|
+
createRegisterSimpleTool,
|
|
6
|
+
createZodToolRegistrar,
|
|
7
|
+
mcpJsonResult as jsonResult,
|
|
8
|
+
requireProcessEnv,
|
|
9
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
10
|
+
import {
|
|
11
|
+
makeRestToolRegistrar,
|
|
12
|
+
type RestFetchResult,
|
|
13
|
+
toRestFetchResult,
|
|
14
|
+
} from "../../../shared/rest-tool-kit.ts";
|
|
15
|
+
|
|
16
|
+
const CCI_API = "https://circleci.com/api/v2";
|
|
17
|
+
|
|
18
|
+
function projectPathSegments(projectSlug: string): string {
|
|
19
|
+
return projectSlug
|
|
20
|
+
.split("/")
|
|
21
|
+
.map((s) => s.trim())
|
|
22
|
+
.filter((s) => s.length > 0)
|
|
23
|
+
.map((s) => encodeURIComponent(s))
|
|
24
|
+
.join("/");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function circleciFetch(
|
|
28
|
+
token: string,
|
|
29
|
+
path: string,
|
|
30
|
+
init?: RequestInit,
|
|
31
|
+
): Promise<RestFetchResult> {
|
|
32
|
+
const url = path.startsWith("http") ? path : `${CCI_API}${path}`;
|
|
33
|
+
const res = await fetch(url, {
|
|
34
|
+
...init,
|
|
35
|
+
headers: {
|
|
36
|
+
Accept: "application/json",
|
|
37
|
+
"Circle-Token": token,
|
|
38
|
+
...(init?.headers as Record<string, string> | undefined),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return toRestFetchResult(res);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
45
|
+
export const CIRCLECI_TOOL_NAMES = [
|
|
46
|
+
"circleci_pipeline_list",
|
|
47
|
+
"circleci_pipeline_get",
|
|
48
|
+
"circleci_workflow_list",
|
|
49
|
+
"circleci_job_list",
|
|
50
|
+
"circleci_job_artifacts",
|
|
51
|
+
"circleci_pipeline_trigger",
|
|
52
|
+
"circleci_job_cancel",
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
export function registerCircleciTools(
|
|
56
|
+
server: ConsentServer & { tool: (...args: never) => unknown },
|
|
57
|
+
): void {
|
|
58
|
+
const reg = createZodToolRegistrar(createRegisterSimpleTool(server));
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Every MUTATING circleci tool goes through here. Outside the gateway this adds the
|
|
62
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
63
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
64
|
+
*/
|
|
65
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
66
|
+
connector: "circleci",
|
|
67
|
+
scopeEnv: "NIMBUS_MCP_CIRCLECI_WRITE_SCOPE",
|
|
68
|
+
scopeKinds: ["project"],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/** Standard CircleCI read tool: token → circleciFetch(buildPath) → mcpJsonResultIfOk("CircleCI"). */
|
|
72
|
+
const registerCciTool = makeRestToolRegistrar({
|
|
73
|
+
registrar: reg,
|
|
74
|
+
tokenEnv: "CIRCLECI_API_TOKEN",
|
|
75
|
+
serviceLabel: "CircleCI",
|
|
76
|
+
fetch: circleciFetch,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const projectSlugSchema = z.object({
|
|
80
|
+
projectSlug: z
|
|
81
|
+
.string()
|
|
82
|
+
.min(3)
|
|
83
|
+
.describe("CircleCI project slug, e.g. gh/org/repo or bb/workspace/repo"),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
registerCciTool(
|
|
87
|
+
"circleci_pipeline_list",
|
|
88
|
+
"List pipelines for a CircleCI project.",
|
|
89
|
+
projectSlugSchema.extend({
|
|
90
|
+
pageToken: z.string().min(1).optional(),
|
|
91
|
+
}),
|
|
92
|
+
(parsed) => {
|
|
93
|
+
const base = `/project/${projectPathSegments(parsed.projectSlug)}/pipeline`;
|
|
94
|
+
const u = new URL(`${CCI_API}${base}`);
|
|
95
|
+
if (parsed.pageToken !== undefined) {
|
|
96
|
+
u.searchParams.set("page-token", parsed.pageToken);
|
|
97
|
+
}
|
|
98
|
+
// Relative to CCI_API, like every other tool here: the fetcher prefixes the base, which
|
|
99
|
+
// already ends in /api/v2, so returning `u.pathname` requested /api/v2/api/v2/….
|
|
100
|
+
return `${base}${u.search}`;
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
registerCciTool(
|
|
105
|
+
"circleci_pipeline_get",
|
|
106
|
+
"Get a pipeline by UUID.",
|
|
107
|
+
z.object({ pipelineId: z.uuid() }),
|
|
108
|
+
(parsed) => `/pipeline/${encodeURIComponent(parsed.pipelineId)}`,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
registerCciTool(
|
|
112
|
+
"circleci_workflow_list",
|
|
113
|
+
"List workflows for a pipeline.",
|
|
114
|
+
z.object({ pipelineId: z.uuid() }),
|
|
115
|
+
(parsed) => `/pipeline/${encodeURIComponent(parsed.pipelineId)}/workflow`,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
registerCciTool(
|
|
119
|
+
"circleci_job_list",
|
|
120
|
+
"List jobs for a workflow.",
|
|
121
|
+
z.object({ workflowId: z.uuid() }),
|
|
122
|
+
(parsed) => `/workflow/${encodeURIComponent(parsed.workflowId)}/job`,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
registerCciTool(
|
|
126
|
+
"circleci_job_artifacts",
|
|
127
|
+
"List artifact metadata for a job number under a project.",
|
|
128
|
+
projectSlugSchema.extend({
|
|
129
|
+
jobNumber: z.number().int().min(1),
|
|
130
|
+
}),
|
|
131
|
+
(parsed) =>
|
|
132
|
+
`/project/${projectPathSegments(parsed.projectSlug)}/job/${String(parsed.jobNumber)}/artifacts`,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
registerWriteTool(
|
|
136
|
+
"circleci_pipeline_trigger",
|
|
137
|
+
{
|
|
138
|
+
mutates: "circleci.pipeline.trigger",
|
|
139
|
+
recoverable: true,
|
|
140
|
+
scopeTargetOf: (p) => ({ kind: "project", value: p.projectSlug }),
|
|
141
|
+
},
|
|
142
|
+
"Trigger a new pipeline on a branch (or tag).",
|
|
143
|
+
projectSlugSchema.extend({
|
|
144
|
+
branch: z.string().min(1).optional(),
|
|
145
|
+
tag: z.string().min(1).optional(),
|
|
146
|
+
parameters: z.record(z.string(), z.string()).optional(),
|
|
147
|
+
}),
|
|
148
|
+
async (parsed) => {
|
|
149
|
+
const token = requireProcessEnv("CIRCLECI_API_TOKEN");
|
|
150
|
+
const path = `/project/${projectPathSegments(parsed.projectSlug)}/pipeline`;
|
|
151
|
+
const body: Record<string, unknown> = {};
|
|
152
|
+
if (parsed.tag !== undefined && parsed.tag !== "") {
|
|
153
|
+
body["tag"] = parsed.tag;
|
|
154
|
+
} else {
|
|
155
|
+
body["branch"] = parsed.branch ?? "main";
|
|
156
|
+
}
|
|
157
|
+
if (parsed.parameters !== undefined && Object.keys(parsed.parameters).length > 0) {
|
|
158
|
+
body["parameters"] = parsed.parameters;
|
|
159
|
+
}
|
|
160
|
+
const res = await circleciFetch(token, path, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: { "Content-Type": "application/json" },
|
|
163
|
+
body: JSON.stringify(body),
|
|
164
|
+
});
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
throw new Error(`CircleCI trigger ${String(res.status)}: ${res.text.slice(0, 400)}`);
|
|
167
|
+
}
|
|
168
|
+
return jsonResult(res.json ?? { ok: true, raw: res.text });
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
registerWriteTool(
|
|
173
|
+
"circleci_job_cancel",
|
|
174
|
+
{
|
|
175
|
+
mutates: "circleci.job.cancel",
|
|
176
|
+
recoverable: true,
|
|
177
|
+
scopeTargetOf: (p) => ({ kind: "project", value: p.projectSlug }),
|
|
178
|
+
},
|
|
179
|
+
"Cancel a running job by project slug and job number.",
|
|
180
|
+
projectSlugSchema.extend({
|
|
181
|
+
jobNumber: z.number().int().min(1),
|
|
182
|
+
}),
|
|
183
|
+
async (parsed) => {
|
|
184
|
+
const token = requireProcessEnv("CIRCLECI_API_TOKEN");
|
|
185
|
+
const path = `/project/${projectPathSegments(parsed.projectSlug)}/job/${String(parsed.jobNumber)}/cancel`;
|
|
186
|
+
const res = await circleciFetch(token, path, { method: "POST" });
|
|
187
|
+
if (!res.ok) {
|
|
188
|
+
throw new Error(`CircleCI job cancel ${String(res.status)}: ${res.text.slice(0, 400)}`);
|
|
189
|
+
}
|
|
190
|
+
return jsonResult(res.json ?? { ok: true });
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
createCliJsonRunner,
|
|
4
|
+
gcloudProjectArgs,
|
|
5
|
+
isRecord,
|
|
6
|
+
strField,
|
|
7
|
+
} from "../../../shared/cli-json-kit.ts";
|
|
2
8
|
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
3
9
|
import { nimbusSpawn } from "../../../shared/nimbus-spawn.ts";
|
|
4
10
|
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
@@ -20,42 +26,23 @@ export const CLOUD_LOGGING_TOOL_NAMES = [
|
|
|
20
26
|
"cloud_logging_search",
|
|
21
27
|
] as const;
|
|
22
28
|
|
|
23
|
-
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
24
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function strField(r: Record<string, unknown>, key: string): string {
|
|
28
|
-
const v = r[key];
|
|
29
|
-
return typeof v === "string" ? v : "";
|
|
30
|
-
}
|
|
31
|
-
|
|
32
29
|
function asArray(parsed: unknown): unknown[] {
|
|
33
30
|
return Array.isArray(parsed) ? parsed : [];
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
/** The configured GCP project id, if `GOOGLE_CLOUD_PROJECT` is set at spawn. */
|
|
37
|
-
function projectArgs(): string[] {
|
|
38
|
-
const project = process.env["GOOGLE_CLOUD_PROJECT"]?.trim();
|
|
39
|
-
return project !== undefined && project !== "" ? ["--project", project] : [];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
33
|
/**
|
|
43
34
|
* Run `gcloud logging <args> --format json` and parse stdout. gcloud reads
|
|
44
35
|
* Application Default Credentials from `GOOGLE_APPLICATION_CREDENTIALS`
|
|
45
36
|
* natively from the process env injected at spawn time. Throws on non-zero exit.
|
|
46
37
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
["gcloud", "logging", ...args, ...
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
throw new Error(`gcloud logging ${args[0] ?? ""} failed: ${err.slice(0, 400)}`);
|
|
56
|
-
}
|
|
57
|
-
return out === "" ? [] : (JSON.parse(out) as unknown);
|
|
58
|
-
}
|
|
38
|
+
const gcloudLogging = createCliJsonRunner(
|
|
39
|
+
{
|
|
40
|
+
argv: (args) => ["gcloud", "logging", ...args, ...gcloudProjectArgs(), "--format", "json"],
|
|
41
|
+
label: "gcloud logging",
|
|
42
|
+
emptyResult: [],
|
|
43
|
+
},
|
|
44
|
+
nimbusSpawn,
|
|
45
|
+
);
|
|
59
46
|
|
|
60
47
|
function sinkMatches(entry: unknown, q: string): boolean {
|
|
61
48
|
if (!isRecord(entry)) {
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
asArray,
|
|
4
|
+
cliArg,
|
|
5
|
+
createCliJsonRunner,
|
|
6
|
+
isRecord,
|
|
7
|
+
strField,
|
|
8
|
+
} from "../../../shared/cli-json-kit.ts";
|
|
2
9
|
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
3
10
|
import { nimbusSpawn } from "../../../shared/nimbus-spawn.ts";
|
|
4
11
|
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
5
|
-
import { isSafeCliArg } from "../../../shared/safe-cli-arg.ts";
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* A log-group name / prefix passed as a value to the `aws logs` CLI. Rejected at
|
|
9
15
|
* the schema boundary if it begins with `-` (argv flag smuggling) or contains
|
|
10
16
|
* control characters.
|
|
11
17
|
*/
|
|
12
|
-
const cliArg = z
|
|
13
|
-
.string()
|
|
14
|
-
.min(1)
|
|
15
|
-
.refine(isSafeCliArg, { message: 'must not start with "-" or contain control characters' });
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Amazon CloudWatch Logs (Tier-3, no-row-data) MCP tool surface. ALL tools
|
|
@@ -32,40 +34,19 @@ export const CLOUDWATCH_TOOL_NAMES = [
|
|
|
32
34
|
|
|
33
35
|
const LIMIT = "50";
|
|
34
36
|
|
|
35
|
-
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
36
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function strField(r: Record<string, unknown>, key: string): string {
|
|
40
|
-
const v = r[key];
|
|
41
|
-
return typeof v === "string" ? v : "";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function asArray(parsed: unknown, key: string): unknown[] {
|
|
45
|
-
if (!isRecord(parsed)) {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
const arr = parsed[key];
|
|
49
|
-
return Array.isArray(arr) ? arr : [];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
37
|
/**
|
|
53
38
|
* Run `aws logs <args> --output json` and parse stdout. The AWS CLI reads
|
|
54
39
|
* AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION / AWS_PROFILE
|
|
55
40
|
* natively from the process env injected at spawn time. Throws on non-zero exit.
|
|
56
41
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
["aws", "logs", ...args, "--output", "json"],
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
throw new Error(`aws logs ${args[0] ?? ""} failed: ${err.slice(0, 400)}`);
|
|
66
|
-
}
|
|
67
|
-
return out === "" ? {} : (JSON.parse(out) as unknown);
|
|
68
|
-
}
|
|
42
|
+
const logsCli = createCliJsonRunner(
|
|
43
|
+
{
|
|
44
|
+
argv: (args) => ["aws", "logs", ...args, "--output", "json"],
|
|
45
|
+
label: "aws logs",
|
|
46
|
+
emptyResult: {},
|
|
47
|
+
},
|
|
48
|
+
nimbusSpawn,
|
|
49
|
+
);
|
|
69
50
|
|
|
70
51
|
function groupMatches(entry: unknown, q: string): boolean {
|
|
71
52
|
if (!isRecord(entry)) {
|
|
@@ -1,74 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { matchesResult } from "../../../shared/mcp-search-tool.ts";
|
|
3
|
-
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
4
1
|
import { runReadOnlyMcpConnector } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const CODEMAGIC_API = "https://api.codemagic.io";
|
|
8
|
-
|
|
9
|
-
function authHeader(): Record<string, string> {
|
|
10
|
-
const t = process.env["CODEMAGIC_TOKEN"]?.trim();
|
|
11
|
-
if (t === undefined || t === "") {
|
|
12
|
-
throw new Error("CODEMAGIC_TOKEN is not set");
|
|
13
|
-
}
|
|
14
|
-
return { "x-auth-token": t, Accept: "application/json" };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function codemagicGet(path: string): Promise<unknown> {
|
|
18
|
-
const res = await fetch(`${CODEMAGIC_API}${path}`, { headers: authHeader() });
|
|
19
|
-
const text = await res.text();
|
|
20
|
-
if (!res.ok) {
|
|
21
|
-
throw new Error(`Codemagic ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
22
|
-
}
|
|
23
|
-
return JSON.parse(text) as unknown;
|
|
24
|
-
}
|
|
2
|
+
import { registerCodemagicTools } from "./tools.ts";
|
|
25
3
|
|
|
26
4
|
await runReadOnlyMcpConnector("nimbus-codemagic", (reg) => {
|
|
27
|
-
reg
|
|
28
|
-
"codemagic_list",
|
|
29
|
-
"List Codemagic resources. When `appId` is omitted, returns the user's accessible apps via `/apps`. When `appId` is provided, returns the app's recent builds via `/builds?appId=<id>` (limit optional).",
|
|
30
|
-
z.object({
|
|
31
|
-
appId: z.string().min(1).optional(),
|
|
32
|
-
limit: z.number().int().min(1).max(50).optional(),
|
|
33
|
-
}),
|
|
34
|
-
async (p) => {
|
|
35
|
-
if (p.appId === undefined) {
|
|
36
|
-
return jsonResult(await codemagicGet("/apps"));
|
|
37
|
-
}
|
|
38
|
-
const limit = p.limit ?? 50;
|
|
39
|
-
return jsonResult(
|
|
40
|
-
await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=${String(limit)}`),
|
|
41
|
-
);
|
|
42
|
-
},
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
reg(
|
|
46
|
-
"codemagic_get",
|
|
47
|
-
"Fetch a single Codemagic resource. When `buildId` is provided, returns the build via `/builds/<buildId>`. When omitted, returns the app via `/apps` filtered to `appId`.",
|
|
48
|
-
z.object({
|
|
49
|
-
appId: z.string().min(1),
|
|
50
|
-
buildId: z.string().min(1).optional(),
|
|
51
|
-
}),
|
|
52
|
-
async (p) => {
|
|
53
|
-
if (p.buildId === undefined) {
|
|
54
|
-
return jsonResult(await codemagicGet("/apps"));
|
|
55
|
-
}
|
|
56
|
-
return jsonResult(await codemagicGet(`/builds/${encodeURIComponent(p.buildId)}`));
|
|
57
|
-
},
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
reg(
|
|
61
|
-
"codemagic_search",
|
|
62
|
-
"Substring search across a Codemagic app's recent builds. Matches the query against `branch`, `message`, `workflowId`, and `status` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
63
|
-
z.object({
|
|
64
|
-
appId: z.string().min(1),
|
|
65
|
-
query: z.string().min(1),
|
|
66
|
-
limit: z.number().int().min(1).max(200).optional(),
|
|
67
|
-
}),
|
|
68
|
-
async (p) => {
|
|
69
|
-
const root = await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=50`);
|
|
70
|
-
const builds = (root as { builds?: unknown[] } | null)?.builds;
|
|
71
|
-
return matchesResult(builds, filterCodemagicBuilds, p);
|
|
72
|
-
},
|
|
73
|
-
);
|
|
5
|
+
registerCodemagicTools(reg);
|
|
74
6
|
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createJsonGetter, envAuthHeaders } from "../../../shared/env-json-api.ts";
|
|
3
|
+
import { matchesResult } from "../../../shared/mcp-search-tool.ts";
|
|
4
|
+
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
5
|
+
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
6
|
+
import { filterCodemagicBuilds } from "./search-filter.ts";
|
|
7
|
+
|
|
8
|
+
const CODEMAGIC_API = "https://api.codemagic.io";
|
|
9
|
+
|
|
10
|
+
const codemagicGet = createJsonGetter({
|
|
11
|
+
base: CODEMAGIC_API,
|
|
12
|
+
label: "Codemagic",
|
|
13
|
+
headers: envAuthHeaders({ env: "CODEMAGIC_TOKEN", scheme: "", header: "x-auth-token" }),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
17
|
+
export const CODEMAGIC_TOOL_NAMES = [
|
|
18
|
+
"codemagic_list",
|
|
19
|
+
"codemagic_get",
|
|
20
|
+
"codemagic_search",
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export function registerCodemagicTools(reg: ZodToolRegistrar): void {
|
|
24
|
+
reg(
|
|
25
|
+
"codemagic_list",
|
|
26
|
+
"List Codemagic resources. When `appId` is omitted, returns the user's accessible apps via `/apps`. When `appId` is provided, returns the app's recent builds via `/builds?appId=<id>` (limit optional).",
|
|
27
|
+
z.object({
|
|
28
|
+
appId: z.string().min(1).optional(),
|
|
29
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
30
|
+
}),
|
|
31
|
+
async (p) => {
|
|
32
|
+
if (p.appId === undefined) {
|
|
33
|
+
return jsonResult(await codemagicGet("/apps"));
|
|
34
|
+
}
|
|
35
|
+
const limit = p.limit ?? 50;
|
|
36
|
+
return jsonResult(
|
|
37
|
+
await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=${String(limit)}`),
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
reg(
|
|
43
|
+
"codemagic_get",
|
|
44
|
+
"Fetch a single Codemagic resource. When `buildId` is provided, returns the build via `/builds/<buildId>`. When omitted, returns the app via `/apps` filtered to `appId`.",
|
|
45
|
+
z.object({
|
|
46
|
+
appId: z.string().min(1),
|
|
47
|
+
buildId: z.string().min(1).optional(),
|
|
48
|
+
}),
|
|
49
|
+
async (p) => {
|
|
50
|
+
if (p.buildId === undefined) {
|
|
51
|
+
return jsonResult(await codemagicGet("/apps"));
|
|
52
|
+
}
|
|
53
|
+
return jsonResult(await codemagicGet(`/builds/${encodeURIComponent(p.buildId)}`));
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
reg(
|
|
58
|
+
"codemagic_search",
|
|
59
|
+
"Substring search across a Codemagic app's recent builds. Matches the query against `branch`, `message`, `workflowId`, and `status` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
60
|
+
z.object({
|
|
61
|
+
appId: z.string().min(1),
|
|
62
|
+
query: z.string().min(1),
|
|
63
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
64
|
+
}),
|
|
65
|
+
async (p) => {
|
|
66
|
+
const root = await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=50`);
|
|
67
|
+
const builds = (root as { builds?: unknown[] } | null)?.builds;
|
|
68
|
+
return matchesResult(builds, filterCodemagicBuilds, p);
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
}
|