Get AppDynamics transaction snapshots
appd_get_transaction_snapshotsRetrieve slow, error, or diagnostic transaction snapshots for an application. Optionally include exit calls to feed dependency-map analysis.
Instructions
Retrieve slow/error/diagnostic transaction snapshots for an application. Default cap 100, hard max 600. Use needExitCalls=true to feed dependency-map analysis.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| application | Yes | ||
| timeRange | No | AppD time range. Defaults to BEFORE_NOW with durationMinutes=30 if omitted by the caller. | |
| businessTransactionIds | No | ||
| tierIds | No | ||
| nodeIds | No | ||
| slowOnly | No | Only include snapshots flagged slow (deep-dive eligible). | |
| errorOnly | No | Only include snapshots with errors. | |
| firstInChain | No | Only return originating snapshots (not children). | |
| needExitCalls | No | Include exit calls (DB/HTTP/queue) per snapshot. Required for dependency-map synthesis. | |
| needProps | No | Include data-collector property fields per snapshot. | |
| maxResults | No | How many snapshots to request (default 100, max 600 per docs). |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| summary | Yes | ||
| evidence | No | ||
| entities | Yes | ||
| timeRange | No | ||
| sourceEndpoints | Yes | ||
| pagination | No | ||
| warnings | Yes | ||
| truncated | Yes |
Implementation Reference
- The async handler function that executes the tool logic: builds query parameters, calls the controller API at /request-snapshots, and returns a formatted envelope with snapshot data and warnings.
>(services.log, 'appd_get_transaction_snapshots', async (input) => { const tr = input.timeRange ?? DEFAULT_TIME_RANGE; const params = applyTimeRangeToParams(tr); const max = input.maxResults ?? SNAPSHOT_DEFAULT; params.append('maximum-results', String(max)); if (input.businessTransactionIds?.length) { for (const id of input.businessTransactionIds) params.append('business-transaction-ids', String(id)); } if (input.tierIds?.length) { for (const id of input.tierIds) params.append('application-component-ids', String(id)); } if (input.nodeIds?.length) { for (const id of input.nodeIds) params.append('application-component-node-ids', String(id)); } if (input.slowOnly !== undefined) params.append('user-experience', input.slowOnly ? 'SLOW' : 'NORMAL'); if (input.errorOnly !== undefined) params.append('error-occurred', String(input.errorOnly)); if (input.firstInChain !== undefined) params.append('first-in-chain', String(input.firstInChain)); if (input.needExitCalls) params.append('need-exit-calls', 'true'); if (input.needProps) params.append('need-props', 'true'); const appPath = appRefToPath(input.application); const path = `applications/${appPath}/request-snapshots`; const res = await services.controller.get<Snapshot[]>(path, Object.fromEntries(params)); const snapshots = Array.isArray(res.body) ? res.body : []; const warnings = timeRangeWarnings(tr); const truncated = snapshots.length >= max && max < SNAPSHOT_HARD_CAP; if (truncated) { warnings.push( `Got exactly maxResults (${max}) snapshots — there may be more. Increase maxResults (cap ${SNAPSHOT_HARD_CAP}) or narrow the time window.`, ); } return toToolResult( buildEnvelope({ summary: `${snapshots.length} snapshot${snapshots.length === 1 ? '' : 's'} returned.`, evidence: { application: input.application, count: snapshots.length, snapshots, } as SnapshotsEvidence, entities: [{ kind: 'application', id: input.application }], timeRange: toEnvelopeTimeRange(tr), sourceEndpoints: [`GET /controller/rest/${path}`], warnings, truncated, }), ); }), - Input schema definition using Zod: validates application, timeRange, businessTransactionIds, tierIds, nodeIds, slowOnly, errorOnly, firstInChain, needExitCalls, needProps, and maxResults.
const inputShape = { application: appRefSchema, timeRange: timeRangeSchema.optional(), businessTransactionIds: z.array(z.number().int().positive()).optional(), tierIds: z.array(z.number().int().positive()).optional(), nodeIds: z.array(z.number().int().positive()).optional(), slowOnly: z.boolean().optional().describe('Only include snapshots flagged slow (deep-dive eligible).'), errorOnly: z.boolean().optional().describe('Only include snapshots with errors.'), firstInChain: z.boolean().optional().describe('Only return originating snapshots (not children).'), needExitCalls: z .boolean() .optional() .default(false) .describe('Include exit calls (DB/HTTP/queue) per snapshot. Required for dependency-map synthesis.'), needProps: z .boolean() .optional() .default(false) .describe('Include data-collector property fields per snapshot.'), maxResults: z .number() .int() .positive() .max(SNAPSHOT_HARD_CAP) .optional() .default(SNAPSHOT_DEFAULT) .describe( `How many snapshots to request (default ${SNAPSHOT_DEFAULT}, max ${SNAPSHOT_HARD_CAP} per docs).`, ), }; - src/tools/getTransactionSnapshots.ts:81-162 (registration)ToolRegistration object that defines the tool name 'appd_get_transaction_snapshots', profile 'read', and the register() function that calls server.registerTool with name, config, and handler.
export const getTransactionSnapshotsTool: ToolRegistration = { name: 'appd_get_transaction_snapshots', profile: 'read', register(server, services) { server.registerTool( 'appd_get_transaction_snapshots', { title: 'Get AppDynamics transaction snapshots', description: `Retrieve slow/error/diagnostic transaction snapshots for an application. Default cap ${SNAPSHOT_DEFAULT}, hard max ${SNAPSHOT_HARD_CAP}. Use needExitCalls=true to feed dependency-map analysis.`, inputSchema: inputShape, outputSchema: envelopeOutputShape, }, wrapHandler< { application: AppRef; timeRange?: TimeRange | undefined; businessTransactionIds?: number[] | undefined; tierIds?: number[] | undefined; nodeIds?: number[] | undefined; slowOnly?: boolean | undefined; errorOnly?: boolean | undefined; firstInChain?: boolean | undefined; needExitCalls?: boolean | undefined; needProps?: boolean | undefined; maxResults?: number | undefined; }, SnapshotsEvidence >(services.log, 'appd_get_transaction_snapshots', async (input) => { const tr = input.timeRange ?? DEFAULT_TIME_RANGE; const params = applyTimeRangeToParams(tr); const max = input.maxResults ?? SNAPSHOT_DEFAULT; params.append('maximum-results', String(max)); if (input.businessTransactionIds?.length) { for (const id of input.businessTransactionIds) params.append('business-transaction-ids', String(id)); } if (input.tierIds?.length) { for (const id of input.tierIds) params.append('application-component-ids', String(id)); } if (input.nodeIds?.length) { for (const id of input.nodeIds) params.append('application-component-node-ids', String(id)); } if (input.slowOnly !== undefined) params.append('user-experience', input.slowOnly ? 'SLOW' : 'NORMAL'); if (input.errorOnly !== undefined) params.append('error-occurred', String(input.errorOnly)); if (input.firstInChain !== undefined) params.append('first-in-chain', String(input.firstInChain)); if (input.needExitCalls) params.append('need-exit-calls', 'true'); if (input.needProps) params.append('need-props', 'true'); const appPath = appRefToPath(input.application); const path = `applications/${appPath}/request-snapshots`; const res = await services.controller.get<Snapshot[]>(path, Object.fromEntries(params)); const snapshots = Array.isArray(res.body) ? res.body : []; const warnings = timeRangeWarnings(tr); const truncated = snapshots.length >= max && max < SNAPSHOT_HARD_CAP; if (truncated) { warnings.push( `Got exactly maxResults (${max}) snapshots — there may be more. Increase maxResults (cap ${SNAPSHOT_HARD_CAP}) or narrow the time window.`, ); } return toToolResult( buildEnvelope({ summary: `${snapshots.length} snapshot${snapshots.length === 1 ? '' : 's'} returned.`, evidence: { application: input.application, count: snapshots.length, snapshots, } as SnapshotsEvidence, entities: [{ kind: 'application', id: input.application }], timeRange: toEnvelopeTimeRange(tr), sourceEndpoints: [`GET /controller/rest/${path}`], warnings, truncated, }), ); }), ); }, }; - src/tools/index.ts:33-38 (registration)Central registration: ALL_TOOLS array includes getTransactionSnapshotsTool, and registerAllTools delegates to registerTools which iterates and calls each tool's register method.
export function registerAllTools( server: McpServer, services: Services, ): { registered: string[]; skipped: string[] } { return registerTools(server, services, ALL_TOOLS); } - src/tools/_common.ts:28-62 (helper)wrapHandler function used by the tool to catch HttpErrors and unexpected errors, returning structured error envelopes instead of crashing.
export function wrapHandler<I, R>( log: Logger, toolName: string, handler: (input: I) => Promise<DualToolResult<R>>, ): (input: I) => Promise<DualToolResult<R> | (DualToolResult<R> & { isError: true })> { return async (input) => { try { return await handler(input); } catch (err) { if (err instanceof HttpError) { log.warn( { tool: toolName, kind: err.kind, statusCode: err.statusCode, sourceEndpoint: err.sourceEndpoint, }, 'tool: HttpError', ); return toErrorResult({ summary: `${toolName} failed: ${err.kind}${err.statusCode ? ` (HTTP ${err.statusCode})` : ''}. ${err.hint ?? ''}`.trim(), evidence: { kind: err.kind, message: err.message, hint: err.hint } as unknown as R, sourceEndpoints: [err.sourceEndpoint], }); } log.error({ tool: toolName, err }, 'tool: unexpected error'); const message = err instanceof Error ? err.message : String(err); return toErrorResult({ summary: `${toolName} failed: unexpected error: ${message}`, evidence: { kind: 'internal', message } as unknown as R, }); } }; }