feat(plugin): expose app metadata (#38179)
This commit is contained in:
parent
2ed8fe5960
commit
8de40be6ea
71 changed files with 477 additions and 286 deletions
|
|
@ -12,6 +12,7 @@ import {
|
|||
ElicitationCompleteNotificationSchema,
|
||||
ElicitRequestSchema,
|
||||
GetPromptResultSchema,
|
||||
type Implementation,
|
||||
type ElicitRequestFormParams,
|
||||
type ElicitRequestParams,
|
||||
type ElicitRequestURLParams,
|
||||
|
|
@ -29,7 +30,6 @@ import {
|
|||
} from "@modelcontextprotocol/sdk/types.js"
|
||||
import { Cause, Effect, Exit, Schema } from "effect"
|
||||
import { ConfigMCP } from "../config/mcp"
|
||||
import { InstallationVersion } from "@opencode-ai/util/installation/version"
|
||||
|
||||
const DEFAULT_STARTUP_TIMEOUT = 30_000
|
||||
const DEFAULT_CATALOG_TIMEOUT = 30_000
|
||||
|
|
@ -185,6 +185,7 @@ export const connect = Effect.fnUntraced(function* (
|
|||
// stored token (and a no-op redirect) surfaces an UnauthorizedError, which we map to needs_auth.
|
||||
authProvider?: OAuthClientProvider,
|
||||
elicitation?: ElicitationHandler,
|
||||
clientInfo: Implementation = { name: "opencode", version: "unknown" },
|
||||
) {
|
||||
const transport: Transport = yield* Effect.gen(function* () {
|
||||
if (config.type === "local") {
|
||||
|
|
@ -209,7 +210,7 @@ export const connect = Effect.fnUntraced(function* (
|
|||
})
|
||||
})
|
||||
const client = new Client(
|
||||
{ name: "opencode", version: InstallationVersion },
|
||||
clientInfo,
|
||||
{
|
||||
capabilities: {
|
||||
...(elicitation ? { elicitation: { form: { applyDefaults: true }, url: {} } } : {}),
|
||||
|
|
|
|||
|
|
@ -157,7 +157,17 @@ export interface Interface {
|
|||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/MCP") {}
|
||||
|
||||
export const layer = Layer.effect(
|
||||
export const Options = Schema.Struct({
|
||||
clientInfo: Schema.optional(
|
||||
Schema.Struct({
|
||||
name: Schema.String,
|
||||
version: Schema.String,
|
||||
}),
|
||||
),
|
||||
})
|
||||
export type Options = typeof Options.Type
|
||||
|
||||
export const layer = (options?: Options) => Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service
|
||||
|
|
@ -498,7 +508,14 @@ export const layer = Layer.effect(
|
|||
const authProvider = yield* connectProvider(entry)
|
||||
// List tools as part of connect so a failure here marks the server failed rather than
|
||||
// leaving it connected with a silently empty tool list and no path to recover.
|
||||
const result = yield* MCPClient.connect(name, entry.config, location.directory, authProvider, elicitation).pipe(
|
||||
const result = yield* MCPClient.connect(
|
||||
name,
|
||||
entry.config,
|
||||
location.directory,
|
||||
authProvider,
|
||||
elicitation,
|
||||
options?.clientInfo,
|
||||
).pipe(
|
||||
Effect.flatMap((connection) => connection.tools().pipe(Effect.map((tools) => ({ connection, tools })))),
|
||||
Scope.provide(scope),
|
||||
Effect.exit,
|
||||
|
|
@ -772,11 +789,15 @@ export const layer = Layer.effect(
|
|||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
service: Service,
|
||||
layer,
|
||||
deps: [Config.node, Location.node, EventV2.node, Form.node, Integration.node, Credential.node],
|
||||
})
|
||||
export function configured(options?: Options) {
|
||||
return makeLocationNode({
|
||||
service: Service,
|
||||
layer: layer(options),
|
||||
deps: [Config.node, Location.node, EventV2.node, Form.node, Integration.node, Credential.node],
|
||||
})
|
||||
}
|
||||
|
||||
export const node = configured()
|
||||
|
||||
// Schema `optional` strips undefined-valued properties on encode, so fields can assign
|
||||
// optional properties directly instead of conditionally spreading them.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue