simple-dynamsoft-mcp 6.3.0 → 7.0.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.
Files changed (47) hide show
  1. package/.env.example +35 -9
  2. package/README.md +156 -497
  3. package/package.json +13 -7
  4. package/scripts/prebuild-rag-index.mjs +1 -1
  5. package/scripts/run-gemini-tests.mjs +1 -1
  6. package/scripts/sync-submodules.mjs +1 -1
  7. package/scripts/verify-doc-resources.mjs +79 -0
  8. package/src/data/bootstrap.js +475 -0
  9. package/src/data/download-utils.js +99 -0
  10. package/src/data/hydration-mode.js +15 -0
  11. package/src/data/hydration-policy.js +39 -0
  12. package/src/data/repo-map.js +149 -0
  13. package/src/{data-root.js → data/root.js} +1 -1
  14. package/src/{submodule-sync.js → data/submodule-sync.js} +1 -1
  15. package/src/index.js +49 -1499
  16. package/src/observability/logging.js +51 -0
  17. package/src/rag/config.js +96 -0
  18. package/src/rag/index.js +266 -0
  19. package/src/rag/lexical-provider.js +170 -0
  20. package/src/rag/logger.js +46 -0
  21. package/src/rag/profile-config.js +48 -0
  22. package/src/rag/providers.js +585 -0
  23. package/src/rag/search-utils.js +166 -0
  24. package/src/rag/vector-cache.js +323 -0
  25. package/src/server/create-server.js +168 -0
  26. package/src/server/helpers/server-helpers.js +33 -0
  27. package/src/{resource-index → server/resource-index}/paths.js +2 -2
  28. package/src/{resource-index → server/resource-index}/samples.js +9 -1
  29. package/src/{resource-index.js → server/resource-index.js} +158 -93
  30. package/src/server/resources/register-resources.js +56 -0
  31. package/src/server/runtime-config.js +66 -0
  32. package/src/server/tools/register-index-tools.js +130 -0
  33. package/src/server/tools/register-project-tools.js +305 -0
  34. package/src/server/tools/register-quickstart-tools.js +572 -0
  35. package/src/server/tools/register-sample-tools.js +333 -0
  36. package/src/server/tools/register-version-tools.js +136 -0
  37. package/src/server/transports/http.js +84 -0
  38. package/src/server/transports/stdio.js +12 -0
  39. package/src/data-bootstrap.js +0 -255
  40. package/src/rag.js +0 -1203
  41. /package/src/{gemini-retry.js → rag/gemini-retry.js} +0 -0
  42. /package/src/{normalizers.js → server/normalizers.js} +0 -0
  43. /package/src/{resource-index → server/resource-index}/builders.js +0 -0
  44. /package/src/{resource-index → server/resource-index}/config.js +0 -0
  45. /package/src/{resource-index → server/resource-index}/docs-loader.js +0 -0
  46. /package/src/{resource-index → server/resource-index}/uri.js +0 -0
  47. /package/src/{resource-index → server/resource-index}/version-policy.js +0 -0
