Skip to main content
Glama

ok-wiki-skills

A local MCP connector and Codex plugin that lets Claude and Codex search, read, create, and edit pages in the ok-wiki knowledge base.

It reaches the wiki over HTTP with an API key that never enters the model's context, and speaks MCP two ways: over stdio as a child process of Claude Code or the self-contained Codex plugin, or over Streamable HTTP for remote hosts such as claude.ai and ChatGPT, which need a public HTTPS URL (or a supported secure tunnel) and OAuth (docs/adr/0006).

Start at docs/architecture.md.

What it can do

  • Search and read — full-text search, fetch a page by path or id, list pages by tag or recency

  • Create and edit — create markdown pages at a path the connector derives (see below); patch an existing page's content, title, description, tags, or published state

  • History — list a page's revisions, fetch a specific version, restore a page to an earlier one

  • Tags — list and autocomplete the wiki's existing tag vocabulary

  • Assets — upload a file and get back a ready-to-paste markdown reference; list assets and create folders

Related MCP server: requarks-wiki-mcp

What it deliberately cannot do

No moving, renaming, or deleting pages. Those break inbound links or lose content, and a mistyped path is exactly the error a model makes. Do them in the ok-wiki UI. The boundary is enforced by the tool surface—move and delete tools do not exist—and the connector group withholds delete:pages. The group still needs manage:pages because ok-wiki requires it for single-page reads. Reasoning: docs/adr/0005.

Also out of scope: comments, navigation, users and groups, theming, and administrative operations.

Where new pages land

You don't choose the path—each creation tool derives it. There is no path input on any creation tool. Claude's wiki_create_page takes three human names and slugifies each into one segment:

project: "Moontower"  chatTitle: "Release planning"  artifactName: "Deployment checklist"
                              ↓
             claude/moontower/release-planning/deployment-checklist

Codex uses the separate wiki_create_codex_page contract:

workspace: "wiki-skills"  threadTitle: "Add Codex support"  artifactName: "Plugin guide.md"
                                      ↓
                 codex/wiki-skills/add-codex-support/plugin-guide

Stapler documents use wiki_create_stapler_page, which files into folders a human already made:

folder: "Moontower AI"  subFolder: "Wiki Skills"  artifactName: "Architecture.md"
                              ↓
              stapler/moontower-ai/wiki-skills/architecture

The claude/, codex/, and stapler/ roots preserve host provenance. Slugification lowercases, turns spaces, underscores and dots into hyphens, drops a trailing file extension, and discards anything outside a-z, 0-9, and -.

wiki_create_stapler_page carries one extra rule the other two don't: folder and subFolder must already exist, and it never creates them. Both values come from the user asking for the document—the model is told not to infer them. Before writing, the tool asks the wiki's page tree whether the parent folder is there and refuses if it isn't, naming the missing level. ok-wiki has no folder entity for pages (pageTree folder rows are synthesized from the paths of existing pages), so the first page under a new Stapler folder has to be created in the ok-wiki UI; every later document can go through the tool.

Because there is no move tool, a wrong path is permanent—which is why placement is a schema rule rather than advice. Pages that predate these conventions stay where they are and remain editable by path through wiki_update_page. Claude conventions live in SKILL.md; Codex policy lives in plugins/ok-wiki/skills/wiki-authoring/SKILL.md.

Quickstart

1. Build

Install Node.js 22 or newer, then:

cd /path/to/wiki-skills
npm install
npm run build          # MCP registration points at dist/index.js, not src/

2. Enable the ok-wiki API

In the wiki: Administration → API Access → enable. Without this, every request is rejected with "API is disabled. You must enable it from the Administration Area first."

3. Mint a scoped API key

Create a group for the connector granting exactly:

read:pages, read:source, write:pages, read:history, read:assets, write:assets, manage:pages

Grant them in both the group's global permissions and its page rules — the wiki checks both layers. manage:pages looks like more than a reader needs, but the wiki's single-page resolvers require it: without it, page lookups fail with "You are not authorized to view this page" even though listing works (docs/ok-wiki-api.md §4).

Then Administration → API Access → New API Key, bound to that group.

