
Security News
GPT-6 Astra Attempts Supply Chain Attacks Against Open Source Maintainers in Testing
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.
opengist-mcp
Advanced tools
Model Context Protocol (MCP) server for Opengist, the self-hosted pastebin powered by Git
A Model Context Protocol (MCP) server for Opengist, the self-hosted pastebin powered by Git.
Lets MCP clients like Claude Code, Claude Desktop or Codex read, search, create, update and delete gists on your own Opengist instance: file contents and revisions, commit history, forks and likes, plus your user account.
Fourteen tools is the ceiling, not the floor: OPENGIST_ALLOW_TOOLS=essential
registers a curated seven instead, and a model picks the right tool far more
reliably from seven than from fourteen — see
choosing which tools load.

Note: this server talks to the Opengist REST API under
/api, which is available in recent Opengist releases and enabled by default (api.enabled). A running instance serves its own OpenAPI spec atGET /api/openapi.yaml— compare it against your version if a tool behaves unexpectedly.
Fourteen tools over one API surface, derived from the Opengist REST API and verified against a live instance: reading, searching, writing, forking and liking gists, including revisions, commit history and raw file access.
Bounded by construction. File contents are capped per file and against an overall budget, binary files are never dumped as text, and every truncation names the call that fetches the rest.
Node.js 22 or newer
An Opengist instance with the REST API enabled
An Opengist Personal Access Token (Settings → Access Tokens). Scopes:
gist:read — read gists, including your private and unlisted onesgist:write — create, update, delete and fork gistsuser:read — read your own accountuser:write — only needed for set_gist_likeA token without gist:read still works, but the API then silently returns only public gists instead of failing.
| Variable | Required | Description |
|---|---|---|
OPENGIST_URL | yes | Base URL of your instance, e.g. https://gist.example.com. A trailing /api is accepted and stripped. |
OPENGIST_TOKEN | yes | Personal Access Token, starts with og_ |
OPENGIST_READ_ONLY | no | true, 1 or yes registers only the read tools; the write tools do not exist at all in that session |
OPENGIST_INSECURE_TLS | no | true accepts self-signed certificates, scoped to the Opengist connection (never process-wide) |
OPENGIST_ALLOW_TOOLS | no | Comma-separated tool names, list_* prefixes, or essential for a curated preset |
OPENGIST_DENY_TOOLS | no | Same syntax; removed from whatever OPENGIST_ALLOW_TOOLS left |
ELICITATION | no | false replaces the approval dialog with the two-call token. Not prefixed |
The token is read once at startup and then removed from
process.env, so it is not visible to child processes. Usehttps://for anything but a loopback address — over plain http the token and every gist travel in cleartext.If your instance's
external-urlis not configured, the URLs Opengist reports (and this server passes through) point atlocalhost. Setexternal-url/OG_EXTERNAL_URLon the instance so links are usable.
OPENGIST_ALLOW_TOOLS and OPENGIST_DENY_TOOLS take comma-separated tool names;
a trailing * matches a whole family. essential is a curated preset of
seven: list_gists, search_gists, get_gist, get_gist_file, create_gist, update_gist, delete_gist.
OPENGIST_ALLOW_TOOLS=essential
OPENGIST_ALLOW_TOOLS=list_gists,get_gist_file,create_gist
OPENGIST_DENY_TOOLS=delete_*
An entry that matches no tool aborts startup and names it, so a typo cannot
silently hide a tool — an absent tool is not something anyone traces back to an
environment variable. A filtered tool is never registered, so it is absent from
tools/list and unknown to tools/call alike, exactly like a write tool under
OPENGIST_READ_ONLY.
If you run several of these servers at once, mcp-hub
is the other answer — its /hub endpoint replaces every server's tools with six
meta-tools.
claude mcp add opengist -s user \
-e OPENGIST_URL=https://gist.example.com \
-e OPENGIST_TOKEN=og_your_token \
-- npx -y opengist-mcp
claude_desktop_config.json:
{
"mcpServers": {
"opengist": {
"command": "npx",
"args": ["-y", "opengist-mcp"],
"env": {
"OPENGIST_URL": "https://gist.example.com",
"OPENGIST_TOKEN": "og_your_token"
}
}
}
}
~/.codex/config.toml:
[mcp_servers.opengist]
command = "npx"
args = ["-y", "opengist-mcp"]
env = { OPENGIST_URL = "https://gist.example.com", OPENGIST_TOKEN = "og_your_token" }
docker run --rm -i \
-e OPENGIST_URL=https://gist.example.com \
-e OPENGIST_TOKEN=og_your_token \
ghcr.io/ni-c/opengist-mcp:latest
-i is required — the transport is stdio. Do not add -t; a TTY corrupts the
protocol stream.
git clone https://github.com/ni-c/opengist-mcp.git
cd opengist-mcp
npm install
npm run build
A client that cannot spawn a local process — ChatGPT connectors, Claude on the web,
Cursor, LibreChat — reaches opengist-mcp through mcp-hub: one
container serves many stdio MCP servers over Streamable HTTP, with an OAuth 2.1 login
behind a single password and long-lived tokens for the clients that cannot do OAuth. Its
/hub endpoint puts every server behind six meta-tools, so one connector reaches all of
them without N×tool schemas in the model's context, and it speaks both protocol revisions
— a question this server asks travels through it to the person at the far end.
Its /config/mcp.json uses Claude Code's format, so the entry is the one you already
have:
{
"mcpServers": {
"opengist": {
"command": "npx",
"args": ["-y", "opengist-mcp"],
"env": { "OPENGIST_ALLOW_TOOLS": "essential" },
"denyTools": ["delete_*"]
}
}
}
allowTools and denyTools there are the hub's own per-server filter, which is not
the same thing as *_ALLOW_TOOLS in env — the difference, and the mistake it invites,
are in the client guide.
| Tool | Description |
|---|---|
list_gists | List gists: your own, another user's, all public ones, or liked/forked ones (scope + optional username) |
search_gists | Find gists by title, description, topics or owner — a bounded client-side scan (Opengist has no search API) |
get_gist | Get one gist with its file contents, optionally at a revision; commits and forks on request |
get_gist_file | Get the raw content of a single file, at a revision and from an offset — for large or truncated files |
list_gist_commits | Commit history of a gist, newest first |
list_gist_forks | Forks of a gist |
get_user | Your own account, or another user by username or userId |
check_gist_like | Whether you liked a gist; distinguishes "not liked" from "not visible to you" |
| Tool | Description |
|---|---|
create_gist 👤 | Create a gist from a list of files. visibility is required; public/unlisted asks a person |
update_gist | Change title/description/visibility and write or rename files. Cannot delete files |
delete_gist_files 👤 | Delete files from a gist — the approval is bound to exactly those filenames |
delete_gist 👤 | Delete a gist permanently |
fork_gist | Fork a gist; reports whether a new fork was created or one already existed |
set_gist_like | Like or unlike a gist idempotently (reads the current state first, so a repeat call is not a toggle) |
Every tool declares an outputSchema and answers with structuredContent
alongside the text block, so a client can use the result without parsing prose:
{
"untrusted": true,
"source": "opengist",
"scope": "self",
"pagination": { "page": 1, "perPage": 20, "total": 42, "nextPage": 2 },
"gists": [{ "id": "abc123", "title": "…", "visibility": "private" }],
"notes": ["…"],
}
Every tool that reports gist content carries untrusted: true and
source: "opengist" as fields. This server has always said so in notes —
prose in a list, which a client can read but not check — and the field is what
makes it checkable. Three tools are without it, because their answer is entirely
this server's own words: check_gist_like, set_gist_like and delete_gist
report an id they were given and a boolean.
An over-budget result still drops file contents first. Where that is not enough
it is now an error: it used to answer with the JSON cut at the ceiling,
which a text block tolerates and structuredContent cannot.
delete_gist, delete_gist_files and widening a gist's visibility raise a real dialog through MCP elicitation where the client supports it — one the model cannot answer on its behalf. A plain confirm: true flag could be set by the model on its own, or be talked into it by text inside a gist. Where the client cannot show a dialog they refuse the first call and return a random, single-use token that expires after five minutes; that proves the call was made twice with the same arguments and nothing more, and the text says so. Either way the approval for delete_gist_files is bound to the exact set of filenames, so one for a single file cannot be replayed to delete another. ELICITATION=false takes the fallback deliberately; it never removes the guard. See Asking a person.public or unlisted gist, and writing files into a gist that already is one, are disclosure events: whatever the model has in its context becomes readable by others and cannot be withdrawn from anyone who already saw it. Both ask before they act. The approval is bound to the exact content, so one for a single file cannot be replayed with a second one attached. A call that makes the gist private in the same breath is not a disclosure and is not asked about.update_gist cannot delete a file. The Opengist API deletes a file when its entry is null or carries neither content nor filename — exactly the shape a sloppily built object has. This server therefore never exposes the raw file map; it accepts explicit write/rename operations and asserts before sending that no entry could be read as a deletion. Files you do not mention are left untouched.allowCreate: true is passed, and the refusal names a case-insensitive near match (readme.md vs README.md).search_gists states how much it scanned and marks incomplete results explicitly../.. and are URL-encoded, and upstream error bodies are truncated with HTML error pages dropped entirely.OPENGIST_READ_ONLY and the approval flow are client-side guards. The real boundary is the scope of your access token and the permission prompts of your MCP host. A token limited to gist:read/user:read cannot write, whatever the model attempts.Not a git client. It talks to the REST API. Cloning, pushing and branching are
git's job — clone_url and ssh_url come back so you can hand them to git.
Not an admin tool. There is nothing here for users, settings or instance
administration, and get_user returns an allowlisted set of fields rather than
whatever the API happens to include.
Not a search index. Opengist has no search endpoint, so search_gists works
with what the API offers rather than pretending to more.
OPENGIST_READ_ONLY=true does not register the write tools, which this server
enforces; a token scoped to gist:read and user:read cannot write whatever
this server or the model does. Use the second one when it matters.The full guide, tool reference and security notes live at
opengist-mcp.ni-c.de (source in docs/).
npm install
npm run build
npm test
npm run lint
Bump the version in package.json and server.json, move the ## [Unreleased] section of CHANGELOG.md to the new version, commit, then push a tag:
git tag -a v0.1.0 -m "v0.1.0"
git push origin main v0.1.0
The release workflow verifies that the tag matches the package version, publishes to npm via OIDC trusted publishing (no long-lived token), waits for the container image to appear on GHCR, registers the release in the MCP registry and creates a GitHub release from the changelog section.
If the registry step fails, fix it on main and dispatch mcp-registry.yml — never re-run the tagged job, which checks out the immutable tag.
Releases are tag-driven. Bump package.json, move the [Unreleased] notes in
CHANGELOG.md under the new version, commit, then:
git tag -s vX.Y.Z -m "vX.Y.Z"
git push origin main vX.Y.Z
The release workflow publishes to npm via Trusted Publishing (OIDC, with provenance), pushes the multi-arch container image to GHCR, creates the GitHub release from the CHANGELOG section, and updates the entry in the official MCP registry.
Issues, discussions and pull requests are welcome — see CONTRIBUTING.md. For vulnerabilities please use private reporting rather than a public issue; the policy is in SECURITY.md.
MIT © Willi Thiel
FAQs
Model Context Protocol (MCP) server for Opengist, the self-hosted pastebin powered by Git
The npm package opengist-mcp receives a total of 62 weekly downloads. As such, opengist-mcp popularity was classified as not popular.
We found that opengist-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.

Product
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.

Security News
pnpm 12 rewrites the package manager in Rust, cutting install times by up to 90% while preserving pnpm 11 workflows and lockfiles.