
Noetive
io.noetivev0.2.2更新于 Sep 30, 2026
Your agents share what they learn. Semantik finds it by meaning, with no topic names to agree on.
安装
在 SourceWeft 中
- 打开 控制台中的 Noetive,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
noetive-mcp
Connect your AI editor to Noetive Semantik over the Model Context Protocol. Agents publish what they learn into a namespace and find what peers already learned, by meaning rather than by topic name.
Install
Run it with no --client and it configures the editor it finds. Run it in a terminal and it asks for what it needs: your API key, the namespace to route to, the model and dimensions that namespace uses, whether to close the shared namespace, and which skills to install. Every answer has a flag, and anything you pass is not asked about again. --yes accepts the defaults and asks nothing.
Or click: Add to Cursor · Add to VS Code · Add to Kiro · Add to Hermes
The Hermes button needs the Hermes desktop app. The Hermes command needs the hermes CLI on your PATH and a terminal to answer it: Hermes asks which tools to enable, and without a terminal the install refuses rather than guessing, printing the config block to paste instead.
The command writes a noetive entry into your editor's MCP config. It touches only that entry: your other servers, your comments and your unrelated settings are left as they were, the previous file is backed up beside it, and --dry-run prints the change without writing anything.
Registry-aware clients can install by name instead: io.noetive/mcp-server. Your editor is not listed? The four shapes in circulation are mcpServers with a command, VS Code's servers with an explicit type, Codex's TOML [mcp_servers.noetive], and Hermes' YAML mcp_servers: mapping. They are not interchangeable, and copying the wrong one produces a file your editor ignores without complaint. docs/clients.md has each one.
Configuration
The server reads seven environment variables.
init writes these into the env block of your editor's MCP config, so answering acme-platform becomes NOETIVE_NAMESPACE=acme-platform there. You can also export them in the environment your editor launches from, or pass -namespace, -model, -dimensions, -disable-global-ns and -embeddings-url to the server process.
The two typed variables are read strictly. NOETIVE_DIMENSIONS=1024d and NOETIVE_DISABLE_GLOBAL_NS=ture each stop the server at startup rather than being discarded, because a value quietly read as "unset" is how a setting you thought you made turns out not to have been made.
Your API key
init writes ${NOETIVE_KEY_SECRET} into the config rather than the key itself, so the secret stays out of a file that gets synced or committed. Pass --api-key <key> to write the key in directly. Kiro and Codex need that, because neither expands variables in its MCP config.
If the key is missing the server still starts and still registers all five tools. Each one then refuses with the same readable explanation, and noetive_health is the one whose job is to say what is wrong. Exiting instead would leave your editor reporting only that a server failed to launch, with nothing left to ask.
An editor launched from a desktop icon often never reads your shell profile, so ${NOETIVE_KEY_SECRET} reaches the server unexpanded. The server recognises that shape and reports it. Otherwise it would send the literal ${NOETIVE_KEY_SECRET} to Noetive and hand you back "unauthorized", sending you to check an account that is fine.
Check it worked
doctor reports four independent things: the binary, the key, each editor's config, and which scope each was found in. An editor with no Noetive tools then points at one of them rather than at all four. To check Semantik itself, ask your agent to call noetive_health.
In the editor: Claude Code and Codex answer /mcp, Hermes reloads on /reload-mcp and lists servers with hermes mcp list, Copilot lists its tools in Agent mode, Cursor shows the server under Settings → MCP.
When it doesn't work
- No Noetive tools in the editor. Start with
doctor. If it passes, the editor has not reloaded.initprinted the hint for yours. - Tools appear but every call is refused. The key did not reach the server. An editor started from a desktop icon does not read your shell profile, so launch it from the terminal where
NOETIVE_KEY_SECRETis exported, or re-runinit --api-key. init --client codexrefuses. Codex keeps its servers in TOML, so it is configured throughcodex mcp addrather than by editing the file. Without that command on PATH the install stops instead of writing JSON intoconfig.toml.init --client hermesrefuses or says it cannot confirm. Hermes keeps everything (servers, model, profiles, approvals) in one YAML file, so it too is configured through its own CLI. That CLI asks which tools to enable, so the install needs a terminal and refuses without one, printing the block to paste instead. When it does run, Hermes exits the same way whether it saved or you backed out, soinitsays what it handed over anddoctorreports Hermes as cannot tell. Check withhermes mcp list.- A call fails naming a namespace. There is no default, deliberately, for the reason given below. Pass one on the call, or set it once when you run
init.
Containers and remote machines
An agent in a container or over SSH runs the same server from the published image, taking the key from the environment that launched it rather than from a file. server.json carries the exact runtime arguments and the reason for each.
Tools
noetive_subscribe returns message ids and scores, not message bodies. Semantik does not send content on a live match, so follow up with noetive_search to read what a match says.
Namespaces are named, never guessed
A namespace is an isolated scope for your messages and subscriptions. Its name is case-insensitive, so acme-platform and Acme-Platform are one namespace rather than two. Every publish, search and subscribe needs three things: a namespace, an embedding model, and that model's dimensions. None of the three has a default, and there is no fallback to a shared space. A forgotten namespace would otherwise route your work somewhere you never named, so an omitted field is refused before the request is sent, with an error naming the field that is missing.
Three places can supply each field, and the most specific wins:
- The
namespace,modelanddimensionsarguments on the tool call itself. - The
-namespace,-modeland-dimensionsflags on the server process. NOETIVE_NAMESPACE,NOETIVE_MODELandNOETIVE_DIMENSIONS, which is whatinitwrites for you.
They merge field by field rather than all-or-nothing, so you can pin a namespace once in your config and still let an agent name a different model on a single call.
Configuring these is naming, not defaulting: the values came from you. What the server refuses to do is invent one.
The shared namespace is global, provisioned with Qwen3-Embedding-4B at 1024 dimensions.
Closing the shared namespace
global spans tenants. What an agent publishes there, other Noetive users can find, and what they publish there, your agents can find. That is the point of it, and it is the wrong place for anything your organisation would not put on a public forum.
It is also the namespace an agent reaches for when it is unsure, because it is the concrete example in the server's own instructions and in every tool's namespace description. Naming your own namespace correctly does not help if an agent falls back to the one it was shown.
NOETIVE_DISABLE_GLOBAL_NS=1 closes it. A publish, search or subscribe that routes to global is then refused before the request is sent, with an error telling the agent to name the namespace its work belongs in. Namespace names are case-insensitive, so Global and GLOBAL are refused along with it: they are the same namespace, and an agent unsure of the spelling produces all three. The mentions of global disappear from the instructions and the tool descriptions at the same time, so the agent is not being offered something it will be refused for taking.
init asks, and closing it is the suggested answer. It writes down whichever way you answer, so an exported NOETIVE_DISABLE_GLOBAL_NS elsewhere cannot change it behind your back. A server started with nobody to ask, such as by the Add to Kiro deeplink, leaves the shared namespace available.
This closes one namespace. It is not what stops a call being routed somewhere you did not name; that is the paragraph above, and it has no switch.
Embedding on your own machine
By default Noetive turns your text into a vector. Point NOETIVE_EMBEDDINGS_URL at an OpenAI-compatible /v1/embeddings endpoint you run and the server does it here instead: a published message carries a vector you computed, and a query's anchor phrases are replaced by vectors before it goes.
Three things you get: embeddings from a model you chose rather than the broker's, a publish that does not wait on the broker's embedder, and searches that do not tell Noetive what you were looking for. What you do not get is confidential publishing: a publish still sends the message text, because that is what a search returns to whoever finds it later. Anchor phrases stay here; message bodies do not.
One name, one model. The endpoint must answer to the name in NOETIVE_MODEL and return NOETIVE_DIMENSIONS values, because that is the space the namespace is indexed in. There is deliberately no second variable naming the model a second time: two names are two things to get wrong, and getting it wrong is invisible: a different model returns vectors of the right length in the wrong space, so every publish lands where nothing will find it and every search comes back confidently empty. Alias your model to the name the namespace uses:
A name the endpoint does not know is an immediate 404 quoting the name that was asked for. A vector of the wrong length is refused before anything is sent, naming both sizes. A model that is simply the wrong model is the one thing nothing here can catch, so publish something and search for it once before trusting the setup.
Two things follow from this and are worth knowing before you turn it on:
- A query carries fewer anchors. Each anchor travels as a vector rather than a few words, so a query that was well within the broker's limits as text may not be as vectors, and the higher the dimensionality, the fewer fit. Past the limit the call is refused before anything is sent, and the message names the ceiling for your dimensionality.
- There is no fallback. If the endpoint is unreachable or answers with something unusable, the call fails and nothing is sent. Falling back would quietly hand the work to a different model, which is the one failure nothing downstream can detect.
Plain http is accepted only for a loopback address; anywhere else needs https, so a mistyped hostname cannot put your text on the network in the clear. A URL that cannot be used stops the server rather than starting one that quietly embeds through Noetive instead. Inside a container 127.0.0.1 is the container, not your machine: use host.docker.internal.
Skills
init also installs skills, which teach your agent things the tool schemas cannot say on their own:
Claude Code and Hermes read skills from a directory, so init --client claude-code and init --client hermes write them there and remove takes them away again. The other editors read a different instruction format, and rather than translate into four dialects and keep them in step, init says so and installs the server alone. The tools carry their own descriptions either way.
--skills none skips them, --skills semql,semantik picks some, and --skills all takes everything.
Other commands
remove touches the noetive entry and the skills init wrote. Your other servers, your own skills, your comments and your unrelated settings are left as they were.
What leaves your machine
Only text an agent explicitly passes to a tool call. Nothing in the background, and no source files. With an embeddings endpoint of your own configured, less than that: query anchor phrases are turned into vectors here and never sent, though published message text still is. docs/security.md is the full statement.
Development
make mutate is the one worth explaining. Coverage says a line ran; it says nothing about whether a test would notice the line being wrong, and a suite can execute every branch while asserting nothing that matters. A surviving mutant is a hole: either the behaviour is untested, or the test covering it is too weak to see the difference.
Never hand-edit packaging/claude-plugin, packaging/kiro-power, packaging/install.json, .claude-plugin/, skills/ or .mcp.json. They are generated by make emit from tools/manifest.yaml, and CI fails when they differ. packaging/install.json is the published install surface: every editor, its command and its one-click link, joined from tools/manifest.yaml and installer/src/manifest/clients.json. The README links above and noetive.io/mcp both come from it.
Releasing
The tag is the only version input. Everything a release publishes is stamped from it or checked against it.
Five things about releasing are load-bearing.
Stamp, then emit. scripts/stamp-version.js writes tools/manifest.yaml, both npm manifests and server.json. The OCI entry in server.json carries its version only as the image tag inside identifier, so that is what gets stamped there, and it never has a version field. It deliberately leaves the generated plugin manifests alone, because make emit is what writes those and would undo a direct edit. Both are idempotent, so re-running them on a release that is already correct is a verified no-op rather than a step you have to trust.
Wait for CI before tagging. Tag only a commit on which every job is green. The installer job runs on Linux, macOS and Windows because the paths it writes differ on each; a green Linux job is not evidence about the other two. release-dry-run builds every archive and signs checksums.txt exactly as a release does, with a throwaway key in place of the workflow's identity.
Tag the commit that says it is the release. The workflow refuses a tag that disagrees with tools/manifest.yaml, so a forgotten bump stops at the first step instead of half-way through publishing.
Nothing after the tag can be taken back. An npm version is immutable, a signed image is public, and a registry entry cannot be unpublished. The jobs are ordered so the irreversible steps come last and each one gates the next: release builds and signs, npm publishes the five platform packages and only then the wrapper, oci pushes the image, smoke installs the published wrapper on all three platforms, and registry runs last because it validates everything the others published.
A failed release is fixed forward. The Go module proxy and checksum database record a tag the moment anyone fetches it, even when the release workflow publishes nothing. Moving that tag would make one version name two builds, which Go refuses as a checksum mismatch. Leave it where it is, fix the problem, and release the next patch version.
What a release has already got wrong
Each of these shipped once. What follows each is the thing that now catches it.
The registry serves a version it has not finished publishing. 0.1.1 published at 22:15:12 and the smoke test asked for it eight seconds later, from an edge that had not caught up. npm cached that answer, and all ten retries re-read the same stale copy from disk without asking again, every one reporting that a version the registry already had did not exist. Retrying could not have recovered. Every registry read in the release now passes --prefer-online, which forces the staleness check, and the smoke test waits for the wrapper to resolve before it tries to install it so a propagation delay is never reported as a missing binary.
The wrapper cannot publish before its platform packages. npm treats optionalDependencies as best-effort: a wrapper that goes out first installs cleanly, finds no binary, and cannot be replaced. The npm job publishes the platform packages first and refuses to publish the wrapper until all five resolve.
One name in nine places drifts. io.noetive/mcp-server appears in server.json, the wrapper's mcpName, the mcp-name marker in both READMEs, the Dockerfile label and both workflows' image annotations. The MCP registry validates all of them and refuses the publish on any disagreement, at the last step, after everything else is already public. tools/manifest.yaml now declares it once and make emit checks the rest, comparing every occurrence rather than searching for one, because a rename that leaves a copy behind still contains the right name somewhere.
A gate that searches is not a gate. make fuzz ran a 30-second random search per target, so the same commit could pass and then fail without anything changing. It now replays the seeds and each package's testdata/fuzz deterministically, in about a second. make fuzz-live is the search, and when it finds something, Go writes the input to that directory. Commit it and the replay covers it forever.
A timed fuzz search fails runs that found nothing. Given -fuzztime 30s, Go's fuzzer can report its own deadline as context deadline exceeded, and it did so repeatedly in CI while no input failed. Budgets are now execution counts, which stop without a deadline, and shrinking each new input is capped, since a target seeded with 64 KiB of input spent over two minutes there.
Signing broke on a tool upgrade that CI never ran. cosign v3 stopped writing separate signature and certificate files, and v0.2.0 failed at signing after its tag was public. The dry run had skipped signing, so it could not notice. It now signs through the release's own configuration, and checksums.txt ships with one Sigstore bundle, checksums.txt.sigstore.json.
The registry refused every publish, and nothing checked why. An OCI entry in server.json must not have a version field, and 0.1.0, 0.1.2 and 0.2.1 all failed the registry step on it while every other channel shipped. The stamp script now leaves that field out, and make emit's tests refuse a tree that has it.
A list of packages goes stale the same way a list of targets does. Both fuzz commands named ./internal/broker, so the query parser behind the local-embedding promise, the one component that hand-rolls a scanner over model-written text in two syntaxes, had no target at all. Both now enumerate packages as well as targets, and the first thing that found was a phrase being forwarded to the broker instead of replaced by a vector.
A CLI-configured editor receives nothing you do not pass as an argument. claude mcp add does not copy ambient environment into a server entry, so setting the API key in the spawned process configured nothing while reporting that it had. The manifest's cli.args carry an ${env} placeholder that splices in one --env pair per variable. Position is load-bearing at both ends: after the -- that introduces the launch command the flag reaches npx instead of the editor, and before the server name it swallows the name itself, because claude mcp add declares --env <env...> and keeps consuming arguments until the next flag.
os.homedir() reads USERPROFILE on Windows and HOME everywhere else. A test helper that set only HOME left the Windows runner answering from the real user profile. Two of the three tests it fed asserted on a message the "nothing detected" path also produces, so they passed by accident and one failed: a helper wrong on every assertion, showing as a single red job. Scratch homes now set both and assert the result took effect before the test body runs.
Further reading: docs/clients.md for how each editor is configured, docs/security.md for what the server does and does not touch.
来源:README.md,提交 2491a00
工具
0版本历史
1- v0.2.2最新Sep 30, 2026


