@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,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Troubleshooting"
|
|
3
|
+
description: "The failures @smthrs/mcp reports, by the message you saw: startup, handshake, catalog, call, argument, limit, and capability problems, each with its cause and fix."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Every failure this package reports is an `McpError` carrying a stable code, the
|
|
7
|
+
server name, and a message. Find the message you saw. The full error schema is
|
|
8
|
+
on the [API reference](./api.md).
|
|
9
|
+
|
|
10
|
+
One failure is not an `McpError` at all, and it is the most common one: see
|
|
11
|
+
[capability_refused](#capability_refused) at the end.
|
|
12
|
+
|
|
13
|
+
## Failed to start MCP server "..."
|
|
14
|
+
|
|
15
|
+
**Code.** `spawn_failed`.
|
|
16
|
+
|
|
17
|
+
**What happened.** The process could not be started. Raw process details are
|
|
18
|
+
withheld. For local debugging, install a trusted
|
|
19
|
+
[Diagnostics observer](./api.md#diagnostics) before opening the connection.
|
|
20
|
+
|
|
21
|
+
**What to change.** Check that `command` is on the parent process's `PATH`, that
|
|
22
|
+
`cwd` exists, and that the entry file is readable. The child receives `PATH`
|
|
23
|
+
from the bootstrap allowlist even when `env` declares other names, so a wrong
|
|
24
|
+
`command` usually is the cause. For a server started through `npx`, run the same
|
|
25
|
+
command by hand first.
|
|
26
|
+
|
|
27
|
+
## ... speaks an unsupported protocol version; this client speaks ...
|
|
28
|
+
|
|
29
|
+
**Code.** `protocol_error`.
|
|
30
|
+
|
|
31
|
+
**What happened.** The server answered `initialize` with a revision outside
|
|
32
|
+
`McpClient.supportedProtocolVersions`, which is `2025-06-18`, `2025-03-26`, and
|
|
33
|
+
`2024-11-05`.
|
|
34
|
+
|
|
35
|
+
**What to change.** Upgrade or downgrade the server to a revision on that list.
|
|
36
|
+
The list is frozen: this client decodes the `tools/list` and `tools/call` shapes
|
|
37
|
+
of those three revisions and cannot claim a fourth.
|
|
38
|
+
|
|
39
|
+
## ... does not serve tools
|
|
40
|
+
|
|
41
|
+
**Code.** `protocol_error`.
|
|
42
|
+
|
|
43
|
+
**What happened.** The server's `initialize` result declared no `tools`
|
|
44
|
+
capability.
|
|
45
|
+
|
|
46
|
+
**What to change.** This package projects tools and nothing else. A server that
|
|
47
|
+
serves only resources, prompts, or sampling has nothing to project, and the
|
|
48
|
+
connection is refused rather than left useless.
|
|
49
|
+
|
|
50
|
+
## ... returned a malformed initialize result
|
|
51
|
+
|
|
52
|
+
**Code.** `protocol_error`.
|
|
53
|
+
|
|
54
|
+
**What happened.** The handshake result was not an object, its `protocolVersion`
|
|
55
|
+
was not a string, or its `capabilities` was not an object. The message names
|
|
56
|
+
which.
|
|
57
|
+
|
|
58
|
+
**What to change.** Fix the server's `initialize` reply. If it is not your
|
|
59
|
+
server, check whether it is writing something other than JSON-RPC to stdout on
|
|
60
|
+
startup; the reply itself must be one line of JSON.
|
|
61
|
+
|
|
62
|
+
## ... returned a tools/list result with no tools array
|
|
63
|
+
|
|
64
|
+
**Code.** `invalid_response`.
|
|
65
|
+
|
|
66
|
+
**What happened.** The catalog reply had no `tools` array. The nearby messages
|
|
67
|
+
`returned tools[N], which is not an object` and `returned tools[N] with no name`
|
|
68
|
+
cover the same class of defect one element deeper.
|
|
69
|
+
|
|
70
|
+
**What to change.** Fix the server's `tools/list` reply.
|
|
71
|
+
|
|
72
|
+
## ... returned a tool whose inputSchema is not a JSON Schema object of type "object"
|
|
73
|
+
|
|
74
|
+
**Code.** `invalid_response`.
|
|
75
|
+
|
|
76
|
+
**What happened.** A catalog entry's `inputSchema` was missing, was not an
|
|
77
|
+
object, or declared a `type` other than `"object"`.
|
|
78
|
+
|
|
79
|
+
**What to change.** Give every tool an `inputSchema` with `"type": "object"`.
|
|
80
|
+
The requirement is not pedantry: that document is disclosed to the model as the
|
|
81
|
+
tool's parameter shape, and a tool without one would be advertised as if it had
|
|
82
|
+
one. `outputSchema`, when present, must also be a JSON object.
|
|
83
|
+
|
|
84
|
+
## ... returned more than N tools, or more than N tools/list pages
|
|
85
|
+
|
|
86
|
+
**Code.** `invalid_response`.
|
|
87
|
+
|
|
88
|
+
**What happened.** The catalog exceeded `maxTools` (256 by default) or the
|
|
89
|
+
cursor walk exceeded `maxCatalogPages` (32 by default). The related message
|
|
90
|
+
`repeated a tools/list cursor` means the server sent a cursor it had
|
|
91
|
+
already sent, which would loop forever.
|
|
92
|
+
|
|
93
|
+
**What to change.** For a genuinely large server, raise `maxTools` or
|
|
94
|
+
`maxCatalogPages`, and narrow the projection with `include` so the model is not
|
|
95
|
+
shown hundreds of flows. For a repeated cursor, fix the server's paging. A
|
|
96
|
+
catalog is refused rather than truncated on purpose: a silently smaller toolset
|
|
97
|
+
is worse than a failed connection. See
|
|
98
|
+
[Bound an untrusted server](./guides/bound-an-untrusted-server.md).
|
|
99
|
+
|
|
100
|
+
## ... returned a tool name longer than N bytes, or that is "." or "..", or contains "/" or an invisible or control character
|
|
101
|
+
|
|
102
|
+
**Code.** `invalid_response`.
|
|
103
|
+
|
|
104
|
+
**What happened.** A tool name exceeded `maxToolNameBytes` (128 by default), or
|
|
105
|
+
was `.` or `..`, or contained `/`, a control character (C0, U+007F, C1), a
|
|
106
|
+
format character such as a zero-width or bidi mark, U+2028, U+2029, or a lone
|
|
107
|
+
surrogate.
|
|
108
|
+
|
|
109
|
+
**What to change.** Rename the tool on the server. The name is embedded in the
|
|
110
|
+
flow name `mcp/<server>/<tool>` and in the journal's declaration digest, so `/`
|
|
111
|
+
would make it ambiguous and an invisible or control character would make it
|
|
112
|
+
read as a different tool. `returned a duplicate tool name at catalog index N` is the same class: a duplicate makes the
|
|
113
|
+
flow name ambiguous.
|
|
114
|
+
|
|
115
|
+
## ... returned a tool description and inputSchema longer than N bytes
|
|
116
|
+
|
|
117
|
+
**Code.** `invalid_response`.
|
|
118
|
+
|
|
119
|
+
**What happened.** One tool's description plus its JSON-encoded `inputSchema`
|
|
120
|
+
exceeded `maxToolDocumentBytes` (65536 by default).
|
|
121
|
+
|
|
122
|
+
**What to change.** Shorten the tool's description or schema on the server, or
|
|
123
|
+
raise `maxToolDocumentBytes` for a server you trust. The text is authored by the
|
|
124
|
+
server and read by a model, so it is bounded like the rest of the catalog.
|
|
125
|
+
|
|
126
|
+
## ... did not answer METHOD within Nms
|
|
127
|
+
|
|
128
|
+
**Code.** `timeout`.
|
|
129
|
+
|
|
130
|
+
**What happened.** The deadline for that method expired.
|
|
131
|
+
`handshakeTimeoutMs` (10 seconds by default) bounds `initialize` and
|
|
132
|
+
`tools/list`; `requestTimeoutMs` (120 seconds by default) bounds `tools/call`.
|
|
133
|
+
The message indicates when a stderr diagnostic was withheld; it never echoes
|
|
134
|
+
the child's output.
|
|
135
|
+
|
|
136
|
+
**What to change.** For a slow tool, raise `requestTimeoutMs`. For a handshake
|
|
137
|
+
that never completes, inspect the private stderr diagnostic through a trusted
|
|
138
|
+
host observer: a server that reports a crash is not merely slow.
|
|
139
|
+
|
|
140
|
+
If the request was still queued, the writer skips it. If it was already handed
|
|
141
|
+
to the writer, one best-effort `notifications/cancelled` is sent, unless the
|
|
142
|
+
outbound queue is full or the method is `initialize`.
|
|
143
|
+
|
|
144
|
+
## MCP server "..." connection scope closed, stdout closed, stdin closed, or exited with code N
|
|
145
|
+
|
|
146
|
+
**Code.** `connection_closed`.
|
|
147
|
+
|
|
148
|
+
**What happened.** The session ended. Every request pending at that moment
|
|
149
|
+
failed with this one error, and every later request fails with it too. A clean
|
|
150
|
+
child exit ends stdout successfully, so an ordinary exit reads as "stdout
|
|
151
|
+
closed" or "exited with code N". Closing a healthy connection's scope reports
|
|
152
|
+
"connection scope closed" before I/O teardown can report "stdin closed".
|
|
153
|
+
|
|
154
|
+
**What to change.** "Connection scope closed" is usually the caller's own bug:
|
|
155
|
+
the scope was closed while a call was still running, or a client was used after
|
|
156
|
+
its `Effect.scoped` block returned. Hold the connection for the life of the
|
|
157
|
+
session rather than per call. For a server that exited on its own, inspect the
|
|
158
|
+
private stderr diagnostic through a trusted host observer.
|
|
159
|
+
|
|
160
|
+
## MCP frame exceeded N bytes
|
|
161
|
+
|
|
162
|
+
**Code.** `protocol_error`.
|
|
163
|
+
|
|
164
|
+
**What happened.** One inbound line exceeded `maxFrameBytes` (1 MiB by default).
|
|
165
|
+
The limit is inclusive and counts UTF-8 bytes, excluding the newline and an
|
|
166
|
+
optional preceding carriage return. Chunk boundaries do not affect the limit.
|
|
167
|
+
The stdout reader retains one bounded partial frame plus at most one pending
|
|
168
|
+
carriage return, so this also fires when a server writes too much output
|
|
169
|
+
without a newline. Each byte is scanned once and each frame is decoded once.
|
|
170
|
+
|
|
171
|
+
**What to change.** For a tool that genuinely returns megabytes, raise
|
|
172
|
+
`maxFrameBytes`. Otherwise check that the server terminates each JSON-RPC
|
|
173
|
+
message with a newline. The mirror message, `tried to send a METHOD frame larger
|
|
174
|
+
than N bytes`, means your own arguments exceeded `maxOutboundFrameBytes`.
|
|
175
|
+
|
|
176
|
+
## ... sent a malformed JSON-RPC reply
|
|
177
|
+
|
|
178
|
+
**Code.** `protocol_error`.
|
|
179
|
+
|
|
180
|
+
**What happened.** A line that claimed JSON-RPC by carrying a `jsonrpc` property
|
|
181
|
+
was not a valid reply: the wrong version, no id, an invalid id, neither `result`
|
|
182
|
+
nor `error`, both of them, or a malformed error object. This closes the connection.
|
|
183
|
+
|
|
184
|
+
Reply ids must be integers or canonical decimal strings. A valid error reply
|
|
185
|
+
with `id: null` is the exception: its details go to the private diagnostic
|
|
186
|
+
observer as `remote-error`, then it is dropped without closing the connection.
|
|
187
|
+
Pending requests still wait for their own replies or deadlines. A result with
|
|
188
|
+
`id: null` is malformed.
|
|
189
|
+
|
|
190
|
+
**What to change.** Fix the server's framing. Note what does **not** cause this:
|
|
191
|
+
a blank line, invalid JSON, a JSON scalar or array, and any object without a
|
|
192
|
+
`jsonrpc` property are treated as log noise and dropped, so ordinary server
|
|
193
|
+
logging on stdout is safe. Only a message that claims to be protocol traffic is
|
|
194
|
+
held to protocol rules.
|
|
195
|
+
|
|
196
|
+
## MCP option "..." must be a positive integer
|
|
197
|
+
|
|
198
|
+
**Code.** `protocol_error`.
|
|
199
|
+
|
|
200
|
+
**What happened.** A limit was zero, negative, or fractional. This is raised
|
|
201
|
+
before the process is spawned.
|
|
202
|
+
|
|
203
|
+
**What to change.** Pass a positive integer.
|
|
204
|
+
|
|
205
|
+
## MCP server "..." option "namePrefix" must not be empty
|
|
206
|
+
|
|
207
|
+
**Code.** `protocol_error`.
|
|
208
|
+
|
|
209
|
+
**What happened.** `McpFlows.connected` was given an empty prefix. This is
|
|
210
|
+
raised before the process is spawned.
|
|
211
|
+
|
|
212
|
+
**What to change.** Pass a non-empty `namePrefix`, or omit it to use
|
|
213
|
+
`mcp/<server>`.
|
|
214
|
+
|
|
215
|
+
## MCP server "..." has no requested tool
|
|
216
|
+
|
|
217
|
+
**Code.** `tool_not_found`.
|
|
218
|
+
|
|
219
|
+
**What happened.** `callTool` was given a name that is not in the catalog. The
|
|
220
|
+
check runs before any frame is written.
|
|
221
|
+
|
|
222
|
+
**What to change.** Read `client.tools` for the names the server actually
|
|
223
|
+
offers. Remember the catalog is a snapshot taken at connect time: a tool the
|
|
224
|
+
server added afterwards is not there until you reconnect. The variant
|
|
225
|
+
`offers no requested include tool` is the same mistake in
|
|
226
|
+
`ProjectionOptions.include`.
|
|
227
|
+
|
|
228
|
+
## ... failed tools/call (CODE); remote details withheld
|
|
229
|
+
|
|
230
|
+
**Code.** `tool_failed`, or `tool_not_found`.
|
|
231
|
+
|
|
232
|
+
**What happened.** The server answered the call with a JSON-RPC error rather
|
|
233
|
+
than a result. It becomes `tool_not_found` only when the JSON-RPC code is
|
|
234
|
+
`-32601` or `-32602` **and** the message combines the word `tool` with
|
|
235
|
+
`unknown`, `unrecognized`, `no such`, or `not found`. Everything else, including
|
|
236
|
+
an ordinary invalid-arguments rejection, is `tool_failed`.
|
|
237
|
+
|
|
238
|
+
**What to change.** Read the numeric code. Remote message text and data are
|
|
239
|
+
available only through the private diagnostic observer, not passed through to
|
|
240
|
+
the caller. A `tool_failed` from bad arguments means the model or the caller
|
|
241
|
+
sent a value the tool's `inputSchema` rejects.
|
|
242
|
+
|
|
243
|
+
Do not confuse this with a tool that ran and reported a problem. That comes back
|
|
244
|
+
as a **successful** call with `isError: true`. See
|
|
245
|
+
[Handle a failed tool call](./guides/handle-a-failed-tool-call.md).
|
|
246
|
+
|
|
247
|
+
## ... was sent a tool argument that is not JSON: REASON; property path withheld
|
|
248
|
+
|
|
249
|
+
**Code.** `protocol_error`.
|
|
250
|
+
|
|
251
|
+
**What happened.** The arguments contained something that cannot cross a JSON
|
|
252
|
+
boundary: a function, a symbol, a bigint, `undefined`, a non-finite number, a
|
|
253
|
+
cyclic reference, an object with a non-plain prototype, an accessor property, a
|
|
254
|
+
symbol-keyed property, or a `toJSON` method. The error names the reason but
|
|
255
|
+
withholds the path and value. A private diagnostic can identify the path without
|
|
256
|
+
re-evaluating an accessor or proxy.
|
|
257
|
+
|
|
258
|
+
**What to change.** Send plain JSON. Accessors are rejected rather than invoked,
|
|
259
|
+
so a getter that would have produced a fine value still fails; compute it before
|
|
260
|
+
the call.
|
|
261
|
+
|
|
262
|
+
## ... returned structuredContent that its own outputSchema rejects: REASON; property path withheld
|
|
263
|
+
|
|
264
|
+
**Code.** `invalid_response`.
|
|
265
|
+
|
|
266
|
+
**What happened.** The tool declared an `outputSchema` and returned
|
|
267
|
+
`structuredContent` that violates it, checked against the supported keyword
|
|
268
|
+
subset: `type`, `required`, `properties`, single-schema `items`, and `enum`.
|
|
269
|
+
|
|
270
|
+
**What to change.** Fix the server, which is disagreeing with its own published
|
|
271
|
+
contract. Unsupported keywords are ignored rather than enforced, so this is
|
|
272
|
+
never a false positive from a constraint the validator half-implements. See
|
|
273
|
+
[Validate structured output](./guides/validate-structured-output.md).
|
|
274
|
+
|
|
275
|
+
## ... returned a tools/call result with no content array
|
|
276
|
+
|
|
277
|
+
**Code.** `invalid_response`.
|
|
278
|
+
|
|
279
|
+
**What happened.** The result carried neither `content` nor
|
|
280
|
+
`structuredContent`, or its `content` was not an array.
|
|
281
|
+
|
|
282
|
+
**What to change.** Return at least one of them. A structured-only result is
|
|
283
|
+
valid and comes back with `content: []`.
|
|
284
|
+
|
|
285
|
+
## capability_refused
|
|
286
|
+
|
|
287
|
+
**Not an McpError.** This is the cell boundary refusing the call, and the tool
|
|
288
|
+
never ran.
|
|
289
|
+
|
|
290
|
+
**What happened.** Every projected flow declares `McpFlows.capabilities`, one
|
|
291
|
+
entry per host action at resource `**`. The refusal names the first declared
|
|
292
|
+
capability the run's envelope does not cover, which under a read-only envelope
|
|
293
|
+
is `fs:write:**`.
|
|
294
|
+
|
|
295
|
+
**What to change.** Do not widen the envelope to `*:*`. Re-declare the source's
|
|
296
|
+
flows under the authority you actually grant this server, and put that same
|
|
297
|
+
string in the run's envelope. The recipe is in
|
|
298
|
+
[Grant authority to MCP tools](./guides/grant-authority-to-mcp-tools.md).
|
|
299
|
+
|
|
300
|
+
## A duplicate flow name fails catalog composition
|
|
301
|
+
|
|
302
|
+
**Not an McpError.** `FlowBinding.catalog` refuses two sources that offer the
|
|
303
|
+
same flow name, rather than dispatching one descriptor to another
|
|
304
|
+
implementation.
|
|
305
|
+
|
|
306
|
+
**What to change.** The default prefix `mcp/<server>` keeps two servers apart, so
|
|
307
|
+
this usually means two entries share a `server` name, or a custom `namePrefix`
|
|
308
|
+
collides with another source. Give each server a distinct name.
|
|
309
|
+
|
|
310
|
+
## Recovering from connection setup failure
|
|
311
|
+
|
|
312
|
+
`McpClient.connect` and `McpFlows.connected` release the subprocess and I/O
|
|
313
|
+
fibers before returning a failed or interrupted setup attempt. This includes
|
|
314
|
+
negotiation, catalog, and projection validation failures. Catching failures or
|
|
315
|
+
retrying inside an open scope does not retain failed connections. Successful
|
|
316
|
+
connections remain open until the caller scope closes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,160 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
2
3
|
"name": "@smthrs/mcp",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "1.0.0-rc.4",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"smthrs": {
|
|
8
|
+
"group": "agent"
|
|
9
|
+
},
|
|
10
|
+
"description": "Model Context Protocol client and flow adapter: projects a remote MCP server's tools as ordinary FlowBinding.Source entries",
|
|
11
|
+
"homepage": "https://github.com/smithersai/smithers",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/smithersai/smithers.git",
|
|
15
|
+
"directory": "packages/smithers/mcp"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/smithersai/smithers/issues"
|
|
19
|
+
},
|
|
20
|
+
"tags": [
|
|
21
|
+
"typescript",
|
|
22
|
+
"effect",
|
|
23
|
+
"mcp",
|
|
24
|
+
"flow-registry"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"typescript",
|
|
28
|
+
"effect",
|
|
29
|
+
"mcp",
|
|
30
|
+
"flow-registry"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=26.4.0"
|
|
34
|
+
},
|
|
35
|
+
"sideEffects": [],
|
|
36
|
+
"exports": {
|
|
37
|
+
"./package.json": "./package.json",
|
|
38
|
+
".": {
|
|
39
|
+
"import": {
|
|
40
|
+
"types": "./dist/esm/index.d.ts",
|
|
41
|
+
"default": "./dist/esm/index.js"
|
|
42
|
+
},
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./dist/cjs/index.d.ts",
|
|
45
|
+
"default": "./dist/cjs/index.js"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"./internal/*": null,
|
|
49
|
+
"./*/index": null,
|
|
50
|
+
"./Diagnostics": {
|
|
51
|
+
"import": {
|
|
52
|
+
"types": "./dist/esm/Diagnostics.d.ts",
|
|
53
|
+
"default": "./dist/esm/Diagnostics.js"
|
|
54
|
+
},
|
|
55
|
+
"require": {
|
|
56
|
+
"types": "./dist/cjs/Diagnostics.d.ts",
|
|
57
|
+
"default": "./dist/cjs/Diagnostics.js"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"./McpClient": {
|
|
61
|
+
"import": {
|
|
62
|
+
"types": "./dist/esm/McpClient.d.ts",
|
|
63
|
+
"default": "./dist/esm/McpClient.js"
|
|
64
|
+
},
|
|
65
|
+
"require": {
|
|
66
|
+
"types": "./dist/cjs/McpClient.d.ts",
|
|
67
|
+
"default": "./dist/cjs/McpClient.js"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"./McpError": {
|
|
71
|
+
"import": {
|
|
72
|
+
"types": "./dist/esm/McpError.d.ts",
|
|
73
|
+
"default": "./dist/esm/McpError.js"
|
|
74
|
+
},
|
|
75
|
+
"require": {
|
|
76
|
+
"types": "./dist/cjs/McpError.d.ts",
|
|
77
|
+
"default": "./dist/cjs/McpError.js"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"./McpFlows": {
|
|
81
|
+
"import": {
|
|
82
|
+
"types": "./dist/esm/McpFlows.d.ts",
|
|
83
|
+
"default": "./dist/esm/McpFlows.js"
|
|
84
|
+
},
|
|
85
|
+
"require": {
|
|
86
|
+
"types": "./dist/cjs/McpFlows.d.ts",
|
|
87
|
+
"default": "./dist/cjs/McpFlows.js"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"./index": {
|
|
91
|
+
"import": {
|
|
92
|
+
"types": "./dist/esm/index.d.ts",
|
|
93
|
+
"default": "./dist/esm/index.js"
|
|
94
|
+
},
|
|
95
|
+
"require": {
|
|
96
|
+
"types": "./dist/cjs/index.d.ts",
|
|
97
|
+
"default": "./dist/cjs/index.js"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"files": [
|
|
102
|
+
"src/**/*.ts",
|
|
103
|
+
"dist/**/*.js",
|
|
104
|
+
"dist/**/*.js.map",
|
|
105
|
+
"dist/**/*.d.ts",
|
|
106
|
+
"dist/**/*.d.ts.map",
|
|
107
|
+
"dist/**/package.json",
|
|
108
|
+
"LICENSE",
|
|
109
|
+
"README.md",
|
|
110
|
+
"CHANGELOG.md",
|
|
111
|
+
"docs/"
|
|
112
|
+
],
|
|
113
|
+
"publishConfig": {
|
|
114
|
+
"access": "public",
|
|
115
|
+
"provenance": true,
|
|
116
|
+
"tag": "next"
|
|
117
|
+
},
|
|
118
|
+
"dependencies": {
|
|
119
|
+
"@smthrs/canonical": "1.0.0-rc.4",
|
|
120
|
+
"@smthrs/capability": "1.0.0-rc.4",
|
|
121
|
+
"@smthrs/core": "1.0.0-rc.4",
|
|
122
|
+
"@smthrs/harness": "1.0.0-rc.4",
|
|
123
|
+
"@smthrs/journal": "1.0.0-rc.4",
|
|
124
|
+
"@smthrs/kernel": "1.0.0-rc.4"
|
|
125
|
+
},
|
|
126
|
+
"devDependencies": {
|
|
127
|
+
"@effect/language-service": "0.87.2",
|
|
128
|
+
"@effect/platform-node": "4.0.0-rc.115",
|
|
129
|
+
"@effect/vitest": "4.0.0-rc.115",
|
|
130
|
+
"@eslint/js": "9.39.5",
|
|
131
|
+
"@smthrs/model": "1.0.0-rc.4",
|
|
132
|
+
"@smthrs/registry": "1.0.0-rc.4",
|
|
133
|
+
"@types/node": "26.4.1",
|
|
134
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
135
|
+
"dprint": "0.57.1",
|
|
136
|
+
"effect": "4.0.0-rc.115",
|
|
137
|
+
"esbuild": "0.28.2",
|
|
138
|
+
"eslint": "9.39.5",
|
|
139
|
+
"eslint-import-resolver-typescript": "4.4.5",
|
|
140
|
+
"eslint-plugin-import": "2.32.0",
|
|
141
|
+
"eslint-plugin-jsdoc": "64.3.4",
|
|
142
|
+
"eslint-plugin-unicorn": "65.0.0",
|
|
143
|
+
"madge": "8.0.0",
|
|
144
|
+
"typescript": "5.9.3",
|
|
145
|
+
"typescript-eslint": "8.69.0",
|
|
146
|
+
"vitest": "5.0.0"
|
|
147
|
+
},
|
|
148
|
+
"peerDependencies": {
|
|
149
|
+
"effect": "4.0.0-rc.115"
|
|
150
|
+
},
|
|
151
|
+
"scripts": {
|
|
152
|
+
"lint": "eslint src --max-warnings=0 && dprint check",
|
|
153
|
+
"format": "dprint fmt",
|
|
154
|
+
"build": "node scripts/build.mjs",
|
|
155
|
+
"check": "tsc -b tsconfig.json && tsc -p tsconfig.test.json --noEmit",
|
|
156
|
+
"circular": "node scripts/circular.mjs",
|
|
157
|
+
"test": "vitest",
|
|
158
|
+
"coverage": "vitest --coverage"
|
|
159
|
+
}
|
|
6
160
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-only access to untrusted MCP diagnostics, separate from model-facing
|
|
3
|
+
* errors. Merely logging or JSON-encoding an event cannot expose its detail.
|
|
4
|
+
*
|
|
5
|
+
* @since 1.0.0-rc.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Context, Layer } from "effect"
|
|
9
|
+
import type { Redacted } from "effect"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One bounded diagnostic from a connection. Detail can contain credentials,
|
|
13
|
+
* private tool arguments, or arbitrary server text. Only an explicitly trusted
|
|
14
|
+
* local host should unwrap it; never forward it to agents, journals, or logs.
|
|
15
|
+
*
|
|
16
|
+
* @category models
|
|
17
|
+
* @since 1.0.0-rc.0
|
|
18
|
+
*/
|
|
19
|
+
export interface Event {
|
|
20
|
+
readonly server: string
|
|
21
|
+
readonly source: "spawn" | "stderr" | "transport" | "remote-error" | "invalid-response" | "invalid-arguments"
|
|
22
|
+
/** At most 16 KiB of UTF-8 text; never the original unbounded value. */
|
|
23
|
+
readonly detail: Redacted.Redacted<string>
|
|
24
|
+
readonly truncated: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Optional diagnostic observer, captured when a connection is opened. The
|
|
29
|
+
* synchronous callback must not block; throws are isolated from the protocol.
|
|
30
|
+
* Absent this service, raw diagnostics are discarded, not logged implicitly.
|
|
31
|
+
*
|
|
32
|
+
* @category services
|
|
33
|
+
* @since 1.0.0-rc.0
|
|
34
|
+
*/
|
|
35
|
+
export class Diagnostics extends Context.Service<Diagnostics, {
|
|
36
|
+
readonly report: (event: Event) => void
|
|
37
|
+
}>()("@smthrs/mcp/Diagnostics") {}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Installs a trusted host observer. Inspecting detail requires an explicit
|
|
41
|
+
* `Redacted.value` call and responsibility for its destination and retention.
|
|
42
|
+
*
|
|
43
|
+
* @category layers
|
|
44
|
+
* @since 1.0.0-rc.0
|
|
45
|
+
*/
|
|
46
|
+
export const layer = (report: (event: Event) => void): Layer.Layer<Diagnostics> =>
|
|
47
|
+
Layer.succeed(Diagnostics)({ report })
|