@ttoss/http-server-mcp 0.29.1 → 0.30.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/README.md +19 -0
- package/dist/index.cjs +76 -20
- package/dist/index.d.cts +26 -2
- package/dist/index.d.mts +26 -2
- package/dist/index.mjs +76 -21
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -503,6 +503,25 @@ register({
|
|
|
503
503
|
|
|
504
504
|
**`buildContext`** injects request-scoped values (DB clients, tenant IDs) into every handler call without threading them through each individual tool. It receives the full `ToolCallContext` so context can vary by identity or by call args.
|
|
505
505
|
|
|
506
|
+
### Localized refusals and errors
|
|
507
|
+
|
|
508
|
+
Pass `i18n` to render a [`LocalizedError`](https://ttoss.dev/docs/modules/packages/i18n-core/) thrown by a **gate** or by the **handler** as an `isError` result `{ "error": "<message>", "code": "<CODE>" }` in the caller's locale. Gates run outside the handler, so the registrar is the one place that covers both.
|
|
509
|
+
|
|
510
|
+
```ts
|
|
511
|
+
const { register } = createGatedToolRegistrar({
|
|
512
|
+
server,
|
|
513
|
+
gates: [subscriptionGate],
|
|
514
|
+
i18n: {
|
|
515
|
+
catalog,
|
|
516
|
+
// Default: the MCP request's Accept-Language (`getRequestLocale()`).
|
|
517
|
+
// Pin the agent contract to one language instead:
|
|
518
|
+
getLocale: () => 'en',
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
`onError` still runs for a handler error before it is rendered. Errors that are not localized behave exactly as without the option.
|
|
524
|
+
|
|
506
525
|
## MCP Apps (interactive UIs)
|
|
507
526
|
|
|
508
527
|
The [`io.modelcontextprotocol/ui`](https://github.com/modelcontextprotocol/ext-apps) extension lets a tool result render as an interactive view instead of text. The view is a `ui://` resource holding an HTML document, which the host loads into a sandboxed iframe and talks to over `postMessage`; the tool points at it through its `_meta`.
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,14 @@ var getApiHeaders = () => {
|
|
|
37
37
|
...(requestContextStore.getStore()?.apiHeaders ?? {})
|
|
38
38
|
};
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* The `Accept-Language` header of the current MCP request, or `undefined`
|
|
42
|
+
* outside a request or when the client sent none. The default locale source
|
|
43
|
+
* for `createGatedToolRegistrar`'s `i18n` option.
|
|
44
|
+
*/
|
|
45
|
+
var getRequestLocale = () => {
|
|
46
|
+
return requestContextStore.getStore()?.acceptLanguage;
|
|
47
|
+
};
|
|
40
48
|
|
|
41
49
|
//#endregion
|
|
42
50
|
//#region src/serveMcpRequest.ts
|
|
@@ -431,18 +439,44 @@ var registerToolFromSchema = (server, params) => {
|
|
|
431
439
|
|
|
432
440
|
//#endregion
|
|
433
441
|
//#region src/createGatedToolRegistrar.ts
|
|
434
|
-
var toolError = message => {
|
|
442
|
+
var toolError = (message, code) => {
|
|
435
443
|
return {
|
|
436
444
|
content: [{
|
|
437
445
|
type: "text",
|
|
438
|
-
text: JSON.stringify({
|
|
446
|
+
text: JSON.stringify(code === void 0 ? {
|
|
439
447
|
error: message
|
|
448
|
+
} : {
|
|
449
|
+
error: message,
|
|
450
|
+
code
|
|
440
451
|
})
|
|
441
452
|
}],
|
|
442
453
|
isError: true
|
|
443
454
|
};
|
|
444
455
|
};
|
|
445
456
|
/**
|
|
457
|
+
* The localized `isError` result for `error`, or `undefined` when it is not
|
|
458
|
+
* a `LocalizedError` or rendering fails — the caller then keeps its usual path.
|
|
459
|
+
*/
|
|
460
|
+
var localizedToolError = async ({
|
|
461
|
+
error,
|
|
462
|
+
ctx,
|
|
463
|
+
i18n
|
|
464
|
+
}) => {
|
|
465
|
+
try {
|
|
466
|
+
const requested = i18n.getLocale ? await i18n.getLocale(ctx) : getRequestLocale();
|
|
467
|
+
const {
|
|
468
|
+
renderLocalizedError
|
|
469
|
+
} = await import("@ttoss/i18n-core");
|
|
470
|
+
const rendered = renderLocalizedError({
|
|
471
|
+
error,
|
|
472
|
+
i18n: await i18n.catalog.getI18n(requested)
|
|
473
|
+
});
|
|
474
|
+
return rendered ? toolError(rendered.message, rendered.code) : void 0;
|
|
475
|
+
} catch {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
446
480
|
* Factory that returns a `register` helper for MCP tools that require
|
|
447
481
|
* authentication and a specific OAuth scope.
|
|
448
482
|
*
|
|
@@ -461,6 +495,8 @@ var toolError = message => {
|
|
|
461
495
|
* 4. Merges `buildContext` output into the handler args.
|
|
462
496
|
* 5. Wraps `null`/`undefined` results in a configurable "Not found" error result.
|
|
463
497
|
* 6. Calls `onError` on handler throw before rethrowing.
|
|
498
|
+
* 7. With `i18n`, renders a `LocalizedError` from a gate or the handler as an
|
|
499
|
+
* `isError` result in the caller's locale instead of rethrowing it.
|
|
464
500
|
*
|
|
465
501
|
* @example
|
|
466
502
|
* ```typescript
|
|
@@ -502,8 +538,37 @@ var createGatedToolRegistrar = ({
|
|
|
502
538
|
enforceScope = true,
|
|
503
539
|
onError,
|
|
504
540
|
buildContext,
|
|
505
|
-
notFoundMessage = "Not found"
|
|
541
|
+
notFoundMessage = "Not found",
|
|
542
|
+
i18n
|
|
506
543
|
}) => {
|
|
544
|
+
const renderOrRethrow = async (error, ctx) => {
|
|
545
|
+
const localized = i18n ? await localizedToolError({
|
|
546
|
+
error,
|
|
547
|
+
ctx,
|
|
548
|
+
i18n
|
|
549
|
+
}) : void 0;
|
|
550
|
+
if (localized) return localized;
|
|
551
|
+
throw error;
|
|
552
|
+
};
|
|
553
|
+
const runMethod = async (def, ctx) => {
|
|
554
|
+
const extra = buildContext ? buildContext(ctx) : {};
|
|
555
|
+
try {
|
|
556
|
+
const result = await def.method({
|
|
557
|
+
...ctx.args,
|
|
558
|
+
...extra
|
|
559
|
+
});
|
|
560
|
+
if (result == null) return toolError(notFoundMessage);
|
|
561
|
+
return {
|
|
562
|
+
content: [{
|
|
563
|
+
type: "text",
|
|
564
|
+
text: JSON.stringify(result)
|
|
565
|
+
}]
|
|
566
|
+
};
|
|
567
|
+
} catch (error) {
|
|
568
|
+
if (onError) await onError(error, ctx);
|
|
569
|
+
return renderOrRethrow(error, ctx);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
507
572
|
const register = def => {
|
|
508
573
|
const handler = async args => {
|
|
509
574
|
const identity = resolveIdentity();
|
|
@@ -517,24 +582,12 @@ var createGatedToolRegistrar = ({
|
|
|
517
582
|
const scopeError = checkScope(identity, def.requiredScope);
|
|
518
583
|
if (scopeError) return scopeError;
|
|
519
584
|
}
|
|
520
|
-
await runGates([...gates, ...(def.gates ?? [])], ctx);
|
|
521
|
-
const extra = buildContext ? buildContext(ctx) : {};
|
|
522
585
|
try {
|
|
523
|
-
|
|
524
|
-
...args,
|
|
525
|
-
...extra
|
|
526
|
-
});
|
|
527
|
-
if (result == null) return toolError(notFoundMessage);
|
|
528
|
-
return {
|
|
529
|
-
content: [{
|
|
530
|
-
type: "text",
|
|
531
|
-
text: JSON.stringify(result)
|
|
532
|
-
}]
|
|
533
|
-
};
|
|
586
|
+
await runGates([...gates, ...(def.gates ?? [])], ctx);
|
|
534
587
|
} catch (error) {
|
|
535
|
-
|
|
536
|
-
throw error;
|
|
588
|
+
return renderOrRethrow(error, ctx);
|
|
537
589
|
}
|
|
590
|
+
return runMethod(def, ctx);
|
|
538
591
|
};
|
|
539
592
|
server.registerTool(def.name, {
|
|
540
593
|
description: def.description,
|
|
@@ -856,10 +909,12 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
856
909
|
await serveRequest(ctx, body);
|
|
857
910
|
ctx.respond = false;
|
|
858
911
|
};
|
|
859
|
-
|
|
912
|
+
const acceptLanguage = ctx.get("Accept-Language") || void 0;
|
|
913
|
+
if (needsContext || acceptLanguage) await requestContextStore.run({
|
|
860
914
|
apiBaseUrl,
|
|
861
915
|
apiHeaders,
|
|
862
|
-
identity
|
|
916
|
+
identity,
|
|
917
|
+
acceptLanguage
|
|
863
918
|
}, runRequest);else await runRequest();
|
|
864
919
|
};
|
|
865
920
|
const postHandler = async ctx => {
|
|
@@ -913,6 +968,7 @@ exports.createMcpRouter = createMcpRouter;
|
|
|
913
968
|
exports.errorBodyMessage = errorBodyMessage;
|
|
914
969
|
exports.getApiHeaders = getApiHeaders;
|
|
915
970
|
exports.getIdentity = getIdentity;
|
|
971
|
+
exports.getRequestLocale = getRequestLocale;
|
|
916
972
|
exports.registerAppResource = registerAppResource;
|
|
917
973
|
exports.registerToolFromSchema = registerToolFromSchema;
|
|
918
974
|
Object.defineProperty(exports, 'z', {
|
package/dist/index.d.cts
CHANGED
|
@@ -16,6 +16,7 @@ import compose from "koa-compose";
|
|
|
16
16
|
import { ListenOptions, Socket } from "net";
|
|
17
17
|
import { ParsedUrlQuery } from "querystring";
|
|
18
18
|
import * as url from "url";
|
|
19
|
+
import { Catalog } from "@ttoss/i18n-core";
|
|
19
20
|
import { z } from "zod";
|
|
20
21
|
|
|
21
22
|
//#region ../../node_modules/.pnpm/@types+koa@3.0.3/node_modules/@types/koa/index.d.ts
|
|
@@ -683,6 +684,12 @@ declare const getIdentity: <T = unknown>() => T | undefined;
|
|
|
683
684
|
* Returns `{}` outside a request, or when `getApiHeaders` is not configured.
|
|
684
685
|
*/
|
|
685
686
|
declare const getApiHeaders: () => Record<string, string>;
|
|
687
|
+
/**
|
|
688
|
+
* The `Accept-Language` header of the current MCP request, or `undefined`
|
|
689
|
+
* outside a request or when the client sent none. The default locale source
|
|
690
|
+
* for `createGatedToolRegistrar`'s `i18n` option.
|
|
691
|
+
*/
|
|
692
|
+
declare const getRequestLocale: () => string | undefined;
|
|
686
693
|
//#endregion
|
|
687
694
|
//#region src/registerAppResource.d.ts
|
|
688
695
|
/**
|
|
@@ -1062,6 +1069,22 @@ type CreateGatedToolRegistrarOptions = {
|
|
|
1062
1069
|
* `null` or `undefined`. Defaults to `"Not found"`.
|
|
1063
1070
|
*/
|
|
1064
1071
|
notFoundMessage?: string;
|
|
1072
|
+
/**
|
|
1073
|
+
* Renders a `LocalizedError` — thrown by a gate or by the handler — as an
|
|
1074
|
+
* `isError` result `{ error: <message>, code }` in the caller's locale,
|
|
1075
|
+
* instead of letting the SDK surface its source-language message. Gates run
|
|
1076
|
+
* outside the handler, so this is the one place both are covered.
|
|
1077
|
+
*
|
|
1078
|
+
* `getLocale` defaults to the MCP request's `Accept-Language`
|
|
1079
|
+
* (`getRequestLocale()`); return a fixed locale to pin the agent contract to
|
|
1080
|
+
* one language. `onError` still runs for a handler error before it is
|
|
1081
|
+
* rendered. Errors that are not localized behave exactly as without this
|
|
1082
|
+
* option.
|
|
1083
|
+
*/
|
|
1084
|
+
i18n?: {
|
|
1085
|
+
catalog: Pick<Catalog, 'getI18n'>;
|
|
1086
|
+
getLocale?: (ctx: ToolCallContext) => string | string[] | null | undefined | Promise<string | string[] | null | undefined>;
|
|
1087
|
+
};
|
|
1065
1088
|
};
|
|
1066
1089
|
declare const createGatedToolRegistrar: ({
|
|
1067
1090
|
server,
|
|
@@ -1070,7 +1093,8 @@ declare const createGatedToolRegistrar: ({
|
|
|
1070
1093
|
enforceScope,
|
|
1071
1094
|
onError,
|
|
1072
1095
|
buildContext,
|
|
1073
|
-
notFoundMessage
|
|
1096
|
+
notFoundMessage,
|
|
1097
|
+
i18n
|
|
1074
1098
|
}: CreateGatedToolRegistrarOptions) => {
|
|
1075
1099
|
register: (def: GatedToolDef) => void;
|
|
1076
1100
|
};
|
|
@@ -1474,4 +1498,4 @@ interface McpRouterOptions {
|
|
|
1474
1498
|
*/
|
|
1475
1499
|
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => Router<Application.DefaultState, Application.DefaultContext>;
|
|
1476
1500
|
//#endregion
|
|
1477
|
-
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, type JsonObjectSchema, McpAuthOptions, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, registerAppResource, registerToolFromSchema, z };
|
|
1501
|
+
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, type JsonObjectSchema, McpAuthOptions, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
package/dist/index.d.mts
CHANGED
|
@@ -18,6 +18,7 @@ import compose from "koa-compose";
|
|
|
18
18
|
import { ListenOptions, Socket } from "net";
|
|
19
19
|
import { ParsedUrlQuery } from "querystring";
|
|
20
20
|
import * as url from "url";
|
|
21
|
+
import { Catalog } from "@ttoss/i18n-core";
|
|
21
22
|
|
|
22
23
|
//#region ../../node_modules/.pnpm/@types+koa@3.0.3/node_modules/@types/koa/index.d.ts
|
|
23
24
|
declare interface ContextDelegatedRequest {
|
|
@@ -684,6 +685,12 @@ declare const getIdentity: <T = unknown>() => T | undefined;
|
|
|
684
685
|
* Returns `{}` outside a request, or when `getApiHeaders` is not configured.
|
|
685
686
|
*/
|
|
686
687
|
declare const getApiHeaders: () => Record<string, string>;
|
|
688
|
+
/**
|
|
689
|
+
* The `Accept-Language` header of the current MCP request, or `undefined`
|
|
690
|
+
* outside a request or when the client sent none. The default locale source
|
|
691
|
+
* for `createGatedToolRegistrar`'s `i18n` option.
|
|
692
|
+
*/
|
|
693
|
+
declare const getRequestLocale: () => string | undefined;
|
|
687
694
|
//#endregion
|
|
688
695
|
//#region src/registerAppResource.d.ts
|
|
689
696
|
/**
|
|
@@ -1063,6 +1070,22 @@ type CreateGatedToolRegistrarOptions = {
|
|
|
1063
1070
|
* `null` or `undefined`. Defaults to `"Not found"`.
|
|
1064
1071
|
*/
|
|
1065
1072
|
notFoundMessage?: string;
|
|
1073
|
+
/**
|
|
1074
|
+
* Renders a `LocalizedError` — thrown by a gate or by the handler — as an
|
|
1075
|
+
* `isError` result `{ error: <message>, code }` in the caller's locale,
|
|
1076
|
+
* instead of letting the SDK surface its source-language message. Gates run
|
|
1077
|
+
* outside the handler, so this is the one place both are covered.
|
|
1078
|
+
*
|
|
1079
|
+
* `getLocale` defaults to the MCP request's `Accept-Language`
|
|
1080
|
+
* (`getRequestLocale()`); return a fixed locale to pin the agent contract to
|
|
1081
|
+
* one language. `onError` still runs for a handler error before it is
|
|
1082
|
+
* rendered. Errors that are not localized behave exactly as without this
|
|
1083
|
+
* option.
|
|
1084
|
+
*/
|
|
1085
|
+
i18n?: {
|
|
1086
|
+
catalog: Pick<Catalog, 'getI18n'>;
|
|
1087
|
+
getLocale?: (ctx: ToolCallContext) => string | string[] | null | undefined | Promise<string | string[] | null | undefined>;
|
|
1088
|
+
};
|
|
1066
1089
|
};
|
|
1067
1090
|
declare const createGatedToolRegistrar: ({
|
|
1068
1091
|
server,
|
|
@@ -1071,7 +1094,8 @@ declare const createGatedToolRegistrar: ({
|
|
|
1071
1094
|
enforceScope,
|
|
1072
1095
|
onError,
|
|
1073
1096
|
buildContext,
|
|
1074
|
-
notFoundMessage
|
|
1097
|
+
notFoundMessage,
|
|
1098
|
+
i18n
|
|
1075
1099
|
}: CreateGatedToolRegistrarOptions) => {
|
|
1076
1100
|
register: (def: GatedToolDef) => void;
|
|
1077
1101
|
};
|
|
@@ -1475,4 +1499,4 @@ interface McpRouterOptions {
|
|
|
1475
1499
|
*/
|
|
1476
1500
|
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => Router<Application.DefaultState, Application.DefaultContext>;
|
|
1477
1501
|
//#endregion
|
|
1478
|
-
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, type JsonObjectSchema, McpAuthOptions, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, registerAppResource, registerToolFromSchema, z };
|
|
1502
|
+
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, type JsonObjectSchema, McpAuthOptions, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
package/dist/index.mjs
CHANGED
|
@@ -34,6 +34,14 @@ var getApiHeaders = () => {
|
|
|
34
34
|
...(requestContextStore.getStore()?.apiHeaders ?? {})
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* The `Accept-Language` header of the current MCP request, or `undefined`
|
|
39
|
+
* outside a request or when the client sent none. The default locale source
|
|
40
|
+
* for `createGatedToolRegistrar`'s `i18n` option.
|
|
41
|
+
*/
|
|
42
|
+
var getRequestLocale = () => {
|
|
43
|
+
return requestContextStore.getStore()?.acceptLanguage;
|
|
44
|
+
};
|
|
37
45
|
|
|
38
46
|
//#endregion
|
|
39
47
|
//#region src/serveMcpRequest.ts
|
|
@@ -428,18 +436,44 @@ var registerToolFromSchema = (server, params) => {
|
|
|
428
436
|
|
|
429
437
|
//#endregion
|
|
430
438
|
//#region src/createGatedToolRegistrar.ts
|
|
431
|
-
var toolError = message => {
|
|
439
|
+
var toolError = (message, code) => {
|
|
432
440
|
return {
|
|
433
441
|
content: [{
|
|
434
442
|
type: "text",
|
|
435
|
-
text: JSON.stringify({
|
|
443
|
+
text: JSON.stringify(code === void 0 ? {
|
|
436
444
|
error: message
|
|
445
|
+
} : {
|
|
446
|
+
error: message,
|
|
447
|
+
code
|
|
437
448
|
})
|
|
438
449
|
}],
|
|
439
450
|
isError: true
|
|
440
451
|
};
|
|
441
452
|
};
|
|
442
453
|
/**
|
|
454
|
+
* The localized `isError` result for `error`, or `undefined` when it is not
|
|
455
|
+
* a `LocalizedError` or rendering fails — the caller then keeps its usual path.
|
|
456
|
+
*/
|
|
457
|
+
var localizedToolError = async ({
|
|
458
|
+
error,
|
|
459
|
+
ctx,
|
|
460
|
+
i18n
|
|
461
|
+
}) => {
|
|
462
|
+
try {
|
|
463
|
+
const requested = i18n.getLocale ? await i18n.getLocale(ctx) : getRequestLocale();
|
|
464
|
+
const {
|
|
465
|
+
renderLocalizedError
|
|
466
|
+
} = await import("@ttoss/i18n-core");
|
|
467
|
+
const rendered = renderLocalizedError({
|
|
468
|
+
error,
|
|
469
|
+
i18n: await i18n.catalog.getI18n(requested)
|
|
470
|
+
});
|
|
471
|
+
return rendered ? toolError(rendered.message, rendered.code) : void 0;
|
|
472
|
+
} catch {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
443
477
|
* Factory that returns a `register` helper for MCP tools that require
|
|
444
478
|
* authentication and a specific OAuth scope.
|
|
445
479
|
*
|
|
@@ -458,6 +492,8 @@ var toolError = message => {
|
|
|
458
492
|
* 4. Merges `buildContext` output into the handler args.
|
|
459
493
|
* 5. Wraps `null`/`undefined` results in a configurable "Not found" error result.
|
|
460
494
|
* 6. Calls `onError` on handler throw before rethrowing.
|
|
495
|
+
* 7. With `i18n`, renders a `LocalizedError` from a gate or the handler as an
|
|
496
|
+
* `isError` result in the caller's locale instead of rethrowing it.
|
|
461
497
|
*
|
|
462
498
|
* @example
|
|
463
499
|
* ```typescript
|
|
@@ -499,8 +535,37 @@ var createGatedToolRegistrar = ({
|
|
|
499
535
|
enforceScope = true,
|
|
500
536
|
onError,
|
|
501
537
|
buildContext,
|
|
502
|
-
notFoundMessage = "Not found"
|
|
538
|
+
notFoundMessage = "Not found",
|
|
539
|
+
i18n
|
|
503
540
|
}) => {
|
|
541
|
+
const renderOrRethrow = async (error, ctx) => {
|
|
542
|
+
const localized = i18n ? await localizedToolError({
|
|
543
|
+
error,
|
|
544
|
+
ctx,
|
|
545
|
+
i18n
|
|
546
|
+
}) : void 0;
|
|
547
|
+
if (localized) return localized;
|
|
548
|
+
throw error;
|
|
549
|
+
};
|
|
550
|
+
const runMethod = async (def, ctx) => {
|
|
551
|
+
const extra = buildContext ? buildContext(ctx) : {};
|
|
552
|
+
try {
|
|
553
|
+
const result = await def.method({
|
|
554
|
+
...ctx.args,
|
|
555
|
+
...extra
|
|
556
|
+
});
|
|
557
|
+
if (result == null) return toolError(notFoundMessage);
|
|
558
|
+
return {
|
|
559
|
+
content: [{
|
|
560
|
+
type: "text",
|
|
561
|
+
text: JSON.stringify(result)
|
|
562
|
+
}]
|
|
563
|
+
};
|
|
564
|
+
} catch (error) {
|
|
565
|
+
if (onError) await onError(error, ctx);
|
|
566
|
+
return renderOrRethrow(error, ctx);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
504
569
|
const register = def => {
|
|
505
570
|
const handler = async args => {
|
|
506
571
|
const identity = resolveIdentity();
|
|
@@ -514,24 +579,12 @@ var createGatedToolRegistrar = ({
|
|
|
514
579
|
const scopeError = checkScope(identity, def.requiredScope);
|
|
515
580
|
if (scopeError) return scopeError;
|
|
516
581
|
}
|
|
517
|
-
await runGates([...gates, ...(def.gates ?? [])], ctx);
|
|
518
|
-
const extra = buildContext ? buildContext(ctx) : {};
|
|
519
582
|
try {
|
|
520
|
-
|
|
521
|
-
...args,
|
|
522
|
-
...extra
|
|
523
|
-
});
|
|
524
|
-
if (result == null) return toolError(notFoundMessage);
|
|
525
|
-
return {
|
|
526
|
-
content: [{
|
|
527
|
-
type: "text",
|
|
528
|
-
text: JSON.stringify(result)
|
|
529
|
-
}]
|
|
530
|
-
};
|
|
583
|
+
await runGates([...gates, ...(def.gates ?? [])], ctx);
|
|
531
584
|
} catch (error) {
|
|
532
|
-
|
|
533
|
-
throw error;
|
|
585
|
+
return renderOrRethrow(error, ctx);
|
|
534
586
|
}
|
|
587
|
+
return runMethod(def, ctx);
|
|
535
588
|
};
|
|
536
589
|
server.registerTool(def.name, {
|
|
537
590
|
description: def.description,
|
|
@@ -853,10 +906,12 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
853
906
|
await serveRequest(ctx, body);
|
|
854
907
|
ctx.respond = false;
|
|
855
908
|
};
|
|
856
|
-
|
|
909
|
+
const acceptLanguage = ctx.get("Accept-Language") || void 0;
|
|
910
|
+
if (needsContext || acceptLanguage) await requestContextStore.run({
|
|
857
911
|
apiBaseUrl,
|
|
858
912
|
apiHeaders,
|
|
859
|
-
identity
|
|
913
|
+
identity,
|
|
914
|
+
acceptLanguage
|
|
860
915
|
}, runRequest);else await runRequest();
|
|
861
916
|
};
|
|
862
917
|
const postHandler = async ctx => {
|
|
@@ -895,4 +950,4 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
895
950
|
};
|
|
896
951
|
|
|
897
952
|
//#endregion
|
|
898
|
-
export { McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, registerAppResource, registerToolFromSchema, z };
|
|
953
|
+
export { McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttoss/http-server-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server integration for @ttoss/http-server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -37,9 +37,10 @@
|
|
|
37
37
|
"@modelcontextprotocol/node": "^2.0.0",
|
|
38
38
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
39
39
|
"zod": "^4.4.3",
|
|
40
|
-
"@ttoss/
|
|
41
|
-
"@ttoss/http-server
|
|
42
|
-
"@ttoss/auth
|
|
40
|
+
"@ttoss/auth-core": "^0.15.0",
|
|
41
|
+
"@ttoss/http-server": "^0.10.0",
|
|
42
|
+
"@ttoss/http-server-auth": "^0.7.1",
|
|
43
|
+
"@ttoss/i18n-core": "^0.2.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@modelcontextprotocol/client": "^2.0.0",
|