Seek
Fast file search for AI agents on Windows.
An MCP server that finds any file by name on every drive in milliseconds, searches text in folders and documents faster than ripgrep and fff, and knows what you were working on. Its answers are a few hundred tokens, not a few hundred thousand.
Install Source on GitHubInstall
One file, no dependencies, no administrator rights.
irm https://raw.githubusercontent.com/SynrgStudio/seek/main/install.ps1 | iex
In PowerShell. It downloads seek.exe from the latest release into %LOCALAPPDATA%\Programs\Seek, checks it against the release's SHA-256, and adds it to Claude Code and Codex when they are installed. Running it again updates Seek. For other clients, see setup.
1Summary
Searching for files is a large part of what coding agents do, and they do it with tools made for people.
An agent that needs a file it doesn't know the place of runs dir /s or Get-ChildItem -Recurse, waits a minute or more, and reads a list that can be hundreds of thousands of tokens long. To search text it runs ripgrep, which starts from zero every time. To guess which file the user means by "the one I was working on", it has nothing to go on.
Seek is a small MCP server for Windows that answers those three questions, and it keeps its answers short:
findlooks every name up in an index of every fixed drive. It takes milliseconds, forgives typos, and ranks what the user touched lately first.grepsearches the text in a folder's files, and in its PDF and Office documents. It follows.gitignore, and after the first search it keeps the folder in memory.recenttells what the user created, changed and opened lately.
One background process serves every agent at once. It keeps the index, watches the drives, and remembers what changed.
On the author's PC it was compared with Everything, fff, ripgrep, fd, git grep, Windows Search and PowerShell. Seek was the fastest in every comparison where it does the same job: names on every drive, text in a folder, and names in a folder. Where it was close, it was close to tools that keep everything in memory, and Seek does that too, with one copy shared by every agent.
The sections below show how it was measured, why the numbers come out that way, and what the other tools still do better.
2The problem: what a search costs an agent
The search itself is rarely the cost. The cost is the round trips, the tokens and the guessing.
Time
A recursive listing of a user profile takes a minute and a half in PowerShell. Of three drives, ten minutes. The agent waits, and so does the person.
Tokens
Every line a command prints is read by the model. Listing the 8,200 .blend files on one PC is about 196,000 tokens: more than many models' whole context, to answer "how many and where".
Round trips
A tool that knows nothing answers in pieces: list, filter, list again, open. Each step is a model turn of several seconds.
Guessing
"The file I was working on" means something to the user and nothing to a shell. The agent asks, or guesses wrong.
The tools agents have don't fit either. ripgrep is the fastest way to search text without an index, but each run starts over: on a 500,000-file checkout fff's authors measured 3 to 9 seconds per run. Claude Code's own Glob runs ripgrep without skipping ignored folders, and users report it timing out at 20 seconds in large repositories, where ripgrep alone takes under one. Everything finds names instantly, but it needs a service or administrator rights, doesn't forgive a typo, and answers with the whole list.
3The tools
Three tools, all read-only. Each answer says why each result is there, and long answers are cut, with a note on how to see more.
find: files and folders by name
On every fixed drive, or below path. Fuzzy and typo-tolerant, with Jolt's matching. Exact names and programs rank first, then what the user changed lately, what is in folders they use in Jolt, and what is inside near. Caches and package stores rank last. summary answers with counts by area, folder, extension and drive instead of a list.
Query: words appear in the name in order · .png or ext:png,jpg for the extension · * and ? as wildcards · !word leaves names out · type:file or type:folder · a fragment with a slash must be in the path.
find {"query": "fuzy.inl"} ← the typo is on purpose
find "fuzy.inl" on all drives: 3 matches:
C:\Users\me\Projects\jolt\src\fuzzy.inl · 11 KB · modified 9 h ago · in a folder used in Jolt
C:\Users\me\Backups\jolt-old\src\fuzzy.inl · 3.4 KB · modified 2 days ago
…
find {"query": ".blend", "summary": true}
find ".blend" on all drives: 8,200 matches.
By area:
3754 D:\Art\Blender Stuff
1907 E:\Art\Blender Stuff\DecalMachine Assets
1666 E:\Art\Blender Stuff\KitOps
… and 111 more areas
Most in one folder:
64 D:\Art\Blender Stuff\KitOps\Hard Surface
By extension: .blend 8,200
By drive: D: 3,958 E: 3,973 C: 269
grep: text inside files
In a folder or a single file. It reads code and text in UTF-8, UTF-16 or the system code page, and documents through the text filters Windows has. PDF and RTF filters come with Windows; Word and Excel need Office or its filter pack. It follows .gitignore, .git\info\exclude, the user's global excludes and .ignore, and it skips dependency folders, binaries and files that live only in the cloud. ignored: true searches all of those too.
The pattern is literal by default, and regex makes it a pattern. The search ignores case unless the pattern has capitals. glob, context, limit, offset and summary shape the answer. With no match in the exact case, grep retries ignoring case. With no text match at all, it suggests file names that look like the pattern.
grep {"pattern": "RecTake", "path": "C:\\Users\\me\\Projects\\jolt"}
grep "RecTake" in C:\Users\me\Projects\jolt: 3 matching lines in 2 files (searched 52 files, kept in memory).
src\app.cpp
1335- void StopRecursive(Tab& t, bool rebuild);
1336: bool RecTake(Tab& t, const std::shared_ptr<const Listing>& l); // search.inl
1337- std::wstring g_renameAfterSelect;
src\search.inl
…
recent: what the user is working on
The files the user created or changed lately and the folders they opened in Jolt, newest first. It covers the whole machine from what Seek saw change, kept between runs. With path it also reads the dates on disk there. query filters names and since sets how far back to look. System places, programs' dot folders, logs and build outputs are left out unless include_system is set.
recent {"limit": 4}
recent on all drives, since 7 days ago, newest first:
C:\Users\me\Projects\slicer\builds\2026-11-07\ · folder opened in Jolt 6 min ago
C:\Users\me\Projects\seek\src\grep.inl · changed 1 h ago · 22 KB
C:\Users\me\Projects\seek\src\util.inl · changed 1 h ago · 13 KB
C:\Users\me\Documents\notes\ideas.md · changed 2 h ago · 4 KB
4Results at a glance
Times are medians, on a log scale: each line on the grid is ten times the one before. Further left is faster.
5How it was measured
On one PC, with every tool doing the same job the way an agent would run it.
- The PC: a Ryzen 7 5800X with 8 cores, 32 GB of memory, two NVMe SSDs and a 1 TB hard disk, on Windows 11 Pro. Three drives hold 4.9 million files and folders.
- The versions: Seek 0.1.0, Everything 1.4.1.1032 with es 1.1.0.38, fff-mcp 0.11.0, ripgrep 15.2.0, fd 10.5.2, git 2.55.0 and Windows PowerShell 5.1.
- MCP servers, Seek and fff, ran warm, the way an agent's session keeps them, and were timed from request to answer over stdio.
- Command-line tools, Everything's
es.exe, ripgrep, fd, git and PowerShell, were started once per search, the way an agent's shell runs them. Their times include starting the process, because the agent pays for that. - Times are medians of five runs, three for the slow ones. Before timing, every tool searched once, so the files were in Windows' cache for all of them. Walks of every drive ran once each, on a warm cache.
- Tokens are the characters of the answer the agent reads, divided by four.
Anyone can repeat this: python -I bench\bench.py --walkers times Seek against whatever of these tools it finds, and the raw numbers behind this page are in results.json. The rows for dir, Get-ChildItem, Select-String and findstr come from an earlier run on November 7, 2026, because a ten-minute walk isn't worth repeating.
6Names, on every drive
Find one file whose place is unknown, the same with a typo, and count a kind of file.
| tool | find fuzzy.inl | with a typo | count .blend, and where | tokens of that answer |
|---|---|---|---|---|
| Seek | 52 ms | yes · 57 ms | 55 ms | 378 |
| Everything | 93 ms | no | 74 ms for the count; 100 ms for the list | 196,438 for the list |
| Windows Search | 233 ms, didn't find it | – | 249 ms, 9 of 8,200 | 129 |
| fd, folders in memory | 4.7 s | no | 5.0 s | 194,388 |
| ripgrep --files, folders in memory | 5.8 s | no | 5.5 s | 194,388 |
| fd, from disk | over 15 min | – | – | – |
| dir /s, the profile only | 85 s | no | 567 s, three drives | 104,157 |
| Get-ChildItem -Recurse, the profile only | 98 s | no | – | – |
fd and ripgrep walk the drives every time. With the folders in Windows' memory they take seconds; read from disk, fd didn't finish in 15 minutes because of the hard disk.
Why Seek is fast here. It doesn't walk anything: every name on every drive is already in memory, in the index Jolt keeps, or in one Seek builds itself (see the drive index). A search runs every name through Jolt's fuzzy matching on 8 threads, and keeps the best few hundred in a heap. Everything is as fast at the lookup itself; most of its time here is starting es.exe, which an agent can't avoid without an MCP server for it.
Why the answer is short. Counting 8,200 files and saying where they are is a summary Seek computes, not a list the model has to read. The difference is 378 tokens against 196,438.
What changed lately
| tool | time | what it answers |
|---|---|---|
| Seek recent | 9.1 ms | the files the user changed and the folders they opened, with system churn left out |
| Everything, newest 10 | 105 ms | the newest files on the PC: logs, caches and browser data |
Every tool that sorts by date answers "newest" with what programs write all the time. Seek watches the drives itself, so it knows what was created, changed, renamed and deleted, and when. It leaves out system places and churn, and it adds the folders the user opened in Jolt.
7Text, in folders
Search a word in one repository and in a folder of many projects, and find a name inside the repository.
| tool | text, Jolt's repository, 52 files | text, every project, 10,700 files | a name in the repository | with a typo |
|---|---|---|---|---|
| Seek, again | 0.40 ms | 11 ms | 0.33 ms | 0.40 ms |
| fff | 0.61 ms | 36 ms | 0.43 ms | 0.44 ms |
| Seek, first search | 3.2 ms | 132 ms | – | – |
| git grep | 35 ms | – | – | – |
| ripgrep | 29 ms | 487 ms | – | – |
ripgrep is what Claude Code's Grep tool runs. "Seek, first search" reads the folder from the drive. After it, the background process keeps the folder in memory, and "Seek, again" is every search after that. fff, in the same way, answers from the index it built when its session started.
The first search is already faster than ripgrep:
- the folders are listed on 12 threads;
- each file is checked for the pattern's bytes before any text is decoded;
- a binary file is recognized by its first 8 KB;
- the ignore rules are looked up rather than matched one by one.
Every search after that reads no file at all: the folder's text is in memory, kept current by the same watchers that feed recent. The search runs on 12 threads, each looking for the pattern's rarest character with memchr and comparing only around it.
Against fff, which is built for this, Seek is faster in every row, and the gap grows with the folder. In the small repository both answer in under a millisecond, and most of that is the trip from the client to the server and back. In every project, Seek answers in 11 ms against fff's 36 ms. fff's documentation explains the design it shares with Seek: a resident index, so that no search pays for reading the drive.
8Starting, and memory
| ready to answer | memory of its own | |
|---|---|---|
| Seek | 296 ms, for every drive, from Jolt's index | 328 MB with every project kept in memory; one process for every agent |
| Seek without Jolt | 18 s for the 2.1 million names on an NVMe SSD, 3.8 s for the 1.3 million on another, and 19.5 minutes for the 1.4 million on a hard disk read cold | the same |
| Everything | always running, as a service | 493 MB |
| fff | 110 ms for the repository, 246 ms for every project | 114 MB for the repository, 201 MB for every project; one process per agent session and folder |
The numbers that matter for memory are how many copies there are, and who pays for them. Each agent session starts its own fff, with its own index of its own folder. Three sessions in the same folder are three copies. Seek is one background process for every agent: one index of every drive and one copy of each folder kept in memory, up to 512 MB of folders.
A folder nobody searched for 20 minutes is let go, and cache = 0 in the settings turns this off. Without folders in memory, Seek uses 32 MB of its own. Its index files are mapped from disk, so Windows reads them in and drops them as it needs.
9Tool by tool: why Seek wins, and what each does better
Everything
Why Seek is ahead: on time, it's even, since both answer from an index in memory, and the difference measured here is mostly starting es.exe. Seek pulls ahead on what the agent gets: typo-tolerant matching, ranking by what the user is doing, summaries instead of lists, text search, and no service or administrator rights. Its recent leaves out churn and private places.
What Everything does better: it indexes NTFS from the file table in seconds and updates it from the change journal, it works for people with no agent around, and it has a rich query language, a GUI, and network and FAT drives.
fff
Why Seek is ahead: both keep a folder's contents in memory and answer from there. Seek's search is faster: it runs on 12 threads, and each one looks for the pattern's rarest byte with memchr. Seek also covers the whole PC rather than one folder, which fff refuses for the home folder and the drives' roots. It reads PDFs and Office documents, knows what changed on the machine, and keeps one copy for every agent.
What fff does better: it ranks by how often files are opened and by git status. It can return only the definitions of a name, and it searches several identifiers in one call with SIMD Aho-Corasick. It runs on Linux and macOS, and plugs into Neovim. Its own benchmark is honest about the limits too: an agent's whole task got faster with it, but the tokens barely moved.
ripgrep, and Claude Code's Grep
Why Seek is ahead: ripgrep starts from zero on every run, which is its design. In a small repository most of its time is starting the process. In a big folder, Seek's first search was already faster, and the next ones read nothing from the drive.
What ripgrep does better: it needs nothing running and no memory, it is on every developer's machine, and its regex engine is far faster than the standard library's one that Seek uses for regex: true.
fd, dir /s, Get-ChildItem, Windows Search
fd is a fast walker. With the folders in Windows' memory it walks three drives in seconds; read from disk it takes as long as the disks do. dir /s and Get-ChildItem are what agents use by default, and they took a minute and a half for one user profile.
Windows Search answers from its own index, but only for the places it indexes. It didn't find a source file in a projects folder, and it found 9 of 8,200 .blend files.
10Published elsewhere
Numbers others measured, for context. They come from other machines and other data.
- fff's README:
- On whole agent tasks in a big repository, 24.7 s with shell search and 14.3 s with fff, with tokens almost the same: 85,400 against 82,500.
- In a held-out run, one model was slower with fff: 9.0 s against 8.1 s.
- On a 500,000-file Chromium checkout, each ripgrep run took 3 to 9 s, and each fff query under 10 ms.
- Everything's FAQ: a million files take about a minute to index and about 100 MB of memory.
- ripgrep's README, searching the Linux kernel's source: ripgrep 0.082 s, git grep -P 0.273 s, The Silver Searcher 0.443 s, ugrep 0.639 s, ack 2.9 s.
- Claude Code's issue tracker: users report its Glob tool timing out at 20 s in large repositories where ripgrep alone takes under a second, because it doesn't skip ignored folders.
Read together, they say the same as this page. A search tool that keeps its index between calls is orders of magnitude faster per search than one that starts over. But a search is only part of an agent's turn, so the biggest wins are where the old way took minutes or flooded the context.
11How it works: one process for every agent
The agent starts seek.exe as a stdio MCP server, as with any other. That process doesn't search: it passes each tool call through a named pipe to seek.exe --daemon, starting it the first time. The daemon outlives the session and serves every agent on the PC. Everything that is expensive to build is built there, once.
- The pipe carries a security descriptor that admits only the user and SYSTEM, and rejects remote clients.
- The connection stays open for the whole session. With the log kept open too, the trip from client to answer and back takes 0.1 ms.
- The daemon runs at below-normal priority, so a burst of searches never competes with the user's work.
12The drive index
Every name on every fixed drive, in the format Jolt keeps: each entry is 12 bytes, with its parent, its name, whether it is a folder, and its depth. The names follow in one block.
- With Jolt installed, Seek copies Jolt's index, which Jolt builds from the drive's file table, and picks up a newer copy every 10 minutes. It copies the file instead of reading it in place because Jolt replaces it when it reindexes, which a mapped file would block.
- Without Jolt, Seek walks each drive itself on 8 threads, with no special rights: 18 s for the 2.1 million names on an NVMe SSD, 3.8 s for the 1.3 million on another, and 19.5 minutes for the 1.4 million on a hard disk read cold. It saves the result and walks the drives again once a day in the background, while the old index keeps answering.
- Between walks, the drive watchers add what is created and hide what is deleted, so an answer never depends on how old the index is.
Searching inside one folder. The index lists names in the order of the drive's file table, so a folder's contents are scattered among millions of names. The first search scoped to a folder builds each folder's list of children: 8 bytes per name, about 30 ms for a drive. From then on, a search below a folder only visits what is below it. That took finding a name in a repository from 5.2 ms to 0.33 ms.
13Ranking: what the user is doing
A name's score comes from Jolt's fuzzy matching. Contiguous letters, word starts and the start of the name count for more, and an exact name or a program counts for most. Then what Seek knows about the user moves results up or down:
| what Seek knows | effect on the score | shown as |
|---|---|---|
| changed in the last hour, day, or week | +3,000, +1,500, +600 | changed 2 h ago |
| in a folder the user opens in Jolt, or up to two levels below it | up to +1,200, by how much it is used | in a folder used in Jolt |
inside the folder given as near | +2,500; otherwise +150 for each folder level shared with it | inside seek |
| in a cache, a package store, a log or AppData | −2,000 | – |
| the name's own score | fuzzy score × 8, minus its depth | exact name, program |
Every result carries its reasons ("exact name, changed 2 h ago, in a folder used in Jolt"), so the agent can tell a confident answer from a guess. Letters scattered along a long name match by chance; they are counted apart and shown only when nothing better is there. Close names, with a typo, are tried only when no name has the letters in order.
14How grep reads a folder
- The walk. One pool of threads both lists folders and takes files, files first, so the queue stays short. Results are sorted back into the order a single walk gives: a folder's files, then its subfolders, by name.
- The rules.
- At the root of each repository Seek adds the user's global excludes and
.git\info\exclude, then each folder's.gitignoreand.ignore. - Without any
!rule, plain names and*.extrules are looked up in a set instead of matched one by one. - Dependency and cache folders are skipped by name; so are private places, checked once per folder on the way down.
- At the root of each repository Seek adds the user's global excludes and
- The bytes.
- A file's first 8 KB tell whether it is binary, and then the rest is never read.
- For a literal pattern in ASCII, Seek looks for its bytes before decoding anything, because those bytes are the same in UTF-8 and in the system code page.
- The search starts at the pattern's rarest character, with
memchr, and compares around it, folding case on the fly when needed. Most files end here.
- The text. Only a file that may match is decoded, from UTF-16 by its mark, from UTF-8, or from the system code page. Then each matching line is cut to 240 characters and given its line number and context.
- Documents go through the text filter Windows has for their type. The PDF filter that comes with Windows only accepts a stream, which the classic
LoadIFiltercan't give it, so Seek creates the filter itself and hands it one.
15Folders kept in memory
After a folder's first search, the daemon reads its text files into memory on a thread of its own. A search that comes while that is happening waits for it, because it ends sooner than a second walk would. From then on:
- The drive watchers note every change below the folder; only changes in folders that were walked count.
- The next search applies the changes first: a changed file is read again, a new one is added if the rules let it in, a deleted one is dropped. A renamed folder is dropped and walked again under its new name.
- A changed
.gitignore,.ignoreorinfo\excludemakes the whole folder be read again. So do lost watcher events and more than 20,000 changes between two searches. - Searches with
hiddenorignoredwalk the folder as before, because the copy only holds what the default rules let in. - A search below a kept folder uses it, unless the rules left that part out.
Ten different searches over every project, compared from memory and from the drive, gave the same text, byte for byte. The tests check the changes one by one: a change, a new file, a deletion, a renamed folder and a .gitignore edit.
16From 6 seconds to 11 milliseconds
The first comparison found Seek's grep slow on a big folder. Each step below was measured with the same search, CreateFileW over every project.
| step | grep, every project | files read |
|---|---|---|
| The first comparison: it stopped at its 6 s budget | 6.0 s | 32,700 |
| Look for the pattern's bytes before decoding any text | 0.96 s | 32,700 |
| Check privacy by the entry's own name; look .gitignore names up | 0.76 s | 32,700 |
| List folders on 12 threads | 0.73 s | 32,700 |
| Read only the first 8 KB of a binary | 0.35 s | 32,700 |
| Follow .git\info\exclude and the global excludes, as git does | 132 ms | 10,700 |
| Keep the folder in memory, current through the drive watchers | 11 ms | none |
Two of these steps were bugs that a comparison found and a test now guards. Seek used to read 25,000 scratch files that git itself ignores, through rules in .git\info\exclude. The log was also opened and closed on every call, which was most of each call's 0.3 ms.
17Privacy
Seek only reads. Some places are never listed, searched or remembered:
- SSH, GPG, AWS, Azure, Kubernetes and Docker credentials
- Windows' credential and key stores
- browser profiles: Chrome, Edge, Brave, Thorium, Vivaldi, Opera and Firefox
- password managers: Bitwarden, 1Password, KeePass
- Telegram and Signal data
- the Recycle Bin,
System Volume Information, and.kdbx,.pfx,.p12and.ppkfiles
More can be added in the settings. Every call is written to %LOCALAPPDATA%\Seek\log.txt: when, which agent, which tool, the arguments and how long it took. The user can see what agents looked for. Nothing leaves the PC, and Seek makes no network connections.
18Setup for agents
The installer adds Seek to Claude Code and Codex. By hand:
# Claude Code
claude mcp add seek --scope user -- "%LOCALAPPDATA%\Programs\Seek\seek.exe"
# Codex
codex mcp add seek -- "%LOCALAPPDATA%\Programs\Seek\seek.exe"
Any other MCP client, such as Cursor, Cline or Claude Desktop, needs a stdio server whose command is the full path to seek.exe, with no arguments:
{ "mcpServers": { "seek": { "command": "C:\\Users\\me\\AppData\\Local\\Programs\\Seek\\seek.exe" } } }
A line in the project's CLAUDE.md or AGENTS.md helps the agent reach for it:
To find files by name anywhere on this PC, or search text in a folder, use the seek tools (find, grep, recent) instead of shell commands.
19Settings and command line
%APPDATA%\Seek\seek.txt, read when the background process starts:
exclude = C:\Users\me\Private a folder
exclude = *.key files with that extension
exclude = secrets any folder or file with that name
log = no don't write log.txt
cache = 256 MB of folders kept in memory; 0 for none
jolt = no don't use Jolt's index or what Jolt knows
seek --cli find "{\"query\":\"report .pdf\"}" run a tool in this process
seek --call grep "{\"pattern\":\"TODO\",\"path\":\"C:\\\\code\"}" through the background process
seek --reindex walk the drives again now
seek --stop stop the background process
20Limits, and what's next
- Windows only, fixed drives. Network shares and other drives can still be searched by giving their folder as
path: they are walked instead. regexuses the C++ standard library's engine. It is correct but much slower than ripgrep's, and it looks only at the first 4,096 characters of very long lines.- Without Jolt, the first index of a big drive takes a while, longer on a hard disk. Searches below a folder work right away, by walking it.
recentknows what changed while Seek was running, plus the dates on disk when given apath.- Not yet: reading the file table directly for an instant first index, ranking by git status, a search for definitions, and a signed executable.