@haikit/mcp 0.0.0-stage → 0.33.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/LICENSE +21 -0
- package/README.md +83 -2
- package/dist/auth.d.ts +80 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +119 -0
- package/dist/auth.js.map +1 -0
- package/dist/errors.d.ts +76 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +57 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +722 -0
- package/dist/index.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +51 -0
- package/dist/text.js.map +1 -0
- package/dist/transports.d.ts +30 -0
- package/dist/transports.d.ts.map +1 -0
- package/dist/transports.js +36 -0
- package/dist/transports.js.map +1 -0
- package/package.json +52 -4
- package/src/auth.ts +160 -0
- package/src/errors.ts +95 -0
- package/src/index.ts +869 -0
- package/src/text.ts +60 -0
- package/src/transports.ts +56 -0
package/src/text.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ContentBlock } from "@modelcontextprotocol/client";
|
|
2
|
+
import type { McpResult } from "./index.js";
|
|
3
|
+
|
|
4
|
+
export interface TextOptions {
|
|
5
|
+
/** The longest text returned, before the line saying it was cut. Default 8,000 (about 2,000 tokens). */
|
|
6
|
+
maxChars?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const MAX_CHARS = 8_000;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A result as text, for a tool that decides to pass some of it on. Nothing
|
|
13
|
+
* calls this for you: what a result becomes is the tool's choice.
|
|
14
|
+
*
|
|
15
|
+
* Text blocks, and resources embedded as text, come as they are; anything
|
|
16
|
+
* else becomes one line saying what it was. With no content, the structured
|
|
17
|
+
* result as JSON. Longer than `maxChars`, it is cut, and a last line says so:
|
|
18
|
+
* a cut nobody mentions turns a cost problem into a correctness problem.
|
|
19
|
+
*/
|
|
20
|
+
export function textOf(result: McpResult, options: TextOptions = {}): string {
|
|
21
|
+
const maxChars = options.maxChars ?? MAX_CHARS;
|
|
22
|
+
if (!Number.isSafeInteger(maxChars) || maxChars < 0) {
|
|
23
|
+
throw new RangeError(`maxChars must be a whole number of characters, 0 or more (got ${String(maxChars)})`);
|
|
24
|
+
}
|
|
25
|
+
const text =
|
|
26
|
+
result.content.length === 0
|
|
27
|
+
? result.structuredContent === undefined
|
|
28
|
+
? ""
|
|
29
|
+
: JSON.stringify(result.structuredContent)
|
|
30
|
+
: result.content.map(blockText).join("\n");
|
|
31
|
+
if (text.length <= maxChars) return text;
|
|
32
|
+
return `${text.slice(0, maxChars)}\n[Cut at ${maxChars} of ${text.length} characters.]`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function blockText(block: ContentBlock): string {
|
|
36
|
+
switch (block.type) {
|
|
37
|
+
case "text":
|
|
38
|
+
return block.text;
|
|
39
|
+
case "image":
|
|
40
|
+
case "audio":
|
|
41
|
+
return `[${block.type}: ${block.mimeType}, ${bytes(block.data)} bytes, not included]`;
|
|
42
|
+
case "resource_link":
|
|
43
|
+
return `[resource link: ${block.name} ${block.uri}]`;
|
|
44
|
+
case "resource": {
|
|
45
|
+
const r = block.resource;
|
|
46
|
+
if ("text" in r && typeof r.text === "string") return r.text;
|
|
47
|
+
const blob = "blob" in r && typeof r.blob === "string" ? r.blob : "";
|
|
48
|
+
return `[resource: ${r.uri}${r.mimeType ? `, ${r.mimeType}` : ""}, ${bytes(blob)} bytes, not included]`;
|
|
49
|
+
}
|
|
50
|
+
default:
|
|
51
|
+
// a content type from a later protocol revision
|
|
52
|
+
return `[${String((block as { type?: unknown }).type)} content, not included]`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The size of what base64 `data` encodes, without decoding it. */
|
|
57
|
+
function bytes(data: string): number {
|
|
58
|
+
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
59
|
+
return Math.max(0, Math.floor((data.length * 3) / 4) - padding);
|
|
60
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StreamableHTTPClientTransport,
|
|
3
|
+
type AuthProvider,
|
|
4
|
+
type StreamableHTTPClientTransportOptions,
|
|
5
|
+
type Transport,
|
|
6
|
+
} from "@modelcontextprotocol/client";
|
|
7
|
+
import { StdioClientTransport, type StdioServerParameters } from "@modelcontextprotocol/client/stdio";
|
|
8
|
+
|
|
9
|
+
export interface HttpOptions extends StreamableHTTPClientTransportOptions {
|
|
10
|
+
/** Sent on every request, over any of the same name in `requestInit.headers`. */
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** What `mcpServer({ auth })` needs to know of a transport `http()` made. */
|
|
15
|
+
export interface HttpMade {
|
|
16
|
+
/** It was given an `authProvider` of its own. */
|
|
17
|
+
authProvider: boolean;
|
|
18
|
+
/** It sends an `Authorization` header of its own. */
|
|
19
|
+
authorization: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The factories `http()` made: only these can carry each user's credential. */
|
|
23
|
+
export const madeByHttp = new WeakMap<object, HttpMade>();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A server at `url`, over Streamable HTTP. Everything but `headers` is the
|
|
27
|
+
* SDK's own `StreamableHTTPClientTransportOptions`, such as an `authProvider`.
|
|
28
|
+
* The URL is checked now, so a bad one fails at startup. For a server with
|
|
29
|
+
* `auth`, each user's connection is given that user's `authProvider`.
|
|
30
|
+
*/
|
|
31
|
+
export function http(url: string | URL, options: HttpOptions = {}): (authProvider?: AuthProvider) => Transport {
|
|
32
|
+
const target = new URL(url);
|
|
33
|
+
const { headers, ...rest } = options;
|
|
34
|
+
if (headers) {
|
|
35
|
+
const merged = new Headers(rest.requestInit?.headers);
|
|
36
|
+
for (const [name, value] of Object.entries(headers)) merged.set(name, value);
|
|
37
|
+
rest.requestInit = { ...rest.requestInit, headers: merged };
|
|
38
|
+
}
|
|
39
|
+
const make = (authProvider?: AuthProvider) =>
|
|
40
|
+
new StreamableHTTPClientTransport(target, authProvider ? { ...rest, authProvider } : rest);
|
|
41
|
+
madeByHttp.set(make, {
|
|
42
|
+
authProvider: rest.authProvider !== undefined,
|
|
43
|
+
authorization: new Headers(rest.requestInit?.headers).has("authorization"),
|
|
44
|
+
});
|
|
45
|
+
return make;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A server this process starts, and talks to over its stdin and stdout. Each
|
|
50
|
+
* connection starts a new child; `close()` stops it. Unless `env` is given,
|
|
51
|
+
* the child gets only the SDK's short list of safe variables (such as `HOME`
|
|
52
|
+
* and `PATH`), not this process's environment and the secrets in it.
|
|
53
|
+
*/
|
|
54
|
+
export function stdio(params: StdioServerParameters): () => Transport {
|
|
55
|
+
return () => new StdioClientTransport(params);
|
|
56
|
+
}
|