Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/core/prompts/__tests__/add-custom-instructions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,16 @@ const mockContext = {
// Instead of extending McpHub, create a mock that implements just what we need
const createMockMcpHub = (withServers: boolean = false): McpHub =>
({
getServers: () => (withServers ? [{ name: "test-server", disabled: false }] : []),
getServers: () =>
withServers
? [
{
name: "test-server",
disabled: false,
resources: [{ uri: "test://resource", name: "Test Resource" }],
},
]
: [],
getMcpServersPath: async () => "/mock/mcp/path",
getMcpSettingsFilePath: async () => "/mock/settings/path",
dispose: async () => {},
Expand Down
11 changes: 10 additions & 1 deletion src/core/prompts/__tests__/system-prompt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,16 @@ const mockContext = {
// Instead of extending McpHub, create a mock that implements just what we need
const createMockMcpHub = (withServers: boolean = false): McpHub =>
({
getServers: () => (withServers ? [{ name: "test-server", disabled: false }] : []),
getServers: () =>
withServers
? [
{
name: "test-server",
disabled: false,
resources: [{ uri: "test://resource", name: "Test Resource" }],
},
]
: [],
getMcpServersPath: async () => "/mock/mcp/path",
getMcpSettingsFilePath: async () => "/mock/settings/path",
dispose: async () => {},
Expand Down
118 changes: 118 additions & 0 deletions src/core/prompts/tools/__tests__/access-mcp-resource.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { getAccessMcpResourceDescription } from "../access-mcp-resource"
import { ToolArgs } from "../types"
import { McpHub } from "../../../../services/mcp/McpHub"

describe("getAccessMcpResourceDescription", () => {
const baseArgs: Omit<ToolArgs, "mcpHub"> = {
cwd: "/test",
supportsComputerUse: false,
}

it("should return undefined when mcpHub is not provided", () => {
const args: ToolArgs = {
...baseArgs,
mcpHub: undefined,
}

const result = getAccessMcpResourceDescription(args)
expect(result).toBeUndefined()
})

it("should return undefined when mcpHub has no servers with resources", () => {
const mockMcpHub = {
getServers: () => [
{
name: "test-server",
resources: [],
},
],
} as unknown as McpHub

const args: ToolArgs = {
...baseArgs,
mcpHub: mockMcpHub,
}

const result = getAccessMcpResourceDescription(args)
expect(result).toBeUndefined()
})

it("should return undefined when mcpHub has servers with undefined resources", () => {
const mockMcpHub = {
getServers: () => [
{
name: "test-server",
resources: undefined,
},
],
} as unknown as McpHub

const args: ToolArgs = {
...baseArgs,
mcpHub: mockMcpHub,
}

const result = getAccessMcpResourceDescription(args)
expect(result).toBeUndefined()
})

it("should return undefined when mcpHub has no servers", () => {
const mockMcpHub = {
getServers: () => [],
} as unknown as McpHub

const args: ToolArgs = {
...baseArgs,
mcpHub: mockMcpHub,
}

const result = getAccessMcpResourceDescription(args)
expect(result).toBeUndefined()
})

it("should return description when mcpHub has servers with resources", () => {
const mockMcpHub = {
getServers: () => [
{
name: "test-server",
resources: [{ uri: "test://resource", name: "Test Resource" }],
},
],
} as unknown as McpHub

const args: ToolArgs = {
...baseArgs,
mcpHub: mockMcpHub,
}

const result = getAccessMcpResourceDescription(args)
expect(result).toBeDefined()
expect(result).toContain("## access_mcp_resource")
expect(result).toContain("server_name")
expect(result).toContain("uri")
})

it("should return description when at least one server has resources", () => {
const mockMcpHub = {
getServers: () => [
{
name: "server-without-resources",
resources: [],
},
{
name: "server-with-resources",
resources: [{ uri: "test://resource", name: "Test Resource" }],
},
],
} as unknown as McpHub

const args: ToolArgs = {
...baseArgs,
mcpHub: mockMcpHub,
}

const result = getAccessMcpResourceDescription(args)
expect(result).toBeDefined()
expect(result).toContain("## access_mcp_resource")
})
})
167 changes: 159 additions & 8 deletions src/core/prompts/tools/__tests__/filter-tools-for-mode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,15 @@ describe("filterNativeToolsForMode", () => {
groups: ["read", "browser", "mcp"] as const,
}

const filtered = filterNativeToolsForMode(mockNativeTools, "architect", [architectMode], {}, undefined, {})
const filtered = filterNativeToolsForMode(
mockNativeTools,
"architect",
[architectMode],
{},
undefined,
{},
undefined,
)

const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))

Expand Down Expand Up @@ -101,7 +109,7 @@ describe("filterNativeToolsForMode", () => {
groups: ["read", "edit", "browser", "command", "mcp"] as const,
}

const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {})
const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {}, undefined)

const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))

Expand All @@ -123,7 +131,15 @@ describe("filterNativeToolsForMode", () => {
groups: [] as const, // No groups
}

const filtered = filterNativeToolsForMode(mockNativeTools, "restrictive", [restrictiveMode], {}, undefined, {})
const filtered = filterNativeToolsForMode(
mockNativeTools,
"restrictive",
[restrictiveMode],
{},
undefined,
{},
undefined,
)

const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))

Expand All @@ -138,7 +154,7 @@ describe("filterNativeToolsForMode", () => {
})

it("should handle undefined mode by using default mode", () => {
const filtered = filterNativeToolsForMode(mockNativeTools, undefined, undefined, {}, undefined, {})
const filtered = filterNativeToolsForMode(mockNativeTools, undefined, undefined, {}, undefined, {}, undefined)

// Should return some tools (default mode is code which has all groups)
expect(filtered.length).toBeGreaterThan(0)
Expand Down Expand Up @@ -168,11 +184,136 @@ describe("filterNativeToolsForMode", () => {
const toolsWithCodebaseSearch = [...mockNativeTools, mockCodebaseSearchTool]

// Without codeIndexManager
const filtered = filterNativeToolsForMode(toolsWithCodebaseSearch, "code", [codeMode], {}, undefined, {})
const filtered = filterNativeToolsForMode(
toolsWithCodebaseSearch,
"code",
[codeMode],
{},
undefined,
{},
undefined,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).not.toContain("codebase_search")
})

it("should exclude access_mcp_resource when mcpHub is not provided", () => {
const codeMode: ModeConfig = {
slug: "code",
name: "Code",
roleDefinition: "Test",
groups: ["read", "edit", "browser", "command", "mcp"] as const,
}

const mockAccessMcpResourceTool: OpenAI.Chat.ChatCompletionTool = {
type: "function",
function: {
name: "access_mcp_resource",
description: "Access MCP resource",
parameters: {},
},
}

const toolsWithAccessMcpResource = [...mockNativeTools, mockAccessMcpResourceTool]

// Without mcpHub
const filtered = filterNativeToolsForMode(
toolsWithAccessMcpResource,
"code",
[codeMode],
{},
undefined,
{},
undefined,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).not.toContain("access_mcp_resource")
})

it("should exclude access_mcp_resource when mcpHub has no resources", () => {
const codeMode: ModeConfig = {
slug: "code",
name: "Code",
roleDefinition: "Test",
groups: ["read", "edit", "browser", "command", "mcp"] as const,
}

const mockAccessMcpResourceTool: OpenAI.Chat.ChatCompletionTool = {
type: "function",
function: {
name: "access_mcp_resource",
description: "Access MCP resource",
parameters: {},
},
}

const toolsWithAccessMcpResource = [...mockNativeTools, mockAccessMcpResourceTool]

// Mock mcpHub with no resources
const mockMcpHub = {
getServers: () => [
{
name: "test-server",
resources: [],
},
],
} as any

const filtered = filterNativeToolsForMode(
toolsWithAccessMcpResource,
"code",
[codeMode],
{},
undefined,
{},
mockMcpHub,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).not.toContain("access_mcp_resource")
})

it("should include access_mcp_resource when mcpHub has resources", () => {
const codeMode: ModeConfig = {
slug: "code",
name: "Code",
roleDefinition: "Test",
groups: ["read", "edit", "browser", "command", "mcp"] as const,
}

const mockAccessMcpResourceTool: OpenAI.Chat.ChatCompletionTool = {
type: "function",
function: {
name: "access_mcp_resource",
description: "Access MCP resource",
parameters: {},
},
}

const toolsWithAccessMcpResource = [...mockNativeTools, mockAccessMcpResourceTool]

// Mock mcpHub with resources
const mockMcpHub = {
getServers: () => [
{
name: "test-server",
resources: [{ uri: "test://resource", name: "Test Resource" }],
},
],
} as any

const filtered = filterNativeToolsForMode(
toolsWithAccessMcpResource,
"code",
[codeMode],
{},
undefined,
{},
mockMcpHub,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).toContain("access_mcp_resource")
})

it("should exclude update_todo_list when todoListEnabled is false", () => {
const codeMode: ModeConfig = {
slug: "code",
Expand All @@ -192,9 +333,17 @@ describe("filterNativeToolsForMode", () => {

const toolsWithTodo = [...mockNativeTools, mockTodoTool]

const filtered = filterNativeToolsForMode(toolsWithTodo, "code", [codeMode], {}, undefined, {
todoListEnabled: false,
})
const filtered = filterNativeToolsForMode(
toolsWithTodo,
"code",
[codeMode],
{},
undefined,
{
todoListEnabled: false,
},
undefined,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).not.toContain("update_todo_list")
})
Expand Down Expand Up @@ -225,6 +374,7 @@ describe("filterNativeToolsForMode", () => {
{ imageGeneration: false },
undefined,
{},
undefined,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).not.toContain("generate_image")
Expand Down Expand Up @@ -256,6 +406,7 @@ describe("filterNativeToolsForMode", () => {
{ runSlashCommand: false },
undefined,
{},
undefined,
)
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
expect(toolNames).not.toContain("run_slash_command")
Expand Down
11 changes: 10 additions & 1 deletion src/core/prompts/tools/access-mcp-resource.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { ToolArgs } from "./types"
import { McpHub } from "../../../services/mcp/McpHub"

/**
* Helper function to check if any MCP server has resources available
*/
function hasAnyMcpResources(mcpHub: McpHub): boolean {
const servers = mcpHub.getServers()
return servers.some((server) => server.resources && server.resources.length > 0)
}

export function getAccessMcpResourceDescription(args: ToolArgs): string | undefined {
if (!args.mcpHub) {
if (!args.mcpHub || !hasAnyMcpResources(args.mcpHub)) {
return undefined
}
return `## access_mcp_resource
Expand Down
Loading
Loading