@amplitude/ai 0.2.1 → 0.3.1

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.
Files changed (107) hide show
  1. package/.claude/commands/instrument-with-amplitude-ai.md +12 -0
  2. package/.cursor/skills/instrument-with-amplitude-ai/SKILL.md +4 -42
  3. package/AGENTS.md +86 -28
  4. package/README.md +190 -111
  5. package/amplitude-ai.md +463 -0
  6. package/bin/amplitude-ai-doctor.mjs +0 -0
  7. package/bin/amplitude-ai-instrument.mjs +0 -0
  8. package/bin/amplitude-ai-mcp.mjs +0 -0
  9. package/bin/amplitude-ai-register-catalog.mjs +0 -0
  10. package/bin/amplitude-ai-status.mjs +0 -0
  11. package/bin/amplitude-ai.mjs +14 -5
  12. package/data/agent_event_catalog.json +52 -2
  13. package/dist/bound-agent.d.ts +18 -14
  14. package/dist/bound-agent.d.ts.map +1 -1
  15. package/dist/bound-agent.js +4 -1
  16. package/dist/bound-agent.js.map +1 -1
  17. package/dist/cli/status.d.ts.map +1 -1
  18. package/dist/cli/status.js +31 -19
  19. package/dist/cli/status.js.map +1 -1
  20. package/dist/client.d.ts +14 -14
  21. package/dist/client.d.ts.map +1 -1
  22. package/dist/client.js +38 -0
  23. package/dist/client.js.map +1 -1
  24. package/dist/context.d.ts +5 -0
  25. package/dist/context.d.ts.map +1 -1
  26. package/dist/context.js +4 -0
  27. package/dist/context.js.map +1 -1
  28. package/dist/core/constants.d.ts +3 -1
  29. package/dist/core/constants.d.ts.map +1 -1
  30. package/dist/core/constants.js +3 -1
  31. package/dist/core/constants.js.map +1 -1
  32. package/dist/core/tracking.d.ts +12 -2
  33. package/dist/core/tracking.d.ts.map +1 -1
  34. package/dist/core/tracking.js +13 -2
  35. package/dist/core/tracking.js.map +1 -1
  36. package/dist/decorators.d.ts +1 -1
  37. package/dist/decorators.d.ts.map +1 -1
  38. package/dist/decorators.js +4 -3
  39. package/dist/decorators.js.map +1 -1
  40. package/dist/index.d.ts +7 -4
  41. package/dist/index.js +5 -2
  42. package/dist/mcp/contract.d.ts +4 -0
  43. package/dist/mcp/contract.d.ts.map +1 -1
  44. package/dist/mcp/contract.js +6 -2
  45. package/dist/mcp/contract.js.map +1 -1
  46. package/dist/mcp/generate-verify-test.d.ts +7 -0
  47. package/dist/mcp/generate-verify-test.d.ts.map +1 -0
  48. package/dist/mcp/generate-verify-test.js +33 -0
  49. package/dist/mcp/generate-verify-test.js.map +1 -0
  50. package/dist/mcp/index.d.ts +2 -1
  51. package/dist/mcp/index.js +2 -1
  52. package/dist/mcp/instrument-file.d.ts +14 -0
  53. package/dist/mcp/instrument-file.d.ts.map +1 -0
  54. package/dist/mcp/instrument-file.js +136 -0
  55. package/dist/mcp/instrument-file.js.map +1 -0
  56. package/dist/mcp/scan-project.d.ts +52 -0
  57. package/dist/mcp/scan-project.d.ts.map +1 -0
  58. package/dist/mcp/scan-project.js +309 -0
  59. package/dist/mcp/scan-project.js.map +1 -0
  60. package/dist/mcp/server.d.ts.map +1 -1
  61. package/dist/mcp/server.js +79 -4
  62. package/dist/mcp/server.js.map +1 -1
  63. package/dist/mcp/validate-file.d.ts +4 -0
  64. package/dist/mcp/validate-file.d.ts.map +1 -1
  65. package/dist/mcp/validate-file.js +559 -11
  66. package/dist/mcp/validate-file.js.map +1 -1
  67. package/dist/middleware.js +2 -1
  68. package/dist/middleware.js.map +1 -1
  69. package/dist/node_modules/.pnpm/acorn-typescript@1.4.13_acorn@8.16.0/node_modules/acorn-typescript/lib/index.js +2389 -0
  70. package/dist/node_modules/.pnpm/acorn-typescript@1.4.13_acorn@8.16.0/node_modules/acorn-typescript/lib/index.js.map +1 -0
  71. package/dist/node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.js +5128 -0
  72. package/dist/node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.js.map +1 -0
  73. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js +1 -1
  74. package/dist/propagation.d.ts.map +1 -1
  75. package/dist/providers/anthropic.d.ts.map +1 -1
  76. package/dist/providers/anthropic.js +1 -0
  77. package/dist/providers/anthropic.js.map +1 -1
  78. package/dist/providers/base.d.ts +2 -1
  79. package/dist/providers/base.d.ts.map +1 -1
  80. package/dist/providers/base.js +4 -0
  81. package/dist/providers/base.js.map +1 -1
  82. package/dist/providers/gemini.d.ts.map +1 -1
  83. package/dist/providers/mistral.d.ts.map +1 -1
  84. package/dist/providers/openai.d.ts.map +1 -1
  85. package/dist/providers/openai.js +2 -0
  86. package/dist/providers/openai.js.map +1 -1
  87. package/dist/serverless.d.ts +19 -0
  88. package/dist/serverless.d.ts.map +1 -0
  89. package/dist/serverless.js +35 -0
  90. package/dist/serverless.js.map +1 -0
  91. package/dist/session.d.ts +24 -8
  92. package/dist/session.d.ts.map +1 -1
  93. package/dist/session.js +20 -1
  94. package/dist/session.js.map +1 -1
  95. package/dist/types.d.ts +1 -0
  96. package/dist/types.d.ts.map +1 -1
  97. package/dist/types.js.map +1 -1
  98. package/dist/utils/logger.d.ts.map +1 -1
  99. package/llms-full.txt +353 -69
  100. package/llms.txt +6 -2
  101. package/mcp.schema.json +7 -3
  102. package/package.json +10 -5
  103. package/bin/amplitude-ai-init.mjs +0 -27
  104. package/dist/cli/init.d.ts +0 -14
  105. package/dist/cli/init.d.ts.map +0 -1
  106. package/dist/cli/init.js +0 -40
  107. package/dist/cli/init.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-project.js","names":["FRAMEWORK_DEPS: Array<[string, string]>","entries: string[]","files: string[]","framework: string | null","packageManager: string | null","CONSTRUCTOR_TO_PROVIDER: Record<string, string>","agents: ScanResult['agents']","content: string","multiAgentSignals: string[]","recommendedTier: ScanResult['recommended_tier']","recommendations: string[]"],"sources":["../../src/mcp/scan-project.ts"],"sourcesContent":["import {\n readFileSync,\n existsSync,\n readdirSync,\n statSync,\n} from 'node:fs';\nimport { join, relative, basename, dirname } from 'node:path';\nimport { analyzeFileInstrumentation } from './validate-file.js';\n\nexport interface ScanResult {\n project_name: string | null;\n runtime: 'node';\n language: 'typescript' | 'javascript';\n framework: string | null;\n providers: string[];\n agent_frameworks: string[];\n package_manager: string | null;\n existing_instrumentation: {\n has_amplitude_ai: boolean;\n has_patch: boolean;\n has_wrappers: boolean;\n has_session_context: boolean;\n };\n agents: Array<{\n inferred_id: string;\n file: string;\n call_sites: number;\n is_instrumented: boolean;\n is_route_handler: boolean;\n inferred_description: string | null;\n call_site_details: Array<{\n line: number;\n provider: string;\n api: string;\n instrumented: boolean;\n containing_function: string | null;\n code_context: string;\n }>;\n tool_definitions: string[];\n function_definitions: string[];\n }>;\n total_call_sites: number;\n instrumented_call_sites: number;\n uninstrumented_call_sites: number;\n is_multi_agent: boolean;\n multi_agent_signals: string[];\n has_streaming: boolean;\n has_vercel_ai_sdk: boolean;\n has_edge_runtime: boolean;\n has_assistants_api: boolean;\n has_langgraph: boolean;\n message_queue_deps: string[];\n has_frontend_deps: boolean;\n recommendations: string[];\n recommended_tier: 'quick_start' | 'standard' | 'advanced';\n}\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);\nconst SKIP_DIRS = new Set([\n 'node_modules',\n 'dist',\n '.next',\n '.git',\n '__tests__',\n 'test',\n]);\nconst MAX_DEPTH = 5;\n\nconst FRAMEWORK_DEPS: Array<[string, string]> = [\n ['next', 'nextjs'],\n ['express', 'express'],\n ['fastify', 'fastify'],\n ['hono', 'hono'],\n ['@remix-run/node', 'remix'],\n];\n\nconst PROVIDER_DEPS = [\n 'openai',\n '@anthropic-ai/sdk',\n '@google/generative-ai',\n '@google/genai',\n '@aws-sdk/client-bedrock-runtime',\n '@mistralai/mistralai',\n '@azure/openai',\n 'cohere-ai',\n];\n\nconst AGENT_FRAMEWORK_DEPS = [\n 'langchain',\n '@langchain/core',\n 'llamaindex',\n '@openai/agents',\n 'crewai',\n];\n\nconst MESSAGE_QUEUE_DEPS = [\n 'bullmq',\n 'bull',\n 'ioredis',\n 'amqplib',\n '@aws-sdk/client-sqs',\n 'kafkajs',\n '@google-cloud/pubsub',\n];\n\nconst FRONTEND_DEPS = [\n 'react',\n 'vue',\n 'svelte',\n '@sveltejs/kit',\n 'angular',\n '@angular/core',\n];\n\n// Tightened to LLM-specific contexts: stream:true near model/messages, or Vercel AI SDK fns\nconst STREAMING_RE = /streamText\\s*\\(|useChat\\s*\\(|stream\\s*:\\s*true/;\n\nconst VERCEL_AI_SDK_DEPS = ['ai', '@ai-sdk/openai', '@ai-sdk/anthropic', '@ai-sdk/google', '@ai-sdk/mistral'];\nconst VERCEL_AI_SDK_RE = /\\b(?:streamText|generateText|streamObject|generateObject|useChat|useCompletion|useAssistant)\\s*\\(/;\n\nconst EDGE_RUNTIME_RE = /runtime\\s*=\\s*['\"]edge['\"]/;\n\nconst ASSISTANTS_API_RE = /\\.beta\\.(?:threads|assistants)\\./;\n\nconst LANGGRAPH_DEPS = ['@langchain/langgraph'];\n\nconst MULTI_AGENT_CODE_RE = /\\.child\\s*\\(|\\.runAs\\s*\\(|\\.runAsSync\\s*\\(/;\n\nconst ROUTE_HANDLER_RE =\n /export\\s+async\\s+function\\s+(?:POST|GET|PUT|DELETE)\\b|app\\.\\s*(?:get|post|put|delete)\\s*\\(|router\\./;\n\nfunction collectSourceFiles(\n dir: string,\n rootPath: string,\n depth: number,\n): string[] {\n if (depth > MAX_DEPTH) return [];\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of entries) {\n if (SKIP_DIRS.has(entry)) continue;\n const fullPath = join(dir, entry);\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n files.push(...collectSourceFiles(fullPath, rootPath, depth + 1));\n } else if (stat.isFile()) {\n const ext = entry.slice(entry.lastIndexOf('.'));\n if (SOURCE_EXTENSIONS.has(ext)) {\n files.push(fullPath);\n }\n }\n }\n return files;\n}\n\nfunction inferAgentId(filePath: string): string {\n const base = basename(filePath).replace(/\\.[^.]+$/, '');\n if (base === 'route' || base === 'index') {\n const parentDir = basename(dirname(filePath));\n return parentDir || base;\n }\n return base;\n}\n\nfunction inferDescription(\n filePath: string,\n isRouteHandler: boolean,\n): string | null {\n const agentId = inferAgentId(filePath);\n if (isRouteHandler) {\n return `Route handler agent: ${agentId}`;\n }\n if (/agent|worker|service/i.test(filePath)) {\n return `Background agent: ${agentId}`;\n }\n return null;\n}\n\nfunction readPackageJson(\n rootPath: string,\n): {\n name: string | null;\n allDeps: Set<string>;\n} {\n const pkgPath = join(rootPath, 'package.json');\n if (!existsSync(pkgPath)) {\n return { name: null, allDeps: new Set() };\n }\n try {\n const raw = readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n const name = typeof pkg.name === 'string' ? pkg.name : null;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const allDeps = new Set([...Object.keys(deps), ...Object.keys(devDeps)]);\n return { name, allDeps };\n } catch {\n return { name: null, allDeps: new Set() };\n }\n}\n\nexport function scanProject(rootPath: string): ScanResult {\n const { name: projectName, allDeps } = readPackageJson(rootPath);\n\n // Detect framework\n let framework: string | null = null;\n for (const [dep, label] of FRAMEWORK_DEPS) {\n if (allDeps.has(dep)) {\n framework = label;\n break;\n }\n }\n\n // Detect LLM providers\n const providers = PROVIDER_DEPS.filter((dep) => allDeps.has(dep));\n\n // Detect agent frameworks\n const agentFrameworks = AGENT_FRAMEWORK_DEPS.filter((dep) =>\n allDeps.has(dep),\n );\n\n // Detect package manager\n let packageManager: string | null = null;\n if (existsSync(join(rootPath, 'pnpm-lock.yaml'))) {\n packageManager = 'pnpm';\n } else if (existsSync(join(rootPath, 'yarn.lock'))) {\n packageManager = 'yarn';\n } else if (existsSync(join(rootPath, 'package-lock.json'))) {\n packageManager = 'npm';\n }\n\n // Detect TypeScript\n const isTypeScript = existsSync(join(rootPath, 'tsconfig.json'));\n\n // Detect existing Amplitude instrumentation from deps\n const hasAmplitudeAiDep = allDeps.has('@amplitude/ai');\n\n // Walk source files and analyze\n const sourceFiles = collectSourceFiles(rootPath, rootPath, 0);\n\n let totalCallSites = 0;\n let instrumentedCallSites = 0;\n let uninstrumentedCallSites = 0;\n let globalHasPatch = false;\n let globalHasSessionContext = false;\n let globalHasAmplitudeImport = hasAmplitudeAiDep;\n let hasStreaming = false;\n let hasVercelAiSdkUsage = false;\n let hasEdgeRuntime = false;\n let hasAssistantsApi = false;\n let hasMultiAgentCodePatterns = false;\n\n // Per-provider tracking: which providers have wrappers globally\n const globalWrappedProviders = new Set<string>();\n\n const CONSTRUCTOR_TO_PROVIDER: Record<string, string> = {\n OpenAI: 'openai', Anthropic: 'anthropic', Gemini: 'gemini',\n GoogleGenerativeAI: 'gemini', GoogleGenAI: 'gemini',\n AzureOpenAI: 'azure-openai', Bedrock: 'bedrock',\n Mistral: 'mistral', CohereClient: 'cohere',\n };\n\n const agents: ScanResult['agents'] = [];\n const filesWithCallSites = new Set<string>();\n const providerImportsPerFile = new Map<string, Set<string>>();\n\n for (const filePath of sourceFiles) {\n let content: string;\n try {\n content = readFileSync(filePath, 'utf8');\n } catch {\n continue;\n }\n\n const analysis = analyzeFileInstrumentation(content);\n if (analysis.has_amplitude_import) globalHasAmplitudeImport = true;\n if (analysis.has_session_context) globalHasSessionContext = true;\n\n if (/\\bpatch\\s*\\(\\s*\\{/.test(content)) globalHasPatch = true;\n // Only flag streaming if the file also has LLM call sites (two-pass approach)\n if (STREAMING_RE.test(content) && analysis.total_call_sites > 0) hasStreaming = true;\n if (VERCEL_AI_SDK_RE.test(content)) hasVercelAiSdkUsage = true;\n if (EDGE_RUNTIME_RE.test(content)) hasEdgeRuntime = true;\n if (ASSISTANTS_API_RE.test(content)) hasAssistantsApi = true;\n if (MULTI_AGENT_CODE_RE.test(content)) hasMultiAgentCodePatterns = true;\n\n // Track wrapped providers: detect which specific provider is wrapped\n if (analysis.has_amplitude_import || /\\bamplitude\\s*:/.test(content)) {\n const constructorRe = /new\\s+(OpenAI|Anthropic|Gemini|AzureOpenAI|Bedrock|Mistral|GoogleGenerativeAI|GoogleGenAI|CohereClient)\\s*\\(/g;\n for (const m of content.matchAll(constructorRe)) {\n const ctorName = m[1] ?? '';\n const provLabel = CONSTRUCTOR_TO_PROVIDER[ctorName];\n if (provLabel && /\\bamplitude\\s*:/.test(content)) {\n globalWrappedProviders.add(provLabel);\n }\n }\n if (/\\.wrap\\s*\\(/.test(content)) {\n // wrap() can wrap any provider; attribute based on which providers are in this file\n for (const site of analysis.call_sites) {\n globalWrappedProviders.add(site.provider);\n }\n }\n }\n\n // Track provider imports per file for multi-agent signal\n const fileProviders = new Set<string>();\n for (const site of analysis.call_sites) {\n fileProviders.add(site.provider);\n }\n if (fileProviders.size > 0) {\n const relPath = relative(rootPath, filePath);\n providerImportsPerFile.set(relPath, fileProviders);\n }\n\n if (analysis.total_call_sites > 0) {\n const relPath = relative(rootPath, filePath);\n filesWithCallSites.add(relPath);\n\n totalCallSites += analysis.total_call_sites;\n instrumentedCallSites += analysis.instrumented;\n uninstrumentedCallSites += analysis.uninstrumented;\n\n const isRouteHandler = ROUTE_HANDLER_RE.test(content);\n const inferredId = inferAgentId(filePath);\n const allInstrumented = analysis.uninstrumented === 0;\n\n agents.push({\n inferred_id: inferredId,\n file: relPath,\n call_sites: analysis.total_call_sites,\n is_instrumented: allInstrumented,\n is_route_handler: isRouteHandler,\n inferred_description: inferDescription(filePath, isRouteHandler),\n call_site_details: analysis.call_sites.map((cs) => ({\n line: cs.line,\n provider: cs.provider,\n api: cs.api,\n instrumented: cs.instrumented,\n containing_function: cs.containing_function,\n code_context: cs.code_context,\n })),\n tool_definitions: analysis.tool_definitions,\n function_definitions: analysis.function_definitions,\n });\n }\n }\n\n const globalHasWrappers = globalWrappedProviders.size > 0;\n\n // Cross-file wrapper propagation (per-provider): only upgrade an agent's\n // instrumentation status if ALL of its call-site providers are wrapped globally.\n if (globalHasWrappers && globalHasAmplitudeImport) {\n for (const agent of agents) {\n if (!agent.is_instrumented) {\n const agentProviders = new Set(agent.call_site_details.map((cs) => cs.provider));\n const allCovered = [...agentProviders].every((p) => globalWrappedProviders.has(p));\n if (allCovered) {\n agent.is_instrumented = true;\n const delta = agent.call_sites;\n uninstrumentedCallSites -= delta;\n instrumentedCallSites += delta;\n }\n }\n }\n }\n\n // Multi-agent signals: collect raw evidence for AI skill to reason over.\n // is_multi_agent is conservative — only set when SDK delegation APIs are found.\n // The AI skill uses multi_agent_signals to perform the real semantic determination.\n const multiAgentSignals: string[] = [];\n\n const multipleFilesWithCalls = filesWithCallSites.size > 1;\n if (multipleFilesWithCalls) {\n multiAgentSignals.push(\n `LLM call sites found in ${filesWithCallSites.size} separate files: ${[...filesWithCallSites].join(', ')}`,\n );\n }\n\n if (agentFrameworks.length > 0) {\n multiAgentSignals.push(`Agent framework dependencies detected: ${agentFrameworks.join(', ')}`);\n }\n\n const allProviders = new Set<string>();\n for (const provSet of providerImportsPerFile.values()) {\n for (const p of provSet) allProviders.add(p);\n }\n if (allProviders.size > 1) {\n multiAgentSignals.push(`Multiple LLM providers in use: ${[...allProviders].join(', ')}`);\n }\n\n // Check if any file's tool_definitions reference functions that also contain LLM calls\n for (const agent of agents) {\n const toolNames = agent.tool_definitions;\n const callFunctions = agent.call_site_details\n .map((cs) => cs.containing_function)\n .filter(Boolean) as string[];\n const overlapping = toolNames.filter((t) => callFunctions.includes(t));\n if (overlapping.length > 0) {\n multiAgentSignals.push(\n `File ${agent.file}: tool definitions ${overlapping.join(', ')} also contain LLM calls — possible delegation-as-tools pattern`,\n );\n }\n }\n\n if (hasMultiAgentCodePatterns) {\n multiAgentSignals.push(\n 'Amplitude AI SDK delegation APIs detected: .child() or .runAs() — multi-agent confirmed',\n );\n }\n\n // is_multi_agent is true only when SDK APIs are found; broader signals are in multi_agent_signals\n const isMultiAgent = hasMultiAgentCodePatterns;\n\n // Message queue deps (cross-service signal)\n const messageQueueDeps = MESSAGE_QUEUE_DEPS.filter((dep) =>\n allDeps.has(dep),\n );\n\n // Frontend deps (browser-server linking signal)\n const hasFrontendDeps = FRONTEND_DEPS.some((dep) => allDeps.has(dep));\n\n // Vercel AI SDK detection (dep-level)\n const hasVercelAiSdk = VERCEL_AI_SDK_DEPS.some((dep) => allDeps.has(dep)) || hasVercelAiSdkUsage;\n\n // LangGraph detection\n const hasLanggraph = LANGGRAPH_DEPS.some((dep) => allDeps.has(dep));\n\n // Recommended tier: always default to advanced for maximum instrumentation.\n // Lower tiers exist as fallbacks, not as recommended starting points.\n const recommendedTier: ScanResult['recommended_tier'] = 'advanced';\n\n // Contextual recommendations\n const recommendations: string[] = [];\n if (hasStreaming) {\n recommendations.push(\n 'Streaming detected: keep sessions open until stream is fully consumed. ' +\n 'Use session.run() with an awaited stream, not a fire-and-forget pattern.',\n );\n }\n if (messageQueueDeps.length > 0) {\n recommendations.push(\n `Message queue deps detected (${messageQueueDeps.join(', ')}): enable propagateContext in the bootstrap file and use injectContext/extractContext to correlate events across services.`,\n );\n }\n if (hasFrontendDeps) {\n recommendations.push(\n 'Frontend framework detected alongside backend: pass browserSessionId and deviceId ' +\n 'from frontend request headers to agent.session() for session replay linking.',\n );\n }\n if (hasVercelAiSdk) {\n recommendations.push(\n 'Vercel AI SDK detected. Provider wrappers instrument the underlying provider SDK (openai, ' +\n '@anthropic-ai/sdk), not the Vercel AI SDK abstraction layer (streamText, generateText). ' +\n 'If you also have the underlying provider as a direct dependency, wrappers will work because ' +\n 'Vercel AI SDK delegates to them internally. Otherwise, use Tier 1 (patch) which intercepts ' +\n 'at the transport level, or add the underlying provider SDK as a direct dependency.',\n );\n }\n if (hasEdgeRuntime) {\n recommendations.push(\n 'Edge Runtime detected. session.run() relies on AsyncLocalStorage which may not be available ' +\n 'in Edge Runtime or Cloudflare Workers. Use explicit context passing instead of session.run(): ' +\n 'call agent.trackUserMessage() and agent.trackAiMessage() directly with sessionId parameter.',\n );\n }\n if (hasAssistantsApi) {\n recommendations.push(\n 'OpenAI Assistants API detected (client.beta.threads/assistants). Provider wrappers do not ' +\n 'auto-instrument the Assistants API. Use manual tracking with trackUserMessage/trackAiMessage, ' +\n 'or migrate to the OpenAI Agents SDK which supports AmplitudeTracingProcessor.',\n );\n }\n if (hasLanggraph) {\n recommendations.push(\n 'LangGraph detected. LLM calls within LangGraph are captured via the LangChain ' +\n 'AmplitudeCallbackHandler, but graph orchestration events (node transitions, checkpoints, ' +\n 'human-in-the-loop) are not yet instrumented.',\n );\n }\n\n if (globalHasAmplitudeImport && globalHasWrappers && globalHasSessionContext) {\n recommendations.push(\n 'Project already has @amplitude/ai instrumentation with wrappers and session context. ' +\n 'No re-instrumentation needed. Consider upgrading contentMode tiers, adding scoring, ' +\n 'or expanding multi-agent coverage if applicable.',\n );\n }\n\n return {\n project_name: projectName,\n runtime: 'node',\n language: isTypeScript ? 'typescript' : 'javascript',\n framework,\n providers,\n agent_frameworks: agentFrameworks,\n package_manager: packageManager,\n existing_instrumentation: {\n has_amplitude_ai: globalHasAmplitudeImport,\n has_patch: globalHasPatch,\n has_wrappers: globalHasWrappers,\n has_session_context: globalHasSessionContext,\n },\n agents,\n total_call_sites: totalCallSites,\n instrumented_call_sites: instrumentedCallSites,\n uninstrumented_call_sites: uninstrumentedCallSites,\n is_multi_agent: isMultiAgent,\n multi_agent_signals: multiAgentSignals,\n has_streaming: hasStreaming,\n has_vercel_ai_sdk: hasVercelAiSdk,\n has_edge_runtime: hasEdgeRuntime,\n has_assistants_api: hasAssistantsApi,\n has_langgraph: hasLanggraph,\n message_queue_deps: messageQueueDeps,\n has_frontend_deps: hasFrontendDeps,\n recommendations,\n recommended_tier: recommendedTier,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/mcp/server.ts"],"sourcesContent":[],"mappings":";;;KAqCK,WAAA;cAaC,iFAGS;AAlDqD,cA2H9D,YAzFU,EAAA,GAAA,GAyFS,SAzFT;AAAA,cAqaV,YAxZA,EAAA,GAAA,GAwZyB,OAhW9B,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/mcp/server.ts"],"sourcesContent":[],"mappings":";;;KA0DK,WAAA;cAaC,iFAGS;AAvEqD,cAgJ9D,YAzFU,EAAA,GAAA,GAyFS,SAzFT;AAAA,cA0hBV,YA7gBA,EAAA,GAAA,GA6gByB,OArd9B,CAAA,IAAA,CArDc"}
