Setting up an MCP server for your AI agent today usually looks like this:
npx -y @some-org/mcp-server # or
uvx some-mcp-server # or the occasional
curl https://example.com/install.sh | bash
The server runs as you. It can read your home directory. It sees your SSH keys, your .env files, your shell history, your browser cookies, your GPG keyring. If the server has a bug — or a malicious dependency sneaks in — the code that reads those files also runs as you. If your kernel or any installed binary has an unpatched local privilege escalation, the agent-invoked tool just inherited that escalation path too.
That isn't a failure mode of any particular MCP server; it's the default deployment model. Ambient-permission native processes, shipped by anyone, invoked on demand by an LLM that's notoriously easy to talk into misusing them."Your agent has your credentials and runs strangers' code on request" is the baseline security posture of every MCP setup built on npx / uvx / curl | bash today. It's a full-blown security nightmare that the industry has collectively decided not to look at.
ACT — Agent Component Tools — is the model that looks at it.
Every ACT tool is a WebAssembly component running inside (next post).
The VM gives us isolation. WASI gives us capability imports. ACT gives us the declaration-plus-ceiling model that makes those capabilities safe to hand to third-party code.
One component, any transport
Because tools are components, not native processes, the host can serve them over whatever wire format the caller wants.
# Claude Desktop / Cursor / Cline → stdio JSON-RPC
act run ghcr.io/actpkg/sqlite:latest --mcp \
--fs-policy allowlist --fs-allow /tmp/demo.sqlite
# Web backend → REST-ish HTTP with SSE streaming
act run ghcr.io/actpkg/sqlite:latest --http --listen "[::1]:3000"
# Script / CI → one-shot direct call
act call ghcr.io/actpkg/sqlite:latest query \
--args '{"sql": "SELECT sqlite_version()"}' \
--metadata '{"database_path":"/tmp/demo.sqlite"}'
# Browser tab → jco transpile, no server at all
jco transpile ghcr.io/actpkg/sqlite:latest -o dist/
Same component, same tool, four deployments. Whatever new transport shows up next, the component doesn't change.
Writing one
Rust, the whole SDK surface for a trivial tool:
use act_sdk::prelude::*;
#[act_component]
mod component {
use super::*;
#[act_tool(description = "Reverse a string", read_only)]
fn reverse(text: String) -> ActResult<String> {
Ok(text.chars().rev().collect())
}
}
cargo build --target wasm32-wasip2 --release + act-build pack and you have a .wasm that speaks MCP, HTTP, and the CLI. #[act_tool] derives the JSON Schema from the function signature; #[act_component] emits the WIT export. Python has the same shape with @component / @tool decorators on top of — a small WIT package for cross-cutting types and an opt-in interface package for tool dispatch, with stateful capabilities (sessions, events, resources) layered on as separate opt-in packages. The host ships as act on npm, cargo, and PyPI. A growing set of components is published on .
More on the architecture:
— how stateful components (databases, OpenAPI clients, MCP-server proxies) own their per-session state and where authentication actually belongs.
If you write MCP servers, build agent tooling, or work on the component model, we'd love your thoughts. Start at , or open a thread in Discussions.
SOCIAL SHARE CARD GENERATOR