@aiconnect/agentjobs-mcp 1.1.0 → 1.3.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 +4 -0
- package/README.md +39 -6
- package/build/config.js +3 -1
- package/build/index.js +10 -3
- package/build/lib/agentJobsClient.js +11 -0
- package/build/test-tools.js +1 -1
- package/build/tools/cancel_job.js +5 -2
- package/build/tools/create_job.js +1 -1
- package/build/tools/get_context.js +61 -0
- package/build/tools/get_job.js +39 -9
- package/build/tools/get_job_activities.js +68 -0
- package/build/tools/get_jobs_stats.js +7 -17
- package/build/tools/list_jobs.js +44 -9
- package/build/utils/formatters.js +301 -31
- package/build/utils/schemas.js +23 -11
- package/build/utils/version.js +12 -0
- package/docs/agent-jobs-api.md +156 -0
- package/package.json +3 -2
- package/build/utils/formatters.test.js +0 -387
- package/build/utils/schemas.test.js +0 -40
package/.env.example
CHANGED
|
@@ -10,6 +10,10 @@ AICONNECT_API_KEY=your-api-key-here
|
|
|
10
10
|
# Optional: Default organization ID (defaults to 'aiconnect' if not set)
|
|
11
11
|
# DEFAULT_ORG_ID=your-organization-id
|
|
12
12
|
|
|
13
|
+
# Optional: Preferred timezone surfaced by the `get_context` tool (defaults to 'UTC' if not set).
|
|
14
|
+
# Informational only — does not change timestamp behavior of other tools.
|
|
15
|
+
# DEFAULT_TIMEZONE=America/Sao_Paulo
|
|
16
|
+
|
|
13
17
|
# Debug Settings
|
|
14
18
|
MCP_DEBUG=false
|
|
15
19
|
NODE_ENV=development
|
package/README.md
CHANGED
|
@@ -59,12 +59,14 @@ Edit the `.env` file with your credentials:
|
|
|
59
59
|
DEFAULT_ORG_ID=your-organization # Default: aiconnect
|
|
60
60
|
AICONNECT_API_KEY=your-api-key # Required: Must be provided
|
|
61
61
|
AICONNECT_API_URL=https://api.aiconnect.cloud/api/v0 # Default
|
|
62
|
+
DEFAULT_TIMEZONE=America/Sao_Paulo # Default: UTC (informational only, surfaced via get_context)
|
|
62
63
|
```
|
|
63
64
|
|
|
64
65
|
**Important**: If no environment variables are provided, the server will use these defaults:
|
|
65
66
|
- `DEFAULT_ORG_ID`: `aiconnect`
|
|
66
67
|
- `AICONNECT_API_URL`: `https://api.aiconnect.cloud/api/v0`
|
|
67
68
|
- `AICONNECT_API_KEY`: empty (must be provided for API calls to work)
|
|
69
|
+
- `DEFAULT_TIMEZONE`: `UTC`
|
|
68
70
|
|
|
69
71
|
4. **Build the project:**
|
|
70
72
|
```bash
|
|
@@ -108,6 +110,11 @@ npx @aiconnect/agentjobs-mcp
|
|
|
108
110
|
- `AICONNECT_API_URL`: API endpoint URL (e.g., https://api.aiconnect.cloud/api/v0)
|
|
109
111
|
- `AICONNECT_API_KEY`: Your API authentication key
|
|
110
112
|
|
|
113
|
+
**Optional Environment Variables:**
|
|
114
|
+
|
|
115
|
+
- `DEFAULT_ORG_ID`: Fallback organization ID when a tool's `org_id` parameter is omitted (default: `aiconnect`)
|
|
116
|
+
- `DEFAULT_TIMEZONE`: Preferred timezone surfaced by the `get_context` tool so LLM clients can format timestamps. Informational only — does not change behavior of other tools, which continue to emit timestamps in UTC (default: `UTC`)
|
|
117
|
+
|
|
111
118
|
**CLI Command Examples:**
|
|
112
119
|
```bash
|
|
113
120
|
# Quick help
|
|
@@ -148,14 +155,18 @@ This MCP server is designed to work out-of-the-box with minimal configuration. I
|
|
|
148
155
|
3. **Partial configuration**: Mix of environment variables and defaults
|
|
149
156
|
|
|
150
157
|
**Default Values (when no env vars are set):**
|
|
158
|
+
|
|
151
159
|
- `DEFAULT_ORG_ID`: `"aiconnect"`
|
|
152
160
|
- `AICONNECT_API_URL`: `"https://api.aiconnect.cloud/api/v0"`
|
|
153
161
|
- `AICONNECT_API_KEY`: `""` (empty - you must provide this)
|
|
162
|
+
- `DEFAULT_TIMEZONE`: `"UTC"`
|
|
154
163
|
|
|
155
164
|
**Error Handling:**
|
|
165
|
+
|
|
156
166
|
- The server will always start, even if environment variables are missing.
|
|
157
167
|
- If `AICONNECT_API_KEY` or `AICONNECT_API_URL` are not provided, each tool will return a clear error message upon execution, guiding the user to configure the environment correctly.
|
|
158
168
|
- If `DEFAULT_ORG_ID` is not set, it defaults to "aiconnect".
|
|
169
|
+
- If `DEFAULT_TIMEZONE` is not set, it defaults to "UTC". This value is purely informational (returned by the `get_context` tool) and does not affect timestamp parsing or formatting in other tools.
|
|
159
170
|
|
|
160
171
|
### Running the MCP server
|
|
161
172
|
|
|
@@ -178,7 +189,8 @@ To use this MCP server with Claude Desktop, add the following configuration to y
|
|
|
178
189
|
"env": {
|
|
179
190
|
"DEFAULT_ORG_ID": "your-organization",
|
|
180
191
|
"AICONNECT_API_KEY": "your-api-key",
|
|
181
|
-
"AICONNECT_API_URL": "https://api.aiconnect.cloud/api/v0"
|
|
192
|
+
"AICONNECT_API_URL": "https://api.aiconnect.cloud/api/v0",
|
|
193
|
+
"DEFAULT_TIMEZONE": "America/Sao_Paulo"
|
|
182
194
|
}
|
|
183
195
|
}
|
|
184
196
|
}
|
|
@@ -228,12 +240,32 @@ Lists all jobs with filtering and pagination options.
|
|
|
228
240
|
- `limit` (optional): Result limit (default: 50)
|
|
229
241
|
- `offset` (optional): Pagination offset
|
|
230
242
|
- `sort` (optional): Field and direction for sorting
|
|
243
|
+
- `include_activities` (optional): Attach recent activities to each job (default: false)
|
|
244
|
+
- `activities_limit_per_job` (optional): Max activities per job (1–100, default 15)
|
|
245
|
+
- `activities_total_limit` (optional): Global cap across the response (1–3000, default 500)
|
|
246
|
+
- `activities_sort` (optional): `created_at` or `-created_at` (default `-created_at`)
|
|
231
247
|
|
|
232
248
|
### 🔍 `get_job`
|
|
233
249
|
Gets details of a specific job.
|
|
234
250
|
|
|
235
251
|
**Parameters:**
|
|
236
252
|
- `job_id` (required): ID of the job to query
|
|
253
|
+
- `include_activities` (optional): Attach recent activities as an inline overlay (default: false)
|
|
254
|
+
- `include_limit` (optional): Max activities to attach (1–100, default 50)
|
|
255
|
+
- `include_sort` (optional): `created_at` or `-created_at` (default `-created_at`)
|
|
256
|
+
|
|
257
|
+
### 🧾 `get_job_activities`
|
|
258
|
+
Retrieves the audit activity trail for a specific agent job via the dedicated `/services/activities` endpoint. Supports real pagination (no truncation) and server-side filtering. Use this for focused investigation of a job's activity log; for a quick overlay of recent activities, use `get_job` with `include_activities=true`.
|
|
259
|
+
|
|
260
|
+
**Parameters:**
|
|
261
|
+
- `job_id` (required): ID of the agent job
|
|
262
|
+
- `org_id` (optional): Organization scope
|
|
263
|
+
- `status` (optional): `submitted`, `completed`, or `canceled`
|
|
264
|
+
- `activity_type_code` (optional): Open-string code (e.g., `ai_completion`)
|
|
265
|
+
- `source_type` (optional): `dispatch`, `process_module`, or `direct`
|
|
266
|
+
- `limit` (optional): Page size (default 50)
|
|
267
|
+
- `offset` (optional): Pagination offset (default 0)
|
|
268
|
+
- `sort` (optional): Sort field/direction (default `-created_at`)
|
|
237
269
|
|
|
238
270
|
### ✅ `create_job`
|
|
239
271
|
Creates a new job for execution.
|
|
@@ -294,11 +326,12 @@ agentjobs-mcp/
|
|
|
294
326
|
│ ├── index.ts # Main MCP server entry point
|
|
295
327
|
│ ├── config.ts # Configuration loader
|
|
296
328
|
│ └── tools/ # Directory for all MCP tools
|
|
297
|
-
│ ├── get_jobs_stats.ts
|
|
298
|
-
│ ├── list_jobs.ts
|
|
299
|
-
│ ├── get_job.ts
|
|
300
|
-
│ ├──
|
|
301
|
-
│
|
|
329
|
+
│ ├── get_jobs_stats.ts # Tool for getting job statistics
|
|
330
|
+
│ ├── list_jobs.ts # Tool for listing jobs
|
|
331
|
+
│ ├── get_job.ts # Tool for getting a job
|
|
332
|
+
│ ├── get_job_activities.ts # Tool for getting a job's activity trail
|
|
333
|
+
│ ├── create_job.ts # Tool for creating a job
|
|
334
|
+
│ └── cancel_job.ts # Tool for canceling a job
|
|
302
335
|
├── build/ # Compiled JavaScript code
|
|
303
336
|
├── docs/ # Documentation
|
|
304
337
|
│ └── agent-jobs-api.md # API documentation
|
package/build/config.js
CHANGED
|
@@ -3,9 +3,11 @@ export const config = {
|
|
|
3
3
|
apiUrl: process.env.AICONNECT_API_URL || 'https://api.aiconnect.cloud/api/v0',
|
|
4
4
|
apiKey: process.env.AICONNECT_API_KEY || '',
|
|
5
5
|
defaultOrgId: process.env.DEFAULT_ORG_ID || 'aiconnect',
|
|
6
|
+
defaultTimezone: process.env.DEFAULT_TIMEZONE || 'UTC',
|
|
6
7
|
debugMode: process.env.DEBUG === 'true',
|
|
7
8
|
// Legacy compatibility
|
|
8
9
|
AICONNECT_API_URL: process.env.AICONNECT_API_URL || 'https://api.aiconnect.cloud/api/v0',
|
|
9
10
|
AICONNECT_API_KEY: process.env.AICONNECT_API_KEY || '',
|
|
10
|
-
DEFAULT_ORG_ID: process.env.DEFAULT_ORG_ID || 'aiconnect'
|
|
11
|
+
DEFAULT_ORG_ID: process.env.DEFAULT_ORG_ID || 'aiconnect',
|
|
12
|
+
DEFAULT_TIMEZONE: process.env.DEFAULT_TIMEZONE || 'UTC'
|
|
11
13
|
};
|
package/build/index.js
CHANGED
|
@@ -7,8 +7,11 @@ import { InitializeRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
|
7
7
|
import fs from "fs/promises";
|
|
8
8
|
import path from "path";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
|
-
|
|
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).
|
|
11
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;
|
|
12
15
|
// CLI argument parsing
|
|
13
16
|
const args = process.argv.slice(2);
|
|
14
17
|
// Help text
|
|
@@ -51,6 +54,8 @@ if (args.includes('--config') || args.includes('-c')) {
|
|
|
51
54
|
console.log('Current Configuration:');
|
|
52
55
|
console.log(` API URL: ${process.env.AICONNECT_API_URL || 'Not set'}`);
|
|
53
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'}`);
|
|
54
59
|
console.log(` Node Version: ${process.version}`);
|
|
55
60
|
console.log(` MCP Server Version: ${packageJson.version}`);
|
|
56
61
|
process.exit(0);
|
|
@@ -74,7 +79,9 @@ console.error(`[DEBUG] Server version: ${packageJson.version}`);
|
|
|
74
79
|
console.error(`[DEBUG] Default capabilities: tools, resources, prompts`);
|
|
75
80
|
// Intercept initialization to detect protocol version
|
|
76
81
|
const originalSetRequestHandler = server.server.setRequestHandler.bind(server.server);
|
|
77
|
-
// Override initialization handler to capture protocol version
|
|
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.
|
|
78
85
|
server.server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
79
86
|
const initParams = request.params;
|
|
80
87
|
clientProtocolVersion = initParams.protocolVersion || "2024-11-05";
|
|
@@ -104,7 +111,7 @@ const toolsDir = path.join(__dirname, 'tools');
|
|
|
104
111
|
try {
|
|
105
112
|
const toolFiles = await fs.readdir(toolsDir);
|
|
106
113
|
for (const file of toolFiles) {
|
|
107
|
-
if (file.endsWith('.js')) { // In production, files will be .js
|
|
114
|
+
if (file.endsWith('.js') && !file.endsWith('.test.js')) { // In production, files will be .js (exclude colocated tests)
|
|
108
115
|
try {
|
|
109
116
|
const toolModule = await import(`./tools/${file}`);
|
|
110
117
|
if (typeof toolModule.default === 'function') {
|
|
@@ -31,6 +31,17 @@ class AgentJobsClient {
|
|
|
31
31
|
this.handleError(error);
|
|
32
32
|
}
|
|
33
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
|
+
}
|
|
34
45
|
async getStats(filters = {}) {
|
|
35
46
|
const params = {
|
|
36
47
|
...filters,
|
package/build/test-tools.js
CHANGED
|
@@ -21,7 +21,7 @@ async function testTools() {
|
|
|
21
21
|
const toolFiles = await fs.readdir(toolsDir);
|
|
22
22
|
console.log(`📁 Found ${toolFiles.length} tool files`);
|
|
23
23
|
for (const file of toolFiles) {
|
|
24
|
-
if (file.endsWith('.js')) {
|
|
24
|
+
if (file.endsWith('.js') && !file.endsWith('.test.js')) {
|
|
25
25
|
try {
|
|
26
26
|
console.log(`⚙️ Loading tool: ${file}`);
|
|
27
27
|
const toolModule = await import(`./tools/${file}`);
|
|
@@ -3,6 +3,7 @@ import agentJobsClient from "../lib/agentJobsClient.js";
|
|
|
3
3
|
import { formatJobSummary } from '../utils/formatters.js';
|
|
4
4
|
import { mcpDebugger, withTiming } from '../utils/debugger.js';
|
|
5
5
|
export default (server) => {
|
|
6
|
+
// @ts-expect-error TS2589: registerTool generic Zod inference exceeds TS depth limit with this schema.
|
|
6
7
|
server.registerTool("cancel_job", {
|
|
7
8
|
description: "Cancels an agent job by its ID.",
|
|
8
9
|
annotations: {
|
|
@@ -10,9 +11,11 @@ export default (server) => {
|
|
|
10
11
|
},
|
|
11
12
|
inputSchema: {
|
|
12
13
|
job_id: z.string({
|
|
13
|
-
description: "The unique identifier of the job to be canceled. Example: 'job-12345'."
|
|
14
|
+
description: "The unique identifier of the job to be canceled. Example: 'job-12345'."
|
|
14
15
|
}),
|
|
15
|
-
reason: z.string(
|
|
16
|
+
reason: z.string({
|
|
17
|
+
description: "An optional reason explaining why the job is being canceled."
|
|
18
|
+
}).optional()
|
|
16
19
|
}
|
|
17
20
|
}, async (params) => {
|
|
18
21
|
mcpDebugger.toolCall("cancel_job", params);
|
|
@@ -32,7 +32,7 @@ export default (server) => {
|
|
|
32
32
|
.record(z.any())
|
|
33
33
|
.optional()
|
|
34
34
|
.describe('Free‑form params passed to the agent'),
|
|
35
|
-
scheduled_at: flexibleDateTimeSchema
|
|
35
|
+
scheduled_at: flexibleDateTimeSchema()
|
|
36
36
|
.optional()
|
|
37
37
|
.describe('Schedule the job to run later')
|
|
38
38
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import agentJobsClient from '../lib/agentJobsClient.js';
|
|
3
|
+
import { config } from '../config.js';
|
|
4
|
+
import { formatContext } from '../utils/formatters.js';
|
|
5
|
+
import { mcpServerVersion } from '../utils/version.js';
|
|
6
|
+
import { mcpDebugger, withTiming } from '../utils/debugger.js';
|
|
7
|
+
export default (server) => {
|
|
8
|
+
server.registerTool('get_context', {
|
|
9
|
+
description: 'Returns the MCP server runtime context: local defaults (org_id, timezone, API URL, server version) plus the list of agent job types available in the effective organization. Designed to be the first call an LLM client makes — surfaces what would otherwise be invisible env vars and avoids guessing valid job type IDs before calling create_job.',
|
|
10
|
+
annotations: {
|
|
11
|
+
title: 'Get MCP Server Runtime Context'
|
|
12
|
+
},
|
|
13
|
+
inputSchema: {
|
|
14
|
+
org_id: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Override DEFAULT_ORG_ID for this call (introspect a different org without restarting the server)')
|
|
18
|
+
}
|
|
19
|
+
}, async (params) => {
|
|
20
|
+
mcpDebugger.toolCall('get_context', params);
|
|
21
|
+
const effectiveOrgId = params.org_id || config.DEFAULT_ORG_ID;
|
|
22
|
+
const localConfig = {
|
|
23
|
+
org_id: effectiveOrgId,
|
|
24
|
+
timezone: config.DEFAULT_TIMEZONE,
|
|
25
|
+
api_url: config.AICONNECT_API_URL,
|
|
26
|
+
server_version: mcpServerVersion
|
|
27
|
+
};
|
|
28
|
+
let jobTypes;
|
|
29
|
+
let total;
|
|
30
|
+
let jobTypesError;
|
|
31
|
+
try {
|
|
32
|
+
const response = await withTiming(() => agentJobsClient.listJobTypes(effectiveOrgId), 'get_context.listJobTypes API call');
|
|
33
|
+
jobTypes = (response?.data ?? []).map((jt) => ({
|
|
34
|
+
id: jt.id,
|
|
35
|
+
name: jt.name,
|
|
36
|
+
description: jt.description,
|
|
37
|
+
emoji: jt.emoji
|
|
38
|
+
}));
|
|
39
|
+
total = response?.meta?.total ?? jobTypes.length;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
jobTypesError = error?.message ?? String(error);
|
|
43
|
+
mcpDebugger.toolError('get_context.listJobTypes', error);
|
|
44
|
+
}
|
|
45
|
+
const text = formatContext({ localConfig, jobTypes, total, jobTypesError });
|
|
46
|
+
mcpDebugger.toolResponse('get_context', {
|
|
47
|
+
org_id: effectiveOrgId,
|
|
48
|
+
jobTypesCount: jobTypes?.length,
|
|
49
|
+
hasError: !!jobTypesError,
|
|
50
|
+
resultLength: text.length
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
};
|
package/build/tools/get_job.js
CHANGED
|
@@ -2,35 +2,65 @@ import { z } from 'zod';
|
|
|
2
2
|
import agentJobsClient from '../lib/agentJobsClient.js';
|
|
3
3
|
import { formatJobDetails } from '../utils/formatters.js';
|
|
4
4
|
import { mcpDebugger, withTiming } from '../utils/debugger.js';
|
|
5
|
+
const ACTIVITIES_SORT_VALUES = ['created_at', '-created_at'];
|
|
5
6
|
export default (server) => {
|
|
6
7
|
server.registerTool('get_job', {
|
|
7
|
-
description: 'Retrieves an agent job by its ID.',
|
|
8
|
+
description: 'Retrieves an agent job by its ID. Optionally includes recent activity records as an inline overlay (use include_activities=true).',
|
|
8
9
|
annotations: {
|
|
9
10
|
title: 'Get Agent Job'
|
|
10
11
|
},
|
|
11
12
|
inputSchema: {
|
|
12
13
|
job_id: z.string({
|
|
13
14
|
description: "The unique identifier of the job you want to retrieve. Example: 'job-12345'."
|
|
14
|
-
}),
|
|
15
|
+
}).min(1, { message: 'job_id must be a non-empty string' }),
|
|
15
16
|
org_id: z
|
|
16
17
|
.string({
|
|
17
18
|
description: "The organization ID. Example: 'aiconnect'."
|
|
18
19
|
})
|
|
19
|
-
.optional()
|
|
20
|
+
.optional(),
|
|
21
|
+
include_activities: z.boolean().optional().describe("When true, attaches recent activities as an overlay (?include=activities). For full pagination of activities use the get_job_activities tool."),
|
|
22
|
+
include_limit: z.number().int().optional().describe("Max activities to attach when include_activities=true. Range 1-100, default 50. Silently ignored when include_activities is false/omitted."),
|
|
23
|
+
include_sort: z.string().optional().describe("Sort order for attached activities when include_activities=true. 'created_at' or '-created_at' (default). Silently ignored when include_activities is false/omitted.")
|
|
20
24
|
}
|
|
21
25
|
}, async (params) => {
|
|
22
26
|
mcpDebugger.toolCall("get_job", params);
|
|
23
|
-
const { job_id } = params;
|
|
24
|
-
const endpoint = `/services/agent-jobs/${job_id}
|
|
25
|
-
|
|
27
|
+
const { job_id, include_activities } = params;
|
|
28
|
+
const endpoint = `/services/agent-jobs/${job_id}`;
|
|
29
|
+
const queryParams = {};
|
|
30
|
+
if (params.org_id)
|
|
31
|
+
queryParams.org_id = params.org_id;
|
|
32
|
+
if (include_activities) {
|
|
33
|
+
// Per spec, range/enum on the overlay-only params are validated here
|
|
34
|
+
// (not in the Zod input schema) so that flag-off callers carrying stale
|
|
35
|
+
// configuration are silently tolerated. With the flag on, invalid
|
|
36
|
+
// values short-circuit before any HTTP call.
|
|
37
|
+
if (params.include_limit !== undefined) {
|
|
38
|
+
if (!Number.isInteger(params.include_limit) || params.include_limit < 1 || params.include_limit > 100) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: 'Error getting job: include_limit must be an integer in [1, 100]' }],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (params.include_sort !== undefined && !ACTIVITIES_SORT_VALUES.includes(params.include_sort)) {
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: 'text', text: "Error getting job: include_sort must be 'created_at' or '-created_at'" }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
queryParams.include = 'activities';
|
|
50
|
+
queryParams.include_limit = params.include_limit ?? 50;
|
|
51
|
+
queryParams.include_sort = params.include_sort ?? '-created_at';
|
|
52
|
+
}
|
|
53
|
+
mcpDebugger.debug("Built endpoint", { endpoint, queryParams });
|
|
26
54
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
55
|
+
const apiResponse = await withTiming(() => agentJobsClient.getWithMeta(endpoint, queryParams), "get_job API call");
|
|
56
|
+
const job = apiResponse?.data ?? apiResponse;
|
|
57
|
+
const meta = apiResponse?.meta;
|
|
58
|
+
mcpDebugger.debug("Raw API response", { job, meta });
|
|
29
59
|
const result = {
|
|
30
60
|
content: [
|
|
31
61
|
{
|
|
32
62
|
type: 'text',
|
|
33
|
-
text: formatJobDetails(job)
|
|
63
|
+
text: formatJobDetails(job, meta)
|
|
34
64
|
}
|
|
35
65
|
]
|
|
36
66
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import agentJobsClient from '../lib/agentJobsClient.js';
|
|
3
|
+
import { formatJobActivitiesList } from '../utils/formatters.js';
|
|
4
|
+
import { activityStatusSchema, activitySourceTypeSchema, } from '../utils/schemas.js';
|
|
5
|
+
import { mcpDebugger, withTiming } from '../utils/debugger.js';
|
|
6
|
+
export default (server) => {
|
|
7
|
+
server.registerTool('get_job_activities', {
|
|
8
|
+
description: "Retrieves the audit activity trail for a specific agent job via the dedicated /services/activities endpoint. Supports real pagination (no truncation) and server-side filters by status, type and source. Use this for focused investigation of a job's activity log; for a quick overlay of recent activities on the job detail, use get_job with include_activities=true.",
|
|
9
|
+
annotations: {
|
|
10
|
+
title: 'Get Agent Job Activities'
|
|
11
|
+
},
|
|
12
|
+
inputSchema: {
|
|
13
|
+
job_id: z.string().min(1).describe("The unique identifier of the agent job whose activities you want to retrieve. Required."),
|
|
14
|
+
org_id: z.string().optional().describe("The organization ID. If omitted, the default from the environment is used."),
|
|
15
|
+
status: activityStatusSchema().optional().describe("Filter by activity status. Possible values: 'submitted', 'completed', 'canceled'."),
|
|
16
|
+
activity_type_code: z.string().optional().describe("Filter by activity type code (open string, e.g., 'ai_completion')."),
|
|
17
|
+
source_type: activitySourceTypeSchema().optional().describe("Filter by source type. Possible values: 'dispatch', 'process_module', 'direct'."),
|
|
18
|
+
limit: z.number().int().positive().optional().describe("Maximum number of activities to return per page. Default 50."),
|
|
19
|
+
offset: z.number().int().nonnegative().optional().describe("Number of activities to skip, used for pagination. Default 0."),
|
|
20
|
+
sort: z.string().optional().describe("Field and direction to sort by. Default '-created_at' (newest first).")
|
|
21
|
+
}
|
|
22
|
+
}, async (params) => {
|
|
23
|
+
mcpDebugger.toolCall('get_job_activities', params);
|
|
24
|
+
const endpoint = '/services/activities';
|
|
25
|
+
const queryParams = {
|
|
26
|
+
job_id: params.job_id,
|
|
27
|
+
limit: params.limit ?? 50,
|
|
28
|
+
offset: params.offset ?? 0,
|
|
29
|
+
sort: params.sort ?? '-created_at',
|
|
30
|
+
};
|
|
31
|
+
if (params.org_id)
|
|
32
|
+
queryParams.org_id = params.org_id;
|
|
33
|
+
if (params.status)
|
|
34
|
+
queryParams.status = params.status;
|
|
35
|
+
if (params.activity_type_code)
|
|
36
|
+
queryParams.activity_type_code = params.activity_type_code;
|
|
37
|
+
if (params.source_type)
|
|
38
|
+
queryParams.source_type = params.source_type;
|
|
39
|
+
mcpDebugger.debug('Built query parameters', { endpoint, queryParams });
|
|
40
|
+
try {
|
|
41
|
+
const apiResponse = await withTiming(() => agentJobsClient.getWithMeta(endpoint, queryParams), 'get_job_activities API call');
|
|
42
|
+
const activities = apiResponse?.data ?? [];
|
|
43
|
+
const meta = apiResponse?.meta ?? {};
|
|
44
|
+
const offset = queryParams.offset;
|
|
45
|
+
const result = {
|
|
46
|
+
content: [{
|
|
47
|
+
type: 'text',
|
|
48
|
+
text: formatJobActivitiesList(params.job_id, activities, meta, offset),
|
|
49
|
+
}]
|
|
50
|
+
};
|
|
51
|
+
mcpDebugger.toolResponse('get_job_activities', {
|
|
52
|
+
jobId: params.job_id,
|
|
53
|
+
activitiesReturned: activities.length,
|
|
54
|
+
resultLength: result.content[0].text.length
|
|
55
|
+
});
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
mcpDebugger.toolError('get_job_activities', error);
|
|
60
|
+
return {
|
|
61
|
+
content: [{
|
|
62
|
+
type: 'text',
|
|
63
|
+
text: `Error getting job activities: ${error.message}`,
|
|
64
|
+
}],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
@@ -3,42 +3,32 @@ import agentJobsClient from "../lib/agentJobsClient.js";
|
|
|
3
3
|
import { formatJobStats } from "../utils/formatters.js";
|
|
4
4
|
import { flexibleDateTimeSchema } from "../utils/schemas.js";
|
|
5
5
|
import { mcpDebugger, withTiming } from "../utils/debugger.js";
|
|
6
|
-
const jobStatusSchema = z.enum([
|
|
7
|
-
"waiting",
|
|
8
|
-
"scheduled",
|
|
9
|
-
"running",
|
|
10
|
-
"completed",
|
|
11
|
-
"failed",
|
|
12
|
-
"canceled"
|
|
13
|
-
]);
|
|
14
6
|
export default (server) => {
|
|
15
7
|
server.registerTool("get_jobs_stats", {
|
|
16
|
-
description: "
|
|
8
|
+
description: "Returns aggregated job counts broken down by status (waiting/scheduled/running/completed/failed/canceled) plus a summary (total, success rate, active, completion rate). To filter to a single status, use `list_jobs` with `status=` instead — this tool intentionally does not expose a `status` filter because the upstream stats endpoint ignores it (the breakdown is itself by status). Filters available here narrow the universe along orthogonal dimensions: `job_type_id`, `channel_code`, `tags`, `scheduled_at_*`, `created_at_*`. Result-code and duration aggregates are not yet available; for those today, fall back to `list_jobs` and aggregate client-side. Optimized for dashboards and monitoring with minimal network overhead.",
|
|
17
9
|
annotations: {
|
|
18
10
|
title: "Get Job Statistics"
|
|
19
11
|
},
|
|
20
12
|
inputSchema: {
|
|
21
13
|
org_id: z.string().optional().describe("Filter by organization ID."),
|
|
22
|
-
scheduled_at_gte: flexibleDateTimeSchema.optional().describe("Start of period (ISO 8601)"),
|
|
23
|
-
scheduled_at_lte: flexibleDateTimeSchema.optional().describe("End of period (ISO 8601)"),
|
|
24
|
-
created_at_gte: flexibleDateTimeSchema.optional().describe("Filter for jobs created at or after a specific time (ISO 8601)."),
|
|
25
|
-
created_at_lte: flexibleDateTimeSchema.optional().describe("Filter for jobs created at or before a specific time (ISO 8601)."),
|
|
14
|
+
scheduled_at_gte: flexibleDateTimeSchema().optional().describe("Start of period (ISO 8601)"),
|
|
15
|
+
scheduled_at_lte: flexibleDateTimeSchema().optional().describe("End of period (ISO 8601)"),
|
|
16
|
+
created_at_gte: flexibleDateTimeSchema().optional().describe("Filter for jobs created at or after a specific time (ISO 8601)."),
|
|
17
|
+
created_at_lte: flexibleDateTimeSchema().optional().describe("Filter for jobs created at or before a specific time (ISO 8601)."),
|
|
26
18
|
job_type_id: z.string().optional().describe("Job type filter"),
|
|
27
19
|
channel_code: z.string().optional().describe("Channel filter"),
|
|
28
20
|
tags: z.string().optional().describe("Tags filter (comma-separated)"),
|
|
29
|
-
status: jobStatusSchema.optional().describe("Status filter"),
|
|
30
21
|
}
|
|
31
22
|
}, async (params) => {
|
|
32
23
|
mcpDebugger.toolCall("get_jobs_stats", params);
|
|
33
24
|
try {
|
|
34
25
|
const response = await withTiming(() => agentJobsClient.getStats(params), "get_jobs_stats API call");
|
|
35
26
|
const stats = response.meta?.stats || {};
|
|
36
|
-
|
|
37
|
-
mcpDebugger.debug("Raw API response", { stats, filters });
|
|
27
|
+
mcpDebugger.debug("Raw API response", { stats, appliedFilters: params });
|
|
38
28
|
const result = {
|
|
39
29
|
content: [{
|
|
40
30
|
type: "text",
|
|
41
|
-
text: formatJobStats(stats,
|
|
31
|
+
text: formatJobStats(stats, params),
|
|
42
32
|
}]
|
|
43
33
|
};
|
|
44
34
|
mcpDebugger.toolResponse("get_jobs_stats", result);
|
package/build/tools/list_jobs.js
CHANGED
|
@@ -3,6 +3,7 @@ import agentJobsClient from "../lib/agentJobsClient.js";
|
|
|
3
3
|
import { formatJobList } from "../utils/formatters.js";
|
|
4
4
|
import { flexibleDateTimeSchema } from "../utils/schemas.js";
|
|
5
5
|
import { mcpDebugger, withTiming } from "../utils/debugger.js";
|
|
6
|
+
const ACTIVITIES_SORT_VALUES = ['created_at', '-created_at'];
|
|
6
7
|
// Define the schema for job status based on docs/agent-jobs-api.md:246-251
|
|
7
8
|
const jobStatusSchema = z.enum([
|
|
8
9
|
"waiting",
|
|
@@ -21,27 +22,60 @@ export default (server) => {
|
|
|
21
22
|
inputSchema: {
|
|
22
23
|
org_id: z.string().optional().describe("Filter by organization ID. If not provided, the default from the environment is used."),
|
|
23
24
|
status: jobStatusSchema.optional().describe("Filter by job status. Possible values are: 'waiting', 'scheduled', 'running', 'completed', 'failed', 'canceled'."),
|
|
24
|
-
scheduled_at: flexibleDateTimeSchema.optional().describe("Filter by the exact scheduled time in ISO 8601 format (e.g., '2024-07-23T10:00:00Z')."),
|
|
25
|
-
scheduled_at_gte: flexibleDateTimeSchema.optional().describe("Filter for jobs scheduled at or after a specific time (ISO 8601)."),
|
|
26
|
-
scheduled_at_lte: flexibleDateTimeSchema.optional().describe("Filter for jobs scheduled at or before a specific time (ISO 8601)."),
|
|
27
|
-
created_at_gte: flexibleDateTimeSchema.optional().describe("Filter for jobs created at or after a specific time (ISO 8601)."),
|
|
28
|
-
created_at_lte: flexibleDateTimeSchema.optional().describe("Filter for jobs created at or before a specific time (ISO 8601)."),
|
|
25
|
+
scheduled_at: flexibleDateTimeSchema().optional().describe("Filter by the exact scheduled time in ISO 8601 format (e.g., '2024-07-23T10:00:00Z')."),
|
|
26
|
+
scheduled_at_gte: flexibleDateTimeSchema().optional().describe("Filter for jobs scheduled at or after a specific time (ISO 8601)."),
|
|
27
|
+
scheduled_at_lte: flexibleDateTimeSchema().optional().describe("Filter for jobs scheduled at or before a specific time (ISO 8601)."),
|
|
28
|
+
created_at_gte: flexibleDateTimeSchema().optional().describe("Filter for jobs created at or after a specific time (ISO 8601)."),
|
|
29
|
+
created_at_lte: flexibleDateTimeSchema().optional().describe("Filter for jobs created at or before a specific time (ISO 8601)."),
|
|
29
30
|
job_type_id: z.string().optional().describe("Filter by the specific job type ID (e.g., 'daily-report')."),
|
|
30
31
|
channel_code: z.string().optional().describe("Filter by the channel code (e.g., 'C123456' for a Slack channel)."),
|
|
31
32
|
limit: z.number().int().positive().optional().describe("Maximum number of jobs to return (e.g.,20). Default is 20."),
|
|
32
33
|
offset: z.number().int().nonnegative().optional().describe("Number of jobs to skip, used for pagination. Default is 0."),
|
|
33
|
-
sort: z.string().optional().describe("Field to sort by and direction. Format is 'field:direction'. Example: 'created_at:desc'.")
|
|
34
|
+
sort: z.string().optional().describe("Field to sort by and direction. Format is 'field:direction'. Example: 'created_at:desc'."),
|
|
35
|
+
include_activities: z.boolean().optional().describe("When true, attaches activities to each job in the list (?include=activities). For full pagination of a single job's activities use the get_job_activities tool."),
|
|
36
|
+
activities_limit_per_job: z.number().int().optional().describe("Max activities per job when include_activities=true. Range 1-100, default 15. Silently ignored when include_activities is false/omitted."),
|
|
37
|
+
activities_total_limit: z.number().int().optional().describe("Global cap on total activities returned across all jobs in the response when include_activities=true. Range 1-3000, default 500. Silently ignored when include_activities is false/omitted."),
|
|
38
|
+
activities_sort: z.string().optional().describe("Sort order for attached activities when include_activities=true. 'created_at' or '-created_at' (default). Silently ignored when include_activities is false/omitted.")
|
|
34
39
|
}
|
|
35
40
|
}, async (params) => {
|
|
36
41
|
mcpDebugger.toolCall("list_jobs", params);
|
|
37
42
|
const endpoint = `/services/agent-jobs`;
|
|
38
|
-
|
|
43
|
+
const { include_activities, activities_limit_per_job, activities_total_limit, activities_sort, ...rest } = params;
|
|
39
44
|
const queryParams = {};
|
|
40
|
-
for (const [key, value] of Object.entries(
|
|
45
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
41
46
|
if (value !== undefined) {
|
|
42
47
|
queryParams[key] = value;
|
|
43
48
|
}
|
|
44
49
|
}
|
|
50
|
+
if (include_activities) {
|
|
51
|
+
// Per spec, range/enum on the overlay-only params are validated here
|
|
52
|
+
// (not in the Zod input schema) so that flag-off callers carrying stale
|
|
53
|
+
// configuration are silently tolerated. With the flag on, invalid
|
|
54
|
+
// values short-circuit before any HTTP call.
|
|
55
|
+
if (activities_limit_per_job !== undefined) {
|
|
56
|
+
if (!Number.isInteger(activities_limit_per_job) || activities_limit_per_job < 1 || activities_limit_per_job > 100) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: 'text', text: 'Error listing jobs: activities_limit_per_job must be an integer in [1, 100]' }],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (activities_total_limit !== undefined) {
|
|
63
|
+
if (!Number.isInteger(activities_total_limit) || activities_total_limit < 1 || activities_total_limit > 3000) {
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: 'text', text: 'Error listing jobs: activities_total_limit must be an integer in [1, 3000]' }],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (activities_sort !== undefined && !ACTIVITIES_SORT_VALUES.includes(activities_sort)) {
|
|
70
|
+
return {
|
|
71
|
+
content: [{ type: 'text', text: "Error listing jobs: activities_sort must be 'created_at' or '-created_at'" }],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
queryParams.include = 'activities';
|
|
75
|
+
queryParams.activities_limit_per_job = activities_limit_per_job ?? 15;
|
|
76
|
+
queryParams.activities_total_limit = activities_total_limit ?? 500;
|
|
77
|
+
queryParams.activities_sort = activities_sort ?? '-created_at';
|
|
78
|
+
}
|
|
45
79
|
mcpDebugger.debug("Built query parameters", { endpoint, queryParams });
|
|
46
80
|
try {
|
|
47
81
|
const apiResponse = await withTiming(() => agentJobsClient.getWithMeta(endpoint, queryParams), "list_jobs API call");
|
|
@@ -54,10 +88,11 @@ export default (server) => {
|
|
|
54
88
|
meta,
|
|
55
89
|
firstJob: jobs[0] || null
|
|
56
90
|
});
|
|
91
|
+
const offset = typeof params.offset === 'number' ? params.offset : 0;
|
|
57
92
|
const result = {
|
|
58
93
|
content: [{
|
|
59
94
|
type: "text",
|
|
60
|
-
text: formatJobList(jobs, meta),
|
|
95
|
+
text: formatJobList(jobs, meta, offset, { includeActivities: include_activities === true }),
|
|
61
96
|
}]
|
|
62
97
|
};
|
|
63
98
|
mcpDebugger.toolResponse("list_jobs", {
|