feat(mcp): restore Exa websearch support (#549)

* feat(mcp): restore Exa MCP websearch support

- Add websearch.ts with Exa remote MCP configuration
- Update McpNameSchema to include websearch
- Wire websearch MCP into plugin initialization

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

* test(mcp): update tests and docs for websearch MCP

- Update index.test.ts to verify 3 MCPs (websearch, context7, grep_app)
- Add Exa/websearch documentation to README.md MCPs section

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2026-01-07 01:24:50 +09:00
committed by GitHub
parent f25f7ed0f5
commit a2bfb5e556
5 changed files with 25 additions and 10 deletions

View File

@@ -582,6 +582,7 @@ These tools enable agents to reference previous conversations and maintain conti
- Use camelCase for function names - Use camelCase for function names
``` ```
- **Online**: Project rules aren't everything. Built-in MCPs for extended capabilities: - **Online**: Project rules aren't everything. Built-in MCPs for extended capabilities:
- **websearch**: Real-time web search powered by [Exa AI](https://exa.ai)
- **context7**: Official documentation lookup - **context7**: Official documentation lookup
- **grep_app**: Ultra-fast code search across public GitHub repos (great for finding implementation examples) - **grep_app**: Ultra-fast code search across public GitHub repos (great for finding implementation examples)
@@ -983,8 +984,9 @@ Available hooks: `todo-continuation-enforcer`, `context-window-monitor`, `sessio
### MCPs ### MCPs
Context7 and grep.app MCP enabled by default. Exa, Context7 and grep.app MCP enabled by default.
- **websearch**: Real-time web search powered by [Exa AI](https://exa.ai) - searches the web and returns relevant content
- **context7**: Fetches up-to-date official documentation for libraries - **context7**: Fetches up-to-date official documentation for libraries
- **grep_app**: Ultra-fast code search across millions of public GitHub repositories via [grep.app](https://grep.app) - **grep_app**: Ultra-fast code search across millions of public GitHub repositories via [grep.app](https://grep.app)
@@ -992,7 +994,7 @@ Don't want them? Disable via `disabled_mcps` in `~/.config/opencode/oh-my-openco
```json ```json
{ {
"disabled_mcps": ["context7", "grep_app"] "disabled_mcps": ["websearch", "context7", "grep_app"]
} }
``` ```

View File

@@ -10,9 +10,10 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps(disabledMcps) const result = createBuiltinMcps(disabledMcps)
//#then //#then
expect(result).toHaveProperty("websearch")
expect(result).toHaveProperty("context7") expect(result).toHaveProperty("context7")
expect(result).toHaveProperty("grep_app") expect(result).toHaveProperty("grep_app")
expect(Object.keys(result)).toHaveLength(2) expect(Object.keys(result)).toHaveLength(3)
}) })
test("should filter out disabled built-in MCPs", () => { test("should filter out disabled built-in MCPs", () => {
@@ -23,19 +24,21 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps(disabledMcps) const result = createBuiltinMcps(disabledMcps)
//#then //#then
expect(result).toHaveProperty("websearch")
expect(result).not.toHaveProperty("context7") expect(result).not.toHaveProperty("context7")
expect(result).toHaveProperty("grep_app") expect(result).toHaveProperty("grep_app")
expect(Object.keys(result)).toHaveLength(1) expect(Object.keys(result)).toHaveLength(2)
}) })
test("should filter out both built-in MCPs when both disabled", () => { test("should filter out all built-in MCPs when all disabled", () => {
//#given //#given
const disabledMcps = ["context7", "grep_app"] const disabledMcps = ["websearch", "context7", "grep_app"]
//#when //#when
const result = createBuiltinMcps(disabledMcps) const result = createBuiltinMcps(disabledMcps)
//#then //#then
expect(result).not.toHaveProperty("websearch")
expect(result).not.toHaveProperty("context7") expect(result).not.toHaveProperty("context7")
expect(result).not.toHaveProperty("grep_app") expect(result).not.toHaveProperty("grep_app")
expect(Object.keys(result)).toHaveLength(0) expect(Object.keys(result)).toHaveLength(0)
@@ -49,9 +52,10 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps(disabledMcps) const result = createBuiltinMcps(disabledMcps)
//#then //#then
expect(result).toHaveProperty("websearch")
expect(result).not.toHaveProperty("context7") expect(result).not.toHaveProperty("context7")
expect(result).toHaveProperty("grep_app") expect(result).toHaveProperty("grep_app")
expect(Object.keys(result)).toHaveLength(1) expect(Object.keys(result)).toHaveLength(2)
}) })
test("should handle empty disabled_mcps by default", () => { test("should handle empty disabled_mcps by default", () => {
@@ -60,9 +64,10 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps() const result = createBuiltinMcps()
//#then //#then
expect(result).toHaveProperty("websearch")
expect(result).toHaveProperty("context7") expect(result).toHaveProperty("context7")
expect(result).toHaveProperty("grep_app") expect(result).toHaveProperty("grep_app")
expect(Object.keys(result)).toHaveLength(2) expect(Object.keys(result)).toHaveLength(3)
}) })
test("should only filter built-in MCPs, ignoring unknown names", () => { test("should only filter built-in MCPs, ignoring unknown names", () => {
@@ -73,8 +78,9 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps(disabledMcps) const result = createBuiltinMcps(disabledMcps)
//#then //#then
expect(result).toHaveProperty("websearch")
expect(result).toHaveProperty("context7") expect(result).toHaveProperty("context7")
expect(result).toHaveProperty("grep_app") expect(result).toHaveProperty("grep_app")
expect(Object.keys(result)).toHaveLength(2) expect(Object.keys(result)).toHaveLength(3)
}) })
}) })

View File

@@ -1,3 +1,4 @@
import { websearch } from "./websearch"
import { context7 } from "./context7" import { context7 } from "./context7"
import { grep_app } from "./grep-app" import { grep_app } from "./grep-app"
import type { McpName } from "./types" import type { McpName } from "./types"
@@ -5,6 +6,7 @@ import type { McpName } from "./types"
export { McpNameSchema, type McpName } from "./types" export { McpNameSchema, type McpName } from "./types"
const allBuiltinMcps: Record<McpName, { type: "remote"; url: string; enabled: boolean }> = { const allBuiltinMcps: Record<McpName, { type: "remote"; url: string; enabled: boolean }> = {
websearch,
context7, context7,
grep_app, grep_app,
} }

View File

@@ -1,6 +1,6 @@
import { z } from "zod" import { z } from "zod"
export const McpNameSchema = z.enum(["context7", "grep_app"]) export const McpNameSchema = z.enum(["websearch", "context7", "grep_app"])
export type McpName = z.infer<typeof McpNameSchema> export type McpName = z.infer<typeof McpNameSchema>

5
src/mcp/websearch.ts Normal file
View File

@@ -0,0 +1,5 @@
export const websearch = {
type: "remote" as const,
url: "https://mcp.exa.ai/mcp?tools=web_search_exa",
enabled: true,
}