@reinsjs/tools-mcp 0.2.0 → 0.3.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/CHANGELOG.md +20 -0
- package/README.md +62 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @reinsjs/tools-mcp
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Version alignment: every `@reinsjs/*` package ships as 0.3.0 together with `@reinsjs/core` and `@reinsjs/brain` (MCP tools on the `lazyTools()` menu via `SocketSetup.tools`). No behaviour change in these packages beyond the dependency bump.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- a3afbf9: README: a recipe for large flat tool tables (TikTok's 377-tool endpoint) — `override` keeps only `readOnlyHint` tools and marks them `lazy: true` for `@reinsjs/brain`'s `lazyTools()`, plus a `withBusinessErrors` wrapper that turns `{"code": <non-zero>}` envelopes returned as successful results into `isError` results. Executable version in `src/readonly-lazy.recipe.test.ts`. No code changes.
|
|
12
|
+
- Updated dependencies [a3afbf9]
|
|
13
|
+
- @reinsjs/core@0.3.0
|
|
14
|
+
|
|
15
|
+
## 0.2.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Republish of 0.2.0. The 0.2.0 tarballs were uploaded with `npm publish`, which does not rewrite pnpm's `workspace:*` dependency ranges, so every package that depends on `@reinsjs/core` could not be installed (`ERR_PNPM_WORKSPACE_PKG_NOT_FOUND` / npm `EUNSUPPORTEDPROTOCOL`). 0.2.1 is the same code published with `pnpm publish`, which writes concrete versions. 0.2.0 is deprecated on npm.
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
- @reinsjs/core@0.2.1
|
|
22
|
+
|
|
3
23
|
## 0.2.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -179,7 +179,68 @@ Two more things worth knowing about servers of this shape:
|
|
|
179
179
|
budget for that one tool: `override: (tool, info) => info.name === "tool_list" ? { ...tool, resultPolicy: { maxTokens: 100_000, overflow: "spill" } } : tool`.
|
|
180
180
|
- **Upstream business errors may not set `isError`.** TikTok returns `{"code": 40001, "message": "..."}` as a
|
|
181
181
|
*successful* MCP result; only MCP-level failures (unknown tool, transport error) are `isError`. Anything keying
|
|
182
|
-
off `isError` — retries, your own policies — will not see those
|
|
182
|
+
off `isError` — retries, your own policies, `onEvent` observers — will not see those unless you translate them;
|
|
183
|
+
the `withBusinessErrors` wrapper in the next section does exactly that.
|
|
184
|
+
|
|
185
|
+
## Large flat tool tables
|
|
186
|
+
|
|
187
|
+
The opposite shape: hundreds of ordinary tools, each annotated honestly. TikTok's flat endpoint lists 377 of them,
|
|
188
|
+
about 200 reads and 130 writes, every one with `readOnlyHint`. Two things you usually want there: keep the writes
|
|
189
|
+
out of the model's reach entirely (they go through your own tools, with your own authorization and audit trail),
|
|
190
|
+
and stop sending two hundred tool definitions on every request. Both are one `override` plus
|
|
191
|
+
`@reinsjs/brain`'s `lazyTools()`:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { lazyTools } from "@reinsjs/brain"
|
|
195
|
+
import { normalizeToolOutput, type Tool, type ToolResult } from "@reinsjs/core"
|
|
196
|
+
|
|
197
|
+
/** TikTok puts business errors in a successful result: text = {"code": <non-zero>, "message": ...} */
|
|
198
|
+
const businessErrorOf = (result: ToolResult): string | undefined => {
|
|
199
|
+
if (result.isError) return undefined
|
|
200
|
+
for (const part of result.content) {
|
|
201
|
+
if (part.type !== "text") continue
|
|
202
|
+
try {
|
|
203
|
+
const body = JSON.parse(part.text) as { code?: unknown; message?: unknown }
|
|
204
|
+
if (typeof body.code === "number" && body.code !== 0)
|
|
205
|
+
return `Upstream error ${body.code}: ${String(body.message ?? "")}`
|
|
206
|
+
} catch {
|
|
207
|
+
// not JSON, not an error envelope
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Keep the original text for the model, but mark it as an error so retries, policies and observers see it */
|
|
214
|
+
const withBusinessErrors = (tool: Tool): Tool => ({
|
|
215
|
+
...tool,
|
|
216
|
+
async execute(input, ctx) {
|
|
217
|
+
const result = normalizeToolOutput(await tool.execute?.(input, ctx))
|
|
218
|
+
const error = businessErrorOf(result)
|
|
219
|
+
return error ? { content: [...result.content, { type: "text", text: error }], isError: true } : result
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
/** reads: on the menu, loaded on demand; everything else: not a tool the model has */
|
|
224
|
+
const readOnlyLazy = (tool: Tool, info: McpToolInfo) =>
|
|
225
|
+
info.annotations?.readOnlyHint === true ? withBusinessErrors({ ...tool, lazy: true }) : false
|
|
226
|
+
|
|
227
|
+
const tiktok = mcpTools({ transport, override: readOnlyLazy })
|
|
228
|
+
|
|
229
|
+
createAgent({ sockets: [tiktok, lazyTools()] }) // order matters — see below
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`lazyTools()` builds its menu from the tools bound *before* it: the host's, plus those of every socket registered
|
|
233
|
+
earlier. So it goes after `mcpTools()`. The other order is not an error, just useless: the MCP tools are sent in
|
|
234
|
+
full every turn, and `lazyTools()` warns once, naming them. With a lowering layer that has a native place for
|
|
235
|
+
deferred tools (`@reinsjs/lowering-fetch` on Anthropic) loading a tool does not even re-warm the prompt cache; see
|
|
236
|
+
the brain README for the numbers.
|
|
237
|
+
|
|
238
|
+
Loaded definitions are trusted like the tools block itself (`tool_find` results are `trust: "system"`) — the
|
|
239
|
+
same descriptions were already going to the model unwrapped. A 200-line menu costs roughly 10k tokens of system
|
|
240
|
+
prompt; it sits in the cached prefix and does not change within a run, and `lazyTools({ summarize })` can shorten it.
|
|
241
|
+
|
|
242
|
+
The executable version of this recipe is `packages/tools-mcp/src/readonly-lazy.recipe.test.ts`, including the
|
|
243
|
+
wrong-order case.
|
|
183
244
|
|
|
184
245
|
## Verified upstream behaviour
|
|
185
246
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reinsjs/tools-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "reins — MCP servers as a Socket: tools/list once per run, tools/call as Tool.execute, annotations → risk / approval defaults. Streamable HTTP on the Web-standard entry, stdio under ./node",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -35,20 +35,20 @@
|
|
|
35
35
|
"dist",
|
|
36
36
|
"CHANGELOG.md"
|
|
37
37
|
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"build": "tsup"
|
|
40
|
-
},
|
|
41
38
|
"dependencies": {
|
|
42
39
|
"@modelcontextprotocol/client": "2.0.0",
|
|
43
|
-
"@reinsjs/core": "
|
|
40
|
+
"@reinsjs/core": "0.3.0"
|
|
44
41
|
},
|
|
45
42
|
"devDependencies": {
|
|
46
43
|
"@modelcontextprotocol/server": "2.0.0",
|
|
47
|
-
"@reinsjs/brain": "workspace:*",
|
|
48
44
|
"@types/node": "^22.0.0",
|
|
49
|
-
"zod": "^4.2.0"
|
|
45
|
+
"zod": "^4.2.0",
|
|
46
|
+
"@reinsjs/brain": "0.3.0"
|
|
50
47
|
},
|
|
51
48
|
"engines": {
|
|
52
49
|
"node": ">=22"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|