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

Function discoverTools

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

Source from the content-addressed store, hash-verified

124 * or the timeout above can still cancel them promptly.
125 */
126export const discoverTools = (
127 connector: McpConnector,
128 timeoutMs: number = Duration.toMillis(DEFAULT_DISCOVER_TIMEOUT),
129): Effect.Effect<McpToolManifest, McpToolDiscoveryError> =>
130 Effect.uninterruptibleMask((restore) =>
131 Effect.gen(function* () {
132 // Acquire connection
133 const connection = yield* restore(
134 connector.pipe(
135 Effect.mapError((failure) => {
136 // Preserve the handshake HTTP status (401/403 = auth wall) and a
137 // connect-level timeout so the liveness health check can classify
138 // structurally — dropping `failureKind: "timeout"` here is what
139 // made a timed-out handshake read as a generic probe failure.
140 const httpStatus = Predicate.isTagged(failure, "McpConnectionError")
141 ? failure.httpStatus
142 : undefined;
143 const reauthorizationRequired = Predicate.isTagged(
144 failure,
145 "McpOAuthReauthorizationRequired",
146 );
147 const timedOut =
148 Predicate.isTagged(failure, "McpConnectionError") &&
149 failure.failureKind === "timeout";
150 return new McpToolDiscoveryError({
151 stage: "connect",
152 message: `Failed connecting to MCP server: ${failure.message}`,
153 ...(httpStatus !== undefined ? { httpStatus } : {}),
154 ...(reauthorizationRequired ? { reauthorizationRequired: true } : {}),
155 ...(timedOut ? { timedOut } : {}),
156 });
157 }),
158 ),
159 );
160
161 // The connection advertises the elicitation capability (connection.ts),
162 // so a server may elicit mid-listTools — the Codex desktop plugins do
163 // this for first-use approvals. Discovery has no user to route the
164 // request to (unlike the invoke path's bridge in invoke.ts), and a
165 // handler-less request would surface as a method-not-found error on the
166 // server's side of an otherwise healthy sync. Decline explicitly: the
167 // server completes the list with whatever it allows unapproved.
168 connection.client.setRequestHandler("elicitation/create", () =>
169 Promise.resolve({ action: "decline" }),
170 );
171
172 const manifest = yield* restore(listAllTools(connection)).pipe(
173 Effect.onExit(() => closeConnection(connection)),
174 );
175
176 return manifest;
177 }),
178 ).pipe(
179 Effect.timeoutOrElse({
180 duration: Duration.millis(timeoutMs),
181 orElse: () =>
182 Effect.fail(
183 new McpToolDiscoveryError({

Callers 5

probeEndpointFunction · 0.90
plugin.tsFile · 0.90

Calls 4

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

Tested by

no test coverage detected