Skip to content

Commit 5a62937

Browse files
authored
Merge pull request #15 from railsware/CPL-20880/provide-guidance-for-errors
[CPL-20880] Provide guidance for errors
2 parents 047bcbb + 2adcd86 commit 5a62937

File tree

5 files changed

+74
-8
lines changed

5 files changed

+74
-8
lines changed

src/tools/get-dataflow/handler.test.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ const mockGetDataflowError = createMockResponse(
2323
async () => new Response('Error when getting dataflow', { status: 500 })
2424
)
2525

26+
const mockGetDataflowErrorWithDetails = createMockResponse(
27+
async () => new Response(JSON.stringify({ error: { message: 'Test error message' } }), { status: 500 })
28+
)
29+
2630
const mockFetch = vi.spyOn(globalThis, 'fetch')
2731

2832
describe('get-dataflow', () => {
@@ -63,7 +67,22 @@ describe('get-dataflow', () => {
6367
isError: true,
6468
content: [{
6569
type: 'text',
66-
text: 'Failed to get data flow gsheet_dataflow. Response status: 500',
70+
text: 'Failed to get data flow gsheet_dataflow. Response status: 500.',
71+
}]
72+
})
73+
})
74+
75+
it('includes error details when present', async () => {
76+
mockFetch
77+
.mockImplementationOnce(mockGetDataflowErrorWithDetails)
78+
79+
const toolResult = await handler({ dataflowId: 'gsheet_dataflow' })
80+
81+
expect(toolResult).toEqual({
82+
isError: true,
83+
content: [{
84+
type: 'text',
85+
text: 'Failed to get data flow gsheet_dataflow. Response status: 500. Error details: Test error message',
6786
}]
6887
})
6988
})

src/tools/get-dataflow/handler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fromError } from 'zod-validation-error'
33

44
import { zodInputSchema } from './schema.js'
55

6-
import { textResponse } from '../../util/tool-response.js'
6+
import { textResponse, buildErrorMessage } from '../../util/tool-response.js'
77
import { COUPLER_ACCESS_TOKEN } from '../../env.js'
88
import { CouplerioClient } from '../../lib/couplerio-client/index.js'
99
import { logger } from '../../logger/index.js'
@@ -33,10 +33,12 @@ export const handler = async (params?: Record<string, unknown>): Promise<CallToo
3333
})
3434

3535
if (!response.ok) {
36-
logger.error(`Failed to get data flow ${validationResult.data.dataflowId}. Response status: ${response.status}`)
36+
const errorText = await buildErrorMessage( { response, customText: `Failed to get data flow ${validationResult.data.dataflowId}.`})
37+
38+
logger.error(errorText)
3739
return textResponse({
3840
isError: true,
39-
text: `Failed to get data flow ${validationResult.data.dataflowId}. Response status: ${response.status}`
41+
text: errorText
4042
})
4143
}
4244

src/tools/list-dataflows/handler.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ const mockListDataflowsError = createMockResponse(
2323
async () => new Response('Error listing dataflows', { status: 500 })
2424
)
2525

26+
const mockGetDataflowErrorWithDetails = createMockResponse(
27+
async () => new Response(
28+
JSON.stringify({ error: { message: 'Test error message' } }), { status: 500 }
29+
)
30+
)
31+
2632
const mockFetch = vi.spyOn(globalThis, 'fetch')
2733

2834
describe('listDataflows', () => {
@@ -63,7 +69,22 @@ describe('listDataflows', () => {
6369
isError: true,
6470
content: [{
6571
type: 'text',
66-
text: 'Failed to list data flows. Response status: 500',
72+
text: 'Failed to list data flows. Response status: 500.',
73+
}]
74+
})
75+
})
76+
77+
it('includes error details when present', async () => {
78+
mockFetch
79+
.mockImplementationOnce(mockGetDataflowErrorWithDetails)
80+
81+
const toolResult = await handler()
82+
83+
expect(toolResult).toEqual({
84+
isError: true,
85+
content: [{
86+
type: 'text',
87+
text: 'Failed to list data flows. Response status: 500. Error details: Test error message',
6788
}]
6889
})
6990
})

src/tools/list-dataflows/handler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
22

3-
import { textResponse } from '../../util/tool-response.js'
3+
import { textResponse, buildErrorMessage } from '../../util/tool-response.js'
44
import { COUPLER_ACCESS_TOKEN } from '../../env.js'
55
import { CouplerioClient } from '../../lib/couplerio-client/index.js'
66
import { logger } from '../../logger/index.js'
@@ -12,10 +12,12 @@ export const handler = async (): Promise<CallToolResult> => {
1212
const response = await coupler.request(`/dataflows?${query}{?type}`)
1313

1414
if (!response.ok) {
15-
logger.error(`Failed to list dataflows. Response status: ${response.status}`)
15+
const errorText = await buildErrorMessage({ response, customText: 'Failed to list data flows.'})
16+
17+
logger.error(errorText)
1618
return textResponse({
1719
isError: true,
18-
text: `Failed to list data flows. Response status: ${response.status}`
20+
text: errorText
1921
})
2022
}
2123

src/util/tool-response.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
2+
import { logger } from '../logger/index.js'
23

34
export const textResponse = ({ text, isError = false, structuredContent }: { text: string, isError?: boolean, structuredContent?: Record<string, unknown> }) => {
45
const callToolResult: CallToolResult = {
@@ -17,3 +18,24 @@ export const textResponse = ({ text, isError = false, structuredContent }: { tex
1718

1819
return callToolResult
1920
}
21+
22+
export const buildErrorMessage = async ({
23+
response,
24+
customText = 'An unexpected error occurred.',
25+
}: {
26+
response: Response,
27+
customText: string,
28+
}): Promise<string> => {
29+
let errorDetails = ''
30+
31+
try {
32+
const { error } = await response.json() as { error?: { message?: string } }
33+
errorDetails = error?.message ?? ''
34+
} catch (err){
35+
// Does not update for JSON parse errors
36+
logger.error('Failed to parse JSON response', err)
37+
}
38+
39+
return `${customText} Response status: ${response.status}.` +
40+
(errorDetails ? ` Error details: ${errorDetails}` : '')
41+
}

0 commit comments

Comments
 (0)