Skip to content

Commit bdda77a

Browse files
authored
Merge pull request #9 from storybookjs/telemetry
Add telemetry for session starting, tool calling
2 parents 253ac1e + 326e67b commit bdda77a

File tree

5 files changed

+88
-11
lines changed

5 files changed

+88
-11
lines changed

.changeset/modern-donkeys-cut.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@storybook/addon-mcp": patch
3+
---
4+
5+
Add basic telemetry for sessions and tool calls

src/mcp-handler.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,43 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
66
import pkgJson from "../package.json" with { type: "json" };
77
import { registerStoryUrlsTool } from "./tools/get-story-urls";
88
import { registerUIBuildingTool } from "./tools/get-ui-building-instructions";
9-
import type { Options } from "storybook/internal/types";
9+
import type { Options, CoreConfig } from "storybook/internal/types";
1010
import type { IncomingMessage, ServerResponse } from "node:http";
11+
import {
12+
collectTelemetry,
13+
mcpSessionIdToClientMap,
14+
setDisableTelemetry,
15+
} from "./telemetry";
1116

12-
function createMcpServer(options: Options) {
17+
async function createMcpServer(options: Options, client: string) {
1318
// New initialization request
1419
const transport = new StreamableHTTPServerTransport({
1520
sessionIdGenerator: () => randomUUID(),
16-
onsessioninitialized: (sessionId) => {
17-
// Store the transport by session ID
21+
onsessioninitialized: async (sessionId) => {
1822
transports[sessionId] = transport;
23+
24+
const { disableTelemetry } = await options.presets.apply<CoreConfig>(
25+
"core",
26+
{},
27+
);
28+
setDisableTelemetry(disableTelemetry);
29+
mcpSessionIdToClientMap[sessionId] = client;
30+
31+
await collectTelemetry({
32+
event: "session:initialized",
33+
mcpSessionId: sessionId,
34+
});
1935
},
2036
});
2137

2238
transport.onclose = () => {
23-
if (transport.sessionId) {
24-
delete transports[transport.sessionId];
39+
if (!transport.sessionId) {
40+
return;
41+
}
42+
43+
delete transports[transport.sessionId];
44+
if (mcpSessionIdToClientMap[transport.sessionId]) {
45+
delete mcpSessionIdToClientMap[transport.sessionId];
2546
}
2647
};
2748

@@ -61,7 +82,7 @@ const handlePostRequest = async (
6182
// Reuse existing transport
6283
transport = transports[sessionId];
6384
} else if (!sessionId && isInitializeRequest(body)) {
64-
transport = await createMcpServer(options);
85+
transport = await createMcpServer(options, body.params.clientInfo.name);
6586
} else {
6687
// Invalid request
6788
res.statusCode = 400;

src/telemetry.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { logger } from "storybook/internal/node-logger";
2+
import { telemetry } from "storybook/internal/telemetry";
3+
4+
export async function collectTelemetry({
5+
event,
6+
mcpSessionId,
7+
...payload
8+
}: {
9+
event: string;
10+
mcpSessionId: string;
11+
[key: string]: any;
12+
}) {
13+
if (disableTelemetry) {
14+
return;
15+
}
16+
17+
try {
18+
return await telemetry("addon-mcp" as any, {
19+
event,
20+
mcpSessionId,
21+
client: mcpSessionIdToClientMap[mcpSessionId!],
22+
...payload,
23+
});
24+
} catch (error) {
25+
logger.debug("Error collecting telemetry:", error);
26+
}
27+
}
28+
29+
export const mcpSessionIdToClientMap: Record<string, string> = {};
30+
31+
let disableTelemetry = false;
32+
export const setDisableTelemetry = (value = false) => {
33+
disableTelemetry = value;
34+
};

src/tools/get-story-urls.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { storyNameFromExport } from "storybook/internal/csf";
44
import type { Options, StoryIndex } from "storybook/internal/types";
55
import { logger } from "storybook/internal/node-logger";
66
import z from "zod";
7+
import { collectTelemetry } from "../telemetry";
78

89
const inputStoriesSchema = z.array(
910
z.object({
@@ -39,7 +40,7 @@ export function registerStoryUrlsTool({
3940
urls: outputUrlsSchema,
4041
},
4142
},
42-
async ({ stories }) => {
43+
async ({ stories }, { sessionId }) => {
4344
const index: StoryIndex = await (
4445
await fetch(`${origin}/index.json`)
4546
).json();
@@ -48,6 +49,7 @@ export function registerStoryUrlsTool({
4849
logger.debug("index entries found:", entriesList.length);
4950

5051
const result: z.infer<typeof outputUrlsSchema> = [];
52+
let foundStoryCount = 0;
5153

5254
for (const {
5355
exportName,
@@ -75,6 +77,7 @@ export function registerStoryUrlsTool({
7577
if (foundStoryId) {
7678
logger.debug("Found story ID:", foundStoryId);
7779
result.push(`${origin}/?path=/story/${foundStoryId}`);
80+
foundStoryCount++;
7881
} else {
7982
logger.debug("No story found");
8083
let errorMessage = `No story found for export name "${exportName}" with absolute file path "${absoluteStoryPath}"`;
@@ -85,6 +88,13 @@ export function registerStoryUrlsTool({
8588
}
8689
}
8790

91+
await collectTelemetry({
92+
event: "tool:getStoryUrls",
93+
mcpSessionId: sessionId!,
94+
inputStoryCount: stories.length,
95+
outputStoryCount: foundStoryCount,
96+
});
97+
8898
return {
8999
content: [
90100
{

src/tools/get-ui-building-instructions.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { GET_STORY_URLS_TOOL_NAME } from "./get-story-urls";
3+
import { collectTelemetry } from "../telemetry";
34

45
const INSTRUCTIONS = `
56
# Writing User Interfaces Components
@@ -51,8 +52,14 @@ export function registerUIBuildingTool(server: McpServer) {
5152
limited to adding or updating new components, pages, screens or layouts.`,
5253
inputSchema: {},
5354
},
54-
async () => ({
55-
content: [{ type: "text", text: INSTRUCTIONS }],
56-
}),
55+
async (_, { sessionId }) => {
56+
await collectTelemetry({
57+
event: "tool:getUIBuildingInstructions",
58+
mcpSessionId: sessionId!,
59+
});
60+
return {
61+
content: [{ type: "text", text: INSTRUCTIONS }],
62+
};
63+
},
5764
);
5865
}

0 commit comments

Comments
 (0)