@@ -1,9 +1,12 @@
1
1
  import { MCP_PROMPTS, MCP_RESOURCES, MCP_SERVER_NAME, MCP_TOOLS } from "./contract.js";
2
+ import { generateVerifyTest } from "./generate-verify-test.js";
3
+ import { instrumentFile } from "./instrument-file.js";
2
4
  import { getIntegrationPatterns } from "./patterns.js";
3
- import { enumType, numberType, stringType } from "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js";
5
+ import { analyzeFileInstrumentation } from "./validate-file.js";
6
+ import { scanProject } from "./scan-project.js";
7
+ import { arrayType, enumType, numberType, stringType } from "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js";
4
8
  import { McpServer } from "../node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js";
5
9
  import { StdioServerTransport } from "../node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js";
6
- import { analyzeFileInstrumentation } from "./validate-file.js";
7
10
  import { readFileSync } from "node:fs";
8
11
  import { dirname, join } from "node:path";
9
12
  import { fileURLToPath } from "node:url";
@@ -11,12 +14,23 @@ import { fileURLToPath } from "node:url";
11
14
  //#region src/mcp/server.ts
12
15
  const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
13
16
  let _catalogCache;
17
+ let _skillCache;
14
18
  const readEventCatalog = () => {
15
19
  if (_catalogCache) return _catalogCache;
16
20
  const raw = readFileSync(join(packageRoot, "data", "agent_event_catalog.json"), "utf8");
17
21
  _catalogCache = JSON.parse(raw);
18
22
  return _catalogCache;
19
23
  };
