@sezzlee/sdk-nestjs 0.2.0 → 0.2.1
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 +8 -1
- package/dist/catalog.js +26 -5
- package/dist/decorators.d.ts +7 -0
- package/dist/discovery/endpoint-discovery.d.ts +4 -0
- package/dist/discovery/endpoint-discovery.js +16 -3
- package/dist/options.d.ts +4 -0
- package/dist/options.js +4 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ An MCP layer that embeds into your existing NestJS backend. It exposes your endp
|
|
|
4
4
|
a search-first tool catalog and replays each call through **your own** pipeline, so your guards,
|
|
5
5
|
pipes and interceptors keep running unchanged. Not a gateway, not duplicated business logic.
|
|
6
6
|
|
|
7
|
-
> Status: `0.2.
|
|
7
|
+
> Status: `0.2.1`, alpha; breaking changes are possible. [samples/demo-api](samples/demo-api) is
|
|
8
8
|
> the reference wiring.
|
|
9
9
|
|
|
10
10
|
## Requirements
|
|
@@ -330,6 +330,13 @@ of BM25, bind a `ToolRanker` through `forRoot`'s overrides (`{ toolRanker: { use
|
|
|
330
330
|
(`"fallback"` or `"error"`) govern it. Visibility, the tag filter and the payload budget still apply
|
|
331
331
|
to its answer — see the how-to on plugging in your own search.
|
|
332
332
|
|
|
333
|
+
When an operation's behaviour changes behind an unchanged schema, raise its revision
|
|
334
|
+
(`@McpTool({ revision: 2 })` on the method, on the controller for every method in it, or
|
|
335
|
+
`options.revision` for the whole catalog). The revision enters the tool version, so during a rolling
|
|
336
|
+
deploy a call pinned to the old version is refused with `tool_changed` instead of running against
|
|
337
|
+
the new behaviour. The levels add up rather than replacing one another — see the how-to on shipping
|
|
338
|
+
a behaviour change mid-deploy.
|
|
339
|
+
|
|
333
340
|
This repository ships a client:
|
|
334
341
|
|
|
335
342
|
```bash
|
package/dist/catalog.js
CHANGED
|
@@ -12,7 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
};
|
|
13
13
|
import { Inject, Injectable, Optional, RequestMethod, } from "@nestjs/common";
|
|
14
14
|
import { ApplicationConfig, DiscoveryService, HttpAdapterHost, ModulesContainer, } from "@nestjs/core";
|
|
15
|
-
import { assertCatalogValid, buildCatalog, combineMarkers, matchesRoute, SezzleeTemplateError, textMediaType, urlEncodedMediaType, } from "@sezzlee/core";
|
|
15
|
+
import { assertCatalogValid, buildCatalog, combineMarkers, declareRevision, matchesRoute, SezzleeTemplateError, textMediaType, urlEncodedMediaType, } from "@sezzlee/core";
|
|
16
16
|
import { severityOf } from "./discovery/diagnostics.js";
|
|
17
17
|
import { createRoutePaths, discoverEndpoints, modulePathOf, normalizeRoute, } from "./discovery/endpoint-discovery.js";
|
|
18
18
|
import { loadFamilies, resolveFamily, } from "./families.js";
|
|
@@ -227,20 +227,41 @@ let SezzleeCatalog = class SezzleeCatalog {
|
|
|
227
227
|
}
|
|
228
228
|
return true;
|
|
229
229
|
});
|
|
230
|
+
const catalogRevision = declareRevision("The catalog", { catalog: [{ value: this.options.revision, by: "options.revision" }] }, report)?.catalog;
|
|
230
231
|
const built = buildCatalog(routed.map((endpoint) => {
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
|
|
232
|
+
const container = endpoint.controller.name;
|
|
233
|
+
const owner = `${container}.${endpoint.handlerName}`;
|
|
234
|
+
const tags = endpoint.hints.tags ?? this.options.tags?.(container);
|
|
235
|
+
const searchTerms = endpoint.hints.searchTerms ?? this.options.searchTerms?.(container);
|
|
234
236
|
return {
|
|
235
237
|
source: {
|
|
236
238
|
controller: endpoint.controller,
|
|
237
239
|
handlerName: endpoint.handlerName,
|
|
238
240
|
},
|
|
239
|
-
owner
|
|
241
|
+
owner,
|
|
240
242
|
descriptor: endpoint.descriptor,
|
|
241
243
|
...markersOf(endpoint),
|
|
242
244
|
...(tags === undefined ? {} : { tags }),
|
|
243
245
|
...(searchTerms === undefined ? {} : { searchTerms }),
|
|
246
|
+
revision: {
|
|
247
|
+
catalog: [{ value: catalogRevision, by: "options.revision" }],
|
|
248
|
+
container: [
|
|
249
|
+
{
|
|
250
|
+
value: endpoint.revisions.container,
|
|
251
|
+
by: `@McpTool({ revision }) on ${container}`,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
value: this.options.containerRevision?.(container),
|
|
255
|
+
by: "options.containerRevision",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
operation: [
|
|
259
|
+
{
|
|
260
|
+
value: endpoint.revisions.operation,
|
|
261
|
+
by: `@McpTool({ revision }) on ${owner}`,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
244
265
|
declare: (cleaned) => declaredDescriptor(endpoint, this.curationFor(endpoint), cleaned, families.get(endpoint)),
|
|
245
266
|
};
|
|
246
267
|
}), {
|
package/dist/decorators.d.ts
CHANGED
|
@@ -98,6 +98,13 @@ export interface McpToolOptions {
|
|
|
98
98
|
* never a filter key and never shown to the agent ([search-semantics.md]).
|
|
99
99
|
*/
|
|
100
100
|
readonly searchTerms?: readonly string[];
|
|
101
|
+
/**
|
|
102
|
+
* The behaviour revision at the level the decorator sits on: a method's covers that operation,
|
|
103
|
+
* a controller's every operation in it. Raised when behaviour changes behind an unchanged
|
|
104
|
+
* schema, it changes the tool version and nothing the agent reads ([metadata-contract.md]).
|
|
105
|
+
* The two levels never replace one another.
|
|
106
|
+
*/
|
|
107
|
+
readonly revision?: number;
|
|
101
108
|
/**
|
|
102
109
|
* The body's media type, replacing the one discovery chooses. It is the host's own claim that a
|
|
103
110
|
* parser for it is registered; when `@ApiConsumes` also lists types, it must be one of them.
|
|
@@ -45,6 +45,10 @@ export interface DiscoveredEndpoint {
|
|
|
45
45
|
readonly containerMarkers: readonly boolean[];
|
|
46
46
|
readonly operationMarkers: readonly boolean[];
|
|
47
47
|
readonly hints: McpToolOptions;
|
|
48
|
+
readonly revisions: {
|
|
49
|
+
readonly container: number | undefined;
|
|
50
|
+
readonly operation: number | undefined;
|
|
51
|
+
};
|
|
48
52
|
}
|
|
49
53
|
export declare function discoverEndpoints(controllers: readonly ControllerCandidate[], options?: DiscoveryOptions): DiscoveredEndpoint[];
|
|
50
54
|
/** `RouterModule` writes the module path under a key scoped to the application instance, falling back to the bare key. */
|
|
@@ -95,6 +95,10 @@ function fromController(candidate, options) {
|
|
|
95
95
|
containerMarkers: containerMarkers.map((marker) => marker.include),
|
|
96
96
|
operationMarkers: operationMarkers.map((marker) => marker.include),
|
|
97
97
|
hints,
|
|
98
|
+
revisions: {
|
|
99
|
+
container: declaredRevision(containerMarkers),
|
|
100
|
+
operation: declaredRevision(operationMarkers),
|
|
101
|
+
},
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
104
|
}
|
|
@@ -639,12 +643,21 @@ function argumentsOf(controller, handlerName) {
|
|
|
639
643
|
return (Reflect.getMetadata(ROUTE_ARGS_METADATA, controller, handlerName) ??
|
|
640
644
|
{});
|
|
641
645
|
}
|
|
646
|
+
function declaredRevision(markers) {
|
|
647
|
+
let revision;
|
|
648
|
+
for (const marker of markers) {
|
|
649
|
+
revision = marker.options.revision ?? revision;
|
|
650
|
+
}
|
|
651
|
+
return revision;
|
|
652
|
+
}
|
|
642
653
|
/**
|
|
643
|
-
* Flat last-wins merge, except for curation.
|
|
654
|
+
* Flat last-wins merge, except for curation and the revision.
|
|
644
655
|
*
|
|
645
656
|
* `arguments` merges per argument and per field so a container-level rule that hides a tenant
|
|
646
657
|
* identifier survives a method-level rule that only renames something else. A flat replacement
|
|
647
|
-
* would silently unhide it.
|
|
658
|
+
* would silently unhide it. `revision` is kept out entirely: its levels are read apart
|
|
659
|
+
* ({@link declaredRevision}), because a method's revision replacing its controller's would hide a
|
|
660
|
+
* raised controller revision from that method's tool version.
|
|
648
661
|
*/
|
|
649
662
|
function mergeHints(container, operation) {
|
|
650
663
|
const merged = {};
|
|
@@ -652,7 +665,7 @@ function mergeHints(container, operation) {
|
|
|
652
665
|
const variants = [];
|
|
653
666
|
for (const marker of [...container, ...operation]) {
|
|
654
667
|
for (const [key, value] of Object.entries(marker.options)) {
|
|
655
|
-
if (value === undefined) {
|
|
668
|
+
if (value === undefined || key === "revision") {
|
|
656
669
|
continue;
|
|
657
670
|
}
|
|
658
671
|
if (key === "arguments") {
|
package/dist/options.d.ts
CHANGED
|
@@ -195,6 +195,10 @@ export declare class SezzleeOptions {
|
|
|
195
195
|
tags?: (container: string) => readonly string[] | undefined;
|
|
196
196
|
/** Search vocabulary for a container the host cannot decorate, below a `@McpTool({ searchTerms })`. */
|
|
197
197
|
searchTerms?: (container: string) => readonly string[] | undefined;
|
|
198
|
+
/** The behaviour revision of the whole catalog; raising it changes every tool version. */
|
|
199
|
+
revision?: number;
|
|
200
|
+
/** A behaviour revision for a container the host cannot decorate, below a `@McpTool({ revision })`. */
|
|
201
|
+
containerRevision?: (container: string) => number | undefined;
|
|
198
202
|
schema?: TypeShapeBinderOptions;
|
|
199
203
|
resourceServer?: SezzleeResourceServerOptions;
|
|
200
204
|
}
|
package/dist/options.js
CHANGED
|
@@ -145,6 +145,10 @@ export class SezzleeOptions {
|
|
|
145
145
|
tags;
|
|
146
146
|
/** Search vocabulary for a container the host cannot decorate, below a `@McpTool({ searchTerms })`. */
|
|
147
147
|
searchTerms;
|
|
148
|
+
/** The behaviour revision of the whole catalog; raising it changes every tool version. */
|
|
149
|
+
revision;
|
|
150
|
+
/** A behaviour revision for a container the host cannot decorate, below a `@McpTool({ revision })`. */
|
|
151
|
+
containerRevision;
|
|
148
152
|
schema;
|
|
149
153
|
resourceServer;
|
|
150
154
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sezzlee/sdk-nestjs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Embeds an MCP layer into an existing NestJS backend: search-first tool discovery over your real endpoints, dispatched through your own guards, pipes and interceptors.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@sezzlee/core": "^0.2.
|
|
44
|
+
"@sezzlee/core": "^0.2.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@modelcontextprotocol/express": ">=2.0.0",
|