read:source is the one that fails quietly if you forget it — pages come back with a null body instead of an error. Use a dedicated key so it can be revoked independently and so wiki history distinguishes agent edits from human ones. Details: docs/ok-wiki-api.md §2.

4. Register with Claude Code

claude mcp add wiki \
  --env WIKI_BASE_URL=http://localhost:3000 \
  --env WIKI_API_KEY=<your-api-key> \
  -- node /path/to/wiki-skills/dist/index.js

Or, to share it with a project via .mcp.json:

{
  "mcpServers": {
    "wiki": {
      "command": "node",
      "args": ["/path/to/wiki-skills/dist/index.js"],
      "env": {
        "WIKI_BASE_URL": "http://localhost:3000",
        "WIKI_API_KEY": "${WIKI_API_KEY}"
      }
    }
  }
}

Prefer the ${VAR} form in any file you might commit — don't put the key in version control.

5. Verify

Run /mcp in Claude Code and confirm wiki is connected with its tools listed. Then ask for something read-only, like "search the wiki for onboarding", to exercise wiki_search_pages.

Codex plugin installation

The repository marketplace packages ok-wiki version 1.0.0 from plugins/ok-wiki for Codex CLI and other local Codex hosts. It uses the bundled ./mcp/server.mjs and local ok-wiki MCP server definition. This is not the package used by hosted ChatGPT. Writable mode publishes 15 total tools; readonly mode publishes 8 read-only tools. The plugin requires Node.js 22 or newer, matching package.json (engines.node: ">=22").

From a clean clone, install dependencies and deterministically rebuild the checked-in bundle:

npm install
npm run build:plugin

The plugin reads required WIKI_BASE_URL and WIKI_API_KEY values from the local process environment. It also forwards the seven optional variables WIKI_LOCALE, WIKI_TIMEOUT_MS, WIKI_READONLY, WIKI_MAX_CONTENT_BYTES, WIKI_LOG_LEVEL, WIKI_MAX_UPLOAD, and WIKI_UPLOAD_ALLOWLIST. Keep secret values only in local environment configuration; never add them to the plugin, .mcp.json, .codex-plugin/plugin.json, or .agents/plugins/marketplace.json.

Codex CLI

Register the repository marketplace, verify discovery, and install the plugin in this exact order:

codex plugin marketplace add <repo-root>
codex plugin list --marketplace wiki-skills --available --json
codex plugin add ok-wiki@wiki-skills

Start a fresh Codex CLI thread and confirm /mcp lists the ok-wiki server. After local source changes, run npm run build:plugin, rerun codex plugin add ok-wiki@wiki-skills, and start a new thread so the rebuilt artifact is loaded.

ChatGPT desktop and chatgpt.com

Do not use the repository marketplace or the local stdio package for the ChatGPT interface. The desktop application uses the same hosted ChatGPT connection and plugin records as chatgpt.com; it does not inherit WIKI_BASE_URL or WIKI_API_KEY from the shell that launched it. Use the public HTTP/OAuth server, register it as a ChatGPT MCP app, and install the separate plugins/ok-wiki-chatgpt package described below.

Authoring modes

The bundled skill starts in ask mode: creating Markdown does not write to the wiki or trigger an unsolicited save offer. An explicit request to save, post, or publish a new Markdown artifact uses wiki_create_codex_page.

A clear instruction such as "use auto mode" enables conversation-local automatic saving until the user disables it. Auto mode saves each completed .md file newly created by Codex during the active task exactly once after finalization. It excludes edited pre-existing Markdown, scratch files, files created by another process, and non-Markdown outputs. Auto mode is never persisted and never converts a collision into an update; existing pages require an explicit wiki_get_page → wiki_update_page workflow.

The product and safety contracts for this workflow are docs/codex-plugin-architecture.md, docs/codex-plugin-prd.md, and docs/codex-plugin-tasks.md.

Connecting from ChatGPT or claude.ai

Remote clients do not spawn this repository's local process. They reach a public HTTPS URL (or a supported secure tunnel), so the connector runs its own OAuth 2.1 authorization server (docs/adr/0006). Client registration is automatic through CIMD or DCR; do not configure a static OAuth client ID or secret.