24
+ const readSkillGuide = () => {
25
+ if (_skillCache) return _skillCache;
26
+ const skillPath = join(packageRoot, ".cursor", "skills", "instrument-with-amplitude-ai", "SKILL.md");
27
+ try {
28
+ _skillCache = readFileSync(skillPath, "utf8");
29
+ } catch {
30
+ _skillCache = "Instrument guide not found. See llms-full.txt for API reference.";
31
+ }
32
+ return _skillCache;
33
+ };
20
34
  const normalizeContentTier = (value) => {
21
35
  if (value === "full" || value === "metadata_only" || value === "customer_enriched") return value;
22
36
  return "full";
@@ -194,6 +208,58 @@ const createServer = () => {
194
208
  }, null, 2)
195
209
  }] };
196
210
  });
211
+ server.registerTool(MCP_TOOLS.scanProject, {
212
+ title: "Scan Project",
213
+ description: "Scan a project directory to detect framework, LLM providers, agents, and call sites. Returns a structured discovery report for the instrument-with-amplitude-ai skill.",
214
+ inputSchema: { root_path: stringType().describe("Absolute path to the project root directory") }
215
+ }, async (args) => {
216
+ const result = scanProject(typeof args?.root_path === "string" ? args.root_path : "");
217
+ return { content: [{
218
+ type: "text",
219
+ text: JSON.stringify(result, null, 2)
220
+ }] };
221
+ });
222
+ server.registerTool(MCP_TOOLS.generateVerifyTest, {
223
+ title: "Generate Verify Test",
224
+ description: "Generate a vitest verification test from a scan_project result. The test exercises all discovered agents in dry-run mode using MockAmplitudeAI.",
225
+ inputSchema: { scan_result: stringType().describe("JSON string of the scan_project result") }
226
+ }, async (args) => {
227
+ const raw = typeof args?.scan_result === "string" ? args.scan_result : "{}";
228
+ return { content: [{
229
+ type: "text",
230
+ text: generateVerifyTest(JSON.parse(raw))
231
+ }] };
232
+ });
233
+ server.registerTool(MCP_TOOLS.instrumentFile, {
234
+ title: "Instrument File",
235
+ description: "Apply instrumentation transforms to a source file. Returns the instrumented source code.",
236
+ inputSchema: {
237
+ source: stringType().describe("Source code of the file"),
238
+ file_path: stringType().describe("Path to the file (for context)"),
239
+ tier: enumType([
240
+ "quick_start",
241
+ "standard",
242
+ "advanced"
243
+ ]),
244
+ bootstrap_import_path: stringType().describe("Import path for the amplitude bootstrap module"),
245
+ agent_id: stringType().describe("Agent ID to use"),
246
+ description: stringType().optional().describe("Agent description"),
247
+ providers: arrayType(stringType()).describe("Provider names used in this file")
248
+ }
249
+ }, async (args) => {
250
+ return { content: [{
251
+ type: "text",
252
+ text: instrumentFile({
253
+ source: typeof args?.source === "string" ? args.source : "",
254
+ filePath: typeof args?.file_path === "string" ? args.file_path : "",
255
+ tier: args?.tier ?? "standard",
256
+ bootstrapImportPath: typeof args?.bootstrap_import_path === "string" ? args.bootstrap_import_path : "@/lib/amplitude",
257
+ agentId: typeof args?.agent_id === "string" ? args.agent_id : "agent",
258
+ description: typeof args?.description === "string" ? args.description : null,
259
+ providers: Array.isArray(args?.providers) ? args.providers : []
260
+ })
261
+ }] };
262
+ });
197
263
  server.registerResource("event_schema", MCP_RESOURCES.eventSchema, {
198
264
  title: "Amplitude AI Event Schema",
199
265
  description: "Current event and property catalog for @amplitude/ai.",
@@ -212,9 +278,18 @@ const createServer = () => {
212
278
  mimeType: "application/json",
213
279
  text: JSON.stringify({ patterns: getIntegrationPatterns() }, null, 2)
214
280
  }] }));
