Repository navigation
feat(agents): serve markdown to agents, add AGENTS.md, skills, and a docs MCP server - #4161
Closed
naveen-bandela-au2 wants to merge 8 commits into
Closed
naveen-bandela-au2 wants to merge 8 commits into
naveen-bandela-au2 wants to merge 8 commits into
Conversation
…docs MCP server gofr.dev scores 53/100 on orank's agent-readiness scan. The website side of the fix is in gofr-dev/website; this is the half that lives here. docs/Dockerfile: bump zopdev/static-server v0.0.9 -> v1.0.0. This is the change that makes the website's work visible. v1.0.0 resolves `Accept: text/markdown` on an extensionless route to the `<route>.md` sibling the site now generates for every page, serves a markdown 404 body pointing at /sitemap.xml and /llms.txt, and reads the `_headers` file the site uses to declare its Link headers and the media types for its extensionless /.well-known documents. On v0.0.9 all of that ships but nothing serves it. AGENTS.md at the repo root: guidance for agents working on the framework itself — repo layout, the development-not-main branch rule, the separate datasource modules, and the public-API contract. Distinct from docs/AGENTS.md, which is published at gofr.dev/AGENTS.md and is about building applications with GoFr. The scan probed this repo and found neither. skills/: three agent skills by capability area — gofr-rest-api, gofr-datasources, gofr-observability. Every API in them was checked against the source rather than written from memory, which caught app.Metrics().NewCounter (not app.NewCounter) and the error return from AddRESTHandlers. mcp/docs-server/: an MCP server exposing the documentation as search_docs, get_doc, and list_sections over stdio. Content is fetched once per process from /llms-full.txt, so it serves current docs without being re-released alongside them, and the fetch is deferred to the first tool call so a network failure reads as a failed call rather than a crashed server. Standard library only — this is in the root gofr.dev module and a docs tool is no reason to add a dependency that every `go get gofr.dev` would carry. 95.9% test coverage. .claude/plugin.json: agent-plugins.org manifest tying the skills and the MCP server together.
Replaces map[string]any with structs. The compiler now checks the wire shape the MCP client parses, and the repeated JSON keys that goconst flagged disappear rather than being suppressed.
corpus.load used sync.Once, which caches the RESULT including a failure. A flaky network at the first tool call left every later search in that editor session returning the same stale error, curable only by restarting the MCP client. That directly contradicts the design note in the PR body, which claims a failure reads as a failed call rather than a crashed server. Now only success is latched; a failure retries on the next call. Added a regression test that fails the first fetch and succeeds the second. Coverage 95.9% -> 96.5%. AGENTS.md said Go 1.25+; go.mod declares go 1.26.0, so a contributor following the file could not build the module. A JSON-RPC parse error returned a nil id, which omitempty drops from the response entirely. The spec requires an explicit null when the id cannot be read.
Second time this landed, in a different place: the first ignore rule was scoped to mcp/docs-server/, but `go build ./mcp/...` run from the repo root drops the binary at the root instead. 8.1 MB of Mach-O in the tree. Root cause is that the artifact lands wherever the build was invoked from, not in the package directory, so a per-directory rule was always going to miss a case. Matching the name repo-wide in .gitignore covers every invocation point.
I wrote the manifest from memory instead of reading the specification. Fetching the published JSON Schema showed three problems: The $schema URL 404s. The real one is agent-plugins.org/schemas/1.0.0/plugin.schema.json. `skills` and `mcpServers` are not manifest fields. The schema declares only $schema, name, version, description, author, homepage, repository, license, keywords and extensions, with additional keys rejected. The spec discovers components by fixed locations instead: skills/<name>/SKILL.md, which this repo already has, and a separate mcp.json at the plugin root. So the arrays were invalid and redundant at the same time. The manifest was at .claude/plugin.json. The spec requires it at the plugin root, which is very likely why the scan that motivated this change reported no plugin.json found in the repo. Both files now validate against the published schemas: no missing required keys, no unknown keys, and the stdio server entry matches $defs/stdioServer. Also from the same review pass: sections() mapped a root route to an empty section name, which would have rendered as a nameless row in list_sections output, and the same output said "1 pages".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
gofr.dev scores 53/100 (grade C) on orank's agent-readiness scan. The website side of the fix is gofr-dev/website#227; this is the half that lives here.
This PR must land before that one has any effect in production.
docs/Dockerfile: static-server v0.0.9 → v1.0.0This one line is what makes the website PR visible. v1.0.0 adds:
Accept: text/markdownon an extensionless route resolves to the<route>.mdsibling the site now generates for every page, withContent-Type: text/markdownandVary: Accept/sitemap.xmland/llms.txt, so an agent that guesses a path can recover_headerssupport, which is how the site declares itsLinkheaders,nosniff,Referrer-Policy, and the media types for its extensionless/.well-knowndocumentsOn v0.0.9 all of the above ships in
out/but nothing serves it —.mdfiles come back as HTML-or-nothing and_headersis an inert file.Verified locally: built the site with this repo's docs overlay, served
out/withstatic-server:v1.0.0, and confirmed all of the above (16/16 assertions, listed in the website PR).AGENTS.mdat the repo rootThe scan probed this repository for agent configuration and found none — the existing
AGENTS.mdlives only on the website. This one is for agents working on the framework itself: repo layout, the PRs-target-developmentrule, the separate datasource modules, the public-API contract, and what to run before opening a PR. Distinct fromdocs/AGENTS.md, which is published atgofr.dev/AGENTS.mdand is about building applications with GoFr.skills/Three agent skills by capability area —
gofr-rest-api,gofr-datasources,gofr-observability.Every API in them was checked against the source rather than written from memory, which caught two errors before they shipped: it is
app.Metrics().NewCounter(...), notapp.NewCounter(...), andAddRESTHandlersreturns an error that the example was discarding.mcp/docs-server/An MCP server exposing the documentation as
search_docs,get_doc, andlist_sectionsover stdio, so an agent looks GoFr APIs up instead of guessing them.Design notes:
/llms-full.txt, cached once per process, so the server stays current without being re-released alongside the docs.gofr.devmodule, and a documentation tool is not a good reason to add a dependency that everygo get gofr.devwould then carry.go.modandgo.sumare unchanged.isErrorresults, not JSON-RPC errors, so the model can read the message and correct its next call. Notifications correctly get no reply.95.9% test coverage (
go test -cover ./mcp/docs-server/)..claude/plugin.jsonagent-plugins.org manifest tying the skills and the MCP server together.
Verification
Known limitation
The MCP server is stdio-only. The scan's MCP checks want a Streamable HTTP endpoint and a
serverUrlthat resolves over HTTP, so those points stay partly out of reach until there is somewhere to host it. Flagged rather than papered over.