A local MeTTa memory and reasoning MCP for coding agents.
You talk to a coding agent normally. Oh My Goals gives the agent a local memory: it turns the material parts of the conversation into short English propositions, parses them into Semantic Hypergraph structures through a real parser, and keeps them in a MeTTa space that survives across turns and MCP restarts. The agent queries that memory before it plans and ranks its candidate actions against the stored goals, norms, and evidence before it acts.
The reasoning is written in MeTTa and runs locally on MeTTa TS. Nobody writes MeTTa, a JSON decision packet, or a numeric score: the agent authors controlled English and the memory does the rest. The same package is an MCP server for Claude Code, Codex, and OpenCode, a matching Agent Skill, and a TypeScript library.
The same memory is also a self-contained scientific literature assistant. Give it a paper by DOI or arXiv id and it fetches, parses, and stores the work with its retraction status, reads it into checkable claims, tracks citations, and reasons across papers: it corroborates a claim several works assert, surfaces contradictions between them, and when a paper is retracted it deactivates every claim and conclusion that rested on it, with a proof. The differentiator is not retrieval. It is a persistent, verifiable, contradiction-aware knowledge base with retraction-aware invalidation, all of it symbolic.
Important
A recommendation from Oh My Goals is advice, not authorization. The agent must still enforce user approval, handler availability, and any operating-system security controls before it acts.
A coding agent reaches the loop through six MCP tools:
| Tool | What it does |
|---|---|
remember |
Store controlled-English propositions (facts, goals, norms, actions) with their real sources, or a proof-backed derived conclusion. |
query |
Answer an English question over memory, keeping exact, reasoned, and semantically related results distinct. |
solve |
Rank the stored candidate actions against the goals, norms, and evidence, reporting a recommendation only for a clear, unblocked winner. |
revise |
Supersede a proposition with a correction. |
forget |
Retract or permanently purge exact propositions. |
explain |
Read a proposition back to its premises, sources, and lifecycle. |
From a built checkout, register the server and install the Agent Skill for your agent in one step:
node dist/cli.js install --agent claude --scope projectThat merges the MCP server into the agent's config and installs the skill that teaches the agent when to use it. The Agent Skill describes the loop and the controlled-English contract; the MCP tool reference lists every field. Memory scopes, lifecycle, persistence, semantic retrieval, and the HyperBase, MeTTa, and MCP boundaries are documented in ARCHITECTURE.md.
The parser is the local mettabase AlphaBeta parser, reached through a replaceable
adapter. Set OH_MY_GOALS_METTABASE_DIR and OH_MY_GOALS_HYPERBASE_PYTHON so the
server can parse English; install and install-mcp carry those settings into the
registered server when they are present in your environment.
Six more MCP tools turn the same knowledge base into a research assistant over the scientific literature. Every mechanical part is an existing, mostly keyless open tool; the novel part is the symbolic layer over them.
| Tool | What it does |
|---|---|
find_papers |
Search Semantic Scholar and OpenAlex for candidate works, ranked across sources by reciprocal rank fusion, with the ones already in your library flagged. |
ingest_paper |
Fetch a paper by DOI or arXiv id, parse it through GROBID into sections and references, store it as a work with its Crossref retraction status, seed its citation edges, and, when a model is configured, read it into validated claims. |
add_claim |
Store one controlled-English claim drawn from a work, sourced with a section-and-quote locator. |
citations |
Walk the citation graph of a work by MeTTa chaining: the works it cites or the works that cite it, one hop or transitively, with an option to fetch the wider graph from OpenAlex. |
review |
Gather the claims about a topic and read agreement and conflict across works: which statements several works corroborate, which are contradicted, each with its supporting and opposing works, a projected opinion, and warnings for a corrected or flagged source. |
check_retractions |
Re-check every work against Crossref, invalidate the claims of any newly retracted or withdrawn work, and flag corrections and expressions of concern; optionally flag the retracted works you cite. |
A paper is a source, so marking a work retracted is the same operation as retracting
a source: every claim sourced from it goes inactive, every conclusion resting on
those claims goes inactive, and explain names the retraction as the cause.
Corroboration and contradiction are read as paraconsistent evidence, where a
statement can carry support and opposition at once, then projected to a
Subjective-Logic opinion. The grouping and the invalidation are MeTTa chaining; the
worker only fetches and parses.
The research worker is a resident Python subprocess configured by
OH_MY_GOALS_RESEARCH_PYTHON (an interpreter with scipdf_parser),
OH_MY_GOALS_GROBID_URL (a running GROBID service; without it ingestion degrades to
metadata and references), and the polite-pool emails OH_MY_GOALS_CROSSREF_EMAIL
and OH_MY_GOALS_OPENALEX_EMAIL. The claim extractor is an optional
OpenAI-compatible model configured by OH_MY_GOALS_LLM_BASE_URL,
OH_MY_GOALS_LLM_MODEL, and an optional OH_MY_GOALS_LLM_API_KEY, so a local
runtime or a hosted provider both work. Without a model you supply claims through
add_claim.
Underneath solve is a decision core you can also call directly with a complete JSON
scenario. Suppose a coding agent can apply a verified change or apply the same change
before verification.
| Candidate action | Required goal | Explicit rule | Evidence | Result |
|---|---|---|---|---|
| Apply the verified change | Satisfied | None blocks it | 9 of 10 checks passed, with 95% coverage | Recommended |
| Apply the unverified change | Missing | Forbidden until verification passes | No calibrated evidence | Blocked |
The abridged receipt is:
{
"selected": "apply-verified",
"status": "recommended",
"selection_tied": false,
"automatic_execution_allowed": true,
"decisions": [
{
"action_id": "apply-verified",
"score": 0.8649,
"status": "recommended",
"norm_status": "unregulated",
"missing_required_goals": []
},
{
"action_id": "apply-unverified",
"score": -1,
"status": "blocked",
"norm_status": "forbidden",
"norm_reasons": [
"forbid:No passing verification result"
],
"missing_required_goals": [
"safe-change"
]
}
]
}The verified action wins because it satisfies the required goal and has attributable evidence. The other action is blocked by an explicit norm rather than merely receiving a lower score. The receipt preserves both outcomes so the choice can be reviewed later.
A model can propose an action, but the proposal does not carry your goals, policies, or authority. A plain allow-or-deny check also cannot answer every choice. You may need to compare several permitted actions, account for required outcomes, weigh evidence of different quality, and refuse to break a tie silently.
Oh My Goals makes those inputs explicit and gives the caller one replayable result:
- priority-aware obligations, permissions, and prohibitions;
- weighted required and optional goals;
- evidence with separate strength, confidence, and source;
- ranked candidate actions with reasons and missing goals;
- explicit tie detection and automatic-execution eligibility;
- the complete scenario declaration needed to audit the result.
Node.js 22.13.0 or newer is required. The package is currently installed from a checkout because version 0.1.0 is not yet published to npm.
git clone https://github.com/MesTTo/oh-my-goals.git
cd oh-my-goals
npm ci
npm run buildSave this as scenario.json:
{
"scenario": {
"title": "Apply a change?",
"goals": [
{
"id": "safe-change",
"owner": "maintainers",
"statement": "Ship only a verified change",
"weight": 1,
"kind": "collective",
"required": true
}
],
"norms": [
{
"id": "require-verification",
"mode": "forbid",
"targetAction": "apply-unverified",
"reason": "No passing verification result",
"priority": 10
}
],
"actions": [
{
"id": "apply-verified",
"label": "Apply the verified change",
"description": "Apply after the required checks pass",
"satisfies": ["safe-change"]
},
{
"id": "apply-unverified",
"label": "Apply the unverified change",
"description": "Apply before the required checks pass",
"satisfies": []
}
]
},
"evidence": {
"apply-verified": {
"strength": 0.9,
"confidence": 0.95,
"source": "9 of 10 required checks passed with 95% check coverage"
}
}
}Run the decision:
node dist/cli.js decide --input scenario.json --prettyThe command prints the complete version of the receipt shown above. Use
--input - to read JSON from stdin. Invalid input exits with code 2. Runtime
failures exit with code 1.
The input reference lists every field and default. Input is limited to 2 MiB.
Build and verify a tarball from the checkout:
cd /path/to/oh-my-goals
npm ci
npm run verify
mkdir -p ai-tmp
npm pack --pack-destination ai-tmp
cd /path/to/consumer-project
npm install /path/to/oh-my-goals/ai-tmp/oh-my-goals-0.1.0.tgz
npx --no-install oh-my-goals --helpThe JavaScript entry point is ESM-only. CommonJS callers must use dynamic
import().
| Input | Meaning |
|---|---|
| Actions | The choices that are actually available. Each action declares which goals it satisfies. |
| Goals | Outcomes an action should advance. Goals can be weighted, required, and marked as individual or collective. |
| Norms | Explicit rules that oblige, permit, or forbid an action. Higher-priority rules defeat lower-priority conflicts. |
| Evidence | Support for an action as a strength, a confidence value, and an attributable source. |
Oh My Goals does not invent any of these values. If a policy choice would change the result, the caller must obtain that choice from the user or another authoritative source.
strength measures how strongly the evidence supports the action.
confidence measures the reliability or coverage of that estimate. A passing
test supports only the behavior that test checks. It does not justify confidence
about unrelated behavior.
When evidence is omitted, the action receives a neutral strength of 0.5 with
confidence 0. That prior cannot produce a recommendation by itself.
| Field | What it tells you |
|---|---|
selected |
The highest-ranked action after norms, goals, motivation, and evidence are evaluated. |
status |
The selected action's status: recommended, candidate, weak, or blocked. |
decisions[].score |
The exact score used for stable ranking. Blocked decisions use -1. |
norm_status and norm_reasons |
Whether the action is unregulated, permitted, obligated, forbidden, or in conflict, plus the rules that caused it. |
missing_required_goals |
Required outcomes the action does not satisfy. |
selection_tied and tied_actions |
Whether more than one action shares the top score within the tie tolerance. |
automatic_execution_allowed |
Whether the reasoning result is recommended and untied. It is not user authorization. |
scenario_declaration |
The normalized goals, norms, actions, and notes needed to replay the decision. |
Even when automatic_execution_allowed is true, an automatic caller should
also confirm that the selected handler exists and that the user has already
authorized the action.
caller-owned actions + goals + norms + evidence
|
v
TypeScript validation and atom encoding
|
v
native MeTTa decision rules
norms -> goals -> scores -> ranking -> ties
|
v
decoded, auditable JSON receipt
|
v
caller checks authorization and handler availability
|
v
optional caller-owned execution
The main rule module is
metta/oh-my-goals.metta. MeTTa owns norm
resolution, goal analysis, scoring, status assignment, stable ranking, tie
handling, motivation consensus, automatic-execution eligibility, and the
selected PLN and SNARS formulas.
TypeScript owns the runtime boundary. It validates input, encodes and decodes atoms, manages files and optional processes, and dispatches caller-provided action handlers. Large collections use bounded grounded operations for specific numeric and structural work, while MeTTa still makes the decision. The exact boundary is documented in ARCHITECTURE.md.
An agent reaches Oh My Goals two ways, and the CLI sets up both. The MCP server makes the tools reachable; the Agent Skill teaches Claude Code, Codex, and OpenCode when and how to use them.
node dist/cli.js install --agent all --scope projectThe install command registers the MCP server in the agent's config and installs
the matching skill. install-mcp and install-skill do each step alone, and
install-mcp --remove deregisters the server. Registration keeps three config
formats and merges into an existing config without disturbing the user's other
servers: Claude Code's .mcp.json, Codex's .codex/config.toml, and OpenCode's
opencode.json. The registered server launches this same CLI, so the exact installed
version runs.
--agent all sets up the shared .agents layout used by Codex and the .claude
layout used by Claude Code; use --agent opencode for OpenCode. Use --scope user
for the corresponding directories under your home directory. Existing differing skill
files are preserved unless you pass --force.
Once installed, a request can be as direct as:
Remember that preserving the public API is required and that applying an unverified change is forbidden, then compare applying the verified change against gathering more evidence.
The agent stores the task's goals, norms, and candidate actions as controlled English
through remember, ranks them with solve, and reads memory back with query and
explain. Oh My Goals does not call a hosted model, read agent credentials, or gain
permission to execute an action.
Upgrading from the former local skill name
Earlier local installs used a goalchainer directory. The installer does not
delete user-owned skill trees. Install oh-my-goals, confirm that the new skill
is discovered, then remove the old directory if you no longer need it. The
goalchainer and goalchainer-ts binaries remain compatibility aliases
during this transition.
import { readFile } from "node:fs/promises";
import {
GoalChainer,
explainDecisions,
goalChainerRunToJson,
} from "oh-my-goals";
const input = JSON.parse(await readFile("scenario.json", "utf8"));
const chainer = new GoalChainer();
const run = chainer.evaluate(input);
console.log(goalChainerRunToJson(run));
console.log(explainDecisions(run.decisions).join("\n"));
if (!run.automaticExecutionAllowed) {
process.exitCode = 2;
}GoalChainer rejects unknown fields, duplicate IDs, invalid probabilities,
and dangling goal or action references. executeDecision refuses blocked
decisions and requires a matching caller-owned handler.
After the caller separately confirms user authorization and handler
availability, it can call executeDecision(run.selected, context, handlers).
That call does not infer or grant authorization.
The CLI and runGoalChainer use static evidence. Applications that need
queries against another reasoner can call evaluateScenario with a custom
EvidenceReasoner. ContextualQueryEvidenceReasoner passes each action's
evidenceQuery and evidenceAtoms to a caller-injected synchronous adapter.
Norm modes are oblige, permit, and forbid. The highest-priority
applicable norms decide the effective status. Opposing top-priority norms
produce conflict instead of an arbitrary winner. Forbidden and conflicting
actions are blocked.
Goal weights determine coverage. A required goal can prevent an otherwise
high-scoring action from becoming recommended. Individual and collective
goal membership also feeds the optional motivation consensus path.
| Status | Meaning |
|---|---|
recommended |
Score is at least 0.72, the action is not blocked, and no required goal is missing. |
candidate |
Score is at least 0.5, but the recommendation conditions are not all met. |
weak |
Score is below 0.5 and the action is not blocked. |
blocked |
A prohibition or unresolved norm conflict prevents selection for execution. |
Scores within 1e-12 are treated as tied. The declaration order remains
stable, but automatic_execution_allowed is false so input order cannot
silently authorize one of the tied actions.
Exact default scoring model
With motivation enabled, the score is:
0.54 * normalized_motivation
+ 0.38 * strength * confidence
+ 0.10 when the action is obligated
Motivation uses individual and collective goal membership masks plus
caller-supplied correlations and risks. Goal weights affect coverage, while
membership feeds motivation. Consensus values are min-max normalized. Equal
values normalize to 1.
With motivation disabled, the score is:
0.42 * goal_coverage
+ 0.38 * strength * confidence
+ 0.12 * min(individual_coverage, collective_coverage)
+ 0.10 when the action is obligated
Blocked decisions use score -1.
| Area | Public entry points |
|---|---|
| Complete decision gate | GoalChainer, runGoalChainer, evaluateScenario |
| Norm resolution | resolveNorms, resolveNormsBatch |
| Scoring and ranking | scoreActions, decideActions |
| Evidence and PLN | StaticEvidenceReasoner, PlnEvidenceReasoner, ContextualQueryEvidenceReasoner, gradeBeliefs |
| Motivation | consensusDecision |
| SNARS opinions | assess, derive |
| Task directives | createDirectivePlan, DirectiveLifecycle |
| Memory and HyperBase ingestion | createMemorySpace, createHyperbaseParser, ingestStatements |
| Optional Prolog comparison | decideActionsWithProlog, verifyScorePrologParity, checkDirectivePrologParity |
| Caller safety helpers | redactRecord, detectLeaks, executeDecision |
The framework pins the MeTTa TS runtime packages to version 1.1.4. The native
paths use standard-library operations such as foldl-atom, map-atom,
filter-atom, msort, and is-member. Bounded host operations cover
large compensated vector math, structural trees, stable ranking, and indexed PLN
matching. They do not compare candidate scores or select a winner outside
MeTTa.
SWI-Prolog is optional. The named score and directive relations can be compared with their MeTTa counterparts:
node dist/cli.js prolog-check --prettyThe command starts the local swipl executable and exits nonzero if a checked
relation differs. It does not claim Prolog parity for every MeTTa relation.
ProbMeTTa is not ported or bundled. A separately managed PeTTa and ProbMeTTa
process can be connected through an EvidenceReasoner or
ContextualQueryEvidenceReasoner, returning strength, confidence, source, and
proof data to this decision gate.
- Oh My Goals does not discover available actions or invent goals, norms, or evidence.
- It does not intercept shell commands, isolate processes, filter prompts, or replace operating-system permissions.
- It does not call Claude Code, Codex, OpenCode, or another hosted model. Agent authentication remains with the caller.
- It contains no built-in scenario data or action handlers.
- The JSON CLI accepts static evidence. Contextual evidence requires an injected TypeScript reasoner.
- Its PLN, SNARS, deontic, motivation, and directive exports are the documented decision relations, not complete replacements for a theorem prover, planner, probabilistic logic runtime, or NARS system.
Do not put credentials, private keys, tokens, or unredacted sensitive text in the input. The receipt repeats the scenario and evidence provenance.
- Parser not configured.
remember,revise, andqueryneed the HyperBase parser. WithoutOH_MY_GOALS_METTABASE_DIRandOH_MY_GOALS_HYPERBASE_PYTHONthey fail with "HyperBase parser is not configured: set ...". Point both at a local mettabase checkout and its Python interpreter.solve,explain, andforgetdo not parse and keep working without it. - A statement is rejected. The parser stores only a faithful controlled-English proposition whose mood suits its kind. A question, an imperative stored as anything but a goal, or an unparseable sentence returns rewrite feedback and stores nothing. Rewrite it as a plain declarative sentence and retry.
- A stale revision.
reviseandforgetwith anexpectedRevisionthat no longer matches return{ ok: false, code: "stale_revision", expected, actual }and change nothing. Read the current revision withexplainand retry with it, or omitexpectedRevisionto skip the check. - An unsupported question. A question that does not compile to the one-slot
subject form, such as an object question that needs do-support, returns an
unsupportedcode and rewrite feedback rather than a wrong answer, and still lists semantic neighbours underrelated. Rephrase as "Which ...?" for an exact answer. - A semantic index without the contextual model. The default token-hash provider
is deterministic but blind to paraphrase, and the contextual
bge-small-en-v1.5provider needs the optional@huggingface/transformerspeer dependency. When it is absent the provider falls back to token-hash and the query receipt reports the active provider, so a fallback is never presented as contextual understanding. Insta 6F40 ll the peer dependency for paraphrase matching. - A failed purge.
forgetwithmode: "purge"fails closed: it removes the record, scrubs the content with SQLite secure-delete and a WAL checkpoint, and returnsnot_foundfor an unknown id orstale_revisionfor a stale one. If a purge reports an error, nothing was removed; resolve the id or revision and retry.
Oh My Goals is at version 0.1.0 and is not yet published to npm.
npm run verify checks the native MeTTa module, the TypeScript API, the CLI, the
packed tarball, optional named Prolog comparisons when SWI-Prolog is available,
and the Agent Skill. The package smoke test installs the tarball into an isolated
consumer and runs the packaged MeTTa source.
The memory loop is verified end to end against the real HyperBase parser and a
real SQLite store. npm run test:mcp-e2e packs the tarball, installs it into a
fresh consumer, registers and spawns the packed oh-my-goals mcp server over
stdio, and drives the whole loop through it: registration, a natural-language
query, ranking, a derived conflict that blocks an action, retraction that
restores it, a purge whose canary string is then absent from the database and its
journals, and a restart that keeps the memory. It is gated on the parser
environment and skips without it, the same way the parser-dependent unit tests
do, so the parser-free suite still runs anywhere.
A property-based test fuzzes the memory lifecycle. It replays generated sequences of remember, derive, retract, restore, supersede, add-proof, purge, and restart, and after each step checks the active set, revisions, scope boundaries, purge removal, and restart against a model of the MeTTa rules. It found a purged-id reuse across restart, now prevented by a persisted id high-water mark and pinned by a regression test.
The installer's config is read back by each agent's own MCP CLI: codex mcp list, opencode mcp list, and claude mcp get oh-my-goals each list the
registered server, so a project proposition stored through one agent is reachable
by another over the same project memory.
Query latency is dominated by the parser subprocess, not the reasoning. Over a representative project memory the HyperBase round-trip is about 52 ms at the median, and the query engine adds about 5 ms on top for both an exact match and a hybrid exact-plus-semantic query, because the token-hash index search is cheap at this size.
Boundary tests cover 5,000 goals, 1,000 ranked actions, 1,000 motivation candidates, large PLN rule sets, malformed inputs, non-finite values, and stable tie behavior.
npm ci
npm run check:metta
npm test
npm run build
npm run test:packageSee ARCHITECTURE.md for rule ownership, large-input boundaries, evaluator isolation, optional interop, and verification scope.
The package code and documentation are MIT licensed. See LICENSE.