Wikantik exposes three separate agent-facing surfaces. Each is a distinct servlet path with its own auth filter, rate limiter, and tool set. Pick which surface to give an agent based on the trust you want to extend, then wire the client.
| Path | Module | Protocol | Tools | Capabilities | Auth filter | Default scope |
|---|---|---|---|---|---|---|
/wikantik-admin-mcp | wikantik-admin-mcp | MCP Streamable HTTP | 27 read + write + analytics | tools, resources, prompts, completions | McpAccessFilter | mcp (or all) |
/knowledge-mcp | wikantik-knowledge | MCP Streamable HTTP | 21 read-only retrieval + KG + spine + projection | tools | KnowledgeMcpAccessFilter | mcp (or all) |
/tools/* | wikantik-tools | OpenAPI 3.1 | 2 (search_wiki, get_page) | OpenAPI document at /tools/openapi.json | ToolsAccessFilter | tools (or all) |
/knowledge-mcp registers several tools conditionally on what a deployment has wired (find_similar, get_page_for_agent, assemble_bundle, get_briefing, get_ontology/sparql_query, list_stale_citations), so 21 is the fully-wired count — a leaner install legitimately advertises fewer. The admin surface does not vary: it is registered unconditionally so the advertised tool list cannot drift with wiring.
The two MCP endpoints share the same access-filter implementation and the same wikantik-mcp.properties, so a legacy property-file key (or a DB-backed mcp key) authorises both. The OpenAPI endpoint is independent: it has its own properties file and key scope.
Default to giving an agent /knowledge-mcp only. Authoring (/wikantik-admin-mcp) is a separate trust decision — grant it once you have decided that the agent should be allowed to write pages, mark them verified, propose KG entries, or rename/delete content. The OpenAPI surface exists for clients that cannot speak MCP at all (OpenWebUI, custom HTTP integrations, ChatGPT-style "Custom GPT" tools).
Mint a key. Log into the wiki as an admin, open the admin panel and go to API Keys (/admin/apikeys). Create a key bound to a real principal (e.g. your account or a dedicated service user) with scope mcp. Copy the plaintext token — it is shown once.
Smoke-test the endpoint with curl:
# /knowledge-mcp — list tools
curl -sS -X POST http://localhost:8080/knowledge-mcp \
-H "Authorization: Bearer $WIKANTIK_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
A 403 means the bearer token is wrong or the scope is insufficient. A 503 means the filter is fail-closed (no auth configured anywhere) — see Authentication below.
Wire it into your client. Drop the snippet for your client from Client setup below.
Sanity-check inside the client. Ask the agent to call assemble_bundle with a query you know lands on a specific page; confirm the cited URLs match.
Every Wikantik agent surface fronts a servlet filter that checks each request before it reaches the transport servlet. The filters share a uniform model:
A request is allowed if it satisfies any one of:
Bearer token that resolves to an active DB-backed API key with a matching scope.Bearer token that exactly matches one of the legacy property-file keys (mcp.access.keys / tools.access.keys).If none match, the response is 403 Access denied. If no keys, no CIDRs, and *.access.allowUnrestricted is unset, the filter is fail-closed and returns 503 MCP not configured (a CRITICAL line is also logged at startup). This is intentional: the rewrite preferred safe-by-default over silent open mode.
Managed via the admin UI at /admin/apikeys, backed by the api_keys table. Each key is bound to a Wikantik principal: when an agent presents the token, the filter wraps the request with that principal's identity. All downstream JAAS / ACL / policy-grant checks then run against that principal, so an agent only ever sees what its bound user can see, and writes are attributed to that user in version history and audit logs.
Available scopes (ApiKeyService.Scope):
| Scope | Wire string | Covers |
|---|---|---|
MCP | mcp | /wikantik-admin-mcp, /knowledge-mcp |
TOOLS | tools | /tools/* |
ALL | all | All three |
Scope matching uses Scope.matches() — ALL covers any required scope; MCP does not cover TOOLS and vice versa.
Keys are stored as SHA-256 hashes; the plaintext is shown exactly once at creation. Revoke via DELETE /admin/apikeys/{id} or the admin UI.
REST surface (admin-only, protected by AdminAuthFilter):
GET /admin/apikeys list keys (hash masked, no plaintext)
POST /admin/apikeys generate; response carries one-time plaintext
DELETE /admin/apikeys/{id} revoke
For lightweight single-user setups (or environments without a database), comma-separated bearer tokens can be set in properties:
mcp.access.keys = generated-token-1,generated-token-2
tools.access.keys = different-token-for-openapi-clients
Legacy keys have no principal binding — every call lands as the wiki's default unauthenticated identity. Use only on private/local instances.
mcp.access.allowedCidrs = 10.0.0.0/8,127.0.0.1/32
tools.access.allowedCidrs = 10.0.0.0/8
Useful when an agent runs on a sidecar pod / loopback and you want to skip the bearer token entirely. CIDR matches are evaluated against ServletRequest.getRemoteAddr() — make sure your reverse proxy preserves the client IP (or the CIDR list is meaningless).
mcp.access.allowUnrestricted = true
tools.access.allowUnrestricted = true
Every request is treated as a superuser. Only use in trusted, single-tenant local development. When set, the filter logs a startup warning each time the webapp comes up.
Token-bucket rate limiter applied after auth. Defaults from wikantik-mcp.properties:
mcp.ratelimit.global = 100 # requests/sec across all clients
mcp.ratelimit.perClient = 10 # requests/sec per resolved client identity
/tools/* has its own knobs (tools.ratelimit.global, tools.ratelimit.perClient, both default 0 = disabled). Excess requests get 429 Rate limit exceeded with a Retry-After: 1 header. Rate-limit hits are written to the SecurityLog logger.
All keys are picked up from a wikantik-mcp.properties (or wikantik-tools.properties) loaded from the classpath. Defaults ship inside the JAR; operators override by placing a file with the same name on a parent classloader (e.g. tomcat/tomcat-11/lib/wikantik-mcp.properties).
wikantik-mcp.properties (admin MCP + knowledge MCP)| Property | Default | Notes |
|---|---|---|
mcp.server.name | wikantik-mcp | Reported in the MCP initialize handshake. |
mcp.server.title | Wikantik Knowledge Base | Human-readable server title. |
mcp.server.version | 2.0.0 | Reported alongside Release.getVersionString() to clients. |
mcp.instructions.file | wikantik-mcp-instructions.txt | Classpath resource read at boot. Replaced by setting mcp.instructions=... inline. |
mcp.access.keys | (empty) | Legacy comma-separated bearer tokens. |
mcp.access.key | (empty) | Legacy single-token form (prefer mcp.access.keys). |
mcp.access.allowedCidrs | (empty) | Comma-separated CIDR allowlist. |
mcp.access.allowUnrestricted | false | Acknowledges the fail-closed default. |
mcp.ratelimit.global | 100 | Requests/sec across all clients. |
mcp.ratelimit.perClient | 10 | Requests/sec per resolved client. |
The same file feeds both McpAccessFilter (admin) and KnowledgeMcpAccessFilter (knowledge), so one configuration block governs both endpoints.
wikantik-tools.properties (OpenAPI tool server)| Property | Default | Notes |
|---|---|---|
tools.access.keys | (empty) | Legacy comma-separated bearer tokens. |
tools.access.allowedCidrs | (empty) | Comma-separated CIDR allowlist. |
tools.access.allowUnrestricted | false | Acknowledges the fail-closed default. |
tools.ratelimit.global | 0 | 0 disables rate limiting. |
tools.ratelimit.perClient | 0 | 0 disables rate limiting. |
wikantik.public.baseURL | (request scheme/host) | Used to build absolute citation URLs in tool responses. |
| Property | Effect |
|---|---|
wikantik.datasource | Enables DB-backed API keys, structural index, KG inclusion policy, retrieval-quality CI. Defaults to JNDI jdbc/wikantikDS. |
wikantik.admin.bootstrap | Bootstrap admin override for first-time setup; bypasses ACLs at startup only. |
wikantik.indexnow.apiKey | Required by ping_search_engines for IndexNow submissions. |
wikantik.structural_spine.enforcement.enabled | Default true. Auto-assigns canonical IDs on save. |
wikantik.cluster_declaration.enforcement.enabled | Default false. When enabled, a duplicate cluster declaration is a 422 at save. Ships dark: enabling it against a corpus that already contains duplicates makes the offending hub pages un-saveable — check /admin/drift first. |
wikantik.runbook.enforcement.enabled | Default true. Schema-validates type: runbook pages at save time. |
wikantik.frontmatter.enforcement.enabled | Default true. Save-time guard that rejects pages whose YAML frontmatter fails strict parsing (e.g. unquoted colon in title:). Disable only as a temporary escape hatch while running the audit at GET /admin/frontmatter-issues on an existing dirty corpus. |
wikantik.verification.stale_days | Default 90. After this many days, a verified page reports confidence: stale. |
wikantik.retrieval.cron.enabled | Default true. Nightly retrieval-quality CI run. |
wikantik.retrieval.cron.hour_utc | Default 3. Hour of day (UTC) for the nightly run. |
wikantik.kg.policy.default | exclude. Cluster-primary KG inclusion policy. Per-page override via kg_include frontmatter. |
Tool names below are canonical wire identifiers (snake_case). Every tool ships with at least one worked input/output example in its schema (Phase 6 of the agent-grade content rewrite, 2026-04-25) — agents should surface examples to the model when constructing the first call rather than reasoning purely from JSON Schema types.
/wikantik-admin-mcp (27 tools)Read-only / analytics — no author resolution required, registered with readOnly=true annotation:
| Tool | Purpose |
|---|---|
read_page | Fetch raw Markdown body + parsed frontmatter for a page. |
get_page_history | Version log: numbers, authors, dates, change notes. |
diff_page | Unified diff between two versions of a page. |
get_backlinks | Pages that link to a given page. |
get_outbound_links | Pages a given page links to. |
get_broken_links | Wiki-wide enumeration of references to nonexistent pages. |
get_orphaned_pages | Pages with no inbound links (system pages excluded). |
get_wiki_stats | Dashboard summary: total pages, broken/orphaned counts, recent changes. |
verify_pages | Compound check across many pages — existence, link health, metadata completeness, optional SEO and retrieval readiness. |
preview_structured_data | What a page's frontmatter will produce in HTML — meta tags, OG/Twitter, JSON-LD, BreadcrumbList, Atom, News Sitemap. |
ping_search_engines | Submit URLs/sitemap to IndexNow after a publish. |
list_proposals | List pending knowledge-graph proposals (only when KG is configured). |
inspect_proposals | Inspect specific proposals in detail before review. |
query_nodes | KG node query with admin bypass, so curators see freshly-created entities. |
search_knowledge | KG full-text search with the same admin bypass. |
list_orphaned_kg_nodes | Degree-0 KG entities, for orphan triage at scale. |
list_retrieval_queries | Real query-log traffic across all four agent surfaces. |
Write / author-configurable — implement AuthorConfigurable; the MCP exchange's clientInfo.name is injected as the default author before each call:
| Tool | Purpose |
|---|---|
write_pages | Create one or more pages (fails any that already exist). Author defaults from the client identity. |
update_page | Patch a page with expectedContentHash for optimistic locking. Frontmatter merges; removeKeys retires a field. |
rename_page | Move a page (and optionally rewrite incoming links) with confirm=true. |
rename_cluster | Rewrite a cluster path across every member, sub-clusters included. An unconfirmed call returns the plan — that is success, not an error. Refuses a target another hub already declares. |
delete_pages | Permanent delete with confirm=true. System pages refused. |
mark_page_verified | Stamp verified_at/verified_by/confidence frontmatter. |
propose_knowledge | Propose a new node or edge for the knowledge graph (KG-conditional). |
review_proposals | Approve / reject pending KG proposals. |
curate_nodes | Direct KG node curation (create/update/delete). |
curate_edges | Direct KG edge curation; refuses an edge that violates a SHACL shape. |
The admin endpoint also serves resources, prompts, and completions (see Capabilities).
/knowledge-mcp (21 tools)Knowledge-graph traversal & introspection (registered when KnowledgeGraphService is configured):
| Tool | Purpose |
|---|---|
discover_schema | Enumerate node types, edge types, properties, mention coverage. |
query_nodes | Filter / search graph nodes by type and properties. |
get_node | One node + its declared edges with provenance. |
traverse | Bounded BFS from a starting node with depth and provenance filters. |
search_knowledge | Full-text search over node names + property values. |
find_similar | Embedding-nearest-neighbour over node mentions (only when NodeMentionSimilarity is configured). |
Hybrid retrieval / RAG (registered when ContextRetrievalService is configured):
| Tool | Purpose |
|---|---|
assemble_bundle | Primary answer-grounding tool. Returns ranked, de-duplicated, version-pinned, citation-bearing section text as a context bundle — never a synthesized answer. Carries a coverage block and a mode=hybrid\|dense\|lexical toggle. |
retrieve_context | Page/section discovery. Hybrid BM25 + dense (RRF-fused) with fail-closed BM25 fallback. (Graph-aware rerank was removed in 2026-07; production runs BM25+dense only.) |
get_briefing | Session-start context briefing: budgeted, de-duplicated, injection-ready markdown assembled from pins, clusters, and the task prompt. |
get_page | Pinned fetch of a specific page once you know its name. |
read_pages | Batched raw-markdown read, up to 20 pages in one call. |
list_pages | Browse-style page enumeration, optionally prefix-filtered. |
list_metadata_values | Distinct frontmatter keys + their values across the corpus — discovery before targeted filtering. |
list_stale_citations | Citations whose grounding span has drifted since it was pinned. |
Ontology (registered when the ontology manager is wired):
| Tool | Purpose |
|---|---|
get_ontology | The formal wikantik: T-Box. |
sparql_query | Read-only SPARQL over the ontology. |
Structural-spine navigation (registered when StructuralIndexService is configured) — fastest path for structural questions because they hit the index, not full-text search:
| Tool | Purpose |
|---|---|
list_clusters | Enumerate clusters with hub pages and member counts. |
list_tags | Enumerate tags with frequency. |
list_pages_by_filter | Pages matching cluster=, tag=, type=, etc. |
get_page_by_id | Resolve a page by its stable canonical_id (ULID). |
Removed 2026-05-02:
traverse_relationsno longer exists. It walked the typedrelations:frontmatter block, which was deleted with typed relations. For page-to-page traversal useget_backlinks/get_outbound_linkson/wikantik-admin-mcp(real wikilink edges), ortraverseon this server for the derived KG co-mention graph.
Agent-grade projection (registered when ForAgentProjectionService is configured):
| Tool | Purpose |
|---|---|
get_page_for_agent | Token-budgeted projection of a page: summary, key facts, headings outline, recent changes, derived agent_hints, verification state. Default-of-choice for "read this page" — falls back gracefully via degraded flag and missing_fields rather than failing the whole request. Memoised in wikantik.forAgentCache (1h TTL). |
/tools/* (2 tools)| Tool | Path | Purpose |
|---|---|---|
search_wiki | GET /tools/search_wiki?q=...&max_results=... | OpenAPI shape over ContextRetrievalService (same hybrid retrieval as retrieve_context). Response shape includes citation URLs and ranked snippets. |
get_page | GET /tools/get_page?name=... | Single page fetch with frontmatter parsed into a structured field. |
OpenAPI 3.1 document live at GET /tools/openapi.json. Every operation includes worked example payloads — sufficient for ChatGPT Custom GPT tools, OpenWebUI tool servers, or any other client that consumes a static OpenAPI document.
/wikantik-admin-mcpBeyond tools, the admin server advertises the standard MCP capability surface:
Direct read-only data access without invoking a tool. Resource subscriptions are enabled (resources(true, true)); the server publishes notifications/resources/updated whenever a page save or delete crosses the WikiEventManager (wired by WikiEventSubscriptionBridge).
wiki://pages — list every page
wiki://recent-changes — recent modifications, newest first
wiki://pages/{pageName} — read a page (template)
wiki://pages/{pageName}/version/{v} — read a specific version (template)
wiki://pages/{pageName}/attachments — list attachments (template)
wiki://pages/{pageName}/backlinks — backlinks for a page (template)
Guided workflows the agent can invoke verbatim. Each prompt declares typed arguments (some prompts accept page names with autocompletion).
| Name | Purpose |
|---|---|
create-article | Structured article creation with metadata. |
summarize-topic | Cross-page research and synthesis on a topic. |
audit-links | Link-integrity check around a starting page. |
rename-page | Safe rename workflow with backlinks update. |
wiki-health-check | End-to-end wiki health report. |
publish-cluster | Hub + sub-articles cluster publish, with verify pass. |
extend-cluster | Add a new article into an existing cluster, including cross-references. |
seo-audit | Run verify_pages with seo_readiness and consume preview_structured_data. |
These are MCP prompts — guided workflows — not tools. The similarly-named compound tools (
publish_cluster,extend_cluster, …) were retired; do not try to call them.
Page-name autocompletion served against ReferenceManager.findCreated():
audit-links / pageName
rename-page / oldName
rename-page / newName
The MCP initialize response carries an instructions text loaded from wikantik-mcp-instructions.txt (classpath). The instructions cover page format (Markdown + YAML frontmatter), CamelCase naming, link syntax, and tool usage. Three safeguards keep the text in sync with the live registry:
InstructionsRegistryDriftTest (under wikantik-admin-mcp/src/test/) loads the resource at test time, asks McpToolRegistry what it actually registers, and asserts every registered tool is mentioned by name and every tool-shaped mention is registered. The unit-test build fails on drift, so a stale instructions file cannot reach packaging unnoticed.McpInstructionsDriftIT (under wikantik-it-tests/wikantik-selenide-tests) does the same check against a deployed server: it pulls the instructions text from the SDK's getServerInstructions() and the live tool list from tools/list, then asserts both directions of the diff. This is the safety net for operator-supplied mcp.instructions.file overrides — the unit test only sees the file shipped in the WAR; the IT sees whatever the deployed server actually returns.McpServerInitializer.logToolNameDriftIfAny re-runs the same check on every server start and warns at WARN level if the loaded file (bundled or override) has drifted from the registered tool set.Because of those three gates, adding a tool means editing wikantik-mcp-instructions.txt and McpProtocolIT.EXPECTED_TOOLS too — otherwise the build goes red. The instructions were rewritten end-to-end on 2026-04-30 and have tracked the registry since; the surface is currently 27 tools. The legacy locking section (lock_page / unlock_page) is gone — concurrency is now optimistic via expectedContentHash on update_page.
Every wiki page is Markdown with optional YAML frontmatter:
---
canonical_id: 01KQ0... # ULID auto-assigned by the structural-spine filter
type: article # article | hub | runbook | reference | report | ...
cluster: wikantik-development
tags: [mcp, integration]
related: [About, KnowledgeGraphCore]
depends-on: [KnowledgeGraphCore]
summary: Short, used for meta description / og:description / Atom <summary>
date: 2026-04-29
verified_at: 2026-04-29T14:00:00Z
verified_by: jakefear
confidence: authoritative # authoritative | provisional | stale (usually computed)
audience: [humans, agents]
kg_include: true # cluster-policy override
---
# Page Title
Body in Markdown. Internal links use text.
cluster: is scalar-or-list on a non-hub page: a page may belong to several clusters, and the first entry is the primary one that drives breadcrumbs, JSON-LD articleSection, sidebar placement and the embedding prefix. A type: hub page's cluster: must stay scalar — the hub is what declares the cluster exists. Sub-clusters are parent/child, one level deep.
Save-time enforcement is on: pages without canonical_id get one auto-assigned and injected; type: runbook pages are schema-validated; cluster KG-inclusion policy is applied. Most of the agent surface area assumes these invariants — read [StructuralSpineDesign](StructuralSpineDesign) and [AgentGradeContentDesign](AgentGradeContentDesign) for the full model.
YAML quoting trips up agents: a value like title: Woodworking Joinery: Structural Mechanics parses as a nested mapping start because of the unquoted colon, and the page used to save with empty metadata + a WARN log. Two layers now close that hole:
WritePagesTool and UpdatePageTool route every save through FrontmatterNormalizer: any embedded --- block is parsed strictly, merged with the explicit metadata argument (explicit wins on conflict), and re-emitted via FrontmatterWriter (SnakeYAML's emitter, which always quotes correctly). If the embedded YAML is malformed, the tool returns a structured error with the message + 1-based line/column + a quoting hint instead of silently dropping metadata.FrontmatterValidationPageFilter runs at preSave priority -1006 (before chunking, defaults, structural spine, runbook validation). Rejects any save whose --- block fails FrontmatterParser.parseStrict. Catches non-MCP paths (REST, JSP editor). Gated by wikantik.frontmatter.enforcement.enabled (default true).FrontmatterParser.parse(...) keeps its graceful-degrade behaviour for read-side callers — the strict path is opt-in via parseStrict(...) and only used on writes. Operators adopting the validator on an existing wiki should run GET /admin/frontmatter-issues first to find broken pages, fix them, then leave the flag at its default true.
GET /admin/frontmatter-issues returns {data: {issues: [{pageName, error, line?, column?}](), issue_count, scanned, error_count}}. Scan cost is O(N) page reads on the request thread — it's a migration tool, not a polling target.
The point of the rebuild was agents reading agents-grade content, not humans browsing. Three load-bearing capabilities:
get_page_for_agent is the right default for "read this page", not read_page or get_page. It returns a token-budgeted projection — summary, key facts, headings outline, recent changes, derived agent_hints, verification state — without the full markdown body. Per-extractor try/catch: failure of one section surfaces as a degraded flag plus missing_fields, not a total error. Memoised by (canonical_id, updated_at_millis) so repeat reads in a session are essentially free.
Verification metadata is part of the wire format. verified_at, verified_by, confidence, and audience flow through the /for-agent projection and the structural index. Authors stamp pages via mark_page_verified; operators triage at GET /admin/verification?confidence=stale. Agents can — and should — refuse to act on pages where confidence: stale.
Runbooks are a first-class type. A page with type: runbook carries a structured runbook: block (when_to_use, inputs, steps, pitfalls, related_tools, references), validated at save time and re-validated on read. Tell agents: when planning a multi-step task, search for runbooks first (e.g. list_pages_by_filter with type=runbook, or retrieve_context filtered by type). The seed corpus lives at [AgentCookbook](AgentCookbook).
A fourth, quieter improvement: every tool now ships with worked input/output examples in its schema (top-level for OpenAPI ops; outputSchema.examples plus per-property examples on MCP tools). First-call success rates rise materially when the agent sees concrete payloads instead of inferring from types alone.
.mcp.json in the repo, or ~/.claude/mcp_servers.json global){
"mcpServers": {
"wikantik-knowledge": {
"type": "http",
"url": "http://localhost:8080/knowledge-mcp",
"headers": { "Authorization": "Bearer YOUR_KEY" }
},
"wikantik-admin": {
"type": "http",
"url": "http://localhost:8080/wikantik-admin-mcp",
"headers": { "Authorization": "Bearer YOUR_AUTHORING_KEY" }
}
}
}
Claude Code surfaces tool examples to the model. Add the admin server only on agents you trust to write.
claude_desktop_config.json)Same shape under mcpServers. Claude Desktop currently only supports the stdio transport directly, so for a remote HTTP MCP endpoint use mcp-remote as a wrapper:
{
"mcpServers": {
"wikantik-knowledge": {
"command": "npx",
"args": [
"mcp-remote",
"https://wiki.example.com/knowledge-mcp",
"--header", "Authorization: Bearer YOUR_KEY"
]
}
}
}
~/.gemini/settings.json){
"mcpServers": {
"wikantik-knowledge": {
"httpUrl": "http://localhost:8080/knowledge-mcp",
"headers": { "Authorization": "Bearer YOUR_KEY" }
}
}
}
Gemini CLI loads MCP server metadata at session start and activates tools on demand.
Point at the OpenAPI tool server:
URL: http://localhost:8080/tools/openapi.json
Auth header: Authorization: Bearer YOUR_TOOLS_KEY
OpenWebUI (and similar) consume the OpenAPI document directly. Two operations are exposed: search_wiki and get_page.
# Context bundle over /knowledge-mcp
curl -sS -X POST http://localhost:8080/knowledge-mcp \
-H "Authorization: Bearer $WIKANTIK_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"assemble_bundle",
"arguments":{"query":"how do agents read pages"}}}'
# OpenAPI tool server
curl -sS "http://localhost:8080/tools/search_wiki?q=hybrid+retrieval&max_results=5" \
-H "Authorization: Bearer $TOOLS_KEY"
Drop a fragment like this into the agent's project / system prompt so it does not blindly full-text-search:
You have access to a Wikantik MCP server. Use it like this:
- Answer a question: prefer
assemble_bundle— it returns ranked, de-duplicated, version-pinned, cited section text to ground on. Cite the handles it returns; do not hand-craft URLs. Check thecoverageblock:weakorpartialconfidence means refine the query rather than answer thinly.- Read a page: prefer
get_page_for_agentoverread_page/get_page. It returns a token-budgeted projection with summary, key facts, headings outline,agent_hints, and verification state. Treat any field listed inmissing_fieldsas unavailable rather than empty. Treatconfidence: staleas untrusted information.- Browse structure: for cluster / tag / type questions use the structural-spine tools (
list_clusters,list_tags,list_pages_by_filter,get_page_by_id). Do not full-text-search structural questions; the spine is faster and exact.- Plan multi-step work: before doing complex authoring or maintenance, search for
type: runbookpages on the topic (list_pages_by_filterwithtype=runbook) and follow thesteps:block.- Discover the schema: call
discover_schemaonce per session before constructing custom KG queries; calllist_metadata_valuesbefore custom frontmatter filters.- Edit pages (if authoring is enabled): use
update_pagewithexpectedContentHashfor optimistic locking. Always provide achangeNote. Usemark_page_verifiedto stamp verification after a substantive review. Never calldelete_pagesorrename_pagewithoutconfirm=true.
assemble_bundle with the user's question — this is the grounding call.get_page_for_agent (cheap thanks to the cache).discover_schema + list_clusters to confirm cluster naming.publish-cluster MCP prompt on /wikantik-admin-mcp to generate hub + members.write_pages (one round trip).verify_pages with checks=["seo_readiness", "retrieval_readiness"] to confirm cross-references and metadata.preview_structured_data on the hub to confirm JSON-LD / breadcrumb shape.mark_page_verified on each page once the cluster is reviewed.ping_search_engines (requires wikantik.indexnow.apiKey).get_wiki_stats for the dashboard.get_broken_links and get_orphaned_pages for the worklist.audit-links prompt on a starting page to walk the local link graph.verify_pages to confirm.get_page_for_agent to get the page's canonical_id.get_outbound_links / get_backlinks on /wikantik-admin-mcp.traverse on /knowledge-mcp.Do not conflate the two: the Page Graph is what authors wrote, the Knowledge Graph is what extraction inferred.
rename_cluster without confirm to get the plan — which pages change, and any conflict with a hub that already declares the target.confirm=true. Page names, canonical_ids and URLs do not move; only cluster: frontmatter is rewritten.GET /admin/retrieval-quality?limit=20 to read recent runs.POST /admin/retrieval-quality/run with {"query_set_id": "core-agent-queries", "mode": "HYBRID"} to trigger an ad-hoc run. (HYBRID_GRAPH still parses for historical rows but is retired — the graph rerank was removed in 2026-07.)nDCG@5 >= 0.5.list_retrieval_queries with max_avg_results=1 surfaces the real queries the corpus answers poorly.Prometheus metrics relevant to integrations:
wikantik_for_agent_response_bytes — histogram of /for-agent response sizes.wikantik_retrieval_ndcg_at_5 / _at_10 / _recall_at_20 / _mrr — gauges keyed by {set, mode}.wikantik_tools_* — request counters, latencies, and rate-limit / auth rejections for /tools/* (registered via ToolsMetricsBridge).Log streams worth tailing:
SecurityLog — every auth failure, every rate-limit rejection on every endpoint.MCP and Knowledge MCP cover startup, tool registration, drift warnings, and shutdown.scope=mcp for read-only research agents. Reserve scope=all for human-supervised authoring, never automation.mcp.access.allowedCidrs to the intended source range; do not rely on bearer tokens alone for agents on shared networks.mcp.access.allowUnrestricted is unset (or false) in production. The fail-closed default is what you want.mcp.ratelimit.global > 0).wikantik.runbook.enforcement.enabled=true (default) so authoring agents cannot land malformed runbooks.wikantik.structural_spine.enforcement.enabled=true (default) so every page gets a canonical ID.[KgInclusionPolicy](KgInclusionPolicy).Authorization header.Accept: text/event-stream header (the Streamable HTTP transport requires SSE).mcp.instructions.file override, mirror the live tool registry in it. The bundled file is regression-tested by InstructionsRegistryDriftTest; an external override is not.| Symptom | Likely cause |
|---|---|
503 MCP not configured | Filter is fail-closed — no API keys, no CIDRs, allowUnrestricted not set. Set one. |
403 Access denied | Bearer token unknown / revoked, or scope insufficient (tools key against /wikantik-admin-mcp will 403). |
403 Key not authorized for MCP | A DB-backed key with scope=tools was presented at an MCP endpoint. Mint a key with scope=mcp or scope=all. |
429 Rate limit exceeded | Rate-limit knobs too tight, or runaway agent. Inspect SecurityLog. |
MCP tools list empty on /knowledge-mcp | None of KnowledgeGraphService, ContextRetrievalService, StructuralIndexService, ForAgentProjectionService are configured — check wikantik.datasource and the embeddings setup. |
/knowledge-mcp advertises fewer than 21 tools | Expected on a partial deployment — several tools register conditionally on what is wired. Not an error. |
get_page_for_agent returns degraded: true | One of the extractors failed; the named field appears in missing_fields. Check log for the underlying exception. |
| Startup log warns about tool-name drift | An external mcp.instructions.file override disagrees with the live registry. The bundled instructions are regression-tested at build time; only an operator-supplied override can drift in a live deployment. Refresh the override or remove it. |
Authoring tool says expectedContentHash mismatch | Optimistic-lock guard fired. The response carries latestContent — rebase against it and retry without a separate read. |
rename_cluster returned a plan instead of applying | Working as designed. An unconfirmed call always returns the plan; re-call with confirm=true. |
WikiEngine could not be created — MCP server not started | The bootstrap servlet listener ran before SPIs were ready. Confirm WikiBootstrapServletContextListener is wired with a lower load-on-startup value than the MCP listeners (admin = 2, knowledge = 3, tools = 2). |
[GoodMcpDesign](GoodMcpDesign) — the principles the tool surface was built against.[AgentGradeContentDesign](AgentGradeContentDesign) — the design that drove the rewrite (verification metadata, /for-agent, runbook type, retrieval-quality CI, examples).[AgentCookbook](AgentCookbook) — runbook-style how-tos that seed the retrieval-quality CI.[StructuralSpineDesign](StructuralSpineDesign) — clusters, tags, canonical IDs, generated Main.md.[HybridRetrieval](HybridRetrieval) — what assemble_bundle, retrieve_context and search_wiki do under the hood.[KgInclusionPolicy](KgInclusionPolicy) — cluster-primary policy + page-level override.[FindingTheRightMcpTool](FindingTheRightMcpTool) — agent-facing chooser.[McpAuditTools](McpAuditTools) — the analytics subset of the admin MCP tools, with examples.