@aiconnect/agentjobs-mcp 1.0.9 → 1.2.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 +7 -0
- package/README.md +67 -0
- package/build/config.js +10 -3
- package/build/config.test.js +22 -0
- package/build/debug.js +86 -0
- package/build/index.js +71 -7
- package/build/lib/agentJobsClient.js +90 -0
- package/build/test-tools.js +53 -0
- package/build/tools/cancel_job.js +35 -50
- package/build/tools/create_job.js +49 -69
- package/build/tools/get_context.js +61 -0
- package/build/tools/get_job.js +30 -58
- package/build/tools/get_job_type.js +40 -56
- package/build/tools/get_jobs_stats.js +47 -0
- package/build/tools/list_jobs.js +44 -58
- package/build/utils/debugger.js +74 -0
- package/build/utils/formatters.js +516 -44
- package/build/utils/formatters.test.js +629 -0
- package/build/utils/schemas.js +24 -0
- package/build/utils/schemas.test.js +95 -0
- package/build/utils/version.js +12 -0
- package/build/utils/version.test.js +9 -0
- package/docs/agent-jobs-api.md +41 -0
- package/docs/debug-guide.md +203 -0
- package/package.json +13 -4
package/.env.example
CHANGED
|
@@ -9,3 +9,10 @@ AICONNECT_API_KEY=your-api-key-here
|
|
|
9
9
|
|
|
10
10
|
# Optional: Default organization ID (defaults to 'aiconnect' if not set)
|
|
11
11
|
# DEFAULT_ORG_ID=your-organization-id
|
|
12
|
+
|
|
13
|
+
# Debug Settings
|
|
14
|
+
MCP_DEBUG=false
|
|
15
|
+
NODE_ENV=development
|
|
16
|
+
|
|
17
|
+
# HTTP Debug (opcional - para ver requisições HTTP detalhadas)
|
|
18
|
+
DEBUG=axios
|
package/README.md
CHANGED
|
@@ -185,8 +185,39 @@ To use this MCP server with Claude Desktop, add the following configuration to y
|
|
|
185
185
|
}
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
+
### Local Development with Claude Code
|
|
189
|
+
|
|
190
|
+
For development and testing, you can add this MCP server directly to your Claude Code project:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Prerequisites: build the project first
|
|
194
|
+
npm install
|
|
195
|
+
npm run build
|
|
196
|
+
|
|
197
|
+
# Configure your .env file
|
|
198
|
+
cp .env.example .env
|
|
199
|
+
# Edit .env with your API credentials
|
|
200
|
+
|
|
201
|
+
# Add MCP server to Claude Code (project scope)
|
|
202
|
+
claude mcp add --scope project agentjobs -- ./mcp-agentjobs.sh
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
This allows you to test and use the AgentJobs tools directly within Claude Code during development, providing immediate feedback and easier debugging.
|
|
206
|
+
|
|
188
207
|
## Available Tools
|
|
189
208
|
|
|
209
|
+
### 📊 `get_jobs_stats`
|
|
210
|
+
Get aggregated statistics for agent jobs without retrieving individual job data. Optimized for dashboards and monitoring with minimal network overhead.
|
|
211
|
+
|
|
212
|
+
**Parameters:**
|
|
213
|
+
- `scheduled_at_gte`: Start of period (ISO 8601)
|
|
214
|
+
- `scheduled_at_lte`: End of period (ISO 8601)
|
|
215
|
+
- `org_id`: Organization filter
|
|
216
|
+
- `job_type_id`: Job type filter
|
|
217
|
+
- `tags`: Tags filter (comma-separated)
|
|
218
|
+
- `status`: Status filter
|
|
219
|
+
- `channel_code`: Channel filter
|
|
220
|
+
|
|
190
221
|
### 🔧 `list_jobs`
|
|
191
222
|
Lists all jobs with filtering and pagination options.
|
|
192
223
|
|
|
@@ -263,6 +294,7 @@ agentjobs-mcp/
|
|
|
263
294
|
│ ├── index.ts # Main MCP server entry point
|
|
264
295
|
│ ├── config.ts # Configuration loader
|
|
265
296
|
│ └── tools/ # Directory for all MCP tools
|
|
297
|
+
│ ├── get_jobs_stats.ts # Tool for getting job statistics
|
|
266
298
|
│ ├── list_jobs.ts # Tool for listing jobs
|
|
267
299
|
│ ├── get_job.ts # Tool for getting a job
|
|
268
300
|
│ ├── create_job.ts # Tool for creating a job
|
|
@@ -282,6 +314,40 @@ agentjobs-mcp/
|
|
|
282
314
|
|
|
283
315
|
- `npm run build`: Compiles TypeScript
|
|
284
316
|
- `npm start`: Runs the compiled server
|
|
317
|
+
- `npm run debug`: Runs server in debug mode with detailed logging
|
|
318
|
+
- `npm run test:tools`: Tests tool loading without starting server
|
|
319
|
+
- `npm run cli:config`: Shows current configuration
|
|
320
|
+
- `npm run cli:version`: Shows version information
|
|
321
|
+
- `npm run cli:help`: Shows help information
|
|
322
|
+
|
|
323
|
+
### Debugging
|
|
324
|
+
|
|
325
|
+
For detailed debugging information, see [Debug Guide](docs/debug-guide.md).
|
|
326
|
+
|
|
327
|
+
**Quick Debug Commands:**
|
|
328
|
+
```bash
|
|
329
|
+
# Test configuration
|
|
330
|
+
npm run cli:config
|
|
331
|
+
|
|
332
|
+
# Test tool loading
|
|
333
|
+
npm run test:tools
|
|
334
|
+
|
|
335
|
+
# Run in debug mode
|
|
336
|
+
MCP_DEBUG=true npm run debug
|
|
337
|
+
|
|
338
|
+
# Use debug helper script (Fish shell)
|
|
339
|
+
./debug.fish help
|
|
340
|
+
./debug.fish quick
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Debug Environment:**
|
|
344
|
+
```bash
|
|
345
|
+
# Copy debug environment template
|
|
346
|
+
cp .env.debug .env
|
|
347
|
+
# Edit .env with your API credentials
|
|
348
|
+
# Run with debug environment
|
|
349
|
+
./debug.fish debug-with-env
|
|
350
|
+
```
|
|
285
351
|
|
|
286
352
|
### Adding new tools
|
|
287
353
|
|
|
@@ -290,6 +356,7 @@ Adding a new tool is simple:
|
|
|
290
356
|
1. Create a new TypeScript file inside the `src/tools/` directory (e.g., `my_new_tool.ts`).
|
|
291
357
|
2. Implement your tool logic following the existing pattern. The server will automatically detect and register it on startup.
|
|
292
358
|
3. Recompile the project with `npm run build`.
|
|
359
|
+
4. Test with `npm run test:tools` to verify loading.
|
|
293
360
|
|
|
294
361
|
## Contributing
|
|
295
362
|
|
package/build/config.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
// Configuration
|
|
1
|
+
// Configuration for the MCP server
|
|
2
2
|
export const config = {
|
|
3
|
-
|
|
3
|
+
apiUrl: process.env.AICONNECT_API_URL || 'https://api.aiconnect.cloud/api/v0',
|
|
4
|
+
apiKey: process.env.AICONNECT_API_KEY || '',
|
|
5
|
+
defaultOrgId: process.env.DEFAULT_ORG_ID || 'aiconnect',
|
|
6
|
+
defaultTimezone: process.env.DEFAULT_TIMEZONE || 'UTC',
|
|
7
|
+
debugMode: process.env.DEBUG === 'true',
|
|
8
|
+
// Legacy compatibility
|
|
9
|
+
AICONNECT_API_URL: process.env.AICONNECT_API_URL || 'https://api.aiconnect.cloud/api/v0',
|
|
4
10
|
AICONNECT_API_KEY: process.env.AICONNECT_API_KEY || '',
|
|
5
|
-
|
|
11
|
+
DEFAULT_ORG_ID: process.env.DEFAULT_ORG_ID || 'aiconnect',
|
|
12
|
+
DEFAULT_TIMEZONE: process.env.DEFAULT_TIMEZONE || 'UTC'
|
|
6
13
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
describe('config.defaultTimezone', () => {
|
|
3
|
+
beforeEach(() => {
|
|
4
|
+
vi.resetModules();
|
|
5
|
+
});
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
vi.unstubAllEnvs();
|
|
8
|
+
vi.resetModules();
|
|
9
|
+
});
|
|
10
|
+
it('reflects DEFAULT_TIMEZONE when set', async () => {
|
|
11
|
+
vi.stubEnv('DEFAULT_TIMEZONE', 'America/Sao_Paulo');
|
|
12
|
+
const { config } = await import('./config.js');
|
|
13
|
+
expect(config.defaultTimezone).toBe('America/Sao_Paulo');
|
|
14
|
+
expect(config.DEFAULT_TIMEZONE).toBe('America/Sao_Paulo');
|
|
15
|
+
});
|
|
16
|
+
it('falls back to UTC when DEFAULT_TIMEZONE is unset', async () => {
|
|
17
|
+
vi.stubEnv('DEFAULT_TIMEZONE', '');
|
|
18
|
+
const { config } = await import('./config.js');
|
|
19
|
+
expect(config.defaultTimezone).toBe('UTC');
|
|
20
|
+
expect(config.DEFAULT_TIMEZONE).toBe('UTC');
|
|
21
|
+
});
|
|
22
|
+
});
|
package/build/debug.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { config } from './config.js';
|
|
10
|
+
// Enable debug mode
|
|
11
|
+
const DEBUG = true;
|
|
12
|
+
function debugLog(message, data) {
|
|
13
|
+
if (DEBUG) {
|
|
14
|
+
const timestamp = new Date().toISOString();
|
|
15
|
+
console.error(`[DEBUG ${timestamp}] ${message}`);
|
|
16
|
+
if (data) {
|
|
17
|
+
console.error('[DEBUG DATA]', JSON.stringify(data, null, 2));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Get package version
|
|
22
|
+
const packageJson = JSON.parse(await import('fs').then(fs => fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8')));
|
|
23
|
+
debugLog('Starting MCP Server in DEBUG mode');
|
|
24
|
+
debugLog('Configuration loaded', {
|
|
25
|
+
DEFAULT_ORG_ID: config.DEFAULT_ORG_ID,
|
|
26
|
+
AICONNECT_API_URL: config.AICONNECT_API_URL,
|
|
27
|
+
AICONNECT_API_KEY: config.AICONNECT_API_KEY ? '[SET]' : '[NOT SET]',
|
|
28
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
29
|
+
version: packageJson.version
|
|
30
|
+
});
|
|
31
|
+
// Initialize server with debug logging
|
|
32
|
+
const server = new McpServer({
|
|
33
|
+
name: "agentjobs-mcp-debug",
|
|
34
|
+
version: packageJson.version
|
|
35
|
+
});
|
|
36
|
+
debugLog('MCP Server initialized');
|
|
37
|
+
// Dynamically load and register tools with debug info
|
|
38
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
const toolsDir = path.join(__dirname, 'tools');
|
|
40
|
+
try {
|
|
41
|
+
debugLog(`Loading tools from: ${toolsDir}`);
|
|
42
|
+
const toolFiles = await fs.readdir(toolsDir);
|
|
43
|
+
debugLog(`Found tool files:`, toolFiles);
|
|
44
|
+
for (const file of toolFiles) {
|
|
45
|
+
if (file.endsWith('.js')) {
|
|
46
|
+
debugLog(`Loading tool: ${file}`);
|
|
47
|
+
try {
|
|
48
|
+
const toolModule = await import(`./tools/${file}`);
|
|
49
|
+
if (typeof toolModule.default === 'function') {
|
|
50
|
+
toolModule.default(server);
|
|
51
|
+
debugLog(`✅ Successfully registered tool: ${file}`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
debugLog(`❌ Tool ${file} does not export a default function`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
debugLog(`❌ Error loading tool ${file}:`, error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
debugLog("❌ Error loading tools directory:", error);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
// Start server with debug transport
|
|
68
|
+
const transport = new StdioServerTransport();
|
|
69
|
+
debugLog('Starting transport connection...');
|
|
70
|
+
// Add connection event handling
|
|
71
|
+
transport.onclose = () => {
|
|
72
|
+
debugLog('Transport connection closed');
|
|
73
|
+
};
|
|
74
|
+
transport.onerror = (error) => {
|
|
75
|
+
debugLog('Transport error:', error);
|
|
76
|
+
};
|
|
77
|
+
console.error(`🔍 AI Connect Agent Jobs MCP Server v${packageJson.version} (DEBUG MODE)`);
|
|
78
|
+
console.error('🚀 Server ready and listening for MCP connections');
|
|
79
|
+
try {
|
|
80
|
+
await server.connect(transport);
|
|
81
|
+
debugLog('✅ Server connected successfully');
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
debugLog('❌ Error connecting MCP server:', error);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
package/build/index.js
CHANGED
|
@@ -3,11 +3,15 @@ import * as dotenv from 'dotenv';
|
|
|
3
3
|
dotenv.config();
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { InitializeRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
6
7
|
import fs from "fs/promises";
|
|
7
8
|
import path from "path";
|
|
8
9
|
import { fileURLToPath } from "url";
|
|
9
|
-
|
|
10
|
+
import { mcpServerVersion } from "./utils/version.js";
|
|
11
|
+
// Get package metadata (description/homepage/author come from package.json directly;
|
|
12
|
+
// version comes from the shared helper to avoid duplication).
|
|
10
13
|
const packageJson = JSON.parse(await import('fs').then(fs => fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8')));
|
|
14
|
+
packageJson.version = mcpServerVersion;
|
|
11
15
|
// CLI argument parsing
|
|
12
16
|
const args = process.argv.slice(2);
|
|
13
17
|
// Help text
|
|
@@ -50,14 +54,56 @@ if (args.includes('--config') || args.includes('-c')) {
|
|
|
50
54
|
console.log('Current Configuration:');
|
|
51
55
|
console.log(` API URL: ${process.env.AICONNECT_API_URL || 'Not set'}`);
|
|
52
56
|
console.log(` API Key: ${process.env.AICONNECT_API_KEY ? '[SET]' : 'Not set'}`);
|
|
57
|
+
console.log(` Default Org: ${process.env.DEFAULT_ORG_ID || 'aiconnect'}`);
|
|
58
|
+
console.log(` Default Timezone: ${process.env.DEFAULT_TIMEZONE || 'UTC'}`);
|
|
53
59
|
console.log(` Node Version: ${process.version}`);
|
|
54
60
|
console.log(` MCP Server Version: ${packageJson.version}`);
|
|
55
61
|
process.exit(0);
|
|
56
62
|
}
|
|
57
|
-
//
|
|
63
|
+
// Protocol version tracking
|
|
64
|
+
let clientProtocolVersion = "2024-11-05"; // Default fallback
|
|
65
|
+
let serverCapabilities = {
|
|
66
|
+
tools: { listChanged: true },
|
|
67
|
+
resources: {},
|
|
68
|
+
prompts: {}
|
|
69
|
+
};
|
|
70
|
+
// Initialize server with dynamic capabilities based on protocol version
|
|
58
71
|
const server = new McpServer({
|
|
59
72
|
name: "agentjobs-mcp",
|
|
60
73
|
version: packageJson.version
|
|
74
|
+
}, {
|
|
75
|
+
capabilities: serverCapabilities
|
|
76
|
+
});
|
|
77
|
+
console.error(`[DEBUG] MCP Server initialized with name: agentjobs-mcp`);
|
|
78
|
+
console.error(`[DEBUG] Server version: ${packageJson.version}`);
|
|
79
|
+
console.error(`[DEBUG] Default capabilities: tools, resources, prompts`);
|
|
80
|
+
// Intercept initialization to detect protocol version
|
|
81
|
+
const originalSetRequestHandler = server.server.setRequestHandler.bind(server.server);
|
|
82
|
+
// Override initialization handler to capture protocol version.
|
|
83
|
+
// The schema is cast to `any` to short-circuit a TS2589 ("excessively deep") error
|
|
84
|
+
// from generic Zod inference in @modelcontextprotocol/sdk's setRequestHandler signature.
|
|
85
|
+
server.server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
86
|
+
const initParams = request.params;
|
|
87
|
+
clientProtocolVersion = initParams.protocolVersion || "2024-11-05";
|
|
88
|
+
console.error(`[VERSION-NEGOTIATION] Client requested protocol: ${clientProtocolVersion}`);
|
|
89
|
+
// Always use minimal capabilities to prevent loops
|
|
90
|
+
console.error(`[VERSION-NEGOTIATION] Client requested: ${clientProtocolVersion}`);
|
|
91
|
+
console.error(`[VERSION-NEGOTIATION] Using minimal capabilities to prevent infinite loops`);
|
|
92
|
+
// Use only tools capability - no resources/prompts to avoid repeated calls
|
|
93
|
+
serverCapabilities = {
|
|
94
|
+
tools: {}
|
|
95
|
+
};
|
|
96
|
+
console.error(`[VERSION-NEGOTIATION] Responding with our supported version: 2025-03-26`);
|
|
97
|
+
console.error(`[VERSION-NEGOTIATION] Updated capabilities:`, JSON.stringify(serverCapabilities, null, 2));
|
|
98
|
+
// Return initialization response with our supported version
|
|
99
|
+
return {
|
|
100
|
+
protocolVersion: "2025-03-26", // Our SDK version
|
|
101
|
+
capabilities: serverCapabilities,
|
|
102
|
+
serverInfo: {
|
|
103
|
+
name: "agentjobs-mcp",
|
|
104
|
+
version: packageJson.version
|
|
105
|
+
}
|
|
106
|
+
};
|
|
61
107
|
});
|
|
62
108
|
// Dynamically load and register tools
|
|
63
109
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -66,10 +112,16 @@ try {
|
|
|
66
112
|
const toolFiles = await fs.readdir(toolsDir);
|
|
67
113
|
for (const file of toolFiles) {
|
|
68
114
|
if (file.endsWith('.js')) { // In production, files will be .js
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
toolModule.default
|
|
72
|
-
|
|
115
|
+
try {
|
|
116
|
+
const toolModule = await import(`./tools/${file}`);
|
|
117
|
+
if (typeof toolModule.default === 'function') {
|
|
118
|
+
toolModule.default(server);
|
|
119
|
+
console.error(`-> Registered tool: ${file}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error(`-> Failed to register tool ${file}:`, error);
|
|
124
|
+
// Continue loading other tools
|
|
73
125
|
}
|
|
74
126
|
}
|
|
75
127
|
}
|
|
@@ -78,8 +130,20 @@ catch (error) {
|
|
|
78
130
|
console.error("Error loading tools:", error);
|
|
79
131
|
process.exit(1);
|
|
80
132
|
}
|
|
133
|
+
console.error(`[DEBUG] Only tools capability enabled - no resources/prompts handlers needed`);
|
|
81
134
|
// Start server with stdio transport
|
|
82
135
|
const transport = new StdioServerTransport();
|
|
83
136
|
console.error(`Starting AI Connect Agent Jobs MCP Server v${packageJson.version}...`);
|
|
137
|
+
console.error('Configuration:');
|
|
138
|
+
console.error(` API URL: ${process.env.AICONNECT_API_URL || 'Using default'}`);
|
|
139
|
+
console.error(` API Key: ${process.env.AICONNECT_API_KEY ? '[SET]' : '[NOT SET]'}`);
|
|
140
|
+
console.error(` Default Org: ${process.env.DEFAULT_ORG_ID || 'aiconnect'}`);
|
|
84
141
|
console.error('Server ready and listening for MCP connections');
|
|
85
|
-
|
|
142
|
+
// Add error handling for the server connection
|
|
143
|
+
try {
|
|
144
|
+
await server.connect(transport);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error('Error connecting MCP server:', error);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { config } from '../config.js';
|
|
3
|
+
class AgentJobsClient {
|
|
4
|
+
client = null;
|
|
5
|
+
getClient() {
|
|
6
|
+
if (!this.client) {
|
|
7
|
+
const { AICONNECT_API_URL, AICONNECT_API_KEY } = config;
|
|
8
|
+
if (!AICONNECT_API_URL || !AICONNECT_API_KEY) {
|
|
9
|
+
throw new Error('API URL or Key is not configured. Please set AICONNECT_API_URL and AICONNECT_API_KEY environment variables.');
|
|
10
|
+
}
|
|
11
|
+
this.client = axios.create({
|
|
12
|
+
baseURL: AICONNECT_API_URL,
|
|
13
|
+
headers: {
|
|
14
|
+
'Authorization': `Bearer ${AICONNECT_API_KEY}`,
|
|
15
|
+
'X-Client-Type': 'mcp',
|
|
16
|
+
'Content-Type': 'application/json'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return this.client;
|
|
21
|
+
}
|
|
22
|
+
async get(endpoint, params) {
|
|
23
|
+
return this.request(() => this.getClient().get(endpoint, { params }), params);
|
|
24
|
+
}
|
|
25
|
+
async getWithMeta(endpoint, params) {
|
|
26
|
+
try {
|
|
27
|
+
const response = await this.getClient().get(endpoint, { params });
|
|
28
|
+
return response.data; // Return the full API response including data and meta
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
this.handleError(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async listJobTypes(orgId, options = {}) {
|
|
35
|
+
const params = {
|
|
36
|
+
enrich: 'emoji',
|
|
37
|
+
limit: options.limit ?? 100,
|
|
38
|
+
sort: options.sort ?? 'name:asc'
|
|
39
|
+
};
|
|
40
|
+
if (options.offset !== undefined) {
|
|
41
|
+
params.offset = options.offset;
|
|
42
|
+
}
|
|
43
|
+
return (await this.getWithMeta(`/organizations/${orgId}/agent-jobs-type`, params));
|
|
44
|
+
}
|
|
45
|
+
async getStats(filters = {}) {
|
|
46
|
+
const params = {
|
|
47
|
+
...filters,
|
|
48
|
+
include: 'stats',
|
|
49
|
+
limit: 1
|
|
50
|
+
};
|
|
51
|
+
return this.get('/services/agent-jobs', params);
|
|
52
|
+
}
|
|
53
|
+
async post(endpoint, data) {
|
|
54
|
+
return this.request(() => this.getClient().post(endpoint, data));
|
|
55
|
+
}
|
|
56
|
+
async patch(endpoint, data) {
|
|
57
|
+
return this.request(() => this.getClient().patch(endpoint, data));
|
|
58
|
+
}
|
|
59
|
+
async delete(endpoint, data) {
|
|
60
|
+
return this.request(() => this.getClient().delete(endpoint, { data }));
|
|
61
|
+
}
|
|
62
|
+
async request(requestFn, params) {
|
|
63
|
+
try {
|
|
64
|
+
const response = await requestFn();
|
|
65
|
+
if (params?.include === 'stats') {
|
|
66
|
+
// @ts-expect-error axios response typing varies per call in this client
|
|
67
|
+
return response.data;
|
|
68
|
+
}
|
|
69
|
+
// @ts-expect-error axios response typing varies per call in this client
|
|
70
|
+
return response.data?.data || response.data;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
this.handleError(error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
handleError(error) {
|
|
77
|
+
if (axios.isAxiosError(error)) {
|
|
78
|
+
const axiosError = error;
|
|
79
|
+
const apiError = axiosError.response?.data?.message || axiosError.response?.data?.error || JSON.stringify(axiosError.response?.data);
|
|
80
|
+
throw new Error(`API Error (${axiosError.response?.status}): ${apiError || axiosError.message}`);
|
|
81
|
+
}
|
|
82
|
+
else if (error instanceof Error) {
|
|
83
|
+
throw new Error(`Error: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
throw new Error('An unknown error occurred.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export default new AgentJobsClient();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
// Load debug environment
|
|
4
|
+
dotenv.config({ path: '.env.debug' });
|
|
5
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
// Test specific tools
|
|
10
|
+
async function testTools() {
|
|
11
|
+
console.log('🧪 Testing MCP Tools...\n');
|
|
12
|
+
// Initialize server
|
|
13
|
+
const server = new McpServer({
|
|
14
|
+
name: 'agentjobs-mcp-test',
|
|
15
|
+
version: 'test'
|
|
16
|
+
});
|
|
17
|
+
// Load tools
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const toolsDir = path.join(__dirname, 'tools');
|
|
20
|
+
try {
|
|
21
|
+
const toolFiles = await fs.readdir(toolsDir);
|
|
22
|
+
console.log(`📁 Found ${toolFiles.length} tool files`);
|
|
23
|
+
for (const file of toolFiles) {
|
|
24
|
+
if (file.endsWith('.js')) {
|
|
25
|
+
try {
|
|
26
|
+
console.log(`⚙️ Loading tool: ${file}`);
|
|
27
|
+
const toolModule = await import(`./tools/${file}`);
|
|
28
|
+
if (typeof toolModule.default === 'function') {
|
|
29
|
+
toolModule.default(server);
|
|
30
|
+
console.log(`✅ Successfully registered: ${file}`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
console.log(`❌ Invalid tool format: ${file}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.log(`❌ Error loading ${file}:`, error?.message || error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Show success message
|
|
42
|
+
console.log('\n✅ Tool testing completed!');
|
|
43
|
+
console.log('💡 To run in debug mode: MCP_DEBUG=true npm run debug');
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('❌ Error during tool testing:', error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Run if this script is executed directly
|
|
50
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
51
|
+
testTools().catch(console.error);
|
|
52
|
+
}
|
|
53
|
+
export { testTools };
|
|
@@ -1,74 +1,59 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import
|
|
3
|
-
import { config } from '../config.js';
|
|
2
|
+
import agentJobsClient from "../lib/agentJobsClient.js";
|
|
4
3
|
import { formatJobSummary } from '../utils/formatters.js';
|
|
4
|
+
import { mcpDebugger, withTiming } from '../utils/debugger.js';
|
|
5
5
|
export default (server) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
// @ts-expect-error TS2589: registerTool generic Zod inference exceeds TS depth limit with this schema.
|
|
7
|
+
server.registerTool("cancel_job", {
|
|
8
|
+
description: "Cancels an agent job by its ID.",
|
|
9
|
+
annotations: {
|
|
10
|
+
title: "Cancel Agent Job"
|
|
11
|
+
},
|
|
12
|
+
inputSchema: {
|
|
13
|
+
job_id: z.string({
|
|
14
|
+
description: "The unique identifier of the job to be canceled. Example: 'job-12345'."
|
|
15
|
+
}),
|
|
16
|
+
reason: z.string({
|
|
17
|
+
description: "An optional reason explaining why the job is being canceled."
|
|
18
|
+
}).optional()
|
|
19
|
+
}
|
|
11
20
|
}, async (params) => {
|
|
21
|
+
mcpDebugger.toolCall("cancel_job", params);
|
|
12
22
|
const { job_id, reason } = params;
|
|
13
|
-
const
|
|
14
|
-
const apiKey = config.AICONNECT_API_KEY;
|
|
15
|
-
if (!apiUrl) {
|
|
16
|
-
return {
|
|
17
|
-
content: [{
|
|
18
|
-
type: "text",
|
|
19
|
-
text: "Error: API URL is not configured. Please set AICONNECT_API_URL environment variable."
|
|
20
|
-
}]
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
if (!apiKey) {
|
|
24
|
-
return {
|
|
25
|
-
content: [{
|
|
26
|
-
type: "text",
|
|
27
|
-
text: "Error: API Key is not configured. Please set AICONNECT_API_KEY environment variable."
|
|
28
|
-
}]
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const endpoint = `${apiUrl}/services/agent-jobs/${job_id}`;
|
|
32
|
-
const headers = {
|
|
33
|
-
"Authorization": `Bearer ${apiKey}`,
|
|
34
|
-
};
|
|
23
|
+
const endpoint = `/services/agent-jobs/${job_id}`;
|
|
35
24
|
let requestBody;
|
|
36
25
|
if (reason) {
|
|
37
|
-
headers["Content-Type"] = "application/json";
|
|
38
26
|
requestBody = { reason };
|
|
39
27
|
}
|
|
28
|
+
mcpDebugger.debug("Job cancellation request", {
|
|
29
|
+
endpoint,
|
|
30
|
+
job_id,
|
|
31
|
+
reason,
|
|
32
|
+
hasRequestBody: !!requestBody
|
|
33
|
+
});
|
|
40
34
|
try {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
data: requestBody, // axios uses 'data' for DELETE request body
|
|
44
|
-
});
|
|
45
|
-
const canceledJob = response.data?.data || { job_id, job_status: 'canceled' };
|
|
35
|
+
const canceledJob = await withTiming(() => agentJobsClient.delete(endpoint, requestBody), "cancel_job API call");
|
|
36
|
+
mcpDebugger.debug("Job cancellation response", { canceledJob });
|
|
46
37
|
const summary = formatJobSummary(canceledJob);
|
|
47
|
-
|
|
38
|
+
const result = {
|
|
48
39
|
content: [{
|
|
49
40
|
type: "text",
|
|
50
41
|
text: `Successfully canceled job:\n\n${summary}`,
|
|
51
42
|
}]
|
|
52
43
|
};
|
|
44
|
+
mcpDebugger.toolResponse("cancel_job", {
|
|
45
|
+
jobId: job_id,
|
|
46
|
+
reason,
|
|
47
|
+
resultLength: result.content[0].text.length
|
|
48
|
+
});
|
|
49
|
+
return result;
|
|
53
50
|
}
|
|
54
51
|
catch (error) {
|
|
55
|
-
|
|
56
|
-
let errorDetails = {};
|
|
57
|
-
if (axios.isAxiosError(error) && error.response) {
|
|
58
|
-
const apiError = error.response.data?.message || error.response.data?.error || JSON.stringify(error.response.data);
|
|
59
|
-
errorMessage = `API Error (${error.response.status}): ${apiError || error.message}`;
|
|
60
|
-
errorDetails = {
|
|
61
|
-
status: error.response.status,
|
|
62
|
-
data: error.response.data
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
else if (error instanceof Error) {
|
|
66
|
-
errorMessage = `Error: ${error.message}`;
|
|
67
|
-
}
|
|
52
|
+
mcpDebugger.toolError("cancel_job", error);
|
|
68
53
|
return {
|
|
69
54
|
content: [{
|
|
70
55
|
type: "text",
|
|
71
|
-
text:
|
|
56
|
+
text: `Error canceling job: ${error.message}`,
|
|
72
57
|
}],
|
|
73
58
|
};
|
|
74
59
|
}
|