Repository navigation
feat(analytics): four read-only MCP tools - #10
Merged
Merged
Conversation
top_entries, unviewed_entries, entry_views and site_totals expose what the sync stored to EmDash's MCP server. Each is a private route with plugins:read, the permission the analytics pages use, and reads storage only: a sandboxed invocation has ten bridge calls, and the worst case per tool is seven. Every answer states its UTC window, whether stored history covers it, whether the content index is complete and when the last sync ran. The zod schemas live in src/tools/declare.ts and are referenced only from the plugin's `mcp` property, which the plugin build strips from the runtime, so zod stays a dev dependency and the bundle grows by the loaders only (81 KB to 86 KB). The handlers validate their input by hand, because the routes are also reachable over HTTP. The list tools read past rows kept for history (unpublished, deleted, moved) until the page is full, at least 25 rows per read, and repeat the last read up to the final row taken so the cursor stays exact. Cursors name their tool, window and collection; one from another query is refused instead of seeking by the wrong column. Verified on a create-emdash site with EmDash 0.39.1: sandboxed through @emdash-cms/sandbox-workerd, tools/list shows all four and every call passes the MCP server's output validation. Registered in plugins: [], EmDash lists none of them: its in-process loader reads tools from the runtime module, where the build removed them. The README says so. isPublished and oldestDay move to the store modules, shared by the per-entry page, the panel and the tools.
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.
Four read-only MCP tools for the analytics plugin, released as 0.2.0 through the release workflow (changeset
minor). This is the first changeset since the workflow landed, so merging the "Version Packages" PR it opens is also the first trusted publish.Tools
analytics__top_entriesanalytics__unviewed_entriesanalytics__entry_viewsanalytics__site_totalsEach tool is a private route with
plugins:read(the analytics pages' permission) anddestructive: false. It reads storage only, and none of them calls Cloudflare. Every answer states its UTC window,partial(whether stored history covers it),historySince,indexCompleteandlastSync;site_totalsaddsestimatedandprovisional. The descriptions tell the agent to say when the data is thin.How it's built
src/tools/declare.ts, which only the plugin'smcpproperty references.emdash-plugin buildstrips that property from the runtime and writes the schemas into the manifest as JSON Schema. zod 4.5.4 (the version emdash and plugin-cli pin) is a dev dependency, anddist/plugin.mjsgrows from 81 to 86 KB, the loaders only.additionalProperties: false), and the MCP server rejects an answer that does not match.tests/tools.test.tschecks every loader's answer against the schema the build wrote, converted the way EmDash converts it for a sandboxed install.tests/budget.test.ts).isPublishedandoldestDaymoved tostore/and are now shared by the per-entry page, the panel and the tools.Limitation: sandboxed installs only
Registered in
plugins: [], EmDash 0.39 and 0.40 load none of a plugin's MCP tools. The build removesmcpfrom the runtime module, and the in-process loader (adaptSandboxEntry) reads tools only from that module and never gets the descriptor'smcp. Sandboxed and registry installs read the descriptor and work. Same code on upstream/main. The README and changeset say so. This means eisbachcode.de, which runs the plugin in-process, will not see the tools until upstream changes that.Verified
pnpm typecheck,pnpm test(23 files, 312 tests),pnpm build.scripts/compat-matrix.sh 0.40.1: 312/312 on the harness baseline and on 0.40.1.@emdash-cms/sandbox-workerd0.7.1):tools/listshows the four tools with output schemas; all four calls pass the server's output validation, with numbers matching the seed by hand.days: 90ontop_entriesis refused by input validation. A cursor from another window comes back asROUTE_ERRORwith the refusal text.unviewed_entrieswithlimit: 1finds the entry behind 30 moved rows, andtop_entriesatlimit: 2pages through every published entry once, in order.plugins: []):mcpTools: [],tools/callanswers "Tool not found"./code-review highon the diff: 10 findings, 8 fixed (cursor scope, index state during a rebuild,allLanguagescounting an unpublished entry's old numbers, per-storehistorySincewording, small-limit scanning, parallel reads, shared helpers, config-pin tests). The other two: the in-process gap (upstream, documented), and the changeset, which this PR adds.Seen along the way, for upstream and not changed here: after an update that changes a tool, EmDash stops serving the tools until they are re-approved, but the plugin's Agent access switch still shows on. The README tells users to switch it off and on.