Releases: kinjelom/mkdocsgo
Release list
v0.4.0
Added
-
Prometheus metrics: what the site and the MCP server are asked for.
Which pages people read (mkdocsgo_site_page_views_total), which tools
agents call and how the calls end, which pages those tools hand out, how
many searches found nothing, and requests, latency and bytes by route and
zone. Every label value comes from a closed set - a page in the build, a
registered tool, a configured zone - never from a request path or a search
query, so a scanner cannot mint series. Off unless asked for. -
-metrics-addrserves/metricson a listener of its own, never on the
published-httpaddress. -
-metrics-pushsends the metrics to a Pushgateway - or VictoriaMetrics - every
-metrics-push-interval, grouped by-metrics-joband-metrics-instance,
and deletes the group on shutdown. The URL may carry Basic credentials and is
read fromMKDOCSGO_METRICS_PUSH_URLwhen the flag is absent, so they stay out
of the process list. This is how metrics leave a Cloud Foundry instance. -
-metrics-users name|hashlabels the metrics that say who read what with
the principal behind the request - on the site, and inside MCP tool calls,
where the identity from the zone check reaches the tools per request.hash
is a 12-character pseudonym, keyed byMKDOCSGO_METRICS_USER_SALTwhen set;
unsalted, it can be recomputed from the names inmkdocsgo.yml, and the
startup log says so. Off by default. -
METRICS.md: the metrics, both ways out, and the queries worth having. -
Markdown sources on the site, per zone. A zone with
markdown: true
serves each page's Markdown at the page's address with.mdadded -
/guides/deploy/as/guides/deploy.md- and at its path underdocs_dir.
On a Material site each page gets a download button among Material's page
actions, and every page gets a<link rel="alternate" type="text/markdown">
for agents that read HTML. A browser is senttext/plain, so it shows the
source instead of saving it. Only pages MkDocs built are offered, the source
is as protected as its page, and every other zone is served exactly what
MkDocs wrote.markdown: trueon a zone that isoffstops the start. -
?section=<anchor>on any of those addresses returns one section - the
heading and the text beneath it - which is whatget_sectionreturns, from
the same code. For an agent that fetches URLs rather than speaking MCP, the
cheap way to read stays cheap. An unknown anchor is a 404 that lists the
ones the page has. -
OAuth sign-in for MCP clients, so claude.ai can connect. claude.ai,
Claude Desktop and Claude mobile add a remote server by its URL and nothing
else, so a static token was no way in for them. A zone withoauth: truenow
runs an OAuth 2.1 authorization server inside the binary: RFC 8414 metadata,
dynamic client registration (RFC 7591), the authorization code flow with
PKCE (S256 only), and refresh. The person signing in is one of the zone's
principals, with the password already inmkdocsgo.yml. Claude Code signs
in the same way, through a loopback redirect on any port, and static tokens
keep working beside it. -
Nothing is stored. Client registrations, codes, access and refresh tokens
are sealed with an HMAC underMKDOCSGO_OAUTH_KEY, the one secret, which
lives in the environment rather than in the file. Any instance accepts what
another issued and a restart signs nobody out. Taking a principal out of the
zone or changing its password, then restarting, signs out every client it
signed in; changing the key signs out everyone. -
oauth.redirect_urisis the allowlist a client registration may name,
defaulting to Claude's callback and Claude Code's loopback;
oauth.access_token_ttl(1 hour) andoauth.refresh_token_ttl(30 days)
set the lifetimes. -
docs/adr/0002-built-in-oauth-for-mcp-clients.md: why the authorization
server is built in and stateless, and what that costs - a secret, no
per-token revocation, no refresh-token rotation.
Changed
- Both halves publish the same pages. With a built site loaded
(-mode site+mcp), the MCP half keeps only the pages MkDocs built a page
from - the rule the Markdown sources use. A page a plugin excluded is no
longer searchable and readable through/mcpwhile missing from the site.
The startup log lists what was left out. draft_docsis honoured likeexclude_docs:mkdocs buildleaves drafts
out, and so does the index, in every mode.- The protected resource metadata of a zone with
oauth: truenames its
authorization server inauthorization_servers. - A bearer token presented and refused on
/mcpgetserror="invalid_token"
in theWWW-Authenticatechallenge, which tells an OAuth client to refresh
rather than start over. - A zone with
oauth: trueserved over HTTP with/mcpwill not start without
MKDOCSGO_OAUTH_KEY, 32 characters or more.
v0.3.0
Added
-
**.matches a domain at any depth, next to*., which still matches
exactly one label.**.incoversa.inanda.b.c.inalike, and the longest
suffix still wins, so**.cfp1.i6e.inbeats**.in.It is for a policy stated in terms of a domain rather than a host - every
internal foundation is internal, everything on the public one asks for
credentials - where the alternative is listing foundations as they are
created and having the newest one answer 403 until somebody notices. Spelled
with two stars because the wide match is the one worth writing on purpose:
nothing that already used*.changes meaning.
v0.2.0
Added
- Zones:
mkdocsgo.ymlbesidemkdocs.ymlmaps the addresses the server
answers on to a named zone, and a zone topublic,restrictedoroff.
Several addresses may share one; the most specific pattern wins; an address
no zone claims is refused. This is what lets one deployment serve an intranet
route openly and an internet route only to named principals. A project
without the file behaves exactly as it did before - everything public, no
challenge - so the upgrade is a no-op until you write one.-configpoints
at the file elsewhere. - A restricted zone asks a browser for HTTP Basic credentials and an agent for
Authorization: Bearer. The MCP401carries theWWW-Authenticate
challenge the specification asks for, pointing at RFC 9728 protected resource
metadata the server publishes at/.well-known/oauth-protected-resource/mcp- so the day a zone moves to Keycloak, a client that already follows the
pointer needs no change.method:is that seam, andoidcis refused at
startup rather than ignored.
- so the day a zone moves to Keycloak, a client that already follows the
- Credentials are stored as hashes - Argon2id for a password, SHA-256 for a
token - which is why the file is not a secret and needs no encryption. Bcrypt
hashes fromhtpasswd -Bare accepted as well.-new-tokenand
-hash-passwordmint them and print the line to paste. - The access log names the zone and, where there is one, the principal and the
credential id it came in on. TheAuthorizationheader is never logged. AUTH.md, anddocs/adr/0001-zone-based-authentication.mdfor why it is
shaped this way - including what was rejected: zones that also scope content,
and an encrypted credentials file.
Changed
- In a restricted zone every
Cache-Controlthe site would have sent as
publicis sent asprivate, and responses carry
X-Robots-Tag: noindex, nofollow. The freshness is unchanged; a shared cache
loses the right to store the page and hand it to the next person. - Configuration errors stop the server. A restricted zone with no principals, a
principal nobody lets in, a plaintext password where a hash belongs, one host
in two zones, an unknown key - each fails at startup rather than at some
later request.
v0.1.1
Fixed
get_pageandget_sectionnow carry the Markdown in the structured result
as well as in the content block. Both tools declare an output schema, so a
client may renderstructuredContentand never read the content block - and
such a client saw the metadata and none of the text. The two channels now
hold the same Markdown. Themarkdownfield is additive, so a client already
reading the structured result keeps every field it had.search_docsand
list_pageswere never affected: their structured results already carried
the text, which is why only these two tools looked empty.
v0.1.0
The first release. Everything below is new.
While the version is 0.x the flag surface and the MCP payload shapes may
still change. A change that breaks either will bump the minor version and say
so here.
Added
- Serving a built MkDocs site over HTTP: content-hash ETags, compression done
once at startup, security headers, a per-path cache policy, directory URLs,
the project's own404.html, range requests. - An MCP server over Streamable HTTP and stdio, with four section-shaped tools
(search_docs,get_section,get_page,list_pages) and one resource per
page. - A BM25 index over sections, weighting heading terms and preserving version
strings through tokenisation. site,mcpandsite+mcpmodes from one binary.-healthcheckand-mcp-probe, so a distroless image with no shell can
still be probed.cmd/anchorcheck, which compares computed anchors against a real built site.scripts/release.shand its parts (test.sh,build.sh,dist.sh,
image.sh): cross-compiled archives with checksums, a container image, an
annotated tag and a GitHub release from one command.- Documentation:
SERVING.md(the site half),MCP.md(the agent half),
ARCHITECTURE.md(how it works inside),DEPLOYMENT.md(Docker, Cloud
Foundry with both a Docker application and the binary buildpack, and
Kubernetes),COMPARISON.md(what this replaces and what it costs) and
RELEASING.md. mkdocsgo-example, a companion repository: a complete MkDocs project wired
to this server, with every deployment manifest filled in.- The MIT licence, shipped inside every release archive.