Skip to main content

MCP is Not Enough: Addressing Security Flaws with a WebAssembly-Based Standard

·11 mins

The rapid proliferation of applications built around Large Language Models (LLMs) has brought a common challenge to the forefront: how do we effectively provide these models with the necessary external context? It’s well understood that LLMs, much like humans, can “hallucinate” or produce incorrect information when operating without sufficient context. We cannot always rely solely on the knowledge embedded within a model, this knowledge may be outdated (due to knowledge cutoffs), limited to publicly available information, and unable to access private or real-time data. These limitations restrict the utility of LLMs. Consequently, all LLM providers offer methods to supplement this, such as Retrieval Augmented Generation (RAG) or tool calling, each with provider-specific implementations.

Amidst this landscape, Anthropic proposed the Model Context Protocol (MCP) as a unified standard for LLMs to access external data. As stated in their official announcement:

As AI assistants gain mainstream adoption, the industry has invested heavily in model capabilities, achieving rapid advances in reasoning and quality. Yet even the most sophisticated models are constrained by their isolation from data—trapped behind information silos and legacy systems. Every new data source requires its own custom implementation, making truly connected systems difficult to scale.

MCP addresses this challenge. It provides a universal, open standard for connecting AI systems with data sources, replacing fragmented integrations with a single protocol. The result is a simpler, more reliable way to give AI systems access to the data they need.

This is the vision behind MCP’s creation.

Learning from the Past: The Language Server Protocol (LSP) #

Anthropic commendably sought to avoid reinventing the wheel, drawing inspiration from existing solutions. They are not the first to face the challenge of creating a universal solution independent of a single framework or language. As detailed in the official MCP documentation:

MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.

Microsoft developed LSP to standardize communication between code editors and language-specific tools like compilers or linters. This allows features such as auto-completion, go-to-definition, and diagnostics to be implemented once in a language server and then used across multiple editors. LSP reduces duplication, simplifies tooling support, and promotes consistency across development environments. To achieve this, it uses JSON-RPC for structured, language-agnostic communication and decouples the language server from the transport layer (e.g., stdio, sockets). The language server itself can be implemented in any language, facilitating integration across diverse ecosystems.

A Similar Problem, But Is MCP the Right Analogy? #

On the surface, the comparison between LSP and MCP is compelling. In both scenarios, the goal is to provide flexibility, allowing solutions to be built using any framework or language. The client application merely needs to start a server application and communicate with it (via stdio or sockets).

However, this is where the crucial similarities end. LSP’s work is predominantly “static”. It primarily focuses on analyzing files within a local workspace, reading them, and providing hints to the user without extensive external interaction. Actions performed by LSP servers are generally predictable and read-only.

In stark contrast, MCP is designed to provide dynamic content to an LLM. An MCP server might call external services, modify database records, or update files on the local filesystem. All these actions could potentially be initiated by an LLM, which might inadvertently (or if compromised, maliciously) attempt destructive operations on sensitive resources.

Furthermore, LSP servers are typically installed from centralized, curated marketplaces provided by IDE developers. Such a vetting and distribution system does not yet exist for MCP. The official MCP website currently offers a list of example servers and a GitHub repository that directs users to various MCP Server implementations with instructions on how to configure them in clients like Claude.

We must remember that MCP Servers are intended for use not just by security-conscious developers, but also by average computer users who may not understand Python or how to execute code safely. They might copy and paste any code snippet they find online if they believe it will solve their problem – the “bash fork bomb” serves as a cautionary tale. LLMs are powerful tools for empowering people to accomplish tasks they don’t fully understand. A user might ask an LLM to apply certain instructions, pasting file contents, and the LLM could provide a ready-to-use (and potentially harmful) response involving an MCP server.

This exposes users to significant risks. And this is before even considering attacks targeting developers, such as malicious packages in official repositories like PyPI or the persistent lack of cryptographic signatures for many Python packages. While NPM now has an npm audit signatures command, vulnerabilities can still be introduced.

The Conventional Solution: Sandboxing (and its Limitations for MCP) #

Sandboxing external applications is always a prudent security measure, especially when running code whose safety cannot be fully verified. This practice is becoming increasingly common.

The Deno runtime, for instance, has built-in sandboxing capabilities, allowing restrictions on file access, environment variables, and network connections. However, most current MCP Servers are developed with Node.js in mind and do not leverage Deno, necessitating a porting effort.

For Python code, Docker containers are a popular sandboxing method, and some developers provide ready-to-use images.

These solutions are excellent for users fluent in multiple technologies. But they are not universally accessible. Not everyone possesses the skills to write their own Dockerfile (or generate a reliable one using an LLM) and then build a working image.

Moreover, these sandboxing methods are often primitive in the context of MCP’s needs. They typically lack granular permission management and proper integration with the client side.

  • With Deno, you can manage access on a file-by-file basis, but all permissions must be declared before starting the MCP Server. Otherwise, the server might hang if Deno prompts for access to a resource for which it lacks permission. This might lead users to configure overly generous “auto-allow” settings for convenience. MCP Clients are unlikely to integrate deeply with Deno’s permission model because an MCP Server can, by design, be built with any technology.
  • The MCP protocol currently lacks a mechanism to notify the MCP Client that the user needs to grant additional permissions. Even if such a notification system were introduced, it couldn’t be reliably enforced, as a malicious or misconfigured application (already having broad system access) could simply claim to be an older version that doesn’t support the new permission features.
  • Sandboxing with Docker on Windows and macOS introduces an additional dependency: running a Linux virtual machine. This brings more potential points of failure (e.g., ensuring filesystem shares are up-to-date, stdio is relayed correctly). It’s worth remembering that stdio was chosen as a transport layer partly for its simplicity and lack of system overhead, an advantage diminished by VM layering.

