@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.
Files changed (128) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/LICENSE +21 -0
  3. package/README.md +111 -2
  4. package/dist/cjs/Diagnostics.d.ts +46 -0
  5. package/dist/cjs/Diagnostics.d.ts.map +1 -0
  6. package/dist/cjs/Diagnostics.js +29 -0
  7. package/dist/cjs/Diagnostics.js.map +7 -0
  8. package/dist/cjs/McpClient.d.ts +304 -0
  9. package/dist/cjs/McpClient.d.ts.map +1 -0
  10. package/dist/cjs/McpClient.js +622 -0
  11. package/dist/cjs/McpClient.js.map +7 -0
  12. package/dist/cjs/McpError.d.ts +44 -0
  13. package/dist/cjs/McpError.d.ts.map +1 -0
  14. package/dist/cjs/McpError.js +41 -0
  15. package/dist/cjs/McpError.js.map +7 -0
  16. package/dist/cjs/McpFlows.d.ts +113 -0
  17. package/dist/cjs/McpFlows.d.ts.map +1 -0
  18. package/dist/cjs/McpFlows.js +127 -0
  19. package/dist/cjs/McpFlows.js.map +7 -0
  20. package/dist/cjs/index.d.ts +39 -0
  21. package/dist/cjs/index.d.ts.map +1 -0
  22. package/dist/cjs/index.js +41 -0
  23. package/dist/cjs/index.js.map +7 -0
  24. package/dist/cjs/internal/DiagnosticReporter.d.ts +17 -0
  25. package/dist/cjs/internal/DiagnosticReporter.d.ts.map +1 -0
  26. package/dist/cjs/internal/DiagnosticReporter.js +56 -0
  27. package/dist/cjs/internal/DiagnosticReporter.js.map +7 -0
  28. package/dist/cjs/internal/HttpTransport.d.ts +67 -0
  29. package/dist/cjs/internal/HttpTransport.d.ts.map +1 -0
  30. package/dist/cjs/internal/HttpTransport.js +298 -0
  31. package/dist/cjs/internal/HttpTransport.js.map +7 -0
  32. package/dist/cjs/internal/JsonLimits.d.ts +29 -0
  33. package/dist/cjs/internal/JsonLimits.d.ts.map +1 -0
  34. package/dist/cjs/internal/JsonLimits.js +51 -0
  35. package/dist/cjs/internal/JsonLimits.js.map +7 -0
  36. package/dist/cjs/internal/Limits.d.ts +36 -0
  37. package/dist/cjs/internal/Limits.d.ts.map +1 -0
  38. package/dist/cjs/internal/Limits.js +34 -0
  39. package/dist/cjs/internal/Limits.js.map +7 -0
  40. package/dist/cjs/internal/Rpc.d.ts +141 -0
  41. package/dist/cjs/internal/Rpc.d.ts.map +1 -0
  42. package/dist/cjs/internal/Rpc.js +92 -0
  43. package/dist/cjs/internal/Rpc.js.map +7 -0
  44. package/dist/cjs/internal/StdioTransport.d.ts +78 -0
  45. package/dist/cjs/internal/StdioTransport.d.ts.map +1 -0
  46. package/dist/cjs/internal/StdioTransport.js +310 -0
  47. package/dist/cjs/internal/StdioTransport.js.map +7 -0
  48. package/dist/cjs/internal/Transport.d.ts +87 -0
  49. package/dist/cjs/internal/Transport.d.ts.map +1 -0
  50. package/dist/cjs/internal/Transport.js +116 -0
  51. package/dist/cjs/internal/Transport.js.map +7 -0
  52. package/dist/cjs/package.json +1 -0
  53. package/dist/esm/Diagnostics.d.ts +46 -0
  54. package/dist/esm/Diagnostics.d.ts.map +1 -0
  55. package/dist/esm/Diagnostics.js +26 -0
  56. package/dist/esm/Diagnostics.js.map +1 -0
  57. package/dist/esm/McpClient.d.ts +304 -0
  58. package/dist/esm/McpClient.d.ts.map +1 -0
  59. package/dist/esm/McpClient.js +671 -0
  60. package/dist/esm/McpClient.js.map +1 -0
  61. package/dist/esm/McpError.d.ts +44 -0
  62. package/dist/esm/McpError.d.ts.map +1 -0
  63. package/dist/esm/McpError.js +43 -0
  64. package/dist/esm/McpError.js.map +1 -0
  65. package/dist/esm/McpFlows.d.ts +113 -0
  66. package/dist/esm/McpFlows.d.ts.map +1 -0
  67. package/dist/esm/McpFlows.js +168 -0
  68. package/dist/esm/McpFlows.js.map +1 -0
  69. package/dist/esm/index.d.ts +39 -0
  70. package/dist/esm/index.d.ts.map +1 -0
  71. package/dist/esm/index.js +39 -0
  72. package/dist/esm/index.js.map +1 -0
  73. package/dist/esm/internal/DiagnosticReporter.d.ts +17 -0
  74. package/dist/esm/internal/DiagnosticReporter.d.ts.map +1 -0
  75. package/dist/esm/internal/DiagnosticReporter.js +44 -0
  76. package/dist/esm/internal/DiagnosticReporter.js.map +1 -0
  77. package/dist/esm/internal/HttpTransport.d.ts +67 -0
  78. package/dist/esm/internal/HttpTransport.d.ts.map +1 -0
  79. package/dist/esm/internal/HttpTransport.js +266 -0
  80. package/dist/esm/internal/HttpTransport.js.map +1 -0
  81. package/dist/esm/internal/JsonLimits.d.ts +29 -0
  82. package/dist/esm/internal/JsonLimits.d.ts.map +1 -0
  83. package/dist/esm/internal/JsonLimits.js +55 -0
  84. package/dist/esm/internal/JsonLimits.js.map +1 -0
  85. package/dist/esm/internal/Limits.d.ts +36 -0
  86. package/dist/esm/internal/Limits.d.ts.map +1 -0
  87. package/dist/esm/internal/Limits.js +41 -0
  88. package/dist/esm/internal/Limits.js.map +1 -0
  89. package/dist/esm/internal/Rpc.d.ts +141 -0
  90. package/dist/esm/internal/Rpc.d.ts.map +1 -0
  91. package/dist/esm/internal/Rpc.js +129 -0
  92. package/dist/esm/internal/Rpc.js.map +1 -0
  93. package/dist/esm/internal/StdioTransport.d.ts +78 -0
  94. package/dist/esm/internal/StdioTransport.d.ts.map +1 -0
  95. package/dist/esm/internal/StdioTransport.js +332 -0
  96. package/dist/esm/internal/StdioTransport.js.map +1 -0
  97. package/dist/esm/internal/Transport.d.ts +87 -0
  98. package/dist/esm/internal/Transport.d.ts.map +1 -0
  99. package/dist/esm/internal/Transport.js +146 -0
  100. package/dist/esm/internal/Transport.js.map +1 -0
  101. package/docs/README.md +139 -0
  102. package/docs/api.md +469 -0
  103. package/docs/concepts/the-session.md +135 -0
  104. package/docs/concepts/tools-as-flows.md +116 -0
  105. package/docs/guides/bound-an-untrusted-server.md +158 -0
  106. package/docs/guides/configure-servers-for-the-cli.md +167 -0
  107. package/docs/guides/connect-a-server.md +161 -0
  108. package/docs/guides/grant-authority-to-mcp-tools.md +130 -0
  109. package/docs/guides/handle-a-failed-tool-call.md +125 -0
  110. package/docs/guides/select-the-tools-a-run-sees.md +92 -0
  111. package/docs/guides/testing.md +132 -0
  112. package/docs/guides/validate-structured-output.md +103 -0
  113. package/docs/installation.md +117 -0
  114. package/docs/quickstart.md +200 -0
  115. package/docs/troubleshooting.md +316 -0
  116. package/package.json +157 -3
  117. package/src/Diagnostics.ts +47 -0
  118. package/src/McpClient.ts +985 -0
  119. package/src/McpError.ts +52 -0
  120. package/src/McpFlows.ts +211 -0
  121. package/src/index.ts +42 -0
  122. package/src/internal/DiagnosticReporter.ts +47 -0
  123. package/src/internal/HttpTransport.ts +400 -0
  124. package/src/internal/JsonLimits.ts +53 -0
  125. package/src/internal/Limits.ts +48 -0
  126. package/src/internal/Rpc.ts +219 -0
  127. package/src/internal/StdioTransport.ts +491 -0
  128. 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
+ }