diff --git a/.agents/claude.md b/.agents/claude.md index 0249864..d78eb81 100644 --- a/.agents/claude.md +++ b/.agents/claude.md @@ -251,7 +251,7 @@ All HTTP services return errors in this shape: ## Important Implementation Notes -- The SDK uses `AsyncLocalStorage` for context propagation — `ctx.yavio` in handlers, singleton for deep utilities +- The SDK uses `AsyncLocalStorage` for context propagation — import the `yavio` singleton from `@yavio/sdk` and call methods inside tool handlers - Event transport: SDK buffers in memory, flushes HTTP batch to ingestion endpoint - Widget auto-config: API key and endpoint injected via `window.__YAVIO__` by server-side proxy - Dashboard uses Next.js Server Components for ClickHouse queries diff --git a/.specs/01_executive-summary.md b/.specs/01_executive-summary.md index fa5bfb3..4a1fe4e 100644 --- a/.specs/01_executive-summary.md +++ b/.specs/01_executive-summary.md @@ -54,7 +54,7 @@ Self-hosted and Cloud run the same codebase. Cloud monetizes through usage-based |----------|--------|-----------| | Instrumentation | Proxy pattern (`withYavio` wraps `McpServer`) | Best DX: 3 lines, zero config. Abstraction layer allows transport-level interception later. | | User identification | `.identify(userId, traits)` on server + widget | Ties events to known users. Enables retention, cohorts, per-user analytics. Stored as `user_id` on events + `users_mv` materialized view. | -| Instance access (server) | Context injection + AsyncLocalStorage singleton | `ctx.yavio` for scoped handler use; singleton for deep utility functions. | +| Instance access (server) | AsyncLocalStorage singleton (`yavio`) | Import `yavio` from `@yavio/sdk` and call methods inside tool handlers. | | Instance access (widget) | `useYavio()` React hook with auto-config | Zero-config: API key and endpoint auto-injected by server via `window.__YAVIO__`. | | Event transport (server) | HTTP batch POST to ingestion API | SDK buffers events in memory, flushes to ingestion endpoint. Stateless, retryable. | | Event transport (widget) | Direct HTTP to ingestion API | Widget sends events directly to ingestion endpoint using short-lived JWT (minted by server-side proxy). Project API key never reaches the browser. | diff --git a/.specs/07_error-catalog.md b/.specs/07_error-catalog.md index a8edb1c..19af8b1 100644 --- a/.specs/07_error-catalog.md +++ b/.specs/07_error-catalog.md @@ -122,7 +122,7 @@ Errors originating in the `@yavio/sdk` SDK (both server and widget entry points) | `YAVIO-1102` | warn | Session ID derivation failed | Could not derive `session_id` from MCP `initialize` handshake. A random session ID is generated as fallback. | Ensure the MCP client sends a valid `initialize` request. | | `YAVIO-1103` | warn | Widget token minting failed | `POST /v1/widget-tokens` returned an error or was unreachable. Widget falls back to no-op mode. | Check ingestion API availability and API key validity. | | `YAVIO-1104` | warn | Widget config injection skipped | Response interception could not detect `_meta.ui.resourceUri` or injection failed. Widget operates without analytics. | Ensure tool response follows MCP widget response format. | -| `YAVIO-1105` | warn | Context injection unavailable | `AsyncLocalStorage` context lost. Explicit tracking calls (`yavio.track()`, etc.) outside a request context will lack `traceId` and `sessionId`. | Call explicit methods from within a tool handler or use `ctx.yavio` instead of the module singleton. | +| `YAVIO-1105` | warn | Context unavailable | `AsyncLocalStorage` context lost. Tracking calls (`yavio.track()`, etc.) outside a request context will lack `traceId` and `sessionId`. | Ensure calls are made from within a tool handler wrapped by `withYavio()`. | | `YAVIO-1106` | warn | Tool response inspection failed | Error inspecting tool handler response for size, content type, or zero-result detection. Event captured without response metadata. | Report the bug. | ### Transport & Delivery (1200–1299) diff --git a/.specs/infrastructure/testing.md b/.specs/infrastructure/testing.md index c614309..400dd02 100644 --- a/.specs/infrastructure/testing.md +++ b/.specs/infrastructure/testing.md @@ -75,8 +75,8 @@ Run against real Docker services using Testcontainers or a shared `docker-compos | Server SDK → ClickHouse | `withYavio()` wraps tool → tool called → SDK flushes → query ClickHouse | Event row exists with correct `project_id`, `event_type=tool_call`, `trace_id` | | Widget → ClickHouse | Mint widget JWT → POST widget event to ingest → query ClickHouse | Widget event exists, `source=widget`, `trace_id` matches JWT | | Trace correlation | Server tool call + widget events share `trace_id` → query by trace | Both server and widget events returned, ordered by timestamp | -| Identify propagation | `ctx.yavio.identify(userId, traits)` → subsequent events | All events after identify carry `user_id`, `user_traits` populated | -| Conversion tracking | `ctx.yavio.conversion("purchase", { value: 29.99, currency: "USD" })` | Event has `conversion_value=29.99`, `conversion_currency=USD` | +| Identify propagation | `yavio.identify(userId, traits)` → subsequent events | All events after identify carry `user_id`, `user_traits` populated | +| Conversion tracking | `yavio.conversion("purchase", { value: 29.99, currency: "USD" })` | Event has `conversion_value=29.99`, `conversion_currency=USD` | | Materialized views | Insert events → wait for MV refresh → query `sessions_mv` and `users_mv` | Aggregated rows match expected counts and sums | ### Dashboard Query Accuracy diff --git a/.specs/metrics/events.md b/.specs/metrics/events.md index 4648caf..f51ff48 100644 --- a/.specs/metrics/events.md +++ b/.specs/metrics/events.md @@ -99,7 +99,7 @@ Fires on `tools/list` request interception. Tracks how often and when clients re ### step -Funnel progression point. Called via `ctx.yavio.step()`. +Funnel progression point. Called via `yavio.step()`. | Field | Type | Description | |-------|------|-------------| @@ -109,7 +109,7 @@ Funnel progression point. Called via `ctx.yavio.step()`. ### track -Generic custom event. Called via `ctx.yavio.track()`. +Generic custom event. Called via `yavio.track()`. | Field | Type | Description | |-------|------|-------------| @@ -118,7 +118,7 @@ Generic custom event. Called via `ctx.yavio.track()`. ### conversion -Revenue attribution event. Called via `ctx.yavio.conversion()`. +Revenue attribution event. Called via `yavio.conversion()`. | Field | Type | Description | |-------|------|-------------| @@ -129,7 +129,7 @@ Revenue attribution event. Called via `ctx.yavio.conversion()`. ### identify -User identification event. Called via `ctx.yavio.identify()`. +User identification event. Called via `yavio.identify()`. | Field | Type | Description | |-------|------|-------------| diff --git a/.specs/sdk/react-widget-sdk.md b/.specs/sdk/react-widget-sdk.md index 5a0f0f2..d595215 100644 --- a/.specs/sdk/react-widget-sdk.md +++ b/.specs/sdk/react-widget-sdk.md @@ -5,7 +5,7 @@ The React entry point (`@yavio/sdk/react`) provides a hook-based API for tracking user interactions inside ChatGPT App widgets. It shares the same method names as the server-side API (including `.identify()`) but sends events directly to the Yavio ingestion API over HTTP. > **Same API, Different Transport** -> Server-side: `ctx.yavio.step()` buffers events and flushes to the ingestion API in batches. Widget-side: `useYavio().step()` does the same — buffers events in memory and sends them via HTTP POST directly to the ingestion API. Configuration (API key, endpoint, traceId) is auto-injected by the server via `window.__YAVIO__`. The developer calls `useYavio()` with no arguments. +> Server-side: `yavio.step()` buffers events and flushes to the ingestion API in batches. Widget-side: `useYavio().step()` does the same — buffers events in memory and sends them via HTTP POST directly to the ingestion API. Configuration (API key, endpoint, traceId) is auto-injected by the server via `window.__YAVIO__`. The developer calls `useYavio()` with no arguments. ## 4.2 Developer-Facing API diff --git a/.specs/sdk/server-sdk.md b/.specs/sdk/server-sdk.md index d5d3ab1..057df02 100644 --- a/.specs/sdk/server-sdk.md +++ b/.specs/sdk/server-sdk.md @@ -70,22 +70,22 @@ For business-level telemetry that the proxy cannot infer automatically, develope server.registerTool("book_room", { description: "Book a hotel room", inputSchema: { userId: z.string(), roomType: z.string() }, -}, async (params, ctx) => { +}, async (params) => { // User identification: tie this session to a known user - ctx.yavio.identify(params.userId, { plan: "premium", country: "DE" }); + yavio.identify(params.userId, { plan: "premium", country: "DE" }); const rooms = await searchRooms(params); // Funnel step: mark progress through a user journey - ctx.yavio.step("rooms_found", { count: rooms.length }); + yavio.step("rooms_found", { count: rooms.length }); // Custom event: track anything not auto-captured - ctx.yavio.track("cache_hit", { provider: "memory" }); + yavio.track("cache_hit", { provider: "memory" }); const booking = await confirmBooking(rooms[0]); // Conversion: revenue attribution - ctx.yavio.conversion("booking_completed", { + yavio.conversion("booking_completed", { value: booking.price, currency: "EUR", }); @@ -94,8 +94,8 @@ server.registerTool("book_room", { }); // The deprecated tool() API is also supported -server.tool("book_room", async (params, ctx) => { - ctx.yavio.identify(params.userId); +server.tool("book_room", async (params) => { + yavio.identify(params.userId); // ... }); ``` @@ -284,7 +284,7 @@ The `traceId` is the correlation key that stitches server-side and widget-side e 2. AI platform invokes MCP tool → `withYavio()` proxy intercepts 3. Proxy generates traceId (e.g., `"tr_" + nanoid(21)`) and inherits `session_id` from the current MCP connection (see [Section 3.7](#37-session-lifecycle)) 4. Proxy starts AsyncLocalStorage context with traceId and sessionId -5. Tool handler executes (`ctx.yavio.step()` calls inherit traceId) +5. Tool handler executes (`yavio.step()` calls inherit traceId via AsyncLocalStorage) 6. If tool returns widget: proxy calls `POST /v1/widget-tokens` with traceId and sessionId to mint a short-lived JWT 7. Proxy injects `window.__YAVIO__` with traceId + sessionId + widget JWT + endpoint (API key stays on server) 8. Widget renders in iframe, initializes `useYavio()` which reads `window.__YAVIO__` diff --git a/package.json b/package.json index 27cc1f4..d0fed95 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,12 @@ "db:generate": "pnpm --filter @yavio/db db:generate", "db:studio": "pnpm --filter @yavio/db db:studio" }, + "pnpm": { + "overrides": { + "hono": ">=4.12.2", + "rollup": ">=4.59.0" + } + }, "devDependencies": { "@biomejs/biome": "^1.9.4", "dotenv-cli": "^11.0.0", diff --git a/packages/docs/content/docs/01-getting-started/01-quickstart.mdx b/packages/docs/content/docs/01-getting-started/01-quickstart.mdx index f44fa58..9688d59 100644 --- a/packages/docs/content/docs/01-getting-started/01-quickstart.mdx +++ b/packages/docs/content/docs/01-getting-started/01-quickstart.mdx @@ -81,21 +81,23 @@ That's it. Every tool call is now automatically captured with latency, status, p ## Track users and conversions -Inside any tool handler, use `ctx.yavio` to identify users and track custom events: +Inside any tool handler, import the `yavio` singleton to identify users and track custom events: ```ts title="server.ts" +import { withYavio, yavio } from "@yavio/sdk"; + instrumented.tool( "checkout", { userId: { type: "string" }, plan: { type: "string" } }, - async ({ userId, plan }, ctx) => { + async ({ userId, plan }) => { // Tie this session to a known user - ctx.yavio.identify(userId, { plan }); + yavio.identify(userId, { plan }); // Track a custom event - ctx.yavio.track("plan_selected", { plan }); + yavio.track("plan_selected", { plan }); // Record a conversion with monetary value - ctx.yavio.conversion("upgrade", { + yavio.conversion("upgrade", { value: 49, currency: "USD", }); diff --git a/packages/docs/content/docs/02-sdk/02-tracking-api.mdx b/packages/docs/content/docs/02-sdk/02-tracking-api.mdx index 03f0cd3..7242c03 100644 --- a/packages/docs/content/docs/02-sdk/02-tracking-api.mdx +++ b/packages/docs/content/docs/02-sdk/02-tracking-api.mdx @@ -5,30 +5,32 @@ description: Use .identify(), .step(), .track(), and .conversion() to capture us import { Callout } from "fumadocs-ui/components/callout"; -The tracking API is available inside tool handlers via `ctx.yavio`, and outside handlers via the `yavio` module singleton. +The tracking API is available via the `yavio` singleton, which you import from `@yavio/sdk`. ## Accessing the context ### Inside tool handlers -The `ctx.yavio` object is injected into every tool handler callback. Both the deprecated `tool()` and the modern `registerTool()` APIs are supported: +Import the `yavio` singleton and call its methods from within any tool handler wrapped by `withYavio()`: ```ts +import { withYavio, yavio } from "@yavio/sdk"; + // Using registerTool (recommended, MCP SDK v1.26+) instrumented.registerTool("search", { description: "Search for items", inputSchema: { query: { type: "string" } }, -}, async ({ query }, ctx) => { - ctx.yavio.identify("user_123"); - ctx.yavio.track("search_executed", { query }); +}, async ({ query }) => { + yavio.identify("user_123"); + yavio.track("search_executed", { query }); return { content: [{ type: "text", text: "Results..." }] }; }); // Using tool (deprecated but still supported) -instrumented.tool("search", { query: { type: "string" } }, async ({ query }, ctx) => { - ctx.yavio.identify("user_123"); - ctx.yavio.track("search_executed", { query }); +instrumented.tool("search", { query: { type: "string" } }, async ({ query }) => { + yavio.identify("user_123"); + yavio.track("search_executed", { query }); return { content: [{ type: "text", text: "Results..." }] }; }); @@ -36,13 +38,14 @@ instrumented.tool("search", { query: { type: "string" } }, async ({ query }, ctx ### Outside tool handlers -For code that runs outside of a tool handler (e.g., middleware or background tasks within the same async context), import the `yavio` singleton: +The `yavio` singleton also works in any code that runs within the same async context as a tool handler (e.g., helper functions called from a tool handler): ```ts import { yavio } from "@yavio/sdk"; -// Works within the same async context as a tool handler -yavio.track("background_task_completed"); +function processResults(results: Result[]) { + yavio.track("results_processed", { count: results.length }); +} ``` @@ -54,7 +57,7 @@ yavio.track("background_task_completed"); Ties the current session to a known user ID. Call this as early as possible in your tool handler. ```ts -ctx.yavio.identify(userId: string, traits?: Record): void +yavio.identify(userId: string, traits?: Record): void ``` ### Parameters @@ -71,7 +74,7 @@ ctx.yavio.identify(userId: string, traits?: Record): void - All subsequent events in the session include the `user_id` and traits. ```ts -ctx.yavio.identify("user_42", { +yavio.identify("user_42", { plan: "enterprise", company: "Acme Corp", }); @@ -84,7 +87,7 @@ See [User Identification](/docs/07-concepts/05-user-identification) for a deeper Records a named step in a multi-step workflow. Steps are auto-numbered within a trace for funnel analysis. ```ts -ctx.yavio.step(name: string, meta?: Record): void +yavio.step(name: string, meta?: Record): void ``` ### Parameters @@ -97,8 +100,8 @@ ctx.yavio.step(name: string, meta?: Record): void ### Example ```ts -instrumented.tool("wizard", { step: { type: "string" } }, async ({ step }, ctx) => { - ctx.yavio.step(step, { page: 1 }); +instrumented.tool("wizard", { step: { type: "string" } }, async ({ step }) => { + yavio.step(step, { page: 1 }); // ... handle the step @@ -113,7 +116,7 @@ Steps generate events with `event_type: "step"` and an auto-incrementing `step_s Records a custom event with arbitrary properties. ```ts -ctx.yavio.track(event: string, properties?: Record): void +yavio.track(event: string, properties?: Record): void ``` ### Parameters @@ -126,7 +129,7 @@ ctx.yavio.track(event: string, properties?: Record): void ### Example ```ts -ctx.yavio.track("document_generated", { +yavio.track("document_generated", { format: "pdf", pages: 12, }); @@ -139,7 +142,7 @@ Custom events use `event_type: "track"` with the event name in the `event_name` Records a conversion event with monetary value. Used for revenue attribution and ROI analysis. ```ts -ctx.yavio.conversion( +yavio.conversion( name: string, data: { value: number; @@ -161,7 +164,7 @@ ctx.yavio.conversion( ### Example ```ts -ctx.yavio.conversion("subscription_upgrade", { +yavio.conversion("subscription_upgrade", { value: 99, currency: "USD", meta: { plan: "enterprise", annual: true }, diff --git a/packages/docs/content/docs/07-concepts/01-events.mdx b/packages/docs/content/docs/07-concepts/01-events.mdx index e3a2b2a..77ab004 100644 --- a/packages/docs/content/docs/07-concepts/01-events.mdx +++ b/packages/docs/content/docs/07-concepts/01-events.mdx @@ -73,23 +73,23 @@ Fires on `tools/list` requests. Tracks how often clients request the tool catalo ## Server-Side Explicit Events -These events require developer code using `ctx.yavio` within a tool handler: +These events require developer code using `yavio` within a tool handler: ### step -Funnel progression point. Called via `ctx.yavio.step("step_name")`. Each step receives an auto-incrementing `step_sequence` for deterministic ordering. +Funnel progression point. Called via `yavio.step("step_name")`. Each step receives an auto-incrementing `step_sequence` for deterministic ordering. ### track -Generic custom event. Called via `ctx.yavio.track("event_name", properties)`. An escape hatch for anything not auto-captured. +Generic custom event. Called via `yavio.track("event_name", properties)`. An escape hatch for anything not auto-captured. ### conversion -Revenue attribution event. Called via `ctx.yavio.conversion("name", { value, currency })`. Powers ROI analytics. +Revenue attribution event. Called via `yavio.conversion("name", { value, currency })`. Powers ROI analytics. ### identify -User identification event. Called via `ctx.yavio.identify(userId, traits)`. Ties the session to a known user. +User identification event. Called via `yavio.identify(userId, traits)`. Ties the session to a known user. ## Widget Auto-Captured Events diff --git a/packages/docs/content/docs/07-concepts/03-traces.mdx b/packages/docs/content/docs/07-concepts/03-traces.mdx index b56a95f..2dabd05 100644 --- a/packages/docs/content/docs/07-concepts/03-traces.mdx +++ b/packages/docs/content/docs/07-concepts/03-traces.mdx @@ -12,7 +12,7 @@ Here's what happens when a user interacts with your MCP app: 1. User sends a prompt to the AI platform (ChatGPT, Claude, Cursor, etc.) 2. The AI platform invokes an MCP tool — `withYavio()` intercepts the call 3. The proxy generates a `trace_id` (e.g., `tr_` + a random ID) and inherits the `session_id` from the current MCP connection -4. The tool handler executes — any `ctx.yavio.step()` calls inherit the trace ID +4. The tool handler executes — any `yavio.step()` calls inherit the trace ID 5. If the tool returns a widget response, the proxy mints a short-lived widget JWT 6. The proxy injects `window.__YAVIO__` with the trace ID, session ID, widget JWT, and endpoint 7. The widget renders and initializes `useYavio()`, which reads the injected config @@ -53,7 +53,7 @@ Both server and widget events share the same `session_id`. The `trace_id` provid ## Step Sequencing -When you call `ctx.yavio.step()` or `useYavio().step()`, each step receives an auto-incrementing `step_sequence` number. On the server, the sequence starts at 0 and increments with each step call within the trace. On the widget, the sequence continues from where the server left off (the starting value is passed via the trace ID context). +When you call `yavio.step()` or `useYavio().step()`, each step receives an auto-incrementing `step_sequence` number. On the server, the sequence starts at 0 and increments with each step call within the trace. On the widget, the sequence continues from where the server left off (the starting value is passed via the trace ID context). This enables deterministic ordering even when multiple steps share the same millisecond timestamp, and ensures the combined funnel view displays steps in the correct order across server and widget boundaries. diff --git a/packages/docs/content/docs/07-concepts/05-user-identification.mdx b/packages/docs/content/docs/07-concepts/05-user-identification.mdx index 4b81cbd..e66f15e 100644 --- a/packages/docs/content/docs/07-concepts/05-user-identification.mdx +++ b/packages/docs/content/docs/07-concepts/05-user-identification.mdx @@ -21,7 +21,7 @@ Anonymous sessions are useful for: When you learn who the user is (e.g., from an auth token or user input), call `.identify()` to attach a user ID to the session: ```ts -ctx.yavio.identify("user_42", { +yavio.identify("user_42", { plan: "enterprise", company: "Acme Corp", }); @@ -44,9 +44,9 @@ Call `.identify()` as early as possible — ideally in the first tool handler th The `userId` is set once per session. If you call `.identify()` a second time with a **different** user ID, the call is ignored and a `YAVIO-1302` warning is logged. ```ts -ctx.yavio.identify("user_42"); // Sets user ID -ctx.yavio.identify("user_42"); // No-op (same ID) -ctx.yavio.identify("user_99"); // Ignored — YAVIO-1302 warning +yavio.identify("user_42"); // Sets user ID +yavio.identify("user_42"); // No-op (same ID) +yavio.identify("user_99"); // Ignored — YAVIO-1302 warning ``` This prevents accidental identity conflicts in multi-tool sessions. @@ -56,8 +56,8 @@ This prevents accidental identity conflicts in multi-tool sessions. User traits are merged across calls. Each `.identify()` call adds to (but doesn't replace) existing traits: ```ts -ctx.yavio.identify("user_42", { plan: "pro" }); -ctx.yavio.identify("user_42", { company: "Acme" }); +yavio.identify("user_42", { plan: "pro" }); +yavio.identify("user_42", { company: "Acme" }); // Session now has: { plan: "pro", company: "Acme" } ``` @@ -65,8 +65,8 @@ ctx.yavio.identify("user_42", { company: "Acme" }); If the same trait key is set twice, the later value wins: ```ts -ctx.yavio.identify("user_42", { plan: "free" }); -ctx.yavio.identify("user_42", { plan: "pro" }); +yavio.identify("user_42", { plan: "free" }); +yavio.identify("user_42", { plan: "pro" }); // Session now has: { plan: "pro" } ``` diff --git a/packages/sdk/src/__tests__/integration/e2e.test.ts b/packages/sdk/src/__tests__/integration/e2e.test.ts index f59c287..ab9b83b 100644 --- a/packages/sdk/src/__tests__/integration/e2e.test.ts +++ b/packages/sdk/src/__tests__/integration/e2e.test.ts @@ -3,6 +3,7 @@ import type { AddressInfo } from "node:net"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"; import type { YavioConfig } from "../../core/types.js"; +import { createYavioContext } from "../../server/context.js"; import { _resetSessionMap, createProxy } from "../../server/proxy.js"; import { HttpTransport } from "../../transport/http.js"; @@ -85,18 +86,11 @@ describe("End-to-end: proxy → tool call → HTTP transport → mock ingest", ( }; const proxy = createProxy(server, config, transport, "0.0.1"); + const yavioCtx = createYavioContext(); let handlerExecuted = false; - proxy.tool("search_rooms", { query: { type: "string" } as never }, (args, extra) => { + proxy.tool("search_rooms", { query: { type: "string" } as never }, (args) => { handlerExecuted = true; - const yavioCtx = (extra as Record).yavio as { - identify: (id: string, traits?: Record) => void; - step: (name: string, meta?: Record) => void; - track: (event: string, props?: Record) => void; - }; - expect(yavioCtx).toBeDefined(); - expect(typeof yavioCtx.identify).toBe("function"); - yavioCtx.identify("user-42", { plan: "premium" }); yavioCtx.step("rooms_found", { count: 3 }); yavioCtx.track("filter_applied", { type: "price" }); @@ -222,6 +216,7 @@ describe("End-to-end: proxy → tool call → HTTP transport → mock ingest", ( }; const proxy = createProxy(server, config, transport, "0.0.1"); + const yavioCtx = createYavioContext(); let handlerExecuted = false; proxy.registerTool( "search_hotels", @@ -229,17 +224,9 @@ describe("End-to-end: proxy → tool call → HTTP transport → mock ingest", ( description: "Search for hotels", inputSchema: { query: { type: "string" } as never }, }, - (args, extra) => { + (args) => { handlerExecuted = true; - const yavioCtx = (extra as Record).yavio as { - identify: (id: string, traits?: Record) => void; - step: (name: string, meta?: Record) => void; - track: (event: string, props?: Record) => void; - }; - expect(yavioCtx).toBeDefined(); - expect(typeof yavioCtx.identify).toBe("function"); - yavioCtx.identify("user-99", { tier: "gold" }); yavioCtx.step("hotels_found", { count: 5 }); yavioCtx.track("filter_applied", { type: "location" }); @@ -303,11 +290,9 @@ describe("End-to-end: proxy → tool call → HTTP transport → mock ingest", ( }; const proxy = createProxy(server, config, transport, "0.0.1"); - proxy.tool("contact_tool", (extra) => { - const ctx = (extra as Record).yavio as { - track: (event: string, props?: Record) => void; - }; - ctx.track("contact_submitted", { email: "test@example.com", phone: "555-123-4567" }); + const yavioCtx = createYavioContext(); + proxy.tool("contact_tool", () => { + yavioCtx.track("contact_submitted", { email: "test@example.com", phone: "555-123-4567" }); return { content: [{ type: "text" as const, text: "ok" }] }; }); diff --git a/packages/sdk/src/__tests__/proxy.test.ts b/packages/sdk/src/__tests__/proxy.test.ts index d4301a5..a59b4c1 100644 --- a/packages/sdk/src/__tests__/proxy.test.ts +++ b/packages/sdk/src/__tests__/proxy.test.ts @@ -68,26 +68,6 @@ describe("createProxy", () => { // The tool was registered — we can verify via the server's internal state }); - it("injects ctx.yavio into tool handler extra parameter", async () => { - const server = new McpServer({ name: "test", version: "1.0" }); - const transport = createMockTransport(); - const proxy = createProxy(server, testConfig, transport, "0.0.1"); - - let capturedYavio: unknown = null; - - // Register with the proxy — the callback gets wrapped - proxy.tool("my_tool", (extra) => { - capturedYavio = (extra as Record).yavio; - return { content: [{ type: "text", text: "ok" }] }; - }); - - // The actual wrapping happens — we need to verify the handler was replaced - // Since McpServer stores the handler internally, we verify through integration - // tests (Step 7). Here we verify the proxy returns a valid McpServer-like object. - expect(typeof proxy.tool).toBe("function"); - expect(typeof proxy.connect).toBe("function"); - }); - it("preserves non-intercepted properties", () => { const server = new McpServer({ name: "test", version: "1.0" }); const transport = createMockTransport(); diff --git a/packages/sdk/src/core/types.ts b/packages/sdk/src/core/types.ts index 2807360..647246f 100644 --- a/packages/sdk/src/core/types.ts +++ b/packages/sdk/src/core/types.ts @@ -21,7 +21,7 @@ export interface WithYavioOptions { capture?: Partial; } -/** The tracking context injected as `ctx.yavio` in tool handlers. */ +/** The tracking context available via the `yavio` singleton. */ export interface YavioContext { identify(userId: string, traits?: Record): void; step(name: string, meta?: Record): void; diff --git a/packages/sdk/src/server/index.ts b/packages/sdk/src/server/index.ts index d2eed17..339c8ae 100644 --- a/packages/sdk/src/server/index.ts +++ b/packages/sdk/src/server/index.ts @@ -11,8 +11,8 @@ export const SDK_VERSION = "0.0.1"; /** * Wrap an MCP server with Yavio instrumentation. * - * Auto-captures tool calls and injects `ctx.yavio` into tool handlers - * for explicit tracking (`.identify()`, `.step()`, `.track()`, `.conversion()`). + * Auto-captures tool calls and provides explicit tracking via the `yavio` singleton + * (`.identify()`, `.step()`, `.track()`, `.conversion()`). * * If no API key is found, returns the original server unchanged (transparent no-op). */ @@ -44,11 +44,13 @@ export function withYavio(server: T, options?: WithYavioOpt } /** - * Module singleton for tracking outside of tool handler context. + * Module singleton for explicit tracking inside tool handlers. * - * When used inside a `runInContext()` scope (e.g., within a tool handler), - * events are associated with the current trace and session. - * When used outside context, calls are no-ops. + * Import this and call `yavio.identify()`, `yavio.track()`, etc. from within + * any tool handler wrapped by `withYavio()`. Uses `AsyncLocalStorage` to + * associate events with the current trace and session automatically. + * + * Calls outside a tool handler context are silently ignored (no-ops). */ export const yavio: YavioContext = createYavioContext(); diff --git a/packages/sdk/src/server/proxy.ts b/packages/sdk/src/server/proxy.ts index 493ab59..6c6bc94 100644 --- a/packages/sdk/src/server/proxy.ts +++ b/packages/sdk/src/server/proxy.ts @@ -8,7 +8,7 @@ import { generateSessionId, generateTraceId } from "../core/ids.js"; import { detectPlatform } from "../core/platform.js"; import type { SessionState, YavioConfig } from "../core/types.js"; import type { Transport } from "../transport/types.js"; -import { type RequestStore, createYavioContext, runInContext } from "./context.js"; +import { type RequestStore, runInContext } from "./context.js"; import { type MintResult, mintWidgetToken } from "./token.js"; /** Cached widget token with parsed expiry for reuse across tool calls. */ @@ -50,7 +50,7 @@ async function getWidgetToken( /** * Wrap a tool callback with Yavio instrumentation. * - * Handles lazy platform detection, trace/session context, ctx.yavio injection, + * Handles lazy platform detection, trace/session context, * latency measurement, and tool_call event emission. */ function wrapToolCallback( @@ -128,11 +128,6 @@ function wrapToolCallback( sdkVersion, }; - // Inject ctx.yavio into the extra parameter (already extracted above) - if (extra && typeof extra === "object") { - (extra as Record).yavio = createYavioContext(store); - } - const startTime = performance.now(); try { const result = await runInContext(store, () => originalCb(...cbArgs)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a22325..905489f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,10 @@ settings: excludeLinksFromLockfile: false injectWorkspacePackages: true +overrides: + hono: '>=4.12.2' + rollup: '>=4.59.0' + importers: .: @@ -993,7 +997,7 @@ packages: resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} peerDependencies: - hono: ^4 + hono: '>=4.12.2' '@hookform/resolvers@5.2.2': resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} @@ -2161,128 +2165,128 @@ packages: react-redux: optional: true - '@rollup/rollup-android-arm-eabi@4.57.1': - resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.57.1': - resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.57.1': - resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.57.1': - resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.57.1': - resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.57.1': - resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': - resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.57.1': - resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.57.1': - resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.57.1': - resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.57.1': - resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-musl@4.57.1': - resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.57.1': - resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-musl@4.57.1': - resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.57.1': - resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.57.1': - resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.57.1': - resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.57.1': - resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.57.1': - resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] - '@rollup/rollup-openbsd-x64@4.57.1': - resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.57.1': - resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.57.1': - resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.57.1': - resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.57.1': - resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.57.1': - resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} cpu: [x64] os: [win32] @@ -3530,8 +3534,8 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - hono@4.12.0: - resolution: {integrity: sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==} + hono@4.12.3: + resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} engines: {node: '>=16.9.0'} html-encoding-sniffer@6.0.0: @@ -4510,8 +4514,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.57.1: - resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5521,9 +5525,9 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@hono/node-server@1.19.9(hono@4.12.0)': + '@hono/node-server@1.19.9(hono@4.12.3)': dependencies: - hono: 4.12.0 + hono: 4.12.3 '@hookform/resolvers@5.2.2(react-hook-form@7.71.1(react@19.2.4))': dependencies: @@ -5678,7 +5682,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.9(hono@4.12.0) + '@hono/node-server': 1.19.9(hono@4.12.3) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -5688,7 +5692,7 @@ snapshots: eventsource-parser: 3.0.6 express: 5.2.1 express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.12.0 + hono: 4.12.3 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -6742,79 +6746,79 @@ snapshots: react: 19.2.4 react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) - '@rollup/rollup-android-arm-eabi@4.57.1': + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true - '@rollup/rollup-android-arm64@4.57.1': + '@rollup/rollup-android-arm64@4.59.0': optional: true - '@rollup/rollup-darwin-arm64@4.57.1': + '@rollup/rollup-darwin-arm64@4.59.0': optional: true - '@rollup/rollup-darwin-x64@4.57.1': + '@rollup/rollup-darwin-x64@4.59.0': optional: true - '@rollup/rollup-freebsd-arm64@4.57.1': + '@rollup/rollup-freebsd-arm64@4.59.0': optional: true - '@rollup/rollup-freebsd-x64@4.57.1': + '@rollup/rollup-freebsd-x64@4.59.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.57.1': + '@rollup/rollup-linux-arm-musleabihf@4.59.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.57.1': + '@rollup/rollup-linux-arm64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.57.1': + '@rollup/rollup-linux-arm64-musl@4.59.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.57.1': + '@rollup/rollup-linux-loong64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.57.1': + '@rollup/rollup-linux-loong64-musl@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.57.1': + '@rollup/rollup-linux-ppc64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.57.1': + '@rollup/rollup-linux-ppc64-musl@4.59.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.57.1': + '@rollup/rollup-linux-riscv64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.57.1': + '@rollup/rollup-linux-riscv64-musl@4.59.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.57.1': + '@rollup/rollup-linux-s390x-gnu@4.59.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.57.1': + '@rollup/rollup-linux-x64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-x64-musl@4.57.1': + '@rollup/rollup-linux-x64-musl@4.59.0': optional: true - '@rollup/rollup-openbsd-x64@4.57.1': + '@rollup/rollup-openbsd-x64@4.59.0': optional: true - '@rollup/rollup-openharmony-arm64@4.57.1': + '@rollup/rollup-openharmony-arm64@4.59.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.57.1': + '@rollup/rollup-win32-arm64-msvc@4.59.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.57.1': + '@rollup/rollup-win32-ia32-msvc@4.59.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.57.1': + '@rollup/rollup-win32-x64-gnu@4.59.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.57.1': + '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true '@scalar/helpers@0.2.12': {} @@ -7845,7 +7849,7 @@ snapshots: dependencies: magic-string: 0.30.21 mlly: 1.8.0 - rollup: 4.57.1 + rollup: 4.59.0 foreach@2.0.6: {} @@ -8174,7 +8178,7 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 - hono@4.12.0: {} + hono@4.12.3: {} html-encoding-sniffer@6.0.0: dependencies: @@ -9435,35 +9439,35 @@ snapshots: rfdc@1.4.1: {} - rollup@4.57.1: + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.57.1 - '@rollup/rollup-android-arm64': 4.57.1 - '@rollup/rollup-darwin-arm64': 4.57.1 - '@rollup/rollup-darwin-x64': 4.57.1 - '@rollup/rollup-freebsd-arm64': 4.57.1 - '@rollup/rollup-freebsd-x64': 4.57.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 - '@rollup/rollup-linux-arm-musleabihf': 4.57.1 - '@rollup/rollup-linux-arm64-gnu': 4.57.1 - '@rollup/rollup-linux-arm64-musl': 4.57.1 - '@rollup/rollup-linux-loong64-gnu': 4.57.1 - '@rollup/rollup-linux-loong64-musl': 4.57.1 - '@rollup/rollup-linux-ppc64-gnu': 4.57.1 - '@rollup/rollup-linux-ppc64-musl': 4.57.1 - '@rollup/rollup-linux-riscv64-gnu': 4.57.1 - '@rollup/rollup-linux-riscv64-musl': 4.57.1 - '@rollup/rollup-linux-s390x-gnu': 4.57.1 - '@rollup/rollup-linux-x64-gnu': 4.57.1 - '@rollup/rollup-linux-x64-musl': 4.57.1 - '@rollup/rollup-openbsd-x64': 4.57.1 - '@rollup/rollup-openharmony-arm64': 4.57.1 - '@rollup/rollup-win32-arm64-msvc': 4.57.1 - '@rollup/rollup-win32-ia32-msvc': 4.57.1 - '@rollup/rollup-win32-x64-gnu': 4.57.1 - '@rollup/rollup-win32-x64-msvc': 4.57.1 + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 router@2.2.0: @@ -9780,7 +9784,7 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2) resolve-from: 5.0.0 - rollup: 4.57.1 + rollup: 4.59.0 source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 @@ -9998,7 +10002,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.57.1 + rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.11 @@ -10014,7 +10018,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.57.1 + rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.2.3