feat(core): allow MCP Code Mode opt-out (#37681)
Co-authored-by: Dax Raad <d@ironbay.co>
This commit is contained in:
parent
33f1b269e9
commit
fe9b051d1a
6 changed files with 57 additions and 9 deletions
|
|
@ -23,6 +23,9 @@ export class Local extends Schema.Class<Local>("ConfigV2.MCP.Local")({
|
|||
}),
|
||||
environment: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
|
||||
disabled: Schema.Boolean.pipe(Schema.optional),
|
||||
codemode: Schema.Boolean.pipe(Schema.optional).annotate({
|
||||
description: "Expose this server's tools through Code Mode. Defaults to true.",
|
||||
}),
|
||||
timeout: Timeout.pipe(Schema.optional),
|
||||
}) {}
|
||||
|
||||
|
|
@ -40,6 +43,9 @@ export class Remote extends Schema.Class<Remote>("ConfigV2.MCP.Remote")({
|
|||
headers: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
|
||||
oauth: Schema.Union([OAuth, Schema.Literal(false)]).pipe(Schema.optional),
|
||||
disabled: Schema.Boolean.pipe(Schema.optional),
|
||||
codemode: Schema.Boolean.pipe(Schema.optional).annotate({
|
||||
description: "Expose this server's tools through Code Mode. Defaults to true.",
|
||||
}),
|
||||
timeout: Timeout.pipe(Schema.optional),
|
||||
}) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export class ServerInstructions extends Schema.Class<ServerInstructions>("MCP.Se
|
|||
export class Tool extends Schema.Class<Tool>("MCP.Tool")({
|
||||
server: ServerName,
|
||||
name: Schema.String,
|
||||
codemode: Schema.Boolean.pipe(Schema.optional),
|
||||
description: Schema.String.pipe(Schema.optional),
|
||||
inputSchema: Schema.Unknown.pipe(Schema.optional),
|
||||
outputSchema: Schema.Unknown.pipe(Schema.optional),
|
||||
|
|
@ -362,10 +363,11 @@ export const layer = Layer.effect(
|
|||
}),
|
||||
} satisfies MCPClient.ElicitationHandler
|
||||
|
||||
const toTool = (server: ServerName, def: MCPClient.ToolDefinition) =>
|
||||
const toTool = (server: ServerName, entry: ServerEntry, def: MCPClient.ToolDefinition) =>
|
||||
new Tool({
|
||||
server,
|
||||
name: def.name,
|
||||
codemode: entry.config.codemode,
|
||||
description: def.description,
|
||||
inputSchema: def.inputSchema,
|
||||
outputSchema: def.outputSchema,
|
||||
|
|
@ -407,7 +409,7 @@ export const layer = Layer.effect(
|
|||
const refreshTools = (name: ServerName, entry: ServerEntry, connection: MCPClient.Connection) =>
|
||||
connection.tools().pipe(
|
||||
Effect.map((defs) => {
|
||||
entry.tools = defs.map((def) => toTool(name, def))
|
||||
entry.tools = defs.map((def) => toTool(name, entry, def))
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -498,7 +500,7 @@ export const layer = Layer.effect(
|
|||
)
|
||||
if (Exit.isSuccess(result)) {
|
||||
entry.client = result.value.connection
|
||||
entry.tools = result.value.tools.map((def) => toTool(name, def))
|
||||
entry.tools = result.value.tools.map((def) => toTool(name, entry, def))
|
||||
entry.prompts = []
|
||||
entry.status = { status: "connected" }
|
||||
watch(name, entry, result.value.connection)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ import { ToolRegistry } from "./registry"
|
|||
* Registry namespace and permission action names for MCP tools.
|
||||
*/
|
||||
export const namespace = (server: string) => server.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
export const name = (server: string, tool: string) =>
|
||||
`${namespace(server)}_${tool.replace(/[^a-zA-Z0-9_-]/g, "_")}`
|
||||
export const name = (server: string, tool: string) => `${namespace(server)}_${tool.replace(/[^a-zA-Z0-9_-]/g, "_")}`
|
||||
|
||||
export const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
|
|
@ -33,11 +32,11 @@ export const layer = Layer.effectDiscard(
|
|||
// registry never has a gap where MCP tools disappear mid-swap.
|
||||
const reconcile = lock.withPermit(
|
||||
Effect.gen(function* () {
|
||||
const groups = new Map<string, Record<string, Tool.AnyTool>>()
|
||||
const groups = new Map<string, { tools: Record<string, Tool.AnyTool>; codemode: boolean }>()
|
||||
for (const tool of yield* mcp.tools()) {
|
||||
const group = groups.get(tool.server) ?? {}
|
||||
const group = groups.get(tool.server) ?? { tools: {}, codemode: tool.codemode !== false }
|
||||
const schema = (tool.inputSchema ?? {}) as JsonSchema.JsonSchema
|
||||
group[tool.name] = Tool.withPermission(
|
||||
group.tools[tool.name] = Tool.withPermission(
|
||||
Tool.make({
|
||||
description: tool.description ?? "",
|
||||
jsonSchema: {
|
||||
|
|
@ -108,7 +107,7 @@ export const layer = Layer.effectDiscard(
|
|||
const next = yield* Scope.fork(scope)
|
||||
yield* Effect.forEach(
|
||||
groups,
|
||||
([server, record]) => tools.register(record, { namespace: namespace(server) }),
|
||||
([server, group]) => tools.register(group.tools, { namespace: namespace(server), codemode: group.codemode }),
|
||||
{
|
||||
discard: true,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue