Quick Start
1. Initialize the Project
mkdir my-trading-skill
cd my-trading-skill
npm init -y2. Install Dependencies
package.json:
{
"name": "my-trading-skill",
"version": "1.0.0",
"type": "module",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"dotenv": "^16.6.1",
"express": "^4.18.2"
}
}Install packages:
npm installCore Implementation
Base Server Structure
import express from 'express';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3015;
const TRADE_API_BASE = process.env.TRADE_API_BASE || 'https://test-dex-api.heima.network';
// Create MCP protocol handler
const mcpServer = new Server(
{
name: 'my-trading-skill',
version: '1.0.0'
},
{
capabilities: {
tools: {}
}
}
);Defining Trading Tools
The Skill exposes abilities to the AI Agent through Tools. Example: open position tool.
mcpServer.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'open_position',
description: 'Open a position on the DEX. Requires skill_token authorization.',
inputSchema: {
type: 'object',
properties: {
skill_token: { type: 'string', description: 'WildMeta authorization token' },
dex: { type: 'string', description: 'DEX identifier (e.g. hyperliquid)' },
coin: { type: 'string', description: 'Trading asset (e.g. BTC, ETH)' },
isBuy: { type: 'boolean', description: 'true=long, false=short' },
limitPx: { type: 'string', description: 'Limit order price' },
sz: { type: 'string', description: 'Position size' },
orderType: { type: 'object', description: 'Order type configuration' }
},
required: ['skill_token', 'dex', 'coin', 'isBuy', 'sz']
}
}
]
};
});Tool Handler Implementation
mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'open_position') {
return await handleOpenPosition(args);
}
throw new Error(`Unknown tool: ${name}`);
});Trading Operations Implementation
Open Position
async function handleOpenPosition(args) {
const { skill_token, dex, coin, isBuy, limitPx, sz, orderType } = args;
if (!skill_token) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: 'skill_token is required for authorization'
})
}]
};
}
console.log(`📈 Open position: ${isBuy ? 'LONG' : 'SHORT'} ${sz} ${coin}`);
try {
const requestBody = {
dex: dex || 'hyperliquid',
coin,
isBuy,
limitPx: limitPx || '0',
sz,
orderType: orderType || {
limit: { tif: 'Gtc' },
trigger: { isMarket: false, triggerPx: '0', tpsl: '' }
},
cloid: `order_${Date.now()}`
};
const response = await fetch(`${TRADE_API_BASE}/openapi/trade/v1/open_position`, {
method: 'POST',
headers: {
'X-Language': 'zh',
'skill-token': skill_token,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
const data = await response.json();
return {
content: [{
type: 'text',
text: JSON.stringify({
success: response.ok,
order: requestBody,
response: data
})
}]
};
} catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message
})
}]
};
}
}HTTP Endpoints
The Skill must expose an HTTP endpoint for MCP communication:
// MCP endpoint
app.all('/message', async (req, res) => {
try {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
res.on('close', () => transport.close());
await mcpServer.connect(transport);
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error('Request error:', error);
res.status(500).json({ error: error.message });
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
server: 'my-trading-skill',
version: '1.0.0',
timestamp: new Date().toISOString()
});
});
app.listen(PORT, () => {
console.log(`🚀 Skill service running on port ${PORT}`);
});Authorization Mechanism
Skill Token Workflow
1. User initiates a request
2. Frontend detects that a Skill requires signature authorization
3. Frontend requests wallet signature from the user
4. AI Agent automatically injects skill_token into tool parameters
5. Skill uses the token to call the WildMeta DEX APINotes
skill_tokenis generated by WildMeta frontend via wallet signatureAI Agent automatically injects the token into tool calls
Skills must include this token when calling WildMeta DEX API
Security Best Practices
1. Token Validation
if (!skill_token) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: 'skill_token is required'
})
}]
};
}2. Risk Controls
const MAX_SIZE = {
BTC: 0.01,
ETH: 0.1
};
if (parseFloat(sz) > MAX_SIZE[coin]) {
return {
success: false,
error: `Position size exceeds max limit for ${coin}: ${MAX_SIZE[coin]}`
};
}3. Logging
console.log(`📈 Open position: ${isBuy ? 'LONG' : 'SHORT'} ${sz} ${coin}`);
console.log(`🔑 Token: ${skill_token.substring(0, 20)}...`);
console.log('📤 Request body:', JSON.stringify(requestBody, null, 2));
console.log('📥 Response:', data);4. Error Handling
try {
// trading logic
} catch (error) {
console.error('❌ Trading error:', error);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message,
timestamp: new Date().toISOString()
})
}]
};
}Testing
Create a script: test.sh
#!/bin/bash
echo "=== Health Check ==="
curl -s http://localhost:3015/health | jq .
echo -e "\n=== List Tools ==="
curl -s -X POST http://localhost:3015/message \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}' | jq .
echo -e "\n=== Test Open Position (requires valid skill_token) ==="
curl -s -X POST http://localhost:3015/message \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "open_position",
"arguments": {
"skill_token": "test-token",
"dex": "hyperliquid",
"coin": "BTC",
"isBuy": true,
"sz": "0.001"
}
}
}' | jq .Full Example
For a complete example, refer to:
mcp-wildmeat-trader/server.js
It includes:
Full Skill server setup
Multiple tools (open, close, cancel order)
WildMeta DEX API integration
Error handling
Logging
Security checks
Last updated