
Contextator
io.github.ContextatorPublished by GitHub account Contextatorv1.0.0Updated Nov 4, 2026
Self-hosted documentation server: each project's indexed docs as its own MCP endpoint.
Overview
Self-hosted documentation server that indexes each project's docs and exposes them as a separate, semantically searchable MCP endpoint.
- What you can do
- Search indexed documentation with hybrid keyword and semantic search, ranked excerpts, and optional version filters
- List indexed topics and read a single document's Markdown by path or heading range
- See what was added, changed or removed since a given point in time
- Report documentation gaps to the maintainers, stored in a separate table
- Expose project documents as MCP resources plus read-only prompts for answering from docs and exploring a topic
- When to use it
- Useful when a team keeps its own documentation (folders, git repos, uploads, Notion, Confluence or a docs site) and wants AI assistants to answer from it without sending the content to a third-party service. Each project gets its own isolated endpoint, so separate products or teams can be kept apart.
- Requirements
- Runs as a remote MCP endpoint at a per-project URL on a self-hosted instance, normally one Docker container with embedded PostgreSQL and pgvector. A project in token mode needs an Authorization bearer token created on the project page; account mode uses OAuth sign-in; open projects need no header. The host needs Docker, and the first start downloads an embedding model.
Installation
In SourceWeft
- Open Contextator in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Other MCP clients
Follow the launch instructions in the repository.
README
Contextator
Self-hosted MCP server that turns your documentation into one semantically searchable endpoint per project.
[Designed & Maintained with Tan]
Give a project its document sources — mounted folders, git repositories, uploaded archives, a Notion workspace, a Confluence Cloud site or Data Center instance, a published documentation site — and it becomes its own Model Context Protocol endpoint that AI agents (Cursor, Claude Code, Claude Desktop, …) can search:
It ships as one Docker container, published as contextator/contextator, that holds both the database and
the app, and is free software under the AGPL-3.0-or-later with a commercial license available
(License).
Features
- One URL per project, fully isolated. Each project has its own document collection and vector embeddings
in PostgreSQL + pgvector. A client connected to
/mcp/billingnever sees/mcp/mobile. - Many sources per project. A local directory, a git repository (or one subdirectory of it), an upload of
files/folders/
.zip/.tar.gz/.rar, a Notion workspace, a Confluence Cloud site or Data Center instance, or a published documentation site read from itssitemap.xml, combined into one searchable endpoint. See Document sources. - 100 % local by default. Embeddings are generated on the CPU with
transformers.js (
Xenova/multilingual-e5-small, a retrieval model covering 100 languages incl. Turkish). Switch to OpenAI embeddings with two env vars. - Admin dashboard at
http://localhost:3444/to manage projects and their sources — add a repository, drop a folder or an archive on the page, test a connection, trigger re-indexing and watch progress. - Accounts and roles. People sign in with their own account.
rootandadminmanage everything and everyone; amembersees only the projects it is assigned to, as a read-onlyvieweror aneditorthat adds sources, uploads files and re-indexes. Any account can mint its own named, scoped, revocable API token for a script or CI job;ADMIN_TOKENstill works too. See Accounts and permissions. - A door on each MCP endpoint, with three settings. A new project requires a bearer token that your client sends as an ordinary header, and is handed its first one as it is created, shown once. Make it open if its documents should be readable by anyone who can reach the URL, or require an account and its access becomes the memberships you already manage. Browser-based connectors sign in through OAuth 2.1. See MCP access.
- Incremental indexing. Files are hashed; only changed files are re-embedded, removed files are deleted.
- More than Markdown.
.html,.docx,.csv,.pdf,.pptx,.xlsx,.ipynb,.rstand.adoc/.asciidocare converted to Markdown as they are indexed, so an agent reads a Word file or a PDF the way it reads a page of documentation. See File types.
Quick Start
Everything runs in a single container named contextator: PostgreSQL 16 + pgvector and the Node.js
app, started and stopped together by a small entrypoint script. Data lives in Docker volumes and
survives container removal (see Data and persistence). The published image
is contextator/contextator; docker compose up -d pulls it, so the two files below — not the whole
repository — are all a fresh install needs.
The port is published on 127.0.0.1 only. The dashboard and every MCP endpoint answer on this
machine and nowhere else, so a container started on a laptop or an office server does not appear on
the network it is plugged into. To publish it on the network deliberately, set CONTEXTATOR_BIND=0.0.0.0
in .env and put a TLS-terminating reverse proxy or a VPN in front of it — see
Running behind a reverse proxy.
-
Create the first account. Open http://localhost:3444/setup, enter the
SETUP_CODEyou chose, and create therootaccount. The code exists so that nobody who reaches the server before you can claim it; it stops working the moment that account is created.Left
SETUP_CODEempty? The server generates one and prints it at every start until an account exists — it is in the log you are already tailing: -
Sign in at http://localhost:3444/.
-
Press New project (or
n): name itdemo, and for the directory point at one of your own subfolders under/docs— the host folder fromDOCS_HOST_PATHis mounted there, sodocs/handbookon the host is/docs/handbookhere. Leaving the directory empty creates an empty project; add its sources afterwards with Add source. -
Watch the project's status go
indexing → idlein the list; the Document sources panel shows every source with its document count, last sync and any error. -
Use the Connect an agent tabs (Claude Code, Cursor, Claude Desktop, and the deprecated legacy SSE) for copy-paste snippets. From a source checkout, the bundled smoke test does the same thing from the command line:
The first start initialises the database and downloads the embedding model (~470 MB for the default
fp32 model, ~235 MB with EMBEDDING_DTYPE=fp16, ~120 MB with EMBEDDING_DTYPE=q8, ~90 MB for
all-MiniLM-L6-v2) into the contextator-models volume; later starts take a few seconds.
Without Compose:
Building the image from source instead of pulling it: Build the image yourself.
Tags. latest is the newest stable release; 1 (from 1.0.0 on) tracks the newest release of the
1.x major line; 1.0 tracks the latest patch inside the 1.0.x line; 1.0.0 is one exact, immutable
release. Pin a versioned tag for anything you upgrade deliberately by setting CONTEXTATOR_TAG in .env
(e.g. CONTEXTATOR_TAG=1.0.0) and running docker compose up -d — this reads at every start, not only
the first. Every one of those tags from 0.2.0 on has a -slim twin — latest-slim, 1-slim,
1.0-slim, 1.0.0-slim — which is described in Bringing your own
PostgreSQL.
Usage
Point Claude Code, Cursor, Claude Desktop or any other MCP client at http://localhost:3444/mcp/<project> —
the dashboard's Connect panel prints the exact snippet, token header included when the project requires
one. For a project that requires a token:
Four read-only tools are exposed: search_docs (hybrid search, ranked excerpts), list_topics (the indexed
document list), read_document (one file's Markdown, by path or heading range) and whats_new (what was added,
changed or removed since a point in time). A fifth, report_gap, lets an agent tell the maintainers what the
documentation did not answer; it writes to its own table (reported_gaps), never to documentation, and is absent while
the query log is off. Beside the tools, each
project publishes its documents as MCP resources and two read-only prompts, answer_from_docs (argument
question) and explore_topic (argument topic).
One URL, Streamable HTTP first. Streamable HTTP is the way to connect. The legacy HTTP+SSE transport is
deprecated: it still answers on the same URL and keeps working, no removal date is set, and its responses
carry a Deprecation: true header. The same URL also speaks the stateless MCP 2026-07-28 protocol beside
2025-11-25.
A project is a set of sources — a local directory, a git repository, an upload, a Notion workspace, a
Confluence site or a documentation site — each added in the dashboard under its own name, so a file
install.md in a source named handbook is searched and read as handbook/install.md. An agent can narrow
a search to one release with search_docs(query: "rotate the signing key", version: "v3").
Everything else — the protocol eras, the source types, access modes and file types — is in Connecting AI clients, Document sources, MCP access and File types.
How It Works
Stack: TypeScript · Node.js 24+ · Fastify 5 · PostgreSQL 16 + pgvector · Drizzle ORM · @modelcontextprotocol/server (MCP TypeScript SDK 2.x) · @huggingface/transformers · unpdf / mammoth / turndown.
Each run syncs every source, hashes files to skip what has not changed, converts and chunks what has
(frontmatter-aware, split at headings, a breadcrumb kept per chunk), then embeds each chunk and writes it
beside a keyword index — search is hybrid, meaning and exact wording fused by rank, which is why an
environment variable name finds its page as readily as a question does. A force re-index rebuilds
everything beside the live index rather than replacing it first, so the project keeps answering while it
runs. CHUNK_MAX_TOKENS defaults to a measured 96, not the model's much larger window — filling the
window measures worse.
Full run mechanics, the chunk-budget measurement, hybrid search and rank fusion, and the cross-lingual search limit (measured, not fixed, with what to do instead): Indexing and Embedding Models.
Configuration
Everything is an environment variable; see .env.example for the full annotated list.
The variables that most installs touch:
The full table — every storage, upload, chunking, search-ranking, sync, auth and observability variable, each with its reasoning — is on Configuration.
Deployment
The single container from the Quick Start is the default, and most installations should stay on it. Two other topologies exist:
-
Your own PostgreSQL. One line of
.envmoves the database out of the container:It needs PostgreSQL 16 or newer with pgvector, and backups become yours. The
-slimimage carries no database at all. See Bringing your own PostgreSQL. -
Kubernetes.
charts/contextator/is a Helm chart for the-slim+ external-database topology. See Running on Kubernetes.
Behind a reverse proxy, three settings have to move together: Running behind a reverse proxy. Where your data goes, and what leaves the machine, is on Where your data goes.
Data and persistence
docker compose down, docker compose pull && docker compose up -d, and docker rm contextator all
keep the volumes. Only docker compose down -v or docker volume rm deletes them. Examples:
Host directories are created on first start and the entrypoint fixes their ownership. Both kinds of
override were verified on Linux-style paths and on Docker Desktop for Windows (WSL 2 backend), where
PostgreSQL initialised and indexed fine on a C:/… directory. Named volumes remain the faster choice for
the database on Docker Desktop; if initdb ever reports permission errors on a host directory, switch
that mount back to a volume.
On an installation that set DATABASE_URL (Bringing your own PostgreSQL)
the database is a server this image does not operate. npm run backup and npm run restore still
work: they reach that server through DATABASE_URL, because the upload trees in the archive are in no
other backup — and backup says, in its output and in the archive's manifest, that the server's own
backup regime is the one that matters. Run them from the default image: the -slim image has no
pg_dump and stops, saying it is not on the PATH (refusal no_pg_tools). The default image's client is PostgreSQL 16, so against a
17+ server pg_dump stops with server version mismatch (and pg_restore 16 cannot read a 17 dump):
install postgresql-client-<major> for the server's major version in the container and put
/usr/lib/postgresql/<major>/bin first on PATH — the Helm chart's
Taking a backup shows how. What does not apply
on that topology is everything about the embedded cluster: the three by-hand commands further down (psql, pg_dump, pg_restore as user
contextator) reach nothing, and the pgdata volume is empty. The two bullets below about what a
dump does not contain apply on both topologies, and so does the paragraph about /data.
Scheduled backups for the external database. Add the optional sidecar to the slim stack:
The sidecar waits for the app to be healthy, runs npm run backup, then waits BACKUP_INTERVAL_SECONDS (default
86400, one day) after each attempt. A sleep loop keeps this optional recipe independent of a cron
daemon; it measures intervals after a run finishes, rather than promising a wall-clock execution time.
Set BACKUP_INTERVAL_SECONDS and BACKUP_KEEP in .env to change the interval and retention; both
must be positive integers. The default retention is 7 completed archives. Failed backups retain
every existing archive and are retried after the interval. Invalid settings stop after at most three
container restarts; correct .env and run up -d backup to start again.
It uses the default, non-slim image for its PostgreSQL tools, overrides that image's entrypoint,
runs as UID/GID 1000:1000 to match the slim app (with HOME=/tmp for npm), and mounts only the same data volume at /data. It never starts an embedded PostgreSQL or mounts
pgdata. Its .env is the app's .env, including DATABASE_URL and SECRET_KEY, so the archive
records the fingerprint of the app's SECRET_KEY and a restore with that key passes the key check.
The PostgreSQL client major version must be at least the server's major version.
Archives are in /data/backups/, written as .partial files and renamed only after a successful
backup, so interrupted writes do not count toward retention. On stop, the sidecar terminates the
backup process group with TERM followed by KILL after one second and removes its partial archive and private staging files. After a forced kill,
the next iteration removes stale scheduled .partial archives and staging files under
/data/.scheduled-backup-staging. Run only one
backup sidecar per data volume. Retention applies only to the default
timestamped
contextator-backup-<timestamp>.tar.gz filenames; custom-named archives are left alone. Copy backups
off the host too: sharing the app's data volume does not protect against losing that volume.
An on-demand backup also runs from the default image, because the slim app container has no
pg_dump. Reuse the backup service definition, but replace its loop and clear
BACKUP_STAGING_DIR, so the backup stages under /data/.backup-*, which the sidecar's cleanup
never touches. Give the archive a name of your own, so retention does not count it:
To restore an archive into a fresh installation, use the npm run restore -- <file> --check and
npm run restore -- <file> procedure below, but run both commands the same way as the on-demand
backup above (run --rm --no-deps -e BACKUP_STAGING_DIR= --entrypoint npm backup run restore -- …),
not with docker exec in the slim app container, and restart the app with
docker compose -f docker-compose.slim.yml restart contextator. Stop the backup sidecar while
restoring so it does not take an archive of the restore in progress:
Start it again with up -d backup after the restore is complete.
The embedded PostgreSQL listens on 127.0.0.1 inside the container only and is not published, so
everything here goes through the container.
One command takes the backup, because a pg_dump is not the installation:
Restoring on a fresh installation — which is the case that matters — starts with the directory:
Run npm run backup with no path at all and it writes <DATA_DIR>/backups/contextator-backup-<timestamp>.tar.gz
— on the data volume, never in the container's working directory, which is an image layer.
To an S3-compatible bucket. Set BACKUP_S3_BUCKET, BACKUP_S3_ACCESS_KEY_ID and
BACKUP_S3_SECRET_ACCESS_KEY (optionally BACKUP_S3_ENDPOINT for MinIO, Ceph or R2, BACKUP_S3_REGION,
BACKUP_S3_PREFIX, BACKUP_S3_SESSION_TOKEN) and add --s3; the archive is built in the staging
directory, uploaded (multipart above 16 MiB) and removed locally, and only the newest BACKUP_KEEP
(default 7) timestamped archives under the prefix are kept:
latest is the newest timestamped archive; a name restores that one. Requests are path-style and signed
with SigV4 by the script itself, so no extra package is installed. Nothing in the server schedules this:
on Kubernetes the chart renders an optional CronJob
(Scheduled backup to S3), elsewhere use cron or your
orchestrator. restore --check with --s3 downloads the archive to the data volume first and deletes
the copy afterwards.
These blocks are the only copy of this procedure. The wiki and the documentation site
carry no commands for it and point here. restore overwrites a live database,
so a command is changed here and nowhere else. test/readme-mirrors.test.ts fails if a copy that
declares itself a mirror (a MIRRORED-IN / MIRRORED-FROM marker) appears; it cannot see an unmarked
copy in another repository, and review is what catches that.
The archive holds the database, the materialised files of every upload source — which exist nowhere
else — and a manifest that is the first entry in it, so --check costs one small read of a file that
may be gigabytes.
SECRET_KEY is not in it and never will be. A backup carrying the key would be the whole instance
in one file, on a volume with a weaker access story than the environment it came from. What travels is
a fingerprint of the key — 128 bits of keyed HMAC — which is what lets restore refuse a wrong or
missing key before it writes anything, when that key would cost something real — an archive whose
sources hold no sync credential encrypted under it restores anyway, with a note, instead of stopping
the one case that is unambiguously safe. Only git, notion, confluence, s3, gdrive and github sources hold a sync
credential, so only their secrets count. An archive taken before the manifest recorded which types it
counted is judged on the rows of its own dump, still before the target is touched: a secret stored on a
local, upload or web source by an older release does not refuse the restore, and after the restore
such secrets — which nothing ever read — are set to empty and their number is printed. Keep the key
where the archive is not.
That key is rotatable — a four-step runbook (npm run rotate-secret is step 3) — which gives the paragraph
above a second edge: retiring the old key — removing
SECRET_KEY_PREVIOUS — and then discarding it invalidates every archive taken before the rotation
that carries a source's sync credential — a private git, Notion, Confluence or GitHub token, an S3 key pair or a Google service account key — encrypted under
it. The old archive's fingerprint names a key the environment no longer holds, and for that archive
restore refuses, because the credential's ciphertext was written under the key that was retired and
discarded and cannot be re-derived; an archive with no such credential restores anyway, and the
refusal starts at step 2 of the four, not step 4. Keeping the retired key instead of discarding it
keeps that archive restorable until its retention expires. A rotation is bracketed by backups, in
three steps and in this order: a normal backup before the
rotation, under the current key; a fresh archive after npm run rotate-secret reports every row
converted and before SECRET_KEY_PREVIOUS is removed, under the new key — the oldest one the
rotated instance can restore; and the retired key kept in the secret store, labelled and not beside
the archives, until the retention of the oldest pre-rotation archive expires — only then discarded.
The full runbook, with each step's command, is
Security → Rotating things.
The restore also refuses a dump taken from a newer PostgreSQL major version than the server it is going into; going the other way, 16 to 17, is the documented upgrade and is what the command exists for.
By hand, when you want the database and nothing else — and these are the two commands the test suite runs:
test/integration/backup-restore.itest.ts seeds a real PostgreSQL, dumps it, drops the database,
restores it, and then asserts that the schema, the search results — identical rows, identical order,
identical scores — and the next start of the application all come back unchanged. A row count would not
have caught a restore that lost the vector index or the lexical column, so it is not one of the
assertions. The same file then does the whole round trip through npm run backup and npm run restore,
with an upload source and an encrypted credential, and asserts that a wrong key leaves the instance
untouched.
Two things a dump does not contain, which is the whole reason the command above exists:
SECRET_KEY. It is an environment variable. Without the original key, every stored source credential has to be re-entered. Keep it somewhere the dump is not — the dump plus the key is the whole instance — and keep the key you retire when you rotate, or the dumps older than the rotation become unrestorable./data. Git checkouts are re-clonable and Notion pulls are re-pullable, but an upload source'scurrent/directory is the only copy of its content anywhere. A/databackup can be restricted to those and skip the re-clonable gigabytes, which is exactly whatnpm run backupdoes.
Take the backup when no project is indexing: a re-index writes a second generation beside the live one
and pg_dump cannot filter rows, so a dump taken mid-run is twice the size. Expect a dump of roughly
2 KB per indexed chunk — vectors dump as text and compress back down — and expect most of a restore's
time to be the HNSW indexes being rebuilt: one per project, and each scans every chunk in the instance
to find its own project's rows, so a restore is sized by the instance and the project count together.
A backup holds project creation and deletion until it ends. pg_dump reads the database in one
snapshot, and creating a project builds its HNSW index with CREATE INDEX CONCURRENTLY while deleting
one drops it with DROP INDEX CONCURRENTLY — both wait for every transaction open when they start.
Creating a project (POST /api/projects or the dashboard), deleting one or importing one during a
backup returns when the backup finishes; nothing fails, and searches and indexing into existing
projects are not held. Schedule the backup away from the hours projects are created.
POSTGRES_PASSWORD is applied when the cluster is created. To change it later run
ALTER USER contextator PASSWORD '...' via psql and update .env before the next start.
Upgrading
An ordinary image upgrade is a docker compose pull; it moves the application and not the cluster. The one
upgrade that needs a procedure is a PostgreSQL major-version change.
Upgrading PostgreSQL across a major version
An ordinary image upgrade never does this. docker compose pull moves the application; it does not
move the cluster, because the cluster is a volume and the image's PostgreSQL major version is what can
read it. The day a release moves from PostgreSQL 16 to 17 is the day this applies, and it applies
before the pull, not after.
What happens if you just pull:
The container exits 1, restart: unless-stopped starts it again, and it exits 1 again. Nothing is
damaged: the old cluster is untouched and the old image still reads it.
If that is where you are right now, this is the way out and it is one line. Put CONTEXTATOR_TAG
back to the major you were on, docker compose up -d, and you are exactly where you were — then do the
upgrade below, in order, on the running old image. Do not start with the rollback block further
down: that one restores a parked copy of the cluster, and on an accidental pull no copy has been
made yet, so it has nothing to restore from.
pg_upgrade is not the path, and it is worth knowing why: it needs the binaries of both majors
present at once, and this image carries exactly one. So the path is a logical dump and a restore — the
same npm run backup and npm run restore as above, run across the version boundary.
Find out what your cluster volume is actually called before anything else, because step 3 removes it
by name and docker run -v <name>:/from creates an empty volume when that name does not exist, without
an error. docker-compose.yml names it ${CONTEXTATOR_PGDATA_VOLUME:-contextator-pgdata}, so an
instance that set that variable has a different name — and one that set CONTEXTATOR_PGDATA_PATH has no
volume at all but a host directory, which is copied and moved with cp -a on the host instead.
Step 3 is one && chain rather than a list with a warning between the lines, and that is the whole
of the protection: docker volume inspect fails on a name that is not there, cp -a fails on a full
disk, the PG_VERSION comparison fails on a short copy — and any of those stops the chain before
docker volume rm. The removal is deliberately the last link, so "stopped" and "nothing was
removed" are the same sentence. Pasting a whole block in one go is how this is actually used, so the
guards are in the shell and not in the prose around it.
Read what STOPPED: actually says, because the two blocks say different things on purpose. Each
is the || of a chain, so it prints when any link fails, and what is true at that moment depends on
which side of the deletion the chain stopped on. In step 3 the deletion is the last link, so stopping
means the volume is still there. In the rollback it cannot be last — the volume has to be emptied
before it can be refilled — so the emptying and the refilling are one container command, and the
message states the only thing true in every failure of that chain: the parked copy was only read. A
message that said "nothing has been deleted" in both places would be wrong in one of them.
On an instance that keeps the cluster in a host directory (CONTEXTATOR_PGDATA_PATH) there is no
volume in any of this: copy that directory aside with cp -a on the host, check the copy, and empty
the original instead of removing a volume.
The rollback is step 3 in reverse, and it is a copy rather than a rename — Docker has no
volume rename. Put CONTEXTATOR_TAG back to the version you were on, then:
That throws away whatever the new major had in it, which after step 5 is the restored instance — so it is a rollback to the moment of step 1 and not to the moment you run it. Anything indexed in between is re-indexed.
Three things to know before you start.
SECRET_KEYhas to be the same on the other side. It is not in the archive, and the restore stops before writing if it is missing or different. Have.envin front of you — and if you have rotatedSECRET_KEYsince this archive was taken,.envis now the wrong key: a pre-rotation archive can only be restored with the key it was taken under, so use the retired key here or take a fresh backup before starting.- The restore rebuilds every index, and that is most of the wall clock. Size the maintenance window from the whole instance's chunk count.
- The restore refuses the other direction. A dump taken from 17 will not go into a 16 server, and it says so before writing rather than half-applying. So a rollback is the volume, not the dump.
This section is the only copy of this procedure. It is written here, beside the code that
implements backup and restore and versioned with it, because a procedure that deletes a cluster has
to have exactly one copy that decides what it says. The wiki and the documentation site point here and
do not repeat the commands.
Documentation
- Document sources — source types, versions, sync, webhooks
- Accounts and permissions
- MCP access — open, token required, account required; OAuth for browser clients
- Connecting AI clients
- File types
- Configuration — reverse proxy, embedding with a server of your own, changing the model
- Deployment topologies — external PostgreSQL, Kubernetes
- Admin API and scaling signals
- Where your data goes, Security notes, Troubleshooting, License
- The full guides are on contextator.com, and installing from source is under Installation.
- Compatibility policy — what
1.xpromises not to break
Project Status
Stable from 1.0.0. The public contracts — the MCP and admin APIs, environment variables, the CLI, the
migrations and the backup archives — change in a major version only. What is covered, how a deprecation runs, the
stated exceptions and what is deprecated now are in docs/compatibility.md. Changes are
listed in CHANGELOG.md.
Contributing
Contributions are welcome. CONTRIBUTING.md is the whole of how this project is worked on:
getting it running, the four checks a change has to pass, the five places where one statement is kept in two
files, and the commit register. It also explains the licence grant an outside contribution is asked for
(CLA.md, signed on the pull request), which exists because the commercial license below depends on
the copyright being held in full. CODE_OF_CONDUCT.md is the Contributor Covenant, and it
applies here.
Security
Found a security problem? Do not open an issue. SECURITY.md says where it goes instead,
and lists the things that look like vulnerabilities and are documented behaviour — a project left open, and
what an MCP token does and does not grant. What the defaults guarantee is
in Security notes.
License
Licensed under the GNU Affero General Public License, version 3 or later (AGPL-3.0-or-later).
If you modify Contextator and let people reach your modified version over a network, you owe those users its
complete source; running the unmodified software triggers nothing. The copyright is held in full by Muhammet
Şafak, so a separate commercial license can be granted where the AGPL does not fit — ask at
tunedness.com. The full explanation is in docs/license.md.
Source: README.md at commit 9d72c5f
Tools
0Version history
2- v1.0.0LatestNov 4, 2026
- v0.7.0Nov 4, 2026
Score
60% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 daysRequired25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declaredRequired10 pts
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts