@stratusagent/plugin-mcp 0.11.3 → 0.11.5
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 +28 -8
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +115 -18
- package/dist/index.js.map +1 -1
- package/dist/normalize.d.ts +157 -6
- package/dist/normalize.d.ts.map +1 -1
- package/dist/normalize.js +809 -18
- package/dist/normalize.js.map +1 -1
- package/package.json +3 -3
package/dist/normalize.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type JsonValue } from '@stratusagent/core';
|
|
2
2
|
import { type TaintedWriteLedger } from '@stratusagent/plugins';
|
|
3
3
|
/**
|
|
4
4
|
* Turning what an MCP server sends back into what a Stratus tool returns:
|
|
@@ -20,22 +20,167 @@ import { type TaintedWriteLedger } from '@stratusagent/plugins';
|
|
|
20
20
|
export declare const sanitizeToolSegment: (raw: string) => string | undefined;
|
|
21
21
|
/** Server keys are operator-chosen and become a name segment; held to the segment shape outright. */
|
|
22
22
|
export declare const SERVER_NAME_PATTERN: RegExp;
|
|
23
|
+
/**
|
|
24
|
+
* The longest tool description a server gets to put in front of the model.
|
|
25
|
+
* The same bound a skill's description has (`SKILL_DESCRIPTION_MAX_LENGTH`
|
|
26
|
+
* in `@stratusagent/agents`), for the same reason: a description is prose
|
|
27
|
+
* the model reads on every turn, written by a party the operator did not
|
|
28
|
+
* author, and a paragraph is a description while a page is instructions.
|
|
29
|
+
*/
|
|
30
|
+
export declare const BRIDGED_DESCRIPTION_MAX_LENGTH = 1024;
|
|
31
|
+
/**
|
|
32
|
+
* A server's tool description as it reaches the registry — and, through
|
|
33
|
+
* it, every provider's tool block on every turn. The text is the server's,
|
|
34
|
+
* re-read on every reconnect, and `tools/list` is the one channel a
|
|
35
|
+
* server has that arrives looking like part of the harness rather than
|
|
36
|
+
* like a result. Two bounds, neither a filter: control characters and the
|
|
37
|
+
* Unicode bidi controls are spelled out the way memory entries are (a
|
|
38
|
+
* right-to-left override in a description is a description that reads
|
|
39
|
+
* differently to a person than to the model — escaped, it reads as what it
|
|
40
|
+
* is; the set is Unicode's `Bidi_Control` property, marks and the Arabic
|
|
41
|
+
* letter mark included, not only the overrides and isolates), and the
|
|
42
|
+
* length is capped with the cut announced. Newlines and tabs stay: a
|
|
43
|
+
* description is allowed to be several lines.
|
|
44
|
+
*/
|
|
45
|
+
export declare const bridgedDescription: (raw: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* The longest input schema a server gets to put in front of the model, in
|
|
48
|
+
* characters of its JSON. A schema is read by the provider as structure,
|
|
49
|
+
* but every string in it lands in the tool block as text, and sixteen
|
|
50
|
+
* thousand characters of parameter list is a page, not a parameter list.
|
|
51
|
+
*/
|
|
52
|
+
export declare const BRIDGED_SCHEMA_MAX_LENGTH = 16384;
|
|
53
|
+
/**
|
|
54
|
+
* The most of one `tools/call` result a server gets to put into the
|
|
55
|
+
* transcript, in characters, counting the joined text and the JSON of
|
|
56
|
+
* `structuredContent` separately.
|
|
57
|
+
*
|
|
58
|
+
* Every first-party tool caps what it returns — `fs.read` at 64 KB,
|
|
59
|
+
* `shell.run` and `browser.read` at 100 KB, `web.fetch` at 400 KB — and
|
|
60
|
+
* a bridged result was the one that did not. Binary blocks already land on
|
|
61
|
+
* disk rather than in the result, so this is about text, and text is the
|
|
62
|
+
* part that is *durable*: a tool result is saved into the session and
|
|
63
|
+
* replayed to the provider on every later turn of that conversation, so a
|
|
64
|
+
* server that answers `list_files` with twenty megabytes does not cost one
|
|
65
|
+
* turn, it costs every turn until the session ends — and survives restarts,
|
|
66
|
+
* because the transcript does. The stdio transport bounds a single message
|
|
67
|
+
* at its reader's buffer (10 MB by default) and the HTTP transports bound
|
|
68
|
+
* nothing, so "the transport will stop it" was never a cap.
|
|
69
|
+
*
|
|
70
|
+
* 100 KB matches `shell.run`, which is the closest comparison: somebody
|
|
71
|
+
* else's program, writing as much as it likes. Counted in characters
|
|
72
|
+
* rather than bytes, like the two bounds above and unlike the tool packs':
|
|
73
|
+
* what is scarce here is the model's context, which is measured in tokens,
|
|
74
|
+
* and a token follows a character more closely than it follows a byte.
|
|
75
|
+
*/
|
|
76
|
+
export declare const BRIDGED_RESULT_MAX_LENGTH = 100000;
|
|
77
|
+
/**
|
|
78
|
+
* The smallest `maxResultChars` that means anything, and the floor every
|
|
79
|
+
* smaller one is raised to.
|
|
80
|
+
*
|
|
81
|
+
* A cap has to be able to hold an account of what it cut. The four
|
|
82
|
+
* annotations a result can carry — a marker for the text, one for the
|
|
83
|
+
* structured payload, and the two truncation notes — come to a little over
|
|
84
|
+
* four hundred characters with the keys they arrive under, and shortening
|
|
85
|
+
* them is not on the table: an announcement is the whole reason a cap is
|
|
86
|
+
* safe to have, and a cut with nothing saying it happened is the failure
|
|
87
|
+
* this bound exists to prevent.
|
|
88
|
+
*
|
|
89
|
+
* So below this a cap cannot be honoured, only approximated — `1` yielded
|
|
90
|
+
* 73 characters for a truncated text and 104 for a dropped resource link —
|
|
91
|
+
* and approximating it silently is worse than raising it. Raised rather
|
|
92
|
+
* than refused, and raised to the floor rather than back to the default,
|
|
93
|
+
* because an operator who asked for the smallest possible result should
|
|
94
|
+
* get the smallest possible result. The markers name the cap that was
|
|
95
|
+
* actually applied.
|
|
96
|
+
*/
|
|
97
|
+
export declare const BRIDGED_RESULT_MIN_LENGTH = 512;
|
|
98
|
+
/**
|
|
99
|
+
* A configured `maxResultChars` as it actually applies: the default when
|
|
100
|
+
* unset, never below the floor. One implementation because it has two
|
|
101
|
+
* consumers — `normalizeCallResult` for a result, and the call path for a
|
|
102
|
+
* `tools/call` that fails before there is a result to normalize. A second
|
|
103
|
+
* copy would leave the floor applying to one and not the other, which is
|
|
104
|
+
* how a cap of 1 came to name itself in a protocol error's marker.
|
|
105
|
+
*/
|
|
106
|
+
export declare const boundedResultLimit: (configured: number | undefined) => number;
|
|
107
|
+
/**
|
|
108
|
+
* How deep a schema may nest before the bridge stops walking it. Sixty-four
|
|
109
|
+
* levels is far past any parameter list a person wrote; a schema nested
|
|
110
|
+
* deeper is a stack-overflow attempt dressed as a tool, and a recursive
|
|
111
|
+
* walk that blew the stack during discovery would take the whole server
|
|
112
|
+
* down as unreachable, and again on every reconnect.
|
|
113
|
+
*/
|
|
114
|
+
export declare const BRIDGED_SCHEMA_MAX_DEPTH = 64;
|
|
115
|
+
/**
|
|
116
|
+
* The longest name segment a server's tool may bridge under. The segment
|
|
117
|
+
* is the tail of `mcp.<server>.<segment>`, sent as the tool's name in
|
|
118
|
+
* every model request, and a name is the one string a server writes that
|
|
119
|
+
* no description bound touches — a tool called `a` three thousand times
|
|
120
|
+
* over is a page in the tool block by another route. Sixty-four is the
|
|
121
|
+
* longest name the strictest provider accepts whole.
|
|
122
|
+
*/
|
|
123
|
+
export declare const BRIDGED_SEGMENT_MAX_LENGTH = 64;
|
|
124
|
+
/**
|
|
125
|
+
* A server's input schema as it reaches the registry — and, through it,
|
|
126
|
+
* the tool block of every turn, the same way the description does. The
|
|
127
|
+
* top-level description is not the only prose in `tools/list`: a
|
|
128
|
+
* `description` or `title` on any property, at any depth, is text the
|
|
129
|
+
* model reads too, and it was copied through unbounded. Each is bounded
|
|
130
|
+
* like the tool's own description. Nothing else in the schema is touched:
|
|
131
|
+
* a property name, an enum value, a const or a pattern is what the model
|
|
132
|
+
* sends back in a call, and the bridge forwards arguments to the server
|
|
133
|
+
* as the model wrote them — a value spelled out here would arrive at the
|
|
134
|
+
* server as a different value, and the schema would be a lie in the
|
|
135
|
+
* direction that breaks calls. An annotation key *inside* a literal (an
|
|
136
|
+
* `enum` member with a `description` field) is data too, and is left
|
|
137
|
+
* alone — while a *parameter* named `enum` or `default` is a schema like
|
|
138
|
+
* any other, because the keys of `properties` and `$defs` are names, not
|
|
139
|
+
* keywords. `undefined` when the bounded schema is still longer than
|
|
140
|
+
* {@link BRIDGED_SCHEMA_MAX_LENGTH} characters, or nests deeper than
|
|
141
|
+
* {@link BRIDGED_SCHEMA_MAX_DEPTH}: that tool is not bridged, and the
|
|
142
|
+
* caller names it.
|
|
143
|
+
*/
|
|
144
|
+
export declare const bridgedSchema: (schema: Record<string, unknown>) => Record<string, unknown> | undefined;
|
|
23
145
|
/** The registered name a server's tool bridges to: `mcp.<server>.<segment>`. */
|
|
24
146
|
export declare const bridgedToolName: (server: string, segment: string) => string;
|
|
147
|
+
/**
|
|
148
|
+
* Server-written text, bounded for the transcript — the same treatment a
|
|
149
|
+
* result's text gets, exported because a `tools/call` can fail in ways that
|
|
150
|
+
* never produce a result at all: a JSON-RPC error, or a transport failure
|
|
151
|
+
* carrying an HTTP body. Those reach the agent as a thrown message, which
|
|
152
|
+
* `DefaultExecutor` copies into `ToolResult.error` and the session persists
|
|
153
|
+
* and replays exactly like output.
|
|
154
|
+
*
|
|
155
|
+
* Bounded to the cap *minus the envelope it is replayed in*, for the same
|
|
156
|
+
* reason a result's fields are: it does not travel as a bare string but as
|
|
157
|
+
* a JSON string value under a key, and a message cut to exactly the cap
|
|
158
|
+
* arrives a dozen characters over it. The marker still names the cap the
|
|
159
|
+
* operator set rather than the number left after that deduction.
|
|
160
|
+
*/
|
|
161
|
+
export declare const THROWN_MESSAGE_ENVELOPE: number;
|
|
162
|
+
export declare const boundServerText: (raw: string, limit: number, what: string) => string;
|
|
25
163
|
export interface NormalizeOptions {
|
|
26
164
|
/** The bridged server's config key — part of where a binary block lands. */
|
|
27
165
|
server: string;
|
|
28
166
|
/** The server-side tool name — part of the written file's name. */
|
|
29
167
|
tool: string;
|
|
30
168
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
169
|
+
* Where this agent's workspace is, asked for rather than passed in — not
|
|
170
|
+
* a root to join an id onto. Binary content lands under
|
|
171
|
+
* `<workspace>/mcp/<server>/`, so two agents never read each other's
|
|
172
|
+
* files, the same shape screenshots take.
|
|
173
|
+
*
|
|
174
|
+
* A function because asking is no longer free: the host's seam *creates*
|
|
175
|
+
* the directory and settles its permissions, and it can fail. A
|
|
176
|
+
* text-only result must not be reported as an error because a workspace
|
|
177
|
+
* it never wanted could not be made — the remote call has already
|
|
178
|
+
* happened, and whoever retries it does the side effect twice.
|
|
34
179
|
*/
|
|
35
|
-
|
|
180
|
+
workspace?: () => string;
|
|
36
181
|
agentId: string;
|
|
37
182
|
/**
|
|
38
|
-
* The filesystem provenance ledger for `
|
|
183
|
+
* The filesystem provenance ledger for `workspace`. A binary block
|
|
39
184
|
* is a server's bytes written to disk without going through `fs.write`,
|
|
40
185
|
* so the write records itself here at `external` before the bytes land —
|
|
41
186
|
* a later `fs.read` of the file then carries the label the tool result
|
|
@@ -43,6 +188,12 @@ export interface NormalizeOptions {
|
|
|
43
188
|
* the loader-less host case gets.
|
|
44
189
|
*/
|
|
45
190
|
ledger?: TaintedWriteLedger;
|
|
191
|
+
/**
|
|
192
|
+
* The operator's cap on this result, in characters. Defaults to
|
|
193
|
+
* {@link BRIDGED_RESULT_MAX_LENGTH}; a server cannot raise it, because
|
|
194
|
+
* the cap exists to bound what the server sends.
|
|
195
|
+
*/
|
|
196
|
+
maxResultChars?: number;
|
|
46
197
|
/** Clock seam for deterministic file names in tests. */
|
|
47
198
|
now?: () => number;
|
|
48
199
|
}
|
package/dist/normalize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAIA,OAAO,EAA4C,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEtF;;;;;GAKG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAI1D,CAAC;AAEF,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,QAA0B,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAUnD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,MAAM,KAAG,MAWhD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,YAAY,MAAM,GAAG,SAAS,KAAG,MACU,CAAC;AAqR/E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAU3C;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAoD7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAazF,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAG,MAAoC,CAAC;AA8JvG;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,QAAwB,CAAC;AAE7D,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,MAAM,MAAM,KAAG,MACC,CAAC;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,OAAO,EACf,SAAS,gBAAgB,KACxB,OAAO,CAAC,SAAS,CA6BnB,CAAC"}
|