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.
- package/.env.example +35 -9
- package/README.md +156 -497
- package/package.json +13 -7
- package/scripts/prebuild-rag-index.mjs +1 -1
- package/scripts/run-gemini-tests.mjs +1 -1
- package/scripts/sync-submodules.mjs +1 -1
- package/scripts/verify-doc-resources.mjs +79 -0
- package/src/data/bootstrap.js +475 -0
- package/src/data/download-utils.js +99 -0
- package/src/data/hydration-mode.js +15 -0
- package/src/data/hydration-policy.js +39 -0
- package/src/data/repo-map.js +149 -0
- package/src/{data-root.js → data/root.js} +1 -1
- package/src/{submodule-sync.js → data/submodule-sync.js} +1 -1
- package/src/index.js +49 -1499
- package/src/observability/logging.js +51 -0
- package/src/rag/config.js +96 -0
- package/src/rag/index.js +266 -0
- package/src/rag/lexical-provider.js +170 -0
- package/src/rag/logger.js +46 -0
- package/src/rag/profile-config.js +48 -0
- package/src/rag/providers.js +585 -0
- package/src/rag/search-utils.js +166 -0
- package/src/rag/vector-cache.js +323 -0
- package/src/server/create-server.js +168 -0
- package/src/server/helpers/server-helpers.js +33 -0
- package/src/{resource-index → server/resource-index}/paths.js +2 -2
- package/src/{resource-index → server/resource-index}/samples.js +9 -1
- package/src/{resource-index.js → server/resource-index.js} +158 -93
- package/src/server/resources/register-resources.js +56 -0
- package/src/server/runtime-config.js +66 -0
- package/src/server/tools/register-index-tools.js +130 -0
- package/src/server/tools/register-project-tools.js +305 -0
- package/src/server/tools/register-quickstart-tools.js +572 -0
- package/src/server/tools/register-sample-tools.js +333 -0
- package/src/server/tools/register-version-tools.js +136 -0
- package/src/server/transports/http.js +84 -0
- package/src/server/transports/stdio.js +12 -0
- package/src/data-bootstrap.js +0 -255
- package/src/rag.js +0 -1203
- /package/src/{gemini-retry.js → rag/gemini-retry.js} +0 -0
- /package/src/{normalizers.js → server/normalizers.js} +0 -0
- /package/src/{resource-index → server/resource-index}/builders.js +0 -0
- /package/src/{resource-index → server/resource-index}/config.js +0 -0
- /package/src/{resource-index → server/resource-index}/docs-loader.js +0 -0
- /package/src/{resource-index → server/resource-index}/uri.js +0 -0
- /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 };
|