mcp-expose 1.1.0 → 1.1.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/CHANGELOG.md +18 -1
- package/README.md +88 -28
- package/dist/adonisjs/index.cjs +128 -6
- package/dist/adonisjs/index.cjs.map +1 -1
- package/dist/adonisjs/index.d.cts +1 -1
- package/dist/adonisjs/index.d.ts +1 -1
- package/dist/adonisjs/index.js +128 -6
- package/dist/adonisjs/index.js.map +1 -1
- package/dist/express/index.cjs +128 -6
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +1 -1
- package/dist/express/index.d.ts +1 -1
- package/dist/express/index.js +128 -6
- package/dist/express/index.js.map +1 -1
- package/dist/fastify/index.cjs +128 -6
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.d.cts +1 -1
- package/dist/fastify/index.d.ts +1 -1
- package/dist/fastify/index.js +128 -6
- package/dist/fastify/index.js.map +1 -1
- package/dist/hono/index.cjs +128 -6
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.d.cts +1 -1
- package/dist/hono/index.d.ts +1 -1
- package/dist/hono/index.js +128 -6
- package/dist/hono/index.js.map +1 -1
- package/dist/index.cjs +128 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +128 -31
- package/dist/index.js.map +1 -1
- package/dist/koa/index.cjs +128 -6
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.d.cts +1 -1
- package/dist/koa/index.d.ts +1 -1
- package/dist/koa/index.js +128 -6
- package/dist/koa/index.js.map +1 -1
- package/dist/nestjs/index.cjs +128 -6
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +1 -1
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/nestjs/index.js +128 -6
- package/dist/nestjs/index.js.map +1 -1
- package/dist/{server-BFQ_Hdh9.d.cts → server--mQVQq3A.d.cts} +55 -2
- package/dist/{server-BRw1E2xy.d.ts → server-BPDTsT2o.d.ts} +55 -2
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.1.1] - 2026-09-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `transformResponse` hook for route tools, on every marker and adapter, plus a server-level default. It shapes what
|
|
14
|
+
the agent sees from a route's response (hide or format fields, add hints) without turning the route into a custom
|
|
15
|
+
tool. The hook receives the status, headers, body and parsed JSON, the tool arguments and the MCP call context.
|
|
16
|
+
`ctx.callRoute()` enriches the response with other routes of the app, using the same credentials and the same
|
|
17
|
+
middleware, guards and validation. Placeholders are URL-encoded, and `.` / `..` segments are rejected.
|
|
18
|
+
- `ResponseTransform`, `ResponseTransformContext` and `RouteToolResponse` types.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A tool handler or hook that returns plain data with its own `content` array (for example a CMS post) is no longer
|
|
23
|
+
mistaken for an MCP `ToolResult`; only `content` arrays of MCP content items (`{ type: ... }`) are.
|
|
24
|
+
|
|
9
25
|
## [1.1.0] - 2026-09-27
|
|
10
26
|
|
|
11
27
|
### Added
|
|
@@ -52,6 +68,7 @@ options, default tool naming) now follows Semantic Versioning.
|
|
|
52
68
|
- Path parameters that are empty, `.` or `..` are rejected, so an agent cannot use them to reach routes
|
|
53
69
|
that were never exposed as tools (for example `/orders/../admin`).
|
|
54
70
|
|
|
55
|
-
[Unreleased]: https://github.com/NITINKACHHADIYA/Node-MCP/compare/v1.1.
|
|
71
|
+
[Unreleased]: https://github.com/NITINKACHHADIYA/Node-MCP/compare/v1.1.1...HEAD
|
|
72
|
+
[1.1.1]: https://github.com/NITINKACHHADIYA/Node-MCP/releases/tag/v1.1.1
|
|
56
73
|
[1.1.0]: https://github.com/NITINKACHHADIYA/Node-MCP/releases/tag/v1.1.0
|
|
57
74
|
[1.0.0]: https://github.com/NITINKACHHADIYA/Node-MCP/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -47,13 +47,14 @@ router.get('/orders/:id', [OrdersController, 'show']).use(middleware.auth()).mcp
|
|
|
47
47
|
6. [Connect an AI client](#connect-an-ai-client)
|
|
48
48
|
7. [OAuth: sign in from AI clients](#oauth-sign-in-from-ai-clients)
|
|
49
49
|
8. [Defining tool inputs (schemas)](#defining-tool-inputs-schemas)
|
|
50
|
-
9. [
|
|
51
|
-
10. [
|
|
52
|
-
11. [
|
|
53
|
-
12. [
|
|
54
|
-
13. [
|
|
55
|
-
14. [
|
|
56
|
-
15. [
|
|
50
|
+
9. [Shaping and enriching responses](#shaping-and-enriching-responses)
|
|
51
|
+
10. [Configuration reference](#configuration-reference)
|
|
52
|
+
11. [Custom (non-HTTP) tools](#custom-non-http-tools)
|
|
53
|
+
12. [Security checklist](#security-checklist)
|
|
54
|
+
13. [Writing tools agents use well](#writing-tools-agents-use-well)
|
|
55
|
+
14. [Limitations and roadmap](#limitations-and-roadmap)
|
|
56
|
+
15. [Development](#development)
|
|
57
|
+
16. [Versioning and support](#versioning-and-support)
|
|
57
58
|
|
|
58
59
|
---
|
|
59
60
|
|
|
@@ -648,30 +649,88 @@ Sources, from highest to lowest precedence:
|
|
|
648
649
|
|
|
649
650
|
Your app's own validation always runs as well. The schema tells the agent what to send, and your API decides what it accepts.
|
|
650
651
|
|
|
652
|
+
## Shaping and enriching responses
|
|
653
|
+
|
|
654
|
+
By default the agent gets the route's response as it is. Add `transformResponse` to a route tool to change
|
|
655
|
+
what the agent sees: hide internal fields, format values, add hints, or pull in related data from other
|
|
656
|
+
routes. The route stays a route tool; there is no need to rewrite it as a custom tool.
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
@Get(':id')
|
|
660
|
+
@McpTool({
|
|
661
|
+
name: 'get_order',
|
|
662
|
+
transformResponse: async (res, ctx) => {
|
|
663
|
+
if (!res.ok) return undefined; // keep the default error result
|
|
664
|
+
const { internalNotes, ...order } = res.json as Order;
|
|
665
|
+
// Another route of this app, with the same user's credentials, guards and pipes:
|
|
666
|
+
const customer = await ctx.callRoute({ path: '/api/customers/:id', params: { id: order.customerId } });
|
|
667
|
+
return { ...order, total: formatMoney(order.total), customer: customer.ok ? customer.json : null };
|
|
668
|
+
},
|
|
669
|
+
})
|
|
670
|
+
findOne(@Param('id') id: string) { ... }
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
It works the same with every marker: `mcpTool({ transformResponse })`, `config: { mcp: { transformResponse } }`,
|
|
674
|
+
`.mcp({ transformResponse })`, and `toolsFromOpenApi(doc, dispatch, { transformResponse })`.
|
|
675
|
+
|
|
676
|
+
**What the hook receives**
|
|
677
|
+
|
|
678
|
+
- `res`: `status`, `headers`, the raw `body`, the parsed `json` (for JSON responses) and `ok` (status below 400).
|
|
679
|
+
- `ctx.args`: the tool arguments. `ctx.request`: the internal request (method, path, query, body).
|
|
680
|
+
- `ctx.toolContext`: the MCP request's headers, client IP and, with [OAuth](#oauth-sign-in-from-ai-clients), `auth`.
|
|
681
|
+
- `ctx.defaultResult()`: the result mcp-expose would return without the hook.
|
|
682
|
+
- `ctx.callRoute({ method, path, params, query, body, headers })`: calls another route of the same app.
|
|
683
|
+
- It forwards the same credentials (`Authorization`, cookies) and runs through the same middleware, guards and validation.
|
|
684
|
+
- `path` is the full path as your server sees it, including any global prefix or router mount path. Fill `:id` / `{id}`
|
|
685
|
+
placeholders from `params`: values are URL-encoded, and `.` or `..` are rejected, so agent input can't redirect the call.
|
|
686
|
+
|
|
687
|
+
**What it returns**
|
|
688
|
+
|
|
689
|
+
| Return value | Result |
|
|
690
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
691
|
+
| a string or any JSON value | Sent to the agent (objects also as `structuredContent`). Still an error result if the route failed. |
|
|
692
|
+
| a full `ToolResult` | Used as is, so you control `isError` (e.g. `{ ...ctx.defaultResult(), content: [...] }`). |
|
|
693
|
+
| `undefined` | The default result. |
|
|
694
|
+
| a thrown error | An error result with the message, which the agent sees. |
|
|
695
|
+
|
|
696
|
+
`maxResponseChars` still applies to whatever the hook returns.
|
|
697
|
+
|
|
698
|
+
**For every tool at once**, set `transformResponse` in the server options, e.g. to strip fields everywhere:
|
|
699
|
+
|
|
700
|
+
```ts
|
|
701
|
+
mountMcp(app, {
|
|
702
|
+
name: 'shop-api',
|
|
703
|
+
transformResponse: (res) => (res.ok ? omitDeep(res.json, ['internalNotes', 'passwordHash']) : undefined),
|
|
704
|
+
});
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
A tool's own `transformResponse` replaces the server-level one.
|
|
708
|
+
|
|
651
709
|
## Configuration reference
|
|
652
710
|
|
|
653
711
|
### Server options (all adapters)
|
|
654
712
|
|
|
655
|
-
| Option
|
|
656
|
-
|
|
|
657
|
-
| `name`
|
|
658
|
-
| `version`
|
|
659
|
-
| `instructions`
|
|
660
|
-
| `path`
|
|
661
|
-
| `allowedOrigins`
|
|
662
|
-
| `forwardHeaders`
|
|
663
|
-
| `maxResponseChars`
|
|
664
|
-
| `oauth`
|
|
665
|
-
| `
|
|
666
|
-
| `
|
|
667
|
-
| `
|
|
668
|
-
| `
|
|
669
|
-
| `
|
|
670
|
-
| `
|
|
671
|
-
| `
|
|
672
|
-
| `
|
|
673
|
-
| `
|
|
674
|
-
| `
|
|
713
|
+
| Option | Type | Default | Description |
|
|
714
|
+
| ------------------- | --------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
715
|
+
| `name` | `string` | required | Server name shown to clients. |
|
|
716
|
+
| `version` | `string` | `'1.0.0'` | Server version. |
|
|
717
|
+
| `instructions` | `string` | none | Guidance for the model on how to use the tools together. |
|
|
718
|
+
| `path` | `string` | `'/mcp'` | Endpoint path. |
|
|
719
|
+
| `allowedOrigins` | `string[] \| '*'` | `[]` | Browser origins allowed to call the endpoint. Requests without `Origin` (CLIs, IDEs, servers) are always allowed. |
|
|
720
|
+
| `forwardHeaders` | `string[]` | `['authorization','cookie','x-api-key','accept-language']` | Headers copied from the MCP request to the internal API call. |
|
|
721
|
+
| `maxResponseChars` | `number` | `100000` | Longer API responses are truncated before reaching the model. |
|
|
722
|
+
| `oauth` | `OAuthOptions` | none | Protect the endpoint with OAuth 2.1. See [OAuth](#oauth-sign-in-from-ai-clients). |
|
|
723
|
+
| `transformResponse` | `ResponseTransform` | none | Default response hook for route tools without their own. See [Shaping responses](#shaping-and-enriching-responses). |
|
|
724
|
+
| `tools` | `McpToolDefinition[]` | `[]` | Extra hand-written tools. |
|
|
725
|
+
| `baseUrl` | `string` | loopback | _Express/Koa/Nest/Adonis._ Where internal calls go. Set it for HTTPS with self-signed certs, unix sockets, or a separate API host. |
|
|
726
|
+
| `routes` | `{method,path,...}[]` | `[]` | _Express/Koa/Hono._ Expose routes without editing them. |
|
|
727
|
+
| `routers` | see guide | none | _Express:_ `{ '/prefix': router }`. _Koa:_ `[router]`. |
|
|
728
|
+
| `middleware` | `Middleware[]` | `[]` | _Express._ Middleware in front of `/mcp`, such as auth. |
|
|
729
|
+
| `guards` | `CanActivate[]` | `[]` | _NestJS._ Guards on the MCP controller. |
|
|
730
|
+
| `decorators` | `ClassDecorator[]` | `[]` | _NestJS._ Extra decorators on the MCP controller, e.g. `[Public()]` for global guards. |
|
|
731
|
+
| `pathPrefix` | `string` | none | _NestJS._ Extra prefix for tool routes. Global prefix and URI versioning are automatic. |
|
|
732
|
+
| `routeOptions` | `object` | none | _Fastify._ Extra route options for `/mcp`, such as `onRequest` hooks. |
|
|
733
|
+
| `configureRoute` | `(route) => void` | none | _AdonisJS._ Configure the MCP route, e.g. add middleware. |
|
|
675
734
|
|
|
676
735
|
### Tool options (`@McpTool()`, `mcpTool()`, `config.mcp`, `.mcp()`)
|
|
677
736
|
|
|
@@ -684,6 +743,7 @@ Your app's own validation always runs as well. The schema tells the agent what t
|
|
|
684
743
|
| `annotations` | MCP hints: `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`. Defaults come from the HTTP method (GET → read-only, DELETE → destructive). |
|
|
685
744
|
| `headers` | Static headers added to the internal request. |
|
|
686
745
|
| `scopes` | OAuth scopes the token needs to call this tool (with the `oauth` option). Missing scopes answer `403 insufficient_scope`. |
|
|
746
|
+
| `transformResponse` | Shape or enrich what the agent sees from this route's response. See [Shaping and enriching responses](#shaping-and-enriching-responses). |
|
|
687
747
|
|
|
688
748
|
Each internal request also carries `X-Mcp-Tool: <tool name>`, so you can log or meter agent traffic separately.
|
|
689
749
|
|
|
@@ -740,7 +800,7 @@ Planned (non-breaking, 1.x minor releases):
|
|
|
740
800
|
|
|
741
801
|
- Per-user tool lists (hide tools the token's scopes can't call)
|
|
742
802
|
- Next.js route handlers, Hapi and Elysia adapters
|
|
743
|
-
- Structured output schemas, and binary/file responses
|
|
803
|
+
- Structured output schemas (`outputSchema`), and binary/file responses
|
|
744
804
|
- Streaming long-running responses over SSE
|
|
745
805
|
- CLI to preview generated tools (`npx mcp-expose inspect`)
|
|
746
806
|
- Resources from GET routes, and prompts
|
package/dist/adonisjs/index.cjs
CHANGED
|
@@ -98,6 +98,38 @@ function loopbackBaseUrl(req) {
|
|
|
98
98
|
}
|
|
99
99
|
__name(loopbackBaseUrl, "loopbackBaseUrl");
|
|
100
100
|
|
|
101
|
+
// src/core/result.ts
|
|
102
|
+
function isToolResult(value) {
|
|
103
|
+
const content = value?.content;
|
|
104
|
+
return !!value && typeof value === "object" && Array.isArray(content) && content.length > 0 && content.every((c) => !!c && typeof c === "object" && typeof c.type === "string");
|
|
105
|
+
}
|
|
106
|
+
__name(isToolResult, "isToolResult");
|
|
107
|
+
function toToolResult(value) {
|
|
108
|
+
if (isToolResult(value)) return value;
|
|
109
|
+
if (typeof value === "string") return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: value
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
const text = JSON.stringify(value ?? null);
|
|
118
|
+
const result = {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
text
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
};
|
|
126
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127
|
+
result.structuredContent = value;
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
__name(toToolResult, "toToolResult");
|
|
132
|
+
|
|
101
133
|
// src/core/schema.ts
|
|
102
134
|
function isStandard(s) {
|
|
103
135
|
return typeof s === "object" && s !== null && "~standard" in s;
|
|
@@ -318,9 +350,7 @@ function splitArgs(args, mapping) {
|
|
|
318
350
|
}
|
|
319
351
|
__name(splitArgs, "splitArgs");
|
|
320
352
|
function responseToResult(res, maxChars) {
|
|
321
|
-
|
|
322
|
-
if (text.length > maxChars) text = `${text.slice(0, maxChars)}
|
|
323
|
-
\u2026[truncated ${text.length - maxChars} chars]`;
|
|
353
|
+
const text = truncate(res.body, maxChars);
|
|
324
354
|
const isError = res.status >= 400;
|
|
325
355
|
const result = {
|
|
326
356
|
content: [
|
|
@@ -345,6 +375,58 @@ ${text}` : text || `HTTP ${res.status}`
|
|
|
345
375
|
return result;
|
|
346
376
|
}
|
|
347
377
|
__name(responseToResult, "responseToResult");
|
|
378
|
+
function safeDecode(segment) {
|
|
379
|
+
try {
|
|
380
|
+
return decodeURIComponent(segment);
|
|
381
|
+
} catch {
|
|
382
|
+
return segment;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
__name(safeDecode, "safeDecode");
|
|
386
|
+
function truncate(text, maxChars) {
|
|
387
|
+
return text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
388
|
+
\u2026[truncated ${text.length - maxChars} chars]` : text;
|
|
389
|
+
}
|
|
390
|
+
__name(truncate, "truncate");
|
|
391
|
+
function toRouteToolResponse(res) {
|
|
392
|
+
let json;
|
|
393
|
+
if ((res.headers["content-type"] ?? "").includes("json") && res.body) {
|
|
394
|
+
try {
|
|
395
|
+
json = JSON.parse(res.body);
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
...res,
|
|
401
|
+
json,
|
|
402
|
+
ok: res.status < 400
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
__name(toRouteToolResponse, "toRouteToolResponse");
|
|
406
|
+
function transformedResult(value, res, maxChars) {
|
|
407
|
+
const explicit = isToolResult(value);
|
|
408
|
+
const result = toToolResult(value);
|
|
409
|
+
if (!explicit && !res.ok) {
|
|
410
|
+
result.isError = true;
|
|
411
|
+
delete result.structuredContent;
|
|
412
|
+
}
|
|
413
|
+
let truncated = false;
|
|
414
|
+
const content = result.content.map((c) => {
|
|
415
|
+
if (c.type !== "text" || c.text.length <= maxChars) return c;
|
|
416
|
+
truncated = true;
|
|
417
|
+
return {
|
|
418
|
+
...c,
|
|
419
|
+
text: truncate(c.text, maxChars)
|
|
420
|
+
};
|
|
421
|
+
});
|
|
422
|
+
if (!truncated) return result;
|
|
423
|
+
const { structuredContent: _dropped, ...rest } = result;
|
|
424
|
+
return {
|
|
425
|
+
...rest,
|
|
426
|
+
content
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
__name(transformedResult, "transformedResult");
|
|
348
430
|
function pickForwardHeaders(ctx, names) {
|
|
349
431
|
const out = {};
|
|
350
432
|
for (const name of names) {
|
|
@@ -360,6 +442,7 @@ function createRouteTool(route, opts, dispatch, serverOpts = {}) {
|
|
|
360
442
|
const { schema, mapping } = buildInputSchema(route, opts);
|
|
361
443
|
const forward = serverOpts.forwardHeaders ?? DEFAULT_FORWARD_HEADERS;
|
|
362
444
|
const maxChars = serverOpts.maxResponseChars ?? 1e5;
|
|
445
|
+
const transform = opts.transformResponse ?? serverOpts.transformResponse;
|
|
363
446
|
return {
|
|
364
447
|
name,
|
|
365
448
|
title: opts.title,
|
|
@@ -405,14 +488,53 @@ function createRouteTool(route, opts, dispatch, serverOpts = {}) {
|
|
|
405
488
|
]
|
|
406
489
|
};
|
|
407
490
|
}
|
|
408
|
-
const
|
|
491
|
+
const request = {
|
|
409
492
|
method,
|
|
410
493
|
path,
|
|
411
494
|
query,
|
|
412
495
|
body,
|
|
413
496
|
headers
|
|
414
|
-
}
|
|
415
|
-
|
|
497
|
+
};
|
|
498
|
+
const res = await dispatch(request, ctx);
|
|
499
|
+
if (!transform) return responseToResult(res, maxChars);
|
|
500
|
+
const { "content-type": _ct, ...baseHeaders } = headers;
|
|
501
|
+
const hookCtx = {
|
|
502
|
+
tool: name,
|
|
503
|
+
args,
|
|
504
|
+
request,
|
|
505
|
+
toolContext: ctx,
|
|
506
|
+
defaultResult: /* @__PURE__ */ __name(() => responseToResult(res, maxChars), "defaultResult"),
|
|
507
|
+
async callRoute(r) {
|
|
508
|
+
if (!r.path.startsWith("/")) {
|
|
509
|
+
throw new Error(`callRoute: path must start with "/", got ${JSON.stringify(r.path)}`);
|
|
510
|
+
}
|
|
511
|
+
const params = r.params ?? {};
|
|
512
|
+
const missing2 = pathParams(r.path).filter((p) => params[p] === void 0 && !r.path.includes(`:${p}?`));
|
|
513
|
+
if (missing2.length) throw new Error(`callRoute: missing params for ${r.path}: ${missing2.join(", ")}`);
|
|
514
|
+
const subPath = interpolatePath(r.path, params);
|
|
515
|
+
if (subPath.split("/").some((seg) => [
|
|
516
|
+
".",
|
|
517
|
+
".."
|
|
518
|
+
].includes(safeDecode(seg)))) {
|
|
519
|
+
throw new Error(`callRoute: path must not contain "." or ".." segments: ${JSON.stringify(subPath)}`);
|
|
520
|
+
}
|
|
521
|
+
const sub = {
|
|
522
|
+
method: (r.method ?? "GET").toUpperCase(),
|
|
523
|
+
path: subPath,
|
|
524
|
+
query: r.query ?? {},
|
|
525
|
+
body: r.body,
|
|
526
|
+
headers: {
|
|
527
|
+
...baseHeaders,
|
|
528
|
+
...r.headers
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
if (sub.body !== void 0) sub.headers["content-type"] = "application/json";
|
|
532
|
+
return toRouteToolResponse(await dispatch(sub, ctx));
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
const response = toRouteToolResponse(res);
|
|
536
|
+
const value = await transform(response, hookCtx);
|
|
537
|
+
return value === void 0 ? responseToResult(res, maxChars) : transformedResult(value, response, maxChars);
|
|
416
538
|
}
|
|
417
539
|
};
|
|
418
540
|
}
|