extract as="feed-entries": project RSS and Atom into entries, so a feed guardfile stops handing the model raw XML #81
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/mcp-beaver#81
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Filed by Angie (engineer,
claudeseat) on Kai's call, as the follow-on to#60and the last open cost ofcoilyco-bridge/deploy#608.The gap
examples/reddit.mcp.kdlserves all four reads, and every one of them returns raw Atom. The bespoke server it replaces returns normalized entries. That downgrade is the single remaining carried cost of retiring reddit-mcp, and it is measurable on both sides:{"coverage":{"truncated":false,"bytes":53381,"over_budget":true},"result":"<?xml version=\"1.0\" .... The model parses 53 KB of XML in context on every call, past the 8192-byte thresholdinternal/mcpserver/coverage.go:14names.title,author,url,permalink,published,updated,subreddit,dedup_key.Kai accepted this trade when she chose migration, with the cost stated rather than hidden. This issue is the offer to stop paying it, because the seam it needs already exists.
Why here rather than in umbra
The obvious reading is that umbra should learn to decode a non-JSON body, since umbra is where
raw-responselives. It should not, and this repo already says so atinternal/mcpserver/pdf.go:51:Confirmed in umbra's tree at
599429frather than taken from that comment.opcore.Descriptorcarries exactly two response-side fields:FailWhen, a JMESPath postcondition that rejects a call and never reshapes it, andRawResponse, which is pass-through. There is noencoding/xmlin any non-test file. A feed parser there would be the first response transform umbra has ever had, in the layer whose job is to not have one.The shape
asalready exists to make exactly this additive, perinternal/mcpserver/pdf.go:54. One new value, one new bound:Wiring points, all of which already do the right thing for a second extraction kind:
parseExtracts(pdf.go:59) reads the node and fails closed on an unknown property. Theasswitch atpdf.go:91currently hard-rejects anything butpdf-text, so this is where the second value lands, alongsidemax-itemsbesidemax-pagesatpdf.go:82.validateExtracts(pdf.go:108) already refuses anextractwhose grant lacksraw-response(pdf.go:123). All four reddit grants declare it, so all four are eligible with no guardfile change beyond theextractlines.server.go:635dispatches topdfToolSuccesswhen an extract is present and totoolSuccessotherwise. That branch becomes a switch on the extraction kind.pdfToolSuccessdoes pages, so a bounded feed read cannot be mistaken for the whole feed.max-itemswants the same defaulting posture asmax-pages: a default the guardfile raises rather than a ceiling it lowers into. 25 matches what reddit's Atom actually carries per feed, with a hard ceiling well under it in spirit.The dependency call
Recommend stdlib
encoding/xml.mmcdole/gofeedhandles RSS 2.0, Atom, and RDF and would be the reflex choice, but this runtime carries one parser dependency total today and#60treated dependency footprint as a deciding factor rather than an afterthought. Reddit publishes two shapes and both are small. If a later source needs RDF or a hostile-feed tolerance the stdlib does not give, that is the moment to take gofeed, and theasproperty makes that switch invisible to every guardfile.Whichever way it goes, the size gate comes before the parser, matching
maxPDFBytesatpdf.go:20.Scope past reddit
Not reddit-specific in principle. Any RSS or Atom source is unreadable by a generated server today for the same reason PDFs were before
#60. It is reddit-only in practice right now: no deployed guardfile incoilyco-bridge/deploydeclaresraw-responseorextract, so reddit is the first and currently the only consumer.Acceptance
extract "<tool>" as="feed-entries"and receive structured entries rather than an XML string./r/{subreddits}/new/.rssand whatever the homepage grant'spath "/.rss"returns.max-itemsbounds the result, and coverage states entries shown of entries total.extractnaming a grant withoutraw-responsestays a build error, unchanged.asvalue stays a build error, unchanged.Sequencing
This is a pre-flight for
coilyco-bridge/deploy#608rather than a cleanup after it. It blocks none of that issue's three deployment items, but landing it first is the difference between a migration that is a pure win and one that takes a downgrade Kai would then pay again to undo.Reprioritized to P4 the same day it was filed, because its motivation moved. Angie (engineer,
claudeseat).I filed this as a pre-flight for
coilyco-bridge/deploy#608, on the argument that landing it first is the difference between a migration that is a pure win and one that takes a downgrade. Kai then made a call that removes that argument entirely: reddit support stays as the shelf guardfile and nothing gets deployed, socoilyco-bridge/deploy#639retires reddit-mcp without standing anything up.What changed
What did not change
The capability argument, which never depended on reddit. A generated server cannot read any RSS or Atom source today, exactly as it could not read a PDF before
#60. The next feed-shaped upstream hits this wall on day one, andexamples/reddit.mcp.kdlis a ready worked example the moment someone wants the parse.Keeping it open at P4 rather than closing it. Closing would file the gap under "solved", and it is not solved, it is just nobody's problem this week. Whoever picks it up should read the body's motivation as the next feed source, not as the reddit migration it was written for.
Landed on
mainas6e8fe6a. Built by Angie (engineer,claudeseat).Acceptance, against the body's list
extract "<tool>" as="feed-entries"returns structured entries. Entries carrytitle,link,author,id,published,updated,categories, each omitted when the source did not supply it. That is the reddit-mcp field set, withcategoriescarrying what it calledsubredditandidcarryingdedup_key.get_subreddit_rssongolang+pythonandget_homepage_rssboth returned 25 of 25 entries with every field populated on every entry.max-itemsbounds the result and coverage states shown of total. Coverage gainsentries: {shown, total}beside the existingpages.extractwithoutraw-responsestays a build error. Unchanged, and now covered by a test on the feed path too.asvalue stays a build error. Unchanged. The message now names both valid values.The measurement
The body measured 53,381 bytes with
over_budget: true. Same tool, same subreddits, after this change:The homepage feed lands at 8,932 bytes and stays
over_budget: true, just past the 8192 threshold. That is honest rather than a miss: it is a 6x reduction, and the flag is doing what#68built it to do.Most of the reduction is one decision worth naming. The entry body is dropped. Atom
contentand RSSdescriptioncarry the whole post as HTML, and they are the bulk of what the projection exists to remove. What survives is what a model uses to pick an entry worth fetching in full.What landed beyond the ask
dc:creator,guid,pubDate, and text<link>all fold into the same entry.max-pageson a feed extract andmax-itemson a PDF extract both fail closed, rather than being ignored.docs/pdf-extraction.mdand the new feed page merged intodocs/extraction.md. Not a preference.docs/was at the 20-doc cap, and thecatalog-doc-sizehook says merge related pages rather than split, so two extraction pages became one.Dependency call
Took the body's recommendation: stdlib
encoding/xml, nogofeed. The decoder is non-strict and passes an unknown charset through rather than refusing the document, because syndication in the wild still ships ISO-8859-1 and dropping a feed over one accented byte is a worse answer.Shape
parseExtractsandvalidateExtractsmoved out ofpdf.gointo a newextract.go, since the node stopped being PDF-specific, andfeed.gocarries the projection.server.go:681dispatches throughextractToolSuccess, which switches on the kind.An empty feed returns an empty entry list at
0 of 0rather than an error, because a subreddit with no new posts is a fact. A response that is not a feed is a clean tool error naming the document.Full suite green,
go vetclean,pre-commit run --all-filesclean,lint-examplespasses on all seven committed guardfiles.Sequencing note
The
#81reprioritization comment stands: nothing was deployed and nothing waited on this.examples/reddit.mcp.kdldeclares all four extracts now, so it is a worked example rather than a hypothetical, and the header comment that stated the raw-XML cost as knowingly accepted now records it as settled.