fix(opencode): honor disabled deferred MCP tools
This commit is contained in:
parent
b8f1ff424a
commit
ca359adafa
3 changed files with 53 additions and 6 deletions
|
|
@ -238,7 +238,10 @@ export const resolve = Effect.fn("SessionMcpTools.resolve")(function* (input: In
|
|||
Object.keys(mcpTools),
|
||||
Permission.merge(input.agent.permission, input.session.permission ?? []),
|
||||
)
|
||||
const allowedMcpTools = Object.fromEntries(Object.entries(mcpTools).filter(([key]) => !mcpDisabled.has(key)))
|
||||
const userTools = currentUserToolOverrides(input.messages)
|
||||
const allowedMcpTools = Object.fromEntries(
|
||||
Object.entries(mcpTools).filter(([key]) => userTools?.[key] !== false && !mcpDisabled.has(key)),
|
||||
)
|
||||
const deferredDescriptors =
|
||||
flags.experimentalToolSearch && Object.keys(allowedMcpTools).length > 0
|
||||
? yield* deferredToolDescriptors(allowedMcpTools)
|
||||
|
|
@ -277,6 +280,7 @@ export const resolve = Effect.fn("SessionMcpTools.resolve")(function* (input: In
|
|||
export const systemPrompt = Effect.fn("SessionMcpTools.systemPrompt")(function* (input: {
|
||||
agent: Agent.Info
|
||||
session: Session.Info
|
||||
messages: SessionV1.WithParts[]
|
||||
}) {
|
||||
const mcp = yield* MCP.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
|
|
@ -287,7 +291,10 @@ export const systemPrompt = Effect.fn("SessionMcpTools.systemPrompt")(function*
|
|||
Object.keys(mcpTools),
|
||||
Permission.merge(input.agent.permission, input.session.permission ?? []),
|
||||
)
|
||||
const allowedTools = Object.fromEntries(Object.entries(mcpTools).filter(([key]) => !mcpDisabled.has(key)))
|
||||
const userTools = currentUserToolOverrides(input.messages)
|
||||
const allowedTools = Object.fromEntries(
|
||||
Object.entries(mcpTools).filter(([key]) => userTools?.[key] !== false && !mcpDisabled.has(key)),
|
||||
)
|
||||
if (Object.keys(allowedTools).length === 0) return undefined
|
||||
|
||||
const deferredDescriptors = yield* deferredToolDescriptors(allowedTools)
|
||||
|
|
@ -755,6 +762,11 @@ function searchDeferredTools(descriptors: DeferredToolDescriptor[], query: strin
|
|||
.map((item) => item.descriptor)
|
||||
}
|
||||
|
||||
function currentUserToolOverrides(messages: SessionV1.WithParts[]) {
|
||||
const user = messages.findLast((message) => message.info.role === "user")
|
||||
return user?.info.role === "user" ? user.info.tools : undefined
|
||||
}
|
||||
|
||||
function mcpToolContent(result: unknown): McpToolContent[] {
|
||||
if (!isRecord(result) || !Array.isArray(result.content)) return []
|
||||
return result.content.flatMap((item): McpToolContent[] => {
|
||||
|
|
|
|||
|
|
@ -1260,7 +1260,7 @@ export const layer = Layer.effect(
|
|||
instruction.system().pipe(Effect.orDie),
|
||||
sys.mcp(agent, session.permission),
|
||||
tools.search_deferred_tools && tools.call_deferred_tool
|
||||
? SessionTools.deferredSystemPrompt({ agent, session }).pipe(
|
||||
? SessionTools.deferredSystemPrompt({ agent, session, messages: msgs }).pipe(
|
||||
Effect.provideService(MCP.Service, mcp),
|
||||
Effect.provideService(RuntimeFlags.Service, flags),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ const assistant = {
|
|||
time: { created: 0 },
|
||||
sessionID,
|
||||
} satisfies SessionV1.Assistant
|
||||
const user = {
|
||||
id: MessageID.make("msg_user"),
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: 0 },
|
||||
agent: agent.name,
|
||||
model: { providerID: ProviderV2.ID.make(model.providerID), modelID: ModelV2.ID.make(model.id) },
|
||||
} satisfies SessionV1.User
|
||||
const processor = {
|
||||
message: assistant,
|
||||
updateToolCall: () => Effect.succeed(undefined),
|
||||
|
|
@ -124,14 +132,14 @@ const belowThresholdIt = makeIt({
|
|||
queryDescription: "Natural language analytics query",
|
||||
})
|
||||
|
||||
function resolveTools() {
|
||||
function resolveTools(messages: SessionV1.WithParts[] = []) {
|
||||
return SessionTools.resolve({
|
||||
agent,
|
||||
model,
|
||||
session,
|
||||
processor,
|
||||
bypassAgentCheck: false,
|
||||
messages: [],
|
||||
messages,
|
||||
promptOps,
|
||||
})
|
||||
}
|
||||
|
|
@ -172,13 +180,40 @@ describe("session.tools", () => {
|
|||
|
||||
deferredIt.instance("lists deferred MCP servers in the system prompt", () =>
|
||||
Effect.gen(function* () {
|
||||
const prompt = yield* SessionTools.deferredSystemPrompt({ agent, session })
|
||||
const prompt = yield* SessionTools.deferredSystemPrompt({ agent, session, messages: [] })
|
||||
|
||||
expect(prompt).toContain("Deferred MCP servers available through `search_deferred_tools`:")
|
||||
expect(prompt).toContain("- posthog: 2 tools")
|
||||
}),
|
||||
)
|
||||
|
||||
deferredIt.instance("does not expose per-message disabled MCP tools through deferred search", () =>
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* resolveTools([
|
||||
{
|
||||
info: { ...user, tools: { posthog_feature_flags: false } },
|
||||
parts: [],
|
||||
},
|
||||
])
|
||||
|
||||
const search = tools.search_deferred_tools.execute
|
||||
if (!search) throw new Error("missing search_deferred_tools executor")
|
||||
const searchResult = yield* Effect.promise(() =>
|
||||
Promise.resolve(search({ query: "feature flags" }, toolExecutionOptions)),
|
||||
)
|
||||
const parsed = JSON.parse(searchResult.output) as { tools: Array<{ tool_id: string }> }
|
||||
expect(parsed.tools.map((item) => item.tool_id)).not.toContain("posthog_feature_flags")
|
||||
|
||||
const call = tools.call_deferred_tool.execute
|
||||
if (!call) throw new Error("missing call_deferred_tool executor")
|
||||
yield* Effect.promise(async () => {
|
||||
await expect(
|
||||
Promise.resolve(call({ tool_id: "posthog_feature_flags", arguments: {} }, toolExecutionOptions)),
|
||||
).rejects.toThrow('Deferred tool "posthog_feature_flags" is not available')
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
directIt.instance("keeps MCP tools direct when tool search is disabled", () =>
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* resolveTools()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue