Back to Experiments

Experiment 015

Live Node MCP SDK · opencode primary agent · Shared Auth

Letting the LLM Into My Own Web App Through an Internal MCP, No Manual Entry.

Can a self built web app let the LLM in through an internal MCP so ideas land without manual entry?

Muse Spark 1.3 via opencode zen · opencode tools · 7 verified captures · 0 new servers · 26 Sep 2026

Source Code

The translator is public. Prove it yourself.

You get the full MCP server plus the agent definition. One small translator file, six honest tools, one primary agent. No secrets inside. The tracker it writes to is the same live ContentOS from EXP 014.

github.com/robbyaliasaakbar/opencode-agents/tree/main/mcp-contentos

github.com/robbyaliasaakbar/robbyaliasaakbar.github.io/tree/main/contentOS

System Requirements

MCP Server

server.js 182 lines, 6 tools, stdio

Agent

contentos-manager, primary, temp 0.7

Auth API

Funnel :443 to :7002, token in memory

Data API

Funnel :9443 to :7010 Node Express

Engine

Node 22, SDK 1.30.1, zod

Model

Muse Spark 1.3 via opencode zen

Dev Tools

opencode session tools

New Servers

Zero, reuse both funnels

First experiment developed with a hosted model instead of the local coder. The translator runs on the laptop config, the tracker data still lives on the home PC behind the same two encrypted funnels as EXP 014.

Screenshots

Terminal Receipts in Order Plus the Live Board

Seven captures in strict order. Shots 3, 4, and 5 are one continuous MCP run split for uniform size, read them top to bottom as crud1, crud2, crud3. Board shots show the public admin dummy rows from EXP 014, while MCP receipts use the isolated test account, written openly below.

1

Agent Registered Globally

agent-list.webp
Terminal listing global opencode agents with contentos-manager highlighted - Robby Aliasa Akbar
Agent list - contentos-manager.md sits beside the portfolio and n8n agents
2

Base Door Open Plus Empty Start

login.webp
Terminal login receipt plus empty dashboard total zero - Robby Aliasa Akbar
Login - password typed blind, token shown as 8 chars, dashboard total 0
3

MCP Run Part 1 of 3 - Login Plus Create

mcp-crud1.webp
MCP transcript start with login plus empty dashboard plus created content record - Robby Aliasa Akbar
CRUD 1 - translator login, dashboard 0, save created content-004
4

MCP Run Part 2 of 3 - Read Back

mcp-crud2.webp
MCP transcript middle with created record JSON plus read back - Robby Aliasa Akbar
CRUD 2 - full JSON of content-004 plus get by id
5

MCP Run Part 3 of 3 - Edit Plus Delete Plus Clean

mcp-crud3.webp
MCP transcript end with updated revision plus deleted record plus clean dashboard - Robby Aliasa Akbar
CRUD 3 - updated to revision, removed, dashboard back to 0
6

Live Board Light Plus Dark

dashboard-light-app.webp / dashboard-dark-app.webp
ContentOS live board in light mode with three admin dummy rows - Robby Aliasa Akbar
Board Light - public admin dummy rows 001 to 003
ContentOS live board in dark mode with three admin dummy rows - Robby Aliasa Akbar
Board Dark - identical board in dark theme

Honest note: board shots show the public admin dummy rows from EXP 014. MCP receipts above use the isolated test account ibborarts with total 0. Two accounts, written openly, no mixing.

Live Demo

No New Web App, Only Receipts

The tracker is still /contentOS/ from EXP 014. This experiment proves an AI scribe can login, list, save, edit, and remove through 6 local tools. Try the tracker live, then compare with the terminal shots above.

Open Live Tracker - /contentOS/ Live tracker, home PC backend

Live tracker, home PC backend. If the computer or tunnel doors are closed, tools fail with an honest message, which is expected and not broken. The captures above document the entire MCP run. Test rows were created and deleted on the same day, the board is clean. Demo uses isolated test records only.

For those of you unable to read this data from technical standpoint, here is the conclusion:

