@nimbus-dev/connectors 0.2.1 → 0.2.3
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 +19 -7
- 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 +26 -43
- package/connectors/aws/src/server.ts +2 -183
- package/connectors/aws/src/tools.ts +199 -0
- package/connectors/azure/src/server.ts +2 -131
- package/connectors/azure/src/tools.ts +134 -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 +19 -35
- package/connectors/cloudwatch/src/tools.ts +20 -38
- 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 +153 -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 +160 -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 +21 -40
- 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 +601 -34
- package/shared/cli-json-kit.ts +191 -0
- package/shared/collection-tool-kit.ts +143 -0
- package/shared/connector-mode.ts +3 -2
- package/shared/consent-kit.ts +81 -26
- 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/nimbus-spawn.ts +84 -27
- package/shared/rest-tool-kit.ts +77 -7
- package/shared/run-cli-json.ts +38 -16
- package/shared/safe-cli-arg.ts +139 -17
- package/shared/search-filter.ts +58 -18
- package/shared/walk-files.ts +118 -0
- package/shared/windows-batch-args.ts +273 -0
- package/shared/write-budget.ts +100 -0
- package/standalone/src/launcher.ts +19 -7
|
@@ -1,119 +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
|
-
function trimTrailingSlash(s: string): string {
|
|
8
|
-
return s.endsWith("/") ? s.slice(0, -1) : s;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function apiBase(): string {
|
|
12
|
-
const v = process.env["SUPERSET_URL"]?.trim();
|
|
13
|
-
if (v === undefined || v === "") {
|
|
14
|
-
throw new Error("SUPERSET_URL is not set");
|
|
15
|
-
}
|
|
16
|
-
return trimTrailingSlash(v);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function requiredEnv(name: string): string {
|
|
20
|
-
const v = process.env[name]?.trim();
|
|
21
|
-
if (v === undefined || v === "") {
|
|
22
|
-
throw new Error(`${name} is not set`);
|
|
23
|
-
}
|
|
24
|
-
return v;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let cachedToken: string | null = null;
|
|
28
|
-
|
|
29
|
-
async function login(): Promise<string> {
|
|
30
|
-
if (cachedToken !== null) {
|
|
31
|
-
return cachedToken;
|
|
32
|
-
}
|
|
33
|
-
const username = requiredEnv("SUPERSET_USERNAME");
|
|
34
|
-
const password = requiredEnv("SUPERSET_PASSWORD");
|
|
35
|
-
const res = await fetch(`${apiBase()}/api/v1/security/login`, {
|
|
36
|
-
method: "POST",
|
|
37
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
38
|
-
body: JSON.stringify({ username, password, provider: "db", refresh: true }),
|
|
39
|
-
});
|
|
40
|
-
const text = await res.text();
|
|
41
|
-
if (!res.ok) {
|
|
42
|
-
throw new Error(`Superset login ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
43
|
-
}
|
|
44
|
-
let parsed: { access_token?: unknown };
|
|
45
|
-
try {
|
|
46
|
-
parsed = JSON.parse(text) as { access_token?: unknown };
|
|
47
|
-
} catch {
|
|
48
|
-
throw new Error("Superset login: invalid JSON response");
|
|
49
|
-
}
|
|
50
|
-
if (typeof parsed.access_token !== "string" || parsed.access_token === "") {
|
|
51
|
-
throw new Error("Superset login: no access_token in response");
|
|
52
|
-
}
|
|
53
|
-
cachedToken = parsed.access_token;
|
|
54
|
-
return cachedToken;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function supersetGet(path: string): Promise<unknown> {
|
|
58
|
-
const token = await login();
|
|
59
|
-
const res = await fetch(`${apiBase()}${path}`, {
|
|
60
|
-
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
61
|
-
});
|
|
62
|
-
const text = await res.text();
|
|
63
|
-
if (!res.ok) {
|
|
64
|
-
throw new Error(`Superset ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
65
|
-
}
|
|
66
|
-
return JSON.parse(text) as unknown;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function dashboardsFrom(root: unknown): unknown[] {
|
|
70
|
-
const result = (root as { result?: unknown } | null)?.result;
|
|
71
|
-
if (Array.isArray(result)) {
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
return Array.isArray(root) ? root : [];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function dashboardListPath(page: number, pageSize: number): string {
|
|
78
|
-
const q = encodeURIComponent(`(page:${String(page)},page_size:${String(pageSize)})`);
|
|
79
|
-
return `/api/v1/dashboard/?q=${q}`;
|
|
80
|
-
}
|
|
2
|
+
import { registerSupersetTools } from "./tools.ts";
|
|
81
3
|
|
|
82
4
|
await runReadOnlyMcpConnector("nimbus-superset", (reg) => {
|
|
83
|
-
reg
|
|
84
|
-
"superset_list",
|
|
85
|
-
"List Apache Superset dashboards (`GET /api/v1/dashboard/`). `limit` (default 100) caps the page_size of the single first-page request. Returns the `.result` array from the Superset envelope.",
|
|
86
|
-
z.object({
|
|
87
|
-
limit: z.number().int().min(1).max(500).optional(),
|
|
88
|
-
}),
|
|
89
|
-
async (p) => {
|
|
90
|
-
const root = await supersetGet(dashboardListPath(0, p.limit ?? 100));
|
|
91
|
-
return jsonResult(dashboardsFrom(root));
|
|
92
|
-
},
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
reg(
|
|
96
|
-
"superset_get",
|
|
97
|
-
"Fetch one Apache Superset dashboard by numeric id (`/api/v1/dashboard/{id}`). Throws when no dashboard with that id exists.",
|
|
98
|
-
z.object({
|
|
99
|
-
id: z.number().int(),
|
|
100
|
-
}),
|
|
101
|
-
async (p) => {
|
|
102
|
-
return jsonResult(await supersetGet(`/api/v1/dashboard/${encodeURIComponent(String(p.id))}`));
|
|
103
|
-
},
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
reg(
|
|
107
|
-
"superset_search",
|
|
108
|
-
"Substring search across Apache Superset dashboards. Matches the query (case-insensitive) against dashboard title and slug. Returns a `{ matches: [...] }` envelope.",
|
|
109
|
-
searchToolInputSchema(200),
|
|
110
|
-
async (p) => {
|
|
111
|
-
const root = await supersetGet(dashboardListPath(0, 500));
|
|
112
|
-
const matches = filterSupersetDashboards(dashboardsFrom(root), {
|
|
113
|
-
query: p.query,
|
|
114
|
-
limit: p.limit,
|
|
115
|
-
});
|
|
116
|
-
return jsonResult({ matches });
|
|
117
|
-
},
|
|
118
|
-
);
|
|
5
|
+
registerSupersetTools(reg);
|
|
119
6
|
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createAccessTokenCache } from "../../../shared/access-token-cache.ts";
|
|
3
|
+
import { requiredBaseUrl, requiredEnv } from "../../../shared/env-json-api.ts";
|
|
4
|
+
import { searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
|
|
5
|
+
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
6
|
+
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
7
|
+
import { filterSupersetDashboards } from "./search-filter.ts";
|
|
8
|
+
|
|
9
|
+
function apiBase(): string {
|
|
10
|
+
return requiredBaseUrl("SUPERSET_URL");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Superset issues its own JWT from a username/password POST, not OAuth. */
|
|
14
|
+
const login = createAccessTokenCache({
|
|
15
|
+
label: "Superset login",
|
|
16
|
+
exchange: async () => {
|
|
17
|
+
const username = requiredEnv("SUPERSET_USERNAME");
|
|
18
|
+
const password = requiredEnv("SUPERSET_PASSWORD");
|
|
19
|
+
const res = await fetch(`${apiBase()}/api/v1/security/login`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
22
|
+
body: JSON.stringify({ username, password, provider: "db", refresh: true }),
|
|
23
|
+
});
|
|
24
|
+
return { ok: res.ok, status: res.status, text: await res.text() };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** Forget the login token. Tests only: the cache otherwise outlives the test that filled it. */
|
|
29
|
+
export function __resetSupersetLoginForTests(): void {
|
|
30
|
+
login.clear();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function supersetGet(path: string): Promise<unknown> {
|
|
34
|
+
const token = await login();
|
|
35
|
+
const res = await fetch(`${apiBase()}${path}`, {
|
|
36
|
+
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
37
|
+
});
|
|
38
|
+
const text = await res.text();
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
throw new Error(`Superset ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
41
|
+
}
|
|
42
|
+
return JSON.parse(text) as unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function dashboardsFrom(root: unknown): unknown[] {
|
|
46
|
+
const result = (root as { result?: unknown } | null)?.result;
|
|
47
|
+
if (Array.isArray(result)) {
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
return Array.isArray(root) ? root : [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function dashboardListPath(page: number, pageSize: number): string {
|
|
54
|
+
const q = encodeURIComponent(`(page:${String(page)},page_size:${String(pageSize)})`);
|
|
55
|
+
return `/api/v1/dashboard/?q=${q}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
59
|
+
export const SUPERSET_TOOL_NAMES = ["superset_get", "superset_list", "superset_search"] as const;
|
|
60
|
+
|
|
61
|
+
export function registerSupersetTools(reg: ZodToolRegistrar): void {
|
|
62
|
+
reg(
|
|
63
|
+
"superset_list",
|
|
64
|
+
"List Apache Superset dashboards (`GET /api/v1/dashboard/`). `limit` (default 100) caps the page_size of the single first-page request. Returns the `.result` array from the Superset envelope.",
|
|
65
|
+
z.object({
|
|
66
|
+
limit: z.number().int().min(1).max(500).optional(),
|
|
67
|
+
}),
|
|
68
|
+
async (p) => {
|
|
69
|
+
const root = await supersetGet(dashboardListPath(0, p.limit ?? 100));
|
|
70
|
+
return jsonResult(dashboardsFrom(root));
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
reg(
|
|
75
|
+
"superset_get",
|
|
76
|
+
"Fetch one Apache Superset dashboard by numeric id (`/api/v1/dashboard/{id}`). Throws when no dashboard with that id exists.",
|
|
77
|
+
z.object({
|
|
78
|
+
id: z.number().int(),
|
|
79
|
+
}),
|
|
80
|
+
async (p) => {
|
|
81
|
+
return jsonResult(await supersetGet(`/api/v1/dashboard/${encodeURIComponent(String(p.id))}`));
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
reg(
|
|
86
|
+
"superset_search",
|
|
87
|
+
"Substring search across Apache Superset dashboards. Matches the query (case-insensitive) against dashboard title and slug. Returns a `{ matches: [...] }` envelope.",
|
|
88
|
+
searchToolInputSchema(200),
|
|
89
|
+
async (p) => {
|
|
90
|
+
const root = await supersetGet(dashboardListPath(0, 500));
|
|
91
|
+
const matches = filterSupersetDashboards(dashboardsFrom(root), {
|
|
92
|
+
query: p.query,
|
|
93
|
+
limit: p.limit,
|
|
94
|
+
});
|
|
95
|
+
return jsonResult({ matches });
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
asRecord,
|
|
3
2
|
makeQueryFilter,
|
|
3
|
+
recordFieldsFromKeys,
|
|
4
4
|
type SearchMatchOptions,
|
|
5
5
|
} from "../../../shared/search-filter.ts";
|
|
6
6
|
|
|
7
7
|
export type TableauSearchMatchOptions = SearchMatchOptions;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
const v = row[key];
|
|
11
|
-
return typeof v === "string" ? v : "";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function fieldsOf(item: unknown): readonly string[] | null {
|
|
15
|
-
const row = asRecord(item);
|
|
16
|
-
if (row === undefined) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
return [stringAt(row, "name"), stringAt(row, "luid")];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const filterTableauViews = makeQueryFilter(fieldsOf);
|
|
9
|
+
export const filterTableauViews = makeQueryFilter(recordFieldsFromKeys(["name", "luid"]));
|
|
@@ -1,39 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type ConsentServer, createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
3
|
+
import { cursorListInputSchema } from "../../../shared/cursor-list-tool.ts";
|
|
4
|
+
import { requiredBaseUrl, requiredEnv } from "../../../shared/env-json-api.ts";
|
|
3
5
|
import { searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
|
|
4
6
|
import { fetchWithTimeout, mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
5
7
|
import {
|
|
6
8
|
runReadOnlyMcpConnector,
|
|
7
9
|
type ZodToolRegistrar,
|
|
8
10
|
} from "../../../shared/run-read-only-mcp-connector.ts";
|
|
11
|
+
import { asRecord } from "../../../shared/search-filter.ts";
|
|
9
12
|
import { filterTableauViews } from "./search-filter.ts";
|
|
10
13
|
|
|
11
|
-
function trimTrailingSlash(s: string): string {
|
|
12
|
-
return s.endsWith("/") ? s.slice(0, -1) : s;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
14
|
function apiBase(): string {
|
|
16
|
-
|
|
17
|
-
if (v === undefined || v === "") {
|
|
18
|
-
throw new Error("TABLEAU_URL is not set");
|
|
19
|
-
}
|
|
20
|
-
return trimTrailingSlash(v);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function patName(): string {
|
|
24
|
-
const v = process.env["TABLEAU_PAT_NAME"]?.trim();
|
|
25
|
-
if (v === undefined || v === "") {
|
|
26
|
-
throw new Error("TABLEAU_PAT_NAME is not set");
|
|
27
|
-
}
|
|
28
|
-
return v;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function patSecret(): string {
|
|
32
|
-
const v = process.env["TABLEAU_PAT_SECRET"]?.trim();
|
|
33
|
-
if (v === undefined || v === "") {
|
|
34
|
-
throw new Error("TABLEAU_PAT_SECRET is not set");
|
|
35
|
-
}
|
|
36
|
-
return v;
|
|
15
|
+
return requiredBaseUrl("TABLEAU_URL");
|
|
37
16
|
}
|
|
38
17
|
|
|
39
18
|
interface SigninResult {
|
|
@@ -48,8 +27,8 @@ async function tableauSignin(): Promise<SigninResult> {
|
|
|
48
27
|
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
49
28
|
body: JSON.stringify({
|
|
50
29
|
credentials: {
|
|
51
|
-
personalAccessTokenName:
|
|
52
|
-
personalAccessTokenSecret:
|
|
30
|
+
personalAccessTokenName: requiredEnv("TABLEAU_PAT_NAME"),
|
|
31
|
+
personalAccessTokenSecret: requiredEnv("TABLEAU_PAT_SECRET"),
|
|
53
32
|
site: { contentUrl: "" },
|
|
54
33
|
},
|
|
55
34
|
}),
|
|
@@ -58,22 +37,9 @@ async function tableauSignin(): Promise<SigninResult> {
|
|
|
58
37
|
if (!res.ok) {
|
|
59
38
|
throw new Error(`Tableau signin ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
60
39
|
}
|
|
61
|
-
const
|
|
62
|
-
const root =
|
|
63
|
-
parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
|
|
64
|
-
? (parsed as Record<string, unknown>)
|
|
65
|
-
: null;
|
|
66
|
-
const creds = root?.["credentials"];
|
|
67
|
-
const credsObj =
|
|
68
|
-
creds !== null && typeof creds === "object" && !Array.isArray(creds)
|
|
69
|
-
? (creds as Record<string, unknown>)
|
|
70
|
-
: null;
|
|
40
|
+
const credsObj = asRecord(asRecord(JSON.parse(text) as unknown)?.["credentials"]);
|
|
71
41
|
const token = typeof credsObj?.["token"] === "string" ? credsObj["token"] : null;
|
|
72
|
-
const
|
|
73
|
-
const siteObj =
|
|
74
|
-
site !== null && typeof site === "object" && !Array.isArray(site)
|
|
75
|
-
? (site as Record<string, unknown>)
|
|
76
|
-
: null;
|
|
42
|
+
const siteObj = asRecord(credsObj?.["site"]);
|
|
77
43
|
const siteId = typeof siteObj?.["id"] === "string" ? siteObj["id"] : null;
|
|
78
44
|
if (token === null || siteId === null) {
|
|
79
45
|
throw new Error("Tableau signin response missing credentials.token or credentials.site.id");
|
|
@@ -101,21 +67,9 @@ async function listViews(
|
|
|
101
67
|
if (!res.ok) {
|
|
102
68
|
throw new Error(`Tableau views ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
103
69
|
}
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
? (parsed as Record<string, unknown>)
|
|
108
|
-
: null;
|
|
109
|
-
const views = root?.["views"];
|
|
110
|
-
const viewsObj =
|
|
111
|
-
views !== null && typeof views === "object" && !Array.isArray(views)
|
|
112
|
-
? (views as Record<string, unknown>)
|
|
113
|
-
: null;
|
|
114
|
-
const pagination = root?.["pagination"];
|
|
115
|
-
const paginationObj =
|
|
116
|
-
pagination !== null && typeof pagination === "object" && !Array.isArray(pagination)
|
|
117
|
-
? (pagination as Record<string, unknown>)
|
|
118
|
-
: null;
|
|
70
|
+
const root = asRecord(JSON.parse(text) as unknown);
|
|
71
|
+
const viewsObj = asRecord(root?.["views"]);
|
|
72
|
+
const paginationObj = asRecord(root?.["pagination"]);
|
|
119
73
|
return {
|
|
120
74
|
views: Array.isArray(viewsObj?.["view"]) ? (viewsObj["view"] as unknown[]) : [],
|
|
121
75
|
totalAvailable: Number(paginationObj?.["totalAvailable"]) || 0,
|
|
@@ -162,10 +116,7 @@ export function registerTableauTools(reg: ZodToolRegistrar, server: unknown): vo
|
|
|
162
116
|
reg(
|
|
163
117
|
"tableau_list",
|
|
164
118
|
"List Tableau views/dashboards (`GET /api/3.4/sites/{siteId}/views`). Requires a PAT sign-in first. Paginated (1-based): `cursor` (page number) + `limit` (default 200, max 500) → `{ items, nextCursor }`.",
|
|
165
|
-
|
|
166
|
-
cursor: z.string().nullable().optional(),
|
|
167
|
-
limit: z.number().int().min(1).max(500).optional(),
|
|
168
|
-
}),
|
|
119
|
+
cursorListInputSchema(),
|
|
169
120
|
async (p) => {
|
|
170
121
|
const pageSize = p.limit ?? 200;
|
|
171
122
|
// 1-based: null / "" / "0" / non-numeric / negative / fractional all resolve to page 1
|
|
@@ -189,10 +140,7 @@ export function registerTableauTools(reg: ZodToolRegistrar, server: unknown): vo
|
|
|
189
140
|
const { token, siteId } = await tableauSignin();
|
|
190
141
|
const { views } = await listViews(token, siteId);
|
|
191
142
|
const found = views.find((v) => {
|
|
192
|
-
const obj =
|
|
193
|
-
v !== null && typeof v === "object" && !Array.isArray(v)
|
|
194
|
-
? (v as Record<string, unknown>)
|
|
195
|
-
: null;
|
|
143
|
+
const obj = asRecord(v);
|
|
196
144
|
return obj?.["luid"] === p.id || obj?.["id"] === p.id;
|
|
197
145
|
});
|
|
198
146
|
if (found === undefined) {
|
|
@@ -1,257 +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
|
-
|
|
5
|
-
import {
|
|
6
|
-
fetchBearerAuthorizedJson,
|
|
7
|
-
resolveUrlWithBase,
|
|
8
|
-
} from "../../../shared/fetch-bearer-json.ts";
|
|
9
|
-
import {
|
|
10
|
-
createRegisterSimpleTool,
|
|
11
|
-
createZodToolRegistrar,
|
|
12
|
-
type McpListResult,
|
|
13
|
-
mcpJsonResultIfOk,
|
|
14
|
-
requireProcessEnv,
|
|
15
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
16
|
-
import { teamsBotSendActivity } from "./bot-send.ts";
|
|
17
|
-
|
|
18
|
-
const GRAPH = "https://graph.microsoft.com/v1.0";
|
|
19
|
-
|
|
20
|
-
async function graphRequest(
|
|
21
|
-
token: string,
|
|
22
|
-
pathOrUrl: string,
|
|
23
|
-
init?: RequestInit,
|
|
24
|
-
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
25
|
-
const url = resolveUrlWithBase(GRAPH, pathOrUrl);
|
|
26
|
-
return fetchBearerAuthorizedJson(url, token, init);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function teamsPagedGraph(
|
|
30
|
-
token: string,
|
|
31
|
-
nextLink: string | undefined,
|
|
32
|
-
initialPath: string,
|
|
33
|
-
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
34
|
-
const path = nextLink !== undefined && nextLink !== "" ? nextLink : initialPath;
|
|
35
|
-
return graphRequest(token, path);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function graphListResult(r: {
|
|
39
|
-
ok: boolean;
|
|
40
|
-
status: number;
|
|
41
|
-
json: unknown;
|
|
42
|
-
text: string;
|
|
43
|
-
}): McpListResult {
|
|
44
|
-
return mcpJsonResultIfOk("Graph", r, 200);
|
|
45
|
-
}
|
|
3
|
+
import { registerTeamsTools } from "./tools.ts";
|
|
46
4
|
|
|
47
5
|
const server = new McpServer({ name: "nimbus-teams", version: "0.1.0" });
|
|
48
6
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
52
|
-
|
|
53
|
-
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Every MUTATING teams tool goes through here. Outside the gateway this adds the
|
|
57
|
-
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
58
|
-
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
59
|
-
*/
|
|
60
|
-
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
61
|
-
connector: "teams",
|
|
62
|
-
scopeEnv: "NIMBUS_MCP_TEAMS_WRITE_SCOPE",
|
|
63
|
-
scopeKinds: ["channel", "chat"],
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const teamsTeamListSchema = z.object({
|
|
67
|
-
top: z.number().int().min(1).max(100).optional(),
|
|
68
|
-
nextLink: z.url().optional(),
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
reg(
|
|
72
|
-
"teams_team_list",
|
|
73
|
-
"List Microsoft Teams the signed-in user has joined (pagination via nextLink).",
|
|
74
|
-
teamsTeamListSchema,
|
|
75
|
-
async (parsed) => {
|
|
76
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
77
|
-
const initial = `/me/joinedTeams?$top=${String(parsed.top ?? 50)}`;
|
|
78
|
-
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
79
|
-
return graphListResult(r);
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const teamsChannelListSchema = z.object({
|
|
84
|
-
teamId: z.string().min(1),
|
|
85
|
-
top: z.number().int().min(1).max(100).optional(),
|
|
86
|
-
nextLink: z.url().optional(),
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
reg(
|
|
90
|
-
"teams_channel_list",
|
|
91
|
-
"List channels in a team (standard + private the app can read).",
|
|
92
|
-
teamsChannelListSchema,
|
|
93
|
-
async (parsed) => {
|
|
94
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
95
|
-
const tid = encodeURIComponent(parsed.teamId);
|
|
96
|
-
const initial = `/teams/${tid}/channels?$top=${String(parsed.top ?? 50)}`;
|
|
97
|
-
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
98
|
-
return graphListResult(r);
|
|
99
|
-
},
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const teamsChannelMessagesSchema = z.object({
|
|
103
|
-
teamId: z.string().min(1),
|
|
104
|
-
channelId: z.string().min(1),
|
|
105
|
-
top: z.number().int().min(1).max(50).optional(),
|
|
106
|
-
nextLink: z.url().optional(),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
reg(
|
|
110
|
-
"teams_channel_messages",
|
|
111
|
-
"List recent messages in a team channel (not delta; for interactive reads).",
|
|
112
|
-
teamsChannelMessagesSchema,
|
|
113
|
-
async (parsed) => {
|
|
114
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
115
|
-
const tid = encodeURIComponent(parsed.teamId);
|
|
116
|
-
const cid = encodeURIComponent(parsed.channelId);
|
|
117
|
-
const initial = `/teams/${tid}/channels/${cid}/messages?$top=${String(parsed.top ?? 25)}`;
|
|
118
|
-
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
119
|
-
return graphListResult(r);
|
|
120
|
-
},
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const teamsChatListSchema = z.object({
|
|
124
|
-
top: z.number().int().min(1).max(50).optional(),
|
|
125
|
-
nextLink: z.url().optional(),
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
reg(
|
|
129
|
-
"teams_chat_list",
|
|
130
|
-
"List 1:1 and group chats for the signed-in user.",
|
|
131
|
-
teamsChatListSchema,
|
|
132
|
-
async (parsed) => {
|
|
133
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
134
|
-
const initial = `/me/chats?$top=${String(parsed.top ?? 25)}`;
|
|
135
|
-
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
136
|
-
return graphListResult(r);
|
|
137
|
-
},
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
const teamsChatMessagesSchema = z.object({
|
|
141
|
-
chatId: z.string().min(1),
|
|
142
|
-
top: z.number().int().min(1).max(50).optional(),
|
|
143
|
-
nextLink: z.url().optional(),
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
reg("teams_chat_messages", "List messages in a chat.", teamsChatMessagesSchema, async (parsed) => {
|
|
147
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
148
|
-
const id = encodeURIComponent(parsed.chatId);
|
|
149
|
-
const initial = `/chats/${id}/messages?$top=${String(parsed.top ?? 25)}`;
|
|
150
|
-
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
151
|
-
return graphListResult(r);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const teamsMessagePostSchema = z.object({
|
|
155
|
-
teamId: z.string().min(1),
|
|
156
|
-
channelId: z.string().min(1),
|
|
157
|
-
body: z.string().min(1),
|
|
158
|
-
contentType: z.enum(["text", "html"]).optional(),
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
registerWriteTool(
|
|
162
|
-
"teams_message_post",
|
|
163
|
-
{
|
|
164
|
-
mutates: "teams.message.post",
|
|
165
|
-
recoverable: true,
|
|
166
|
-
scopeTargetOf: (p) => ({ kind: "channel", value: `${p.teamId}/${p.channelId}` }),
|
|
167
|
-
},
|
|
168
|
-
"Post a message to a team channel.",
|
|
169
|
-
teamsMessagePostSchema,
|
|
170
|
-
async (parsed) => {
|
|
171
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
172
|
-
const tid = encodeURIComponent(parsed.teamId);
|
|
173
|
-
const cid = encodeURIComponent(parsed.channelId);
|
|
174
|
-
const ct = parsed.contentType ?? "text";
|
|
175
|
-
const r = await graphRequest(token, `/teams/${tid}/channels/${cid}/messages`, {
|
|
176
|
-
method: "POST",
|
|
177
|
-
headers: { "Content-Type": "application/json" },
|
|
178
|
-
body: JSON.stringify({
|
|
179
|
-
body: {
|
|
180
|
-
contentType: ct === "html" ? "html" : "text",
|
|
181
|
-
content: parsed.body,
|
|
182
|
-
},
|
|
183
|
-
}),
|
|
184
|
-
});
|
|
185
|
-
return graphListResult(r);
|
|
186
|
-
},
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
const teamsMessagePostChatSchema = z.object({
|
|
190
|
-
chatId: z.string().min(1),
|
|
191
|
-
body: z.string().min(1),
|
|
192
|
-
contentType: z.enum(["text", "html"]).optional(),
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
reg(
|
|
196
|
-
"teams_message_post_chat",
|
|
197
|
-
"Post a message to a chat (requires HITL teams.message.postChat).",
|
|
198
|
-
teamsMessagePostChatSchema,
|
|
199
|
-
async (parsed) => {
|
|
200
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
201
|
-
const id = encodeURIComponent(parsed.chatId);
|
|
202
|
-
const ct = parsed.contentType ?? "text";
|
|
203
|
-
const r = await graphRequest(token, `/chats/${id}/messages`, {
|
|
204
|
-
method: "POST",
|
|
205
|
-
headers: { "Content-Type": "application/json" },
|
|
206
|
-
body: JSON.stringify({
|
|
207
|
-
body: {
|
|
208
|
-
contentType: ct === "html" ? "html" : "text",
|
|
209
|
-
content: parsed.body,
|
|
210
|
-
},
|
|
211
|
-
}),
|
|
212
|
-
});
|
|
213
|
-
return graphListResult(r);
|
|
214
|
-
},
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// --- ChatOps operational tools (Slice 5) -------------------------------------------------------
|
|
218
|
-
// `teams_chat_post` uses bot app credentials (Bot Framework), distinct from the user Graph token.
|
|
219
|
-
// Only `chatops/reply-dispatcher.ts` + `chatops/transport/` reference `teams_chat_post` (static D17).
|
|
220
|
-
|
|
221
|
-
const teamsUserInfoSchema = z.object({ userId: z.string().min(1) });
|
|
222
|
-
reg(
|
|
223
|
-
"teams_user_info",
|
|
224
|
-
"Fetch a Teams/AAD user (incl. mail/userPrincipalName) by id (ChatOps identity mapping).",
|
|
225
|
-
teamsUserInfoSchema,
|
|
226
|
-
async (parsed) => {
|
|
227
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
228
|
-
const r = await teamsPagedGraph(
|
|
229
|
-
token,
|
|
230
|
-
undefined,
|
|
231
|
-
`/users/${encodeURIComponent(parsed.userId)}`,
|
|
232
|
-
);
|
|
233
|
-
return graphListResult(r);
|
|
234
|
-
},
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
const teamsChatPostSchema = z.object({
|
|
238
|
-
conversationId: z.string().min(1),
|
|
239
|
-
text: z.string().min(1),
|
|
240
|
-
});
|
|
241
|
-
registerWriteTool(
|
|
242
|
-
"teams_chat_post",
|
|
243
|
-
{
|
|
244
|
-
mutates: "teams.chat.post",
|
|
245
|
-
recoverable: true,
|
|
246
|
-
scopeTargetOf: (p) => ({ kind: "chat", value: p.conversationId }),
|
|
247
|
-
},
|
|
248
|
-
"Post an operational bot message to a Teams conversation (ChatOps reply surface; bot app creds).",
|
|
249
|
-
teamsChatPostSchema,
|
|
250
|
-
async (parsed) => {
|
|
251
|
-
const r = await teamsBotSendActivity(parsed.conversationId, parsed.text);
|
|
252
|
-
return graphListResult(r);
|
|
253
|
-
},
|
|
254
|
-
);
|
|
7
|
+
registerTeamsTools(server);
|
|
255
8
|
|
|
256
9
|
const transport = new StdioServerTransport();
|
|
257
10
|
await server.connect(transport);
|