<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Carlo Santos</title>
    <description>The latest articles on DEV Community by John Carlo Santos (@kuyacarlo).</description>
    <link>https://dev.to/kuyacarlo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3833821%2F23c7960c-4486-4078-bfc0-f0b779690422.png</url>
      <title>DEV Community: John Carlo Santos</title>
      <link>https://dev.to/kuyacarlo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kuyacarlo"/>
    <language>en</language>
    <item>
      <title>We almost forgot about the UGREEN hackathon. We shipped anyway.</title>
      <dc:creator>John Carlo Santos</dc:creator>
      <pubDate>Sat, 14 Nov 2026 20:31:44 +0000</pubDate>
      <link>https://dev.to/kuyacarlo/we-almost-forgot-about-the-ugreen-hackathon-we-shipped-anyway-m8i</link>
      <guid>https://dev.to/kuyacarlo/we-almost-forgot-about-the-ugreen-hackathon-we-shipped-anyway-m8i</guid>
      <description>&lt;p&gt;So we found the UGREEN Hackathon 2026 on AI NAS, said "yeah we join this," started some research... and then kinda forgot about it, twice. A few days before the deadline it came back to haunt us. This is the story of how we still got &lt;strong&gt;Photo Backstory&lt;/strong&gt; in, a Skill that reads the handwriting on the back of old scanned photos, and what we'd tell anyone (especially our own teammates who are new to this) about doing hackathons with AI agents.&lt;/p&gt;

&lt;p&gt;Spoiler: &lt;strong&gt;it passes UGREEN's checker with 0 errors and 0 warnings.&lt;/strong&gt; It has also never touched a real NAS yet. Both of those are true at the same time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc16hnqe01imx7sji9r18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc16hnqe01imx7sji9r18.png" alt="Photo Backstory cover: a scanned print beside its handwritten back, reading Aunt Sarah and kids, cabin at Pine Lake, July 1974, with the names, place and date the Skill extracts" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, what even is a UGOS Skill
&lt;/h2&gt;

&lt;p&gt;Not a system, not an app, a Markdown file. That's it. You write SKILL.md, and it tells the NAS's built-in AI which of its own agents to call, in what order, and what to do when stuff breaks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No code.&lt;/strong&gt; No scripts, no binaries, no manifests. Just &lt;code&gt;SKILL.md&lt;/code&gt; and some Markdown references.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't do the work, the agents do.&lt;/strong&gt; Every step is one line like &lt;code&gt;delegate --target photo-agent --instruction '...'&lt;/code&gt;. There are six built-in agents: files, photos, documents, knowledge, media and cameras.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There's a checker.&lt;/strong&gt; UGREEN ships one in their kit. Fail it and your ZIP isn't even uploadable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No NAS needed for Round 1.&lt;/strong&gt; You send a 500-word PDF and the ZIP. If you make the top 30, you get one day on a real iDX6011 Pro.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you make the top 30, you get one day on a real iDX6011 Pro.&lt;br&gt;
So it's less "build a program" and more "write really precise instructions for someone else's robots." Which, honestly, changes everything about how you should work.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it actually went (oldest first, from vibes to PANIK):
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date (Manila)&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Oct 25&lt;/td&gt;
&lt;td&gt;We found the Hackathon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 26-31&lt;/td&gt;
&lt;td&gt;silence... part 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 31&lt;/td&gt;
&lt;td&gt;Read the rules, wrote research prompts, felt very productive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 1–9&lt;/td&gt;
&lt;td&gt;[the forgetting era]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 10&lt;/td&gt;
&lt;td&gt;Research reports came back, one per track&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 10&lt;/td&gt;
&lt;td&gt;Finally downloaded UGREEN's kit. Read the real capability list. Picked the idea after gajillion tries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 12&lt;/td&gt;
&lt;td&gt;First actual team call. Built the Skill, the PDF and the Devpost text the same day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 13&lt;/td&gt;
&lt;td&gt;Team research doc with 11 more ideas (thanks Gemini)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 15&lt;/td&gt;
&lt;td&gt;Fact-checked our own team's research doc, checked who else had built what, locked the idea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 15&lt;/td&gt;
&lt;td&gt;Second pass, found a real design flaw, fixed it, made gallery images, submitted. Also wrote this(thanks claude)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If we'd just started research at Oct 25, and downloaded the kit on Oct 31, we'd have saved like a week. Lesson one, and we're not even at the lessons yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking the idea
&lt;/h2&gt;

