Skip to content

Commit b9f200f

Browse files
authored
feat(tasks): Add MCP boilerplate with echo command to tasks/mcp (#12925)
1 parent 8c57153 commit b9f200f

File tree

10 files changed

+815
-1
lines changed

10 files changed

+815
-1
lines changed

.github/workflows/copilot-setup-steps.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,5 @@ jobs:
4343
with:
4444
key: dprint-${{ hashFiles('dprint.json') }}
4545
path: ~/.cache/dprint
46+
47+
- run: pnpm run --filter mcp-oxc build

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[workspace]
22
resolver = "3"
33
members = ["apps/*", "crates/*", "napi/*", "tasks/*"]
4-
exclude = ["tasks/lint_rules", "tasks/e2e"]
4+
exclude = ["tasks/lint_rules", "tasks/e2e", "tasks/mcp"]
55

66
[workspace.package]
77
authors = ["Boshen <boshenc@gmail.com>", "Oxc contributors"]

pnpm-lock.yaml

Lines changed: 481 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ packages:
55
- editors/*
66
- tasks/transform_conformance
77
- tasks/compat_data
8+
- tasks/mcp
89

910
catalog:
1011
'@napi-rs/cli': 3.0.0

tasks/mcp/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.DS_Store

tasks/mcp/README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# MCP Boilerplate for Oxc
2+
3+
This is a Model Context Protocol (MCP) server boilerplate for the oxc project. It provides a simple echo command as an example.
4+
5+
## Features
6+
7+
- TypeScript-based MCP server
8+
- Single `echo` command that echoes back any message
9+
- Built using @modelcontextprotocol/sdk
10+
11+
## Installation
12+
13+
From the root of the oxc repository:
14+
15+
```bash
16+
pnpm install
17+
```
18+
19+
## Building
20+
21+
```bash
22+
cd tasks/mcp
23+
pnpm build
24+
```
25+
26+
## Usage
27+
28+
Start the MCP server:
29+
30+
```bash
31+
cd tasks/mcp
32+
pnpm start
33+
```
34+
35+
Or run in development mode (builds and starts):
36+
37+
```bash
38+
cd tasks/mcp
39+
pnpm dev
40+
```
41+
42+
## Testing
43+
44+
Run the included test to verify the server works:
45+
46+
```bash
47+
cd tasks/mcp
48+
pnpm test
49+
```
50+
51+
## Available Tools
52+
53+
### echo
54+
55+
Echoes back the provided message.
56+
57+
**Parameters:**
58+
59+
- `message` (string, required): The message to echo back
60+
61+
**Example:**
62+
63+
```json
64+
{
65+
"name": "echo",
66+
"arguments": {
67+
"message": "Hello, MCP!"
68+
}
69+
}
70+
```
71+
72+
**Response:**
73+
74+
```json
75+
{
76+
"content": [
77+
{
78+
"type": "text",
79+
"text": "Hello, MCP!"
80+
}
81+
]
82+
}
83+
```
84+
85+
## Development
86+
87+
The MCP server is structured as follows:
88+
89+
- `src/index.ts` - Main server implementation
90+
- `dist/` - Compiled JavaScript output
91+
- `test/test.mjs` - Simple test script
92+
93+
To add new tools:
94+
95+
1. Add the tool definition to the `ListToolsRequestSchema` handler
96+
2. Add the tool implementation to the `CallToolRequestSchema` handler
97+
3. Update this README with documentation for the new tool
98+
99+
## MCP Integration
100+
101+
This server can be used with any MCP-compatible client. The server communicates over stdio using JSON-RPC messages.
102+
103+
For more information about MCP, see: https://modelcontextprotocol.io/

tasks/mcp/package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "mcp-oxc",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "MCP server for oxc project",
6+
"type": "module",
7+
"main": "dist/index.js",
8+
"bin": {
9+
"mcp-oxc": "dist/index.js"
10+
},
11+
"scripts": {
12+
"build": "tsc",
13+
"start": "node dist/index.js",
14+
"dev": "tsc && node dist/index.js",
15+
"test": "node test/test.mjs"
16+
},
17+
"dependencies": {
18+
"@modelcontextprotocol/sdk": "^1.17.2"
19+
},
20+
"devDependencies": {
21+
"@types/node": "^22.0.0",
22+
"typescript": "catalog:"
23+
}
24+
}

tasks/mcp/src/index.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env node
2+
3+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
6+
7+
/**
8+
* MCP server for oxc project with echo command
9+
*/
10+
11+
class McpOxcServer {
12+
private server: Server;
13+
14+
constructor() {
15+
this.server = new Server(
16+
{
17+
name: 'mcp-oxc',
18+
version: '0.0.0',
19+
},
20+
{
21+
capabilities: {
22+
tools: {},
23+
},
24+
},
25+
);
26+
27+
this.setupToolHandlers();
28+
}
29+
30+
private setupToolHandlers() {
31+
// List available tools
32+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
33+
return {
34+
tools: [
35+
{
36+
name: 'echo',
37+
description: 'Echo back the provided message',
38+
inputSchema: {
39+
type: 'object',
40+
properties: {
41+
message: {
42+
type: 'string',
43+
description: 'The message to echo back',
44+
},
45+
},
46+
required: ['message'],
47+
},
48+
},
49+
],
50+
};
51+
});
52+
53+
// Handle tool calls
54+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
55+
const { name, arguments: args } = request.params;
56+
57+
if (name === 'echo') {
58+
const message = args?.message;
59+
if (typeof message !== 'string') {
60+
throw new Error('Message must be a string');
61+
}
62+
63+
return {
64+
content: [
65+
{
66+
type: 'text',
67+
text: message,
68+
},
69+
],
70+
};
71+
}
72+
73+
throw new Error(`Unknown tool: ${name}`);
74+
});
75+
}
76+
77+
async run() {
78+
const transport = new StdioServerTransport();
79+
await this.server.connect(transport);
80+
}
81+
}
82+
83+
// Start the server
84+
const server = new McpOxcServer();
85+
server.run().catch((error) => {
86+
console.error('Server error:', error);
87+
process.exit(1);
88+
});

