Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [
"@spotlightjs/website",
"@spotlightjs/demo-astro",
"@spotlightjs/demo-sveltekit",
"@spotlightjs/e2e-tests-astro",
"@spotlightjs/e2e-tests-nextjs",
"@spotlightjs/e2e-tests-sveltekit"
]
"ignore": ["@spotlightjs/website"]
}
6 changes: 6 additions & 0 deletions .changeset/wide-hornets-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@spotlightjs/overlay": minor
"@spotlightjs/sidecar": minor
---

- Added tracking support in sidecar mcp server and added new integration in overlay to list interactions of sidecar mcp tracked in realtime
7 changes: 6 additions & 1 deletion packages/overlay/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import globalStyles from "./index.css?inline";
import { type SpotlightContext, initIntegrations } from "./integrations/integration";
import { default as sentry } from "./integrations/sentry/index";
import sidecarMcpIntegration from "./integrations/sidecar-mcp";
import { getPanelsFromIntegrations } from "./integrations/utils/extractPanelsFromIntegrations";
import { off, on, trigger } from "./lib/eventTarget";
import initSentry from "./lib/instrumentation";
Expand All @@ -24,6 +25,7 @@ import type { SpotlightOverlayOptions, WindowWithSpotlight } from "./types";
export { default as hydrationError } from "./integrations/hydration-error/index";
export { default as sentry } from "./integrations/sentry/index";
export { default as viteInspect } from "./integrations/vite-inspect/index";
export { default as sidecarMcp } from "./integrations/sidecar-mcp";
export type { SpotlightOverlayOptions, WindowWithSpotlight } from "./types";
export {
SPOTLIGHT_OPEN_CLASS_NAME,
Expand Down Expand Up @@ -140,7 +142,10 @@ export async function init(initOptions: SpotlightOverlayOptions = {}) {
const finalExperiments = { ...DEFAULT_EXPERIMENTS, ...experiments };

// Sentry is enabled by default
const defaultIntegrations = () => [sentry({ injectIntoSDK: !isLoadedFromSidecar && !fullPage })];
const defaultIntegrations = () => [
sentry({ injectIntoSDK: !isLoadedFromSidecar && !fullPage }),
sidecarMcpIntegration(),
];

const context: SpotlightContext = {
open: openSpotlight,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useMemo } from "react";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import useSort from "~/integrations/hooks/useSort";
import { cn } from "~/lib/cn";
import Table from "~/ui/table";
import { AGGREGATE_CALL_PROFILES_SORT_KEYS, AGGREGATE_PROFILES_HEADERS } from "../../constants";
import useSort from "../../hooks/useSort";
import useSentryStore from "../../store";
import type { AggregateCallData } from "../../types";
import { getFormattedDuration, getSpanDurationClassName } from "../../utils/duration";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import useSort from "~/integrations/hooks/useSort";
import { TimeBar } from "~/integrations/sentry/components/shared/TimeBar";
import { DB_SPAN_REGEX, QUERIES_HEADERS, QUERIES_SORT_KEYS } from "~/integrations/sentry/constants";
import { useSentrySpans } from "~/integrations/sentry/data/useSentrySpans";
import useSort from "~/integrations/sentry/hooks/useSort";
import type { Span } from "~/integrations/sentry/types";
import { getFormattedDuration, getSpanDurationClassName } from "~/integrations/sentry/utils/duration";
import { cn } from "~/lib/cn";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { useMemo } from "react";
import { Link, useParams } from "react-router-dom";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import useSort from "~/integrations/hooks/useSort";
import { cn } from "~/lib/cn";
import Breadcrumbs from "~/ui/breadcrumbs";
import Table from "~/ui/table";
import { QUERY_SUMMARY_HEADERS, QUERY_SUMMARY_SORT_KEYS } from "../../constants";
import { useSentrySpans } from "../../data/useSentrySpans";
import useSort from "../../hooks/useSort";
import type { Span } from "../../types";
import { getFormattedDuration, getSpanDurationClassName } from "../../utils/duration";
import { truncateId } from "../../utils/text";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useMemo } from "react";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import useSort from "~/integrations/hooks/useSort";
import { cn } from "~/lib/cn";
import Table from "~/ui/table";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/ui/tooltip";
import { RESOURCES_SORT_KEYS, RESOURCE_HEADERS } from "../../constants";
import { useSentrySpans } from "../../data/useSentrySpans";
import useSort from "../../hooks/useSort";
import type { Span } from "../../types";
import { formatBytes } from "../../utils/bytes";
import { getFormattedDuration, getSpanDurationClassName } from "../../utils/duration";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { useMemo } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import useSort from "~/integrations/hooks/useSort";
import type { SpotlightAITrace } from "~/integrations/sentry/types";
import { cn } from "~/lib/cn";
import Table from "~/ui/table";
import { AI_TRACES_HEADERS, AI_TRACES_SORT_KEYS } from "../../../constants";
import { SearchProvider } from "../../../context/SearchContext";
import { useSpotlightAITraces } from "../../../data/useSpotlightAITraces";
import useSort from "../../../hooks/useSort";
import useSentryStore from "../../../store";
import AITraceDetail from "./AITraceDetails";
import AITraceItem from "./AITraceItem";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useState } from "react";
import { ReactComponent as Download } from "~/assets/download.svg";
import JsonViewer from "~/components/JsonViewer";
import type { RawEventContext } from "~/integrations/integration";
import { parseStringFromBuffer } from "~/integrations/sentry/utils/bufferParsers";
import { parseStringFromBuffer } from "~/integrations/utils/bufferParsers";
import SidePanel, { SidePanelHeader } from "~/ui/sidePanel";

