Skip to main content
See Node API reference for the entry, context, Connector, and tool contracts, and Agent API reference for session methods.

Entry contract

  • The entry is an ES module that exports start(context). Return from start only after the HTTP listener accepts connections and every declared route is installed. That is the readiness signal; you don’t write a health-check route.
  • In start, only register routes and start listening. Don’t fetch business data synchronously; load it on demand in request handlers.
  • Return { dispose }. dispose closes the listener, SSE / WebSocket connections, child processes, timers, subscriptions, and persistence handles. It is not a per-request completion callback.
  • The entry process is the one the host manages directly. It must stay alive, holding its assigned port, for as long as the Mini App is available.
  • No Mini App SDK is installed at runtime. The miniapp-api.ts file from the Skill is for type checking only.

context fields

The manifest determines whether hostConnector, agent, and tool are injected. Switching a permission off in the permission panel keeps the object but rejects the corresponding calls, so object presence alone does not establish authorization. capabilities.has(name) reports host implementation support, not a grant to this plugin. Use optional chaining for older hosts, such as context.capabilities?.has('tool').

Minimal entry

miniapp/node/server.mjs

When it runs

  • On demand: it starts when a Mini App is selected with @, its page is opened, or on publish or restart. Enabling the plugin doesn’t start it.
  • Shared service: each Mini App has one Node process shared by all conversations, and each conversation gets its own page. See Mini Apps and sessions.
  • Capacity: at most 3 different Mini App services run at once. A fourth returns BUSY; updating a running Mini App doesn’t take an extra slot.
  • Stop: stop only stops the service and keeps the plugin and its other capabilities. Closing a running Mini App’s tab and confirming stops its shared service and closes that plugin’s pages in every conversation.
  • Update and restart: the old process stops before the new one starts. A Mini App never has more than one process, so don’t start a second entry yourself.
When a marketplace update still provides a Mini App definition compatible with this host and requires a new process, the host keeps the running version until you apply the update from the toolbar or plugin details, or the service stops and the new definition is adopted. If the new package removes the definition or the host withholds it because of its version requirement, the old service is retired. See Marketplace updates.

Other languages and child processes

Anything can sit behind the entry. For a Python, Go, or other backend, have the Node entry launch it on its own internal port and reverse-proxy to it from the assigned port.
The host manages only the entry Node process. Close any child processes the plugin starts in dispose; the host doesn’t promise to find and end them.

Port authentication

Before loading the entry, the host installs an authentication guard on the node:http servers in the process. Only the Mini App page and the managed MCP client carry the current run’s credential; every other request gets 401 before it reaches plugin code. Plugins need no auth logic and never see the credential. See Ports and credentials for how it works.
  • Serve the assigned port with node:http, either createServer or a framework built on it. If https, http2, a raw net server, or a child process binds the port directly, the guard can’t protect it, the service never becomes ready, and startup fails with READINESS_TIMEOUT.
  • The /__mavis/ path prefix belongs to the host. Don’t declare or handle routes under it.
  • Build page requests from relative or same-origin URLs. Service Workers, pop-ups, external browsers, and ordinary browser tabs don’t carry the credential and get 401.
  • The guard covers every node:http server in the process. To receive third-party callbacks such as OAuth redirects, route them through the assigned port instead of opening another HTTP server in the process.
  • A restart issues a new credential. An already open tab gets 401 until the user refreshes or reopens it; this is expected.

Logging

Use context.logger for diagnostics. The host collects only bounded stdout / stderr samples, so keep credentials and business content out of logs and don’t rely on them being retained in full.The current log bridge truncates messages and sends only the keys of fields, not their values. See the logger reference for methods and limits.