1. My content notebook worked but my hands were still the only pen, so I built a small translator that lets AI carry ideas to the shelf

2. You lock one idea in chat, the scribe saves it straight to the live tracker, no browser opened

3. One login opens the door, the key lives in short memory and dies on restart, zero new passwords written

4. Test notes were created and deleted on the same day, the board is clean and the receipts are on this page

5. The page is public but the engines sleep in my home computer, when it is off the scribe says so honestly

6. Everything that broke is written here, two small stories, and no robot works alone without your lock

Experiment Details

Architectural Decisions and Engineering Tradeoffs

No new app, no new auth, no autonomous robot. Here are the five decisions that let an LLM write into my own web app through a small internal door.

Translator, Not a New Backend

MCP server.js, 182 lines

The MCP server is a thin translator between AI tools and the exact same REST endpoints the React frontend uses (src/api.js plus src/auth.js). One file holds six tools, an in memory token slot, and the same posted requires link check the form enforces. Because the contract never changed, every frontend rule automatically applies to the AI path too.

Development happened inside opencode with hosted model Muse Spark 1.3 via opencode zen, the first experiment in this portfolio not built with the local coder. The translator itself stays tiny on purpose so a beginner can read it end to end in one sitting.

Six Honest Tools

login, list, get, save, remove, dashboard

login runs first and stores the token for the other five. list plus filters checks for similar ideas before writing. get reads one record before editing. save without id creates, with id updates. remove deletes by id after confirmation. dashboard opens and closes every session so start and end totals stay visible.

The full lifecycle ran live as content-004: created as ide, read back identical, updated to revision with edited caption, deleted, dashboard back to total 0. Six tools proved enough to carry ideas, small enough to audit.

Memory Token, Honest Hours

Strict TLS, fail closed

The bearer token lives in a process variable after login and dies on restart, so every fresh session starts with an explicit login. Strict certificate checks pass without any insecure flag, and missing tokens answer HTTP 401 immediately. The backend runs on the home PC from morning to evening, off hours produce an honest offline message instead of a blank screen.

Passwords never touch screenshots because entry uses blind input, tokens appear as 8 characters only, and full secrets never land in git or on this page.

Global but Backed Up Safely

opencode.jsonc vs example

The MCP registers as a local entry in opencode.jsonc with a node command plus the two public funnel URLs, enabled by default, usable from any project folder. That file holds real secrets so it stays ignored by git. The public opencode.jsonc.example carries the same shape with placeholder secrets, safe to push for restores on a new laptop.

A small gitignore fix narrowed the root package ignore so the MCP package file joins the backup while every node modules folder stays out.

Primary Agent With Manners

contentos-manager, primary

The contentos-manager agent runs as a primary agent, not a subagent, so it chats directly, brainstorms 3 to 5 ideas per batch, attaches references, and only writes after an explicit lock. Delete always names the id and asks first. Its fourteen permissions mirror the portfolio agent, plus an explicit allow for the contentos tools so the MCP stays on by default.

Work runs in small batches of understand, do, verify, then continue, with exactly one active todo at a time and no next batch before the current one is locked.

Evidence Log

Standalone Evidence Log: Terminal Receipts and Verification

Every claim about the translator and the agent is backed by verifiable command line outputs from 26 Sep 2026. Passwords and full tokens are truncated, raw status codes, JSON structures, and file listings remain exact.

E1. Both Doors Answer With Fail Closed 401

401 Both Doors

Hitting both public funnels without a token proves the services are awake and defensive.

curl -sk -m 15 -o /dev/null -w "AUTH %{http_code}\n" https://aispec.tail06293c.ts.net/api/me
AUTH 401

curl -sk -m 15 -o /dev/null -w "DATA %{http_code}\n" https://aispec.tail06293c.ts.net:9443/dashboard
DATA 401

Result: both doors reachable in about 0.01 seconds, both demand a token.

E2. Strict Certificates Pass Without Insecure Flags

TLS Valid