&lt;p&gt;We went with whatever we felt comfortable, then ditched it and used an idea nobody else seemed to have(besides Google and Claude(?), not the one the AI scored highest(ps. theyh all are ass). Here's why.&lt;br&gt;
We ran Gemini Deep Research(Extended Thinking, at that) prompts per track: real user problems, a scoring matrix, test cases, the works. The reports came back long, very confident and very broken:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Every idea scored 21 to 24 out of 25 across three independent runs.&lt;/strong&gt; When everything's an A, its totally ass. We threw the scores out.&lt;br&gt;
&lt;strong&gt;2. The same idea showed up three times.&lt;/strong&gt; A "B-roll video indexer" kept topping the list. Problem: it needs to search what's inside the video, and the platform can't do that, yet.&lt;br&gt;
&lt;strong&gt;3. Nobody checked the competition.&lt;/strong&gt; Devpost project pages are public before judging. A later report ranked a receipt reconciler and a contract-renewal tracker #1 and #2. A five-minute search found people who'd already built pretty much exactly those.&lt;/p&gt;

&lt;p&gt;What actually helped: we took UGREEN's own example ideas (dedupe your downloads, travel albums, meeting notes, invoices, garage camera summaries) and put them on a &lt;strong&gt;do-not-build list&lt;/strong&gt;(aka. dumpster. TL:DR; It's easier to prompt away for these use cases than to build one and test it blindingly). Then we looked for something that only makes sense on a NAS full of private family stuff.&lt;br&gt;
That's how we landed on photo backs. Old prints have the who, when and where written by hand on the back. Scan them, and the front and back become two random files, and the writing is basically gone forever. We searched the public entries and nobody was doing it. Easy pick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust the kit, not the vibes
&lt;/h2&gt;

&lt;p&gt;Best thing we did, by far: open UGREEN's kit and read the capability catalog and the checker ourselves. The marketing page and the AI summaries were both wrong in ways that would've cooked us.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What we were told&lt;/th&gt;
&lt;th&gt;Who said it&lt;/th&gt;
&lt;th&gt;What the kit actually says&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;There are six capability areas&lt;/td&gt;
&lt;td&gt;UGREEN's own starter page&lt;/td&gt;
&lt;td&gt;9 tools and 78 actions, including OCR and a vision model you can prompt. Way more than six&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video search can find shots by what's on screen&lt;/td&gt;
&lt;td&gt;Several AI research reports&lt;/td&gt;
&lt;td&gt;It browses a movie/TV library by title, actor, genre. No scene search. B-roll idea: dead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a &lt;code&gt;README.md&lt;/code&gt; to your ZIP&lt;/td&gt;
&lt;td&gt;Starter page and an AI report&lt;/td&gt;
&lt;td&gt;The checker fails anything at the root besides &lt;code&gt;SKILL.md&lt;/code&gt; and &lt;code&gt;references/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capability lines look like &lt;code&gt;- cli -&amp;gt; agent&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;An AI report&lt;/td&gt;
&lt;td&gt;Wrong heading, wrong arrow. The checker wants an exact heading and a real &lt;code&gt;→&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writes need one approval each&lt;/td&gt;
&lt;td&gt;Us, in our first draft&lt;/td&gt;
&lt;td&gt;Approvals are per action: one per new tag, one per file for date changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An AI summary of a platform is a guess about the platform. The catalog file &lt;em&gt;is&lt;/em&gt; the platform. When they disagree, the file wins, and you only find out if you open it.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what does it do
&lt;/h2&gt;

