@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,92 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { matchesResult, searchToolInputSchema } 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 BASE = "https://readwise.io";
|
|
8
|
-
|
|
9
|
-
function apiToken(): string {
|
|
10
|
-
const t = process.env["READWISE_TOKEN"]?.trim();
|
|
11
|
-
if (t === undefined || t === "") {
|
|
12
|
-
throw new Error("READWISE_TOKEN is not set");
|
|
13
|
-
}
|
|
14
|
-
return t;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function authHeader(): Record<string, string> {
|
|
18
|
-
return { Authorization: `Token ${apiToken()}`, Accept: "application/json" };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function readwiseGet(path: string): Promise<unknown> {
|
|
22
|
-
const res = await fetch(`${BASE}${path}`, { headers: authHeader() });
|
|
23
|
-
const text = await res.text();
|
|
24
|
-
if (!res.ok) {
|
|
25
|
-
throw new Error(`Readwise ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
26
|
-
}
|
|
27
|
-
return JSON.parse(text) as unknown;
|
|
28
|
-
}
|
|
2
|
+
import { registerReadwiseTools } from "./tools.ts";
|
|
29
3
|
|
|
30
4
|
await runReadOnlyMcpConnector("nimbus-readwise", (reg) => {
|
|
31
|
-
reg
|
|
32
|
-
"readwise_list",
|
|
33
|
-
"List the user's Readwise highlights (`GET /api/v2/highlights/?page_size=1000`). Returns the DRF `{ count, next, previous, results: [...] }` envelope — `results` holds the highlight objects and `next` is the next-page URL (null on the last page).",
|
|
34
|
-
z.object({}),
|
|
35
|
-
async () => {
|
|
36
|
-
return jsonResult(await readwiseGet(`/api/v2/highlights/?page_size=1000`));
|
|
37
|
-
},
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
reg(
|
|
41
|
-
"readwise_get",
|
|
42
|
-
"Fetch one Readwise highlight by its id (`GET /api/v2/highlights/{id}/`). Returns the highlight object directly. Throws when no match is found.",
|
|
43
|
-
z.object({
|
|
44
|
-
id: z.string().min(1),
|
|
45
|
-
}),
|
|
46
|
-
async (p) => {
|
|
47
|
-
return jsonResult(await readwiseGet(`/api/v2/highlights/${encodeURIComponent(p.id)}/`));
|
|
48
|
-
},
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
reg(
|
|
52
|
-
"readwise_search",
|
|
53
|
-
"Substring search across the user's Readwise highlights (first page only). Matches the query against the highlight text, the user's note, color, location_type, and tag names (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
54
|
-
searchToolInputSchema(100),
|
|
55
|
-
async (p) => {
|
|
56
|
-
const root = await readwiseGet(`/api/v2/highlights/?page_size=1000`);
|
|
57
|
-
const results = (root as { results?: unknown[] } | null)?.results;
|
|
58
|
-
return matchesResult(results, filterReadwiseHighlights, p);
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
reg(
|
|
63
|
-
"readwise_books_list",
|
|
64
|
-
"List the user's Readwise books/articles (`GET /api/v2/books/?page_size=1000`) — the parent records a highlight's `book_id` points at. Returns the same DRF `{ count, next, previous, results: [...] }` envelope as `readwise_list`; `results` holds the book objects (`id`, `title`, `author`, `category`, `source`, `num_highlights`, `highlights_url`, `source_url`, `asin`, `tags`, `document_note`).",
|
|
65
|
-
z.object({}),
|
|
66
|
-
async () => {
|
|
67
|
-
return jsonResult(await readwiseGet(`/api/v2/books/?page_size=1000`));
|
|
68
|
-
},
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
reg(
|
|
72
|
-
"readwise_book_get",
|
|
73
|
-
"Fetch one Readwise book/article by its id (`GET /api/v2/books/{id}/`). Note the id space is SEPARATE from the highlight id space — pass a highlight's `book_id`, not its `id`. Returns the book object directly. Throws when no match is found.",
|
|
74
|
-
z.object({
|
|
75
|
-
id: z.string().min(1),
|
|
76
|
-
}),
|
|
77
|
-
async (p) => {
|
|
78
|
-
return jsonResult(await readwiseGet(`/api/v2/books/${encodeURIComponent(p.id)}/`));
|
|
79
|
-
},
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
reg(
|
|
83
|
-
"readwise_books_search",
|
|
84
|
-
"Substring search across the user's Readwise books/articles (first page only). Matches the query against the title, author, category, source, the user's document note, and tag names (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
85
|
-
searchToolInputSchema(100),
|
|
86
|
-
async (p) => {
|
|
87
|
-
const root = await readwiseGet(`/api/v2/books/?page_size=1000`);
|
|
88
|
-
const results = (root as { results?: unknown[] } | null)?.results;
|
|
89
|
-
return matchesResult(results, filterReadwiseBooks, p);
|
|
90
|
-
},
|
|
91
|
-
);
|
|
5
|
+
registerReadwiseTools(reg);
|
|
92
6
|
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CollectionToolsConfig,
|
|
3
|
+
collectionToolNames,
|
|
4
|
+
envelopeRows,
|
|
5
|
+
registerCollectionTools,
|
|
6
|
+
} from "../../../shared/collection-tool-kit.ts";
|
|
7
|
+
import { createJsonGetter, envAuthHeaders } from "../../../shared/env-json-api.ts";
|
|
8
|
+
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
9
|
+
import { filterReadwiseBooks, filterReadwiseHighlights } from "./search-filter.ts";
|
|
10
|
+
|
|
11
|
+
const BASE = "https://readwise.io";
|
|
12
|
+
const HIGHLIGHTS = "/api/v2/highlights/?page_size=1000";
|
|
13
|
+
const BOOKS = "/api/v2/books/?page_size=1000";
|
|
14
|
+
|
|
15
|
+
/** Readwise authenticates with `Authorization: Token <key>`, not Bearer. */
|
|
16
|
+
const readwiseGet = createJsonGetter({
|
|
17
|
+
base: BASE,
|
|
18
|
+
label: "Readwise",
|
|
19
|
+
headers: envAuthHeaders({ env: "READWISE_TOKEN", scheme: "Token" }),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Readwise exposes two collections with separate id spaces. Both are the plain
|
|
24
|
+
* list/get/search triple, so both are declarations rather than hand-written
|
|
25
|
+
* handlers; only the books collection needs non-default tool names.
|
|
26
|
+
*/
|
|
27
|
+
const HIGHLIGHT_TOOLS: CollectionToolsConfig = {
|
|
28
|
+
prefix: "readwise",
|
|
29
|
+
get: readwiseGet,
|
|
30
|
+
list: {
|
|
31
|
+
path: HIGHLIGHTS,
|
|
32
|
+
description:
|
|
33
|
+
"List the user's Readwise highlights (`GET /api/v2/highlights/?page_size=1000`). Returns the DRF `{ count, next, previous, results: [...] }` envelope — `results` holds the highlight objects and `next` is the next-page URL (null on the last page).",
|
|
34
|
+
},
|
|
35
|
+
item: {
|
|
36
|
+
path: (id) => `/api/v2/highlights/${id}/`,
|
|
37
|
+
description:
|
|
38
|
+
"Fetch one Readwise highlight by its id (`GET /api/v2/highlights/{id}/`). Returns the highlight object directly. Throws when no match is found.",
|
|
39
|
+
},
|
|
40
|
+
search: {
|
|
41
|
+
description:
|
|
42
|
+
"Substring search across the user's Readwise highlights (first page only). Matches the query against the highlight text, the user's note, color, location_type, and tag names (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
43
|
+
rows: envelopeRows("results"),
|
|
44
|
+
filter: filterReadwiseHighlights,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const BOOK_TOOLS: CollectionToolsConfig = {
|
|
49
|
+
prefix: "readwise",
|
|
50
|
+
get: readwiseGet,
|
|
51
|
+
list: {
|
|
52
|
+
path: BOOKS,
|
|
53
|
+
tool: "books_list",
|
|
54
|
+
description:
|
|
55
|
+
"List the user's Readwise books/articles (`GET /api/v2/books/?page_size=1000`) — the parent records a highlight's `book_id` points at. Returns the same DRF `{ count, next, previous, results: [...] }` envelope as `readwise_list`; `results` holds the book objects (`id`, `title`, `author`, `category`, `source`, `num_highlights`, `highlights_url`, `source_url`, `asin`, `tags`, `document_note`).",
|
|
56
|
+
},
|
|
57
|
+
item: {
|
|
58
|
+
path: (id) => `/api/v2/books/${id}/`,
|
|
59
|
+
tool: "book_get",
|
|
60
|
+
description:
|
|
61
|
+
"Fetch one Readwise book/article by its id (`GET /api/v2/books/{id}/`). Note the id space is SEPARATE from the highlight id space — pass a highlight's `book_id`, not its `id`. Returns the book object directly. Throws when no match is found.",
|
|
62
|
+
},
|
|
63
|
+
search: {
|
|
64
|
+
tool: "books_search",
|
|
65
|
+
description:
|
|
66
|
+
"Substring search across the user's Readwise books/articles (first page only). Matches the query against the title, author, category, source, the user's document note, and tag names (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
67
|
+
rows: envelopeRows("results"),
|
|
68
|
+
filter: filterReadwiseBooks,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
73
|
+
export const READWISE_TOOL_NAMES = [
|
|
74
|
+
...collectionToolNames(HIGHLIGHT_TOOLS),
|
|
75
|
+
...collectionToolNames(BOOK_TOOLS),
|
|
76
|
+
] as const;
|
|
77
|
+
|
|
78
|
+
export function registerReadwiseTools(reg: ZodToolRegistrar): void {
|
|
79
|
+
registerCollectionTools(reg, HIGHLIGHT_TOOLS);
|
|
80
|
+
registerCollectionTools(reg, BOOK_TOOLS);
|
|
81
|
+
}
|
|
@@ -1,19 +1,21 @@
|
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* a `-`-prefixed value.
|
|
14
|
+
* The argv-injection guard every SageMaker model name passes through on its way to the
|
|
15
|
+
* `aws sagemaker` CLI. Re-exported so this connector's own test can assert the guard rejects a
|
|
16
|
+
* `-`-prefixed value.
|
|
12
17
|
*/
|
|
13
|
-
export
|
|
14
|
-
.string()
|
|
15
|
-
.min(1)
|
|
16
|
-
.refine(isSafeCliArg, { message: 'must not start with "-" or contain control characters' });
|
|
18
|
+
export { cliArg };
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Amazon SageMaker (Tier-3, metadata-only) MCP tool surface. ALL tools index
|
|
@@ -32,40 +34,19 @@ export const SAGEMAKER_TOOL_NAMES = [
|
|
|
32
34
|
|
|
33
35
|
const PAGE = "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 sagemaker <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", "sagemaker", ...args, "--output", "json"],
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
throw new Error(`aws sagemaker ${args[0] ?? ""} failed: ${err.slice(0, 400)}`);
|
|
66
|
-
}
|
|
67
|
-
return out === "" ? {} : (JSON.parse(out) as unknown);
|
|
68
|
-
}
|
|
42
|
+
const sagemakerCli = createCliJsonRunner(
|
|
43
|
+
{
|
|
44
|
+
argv: (args) => ["aws", "sagemaker", ...args, "--output", "json"],
|
|
45
|
+
label: "aws sagemaker",
|
|
46
|
+
emptyResult: {},
|
|
47
|
+
},
|
|
48
|
+
nimbusSpawn,
|
|
49
|
+
);
|
|
69
50
|
|
|
70
51
|
function modelMatches(entry: unknown, q: string): boolean {
|
|
71
52
|
if (!isRecord(entry)) {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
fieldsFromKeys,
|
|
3
3
|
makeQueryFilter,
|
|
4
4
|
type SearchMatchOptions,
|
|
5
|
-
stringField,
|
|
6
5
|
} from "../../../shared/search-filter.ts";
|
|
7
6
|
|
|
8
7
|
export type SalesforceSearchMatchOptions = SearchMatchOptions;
|
|
@@ -12,12 +11,6 @@ export type SalesforceSearchMatchOptions = SearchMatchOptions;
|
|
|
12
11
|
* `{ Id, Name, StageName, Amount, CloseDate, Type, ... }`. Match (case-insensitive
|
|
13
12
|
* substring) against the opportunity name, its stage, and its type.
|
|
14
13
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
return [stringField(row, "Name"), stringField(row, "StageName"), stringField(row, "Type")];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const filterSalesforceOpportunities = makeQueryFilter(fieldsOf);
|
|
14
|
+
export const filterSalesforceOpportunities = makeQueryFilter(
|
|
15
|
+
fieldsFromKeys(["Name", "StageName", "Type"]),
|
|
16
|
+
);
|
|
@@ -1,94 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { searchToolInputSchema } 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 API_VERSION = "v60.0";
|
|
8
|
-
const OPPORTUNITY_FIELDS =
|
|
9
|
-
"Id, Name, StageName, Amount, CloseDate, Probability, Type, IsClosed, IsWon, LastModifiedDate, CreatedDate";
|
|
10
|
-
|
|
11
|
-
function accessToken(): string {
|
|
12
|
-
const t = process.env["SALESFORCE_ACCESS_TOKEN"]?.trim();
|
|
13
|
-
if (t === undefined || t === "") {
|
|
14
|
-
throw new Error("SALESFORCE_ACCESS_TOKEN is not set");
|
|
15
|
-
}
|
|
16
|
-
return t;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function instanceUrl(): string {
|
|
20
|
-
let raw = process.env["SALESFORCE_INSTANCE_URL"]?.trim();
|
|
21
|
-
if (raw === undefined || raw === "") {
|
|
22
|
-
throw new Error("SALESFORCE_INSTANCE_URL is not set");
|
|
23
|
-
}
|
|
24
|
-
// Strip trailing slashes without a backtracking regex.
|
|
25
|
-
while (raw.endsWith("/")) {
|
|
26
|
-
raw = raw.slice(0, -1);
|
|
27
|
-
}
|
|
28
|
-
return raw;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function authHeader(): Record<string, string> {
|
|
32
|
-
return { Authorization: `Bearer ${accessToken()}`, Accept: "application/json" };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function salesforceGet(path: string): Promise<unknown> {
|
|
36
|
-
const res = await fetch(`${instanceUrl()}${path}`, { headers: authHeader() });
|
|
37
|
-
const text = await res.text();
|
|
38
|
-
if (!res.ok) {
|
|
39
|
-
throw new Error(`Salesforce ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
40
|
-
}
|
|
41
|
-
return JSON.parse(text) as unknown;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function recordsFrom(root: unknown): unknown[] {
|
|
45
|
-
const records = (root as { records?: unknown } | null)?.records;
|
|
46
|
-
return Array.isArray(records) ? records : [];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function listQueryPath(limit: number): string {
|
|
50
|
-
const soql = `SELECT ${OPPORTUNITY_FIELDS} FROM Opportunity ORDER BY LastModifiedDate DESC LIMIT ${String(limit)}`;
|
|
51
|
-
return `/services/data/${API_VERSION}/query?q=${encodeURIComponent(soql)}`;
|
|
52
|
-
}
|
|
2
|
+
import { registerSalesforceTools } from "./tools.ts";
|
|
53
3
|
|
|
54
4
|
await runReadOnlyMcpConnector("nimbus-salesforce", (reg) => {
|
|
55
|
-
reg
|
|
56
|
-
"salesforce_list",
|
|
57
|
-
"List the authenticated org's Salesforce Opportunities via the SOQL query API (GET <instance_url>/services/data/v60.0/query?q=SELECT ... FROM Opportunity ORDER BY LastModifiedDate DESC LIMIT N). Returns the { totalSize, done, records: [...], nextRecordsUrl? } envelope — `records` holds the Opportunity objects, each shaped { Id, Name, StageName, Amount, CloseDate, Probability, Type, IsClosed, IsWon, LastModifiedDate, CreatedDate }. `limit` (default 200) caps the single first-page request; when `done` is false a `nextRecordsUrl` cursor is present for the next page (the local Nimbus index has full coverage).",
|
|
58
|
-
z.object({
|
|
59
|
-
limit: z.number().int().min(1).max(2000).optional(),
|
|
60
|
-
}),
|
|
61
|
-
async (p) => {
|
|
62
|
-
return jsonResult(await salesforceGet(listQueryPath(p.limit ?? 200)));
|
|
63
|
-
},
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
reg(
|
|
67
|
-
"salesforce_get",
|
|
68
|
-
"Fetch one Salesforce Opportunity by its 15- or 18-character Id (GET <instance_url>/services/data/v60.0/sobjects/Opportunity/<id>). Returns the Opportunity object directly (NOT wrapped in `records`). Throws when no Opportunity with that id exists or is accessible.",
|
|
69
|
-
z.object({
|
|
70
|
-
id: z.string().min(1),
|
|
71
|
-
}),
|
|
72
|
-
async (p) => {
|
|
73
|
-
return jsonResult(
|
|
74
|
-
await salesforceGet(
|
|
75
|
-
`/services/data/${API_VERSION}/sobjects/Opportunity/${encodeURIComponent(p.id)}`,
|
|
76
|
-
),
|
|
77
|
-
);
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
reg(
|
|
82
|
-
"salesforce_search",
|
|
83
|
-
"**Substring search over the FIRST PAGE only** (up to the queried limit of most-recently-modified Opportunities) of the authenticated org's Salesforce Opportunities. This tool runs the SOQL query once and matches the query locally (case-insensitive) against each opportunity's name, stage, and type. **Opportunities beyond the first page are not searchable here — query the local Nimbus index instead for full coverage.** Returns a { matches: [...] } envelope.",
|
|
84
|
-
searchToolInputSchema(2000),
|
|
85
|
-
async (p) => {
|
|
86
|
-
const root = await salesforceGet(listQueryPath(2000));
|
|
87
|
-
const matches = filterSalesforceOpportunities(recordsFrom(root), {
|
|
88
|
-
query: p.query,
|
|
89
|
-
limit: p.limit,
|
|
90
|
-
});
|
|
91
|
-
return jsonResult({ matches });
|
|
92
|
-
},
|
|
93
|
-
);
|
|
5
|
+
registerSalesforceTools(reg);
|
|
94
6
|
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createJsonGetter, envAuthHeaders, requiredBaseUrl } from "../../../shared/env-json-api.ts";
|
|
3
|
+
import { searchToolInputSchema } 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 { filterSalesforceOpportunities } from "./search-filter.ts";
|
|
7
|
+
|
|
8
|
+
const API_VERSION = "v60.0";
|
|
9
|
+
const OPPORTUNITY_FIELDS =
|
|
10
|
+
"Id, Name, StageName, Amount, CloseDate, Probability, Type, IsClosed, IsWon, LastModifiedDate, CreatedDate";
|
|
11
|
+
|
|
12
|
+
const salesforceGet = createJsonGetter({
|
|
13
|
+
base: () => requiredBaseUrl("SALESFORCE_INSTANCE_URL"),
|
|
14
|
+
label: "Salesforce",
|
|
15
|
+
headers: envAuthHeaders({ env: "SALESFORCE_ACCESS_TOKEN" }),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function recordsFrom(root: unknown): unknown[] {
|
|
19
|
+
const records = (root as { records?: unknown } | null)?.records;
|
|
20
|
+
return Array.isArray(records) ? records : [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function listQueryPath(limit: number): string {
|
|
24
|
+
const soql = `SELECT ${OPPORTUNITY_FIELDS} FROM Opportunity ORDER BY LastModifiedDate DESC LIMIT ${String(limit)}`;
|
|
25
|
+
return `/services/data/${API_VERSION}/query?q=${encodeURIComponent(soql)}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
29
|
+
export const SALESFORCE_TOOL_NAMES = [
|
|
30
|
+
"salesforce_list",
|
|
31
|
+
"salesforce_get",
|
|
32
|
+
"salesforce_search",
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
export function registerSalesforceTools(reg: ZodToolRegistrar): void {
|
|
36
|
+
reg(
|
|
37
|
+
"salesforce_list",
|
|
38
|
+
"List the authenticated org's Salesforce Opportunities via the SOQL query API (GET <instance_url>/services/data/v60.0/query?q=SELECT ... FROM Opportunity ORDER BY LastModifiedDate DESC LIMIT N). Returns the { totalSize, done, records: [...], nextRecordsUrl? } envelope — `records` holds the Opportunity objects, each shaped { Id, Name, StageName, Amount, CloseDate, Probability, Type, IsClosed, IsWon, LastModifiedDate, CreatedDate }. `limit` (default 200) caps the single first-page request; when `done` is false a `nextRecordsUrl` cursor is present for the next page (the local Nimbus index has full coverage).",
|
|
39
|
+
z.object({
|
|
40
|
+
limit: z.number().int().min(1).max(2000).optional(),
|
|
41
|
+
}),
|
|
42
|
+
async (p) => {
|
|
43
|
+
return jsonResult(await salesforceGet(listQueryPath(p.limit ?? 200)));
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
reg(
|
|
48
|
+
"salesforce_get",
|
|
49
|
+
"Fetch one Salesforce Opportunity by its 15- or 18-character Id (GET <instance_url>/services/data/v60.0/sobjects/Opportunity/<id>). Returns the Opportunity object directly (NOT wrapped in `records`). Throws when no Opportunity with that id exists or is accessible.",
|
|
50
|
+
z.object({
|
|
51
|
+
id: z.string().min(1),
|
|
52
|
+
}),
|
|
53
|
+
async (p) => {
|
|
54
|
+
return jsonResult(
|
|
55
|
+
await salesforceGet(
|
|
56
|
+
`/services/data/${API_VERSION}/sobjects/Opportunity/${encodeURIComponent(p.id)}`,
|
|
57
|
+
),
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
reg(
|
|
63
|
+
"salesforce_search",
|
|
64
|
+
"**Substring search over the FIRST PAGE only** (up to the queried limit of most-recently-modified Opportunities) of the authenticated org's Salesforce Opportunities. This tool runs the SOQL query once and matches the query locally (case-insensitive) against each opportunity's name, stage, and type. **Opportunities beyond the first page are not searchable here — query the local Nimbus index instead for full coverage.** Returns a { matches: [...] } envelope.",
|
|
65
|
+
searchToolInputSchema(2000),
|
|
66
|
+
async (p) => {
|
|
67
|
+
const root = await salesforceGet(listQueryPath(2000));
|
|
68
|
+
const matches = filterSalesforceOpportunities(recordsFrom(root), {
|
|
69
|
+
query: p.query,
|
|
70
|
+
limit: p.limit,
|
|
71
|
+
});
|
|
72
|
+
return jsonResult({ matches });
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
asObjectish,
|
|
3
3
|
makeQueryFilter,
|
|
4
|
+
nestedString,
|
|
4
5
|
type SearchMatchOptions,
|
|
5
6
|
stringField,
|
|
6
7
|
} from "../../../shared/search-filter.ts";
|
|
7
8
|
|
|
8
9
|
export type SemgrepSearchMatchOptions = SearchMatchOptions;
|
|
9
10
|
|
|
10
|
-
function nestedString(row: Record<string, unknown>, parent: string, key: string): string {
|
|
11
|
-
const p = row[parent];
|
|
12
|
-
if (p === null || typeof p !== "object") {
|
|
13
|
-
return "";
|
|
14
|
-
}
|
|
15
|
-
return stringField(p as Record<string, unknown>, key);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
11
|
function fieldsOf(item: unknown): readonly string[] | null {
|
|
19
12
|
const row = asObjectish(item);
|
|
20
13
|
if (row === undefined) {
|
|
@@ -23,8 +16,8 @@ function fieldsOf(item: unknown): readonly string[] | null {
|
|
|
23
16
|
return [
|
|
24
17
|
stringField(row, "rule_name"),
|
|
25
18
|
stringField(row, "rule_message"),
|
|
26
|
-
nestedString(row, "location", "file_path"),
|
|
27
|
-
nestedString(row, "repository", "name"),
|
|
19
|
+
nestedString(row, ["location", "file_path"]),
|
|
20
|
+
nestedString(row, ["repository", "name"]),
|
|
28
21
|
];
|
|
29
22
|
}
|
|
30
23
|
|
|
@@ -1,108 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { matchesResult, searchToolInputSchema } 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 SEMGREP_API = "https://semgrep.dev/api/v1";
|
|
8
|
-
const SEVERITIES = ["critical", "high", "medium", "low", "info"] as const;
|
|
9
|
-
const STATUSES = ["open", "ignored", "fixed", "removed"] as const;
|
|
10
|
-
|
|
11
|
-
function deploymentSlug(): string | undefined {
|
|
12
|
-
const v = process.env["SEMGREP_DEPLOYMENT_SLUG"]?.trim();
|
|
13
|
-
return v === undefined || v === "" ? undefined : v;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function authHeader(): Record<string, string> {
|
|
17
|
-
const t = process.env["SEMGREP_TOKEN"]?.trim();
|
|
18
|
-
if (t === undefined || t === "") {
|
|
19
|
-
throw new Error("SEMGREP_TOKEN is not set");
|
|
20
|
-
}
|
|
21
|
-
return { Authorization: `Bearer ${t}`, Accept: "application/json" };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function semgrepGet(path: string): Promise<unknown> {
|
|
25
|
-
const res = await fetch(`${SEMGREP_API}${path}`, { headers: authHeader() });
|
|
26
|
-
const text = await res.text();
|
|
27
|
-
if (!res.ok) {
|
|
28
|
-
throw new Error(`Semgrep ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
29
|
-
}
|
|
30
|
-
return JSON.parse(text) as unknown;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function requireSlug(): string {
|
|
34
|
-
const slug = deploymentSlug();
|
|
35
|
-
if (slug === undefined) {
|
|
36
|
-
throw new Error("SEMGREP_DEPLOYMENT_SLUG is not set");
|
|
37
|
-
}
|
|
38
|
-
return slug;
|
|
39
|
-
}
|
|
2
|
+
import { registerSemgrepTools } from "./tools.ts";
|
|
40
3
|
|
|
41
4
|
await runReadOnlyMcpConnector("nimbus-semgrep", (reg) => {
|
|
42
|
-
reg
|
|
43
|
-
"semgrep_list",
|
|
44
|
-
"List Semgrep deployments, or open findings for the configured deployment. When called without filters, returns the user's deployments (`/deployments`). Provide a `severity` or `status` filter to query findings under the deployment slug from `SEMGREP_DEPLOYMENT_SLUG` env.",
|
|
45
|
-
z.object({
|
|
46
|
-
severity: z.array(z.enum(SEVERITIES)).min(1).optional(),
|
|
47
|
-
status: z.array(z.enum(STATUSES)).min(1).optional(),
|
|
48
|
-
repository: z.string().min(1).optional(),
|
|
49
|
-
limit: z.number().int().min(1).max(500).optional(),
|
|
50
|
-
}),
|
|
51
|
-
async (p) => {
|
|
52
|
-
if (p.severity === undefined && p.status === undefined && p.repository === undefined) {
|
|
53
|
-
return jsonResult(await semgrepGet("/deployments"));
|
|
54
|
-
}
|
|
55
|
-
const slug = requireSlug();
|
|
56
|
-
const search = new URLSearchParams({
|
|
57
|
-
page_size: String(p.limit ?? 100),
|
|
58
|
-
});
|
|
59
|
-
if (p.severity !== undefined && p.severity.length > 0) {
|
|
60
|
-
search.set("severities", p.severity.join(","));
|
|
61
|
-
}
|
|
62
|
-
if (p.status !== undefined && p.status.length > 0) {
|
|
63
|
-
search.set("statuses", p.status.join(","));
|
|
64
|
-
}
|
|
65
|
-
if (p.repository !== undefined) {
|
|
66
|
-
search.set("repos", p.repository);
|
|
67
|
-
}
|
|
68
|
-
const path = `/deployments/${encodeURIComponent(slug)}/findings?${search.toString()}`;
|
|
69
|
-
return jsonResult(await semgrepGet(path));
|
|
70
|
-
},
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
reg(
|
|
74
|
-
"semgrep_get",
|
|
75
|
-
"Fetch one Semgrep finding by id from the configured deployment. Returns the matching finding envelope; throws when no match is found.",
|
|
76
|
-
z.object({
|
|
77
|
-
findingId: z.string().min(1),
|
|
78
|
-
}),
|
|
79
|
-
async (p) => {
|
|
80
|
-
const slug = requireSlug();
|
|
81
|
-
const search = new URLSearchParams({ ids: p.findingId, page_size: "1" });
|
|
82
|
-
const path = `/deployments/${encodeURIComponent(slug)}/findings?${search.toString()}`;
|
|
83
|
-
const root = await semgrepGet(path);
|
|
84
|
-
const findings = (root as { findings?: unknown[] } | null)?.findings;
|
|
85
|
-
if (!Array.isArray(findings) || findings.length === 0) {
|
|
86
|
-
throw new Error(`Semgrep: finding ${p.findingId} not found`);
|
|
87
|
-
}
|
|
88
|
-
return jsonResult(findings[0]);
|
|
89
|
-
},
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
reg(
|
|
93
|
-
"semgrep_search",
|
|
94
|
-
"Substring search across the deployment's open findings. Matches the query against `rule_name`, `rule_message`, `location.file_path`, and `repository.name` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
95
|
-
searchToolInputSchema(200),
|
|
96
|
-
async (p) => {
|
|
97
|
-
const slug = requireSlug();
|
|
98
|
-
const search = new URLSearchParams({
|
|
99
|
-
statuses: "open",
|
|
100
|
-
page_size: "500",
|
|
101
|
-
});
|
|
102
|
-
const path = `/deployments/${encodeURIComponent(slug)}/findings?${search.toString()}`;
|
|
103
|
-
const root = await semgrepGet(path);
|
|
104
|
-
const findings = (root as { findings?: unknown[] } | null)?.findings;
|
|
105
|
-
return matchesResult(findings, filterSemgrepFindings, p);
|
|
106
|
-
},
|
|
107
|
-
);
|
|
5
|
+
registerSemgrepTools(reg);
|
|
108
6
|
});
|