@@ -0,0 +1,333 @@
1
+ import { z } from "zod";
2
+ import { formatScoreLabel, formatScoreNote } from "../helpers/server-helpers.js";
3
+
4
+ export function registerSampleTools({
5
+ server,
6
+ ensureScopeHydrated,
7
+ ensureLatestMajor,
8
+ normalizeProduct,
9
+ normalizePlatform,
10
+ normalizeEdition,
11
+ normalizeSampleName,
12
+ parseSampleUri,
13
+ resourceIndex,
14
+ getSampleEntries,
15
+ getSampleIdFromUri,
16
+ getDisplayEdition,
17
+ getDisplayPlatform,
18
+ formatScopeLabel,
19
+ searchResources,
20
+ getSampleSuggestions
21
+ }) {
22
+ server.registerTool(
23
+ "list_samples",
24
+ {
25
+ title: "List Samples",
26
+ description: "List available sample IDs and URIs for a given scope. Use DCV scope for MRZ/VIN/document normalization scenarios.",
27
+ inputSchema: {
28
+ product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
29
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
30
+ platform: z.string().optional().describe("Platform: android, ios, maui, react-native, flutter, js, python, cpp, java, dotnet, nodejs, angular, blazor, capacitor, electron, es6, native-ts, next, nuxt, pwa, react, requirejs, svelte, vue, webview, spm, core"),
31
+ limit: z.number().int().min(1).max(200).optional().describe("Max results (default 50)")
32
+ }
33
+ },
34
+ async ({ product, edition, platform, limit }) => {
35
+ const normalizedProduct = normalizeProduct(product);
36
+ const normalizedPlatform = normalizePlatform(platform);
37
+ const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
38
+
39
+ await ensureScopeHydrated({
40
+ product: normalizedProduct,
41
+ edition: normalizedEdition,
42
+ platform: normalizedPlatform,
43
+ type: "sample"
44
+ });
45
+
46
+ const policy = ensureLatestMajor({
47
+ product: normalizedProduct,
48
+ version: undefined,
49
+ query: "",
50
+ edition: normalizedEdition,
51
+ platform: normalizedPlatform
52
+ });
53
+
54
+ if (!policy.ok) {
55
+ return { isError: true, content: [{ type: "text", text: policy.message }] };
56
+ }
57
+
58
+ const samples = getSampleEntries({
59
+ product: normalizedProduct,
60
+ edition: normalizedEdition,
61
+ platform: normalizedPlatform
62
+ });
63
+
64
+ const maxResults = Math.min(limit || 50, 200);
65
+ const selected = samples.slice(0, maxResults);
66
+
67
+ const payload = selected.map((entry) => ({
68
+ sample_id: getSampleIdFromUri(entry.uri),
69
+ uri: entry.uri,
70
+ product: entry.product,
71
+ edition: getDisplayEdition(entry.edition),
72
+ platform: getDisplayPlatform(entry.platform),
73
+ version: entry.version,
74
+ title: entry.title,
75
+ summary: entry.summary
76
+ }));
77
+
78
+ const lines = [
79
+ `Total matches: ${samples.length}`,
80
+ `Returned: ${payload.length}`,
81
+ "",
82
+ "Plain URIs (copy/paste):",
83
+ ...payload.map((item, index) => {
84
+ const sampleNote = item.sample_id ? ` (sample_id: ${item.sample_id})` : "";
85
+ return `- ${index + 1}. ${item.uri}${sampleNote}`;
86
+ })
87
+ ];
88
+
89
+ const output = {
90
+ total: samples.length,
91
+ returned: payload.length,
92
+ samples: payload
93
+ };
94
+
95
+ return {
96
+ content: [{
97
+ type: "text",
98
+ text: `${lines.join("\n")}\n\nJSON:\n${JSON.stringify(output, null, 2)}`
99
+ }]
100
+ };
101
+ }
102
+ );
103
+
104
+ server.registerTool(
105
+ "resolve_sample",
106
+ {
107
+ title: "Resolve Sample",
108
+ description: "Resolve a sample_id (or sample URI) to matching sample URIs.",
109
+ inputSchema: {
110
+ sample_id: z.string().describe("Sample identifier or sample:// URI"),
111
+ product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
112
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
113
+ platform: z.string().optional().describe("Platform: android, ios, maui, react-native, flutter, js, python, cpp, java, dotnet, nodejs, angular, blazor, capacitor, electron, es6, native-ts, next, nuxt, pwa, react, requirejs, svelte, vue, webview, spm, core"),
114
+ limit: z.number().int().min(1).max(10).optional().describe("Max results (default 5)")
115
+ }
116
+ },
117
+ async ({ sample_id, product, edition, platform, limit }) => {
118
+ if (!sample_id || !sample_id.trim()) {
119
+ return { isError: true, content: [{ type: "text", text: "sample_id is required." }] };
120
+ }
121
+
122
+ const normalizedProduct = normalizeProduct(product);
123
+ const normalizedPlatform = normalizePlatform(platform);
124
+ const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
125
+
126
+ await ensureScopeHydrated({
127
+ product: normalizedProduct,
128
+ edition: normalizedEdition,
129
+ platform: normalizedPlatform,
130
+ type: "sample"
131
+ });
132
+
133
+ const policy = ensureLatestMajor({
134
+ product: normalizedProduct,
135
+ version: undefined,
136
+ query: sample_id,
137
+ edition: normalizedEdition,
138
+ platform: normalizedPlatform
139
+ });
140
+
141
+ if (!policy.ok) {
142
+ return { isError: true, content: [{ type: "text", text: policy.message }] };
143
+ }
144
+
145
+ if (sample_id.includes("://")) {
146
+ const parsed = parseSampleUri(sample_id);
147
+ if (!parsed) {
148
+ return {
149
+ isError: true,
150
+ content: [{
151
+ type: "text",
152
+ text: "sample_id looks like a URI but is not a valid sample:// URI. For doc:// URIs, use resources/read."
153
+ }]
154
+ };
155
+ }
156
+ const entry = resourceIndex.find((item) => item.uri === sample_id && item.type === "sample");
157
+ if (!entry) {
158
+ return {
159
+ isError: true,
160
+ content: [{
161
+ type: "text",
162
+ text: `Sample URI not found in index: ${sample_id}. Use list_samples or search.`
163
+ }]
164
+ };
165
+ }
166
+
167
+ const payload = [{
168
+ sample_id: getSampleIdFromUri(entry.uri),
169
+ uri: entry.uri,
170
+ product: entry.product,
171
+ edition: getDisplayEdition(entry.edition),
172
+ platform: getDisplayPlatform(entry.platform),
173
+ version: entry.version,
174
+ title: entry.title,
175
+ summary: entry.summary
176
+ }];
177
+
178
+ const output = {
179
+ query: sample_id,
180
+ returned: payload.length,
181
+ samples: payload
182
+ };
183
+
184
+ return {
185
+ content: [{
186
+ type: "text",
187
+ text: [
188
+ `Found ${payload.length} match(es) for "${sample_id}".`,
189
+ "Plain URIs (copy/paste):",
190
+ `- 1. ${entry.uri} (sample_id: ${payload[0].sample_id})`,
191
+ "",
192
+ "JSON:",
193
+ JSON.stringify(output, null, 2)
194
+ ].join("\n")
195
+ }, {
196
+ type: "resource_link",
197
+ uri: entry.uri,
198
+ name: entry.title,
199
+ description: `SAMPLE | ${formatScopeLabel(entry)} | v${entry.version}${formatScoreLabel(entry)} | sample_id: ${payload[0].sample_id}`,
200
+ mimeType: entry.mimeType,
201
+ annotations: {
202
+ audience: ["assistant"],
203
+ priority: 0.8
204
+ }
205
+ }]
206
+ };
207
+ }
208
+
209
+ const sampleQuery = normalizeSampleName(sample_id);
210
+ const maxResults = Math.min(limit || 5, 10);
211
+
212
+ const scopedSamples = getSampleEntries({
213
+ product: normalizedProduct,
214
+ edition: normalizedEdition,
215
+ platform: normalizedPlatform
216
+ });
217
+
218
+ let matches = scopedSamples.filter((entry) => {
219
+ const entryId = getSampleIdFromUri(entry.uri);
220
+ return entryId && entryId.toLowerCase() === sampleQuery.toLowerCase();
221
+ });
222
+
223
+ if (matches.length === 0) {
224
+ matches = await searchResources({
225
+ query: sample_id,
226
+ product: normalizedProduct,
227
+ edition: normalizedEdition,
228
+ platform: normalizedPlatform,
229
+ type: "sample",
230
+ limit: maxResults
231
+ });
232
+ }
233
+
234
+ const selected = matches.slice(0, maxResults);
235
+ if (selected.length === 0) {
236
+ const suggestions = await getSampleSuggestions({
237
+ query: sample_id,
238
+ product: normalizedProduct,
239
+ edition: normalizedEdition,
240
+ platform: normalizedPlatform,
241
+ limit: maxResults
242
+ });
243
+
244
+ const content = [{
245
+ type: "text",
246
+ text: suggestions.length
247
+ ? `No exact sample match for "${sample_id}". Related samples:`
248
+ : `No samples found for "${sample_id}". Try list_samples or search.`
249
+ }];
250
+
251
+ for (const entry of suggestions) {
252
+ const sampleId = getSampleIdFromUri(entry.uri);
253
+ content.push({
254
+ type: "resource_link",
255
+ uri: entry.uri,
256
+ name: entry.title,
257
+ description: `${entry.type.toUpperCase()} | ${formatScopeLabel(entry)} | v${entry.version}${formatScoreLabel(entry)} | sample_id: ${sampleId || "n/a"}`,
258
+ mimeType: entry.mimeType,
259
+ annotations: {
260
+ audience: ["assistant"],
261
+ priority: 0.6
262
+ }
263
+ });
264
+ }
265
+
266
+ if (suggestions.length) {
267
+ const plainLines = suggestions.map((entry, index) => {
268
+ const sampleId = getSampleIdFromUri(entry.uri);
269
+ const sampleNote = sampleId ? ` (sample_id: ${sampleId})` : "";
270
+ const scoreNote = formatScoreNote(entry);
271
+ return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote}`;
272
+ });
273
+ content.push({
274
+ type: "text",
275
+ text: ["Plain URIs (copy/paste):", ...plainLines].join("\n")
276
+ });
277
+ }
278
+
279
+ return { isError: true, content };
280
+ }
281
+
282
+ const payload = selected.map((entry) => ({
283
+ sample_id: getSampleIdFromUri(entry.uri),
284
+ uri: entry.uri,
285
+ product: entry.product,
286
+ edition: getDisplayEdition(entry.edition),
287
+ platform: getDisplayPlatform(entry.platform),
288
+ version: entry.version,
289
+ title: entry.title,
290
+ summary: entry.summary
291
+ }));
292
+
293
+ const lines = [
294
+ `Found ${selected.length} match(es) for "${sample_id}".`,
295
+ "Plain URIs (copy/paste):",
296
+ ...selected.map((entry, index) => {
297
+ const sampleId = getSampleIdFromUri(entry.uri);
298
+ const sampleNote = sampleId ? ` (sample_id: ${sampleId})` : "";
299
+ const scoreNote = formatScoreNote(entry);
300
+ return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote}`;
301
+ })
302
+ ];
303
+
304
+ const output = {
305
+ query: sample_id,
306
+ returned: payload.length,
307
+ samples: payload
308
+ };
309
+
310
+ const content = [{
311
+ type: "text",
312
+ text: `${lines.join("\n")}\n\nJSON:\n${JSON.stringify(output, null, 2)}`
313
+ }];
314
+
315
+ for (const entry of selected) {
316
+ const sampleId = getSampleIdFromUri(entry.uri);
317
+ content.push({
318
+ type: "resource_link",
319
+ uri: entry.uri,
320
+ name: entry.title,
321
+ description: `${entry.type.toUpperCase()} | ${formatScopeLabel(entry)} | v${entry.version}${formatScoreLabel(entry)} | sample_id: ${sampleId || "n/a"}`,
322
+ mimeType: entry.mimeType,
323
+ annotations: {
324
+ audience: ["assistant"],
325
+ priority: 0.8
326
+ }
327
+ });
328
+ }
329
+
330
+ return { content };
331
+ }
332
+ );
333
+ }
@@ -0,0 +1,136 @@
1
+ import { z } from "zod";
2
+
3
+ export function registerVersionTools({
4
+ server,
5
+ ensureLatestMajor,
6
+ normalizeProduct,
7
+ normalizePlatform,
8
+ normalizeEdition,
9
+ LATEST_MAJOR,
10
+ LATEST_VERSIONS
11
+ }) {
12
+ server.registerTool(
13
+ "resolve_version",
14
+ {
15
+ title: "Resolve Version",
16
+ description: "Resolve a concrete latest-major version for a product/edition/platform.",
17
+ inputSchema: {
18
+ product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
19
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
20
+ platform: z.string().optional().describe("Platform: android, ios, maui, react-native, flutter, js, python, cpp, java, dotnet, nodejs, angular, blazor, capacitor, electron, es6, native-ts, next, nuxt, pwa, react, requirejs, svelte, vue, webview, spm, core"),
21
+ constraint: z.string().optional().describe("Version constraint, e.g., latest, 11.x, 10"),
22
+ feature: z.string().optional().describe("Optional feature hint")
23
+ }
24
+ },
25
+ async ({ product, edition, platform, constraint, feature }) => {
26
+ const normalizedProduct = normalizeProduct(product);
27
+ const normalizedPlatform = normalizePlatform(platform);
28
+ const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
29
+
30
+ if (!["dcv", "dbr", "dwt", "ddv"].includes(normalizedProduct)) {
31
+ return {
32
+ isError: true,
33
+ content: [{ type: "text", text: `Unknown product "${product}". Use dcv, dbr, dwt, or ddv.` }]
34
+ };
35
+ }
36
+
37
+ const policy = ensureLatestMajor({
38
+ product: normalizedProduct,
39
+ version: constraint,
40
+ query: feature,
41
+ edition: normalizedEdition,
42
+ platform: normalizedPlatform
43
+ });
44
+
45
+ if (!policy.ok) {
46
+ return { isError: true, content: [{ type: "text", text: policy.message }] };
47
+ }
48
+
49
+ if (normalizedProduct === "dcv") {
50
+ if (!normalizedEdition) {
51
+ const lines = [
52
+ "# DCV Version Resolution",
53
+ `- Latest major: v${LATEST_MAJOR.dcv}`,
54
+ `- Core: ${LATEST_VERSIONS.dcv.core}`,
55
+ `- Web: ${LATEST_VERSIONS.dcv.web}`,
56
+ `- Mobile: ${LATEST_VERSIONS.dcv.mobile}`,
57
+ `- Server/Desktop: ${LATEST_VERSIONS.dcv.server}`,
58
+ "",
59
+ "Specify edition/platform to resolve a single version."
60
+ ];
61
+ return { content: [{ type: "text", text: lines.join("\n") }] };
62
+ }
63
+
64
+ const resolved = LATEST_VERSIONS.dcv[normalizedEdition];
65
+ if (!resolved) {
66
+ return {
67
+ isError: true,
68
+ content: [{ type: "text", text: `Edition "${normalizedEdition}" is not hosted by this MCP server.` }]
69
+ };
70
+ }
71
+
72
+ const displayPlatform = normalizedPlatform === "web" ? "js" : normalizedPlatform;
73
+ const lines = [
74
+ "# DCV Version Resolution",
75
+ `- Edition: ${normalizedEdition}`,
76
+ displayPlatform ? `- Platform: ${displayPlatform}` : "",
77
+ `- Latest major: v${LATEST_MAJOR.dcv}`,
78
+ `- Resolved version: ${resolved}`
79
+ ].filter(Boolean);
80
+
81
+ return { content: [{ type: "text", text: lines.join("\n") }] };
82
+ }
83
+
84
+ if (normalizedProduct === "dbr") {
85
+ if (!normalizedEdition) {
86
+ const lines = [
87
+ "# DBR Version Resolution",
88
+ `- Latest major: v${LATEST_MAJOR.dbr}`,
89
+ `- Mobile: ${LATEST_VERSIONS.dbr.mobile}`,
90
+ `- Web: ${LATEST_VERSIONS.dbr.web}`,
91
+ `- Server/Desktop: ${LATEST_VERSIONS.dbr.server}`,
92
+ "",
93
+ "Specify edition/platform to resolve a single version."
94
+ ];
95
+ return { content: [{ type: "text", text: lines.join("\n") }] };
96
+ }
97
+
98
+ const resolved = LATEST_VERSIONS.dbr[normalizedEdition];
99
+ if (!resolved) {
100
+ return {
101
+ isError: true,
102
+ content: [{ type: "text", text: `Edition "${normalizedEdition}" is not hosted by this MCP server.` }]
103
+ };
104
+ }
105
+
106
+ const displayPlatform = normalizedPlatform === "web" ? "js" : normalizedPlatform;
107
+ const lines = [
108
+ "# DBR Version Resolution",
109
+ `- Edition: ${normalizedEdition}`,
110
+ displayPlatform ? `- Platform: ${displayPlatform}` : "",
111
+ `- Latest major: v${LATEST_MAJOR.dbr}`,
112
+ `- Resolved version: ${resolved}`
113
+ ].filter(Boolean);
114
+
115
+ return { content: [{ type: "text", text: lines.join("\n") }] };
116
+ }
117
+
118
+ if (normalizedProduct === "dwt") {
119
+ const lines = [
120
+ "# DWT Version Resolution",
121
+ `- Latest major: v${LATEST_MAJOR.dwt}`,
122
+ `- Resolved version: ${LATEST_VERSIONS.dwt.web}`
123
+ ];
124
+ return { content: [{ type: "text", text: lines.join("\n") }] };
125
+ }
126
+
127
+ const lines = [
128
+ "# DDV Version Resolution",
129
+ `- Latest major: v${LATEST_MAJOR.ddv}`,
130
+ `- Resolved version: ${LATEST_VERSIONS.ddv.web}`
131
+ ];
132
+
133
+ return { content: [{ type: "text", text: lines.join("\n") }] };
134
+ }
135
+ );
136
+ }
@@ -0,0 +1,84 @@
1
+ import { createServer as createHttpServer } from "node:http";
2
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3
+ import { logEvent } from "../../observability/logging.js";
4
+
5
+ async function startHttpServer({ host, port, mcpPath, createServer }) {
6
+ logEvent("transport", "server_start", { mode: "http", host, port, path: mcpPath });
7
+
8
+ const httpServer = createHttpServer(async (req, res) => {
9
+ const requestUrl = new URL(req.url || "/", `http://${host}:${port}`);
10
+ if (requestUrl.pathname !== mcpPath) {
11
+ res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
12
+ res.end("Not Found");
13
+ return;
14
+ }
15
+
16
+ const server = createServer();
17
+ const transport = new StreamableHTTPServerTransport({
18
+ sessionIdGenerator: undefined
19
+ });
20
+
21
+ let closed = false;
22
+ const closeResources = async () => {
23
+ if (closed) return;
24
+ closed = true;
25
+ try {
26
+ await transport.close();
27
+ } catch {}
28
+ try {
29
+ await server.close();
30
+ } catch {}
31
+ };
32
+
33
+ res.on("close", () => {
34
+ void closeResources();
35
+ });
36
+
37
+ try {
38
+ await server.connect(transport);
39
+ await transport.handleRequest(req, res);
40
+ } catch (error) {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ logEvent("transport", "request_error", { mode: "http", message }, { level: "error" });
43
+ if (!res.headersSent) {
44
+ res.writeHead(500, { "content-type": "application/json; charset=utf-8" });
45
+ res.end(JSON.stringify({
46
+ jsonrpc: "2.0",
47
+ error: {
48
+ code: -32603,
49
+ message: "Internal server error"
50
+ },
51
+ id: null
52
+ }));
53
+ }
54
+ await closeResources();
55
+ }
56
+ });
57
+
58
+ await new Promise((resolve, reject) => {
59
+ httpServer.once("error", reject);
60
+ httpServer.listen(port, host, () => {
61
+ httpServer.off("error", reject);
62
+ resolve();
63
+ });
64
+ });
65
+
66
+ const shutdown = async (signal) => {
67
+ logEvent("transport", "server_shutdown", { mode: "http", signal });
68
+ await new Promise((resolve) => {
69
+ httpServer.close(() => resolve());
70
+ });
71
+ process.exit(0);
72
+ };
73
+
74
+ process.on("SIGINT", () => {
75
+ void shutdown("SIGINT");
76
+ });
77
+ process.on("SIGTERM", () => {
78
+ void shutdown("SIGTERM");
79
+ });
80
+
81
+ return { httpServer };
82
+ }
83
+
84
+ export { startHttpServer };
@@ -0,0 +1,12 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
+ import { logEvent } from "../../observability/logging.js";
3
+
4
+ async function startStdioServer({ createServer }) {
5
+ logEvent("transport", "server_start", { mode: "stdio" });
6
+ const server = createServer();
7
+ const transport = new StdioServerTransport();
8
+ await server.connect(transport);
9
+ return { server, transport };
10
+ }
11
+
12
+ export { startStdioServer };