Skip to content
Open
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ print("All done!")

For installation instructions and detailed setup, see the [Getting Started Guide](https://docs.openhands.dev/sdk/getting-started).

### Codebase Search (Optional)

Add natural-language code search to your agent with [Morph's WarpGrep](https://morphllm.com). Requires a `MORPH_API_KEY` ([get one here](https://morphllm.com/dashboard/api-keys)) and Node.js 18+.

```python
from openhands.tools.codebase_search import register_codebase_search_tools

register_codebase_search_tools()

agent = Agent(
llm=llm,
tools=[
# ... your other tools ...
Tool(name="codebase_search"), # search local repos
Tool(name="github_codebase_search"), # search public GitHub repos
],
)
```

See [`examples/01_standalone_sdk/45_codebase_search.py`](examples/01_standalone_sdk/45_codebase_search.py) for a full working example.

## Documentation

For detailed documentation, tutorials, and API reference, visit:
Expand Down
53 changes: 53 additions & 0 deletions examples/01_standalone_sdk/45_codebase_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Codebase search using Morph's WarpGrep.

Natural-language code search backed by an LLM sub-agent that uses ripgrep,
file reads, and directory listing under the hood. Two tools are provided:

- ``codebase_search`` — search a local repository
- ``github_codebase_search`` — search a public GitHub repository

Requirements:
- MORPH_API_KEY : Get from https://morphllm.com/dashboard/api-keys
- LLM_API_KEY : Your LLM provider API key
- Node.js 18+ : Required by the MCP server (``npx``)
"""

import os

from openhands.sdk import LLM, Agent, Conversation, Tool
from openhands.tools.codebase_search import register_codebase_search_tools
from openhands.tools.file_editor import FileEditorTool
from openhands.tools.terminal import TerminalTool

# 1. Register the Morph search tools (explicit, not automatic)
register_codebase_search_tools()

# 2. Configure the LLM
llm = LLM(
model=os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929"),
api_key=os.getenv("LLM_API_KEY"),
base_url=os.getenv("LLM_BASE_URL", None),
)

# 3. Build the agent with search + editing tools
agent = Agent(
llm=llm,
tools=[
Tool(name=TerminalTool.name),
Tool(name=FileEditorTool.name),
# Morph search tools — pass api_key here or set MORPH_API_KEY env var
Tool(name="codebase_search"),
Tool(name="github_codebase_search"),
],
)

# 4. Run a conversation
cwd = os.getcwd()
conversation = Conversation(agent=agent, workspace=cwd)
conversation.send_message(
"Use codebase_search to find how errors are handled in this project, "
"then summarize what you found."
)
conversation.run()

print(f"EXAMPLE_COST: {llm.metrics.accumulated_cost}")
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,15 @@ RUN set -eux; \
rm -rf /var/lib/apt/lists/*

# Pre-install ACP servers for ACPAgent support (Claude Code + Codex)
# and Morph WarpGrep SDK for codebase_search / github_codebase_search tools.
# Install Node.js/npm if not present (SWE-bench base images may lack them)
RUN set -eux; \
if ! command -v npm >/dev/null 2>&1; then \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y --no-install-recommends nodejs && \
rm -rf /var/lib/apt/lists/*; \
fi; \
npm install -g @zed-industries/claude-agent-acp @zed-industries/codex-acp
npm install -g @zed-industries/claude-agent-acp @zed-industries/codex-acp @morphllm/morphsdk

# Configure Claude Code managed settings for headless operation:
# Allow all tool permissions (no human in the loop to approve).
Expand Down
2 changes: 2 additions & 0 deletions openhands-agent-server/openhands/agent_server/tool_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from fastapi import APIRouter

from openhands.sdk.tool.registry import list_registered_tools
from openhands.tools.codebase_search import register_codebase_search_tools
from openhands.tools.preset.default import register_default_tools
from openhands.tools.preset.gemini import register_gemini_tools
from openhands.tools.preset.planning import register_planning_tools
Expand All @@ -12,6 +13,7 @@
register_default_tools(enable_browser=True)
register_gemini_tools(enable_browser=True)
register_planning_tools()
register_codebase_search_tools()


# Tool listing
Expand Down
2 changes: 2 additions & 0 deletions openhands-tools/openhands/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from importlib.metadata import PackageNotFoundError, version

from openhands.tools.codebase_search import register_codebase_search_tools
from openhands.tools.delegate import DelegationVisualizer
from openhands.tools.file_editor import FileEditorTool
from openhands.tools.preset.default import (
Expand All @@ -39,6 +40,7 @@
__all__ = [
"__version__",
"DelegationVisualizer",
"register_codebase_search_tools",
"FileEditorTool",
"TaskTrackerTool",
"TerminalTool",
Expand Down
28 changes: 28 additions & 0 deletions openhands-tools/openhands/tools/codebase_search/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Codebase search tools powered by Morph's WarpGrep SDK.

Two tools are registered:

- ``codebase_search`` — search a local repository
- ``github_codebase_search`` — search a public GitHub repository

Requires ``MORPH_API_KEY`` (get one at https://morphllm.com/dashboard/api-keys),
Node.js 18+, and ``@morphllm/morphsdk`` (``npm install -g @morphllm/morphsdk``).
"""

from openhands.tools.codebase_search.definition import (
CodebaseSearchAction,
CodebaseSearchObservation,
CodebaseSearchTool,
GitHubCodebaseSearchAction,
GitHubCodebaseSearchTool,
register_codebase_search_tools,
)

__all__ = [
"CodebaseSearchAction",
"CodebaseSearchObservation",
"CodebaseSearchTool",
"GitHubCodebaseSearchAction",
"GitHubCodebaseSearchTool",
"register_codebase_search_tools",
]
52 changes: 52 additions & 0 deletions openhands-tools/openhands/tools/codebase_search/bridge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env node
/**
* Bridge script that wraps @morphllm/morphsdk for use from Python.
*
* Reads a single JSON object from stdin, calls the appropriate SDK method,
* and writes the result as JSON to stdout.
*
* Input schema:
* { "type": "local"|"github", "query": "...", "repo_path": "...", "owner_repo": "...", "github_url": "...", "branch": "..." }
*
* Output schema:
* { "success": bool, "contexts": [...], "summary": "...", "error": "..." }
*/

const { WarpGrepClient } = require("@morphllm/morphsdk/tools/warp-grep");

async function main() {
// Read JSON from stdin
const chunks = [];
for await (const chunk of process.stdin) chunks.push(chunk);
const input = JSON.parse(Buffer.concat(chunks).toString());

const client = new WarpGrepClient({
morphApiKey: process.env.MORPH_API_KEY,
morphApiUrl: process.env.MORPH_API_URL || undefined,
timeout: Number(process.env.MORPH_WARP_GREP_TIMEOUT) || undefined,
});

let result;
if (input.type === "github") {
const github = input.github_url || input.owner_repo || "";
result = await client.searchGitHub({
searchTerm: input.query,
github,
branch: input.branch || undefined,
});
} else {
result = await client.execute({
searchTerm: input.query,
repoRoot: input.repo_path || ".",
});
}

process.stdout.write(JSON.stringify(result));
}

main().catch((err) => {
process.stdout.write(
JSON.stringify({ success: false, error: err.message || String(err) })
);
process.exit(0); // exit clean so Python gets the JSON
});
Loading