@smthrs/mcp 0.0.0-stage → 1.0.0-rc.4
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 +121 -0
- package/LICENSE +21 -0
- package/README.md +111 -2
- package/dist/cjs/Diagnostics.d.ts +46 -0
- package/dist/cjs/Diagnostics.d.ts.map +1 -0
- package/dist/cjs/Diagnostics.js +29 -0
- package/dist/cjs/Diagnostics.js.map +7 -0
- package/dist/cjs/McpClient.d.ts +304 -0
- package/dist/cjs/McpClient.d.ts.map +1 -0
- package/dist/cjs/McpClient.js +622 -0
- package/dist/cjs/McpClient.js.map +7 -0
- package/dist/cjs/McpError.d.ts +44 -0
- package/dist/cjs/McpError.d.ts.map +1 -0
- package/dist/cjs/McpError.js +41 -0
- package/dist/cjs/McpError.js.map +7 -0
- package/dist/cjs/McpFlows.d.ts +113 -0
- package/dist/cjs/McpFlows.d.ts.map +1 -0
- package/dist/cjs/McpFlows.js +127 -0
- package/dist/cjs/McpFlows.js.map +7 -0
- package/dist/cjs/index.d.ts +39 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +41 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/DiagnosticReporter.d.ts +17 -0
- package/dist/cjs/internal/DiagnosticReporter.d.ts.map +1 -0
- package/dist/cjs/internal/DiagnosticReporter.js +56 -0
- package/dist/cjs/internal/DiagnosticReporter.js.map +7 -0
- package/dist/cjs/internal/HttpTransport.d.ts +67 -0
- package/dist/cjs/internal/HttpTransport.d.ts.map +1 -0
- package/dist/cjs/internal/HttpTransport.js +298 -0
- package/dist/cjs/internal/HttpTransport.js.map +7 -0
- package/dist/cjs/internal/JsonLimits.d.ts +29 -0
- package/dist/cjs/internal/JsonLimits.d.ts.map +1 -0
- package/dist/cjs/internal/JsonLimits.js +51 -0
- package/dist/cjs/internal/JsonLimits.js.map +7 -0
- package/dist/cjs/internal/Limits.d.ts +36 -0
- package/dist/cjs/internal/Limits.d.ts.map +1 -0
- package/dist/cjs/internal/Limits.js +34 -0
- package/dist/cjs/internal/Limits.js.map +7 -0
- package/dist/cjs/internal/Rpc.d.ts +141 -0
- package/dist/cjs/internal/Rpc.d.ts.map +1 -0
- package/dist/cjs/internal/Rpc.js +92 -0
- package/dist/cjs/internal/Rpc.js.map +7 -0
- package/dist/cjs/internal/StdioTransport.d.ts +78 -0
- package/dist/cjs/internal/StdioTransport.d.ts.map +1 -0
- package/dist/cjs/internal/StdioTransport.js +310 -0
- package/dist/cjs/internal/StdioTransport.js.map +7 -0
- package/dist/cjs/internal/Transport.d.ts +87 -0
- package/dist/cjs/internal/Transport.d.ts.map +1 -0
- package/dist/cjs/internal/Transport.js +116 -0
- package/dist/cjs/internal/Transport.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/Diagnostics.d.ts +46 -0
- package/dist/esm/Diagnostics.d.ts.map +1 -0
- package/dist/esm/Diagnostics.js +26 -0
- package/dist/esm/Diagnostics.js.map +1 -0
- package/dist/esm/McpClient.d.ts +304 -0
- package/dist/esm/McpClient.d.ts.map +1 -0
- package/dist/esm/McpClient.js +671 -0
- package/dist/esm/McpClient.js.map +1 -0
- package/dist/esm/McpError.d.ts +44 -0
- package/dist/esm/McpError.d.ts.map +1 -0
- package/dist/esm/McpError.js +43 -0
- package/dist/esm/McpError.js.map +1 -0
- package/dist/esm/McpFlows.d.ts +113 -0
- package/dist/esm/McpFlows.d.ts.map +1 -0
- package/dist/esm/McpFlows.js +168 -0
- package/dist/esm/McpFlows.js.map +1 -0
- package/dist/esm/index.d.ts +39 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +39 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/DiagnosticReporter.d.ts +17 -0
- package/dist/esm/internal/DiagnosticReporter.d.ts.map +1 -0
- package/dist/esm/internal/DiagnosticReporter.js +44 -0
- package/dist/esm/internal/DiagnosticReporter.js.map +1 -0
- package/dist/esm/internal/HttpTransport.d.ts +67 -0
- package/dist/esm/internal/HttpTransport.d.ts.map +1 -0
- package/dist/esm/internal/HttpTransport.js +266 -0
- package/dist/esm/internal/HttpTransport.js.map +1 -0
- package/dist/esm/internal/JsonLimits.d.ts +29 -0
- package/dist/esm/internal/JsonLimits.d.ts.map +1 -0
- package/dist/esm/internal/JsonLimits.js +55 -0
- package/dist/esm/internal/JsonLimits.js.map +1 -0
- package/dist/esm/internal/Limits.d.ts +36 -0
- package/dist/esm/internal/Limits.d.ts.map +1 -0
- package/dist/esm/internal/Limits.js +41 -0
- package/dist/esm/internal/Limits.js.map +1 -0
- package/dist/esm/internal/Rpc.d.ts +141 -0
- package/dist/esm/internal/Rpc.d.ts.map +1 -0
- package/dist/esm/internal/Rpc.js +129 -0
- package/dist/esm/internal/Rpc.js.map +1 -0
- package/dist/esm/internal/StdioTransport.d.ts +78 -0
- package/dist/esm/internal/StdioTransport.d.ts.map +1 -0
- package/dist/esm/internal/StdioTransport.js +332 -0
- package/dist/esm/internal/StdioTransport.js.map +1 -0
- package/dist/esm/internal/Transport.d.ts +87 -0
- package/dist/esm/internal/Transport.d.ts.map +1 -0
- package/dist/esm/internal/Transport.js +146 -0
- package/dist/esm/internal/Transport.js.map +1 -0
- package/docs/README.md +139 -0
- package/docs/api.md +469 -0
- package/docs/concepts/the-session.md +135 -0
- package/docs/concepts/tools-as-flows.md +116 -0
- package/docs/guides/bound-an-untrusted-server.md +158 -0
- package/docs/guides/configure-servers-for-the-cli.md +167 -0
- package/docs/guides/connect-a-server.md +161 -0
- package/docs/guides/grant-authority-to-mcp-tools.md +130 -0
- package/docs/guides/handle-a-failed-tool-call.md +125 -0
- package/docs/guides/select-the-tools-a-run-sees.md +92 -0
- package/docs/guides/testing.md +132 -0
- package/docs/guides/validate-structured-output.md +103 -0
- package/docs/installation.md +117 -0
- package/docs/quickstart.md +200 -0
- package/docs/troubleshooting.md +316 -0
- package/package.json +157 -3
- package/src/Diagnostics.ts +47 -0
- package/src/McpClient.ts +985 -0
- package/src/McpError.ts +52 -0
- package/src/McpFlows.ts +211 -0
- package/src/index.ts +42 -0
- package/src/internal/DiagnosticReporter.ts +47 -0
- package/src/internal/HttpTransport.ts +400 -0
- package/src/internal/JsonLimits.ts +53 -0
- package/src/internal/Limits.ts +48 -0
- package/src/internal/Rpc.ts +219 -0
- package/src/internal/StdioTransport.ts +491 -0
- package/src/internal/Transport.ts +178 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-RPC 2.0 envelope encoding for the MCP stdio transport.
|
|
3
|
+
*
|
|
4
|
+
* MCP's stdio transport frames every message as exactly one line of JSON on
|
|
5
|
+
* standard input or output, so this module is pure line-shaped codec: no
|
|
6
|
+
* process, no scheduling, no retry policy. {@link StdioTransport} owns those.
|
|
7
|
+
*
|
|
8
|
+
* @since 1.0.0-rc.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A JSON-RPC call this client sends. Omitting `id` sends a notification, for
|
|
13
|
+
* which the server never replies.
|
|
14
|
+
*
|
|
15
|
+
* @category models
|
|
16
|
+
* @since 1.0.0-rc.0
|
|
17
|
+
*/
|
|
18
|
+
export interface Outbound {
|
|
19
|
+
readonly jsonrpc: "2.0"
|
|
20
|
+
readonly id?: number | undefined
|
|
21
|
+
readonly method: string
|
|
22
|
+
readonly params?: unknown
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Any outbound wire message, including replies to server requests. A reply
|
|
27
|
+
* preserves the server's exact id type rather than normalizing it as a
|
|
28
|
+
* correlation id in the client's pending-request map.
|
|
29
|
+
*
|
|
30
|
+
* @category models
|
|
31
|
+
* @since 1.0.0-rc.0
|
|
32
|
+
*/
|
|
33
|
+
export type OutboundMessage = Outbound | {
|
|
34
|
+
readonly jsonrpc: "2.0"
|
|
35
|
+
readonly id: string | number
|
|
36
|
+
readonly result: unknown
|
|
37
|
+
} | {
|
|
38
|
+
readonly jsonrpc: "2.0"
|
|
39
|
+
readonly id: string | number
|
|
40
|
+
readonly error: { readonly code: number; readonly message: string }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A JSON object from server stdout that claims JSON-RPC by carrying its own
|
|
45
|
+
* `jsonrpc` property. Validation happens after parsing so an incorrect version
|
|
46
|
+
* cannot be mistaken for ordinary stdout noise.
|
|
47
|
+
*
|
|
48
|
+
* @category models
|
|
49
|
+
* @since 1.0.0-rc.0
|
|
50
|
+
*/
|
|
51
|
+
export interface Inbound {
|
|
52
|
+
readonly jsonrpc: unknown
|
|
53
|
+
readonly id?: unknown
|
|
54
|
+
readonly method?: unknown
|
|
55
|
+
readonly params?: unknown
|
|
56
|
+
readonly result?: unknown
|
|
57
|
+
readonly error?: unknown
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A validated JSON-RPC reply, normalized to the numeric request id this
|
|
62
|
+
* client uses for correlation, or an error whose null id cannot be correlated.
|
|
63
|
+
*
|
|
64
|
+
* @category models
|
|
65
|
+
* @since 1.0.0-rc.0
|
|
66
|
+
*/
|
|
67
|
+
export type Reply = {
|
|
68
|
+
readonly _tag: "Result"
|
|
69
|
+
readonly id: number
|
|
70
|
+
readonly result: unknown
|
|
71
|
+
} | {
|
|
72
|
+
readonly _tag: "Error"
|
|
73
|
+
readonly id: number
|
|
74
|
+
readonly code: number
|
|
75
|
+
readonly message: string
|
|
76
|
+
readonly data: unknown
|
|
77
|
+
} | {
|
|
78
|
+
readonly _tag: "UncorrelatedError"
|
|
79
|
+
readonly code: number
|
|
80
|
+
readonly message: string
|
|
81
|
+
readonly data: unknown
|
|
82
|
+
} | {
|
|
83
|
+
readonly _tag: "Malformed"
|
|
84
|
+
readonly reason: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The transport-relevant classification of one parsed JSON-RPC object.
|
|
89
|
+
* Server request ids belong to the opposite direction and must never be
|
|
90
|
+
* looked up in the client's pending-request map.
|
|
91
|
+
*
|
|
92
|
+
* @category models
|
|
93
|
+
* @since 1.0.0-rc.0
|
|
94
|
+
*/
|
|
95
|
+
export type Classification = { readonly _tag: "Notification" } | {
|
|
96
|
+
readonly _tag: "Request"
|
|
97
|
+
readonly id: string | number
|
|
98
|
+
readonly method: string
|
|
99
|
+
readonly params: unknown
|
|
100
|
+
} | Reply
|
|
101
|
+
|
|
102
|
+
const encoder = new TextEncoder()
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Encodes one outbound message as a newline-terminated UTF-8 frame.
|
|
106
|
+
*
|
|
107
|
+
* @category conversions
|
|
108
|
+
* @since 1.0.0-rc.0
|
|
109
|
+
*/
|
|
110
|
+
export const encode = (message: OutboundMessage): Uint8Array => encoder.encode(`${JSON.stringify(message)}\n`)
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parses one line of server output. A blank line, invalid JSON, a non-object,
|
|
114
|
+
* or an object with no own `jsonrpc` property returns `undefined`. MCP servers
|
|
115
|
+
* commonly log to stdout, so output that does not claim to be JSON-RPC is
|
|
116
|
+
* noise rather than a protocol violation. Tagged objects are preserved for
|
|
117
|
+
* {@link classify}, including objects that claim the wrong version.
|
|
118
|
+
*
|
|
119
|
+
* @category conversions
|
|
120
|
+
* @since 1.0.0-rc.0
|
|
121
|
+
*/
|
|
122
|
+
export const parse = (line: string): Inbound | undefined => {
|
|
123
|
+
const trimmed = line.trim()
|
|
124
|
+
if (trimmed === "") return undefined
|
|
125
|
+
let value: unknown
|
|
126
|
+
try {
|
|
127
|
+
value = JSON.parse(trimmed)
|
|
128
|
+
} catch {
|
|
129
|
+
return undefined
|
|
130
|
+
}
|
|
131
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined
|
|
132
|
+
if (!Object.hasOwn(value, "jsonrpc")) return undefined
|
|
133
|
+
return value as Inbound
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const malformed = (reason: string): Reply => ({ _tag: "Malformed", reason })
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Validates and normalizes a parsed inbound object as a reply.
|
|
140
|
+
*
|
|
141
|
+
* Digit-string ids are accepted only in their canonical ASCII decimal form,
|
|
142
|
+
* then converted back to the safe integer id used by the pending-request map.
|
|
143
|
+
* A reply must carry an own id and exactly one own `result` or `error`
|
|
144
|
+
* property. A null id is accepted only for a valid error, which cannot settle
|
|
145
|
+
* any particular request.
|
|
146
|
+
*
|
|
147
|
+
* @category conversions
|
|
148
|
+
* @since 1.0.0-rc.0
|
|
149
|
+
*/
|
|
150
|
+
export const replyOf = (message: Inbound): Reply => {
|
|
151
|
+
if (!Object.hasOwn(message, "id")) return malformed("a reply carried no id")
|
|
152
|
+
const rawId = message.id
|
|
153
|
+
const id = typeof rawId === "number"
|
|
154
|
+
? rawId
|
|
155
|
+
: typeof rawId === "string" && /^(0|[1-9][0-9]*)$/.test(rawId)
|
|
156
|
+
? Number(rawId)
|
|
157
|
+
: Number.NaN
|
|
158
|
+
if (rawId !== null && !Number.isSafeInteger(id)) return malformed("a reply id must be a JSON-RPC integer")
|
|
159
|
+
|
|
160
|
+
const hasResult = Object.hasOwn(message, "result")
|
|
161
|
+
const hasError = Object.hasOwn(message, "error")
|
|
162
|
+
if (!hasResult && !hasError) return malformed("a reply carried neither result nor error")
|
|
163
|
+
if (hasResult && hasError) return malformed("a reply carried both result and error")
|
|
164
|
+
if (hasResult) {
|
|
165
|
+
if (rawId === null) return malformed("a reply id must be a JSON-RPC integer")
|
|
166
|
+
return { _tag: "Result", id, result: message.result }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const error = message.error
|
|
170
|
+
if (
|
|
171
|
+
typeof error !== "object" || error === null || Array.isArray(error) ||
|
|
172
|
+
!Number.isInteger((error as { readonly code?: unknown }).code) ||
|
|
173
|
+
typeof (error as { readonly message?: unknown }).message !== "string"
|
|
174
|
+
) {
|
|
175
|
+
return malformed("a reply carried a malformed error object")
|
|
176
|
+
}
|
|
177
|
+
const record = error as { readonly code: number; readonly message: string; readonly data?: unknown }
|
|
178
|
+
if (rawId === null) {
|
|
179
|
+
return { _tag: "UncorrelatedError", code: record.code, message: record.message, data: record.data }
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
_tag: "Error",
|
|
183
|
+
id,
|
|
184
|
+
code: record.code,
|
|
185
|
+
message: record.message,
|
|
186
|
+
data: record.data
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Classifies a parsed JSON-RPC object for the stdio reader. A wrong version is
|
|
192
|
+
* malformed; a valid own `method` with an own id is a server request, without
|
|
193
|
+
* an id a notification. Every remaining object must satisfy {@link replyOf}.
|
|
194
|
+
*
|
|
195
|
+
* @category conversions
|
|
196
|
+
* @since 1.0.0-rc.0
|
|
197
|
+
*/
|
|
198
|
+
export const classify = (message: Inbound): Classification => {
|
|
199
|
+
if (message.jsonrpc !== "2.0") {
|
|
200
|
+
return malformed("a JSON-RPC message must carry jsonrpc \"2.0\"")
|
|
201
|
+
}
|
|
202
|
+
if (Object.hasOwn(message, "method")) {
|
|
203
|
+
if (typeof message.method !== "string") return malformed("a method must be a string")
|
|
204
|
+
if (Object.hasOwn(message, "result") || Object.hasOwn(message, "error")) {
|
|
205
|
+
return malformed("a method-bearing message cannot also carry result or error")
|
|
206
|
+
}
|
|
207
|
+
if (
|
|
208
|
+
Object.hasOwn(message, "params") &&
|
|
209
|
+
(typeof message.params !== "object" || message.params === null || Array.isArray(message.params))
|
|
210
|
+
) return malformed("MCP method params must be an object")
|
|
211
|
+
if (!Object.hasOwn(message, "id")) return { _tag: "Notification" }
|
|
212
|
+
const id = message.id
|
|
213
|
+
if (typeof id !== "string" && (typeof id !== "number" || !Number.isSafeInteger(id))) {
|
|
214
|
+
return malformed("a server request id must be a string or safe integer")
|
|
215
|
+
}
|
|
216
|
+
return { _tag: "Request", id, method: message.method, params: message.params }
|
|
217
|
+
}
|
|
218
|
+
return replyOf(message)
|
|
219
|
+
}
|