Skip to content
Draft
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
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Mixedbread-Grep",
"interface": {
"displayName": "Mixedbread"
},
"plugins": [
{
"name": "mgrep",
"source": {
"source": "local",
"path": "./plugins/mgrep"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,44 @@ These commands sign you in (if needed) and add Mixedbread `mgrep` support to the
agent. After that you only have to start the agent in your project folder, thats
it.

### Codex

With the Codex CLI installed, run:

```bash
mgrep install-codex
```

This authenticates with Mixedbread, adds the `Mixedbread-Grep` marketplace, and
installs `mgrep@Mixedbread-Grep`. The plugin includes an MCP server with a
read-only `search` tool and a short skill. The MCP server syncs the session's
project directory in the background while Codex runs.

A SessionStart hook adds search guidance on startup and resume, helping Codex
choose semantic search for conceptual or cross-file questions and rg or grep for
exact matches. On first start, Codex asks you to review the hook ("Hooks need
review"); you can also review it with `/hooks`. This one-time review allows the
hook to run. The hook only adds context; the MCP server owns background sync.

Use MCP `search` for conceptual questions; `path` is optional and defaults to the
session's project directory. Use rg or grep for exact strings, symbol names, and
regexes. The MCP server runs `mgrep mcp --expose-tools`. Searches within the
project wait for the watcher's initial sync. If the watcher cannot start, or the
search path is outside the project, the first search syncs that root once.

The plugin forwards `MXBAI_API_KEY` and `MXBAI_STORE` from Codex's environment.
You can also authenticate with `mgrep login`. No API key is written into plugin
configuration. To install from a local checkout or another Git URL, set
`MGREP_CODEX_MARKETPLACE` before running `mgrep install-codex`.

```bash
mgrep uninstall-codex
```

Uninstall removes the plugin, its marketplace, and the legacy MCP registration.
Both commands remove the old mgrep block from `$CODEX_HOME/AGENTS.md` and
`~/.codex/AGENTS.md`, preserving surrounding instructions.

### More Agents Coming Soon

More agents (Cursor, Windsurf, etc.) are on the way—this section will grow as soon as each integration lands.
Expand Down Expand Up @@ -245,6 +283,20 @@ mgrep watch --max-file-size 1048576 # limit uploads to files under 1MB
mgrep watch --max-file-count 5000 # limit sync to 5000 changed files or fewer
```

#### Multiple sessions

Watchers using the same store coordinate across sessions: the first started
watcher owns each directory tree. Later sessions covered by it stand by without
syncing or watching files, and take over when it exits. A parent started after a
child watcher leaves that child's subtree to the child, then takes it over if
the child exits.

A folder containing `.mgreprc.yaml` or `.mgreprc.yml` is a configuration boundary:
ancestor watchers always exclude it, so a session in that folder gets its own
watcher. Boundaries are computed when a watcher becomes active, including on
takeover; restart watchers after adding or removing local config files.
Dry runs skip coordination.

## Mixedbread under the hood

- Every file is pushed into a Mixedbread Store using the same SDK your apps get.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"build": "tsc",
"postbuild": "chmod +x dist/index.js && cp -r plugins dist/",
"dev": "npx tsc && node dist/index.js",
"test": "bats test/test.bats --filter-tags !long-running",
"test": "bats test/ --filter-tags !long-running",
"format": "biome check --write .",
"format:check": "biome check .",
"lint": "biome lint --check .",
Expand Down
17 changes: 17 additions & 0 deletions plugins/mgrep/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "mgrep",
"version": "0.1.13",
"description": "Search your local files using Mixedbread",
"author": {
"name": "Joel Dierkes"
},
"skills": "./codex/skills/",
"mcpServers": "./codex/mcp.json",
"hooks": "./codex/hooks.json",
"interface": {
"displayName": "mgrep",
"shortDescription": "Semantic search for your project files",
"developerName": "Mixedbread",
"category": "Developer Tools"
}
}
16 changes: 16 additions & 0 deletions plugins/mgrep/codex/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "python3 \"${CLAUDE_PLUGIN_ROOT:-$PLUGIN_ROOT}/codex/session_start.py\"",
"timeout": 5
}
]
}
]
}
}
9 changes: 9 additions & 0 deletions plugins/mgrep/codex/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mcpServers": {
"mgrep": {
"command": "mgrep",
"args": ["mcp", "--expose-tools"],
"env_vars": ["MXBAI_API_KEY", "MXBAI_STORE"]
}
}
}
46 changes: 46 additions & 0 deletions plugins/mgrep/codex/session_start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import json
import os
import sys
from pathlib import Path


def read_update_notice():
try:
cache_path = Path.home() / ".mgrep" / "update-check.json"
with open(cache_path, "r", encoding="utf-8") as handle:
cache = json.load(handle)
latest = cache.get("latestVersion")
if not isinstance(latest, str):
return ""
return f" An mgrep update is available (latest: {latest}). Tell the user to run `mgrep update` to upgrade."
except Exception:
return ""


def main():
try:
payload = json.load(sys.stdin)
except (ValueError, OSError):
payload = {}
cwd = payload.get("cwd") if isinstance(payload, dict) else None
if not isinstance(cwd, str) or not cwd or "\0" in cwd:
cwd = os.getcwd()
cwd = os.path.abspath(cwd)
context = (
"mgrep semantic search is available through the `search` tool of the mgrep MCP server. "
"Use it for conceptual or cross-file questions where you don't know the exact identifier, "
"such as 'where is authentication handled'. "
f"It searches {cwd} by default. "
"Use rg or grep for exact strings, symbol names and regexes. "
"Both are fine; pick whichever fits the question."
)
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": context + read_update_notice(),
}
}))


if __name__ == "__main__":
main()
28 changes: 28 additions & 0 deletions plugins/mgrep/codex/skills/mgrep/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: mgrep
description: Semantic search for conceptual questions about project files, alongside rg and grep for exact matches.
---

Use the mgrep MCP server's `search` tool for conceptual or cross-file questions
when you do not know the exact identifier, such as "where is authentication
handled". Provide a natural-language `query`; `path` is optional and defaults
to the session's project directory. Results contain file paths and line ranges;
read only those ranges. The MCP server keeps the session's project directory synced.

Use rg or grep for exact strings, symbol names, and regexes. Both approaches are
fine; choose whichever fits the question.

Outside MCP, use the CLI from your project directory:

```bash
mgrep search "how are failed uploads retried"
mgrep search --sync "where is authentication handled" ./src
mgrep search --web --answer "how does this API work"
```

Prefer the MCP tool inside Codex to avoid shell network approval prompts. The CLI
works in a terminal or when MCP is unavailable. Set `web` and `answer` to true
for an MCP web lookup. Use `maxCount` (CLI `-m`) to limit results.

`content: true` and CLI `-c` include full matching content and can produce large
output. Start with paths and line ranges, then read the relevant ranges.
2 changes: 1 addition & 1 deletion plugins/mgrep/hooks/mgrep_watch_kill.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def read_hook_input():
sys.exit(0)
debug_log(f"Killing mgrep watch process: {pid}")
try:
os.kill(pid, signal.SIGKILL)
os.kill(pid, signal.SIGTERM)
debug_log(f"Killed mgrep watch process: {pid}")
except ProcessLookupError:
debug_log(f"Process {pid} already exited")
Expand Down
Loading