@futdevpro/nts-dynamo 1.15.58 → 1.15.60
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/.dynamo/logs/cicd-pipeline/output.log +1622 -1705
- package/.dynamo/logs/cicd-pipeline/status.json +30 -30
- package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.d.ts.map +1 -1
- package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.js +2 -2
- package/build/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.js.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts +1 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.js.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.d.ts.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.js.map +1 -1
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.d.ts +1 -1
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.d.ts.map +1 -1
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.js +3 -3
- package/build/_modules/ai/_services/lmstudio-embedding.control-service.js.map +1 -1
- package/build/_modules/ai/index.d.ts +2 -0
- package/build/_modules/ai/index.d.ts.map +1 -1
- package/build/_modules/ai/index.js +4 -0
- package/build/_modules/ai/index.js.map +1 -1
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.d.ts +17 -17
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.d.ts.map +1 -1
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.js +21 -21
- package/build/_modules/data-readers/_collections/dynts-sqlite-reader.util.js.map +1 -1
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.d.ts +4 -4
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.js +5 -5
- package/build/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts +4 -4
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js +4 -4
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts +6 -6
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js +5 -5
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js.map +1 -1
- package/build/_modules/mcp/_models/interfaces/dynts-mcp.interface.d.ts +1 -1
- package/build/_modules/mcp/_models/interfaces/dynts-mcp.interface.js +1 -1
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.d.ts +4 -4
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.d.ts.map +1 -1
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.js +6 -6
- package/build/_modules/mcp/_services/dynts-mcp-server.service-base.js.map +1 -1
- package/build/_modules/mcp/_services/dynts-mcp.adapter.d.ts +11 -11
- package/build/_modules/mcp/_services/dynts-mcp.adapter.d.ts.map +1 -1
- package/build/_modules/mcp/_services/dynts-mcp.adapter.js +16 -11
- package/build/_modules/mcp/_services/dynts-mcp.adapter.js.map +1 -1
- package/build/_modules/mcp/index.js +1 -1
- package/build/_modules/mcp/index.js.map +1 -1
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.d.ts +3 -3
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.d.ts.map +1 -1
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.js +4 -4
- package/build/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.js.map +1 -1
- package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.d.ts.map +1 -1
- package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.js +9 -0
- package/build/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.js.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.d.ts +3 -3
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.d.ts.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.js +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.control-service.js.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.d.ts +7 -7
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.d.ts.map +1 -1
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.js +2 -2
- package/build/_modules/scoped-config/_services/dynts-scoped-config.data-service.js.map +1 -1
- package/package.json +1 -1
- package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +39 -7
- package/src/_modules/ai/_services/ai-embedding-mock.service.ts +18 -4
- package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +4 -0
- package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +26 -5
- package/src/_modules/ai/index.ts +5 -0
- package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +145 -130
- package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +131 -120
- package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +6 -5
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +35 -35
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +9 -5
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +11 -11
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +19 -17
- package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +1 -1
- package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +123 -114
- package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +44 -39
- package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +114 -103
- package/src/_modules/mcp/index.ts +1 -1
- package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +5 -4
- package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +0 -2
- package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +19 -13
- package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +37 -21
- package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +11 -6
- package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.ts +17 -14
|
@@ -3,7 +3,7 @@ import { DyFM_EnvironmentFlag } from '@futdevpro/fsm-dynamo';
|
|
|
3
3
|
|
|
4
4
|
import { DyNTS_global_settings } from '../../../_collections/global-settings.const';
|
|
5
5
|
import { DyNTS_GlobalService } from '../../../_services/core/global.service';
|
|
6
|
-
import {
|
|
6
|
+
import { DyNTS_LVS_VectorPersist } from '../_models/data-models/lvs-vector-persist.data-model';
|
|
7
7
|
import { DyNTS_LVS_VectorPersist_DataService } from './lvs-vector-persist.data-service';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -36,7 +36,7 @@ describe('| DyNTS_LVS_VectorPersist_DataService', () => {
|
|
|
36
36
|
describe('| constructor', () => {
|
|
37
37
|
|
|
38
38
|
it('| should create the service with a data model', () => {
|
|
39
|
-
const data:
|
|
39
|
+
const data: DyNTS_LVS_VectorPersist = new DyNTS_LVS_VectorPersist({
|
|
40
40
|
collectionKey: 'pool-a',
|
|
41
41
|
vectorId: 'v1',
|
|
42
42
|
embedding: [1, 2, 3],
|
|
@@ -56,7 +56,7 @@ describe('| DyNTS_LVS_VectorPersist_DataService', () => {
|
|
|
56
56
|
new DyNTS_LVS_VectorPersist_DataService({ issuer: 'issuer-123' });
|
|
57
57
|
|
|
58
58
|
expect(service).toBeInstanceOf(DyNTS_LVS_VectorPersist_DataService);
|
|
59
|
-
expect(service.data).toBeInstanceOf(
|
|
59
|
+
expect(service.data).toBeInstanceOf(DyNTS_LVS_VectorPersist);
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -66,12 +66,12 @@ describe('| DyNTS_LVS_VectorPersist_DataService', () => {
|
|
|
66
66
|
const service: DyNTS_LVS_VectorPersist_DataService =
|
|
67
67
|
new DyNTS_LVS_VectorPersist_DataService({ issuer: 'issuer-123' });
|
|
68
68
|
|
|
69
|
-
const records:
|
|
70
|
-
new
|
|
69
|
+
const records: DyNTS_LVS_VectorPersist[] = [
|
|
70
|
+
new DyNTS_LVS_VectorPersist({ collectionKey: 'pool-a', vectorId: 'v1', embedding: [1, 0] }),
|
|
71
71
|
];
|
|
72
72
|
const findSpy = spyOn(service, 'findDataList').and.returnValue(Promise.resolve(records));
|
|
73
73
|
|
|
74
|
-
const result:
|
|
74
|
+
const result: DyNTS_LVS_VectorPersist[] = await service.findByCollectionKey('pool-a');
|
|
75
75
|
|
|
76
76
|
expect(findSpy).toHaveBeenCalledTimes(1);
|
|
77
77
|
const filterArg = findSpy.calls.mostRecent().args[0] as { collectionKey: string };
|
|
@@ -86,7 +86,7 @@ describe('| DyNTS_LVS_VectorPersist_DataService', () => {
|
|
|
86
86
|
const service: DyNTS_LVS_VectorPersist_DataService =
|
|
87
87
|
new DyNTS_LVS_VectorPersist_DataService({ issuer: 'issuer-123' });
|
|
88
88
|
|
|
89
|
-
const existing:
|
|
89
|
+
const existing: DyNTS_LVS_VectorPersist = new DyNTS_LVS_VectorPersist({
|
|
90
90
|
collectionKey: 'pool-a', vectorId: 'v1', embedding: [1, 2, 3],
|
|
91
91
|
});
|
|
92
92
|
existing._id = 'rec-1';
|
|
@@ -118,14 +118,14 @@ describe('| DyNTS_LVS_VectorPersist_DataService', () => {
|
|
|
118
118
|
spyOn(service, 'findData').and.returnValue(Promise.resolve(null));
|
|
119
119
|
const updateSpy = spyOn(service, 'updateData').and.returnValue(Promise.resolve());
|
|
120
120
|
const saveSpy = spyOn(service, 'saveData')
|
|
121
|
-
.and.callFake((data?:
|
|
122
|
-
Promise.resolve(data as
|
|
121
|
+
.and.callFake((data?: DyNTS_LVS_VectorPersist) =>
|
|
122
|
+
Promise.resolve(data as DyNTS_LVS_VectorPersist));
|
|
123
123
|
|
|
124
124
|
await service.upsertVector({ collectionKey: 'pool-a', vectorId: 'v2', embedding: [0.1, 0.2] });
|
|
125
125
|
|
|
126
126
|
expect(updateSpy).not.toHaveBeenCalled();
|
|
127
127
|
expect(saveSpy).toHaveBeenCalledTimes(1);
|
|
128
|
-
const written = saveSpy.calls.mostRecent().args[0] as
|
|
128
|
+
const written = saveSpy.calls.mostRecent().args[0] as DyNTS_LVS_VectorPersist;
|
|
129
129
|
expect(written.vectorId).toBe('v2');
|
|
130
130
|
expect(written.collectionKey).toBe('pool-a');
|
|
131
131
|
expect(written.embedding).toEqual([0.1, 0.2]);
|
|
@@ -139,7 +139,7 @@ describe('| DyNTS_LVS_VectorPersist_DataService', () => {
|
|
|
139
139
|
const service: DyNTS_LVS_VectorPersist_DataService =
|
|
140
140
|
new DyNTS_LVS_VectorPersist_DataService({ issuer: 'issuer-123' });
|
|
141
141
|
|
|
142
|
-
const existing:
|
|
142
|
+
const existing: DyNTS_LVS_VectorPersist = new DyNTS_LVS_VectorPersist({
|
|
143
143
|
collectionKey: 'pool-a', vectorId: 'v1', embedding: [1, 0],
|
|
144
144
|
});
|
|
145
145
|
existing._id = 'rec-1';
|
|
@@ -3,8 +3,8 @@ import { DyFM_DBFilter, DyFM_DBFilterSimple } from '@futdevpro/fsm-dynamo';
|
|
|
3
3
|
|
|
4
4
|
import { DyNTS_DataService } from '../../../_services/base/data.service';
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
DyNTS_LVS_VectorPersist,
|
|
7
|
+
DyNTS_lvsVectorPersist_dataParams
|
|
8
8
|
} from '../_models/data-models/lvs-vector-persist.data-model';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -14,36 +14,36 @@ import {
|
|
|
14
14
|
*
|
|
15
15
|
* **FIGYELEM (memory: dynts_dataservice_eager_resolve):** a `DyNTS_DataService` base-ctor EAGER
|
|
16
16
|
* `getDBService`-t hív → a hívó NEM tarthat élő data-service-példányt mezőként; minden művelet előtt
|
|
17
|
-
* lazy `new DyNTS_LVS_VectorPersist_DataService(...)` kell (a `
|
|
17
|
+
* lazy `new DyNTS_LVS_VectorPersist_DataService(...)` kell (a `DyNTS_LVS_PersistentVectorPool_ControlService` így jár el).
|
|
18
18
|
*
|
|
19
19
|
* **Atomikus írás (memory: mongoose_mixed_atomic_write):** az upsert a `(collectionKey, vectorId)`
|
|
20
20
|
* logikai-kulcson MEGLÉVŐ rekordot `updateOne({...},{$set:{embedding,dimensions,metadata}})`-tel
|
|
21
21
|
* frissíti (a Mixed `metadata` `findOne→mutate→save` SILENT-DROP-ja ellen); ÚJ rekordot `saveData`-val ír.
|
|
22
22
|
*/
|
|
23
|
-
export class DyNTS_LVS_VectorPersist_DataService extends DyNTS_DataService<
|
|
23
|
+
export class DyNTS_LVS_VectorPersist_DataService extends DyNTS_DataService<DyNTS_LVS_VectorPersist> {
|
|
24
24
|
|
|
25
25
|
constructor(
|
|
26
26
|
set: {
|
|
27
|
-
data?:
|
|
27
|
+
data?: DyNTS_LVS_VectorPersist,
|
|
28
28
|
issuer: string,
|
|
29
29
|
},
|
|
30
30
|
) {
|
|
31
31
|
super(
|
|
32
|
-
set.data instanceof
|
|
32
|
+
set.data instanceof DyNTS_LVS_VectorPersist
|
|
33
33
|
? set.data
|
|
34
|
-
: new
|
|
35
|
-
|
|
34
|
+
: new DyNTS_LVS_VectorPersist(set.data),
|
|
35
|
+
DyNTS_lvsVectorPersist_dataParams,
|
|
36
36
|
set.issuer,
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Egy (logikai) pool ÖSSZES perzistált, nem-soft-delete-elt vektor-rekordja — a boot-hidratáláshoz
|
|
42
|
-
* (a `
|
|
42
|
+
* (a `DyNTS_LVS_PersistentVectorPool_ControlService.hydrateFromMongo` ezt tölti az in-memory pool-ba).
|
|
43
43
|
*/
|
|
44
|
-
async findByCollectionKey(collectionKey: string): Promise<
|
|
44
|
+
async findByCollectionKey(collectionKey: string): Promise<DyNTS_LVS_VectorPersist[]> {
|
|
45
45
|
return this.findDataList(
|
|
46
|
-
{ collectionKey: collectionKey }
|
|
46
|
+
{ collectionKey: collectionKey },
|
|
47
47
|
true,
|
|
48
48
|
);
|
|
49
49
|
}
|
|
@@ -59,14 +59,14 @@ export class DyNTS_LVS_VectorPersist_DataService extends DyNTS_DataService<DyNTS
|
|
|
59
59
|
embedding: number[],
|
|
60
60
|
metadata?: unknown,
|
|
61
61
|
}): Promise<void> {
|
|
62
|
-
const existing:
|
|
63
|
-
{ collectionKey: set.collectionKey, vectorId: set.vectorId }
|
|
62
|
+
const existing: DyNTS_LVS_VectorPersist = await this.findData(
|
|
63
|
+
{ collectionKey: set.collectionKey, vectorId: set.vectorId },
|
|
64
64
|
true,
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
if (existing?._id) {
|
|
68
68
|
await this.updateData({
|
|
69
|
-
filterBy: { _id: existing._id }
|
|
69
|
+
filterBy: { _id: existing._id },
|
|
70
70
|
update: {
|
|
71
71
|
$set: {
|
|
72
72
|
embedding: set.embedding,
|
|
@@ -75,11 +75,12 @@ export class DyNTS_LVS_VectorPersist_DataService extends DyNTS_DataService<DyNTS
|
|
|
75
75
|
},
|
|
76
76
|
},
|
|
77
77
|
});
|
|
78
|
+
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
await this.saveData(
|
|
82
|
-
new
|
|
83
|
+
new DyNTS_LVS_VectorPersist({
|
|
83
84
|
collectionKey: set.collectionKey,
|
|
84
85
|
vectorId: set.vectorId,
|
|
85
86
|
embedding: set.embedding,
|
|
@@ -95,10 +96,11 @@ export class DyNTS_LVS_VectorPersist_DataService extends DyNTS_DataService<DyNTS
|
|
|
95
96
|
* `removeVector`-ja után — a tartós tár is törli). `addArchive` nincs → `absolute` true-val töröl.
|
|
96
97
|
*/
|
|
97
98
|
async removeVector(collectionKey: string, vectorId: string): Promise<void> {
|
|
98
|
-
const existing:
|
|
99
|
-
{ collectionKey: collectionKey, vectorId: vectorId }
|
|
99
|
+
const existing: DyNTS_LVS_VectorPersist = await this.findData(
|
|
100
|
+
{ collectionKey: collectionKey, vectorId: vectorId },
|
|
100
101
|
true,
|
|
101
102
|
);
|
|
103
|
+
|
|
102
104
|
if (existing?._id) {
|
|
103
105
|
await this.deleteData(existing._id, true);
|
|
104
106
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Boundary: a `DyNTS_McpToolDefinition` + a `DyNTS_Mcp_Adapter` egyetlen `registerTool` choke-pontot
|
|
7
7
|
* ad — a hivatalos `@modelcontextprotocol/sdk` KIZÁRÓLAG az adaptor mögött él, így egy jövőbeli
|
|
8
|
-
* transport-/SDK-csere NEM-breaking (csak az adaptor cserélődik, a `
|
|
8
|
+
* transport-/SDK-csere NEM-breaking (csak az adaptor cserélődik, a `DyNTS_Mcp_Server_ServiceBase` + a
|
|
9
9
|
* consumer-tool-ok változatlanok). A consumer a SAJÁT tool-jait regisztrálja (a bedrock NEM definiál
|
|
10
10
|
* domain-tool-okat — csak a server-base + a transport + a registry).
|
|
11
11
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DyNTS_Mcp_Server_ServiceBase } from './dynts-mcp-server.service-base';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
DyNTS_Mcp_CallResult,
|
|
4
|
+
DyNTS_Mcp_ServerInfo,
|
|
5
|
+
DyNTS_Mcp_ToolDefinition
|
|
6
6
|
} from '../_models/interfaces/dynts-mcp.interface';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* CONTRACT — `
|
|
9
|
+
* CONTRACT — `DyNTS_Mcp_Server_ServiceBase` tool-registry + dispatch (BFR-AM-003). A transportot NEM nyitjuk
|
|
10
10
|
* meg (nincs valódi stdio-szerver) — a `build()` + `callTool()` közvetlen dispatch-ét teszteljük,
|
|
11
11
|
* ami UGYANAZ a logika, mint a SDK `tools/call`-ja:
|
|
12
12
|
* - N tool regisztrálása → `getAdvertisedToolNames` PONTOSAN ezeket listázza,
|
|
@@ -15,128 +15,137 @@ import {
|
|
|
15
15
|
* - handler-dobott hiba → `isError:true` fordítás (a payload tartalmazza a message-et),
|
|
16
16
|
* - `isError:true` outcome → `isError:true` call-result.
|
|
17
17
|
*/
|
|
18
|
-
describe('
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
describe('| DyNTS_Mcp_Server_ServiceBase (tool-registry + dispatch, BFR-AM-003)', () => {
|
|
19
|
+
|
|
20
|
+
/** A handler-hívások nyoma (melyik tool, milyen args) — a dispatch-helyesség igazolásához. */
|
|
21
|
+
let calls: { name: string; args: unknown }[];
|
|
22
|
+
|
|
23
|
+
/** Egy teszt-tool-definíció gyára (a handler `data`-ba teszi a tool-nevet + az args-ot). */
|
|
24
|
+
const makeTool = (name: string): DyNTS_Mcp_ToolDefinition => ({
|
|
25
|
+
name: name,
|
|
26
|
+
description: `Test tool ${name}.`,
|
|
27
|
+
inputSchema: { type: 'object', properties: { x: { type: 'string' } } },
|
|
28
|
+
handler: async (args: unknown) => {
|
|
29
|
+
calls.push({ name: name, args: args });
|
|
30
|
+
|
|
31
|
+
return { data: { tool: name, echo: args } };
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** A teszt-szerver: 3 tool + egy szándékosan dobó tool. */
|
|
36
|
+
class TestServer extends DyNTS_Mcp_Server_ServiceBase {
|
|
37
|
+
protected getServerInfo(): DyNTS_Mcp_ServerInfo {
|
|
38
|
+
return { name: 'test-mcp', version: '0.0.1-spec' };
|
|
39
|
+
}
|
|
40
|
+
protected getTools(): DyNTS_Mcp_ToolDefinition[] {
|
|
41
|
+
return [
|
|
42
|
+
makeTool('alpha'),
|
|
43
|
+
makeTool('beta'),
|
|
44
|
+
{
|
|
45
|
+
name: 'boom',
|
|
46
|
+
description: 'Always throws.',
|
|
47
|
+
inputSchema: { type: 'object' },
|
|
48
|
+
handler: async () => {
|
|
49
|
+
throw new Error('kaboom from handler');
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'structuredError',
|
|
54
|
+
description: 'Returns a structured error outcome.',
|
|
55
|
+
inputSchema: { type: 'object' },
|
|
56
|
+
handler: async () => ({ data: { reason: 'nope' }, isError: true }),
|
|
31
57
|
},
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let server: TestServer;
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
calls = [];
|
|
66
|
+
server = new TestServer();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** A `content[0].text` JSON-payload kiparse-olása (a transport text-JSON shape-je). */
|
|
70
|
+
const payloadOf = (result: DyNTS_Mcp_CallResult): { [key: string]: unknown } =>
|
|
71
|
+
JSON.parse(result.content[0].text) as { [key: string]: unknown };
|
|
72
|
+
|
|
73
|
+
describe('| registry — getTools → advertised', () => {
|
|
74
|
+
it('| a deklarált tool-okat hirdeti build ELŐTT is (determinisztikus)', () => {
|
|
75
|
+
expect(server.getAdvertisedToolNames().sort())
|
|
76
|
+
.toEqual([ 'alpha', 'beta', 'boom', 'structuredError' ]);
|
|
32
77
|
});
|
|
33
78
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{
|
|
44
|
-
name: 'boom',
|
|
45
|
-
description: 'Always throws.',
|
|
46
|
-
inputSchema: { type: 'object' },
|
|
47
|
-
handler: async () => {
|
|
48
|
-
throw new Error('kaboom from handler');
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'structuredError',
|
|
53
|
-
description: 'Returns a structured error outcome.',
|
|
54
|
-
inputSchema: { type: 'object' },
|
|
55
|
-
handler: async () => ({ data: { reason: 'nope' }, isError: true }),
|
|
56
|
-
},
|
|
57
|
-
];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
79
|
+
it('| build() után UGYANAZOK a hirdetett tool-ok (idempotens build)', () => {
|
|
80
|
+
const adapter1 = server.build();
|
|
81
|
+
const adapter2 = server.build();
|
|
82
|
+
|
|
83
|
+
expect(adapter1).toBe(adapter2);
|
|
84
|
+
expect(adapter1.getAdvertisedToolNames().sort())
|
|
85
|
+
.toEqual([ 'alpha', 'beta', 'boom', 'structuredError' ]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
60
88
|
|
|
61
|
-
|
|
89
|
+
describe('| dispatch — callTool a megfelelő handler-re route-ol', () => {
|
|
90
|
+
it('| alpha → az alpha handler fut (a data a tool-nevet + az args-ot adja)', async () => {
|
|
91
|
+
const result: DyNTS_Mcp_CallResult = await server.callTool({ name: 'alpha', arguments: { x: '1' } });
|
|
62
92
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
expect(result.isError).toBe(false);
|
|
94
|
+
expect(calls).toEqual([{ name: 'alpha', args: { x: '1' } }]);
|
|
95
|
+
expect(payloadOf(result)).toEqual({ tool: 'alpha', echo: { x: '1' } });
|
|
66
96
|
});
|
|
67
97
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
98
|
+
it('| beta → a beta handler fut (a dispatch a `name` szerint választ)', async () => {
|
|
99
|
+
await server.callTool({ name: 'beta', arguments: { x: '2' } });
|
|
100
|
+
expect(calls).toEqual([{ name: 'beta', args: { x: '2' } }]);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('| strukturált hiba (no-silent-failure)', () => {
|
|
105
|
+
it('| ismeretlen tool → isError:true + advertised-lista a message-ben (NEM néma/crash)', async () => {
|
|
106
|
+
const result: DyNTS_Mcp_CallResult = await server.callTool({ name: 'does_not_exist' });
|
|
71
107
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const adapter2 = server.build();
|
|
81
|
-
expect(adapter1).toBe(adapter2);
|
|
82
|
-
expect(adapter1.getAdvertisedToolNames().sort())
|
|
83
|
-
.toEqual(['alpha', 'beta', 'boom', 'structuredError']);
|
|
84
|
-
});
|
|
108
|
+
expect(result.isError).toBe(true);
|
|
109
|
+
const payload = payloadOf(result) as { ok: boolean; error: { message: string } };
|
|
110
|
+
|
|
111
|
+
expect(payload.ok).toBe(false);
|
|
112
|
+
expect(payload.error.message).toContain('does_not_exist');
|
|
113
|
+
expect(payload.error.message).toContain('alpha');
|
|
114
|
+
// egyetlen ismeretlen tool sem futtatott handlert.
|
|
115
|
+
expect(calls.length).toBe(0);
|
|
85
116
|
});
|
|
86
117
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
it('beta → a beta handler fut (a dispatch a `name` szerint választ)', async () => {
|
|
96
|
-
await server.callTool({ name: 'beta', arguments: { x: '2' } });
|
|
97
|
-
expect(calls).toEqual([{ name: 'beta', args: { x: '2' } }]);
|
|
98
|
-
});
|
|
118
|
+
it('| handler-dobott hiba → isError:true + a hiba message-e a payload-ban (NEM crash)', async () => {
|
|
119
|
+
const result: DyNTS_Mcp_CallResult = await server.callTool({ name: 'boom' });
|
|
120
|
+
|
|
121
|
+
expect(result.isError).toBe(true);
|
|
122
|
+
const payload = payloadOf(result) as { ok: boolean; error: { message: string } };
|
|
123
|
+
|
|
124
|
+
expect(payload.ok).toBe(false);
|
|
125
|
+
expect(payload.error.message).toContain('kaboom from handler');
|
|
99
126
|
});
|
|
100
127
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
expect(payload.ok).toBe(false);
|
|
107
|
-
expect(payload.error.message).toContain('does_not_exist');
|
|
108
|
-
expect(payload.error.message).toContain('alpha');
|
|
109
|
-
// egyetlen ismeretlen tool sem futtatott handlert.
|
|
110
|
-
expect(calls.length).toBe(0);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('handler-dobott hiba → isError:true + a hiba message-e a payload-ban (NEM crash)', async () => {
|
|
114
|
-
const result: DyNTS_Mcp_CallResult = await server.callTool({ name: 'boom' });
|
|
115
|
-
expect(result.isError).toBe(true);
|
|
116
|
-
const payload = payloadOf(result) as { ok: boolean; error: { message: string } };
|
|
117
|
-
expect(payload.ok).toBe(false);
|
|
118
|
-
expect(payload.error.message).toContain('kaboom from handler');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('isError:true outcome → isError:true call-result (a strukturált hiba átmegy)', async () => {
|
|
122
|
-
const result: DyNTS_Mcp_CallResult = await server.callTool({ name: 'structuredError' });
|
|
123
|
-
expect(result.isError).toBe(true);
|
|
124
|
-
expect(payloadOf(result)).toEqual({ reason: 'nope' });
|
|
125
|
-
});
|
|
128
|
+
it('| isError:true outcome → isError:true call-result (a strukturált hiba átmegy)', async () => {
|
|
129
|
+
const result: DyNTS_Mcp_CallResult = await server.callTool({ name: 'structuredError' });
|
|
130
|
+
|
|
131
|
+
expect(result.isError).toBe(true);
|
|
132
|
+
expect(payloadOf(result)).toEqual({ reason: 'nope' });
|
|
126
133
|
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('| start — transport-választás', () => {
|
|
137
|
+
it('| ismeretlen transport → dob (exhaustiveness-guard); stdio nem nyílik a spec-ben', async () => {
|
|
138
|
+
let thrown: unknown;
|
|
127
139
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
expect(thrown).toBeDefined();
|
|
139
|
-
expect((thrown as Error).message).toContain('Unsupported MCP transport');
|
|
140
|
-
});
|
|
140
|
+
try {
|
|
141
|
+
// Szándékosan érvénytelen transport (a típus-szűkítés megkerülésével) — a guard-ágat teszteli,
|
|
142
|
+
// valódi stdio-szervert NEM indítunk.
|
|
143
|
+
await server.start({ transport: 'http' as never });
|
|
144
|
+
} catch (error) {
|
|
145
|
+
thrown = error;
|
|
146
|
+
}
|
|
147
|
+
expect(thrown).toBeDefined();
|
|
148
|
+
expect((thrown as Error).message).toContain('Unsupported MCP transport');
|
|
141
149
|
});
|
|
150
|
+
});
|
|
142
151
|
});
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
DyNTS_Mcp_CallResult,
|
|
5
|
+
DyNTS_Mcp_ServerInfo,
|
|
6
|
+
DyNTS_Mcp_StartOptions,
|
|
7
|
+
DyNTS_Mcp_ToolDefinition
|
|
8
8
|
} from '../_models/interfaces/dynts-mcp.interface';
|
|
9
9
|
import { DyNTS_Mcp_Adapter } from './dynts-mcp.adapter';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* `
|
|
12
|
+
* `DyNTS_Mcp_Server_ServiceBase` (BFR-AM-003) — a **domain-agnosztikus** MCP-szerver-base. Egy bedrock-réteg
|
|
13
13
|
* a hivatalos `@modelcontextprotocol/sdk` fölött: a server-base felel a tool-registry-ért, a
|
|
14
14
|
* `tools/list`/`tools/call` dispatch-ért (az adaptoron át) + a transport-indításért — a SDK KIZÁRÓLAG
|
|
15
15
|
* a `DyNTS_Mcp_Adapter` mögött él (egy jövőbeli transport-/SDK-csere NON-breaking).
|
|
16
16
|
*
|
|
17
|
-
* **Használat (consumer):** a consumer leszármazik (`extends
|
|
17
|
+
* **Használat (consumer):** a consumer leszármazik (`extends DyNTS_Mcp_Server_ServiceBase`) + implementálja a
|
|
18
18
|
* `getServerInfo()` (név/verzió) és `getTools()` (a SAJÁT tool-jai) abstract metódusokat. A bedrock
|
|
19
19
|
* SEMMILYEN domain-tool-t NEM definiál — csak a server-base + a transport + a registry. (Pl. a FAM a
|
|
20
20
|
* `read`/`write`/`capabilities` tool-jait itt regisztrálja, a bedrock-on kívül.)
|
|
21
21
|
*
|
|
22
22
|
* ```ts
|
|
23
|
-
* class My_McpServer extends
|
|
23
|
+
* class My_McpServer extends DyNTS_Mcp_Server_ServiceBase {
|
|
24
24
|
* protected getServerInfo() { return { name: 'my-app', version: '1.0.0' }; }
|
|
25
25
|
* protected getTools() { return [readTool, writeTool]; }
|
|
26
26
|
* }
|
|
@@ -30,10 +30,10 @@ import { DyNTS_Mcp_Adapter } from './dynts-mcp.adapter';
|
|
|
30
30
|
* **stdio-konvenció:** a stdout a JSON-RPC csatorna — a boot-üzenet + minden log a stderr-re megy (a
|
|
31
31
|
* hívó App a `DyFM_Log`-ot stdio-módban stderr-re konfigurálja).
|
|
32
32
|
*/
|
|
33
|
-
export abstract class
|
|
33
|
+
export abstract class DyNTS_Mcp_Server_ServiceBase {
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
/** Az MCP-adaptor (a regisztrált tool-ok + a transport mögötti SDK-réteg). Lazy a `build()`-ben. */
|
|
36
|
+
private adapter: DyNTS_Mcp_Adapter | null = null;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* A szerver identitása (név/verzió) — a consumer adja. A SDK `serverInfo`-jaként hirdetődik.
|
|
@@ -52,15 +52,17 @@ export abstract class DyNTS_Mcp_ServerBase {
|
|
|
52
52
|
* hirdeti, transport nélkül). Idempotens: ismételt hívás ugyanazt az adaptort adja vissza.
|
|
53
53
|
*/
|
|
54
54
|
public build(): DyNTS_Mcp_Adapter {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
if (this.adapter) {
|
|
56
|
+
return this.adapter;
|
|
57
|
+
}
|
|
58
|
+
const adapter: DyNTS_Mcp_Adapter = new DyNTS_Mcp_Adapter(this.getServerInfo());
|
|
59
|
+
|
|
60
|
+
for (const tool of this.getTools()) {
|
|
61
|
+
adapter.registerTool(tool);
|
|
62
|
+
}
|
|
63
|
+
this.adapter = adapter;
|
|
64
|
+
|
|
65
|
+
return adapter;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
/**
|
|
@@ -69,12 +71,13 @@ export abstract class DyNTS_Mcp_ServerBase {
|
|
|
69
71
|
* a `tools/call` route-ol. A boot-üzenet a stderr-en.
|
|
70
72
|
*/
|
|
71
73
|
public async start(options?: DyNTS_Mcp_StartOptions): Promise<void> {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const transport: 'stdio' = options?.transport ?? 'stdio';
|
|
75
|
+
const adapter: DyNTS_Mcp_Adapter = this.build();
|
|
76
|
+
const info: DyNTS_Mcp_ServerInfo = this.getServerInfo();
|
|
77
|
+
|
|
78
|
+
DyFM_Log.testInfo(`[DyNTS MCP] ${transport} server starting (${info.name} v${info.version}); `
|
|
76
79
|
+ `advertised tools: ${adapter.getAdvertisedToolNames().join(', ')}`);
|
|
77
|
-
|
|
80
|
+
await this.startTransport(adapter, transport);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
/**
|
|
@@ -82,9 +85,9 @@ export abstract class DyNTS_Mcp_ServerBase {
|
|
|
82
85
|
* neveit adja (transport nélkül is determinisztikus).
|
|
83
86
|
*/
|
|
84
87
|
public getAdvertisedToolNames(): string[] {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
return this.adapter
|
|
89
|
+
? this.adapter.getAdvertisedToolNames()
|
|
90
|
+
: this.getTools().map((tool) => tool.name);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
/**
|
|
@@ -93,14 +96,14 @@ export abstract class DyNTS_Mcp_ServerBase {
|
|
|
93
96
|
* tool → strukturált hiba; handler-hiba → `isError:true`).
|
|
94
97
|
*/
|
|
95
98
|
public async callTool(set: { name: string; arguments?: unknown }): Promise<DyNTS_Mcp_CallResult> {
|
|
96
|
-
|
|
99
|
+
return this.build().dispatchToolCall(set);
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
/** A szerver leállítása (graceful close — teszt-/shutdown-úthoz). */
|
|
100
103
|
public async close(): Promise<void> {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
if (this.adapter) {
|
|
105
|
+
await this.adapter.close();
|
|
106
|
+
}
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
/**
|
|
@@ -108,13 +111,15 @@ export abstract class DyNTS_Mcp_ServerBase {
|
|
|
108
111
|
* módon bővíthető legyen (a `start()` + a registry változatlan). MVP: csak `stdio`.
|
|
109
112
|
*/
|
|
110
113
|
private async startTransport(adapter: DyNTS_Mcp_Adapter, transport: 'stdio'): Promise<void> {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
switch (transport) {
|
|
115
|
+
case 'stdio':
|
|
116
|
+
await adapter.startStdio();
|
|
117
|
+
|
|
118
|
+
return;
|
|
119
|
+
|
|
120
|
+
default:
|
|
121
|
+
// Exhaustiveness-guard: a `DyNTS_Mcp_TransportKind` bővülésekor itt fordítási hibát kapunk.
|
|
122
|
+
throw new Error(`Unsupported MCP transport: '${transport as string}'.`);
|
|
123
|
+
}
|
|
119
124
|
}
|
|
120
125
|
}
|