1. Run the HTTP entrypoint

WIKI_BASE_URL=http://localhost:3000 \
WIKI_API_KEY=<your-api-key> \
WIKI_MCP_BEARER=$(openssl rand -base64 32) \
WIKI_MCP_PUBLIC_URL=https://wiki.example.com \
WIKI_MCP_HOST=127.0.0.1 \
npm run http

npm run http runs the compiled dist/http.js, so step 1 above has to have happened.

WIKI_MCP_PUBLIC_URL is the OAuth issuer and must match the URL you give the remote client, minus the /mcp path. Setting it is what turns OAuth on; without it the entrypoint stays static-bearer only.

To run it as a service rather than a foreground process, see deploy/README.md — a hardened systemd user unit, plus the two settings whose obvious values are the wrong ones.

Redeploying after a source change

The unit runs dist/http.js, which systemd loaded into a long-lived process at start. Building new output does not touch that process — restart it, or the old code keeps serving:

npm run build
systemctl --user restart wiki-skills-http
systemctl --user status wiki-skills-http --no-pager

This is a user unit, so --user is required and sudo is wrong — sudo systemctl restart wiki-skills-http looks for a system unit that does not exist. (sudo systemctl restart wiki.service, without --user, is the wiki itself — a different service.)

A restart drops nothing a client will notice: the entrypoint is stateless, with a fresh McpServer per request, and issued OAuth tokens survive in WIKI_MCP_STATE_FILE. Clients reconnect on their next call without re-consenting. Only edits to the unit file itself need systemctl --user daemon-reload first.

The failure this prevents is a quiet one: the connector stays up and answers normally, just with the previous build's tool list — a new tool never appears, and a fixed bug is still there.

2. Put an HTTPS ingress in front

TLS and the public hostname belong to the ingress, not this process. With a Cloudflare Tunnel, point the public hostname at the origin, with no path:

ingress:
  - hostname: wiki.example.com
    service: http://127.0.0.1:8787