Repeating the same probe without skipping certificate checks.

curl -s -m 15 -o /dev/null -w "AUTH_STRICT %{http_code}\n" https://aispec.tail06293c.ts.net/api/me
AUTH_STRICT 401

curl -s -m 15 -o /dev/null -w "DATA_STRICT %{http_code}\n" https://aispec.tail06293c.ts.net:9443/dashboard
DATA_STRICT 401

Result: certificates valid, Node fetch needs no bypass.

E3. MCP Installs Clean and Agent Registers

0 Vulnerabilities

Installing the translator and confirming the agent appears beside the older agents.

npm install
added 94 packages, 0 vulnerabilities

node --check server.js && echo SYNTAX_OK
SYNTAX_OK

opencode agent list | grep contentos-manager
contentos-manager (primary)

Result: 182 line server plus 83 line agent, both load first try after install.

E4. Full MCP Lifecycle on Isolated Account

content-004 Round Trip

One MCP run on the isolated test account, created and cleaned the same day.

TOOLS: login, list, get, save, remove, dashboard
login: Login ok sebagai ibborarts
dashboard awal: {"total":0}
save: Tersimpan (created) jadi content-004
get: content-004 kebaca (IG, reels, ai, ide)
save: Tersimpan (updated) jadi content-004 (revision)
remove: content-004 dihapus
dashboard akhir: {"total":0}

Result: write, read, edit, delete verified, board back to clean zero.

E5. Backup Separates Secrets From Shape

Git Safe

Proving the real config stays local while the restorable shape is public.

git status --short
M .gitignore
M opencode.jsonc.example
?? agents/contentos-manager.md
?? mcp-contentos/

git check-ignore -v opencode.jsonc
.gitignore:5:opencode.jsonc opencode.jsonc

Result: secrets ignored, translator plus agent plus example backed up.

Failure Log

Both Failures Documented Honestly

Only two defects appeared during this build and both were fixed the same day. Written here with cause plus fix plus check, same honesty as the twelve in EXP 014.

Tier 1 Incidents Two Deep Dive Case Studies

1. Test Script Cannot Find the SDK Outside Its Folder

Module Resolution Trap

Symptom: The MCP run passed syntax checks yet the throwaway test in the temp folder crashed with a module not found error for the SDK package.

Diagnosis: Modern module resolution looks beside the running file, not at the shell folder, and it ignores the legacy module path variable. The test file sat far from its dependencies.

Fix: Moved the test beside the server file so resolution found the installed packages, verified the full green run, then deleted the throwaway file to keep the folder clean.

Lesson: Run throwaway tests beside their dependencies, then remove them the same day.

2. Global Ignore Rule Hides the New Package File

Backup Trap

Symptom: The new translator folder showed as untracked but its package file stayed invisible to git, which would have broken restores on a new laptop.

Diagnosis: A bare filename pattern in the ignore file matches at every folder depth, so it swallowed the nested package files along with the root ones.

Fix: Scoped the root ignores with a leading slash so nested package files join the backup while root secrets plus every modules folder stay out.

Lesson: A pattern without a slash hits every level, scope root rules explicitly.

Tier 2 Log No Further Failures

No minor defects beyond the two above. Stated explicitly so the count stays honest at two, not inflated.

MCP Code

Small Translator, Readable in One Sitting

The server mirrors the frontend contract tool for tool. Below are the essence of the login tool that stores the token plus the save tool that creates and updates through one endpoint.

Login Tool Stores Token - server.js (essence)

server.tool("login", "Login plus store token", {
  identifier: z.string(),
  password: z.string()
}, async ({ identifier, password }) => {
  const r = await fetch(AUTH_URL + "/api/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ identifier, password })
  })
  const j = await r.json()
  if (!r.ok) return err("identifier or password wrong")
  savedToken = j.token
  return ok("Login ok, token stored")
})

Save Tool Creates and Updates - server.js (essence)

