v2.0.1: Add /qwenclaw slash commands for Qwen Code CLI
This commit is contained in:
200
src/mcp-server.js
Normal file
200
src/mcp-server.js
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* QwenClaw MCP Server
|
||||
*
|
||||
* Provides QwenClaw functionality as MCP tools for Qwen Code CLI
|
||||
*
|
||||
* Usage:
|
||||
* In Qwen Code, type: /qwenclaw <command>
|
||||
* Commands: start, status, send, skills, help
|
||||
*/
|
||||
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import { spawn } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
const QWENCLAW_DIR = join(process.cwd());
|
||||
|
||||
// Tool definitions
|
||||
const TOOLS = [
|
||||
{
|
||||
name: 'qwenclaw_start',
|
||||
description: 'Start QwenClaw daemon',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
web: {
|
||||
type: 'boolean',
|
||||
description: 'Enable web dashboard',
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'qwenclaw_status',
|
||||
description: 'Check QwenClaw daemon status',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'qwenclaw_send',
|
||||
description: 'Send message to QwenClaw daemon',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Message to send',
|
||||
},
|
||||
},
|
||||
required: ['message'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'qwenclaw_skills',
|
||||
description: 'List all available QwenClaw skills',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'qwenclaw_help',
|
||||
description: 'Show QwenClaw help information',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Create MCP server
|
||||
const server = new Server(
|
||||
{
|
||||
name: 'qwenclaw',
|
||||
version: '2.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Handle tool listing
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: TOOLS,
|
||||
};
|
||||
});
|
||||
|
||||
// Handle tool execution
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args = {} } = request.params;
|
||||
|
||||
try {
|
||||
switch (name) {
|
||||
case 'qwenclaw_start': {
|
||||
const web = args.web || false;
|
||||
const result = await runCommand('start', web ? ['--web'] : []);
|
||||
return {
|
||||
content: [{ type: 'text', text: result }],
|
||||
};
|
||||
}
|
||||
|
||||
case 'qwenclaw_status': {
|
||||
const result = await runCommand('status', []);
|
||||
return {
|
||||
content: [{ type: 'text', text: result }],
|
||||
};
|
||||
}
|
||||
|
||||
case 'qwenclaw_send': {
|
||||
const message = args.message;
|
||||
if (!message) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'Error: message is required' }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
const result = await runCommand('send', [message]);
|
||||
return {
|
||||
content: [{ type: 'text', text: result }],
|
||||
};
|
||||
}
|
||||
|
||||
case 'qwenclaw_skills': {
|
||||
const result = await runCommand('skills', []);
|
||||
return {
|
||||
content: [{ type: 'text', text: result }],
|
||||
};
|
||||
}
|
||||
|
||||
case 'qwenclaw_help': {
|
||||
const result = await runCommand('help', []);
|
||||
return {
|
||||
content: [{ type: 'text', text: result }],
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Run QwenClaw command
|
||||
async function runCommand(command, args = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = spawn('node', [join(QWENCLAW_DIR, 'bin', 'qwenclaw.js'), command, ...args], {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
let output = '';
|
||||
let error = '';
|
||||
|
||||
proc.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
proc.stderr.on('data', (data) => {
|
||||
error += data.toString();
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve(output.trim());
|
||||
} else {
|
||||
reject(new Error(error || `Command failed with code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Start server
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error('QwenClaw MCP server running on stdio');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user