Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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