@runhuman/mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +12 -0
- package/README.md +206 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/.env.example
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# RunHuman API Configuration
|
|
2
|
+
|
|
3
|
+
# API Base URL
|
|
4
|
+
# For local development, use http://localhost:3000
|
|
5
|
+
# For production, use your deployed API URL
|
|
6
|
+
RUNHUMAN_API_URL=http://localhost:3000
|
|
7
|
+
|
|
8
|
+
# API Key
|
|
9
|
+
# Get this from the API dashboard at http://localhost:3000/app.html
|
|
10
|
+
# Or use the default test key below for local development
|
|
11
|
+
# Format: qa_live_xxxxxxxxxxxxxxxxxxxxx
|
|
12
|
+
RUNHUMAN_API_KEY=qa_live_test_key_for_demo_purposes_only_12345
|
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# RunHuman MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server that allows AI agents to interact with the RunHuman QA testing service.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This MCP server provides tools for creating and managing human QA jobs through the RunHuman API. AI agents can use this server to:
|
|
8
|
+
|
|
9
|
+
- Create new QA jobs with custom schemas
|
|
10
|
+
- Check the status of running jobs
|
|
11
|
+
- Retrieve completed job results
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### For Claude Desktop (Recommended)
|
|
16
|
+
|
|
17
|
+
1. Get your API key at: https://qa-experiment.fly.dev/app.html
|
|
18
|
+
|
|
19
|
+
2. Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac):
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"runhuman": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "@runhuman/mcp-server", "--api-key=qa_live_xxxxxxxxxxxxx"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
3. Restart Claude Desktop
|
|
33
|
+
|
|
34
|
+
That's it! The server will be automatically downloaded and run by Claude.
|
|
35
|
+
|
|
36
|
+
### For Development
|
|
37
|
+
|
|
38
|
+
From the monorepo root:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install
|
|
42
|
+
npm run build --workspace=@runhuman/mcp-server
|
|
43
|
+
|
|
44
|
+
# Run with API key
|
|
45
|
+
node packages/mcp-server/dist/index.js --api-key=qa_live_xxxxx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Available Tools
|
|
49
|
+
|
|
50
|
+
#### `create_job`
|
|
51
|
+
Create a new QA job with human testers.
|
|
52
|
+
|
|
53
|
+
**Parameters:**
|
|
54
|
+
- `url` (string): The URL to test
|
|
55
|
+
- `description` (string): Instructions for the human tester describing what to test
|
|
56
|
+
- `schema` (object): Expected result schema that the tester response will be extracted into
|
|
57
|
+
|
|
58
|
+
#### `get_job_status`
|
|
59
|
+
Get the current status of a QA job.
|
|
60
|
+
|
|
61
|
+
**Parameters:**
|
|
62
|
+
- `jobId` (string): The ID of the job to check
|
|
63
|
+
|
|
64
|
+
#### `get_job_result`
|
|
65
|
+
Get the results of a completed QA job.
|
|
66
|
+
|
|
67
|
+
**Parameters:**
|
|
68
|
+
- `jobId` (string): The ID of the completed job
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
The MCP server needs to be configured with your RunHuman API credentials.
|
|
73
|
+
|
|
74
|
+
### 1. Get an API Key
|
|
75
|
+
|
|
76
|
+
**Option A: Via Dashboard**
|
|
77
|
+
1. Start the API server: `npm run dev --workspace=@runhuman/api`
|
|
78
|
+
2. Open http://localhost:3000/app.html
|
|
79
|
+
3. Go to "API Keys" tab
|
|
80
|
+
4. Click "Create API Key"
|
|
81
|
+
5. Copy the key (starts with `qa_live_`)
|
|
82
|
+
|
|
83
|
+
**Option B: Use Default Test Key**
|
|
84
|
+
- For local development, you can use: `qa_live_test_key_123`
|
|
85
|
+
- This key exists in `packages/api/data/api-keys.json`
|
|
86
|
+
|
|
87
|
+
### 2. Configure Environment Variables
|
|
88
|
+
|
|
89
|
+
Create a `.env` file in the MCP server directory:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# For local development
|
|
93
|
+
RUNHUMAN_API_URL=http://localhost:3000
|
|
94
|
+
RUNHUMAN_API_KEY=qa_live_test_key_123
|
|
95
|
+
|
|
96
|
+
# For production
|
|
97
|
+
RUNHUMAN_API_URL=https://api.runhuman.com
|
|
98
|
+
RUNHUMAN_API_KEY=qa_live_xxxxxxxxxxxxxxxxxxxxx
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Important:** Never commit `.env` files to git! They're already in `.gitignore`.
|
|
102
|
+
|
|
103
|
+
### 3. Verify Configuration
|
|
104
|
+
|
|
105
|
+
Test your API key works:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
curl http://localhost:3000/api/jobs \
|
|
109
|
+
-H "Authorization: Bearer qa_live_test_key_123" \
|
|
110
|
+
-H "Content-Type: application/json" \
|
|
111
|
+
-d '{"url":"https://example.com","description":"test","outputSchema":{}}'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Should return a job ID if authentication works.
|
|
115
|
+
|
|
116
|
+
For more details, see [docs/API-AUTHENTICATION.md](docs/API-AUTHENTICATION.md)
|
|
117
|
+
|
|
118
|
+
## Testing
|
|
119
|
+
|
|
120
|
+
The MCP server includes automated tests to verify it's working correctly:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Build first
|
|
124
|
+
npm run build --workspace=@runhuman/mcp-server
|
|
125
|
+
|
|
126
|
+
# Run simple automated test
|
|
127
|
+
npm run test --workspace=@runhuman/mcp-server
|
|
128
|
+
|
|
129
|
+
# Or use the MCP Inspector (interactive testing)
|
|
130
|
+
npm run test:inspector --workspace=@runhuman/mcp-server
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The test script will:
|
|
134
|
+
1. ✅ Initialize a connection to the MCP server
|
|
135
|
+
2. ✅ List all available tools (create_job, get_job_status, get_job_result)
|
|
136
|
+
3. ✅ Test calling the create_job tool
|
|
137
|
+
|
|
138
|
+
### Expected Test Output
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
✅ Server initialized successfully
|
|
142
|
+
✅ Tools listed: create_job, get_job_status, get_job_result
|
|
143
|
+
✅ create_job tool called successfully
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Watch mode (auto-rebuild on changes)
|
|
150
|
+
npm run dev --workspace=@runhuman/mcp-server
|
|
151
|
+
|
|
152
|
+
# Build
|
|
153
|
+
npm run build --workspace=@runhuman/mcp-server
|
|
154
|
+
|
|
155
|
+
# Test after building
|
|
156
|
+
npm run test --workspace=@runhuman/mcp-server
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Integration with Claude Desktop
|
|
160
|
+
|
|
161
|
+
To use this MCP server with Claude Desktop, add it to your configuration:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"mcpServers": {
|
|
166
|
+
"runhuman": {
|
|
167
|
+
"command": "node",
|
|
168
|
+
"args": ["/path/to/qa-experiment/packages/mcp-server/dist/index.js"]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Example Usage
|
|
175
|
+
|
|
176
|
+
Once connected to an AI agent (like Claude), the agent can use these tools naturally:
|
|
177
|
+
|
|
178
|
+
**User:** "Can someone test my checkout page at https://myapp.com/checkout?"
|
|
179
|
+
|
|
180
|
+
**Agent uses create_job:**
|
|
181
|
+
```
|
|
182
|
+
✅ Job created successfully!
|
|
183
|
+
Job ID: job_abc123
|
|
184
|
+
Status: pending
|
|
185
|
+
...
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Agent polls get_job_status until complete, then calls get_job_result:**
|
|
189
|
+
```
|
|
190
|
+
✅ Test completed!
|
|
191
|
+
Results Summary:
|
|
192
|
+
- Checkout Flow: ✅ Working
|
|
193
|
+
- Payment Processing: ✅ Successful
|
|
194
|
+
...
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Developer Documentation
|
|
198
|
+
|
|
199
|
+
For developers working on this MCP server:
|
|
200
|
+
- [docs/HOW-AGENTS-USE-MCP.md](docs/HOW-AGENTS-USE-MCP.md) - How AI agents discover and use MCP servers
|
|
201
|
+
- [docs/TOOL-RESPONSE-BEST-PRACTICES.md](docs/TOOL-RESPONSE-BEST-PRACTICES.md) - Best practices for tool responses
|
|
202
|
+
|
|
203
|
+
## Learn More
|
|
204
|
+
|
|
205
|
+
- [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
|
|
206
|
+
- [RunHuman API Documentation](../api/README.md)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* RunHuman MCP Server
|
|
4
|
+
*
|
|
5
|
+
* This MCP server provides tools for AI agents to interact with the RunHuman QA testing service.
|
|
6
|
+
* It allows agents to:
|
|
7
|
+
* - Create QA jobs
|
|
8
|
+
* - Check job status
|
|
9
|
+
* - Retrieve job results
|
|
10
|
+
*
|
|
11
|
+
* @see https://modelcontextprotocol.io/
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* RunHuman MCP Server
|
|
4
|
+
*
|
|
5
|
+
* This MCP server provides tools for AI agents to interact with the RunHuman QA testing service.
|
|
6
|
+
* It allows agents to:
|
|
7
|
+
* - Create QA jobs
|
|
8
|
+
* - Check job status
|
|
9
|
+
* - Retrieve job results
|
|
10
|
+
*
|
|
11
|
+
* @see https://modelcontextprotocol.io/
|
|
12
|
+
*/
|
|
13
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
14
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
15
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
16
|
+
import * as dotenv from 'dotenv';
|
|
17
|
+
// Load environment variables (optional - for standalone testing)
|
|
18
|
+
dotenv.config();
|
|
19
|
+
// Configuration
|
|
20
|
+
// Priority: CLI args > env vars > defaults
|
|
21
|
+
// Usage: node dist/index.js --api-key=qa_live_xxx [--api-url=https://...]
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
const apiKeyArg = args.find(arg => arg.startsWith('--api-key='))?.split('=')[1];
|
|
24
|
+
const apiUrlArg = args.find(arg => arg.startsWith('--api-url='))?.split('=')[1];
|
|
25
|
+
const API_URL = apiUrlArg || process.env.RUNHUMAN_API_URL || 'https://qa-experiment.fly.dev';
|
|
26
|
+
const API_KEY = apiKeyArg || process.env.RUNHUMAN_API_KEY;
|
|
27
|
+
if (!API_KEY) {
|
|
28
|
+
console.error('❌ Error: API key is required');
|
|
29
|
+
console.error('');
|
|
30
|
+
console.error('For Claude Desktop, add to your config:');
|
|
31
|
+
console.error('{');
|
|
32
|
+
console.error(' "mcpServers": {');
|
|
33
|
+
console.error(' "runhuman": {');
|
|
34
|
+
console.error(' "command": "npx",');
|
|
35
|
+
console.error(' "args": ["-y", "@runhuman/mcp-server", "--api-key=qa_live_xxxxx"]');
|
|
36
|
+
console.error(' }');
|
|
37
|
+
console.error(' }');
|
|
38
|
+
console.error('}');
|
|
39
|
+
console.error('');
|
|
40
|
+
console.error('Get your API key at: https://qa-experiment.fly.dev/app.html');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
console.error(`🔗 Connected to RunHuman API at: ${API_URL}`);
|
|
44
|
+
console.error(`🔑 Using API key: ${API_KEY.substring(0, 12)}...`);
|
|
45
|
+
/**
|
|
46
|
+
* Create and configure the MCP server
|
|
47
|
+
*/
|
|
48
|
+
const server = new Server({
|
|
49
|
+
name: 'runhuman-mcp-server',
|
|
50
|
+
version: '1.0.0',
|
|
51
|
+
}, {
|
|
52
|
+
capabilities: {
|
|
53
|
+
tools: {},
|
|
54
|
+
prompts: {},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* List available prompts (documentation for the agent)
|
|
59
|
+
*/
|
|
60
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
61
|
+
return {
|
|
62
|
+
prompts: [
|
|
63
|
+
{
|
|
64
|
+
name: 'explain_runhuman',
|
|
65
|
+
description: 'Get an explanation of how to use RunHuman for QA testing',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* List available tools
|
|
72
|
+
*/
|
|
73
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
74
|
+
return {
|
|
75
|
+
tools: [
|
|
76
|
+
{
|
|
77
|
+
name: 'create_job',
|
|
78
|
+
description: `Create a new QA job with real human testers. This sends your test to a human who will manually test your application and describe what they find in natural language. The response is then extracted into structured data matching your schema using GPT-4o.
|
|
79
|
+
|
|
80
|
+
Use this when you need human verification of:
|
|
81
|
+
- UI/UX functionality that's hard to automate
|
|
82
|
+
- Visual issues, accessibility problems
|
|
83
|
+
- Complex user flows (login, checkout, forms)
|
|
84
|
+
- Cross-browser compatibility
|
|
85
|
+
- Real user experience feedback
|
|
86
|
+
|
|
87
|
+
Returns a jobId that you can use with get_job_status and get_job_result.
|
|
88
|
+
|
|
89
|
+
Example workflow:
|
|
90
|
+
1. create_job → get jobId
|
|
91
|
+
2. get_job_status → poll until status is "completed"
|
|
92
|
+
3. get_job_result → retrieve structured results`,
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
url: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: 'The URL to test (must be publicly accessible). Example: "https://myapp.com/checkout"',
|
|
99
|
+
},
|
|
100
|
+
description: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: 'Clear instructions for the human tester. Be specific about what to test and how. Example: "Test the checkout flow: Add a product to cart, proceed to checkout, fill in shipping info, and verify the order summary shows correct totals before submitting."',
|
|
103
|
+
},
|
|
104
|
+
schema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
description: 'JSON Schema defining the structure you want extracted from the tester\'s response. Example: { "type": "object", "properties": { "checkoutWorks": { "type": "boolean" }, "totalIsCorrect": { "type": "boolean" }, "issues": { "type": "array", "items": { "type": "string" } } } }',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ['url', 'description', 'schema'],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'get_job_status',
|
|
114
|
+
description: `Check the current status of a QA job. Jobs progress through states: pending → claimed → in_progress → completed (or failed/timeout).
|
|
115
|
+
|
|
116
|
+
Use this to poll for completion before fetching results. Typical job completion time is 2-10 minutes depending on test complexity.
|
|
117
|
+
|
|
118
|
+
Returns: { status: "pending" | "claimed" | "in_progress" | "completed" | "failed" | "timeout", message: "..." }`,
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: 'object',
|
|
121
|
+
properties: {
|
|
122
|
+
jobId: {
|
|
123
|
+
type: 'string',
|
|
124
|
+
description: 'The job ID returned from create_job. Example: "550e8400-e29b-41d4-a716-446655440000"',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
required: ['jobId'],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'get_job_result',
|
|
132
|
+
description: `Get the structured results of a completed QA job. Only call this after get_job_status shows status="completed".
|
|
133
|
+
|
|
134
|
+
Returns the tester's response extracted into your specified schema, plus metadata about timing and the raw tester response.
|
|
135
|
+
|
|
136
|
+
If the job isn't complete yet, returns an error. If extraction failed, includes the raw response so you can see what the tester said.`,
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
jobId: {
|
|
141
|
+
type: 'string',
|
|
142
|
+
description: 'The job ID of a completed job. Example: "550e8400-e29b-41d4-a716-446655440000"',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
required: ['jobId'],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* Handle tool calls
|
|
153
|
+
*/
|
|
154
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
155
|
+
const { name, arguments: args } = request.params;
|
|
156
|
+
if (!args) {
|
|
157
|
+
throw new Error('Missing arguments');
|
|
158
|
+
}
|
|
159
|
+
switch (name) {
|
|
160
|
+
case 'create_job':
|
|
161
|
+
try {
|
|
162
|
+
// Call RunHuman API to create job
|
|
163
|
+
const response = await fetch(`${API_URL}/api/jobs`, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
167
|
+
'Content-Type': 'application/json'
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({
|
|
170
|
+
url: args.url,
|
|
171
|
+
description: args.description,
|
|
172
|
+
outputSchema: args.schema
|
|
173
|
+
})
|
|
174
|
+
});
|
|
175
|
+
if (!response.ok) {
|
|
176
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
177
|
+
return {
|
|
178
|
+
content: [{
|
|
179
|
+
type: 'text',
|
|
180
|
+
text: `❌ Failed to create job
|
|
181
|
+
|
|
182
|
+
Error: ${error.error || error.message || response.statusText}
|
|
183
|
+
Status: ${response.status}
|
|
184
|
+
|
|
185
|
+
Please check:
|
|
186
|
+
- Your RUNHUMAN_API_KEY is valid
|
|
187
|
+
- The API server is running at ${API_URL}
|
|
188
|
+
- Your API key has permission to create jobs`
|
|
189
|
+
}],
|
|
190
|
+
isError: true
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const data = await response.json();
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
type: 'text',
|
|
198
|
+
text: `✅ Job created successfully!
|
|
199
|
+
|
|
200
|
+
Job ID: ${data.jobId}
|
|
201
|
+
Status: ${data.status}
|
|
202
|
+
URL: ${args.url}
|
|
203
|
+
|
|
204
|
+
Your test has been queued and will be picked up by a human tester shortly.
|
|
205
|
+
|
|
206
|
+
Next steps:
|
|
207
|
+
- Use get_job_status(jobId: "${data.jobId}") to check progress
|
|
208
|
+
- Typical completion time: 2-10 minutes`,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
return {
|
|
215
|
+
content: [{
|
|
216
|
+
type: 'text',
|
|
217
|
+
text: `❌ Error creating job
|
|
218
|
+
|
|
219
|
+
${error instanceof Error ? error.message : 'Unknown error'}
|
|
220
|
+
|
|
221
|
+
Please check:
|
|
222
|
+
- The API server is running at ${API_URL}
|
|
223
|
+
- Your network connection
|
|
224
|
+
- Your .env file configuration`
|
|
225
|
+
}],
|
|
226
|
+
isError: true
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
case 'get_job_status':
|
|
230
|
+
try {
|
|
231
|
+
const response = await fetch(`${API_URL}/api/jobs/${args.jobId}`, {
|
|
232
|
+
headers: {
|
|
233
|
+
'Authorization': `Bearer ${API_KEY}`
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
if (response.status === 404) {
|
|
238
|
+
return {
|
|
239
|
+
content: [{
|
|
240
|
+
type: 'text',
|
|
241
|
+
text: `❌ Job not found
|
|
242
|
+
|
|
243
|
+
Job ID: ${args.jobId}
|
|
244
|
+
|
|
245
|
+
The job does not exist or you don't have permission to access it.`
|
|
246
|
+
}],
|
|
247
|
+
isError: true
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
content: [{
|
|
252
|
+
type: 'text',
|
|
253
|
+
text: `❌ Failed to get job status
|
|
254
|
+
|
|
255
|
+
Status: ${response.status}
|
|
256
|
+
Error: ${response.statusText}`
|
|
257
|
+
}],
|
|
258
|
+
isError: true
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const job = await response.json();
|
|
262
|
+
// Format timeline
|
|
263
|
+
const createdAt = new Date(job.createdAt);
|
|
264
|
+
const now = new Date();
|
|
265
|
+
const elapsed = Math.floor((now.getTime() - createdAt.getTime()) / 1000 / 60);
|
|
266
|
+
const statusEmoji = {
|
|
267
|
+
pending: '⏳',
|
|
268
|
+
claimed: '👤',
|
|
269
|
+
in_progress: '🔄',
|
|
270
|
+
completed: '✅',
|
|
271
|
+
failed: '❌',
|
|
272
|
+
timeout: '⏰'
|
|
273
|
+
};
|
|
274
|
+
const emoji = statusEmoji[job.status] || '📊';
|
|
275
|
+
let message = `${emoji} Job Status: ${job.status}
|
|
276
|
+
|
|
277
|
+
Job ID: ${job.id}
|
|
278
|
+
URL: ${job.url}
|
|
279
|
+
|
|
280
|
+
Timeline:
|
|
281
|
+
- Created: ${elapsed} minute${elapsed !== 1 ? 's' : ''} ago`;
|
|
282
|
+
if (job.claimedAt) {
|
|
283
|
+
const claimedElapsed = Math.floor((now.getTime() - new Date(job.claimedAt).getTime()) / 1000 / 60);
|
|
284
|
+
message += `\n- Claimed: ${claimedElapsed} minute${claimedElapsed !== 1 ? 's' : ''} ago`;
|
|
285
|
+
}
|
|
286
|
+
if (job.status === 'pending') {
|
|
287
|
+
message += '\n\nWaiting for a tester to claim this job...';
|
|
288
|
+
}
|
|
289
|
+
else if (job.status === 'claimed' || job.status === 'in_progress') {
|
|
290
|
+
message += '\n\nThe tester is working on your test. Estimated completion: 2-10 minutes';
|
|
291
|
+
}
|
|
292
|
+
else if (job.status === 'completed') {
|
|
293
|
+
message += '\n\n✅ Job is complete! Use get_job_result to retrieve the results.';
|
|
294
|
+
}
|
|
295
|
+
else if (job.status === 'failed') {
|
|
296
|
+
message += '\n\n❌ Job failed. Check the error with get_job_result.';
|
|
297
|
+
}
|
|
298
|
+
else if (job.status === 'timeout') {
|
|
299
|
+
message += '\n\n⏰ Job timed out. The tester did not complete in time.';
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
content: [{
|
|
303
|
+
type: 'text',
|
|
304
|
+
text: message
|
|
305
|
+
}]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
return {
|
|
310
|
+
content: [{
|
|
311
|
+
type: 'text',
|
|
312
|
+
text: `❌ Error checking job status
|
|
313
|
+
|
|
314
|
+
${error instanceof Error ? error.message : 'Unknown error'}`
|
|
315
|
+
}],
|
|
316
|
+
isError: true
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
case 'get_job_result':
|
|
320
|
+
try {
|
|
321
|
+
const response = await fetch(`${API_URL}/api/jobs/${args.jobId}`, {
|
|
322
|
+
headers: {
|
|
323
|
+
'Authorization': `Bearer ${API_KEY}`
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
if (response.status === 404) {
|
|
328
|
+
return {
|
|
329
|
+
content: [{
|
|
330
|
+
type: 'text',
|
|
331
|
+
text: `❌ Job not found
|
|
332
|
+
|
|
333
|
+
Job ID: ${args.jobId}
|
|
334
|
+
|
|
335
|
+
The job does not exist or you don't have permission to access it.`
|
|
336
|
+
}],
|
|
337
|
+
isError: true
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
content: [{
|
|
342
|
+
type: 'text',
|
|
343
|
+
text: `❌ Failed to get job result
|
|
344
|
+
|
|
345
|
+
Status: ${response.status}
|
|
346
|
+
Error: ${response.statusText}`
|
|
347
|
+
}],
|
|
348
|
+
isError: true
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
const job = await response.json();
|
|
352
|
+
if (job.status !== 'completed') {
|
|
353
|
+
return {
|
|
354
|
+
content: [{
|
|
355
|
+
type: 'text',
|
|
356
|
+
text: `⏳ Job not yet completed
|
|
357
|
+
|
|
358
|
+
Job ID: ${job.id}
|
|
359
|
+
Current status: ${job.status}
|
|
360
|
+
|
|
361
|
+
${job.status === 'pending' ? 'Waiting for a tester to claim this job...' :
|
|
362
|
+
job.status === 'claimed' || job.status === 'in_progress' ? 'The tester is working on your test...' :
|
|
363
|
+
job.status === 'failed' ? '❌ Job failed. Error: ' + (job.error || 'Unknown error') :
|
|
364
|
+
job.status === 'timeout' ? '⏰ Job timed out.' :
|
|
365
|
+
'Use get_job_status to check current status.'}`
|
|
366
|
+
}]
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
// Job is completed, format results
|
|
370
|
+
let message = `✅ Test completed!
|
|
371
|
+
|
|
372
|
+
Job ID: ${job.id}
|
|
373
|
+
URL: ${job.url}
|
|
374
|
+
|
|
375
|
+
**Tester Response:**
|
|
376
|
+
"${job.testerResponse || 'No response recorded'}"
|
|
377
|
+
|
|
378
|
+
**Extracted Data:**`;
|
|
379
|
+
const contents = [
|
|
380
|
+
{
|
|
381
|
+
type: 'text',
|
|
382
|
+
text: message
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
type: 'text',
|
|
386
|
+
text: JSON.stringify(job.result || {}, null, 2)
|
|
387
|
+
}
|
|
388
|
+
];
|
|
389
|
+
if (job.error) {
|
|
390
|
+
contents.push({
|
|
391
|
+
type: 'text',
|
|
392
|
+
text: `\n⚠️ Extraction Warnings:\n${job.error}`
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return { content: contents };
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
return {
|
|
399
|
+
content: [{
|
|
400
|
+
type: 'text',
|
|
401
|
+
text: `❌ Error getting job result
|
|
402
|
+
|
|
403
|
+
${error instanceof Error ? error.message : 'Unknown error'}`
|
|
404
|
+
}],
|
|
405
|
+
isError: true
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
default:
|
|
409
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
/**
|
|
413
|
+
* Start the server
|
|
414
|
+
*/
|
|
415
|
+
async function main() {
|
|
416
|
+
const transport = new StdioServerTransport();
|
|
417
|
+
await server.connect(transport);
|
|
418
|
+
console.error('RunHuman MCP server running on stdio');
|
|
419
|
+
}
|
|
420
|
+
main().catch((error) => {
|
|
421
|
+
console.error('Server error:', error);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
});
|
|
424
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@runhuman/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for RunHuman - Human-powered QA testing for AI agents",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"runhuman-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
".env.example"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsx watch src/index.ts",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"test": "node test-simple.cjs",
|
|
21
|
+
"test:all": "node test-all-tools.cjs",
|
|
22
|
+
"test:inspector": "npx @modelcontextprotocol/inspector node dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mcp",
|
|
26
|
+
"model-context-protocol",
|
|
27
|
+
"qa",
|
|
28
|
+
"testing",
|
|
29
|
+
"human-in-the-loop",
|
|
30
|
+
"ai-agent",
|
|
31
|
+
"claude",
|
|
32
|
+
"anthropic",
|
|
33
|
+
"qa-testing",
|
|
34
|
+
"manual-testing"
|
|
35
|
+
],
|
|
36
|
+
"author": "RunHuman <hey@runhuman.com>",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/yueranyuan/qa-experiment.git",
|
|
40
|
+
"directory": "packages/mcp-server"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://runhuman.com",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/yueranyuan/qa-experiment/issues"
|
|
45
|
+
},
|
|
46
|
+
"license": "ISC",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "latest",
|
|
52
|
+
"dotenv": "^17.2.3"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^20.11.17",
|
|
56
|
+
"tsx": "^4.7.1",
|
|
57
|
+
"typescript": "^5.3.3"
|
|
58
|
+
}
|
|
59
|
+
}
|