281
+ server.registerResource("instrument_guide", MCP_RESOURCES.instrumentGuide, {
282
+ title: "Amplitude AI Instrumentation Guide",
283
+ description: "Complete 4-phase workflow (Detect → Discover → Instrument → Verify) for instrumenting JS/TS AI apps with @amplitude/ai. Includes code examples for every step.",
284
+ mimeType: "text/markdown"
285
+ }, async () => ({ contents: [{
286
+ uri: MCP_RESOURCES.instrumentGuide,
287
+ mimeType: "text/markdown",
288
+ text: readSkillGuide()
289
+ }] }));
215
290
  server.registerPrompt(MCP_PROMPTS.instrumentApp, {
216
291
  title: "Instrument App",
217
- description: "Guided checklist for instrumenting an app with @amplitude/ai from zero to validated setup.",
292
+ description: "Full guided instrumentation of a JS/TS AI app with @amplitude/ai. Includes the complete 4-phase workflow: Detect environment, Discover agents, Instrument code, Verify correctness.",
218
293
  argsSchema: {
219
294
  framework: stringType().optional(),
220
295
  provider: stringType().optional()
@@ -224,7 +299,7 @@ const createServer = () => {
224
299
  role: "user",
225
300
  content: {
226
301
  type: "text",
227
- text: `Instrument this ${typeof args?.framework === "string" ? args.framework : "node"} app with ${typeof args?.provider === "string" ? args.provider : "openai"} using @amplitude/ai. Start from the production default: wrapper/swap integration plus ai.agent(...).session(...), choose a content tier (full/metadata_only/customer_enriched) with privacy defaults, and treat patch() only as a migration fallback. Then validate setup and annotate any uninstrumented call sites.`
302
+ text: `Instrument this ${typeof args?.framework === "string" ? args.framework : "node"} app using the ${typeof args?.provider === "string" ? args.provider : "openai"} provider with @amplitude/ai.\n\nFollow this guide step by step:\n\n${readSkillGuide()}`
228
303
  }
229
304
  }] };
230
305
  });
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":["_catalogCache: EventCatalog | undefined","z.string","z\n .enum","z\n .number","sources: Array<{ name: string; path: string }>","results: Array<{\n source: string;\n heading: string;\n snippet: string;\n line: number;\n priority: number;\n }>","content: string"],"sources":["../../src/mcp/server.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport {\n MCP_PROMPTS,\n MCP_RESOURCES,\n MCP_SERVER_NAME,\n MCP_TOOLS,\n} from './contract.js';\nimport { getIntegrationPatterns } from './patterns.js';\nimport { analyzeFileInstrumentation } from './validate-file.js';\n\ntype EventSchema = {\n event_type: string;\n description?: string;\n event_properties?: Record<string, unknown>;\n};\n\ntype EventCatalog = {\n events?: EventSchema[];\n};\n\nconst packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..');\n\nlet _catalogCache: EventCatalog | undefined;\n\nconst readEventCatalog = (): EventCatalog => {\n if (_catalogCache) return _catalogCache;\n const filePath = join(packageRoot, 'data', 'agent_event_catalog.json');\n const raw = readFileSync(filePath, 'utf8');\n _catalogCache = JSON.parse(raw) as EventCatalog;\n return _catalogCache;\n};\n\ntype ContentTier = 'full' | 'metadata_only' | 'customer_enriched';\n\nconst normalizeContentTier = (value: unknown): ContentTier => {\n if (\n value === 'full' ||\n value === 'metadata_only' ||\n value === 'customer_enriched'\n ) {\n return value;\n }\n return 'full';\n};\n\nconst buildInstrumentationGuidance = (\n framework: string,\n provider: string,\n contentTier: ContentTier,\n): string[] => {\n const providerSetup =\n provider === 'openai' || provider === 'anthropic'\n ? `use ${provider} wrapper/swap integration (for example: \\`new ${provider === 'openai' ? 'OpenAI' : 'Anthropic'}({ amplitude: ai, ... })\\`)`\n : `use the ${provider} provider wrapper class from @amplitude/ai`;\n\n const frameworkStep =\n framework === 'express' || framework === 'koa' || framework === 'fastify'\n ? 'wire `createAmplitudeAIMiddleware()` for per-request identity propagation and run LLM calls inside session context'\n : framework === 'next' || framework === 'nextjs'\n ? 'wrap route handlers in `session.run()` so every LLM call inherits user/session lineage'\n : 'attach `ai.agent(...).session(...)` where request or conversation identity exists';\n\n const tierGuidance =\n contentTier === 'full'\n ? 'Content tier (`full`): maximum insight and automatic server enrichments. Prefer `redactPii: true` (+ optional `customRedactionPatterns`).'\n : contentTier === 'metadata_only'\n ? 'Content tier (`metadata_only`): no content leaves your infrastructure; keep token/cost/latency/session analytics.'\n : 'Content tier (`customer_enriched`): no content leaves infra; send your own labels via `trackSessionEnrichment(...)` for advanced analytics.';\n\n const nextForTier =\n contentTier === 'metadata_only'\n ? 'if you need quality/topic analytics without sending content, move to `customer_enriched` and emit structured enrichments.'\n : contentTier === 'customer_enriched'\n ? 'if policy allows and you want zero eval-code overhead, consider `full` with redaction for automatic server enrichments.'\n : 'if policy prohibits content egress, switch to `metadata_only` or `customer_enriched` without changing session instrumentation.';\n\n return [\n `Framework: ${framework}`,\n `Provider: ${provider}`,\n `Content tier: ${contentTier}`,\n '',\n 'Now:',\n '1) install @amplitude/ai and @amplitude/analytics-node',\n '2) initialize `AmplitudeAI` with API key',\n `3) ${providerSetup}`,\n '4) bind session context with `const session = ai.agent(...).session(...)` and run calls in `session.run(...)`',\n `5) ${frameworkStep}`,\n '',\n 'Next:',\n `- ${nextForTier}`,\n '- treat `patch({ amplitudeAI: ai })` as migration quickstart, not steady-state production.',\n '',\n 'Why:',\n '- session lineage unlocks scoring, enrichments, and reliable product-to-AI funnels.',\n '- wrapper/swap integration preserves fidelity while keeping implementation effort low.',\n `- ${tierGuidance}`,\n '- privacy controls apply before events leave your process.',\n '',\n 'Validate:',\n '- run `amplitude-ai doctor` and verify [Agent] session-scoped events.',\n ];\n};\n\nconst headingPriority = (heading: string): number => {\n const normalized = heading.toLowerCase();\n if (\n normalized.includes('choose your integration tier') ||\n normalized.includes('privacy & content control')\n ) {\n return 3;\n }\n if (\n normalized.includes('boundagent') ||\n normalized.includes('bound agent') ||\n normalized.includes('session')\n ) {\n return 2;\n }\n return 1;\n};\n\nconst createServer = (): McpServer => {\n const server = new McpServer({\n name: MCP_SERVER_NAME,\n version: '0.1.0',\n });\n\n server.registerTool(\n MCP_TOOLS.getEventSchema,\n {\n title: 'Get Event Schema',\n description:\n 'Return Amplitude AI event schema and event-property definitions.',\n inputSchema: {\n event_type: z.string().optional(),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const eventType =\n typeof args?.event_type === 'string' ? args.event_type : undefined;\n const catalog = readEventCatalog();\n const events = catalog.events ?? [];\n const selected = eventType\n ? events.filter((event) => event.event_type === eventType)\n : events;\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n total: selected.length,\n events: selected,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.getIntegrationPattern,\n {\n title: 'Get Integration Pattern',\n description:\n 'Return canonical instrumentation patterns for @amplitude/ai.',\n inputSchema: {\n id: z.string().optional(),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const id = typeof args?.id === 'string' ? args.id : undefined;\n const patterns = getIntegrationPatterns();\n const selected = id\n ? patterns.filter((pattern) => pattern.id === id)\n : patterns;\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({ patterns: selected }, null, 2),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.validateSetup,\n {\n title: 'Validate Setup',\n description: 'Validate environment variables required for @amplitude/ai.',\n inputSchema: {},\n },\n async () => {\n const required = ['AMPLITUDE_AI_API_KEY'];\n const missing = required.filter((name) => !process.env[name]);\n const status = missing.length === 0 ? 'ok' : 'missing_env';\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n status,\n missing_env: missing,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.suggestInstrumentation,\n {\n title: 'Suggest Instrumentation',\n description:\n 'Suggest practical next instrumentation steps from minimal hints.',\n inputSchema: {\n framework: z.string().optional(),\n provider: z.string().optional(),\n content_tier: z\n .enum(['full', 'metadata_only', 'customer_enriched'])\n .optional()\n .describe('Desired content tier (default: full)'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const framework =\n typeof args?.framework === 'string' ? args.framework : 'node';\n const provider =\n typeof args?.provider === 'string' ? args.provider : 'openai';\n const contentTier = normalizeContentTier(args?.content_tier);\n const guidance = buildInstrumentationGuidance(\n framework,\n provider,\n contentTier,\n );\n\n return {\n content: [\n {\n type: 'text',\n text: guidance.join('\\n'),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.validateFile,\n {\n title: 'Validate File Instrumentation',\n description:\n 'Analyze source code to detect LLM call sites and report which are instrumented vs uninstrumented.',\n inputSchema: {\n source: z.string().describe('Source code content to analyze'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const source = typeof args?.source === 'string' ? args.source : '';\n const result = analyzeFileInstrumentation(source);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.searchDocs,\n {\n title: 'Search Documentation',\n description:\n 'Search the SDK README and API reference for a keyword or phrase. Returns matching sections with surrounding context.',\n inputSchema: {\n query: z.string().describe('Keyword or phrase to search for'),\n max_results: z\n .number()\n .optional()\n .describe('Maximum number of results to return (default: 5)'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const query =\n typeof args?.query === 'string' ? args.query.toLowerCase() : '';\n const maxResults =\n typeof args?.max_results === 'number' ? args.max_results : 5;\n\n if (!query) {\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({ error: 'query is required' }),\n },\n ],\n };\n }\n\n const sources: Array<{ name: string; path: string }> = [\n { name: 'README.md', path: join(packageRoot, 'README.md') },\n { name: 'llms-full.txt', path: join(packageRoot, 'llms-full.txt') },\n ];\n\n const results: Array<{\n source: string;\n heading: string;\n snippet: string;\n line: number;\n priority: number;\n }> = [];\n\n for (const { name, path } of sources) {\n let content: string;\n try {\n content = readFileSync(path, 'utf8');\n } catch {\n continue;\n }\n const lines = content.split('\\n');\n let currentHeading = '(top)';\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n if (line.startsWith('#')) {\n currentHeading = line.replace(/^#+\\s*/, '');\n }\n if (line.toLowerCase().includes(query)) {\n const start = Math.max(0, i - 2);\n const end = Math.min(lines.length, i + 3);\n results.push({\n source: name,\n heading: currentHeading,\n snippet: lines.slice(start, end).join('\\n'),\n line: i + 1,\n priority: headingPriority(currentHeading),\n });\n }\n }\n }\n\n results.sort((a, b) => b.priority - a.priority || a.line - b.line);\n const selected = results.slice(0, maxResults);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n query,\n total: selected.length,\n results: selected.map(({ priority: _priority, ...rest }) => rest),\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n server.registerResource(\n 'event_schema',\n MCP_RESOURCES.eventSchema,\n {\n title: 'Amplitude AI Event Schema',\n description: 'Current event and property catalog for @amplitude/ai.',\n mimeType: 'application/json',\n },\n async () => ({\n contents: [\n {\n uri: MCP_RESOURCES.eventSchema,\n mimeType: 'application/json',\n text: JSON.stringify(readEventCatalog(), null, 2),\n },\n ],\n }),\n );\n\n server.registerResource(\n 'integration_patterns',\n MCP_RESOURCES.integrationPatterns,\n {\n title: 'Amplitude AI Integration Patterns',\n description: 'Canonical setup patterns used by docs and agent workflows.',\n mimeType: 'application/json',\n },\n async () => ({\n contents: [\n {\n uri: MCP_RESOURCES.integrationPatterns,\n mimeType: 'application/json',\n text: JSON.stringify({ patterns: getIntegrationPatterns() }, null, 2),\n },\n ],\n }),\n );\n\n server.registerPrompt(\n MCP_PROMPTS.instrumentApp,\n {\n title: 'Instrument App',\n description:\n 'Guided checklist for instrumenting an app with @amplitude/ai from zero to validated setup.',\n argsSchema: {\n framework: z.string().optional(),\n provider: z.string().optional(),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const framework =\n typeof args?.framework === 'string' ? args.framework : 'node';\n const provider =\n typeof args?.provider === 'string' ? args.provider : 'openai';\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Instrument this ${framework} app with ${provider} using @amplitude/ai. Start from the production default: wrapper/swap integration plus ai.agent(...).session(...), choose a content tier (full/metadata_only/customer_enriched) with privacy defaults, and treat patch() only as a migration fallback. Then validate setup and annotate any uninstrumented call sites.`,\n },\n },\n ],\n };\n },\n );\n\n return server;\n};\n\nconst runMcpServer = async (): Promise<void> => {\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n};\n\nexport { buildInstrumentationGuidance, createServer, runMcpServer };\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.js","names":["_catalogCache: EventCatalog | undefined","_skillCache: string | undefined","z.string","z\n .enum","z\n .number","sources: Array<{ name: string; path: string }>","results: Array<{\n source: string;\n heading: string;\n snippet: string;\n line: number;\n priority: number;\n }>","content: string","z\n .string","z.enum","z\n .array"],"sources":["../../src/mcp/server.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport {\n MCP_PROMPTS,\n MCP_RESOURCES,\n MCP_SERVER_NAME,\n MCP_TOOLS,\n} from './contract.js';\nimport { getIntegrationPatterns } from './patterns.js';\nimport { generateVerifyTest } from './generate-verify-test.js';\nimport { instrumentFile } from './instrument-file.js';\nimport { type ScanResult, scanProject } from './scan-project.js';\nimport { analyzeFileInstrumentation } from './validate-file.js';\n\ntype EventSchema = {\n event_type: string;\n description?: string;\n event_properties?: Record<string, unknown>;\n};\n\ntype EventCatalog = {\n events?: EventSchema[];\n};\n\nconst packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..');\n\nlet _catalogCache: EventCatalog | undefined;\nlet _skillCache: string | undefined;\n\nconst readEventCatalog = (): EventCatalog => {\n if (_catalogCache) return _catalogCache;\n const filePath = join(packageRoot, 'data', 'agent_event_catalog.json');\n const raw = readFileSync(filePath, 'utf8');\n _catalogCache = JSON.parse(raw) as EventCatalog;\n return _catalogCache;\n};\n\nconst readSkillGuide = (): string => {\n if (_skillCache) return _skillCache;\n const skillPath = join(\n packageRoot,\n '.cursor',\n 'skills',\n 'instrument-with-amplitude-ai',\n 'SKILL.md',\n );\n try {\n _skillCache = readFileSync(skillPath, 'utf8');\n } catch {\n _skillCache = 'Instrument guide not found. See llms-full.txt for API reference.';\n }\n return _skillCache;\n};\n\ntype ContentTier = 'full' | 'metadata_only' | 'customer_enriched';\n\nconst normalizeContentTier = (value: unknown): ContentTier => {\n if (\n value === 'full' ||\n value === 'metadata_only' ||\n value === 'customer_enriched'\n ) {\n return value;\n }\n return 'full';\n};\n\nconst buildInstrumentationGuidance = (\n framework: string,\n provider: string,\n contentTier: ContentTier,\n): string[] => {\n const providerSetup =\n provider === 'openai' || provider === 'anthropic'\n ? `use ${provider} wrapper/swap integration (for example: \\`new ${provider === 'openai' ? 'OpenAI' : 'Anthropic'}({ amplitude: ai, ... })\\`)`\n : `use the ${provider} provider wrapper class from @amplitude/ai`;\n\n const frameworkStep =\n framework === 'express' || framework === 'koa' || framework === 'fastify'\n ? 'wire `createAmplitudeAIMiddleware()` for per-request identity propagation and run LLM calls inside session context'\n : framework === 'next' || framework === 'nextjs'\n ? 'wrap route handlers in `session.run()` so every LLM call inherits user/session lineage'\n : 'attach `ai.agent(...).session(...)` where request or conversation identity exists';\n\n const tierGuidance =\n contentTier === 'full'\n ? 'Content tier (`full`): maximum insight and automatic server enrichments. Prefer `redactPii: true` (+ optional `customRedactionPatterns`).'\n : contentTier === 'metadata_only'\n ? 'Content tier (`metadata_only`): no content leaves your infrastructure; keep token/cost/latency/session analytics.'\n : 'Content tier (`customer_enriched`): no content leaves infra; send your own labels via `trackSessionEnrichment(...)` for advanced analytics.';\n\n const nextForTier =\n contentTier === 'metadata_only'\n ? 'if you need quality/topic analytics without sending content, move to `customer_enriched` and emit structured enrichments.'\n : contentTier === 'customer_enriched'\n ? 'if policy allows and you want zero eval-code overhead, consider `full` with redaction for automatic server enrichments.'\n : 'if policy prohibits content egress, switch to `metadata_only` or `customer_enriched` without changing session instrumentation.';\n\n return [\n `Framework: ${framework}`,\n `Provider: ${provider}`,\n `Content tier: ${contentTier}`,\n '',\n 'Now:',\n '1) install @amplitude/ai and @amplitude/analytics-node',\n '2) initialize `AmplitudeAI` with API key',\n `3) ${providerSetup}`,\n '4) bind session context with `const session = ai.agent(...).session(...)` and run calls in `session.run(...)`',\n `5) ${frameworkStep}`,\n '',\n 'Next:',\n `- ${nextForTier}`,\n '- treat `patch({ amplitudeAI: ai })` as migration quickstart, not steady-state production.',\n '',\n 'Why:',\n '- session lineage unlocks scoring, enrichments, and reliable product-to-AI funnels.',\n '- wrapper/swap integration preserves fidelity while keeping implementation effort low.',\n `- ${tierGuidance}`,\n '- privacy controls apply before events leave your process.',\n '',\n 'Validate:',\n '- run `amplitude-ai doctor` and verify [Agent] session-scoped events.',\n ];\n};\n\nconst headingPriority = (heading: string): number => {\n const normalized = heading.toLowerCase();\n if (\n normalized.includes('choose your integration tier') ||\n normalized.includes('privacy & content control')\n ) {\n return 3;\n }\n if (\n normalized.includes('boundagent') ||\n normalized.includes('bound agent') ||\n normalized.includes('session')\n ) {\n return 2;\n }\n return 1;\n};\n\nconst createServer = (): McpServer => {\n const server = new McpServer({\n name: MCP_SERVER_NAME,\n version: '0.1.0',\n });\n\n server.registerTool(\n MCP_TOOLS.getEventSchema,\n {\n title: 'Get Event Schema',\n description:\n 'Return Amplitude AI event schema and event-property definitions.',\n inputSchema: {\n event_type: z.string().optional(),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const eventType =\n typeof args?.event_type === 'string' ? args.event_type : undefined;\n const catalog = readEventCatalog();\n const events = catalog.events ?? [];\n const selected = eventType\n ? events.filter((event) => event.event_type === eventType)\n : events;\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n total: selected.length,\n events: selected,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.getIntegrationPattern,\n {\n title: 'Get Integration Pattern',\n description:\n 'Return canonical instrumentation patterns for @amplitude/ai.',\n inputSchema: {\n id: z.string().optional(),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const id = typeof args?.id === 'string' ? args.id : undefined;\n const patterns = getIntegrationPatterns();\n const selected = id\n ? patterns.filter((pattern) => pattern.id === id)\n : patterns;\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({ patterns: selected }, null, 2),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.validateSetup,\n {\n title: 'Validate Setup',\n description: 'Validate environment variables required for @amplitude/ai.',\n inputSchema: {},\n },\n async () => {\n const required = ['AMPLITUDE_AI_API_KEY'];\n const missing = required.filter((name) => !process.env[name]);\n const status = missing.length === 0 ? 'ok' : 'missing_env';\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n status,\n missing_env: missing,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.suggestInstrumentation,\n {\n title: 'Suggest Instrumentation',\n description:\n 'Suggest practical next instrumentation steps from minimal hints.',\n inputSchema: {\n framework: z.string().optional(),\n provider: z.string().optional(),\n content_tier: z\n .enum(['full', 'metadata_only', 'customer_enriched'])\n .optional()\n .describe('Desired content tier (default: full)'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const framework =\n typeof args?.framework === 'string' ? args.framework : 'node';\n const provider =\n typeof args?.provider === 'string' ? args.provider : 'openai';\n const contentTier = normalizeContentTier(args?.content_tier);\n const guidance = buildInstrumentationGuidance(\n framework,\n provider,\n contentTier,\n );\n\n return {\n content: [\n {\n type: 'text',\n text: guidance.join('\\n'),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.validateFile,\n {\n title: 'Validate File Instrumentation',\n description:\n 'Analyze source code to detect LLM call sites and report which are instrumented vs uninstrumented.',\n inputSchema: {\n source: z.string().describe('Source code content to analyze'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const source = typeof args?.source === 'string' ? args.source : '';\n const result = analyzeFileInstrumentation(source);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.searchDocs,\n {\n title: 'Search Documentation',\n description:\n 'Search the SDK README and API reference for a keyword or phrase. Returns matching sections with surrounding context.',\n inputSchema: {\n query: z.string().describe('Keyword or phrase to search for'),\n max_results: z\n .number()\n .optional()\n .describe('Maximum number of results to return (default: 5)'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const query =\n typeof args?.query === 'string' ? args.query.toLowerCase() : '';\n const maxResults =\n typeof args?.max_results === 'number' ? args.max_results : 5;\n\n if (!query) {\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({ error: 'query is required' }),\n },\n ],\n };\n }\n\n const sources: Array<{ name: string; path: string }> = [\n { name: 'README.md', path: join(packageRoot, 'README.md') },\n { name: 'llms-full.txt', path: join(packageRoot, 'llms-full.txt') },\n ];\n\n const results: Array<{\n source: string;\n heading: string;\n snippet: string;\n line: number;\n priority: number;\n }> = [];\n\n for (const { name, path } of sources) {\n let content: string;\n try {\n content = readFileSync(path, 'utf8');\n } catch {\n continue;\n }\n const lines = content.split('\\n');\n let currentHeading = '(top)';\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n if (line.startsWith('#')) {\n currentHeading = line.replace(/^#+\\s*/, '');\n }\n if (line.toLowerCase().includes(query)) {\n const start = Math.max(0, i - 2);\n const end = Math.min(lines.length, i + 3);\n results.push({\n source: name,\n heading: currentHeading,\n snippet: lines.slice(start, end).join('\\n'),\n line: i + 1,\n priority: headingPriority(currentHeading),\n });\n }\n }\n }\n\n results.sort((a, b) => b.priority - a.priority || a.line - b.line);\n const selected = results.slice(0, maxResults);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n query,\n total: selected.length,\n results: selected.map(({ priority: _priority, ...rest }) => rest),\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.scanProject,\n {\n title: 'Scan Project',\n description:\n 'Scan a project directory to detect framework, LLM providers, agents, and call sites. Returns a structured discovery report for the instrument-with-amplitude-ai skill.',\n inputSchema: {\n root_path: z\n .string()\n .describe('Absolute path to the project root directory'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const rootPath =\n typeof args?.root_path === 'string' ? args.root_path : '';\n const result = scanProject(rootPath);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.generateVerifyTest,\n {\n title: 'Generate Verify Test',\n description:\n 'Generate a vitest verification test from a scan_project result. The test exercises all discovered agents in dry-run mode using MockAmplitudeAI.',\n inputSchema: {\n scan_result: z\n .string()\n .describe('JSON string of the scan_project result'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const raw =\n typeof args?.scan_result === 'string' ? args.scan_result : '{}';\n const parsed = JSON.parse(raw) as ScanResult;\n const testCode = generateVerifyTest(parsed);\n return {\n content: [{ type: 'text' as const, text: testCode }],\n };\n },\n );\n\n server.registerTool(\n MCP_TOOLS.instrumentFile,\n {\n title: 'Instrument File',\n description:\n 'Apply instrumentation transforms to a source file. Returns the instrumented source code.',\n inputSchema: {\n source: z.string().describe('Source code of the file'),\n file_path: z.string().describe('Path to the file (for context)'),\n tier: z.enum(['quick_start', 'standard', 'advanced']),\n bootstrap_import_path: z\n .string()\n .describe('Import path for the amplitude bootstrap module'),\n agent_id: z.string().describe('Agent ID to use'),\n description: z\n .string()\n .optional()\n .describe('Agent description'),\n providers: z\n .array(z.string())\n .describe('Provider names used in this file'),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const result = instrumentFile({\n source: typeof args?.source === 'string' ? args.source : '',\n filePath: typeof args?.file_path === 'string' ? args.file_path : '',\n tier: args?.tier ?? 'standard',\n bootstrapImportPath:\n typeof args?.bootstrap_import_path === 'string'\n ? args.bootstrap_import_path\n : '@/lib/amplitude',\n agentId: typeof args?.agent_id === 'string' ? args.agent_id : 'agent',\n description:\n typeof args?.description === 'string' ? args.description : null,\n providers: Array.isArray(args?.providers) ? args.providers : [],\n });\n return {\n content: [{ type: 'text' as const, text: result }],\n };\n },\n );\n\n server.registerResource(\n 'event_schema',\n MCP_RESOURCES.eventSchema,\n {\n title: 'Amplitude AI Event Schema',\n description: 'Current event and property catalog for @amplitude/ai.',\n mimeType: 'application/json',\n },\n async () => ({\n contents: [\n {\n uri: MCP_RESOURCES.eventSchema,\n mimeType: 'application/json',\n text: JSON.stringify(readEventCatalog(), null, 2),\n },\n ],\n }),\n );\n\n server.registerResource(\n 'integration_patterns',\n MCP_RESOURCES.integrationPatterns,\n {\n title: 'Amplitude AI Integration Patterns',\n description: 'Canonical setup patterns used by docs and agent workflows.',\n mimeType: 'application/json',\n },\n async () => ({\n contents: [\n {\n uri: MCP_RESOURCES.integrationPatterns,\n mimeType: 'application/json',\n text: JSON.stringify({ patterns: getIntegrationPatterns() }, null, 2),\n },\n ],\n }),\n );\n\n server.registerResource(\n 'instrument_guide',\n MCP_RESOURCES.instrumentGuide,\n {\n title: 'Amplitude AI Instrumentation Guide',\n description:\n 'Complete 4-phase workflow (Detect → Discover → Instrument → Verify) for instrumenting JS/TS AI apps with @amplitude/ai. Includes code examples for every step.',\n mimeType: 'text/markdown',\n },\n async () => ({\n contents: [\n {\n uri: MCP_RESOURCES.instrumentGuide,\n mimeType: 'text/markdown',\n text: readSkillGuide(),\n },\n ],\n }),\n );\n\n server.registerPrompt(\n MCP_PROMPTS.instrumentApp,\n {\n title: 'Instrument App',\n description:\n 'Full guided instrumentation of a JS/TS AI app with @amplitude/ai. Includes the complete 4-phase workflow: Detect environment, Discover agents, Instrument code, Verify correctness.',\n argsSchema: {\n framework: z.string().optional(),\n provider: z.string().optional(),\n },\n },\n // biome-ignore lint/suspicious/noExplicitAny: SDK callback type is intentionally broad.\n async (args: any) => {\n const framework =\n typeof args?.framework === 'string' ? args.framework : 'node';\n const provider =\n typeof args?.provider === 'string' ? args.provider : 'openai';\n const guide = readSkillGuide();\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Instrument this ${framework} app using the ${provider} provider with @amplitude/ai.\\n\\nFollow this guide step by step:\\n\\n${guide}`,\n },\n },\n ],\n };\n },\n );\n\n return server;\n};\n\nconst runMcpServer = async (): Promise<void> => {\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n};\n\nexport { buildInstrumentationGuidance, createServer, runMcpServer };\n"],"mappings":"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"}
@@ -4,6 +4,8 @@ interface CallSite {
4
4
  provider: string;
5
5
  api: string;
6
6
  instrumented: boolean;
7
+ containing_function: string | null;
8
+ code_context: string;
7
9
  }
