Conversation
why: A single 626-line mcp.py is fine for two tools but blocks the planned domain coverage expansion. Per-domain tool modules with a register(mcp) dispatcher mirrors the libtmux-mcp pattern and keeps each module focused. what: - Replace src/agentgrep/mcp.py with the src/agentgrep/mcp/ subpackage. - Models, protocols, instructions, resources, prompts, and tools each get their own module. - tools/__init__.py exposes register_tools(mcp); server.py imports and calls it during build_mcp_server(). - Entry point agentgrep-mcp = agentgrep.mcp:main keeps working via the mcp/__init__.py re-export.
why: A single string blob makes it hard to extend instructions without churn. Composed _INSTR_* segments let downstream readers (clients, dashboards) scan section headers and let us add agent-context segments later without breaking the base set. what: - Split _build_instructions() into HEADER / SCOPE / SEARCH_VS_DISCOVERY / DEFAULTS / RESOURCES / PRIVACY segments. - Add a segment-presence test in tests/test_agentgrep_mcp.py.
why: The MCP server previously had no observability and no
response cap. A large search dump from a power user could OOM
a client; an exception inside a tool surfaced as a raw Python
traceback instead of an MCP error code. Adding the standard
FastMCP middleware quartet plus an agentgrep-flavored audit log
brings the server in line with libtmux-mcp's hardening level.
what:
- Add AgentgrepAuditMiddleware that logs agentgrep_tool,
agentgrep_outcome, agentgrep_duration_ms, and
agentgrep_args_summary; redacts terms and pattern to
{len, sha256_prefix}.
- Wire FastMCP's TimingMiddleware, ResponseLimitingMiddleware
(512KB cap), and ErrorHandlingMiddleware(transform_errors=True).
- Tests cover audit extras, pattern redaction, and middleware
wiring assertion.
why: The MCP server only exposed search and find — leaving every other library capability invisible to MCP clients. Full domain coverage means clients can introspect the store catalog, validate regex before running it, summarize what's discoverable, and inspect adapter samples without dropping back to the CLI. what: - list_stores / get_store_descriptor wrap the StoreCatalog (catalog_tools). - list_sources / filter_sources / summarize_discovery layer structured filters on top of discover_sources (discovery_tools). - validate_query exposes matches_text for cheap pre-flight regex checks (diagnostic_tools). - recent_sessions filters discovered sources by mtime (search_tools). - inspect_record_sample returns first-N records from a named adapter + path for schema validation (catalog_tools). - Each tool runs through asyncio.to_thread to keep the event loop unblocked. - Eleven new test cases plus the lockstep tool-count assertion bumped from 2 to 10.
why: Clients that want to reason about the on-disk layout without spawning a search shouldn't have to scrape the docs site. Surfacing the StoreCatalog plus its supporting enums as resources gives agents a self-describing data dictionary. what: - agentgrep://catalog returns the full StoreCatalog model. - agentgrep://store-roles and agentgrep://store-formats enumerate enum members with one-line descriptions. - CapabilitiesModel.resources is updated; the lockstep test covers the new URIs.
why: sphinx_autodoc_fastmcp introspects the docs-only shim, not the live server, so new tools stayed invisible to the docs site until we mirror their signatures and metadata here. what: - Add docs-only shims for the eight new tools (list_stores, get_store_descriptor, list_sources, filter_sources, summarize_discovery, validate_query, recent_sessions, inspect_record_sample). - Each shim carries the Pydantic Field annotations the live tool uses, plus examples. - Update fastmcp_model_classes in docs/conf.py with the new request and response models, and add Catalog/Diagnostic to the section badge map. - Add the new directives to docs/mcp/tools.md, picking H2 titles that don't collide with the tool slug anchors. - Document the three new resources in docs/mcp/resources.md.
…ions why: The Packages > agentgrep nesting was premature scaffolding for a future package split that never materialized. Readers care about the library and the MCP server as products, not as members of a packages collection. Promoting both to top-level sidebar sections puts them at the same visual weight as Get started and Reference. what: - git mv docs/packages/agentgrep/*.md to docs/library/*.md. - Delete docs/packages/ entirely. - Rebuild the top-level toctrees in docs/index.md with explicit captions in this order: Get started, Library, MCP, Reference, Project. - Repoint the landing-page Library card and quickstart 'Next steps' link to library/*. - docs/redirects.txt redirects /packages/agentgrep/* to /library/* (dirhtml form, no .html suffix).
…btmux-mcp
why: We want a polished, tabbed install widget on the MCP landing
page rather than scattered install snippets. Building the widget
infrastructure from scratch duplicates work already proven in
libtmux-mcp — its autodiscovery + Jinja + prehydrate + asset-copy
pipeline is exactly what we need, and lifting it verbatim keeps
both projects on the same widget contract.
The framework alone won't load (the prehydrate hook imports from
mcp_install at module-import time), so the widget and framework
land in the same commit. Asset retrofits cover the storage-key
namespace, the CSS class prefix lm-/ag-, and the per-CLI install
commands which target ``agentgrep-mcp`` inside the ``agentgrep``
PyPI package via ``--from`` (uvx) / ``--spec`` (pipx).
what:
- Copy docs/_ext/widgets/{__init__,_base,_directive,_assets,
_discovery,_prehydrate}.py verbatim from libtmux-mcp.
- Copy docs/_ext/widgets/mcp_install.py and retrofit
install commands, JSON/TOML bodies, server slug from
tmux/libtmux-mcp to agentgrep.
- Copy docs/_widgets/mcp-install/{widget.html,widget.js,
widget.css}; rename lm- prefix to ag- and storage namespace
to agentgrep.mcp-install.*.
- Register docs._ext.widgets in conf.py extra_extensions.
- Embed {mcp-install} at the top of docs/mcp/index.md.
why: The library landing page should present install + import +
first-search as a single tabbed onboarding panel, not as three
scattered code blocks across a tutorial page. Mirrors the MCP
install widget so readers see consistent affordances across the
docs site.
what:
- Add docs/_ext/widgets/library_install.py with four methods
(uvx run / pipx run / uv add / pip install) and an embedded
Python quickstart that drives run_search_query end-to-end.
- Add docs/_widgets/library-install/{widget.html,widget.js,
widget.css}. Storage namespace: agentgrep.library-install.method.
- Extend _prehydrate to inject a small <script> + the
inject_library_install_prehydrate hook so the saved method
paints without FOUC; wire both hooks in widgets/__init__.py.
- Embed {library-install} at the top of docs/library/index.md.
- tests/test_widgets.py smoke-tests both widgets render and ship
their CSS/JS assets to _static/widgets/<name>/.
why: Iterating on the MCP server required hand-editing four CLI config files (Claude, Codex, Cursor, Gemini) to point at the local checkout, then reverting them before testing release behavior. The script lifted from libtmux-mcp automates both directions with timestamped backups and LIFO undo, eliminating the friction that's been blocking rapid MCP iteration. what: - Copy scripts/mcp_swap.py from libtmux-mcp (PEP 723 standalone, requires tomlkit). - Retrofit STATE_DIR namespace from libtmux-mcp-dev to agentgrep-dev and update docstrings to clarify the slug-strip rule passes the ``agentgrep`` name through unchanged. - Tighten an except clause from the Python 3.14-only relaxed syntax (``except E1, E2:``) to the parenthesised form so the script runs under the >=3.10 declared in its PEP 723 header. - Add justfile recipes: mcp-detect / mcp-status / mcp-use-local / mcp-revert.
why: The lifted script is dev-critical — a regression that corrupts a contributor's Claude / Codex / Cursor / Gemini config is a worst-case bug. The libtmux-mcp test suite already exercises every command, scope, and revert path; lifting it gives us the same coverage on day one. what: - Copy tests/test_mcp_swap.py from libtmux-mcp and retrofit the fake-repo pyproject to use ``agentgrep-mcp`` so the slug-strip test exercises the ``agentgrep-mcp`` -> ``agentgrep`` mapping agentgrep contributors will actually see. - Add ``tomlkit`` to the dev dependency group so the test file imports outside the PEP 723 standalone path. - Bump the script's PEP 723 ``requires-python`` from 3.10 to 3.14 to match the project's declared floor and silence ruff's preference for the bare ``except E1, E2:`` form. - Add an explicit ``import tomlkit.items`` so ty doesn't warn about the implicit submodule reference in the isinstance check.
why: GitHub and PyPI both render the top-level README; the project shipped without one. A short README with the one-paragraph pitch, install snippet, and links to the docs site closes the gap without duplicating the install widget content. what: - Add README.md with the project pitch, a single-client install snippet, a library quickstart, and links to docs, source, and issues.
why: The unreleased entry was a placeholder. Documenting the new tools, widgets, sidebar reorg, and dev tooling in user vocabulary lets future readers (and future-me preparing a release) reconstruct what shipped without paging through commits. what: - Append #### What's new sections covering the eight new MCP tools, the three new resources, the FastMCP middleware hardening, the MCP install widget, the library install + quickstart widget, and the sidebar reorg. - Append #### Development sections for the lifted mcp_swap.py, the mcp/ subpackage refactor, and the new top-level README.
why: The MCP install widget already ships on the MCP landing
page but the Sphinx homepage shows only the warning and card
grid — readers have to click through before they see an
install affordance. libtmux-mcp embeds the same widget on
its homepage with :variant: compact between the warning and
the grid; mirroring that shape gives agentgrep readers a
one-step install path before they pick a docs section.
what:
- Insert {mcp-install} :variant: compact between the
pre-alpha warning and the grid in docs/index.md.
why: Method tabs and cooldown controls on the install widget did nothing when clicked. Root cause: widget.js never mutates the [hidden] attribute on panels — it relies entirely on the CSS rules emitted by _prehydrate.py to drive panel visibility from <html data-mcp-install-*> attributes. Those rules still keyed on .lm-mcp-install__panel selectors from the original libtmux-mcp lift, which never match agentgrep's .ag-mcp-install__panel HTML, so flipping the html attribute did nothing visually. what: - Rename lm-mcp-install -> ag-mcp-install across _prehydrate.py and the cooldown-days slot spans in _base.py. - After this fix the prehydrate <style> block in the rendered HTML uses ag-mcp-install__panel everywhere, and tab switching actually drives panel display.
why: libtmux-mcp ships three small static assets that polish sphinx-design card titles, style prompt-style admonitions, and preserve inline markdown when sphinx-copybutton copies a prompt block. agentgrep was missing all three; lifting them brings the two projects to parity and quietly fixes the Furo border quirk on any future card title that contains a <code> chip. what: - Copy docs/_static/css/project-cards.css verbatim — extends Furo's <code> chip border rule to .sd-card-title contexts. - Copy docs/_static/css/project-admonitions.css verbatim — styles .admonition.prompt, .admonition.agent-thought, and the system-prompt / server-prompt code panels. - Copy docs/_static/js/prompt-copy.js verbatim — intercepts sphinx-copybutton clicks on .admonition.prompt blocks and reconstructs inline backtick markdown for clipboard write. - Register all three from conf.py setup() with the same add_js_file(..., loading_method="defer") and add_css_file call shape libtmux-mcp uses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
list_stores,get_store_descriptor,inspect_record_sample,list_sources,filter_sources,summarize_discovery,recent_sessions,validate_query.agentgrep://catalog,agentgrep://store-roles,agentgrep://store-formats) so MCP clients have a self-describing data dictionary for the on-disk layout.TimingMiddleware+ResponseLimitingMiddleware(512 KB cap) +ErrorHandlingMiddleware, plus anAgentgrepAuditMiddlewarethat logs every tool call withagentgrep_*structuredextrakeys and redactsterms/pattern/sample_textto{len, sha256_prefix}.src/agentgrep/mcp.pyinto amcp/subpackage with per-domain tool modules; theagentgrep-mcp = "agentgrep.mcp:main"entry point is preserved through__init__.pyre-exports.docs/packages/is gone and/packages/agentgrep/*URLs redirect to/library/*.libtmux-mcp.scripts/mcp_swap.pyso contributors can swap installed Claude / Codex / Cursor / Gemini configs between pinned releases and the local checkout, with timestamped backups and LIFO revert.Changes by area
MCP server (
src/agentgrep/mcp/)mcp/__init__.py+mcp/server.py+mcp/_library.py+mcp/models.py+mcp/instructions.py+mcp/middleware.py+mcp/prompts.py+mcp/resources.py: split of the former flat module._library.pycarries the protocol-typed view of the parentagentgreppackage and the dynamicimportlib.import_module("agentgrep")cast, plus the sharedAgentName/AgentSelector/SearchTypeNamealiases and theREADONLY_TAGS/RESOURCE_ANNOTATIONSconstants.mcp/tools/{search,discovery,catalog,diagnostic}_tools.py: per-domain tool modules, each exposing aregister(mcp)function thetools/__init__.py:register_toolsdispatcher calls. Mirrorslibtmux-mcp's pane/window/session split.mcp/middleware.py:AgentgrepAuditMiddlewareemits one record per tool call withagentgrep_tool,agentgrep_outcome,agentgrep_duration_ms,agentgrep_error_type(on failure),agentgrep_client_id,agentgrep_request_id, and a redactedagentgrep_args_summary. Sensitive scalar payloads become{len, sha256_prefix}; sensitive list payloads digest each element.mcp/instructions.py:_build_instructions()returns segments joined with double newlines —_INSTR_HEADER/_INSTR_SCOPE/_INSTR_SEARCH_VS_DISCOVERY/_INSTR_DEFAULTS/_INSTR_RESOURCES/_INSTR_PRIVACY.New tools
list_storesStoreCatalogwith optional agent + role filtersget_store_descriptorinspect_record_samplelist_sourcespath_kind_filter/source_kind_filterfilter_sourcessummarize_discoveryrecent_sessionsvalidate_queryNew resources
agentgrep://catalogStoreCatalogpayload (role, format, upstream ref, schema notes per entry)agentgrep://store-rolesStoreRoleenum members with one-line descriptionsagentgrep://store-formatsStoreFormatenum members with one-line descriptionsDocs (
docs/)docs/_ext/widgets/(autodiscovery framework) +docs/_widgets/{mcp-install,library-install}/{widget.html,widget.js,widget.css}(assets). The framework's_prehydrate.pyemits an inline<head>script that mirrors saved selections fromlocalStorageonto<html data-*>attributes before first paint, eliminating FOUC across gp-sphinx SPA navigation.docs/library/{index,tutorial,how-to,reference,examples}.md: moved verbatim fromdocs/packages/agentgrep/viagit mv.docs/packages/is deleted;docs/redirects.txtcovers the six old URL paths in dirhtml form (no.htmlsuffix).docs/index.md: top-level toctrees re-ordered with explicit captions in the order Get started → Library → MCP → Reference → Project.docs/_ext/agentgrep_fastmcp.py: docs-only signature shims for the eight new tools, with PydanticFieldannotations andexamples=[...]for each parameter.__fastmcp__metadata attached for sphinx-autodoc-fastmcp.docs/mcp/{tools,resources}.md: each new tool gets a{fastmcp-tool} <name>+{fastmcp-tool-input} <name>pair under an H2 that doesn't collide with the tool slug; each new resource gets a{fastmcp-resource} <name>block with prose.docs/conf.py:extra_extensionsregistersdocs._ext.widgets;fastmcp_model_classeslists the fifteen new request/response models;fastmcp_section_badge_mapgainsCatalogandDiagnostic.Dev workflow
scripts/mcp_swap.py: PEP 723 standalone (only third-party dep istomlkit) lifted fromlibtmux-mcp. Defaults derive the server slug frompyproject.toml(-mcpsuffix stripped —agentgreppasses through unchanged) and the entry command from the first[project.scripts]key. State lives at$XDG_STATE_HOME/agentgrep-dev/swap/state.json; backups sit beside each config file with a.bak.mcp-swap-<timestamp>suffix.justfile: newmcp-swapgroup withmcp-detect/mcp-status/mcp-use-local/mcp-revertrecipes.Top-level
README.md: new top-level README for GitHub and PyPI. Single-client install snippet (Claude Code) plus the library quickstart, with the install widget on agentgrep.org cited as the canonical multi-client picker.CHANGES: unreleased entry covering the deliverables in user vocabulary, with### What's newand### Developmentsubheadings per the changelog conventions.pyproject.toml:tomlkit>=0.13added to the dev group sotests/test_mcp_swap.pycan import outside the PEP 723 path; no runtime dep change.Design decisions
Subpackage over flat module: a 625-line file was fine for two tools but would have pushed past 1500 with eight more tools, their request/response models, and middleware. Per-domain modules under
tools/keep each file focused and match thelibtmux-mcppattern this repo's MCP work is modelled on.Audit via
loggingextra=, not f-string message: per AGENTS.md, structured fields belong inextraso JSON formatters can index them (agentgrep_tool="search") rather than being trapped inside a message template. The message stays a short stable string ("tool call completed"), and aggregators can group by template signature without each invocation becoming a unique signature.No Safety / Retry middleware: every tool is read-only, so
libtmux-mcp's safety-tier gating doesn't apply.RetryMiddlewareexists upstream to recover from libtmux socket transients; agentgrep's blocking backends (subprocessrg/jq, SQLite opened read-only) don't fail with analogous transient errors, so retrying would only mask real bugs.TimingMiddleware,ResponseLimitingMiddleware, andErrorHandlingMiddlewarestay because they're tool-shape-independent hardening.Combine widget-framework + MCP-install widget in one commit:
_prehydrate.pyimports frommcp_install.pyat module-import time, so registering the framework's Sphinx extension alone would break the build until the install widget lands. Bundling them is the smallest atomic unit that keeps every gate green.agentgrep-mcpinvocation via--from agentgrep: the MCP server ships as a[project.scripts]console script inside theagentgrepPyPI distribution, not as a separate package.uvx --from agentgrep agentgrep-mcpandpipx run --spec agentgrep agentgrep-mcpinstall the package then invoke the matching script name.Library widget skips the scope/cooldown axes: library users are pulling the package into their own project; they don't have an
mcpServers.<slug>registration target and their cooldown policy is whatever uv / pipx / pip is already configured with. One axis (method) keeps the picker focused on the actual decision.Page-level MyST anchors kept as
package-agentgrep-*: per-page anchors indocs/library/{tutorial,how-to,reference,examples}.mdare self-referential — nothing else in the docs cross-references them — so renaming tolibrary-*would be cosmetic churn and add rebase risk to any in-flight work that references them.Verification
The widget framework should have no stale
libtmuxreferences after the lift + retrofit:$ rg -n 'libtmux' docs/_ext/widgets/ docs/_widgets/The capabilities resource should advertise exactly the tools and resources the server registers:
$ uv run pytest tests/test_agentgrep_mcp.py::test_mcp_lists_tools_resources_prompts_and_templates tests/test_agentgrep_mcp.py::test_mcp_capabilities_advertises_new_resources -vThe pre-commit gate (gating every commit on this branch):
$ rm -rf docs/_build$ uv run ruff check . --fix --show-fixes$ uv run ruff format .$ uv run ty check$ uv run py.test --reruns 0 -vvv$ just build-docsTest plan
test_mcp_lists_tools_resources_prompts_and_templates— lockstep on the full tool surface (search, find, list_sources, filter_sources, summarize_discovery, list_stores, get_store_descriptor, inspect_record_sample, validate_query, recent_sessions)test_mcp_instructions_carry_every_segment_header— guards against accidental_INSTR_*segment deletiontest_audit_middleware_emits_extras+test_audit_middleware_redacts_pattern— structured audit log shape + sensitive-payload redactiontest_response_limit_middleware_is_wired—ResponseLimitingMiddlewareandAgentgrepAuditMiddlewareare both present on the built servertest_mcp_list_stores_returns_catalog_entries+test_mcp_list_stores_filters_by_agent+test_mcp_get_store_descriptor_known_and_unknown+test_mcp_inspect_record_sample_unknown_path+test_mcp_inspect_record_sample_returns_codex_history— catalog tools happy path and error pathstest_mcp_list_sources_with_filters+test_mcp_filter_sources_requires_pattern+test_mcp_summarize_discovery_totals_match_list_sources— discovery toolstest_mcp_validate_query_invalid_regex+test_mcp_validate_query_substring_match— diagnostic regex parsing and literal-substring matchingtest_mcp_recent_sessions_filters_by_mtime— mtime cutoff arithmetic on a backdated fixturetest_mcp_catalog_resource_returns_full_catalog+test_mcp_store_roles_resource+test_mcp_store_formats_resource+test_mcp_capabilities_advertises_new_resources— three new resources land + capabilities advertises themtests/test_widgets.py::test_widgets_render_in_built_docs+tests/test_widgets.py::test_widget_assets_copied— both widgets emit their class hooks and ship CSS/JS to_static/widgets/<name>/tests/test_mcp_swap.py— full swap/revert lifecycle across Claude (user + project scope), Codex, Cursor, Gemini; LIFO revert ordering; env-preservation on replacements; dry-run output stabilityjust mcp-detect && just mcp-status && just mcp-use-local --entry agentgrep-mcpagainst a real~/.claude.json/~/.codex/config.toml/~/.cursor/mcp.json/~/.gemini/settings.jsonround-trip