> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parashell.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# grep

> Search the open CAD workspace with ripgrep or semgrep and get back the object that matched.

## Signature

```python theme={null}
grep(query: str, mode: str = "both", location: str = "*", limit: int = 50) -> list[TextContent]
```

Descriptor: `search the cad workspace with ripgrep or semgrep`

## Parameters

| Name       | Type  | Default  | Meaning                                                                                                                                                               |
| ---------- | ----- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query`    | `str` | required | The search itself. A ripgrep regular expression, a semgrep Python pattern, or in `both` mode a string that is handed to each engine as-is. Capped at 2000 characters. |
| `mode`     | `str` | `"both"` | `"ripgrep"`, `"semgrep"`, or `"both"`.                                                                                                                                |
| `location` | `str` | `"*"`    | A document name or label to restrict the search to, or `"*"` for every open document. Matching is case-insensitive and falls back to a substring match.               |
| `limit`    | `int` | `50`     | Maximum number of results returned, 1 to 200.                                                                                                                         |

## Response

A JSON object. The search-wide fields are:

| Field                 | Meaning                                                                 |
| --------------------- | ----------------------------------------------------------------------- |
| `elapsed_ms`          | Total wall time for the call.                                           |
| `index_ms`            | Time spent serializing the workspace into the searchable corpus.        |
| `search_ms`           | Time spent inside the engines.                                          |
| `engines_used`        | Engines that returned results.                                          |
| `engines_unavailable` | Requested engines whose executable could not be resolved.               |
| `engine_errors`       | Per-engine failure detail when one engine fails and the other succeeds. |
| `documents_searched`  | Document names that were indexed.                                       |
| `objects_indexed`     | Number of objects in the corpus.                                        |
| `match_count`         | Matches found before `limit` was applied.                               |
| `returned_count`      | Matches actually returned.                                              |
| `truncated`           | True when `match_count` exceeds `returned_count`.                       |
| `index_truncated`     | True when the workspace exceeded the corpus size cap.                   |
| `scan_capped`         | True when an engine reported more matches than the scan cap.            |

Each entry in `results` names the location that matched and how to read it back:

| Field                          | Meaning                                                                                                         |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `engine`                       | `ripgrep` or `semgrep`.                                                                                         |
| `document` / `document_label`  | The document holding the match.                                                                                 |
| `object_name` / `object_label` | The object holding the match. Empty for a document-level match.                                                 |
| `type_id`                      | FreeCAD TypeId, for example `Sketcher::SketchObject`.                                                           |
| `kind`                         | `object` or `document`.                                                                                         |
| `field_path`                   | Dotted path to the exact field, for example `Sketch.Geometry[1].radius` or `Properties.ExpressionEngine[0][1]`. |
| `line` / `end_line`            | Line span of the match inside the corpus record.                                                                |
| `match`                        | The matched corpus text.                                                                                        |
| `rule`                         | The semgrep `check_id`, empty for ripgrep.                                                                      |
| `retrieve`                     | An MCP tool call that pulls the real data for this location.                                                    |
| `retrieve_code`                | An `execute_code` snippet that pulls the real data for this location.                                           |

## Invocation

```json theme={null}
{
  "query": "Sketcher::\\w+",
  "mode": "ripgrep",
  "location": "*",
  "limit": 20
}
```

```json theme={null}
{
  "query": "{..., 'radius': $X, ...}",
  "mode": "semgrep",
  "location": "Bracket",
  "limit": 20
}
```

## Operational notes

Every open document is serialized on the Parashell main thread into one Python
record per object, written to a private temporary directory, searched, and then
deleted. The record is the same payload `get_object` returns, so anything that
tool can see is searchable — properties, placement, shape summary, sketch
geometry and constraints, and the `ExpressionEngine` entries that carry
parametric expressions. No object type receives special handling, so anything
the shared serializer cannot reach is not searchable either; a
`Spreadsheet::Sheet` exposes its cell grid through a property object rather than
plain values, so cell contents do not appear in the corpus.

Because records are emitted as Python literals, ripgrep sees them as text and
semgrep parses them as Python. Semgrep patterns therefore follow Python
structure: match a mapping entry as `{..., 'Radius': $X, ...}`, not
`'Radius': $X`. Semgrep does not return matched source text to anonymous
callers, so `match` is always read back from the corpus rather than from engine
output, and multi-line semgrep matches are joined into a single line.

Ripgrep queries are case-sensitive regular expressions and are passed as a
single argument, never through a shell. In `both` mode results are interleaved
between engines so one noisy engine cannot consume the whole `limit`.

Engine resolution is cached per process. Each engine gets a 60 second wall-clock
budget and indexing gets the standard RPC budget; exceeding either returns an
actionable error telling you to narrow `location` or simplify `query`. If one
engine fails while the other succeeds the call still succeeds and the failure is
reported in `engine_errors`.
