micrOSMCP
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@micrOSMCPRun version on device TinyDevBoard"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.

micrOSMCP
Standalone TypeScript MCP server and browser tester UI for micrOS devices. Use it to discover devices, inspect the device cache, run micrOS commands, and discover each device's available module commands.
Table of Contents
Related MCP server: MCP Server TypeScript Template
Quick Start
npm install
npm run start:uiOpen one of the URLs printed at startup. The native tester binds on all interfaces and prints localhost plus detected LAN addresses:
https://127.0.0.1:3333
https://10.0.1.42:3333The tester has no authentication layer. Localhost and LAN clients can use every UI API directly, including persisted server-side API-key detection and MCP tools. Run it only on a trusted network, or set HOST=127.0.0.1 to restrict access to the local machine. Each AI assistant response shows a compact footer with aggregate input, output, and total token usage, including all model calls made during its tool loop.
The UI is the easiest way to verify everything locally. It includes an optional AI chat panel for testing the MCP tools with an OpenAI API key, plus manual tool forms that render schemas, keep JSON arguments editable, and give device dropdowns for device-targeted tools.
Browser microphone access for the listen button requires a secure origin, so the UI automatically creates and serves HTTPS with a self-signed certificate stored in data/ui-self-signed-cert.pem and its private key in data/ui-self-signed-key.pem. The certificate includes localhost and detected LAN addresses and is reused until it expires or the address list changes. The server prints https:// URLs for every detected local address.
Client devices must trust data/ui-self-signed-cert.pem before their browsers will permit microphone access. Accepting the certificate warning is sufficient in browsers that then treat the connection as secure, including Safari in typical local setups; other clients may require installing the certificate as trusted.
MICROS_UI_CERT_HOSTS adds IP addresses or DNS hostnames to the generated certificate. Multiple values are comma-separated:
MICROS_UI_CERT_HOSTS=gateway.local,10.0.1.42 npm run start:uiThe hostname must resolve to the UI host from the client device. Changing this list or the detected addresses regenerates the certificate, so clients must accept or trust the replacement certificate. The UI always uses HTTPS, even when MICROS_UI_CERT_HOSTS is omitted; it never falls back to HTTP. Browsers with speech recognition use live dictation; Safari falls back to recording audio and transcribing it with the saved OpenAI API key.
Stopping dictation immediately aborts browser speech recognition. A final dictation result, chat send or clear, tab hiding, and page exit also release capture. The recording fallback stops every media track before transcription, so the browser microphone indicator should turn off as soon as capture ends.
The AI chat API key, selected model, and Speak setting are saved locally by the UI server in data/ui_chat_config.json so reloads can reuse them. The saved key remains server-side: browser configuration responses expose only whether a key exists, never the key itself. Override the config path with MICROS_CHAT_CONFIG_PATH if needed. The model dropdown loads available OpenAI Chat Completions models from supported tool-calling families using the saved key; non-chat and specialized model variants are omitted because the tester's MCP bridge requires function tools. Browser speech recognition and speech synthesis are used for the optional listen/speak controls when the current browser supports them.
Use With An MCP Client
Build first:
npm install
npm run buildCodex-style config.toml:
[mcp_servers.microsmcp]
command = "npm"
args = ["run", "--silent", "start"]
cwd = "/Users/bnm/Development/micrOSMCP"Generic JSON-style MCP config:
{
"mcpServers": {
"microsmcp": {
"command": "npm",
"args": ["run", "--silent", "start"],
"cwd": "/Users/bnm/Development/micrOSMCP"
}
}
}Use --silent with npm in MCP client config so npm does not print lifecycle banners to stdout before the MCP protocol starts. The direct equivalent is:
{
"mcpServers": {
"microsmcp": {
"command": "node",
"args": ["/Users/bnm/Development/micrOSMCP/scripts/start.mjs", "mcp"],
"cwd": "/Users/bnm/Development/micrOSMCP"
}
}
}Commands
npm run help # Show start modes and environment variables
npm run build # Clean dist, compile TypeScript, and copy MCP metadata
npm run start # Start stdio MCP server from dist/
npm run start:test # Build and run minimal MCP/tool contract tests
npm run start -- ui # Start UI from dist/ without rebuilding
npm run start:mcp # Explicit stdio MCP mode
MICROS_INITIALIZE_ON_START=0 npm run start:mcp # MCP mode without startup discovery or feature scan
npm run start:ui # Build, then start the browser tester UI
npm run docker:build # Build and export Docker image tarForwarded start help:
npm run start -- --helpUseful environment variables:
MICROS_DEVICE_CACHE_PATH=/path/to/device_conn_cache.json npm run start
MICROS_DEVICE_FEATURE_CACHE_PATH=/path/to/device_feature_cache.json npm run start
MICROS_DEVICE_NOTES_CACHE_PATH=/path/to/device_notes_cache.json npm run start
MICROS_FUNCTION_MANUAL_PATH=/path/to/sfuncman.json npm run start
MICROS_NETWORK_PREFIX=10.0.1 npm run start
MICROS_CHAT_CONFIG_PATH=/path/to/ui_chat_config.json npm run start -- ui
MICROS_UI_MAX_BODY_BYTES=12582912 npm run start -- ui
HOST=0.0.0.0 PORT=3333 npm run start -- uiTools
The MCP server exposes six tools.
Tool | Purpose |
| Primary device and feature lookup before command execution. Search cached identity, notes, modules, complete function signatures, and optional live status. |
| Return a compact cached device inventory with device identity, note, and known module names only. |
| Run a fresh |
| Run a command or command pipeline on one selected device. |
| Read, append, or replace the persistent note for a cached device. |
| Run |
run_command
String pipeline using the micrOS <a> separator:
{
"deviceTag": "TinyDevBoard",
"command": "version<a>conf webui"
}Array pipeline:
{
"deviceTag": "TinyDevBoard",
"command": ["version", "conf webui"]
}Use read-only commands such as version for smoke tests. Other micrOS commands may change device state.
Commands pass through a denial policy before device lookup or socket execution. Configuration reads such as conf and conf webui are allowed. Configuration writes are denied in direct and pipeline forms, including conf webui true, conf<a>webui true, and the equivalent array representation. Controlled denials return ok: false, the matching policy rule, and deniedCommand.
When the first word of the first command exactly matches a cached module name, case-insensitively, the response includes an optional moduleHint. It contains that module's complete cached function list with signatures and available sfuncman.json documentation:
{
"moduleHint": {
"matchedCommands": ["dht22"],
"modules": [
{
"name": "dht22",
"functions": [
{
"name": "measure",
"signature": "measure log=False",
"doc": "Measure with dht22"
}
]
}
]
}
}The hint is derived from cached discovery data and may be present in both successful and failed command responses. It is omitted unless the first token of the first command matches a module exactly; a module appearing only in a later pipeline command does not produce a hint.
set_device_note
Store persistent context about a device, such as location, attached peripherals, wiring, or command interpretation hints:
{
"deviceTag": "TerraceSensor",
"note": "Mounted on the terrace. DHT22 readings are outdoor temperature and humidity.",
"mode": "replace"
}Use mode: "append" to add a line without replacing the existing note. Omit note or send an empty value to return the current note without changing it. Notes are stored by device name in data/device_notes_cache.json, survive feature rediscovery, and are shown by list_devices and search_devices.
search_devices
Use this as the primary device selection tool when you know part of a device name or part of a capability:
{
"query": "dht22"
}The query searches cached device identity fields, persistent device notes, and cached feature metadata, including module names and complete function signatures. Set fuzziness to 0 for literal substring matching, 1 for conservative typo tolerance (the default), or 2 for broader matching. Very short queries remain strict at the lower levels to avoid noisy results.
Multi-word searches use two passes. The complete query is tried first and its results are returned when any device matches. If it returns no devices, the tool retries with each individual word and returns devices matching any word. The response reports matchMode as query or words and lists the effective matchedTerms.
For each matching device, modules are selected using both the active query terms and words longer than two characters from its device note. Irrelevant modules are removed, while every selected module retains its complete function signatures. Each returned function also includes name and, when found in data/sfuncman.json, doc.
Common use cases:
Device identity:
{"query":"TerraceSensor","fuzziness":0}for a precise name, UID, or IP fragment.Capability:
{"query":"brightness"}to find devices whose cached module signatures expose brightness control.Persistent context:
{"query":"outdoor temperature"}to search device notes as a phrase, then asoutdoorortemperatureonly when the phrase has no matches.Misspelling recovery:
{"query":"temprature","fuzziness":1}for conservative typo tolerance.Broad recovery:
{"query":"kitchn diming","fuzziness":2}when multiple words may be misspelled.Live availability: add
"status":"online"to return only currently reachable matches.
Require live status while searching:
{
"query": "Terrace",
"status": "online"
}In the tester UI, status defaults to Any; choosing online or offline performs live TCP checks only for cached devices matching the text query.
discover_devices
{
"networkPrefix": "10.0.1",
"startHost": 2,
"endHost": 254,
"port": 9008,
"timeoutMs": 1000,
"concurrency": 50,
"refreshFeatures": true,
"featureTimeout": 3,
"featureConcurrency": 3
}If networkPrefix is omitted, the server uses MICROS_NETWORK_PREFIX when set, otherwise the active local IPv4 interface. Startup logs whether the scan prefix was native auto-detected or injected through the environment for container mode.
Discovery is a fresh network scan every time this tool runs. By default, it also refreshes the feature cache for newly discovered devices, but the tool response stays compact with module and command counts rather than full function details. Set refreshFeatures to false when you only want to update device addresses. Use featureConcurrency to control how many discovered devices are inspected in parallel during feature refresh.
discover_commands
All cached devices:
{}One device by UID, IP, or partial device name:
{
"deviceTag": "TinyDevBoard"
}The feature cache stores each module once with compact signature strings. The response expands each function with its parsed name and optional reference documentation:
{
"name": "gameOfLife",
"functions": [
{
"name": "load",
"signature": "load w=32 h=16 custom=None",
"doc": "Load an initial state."
},
{
"name": "next_gen",
"signature": "next_gen w=32 h=16 raw=False"
}
]
}Discovery requests <module> help >json and parses the returned JSON signature array, with a text fallback for older firmware. Response documentation is matched by module name and the first word of each signature; lookup is case-insensitive as a fallback. A missing manual, module, function, null doc, or invalid manual does not fail the tool: the doc field is simply omitted. Override the manual path with MICROS_FUNCTION_MANUAL_PATH. Legacy caches containing raw help and flattened commands are normalized into the compact structure when read. Each discovery result uses the same top-level uid, ip, port, deviceName, and deviceNote fields as other device tools.
Use password if the device requires micrOS app authentication.
How Tools Are Defined
Each MCP tool is defined in one file under mcp/tools/ plus one adjacent Markdown description file. The TypeScript file owns:
the business function, such as
runCommand(...)the MCP definition object, such as
runCommandTool
The file basename is the metadata source of truth: mcp/tools/run-command.ts becomes the MCP tool run_command with title Run Command. The adjacent mcp/tools/run-command.md file owns the MCP-facing tool description. This keeps each tool standalone while allowing the generic MCP registrar to discover tools dynamically.
Server-level MCP instructions are stored in mcp/description.md and loaded at startup. The tester chat system prompt is stored separately in ui/chat-system-prompt.md. mcp/mcp-tools.ts only discovers tool modules, registers the collected definitions, and formats MCP responses. Generic MCP helper code lives in mcp/tool-definition.ts, mcp/tool-loader.ts, and mcp/tool-registry.ts. mcp/tools.ts is the public barrel for tool functions, tool definitions, and shared types.
The rough call path is:
MCP client
-> mcp/index.ts
-> registerMcpTools() in mcp/mcp-tools.ts
-> loadToolDefinitions() in mcp/tool-loader.ts scans mcp/tools/
-> focused definition + implementation in mcp/tools/<tool-name>.ts
-> description text from mcp/tools/<tool-name>.md
-> shared micrOS helpers in mcp/tools/common.ts when neededAdd A New Tool
Create a focused tool file under
mcp/tools/, for examplemcp/tools/reboot-device.ts.Define the tool input type in that same file. Put types in
mcp/tools/common.tsonly when they are genuinely shared helper types.In the same file, export the business function and an
McpToolDefinition.Add
mcp/tools/reboot-device.mdwith the MCP-facing description.Export the function and definition from
mcp/tools.tswhen other code or tests should import them directly.Add a short README entry in the tool table or tool examples.
Run
npm run start:testfor focused contract tests and project entrypoint checks.
Implementation example:
// mcp/tools/example-tool.ts
import { z } from "zod";
import { cacheToDevices, readDeviceCache } from "./common.js";
import { defineTool } from "../tool-definition.js";
export type ExampleToolInput = {
query?: string;
};
export async function exampleTool(input: ExampleToolInput = {}) {
const cache = await readDeviceCache();
const devices = cacheToDevices(cache);
return {
query: input.query ?? null,
count: devices.length,
devices
};
}
export const exampleToolDefinition = defineTool<ExampleToolInput>(import.meta.url, {
inputSchema: {
query: z.string().optional().describe("Optional filter text.")
},
handler: exampleTool
});Description file:
Describe what the tool does for MCP clients and humans.Barrel export:
// mcp/tools.ts
export type { ExampleToolInput } from "./tools/example-tool.js";
export { exampleTool, exampleToolDefinition } from "./tools/example-tool.js";Tool responses should be JSON-serializable objects. If a tool can fail in a controlled way, prefer returning { ok: false, error: "..." }; the generic registrar in mcp/mcp-tools.ts marks those responses as MCP errors when appropriate.
Device Cache
Default cache path:
data/device_conn_cache.jsonCache format:
{
"device_uid": {
"ip": "ip-address",
"port": 9008,
"deviceName": "device-name"
}
}If the cache is missing or invalid, the server creates it with these defaults:
__devuid__:192.168.4.1, port9008, device name__device_on_AP____localhost__:127.0.0.1, port9008, device name__simulator__
The first cache read also attempts one automatic discovery and continues with whatever cache is available. Discovery is additive: it updates discovered devices but does not delete stale cached entries.
At MCP startup, the server runs an initialization pass that scans for devices, then discovers each cached device's modules and functions. Successful feature discoveries are persisted in:
data/device_feature_cache.jsonFeature cache entries contain only discovery data. Device names remain in the connection cache, and user notes remain in the notes cache, avoiding duplicated fields across runtime files. Legacy three-element connection arrays and feature records with duplicated metadata are migrated and overwritten in the compact format when read.
Persistent user notes are stored separately by device name in:
data/device_notes_cache.jsonOptional function documentation is read from the static data/sfuncman.json reference file. It enriches search_devices, discover_commands, and command module hints without being copied into the feature cache.
list_devices stays compact: it includes device identity, persistent notes, and known module names, but not function-level feature details. Use search_devices as the normal device and capability lookup before run_command, and discover_commands when cached module/function details need refreshing. Startup progress is logged to stderr so MCP stdout remains protocol-safe while clients can show that discovery is pending. Set MICROS_INITIALIZE_ON_START=0 to skip startup initialization, for example when you need the stdio server to start without touching the network.
Docker
Build and export a standalone Docker image archive:
npm run docker:buildDefaults:
image: micros-mcp:latest
export: dist/micros-mcp_latest.tar.gzThe exported archive is a standard docker save artifact. Load it with docker load on another machine, then run the micros-mcp:<tag> image.
Customize:
npm run docker:build -- --image micros-mcp:dev
npm run docker:build -- --output dist/micros-mcp.tar
npm run docker:build -- --image micros-mcp:dev --output dist/micros-mcp_dev.tar.gzInstall an exported image on another machine:
docker load -i dist/micros-mcp_latest.tar.gzPublish a public Docker Hub image:
docker login
docker build -t bxnxm/micros-mcp:latest .
docker push bxnxm/micros-mcp:latestFor a public multi-architecture image that supports common 64-bit Intel/AMD and ARM hosts, build and push with buildx:
docker login
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t bxnxm/micros-mcp:latest \
--push \
.The archive workflow above exports the image for the local Docker builder's platform. The buildx publish flow creates a registry manifest so compatible hosts pull the right architecture automatically.
Run as stdio MCP in Docker:
docker run --rm -i -e MICROS_NETWORK_PREFIX=10.0.1 -v micros-mcp-data:/app/data micros-mcp:latest mcpRun the tester UI endpoint in Docker:
docker run --rm -p 3333:3333 -e MICROS_NETWORK_PREFIX=10.0.1 -e MICROS_UI_CERT_HOSTS=gateway.local,10.0.1.42 -v micros-mcp-data:/app/data micros-mcp:latest uiSet MICROS_NETWORK_PREFIX to your LAN /24 prefix, such as 10.0.1. In native host mode this prefix is auto-detected; in Docker mode it must be injected because the container usually sees Docker's network interface instead of your LAN interface.
Persist the device cache:
docker volume create micros-mcp-data
docker run --rm -i -e MICROS_NETWORK_PREFIX=10.0.1 -v micros-mcp-data:/app/data micros-mcp:latest mcp
OR
docker run --rm -p 3333:3333 -e MICROS_NETWORK_PREFIX=10.0.1 -e MICROS_UI_CERT_HOSTS=gateway.local,10.0.1.42 -v micros-mcp-data:/app/data micros-mcp:latest uiImage contents:
Included: compiled MCP server in
dist/mcp, compiled optional UI server indist/ui, UI static assets inui/assets, the static function manual,scripts/start.mjs,package.json, and productionnode_modules.Generated at runtime:
/app/data. The image creates this as an empty directory.Excluded from the Docker build context: runtime files under
data/,dist/,node_modules/, Git metadata, and local archive files. Onlydata/sfuncman.jsonis admitted as static reference data; device caches, device notes, certificates, and optional UI chat config files are not copied from the local checkout into the image.
Docker network notes:
Native mode: the server auto-detects the active local IPv4 prefix and logs it as
native/auto-detected.Docker mode: pass
MICROS_NETWORK_PREFIXand the server logs it ascontainerized/injected.If Docker still cannot find devices, confirm the container can route TCP traffic to micrOS devices on port
9008. Docker Desktop, host firewalls, VPNs, or Wi-Fi client isolation can block this even when the prefix is correct.Native and Docker UI modes bind to
0.0.0.0:3333by default so LAN access and published container ports work consistently. The tester has no authentication; expose it only on trusted networks, or override native mode withHOST=127.0.0.1. OmittingMICROS_UI_CERT_HOSTSdoes not enable HTTP.A container normally sees its own addresses rather than the Docker host's LAN address. Add every host LAN IP or DNS name used by clients to
MICROS_UI_CERT_HOSTS, such asgateway.local,10.0.1.42.Ensure names such as
gateway.localresolve to the Docker host, then openhttps://gateway.local:3333. Without a matching certificate entry, browsers report a hostname mismatch.Mount
/app/dataas a persistent volume to reuse the generated certificate across container restarts and avoid unnecessary certificate warnings.
Docker MCP client config:
{
"mcpServers": {
"microsmcp": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-e",
"MICROS_NETWORK_PREFIX=10.0.1",
"-v",
"micros-mcp-data:/app/data",
"micros-mcp:latest",
"mcp"
]
}
}
}Architecture
MCP means Model Context Protocol. It lets a client application start this server, list available tools, and call them with structured JSON arguments.
For this project, MCP is the adapter layer between a client and micrOS devices:
MCP client -> stdio -> micrOSMCP -> TCP socket -> micrOS deviceThe implementation mirrors the useful behavior of micrOS socketClient.py and micrOSClient.py, but it is standalone TypeScript and does not call Python.
Project structure:
mcp/: standalone MCP stdio server. This owns tool registration, tool definitions, micrOS socket/discovery helpers, and the public tool barrel.ui/: tester mini app. This owns the local HTTPS bridge, optional AI chat bridge, and static browser assets underui/assets/.data/: local runtime state plus the tracked staticsfuncman.jsonfunction reference. Connection, feature, note, certificate, and optional UI chat config files live here by default and are ignored by git.scripts/: operational entrypoints for start modes, Docker image export, and minimal tests.Dockerfile: minimal runtime image for stdio MCP or the tester UI endpoint.
Runtime flow:
MCP client calls a tool, for example
run_command.mcp/index.tsstarts the MCP server and registers tools throughmcp/mcp-tools.ts.mcp/mcp-tools.tsasksmcp/tool-loader.tsto discover tool modules frommcp/tools/.The matching file under
mcp/tools/owns the Zod schema, reads the cache, selects a device, opens a TCP socket if needed, and performs the micrOS operation.The result is serialized as formatted JSON text and returned to the MCP client.
Requirements
Node.js 20 or newer.
Network access from the host or container to micrOS devices on TCP port
9008.Docker, only if you want to build or run the container image.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/BxNxM/micrOSMCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server