@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.
@@ -1,71 +1,56 @@
1
1
  import { z } from 'zod';
2
- import axios from 'axios';
2
+ import agentJobsClient from '../lib/agentJobsClient.js';
3
3
  import { config } from '../config.js';
4
- /**
5
- * Lightweight Agent‑Jobs creator.
6
- * Only the essentials are required to keep the contract LLM‑friendly.
7
- */
4
+ import { flexibleDateTimeSchema } from '../utils/schemas.js';
5
+ import { mcpDebugger, withTiming } from '../utils/debugger.js';
8
6
  export default (server) => {
9
- server.tool('create_job', 'Create a new Agent Job with the minimal set of fields.', {
10
- // ─────────────────────────────────────────────────────────────────────────┐
11
- // Required
12
- // ─────────────────────────────────────────────────────────────────────────┘
13
- job_type_id: z
14
- .string()
15
- .describe('ID of the job type (e.g. "mood-monitor")'),
16
- target_channel: z
17
- .object({
18
- platform: z
19
- .enum(['whatsapp', 'slack', 'web'])
20
- .describe('Destination platform.'),
21
- code: z
22
- .string()
23
- .describe('Channel identifier, phone number, or user ID.'),
24
- org_id: z
7
+ server.registerTool('create_job', {
8
+ description: 'Create a new Agent Job with the minimal set of fields.',
9
+ annotations: {
10
+ title: 'Create Agent Job'
11
+ },
12
+ inputSchema: {
13
+ // Required fields
14
+ job_type_id: z
25
15
  .string()
16
+ .describe('ID of the job type (e.g. "mood-monitor")'),
17
+ target_channel: z
18
+ .object({
19
+ platform: z
20
+ .enum(['whatsapp', 'slack', 'web'])
21
+ .describe('Destination platform.'),
22
+ code: z
23
+ .string()
24
+ .describe('Channel identifier, phone number, or user ID.'),
25
+ org_id: z
26
+ .string()
27
+ .optional()
28
+ .describe('Org ID – defaults to config.DEFAULT_ORG_ID')
29
+ })
30
+ .describe('Where the agent will communicate.'),
31
+ params: z
32
+ .record(z.any())
26
33
  .optional()
27
- .describe('Org ID defaults to config.DEFAULT_ORG_ID')
28
- })
29
- .describe('Where the agent will communicate.'),
30
- params: z
31
- .record(z.any())
32
- .optional()
33
- .describe('Free‑form params passed to the agent'),
34
- scheduled_at: z
35
- .string()
36
- .datetime({ message: 'Use ISO‑8601' })
37
- .optional()
38
- .describe('Schedule the job to run later')
39
- },
40
- // ───────────────────────────────────────────────────────────────────────────┐
41
- // Implementation │
42
- // ───────────────────────────────────────────────────────────────────────────┘
43
- async (params) => {
44
- const { AICONNECT_API_URL: apiUrl, AICONNECT_API_KEY: apiKey, DEFAULT_ORG_ID } = config;
45
- if (!apiUrl) {
46
- return {
47
- content: [{
48
- type: "text",
49
- text: "Error: API URL is not configured. Please set AICONNECT_API_URL environment variable."
50
- }]
51
- };
52
- }
53
- if (!apiKey) {
54
- return {
55
- content: [{
56
- type: "text",
57
- text: "Error: API Key is not configured. Please set AICONNECT_API_KEY environment variable."
58
- }]
59
- };
34
+ .describe('Free‑form params passed to the agent'),
35
+ scheduled_at: flexibleDateTimeSchema()
36
+ .optional()
37
+ .describe('Schedule the job to run later')
60
38
  }
39
+ }, async (params) => {
40
+ mcpDebugger.toolCall("create_job", params);
61
41
  // Fall back to default org if none supplied.
62
- params.target_channel.org_id ??= DEFAULT_ORG_ID;
42
+ params.target_channel.org_id ??= config.DEFAULT_ORG_ID;
43
+ mcpDebugger.debug("Job creation payload", {
44
+ job_type_id: params.job_type_id,
45
+ target_channel: params.target_channel,
46
+ scheduled_at: params.scheduled_at,
47
+ paramsCount: params.params ? Object.keys(params.params).length : 0
48
+ });
63
49
  try {
64
- const res = await axios.post(`${apiUrl}/services/agent-jobs`, params, {
65
- headers: { Authorization: `Bearer ${apiKey}` }
66
- });
67
- const jobId = res.data?.data?.id ?? res.data?.id ?? 'unknown';
68
- return {
50
+ const res = await withTiming(() => agentJobsClient.post('/services/agent-jobs', params), "create_job API call");
51
+ const jobId = res.id ?? 'unknown';
52
+ mcpDebugger.debug("Job creation response", { jobId, fullResponse: res });
53
+ const result = {
69
54
  content: [
70
55
  {
71
56
  type: 'text',
@@ -73,21 +58,16 @@ export default (server) => {
73
58
  }
74
59
  ]
75
60
  };
61
+ mcpDebugger.toolResponse("create_job", { jobId });
62
+ return result;
76
63
  }
77
64
  catch (error) {
78
- let errorMessage = `Failed to create job.`;
79
- if (axios.isAxiosError(error) && error.response) {
80
- const apiError = error.response.data?.message || error.response.data?.error || JSON.stringify(error.response.data);
81
- errorMessage = `API Error (${error.response.status}): ${apiError || error.message}`;
82
- }
83
- else if (error instanceof Error) {
84
- errorMessage = `Error: ${error.message}`;
85
- }
65
+ mcpDebugger.toolError("create_job", error);
86
66
  return {
87
67
  content: [
88
68
  {
89
69
  type: 'text',
90
- text: errorMessage
70
+ text: `Error creating job: ${error.message}`
91
71
  }
92
72
  ]
93
73
  };
@@ -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
+ };
@@ -1,51 +1,32 @@
1
1
  import { z } from 'zod';
2
- import axios from 'axios';
3
- import { config } from '../config.js';
2
+ import agentJobsClient from '../lib/agentJobsClient.js';
4
3
  import { formatJobDetails } from '../utils/formatters.js';
4
+ import { mcpDebugger, withTiming } from '../utils/debugger.js';
5
5
  export default (server) => {
6
- server.tool('get_job', 'Retrieves an agent job by its ID.', {
7
- job_id: z.string({
8
- description: "The unique identifier of the job you want to retrieve. Example: 'job-12345'."
9
- }),
10
- org_id: z
11
- .string({
12
- description: "The organization ID. Example: 'aiconnect'."
13
- })
14
- .optional()
6
+ server.registerTool('get_job', {
7
+ description: 'Retrieves an agent job by its ID.',
8
+ annotations: {
9
+ title: 'Get Agent Job'
10
+ },
11
+ inputSchema: {
12
+ job_id: z.string({
13
+ description: "The unique identifier of the job you want to retrieve. Example: 'job-12345'."
14
+ }),
15
+ org_id: z
16
+ .string({
17
+ description: "The organization ID. Example: 'aiconnect'."
18
+ })
19
+ .optional()
20
+ }
15
21
  }, async (params) => {
22
+ mcpDebugger.toolCall("get_job", params);
16
23
  const { job_id } = params;
17
- const apiUrl = config.AICONNECT_API_URL;
18
- const apiKey = config.AICONNECT_API_KEY;
19
- if (!apiUrl) {
20
- return {
21
- content: [
22
- {
23
- type: 'text',
24
- text: 'Error: API URL is not configured. Please set AICONNECT_API_URL environment variable.'
25
- }
26
- ]
27
- };
28
- }
29
- if (!apiKey) {
30
- return {
31
- content: [
32
- {
33
- type: 'text',
34
- text: 'Error: API Key is not configured. Please set AICONNECT_API_KEY environment variable.'
35
- }
36
- ]
37
- };
38
- }
39
- const endpoint = `${apiUrl}/services/agent-jobs/${job_id}${params.org_id ? `?org_id=${params.org_id}` : ''}`;
40
- const headers = {
41
- Authorization: `Bearer ${apiKey}`
42
- };
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 });
43
26
  try {
44
- const response = await axios.get(endpoint, {
45
- headers
46
- });
47
- const job = response.data?.data || response.data;
48
- return {
27
+ const job = await withTiming(() => agentJobsClient.get(endpoint), "get_job API call");
28
+ mcpDebugger.debug("Raw API response", { job });
29
+ const result = {
49
30
  content: [
50
31
  {
51
32
  type: 'text',
@@ -53,28 +34,19 @@ export default (server) => {
53
34
  }
54
35
  ]
55
36
  };
37
+ mcpDebugger.toolResponse("get_job", {
38
+ jobId: job_id,
39
+ resultLength: result.content[0].text.length
40
+ });
41
+ return result;
56
42
  }
57
43
  catch (error) {
58
- let errorMessage = `Failed to retrieve job ${job_id}.`;
59
- let errorDetails = {};
60
- if (axios.isAxiosError(error) && error.response) {
61
- const apiError = error.response.data?.message ||
62
- error.response.data?.error ||
63
- JSON.stringify(error.response.data);
64
- errorMessage = `API Error (${error.response.status}): ${apiError || error.message}`;
65
- errorDetails = {
66
- status: error.response.status,
67
- data: error.response.data
68
- };
69
- }
70
- else if (error instanceof Error) {
71
- errorMessage = `Error: ${error.message}`;
72
- }
44
+ mcpDebugger.toolError("get_job", error);
73
45
  return {
74
46
  content: [
75
47
  {
76
48
  type: 'text',
77
- text: errorMessage
49
+ text: `Error getting job: ${error.message}`
78
50
  }
79
51
  ]
80
52
  };
@@ -1,79 +1,63 @@
1
1
  import { z } from 'zod';
2
- import axios from 'axios';
2
+ import agentJobsClient from '../lib/agentJobsClient.js';
3
3
  import { config } from '../config.js';
4
4
  import { formatJobTypeDetails } from '../utils/formatters.js';
5
+ import { mcpDebugger, withTiming } from '../utils/debugger.js';
5
6
  export default (server) => {
6
- server.tool('get_job_type', 'Retrieves an agent job type by its ID.', {
7
- job_type_id: z.string({
8
- description: "The unique identifier of the job type you want to retrieve. Example: 'mood-monitor'."
9
- }),
10
- org_id: z.string({
11
- description: "The organization ID. Example: 'aiconnect'."
12
- }).optional()
7
+ server.registerTool('get_job_type', {
8
+ description: 'Retrieves an agent job type by its ID.',
9
+ annotations: {
10
+ title: 'Get Job Type Configuration'
11
+ },
12
+ inputSchema: {
13
+ job_type_id: z.string({
14
+ description: "The unique identifier of the job type you want to retrieve. Example: 'mood-monitor'."
15
+ }),
16
+ org_id: z.string({
17
+ description: "The organization ID. Example: 'aiconnect'."
18
+ }).optional()
19
+ }
13
20
  }, async (params) => {
21
+ mcpDebugger.toolCall("get_job_type", params);
14
22
  const { job_type_id } = params;
15
23
  const org_id = params.org_id || config.DEFAULT_ORG_ID;
16
- const apiUrl = config.AICONNECT_API_URL;
17
- const apiKey = config.AICONNECT_API_KEY;
18
- if (!apiUrl) {
19
- return {
20
- content: [
21
- {
22
- type: 'text',
23
- text: 'Error: API URL is not configured. Please set AICONNECT_API_URL environment variable.'
24
- }
25
- ]
26
- };
27
- }
28
- if (!apiKey) {
29
- return {
30
- content: [
31
- {
32
- type: 'text',
33
- text: 'Error: API Key is not configured. Please set AICONNECT_API_KEY environment variable.'
34
- }
35
- ]
36
- };
37
- }
38
- const endpoint = `${apiUrl}/organizations/${org_id}/agent-jobs-type/${job_type_id}`;
39
- const headers = {
40
- Authorization: `Bearer ${apiKey}`
41
- };
24
+ const endpoint = `/organizations/${org_id}/agent-jobs-type/${job_type_id}`;
25
+ mcpDebugger.debug("Job type request", {
26
+ endpoint,
27
+ job_type_id,
28
+ org_id,
29
+ usingDefaultOrg: !params.org_id
30
+ });
42
31
  try {
43
- const response = await axios.get(endpoint, {
44
- headers
45
- });
46
- const jobType = response.data?.data || response.data;
47
- return {
32
+ const response = await withTiming(() => agentJobsClient.get(endpoint), "get_job_type API call");
33
+ mcpDebugger.debug("Job type raw response", { response });
34
+ // The API returns { data: {...}, meta: {...} } but agentJobsClient should extract data
35
+ // However, let's be defensive and handle both cases
36
+ const jobTypeData = response?.data ? response.data : response;
37
+ mcpDebugger.debug("Job type extracted data", { jobTypeData });
38
+ const result = {
48
39
  content: [
49
40
  {
50
41
  type: 'text',
51
- text: formatJobTypeDetails(jobType)
42
+ text: formatJobTypeDetails(jobTypeData)
52
43
  }
53
44
  ]
54
45
  };
46
+ mcpDebugger.toolResponse("get_job_type", {
47
+ job_type_id,
48
+ org_id,
49
+ resultLength: result.content[0].text.length,
50
+ hasData: !!jobTypeData
51
+ });
52
+ return result;
55
53
  }
56
54
  catch (error) {
57
- let errorMessage = `Failed to retrieve job type ${job_type_id}.`;
58
- let errorDetails = {};
59
- if (axios.isAxiosError(error) && error.response) {
60
- const apiError = error.response.data?.message ||
61
- error.response.data?.error ||
62
- JSON.stringify(error.response.data);
63
- errorMessage = `API Error (${error.response.status}): ${apiError || error.message}`;
64
- errorDetails = {
65
- status: error.response.status,
66
- data: error.response.data
67
- };
68
- }
69
- else if (error instanceof Error) {
70
- errorMessage = `Error: ${error.message}`;
71
- }
55
+ mcpDebugger.toolError("get_job_type", error);
72
56
  return {
73
57
  content: [
74
58
  {
75
59
  type: 'text',
76
- text: errorMessage
60
+ text: `Error getting job type: ${error.message}`
77
61
  }
78
62
  ]
79
63
  };
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import agentJobsClient from "../lib/agentJobsClient.js";
3
+ import { formatJobStats } from "../utils/formatters.js";
4
+ import { flexibleDateTimeSchema } from "../utils/schemas.js";
5
+ import { mcpDebugger, withTiming } from "../utils/debugger.js";
6
+ export default (server) => {
7
+ server.registerTool("get_jobs_stats", {
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.",
9
+ annotations: {
10
+ title: "Get Job Statistics"
11
+ },
12
+ inputSchema: {
13
+ org_id: z.string().optional().describe("Filter by organization ID."),
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)."),
18
+ job_type_id: z.string().optional().describe("Job type filter"),
19
+ channel_code: z.string().optional().describe("Channel filter"),
20
+ tags: z.string().optional().describe("Tags filter (comma-separated)"),
21
+ }
22
+ }, async (params) => {
23
+ mcpDebugger.toolCall("get_jobs_stats", params);
24
+ try {
25
+ const response = await withTiming(() => agentJobsClient.getStats(params), "get_jobs_stats API call");
26
+ const stats = response.meta?.stats || {};
27
+ mcpDebugger.debug("Raw API response", { stats, appliedFilters: params });
28
+ const result = {
29
+ content: [{
30
+ type: "text",
31
+ text: formatJobStats(stats, params),
32
+ }]
33
+ };
34
+ mcpDebugger.toolResponse("get_jobs_stats", result);
35
+ return result;
36
+ }
37
+ catch (error) {
38
+ mcpDebugger.toolError("get_jobs_stats", error);
39
+ return {
40
+ content: [{
41
+ type: "text",
42
+ text: `Error getting job stats: ${error.message}`,
43
+ }],
44
+ };
45
+ }
46
+ });
47
+ };
@@ -1,7 +1,8 @@
1
1
  import { z } from "zod";
2
- import axios from 'axios';
3
- import { config } from '../config.js';
2
+ import agentJobsClient from "../lib/agentJobsClient.js";
4
3
  import { formatJobList } from "../utils/formatters.js";
4
+ import { flexibleDateTimeSchema } from "../utils/schemas.js";
5
+ import { mcpDebugger, withTiming } from "../utils/debugger.js";
5
6
  // Define the schema for job status based on docs/agent-jobs-api.md:246-251
6
7
  const jobStatusSchema = z.enum([
7
8
  "waiting",
@@ -12,42 +13,28 @@ const jobStatusSchema = z.enum([
12
13
  "canceled"
13
14
  ]);
14
15
  export default (server) => {
15
- server.tool("list_jobs", "Retrieves a list of agent jobs, with optional filters and pagination.", {
16
- org_id: z.string().optional().describe("Filter by organization ID. If not provided, the default from the environment is used."),
17
- status: jobStatusSchema.optional().describe("Filter by job status. Possible values are: 'waiting', 'scheduled', 'running', 'completed', 'failed', 'canceled'."),
18
- scheduled_at: z.string().datetime().optional().describe("Filter by the exact scheduled time in ISO 8601 format (e.g., '2024-07-23T10:00:00Z')."),
19
- scheduled_at_gte: z.string().datetime().optional().describe("Filter for jobs scheduled at or after a specific time (ISO 8601)."),
20
- scheduled_at_lte: z.string().datetime().optional().describe("Filter for jobs scheduled at or before a specific time (ISO 8601)."),
21
- created_at_gte: z.string().datetime().optional().describe("Filter for jobs created at or after a specific time (ISO 8601)."),
22
- created_at_lte: z.string().datetime().optional().describe("Filter for jobs created at or before a specific time (ISO 8601)."),
23
- job_type_id: z.string().optional().describe("Filter by the specific job type ID (e.g., 'daily-report')."),
24
- channel_code: z.string().optional().describe("Filter by the channel code (e.g., 'C123456' for a Slack channel)."),
25
- limit: z.number().int().positive().optional().describe("Maximum number of jobs to return (e.g.,20). Default is 20."),
26
- offset: z.number().int().nonnegative().optional().describe("Number of jobs to skip, used for pagination. Default is 0."),
27
- sort: z.string().optional().describe("Field to sort by and direction. Format is 'field:direction'. Example: 'created_at:desc'."),
28
- }, async (params) => {
29
- const apiUrl = config.AICONNECT_API_URL;
30
- const apiKey = config.AICONNECT_API_KEY;
31
- if (!apiUrl) {
32
- return {
33
- content: [{
34
- type: "text",
35
- text: "Error: API URL is not configured. Please set AICONNECT_API_URL environment variable."
36
- }]
37
- };
16
+ server.registerTool("list_jobs", {
17
+ description: "Retrieves a list of agent jobs, with optional filters and pagination.",
18
+ annotations: {
19
+ title: "List Agent Jobs"
20
+ },
21
+ inputSchema: {
22
+ org_id: z.string().optional().describe("Filter by organization ID. If not provided, the default from the environment is used."),
23
+ 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)."),
29
+ job_type_id: z.string().optional().describe("Filter by the specific job type ID (e.g., 'daily-report')."),
30
+ channel_code: z.string().optional().describe("Filter by the channel code (e.g., 'C123456' for a Slack channel)."),
31
+ limit: z.number().int().positive().optional().describe("Maximum number of jobs to return (e.g.,20). Default is 20."),
32
+ 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'.")
38
34
  }
39
- if (!apiKey) {
40
- return {
41
- content: [{
42
- type: "text",
43
- text: "Error: API Key is not configured. Please set AICONNECT_API_KEY environment variable."
44
- }]
45
- };
46
- }
47
- const endpoint = `${apiUrl}/services/agent-jobs`;
48
- const headers = {
49
- "Authorization": `Bearer ${apiKey}`,
50
- };
35
+ }, async (params) => {
36
+ mcpDebugger.toolCall("list_jobs", params);
37
+ const endpoint = `/services/agent-jobs`;
51
38
  // Build query parameters object from provided params
52
39
  const queryParams = {};
53
40
  for (const [key, value] of Object.entries(params)) {
@@ -55,38 +42,37 @@ export default (server) => {
55
42
  queryParams[key] = value;
56
43
  }
57
44
  }
45
+ mcpDebugger.debug("Built query parameters", { endpoint, queryParams });
58
46
  try {
59
- const response = await axios.get(endpoint, {
60
- headers,
61
- params: queryParams,
47
+ const apiResponse = await withTiming(() => agentJobsClient.getWithMeta(endpoint, queryParams), "list_jobs API call");
48
+ // Access the full API response structure
49
+ const jobs = apiResponse.data || [];
50
+ const meta = apiResponse.meta || {};
51
+ mcpDebugger.debug("Raw API response", {
52
+ fullResponse: apiResponse,
53
+ jobsCount: jobs.length,
54
+ meta,
55
+ firstJob: jobs[0] || null
62
56
  });
63
- const jobs = response.data?.data || [];
64
- const meta = response.data?.meta || {};
65
- return {
57
+ const offset = typeof params.offset === 'number' ? params.offset : 0;
58
+ const result = {
66
59
  content: [{
67
60
  type: "text",
68
- text: formatJobList(jobs, meta),
61
+ text: formatJobList(jobs, meta, offset),
69
62
  }]
70
63
  };
64
+ mcpDebugger.toolResponse("list_jobs", {
65
+ jobsReturned: jobs.length,
66
+ resultLength: result.content[0].text.length
67
+ });
68
+ return result;
71
69
  }
72
70
  catch (error) {
73
- let errorMessage = `Failed to list jobs.`;
74
- let errorDetails = {};
75
- if (axios.isAxiosError(error) && error.response) {
76
- const apiError = error.response.data?.message || error.response.data?.error || JSON.stringify(error.response.data);
77
- errorMessage = `API Error (${error.response.status}): ${apiError || error.message}`;
78
- errorDetails = {
79
- status: error.response.status,
80
- data: error.response.data
81
- };
82
- }
83
- else if (error instanceof Error) {
84
- errorMessage = `Error: ${error.message}`;
85
- }
71
+ mcpDebugger.toolError("list_jobs", error);
86
72
  return {
87
73
  content: [{
88
74
  type: "text",
89
- text: errorMessage,
75
+ text: `Error listing jobs: ${error.message}`,
90
76
  }],
91
77
  };
92
78
  }