@transcend-io/mcp-server-discovery 0.1.0

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 ADDED
@@ -0,0 +1,88 @@
1
+ # @transcend-io/mcp-server-discovery
2
+
3
+ > **Alpha** — this package is under active development and has not yet been published to npm. APIs may change without notice.
4
+
5
+ Transcend MCP Server for data discovery. Provides tools for scanning, classifying, and extracting entities from data.
6
+
7
+ Requires **Node.js ≥ 22.12** (see `engines` in `package.json`).
8
+
9
+ For local runs from this repository, copy [`secret.env.example`](../../../secret.env.example) to **`secret.env`** at the repo root (gitignored) and set your API key (see **Run from the monorepo**).
10
+
11
+ ## Install
12
+
13
+ When the package is available on npm, install the CLI globally:
14
+
15
+ ```bash
16
+ npm install -g @transcend-io/mcp-server-discovery
17
+ ```
18
+
19
+ Until then, run from a checkout of this repository (see **Run from the monorepo** below).
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ # With TRANSCEND_API_KEY in the environment; from the monorepo use secret.env (see Run from the monorepo)
25
+ TRANSCEND_API_KEY=your-api-key transcend-mcp-discovery
26
+ ```
27
+
28
+ The process speaks MCP over **stdio** and is meant to be launched by an MCP client (for example Cursor or Claude Desktop), not used as an interactive shell.
29
+
30
+ ### MCP client configuration
31
+
32
+ `npx` runs the package’s `transcend-mcp-discovery` binary (see `bin` in `package.json`).
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "transcend-discovery": {
38
+ "command": "npx",
39
+ "args": ["-y", "@transcend-io/mcp-server-discovery"],
40
+ "env": {
41
+ "TRANSCEND_API_KEY": "your-api-key"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ When developing in this repository, reuse the same variable names from root **`secret.env`** in the `env` block, or use your client’s env-file support if it has one.
49
+
50
+ ### Run from the monorepo
51
+
52
+ 1. **Credentials** — From the repository root, copy [`secret.env.example`](../../../secret.env.example) to **`secret.env`** and set `TRANSCEND_API_KEY` (and optional URL overrides).
53
+
54
+ 2. **Build and run** — `node ./dist/cli.mjs` matches the `transcend-mcp-discovery` `bin` (use `node` because `pnpm exec transcend-mcp-discovery` may not resolve this package’s own binary in a pnpm workspace):
55
+
56
+ ```bash
57
+ # from the repository root
58
+ pnpm exec turbo run build --filter="@transcend-io/mcp-server-discovery..."
59
+ set -a && source ./secret.env && set +a
60
+ pnpm -F @transcend-io/mcp-server-discovery exec node ./dist/cli.mjs
61
+ ```
62
+
63
+ **Alternative:** `./scripts/mcp-run.sh ./packages/mcp/mcp-server-discovery/dist/cli.mjs` (sources `secret.env` when present; run after build).
64
+
65
+ See [CONTRIBUTING.md](../../../CONTRIBUTING.md#mcp-servers) for workspace layout and `pnpm --filter` workflows.
66
+
67
+ ### Environment variables
68
+
69
+ | Variable | Required | Default | Description |
70
+ | ----------------------- | -------- | ------------------------------------------ | ----------------- |
71
+ | `TRANSCEND_API_KEY` | Yes | — | Transcend API key |
72
+ | `TRANSCEND_API_URL` | No | `https://multi-tenant.sombra.transcend.io` | Sombra API URL |
73
+ | `TRANSCEND_GRAPHQL_URL` | No | `https://api.transcend.io` | GraphQL API URL |
74
+
75
+ **Monorepo:** keep these in root **`secret.env`** (from [`secret.env.example`](../../../secret.env.example)); see **Run from the monorepo**.
76
+
77
+ ## Tools
78
+
79
+ - `discovery_list_scans` — List data discovery scans
80
+ - `discovery_get_scan` — Get scan details
81
+ - `discovery_start_scan` — Start a new scan
82
+ - `discovery_list_plugins` — List discovery plugins
83
+ - `discovery_classify_text` — Classify text into data categories
84
+ - `discovery_ner_extract` — Extract named entities from text
85
+
86
+ ## Related packages
87
+
88
+ Also available as part of the unified [`@transcend-io/mcp-server`](../mcp-server/README.md) which includes all domains. See the [root README](../../../README.md#mcp-servers) for the full list.
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.mjs ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { n as getDiscoveryTools, t as DiscoveryMixin } from "./graphql-B5D-riMf.mjs";
3
+ import { TranscendRestClient, createMCPServer } from "@transcend-io/mcp-server-core";
4
+ //#region src/cli.ts
5
+ createMCPServer({
6
+ name: "transcend-mcp-discovery",
7
+ version: "1.0.0",
8
+ getTools: getDiscoveryTools,
9
+ createClients: (auth, sombraUrl, graphqlUrl) => ({
10
+ rest: new TranscendRestClient(auth, sombraUrl),
11
+ graphql: new DiscoveryMixin(auth, graphqlUrl)
12
+ })
13
+ });
14
+ //#endregion
15
+ export {};
16
+
17
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { createMCPServer, TranscendRestClient } from '@transcend-io/mcp-server-core';\n\nimport { DiscoveryMixin } from './graphql.js';\nimport { getDiscoveryTools } from './tools/index.js';\n\ncreateMCPServer({\n name: 'transcend-mcp-discovery',\n version: '1.0.0',\n getTools: getDiscoveryTools,\n createClients: (auth, sombraUrl, graphqlUrl) => ({\n rest: new TranscendRestClient(auth, sombraUrl),\n graphql: new DiscoveryMixin(auth, graphqlUrl),\n }),\n});\n"],"mappings":";;;;AAMA,gBAAgB;CACd,MAAM;CACN,SAAS;CACT,UAAU;CACV,gBAAgB,MAAM,WAAW,gBAAgB;EAC/C,MAAM,IAAI,oBAAoB,MAAM,UAAU;EAC9C,SAAS,IAAI,eAAe,MAAM,WAAW;EAC9C;CACF,CAAC"}
@@ -0,0 +1,303 @@
1
+ import { PaginationSchema, TranscendGraphQLBase, createListResult, createToolResult, defineTool, z } from "@transcend-io/mcp-server-core";
2
+ //#region src/tools/discovery_classify_text.ts
3
+ const ClassifyTextSchema = z.object({
4
+ texts: z.array(z.string()).describe("Array of text strings to classify"),
5
+ categories: z.array(z.string()).optional().describe("Specific categories to classify against (optional)"),
6
+ model: z.string().optional().describe("LLM model to use for classification (optional)")
7
+ });
8
+ function createDiscoveryClassifyTextTool(clients) {
9
+ const { rest } = clients;
10
+ return defineTool({
11
+ name: "discovery_classify_text",
12
+ description: "Classify text content using Transcend's LLM classifier to identify data categories",
13
+ category: "Data Discovery",
14
+ readOnly: true,
15
+ annotations: {
16
+ readOnlyHint: true,
17
+ destructiveHint: false,
18
+ idempotentHint: true
19
+ },
20
+ zodSchema: ClassifyTextSchema,
21
+ handler: async ({ texts, categories, model }) => {
22
+ return createToolResult(true, {
23
+ results: await rest.classifyText({
24
+ texts,
25
+ categories,
26
+ model
27
+ }),
28
+ inputCount: texts.length,
29
+ message: `Classified ${texts.length} text(s) successfully`
30
+ });
31
+ }
32
+ });
33
+ }
34
+ //#endregion
35
+ //#region src/tools/discovery_get_scan.ts
36
+ const GetScanSchema = z.object({ scan_id: z.string().describe("ID of the classification scan to retrieve") });
37
+ function createDiscoveryGetScanTool(clients) {
38
+ const graphql = clients.graphql;
39
+ return defineTool({
40
+ name: "discovery_get_scan",
41
+ description: "Get detailed information about a specific classification scan including results.",
42
+ category: "Data Discovery",
43
+ readOnly: true,
44
+ annotations: {
45
+ readOnlyHint: true,
46
+ destructiveHint: false,
47
+ idempotentHint: true
48
+ },
49
+ zodSchema: GetScanSchema,
50
+ handler: async ({ scan_id }) => {
51
+ return createToolResult(true, await graphql.getClassificationScan(scan_id));
52
+ }
53
+ });
54
+ }
55
+ //#endregion
56
+ //#region src/tools/discovery_list_plugins.ts
57
+ const ListPluginsSchema = PaginationSchema.extend({ cursor: z.string().optional().describe("Pagination cursor from previous response (where supported)") });
58
+ function createDiscoveryListPluginsTool(clients) {
59
+ const graphql = clients.graphql;
60
+ return defineTool({
61
+ name: "discovery_list_plugins",
62
+ description: "List all available discovery plugins (integration types) in your organization.",
63
+ category: "Data Discovery",
64
+ readOnly: true,
65
+ annotations: {
66
+ readOnlyHint: true,
67
+ destructiveHint: false,
68
+ idempotentHint: true
69
+ },
70
+ zodSchema: ListPluginsSchema,
71
+ handler: async ({ limit, cursor }) => {
72
+ const result = await graphql.listDiscoveryPlugins({
73
+ first: limit,
74
+ after: cursor
75
+ });
76
+ return createListResult(result.nodes, {
77
+ totalCount: result.totalCount,
78
+ hasNextPage: result.pageInfo?.hasNextPage
79
+ });
80
+ }
81
+ });
82
+ }
83
+ //#endregion
84
+ //#region src/tools/discovery_list_scans.ts
85
+ const ListScansSchema = PaginationSchema.extend({ cursor: z.string().optional().describe("Pagination cursor from previous response (where supported)") });
86
+ function createDiscoveryListScansTool(clients) {
87
+ const graphql = clients.graphql;
88
+ return defineTool({
89
+ name: "discovery_list_scans",
90
+ description: "List all data classification scans. Returns data silos with classification info.",
91
+ category: "Data Discovery",
92
+ readOnly: true,
93
+ annotations: {
94
+ readOnlyHint: true,
95
+ destructiveHint: false,
96
+ idempotentHint: true
97
+ },
98
+ zodSchema: ListScansSchema,
99
+ handler: async ({ limit, cursor }) => {
100
+ const result = await graphql.listClassificationScans({
101
+ first: limit,
102
+ after: cursor
103
+ });
104
+ return createListResult(result.nodes, {
105
+ totalCount: result.totalCount,
106
+ hasNextPage: result.pageInfo?.hasNextPage
107
+ });
108
+ }
109
+ });
110
+ }
111
+ //#endregion
112
+ //#region src/tools/discovery_ner_extract.ts
113
+ const NerExtractSchema = z.object({
114
+ text: z.string().describe("Text to extract entities from"),
115
+ entity_types: z.array(z.string()).optional().describe("Specific entity types to extract (optional)")
116
+ });
117
+ function createDiscoveryNerExtractTool(clients) {
118
+ const { rest } = clients;
119
+ return defineTool({
120
+ name: "discovery_ner_extract",
121
+ description: "Extract named entities (PII, organizations, locations, etc.) from text using NER",
122
+ category: "Data Discovery",
123
+ readOnly: true,
124
+ annotations: {
125
+ readOnlyHint: true,
126
+ destructiveHint: false,
127
+ idempotentHint: true
128
+ },
129
+ zodSchema: NerExtractSchema,
130
+ handler: async ({ text, entity_types }) => {
131
+ const result = await rest.extractEntities({
132
+ text,
133
+ entityTypes: entity_types
134
+ });
135
+ return createToolResult(true, {
136
+ entities: result.entities,
137
+ entityCount: result.entities.length,
138
+ entityTypes: [...new Set(result.entities.map((e) => e.type))],
139
+ message: `Extracted ${result.entities.length} entities from text`
140
+ });
141
+ }
142
+ });
143
+ }
144
+ //#endregion
145
+ //#region src/tools/discovery_start_scan.ts
146
+ const StartScanSchema = z.object({
147
+ name: z.string().describe("Name for the classification scan"),
148
+ data_silo_id: z.string().optional().describe("ID of the data silo to scan (optional)"),
149
+ type: z.string().optional().describe("Type of scan (optional)")
150
+ });
151
+ function createDiscoveryStartScanTool(clients) {
152
+ const graphql = clients.graphql;
153
+ return defineTool({
154
+ name: "discovery_start_scan",
155
+ description: "Start a new data classification scan on a data silo.",
156
+ category: "Data Discovery",
157
+ readOnly: false,
158
+ confirmationHint: "Starts a new classification scan on the data silo",
159
+ annotations: {
160
+ readOnlyHint: false,
161
+ destructiveHint: false,
162
+ idempotentHint: false
163
+ },
164
+ zodSchema: StartScanSchema,
165
+ handler: async ({ name, data_silo_id, type }) => {
166
+ return createToolResult(true, {
167
+ scan: await graphql.startClassificationScan({
168
+ name,
169
+ dataSiloId: data_silo_id,
170
+ type
171
+ }),
172
+ message: `Classification scan "${name}" started successfully`
173
+ });
174
+ }
175
+ });
176
+ }
177
+ //#endregion
178
+ //#region src/tools/index.ts
179
+ function getDiscoveryTools(clients) {
180
+ return [
181
+ createDiscoveryClassifyTextTool(clients),
182
+ createDiscoveryNerExtractTool(clients),
183
+ createDiscoveryListScansTool(clients),
184
+ createDiscoveryStartScanTool(clients),
185
+ createDiscoveryGetScanTool(clients),
186
+ createDiscoveryListPluginsTool(clients)
187
+ ];
188
+ }
189
+ //#endregion
190
+ //#region src/graphql.ts
191
+ var DiscoveryMixin = class extends TranscendGraphQLBase {
192
+ async listClassificationScans(options) {
193
+ const data = await this.makeRequest(`
194
+ query ListDataSilosForClassification($first: Int) {
195
+ dataSilos(first: $first) {
196
+ nodes {
197
+ id
198
+ title
199
+ type
200
+ createdAt
201
+ }
202
+ totalCount
203
+ }
204
+ }
205
+ `, { first: Math.min(options?.first || 50, 100) });
206
+ const scans = data.dataSilos.nodes.map((silo) => ({
207
+ id: silo.id,
208
+ name: `Classification: ${silo.title}`,
209
+ type: silo.type,
210
+ status: "COMPLETED",
211
+ dataSiloId: silo.id,
212
+ createdAt: silo.createdAt
213
+ }));
214
+ return {
215
+ nodes: scans,
216
+ pageInfo: {
217
+ hasNextPage: scans.length < data.dataSilos.totalCount,
218
+ hasPreviousPage: false
219
+ },
220
+ totalCount: data.dataSilos.totalCount
221
+ };
222
+ }
223
+ async startClassificationScan(input) {
224
+ return (await this.makeRequest(`
225
+ mutation StartClassificationScan($input: StartClassificationScanInput!) {
226
+ startClassificationScan(input: $input) {
227
+ scan {
228
+ id
229
+ name
230
+ type
231
+ status
232
+ startedAt
233
+ createdAt
234
+ }
235
+ }
236
+ }
237
+ `, { input })).startClassificationScan.scan;
238
+ }
239
+ async getClassificationScan(id) {
240
+ return (await this.makeRequest(`
241
+ query GetClassificationScan($id: ID!) {
242
+ classificationScan(id: $id) {
243
+ id
244
+ name
245
+ type
246
+ status
247
+ startedAt
248
+ completedAt
249
+ dataSiloId
250
+ results {
251
+ id
252
+ path
253
+ dataCategory {
254
+ id
255
+ name
256
+ category
257
+ }
258
+ confidence
259
+ }
260
+ createdAt
261
+ }
262
+ }
263
+ `, { id })).classificationScan;
264
+ }
265
+ async listDiscoveryPlugins(options) {
266
+ const data = await this.makeRequest(`
267
+ query ListDataSiloTypes($first: Int) {
268
+ dataSilos(first: $first) {
269
+ nodes {
270
+ id
271
+ title
272
+ type
273
+ description
274
+ }
275
+ totalCount
276
+ }
277
+ }
278
+ `, { first: options?.first || 50 });
279
+ const typeMap = /* @__PURE__ */ new Map();
280
+ data.dataSilos.nodes.forEach((silo) => {
281
+ if (!typeMap.has(silo.type)) typeMap.set(silo.type, {
282
+ id: silo.type,
283
+ name: silo.type.replace(/([A-Z])/g, " $1").trim(),
284
+ type: silo.type,
285
+ description: `Integration for ${silo.type}`,
286
+ isEnabled: true
287
+ });
288
+ });
289
+ const plugins = Array.from(typeMap.values());
290
+ return {
291
+ nodes: plugins,
292
+ pageInfo: {
293
+ hasNextPage: false,
294
+ hasPreviousPage: false
295
+ },
296
+ totalCount: plugins.length
297
+ };
298
+ }
299
+ };
300
+ //#endregion
301
+ export { ListScansSchema as a, ClassifyTextSchema as c, NerExtractSchema as i, getDiscoveryTools as n, ListPluginsSchema as o, StartScanSchema as r, GetScanSchema as s, DiscoveryMixin as t };
302
+
303
+ //# sourceMappingURL=graphql-B5D-riMf.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-B5D-riMf.mjs","names":[],"sources":["../src/tools/discovery_classify_text.ts","../src/tools/discovery_get_scan.ts","../src/tools/discovery_list_plugins.ts","../src/tools/discovery_list_scans.ts","../src/tools/discovery_ner_extract.ts","../src/tools/discovery_start_scan.ts","../src/tools/index.ts","../src/graphql.ts"],"sourcesContent":["import { createToolResult, defineTool, z, type ToolClients } from '@transcend-io/mcp-server-core';\n\nexport const ClassifyTextSchema = z.object({\n texts: z.array(z.string()).describe('Array of text strings to classify'),\n categories: z\n .array(z.string())\n .optional()\n .describe('Specific categories to classify against (optional)'),\n model: z.string().optional().describe('LLM model to use for classification (optional)'),\n});\nexport type ClassifyTextInput = z.infer<typeof ClassifyTextSchema>;\n\nexport function createDiscoveryClassifyTextTool(clients: ToolClients) {\n const { rest } = clients;\n return defineTool({\n name: 'discovery_classify_text',\n description:\n \"Classify text content using Transcend's LLM classifier to identify data categories\",\n category: 'Data Discovery',\n readOnly: true,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n zodSchema: ClassifyTextSchema,\n handler: async ({ texts, categories, model }) => {\n const results = await rest.classifyText({\n texts,\n categories,\n model,\n });\n\n return createToolResult(true, {\n results,\n inputCount: texts.length,\n message: `Classified ${texts.length} text(s) successfully`,\n });\n },\n });\n}\n","import { createToolResult, defineTool, z, type ToolClients } from '@transcend-io/mcp-server-core';\n\nimport type { DiscoveryMixin } from '../graphql.js';\n\nexport const GetScanSchema = z.object({\n scan_id: z.string().describe('ID of the classification scan to retrieve'),\n});\nexport type GetScanInput = z.infer<typeof GetScanSchema>;\n\nexport function createDiscoveryGetScanTool(clients: ToolClients) {\n const graphql = clients.graphql as DiscoveryMixin;\n return defineTool({\n name: 'discovery_get_scan',\n description: 'Get detailed information about a specific classification scan including results.',\n category: 'Data Discovery',\n readOnly: true,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n zodSchema: GetScanSchema,\n handler: async ({ scan_id }) => {\n const result = await graphql.getClassificationScan(scan_id);\n return createToolResult(true, result);\n },\n });\n}\n","import {\n createListResult,\n defineTool,\n PaginationSchema,\n z,\n type ToolClients,\n} from '@transcend-io/mcp-server-core';\n\nimport type { DiscoveryMixin } from '../graphql.js';\n\nexport const ListPluginsSchema = PaginationSchema.extend({\n cursor: z\n .string()\n .optional()\n .describe('Pagination cursor from previous response (where supported)'),\n});\nexport type ListPluginsInput = z.infer<typeof ListPluginsSchema>;\n\nexport function createDiscoveryListPluginsTool(clients: ToolClients) {\n const graphql = clients.graphql as DiscoveryMixin;\n return defineTool({\n name: 'discovery_list_plugins',\n description: 'List all available discovery plugins (integration types) in your organization.',\n category: 'Data Discovery',\n readOnly: true,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n zodSchema: ListPluginsSchema,\n handler: async ({ limit, cursor }) => {\n const result = await graphql.listDiscoveryPlugins({\n first: limit,\n after: cursor,\n });\n return createListResult(result.nodes, {\n totalCount: result.totalCount,\n hasNextPage: result.pageInfo?.hasNextPage,\n });\n },\n });\n}\n","import {\n createListResult,\n defineTool,\n PaginationSchema,\n z,\n type ToolClients,\n} from '@transcend-io/mcp-server-core';\n\nimport type { DiscoveryMixin } from '../graphql.js';\n\nexport const ListScansSchema = PaginationSchema.extend({\n cursor: z\n .string()\n .optional()\n .describe('Pagination cursor from previous response (where supported)'),\n});\nexport type ListScansInput = z.infer<typeof ListScansSchema>;\n\nexport function createDiscoveryListScansTool(clients: ToolClients) {\n const graphql = clients.graphql as DiscoveryMixin;\n return defineTool({\n name: 'discovery_list_scans',\n description: 'List all data classification scans. Returns data silos with classification info.',\n category: 'Data Discovery',\n readOnly: true,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n zodSchema: ListScansSchema,\n handler: async ({ limit, cursor }) => {\n const result = await graphql.listClassificationScans({\n first: limit,\n after: cursor,\n });\n return createListResult(result.nodes, {\n totalCount: result.totalCount,\n hasNextPage: result.pageInfo?.hasNextPage,\n });\n },\n });\n}\n","import { createToolResult, defineTool, z, type ToolClients } from '@transcend-io/mcp-server-core';\n\nexport const NerExtractSchema = z.object({\n text: z.string().describe('Text to extract entities from'),\n entity_types: z\n .array(z.string())\n .optional()\n .describe('Specific entity types to extract (optional)'),\n});\nexport type NerExtractInput = z.infer<typeof NerExtractSchema>;\n\nexport function createDiscoveryNerExtractTool(clients: ToolClients) {\n const { rest } = clients;\n return defineTool({\n name: 'discovery_ner_extract',\n description: 'Extract named entities (PII, organizations, locations, etc.) from text using NER',\n category: 'Data Discovery',\n readOnly: true,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n zodSchema: NerExtractSchema,\n handler: async ({ text, entity_types }) => {\n const result = await rest.extractEntities({\n text,\n entityTypes: entity_types,\n });\n\n return createToolResult(true, {\n entities: result.entities,\n entityCount: result.entities.length,\n entityTypes: [...new Set(result.entities.map((e) => e.type))],\n message: `Extracted ${result.entities.length} entities from text`,\n });\n },\n });\n}\n","import { createToolResult, defineTool, z, type ToolClients } from '@transcend-io/mcp-server-core';\n\nimport type { DiscoveryMixin } from '../graphql.js';\n\nexport const StartScanSchema = z.object({\n name: z.string().describe('Name for the classification scan'),\n data_silo_id: z.string().optional().describe('ID of the data silo to scan (optional)'),\n type: z.string().optional().describe('Type of scan (optional)'),\n});\nexport type StartScanInput = z.infer<typeof StartScanSchema>;\n\nexport function createDiscoveryStartScanTool(clients: ToolClients) {\n const graphql = clients.graphql as DiscoveryMixin;\n return defineTool({\n name: 'discovery_start_scan',\n description: 'Start a new data classification scan on a data silo.',\n category: 'Data Discovery',\n readOnly: false,\n confirmationHint: 'Starts a new classification scan on the data silo',\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n zodSchema: StartScanSchema,\n handler: async ({ name, data_silo_id, type }) => {\n const result = await graphql.startClassificationScan({\n name,\n dataSiloId: data_silo_id,\n type,\n });\n return createToolResult(true, {\n scan: result,\n message: `Classification scan \"${name}\" started successfully`,\n });\n },\n });\n}\n","import type { ToolDefinition, ToolClients } from '@transcend-io/mcp-server-core';\n\nimport { createDiscoveryClassifyTextTool } from './discovery_classify_text.js';\nimport { createDiscoveryGetScanTool } from './discovery_get_scan.js';\nimport { createDiscoveryListPluginsTool } from './discovery_list_plugins.js';\nimport { createDiscoveryListScansTool } from './discovery_list_scans.js';\nimport { createDiscoveryNerExtractTool } from './discovery_ner_extract.js';\nimport { createDiscoveryStartScanTool } from './discovery_start_scan.js';\n\nexport function getDiscoveryTools(clients: ToolClients): ToolDefinition[] {\n return [\n createDiscoveryClassifyTextTool(clients),\n createDiscoveryNerExtractTool(clients),\n createDiscoveryListScansTool(clients),\n createDiscoveryStartScanTool(clients),\n createDiscoveryGetScanTool(clients),\n createDiscoveryListPluginsTool(clients),\n ];\n}\n","import {\n TranscendGraphQLBase,\n type PaginatedResponse,\n type ClassificationScan,\n type DiscoveryPlugin,\n type ListOptions,\n} from '@transcend-io/mcp-server-core';\n\nexport class DiscoveryMixin extends TranscendGraphQLBase {\n async listClassificationScans(\n options?: ListOptions,\n ): Promise<PaginatedResponse<ClassificationScan>> {\n const query = `\n query ListDataSilosForClassification($first: Int) {\n dataSilos(first: $first) {\n nodes {\n id\n title\n type\n createdAt\n }\n totalCount\n }\n }\n `;\n const data = await this.makeRequest<{\n dataSilos: {\n nodes: Array<{ id: string; title: string; type: string; createdAt: string }>;\n totalCount: number;\n };\n }>(query, { first: Math.min(options?.first || 50, 100) });\n const scans: ClassificationScan[] = data.dataSilos.nodes.map((silo) => ({\n id: silo.id,\n name: `Classification: ${silo.title}`,\n type: silo.type,\n status: 'COMPLETED',\n dataSiloId: silo.id,\n createdAt: silo.createdAt,\n }));\n return {\n nodes: scans,\n pageInfo: { hasNextPage: scans.length < data.dataSilos.totalCount, hasPreviousPage: false },\n totalCount: data.dataSilos.totalCount,\n };\n }\n\n async startClassificationScan(input: {\n name: string;\n dataSiloId?: string;\n type?: string;\n }): Promise<ClassificationScan> {\n const mutation = `\n mutation StartClassificationScan($input: StartClassificationScanInput!) {\n startClassificationScan(input: $input) {\n scan {\n id\n name\n type\n status\n startedAt\n createdAt\n }\n }\n }\n `;\n const data = await this.makeRequest<{ startClassificationScan: { scan: ClassificationScan } }>(\n mutation,\n { input },\n );\n return data.startClassificationScan.scan;\n }\n\n async getClassificationScan(id: string): Promise<ClassificationScan> {\n const query = `\n query GetClassificationScan($id: ID!) {\n classificationScan(id: $id) {\n id\n name\n type\n status\n startedAt\n completedAt\n dataSiloId\n results {\n id\n path\n dataCategory {\n id\n name\n category\n }\n confidence\n }\n createdAt\n }\n }\n `;\n const data = await this.makeRequest<{ classificationScan: ClassificationScan }>(query, { id });\n return data.classificationScan;\n }\n\n async listDiscoveryPlugins(options?: ListOptions): Promise<PaginatedResponse<DiscoveryPlugin>> {\n const query = `\n query ListDataSiloTypes($first: Int) {\n dataSilos(first: $first) {\n nodes {\n id\n title\n type\n description\n }\n totalCount\n }\n }\n `;\n const data = await this.makeRequest<{\n dataSilos: {\n nodes: Array<{ id: string; title: string; type: string; description: string | null }>;\n totalCount: number;\n };\n }>(query, { first: options?.first || 50 });\n const typeMap = new Map<string, DiscoveryPlugin>();\n data.dataSilos.nodes.forEach((silo) => {\n if (!typeMap.has(silo.type)) {\n typeMap.set(silo.type, {\n id: silo.type,\n name: silo.type.replace(/([A-Z])/g, ' $1').trim(),\n type: silo.type,\n description: `Integration for ${silo.type}`,\n isEnabled: true,\n });\n }\n });\n const plugins = Array.from(typeMap.values());\n return {\n nodes: plugins,\n pageInfo: { hasNextPage: false, hasPreviousPage: false },\n totalCount: plugins.length,\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { ClassificationScan, DiscoveryPlugin, ListOptions, PaginatedResponse, ToolClients, ToolDefinition, TranscendGraphQLBase, z } from "@transcend-io/mcp-server-core";
2
+
3
+ //#region src/tools/index.d.ts
4
+ declare function getDiscoveryTools(clients: ToolClients): ToolDefinition[];
5
+ //#endregion
6
+ //#region src/graphql.d.ts
7
+ declare class DiscoveryMixin extends TranscendGraphQLBase {
8
+ listClassificationScans(options?: ListOptions): Promise<PaginatedResponse<ClassificationScan>>;
9
+ startClassificationScan(input: {
10
+ name: string;
11
+ dataSiloId?: string;
12
+ type?: string;
13
+ }): Promise<ClassificationScan>;
14
+ getClassificationScan(id: string): Promise<ClassificationScan>;
15
+ listDiscoveryPlugins(options?: ListOptions): Promise<PaginatedResponse<DiscoveryPlugin>>;
16
+ }
17
+ //#endregion
18
+ //#region src/tools/discovery_classify_text.d.ts
19
+ declare const ClassifyTextSchema: z.ZodObject<{
20
+ texts: z.ZodArray<z.ZodString>;
21
+ categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
22
+ model: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>;
24
+ type ClassifyTextInput = z.infer<typeof ClassifyTextSchema>;
25
+ //#endregion
26
+ //#region src/tools/discovery_get_scan.d.ts
27
+ declare const GetScanSchema: z.ZodObject<{
28
+ scan_id: z.ZodString;
29
+ }, z.core.$strip>;
30
+ type GetScanInput = z.infer<typeof GetScanSchema>;
31
+ //#endregion
32
+ //#region src/tools/discovery_list_plugins.d.ts
33
+ declare const ListPluginsSchema: z.ZodObject<{
34
+ limit: z.ZodDefault<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
35
+ cursor: z.ZodOptional<z.ZodString>;
36
+ }, z.core.$strip>;
37
+ type ListPluginsInput = z.infer<typeof ListPluginsSchema>;
38
+ //#endregion
39
+ //#region src/tools/discovery_list_scans.d.ts
40
+ declare const ListScansSchema: z.ZodObject<{
41
+ limit: z.ZodDefault<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
42
+ cursor: z.ZodOptional<z.ZodString>;
43
+ }, z.core.$strip>;
44
+ type ListScansInput = z.infer<typeof ListScansSchema>;
45
+ //#endregion
46
+ //#region src/tools/discovery_ner_extract.d.ts
47
+ declare const NerExtractSchema: z.ZodObject<{
48
+ text: z.ZodString;
49
+ entity_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
+ }, z.core.$strip>;
51
+ type NerExtractInput = z.infer<typeof NerExtractSchema>;
52
+ //#endregion
53
+ //#region src/tools/discovery_start_scan.d.ts
54
+ declare const StartScanSchema: z.ZodObject<{
55
+ name: z.ZodString;
56
+ data_silo_id: z.ZodOptional<z.ZodString>;
57
+ type: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strip>;
59
+ type StartScanInput = z.infer<typeof StartScanSchema>;
60
+ //#endregion
61
+ export { type ClassifyTextInput, ClassifyTextSchema, DiscoveryMixin, type GetScanInput, GetScanSchema, type ListPluginsInput, ListPluginsSchema, type ListScansInput, ListScansSchema, type NerExtractInput, NerExtractSchema, type StartScanInput, StartScanSchema, getDiscoveryTools };
62
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/tools/index.ts","../src/graphql.ts","../src/tools/discovery_classify_text.ts","../src/tools/discovery_get_scan.ts","../src/tools/discovery_list_plugins.ts","../src/tools/discovery_list_scans.ts","../src/tools/discovery_ner_extract.ts","../src/tools/discovery_start_scan.ts"],"mappings":";;;iBASgB,iBAAA,CAAkB,OAAA,EAAS,WAAA,GAAc,cAAA;;;cCD5C,cAAA,SAAuB,oBAAA;EAC5B,uBAAA,CACJ,OAAA,GAAU,WAAA,GACT,OAAA,CAAQ,iBAAA,CAAkB,kBAAA;EAmCvB,uBAAA,CAAwB,KAAA;IAC5B,IAAA;IACA,UAAA;IACA,IAAA;EAAA,IACE,OAAA,CAAQ,kBAAA;EAsBN,qBAAA,CAAsB,EAAA,WAAa,OAAA,CAAQ,kBAAA;EA6B3C,oBAAA,CAAqB,OAAA,GAAU,WAAA,GAAc,OAAA,CAAQ,iBAAA,CAAkB,eAAA;AAAA;;;cCnGlE,kBAAA,EAAkB,CAAA,CAAA,SAAA;;;;;KAQnB,iBAAA,GAAoB,CAAA,CAAE,KAAA,QAAa,kBAAA;;;cCNlC,aAAA,EAAa,CAAA,CAAA,SAAA;;;KAGd,YAAA,GAAe,CAAA,CAAE,KAAA,QAAa,aAAA;;;cCG7B,iBAAA,EAAiB,CAAA,CAAA,SAAA;;;;KAMlB,gBAAA,GAAmB,CAAA,CAAE,KAAA,QAAa,iBAAA;;;cCNjC,eAAA,EAAe,CAAA,CAAA,SAAA;;;;KAMhB,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,eAAA;;;cCd/B,gBAAA,EAAgB,CAAA,CAAA,SAAA;;;;KAOjB,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,gBAAA;;;cCLhC,eAAA,EAAe,CAAA,CAAA,SAAA;;;;;KAKhB,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,eAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { a as ListScansSchema, c as ClassifyTextSchema, i as NerExtractSchema, n as getDiscoveryTools, o as ListPluginsSchema, r as StartScanSchema, s as GetScanSchema, t as DiscoveryMixin } from "./graphql-B5D-riMf.mjs";
2
+ export { ClassifyTextSchema, DiscoveryMixin, GetScanSchema, ListPluginsSchema, ListScansSchema, NerExtractSchema, StartScanSchema, getDiscoveryTools };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@transcend-io/mcp-server-discovery",
3
+ "version": "0.1.0",
4
+ "description": "Transcend MCP Server — Data Discovery tools.",
5
+ "homepage": "https://github.com/transcend-io/tools/tree/main/packages/mcp/mcp-server-discovery",
6
+ "license": "Apache-2.0",
7
+ "author": "Transcend Inc.",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/transcend-io/tools.git",
11
+ "directory": "packages/mcp/mcp-server-discovery"
12
+ },
13
+ "bin": {
14
+ "transcend-mcp-discovery": "./dist/cli.mjs"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "types": "./dist/index.d.mts",
22
+ "exports": {
23
+ ".": {
24
+ "@transcend-io/source": "./src/index.ts",
25
+ "types": "./dist/index.d.mts",
26
+ "default": "./dist/index.mjs"
27
+ }
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "build": "tsdown",
34
+ "test": "vitest run",
35
+ "typecheck": "tsc -p tsconfig.json --noEmit",
36
+ "check:exports": "attw --pack . --ignore-rules cjs-resolves-to-esm",
37
+ "check:publint": "publint --level warning --strict --pack pnpm"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "catalog:",
41
+ "@transcend-io/mcp-server-core": "workspace:*",
42
+ "zod": "catalog:"
43
+ },
44
+ "devDependencies": {
45
+ "@arethetypeswrong/cli": "catalog:",
46
+ "@types/node": "catalog:",
47
+ "publint": "catalog:",
48
+ "tsdown": "catalog:",
49
+ "typescript": "catalog:",
50
+ "vitest": "catalog:"
51
+ },
52
+ "engines": {
53
+ "node": ">=22.12.0"
54
+ }
55
+ }