Testing instructions

Live demo (simulated Alexa+ host): https://po-39e714efffb84f8b9a5548f7fa1ca5cf.ecs.us-east-1.on.aws/demo
Token when the page asks: a0e3c2cc15326607c0d7d4c5844be4de9111cd5e06d7f4f3
MCP endpoint for any host: https://po-39e714efffb84f8b9a5548f7fa1ca5cf.ecs.us-east-1.on.aws/mcp
  - as a bearer:  Authorization: Bearer a0e3c2cc15326607c0d7d4c5844be4de9111cd5e06d7f4f3
  - or OAuth 2.1: point the host at /mcp with no token; on the consent page use household "judge" and the token as the secret.
Health: https://po-39e714efffb84f8b9a5548f7fa1ca5cf.ecs.us-east-1.on.aws/healthz
Prepaid units (simulated vendor, no real money): tap the chip "My meter is 1111 1111 1111 1, Ikeja Electric, 3 units left", then "How are my units?", then "Buy ₦5,000 of units" and answer Yes in the dialog. Limit: ₦20,000 per purchase (the daily limit is lifted on this shared demo household).
The live instance runs the simulator at 60x (a day every 24 minutes) with a seeded week of history. The README's "Evaluate it in five minutes" runs the same sequence locally in Docker, and POWER_SOURCE=victron VRM_DEMO=1 reads Victron's public demo installation, live.

Inspiration

In Lagos, where I live, the grid is a rumour. Most households I know run a hybrid inverter (a solar array, a battery and a grid connection that comes and goes in "bands") and make a dozen small power decisions a day by guesswork. Questions like - "Can I iron now", or "will that kill the battery before morning?"; "Should the freezer stay on tonight?"; "When is the grid likely to come back?". The data to answer these questions already exists, because every modern inverter streams solar, load and state of charge to a cloud portal. But that data sits in the inverter's monitoring app, and nobody opens a monitoring app at 9 pm to find out whether they can iron a shirt.

Alexa+ made the missing piece obvious. If the assistant in the living room could ask the inverter, remember the household from one conversation to the next, and push a warning before the battery hits its floor, or a reminder when the sun is finally covering the washing machine, the guesswork disappears.

This is a large market, not a niche. Nigeria has over 200 million people and a national grid that fails for hours most days, so any household that can afford an inverter and a battery has one, and uses it carefully in terms of what to run, when, and for how long. Across sub-Saharan Africa, 563 million people still have no electricity at all (Tracking SDG 7 report, 2026), and when they are connected they will get the same unreliable grid, so the same careful planning will follow. The same question is spreading to other countries for different reasons. In Europe and the US, time-of-use tariffs make electricity cheap at some hours and expensive at others, and in South Asia and Latin America the grid is often unstable. Wherever there is a battery on the wall, someone is asking the same thing every evening, "what can I run tonight?".

What it does

