@ttoss/http-server-mcp 0.28.1 → 0.29.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 +39 -0
- package/dist/index.cjs +54 -2
- package/dist/index.d.cts +31 -1
- package/dist/index.d.mts +31 -1
- package/dist/index.mjs +53 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -127,6 +127,31 @@ const data = await apiCall('GET', 'https://partner.api.com/data', {
|
|
|
127
127
|
|
|
128
128
|
`apiCall` throws with a clear message when called with a relative path and no `apiBaseUrl` is configured in the context.
|
|
129
129
|
|
|
130
|
+
### Errors
|
|
131
|
+
|
|
132
|
+
A non-2xx response throws, with the message read off the error body — `{ error: 'text' }` and `{ error: { code, message } }` both work, the latter as `code: message`. A body naming neither falls back to `HTTP <status>`.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// { "error": { "code": "plan_limit_reached", "message": "Upgrade to add more." } }
|
|
136
|
+
// → Error: plan_limit_reached: Upgrade to add more.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Inside a tool handler the thrown message is what the MCP SDK returns to the client, so it is the whole answer the calling model acts on.
|
|
140
|
+
|
|
141
|
+
### Using your own HTTP client
|
|
142
|
+
|
|
143
|
+
A handler that does not use `apiCall` can still reuse its two request-scoped pieces: `getApiHeaders()` returns the headers `getApiHeaders` produced for the current MCP request, and `errorBodyMessage(body)` renders an error body exactly as `apiCall` does.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { errorBodyMessage, getApiHeaders } from '@ttoss/http-server-mcp';
|
|
147
|
+
|
|
148
|
+
const response = await fetch(url, { headers: getApiHeaders() });
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const body = await response.json().catch(() => undefined);
|
|
151
|
+
throw new Error(errorBodyMessage(body) ?? `HTTP ${response.status}`);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
130
155
|
## Authentication
|
|
131
156
|
|
|
132
157
|
`createMcpRouter` supports OAuth 2.0 Bearer token authentication via the `auth` option. Incoming MCP requests must include a valid `Authorization: Bearer <token>` header — invalid or missing tokens receive a `401 Unauthorized` response. The MCP lifecycle methods `initialize` and `tools/list` are exempt by default so clients can discover the server before authenticating (see [Public methods and discovery](#public-methods-and-discovery)).
|
|
@@ -593,6 +618,20 @@ Generic HTTP helper for use inside MCP tool handlers.
|
|
|
593
618
|
|
|
594
619
|
**Returns:** `Promise<unknown>` — Parsed JSON response body
|
|
595
620
|
|
|
621
|
+
**Throws:** `Error` on a non-2xx response, carrying the error body's message (see [Errors](#errors))
|
|
622
|
+
|
|
623
|
+
### `getApiHeaders()`
|
|
624
|
+
|
|
625
|
+
Returns a copy of the headers `createMcpRouter`'s `getApiHeaders` option produced for the current MCP request — the same headers `apiCall` injects.
|
|
626
|
+
|
|
627
|
+
**Returns:** `Record<string, string>` — `{}` outside a request or when `getApiHeaders` is not configured
|
|
628
|
+
|
|
629
|
+
### `errorBodyMessage(body)`
|
|
630
|
+
|
|
631
|
+
Reads the message off a REST error body — `{ error: 'text' }` or `{ error: { code?, message? } }` (rendered `code: message`).
|
|
632
|
+
|
|
633
|
+
**Returns:** `string | undefined` — `undefined` when the body carries no message
|
|
634
|
+
|
|
596
635
|
### `getIdentity<T>()`
|
|
597
636
|
|
|
598
637
|
Returns the verified JWT payload for the current MCP request. Only available inside a tool handler when `auth` is configured. Returns `undefined` when called outside an authenticated context.
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,19 @@ var requestContextStore = new node_async_hooks.AsyncLocalStorage();
|
|
|
24
24
|
var getIdentity = () => {
|
|
25
25
|
return requestContextStore.getStore()?.identity;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Returns the headers `getApiHeaders` produced for the current MCP request —
|
|
29
|
+
* the same headers `apiCall` injects. Use it inside a tool handler that runs
|
|
30
|
+
* its own HTTP client instead of `apiCall`, so caller credentials still reach
|
|
31
|
+
* the upstream API.
|
|
32
|
+
*
|
|
33
|
+
* Returns `{}` outside a request, or when `getApiHeaders` is not configured.
|
|
34
|
+
*/
|
|
35
|
+
var getApiHeaders = () => {
|
|
36
|
+
return {
|
|
37
|
+
...(requestContextStore.getStore()?.apiHeaders ?? {})
|
|
38
|
+
};
|
|
39
|
+
};
|
|
27
40
|
|
|
28
41
|
//#endregion
|
|
29
42
|
//#region src/serveMcpRequest.ts
|
|
@@ -588,6 +601,43 @@ var buildVerifyToken = auth => {
|
|
|
588
601
|
return payload;
|
|
589
602
|
};
|
|
590
603
|
};
|
|
604
|
+
var isRecord = value => {
|
|
605
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
606
|
+
};
|
|
607
|
+
var readText = value => {
|
|
608
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
609
|
+
};
|
|
610
|
+
/**
|
|
611
|
+
* The message an error body carries, across the two shapes REST APIs answer
|
|
612
|
+
* with: `{ error: 'text' }` and `{ error: { code?, message? } }`.
|
|
613
|
+
*
|
|
614
|
+
* The structured one is why this is read rather than taken: an object handed
|
|
615
|
+
* to `new Error()` renders as `[object Object]`, and inside a tool handler
|
|
616
|
+
* that string is the whole answer the calling model acts on.
|
|
617
|
+
*
|
|
618
|
+
* Exported so a tool handler with its own HTTP client renders error bodies
|
|
619
|
+
* exactly as `apiCall` does. Returns `undefined` when the body carries no
|
|
620
|
+
* message.
|
|
621
|
+
*
|
|
622
|
+
* @example
|
|
623
|
+
* ```typescript
|
|
624
|
+
* if (!response.ok) {
|
|
625
|
+
* const body = await response.json().catch(() => undefined);
|
|
626
|
+
* throw new Error(errorBodyMessage(body) ?? `HTTP ${response.status}`);
|
|
627
|
+
* }
|
|
628
|
+
* ```
|
|
629
|
+
*/
|
|
630
|
+
var errorBodyMessage = body => {
|
|
631
|
+
if (!isRecord(body)) return;
|
|
632
|
+
const {
|
|
633
|
+
error
|
|
634
|
+
} = body;
|
|
635
|
+
if (!isRecord(error)) return readText(error);
|
|
636
|
+
const code = readText(error.code);
|
|
637
|
+
const message = readText(error.message);
|
|
638
|
+
if (message === void 0) return code;
|
|
639
|
+
return code === void 0 ? message : `${code}: ${message}`;
|
|
640
|
+
};
|
|
591
641
|
/**
|
|
592
642
|
* Generic HTTP helper for use inside MCP tool handlers.
|
|
593
643
|
*
|
|
@@ -663,12 +713,12 @@ var apiCall = async (method, url, options) => {
|
|
|
663
713
|
body: hasBody ? JSON.stringify(options.body) : void 0
|
|
664
714
|
});
|
|
665
715
|
if (!response.ok) {
|
|
666
|
-
const
|
|
716
|
+
const errorBody = await response.json().catch(() => {
|
|
667
717
|
return {
|
|
668
718
|
error: response.statusText
|
|
669
719
|
};
|
|
670
720
|
});
|
|
671
|
-
throw new Error(
|
|
721
|
+
throw new Error(errorBodyMessage(errorBody) ?? `HTTP ${response.status}`);
|
|
672
722
|
}
|
|
673
723
|
if (response.status === 204 || response.status === 205) return;
|
|
674
724
|
if (response.headers.get("content-type")?.includes("application/json")) return response.json();
|
|
@@ -860,6 +910,8 @@ exports.apiCall = apiCall;
|
|
|
860
910
|
exports.checkScopes = checkScopes;
|
|
861
911
|
exports.createGatedToolRegistrar = createGatedToolRegistrar;
|
|
862
912
|
exports.createMcpRouter = createMcpRouter;
|
|
913
|
+
exports.errorBodyMessage = errorBodyMessage;
|
|
914
|
+
exports.getApiHeaders = getApiHeaders;
|
|
863
915
|
exports.getIdentity = getIdentity;
|
|
864
916
|
exports.registerAppResource = registerAppResource;
|
|
865
917
|
exports.registerToolFromSchema = registerToolFromSchema;
|
package/dist/index.d.cts
CHANGED
|
@@ -674,6 +674,15 @@ declare namespace Application {
|
|
|
674
674
|
* Omitting the type parameter keeps the return type as `unknown | undefined`.
|
|
675
675
|
*/
|
|
676
676
|
declare const getIdentity: <T = unknown>() => T | undefined;
|
|
677
|
+
/**
|
|
678
|
+
* Returns the headers `getApiHeaders` produced for the current MCP request —
|
|
679
|
+
* the same headers `apiCall` injects. Use it inside a tool handler that runs
|
|
680
|
+
* its own HTTP client instead of `apiCall`, so caller credentials still reach
|
|
681
|
+
* the upstream API.
|
|
682
|
+
*
|
|
683
|
+
* Returns `{}` outside a request, or when `getApiHeaders` is not configured.
|
|
684
|
+
*/
|
|
685
|
+
declare const getApiHeaders: () => Record<string, string>;
|
|
677
686
|
//#endregion
|
|
678
687
|
//#region src/registerAppResource.d.ts
|
|
679
688
|
/**
|
|
@@ -1191,6 +1200,27 @@ interface McpAuthOptions {
|
|
|
1191
1200
|
*/
|
|
1192
1201
|
resourceIndicator?: string | string[];
|
|
1193
1202
|
}
|
|
1203
|
+
/**
|
|
1204
|
+
* The message an error body carries, across the two shapes REST APIs answer
|
|
1205
|
+
* with: `{ error: 'text' }` and `{ error: { code?, message? } }`.
|
|
1206
|
+
*
|
|
1207
|
+
* The structured one is why this is read rather than taken: an object handed
|
|
1208
|
+
* to `new Error()` renders as `[object Object]`, and inside a tool handler
|
|
1209
|
+
* that string is the whole answer the calling model acts on.
|
|
1210
|
+
*
|
|
1211
|
+
* Exported so a tool handler with its own HTTP client renders error bodies
|
|
1212
|
+
* exactly as `apiCall` does. Returns `undefined` when the body carries no
|
|
1213
|
+
* message.
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* ```typescript
|
|
1217
|
+
* if (!response.ok) {
|
|
1218
|
+
* const body = await response.json().catch(() => undefined);
|
|
1219
|
+
* throw new Error(errorBodyMessage(body) ?? `HTTP ${response.status}`);
|
|
1220
|
+
* }
|
|
1221
|
+
* ```
|
|
1222
|
+
*/
|
|
1223
|
+
declare const errorBodyMessage: (body: unknown) => string | undefined;
|
|
1194
1224
|
/**
|
|
1195
1225
|
* Options for a single `apiCall` request.
|
|
1196
1226
|
*/
|
|
@@ -1444,4 +1474,4 @@ interface McpRouterOptions {
|
|
|
1444
1474
|
*/
|
|
1445
1475
|
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => Router<Application.DefaultState, Application.DefaultContext>;
|
|
1446
1476
|
//#endregion
|
|
1447
|
-
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, getIdentity, registerAppResource, registerToolFromSchema, z };
|
|
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 };
|
package/dist/index.d.mts
CHANGED
|
@@ -675,6 +675,15 @@ declare namespace Application {
|
|
|
675
675
|
* Omitting the type parameter keeps the return type as `unknown | undefined`.
|
|
676
676
|
*/
|
|
677
677
|
declare const getIdentity: <T = unknown>() => T | undefined;
|
|
678
|
+
/**
|
|
679
|
+
* Returns the headers `getApiHeaders` produced for the current MCP request —
|
|
680
|
+
* the same headers `apiCall` injects. Use it inside a tool handler that runs
|
|
681
|
+
* its own HTTP client instead of `apiCall`, so caller credentials still reach
|
|
682
|
+
* the upstream API.
|
|
683
|
+
*
|
|
684
|
+
* Returns `{}` outside a request, or when `getApiHeaders` is not configured.
|
|
685
|
+
*/
|
|
686
|
+
declare const getApiHeaders: () => Record<string, string>;
|
|
678
687
|
//#endregion
|
|
679
688
|
//#region src/registerAppResource.d.ts
|
|
680
689
|
/**
|
|
@@ -1192,6 +1201,27 @@ interface McpAuthOptions {
|
|
|
1192
1201
|
*/
|
|
1193
1202
|
resourceIndicator?: string | string[];
|
|
1194
1203
|
}
|
|
1204
|
+
/**
|
|
1205
|
+
* The message an error body carries, across the two shapes REST APIs answer
|
|
1206
|
+
* with: `{ error: 'text' }` and `{ error: { code?, message? } }`.
|
|
1207
|
+
*
|
|
1208
|
+
* The structured one is why this is read rather than taken: an object handed
|
|
1209
|
+
* to `new Error()` renders as `[object Object]`, and inside a tool handler
|
|
1210
|
+
* that string is the whole answer the calling model acts on.
|
|
1211
|
+
*
|
|
1212
|
+
* Exported so a tool handler with its own HTTP client renders error bodies
|
|
1213
|
+
* exactly as `apiCall` does. Returns `undefined` when the body carries no
|
|
1214
|
+
* message.
|
|
1215
|
+
*
|
|
1216
|
+
* @example
|
|
1217
|
+
* ```typescript
|
|
1218
|
+
* if (!response.ok) {
|
|
1219
|
+
* const body = await response.json().catch(() => undefined);
|
|
1220
|
+
* throw new Error(errorBodyMessage(body) ?? `HTTP ${response.status}`);
|
|
1221
|
+
* }
|
|
1222
|
+
* ```
|
|
1223
|
+
*/
|
|
1224
|
+
declare const errorBodyMessage: (body: unknown) => string | undefined;
|
|
1195
1225
|
/**
|
|
1196
1226
|
* Options for a single `apiCall` request.
|
|
1197
1227
|
*/
|
|
@@ -1445,4 +1475,4 @@ interface McpRouterOptions {
|
|
|
1445
1475
|
*/
|
|
1446
1476
|
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => Router<Application.DefaultState, Application.DefaultContext>;
|
|
1447
1477
|
//#endregion
|
|
1448
|
-
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, getIdentity, registerAppResource, registerToolFromSchema, z };
|
|
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -21,6 +21,19 @@ var requestContextStore = new AsyncLocalStorage();
|
|
|
21
21
|
var getIdentity = () => {
|
|
22
22
|
return requestContextStore.getStore()?.identity;
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Returns the headers `getApiHeaders` produced for the current MCP request —
|
|
26
|
+
* the same headers `apiCall` injects. Use it inside a tool handler that runs
|
|
27
|
+
* its own HTTP client instead of `apiCall`, so caller credentials still reach
|
|
28
|
+
* the upstream API.
|
|
29
|
+
*
|
|
30
|
+
* Returns `{}` outside a request, or when `getApiHeaders` is not configured.
|
|
31
|
+
*/
|
|
32
|
+
var getApiHeaders = () => {
|
|
33
|
+
return {
|
|
34
|
+
...(requestContextStore.getStore()?.apiHeaders ?? {})
|
|
35
|
+
};
|
|
36
|
+
};
|
|
24
37
|
|
|
25
38
|
//#endregion
|
|
26
39
|
//#region src/serveMcpRequest.ts
|
|
@@ -585,6 +598,43 @@ var buildVerifyToken = auth => {
|
|
|
585
598
|
return payload;
|
|
586
599
|
};
|
|
587
600
|
};
|
|
601
|
+
var isRecord = value => {
|
|
602
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
603
|
+
};
|
|
604
|
+
var readText = value => {
|
|
605
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
606
|
+
};
|
|
607
|
+
/**
|
|
608
|
+
* The message an error body carries, across the two shapes REST APIs answer
|
|
609
|
+
* with: `{ error: 'text' }` and `{ error: { code?, message? } }`.
|
|
610
|
+
*
|
|
611
|
+
* The structured one is why this is read rather than taken: an object handed
|
|
612
|
+
* to `new Error()` renders as `[object Object]`, and inside a tool handler
|
|
613
|
+
* that string is the whole answer the calling model acts on.
|
|
614
|
+
*
|
|
615
|
+
* Exported so a tool handler with its own HTTP client renders error bodies
|
|
616
|
+
* exactly as `apiCall` does. Returns `undefined` when the body carries no
|
|
617
|
+
* message.
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```typescript
|
|
621
|
+
* if (!response.ok) {
|
|
622
|
+
* const body = await response.json().catch(() => undefined);
|
|
623
|
+
* throw new Error(errorBodyMessage(body) ?? `HTTP ${response.status}`);
|
|
624
|
+
* }
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
var errorBodyMessage = body => {
|
|
628
|
+
if (!isRecord(body)) return;
|
|
629
|
+
const {
|
|
630
|
+
error
|
|
631
|
+
} = body;
|
|
632
|
+
if (!isRecord(error)) return readText(error);
|
|
633
|
+
const code = readText(error.code);
|
|
634
|
+
const message = readText(error.message);
|
|
635
|
+
if (message === void 0) return code;
|
|
636
|
+
return code === void 0 ? message : `${code}: ${message}`;
|
|
637
|
+
};
|
|
588
638
|
/**
|
|
589
639
|
* Generic HTTP helper for use inside MCP tool handlers.
|
|
590
640
|
*
|
|
@@ -660,12 +710,12 @@ var apiCall = async (method, url, options) => {
|
|
|
660
710
|
body: hasBody ? JSON.stringify(options.body) : void 0
|
|
661
711
|
});
|
|
662
712
|
if (!response.ok) {
|
|
663
|
-
const
|
|
713
|
+
const errorBody = await response.json().catch(() => {
|
|
664
714
|
return {
|
|
665
715
|
error: response.statusText
|
|
666
716
|
};
|
|
667
717
|
});
|
|
668
|
-
throw new Error(
|
|
718
|
+
throw new Error(errorBodyMessage(errorBody) ?? `HTTP ${response.status}`);
|
|
669
719
|
}
|
|
670
720
|
if (response.status === 204 || response.status === 205) return;
|
|
671
721
|
if (response.headers.get("content-type")?.includes("application/json")) return response.json();
|
|
@@ -845,4 +895,4 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
845
895
|
};
|
|
846
896
|
|
|
847
897
|
//#endregion
|
|
848
|
-
export { McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, getIdentity, registerAppResource, registerToolFromSchema, z };
|
|
898
|
+
export { McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, registerAppResource, registerToolFromSchema, z };
|