8
10
  interface FileAnalysis {
9
11
  total_call_sites: number;
@@ -13,6 +15,8 @@ interface FileAnalysis {
13
15
  has_session_context: boolean;
14
16
  call_sites: CallSite[];
15
17
  suggestions: string[];
18
+ tool_definitions: string[];
19
+ function_definitions: string[];
16
20
  }
17
21
  declare function analyzeFileInstrumentation(source: string): FileAnalysis;
18
22
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"validate-file.d.ts","names":[],"sources":["../../src/mcp/validate-file.ts"],"sourcesContent":[],"mappings":";UAAiB,QAAA;EAAA,IAAA,EAAA,MAAQ;EAOR,QAAA,EAAA,MAAY;EA+Eb,GAAA,EAAA,MAAA;;;UA/EC,YAAA;;;;;;cAMH;;;iBAyEE,0BAAA,kBAA4C"}
1
+ {"version":3,"file":"validate-file.d.ts","names":[],"sources":["../../src/mcp/validate-file.ts"],"sourcesContent":[],"mappings":";UAEiB,QAAA;EAAA,IAAA,EAAA,MAAQ;EASR,QAAA,EAAA,MAAY;EA2uBb,GAAA,EAAA,MAAA;;;;;UA3uBC,YAAA;;;;;;cAMH;;;;;iBAquBE,0BAAA,kBAA4C"}