tasks/mcp/test/test.mjs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Simple test script to verify the MCP server works
5+
*/
6+
7+
import { spawn } from 'child_process';
8+
import path from 'path';
9+
import { fileURLToPath } from 'url';
10+
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = path.dirname(__filename);
13+
14+
function testMcpServer() {
15+
return new Promise((resolve, reject) => {
16+
const serverPath = path.join(__dirname, '../dist/index.js');
17+
const server = spawn('node', [serverPath], {
18+
stdio: ['pipe', 'pipe', 'pipe'],
19+
});
20+
21+
let output = '';
22+
let errorOutput = '';
23+
24+
server.stdout.on('data', (data) => {
25+
output += data.toString();
26+
});
27+
28+
server.stderr.on('data', (data) => {
29+
errorOutput += data.toString();
30+
});
31+
32+
// Send a JSON-RPC request to list tools
33+
const listToolsRequest = {
34+
jsonrpc: '2.0',
35+
id: 1,
36+
method: 'tools/list',
37+
params: {},
38+
};
39+
40+
const echoRequest = {
41+
jsonrpc: '2.0',
42+
id: 2,
43+
method: 'tools/call',
44+
params: {
45+
name: 'echo',
46+
arguments: {
47+
message: 'Hello, MCP!',
48+
},
49+
},
50+
};
51+
52+
// Send requests
53+
server.stdin.write(JSON.stringify(listToolsRequest) + '\n');
54+
server.stdin.write(JSON.stringify(echoRequest) + '\n');
55+
server.stdin.end();
56+
57+
server.on('close', (code) => {
58+
if (code === 0) {
59+
console.log('✓ MCP server started successfully');
60+
console.log('Server output:', output);
61+
if (errorOutput) {
62+
console.log('Server errors:', errorOutput);
63+
}
64+
resolve({ output, errorOutput });
65+
} else {
66+
console.error(`✗ MCP server exited with code ${code}`);
67+
console.error('Error output:', errorOutput);
68+
reject(new Error(`Server exited with code ${code}`));
69+
}
70+
});
71+
72+
server.on('error', (error) => {
73+
console.error('✗ Failed to start MCP server:', error);
74+
reject(error);
75+
});
76+
77+
// Timeout after 10 seconds
78+
setTimeout(() => {
79+
server.kill();
80+
reject(new Error('Test timeout'));
81+
}, 10000);
82+
});
83+
}
84+
85+
// Run the test
86+
testMcpServer()
87+
.then(() => {
88+
console.log('✓ All tests passed');
89+
process.exit(0);
90+
})
91+
.catch((error) => {
92+
console.error('✗ Test failed:', error);
93+
process.exit(1);
94+
});

tasks/mcp/tsconfig.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"module": "ESNext",
5+
"moduleResolution": "Node",
6+
"esModuleInterop": true,
7+
"allowSyntheticDefaultImports": true,
8+
"strict": true,
9+
"outDir": "./dist",
10+
"rootDir": "./src",
11+
"declaration": true,
12+
"skipLibCheck": true,
13+
"forceConsistentCasingInFileNames": true
14+
},
15+
"include": ["src/**/*"],
16+
"exclude": ["node_modules", "dist"]
17+
}

0 commit comments

Comments
 (0)