@aiconnect/agentjobs-mcp 1.2.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 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 # Tool for getting job statistics
298
- │ ├── list_jobs.ts # Tool for listing jobs
299
- │ ├── get_job.ts # Tool for getting a job
300
- │ ├── create_job.ts # Tool for creating a job
301
- └── cancel_job.ts # Tool for canceling a job
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/index.js CHANGED
@@ -111,7 +111,7 @@ const toolsDir = path.join(__dirname, 'tools');
111
111
  try {
112
112
  const toolFiles = await fs.readdir(toolsDir);
113
113
  for (const file of toolFiles) {
114
- 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)
115
115
  try {
116
116
  const toolModule = await import(`./tools/${file}`);
117
117
  if (typeof toolModule.default === 'function') {
@@ -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}`);
@@ -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}${params.org_id ? `?org_id=${params.org_id}` : ''}`;
25
- mcpDebugger.debug("Built endpoint", { endpoint, job_id, org_id: params.org_id });
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 job = await withTiming(() => agentJobsClient.get(endpoint), "get_job API call");
28
- mcpDebugger.debug("Raw API response", { job });
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,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",
@@ -30,18 +31,51 @@ export default (server) => {
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
- // Build query parameters object from provided params
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(params)) {
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");
@@ -58,7 +92,7 @@ export default (server) => {
58
92
  const result = {
59
93
  content: [{
60
94
  type: "text",
61
- text: formatJobList(jobs, meta, offset),
95
+ text: formatJobList(jobs, meta, offset, { includeActivities: include_activities === true }),
62
96
  }]
63
97
  };
64
98
  mcpDebugger.toolResponse("list_jobs", {