WebAssembly: A Secure and Universal Foundation #

What if we reimagined the entire concept of MCP with security and universality as core tenets from the outset? My proposition is to leverage WebAssembly (WASM). Although many still associate WASM primarily with browser enhancements, its capabilities extend far beyond that.

This potential has been partially recognized. For example, the Pydantic AI team created a Python MCP Server with sandboxed code execution by running Pyodide (a Python distribution in WebAssembly) within a Deno runner. While functional, this approach introduces significant dependencies and can be memory-intensive. Running a simple “hello world” example involves JavaScript spinning up an MCP Server, which uses a WASM module to interpret and execute Python code.

Today, numerous independent WASM runtime environments exist. Code can be executed not only in any browser but also natively in Deno, as a Docker or Kubernetes container, or embedded to extend an application’s functionality. Furthermore, many platforms support building WebAssembly modules natively from source code. Pyodide has already been mentioned for Python; C, C++, Rust, and many other languages can also be compiled directly to WASM.

This opens a new world of opportunities. WASM extensions are platform-independent, processor-architecture-independent, run at near-native speed, and crucially, are sandboxed by default. When implementing WebAssembly in an application, permission management can be fully and granularly integrated with the host. WASM modules can not only export functions but also import functionality from the host application, enabling controlled interaction.

Originally, WebAssembly modules had a limitation: functions could only accept basic numeric types (integers and floats). Transferring complex data structures required custom memory marshalling on both host and client sides. While workarounds existed, technologies like Emscripten primarily focused on browser and JavaScript interactions.

Fortunately, this limitation is largely a thing of the past. The Bytecode Alliance—a nonprofit organization spearheading WebAssembly and WebAssembly System Interface (WASI) standards—has been instrumental in making WebAssembly more powerful and developer-friendly.

WebAssembly Components and WASI #

As part of their efforts, the Bytecode Alliance introduced the WebAssembly Component Model. This standardizes how modules interact by defining a system for importing and exporting rich types like strings, lists, records, and other complex data structures. It enables developers to define clear interfaces with structured functions and methods, allowing components written in different languages to communicate seamlessly without relying on low-level memory manipulation.

Alongside the Component Model, the WebAssembly System Interface (WASI) provides a standardized way for WebAssembly applications to interact with the outside world—accessing files, network sockets, environment variables, and more. Critically, all these interactions occur within a sandboxed environment where access is explicitly granted and tightly controlled by the host application, ensuring safety and portability.

WebAssembly Components bring multiple benefits, including the ability to easily compose more complex solutions by combining multiple elements (potentially written in different languages) into a single module, all thanks to the WIT (WebAssembly Interface Type) language.

Model Context Protocol with WebAssembly Components (MCP-WASM) #

This is not just a theoretical exercise. I want to introduce my work in this area: MCP in WebAssembly Components. This project focuses on enabling the use of MCP within the sandboxed environment of WebAssembly by providing ready-to-use WIT interface files and several examples.

I have split the project into three phases:

Phase 1 #

Recreate the current MCP specification in WebAssembly as closely as possible, with the JSON-RPC interface being mapped to WIT files. This will allow for verification of what can be directly translated and what needs refinement to fully leverage WASM’s benefits, leading to version 1 of an MCP-WASM specification.

Phase 2 #

Develop easy-to-use conversion tools for MCP-WASM, facilitating bidirectional compatibility. This will comprise two components:

  • An MCP Client for MCP-WASM: This will allow MCP-WASM modules to interact with the existing ecosystem of MCP Servers.
  • An MCP Server for WASM Components: This will enable the creation of new MCP-WASM modules that can run in a sandboxed environment on any current MCP Client (with appropriate WASM hosting capabilities).

Phase 3 #

Integrate full support for MCP-WASM into the AgentAI crate. My library already supports MCP Servers in an agentic flow. Implementing MCP-WASM will enable their use with actual AI agents in a secure manner.

Beyond Local Execution: Remote and Browser Advantages #

While this article has focused primarily on executing MCP Servers in a local environment, the challenges and benefits of MCP-WASM extend further. Currently, securely exposing an MCP Server over the internet for external services, to extend web application functionality, is difficult. Solutions like OpenWebUI’s MCPO promote running local applications to introduce additional functionality. While this helps access locally stored data, it can be overkill for interfacing with data already available online and carries inherent risks if the local server is broadly exposed.

MCP-WASM, being sandboxed by default, can be uploaded and used in Web Applications in multiple, safer ways:

  • Client-Side Execution: Supplementing information by running the MCP-WASM module directly in the user’s web browser. This can potentially eliminate the need for separate authorization steps for external services, as the module operates within the user’s existing session context.
  • Secure Remote Execution: Uploading and executing the MCP-WASM module on a remote node without exposing the user’s local computer or requiring complex local server setups.

In the growing MCP ecosystem, the lack of a secure way to provide and consume MCP Servers is a serious concern. Current recommendations from market leaders like Make.com and Zapier often involve “security through obscurity” for their MCP server integrations:

You’ll receive a unique URL that can be used to configure your MCP client. This URL includes a secret key, so please treat it as sensitive information and share it only with trusted parties.
Make.com Documentation

Your MCP server URL is like a password. Do not share it, as it can be used to run your actions and access your data.
Zapier Documentation

This approach is a stopgap, not a secure, scalable way forward.

Summary #

In my opinion, the goals of the Model Context Protocol and the capabilities of WebAssembly Components are a perfect match. Both technologies share a crucial value: universality. By combining them, we can create a truly secure, portable, and powerful standard for connecting LLMs to the data and tools they need, opening up a world of new opportunities. I hope this article has inspired you to explore this promising approach.