export default function EnvelopeDetails({ data }: { data: { envelope: Envelope; rawEnvelope: RawEventContext } }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { type KeyboardEvent, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import useSort from "~/integrations/hooks/useSort";
import { PERFORMANCE_SCORE_PROFILES, WEB_VITALS_HEADERS, WEB_VITALS_SORT_KEYS } from "~/integrations/sentry/constants";
import { useSentryEvents } from "~/integrations/sentry/data/useSentryEvents";
import useSort from "~/integrations/sentry/hooks/useSort";
import type { SentryEventWithPerformanceData } from "~/integrations/sentry/types";
import { getFormattedDuration } from "~/integrations/sentry/utils/duration";
import { cn } from "~/lib/cn";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { useNavigate, useParams } from "react-router-dom";
import { ReactComponent as Sort } from "~/assets/sort.svg";
import { ReactComponent as SortDown } from "~/assets/sortDown.svg";
import CardList from "~/components/CardList";
import useSort from "~/integrations/hooks/useSort";
import { cn } from "~/lib/cn";
import { useSpotlightContext } from "~/lib/useSpotlightContext";
import Table from "~/ui/table";
import { LOGS_HEADERS, LOGS_SORT_KEYS, LOG_LEVEL_COLORS } from "../../constants";
import { useSentryLogs } from "../../data/useSentryLogs";
import useSort from "../../hooks/useSort";
import type { SentryLogEventItem } from "../../types";
import { formatTimestamp } from "../../utils/duration";
import HiddenItemsButton from "../shared/HiddenItemsButton";
Expand Down
2 changes: 1 addition & 1 deletion packages/overlay/src/integrations/sentry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import ErrorsTab from "./tabs/ErrorsTab";
import InsightsTab from "./tabs/InsightsTab";

import { spotlightBrowserIntegration } from "@sentry/browser";
import { parseJSONFromBuffer } from "../utils/bufferParsers";
import { getLocalTraces, isLocalTrace } from "./store/helpers";
import LogsTab from "./tabs/LogsTab";
import TracesTab from "./tabs/TracesTab";
import type { SentryErrorEvent, SentryEvent } from "./types";
import { parseJSONFromBuffer } from "./utils/bufferParsers";
import { isErrorEvent } from "./utils/sentry";
import { createTab } from "./utils/tabs";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import JsonViewer from "~/components/JsonViewer";
import { Badge } from "~/ui/badge";
import SidePanel, { SidePanelHeader } from "~/ui/sidePanel";
import useSidecarMcpStore from "./store";
import { getFormattedDuration } from "./utils";

interface SidecarMcpInteractionDetailsProps {
interactionId: string;
}

export default function SidecarMcpInteractionDetails({ interactionId }: SidecarMcpInteractionDetailsProps) {
const { interactions } = useSidecarMcpStore();
const interaction = interactions.find(i => i.id === interactionId);

if (!interaction) {
return (
<SidePanel backto="/sidecar-mcp">
<SidePanelHeader
title="Interaction Not Found"
subtitle="The requested interaction could not be found"
backto="/sidecar-mcp"
/>
<div className="p-6 text-center text-primary-300">
Interaction with ID "{interactionId}" was not found. It may have been cleared or expired.
</div>
</SidePanel>
);
}

return (
<SidePanel backto="/sidecar-mcp">
<SidePanelHeader
title="Sidecar MCP Interaction Details"
subtitle={
<div className="flex items-center gap-2">
<Badge
variant={interaction.success ? "default" : "destructive"}
className={
interaction.success
? "bg-green-600/20 text-green-300 border-green-500/30"
: "bg-red-600/20 text-red-300 border-red-500/30"
}
>
{interaction.success ? "Success" : "Error"}
</Badge>
<span className="text-primary-300">{interaction.method}</span>
{interaction.tool && (
<Badge variant="outline" className="text-xs bg-primary-800/50 text-primary-200 border-primary-600/50">
{interaction.tool}
</Badge>
)}
</div>
}
backto="/sidecar-mcp"
/>

<div className="space-y-6">
<div className="grid grid-cols-2 gap-6">
<div>
<h2 className="mb-2 font-bold uppercase text-primary-200">TIMING</h2>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-primary-400">Duration:</span>
<span className="text-primary-100 font-mono">{getFormattedDuration(interaction.duration)}</span>
</div>
<div className="flex justify-between">
<span className="text-primary-400">Timestamp:</span>
<span className="text-primary-100 font-mono">
{interaction.timestamp ? new Date(interaction.timestamp).toLocaleString() : "N/A"}
</span>
</div>
</div>
</div>

<div>
<h2 className="mb-2 font-bold uppercase text-primary-200">CLIENT INFO</h2>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-primary-400">Client:</span>
<span className="text-primary-100">{interaction.client || "N/A"}</span>
</div>
<div className="flex justify-between">
<span className="text-primary-400">Method:</span>
<span className="text-primary-100 font-mono">{interaction.method || "N/A"}</span>
</div>
{interaction.tool && (
<div className="flex justify-between">
<span className="text-primary-400">Tool:</span>
<span className="text-primary-100">{interaction.tool}</span>
</div>
)}
</div>
</div>
</div>

{/* Input Parameters */}
{interaction.input && Object.keys(interaction.input).length > 0 && (
<div>
<h2 className="mb-2 font-bold uppercase text-primary-200">INPUT PARAMETERS</h2>
<JsonViewer data={interaction.input} />
</div>
)}

{/* Error Details */}
{interaction.error && (
<div>
<h2 className="mb-2 font-bold uppercase text-red-400">ERROR DETAILS</h2>
<div className="bg-red-950/20 border border-red-800 rounded-md p-4">
<pre className="text-sm text-red-300 whitespace-pre-wrap">{interaction.error}</pre>
</div>
</div>
)}

{/* Output Results */}
{interaction.output && Object.keys(interaction.output).length > 0 && (
<div>
<h2 className="mb-2 font-bold uppercase text-primary-200">OUTPUT RESULTS</h2>
<JsonViewer data={interaction.output} />
</div>
)}

{/* Debug Info */}
<div>
<h2 className="mb-2 font-bold uppercase text-primary-200">DEBUG INFO</h2>
<JsonViewer data={interaction} />
</div>
</div>
</SidePanel>
);
}
13 changes: 13 additions & 0 deletions packages/overlay/src/integrations/sidecar-mcp/SidecarMcpTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Route, Routes } from "react-router-dom";
import SidecarMcpTabList from "./SidecarMcpTabList";

export default function SidecarMcpTab() {
return (
<div className="flex-1 overflow-auto">
<Routes>
<Route path="/:id/*" element={<SidecarMcpTabList />} />
<Route path="/" element={<SidecarMcpTabList />} />
</Routes>
</div>
);
}
Loading
Loading