|
@@ -1,4 +1,5 @@
|
|
|
import path from "node:path"
|
|
import path from "node:path"
|
|
|
|
|
+import { pathToFileURL } from "node:url"
|
|
|
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
|
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
|
|
import { type Tool } from "ai"
|
|
import { type Tool } from "ai"
|
|
|
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
|
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
|
@@ -9,6 +10,7 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
|
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
|
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
|
|
import {
|
|
import {
|
|
|
|
|
+ ListRootsRequestSchema,
|
|
|
type LoggingMessageNotification,
|
|
type LoggingMessageNotification,
|
|
|
LoggingMessageNotificationSchema,
|
|
LoggingMessageNotificationSchema,
|
|
|
type Tool as MCPToolDef,
|
|
type Tool as MCPToolDef,
|
|
@@ -42,7 +44,7 @@ const CLIENT_OPTIONS = {
|
|
|
// https://github.com/anomalyco/opencode/issues/23066
|
|
// https://github.com/anomalyco/opencode/issues/23066
|
|
|
// elicitation: {},
|
|
// elicitation: {},
|
|
|
// https://github.com/anomalyco/opencode/issues/2308
|
|
// https://github.com/anomalyco/opencode/issues/2308
|
|
|
- // roots: {},
|
|
|
|
|
|
|
+ roots: {},
|
|
|
// https://github.com/anomalyco/opencode/issues/28567
|
|
// https://github.com/anomalyco/opencode/issues/28567
|
|
|
// tasks: {},
|
|
// tasks: {},
|
|
|
},
|
|
},
|
|
@@ -82,6 +84,14 @@ export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("MCP
|
|
|
|
|
|
|
|
type MCPClient = Client
|
|
type MCPClient = Client
|
|
|
|
|
|
|
|
|
|
+function createClient(directory: string) {
|
|
|
|
|
+ const client = new Client({ name: "opencode", version: InstallationVersion }, CLIENT_OPTIONS)
|
|
|
|
|
+ client.setRequestHandler(ListRootsRequestSchema, () =>
|
|
|
|
|
+ Promise.resolve({ roots: [{ uri: pathToFileURL(directory).href }] }),
|
|
|
|
|
+ )
|
|
|
|
|
+ return client
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const StatusConnected = Schema.Struct({ status: Schema.Literal("connected") }).annotate({
|
|
const StatusConnected = Schema.Struct({ status: Schema.Literal("connected") }).annotate({
|
|
|
identifier: "MCPStatusConnected",
|
|
identifier: "MCPStatusConnected",
|
|
|
})
|
|
})
|
|
@@ -192,19 +202,21 @@ export const layer = Layer.effect(
|
|
|
* Connect a client via the given transport with resource safety:
|
|
* Connect a client via the given transport with resource safety:
|
|
|
* on failure the transport is closed; on success the caller owns it.
|
|
* on failure the transport is closed; on success the caller owns it.
|
|
|
*/
|
|
*/
|
|
|
- const connectTransport = (transport: Transport, timeout: number) =>
|
|
|
|
|
- Effect.acquireUseRelease(
|
|
|
|
|
|
|
+ const connectTransport = Effect.fn("MCP.connectTransport")(function* (transport: Transport, timeout: number) {
|
|
|
|
|
+ const directory = yield* InstanceState.directory
|
|
|
|
|
+ return yield* Effect.acquireUseRelease(
|
|
|
Effect.succeed(transport),
|
|
Effect.succeed(transport),
|
|
|
(t) =>
|
|
(t) =>
|
|
|
Effect.tryPromise({
|
|
Effect.tryPromise({
|
|
|
try: () => {
|
|
try: () => {
|
|
|
- const client = new Client({ name: "opencode", version: InstallationVersion }, CLIENT_OPTIONS)
|
|
|
|
|
|
|
+ const client = createClient(directory)
|
|
|
return withTimeout(client.connect(t), timeout).then(() => client)
|
|
return withTimeout(client.connect(t), timeout).then(() => client)
|
|
|
},
|
|
},
|
|
|
catch: (e) => (e instanceof Error ? e : new Error(String(e))),
|
|
catch: (e) => (e instanceof Error ? e : new Error(String(e))),
|
|
|
}),
|
|
}),
|
|
|
(t, exit) => (Exit.isFailure(exit) ? Effect.tryPromise(() => t.close()).pipe(Effect.ignore) : Effect.void),
|
|
(t, exit) => (Exit.isFailure(exit) ? Effect.tryPromise(() => t.close()).pipe(Effect.ignore) : Effect.void),
|
|
|
)
|
|
)
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
const DISABLED_RESULT: CreateResult = { status: { status: "disabled" } }
|
|
const DISABLED_RESULT: CreateResult = { status: { status: "disabled" } }
|
|
|
|
|
|
|
@@ -777,10 +789,11 @@ export const layer = Layer.effect(
|
|
|
authProvider,
|
|
authProvider,
|
|
|
requestInit: mcpConfig.headers ? { headers: mcpConfig.headers } : undefined,
|
|
requestInit: mcpConfig.headers ? { headers: mcpConfig.headers } : undefined,
|
|
|
})
|
|
})
|
|
|
|
|
+ const directory = yield* InstanceState.directory
|
|
|
|
|
|
|
|
return yield* Effect.tryPromise({
|
|
return yield* Effect.tryPromise({
|
|
|
try: () => {
|
|
try: () => {
|
|
|
- const client = new Client({ name: "opencode", version: InstallationVersion }, CLIENT_OPTIONS)
|
|
|
|
|
|
|
+ const client = createClient(directory)
|
|
|
return client
|
|
return client
|
|
|
.connect(transport)
|
|
.connect(transport)
|
|
|
.then(() => ({ authorizationUrl: "", oauthState, client }) satisfies AuthResult)
|
|
.then(() => ({ authorizationUrl: "", oauthState, client }) satisfies AuthResult)
|