MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / discoverTools

Function discoverTools

packages/plugins/mcp/src/sdk/discover.ts:183–247  ·  view source on GitHub ↗
(
  connector: McpConnector,
  timeoutMs: number = Duration.toMillis(DEFAULT_DISCOVER_TIMEOUT),
)

Source from the content-addressed store, hash-verified

181 * or the timeout above can still cancel them promptly.
182 */
183export const discoverTools = (
184 connector: McpConnector,
185 timeoutMs: number = Duration.toMillis(DEFAULT_DISCOVER_TIMEOUT),
186): Effect.Effect<McpToolManifest, McpToolDiscoveryError> =>
187 Effect.uninterruptibleMask((restore) =>
188 Effect.gen(function* () {
189 // Acquire connection
190 const connection = yield* restore(
191 connector.pipe(
192 Effect.mapError((failure) => {
193 // Preserve the handshake HTTP status (401/403 = auth wall) and a
194 // connect-level timeout so the liveness health check can classify
195 // structurally — dropping `failureKind: "timeout"` here is what
196 // made a timed-out handshake read as a generic probe failure.
197 const httpStatus = Predicate.isTagged(failure, "McpConnectionError")
198 ? failure.httpStatus
199 : undefined;
200 const reauthorizationRequired = Predicate.isTagged(
201 failure,
202 "McpOAuthReauthorizationRequired",
203 );
204 const timedOut =
205 Predicate.isTagged(failure, "McpConnectionError") &&
206 failure.failureKind === "timeout";
207 return new McpToolDiscoveryError({
208 stage: "connect",
209 message: `Failed connecting to MCP server: ${failure.message}`,
210 ...(httpStatus !== undefined ? { httpStatus } : {}),
211 ...(reauthorizationRequired ? { reauthorizationRequired: true } : {}),
212 ...(timedOut ? { timedOut } : {}),
213 });
214 }),
215 ),
216 );
217
218 // The connection advertises the elicitation capability (connection.ts),
219 // so a server may elicit mid-listTools — the Codex desktop plugins do
220 // this for first-use approvals. Discovery has no user to route the
221 // request to (unlike the invoke path's bridge in invoke.ts), and a
222 // handler-less request would surface as a method-not-found error on the
223 // server's side of an otherwise healthy sync. Decline explicitly: the
224 // server completes the list with whatever it allows unapproved.
225 connection.client.setRequestHandler("elicitation/create", () =>
226 Promise.resolve({ action: "decline" }),
227 );
228
229 const manifest = yield* restore(listAllTools(connection)).pipe(
230 Effect.onExit(() => closeConnection(connection)),
231 );
232
233 return manifest;
234 }),
235 ).pipe(
236 Effect.timeoutOrElse({
237 duration: Duration.millis(timeoutMs),
238 orElse: () =>
239 Effect.fail(
240 new McpToolDiscoveryError({

Calls 4

restoreFunction · 0.85
listAllToolsFunction · 0.85
resolveMethod · 0.80
closeConnectionFunction · 0.70

Tested by

no test coverage detected