Opyt is an MCP server. It exposes tools to the AI
client you already use. A local install keeps its data under ~/.opyt:
opyt.db holds the knowledge-base records, each item's Markdown snapshot and
the search index, and rail_jobs.db holds the
worker's job queue. Its background helper, opyt-worker, which
onboard installs on your Mac, runs the ingest jobs, including while the
client is closed. The worker opens no port and holds no credential
of its own.
Local clients start Opyt over stdio: Claude Code,
Claude Desktop, ChatGPT Desktop, Codex, Cursor, and
Windsurf, on a Mac.
claude.ai and chatgpt.com read the library that install builds: say “connect Opyt to Google” in the
local client, then add the Opyt connector at https://api.useopyt.com/mcp and sign in with the same
Google account — step by step here, and the connector's own
documentation is here. They search a published copy by keyword,
and a change they ask for (a saved link, a topic, a person, a removal) is made by this
install. There is no Opyt chat UI.
The tools, in roughly the order you meet them. Every argument, its type and its default, and what comes back.
| Tool | Effect | What it does |
|---|---|---|
| onboard | writes · re-entrant | Asks for a source where you already read, then runs setup. |
| oracle | writes | Screens candidates from your activity and tracked sources, confirms picks, and follows imports. |
| add_oracle | two-phase | Adds a person or research venue by handle or URL, previews the source, then pulls what is available. |
| search | read-only | Ranked retrieval over the store, filterable by tag, kind, source, author and date. |
| open | reads · may read a paper's full text | The full stored text of one atom, plus its live source URL. |
| aggregate | read-only · free | Counts the whole scope by author, kind and source; can sample descriptions to help identify subjects. |
| latest | read-only · free | What is new since you last looked, by the topics you follow: finds to check, and your people's posts. |
| hopper | two-phase · ingests | Takes a supported paper, repo, post or article link and saves it as an atom. |
| watch | writes · free | Follows a topic: names to watch on arXiv, GitHub, OpenAlex, X and Substack, and words and people for your own people's posts. |
| look | read-only · free | Searches X, Substack, arXiv, OpenAlex and GitHub now for a few named things, and saves nothing. |
| share | two-phase · publishes | Hands someone a link to search your store, makes one for an agent you run, or connects your own claude.ai or ChatGPT. |
| accept | single-phase · writes | Registers a knowledge base somebody shared with you, from the link they sent. |
| unshare | two-phase · deletes | Cuts off one reader, or deletes everything Opyt's server holds for the knowledge base. |
| reader_view | publishes · expires | Puts an answer and the sources behind it on one private page, in full, that you can read or pass on. |
| forget | two-phase · removes | Removes atoms, ends one Oracle subscription, or drops one of an Oracle's other sources. Existing atoms stay unless you ask for them to go. |
The server owns only the last segment of each name, and a client
that namespaces adds its own prefix: search reaches Claude Code as
mcp__Opyt__search. Argument names are the ones below in every client.
Required arguments have no default and the call fails without them; optional
ones name their default. Every tool returns a JSON object.
Sets up this Opyt home. It recomputes where you are from disk on every
call, so calling it twice never repeats a finished step. It asks one thing: a source where you
already read. Connecting X or Substack imports what you saved there and keeps it current,
with nothing else to answer. Opyt is free and calls no model: search matches your words
rather than their meaning, and images are not read. Any step that opens a sign-in
or approval page is explained before you agree to begin it. Setup runs the applicable
curation collectors and hands off to
oracle.
onboard(source=None, start=None) -> dict
"x" · "substack" ·
"research" · "blog" · "skip". The roots
are not exclusive — take one, then call again with another.
On a local install, x and substack open OPYT's own
browser profile at that site's sign-in page, so OPYT never reads a session from
your normal browser profiles. On a computer that is not a Mac, such as an
assistant's computer with no browser of its own, x returns a link
(login_url) to a private sign-in browser on Opyt's service instead,
and the install collects the session once X shows its home page.
research
and blog have no account to connect:
they hand back the question to put to you and the exact call to make with your
answer. skip records that the question was asked and moves on;
every root still works later."github" · "openalex". Each opens a
browser page, so say what it is for and wait for the user's agreement before
passing it. github and openalex are optional keys that lift those
services' anonymous rate limits on what Opyt pulls. On a local install the tool
names them once, beside the answer to the first Oracle confirmation.
github shows a code to enter on github.com; pass it again once
approved, to finish. openalex opens a one-time page on the user's own
machine and checks the key before keeping it. Every word works at any time."ok" · "in_progress" ·
"needs_source" · "needs_name" ·
"needs_login" · "awaiting_login" ·
"connected" · "unsupported_platform" ·
"error". A key step can also return
"waiting".sources →
curation → done. On a computer that is not a Mac,
unsupported: the local install runs on macOS only, and nothing is
set up.needs_* status it is the question.start word it
answers.login_url for the X sign-in that runs on Opyt's service). After
they finish, call onboard() again to read the current state.cause and the step that fixes it. Work waits meanwhile;
nothing is lost.oracle to screen."oracle" once setup is done.onboard again.Chooses the Oracles the knowledge base deep-ingests. Candidates come from your follows, Lists, subscriptions, bookmarks, likes and saved papers, plus tracked researchers' coauthors and tracked Substack publications' recommendations. Ranking puts person-level acts (follow, subscribe, add to a List) ahead of content-level acts. The screen lists candidates by source: your top people on X, on Substack and from papers, then people your Oracles name, then one group for organisations, media and projects. A person found on several sources is listed once, under the stronger one. Nothing is selected by default; you choose which to confirm. Six actions run the flow in chat.
oracle(action="screen", canonical_ids=None, add_handles=None,
top_n=30, limit=10, source=None, force=False, x_lookback=None,
web_lookback=None, scholar_lookback=None, scholar_topics=None,
query="", min_signals=1, oracle=None) -> dict
| action | Reads | What it does |
|---|---|---|
| "screen" | limit, source | Default. The candidates in groups, in print order: one per source
(X, Substack, papers), then organisations, media and projects. Each group holds
its top limit people in rank order and more, the count
of the rest. Each candidate carries a one-line reason ("you follow,
bookmarked 12") and its canonical_id. Once you have Oracles, a
last group, Named by your Oracles, lists people their posts quote, mention or
link who are in none of your lists; they have no canonical_id, so a
pick goes in add_handles by handle. Nobody is pre-selected; the
reply carries the pick question in ask. Also carries
oracle_freshness unasked: per-Oracle, per-source last-pulled
times, worst first. |
| "candidates" | query, top_n, min_signals, oracle |
The same people ranked by what they write rather than how hard you
vouched. Evidence is one of probed (a ~25-post timeline sample,
unvetted), saved (a post you kept, so it is a real atom) or
mentioned (someone an Oracle's own posts quote, mention or link).
Scores compare only within a basis. |
| "confirm" | canonical_ids, add_handles, x_lookback, web_lookback, scholar_lookback, scholar_topics |
Commits picks into the oracles table and records how far back each
is owed. Idempotent, and it returns at once: nothing is pulled inside the call.
Opyt's background process then finds each person's other profiles and pulls
every trusted personal profile that has an available collector; X is included
only when X is connected, and an org link becomes an affiliation edge instead.
Raw handles and URLs are resolved at confirm time. Company, team and publication
accounts are confirmed as given. |
| "ingest" | canonical_ids, force, x_lookback, web_lookback, scholar_lookback, scholar_topics |
Asks again for confirmed Oracles: a wider window, subjects, a top-up of their
newest posts (x_lookback="since_last") or a fresh discovery of their
other profiles (force). Windows only ever widen. Returns at once,
like confirm. |
| "progress" | canonical_ids | Where each person stands: their newest posts, their archives beyond X, and how
far back their X goes against how far it is owed. Waits up to about 40 seconds
for something to change, then returns. There is no complete, and
nothing waits on this call. |
| "map" | (none) | The people you read, grouped by what they write: each group's
words, its central people, and which of your Oracles sit
in it. A group with no Oracle is where a new one would add the most. |
"screen"confirm, the candidates you
kept, verbatim from screen. On ingest, which confirmed Oracles to
ask again for; omitting it means all of them. On progress,
whose status to report; omitting it means all of them.confirm only. Raw X @handles or
supported URLs, including Substack, personal sites and OpenAlex records,
to add beyond the ranked list.30candidates only. Maximum people returned by content
search. Screening uses limit to bound its returned cards.10screen only. How many people each group lists, in
rank order; the rest are counted in the group's more. A screen
returns at most 80 people across its groups, so a larger value is clamped and
reported in limit_clamped. To go further into one group, pass its
source.screen only. List only the people who reach you
through one source, such as "x", "substack" or
"papers", or "named" for the people your Oracles name.
A person on two sources is listed under the one you filter by. Ranking stays unchanged. An unknown platform returns an error and
known_platforms.Falseingest only. Re-runs discovery from scratch, ignoring
the trust cache."6mo"confirm and ingest, only when X is
connected: "6mo" · "1yr" · "2yr" ·
"since_last". Hard-capped at two years whatever you pass.
since_last checks now for what has appeared since this Oracle's last
pull, and nothing else; someone never pulled gets their first pull in full."all""1yr" · "2yr" · "5yr" ·
"all". Narrows a discovered Substack or blog source."all""2yr" · "5yr" · "10yr" ·
"all". Narrows the paper record of a researcher or research venue.confirm and ingest, for one named
Oracle. OpenAlex topic ids from an Oracle preview's
scholar_topics.topics. This filter persists for that source; pass
[] to return to a researcher's whole corpus. A research venue has no
whole corpus to return to: without subjects, none of its papers are pulled.""candidates only. A topic question to rank people by ("agent
memory"). Omit it for "who has been sampled".1candidates only. Floor on distinct curation signals; 1 is everyone.candidates only. One Oracle's handle or name: lists
only the people that Oracle's own posts quote, mention or link (add
query for one subject), plus their coauthors.total_candidates,
groups[], ask, note,
lookback_options, oracle_freshness, and
model_routing only when a model is dead or fragile. Each group
carries title, candidates[], more and
source (x, substack, papers
or named). Each candidate card
carries canonical_id, name, handle and
reason. lookback_options.x, present only when X is
connected, is the separate X lookback question: its ask, the
presets ("6mo" · "1yr" ·
"2yr") and the default. When applicable, the response
also includes source and limit_clamped (asked and
applied limits).confirmed, unresolved (handles a lookup could not
find; nothing was written for those), unknown,
total_oracles, picks (where each stands, as in
progress) and message, what to tell the user about what
is still to come. A research venue confirmed without subjects carries
needs_topics and a note.picks and message, as on
confirm, and checking: the Oracles whose newest posts
are being checked now (since_last).picks[], one per Oracle: name,
canonical_id, newest_posts (null without an X
timeline), archives (null when nothing was asked of them),
reach (how far back their X goes against how far it is owed),
asked (the windows on record), and stuck, with its
cause and the step that clears it, only when the user
can do something. other_sources, only when Opyt found more of
someone's writing: added, also and a
message to tell the user once. waiting_on says when X
will next move. A
false means not yet, never that something is missing.note asks.
Nothing changes a candidate's rank.confirm and ingest
record what each person is owed and return at once, and Opyt's background process
does the pulling. Call progress when the user asks how it is going;
what lands while they are away is announced once, on their next call.Adds a person or a supported research venue as an Oracle. For a
person, Opyt discovers where they actually
publish (X, GitHub, a Substack, a blog found by feed or sitemap), and verifies each
profile belongs to them through links they attest to themselves, so it never merges two
people with the same name. X is pulled only when you have connected X; other discovered
sources are collected only when their collector is available. OpenAlex author records
and research venues are pulled as papers with their authors and DOIs.
Two phases: a free preview showing the resolved person or venue and what a
confirm would do, then, once you agree, the add, with the pull in the background.
To stop following someone, or drop one of their sources, use
forget.
add_oracle(reference, confirm=False, x_lookback=None,
web_lookback=None, scholar_lookback=None, scholar_topics=None,
force=False) -> dict
"@karpathy"), a Substack, blog or site URL
("https://simonwillison.net"), an OpenAlex author or source URL,
a supported research venue URL, or a canonical_id from
oracle(action="screen"), to promote a below-the-cut candidate.
There is no name-search endpoint: a name has to be resolved to a handle or URL
first.False"6mo""6mo" ·
"1yr" · "2yr" ·
"since_last", hard-capped at two years. since_last is
the top-up for someone already on the roster."all""1yr" · "2yr" · "5yr" ·
"all". Narrows only."all""2yr" · "5yr" · "10yr" ·
"all". Narrows the paper record of a researcher or research venue.scholar_topics.topics. The choice persists for that source; pass
[] to clear it. A research venue without subjects gets none of its
papers pulled.Falseconfirm_required, reference, lookback,
on_confirm, mode ("new" or
"existing") and resolved: name, bio, followers, site
for an X handle; the roster entry for someone already known. A reference that
does not resolve comes back as unresolved with nothing to
confirm. For a new OpenAlex root, resolved includes
root_entity, platform ("openalex_author"
or "openalex_venue") and works, the publication count.
Paper previews can also include lookback.scholar_counts for an
existing record and scholar_topics with topic counts. For a link to
a single page it also carries ambiguous_scope, since confirming
follows the whole site; ask which was meant before confirming.added (canonical_id, name,
source, was_already_oracle), picks and
message as on oracle(action="confirm").scholar_topics narrows the pull by
subject and persists into later refreshes.error naming what to pass instead, such
as the bare @handle. Supported research venues use the OpenAlex paper route.Ranked retrieval over the whole store. An optional
tag / kind / source / author pre-filter, then keyword ranking (BM25) over the stemmed
words of each item, whole. Ranking is pure relevance: there is no trust or
popularity re-rank. This is a router, not an answer: each hit is a thin card
carrying an excerpt from where its matches are densest, a pointer and a score, not a
statement of what the source says. That split is the trust
model, and open is the
other half of it.
search(query, tags=None, what_kind=None, source_type=None,
who=None, who_id=None, date_from=None, date_to=None,
entry_mode=None, k=8, kb=None) -> dict
tokenizer Karpathy, x402 micropayments). Each item is
matched whole on its words (stemmed; common words dropped), never on meaning, and
a rare literal token ranks best. Several short searches with different words find
more than one long one."ai-agents"). Matched as slugs: a value that
normalizes to nothing matches nothing, never "no filter", and a notice
says which value was dropped or rewritten."opinion" (saved posts) or
"artifact" (repos, papers)."x" · "github" · "substack" ·
"blog" · "paper"."@karpathy", "karpathy", a Substack or blog URL, or an
id. This is how you ask what one person said about a topic; putting their name in
query matches by content, so posts merely mentioning them
rank alongside posts they wrote. Resolved locally against people already in the
store: free, no network, and it never invents anyone."x:user:33836629"). A person's atoms sit under a per-platform id
each, so one id alone returns one platform's worth of them."2026", "2026-05" or "2026-05-11"; a
partial widens to its natural edge, so "2026" means 2026-01-01.
Any other shape raises rather than being silently dropped."2026" means 2026-12-31."user-saved" · "oracle-footprint" ·
"author_referenced" · "frontier". Leave it off
for the normal case, which returns a sectioned answer (see Returns). Set it to
scope the whole answer to one population instead: "frontier" with a
larger k is how you dig into what your topics kept. Scoping returns one list and
no frontier_atoms.8trace.cutoff gives the score at the boundary, so you can tell whether
raising it would have helped.kb it came from,
"me" for your own.citation_id, atom_id,
kb, source_type, what_kind,
who_id, who_name, when_ts,
when_precision, description, snippet,
snippet_span (where the snippet sits in the text
open returns), source_url, cite,
score, entry_mode, body_state,
body_basis and payload.authors, sources,
topics, date_span, body_state,
saved_vs_crawled, corpus_newest,
filter_cost, and resolved_who when you passed
who. These describe hits only.candidates,
ranked, showing, cutoff,
fts_query, filters as applied.hits, with its
candidates count: papers, repos and posts your topics' watches found and
your agent kept, rather than saved or written by someone you track. Present only when
entry_mode is left off. Same card shape, at most 8. Its scores are ranked against other frontier
atoms and mean nothing next to the ones in hits.cite, the ready-made Markdown link, with the item's title as its
text. This also applies to frontier_atoms. If cite is
null, say the source URL is missing; do not invent one. Open the source before
asserting what it claims.score is a reciprocal rank (1, ½, ⅓, …), so it orders one
call's hits and says nothing across calls.body_state qualifies every snippet: complete,
partial (a paywall teaser, a truncated feed entry), absent,
or pending. body_basis says how that was decided:
observed, stated or assumed.when_precision qualifies every date, and when_ts always
looks like a day. "year" means only the year is known and the
timestamp is that January 1 as a floor, so such an atom is included whenever its year
overlaps the window, deliberately. "push" is GitHub's last-push date,
not a publication date: a repo matching "after May" was active then and may
have been created years earlier. Atoms with no recorded date are excluded by either
bound, and a notice says how many.payload is not a fixed schema and is not filterable: it is
whatever that atom's source had, verbatim. GitHub atoms carry stars and
code_language, X atoms like_count and is_thread, papers citationCount and venue.Follows an atom's pointer and returns its real stored snapshot text plus
the live source URL, so a claim can always be traced back to where it was published.
search routes you to
an atom; this is the ground truth to reason from.
open(atom_id, kb=None) -> dict
{"error": "not found"} rather than
raising.kb
either finds nothing or hands back your own copy of the same source.raw_available says whether it is there at all."complete" · "partial" · "absent" ·
"pending", with body_basis saying how it was
determined.atom_id, kb, source_type,
what_kind, who_id, when_ts, cite,
when_precision, description, payload, and
papers when the item is a post that links papers: each with its
full_text while the answer's size allows, and an
open_id to pass to open for the rest.raw is only as complete as body_state says. On
partial you hold a knowing fragment, so quoting it as the full article
invents a citation; attribute what is there and send the reader to
source_url.cite link in the
answer. cite is null if no source URL is stored.Counts across the entire store rather than a sample. This is what answers distributional questions: what you saved most of, when your interests moved, how much of the store comes from people you confirmed. Scope is optional: omit everything for the whole store.
aggregate(tags=None, what_kind=None, source_type=None,
who_id=None, date_from=None, date_to=None,
kb=None, sample=0) -> dict
search.
These cover only part of the corpus and are not a topic taxonomy."opinion" or
"artifact"."x" · "github" ·
"substack" · "blog" · "paper".search(who="@handle") first and pass its
insights.resolved_who[].who_ids here: resolution has one home
rather than two that can drift.search; any other shape raises.trusted_atoms then counts atoms whose author
that owner confirmed, not you. The optional sample is currently
not forwarded to remote shared knowledge bases.0corpus_sample, capped at 500. The sample cycles through authors
to show breadth. Use 200 for a corpus-topic overview; leave it
off for ordinary counts. Counts still cover the entire selected scope.descriptions (plain
description strings, spread across authors; find an item with
search), sampled, of, authors,
of_authors and host_note. There is no
top_topics field.{who_id, count}.{atom_id, description, who_id, when_ts}. These
are mechanical descriptions, so they can be read without opening, but a claim
still needs open.scope as applied, kb, and notices in the
same shape search uses, so one channel serves both.search, this tool does not report how many undated atoms that
dropped.aggregate(sample=200),
read the descriptions, and propose subjects. Verify each with
search(query=...); report only supported subjects with cited examples
and the sample size out of the total. Do not present raw tags or the raw sample as
a topic overview, or save the inferred subject names as a taxonomy.What is new for you since you last looked. For each topic you follow
(watch): new papers,
repos and posts its watches found, which wait outside your library until your agent checks
them, and
your own people's new posts on it. Then what your Oracles posted, what you saved and pages
they linked, outside every topic, each saying why it is here. New means recently published:
when an Oracle's archive arrives, its older posts are counted in one line rather than
listed. A line about what is new can appear in search responses for your own knowledge
base, mentioned to you once per conversation.
latest(topic_id=None, limit=50) -> dict
50topic_id,
name): finds, each with its title, cite,
summary and the watch that found it, and posts, your people's new
posts on it. A post two topics cover appears under both.new, its count,
atoms, and more when some were left out. Each atom
carries who, when, cite,
description, and why: you saved it, or an Oracle posted
or linked it.watch(action="checked"), and tells you nothing about the rest. Kept finds
join your library; the rest never come back.Takes a supported link (a paper, repo, Substack post, article or single X post),
detects what it is, routes it to the right ingester, and shows you what it found before
writing anything. This is the only manual save path; there is no separate
save-a-paper or save-a-repo tool. Every atom it stores is stamped
entry_mode="user-saved", the same mark an X bookmark gets, and that is
load-bearing downstream: search ranks hand-saved items as your own, and
latest says you saved them.
hopper(reference, confirm=False, kind_hint=None) -> dict
.pdf), a GitHub repo, a Substack post, or an
x.com status link. Anything that is not an http(s) URL comes back
unroutable with nothing written, rather than being filed somewhere
plausible. Unsupported video, audio and login-walled platform URLs are also
refused as unroutable; a supported written transcript or article
can be saved instead.False"substack" is the one value worth passing: a Substack post on a
custom domain cannot be detected without fetching, and routed as a plain article
it gets a different atom id and never dedupes against the same post saved from a
bookmark. "paper", "github" and "x" cannot
override anything: those adapters check the host themselves."preview" · "saved" ·
"already_present" · "rejected" (the page was nav or
promo boilerplate, not an error) · "blocked" (a bot-check;
retryable) · "failed" · "unroutable".
already_present can still update an existing Frontier item's
save attribution; it does not mean the call made no writes.routable, kind, why (what the route was
decided from), atom_id, already_present,
entry_mode, and a note for the two
kinds whose id cannot be predicted offline.rejected, blocked and failed: what
actually happened, and whether it is worth retrying.saved result, marks a degraded success: most often a
paper whose full text landed but whose metadata lookup was throttled, so it is
stored with no title, date or author. The atom is searchable by its body, but
re-saving will not repair it.x.com status can also be read to return a
description, so you can check which post the link identifies.failed: the same cookie-less public endpoint a
reader hits, so it cannot see past a wall either.entry_mode becomes user-saved and
its promoted_at timestamp is set, without rewriting its body.
The result is still already_present. Subsequent saves leave that
attribution unchanged; the preview's route inspection may still make a fetch.add_oracle
is the only way in.Follows a topic for you, so you are told when something new on it
appears. Your agent writes three lists: watches, each naming one specific thing (a
model, a drug, a benchmark), searched every day on arXiv, GitHub, OpenAlex, X and
Substack;
words that pick out your own people's posts on the subject; and the
people who mostly write about it. Opyt runs no model for this: the searches run in
the background, and your agent checks what they find in
latest before it
shows you anything.
watch(action, name=None, watches=None, words=None, people=None, topic_id=None,
drop_watches=None, keep=None, passed=None) -> dict
"preview" shows what a topic would have found last
month and writes nothing. "create" starts following it,
"edit" changes it, "drop" stops it, "list"
shows them, and "checked" records your agent's check of the finds
latest handed it.name for one specific thing, its other
spellings, the sources to search, and, for X,
context: a word or two that narrow a name that also means something
else ("Chai-2" with "antibody"). A find is kept only when the name appears in its
title or text."checked": the finds your agent kept and the
ones it passed over.look.Searches outside your library, right now, for one to three specific
named things: what X, Substack, arXiv, OpenAlex and GitHub have on each over the last days
("what are people saying about Boltz-2 this week?"). It is the search a topic's watches
run every day, with the same rule: a result is kept only if it names the thing. Nothing
is saved. Your agent shows you what is worth your time, with links, and can save any of
it or follow the name with
watch.
look(names, sources=None, days=None) -> dict
name, its other spellings, and context:
a word or two that narrow a name on X when it also means something else
("Chai-2" with "antibody").x, substack, arxiv,
openalex, github; all of them when left out.7by_source, how many each source found
(or that it could not be searched), and results by source, newest
first, each with its title, date, cite link, a snippet, and the
save arguments for
hopper.
A paid Substack post is marked paid and cannot be saved.Takes an invite somebody sent you and registers their knowledge base
locally. After one call, search, open and
aggregate accept kb="their-name". There is deliberately no
preview: a grant code buys one reader token and then dies, so checking it would spend it,
and pasting the invite is already the yes.
accept(invite, name=None) -> dict
kb= should call this knowledge
base on this machine. Omitted, the owner's own name is used."accepted" ·
"code_unavailable" (already used, or not one this service issued) ·
"not_an_invite" ·
"reserved_name" (that name is what this install calls
its own library; pass another or leave it out). None of the failures
registered anything.kb=. Use
this string: a name already taken on this install is suffixed rather than
overwritten, because the registry holds the only copy of each reader token in
existence and replacing one destroys access nothing can restore.Opens an answer and the sources behind it in reader view, on one private page: your assistant's answer on top, then every post and thread with its photos and video, every article in full, a repository's README, a paper as its own PDF. One link, readable on a phone, that you can keep or send on. Opyt cannot see your conversation, so your assistant names the items it cited.
reader_view(atom_ids=None, title=None, note=None, answer=None, delete=None) -> dict
atom_id of search hits or open results from your own library. At
most 50."made" · "deleted" ·
"not_found" · "failed".made: the page's link.
missing lists any id that is not in your library and so is not on
the page.Remove local atoms, stop tracking one Oracle, or drop one of an Oracle's other sources, and optionally delete what they brought in. Preview the consequences first, then confirm the same target. There is no foreign knowledge-base target.
forget(atom_id=None, atom_ids=None, oracle=None, source=None, delete_atoms=False, confirm=False) -> dict
atom_id — optional string, default None. Deletes one atom,
its text and search entry.atom_ids — optional list of strings, default None.
Deletes several atoms in one preview and one confirm. An unknown id refuses the whole
list and nothing is removed.oracle — optional string, default None. A canonical id,
locally known handle, URL or exact roster name. Ends that person's subscription and
refresh registrations. Existing atoms, identity records and trust roots remain.source — optional string, default None. One of an
Oracle's other places, by URL: a Substack, blog or GitHub Opyt found and added for
them. Opyt stops pulling it and never adds it back, even if their profile still links
it; their other sources keep going. When two Oracles share it, also pass
oracle to choose. The profile they were added by is not a source: to
drop that, forget the Oracle.delete_atoms — boolean, default False, with
oracle or source. Also deletes every atom Opyt pulled from
that person's own sources, or from that one source, each as completely as atom_id would. Atoms you saved yourself,
such as bookmarked posts and saved links, remain.atom_id, atom_ids,
oracle and source. An ambiguous
name returns candidates; choose a canonical id and preview again.confirm — boolean, default False. The preview includes
human identity and finished consent sentences to read before setting it
to True. Removal makes no model calls.status is preview, forgotten,
not_found, ambiguous, refused (a
source that is the profile the Oracle was added by) or
error.
Atom previews include description, who_id,
when_ts, source_url and reference counts. Oracle previews
include canonical_id, name, atoms_kept and
sources_removed, plus atoms_deleted with
delete_atoms.
An X post still bookmarked on x.com can return on an hourly bookmark pass. A tracked author's work can return on a due refresh; ending their subscription prevents future scheduled pulls. A materialized Frontier candidate remains recorded and will not be admitted again automatically.
A shared copy loses a removed atom at its next upload, within about an hour while your Mac is awake. Readers can receive the previous copy before then. There is no local undo.
Seven curation signal feeds use an OPYT-managed signed-in X or Substack session, with no API key and no password. Locally, OPYT opens that profile; on an assistant's computer with no browser, X signs in through a private browser on useopyt.com. Everything else is public or handed over by you.
| X | bookmarks · likes · follows · Lists | your connected OPYT-managed session |
|---|---|---|
| Substack | follows · subscriptions · saved posts, full text | your connected OPYT-managed session |
| GitHub | a tracked person's repos and READMEs | public |
| Blogs | a tracked person's whole archive, found by feed or sitemap | public |
| arXiv · GitHub · OpenAlex | new papers and repos that name what a topic you follow watches, added once your AI keeps them; tracked OpenAlex authors and research venues | public |
| X search | new posts that name what a topic you follow watches, added once your AI keeps them, and what look finds | your connected OPYT-managed session, on a local install |
| Substack search | new newsletter posts that name what a topic you follow watches, added once your AI keeps them, and what look finds | public |
| A link you hand it | a supported paper, repo, post or article for hopper | you |
Opyt only reads. It never posts, follows, likes, or changes anything on
any account. A resident process on your machine, opyt-worker, runs each feed as
a bounded one-pass job — so pulls continue while your client is closed, and each feed decides
for itself whether there is anything new before it asks. A feed starts running when you give
it something to do: you connect a platform during setup, you track a person, you share your knowledge
base. Nothing is scheduled on a fresh install.
This is the part that runs without you. A topic you follow is what creates the watchers for its subject.
watch adds standing
searches outside your store, one per spelling of each name, and those become durable
queries. A watch's identity is a hash of its normalized text, so asking the same thing
twice does not create a second watcher.latest,
checked first: only what is worth your time, each with the watch that found it.You say "share my knowledge base" and get a link. They paste it. Their next question can draw on everything you have read. Nobody opens a terminal, and the copy keeps itself current.
share shows you what
is in there first (how many atoms, whose, over what span), then registers you, starts
the upload, and hands back a link. Publishing is self-service: no invitation, no
account, no waiting. The first upload runs in the background and takes a minute or two;
the link works immediately.accept registers
your knowledge base on their machine, and from then on their agent passes
kb="your-name" to
search,
open and
aggregate.
Those three over HTTPS. No upload, no grant, no write of any kind.share(refresh=True) to update it at once. The copy is
made on your Mac, so while it sleeps, readers see the last upload.unshare(reader="Leo", confirm=True)
cuts off Leo and leaves the copy serving for everybody else.
unshare(confirm=True)
deletes everything the service holds for it (the copy, every reader, the read counts and
the Google link) in one act. Either way it
takes effect on that reader's very next request: there is no refresh cycle and no
window. Without confirm=True, either call only previews the removal.To pass on the sources of one answer rather than the whole knowledge
base, ask for a reader view:
reader_view puts every item the
answer cited on one private link, in full, where whoever you send it to can comment.
Every result from a shared knowledge base is labeled with whose it is, so an answer built from a friend's research says so. It goes one way: accepting gives you read access and gives them nothing, and Opyt offers once, after your first search that finds something, to share yours back.
The invite code travels in the URL fragment, which is excluded from
the browser's initial page request. accept sends the code to the sharing
service's redemption endpoint to obtain a reader token. The name a reader searches under
is theirs to pick
and never has to be unique; the address in the URL is an assigned key nobody types. What
the service records is that a read happened, never what was searched for.
TELEMETRY.md
is the full schema, checked against the code by a test.
Opyt is free and MIT-licensed, and it calls no model at all: search matches your words rather than their meaning, and images are not read.
| Your AI client | every question, every answer, all the reasoning | your configured client |
|---|---|---|
| X | posts, timelines, profiles, threads | your OPYT-managed browser session |
| Substack | subscriptions, saved posts, full text | your OPYT-managed browser session |
| Everything else | papers, blogs, GitHub, supported article pages | public source access |
Failure handling depends on the operation. An import can keep items already saved when a later source read fails; it reports the error or incomplete pull so the remaining work can be retried. A first share can register the installation before invite creation fails; retrying then uses that registration. Read the tool's result to see what completed and what remains.