Migrating from JavaScript MCP Servers to Wassette Components
Traditional MCP servers run as standalone Node.js processes with full system access. Wassette components run as sandboxed WebAssembly modules with explicit permissions. The key difference is that you write just the business logic—no server boilerplate, better security, and cleaner code.
Migration Example
Here’s how to convert a weather MCP server to a Wassette component:
Before: Traditional MCP Server
package.json:
{
"dependencies": {
"@modelcontextprotocol/sdk": "^0.5.0"
}
}
index.js: index.js:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server({
name: 'weather-server',
version: '1.0.0'
}, {
capabilities: { tools: {} }
});
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'get_weather',
description: 'Get current weather for a city',
inputSchema: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' }
},
required: ['city']
}
}]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { city } = request.params.arguments;
const apiKey = process.env.WEATHER_API_KEY;
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`
);
const data = await response.json();
return {
content: [{
type: 'text',
text: `Temperature: ${data.main.temp}°C`
}]
};
});
const transport = new StdioServerTransport();
await server.connect(transport);
Total: ~60 lines of boilerplate + business logic.
After: Wassette Component
package.json:
{
"type": "module",
"dependencies": {
"@bytecodealliance/componentize-js": "^0.18.1",
"@bytecodealliance/jco": "^1.11.1"
},
"scripts": {
"build": "jco componentize -w ./wit weather.js -o weather.wasm"
}
}
wit/world.wit:
package local:weather;
world weather-component {
import wasi:config/store@0.2.0-draft;
/// Get current weather for a city
export get-weather: func(city: string) -> result<string, string>;
}
Note: You’ll need the WASI config WIT definitions. Copy them from the get-weather-js example or download from the WASI repository.
weather.js:
import { get } from "wasi:config/store@0.2.0-draft";
export async function getWeather(city) {
const apiKey = await get("WEATHER_API_KEY");
if (!apiKey) {
throw "WEATHER_API_KEY not configured";
}
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`
);
const data = await response.json();
return `Temperature: ${data.main.temp}°C`;
}
Total: ~20 lines of business logic.
Key Changes
- No MCP SDK - Just export your functions directly
- Environment variables - Replace
process.envwith WASI config store (wasi:config/store) - Error handling - Throw errors or return result types instead of MCP response objects
- WIT interface - Define your API in WIT instead of MCP tool schemas
- Build - Run
npm run buildto create the.wasmcomponent
Migration Steps
- Extract your tool’s business logic (the actual work it does)
- Create a WIT file defining your function signatures
- Include
importstatements for any WASI interfaces you use (e.g.,wasi:config/store) - Copy required WIT dependencies to
wit/deps/(see examples for reference)
- Include
- Update environment variable access to use WASI config store
- Export your functions directly from your JS file
- Build with
jco componentize
Next Steps
- See the JavaScript/TypeScript guide for more details on building components
- Review example components for working code
- Check the Permissions reference for security configuration