If cloudflared runs in a Docker container, 127.0.0.1 there is the container, not your host — use the bridge gateway (typically http://172.17.0.1:8787) and set WIKI_MCP_HOST to match. Point the tunnel's health check at /healthz, which is unauthenticated; /mcp answers 405 to GET.

3. Register the MCP app in ChatGPT

Use the ChatGPT Plugins interface in a browser and create an MCP app/connection using:

https://wiki.example.com/mcp

The exact navigation and labels vary by ChatGPT account and workspace. Do not rely on a Settings → Security and login → Developer mode toggle: it was not present in the account used for this integration. Likewise, do not manually enter an OAuth client ID, client secret, or API key. ChatGPT registers itself as a public client and negotiates token_endpoint_auth_method=none.

Complete browser authorization using WIKI_MCP_BEARER as the consent passphrase. Enable the write scope only if ChatGPT should create and edit pages. After ChatGPT scans the tools, record the generated technical app ID; it has the form plugin_asdk_app_....

An MCP app exposes tools, but it does not automatically install this repository's authoring skill. Build the upload archive with:

npm run package:chatgpt-plugin

This validates the package and writes dist/ok-wiki-chatgpt-plugin.tar.gz. In ChatGPT's plugin creator, attach that archive and ask it to create/install a personal plugin from the package. The checked-in .app.json maps the plugin to the registered technical app ID. Start a new conversation after installing or updating it so the skill and tools are reloaded.

See docs/chatgpt-plugin-installation.md for the complete procedure, update workflow, and the OAuth failures encountered during the first installation.

ChatGPT's production OAuth callback is under https://chatgpt.com/connector/oauth/; the server learns the exact redirect URI through CIMD or dynamic registration and validates it on every OAuth exchange.

4. Add the connector to claude.ai

In claude.ai, Settings → Connectors → Add custom connector, URL:

https://wiki.example.com/mcp

The /mcp suffix is required. Approve in the browser using WIKI_MCP_BEARER as the passphrase, and tick Create and edit pages if you want write access — it is unchecked by default, and a read-only grant publishes 8 tools instead of 15.

Rotating credentials

WIKI_MCP_BEARER guards two doors: it is the header credential and the consent passphrase. Rotating it closes both but does not invalidate tokens already issued — for that, delete WIKI_MCP_STATE_FILE, which is the revoke-everything gesture. That file holds the token signing key, so it lives at mode 0600; the server refuses to start if that has slipped.

Configuration

Variable

Required

Default

Purpose

WIKI_BASE_URL

yes

—

e.g. http://localhost:3000 — http://ok-wiki.local also serves the wiki

WIKI_API_KEY

yes

—

ok-wiki API key (JWT)

WIKI_LOCALE

no

en

Default locale

WIKI_TIMEOUT_MS

no

15000

Per-request timeout

WIKI_READONLY

no

0

1 registers only read tools

WIKI_MAX_CONTENT_BYTES

no

100000

Cap on page body returned into context

WIKI_LOG_LEVEL

no

info

Diagnostics, always to stderr

WIKI_MAX_UPLOAD

no

10485760

Max upload size in bytes for wiki_upload_asset (10 MB)

WIKI_UPLOAD_ALLOWLIST

no

unset

Colon-separated absolute path prefixes uploads may come from; unset = unrestricted

WIKI_MCP_HOST

no

127.0.0.1

Bind address for the remote HTTP entrypoint

WIKI_MCP_PORT

no

8787

Listen port for the remote HTTP entrypoint

WIKI_MCP_BEARER

no

unset

Static bearer token for header-auth clients; also the consent passphrase

WIKI_MCP_PUBLIC_URL

no

unset

Public https:// origin — the OAuth issuer. Unset disables OAuth

WIKI_MCP_STATE_FILE

no

$XDG_STATE_HOME/wiki-skills/oauth.json, else ~/.local/state/wiki-skills/oauth.json

OAuth signing key and issued-token state

WIKI_MCP_CLIENT_HOSTS

no

claude.ai,chatgpt.com

Hosts whose OAuth client metadata may be fetched; an explicit override must retain every remote client you use

Development

Command

What it does

npm run build

Compile the shared stdio and HTTP entrypoints into dist/

npm run build:plugin

Deterministically rebuild the checked-in self-contained Codex bundle

npm run package:chatgpt-plugin

Validate and rebuild dist/ok-wiki-chatgpt-plugin.tar.gz for upload

npm run dev

Watch-mode stdio server via tsx — no build step

npm test

Full vitest suite, including the doc-consistency tests that pin this README to src/config.ts

npm run lint

ESLint, which carries the rule that forbids writing to stdout

npm run smoke

End-to-end against a live wiki; needs WIKI_BASE_URL and WIKI_API_KEY. Creates one unpublished throwaway page and one small asset, and deletes nothing — clean up by hand

systemctl --user restart wiki-skills-http

Load a new build into the deployed HTTP connector. npm run build alone leaves the running process on the old code — see Redeploying after a source change

Documentation

Document

What's in it

docs/architecture.md

System context, module layout, invariants, request flows, error model, security, testing

docs/codex-plugin-architecture.md

Codex desktop/CLI plugin packaging, page namespace, authoring modes, and compatibility design

docs/codex-plugin-prd.md

Numbered Codex plugin requirements, milestones, acceptance criteria, risks, and rollout plan

docs/codex-plugin-tasks.md

Developer/QA task pairs, dependency graph, milestone gates, and completion criteria for the Codex plugin

docs/codex-plugin-development-status.md

Implementation ledger, completed QA evidence, and remaining manual release work

docs/chatgpt-plugin-installation.md

Working hosted ChatGPT setup, update procedure, and first-install failure record

docs/tool-surface.md

Every tool's inputs, outputs, and behavior

docs/ok-wiki-api.md

Upstream endpoints, auth, permissions, and the gotchas that shape the design

SKILL.md

The wiki-authoring skill — placement, naming, and formatting conventions the model follows

docs/conventions.md

Survey of how the live wiki is actually written, which is where those conventions came from

deploy/README.md

Running the HTTP entrypoint as a systemd user unit

docs/prd.md

Numbered requirements, milestones, and acceptance criteria — the build plan, decomposed into tasks in docs/tasks.md

docs/adr/

Why stdio, why GraphQL, why TypeScript, why read-modify-write, why no deletes, why our own OAuth server

If you read only one thing before writing code, make it docs/adr/0004: ok-wiki's pages.update is a full replace, not a patch, and a naive implementation silently unpublishes pages and drops their tags.

Troubleshooting

Symptom

Cause

Connector won't connect; no useful error

Something wrote to stdout. Under stdio, stdout is the protocol channel — all logging must go to stderr.

Tools missing after a source edit

You edited src/ but the server runs dist/. Re-run npm run build.

A new tool or fix is missing from the remote connector, which is otherwise healthy

The deployed process is still on the previous build. npm run build does not restart it: systemctl --user restart wiki-skills-http. Confirm the reload with systemctl --user status wiki-skills-http — the start time should be the restart, not the original boot.

Codex still uses old plugin behavior

Re-run npm run build:plugin, reinstall or restart the plugin for that surface, and start a fresh thread.

"API is disabled"

Step 2 not done.

"API Key is invalid or was revoked"

Key revoked or expired; mint a new one.

Listing works but fetching a single page is "not authorized"

The key's group is missing manage:pages (required by the single-page resolvers), or lacks it in the group's page rules.

Page reads succeed but content is null

The key's group is missing read:source.

Edits rejected as conflicts

Someone edited the page after your read. Re-read and retry.

claude.ai fails at the authorize step

WIKI_MCP_PUBLIC_URL must be the connector URL minus /mcp, exactly. It is the OAuth issuer, and a mismatch fails discovery.

"cannot reach the wiki"

ok-wiki isn't up. It runs as a systemd unit — check systemctl status wiki.service, and journalctl -u wiki.service for why it stopped.

License

MIT

Available Tools

13 tools
wiki_create_asset_folderCreate a wiki asset folderA

Create a new asset folder under a parent folder (parentFolderId 0 = root). If the folder already exists the error says so — reuse the existing folder in that case.

Inputs: slug (required), name, parentFolderId.

wiki_create_pageCreate wiki pageA

Create a new markdown page in the ok-wiki at the given path. The path is validated locally first (no dots, spaces, backslashes, or double slashes). If a page already exists at the path, the error says so and points at wiki_update_page — edit rather than re-create.

Inputs: path (required), title (required), content (required), tags, +3 more.

wiki_get_pageGet a wiki pageA

Fetch a single ok-wiki page — by numeric id or by path (plus locale) — with its metadata and markdown source. The returned updatedAt is the conflict checkout stamp: pass this page to wiki_update_page promptly rather than editing against a stale read. contentTruncated: true means the body was cut at WIKI_MAX_CONTENT_BYTES and is not the full page. Single-page reads require the manage:pages permission, and content requires read:source, on the API key's group.

Inputs: id, path, locale, includeContent.

wiki_get_page_versionFetch one historical page versionA

Fetch one historical revision of a page as a full snapshot: content, title, description, tags, path, isPublished, versionDate, authorName. Content is bounded by WIKI_MAX_CONTENT_BYTES under the same rule as wiki_get_page — when cut, the result carries an explicit truncation marker and contentTruncated: true. Pair two calls to show a before/after when explaining a change.

Inputs: pageId (required), versionId (required).

wiki_list_assetsList wiki assetsA

List the assets (uploaded files) in a wiki asset folder. folderId 0 is the root folder. Returns id, filename, ext, kind, mime, fileSize (bytes), and url per asset; url assumes the root folder — for assets in another folder, prefix its folder path.

Inputs: kind, folderId.

wiki_list_pagesList wiki pagesA

Enumerate ok-wiki pages without a search query — for "what's under projects/?" or "what's tagged runbook?". Filters by tag server-side; pathPrefix is a client-side filter over the fetched page of results only. Returns { pages, count }.

Inputs: tags, limit, orderBy, direction, +1 more.

wiki_list_tagsList wiki tagsA

Lists the wiki's entire tag vocabulary as { tags: [{ tag, title }] }, where tag is the slug used when writing pages and title is its display text. Call this before creating or retagging a page so new pages reuse the existing vocabulary instead of inventing near-duplicate tags.

Inputs: no named parameters.

wiki_page_historyList page historyA

List the revision history of a page: a trail of versions (versionId, versionDate, authorName, actionType — initial/edit/move; a restore shows as an edit here, and the snapshot it created reports action 'restored' via wiki_get_page_version) plus total, the true version count, so a sliced trail is distinguishable from the whole history. Attribution limit: every edit made through this connector is attributed to user id 1 ('API'), the wiki’s synthetic API user — the author column separates agent edits from human ones, but cannot tell one agent session from another. Pass a versionId to wiki_get_page_version to inspect a snapshot.

Inputs: id (required), limit, offsetPage.

wiki_restore_page_versionRestore a page to an earlier versionA

Roll a page back to an earlier version from its history — the undo for everything else this connector can do. Restoring is non-destructive in the sense that matters: it appends a new version to the page’s history rather than erasing anything, the intermediate edits stay in the trail, and a mistaken restore can itself be restored away. Get the versionId from wiki_page_history, and preview it with wiki_get_page_version before restoring.

Inputs: pageId (required), versionId (required).

wiki_search_pagesSearch wiki pagesA

Full-text search across ok-wiki pages — the first tool to reach for when asked about anything in the wiki. Matching is substring-ish, not semantic: the default search engine is a database LIKE scan over title, description, and path, so search for literal words that would appear on the page rather than paraphrases. Returns { results, totalHits, suggestions }; results are capped at limit, while totalHits reports the true match count so you can tell when you are seeing a slice.

Inputs: query (required), path, limit.

wiki_search_tagsSearch wiki tagsA

Searches the existing tag vocabulary and returns matching tag slugs as { tags: string[] }. Use it before creating a page to check whether a tag you are about to invent already exists in a near-duplicate form, so pages converge on shared vocabulary.

Inputs: query (required).

wiki_update_pageUpdate wiki pageA

Edit an existing wiki page with a partial patch, safely: the tool reads the current page, checks for concurrent edits, merges only the fields you supply, and writes the complete page back. Everything you do not supply — title, description, tags, published state, publish window, scripts, path, locale — is preserved exactly as read. Note that tags REPLACES the whole tag list. This tool never moves, renames, or deletes a page; an identical no-op patch writes nothing.

Inputs: id, path, tags, title, +3 more.

wiki_upload_assetUpload a wiki assetA

Upload a local file to the wiki as an asset and return its id, URL, and a ready-to-paste markdown reference. The file is read from the machine running this server, subject to WIKI_MAX_UPLOAD and WIKI_UPLOAD_ALLOWLIST. The returned url and markdown assume the root folder; for a non-root folderId, prefix the folder path.

Inputs: filePath (required), altText, folderId.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 13 tools

    Disambiguation4/5

    Most tools target a distinct resource and action (search vs list, current vs version, page vs asset). The two tag tools (list_tags and search_tags) overlap in purpose but their descriptions clearly distinguish batch listing from duplicate-checking, so an agent can separate them. Overall low ambiguity.

    Naming Consistency4/5

    All tools share the wiki_ prefix and mostly follow verb_noun (list_tags, get_page, create_page). The one exception is wiki_page_history, which is noun_noun rather than a verb form, making it slightly inconsistent. Otherwise the pattern is predictable.

    Tool Count5/5

    At 13 tools, the set is well-scoped for a wiki server covering pages, tags, history, and assets. Each tool serves a clear function without redundancy. This is within the ideal range and not overwhelming.

    Completeness2/5

    The page lifecycle is incomplete: there is no delete or move/rename tool, and update_page explicitly refuses to do so. Asset support also lacks folder listing, deletion, and update, leaving obvious dead ends. These gaps would cause agent failures when cleanup or reorganization is needed.

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      An MCP Server implementation that enables managing Confluence wiki pages through natural language queries, supporting operations like creating, updating, deleting, and searching pages across different knowledge bases.
      2
      -
    • A
      license
      A
      quality
      D
      maintenance
      An MCP server that enables AI agents to interact with Wiki.js as a knowledge base through a comprehensive set of 29 tools for content retrieval and management. It supports full-text search, page versioning, and asset browsing with optional write operations secured by safety gates.
      29
      46 npm
      8
      MIT