&lt;p&gt;You point Photo Backstory at a folder of scans. It reads the backs, shows you what it found, and only writes anything after you say yes. The whole thing is one &lt;code&gt;SKILL.md&lt;/code&gt; plus three Markdown references.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lists the scans&lt;/strong&gt; in the folder you pick. Empty folder, or more than 200 images? It tells you and stops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pairs fronts with backs&lt;/strong&gt;, using names like &lt;code&gt;_a&lt;/code&gt;/&lt;code&gt;_b&lt;/code&gt; or just scan order. You confirm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reads every back twice:&lt;/strong&gt; once with OCR, once with a vision model at temperature 0. If the two disagree on a name or a number, it says "not sure" instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shows you a review table:&lt;/strong&gt; names, dates, places, confidence, plus flags for blank backs, swapped pairs and date conflicts. Fix whatever you want. It also tells you how many approval pop-ups are coming, so no surprises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writes what you approved:&lt;/strong&gt; a Markdown "backstory index" first (that's the source of truth), then two tags per scan, one album, and optional file dates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhxepovbd3wkevbc2kozw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhxepovbd3wkevbc2kozw.png" alt="The five steps of Photo Backstory: list scans, pair fronts and backs, read every back twice, review the table, write what was approved" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It never deletes, moves, renames or touches the EXIF of your originals, and nothing leaves the NAS. Grandma's photos stay home.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Let it ride" part 2
&lt;/h2&gt;

&lt;p&gt;Version one passed the checker. It still had a real design flaw. A second pass, this time checking every &lt;em&gt;parameter&lt;/em&gt; instead of just every action name, caught it the day before the deadline.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approval spam.&lt;/strong&gt; Creating a tag and changing a file's date each need their own approval. Our first version tagged every name and place, which meant 20+ pop-ups for ten photos. Nobody wants that. Now it's two default tags, the rest are opt-in, and it tells you the pop-up count up front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free consistency.&lt;/strong&gt; The vision model takes a &lt;code&gt;temperature&lt;/code&gt; setting. Set it to 0 and the same scan gives the same reading every time. Free win, almost missed it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust the receipt.&lt;/strong&gt; The save action returns a &lt;code&gt;verified&lt;/code&gt; flag. Now the index only counts as saved when that flag says so, and if it fails, nothing else gets written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The word count jumpscare.&lt;/strong&gt; Our counter said 496 words, under the 500 limit. A stricter counter, which splits &lt;code&gt;vision-ocr&lt;/code&gt; into two words, said 528. Over. We trimmed it to 486 by the strictest count.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Passing the checker means your file is well-formed. It doesn't mean your Skill is good. Leave time for a second, slower look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a few AIs at once without losing the plot
&lt;/h2&gt;

&lt;p&gt;Each AI got one job, and the humans kept the decisions. The thing holding it together was one shared Markdown file.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Who&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Watch out for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini (Deep Research)&lt;/td&gt;
&lt;td&gt;Big-picture research: user problems, market, idea lists&lt;/td&gt;
&lt;td&gt;Very confident numbers with no source. Inventing features the platform doesn't have&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;Reading the kit, building the Skill and PDF, running the checker, fact-checking the research&lt;/td&gt;
&lt;td&gt;Saying "done" before anything has actually been checked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Us&lt;/td&gt;
&lt;td&gt;Picking the idea and the track, actually hitting submit&lt;/td&gt;
&lt;td&gt;Deciding stuff in a call and never writing it down&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The handoff file&lt;/strong&gt; is one Markdown doc that every AI reads first and updates after every change. One of our teammates brought the template over from another project, and it slaps. It has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;Audit&lt;/strong&gt; log, newest first, never deleted;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Raw Context&lt;/strong&gt; section only humans are allowed to touch;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Erroneous Context&lt;/strong&gt;, where wrong facts go to retire (with the date and the fix) instead of getting quietly erased;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Done&lt;/strong&gt; list that only takes things a check actually passed (checker exit code 0, a real word count);&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Decision Log&lt;/strong&gt; with what people said, word for word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how research from one AI and a build from another lined up without anyone losing track of what was true. When one AI got something wrong, the fix went into Erroneous Context, so the next AI didn't repeat it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The playbook (for teammates, and future us)
&lt;/h2&gt;

&lt;p&gt;If you remember one thing: get the platform's real files on day one, and make every AI claim prove itself against them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Download the official kit first.&lt;/strong&gt; Before research, before ideas. Read the capability list, run the checker on an empty Skill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paste the real capability list into every research prompt.&lt;/strong&gt; Tell the AI that's the full list and anything else is unverified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put the organizer's example ideas on a do-not-build list.&lt;/strong&gt; Everyone else is building them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check who already built your idea.&lt;/strong&gt; On Devpost, project pages are often public before judging, even when the gallery isn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignore scores the AI gave itself.&lt;/strong&gt; If everything is a 9/10, ask two questions instead: has someone built this already, and does it actually need &lt;em&gt;this&lt;/em&gt; platform?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build to the checker, not the web page.&lt;/strong&gt; When they disagree, the checker is the one deciding if you get in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Done" means a check passed.&lt;/strong&gt; Exit code 0, a measured word count, a file under the limit. Not "the AI said it's done."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do a second pass on the details.&lt;/strong&gt; Approval rules, return values, option names. That's where the real problems hide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep one handoff file&lt;/strong&gt; that every human and AI reads first and updates after every change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submit early, then keep editing.&lt;/strong&gt; Devpost lets you edit until the deadline, and a draft you forgot to submit scores a big fat zero.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What we'd do differently (a lot, honestly)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;We lost more than a week&lt;/strong&gt; to the forgetting era and to waiting on research before opening the kit. The kit would've killed half our ideas in an hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We decided late.&lt;/strong&gt; The team's first real call was three days before the deadline, and a second round of research almost dragged us into the most crowded track.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We haven't seen it run.&lt;/strong&gt; Round 1 doesn't need hardware, so whether it reads real cursive and faded pencil, makes albums from file paths, and shows file dates on the Photos timeline are all still open questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We leaned hard on AI.&lt;/strong&gt; It did most of the typing and caught a bunch of its own mistakes on the second pass. It also said wrong things with total confidence, and only checking against the real files caught those. All of it is disclosed in the package.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;If we make the top 30 (announced Nov 21), we get one day on a real iDX6011 Pro, and the plan is already written:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20 scans with &lt;code&gt;_a&lt;/code&gt;/&lt;code&gt;_b&lt;/code&gt; names;&lt;/li&gt;
&lt;li&gt;scans without suffixes, plus one correction during review;&lt;/li&gt;
&lt;li&gt;a folder with a blank back, a swapped pair and a file with no partner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that, the same trick works on other handwritten stuff: recipe cards, postcards, letters. Wish us luck.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>hackathon</category>
      <category>showdev</category>
    </item>
    <item>
      <title>SAGE — A Notion MCP Academic Co-Pilot for Filipino University Students</title>
      <dc:creator>John Carlo Santos</dc:creator>
      <pubDate>Sat, 28 Mar 2026 13:12:44 +0000</pubDate>
      <link>https://dev.to/kuyacarlo/sage-a-notion-mcp-academic-co-pilot-for-filipino-university-students-3lmb</link>
      <guid>https://dev.to/kuyacarlo/sage-a-notion-mcp-academic-co-pilot-for-filipino-university-students-3lmb</guid>
      <description>&lt;p&gt;SAGE: The Notion MCP Academic Co-Pilot Built for Filipino Students Who Are Tired of Having 7 Tabs Open&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo20ayw6zj41gqni7sw1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo20ayw6zj41gqni7sw1o.png" alt="Cover Image showing SAGE Homepage" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;notes in Google Keep. also in GDocs. also in Obsidian. also in Notion. tasks in Teams. also Google Classroom. also Canvas. also the blackboard in the room that gets erased before you can photo it — and if you did take a pic, you'd forget about it til it's too late anyway. rip bozo.&lt;/p&gt;

&lt;p&gt;six, seven different apps, each going their own way, far from what I actually need. advanced studying? researching ahead? a completely different beast on top of all that chaos.&lt;/p&gt;

&lt;p&gt;I'm a 2nd year BS CpE student at BulSU. my academic life was a mess. still kind of is. but I built SAGE anyway.&lt;/p&gt;

&lt;p&gt;(college is shit. life is also shit. but at least the workspace builds itself now. probably.)&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;your program, year, and semester. that's it. SAGE hooks into Notion via MCP, pulls your curriculum from Ghost Commons, and builds your entire workspace automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "I am a 2nd year BS CpE student, semester 1. Build my workspace."

SAGE: "We built your 2nd Year BS CpE — Semester 1 workspace! 🎉

- COE201 — Differential Equations
- COE202 — Engineering Data Analysis
- CPE201 — Discrete Mathematics
- CPE202L — Object Oriented Programming
- CPE203 — Fundamentals of Electrical Circuits
- CPE204L — Computer Aided Drafting

Each course has its own page with a Topics database and a Tasks database."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;60 seconds. six course pages. topic breakdowns. task databases. done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmfhdbacw4nq494z8usx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmfhdbacw4nq494z8usx.png" alt="SAGE starting to build workspace" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo5r8skhjt9gj2ye012a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo5r8skhjt9gj2ye012a.png" alt="Notion showing courses under the program" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkgn4ihuedz3hr0g15z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkgn4ihuedz3hr0g15z7.png" alt="Notion showing topics under a course" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyrtgqpgu2p339n8cnvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyrtgqpgu2p339n8cnvm.png" alt="Notion showing topics table" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ghost Commons — the Library of Alexandria
&lt;/h3&gt;

&lt;p&gt;not the piracy site. and not the one that burned down. the noble one — the one that was &lt;em&gt;supposed&lt;/em&gt; to hold all human knowledge.&lt;/p&gt;

&lt;p&gt;Ghost Commons is a curriculum registry pre-seeded from official CHED CMO data. full topic sets, summaries, and study prompts for all six BSCPE Year 2 Semester 1 courses — structured, cached, and queryable. no scraping at runtime. no broken government links. no Cloudflare walls.&lt;/p&gt;

&lt;p&gt;the spicy part: building this taught me exactly how painful Philippine education data is. some state universities follow CHED Memorandum Orders like a strict rulebook. others treat it more like... a suggestion. same course code, completely different content. same course name, different units. revisions on top of revisions, branches on top of branches. government websites: archaic, half behind Cloudflare, half with dead links.&lt;/p&gt;

&lt;p&gt;normalization was its own beast — GE courses, electives, specific majors, shared majors, all overlapping across institutions. I now deeply understand the pain of anyone who's ever had to build an education system in this country.&lt;/p&gt;

&lt;p&gt;Ghost Commons is the answer I built instead. deterministic. instant. no broken links. and it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Video Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Ua9ZzSBXpbw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Show us the code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/kuya-carlo/sage-mcp" rel="noopener noreferrer"&gt;github.com/kuya-carlo/sage-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the MCP server exposes tools to the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_commons_tree&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;# fetch curriculum from Ghost Commons
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create_semester_tree&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# build the actual Notion pages
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;breakdown_task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;# split overwhelming tasks into micro-steps
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weekly_load&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# detect burnout-level weeks
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;block_calendar_slot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# protect recovery time on calendar
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_dismissed_blocks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# track dismissed burnout warnings
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the agentic loop — every inference call replays the full conversation. every tool result. every message. the model's memory is too shite to do otherwise — keeping it all in the messages array is the footgun you accept to make agents work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;iteration&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_iterations&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_mcp_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# loop again
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;  &lt;span class="c1"&gt;# done
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the bug that haunted me until 1am: agent says &lt;code&gt;BSCpE&lt;/code&gt;. database has &lt;code&gt;BSCPE&lt;/code&gt;. SQL doing SQL things. query returned nothing. seeding kept retriggering.&lt;/p&gt;

&lt;p&gt;fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;program_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;program_code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;one line. I want those hours back. 😭&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion MCP is the core of SAGE — not a wrapper, not an afterthought. every workspace action goes through it: creating the root page, spinning up course databases with full property schemas, inserting topic rows, querying workspace state for the load monitor.&lt;/p&gt;

&lt;p&gt;the hardest part was making it stable at scale. building a full semester workspace means dozens of concurrent MCP calls — course pages, Topics databases, Tasks databases, individual topic entries — all in parallel. I implemented a global semaphore, persistent SSE connection pooling via an async context manager, and inter-course delays to keep the MCP server from choking.&lt;/p&gt;

&lt;p&gt;what Notion MCP unlocks here is genuine multi-tenant isolation: every student authorizes their own Notion workspace via OAuth, and SAGE writes only to the pages they explicitly grant access to. tokens are AES-encrypted at rest. no shared state, no data leakage.&lt;/p&gt;

&lt;p&gt;the result is a workspace that feels like it was built by hand — because structurally, it was. SAGE just does it in one shot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; — MCP server + Ghost Commons API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion MCP&lt;/strong&gt; — workspace orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen2.5-Coder-32B&lt;/strong&gt; via Vultr Serverless Inference (Thanks MLH)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase PostgreSQL&lt;/strong&gt; — curriculum registry + token store&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gaffa&lt;/strong&gt; — CMO PDF scraping + extraction (THANKS MLH!!!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker + Podman&lt;/strong&gt; — works on my machine AND in prod. probably.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; — free HTTPS, no ingress pain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;asyncpg + Pydantic v2&lt;/strong&gt; — async all the things&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;v1.1&lt;/strong&gt; — pgvector semantic search ("find courses related to embedded systems")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.2&lt;/strong&gt; — Chrome extension LMS bridge (Google Classroom, Canvas, BulSU eLMS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.3&lt;/strong&gt; — Gmail scraper for GCash/Maya burn-rate dashboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v2.0&lt;/strong&gt; — Federated Ghost Commons with Tagalog/English bridges&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;built solo in under a week. 28 units. part-time job. submitted on hungarian sausages and chaos energy. 🇵🇭&lt;/p&gt;

&lt;p&gt;college is shit. life is also shit. but at least the workspace builds itself now. probably.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxb8j36gxgl90l37pqla.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxb8j36gxgl90l37pqla.webp" alt="THE FACTORY MUST GROW" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