Powerline is a self-hosted MCP server. MCP is the open standard through which an assistant like Alexa+ connects to outside tools. It follows the 2025-11-25 revision of the standard over Streamable HTTP, the transport that lets a server push messages to the assistant without being asked. It answers with interactive MCP App cards, includes an Agent Skill (a short instruction file that teaches any AI agent to use its tools well), comes with its own simulated Alexa+ host, and does several things on its own initiative.

  • "How's the power?" Powerline reads the inverter and shows a dashboard card right in the conversation: how much solar is coming in, what the house is drawing, the battery's state of charge, whether the grid is on, and a 24-hour battery forecast. The card gives a plain verdict ("Holds through the night") and a ribbon that shows, hour by hour, the battery level, the solar curve, the hours the grid is expected, and the reserve floor.
  • "Can I run the iron for 30 minutes?" The answer comes from a physics-based forecast that adds the iron to tonight's load and re-runs the numbers. It says yes, or "not now, best time 08:00, on solar", and tells you how low the battery would go.
  • "Plan my power for today." A scheduler takes every appliance it knows about and gives each one the earliest hour that keeps the battery above the reserve, preferring hours when the sun produces more than the house uses. The planner card works in both directions: the server sends the plan to the card to display, and when you untick an appliance the card calls the server, which re-plans the day and sends the new plan back, live.
  • Arm the plan, and Powerline stops advising and starts acting. It keeps the plan with the household, watches the clock, and at each start time pushes a message into the conversation on its own: "It's 09:00, run the iron now, the sun is covering it." The same channel warns when the battery is getting close to the reserve, when it reaches it, and when the grid goes off or comes back. If a different plan is already armed, Powerline asks before replacing it on hosts that support MCP elicitation, and says what it did on hosts that do not. Technically, each push is an update to a subscribed powerline://alerts resource, which is the push channel in the 2025-11-25 spec and maps onto the subscription stream in 2026-07-28. MCP logging, which is deprecated in 2026-07-28, is kept only as a fallback for hosts that do not subscribe to the alerts resource.
  • Prepaid units, and buying more when you say yes. Most Nigerian homes are on prepaid meters, and when the units run out the grid is no use to that house even when it is on. Powerline already forecasts the hours the grid will carry the house, so "How are my units?" converts those hours into kWh, counts down from the last meter reading using the grid energy it has recorded since, and re-runs the battery forecast without the grid from the hour the units run out: "The units run out around 22:00 while the grid is on; after that the battery carries the house and bottoms out at 24%. A top-up of ₦1,000 covers the next day." "Buy 5,000 naira of units" asks for confirmation first (through MCP elicitation, or a quote that stays valid for five minutes on hosts that cannot ask), then returns the 20-digit token to type into the meter and pushes it as an alert. This is the only tool that spends money, so it is the most carefully guarded one. If the answer is no, arrives too late or cannot be read, nothing is bought. A per-purchase ceiling and a 24-hour ceiling are checked inside the same database write that records the purchase, so two sessions cannot both slip under the limit. Every purchase is saved as pending, under a fresh VTpass request id, before the vendor is called. If the vendor's reply is unclear, Powerline asks the vendor what happened rather than buying again. The autonomous agent is never given this tool. Vendors sit behind one interface, so Powerline can use either a simulated vendor (every reply says it is simulated) or VTpass's bill-payment API (sandbox, tested against recorded replies).
  • "How did we do this week?" A third card shows, day by day, how much solar the array (panels) made, how much the house used, how many hours the grid was on, what the grid cost and what the sun was worth (once you have said "grid costs 225 naira a unit"), and the lowest the battery went. Every number comes from the household's own hourly readings, which Powerline records itself.
  • It learns. After three full days, the forecast stops using a generic load curve and uses this household's own hour-by-hour pattern instead, and it scales the solar model to what the array has actually produced. The report says when this has happened. The longer Powerline runs, the better its forecasts get.
  • "How long can we last if NEPA goes?" How many hours the battery would carry the house to the reserve if the grid dropped right now, alongside how many hours the grid is usually off, from the household's history.
  • Evening briefing. One spoken paragraph for the evening, written by Claude on Amazon Bedrock from the figures the forecast engine has already computed. The model is instructed never to invent a number, and if Bedrock is unavailable the plain figures are returned instead.
  • Household memory that outlives the session. The system profile, the appliances, the armed plan and a rolling log of when the grid went on and off are stored in Amazon DynamoDB (or in a local JSON file; the live judging instance uses the file store). From that log, Powerline learns when the grid is usually available for each hour of each weekday, and feeds that into every forecast.
  • Per-household identity, two ways in. Either a bearer token identifies the household, or a host links an account through OAuth 2.1, the way Alexa+ account linking works: the host discovers the authorization server from the 401 response's resource_metadata, registers itself dynamically, sends the user to a consent page, and exchanges an authorization code (with PKCE required) for a short-lived access token and a rotating refresh token. The same process serves all of it, and the official MCP client completes the whole flow in the test suite. Two homes can share one server and can never see each other's state, even by guessing a session id.
  • Weather-aware solar. Give Powerline the roof's coordinates and the 24-hour solar curve comes from Open-Meteo's radiation forecast for that location instead of a clear-sky model. The card footer says which source is in use, and if the weather service fails Powerline quietly falls back to the model.
  • An agent that plans the day on its own. A separate evening agent, built with the Strands Agents SDK on Amazon Bedrock, connects to Powerline as an MCP client, checks the battery and the outage outlook, plans tomorrow and arms the plan, so the morning reminders are already set before anyone speaks to Alexa+. It runs on Amazon Bedrock AgentCore Runtime, where it reads the live household, plans tomorrow and arms it.
  • Multi-vendor telemetry. One adapter interface covers Victron VRM (tested live against Victron's public demo installation), Growatt's OpenAPI (tested against recorded responses from both of Growatt's device families), and a simulator used for demos and tests.
  • A simulated Alexa+ host, built in. Open /demo and you are talking to Powerline through a complete MCP Apps host served by the same process as the Powerline server itself. This host has a real MCP client, cards rendered through the SDK's AppBridge, reminders appearing the moment they are pushed, voice in and out, and the confirmation question shown as a dialog. The host understands what you type by sending it to a language model on Amazon Bedrock, which has been given Powerline's tools and decides which one to call. If Bedrock is not configured, a simple rule-based matcher takes over, so the demo still works with no internet connection, and the page header shows which of the two is in use. The host stands in for a real Alexa+ device, and a label on screen says so.
  • "Add the kettle" just works. Powerline has twenty appliance presets with the local names people use ("pumping machine", "split unit"). The reply says what wattage and duration it assumed, so the user can correct it.
  • Per-household timezone. One server can serve many homes, each on its own clock.
  • Prompts, a resource, annotations and an Agent Skill. prompts/list offers ready-made phrasings for the questions a household asks. powerline://household is a read-only snapshot of everything Powerline knows about the home. Every tool is labelled readOnlyHint, destructiveHint or idempotentHint, so a host knows which calls are safe to make without asking. And skills/powerline-home-power/SKILL.md, written in the agentskills.io format, teaches any AI agent to use the tools well: check the power status before giving advice, offer to arm the plan after making one, pass reminders on the moment they arrive, and never invent a figure.

Who it is for, and how it reaches them

The first customer is a household like mine, with a hybrid inverter from one of the brands sold across West Africa, a prepaid electricity meter, and an Alexa+ device in the living room. Nothing needs to be installed in the house, because Powerline reads the inverter through the manufacturer's own cloud portal. One hosted copy of Powerline can serve many households at once. Each household links its account through OAuth 2.1, the same way any Alexa+ integration is linked, and each keeps its own memory, clock and electricity tariff, with no way to see another household's data. Supporting a new inverter brand means writing one adapter file, so the same server can grow from Victron and Growatt to Deye, SolarEdge and Enphase as the market changes. The prepaid-units feature matches how people here actually pay for electricity, and its safeguards are built so that an assistant can be trusted to spend money. And the same tools work, unchanged, for a home in Europe or the US on a time-of-use tariff, because the forecast only needs to know when the grid is cheap or unavailable. It doesn't need to know why.

How I built it

  • Transport. The server is built on @modelcontextprotocol/server v2 with its WebStandardStreamableHTTPServerTransport. Each connected host gets its own McpServer instance, identified by the MCP-Session-Id header. On top of that, the server has all the safeguards the 2025-11-25 spec expects from a server that keeps a session open for each host. The request origin is checked, CORS preflight requests are answered before authentication, a dead session returns 404, DELETE ends a session, sessions that have gone quiet for too long are closed automatically, there are caps on the number of sessions and the size of a request body, and shutdown is immediate. All of it is served through Hono on Node 22. A status page at / and a /healthz endpoint serve people and load balancers respectively.
  • Simulated host. src/ui/demo.ts is a complete MCP Apps host. It connects to /mcp with StreamableHTTPClientTransport, and renders each card in a sandboxed srcdoc iframe with its own AppBridge and PostMessageTransport, passing size changes and theme changes through to the card. A confirmation request (elicitation/create) becomes a dialog, and pushed alerts become toasts. The host subscribes to powerline://alerts and reads each notifications/resources/updated message, using notifications/message as a fallback, and ignores duplicates by alert id. The language-model step (/demo/llm) runs on the server, so no credentials reach the browser. It calls Bedrock Converse with a toolConfig built from tools/list, or falls back to src/demo/router.ts when no model is configured.
  • MCP Apps. The cards use @modelcontextprotocol/ext-apps v2. There are three ui:// resources, each built by Vite into a single HTML file. The planner card calls back into the server with callServerTool. All three cards follow the host's light or dark theme, disable their controls while a call is in progress, and show a readable message if a call fails, is cancelled or cannot connect. They are tested in jsdom with missing, partial and deliberately malformed data, and checked by screenshot at phone width.
  • Forecast engine. Pure TypeScript with no I/O. It integrates net = pv + grid - load to track the battery's state of charge in 15-minute steps, and only lets the grid cover a shortfall in hours where the learned pattern says the grid is usually on. can_i_run and plan_day add the appliance to the load, re-run the forecast, and search for hours where it does no harm. The engine is property-tested over 1,500 randomly generated households (the state of charge stays within bounds, adding load never raises it, the inverter's rating is never exceeded, and a plan never breaks a reserve the baseline kept). Alongside that are 165 unit, card, tool, transport, OAuth, fuzz and end-to-end tests, plus browser-level checks of the simulated host (cards, card-to-server re-planning, the confirmation dialog, reminder toasts and token mode). One test suite starts the real server and drives it with the official MCP client over Streamable HTTP through the whole README sequence, including waiting for a reminder to arrive. The Strands agent has 7 tests of its own, which CI runs against a real Powerline.
  • History and learning. A server-level recorder (src/recorder.ts) stores one reading per hour for each household, whether or not anyone is connected, without duplicating what the per-session monitors already saved, and keeps 14 days. A new simulated household starts with a week of seeded history so reports and learning are visible from the first minute. src/history.ts is pure and tested. It produces the daily reports, slices periods in the household's local time, works out the per-hour median load shape, compares how much solar the panels actually produced with how much the model predicted, and integrates hours of autonomy.
  • Acting. arm_plan stores the committed plan with the household. Each session's monitor delivers the reminders, with a grace window that covers any gap (a laptop that went to sleep, a simulated clock running fast), and marks each one as fired using the same conditional write as every other change, so that several open sessions never send the same reminder twice.
  • State. DynamoDB, with a version attribute and conditional writes. If two writes collide, the losing one re-reads the record and applies its change again. If DynamoDB says it is too busy, Powerline waits and tries again, doubling the wait each time and adding a random amount so that many retries do not all land at once. Records are kept below DynamoDB's 400 KB item limit. A missing table or bad credentials produce an error that includes the command to fix it. There is no silent fallback to the disk store.
  • Account linking. src/oauth.ts is a small OAuth 2.1 authorization server. It accepts RFC 7591 client registration only for public clients with https or loopback redirect addresses and requires PKCE with S256. It also issues single-use codes that expire after five minutes, rotates refresh tokens, compares secrets in constant time, locks a household out after ten wrong secrets, and honours PUBLIC_URL and the x-forwarded-* headers so that its metadata advertises the correct public address when it is behind a load balancer.
  • AWS. The Amazon Bedrock Converse API (us.anthropic.claude-haiku-4-5-20251001-v1:0 in us-east-1) with a 15-second deadline and a plain-facts fallback. Amazon DynamoDB (the powerline table, on-demand capacity). The evening agent is written in Python with the Strands Agents SDK, using BedrockModel and MCPClient over Streamable HTTP, and is deployed on Amazon Bedrock AgentCore Runtime (BedrockAgentCoreApp, direct code deploy), where it has been invoked against the live server. A CI job runs the agent against a real Powerline with a scripted model, so the agent's behaviour is tested on every push.
  • Ops. A multi-stage, non-root Dockerfile with a healthcheck, a compose file, GitHub Actions CI on Ubuntu and Windows plus a job that builds the Docker image and probes it, and a one-command deploy to Amazon ECS Express Mode that gives Powerline a public address an Alexa+ host can reach. The README is clear about what is real and what is simulated.
  • Tooling. Claude (Anthropic) for code assistance and review, MCPJam Inspector as the MCP Apps host during development, and GitHub Actions for CI.

Challenges I ran into

  • The SDK generation gap. The official TypeScript SDK has been split into new v2 packages (@modelcontextprotocol/server, core and client), and MCP Apps 2.0 is built for those. But most tutorials, and the hackathon's own resources, still point at the older @modelcontextprotocol/sdk v1, and the two cannot be mixed in one project. I lost half a day to that before switching everything to v2.
  • Silent failures in the transport. Two things fail without any error. sendLoggingMessage() reports success but sends nothing unless the server has declared the logging capability, and any notification sent before the client has opened its GET stream is simply dropped. I wrote both up in my friction log and in a documentation pull request to the SDK, so the next person finds the warning where they need it.
  • Two callers, one empty list. When a small language model calls plan_day with appliances: [], it means "plan all of them". When the planner card sends the same empty list, it means "the user unticked everything". Because the server treated both the same way, unticking every appliance in the card made it plan all of them instead of none, and nothing reported an error. The fix was an explicit exact flag, and the lesson was that a tool used by both a model and a user interface needs a way for the interface to say "I mean exactly this list".
  • What keeps this alive while the user is silent? If the inverter's API had hung, every tool call would have hung with it, and the 30-minute idle timer I started with would have closed the session long before a 15:00 reminder was due. Every call to an outside service now has a deadline, and a session stays alive for six hours.
  • Vendor APIs. Victron's VRM API was a pleasure to work with. It has self-service tokens, a public demo account and even a solar-forecast endpoint. Growatt's field names change from one model to the next, and the only reference is a Chinese-language document on showdoc. Deye and Solarman only hand out API keys through a support ticket, so I dropped them.
  • AWS onboarding. On a brand-new account, Bedrock refused me access for two hours while verification completed. The Free plan turned out not to qualify for promotional credits, and the "Model access" page I had been told to use had been retired in favour of a use-case form. Each of these is in the friction log with the time it cost.

Accomplishments that I'm proud of

  • A real forecast. When you ask "can I run this?", the answer is worked out from the household's own numbers, and it changes when the sun, the grid pattern or the reserve changes.
  • MCP App cards that work in both directions. The planner card does not just display the plan. It sends changes back to the server and gets a new plan in return.
  • An assistant that acts. Once a plan is armed, reminders and alerts are pushed to the host over the open GET stream, end to end, through a real host.
  • Live data from a real inverter. Powerline reads Victron's public demo installation through the VRM API, and every tool, card and forecast works on that live data, even though I have no Victron hardware of my own.
  • A purchase you can trust an assistant with. The one tool that spends money asks before it acts, has a spending cap, records the purchase before it calls the vendor, and cannot buy the same thing twice. There is a test for each of those safeguards, and another that checks the autonomous agent is never given this tool at all.
  • Tests that tried to break the forecast engine but could not. Across 1,500 randomly generated households the engine's rules never broke, and writing those tests turned up two bugs that reading the code had missed.
  • I made 54 commits, wrote 165 tests and 30 friction-log entries, and shipped a live deployment, all inside the submission window, and I did all of this as a solo entrant with a full-time job

What I learned

  • The part of the MCP spec that covers keeping a session open over Streamable HTTP is only a few pages long, but it is easy to get wrong in ways that fail silently. Most of my time went into getting the transport right, not into writing the tools.
  • MCP Apps change what an assistant can be. A card that can call back into the server turns a chat into something you can operate.
  • Forecasting a household's power is mostly careful bookkeeping. A clear-sky solar curve, a load profile and a learned grid pattern give you most of the value. The rest is being honest about what you do not know.
  • Every call to something outside the process needs a deadline, and every feature that runs in the background needs an answer to the question "what keeps this alive while the user is silent?"
  • My day job involves keeping enterprise infrastructure running. Building something for my own house in the evenings, where a mistake means a thawed freezer or a night without power, taught me a whole lot about designing for unreliable systems.

What's next for Powerline: Home Power Copilot for Alexa+

  • Store OAuth logins in DynamoDB, so that a host stays linked even when the server restarts, and combine the inverter maker's own solar forecast (Victron's VRM provides one) with the weather-based curve.
  • Support more inverter brands through the one-file adapter interface, starting with Deye and Solarman, SolarEdge and Enphase.
  • Purchases with real money. Today prepaid units are bought through a simulated vendor or VTpass's sandbox. Going live needs a merchant account with a licensed vending provider (or Amazon Pay, if Alexa+ makes it available to MCP integrations), and the forecast will need to account for the grid charging the battery, so the units estimate covers that too.
  • "Turn it off for me". Close the loop by letting Powerline act on appliances through Alexa smart-home routines and smart plugs.
  • A Fire TV version of the dashboard card, so the household's power is visible on the living-room screen.

A note on names

Alexa, Alexa+ and Amazon are trademarks of Amazon.com, Inc. or its affiliates. Victron Energy and VRM, Growatt and ShinePhone are trademarks of their respective owners. Powerline is an independent project and is not affiliated with or endorsed by any of them. The demo host is a simulation, not the Alexa+ app.

Built With

Share this project:

Updates

Submission history