@ttoss/http-server-mcp 0.12.4 → 0.12.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/dist/index.cjs +319 -0
- package/dist/index.d.cts +749 -88
- package/dist/index.d.mts +905 -0
- package/dist/index.mjs +302 -0
- package/package.json +5 -5
- package/dist/esm/index.js +0 -205
- package/dist/index.d.ts +0 -244
- package/dist/index.js +0 -244
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,905 @@
|
|
|
1
|
+
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Router } from "@ttoss/http-server";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { McpServer, McpServer as McpServer$1 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import accepts from "accepts";
|
|
8
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
9
|
+
import Cookies from "cookies";
|
|
10
|
+
import { EventEmitter } from "events";
|
|
11
|
+
import { IncomingHttpHeaders, IncomingMessage, OutgoingHttpHeaders, Server, ServerResponse } from "http";
|
|
12
|
+
import { Http2ServerRequest, Http2ServerResponse } from "http2";
|
|
13
|
+
import httpAssert from "http-assert";
|
|
14
|
+
import contentDisposition from "content-disposition";
|
|
15
|
+
import HttpErrors from "http-errors";
|
|
16
|
+
import Keygrip from "keygrip";
|
|
17
|
+
import compose from "koa-compose";
|
|
18
|
+
import { ListenOptions, Socket } from "net";
|
|
19
|
+
import { ParsedUrlQuery } from "querystring";
|
|
20
|
+
import * as url from "url";
|
|
21
|
+
|
|
22
|
+
//#region ../../node_modules/.pnpm/@types+koa@3.0.3/node_modules/@types/koa/index.d.ts
|
|
23
|
+
declare interface ContextDelegatedRequest {
|
|
24
|
+
/**
|
|
25
|
+
* Return request header.
|
|
26
|
+
*/
|
|
27
|
+
header: IncomingHttpHeaders;
|
|
28
|
+
/**
|
|
29
|
+
* Return request header, alias as request.header
|
|
30
|
+
*/
|
|
31
|
+
headers: IncomingHttpHeaders;
|
|
32
|
+
/**
|
|
33
|
+
* Get/Set request URL.
|
|
34
|
+
*/
|
|
35
|
+
url: string;
|
|
36
|
+
/**
|
|
37
|
+
* Get origin of URL.
|
|
38
|
+
*/
|
|
39
|
+
origin: string;
|
|
40
|
+
/**
|
|
41
|
+
* Get full request URL.
|
|
42
|
+
*/
|
|
43
|
+
href: string;
|
|
44
|
+
/**
|
|
45
|
+
* Get/Set request method.
|
|
46
|
+
*/
|
|
47
|
+
method: string;
|
|
48
|
+
/**
|
|
49
|
+
* Get request pathname.
|
|
50
|
+
* Set pathname, retaining the query-string when present.
|
|
51
|
+
*/
|
|
52
|
+
path: string;
|
|
53
|
+
/**
|
|
54
|
+
* Get parsed query-string.
|
|
55
|
+
* Set query-string as an object.
|
|
56
|
+
*/
|
|
57
|
+
query: ParsedUrlQuery;
|
|
58
|
+
/**
|
|
59
|
+
* Get/Set query string.
|
|
60
|
+
*/
|
|
61
|
+
querystring: string;
|
|
62
|
+
/**
|
|
63
|
+
* Get the search string. Same as the querystring
|
|
64
|
+
* except it includes the leading ?.
|
|
65
|
+
*
|
|
66
|
+
* Set the search string. Same as
|
|
67
|
+
* response.querystring= but included for ubiquity.
|
|
68
|
+
*/
|
|
69
|
+
search: string;
|
|
70
|
+
/**
|
|
71
|
+
* Parse the "Host" header field host
|
|
72
|
+
* and support X-Forwarded-Host when a
|
|
73
|
+
* proxy is enabled.
|
|
74
|
+
*/
|
|
75
|
+
host: string;
|
|
76
|
+
/**
|
|
77
|
+
* Parse the "Host" header field hostname
|
|
78
|
+
* and support X-Forwarded-Host when a
|
|
79
|
+
* proxy is enabled.
|
|
80
|
+
*/
|
|
81
|
+
hostname: string;
|
|
82
|
+
/**
|
|
83
|
+
* Get WHATWG parsed URL object.
|
|
84
|
+
*/
|
|
85
|
+
URL: url.URL;
|
|
86
|
+
/**
|
|
87
|
+
* Check if the request is fresh, aka
|
|
88
|
+
* Last-Modified and/or the ETag
|
|
89
|
+
* still match.
|
|
90
|
+
*/
|
|
91
|
+
fresh: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Check if the request is stale, aka
|
|
94
|
+
* "Last-Modified" and / or the "ETag" for the
|
|
95
|
+
* resource has changed.
|
|
96
|
+
*/
|
|
97
|
+
stale: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Check if the request is idempotent.
|
|
100
|
+
*/
|
|
101
|
+
idempotent: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Return the request socket.
|
|
104
|
+
*/
|
|
105
|
+
socket: Socket;
|
|
106
|
+
/**
|
|
107
|
+
* Return the protocol string "http" or "https"
|
|
108
|
+
* when requested with TLS. When the proxy setting
|
|
109
|
+
* is enabled the "X-Forwarded-Proto" header
|
|
110
|
+
* field will be trusted. If you're running behind
|
|
111
|
+
* a reverse proxy that supplies https for you this
|
|
112
|
+
* may be enabled.
|
|
113
|
+
*/
|
|
114
|
+
protocol: string;
|
|
115
|
+
/**
|
|
116
|
+
* Short-hand for:
|
|
117
|
+
*
|
|
118
|
+
* this.protocol == 'https'
|
|
119
|
+
*/
|
|
120
|
+
secure: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Request remote address. Supports X-Forwarded-For when app.proxy is true.
|
|
123
|
+
*/
|
|
124
|
+
ip: string;
|
|
125
|
+
/**
|
|
126
|
+
* When `app.proxy` is `true`, parse
|
|
127
|
+
* the "X-Forwarded-For" ip address list.
|
|
128
|
+
*
|
|
129
|
+
* For example if the value were "client, proxy1, proxy2"
|
|
130
|
+
* you would receive the array `["client", "proxy1", "proxy2"]`
|
|
131
|
+
* where "proxy2" is the furthest down-stream.
|
|
132
|
+
*/
|
|
133
|
+
ips: string[];
|
|
134
|
+
/**
|
|
135
|
+
* Return subdomains as an array.
|
|
136
|
+
*
|
|
137
|
+
* Subdomains are the dot-separated parts of the host before the main domain
|
|
138
|
+
* of the app. By default, the domain of the app is assumed to be the last two
|
|
139
|
+
* parts of the host. This can be changed by setting `app.subdomainOffset`.
|
|
140
|
+
*
|
|
141
|
+
* For example, if the domain is "tobi.ferrets.example.com":
|
|
142
|
+
* If `app.subdomainOffset` is not set, this.subdomains is
|
|
143
|
+
* `["ferrets", "tobi"]`.
|
|
144
|
+
* If `app.subdomainOffset` is 3, this.subdomains is `["tobi"]`.
|
|
145
|
+
*/
|
|
146
|
+
subdomains: string[];
|
|
147
|
+
/**
|
|
148
|
+
* Check if the given `type(s)` is acceptable, returning
|
|
149
|
+
* the best match when true, otherwise `false`, in which
|
|
150
|
+
* case you should respond with 406 "Not Acceptable".
|
|
151
|
+
*
|
|
152
|
+
* The `type` value may be a single mime type string
|
|
153
|
+
* such as "application/json", the extension name
|
|
154
|
+
* such as "json" or an array `["json", "html", "text/plain"]`. When a list
|
|
155
|
+
* or array is given the _best_ match, if any is returned.
|
|
156
|
+
*
|
|
157
|
+
* Examples:
|
|
158
|
+
*
|
|
159
|
+
* // Accept: text/html
|
|
160
|
+
* this.accepts('html');
|
|
161
|
+
* // => "html"
|
|
162
|
+
*
|
|
163
|
+
* // Accept: text/*, application/json
|
|
164
|
+
* this.accepts('html');
|
|
165
|
+
* // => "html"
|
|
166
|
+
* this.accepts('text/html');
|
|
167
|
+
* // => "text/html"
|
|
168
|
+
* this.accepts('json', 'text');
|
|
169
|
+
* // => "json"
|
|
170
|
+
* this.accepts('application/json');
|
|
171
|
+
* // => "application/json"
|
|
172
|
+
*
|
|
173
|
+
* // Accept: text/*, application/json
|
|
174
|
+
* this.accepts('image/png');
|
|
175
|
+
* this.accepts('png');
|
|
176
|
+
* // => undefined
|
|
177
|
+
*
|
|
178
|
+
* // Accept: text/*;q=.5, application/json
|
|
179
|
+
* this.accepts(['html', 'json']);
|
|
180
|
+
* this.accepts('html', 'json');
|
|
181
|
+
* // => "json"
|
|
182
|
+
*/
|
|
183
|
+
accepts(): string[];
|
|
184
|
+
accepts(...types: string[]): string | false;
|
|
185
|
+
accepts(types: string[]): string | false;
|
|
186
|
+
/**
|
|
187
|
+
* Return accepted encodings or best fit based on `encodings`.
|
|
188
|
+
*
|
|
189
|
+
* Given `Accept-Encoding: gzip, deflate`
|
|
190
|
+
* an array sorted by quality is returned:
|
|
191
|
+
*
|
|
192
|
+
* ['gzip', 'deflate']
|
|
193
|
+
*/
|
|
194
|
+
acceptsEncodings(): string[];
|
|
195
|
+
acceptsEncodings(...encodings: string[]): string | false;
|
|
196
|
+
acceptsEncodings(encodings: string[]): string | false;
|
|
197
|
+
/**
|
|
198
|
+
* Return accepted charsets or best fit based on `charsets`.
|
|
199
|
+
*
|
|
200
|
+
* Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
|
|
201
|
+
* an array sorted by quality is returned:
|
|
202
|
+
*
|
|
203
|
+
* ['utf-8', 'utf-7', 'iso-8859-1']
|
|
204
|
+
*/
|
|
205
|
+
acceptsCharsets(): string[];
|
|
206
|
+
acceptsCharsets(...charsets: string[]): string | false;
|
|
207
|
+
acceptsCharsets(charsets: string[]): string | false;
|
|
208
|
+
/**
|
|
209
|
+
* Return accepted languages or best fit based on `langs`.
|
|
210
|
+
*
|
|
211
|
+
* Given `Accept-Language: en;q=0.8, es, pt`
|
|
212
|
+
* an array sorted by quality is returned:
|
|
213
|
+
*
|
|
214
|
+
* ['es', 'pt', 'en']
|
|
215
|
+
*/
|
|
216
|
+
acceptsLanguages(): string[];
|
|
217
|
+
acceptsLanguages(...langs: string[]): string | false;
|
|
218
|
+
acceptsLanguages(langs: string[]): string | false;
|
|
219
|
+
/**
|
|
220
|
+
* Check if the incoming request contains the "Content-Type"
|
|
221
|
+
* header field, and it contains any of the give mime `type`s.
|
|
222
|
+
* If there is no request body, `null` is returned.
|
|
223
|
+
* If there is no content type, `false` is returned.
|
|
224
|
+
* Otherwise, it returns the first `type` that matches.
|
|
225
|
+
*
|
|
226
|
+
* Examples:
|
|
227
|
+
*
|
|
228
|
+
* // With Content-Type: text/html; charset=utf-8
|
|
229
|
+
* this.is('html'); // => 'html'
|
|
230
|
+
* this.is('text/html'); // => 'text/html'
|
|
231
|
+
* this.is('text/*', 'application/json'); // => 'text/html'
|
|
232
|
+
*
|
|
233
|
+
* // When Content-Type is application/json
|
|
234
|
+
* this.is('json', 'urlencoded'); // => 'json'
|
|
235
|
+
* this.is('application/json'); // => 'application/json'
|
|
236
|
+
* this.is('html', 'application/*'); // => 'application/json'
|
|
237
|
+
*
|
|
238
|
+
* this.is('html'); // => false
|
|
239
|
+
*/
|
|
240
|
+
// is(): string | boolean;
|
|
241
|
+
is(...types: string[]): string | false | null;
|
|
242
|
+
is(types: string[]): string | false | null;
|
|
243
|
+
/**
|
|
244
|
+
* Return request header. If the header is not set, will return an empty
|
|
245
|
+
* string.
|
|
246
|
+
*
|
|
247
|
+
* The `Referrer` header field is special-cased, both `Referrer` and
|
|
248
|
+
* `Referer` are interchangeable.
|
|
249
|
+
*
|
|
250
|
+
* Examples:
|
|
251
|
+
*
|
|
252
|
+
* this.get('Content-Type');
|
|
253
|
+
* // => "text/plain"
|
|
254
|
+
*
|
|
255
|
+
* this.get('content-type');
|
|
256
|
+
* // => "text/plain"
|
|
257
|
+
*
|
|
258
|
+
* this.get('Something');
|
|
259
|
+
* // => ''
|
|
260
|
+
*/
|
|
261
|
+
get(field: string): string;
|
|
262
|
+
}
|
|
263
|
+
declare interface ContextDelegatedResponse {
|
|
264
|
+
/**
|
|
265
|
+
* Get/Set response status code.
|
|
266
|
+
*/
|
|
267
|
+
status: number;
|
|
268
|
+
/**
|
|
269
|
+
* Get response status message
|
|
270
|
+
*/
|
|
271
|
+
message: string;
|
|
272
|
+
/**
|
|
273
|
+
* Get/Set response body.
|
|
274
|
+
*/
|
|
275
|
+
body: unknown;
|
|
276
|
+
/**
|
|
277
|
+
* Return parsed response Content-Length when present.
|
|
278
|
+
* Set Content-Length field to `n`.
|
|
279
|
+
*/
|
|
280
|
+
length: number;
|
|
281
|
+
/**
|
|
282
|
+
* Check if a header has been written to the socket.
|
|
283
|
+
*/
|
|
284
|
+
headerSent: boolean;
|
|
285
|
+
/**
|
|
286
|
+
* Vary on `field`.
|
|
287
|
+
*/
|
|
288
|
+
vary(field: string | string[]): void;
|
|
289
|
+
/**
|
|
290
|
+
* Perform a special-cased "back" to provide Referrer support.
|
|
291
|
+
* When Referrer is not present, `alt` or "/" is used.
|
|
292
|
+
*
|
|
293
|
+
* Examples:
|
|
294
|
+
*
|
|
295
|
+
* ctx.back()
|
|
296
|
+
* ctx.back('/index.html')
|
|
297
|
+
*/
|
|
298
|
+
back(alt?: string): void;
|
|
299
|
+
/**
|
|
300
|
+
* Perform a 302 redirect to `url`.
|
|
301
|
+
*
|
|
302
|
+
* The string "back" is special-cased
|
|
303
|
+
* to provide Referrer support, when Referrer
|
|
304
|
+
* is not present `alt` or "/" is used.
|
|
305
|
+
*
|
|
306
|
+
* Examples:
|
|
307
|
+
*
|
|
308
|
+
* this.redirect('/login');
|
|
309
|
+
* this.redirect('http://google.com');
|
|
310
|
+
*/
|
|
311
|
+
redirect(url: string): void;
|
|
312
|
+
/**
|
|
313
|
+
* Set Content-Disposition to "attachment" to signal the client to prompt for download.
|
|
314
|
+
* Optionally specify the filename of the download and some options.
|
|
315
|
+
*/
|
|
316
|
+
attachment(filename?: string, options?: contentDisposition.Options): void;
|
|
317
|
+
/**
|
|
318
|
+
* Return the response mime type void of
|
|
319
|
+
* parameters such as "charset".
|
|
320
|
+
*
|
|
321
|
+
* Set Content-Type response header with `type` through `mime.lookup()`
|
|
322
|
+
* when it does not contain a charset.
|
|
323
|
+
*
|
|
324
|
+
* Examples:
|
|
325
|
+
*
|
|
326
|
+
* this.type = '.html';
|
|
327
|
+
* this.type = 'html';
|
|
328
|
+
* this.type = 'json';
|
|
329
|
+
* this.type = 'application/json';
|
|
330
|
+
* this.type = 'png';
|
|
331
|
+
*/
|
|
332
|
+
type: string;
|
|
333
|
+
/**
|
|
334
|
+
* Get the Last-Modified date in Date form, if it exists.
|
|
335
|
+
* Set the Last-Modified date using a string or a Date.
|
|
336
|
+
*
|
|
337
|
+
* this.response.lastModified = new Date();
|
|
338
|
+
* this.response.lastModified = '2013-09-13';
|
|
339
|
+
*/
|
|
340
|
+
lastModified: Date;
|
|
341
|
+
/**
|
|
342
|
+
* Get/Set the ETag of a response.
|
|
343
|
+
* This will normalize the quotes if necessary.
|
|
344
|
+
*
|
|
345
|
+
* this.response.etag = 'md5hashsum';
|
|
346
|
+
* this.response.etag = '"md5hashsum"';
|
|
347
|
+
* this.response.etag = 'W/"123456789"';
|
|
348
|
+
*
|
|
349
|
+
* @param {String} etag
|
|
350
|
+
* @api public
|
|
351
|
+
*/
|
|
352
|
+
etag: string;
|
|
353
|
+
/**
|
|
354
|
+
* Set header `field` to `val`, or pass
|
|
355
|
+
* an object of header fields.
|
|
356
|
+
*
|
|
357
|
+
* Examples:
|
|
358
|
+
*
|
|
359
|
+
* this.set('Foo', ['bar', 'baz']);
|
|
360
|
+
* this.set('Accept', 'application/json');
|
|
361
|
+
* this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
|
362
|
+
*/
|
|
363
|
+
set(field: {
|
|
364
|
+
[key: string]: string | string[];
|
|
365
|
+
}): void;
|
|
366
|
+
set(field: string, val: string | string[]): void;
|
|
367
|
+
/**
|
|
368
|
+
* Append additional header `field` with value `val`.
|
|
369
|
+
*
|
|
370
|
+
* Examples:
|
|
371
|
+
*
|
|
372
|
+
* ```
|
|
373
|
+
* this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
|
|
374
|
+
* this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
|
|
375
|
+
* this.append('Warning', '199 Miscellaneous warning');
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
append(field: string, val: string | string[]): void;
|
|
379
|
+
/**
|
|
380
|
+
* Remove header `field`.
|
|
381
|
+
*/
|
|
382
|
+
remove(field: string): void;
|
|
383
|
+
/**
|
|
384
|
+
* Checks if the request is writable.
|
|
385
|
+
* Tests for the existence of the socket
|
|
386
|
+
* as node sometimes does not set it.
|
|
387
|
+
*/
|
|
388
|
+
writable: boolean;
|
|
389
|
+
/**
|
|
390
|
+
* Flush any set headers, and begin the body
|
|
391
|
+
*/
|
|
392
|
+
flushHeaders(): void;
|
|
393
|
+
}
|
|
394
|
+
declare class Application<StateT = Application.DefaultState, ContextT = Application.DefaultContext> extends EventEmitter {
|
|
395
|
+
proxy: boolean;
|
|
396
|
+
proxyIpHeader: string;
|
|
397
|
+
maxIpsCount: number;
|
|
398
|
+
middleware: Array<Application.Middleware<StateT, ContextT>>;
|
|
399
|
+
subdomainOffset: number;
|
|
400
|
+
env: string;
|
|
401
|
+
context: Application.BaseContext & ContextT;
|
|
402
|
+
request: Application.BaseRequest;
|
|
403
|
+
response: Application.BaseResponse;
|
|
404
|
+
silent: boolean;
|
|
405
|
+
keys: Keygrip | string[];
|
|
406
|
+
ctxStorage: AsyncLocalStorage<ContextT> | undefined;
|
|
407
|
+
/**
|
|
408
|
+
* @param {object} [options] Application options
|
|
409
|
+
* @param {string} [options.env='development'] Environment
|
|
410
|
+
* @param {string[]} [options.keys] Signed cookie keys
|
|
411
|
+
* @param {boolean} [options.proxy] Trust proxy headers
|
|
412
|
+
* @param {number} [options.subdomainOffset] Subdomain offset
|
|
413
|
+
* @param {string} [options.proxyIpHeader] Proxy IP header, defaults to X-Forwarded-For
|
|
414
|
+
* @param {number} [options.maxIpsCount] Max IPs read from proxy IP header, default to 0 (means infinity)
|
|
415
|
+
* @param {boolean|AsyncLocalStorage} [options.asyncLocalStorage] Pass `true` or an instance of `AsyncLocalStorage` to enable async local storage
|
|
416
|
+
*/
|
|
417
|
+
constructor(options?: {
|
|
418
|
+
env?: string | undefined;
|
|
419
|
+
keys?: string[] | undefined;
|
|
420
|
+
proxy?: boolean | undefined;
|
|
421
|
+
subdomainOffset?: number | undefined;
|
|
422
|
+
proxyIpHeader?: string | undefined;
|
|
423
|
+
maxIpsCount?: number | undefined;
|
|
424
|
+
asyncLocalStorage?: boolean | AsyncLocalStorage<ContextT> | undefined;
|
|
425
|
+
});
|
|
426
|
+
/**
|
|
427
|
+
* Shorthand for:
|
|
428
|
+
*
|
|
429
|
+
* http.createServer(app.callback()).listen(...)
|
|
430
|
+
*/
|
|
431
|
+
listen(port?: number, hostname?: string, backlog?: number, listeningListener?: () => void): Server;
|
|
432
|
+
listen(port: number, hostname?: string, listeningListener?: () => void): Server;
|
|
433
|
+
listen(port: number, backlog?: number, listeningListener?: () => void): Server;
|
|
434
|
+
listen(port: number, listeningListener?: () => void): Server;
|
|
435
|
+
listen(path: string, backlog?: number, listeningListener?: () => void): Server;
|
|
436
|
+
listen(path: string, listeningListener?: () => void): Server;
|
|
437
|
+
listen(options: ListenOptions, listeningListener?: () => void): Server;
|
|
438
|
+
listen(handle: any, backlog?: number, listeningListener?: () => void): Server;
|
|
439
|
+
listen(handle: any, listeningListener?: () => void): Server;
|
|
440
|
+
/**
|
|
441
|
+
* Return JSON representation.
|
|
442
|
+
* We only bother showing settings.
|
|
443
|
+
*/
|
|
444
|
+
inspect(): any;
|
|
445
|
+
/**
|
|
446
|
+
* Return JSON representation.
|
|
447
|
+
* We only bother showing settings.
|
|
448
|
+
*/
|
|
449
|
+
toJSON(): any;
|
|
450
|
+
/**
|
|
451
|
+
* Use the given middleware `fn`.
|
|
452
|
+
*
|
|
453
|
+
* Old-style middleware will be converted.
|
|
454
|
+
*/
|
|
455
|
+
use<NewStateT = {}, NewContextT = {}>(middleware: Application.Middleware<StateT & NewStateT, ContextT & NewContextT>): Application<StateT & NewStateT, ContextT & NewContextT>;
|
|
456
|
+
/**
|
|
457
|
+
* Return a request handler callback
|
|
458
|
+
* for node's native http/http2 server.
|
|
459
|
+
*/
|
|
460
|
+
callback(): (req: IncomingMessage | Http2ServerRequest, res: ServerResponse | Http2ServerResponse) => Promise<void>;
|
|
461
|
+
/**
|
|
462
|
+
* Initialize a new context.
|
|
463
|
+
*
|
|
464
|
+
* @api private
|
|
465
|
+
*/
|
|
466
|
+
createContext<StateT = Application.DefaultState>(req: IncomingMessage, res: ServerResponse): Application.ParameterizedContext<StateT>;
|
|
467
|
+
/**
|
|
468
|
+
* Default error handler.
|
|
469
|
+
*
|
|
470
|
+
* @api private
|
|
471
|
+
*/
|
|
472
|
+
onerror(err: Error): void;
|
|
473
|
+
/**
|
|
474
|
+
* return current context from async local storage
|
|
475
|
+
*/
|
|
476
|
+
readonly currentContext: ContextT | undefined;
|
|
477
|
+
}
|
|
478
|
+
declare namespace Application {
|
|
479
|
+
interface DefaultContextDelegatedRequest extends ContextDelegatedRequest {}
|
|
480
|
+
interface DefaultContextDelegatedResponse extends ContextDelegatedResponse {}
|
|
481
|
+
type DefaultStateExtends = any;
|
|
482
|
+
/**
|
|
483
|
+
* This interface can be augmented by users to add types to Koa's default state
|
|
484
|
+
*/
|
|
485
|
+
interface DefaultState extends DefaultStateExtends {}
|
|
486
|
+
type DefaultContextExtends = {};
|
|
487
|
+
/**
|
|
488
|
+
* This interface can be augmented by users to add types to Koa's default context
|
|
489
|
+
*/
|
|
490
|
+
interface DefaultContext extends DefaultContextExtends {
|
|
491
|
+
/**
|
|
492
|
+
* Custom properties.
|
|
493
|
+
*/
|
|
494
|
+
[key: PropertyKey]: any;
|
|
495
|
+
}
|
|
496
|
+
type Middleware<StateT = DefaultState, ContextT = DefaultContext, ResponseBodyT = any> = compose.Middleware<ParameterizedContext<StateT, ContextT, ResponseBodyT>>;
|
|
497
|
+
interface BaseRequest extends DefaultContextDelegatedRequest {
|
|
498
|
+
/**
|
|
499
|
+
* Get the charset when present or undefined.
|
|
500
|
+
*/
|
|
501
|
+
charset: string;
|
|
502
|
+
/**
|
|
503
|
+
* Return parsed Content-Length when present.
|
|
504
|
+
*/
|
|
505
|
+
length: number;
|
|
506
|
+
/**
|
|
507
|
+
* Return the request mime type void of
|
|
508
|
+
* parameters such as "charset".
|
|
509
|
+
*/
|
|
510
|
+
type: string;
|
|
511
|
+
/**
|
|
512
|
+
* Inspect implementation.
|
|
513
|
+
*/
|
|
514
|
+
inspect(): any;
|
|
515
|
+
/**
|
|
516
|
+
* Return JSON representation.
|
|
517
|
+
*/
|
|
518
|
+
toJSON(): any;
|
|
519
|
+
}
|
|
520
|
+
interface BaseResponse extends DefaultContextDelegatedResponse {
|
|
521
|
+
/**
|
|
522
|
+
* Return the request socket.
|
|
523
|
+
*
|
|
524
|
+
* @return {Connection}
|
|
525
|
+
* @api public
|
|
526
|
+
*/
|
|
527
|
+
socket: Socket;
|
|
528
|
+
/**
|
|
529
|
+
* Return response header.
|
|
530
|
+
*/
|
|
531
|
+
header: OutgoingHttpHeaders;
|
|
532
|
+
/**
|
|
533
|
+
* Return response header, alias as response.header
|
|
534
|
+
*/
|
|
535
|
+
headers: OutgoingHttpHeaders;
|
|
536
|
+
/**
|
|
537
|
+
* Check whether the response is one of the listed types.
|
|
538
|
+
* Pretty much the same as `this.request.is()`.
|
|
539
|
+
*
|
|
540
|
+
* @param {String|Array} types...
|
|
541
|
+
* @return {String|false}
|
|
542
|
+
* @api public
|
|
543
|
+
*/
|
|
544
|
+
// is(): string;
|
|
545
|
+
is(...types: string[]): string | false | null;
|
|
546
|
+
is(types: string[]): string | false | null;
|
|
547
|
+
/**
|
|
548
|
+
* Return response header. If the header is not set, will return an empty
|
|
549
|
+
* string.
|
|
550
|
+
*
|
|
551
|
+
* The `Referrer` header field is special-cased, both `Referrer` and
|
|
552
|
+
* `Referer` are interchangeable.
|
|
553
|
+
*
|
|
554
|
+
* Examples:
|
|
555
|
+
*
|
|
556
|
+
* this.get('Content-Type');
|
|
557
|
+
* // => "text/plain"
|
|
558
|
+
*
|
|
559
|
+
* this.get('content-type');
|
|
560
|
+
* // => "text/plain"
|
|
561
|
+
*
|
|
562
|
+
* this.get('Something');
|
|
563
|
+
* // => ''
|
|
564
|
+
*/
|
|
565
|
+
get(field: string): string;
|
|
566
|
+
/**
|
|
567
|
+
* Inspect implementation.
|
|
568
|
+
*/
|
|
569
|
+
inspect(): any;
|
|
570
|
+
/**
|
|
571
|
+
* Return JSON representation.
|
|
572
|
+
*/
|
|
573
|
+
toJSON(): any;
|
|
574
|
+
}
|
|
575
|
+
interface BaseContext extends DefaultContextDelegatedRequest, DefaultContextDelegatedResponse {
|
|
576
|
+
/**
|
|
577
|
+
* util.inspect() implementation, which
|
|
578
|
+
* just returns the JSON output.
|
|
579
|
+
*/
|
|
580
|
+
inspect(): any;
|
|
581
|
+
/**
|
|
582
|
+
* Return JSON representation.
|
|
583
|
+
*
|
|
584
|
+
* Here we explicitly invoke .toJSON() on each
|
|
585
|
+
* object, as iteration will otherwise fail due
|
|
586
|
+
* to the getters and cause utilities such as
|
|
587
|
+
* clone() to fail.
|
|
588
|
+
*/
|
|
589
|
+
toJSON(): any;
|
|
590
|
+
/**
|
|
591
|
+
* Similar to .throw(), adds assertion.
|
|
592
|
+
*
|
|
593
|
+
* this.assert(this.user, 401, 'Please login!');
|
|
594
|
+
*
|
|
595
|
+
* See: https://github.com/jshttp/http-assert
|
|
596
|
+
*/
|
|
597
|
+
assert: typeof httpAssert;
|
|
598
|
+
/**
|
|
599
|
+
* Throw an error with `msg` and optional `status`
|
|
600
|
+
* defaulting to 500. Note that these are user-level
|
|
601
|
+
* errors, and the message may be exposed to the client.
|
|
602
|
+
*
|
|
603
|
+
* this.throw(403)
|
|
604
|
+
* this.throw('name required', 400)
|
|
605
|
+
* this.throw(400, 'name required')
|
|
606
|
+
* this.throw('something exploded')
|
|
607
|
+
* this.throw(new Error('invalid'), 400);
|
|
608
|
+
* this.throw(400, new Error('invalid'));
|
|
609
|
+
*
|
|
610
|
+
* See: https://github.com/jshttp/http-errors
|
|
611
|
+
*/
|
|
612
|
+
throw(status: number, ...args: HttpErrors.UnknownError[]): never;
|
|
613
|
+
throw(...args: HttpErrors.UnknownError[]): never;
|
|
614
|
+
/**
|
|
615
|
+
* Default error handling.
|
|
616
|
+
*/
|
|
617
|
+
onerror(err: Error): void;
|
|
618
|
+
}
|
|
619
|
+
interface Request extends BaseRequest {
|
|
620
|
+
app: Application;
|
|
621
|
+
req: IncomingMessage;
|
|
622
|
+
res: ServerResponse;
|
|
623
|
+
ctx: Context;
|
|
624
|
+
response: Response;
|
|
625
|
+
originalUrl: string;
|
|
626
|
+
ip: string;
|
|
627
|
+
accept: accepts.Accepts;
|
|
628
|
+
}
|
|
629
|
+
interface Response extends BaseResponse {
|
|
630
|
+
app: Application;
|
|
631
|
+
req: IncomingMessage;
|
|
632
|
+
res: ServerResponse;
|
|
633
|
+
ctx: Context;
|
|
634
|
+
request: Request;
|
|
635
|
+
}
|
|
636
|
+
interface ExtendableContext extends BaseContext {
|
|
637
|
+
app: Application;
|
|
638
|
+
request: Request;
|
|
639
|
+
response: Response;
|
|
640
|
+
req: IncomingMessage;
|
|
641
|
+
res: ServerResponse;
|
|
642
|
+
originalUrl: string;
|
|
643
|
+
cookies: Cookies;
|
|
644
|
+
accept: accepts.Accepts;
|
|
645
|
+
/**
|
|
646
|
+
* To bypass Koa's built-in response handling, you may explicitly set `ctx.respond = false;`
|
|
647
|
+
*/
|
|
648
|
+
respond?: boolean | undefined;
|
|
649
|
+
}
|
|
650
|
+
type ParameterizedContext<StateT = DefaultState, ContextT = DefaultContext, ResponseBodyT = unknown> = ExtendableContext & {
|
|
651
|
+
state: StateT;
|
|
652
|
+
} & ContextT & {
|
|
653
|
+
body: ResponseBodyT;
|
|
654
|
+
response: {
|
|
655
|
+
body: ResponseBodyT;
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
interface Context extends ParameterizedContext {}
|
|
659
|
+
type Next = () => Promise<any>;
|
|
660
|
+
/**
|
|
661
|
+
* A re-export of `HttpError` from the `http-error` package.
|
|
662
|
+
*
|
|
663
|
+
* This is the error type that is thrown by `ctx.assert()` and `ctx.throw()`.
|
|
664
|
+
*/
|
|
665
|
+
const HttpError: typeof HttpErrors.HttpError;
|
|
666
|
+
}
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/index.d.ts
|
|
669
|
+
type Context$1 = Application.Context;
|
|
670
|
+
/**
|
|
671
|
+
* Options for a single `apiCall` request.
|
|
672
|
+
*/
|
|
673
|
+
interface ApiCallOptions {
|
|
674
|
+
/**
|
|
675
|
+
* JSON-serialisable request body. Automatically serialised and sent with
|
|
676
|
+
* `Content-Type: application/json`.
|
|
677
|
+
*/
|
|
678
|
+
body?: unknown;
|
|
679
|
+
/**
|
|
680
|
+
* Additional or override headers for this specific request.
|
|
681
|
+
* These are merged on top of any headers injected from the MCP request
|
|
682
|
+
* context via `getApiHeaders`, allowing per-call overrides.
|
|
683
|
+
*/
|
|
684
|
+
headers?: Record<string, string>;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Generic HTTP helper for use inside MCP tool handlers.
|
|
688
|
+
*
|
|
689
|
+
* Accepts any full URL (third-party APIs, public APIs, etc.) or a path
|
|
690
|
+
* relative to the `apiBaseUrl` configured in `createMcpRouter`.
|
|
691
|
+
*
|
|
692
|
+
* Headers configured via `getApiHeaders` in `createMcpRouter` are injected
|
|
693
|
+
* automatically into every request, allowing transparent forwarding of auth
|
|
694
|
+
* tokens, API keys, or any other header — without coupling this helper to a
|
|
695
|
+
* specific authentication scheme. Per-call `options.headers` take precedence
|
|
696
|
+
* over context-injected headers.
|
|
697
|
+
*
|
|
698
|
+
* @param method - HTTP method (e.g. `'GET'`, `'POST'`, `'PUT'`, `'DELETE'`)
|
|
699
|
+
* @param url - Full URL **or** a path starting with `/` (appended to `apiBaseUrl`)
|
|
700
|
+
* @param options - Optional body and per-call header overrides
|
|
701
|
+
* @returns Parsed JSON response body
|
|
702
|
+
*
|
|
703
|
+
* @example Bearer token forwarding (configured once in `createMcpRouter`)
|
|
704
|
+
* ```typescript
|
|
705
|
+
* import { apiCall, createMcpRouter, McpServer } from '@ttoss/http-server-mcp';
|
|
706
|
+
*
|
|
707
|
+
* // Tool handler – no manual auth wiring needed
|
|
708
|
+
* mcpServer.registerTool('list-portfolios', { description: '...', inputSchema: {} }, async () => {
|
|
709
|
+
* const data = await apiCall('GET', '/portfolios');
|
|
710
|
+
* return { content: [{ type: 'text', text: JSON.stringify(data) }] };
|
|
711
|
+
* });
|
|
712
|
+
*
|
|
713
|
+
* const mcpRouter = createMcpRouter(mcpServer, {
|
|
714
|
+
* apiBaseUrl: `http://localhost:${process.env.PORT}/api/v1`,
|
|
715
|
+
* // Forward the caller's Bearer token to every apiCall
|
|
716
|
+
* getApiHeaders: (ctx) => ({ Authorization: ctx.headers.authorization ?? '' }),
|
|
717
|
+
* });
|
|
718
|
+
* ```
|
|
719
|
+
*
|
|
720
|
+
* @example x-api-key forwarding
|
|
721
|
+
* ```typescript
|
|
722
|
+
* const mcpRouter = createMcpRouter(mcpServer, {
|
|
723
|
+
* apiBaseUrl: 'https://internal-service/api',
|
|
724
|
+
* getApiHeaders: (ctx) => ({
|
|
725
|
+
* 'x-api-key': ctx.headers['x-api-key'] as string,
|
|
726
|
+
* }),
|
|
727
|
+
* });
|
|
728
|
+
* ```
|
|
729
|
+
*
|
|
730
|
+
* @example Third-party or public API (full URL, no context required)
|
|
731
|
+
* ```typescript
|
|
732
|
+
* const weather = await apiCall('GET', 'https://api.weather.com/current?city=Berlin');
|
|
733
|
+
* const created = await apiCall('POST', 'https://api.example.com/items', {
|
|
734
|
+
* body: { name: 'widget' },
|
|
735
|
+
* headers: { Authorization: 'Bearer fixed-service-token' },
|
|
736
|
+
* });
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
declare const apiCall: (method: string, url: string, options?: ApiCallOptions) => Promise<unknown>;
|
|
740
|
+
/**
|
|
741
|
+
* Options for configuring the MCP router
|
|
742
|
+
*/
|
|
743
|
+
interface McpRouterOptions {
|
|
744
|
+
/**
|
|
745
|
+
* The HTTP path where the MCP server will be mounted
|
|
746
|
+
* @default '/mcp'
|
|
747
|
+
*/
|
|
748
|
+
path?: string;
|
|
749
|
+
/**
|
|
750
|
+
* Optional session ID generator for stateful MCP servers.
|
|
751
|
+
* When provided, a single shared transport is created and sessions are tracked.
|
|
752
|
+
* When undefined (default), the server operates in stateless mode where each
|
|
753
|
+
* HTTP request uses its own transport instance.
|
|
754
|
+
*/
|
|
755
|
+
sessionIdGenerator?: () => string;
|
|
756
|
+
/**
|
|
757
|
+
* Base URL prepended to relative paths passed to `apiCall` (paths starting
|
|
758
|
+
* with `/`). Tool handlers can then call `apiCall('GET', '/resource')` without
|
|
759
|
+
* specifying a host.
|
|
760
|
+
*
|
|
761
|
+
* @example 'http://localhost:3000/api/v1'
|
|
762
|
+
*/
|
|
763
|
+
apiBaseUrl?: string;
|
|
764
|
+
/**
|
|
765
|
+
* Called once per incoming MCP HTTP request. Return a plain object whose
|
|
766
|
+
* key-value pairs will be merged into the headers of every `apiCall` made
|
|
767
|
+
* within that request's tool handlers.
|
|
768
|
+
*
|
|
769
|
+
* Use this to forward any header from the MCP request — Bearer tokens, API
|
|
770
|
+
* keys, tenant IDs, trace headers, etc. — without coupling tool handlers to
|
|
771
|
+
* a specific authentication scheme.
|
|
772
|
+
*
|
|
773
|
+
* @example Forward a Bearer token
|
|
774
|
+
* ```typescript
|
|
775
|
+
* getApiHeaders: (ctx) => ({ Authorization: ctx.headers.authorization ?? '' })
|
|
776
|
+
* ```
|
|
777
|
+
*
|
|
778
|
+
* @example Forward an x-api-key header
|
|
779
|
+
* ```typescript
|
|
780
|
+
* getApiHeaders: (ctx) => ({ 'x-api-key': ctx.headers['x-api-key'] as string })
|
|
781
|
+
* ```
|
|
782
|
+
*
|
|
783
|
+
* @example Inject a static service-to-service key
|
|
784
|
+
* ```typescript
|
|
785
|
+
* getApiHeaders: () => ({ 'x-internal-key': process.env.INTERNAL_API_KEY! })
|
|
786
|
+
* ```
|
|
787
|
+
*/
|
|
788
|
+
getApiHeaders?: (ctx: Context$1) => Record<string, string>;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Creates a Koa router configured to handle MCP protocol requests
|
|
792
|
+
*
|
|
793
|
+
* @param server - The MCP server instance with registered tools and resources
|
|
794
|
+
* @param options - Configuration options for the router
|
|
795
|
+
* @returns A Koa Router instance configured for MCP
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* ```typescript
|
|
799
|
+
* import { App, bodyParser } from '@ttoss/http-server';
|
|
800
|
+
* import { createMcpRouter, McpServer, z } from '@ttoss/http-server-mcp';
|
|
801
|
+
*
|
|
802
|
+
* const mcpServer = new McpServer({
|
|
803
|
+
* name: 'my-server',
|
|
804
|
+
* version: '1.0.0',
|
|
805
|
+
* });
|
|
806
|
+
*
|
|
807
|
+
* mcpServer.registerTool(
|
|
808
|
+
* 'hello',
|
|
809
|
+
* {
|
|
810
|
+
* description: 'Say hello',
|
|
811
|
+
* inputSchema: { name: z.string() },
|
|
812
|
+
* },
|
|
813
|
+
* async ({ name }) => ({
|
|
814
|
+
* content: [{ type: 'text', text: `Hello, ${name}!` }],
|
|
815
|
+
* })
|
|
816
|
+
* );
|
|
817
|
+
*
|
|
818
|
+
* const app = new App();
|
|
819
|
+
* app.use(bodyParser());
|
|
820
|
+
*
|
|
821
|
+
* const mcpRouter = createMcpRouter(mcpServer);
|
|
822
|
+
* app.use(mcpRouter.routes());
|
|
823
|
+
*
|
|
824
|
+
* app.listen(3000);
|
|
825
|
+
* ```
|
|
826
|
+
*/
|
|
827
|
+
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => Router<Application.DefaultState, Application.DefaultContext>;
|
|
828
|
+
/**
|
|
829
|
+
* A plain JSON Schema object (draft-07 compatible) describing the shape of a
|
|
830
|
+
* tool's input. Used with {@link registerToolFromSchema} as an alternative to
|
|
831
|
+
* providing a Zod shape, enabling lossless round-trips for schemas that contain
|
|
832
|
+
* features not expressible in Zod v3 (`anyOf`, `$ref`, `pattern`, `allOf`, …).
|
|
833
|
+
*/
|
|
834
|
+
interface JsonObjectSchema {
|
|
835
|
+
type: 'object';
|
|
836
|
+
properties?: Record<string, unknown>;
|
|
837
|
+
required?: string[];
|
|
838
|
+
[key: string]: unknown;
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Parameters accepted by {@link registerToolFromSchema}.
|
|
842
|
+
*/
|
|
843
|
+
interface RegisterToolFromSchemaParams {
|
|
844
|
+
/** Unique tool name. */
|
|
845
|
+
name: string;
|
|
846
|
+
/** Human-readable description shown to the AI client. */
|
|
847
|
+
description?: string;
|
|
848
|
+
/**
|
|
849
|
+
* Plain JSON Schema that describes the tool's input object.
|
|
850
|
+
* This schema is forwarded verbatim over the MCP wire protocol, so any
|
|
851
|
+
* JSON Schema feature (`anyOf`, `$ref`, `pattern`, …) is preserved without
|
|
852
|
+
* loss. Defaults to `{ type: 'object', properties: {} }` when omitted.
|
|
853
|
+
*/
|
|
854
|
+
inputSchema?: JsonObjectSchema;
|
|
855
|
+
/**
|
|
856
|
+
* Tool handler invoked when the AI client calls the tool.
|
|
857
|
+
* Receives the raw request arguments as a plain object (no Zod validation is
|
|
858
|
+
* applied, so the shape matches whatever the client sends).
|
|
859
|
+
*/
|
|
860
|
+
handler: (args: Record<string, unknown>) => CallToolResult | Promise<CallToolResult>;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Registers a tool on an MCP server using a **plain JSON Schema** object for
|
|
864
|
+
* `inputSchema` instead of a Zod shape.
|
|
865
|
+
*
|
|
866
|
+
* This is useful when tool definitions are shared between the MCP server and an
|
|
867
|
+
* AI SDK agent (e.g. Vercel AI SDK's `tool()` helper), because both consume a
|
|
868
|
+
* plain JSON Schema at runtime. Using this helper eliminates the lossy
|
|
869
|
+
* JSON-Schema→Zod conversion that would otherwise be required.
|
|
870
|
+
*
|
|
871
|
+
* Internally the helper:
|
|
872
|
+
* 1. Registers the tool via the standard `McpServer.registerTool` API using a
|
|
873
|
+
* Zod `z.record(z.unknown())` pass-through schema so that the existing MCP
|
|
874
|
+
* `tools/call` handler correctly routes requests and delivers raw args to
|
|
875
|
+
* your handler.
|
|
876
|
+
* 2. Stores the original JSON Schema and patches the `tools/list` response
|
|
877
|
+
* handler so clients receive the verbatim JSON Schema over the wire.
|
|
878
|
+
*
|
|
879
|
+
* @param server - The `McpServer` instance to register the tool on.
|
|
880
|
+
* @param params - Tool configuration including name, description, inputSchema,
|
|
881
|
+
* and handler.
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* ```typescript
|
|
885
|
+
* import { registerToolFromSchema, McpServer } from '@ttoss/http-server-mcp';
|
|
886
|
+
*
|
|
887
|
+
* const server = new McpServer({ name: 'my-server', version: '1.0.0' });
|
|
888
|
+
*
|
|
889
|
+
* registerToolFromSchema(server, {
|
|
890
|
+
* name: 'get-project',
|
|
891
|
+
* description: 'Get a project by ID',
|
|
892
|
+
* inputSchema: {
|
|
893
|
+
* type: 'object',
|
|
894
|
+
* properties: { id: { type: 'string', description: 'Project public ID' } },
|
|
895
|
+
* required: ['id'],
|
|
896
|
+
* },
|
|
897
|
+
* handler: async ({ id }) => ({
|
|
898
|
+
* content: [{ type: 'text', text: `Project: ${id}` }],
|
|
899
|
+
* }),
|
|
900
|
+
* });
|
|
901
|
+
* ```
|
|
902
|
+
*/
|
|
903
|
+
declare const registerToolFromSchema: (server: McpServer$1, params: RegisterToolFromSchemaParams) => void;
|
|
904
|
+
//#endregion
|
|
905
|
+
export { ApiCallOptions, JsonObjectSchema, McpRouterOptions, type McpServer, RegisterToolFromSchemaParams, apiCall, createMcpRouter, registerToolFromSchema, z };
|