server.tool("save", "Create or edit content", {
  id: z.string().optional(),
  status: z.string().optional(),
  link_postingan: z.string().optional()
}, async (f) => {
  if (f.status === "posted" && !f.link_postingan) {
    return err("Link required for posted")
  }
  const r = await fetch(API_URL + "/content/ingest", {
    method: "POST",
    headers: { "Content-Type": "application/json", ...authHeader() },
    body: JSON.stringify(f)
  })
  const j = await r.json()
  return ok("Stored (" + j.action + ") as " + j.item.id)
})

Agent Notes

Primary Agent Definition in One Markdown File

The agent file name becomes the agent name. Primary mode keeps it directly chatable, warmer temperature keeps brainstorming lively, explicit tool permission keeps the MCP on by default.

Agent Frontmatter - contentos-manager.md (essence)

description: content calendar scribe for ContentOS
mode: primary
temperature: 0.7
permission:
  read: allow
  edit: ask
  bash: ask
  contentos_*: allow
File name becomes agent name. Global folder makes it available in every project. Fourteen permissions mirror the portfolio agent, the last line switches the MCP tools on.

Networking Architecture

Same Two Doors, Zero New Servers

This experiment adds no infrastructure at all. The translator calls the same two public doors the browser already uses, keeping traffic paths clean and isolated per service.

Door 1: Shared Authentication Gateway

Public URL: https://aispec.tail06293c.ts.net

Internal Target: Docker container on port 7002 (PHP auth engine from EXP 011).

The login tool posts here, tokens verify here, MCP memory holds the result until restart.

Door 2: Dedicated ContentOS Data Gateway

Public URL: https://aispec.tail06293c.ts.net:9443

Internal Target: Docker container on port 7010 (Node Express plus SQLite content.db).

List, get, save, remove, and dashboard all pass this door with the stored bearer token.

Both gateways terminate encrypted TLS connections at the machine boundary. The browser and the MCP scribe are two hands reaching through the same doors.

FAQ

Frequently Asked Questions

Why not just type in the app? ▼

Typing stays. The scribe is for locked ideas from chat, it only carries what you approve.

Does the AI work alone? ▼

No. You brainstorm, you lock, it saves. Delete always asks for the id first.

Is this a new backend? ▼

No. Same auth and same data doors as EXP 014. One translator file, zero new servers.

Where is my password? ▼

Nowhere on this page. Login happens in session memory, secrets never land in git or screenshots.

Why only six tools? ▼

List, get, save, remove, dashboard plus login. Enough to carry ideas, small enough to track.

Why does it fail when your PC is off? ▼

No VPS by decision, home PC plus secure tunnels (:443 auth, :9443 data). Off means doors closed and the page says so instead of pretending. Same honesty as EXP 010 through 014.

Live Status

Live Status - Live Translator, Home-PC Backend

✅ What works now

  • MCP with 6 tools for login, list, get, save, remove, dashboard
  • Full record round trip created, read, updated, deleted live
  • Primary agent global with brainstorming plus direct execution
  • Strict certificate checks plus fail closed 401 responses
  • Backup safe config with secrets ignored and shape public
  • Two honest failures logged with cause plus fix plus check

🚧 What isn't production yet

  • Service runs on personal home computer rather than 24/7 cloud VPS
  • Token lives in process memory only, gone on every restart
  • Single file SQLite database without clustering or failover
  • No export tool inside the MCP yet, browser export covers it
  • Long term service hardening reserved for future infrastructure projects
Translator live on my laptop config. Backend runs on my personal PC (funnels :443 + :9443). Off means tools fail with a message, expected. Transcripts plus file listing show how it works.

Disclaimer

Disclaimer - Local Translator, Local Backend, Not Production-Hardened

Built with Muse Spark 1.3 via opencode zen inside opencode, the first hosted model claim in this portfolio. Test rows only, board cleaned. Passwords, tokens, and OTP codes are never shown.

Shared backend: EXP 011 (:7002) · Tracker: EXP 014 (/contentOS/) · This: EXP 015 (MCP plus agent)