From 37be6230f94c4777cedb32202bc23f00f786228d Mon Sep 17 00:00:00 2001 From: Ali Ibrahim Jr <48456829+IBJunior@users.noreply.github.com> Date: Sun, 8 Nov 2026 20:33:29 +0100 Subject: [PATCH] feat(mcp): move to @langchain/mcp-adapters v2 on MCP SDK v2 Upgrade the MCP adapter to 2.x, built on @modelcontextprotocol/client, and drop @modelcontextprotocol/sdk (v1) everywhere, OAuth included. - Connector: MCPAdapter + listTools(). Tools keep the adapter's default `server_tool` names; the disclosure package builds the same names via prefixedToolName(), so the connector renames nothing. - Elicitation is off per server: its interrupt is one the approval flow cannot answer, so the thread would hang. - The Connections probe opens its session through the connector, which is now the only file using the adapter. - OAuth: the check route delegates to the SDK's auth(), which picks the scope from the server instead of a hardcoded "read write" (Keycloak rejected registration with it), and adds the RFC 8707 resource. The callback forwards all params so the SDK can check `iss` (RFC 9207). The provider implements invalidateCredentials(). - Saving an HTTP server probes it and records its OAuth status, so the Connect button appears; a 401 at connect time records REQUIRED or EXPIRED and returns auth_required. - Bump @langchain/core and @langchain/langgraph to the adapter's peers. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 25 +- docs/ARCHITECTURE.md | 34 +- docs/MCP_DISCLOSURE.md | 47 +- docs/OAUTH.md | 83 +- eval/cases/mcp.cases.mts | 2 +- eval/mcpMocks.mts | 10 +- package.json | 8 +- packages/mcp-disclosure/package.json | 4 +- .../mcp-disclosure/src/catalogTools.test.ts | 2 +- packages/mcp-disclosure/src/catalogTools.ts | 6 +- .../mcp-disclosure/src/middleware.test.ts | 65 +- packages/mcp-disclosure/src/middleware.ts | 5 +- packages/mcp-disclosure/src/policy.test.ts | 16 +- packages/mcp-disclosure/src/policy.ts | 14 +- packages/mcp-disclosure/src/search.test.ts | 10 +- packages/mcp-disclosure/src/search.ts | 4 +- packages/mcp-disclosure/src/types.ts | 9 +- pnpm-lock.yaml | 753 +++--------------- pnpm-workspace.yaml | 1 - src/app/api/mcp-servers/route.ts | 12 + .../api/oauth/callback/[serverId]/route.ts | 7 +- src/app/api/oauth/check/[serverId]/route.ts | 91 +-- src/components/connections/ServerForm.tsx | 2 +- src/hooks/useMCPServers.ts | 14 +- src/lib/agent/mcp.ts | 74 +- src/lib/mcp/authFailure.test.ts | 27 + src/lib/mcp/authFailure.ts | 29 + src/lib/mcp/connector.ts | 32 +- src/lib/mcp/oauth-detection.ts | 22 + src/lib/mcp/oauth-provider.ts | 24 +- src/lib/mcp/serverConfig.test.ts | 10 +- src/lib/mcp/serverConfig.ts | 26 +- src/lib/repositories/mcpServerRepository.ts | 24 + src/lib/tracing/genAiTracer.test.ts | 8 +- 34 files changed, 555 insertions(+), 945 deletions(-) create mode 100644 src/lib/mcp/authFailure.test.ts create mode 100644 src/lib/mcp/authFailure.ts diff --git a/AGENTS.md b/AGENTS.md index 00bae10..decf2e2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,7 +93,8 @@ This is a Next.js 15 fullstack AI agent chat application using LangGraph.js with first initializes the Postgres checkpointer, then assembles the model, tools, and middleware. - **MCP Integration**: MCP servers are stored in Postgres; the agent sees their tools through the disclosure middleware and connects on demand (see "Progressive disclosure" below). - `src/lib/agent/mcp.ts` now holds only the Connections page's tool-listing probe. + `src/lib/agent/mcp.ts` now holds only the Connections page's tool-listing probe, which opens + its session through the connector. - **Persistent Memory**: Uses LangGraph's Postgres checkpointer for conversation history - **Built-in finance tools** (always registered server-side in `index.ts`, provider-agnostic Zod): - `src/lib/agent/tools/finance.ts` — `log_expense` (mutating), `query_transactions` (read-only, @@ -354,17 +355,27 @@ Instructions loaded on demand, in the standard `SKILL.md` format — full detail spawning a process), so nothing connects on page load and no tool count is stored — a stored count goes stale while looking current. Failures are typed (`auth_required`, `invalid_config`, `timeout`, `connect_failed`), never an empty list. -- `toServerConfig()` (`src/lib/mcp/serverConfig.ts`) turns a row into a client config for BOTH the - agent (`src/lib/mcp/connector.ts`) and the probe, so the page connects exactly the way the - agent will. -- Tool names are prefixed `server__tool`; server names are restricted to `[A-Za-z0-9_-]` in the form +- `toServerConfig()` (`src/lib/mcp/serverConfig.ts`) turns a row into a client config, and + `openSession()` (`src/lib/mcp/connector.ts`) connects it for BOTH the agent and the probe, so + the page connects exactly the way the agent will. +- **Adapter is `@langchain/mcp-adapters` 2.x on MCP SDK v2 (`@modelcontextprotocol/client`)**; + `@modelcontextprotocol/sdk` (v1) is gone, OAuth code included. Tool names are the adapter's + default `server_tool`, and the disclosure package builds the same names (`prefixedToolName()`), + so the connector renames nothing. The one default overridden is elicitation (`elicitation: false` + per server — its interrupt is one the approval flow cannot answer, so the thread would hang). v2 config is strict: unknown + keys throw at construction, and stdio `args` is required. +- Tool names are prefixed `server_tool`; server names are restricted to `[A-Za-z0-9_-]` in the form because the prefix becomes part of a provider tool name. -- HTTP servers may require OAuth authentication - see [docs/OAUTH.md](docs/OAUTH.md) +- HTTP servers may require OAuth authentication - see [docs/OAUTH.md](docs/OAUTH.md). Saving an + HTTP server probes it and records its OAuth status before responding (the Connect button only + renders for `REQUIRED` / `EXPIRED` / `UNKNOWN`, so a NULL status hid sign-in entirely), and a + 401 at connect time records `REQUIRED`/`EXPIRED` and returns `auth_required`. The check route + delegates to the SDK's `auth()` — never hardcode a scope in the client metadata. - **Progressive disclosure** ([docs/MCP_DISCLOSURE.md](docs/MCP_DISCLOSURE.md), read it before changing this code): the model sees a short list of capabilities plus `load_tools`, and a tool's definition only once a search has loaded it; nothing connects until a tool is called. The logic is the workspace package `packages/mcp-disclosure`, which must not import app code or the MCP - client library; `src/lib/mcp/connector.ts` is the only agent-path file using the adapters. + client library; `src/lib/mcp/connector.ts` is the only file using the adapters. ### Tool Approval Workflow diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 05e9c95..eb6cb53 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -142,34 +142,20 @@ async function buildAgent(cfg?: AgentConfigOptions) { - Model configuration (`createChatModel`) and system prompt - Checkpointer integration for persistence -### 2. MCP Integration (`src/lib/agent/mcp.ts`) +### 2. MCP Integration (`src/lib/mcp/connector.ts`) -Manages dynamic tool loading from Model Context Protocol servers. - -```typescript -export async function createMCPClient(): Promise { - const mcpServers = await getMCPServerConfigs(); // From database - - if (Object.keys(mcpServers).length === 0) { - return null; - } - - const client = new MultiServerMCPClient({ - mcpServers: mcpServers, - throwOnLoadError: false, - prefixToolNameWithServerName: true, // Prevent conflicts - }); - - return client; -} -``` +MCP servers are disclosed progressively (see [MCP_DISCLOSURE.md](MCP_DISCLOSURE.md)): the model +sees a capability list and `load_tools`, and a server connects only when one of its tools is +called. `openSession()` in `connector.ts` is the one place that uses `@langchain/mcp-adapters` +(2.x, on MCP SDK v2): it builds a single-server `MCPAdapter` from the stored row via +`toServerConfig()`. **Key Features:** - Database-driven MCP server configuration - Support for stdio and HTTP transports -- Tool name prefixing for conflict prevention -- Graceful error handling for failed servers +- Tool names prefixed `server_tool` (the adapter default) for conflict prevention +- Typed connection failures (`auth_required`, `invalid_config`, `timeout`, `connect_failed`) ### 3. Streaming Service (`src/services/agentService.ts`) @@ -443,7 +429,7 @@ client sends one decision per call id (`decisions={"":"allow"|"deny"}`), ### Server Configuration Flow ``` -Database MCPServer → getMCPServerConfigs() → MultiServerMCPClient → Agent Tools +Database MCPServer → toServerConfig() → MCPAdapter (one per session) → Agent Tools ``` ### Configuration Examples @@ -477,7 +463,7 @@ Database MCPServer → getMCPServerConfigs() → MultiServerMCPClient → Agent ### Tool Loading Process 1. **Database Query**: Fetch enabled MCP servers -2. **Client Creation**: Initialize MultiServerMCPClient +2. **Client Creation**: Open a single-server `MCPAdapter` session on first tool call 3. **Tool Discovery**: Get available tools from each server 4. **Name Prefixing**: Add server name prefix to prevent conflicts 5. **Agent Binding**: Bind tools to language model diff --git a/docs/MCP_DISCLOSURE.md b/docs/MCP_DISCLOSURE.md index 792df14..587e444 100644 --- a/docs/MCP_DISCLOSURE.md +++ b/docs/MCP_DISCLOSURE.md @@ -16,16 +16,29 @@ The logic lives in the workspace package [`packages/mcp-disclosure`](../packages (`@agentailor/mcp-disclosure`). It imports nothing from the app and **nothing from the MCP client library** — only MCP protocol shapes (`tools/list`) and LangChain tool types. The app supplies: -| App file | Role | -| -------------------------- | --------------------------------------------------------------------- | -| `src/lib/mcp/connector.ts` | The only agent-path file using `@langchain/mcp-adapters`: one session | -| `src/lib/mcp/pool.ts` | The process-wide pool singleton and the middleware's `resolveTool` | -| `src/lib/mcp/catalog.ts` | Catalog refresh and loading, through the connector | -| `src/lib/agent/index.ts` | Registers core tools only, plus the middleware | - -Moving to another MCP client (the v2 adapters) means rewriting `connector.ts`; the catalog rows, -the pool and the middleware are unaffected. The Connections page's "list tools" probe -(`listServerTools`) still uses the adapter directly and is left for that migration. +| App file | Role | +| -------------------------- | ------------------------------------------------------------------ | +| `src/lib/mcp/connector.ts` | The only file using `@langchain/mcp-adapters`: one session | +| `src/lib/mcp/pool.ts` | The process-wide pool singleton and the middleware's `resolveTool` | +| `src/lib/mcp/catalog.ts` | Catalog refresh and loading, through the connector | +| `src/lib/agent/index.ts` | Registers core tools only, plus the middleware | + +Moving to another MCP client means rewriting `connector.ts`; the catalog rows, the pool and the +middleware are unaffected. The Connections page's "list tools" probe (`listServerTools`) opens a +session through the connector too, so it connects exactly as the agent does. + +The connector runs on `@langchain/mcp-adapters` 2.x (MCP TypeScript SDK v2, +`@modelcontextprotocol/client`), and the package is built to be a pass-through over it: + +- **Tool names are the adapter's own.** v2 names tools `{server}_{tool}` by default, and + `prefixedToolName()` in `policy.ts` builds the same name for the catalog's stand-ins, the search + index and `activeTools`. The tool the model is shown and the tool that runs carry one name, so + the connector keys its tools by the adapter's name and `getTool` / `resolveTool` take the name + as the model called it. A server name may itself contain `_`; `createToolServerIndex` resolves + that by preferring the server whose catalog lists the remainder. +- **Elicitation is the one default overridden.** v2 raises a server's elicitation request as a LangGraph interrupt. The approval + flow only answers `humanInTheLoopMiddleware` interrupts, so such a pause would hang the thread. + `toServerConfig` sets `elicitation: false` on every server until it is supported (Deferred). ## Overview @@ -71,7 +84,7 @@ sequenceDiagram A->>M: load_tools("search pages") M-->>A: matches loaded Note over M: no connection yet - A->>M: notion__search(...) + A->>M: notion_search(...) M->>P: resolveTool P->>S: connect (first call) M->>S: run tool @@ -80,8 +93,10 @@ sequenceDiagram ## Rules for changing this code -- **Keep `connector.ts` the only agent-path adapter file.** The catalog stores protocol data +- **Keep `connector.ts` the only adapter file.** The catalog stores protocol data (`tools/list`), never adapter-converted schemas. +- **Don't rename tools.** `prefixedToolName()` must match the adapter's default naming; if the + adapter ever changes it, change that one function, not the connector. - **`load_tools` returns a plain string; `wrapToolCall` applies the state change.** The chat UI streams a tool's raw return value, so a tool returning a `Command` showed up as a serialized object. `planLoadTools` is the single source for both. @@ -171,7 +186,7 @@ interrupt carrying the authorization URL, with a Connect button in the chat) is No MCP tool is registered with `createAgent`. Instead: 1. **Stand-ins from the catalog.** `catalogTools()` builds one tool per cached entry - (`server__tool`, its description, and its `inputSchema` exactly as the server listed it). `wrapModelCall` adds the loaded stand-ins to `request.tools` — + (`server_tool`, its description, and its `inputSchema` exactly as the server listed it). `wrapModelCall` adds the loaded stand-ins to `request.tools` — LangChain accepts tools added there as long as a `wrapToolCall` runs them. A stand-in throws if it is ever run directly. 2. **The real tool on call.** `wrapToolCall` asks `resolveTool(serverId, toolName)` for the real tool and passes it on as `request.tool`, which the tool node runs; result formatting stays the @@ -229,7 +244,7 @@ Code defaults in `packages/mcp-disclosure/src/types.ts`; the pool's idle timeout ## State (per thread, via the checkpointer) -`activeTools: string[]` holds `server__tool` names in load order, through an appending, de-duplicating +`activeTools: string[]` holds `server_tool` names in load order, through an appending, de-duplicating reducer, so two `load_tools` calls in one step both apply. A tool whose server was disabled or removed is left out when the list is built; no state change is needed. @@ -299,6 +314,6 @@ model was offered (see [OBSERVABILITY.md](OBSERVABILITY.md)). - Resuming a run after sign-in in place (an interrupt with the authorization URL), instead of asking the user to reconnect on the Connections page and ask again. - Provider-native tool search (`defer_loading`). The same tool could sit in front of it. -- Moving the Connections page probe (`listServerTools`) behind the connector — with the v2 - adapter migration. +- MCP elicitation: answering a server's `mcp_elicitation` interrupt in the chat, beside the + approval card. Disabled per server until then. - Background refresh jobs. diff --git a/docs/OAUTH.md b/docs/OAUTH.md index edf3850..cef83dc 100644 --- a/docs/OAUTH.md +++ b/docs/OAUTH.md @@ -4,13 +4,18 @@ Some MCP HTTP servers require OAuth 2.0/2.1 authentication. This document explai ## Overview -OAuth support is implemented using a **lazy detection** approach: - -1. When a user adds an HTTP MCP server, OAuth status is initially "UNKNOWN" -2. The user clicks "Connect" to check if OAuth is required +1. When an HTTP server is **saved** (added, or its URL changed), the app probes it before + responding and records `REQUIRED`, `NOT_REQUIRED`, or `UNKNOWN` if it could not be reached. + This is what makes the Connect button appear: it renders for `REQUIRED`, `EXPIRED` and + `UNKNOWN` only, so a server left with no status never offered sign-in. +2. The user clicks "Connect" ("Check auth" for `UNKNOWN`) 3. If required, the user is redirected to the authorization server 4. Upon successful authorization, tokens are stored and the server becomes usable +A connection that later fails with a 401 (an `UnauthorizedError` or HTTP 401 anywhere in the +error's cause chain, `src/lib/mcp/authFailure.ts`) records `REQUIRED` — or `EXPIRED` for a server +that was `CONNECTED` — and reports `auth_required`, so the page offers Connect again. + ## OAuth Flow ``` @@ -41,7 +46,7 @@ OAuth support is implemented using a **lazy detection** approach: | Status | Description | UI Indicator | | -------------- | ---------------------------------------- | ------------ | -| `UNKNOWN` | OAuth requirement not yet checked | None | +| `UNKNOWN` | Probe on save could not reach the server | "Check auth" | | `NOT_REQUIRED` | Server does not require OAuth | None | | `REQUIRED` | OAuth required, user needs to connect | Yellow badge | | `CONNECTED` | Successfully authenticated | Green badge | @@ -52,7 +57,7 @@ OAuth support is implemented using a **lazy detection** approach: | File | Purpose | | ------------------------------------------------ | ------------------------------------------- | | `src/lib/mcp/oauth-detection.ts` | Detects OAuth requirements via 401 response | -| `src/lib/mcp/oauth-provider.ts` | Implements `OAuthClientProvider` interface | +| `src/lib/mcp/oauth-provider.ts` | Implements `OAuthClientProvider` (SDK v2) | | `src/app/api/oauth/check/[serverId]/route.ts` | Initiates OAuth flow, returns auth URL | | `src/app/api/oauth/callback/[serverId]/route.ts` | Handles OAuth callback, exchanges tokens | | `src/components/OAuthStatusBadge.tsx` | Displays OAuth status in server list | @@ -62,48 +67,62 @@ OAuth support is implemented using a **lazy detection** approach: The `MCPServer` model includes these OAuth-related fields: -```prisma -model MCPServer { - // ... other fields - requiresAuth Boolean? @default(false) - authTokens Json? // { access_token, refresh_token, expires_at, ... } - clientInfo Json? // { client_id, client_secret, ... } - codeVerifier String? // PKCE code verifier for ongoing flow - oauthStatus String? @default("UNKNOWN") -} -``` +Columns on `mcp_server` (`src/lib/database/schema.ts`): + +| Column | Holds | +| --------------- | -------------------------------------------------------- | +| `requires_auth` | Set true once OAuth completes | +| `auth_tokens` | `{ access_token, refresh_token, expires_at, issuer, … }` | +| `client_info` | The dynamically registered client (`client_id`, …) | +| `code_verifier` | PKCE verifier for the flow in progress | +| `oauth_status` | One of the statuses above; NULL until first probed | ## How It Works ### 1. OAuth Detection -When the user clicks "Connect", the app makes a request to the MCP server. If the server responds with: - -- **401 Unauthorized** + `WWW-Authenticate: Bearer` header → OAuth is required -- **200 OK** or other status → OAuth is not required +On save, and again when the user clicks "Connect", the app sends a GET to the MCP server +(5s timeout). If the server responds with: -### 2. Client Registration +- **401 Unauthorized** → OAuth is required; the `WWW-Authenticate` header's `resource_metadata` + and `scope` are kept for the next step +- **any other status** → OAuth is not required +- **no response** → `UNKNOWN` -If OAuth is required and no client is registered: +### 2. Registration and authorization (the SDK's `auth()`) -1. Discover protected resource metadata from the server -2. Discover authorization server metadata -3. Dynamically register a client (if supported) -4. Store `clientInfo` for future use +The check route calls `auth()` from `@modelcontextprotocol/client` rather than running the steps +itself, so the authorization request is built exactly the way the callback's token exchange +expects. It: -### 3. Authorization - -1. Generate authorization URL with PKCE (`code_verifier` stored in database) -2. Redirect user to authorization server -3. User grants permission +1. Discovers the protected resource and authorization server metadata +2. Picks the **scope**: the challenge's `scope`, else the resource's `scopes_supported`, plus + `offline_access` when the server offers it. The client metadata carries no scope of its own — + a hardcoded `read write` made Keycloak's client-scope policy refuse registration (HTTP 403). +3. Dynamically registers a client if none is stored, and stores `clientInfo` +4. Uses a stored refresh token if one still works (the route then reports `CONNECTED`) +5. Otherwise builds the authorization URL with PKCE and the RFC 8707 `resource` parameter. The + provider's `redirectToAuthorization` only records that URL; the route returns it and the + browser goes there. ### 4. Token Exchange 1. Authorization server redirects to `/api/oauth/callback/[serverId]` -2. Exchange authorization code for tokens +2. Exchange the code for tokens with `transport.finishAuth(searchParams)` — all the callback + params, not just `code`, because the SDK checks the `iss` parameter (RFC 9207) when the + authorization server sends one 3. Store tokens in database 4. Update status to `CONNECTED` +### 5. Invalidation + +When the SDK finds stored credentials rejected it calls `invalidateCredentials(scope)`. The +provider clears the matching columns (`clearOAuthCredentials` in the repository): dropping tokens +sets `EXPIRED`, dropping the client registration sets `REQUIRED`, so the agent stops connecting +and the Connections page asks for sign-in again. `discovery` is a no-op — nothing is cached. + +All OAuth helpers and types come from `@modelcontextprotocol/client` (MCP SDK v2). + ## Environment Variables ```env diff --git a/eval/cases/mcp.cases.mts b/eval/cases/mcp.cases.mts index a7ad002..c80c55a 100644 --- a/eval/cases/mcp.cases.mts +++ b/eval/cases/mcp.cases.mts @@ -80,7 +80,7 @@ export const cases: EvalCase[] = [ mcp: true, graders: [ toolCalled("load_tools"), - toolNotCalled(`${MCP_FIXTURE.driveServer}__list_files`), + toolNotCalled(`${MCP_FIXTURE.driveServer}_list_files`), statesAnyOf(["Connections"]), ], runs: RUN_POLICY.single, diff --git a/eval/mcpMocks.mts b/eval/mcpMocks.mts index 0e69c7d..7627bde 100644 --- a/eval/mcpMocks.mts +++ b/eval/mcpMocks.mts @@ -6,6 +6,8 @@ import type { ResolveMcpTool, ToolResolution, } from "@agentailor/mcp-disclosure"; +// By path: the package's named value exports don't survive this tree's CJS interop. +import { prefixedToolName } from "../packages/mcp-disclosure/src/policy.ts"; /** * In-process mock MCP servers: catalog rows and canned results, with no connection. These cases @@ -18,8 +20,8 @@ import type { export const MCP_FIXTURE = { balance: 4217.35, - balanceTool: "bank__get_account_balance", - noteSearchTool: "notes__search_notes", + balanceTool: "bank_get_account_balance", + noteSearchTool: "notes_search_notes", landlordNote: "Landlord raises rent to 1150 in March", driveServer: "drive", } as const; @@ -127,10 +129,10 @@ export const mockServers: DisclosureServer[] = MOCKS.map((m) => ({ export const mockResolveTool: ResolveMcpTool = async (serverId, toolName) => { const server = MOCKS.find((m) => `mock-${m.name}` === serverId); - const mock = server?.tools.find((t) => t.name === toolName); + const mock = server?.tools.find((t) => prefixedToolName(server.name, t.name) === toolName); if (!server || !mock) return { ok: false, error: "connect_failed", message: "no such mock" }; const real = tool(async () => JSON.stringify(mock.result), { - name: `${server.name}__${toolName}`, + name: toolName, description: mock.description ?? "", schema: z.object({}).passthrough(), }); diff --git a/package.json b/package.json index 262d8bc..3c7287e 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,13 @@ "@aws-sdk/s3-request-presigner": "^3.1075.0", "@heroicons/react": "^2.2.0", "@langchain/anthropic": "^1.5.1", - "@langchain/core": "^1.2.1", + "@langchain/core": "^1.3.5", "@langchain/google-genai": "^2.2.0", - "@langchain/langgraph": "^1.4.5", + "@langchain/langgraph": "^1.7.2", "@langchain/langgraph-checkpoint-postgres": "^1.0.3", - "@langchain/mcp-adapters": "^1.1.3", + "@langchain/mcp-adapters": "^2.0.2", "@langchain/openai": "^1.5.2", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "^2.4.0", "@opentelemetry/api": "^1.9.1", "@opentelemetry/exporter-trace-otlp-proto": "^0.219.0", "@opentelemetry/sdk-node": "^0.219.0", diff --git a/packages/mcp-disclosure/package.json b/packages/mcp-disclosure/package.json index 3a5b990..5199328 100644 --- a/packages/mcp-disclosure/package.json +++ b/packages/mcp-disclosure/package.json @@ -15,8 +15,8 @@ "zod": "^4.4.3" }, "devDependencies": { - "@langchain/core": "^1.2.1", - "@langchain/langgraph": "^1.4.5", + "@langchain/core": "^1.3.5", + "@langchain/langgraph": "^1.7.2", "langchain": "^1.5.3", "zod": "^4.4.3" } diff --git a/packages/mcp-disclosure/src/catalogTools.test.ts b/packages/mcp-disclosure/src/catalogTools.test.ts index f2c3c41..3ed24e9 100644 --- a/packages/mcp-disclosure/src/catalogTools.test.ts +++ b/packages/mcp-disclosure/src/catalogTools.test.ts @@ -10,7 +10,7 @@ describe("catalogTools", () => { it("builds prefixed stand-ins per cataloged server, without connecting", () => { const byServer = catalogTools(servers); - expect(byServer.get("id-bank")!.map((t) => t.name)).toEqual(["bank__balance", "bank__list"]); + expect(byServer.get("id-bank")!.map((t) => t.name)).toEqual(["bank_balance", "bank_list"]); expect(byServer.has("id-gone")).toBe(false); }); diff --git a/packages/mcp-disclosure/src/catalogTools.ts b/packages/mcp-disclosure/src/catalogTools.ts index 1aa7fd5..7726a60 100644 --- a/packages/mcp-disclosure/src/catalogTools.ts +++ b/packages/mcp-disclosure/src/catalogTools.ts @@ -1,11 +1,11 @@ import { DynamicStructuredTool, type StructuredToolInterface } from "@langchain/core/tools"; -import { TOOL_NAME_SEPARATOR } from "./policy"; +import { prefixedToolName } from "./policy"; import type { DisclosureServer } from "./types"; export class CatalogToolNotRunnable extends Error {} /** - * One stand-in tool per catalog entry, named `server__tool`, built WITHOUT connecting. The model + * One stand-in tool per catalog entry, named `server_tool`, built WITHOUT connecting. The model * is shown these, with each tool's `inputSchema` exactly as its server listed it; the disclosure * middleware swaps in the real tool when one is called. Running a stand-in directly means a call * skipped the middleware, which is a wiring bug — so it throws. @@ -17,7 +17,7 @@ export function catalogTools(servers: DisclosureServer[]): Map { - const name = `${server.name}${TOOL_NAME_SEPARATOR}${t.name}`; + const name = prefixedToolName(server.name, t.name); return new DynamicStructuredTool({ name, description: t.description ?? "", diff --git a/packages/mcp-disclosure/src/middleware.test.ts b/packages/mcp-disclosure/src/middleware.test.ts index ce74ef7..aa8d06c 100644 --- a/packages/mcp-disclosure/src/middleware.test.ts +++ b/packages/mcp-disclosure/src/middleware.test.ts @@ -74,8 +74,7 @@ const CORE = ["load_tools", "log_expense"]; const connected: string[] = []; const fakeResolver: ResolveMcpTool = async (serverId, name) => { connected.push(serverId); - const server = servers.find((s) => s.id === serverId)!; - return { ok: true, tool: fakeTool(`${server.name}__${name}`) }; + return { ok: true, tool: fakeTool(name) }; }; function build( @@ -147,15 +146,15 @@ describe("disclosure", () => { it("loads only the tools that match, from the next step on", async () => { const { model, run, stateOf } = build([ { tools: [["load_tools", { query: "transactions" }]] }, - { tools: [["bank__list_transactions", {}]] }, + { tools: [["bank_list_transactions", {}]] }, { text: "Here they are" }, ]); await run("t1", "show my last 5 transactions"); - expect(model.seen[0].tools).not.toContain("bank__list_transactions"); - expect(model.seen[1].tools).toContain("bank__list_transactions"); - expect(model.seen[1].tools).not.toContain("bank__get_balance"); - expect(calls).toEqual(["bank__list_transactions"]); - expect((await stateOf("t1")).activeTools).toEqual(["bank__list_transactions"]); + expect(model.seen[0].tools).not.toContain("bank_list_transactions"); + expect(model.seen[1].tools).toContain("bank_list_transactions"); + expect(model.seen[1].tools).not.toContain("bank_get_balance"); + expect(calls).toEqual(["bank_list_transactions"]); + expect((await stateOf("t1")).activeTools).toEqual(["bank_list_transactions"]); }); it("appends in load order, so the existing tools stay a stable prefix", async () => { @@ -167,7 +166,7 @@ describe("disclosure", () => { const before = model.seen[1].tools; const after = model.seen[2].tools; expect(after.slice(0, before.length)).toEqual(before); - expect(after.slice(before.length)).toEqual(["notes__search_notes"]); + expect(after.slice(before.length)).toEqual(["notes_search_notes"]); }); it("keeps loaded tools across turns of a thread, but not in a new thread", async () => { @@ -175,7 +174,7 @@ describe("disclosure", () => { await run("t1"); const before = model.seen.length; await run("t1"); - expect(model.seen[before].tools).toContain("notes__search_notes"); + expect(model.seen[before].tools).toContain("notes_search_notes"); await run("t2"); expect(model.seen.at(-1)!.tools.sort()).toEqual(CORE); }); @@ -187,7 +186,7 @@ describe("disclosure", () => { expect(typeof raw).toBe("string"); expect(JSON.parse(raw as string)).toEqual({ ok: true, - loaded: [{ name: "bank__get_balance", description: "fake get_balance" }], + loaded: [{ name: "bank_get_balance", description: "fake get_balance" }], alreadyLoaded: [], matched: 1, }); @@ -204,8 +203,8 @@ describe("disclosure", () => { ]); await run("t1"); expect((await stateOf("t1")).activeTools.sort()).toEqual([ - "bank__get_balance", - "notes__search_notes", + "bank_get_balance", + "notes_search_notes", ]); }); @@ -218,26 +217,26 @@ describe("disclosure", () => { const results = out.messages.filter((m) => m.getType() === "tool"); expect(JSON.parse(String(results[1].content))).toMatchObject({ loaded: [], - alreadyLoaded: ["bank__get_balance"], + alreadyLoaded: ["bank_get_balance"], }); - expect((await stateOf("t1")).activeTools).toEqual(["bank__get_balance"]); + expect((await stateOf("t1")).activeTools).toEqual(["bank_get_balance"]); }); it("loads only the named tool for an exact name, prefixed or not", async () => { const many = [makeServer("big", { toolNames: ["get_a", "get_b", "get_c"] })]; - for (const query of ["big__get_b", "get_b"]) { + for (const query of ["big_get_b", "get_b"]) { const { run, stateOf } = build([{ tools: [["load_tools", { query }]] }], { using: many }); await run(query); - expect((await stateOf(query)).activeTools).toEqual(["big__get_b"]); + expect((await stateOf(query)).activeTools).toEqual(["big_get_b"]); } }); it("does not load another server's tool by exact name when scoped", async () => { const { run, stateOf } = build([ - { tools: [["load_tools", { query: "bank__get_balance", server: "notes" }]] }, + { tools: [["load_tools", { query: "bank_get_balance", server: "notes" }]] }, ]); await run("scoped-exact"); - expect((await stateOf("scoped-exact")).activeTools).not.toContain("bank__get_balance"); + expect((await stateOf("scoped-exact")).activeTools).not.toContain("bank_get_balance"); }); it("respects limit, caps it, and reports how many matched", async () => { @@ -291,21 +290,21 @@ describe("disclosure", () => { it("loads usable matches and names the server needing sign-in when both match", async () => { const { run, stateOf } = build([{ tools: [["load_tools", { query: "search" }]] }]); const result = toolResult(await run("t1")); - expect(result.loaded.map((t: { name: string }) => t.name)).toEqual(["notes__search_notes"]); + expect(result.loaded.map((t: { name: string }) => t.name)).toEqual(["notes_search_notes"]); expect(result.signInRequired[0].server).toBe("files"); - expect((await stateOf("t1")).activeTools).toEqual(["notes__search_notes"]); + expect((await stateOf("t1")).activeTools).toEqual(["notes_search_notes"]); }); it("auto-loads a catalog tool called before it was loaded", async () => { - const { model, run, stateOf } = build([{ tools: [["notes__search_notes", {}]] }]); + const { model, run, stateOf } = build([{ tools: [["notes_search_notes", {}]] }]); await run("t1"); - expect(calls).toEqual(["notes__search_notes"]); - expect(model.seen[1].tools).toContain("notes__search_notes"); - expect((await stateOf("t1")).activeTools).toEqual(["notes__search_notes"]); + expect(calls).toEqual(["notes_search_notes"]); + expect(model.seen[1].tools).toContain("notes_search_notes"); + expect((await stateOf("t1")).activeTools).toEqual(["notes_search_notes"]); }); it("refuses an unloaded tool when auto-load is off", async () => { - const { run } = build([{ tools: [["notes__search_notes", {}]] }], { + const { run } = build([{ tools: [["notes_search_notes", {}]] }], { options: { autoLoadOnCall: false }, }); const result = toolResult(await run("t1")); @@ -332,11 +331,11 @@ describe("disclosure", () => { const input = { messages: [{ role: "user", content: "hi" }] }; const first = new ScriptedModel([{ tools: [["load_tools", { query: "notes" }]] }]); await make(servers, first).invoke(input, config); - expect(first.seen.at(-1)!.tools).toContain("notes__search_notes"); + expect(first.seen.at(-1)!.tools).toContain("notes_search_notes"); const second = new ScriptedModel([]); await make([servers[0]], second).invoke(input, config); - expect(second.seen[0].tools).not.toContain("notes__search_notes"); + expect(second.seen[0].tools).not.toContain("notes_search_notes"); }); }); @@ -354,11 +353,11 @@ describe("connecting on demand", () => { ["load_tools", { query: "notes" }], ], }, - { tools: [["bank__list_transactions", {}]] }, + { tools: [["bank_list_transactions", {}]] }, ]); await run("t1"); expect(connected).toEqual(["id-bank"]); - expect(calls).toEqual(["bank__list_transactions"]); + expect(calls).toEqual(["bank_list_transactions"]); }); it("reports a connection failure as an error result and does not auto-load", async () => { @@ -367,7 +366,7 @@ describe("connecting on demand", () => { error: "connect_failed", message: "spawn ENOENT", }); - const { run, stateOf } = build([{ tools: [["notes__search_notes", {}]] }], { + const { run, stateOf } = build([{ tools: [["notes_search_notes", {}]] }], { resolveTool: failing, }); expect(toolResult(await run("t1"))).toMatchObject({ ok: false, error: "connect_failed" }); @@ -380,14 +379,14 @@ describe("connecting on demand", () => { error: "auth_required", message: "Connect this server with OAuth first.", }); - const { run } = build([{ tools: [["notes__search_notes", {}]] }], { resolveTool: expired }); + const { run } = build([{ tools: [["notes_search_notes", {}]] }], { resolveTool: expired }); const result = toolResult(await run("t1")); expect(result).toMatchObject({ ok: false, error: "auth_required" }); expect(result.message).toContain("Connections page"); }); it("refuses a tool the catalog does not list, without connecting", async () => { - const { run } = build([{ tools: [["bank__drop_tables", {}]] }]); + const { run } = build([{ tools: [["bank_drop_tables", {}]] }]); expect(toolResult(await run("t1"))).toMatchObject({ error: "unknown_tool" }); expect(connected).toEqual([]); }); diff --git a/packages/mcp-disclosure/src/middleware.ts b/packages/mcp-disclosure/src/middleware.ts index 45e00f4..d005e62 100644 --- a/packages/mcp-disclosure/src/middleware.ts +++ b/packages/mcp-disclosure/src/middleware.ts @@ -9,6 +9,7 @@ import { catalogedServers, createToolServerIndex, findServer, + prefixedToolName, renderCatalogBlock, TOOL_NAME_SEPARATOR, } from "./policy"; @@ -80,7 +81,7 @@ function exactMatch( const name = query.trim(); const direct = index.tool(name); if (direct && scope.some((s) => s.id === direct.server.id)) return direct; - return scope.map((s) => index.tool(`${s.name}${TOOL_NAME_SEPARATOR}${name}`)).find(Boolean); + return scope.map((s) => index.tool(prefixedToolName(s.name, name))).find(Boolean); } /** @@ -235,7 +236,7 @@ export function mcpServerDisclosureMiddleware(config: DisclosureMiddlewareConfig message: `${server.name} has no tool named "${name}".`, }); } - const resolved = await config.resolveTool(server.id, name); + const resolved = await config.resolveTool(server.id, request.toolCall.name); if (!resolved.ok) { log(`[mcp-disclosure] ${server.name}: ${resolved.error} — ${resolved.message}`); // A token can expire after the tool was loaded; same instruction as load_tools gives. diff --git a/packages/mcp-disclosure/src/policy.test.ts b/packages/mcp-disclosure/src/policy.test.ts index 45b95a7..ef429f3 100644 --- a/packages/mcp-disclosure/src/policy.test.ts +++ b/packages/mcp-disclosure/src/policy.test.ts @@ -38,24 +38,24 @@ describe("renderCatalogBlock", () => { }); describe("createToolServerIndex", () => { - const servers = [makeServer("bank"), makeServer("bank__eu"), makeServer("notes")]; + const servers = [makeServer("bank"), makeServer("bank_eu"), makeServer("notes")]; const serverOf = createToolServerIndex(servers); it("maps a prefixed tool name back to its server", () => { - expect(serverOf("notes__search")?.name).toBe("notes"); + expect(serverOf("notes_search")?.name).toBe("notes"); }); it("prefers the longest matching name", () => { - expect(serverOf("bank__eu__balance")?.name).toBe("bank__eu"); - expect(serverOf("bank__balance")?.name).toBe("bank"); + expect(serverOf("bank_eu_balance")?.name).toBe("bank_eu"); + expect(serverOf("bank_balance")?.name).toBe("bank"); }); it("picks the server whose catalog lists the tool when the prefix is ambiguous", () => { - const gh = makeServer("gh", { toolNames: ["ent__search"] }); - const ghEnt = makeServer("gh__ent", { toolNames: ["list"] }); + const gh = makeServer("gh", { toolNames: ["ent_search"] }); + const ghEnt = makeServer("gh_ent", { toolNames: ["list"] }); const pick = createToolServerIndex([gh, ghEnt]); - expect(pick("gh__ent__search")?.name).toBe("gh"); - expect(pick("gh__ent__list")?.name).toBe("gh__ent"); + expect(pick("gh_ent_search")?.name).toBe("gh"); + expect(pick("gh_ent_list")?.name).toBe("gh_ent"); }); it("returns null for tools that are not MCP tools", () => { diff --git a/packages/mcp-disclosure/src/policy.ts b/packages/mcp-disclosure/src/policy.ts index de411a1..9b69233 100644 --- a/packages/mcp-disclosure/src/policy.ts +++ b/packages/mcp-disclosure/src/policy.ts @@ -1,6 +1,12 @@ import type { DisclosureServer } from "./types"; -export const TOOL_NAME_SEPARATOR = "__"; +/** The `{server}_{tool}` form `@langchain/mcp-adapters` 2.x gives tools by default. */ +export const TOOL_NAME_SEPARATOR = "_"; + +/** The name the model sees for a server's tool; also the adapter's own name for it. */ +export function prefixedToolName(serverName: string, toolName: string): string { + return `${serverName}${TOOL_NAME_SEPARATOR}${toolName}`; +} /** Servers the model can be told about: a server with no catalog row is unavailable. */ export function catalogedServers(servers: DisclosureServer[]): DisclosureServer[] { @@ -26,16 +32,14 @@ export function renderCatalogBlock(servers: DisclosureServer[]): string | null { } /** - * Maps a tool name (`server__tool`) back to its server. Names may contain `__`, so a prefix can + * Maps a tool name (`server_tool`) back to its server. Names may contain `_`, so a prefix can * fit several servers: the one whose catalog lists the rest wins, else the longest name. Returns * null for a tool that is not an MCP tool. */ export function createToolServerIndex(servers: DisclosureServer[]) { const ordered = [...servers].sort((a, b) => b.name.length - a.name.length); return (toolName: string): DisclosureServer | null => { - const candidates = ordered.filter((s) => - toolName.startsWith(`${s.name}${TOOL_NAME_SEPARATOR}`), - ); + const candidates = ordered.filter((s) => toolName.startsWith(prefixedToolName(s.name, ""))); const listing = candidates.find((s) => { const rest = toolName.slice(s.name.length + TOOL_NAME_SEPARATOR.length); return s.catalog?.tools.some((t) => t.name === rest); diff --git a/packages/mcp-disclosure/src/search.test.ts b/packages/mcp-disclosure/src/search.test.ts index 7aba4b6..5a22c88 100644 --- a/packages/mcp-disclosure/src/search.test.ts +++ b/packages/mcp-disclosure/src/search.test.ts @@ -41,13 +41,13 @@ describe("tokenize", () => { describe("createToolSearchIndex", () => { it("finds a tool by a word in its name or description", () => { - expect(names("search")[0]).toBe("notionApi__API-post-search"); - expect(names("what is my balance")[0]).toBe("bank__get_balance"); + expect(names("search")[0]).toBe("notionApi_API-post-search"); + expect(names("what is my balance")[0]).toBe("bank_get_balance"); }); it("matches argument names and descriptions nested in $defs and anyOf", () => { - expect(names("database_id")).toEqual(["notionApi__API-post-page"]); - expect(names("row")).toEqual(["notionApi__API-post-page"]); + expect(names("database_id")).toEqual(["notionApi_API-post-page"]); + expect(names("row")).toEqual(["notionApi_API-post-page"]); }); it("weights the tool name above other fields", () => { @@ -77,7 +77,7 @@ describe("createToolSearchIndex", () => { { name: "a", description: "same words" }, ]), ]); - expect(tied.search("words").map((t) => t.name)).toEqual(["s__a", "s__b"]); + expect(tied.search("words").map((t) => t.name)).toEqual(["s_a", "s_b"]); }); it("skips servers with no catalog row", () => { diff --git a/packages/mcp-disclosure/src/search.ts b/packages/mcp-disclosure/src/search.ts index 27c9949..15bda7d 100644 --- a/packages/mcp-disclosure/src/search.ts +++ b/packages/mcp-disclosure/src/search.ts @@ -1,4 +1,4 @@ -import { TOOL_NAME_SEPARATOR } from "./policy"; +import { prefixedToolName } from "./policy"; import type { DisclosureServer, McpToolDefinition } from "./types"; // Standard BM25 constants. @@ -85,7 +85,7 @@ export interface ToolSearchIndex { export function createToolSearchIndex(servers: DisclosureServer[]): ToolSearchIndex { const tools: SearchableTool[] = servers.flatMap((server) => (server.catalog?.tools ?? []).map((definition) => ({ - name: `${server.name}${TOOL_NAME_SEPARATOR}${definition.name}`, + name: prefixedToolName(server.name, definition.name), server, definition, })), diff --git a/packages/mcp-disclosure/src/types.ts b/packages/mcp-disclosure/src/types.ts index b0b6093..e694e71 100644 --- a/packages/mcp-disclosure/src/types.ts +++ b/packages/mcp-disclosure/src/types.ts @@ -33,7 +33,7 @@ export interface ServerCatalogEntry { /** An enabled server as the middleware sees it. `catalog: null` means unavailable this request. */ export interface DisclosureServer { id: string; - /** Unique; also the prefix of the server's tool names (`name__tool`). */ + /** Unique; also the prefix of the server's tool names (`name_tool`). */ name: string; /** * The server needs the user to (re)authorize it before it can be used — known from stored @@ -61,7 +61,7 @@ export const DEFAULT_DISCLOSURE_OPTIONS: DisclosureOptions = { export interface McpSession { /** `tools/list` plus the server's `instructions`, read live. */ manifest(): Promise; - /** A runnable tool by its MCP name (unprefixed), or undefined if the server has no such tool. */ + /** A runnable tool by its prefixed name (`server_tool`), or undefined if there is none. */ getTool(name: string): StructuredToolInterface | undefined; close(): Promise; /** Called when the connection drops on its own (a stdio process exits, a stream closes). */ @@ -73,5 +73,8 @@ export type ToolResolution = | { ok: true; tool: StructuredToolInterface; release?: () => void } | { ok: false; error: string; message: string }; -/** Finds the runnable tool for a call, connecting to its server if needed. Never throws. */ +/** + * Finds the runnable tool for a call (`toolName` as the model called it, `server_tool`), + * connecting to its server if needed. Never throws. + */ export type ResolveMcpTool = (serverId: string, toolName: string) => Promise; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a05a92..f4eeb1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,6 @@ settings: excludeLinksFromLockfile: false overrides: - '@modelcontextprotocol/sdk@<1.24.0': '>=1.24.0' body-parser@>=2.2.0 <2.2.1: '>=2.2.1' js-yaml@>=4.0.0 <4.1.1: '>=4.1.1' jws@<3.2.3: '>=3.2.3' @@ -36,28 +35,28 @@ importers: version: 2.2.0(react@19.2.7) '@langchain/anthropic': specifier: ^1.5.1 - version: 1.5.1(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) + version: 1.5.1(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) '@langchain/core': - specifier: ^1.2.1 - version: 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + specifier: ^1.3.5 + version: 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) '@langchain/google-genai': specifier: ^2.2.0 - version: 2.2.0(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) + version: 2.2.0(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) '@langchain/langgraph': - specifier: ^1.4.5 - version: 1.4.5(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + specifier: ^1.7.2 + version: 1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) '@langchain/langgraph-checkpoint-postgres': specifier: ^1.0.3 - version: 1.0.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph-checkpoint@1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))) + version: 1.0.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph-checkpoint@1.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))) '@langchain/mcp-adapters': - specifier: ^1.1.3 - version: 1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph@1.4.5(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3)) + specifier: ^2.0.2 + version: 2.0.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph@1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3)) '@langchain/openai': specifier: ^1.5.2 - version: 1.5.2(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(ws@8.18.3) - '@modelcontextprotocol/sdk': - specifier: ^1.29.0 - version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3) + version: 1.5.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(ws@8.18.3) + '@modelcontextprotocol/client': + specifier: ^2.4.0 + version: 2.4.0 '@opentelemetry/api': specifier: ^1.9.1 version: 1.9.1 @@ -108,7 +107,7 @@ importers: version: 1.8.1(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.55.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) langchain: specifier: ^1.5.3 - version: 1.5.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3) + version: 1.5.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3) lucide-react: specifier: ^1.21.0 version: 1.21.0(react@19.2.7) @@ -193,7 +192,7 @@ importers: version: 16.2.9(@typescript-eslint/parser@8.58.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) openevals: specifier: ^0.2.2 - version: 0.2.2(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(ws@8.18.3)(zod@4.4.3) + version: 0.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(ws@8.18.3)(zod@4.4.3) postcss: specifier: ^8.5.15 version: 8.5.15 @@ -222,14 +221,14 @@ importers: packages/mcp-disclosure: devDependencies: '@langchain/core': - specifier: ^1.2.1 - version: 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + specifier: ^1.3.5 + version: 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) '@langchain/langgraph': - specifier: ^1.4.5 - version: 1.4.7(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) + specifier: ^1.7.2 + version: 1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) langchain: specifier: ^1.5.3 - version: 1.5.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3) + version: 1.5.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3) zod: specifier: ^4.4.3 version: 4.4.3 @@ -438,8 +437,8 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@cfworker/json-schema@4.1.1': - resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + '@cfworker/json-schema@4.1.6': + resolution: {integrity: sha512-YzVGTHXf4bzlyAL5KAvHYCdNbbt6zM5cYkAgxqxQPDQM6aZ4ShIIN5yJ+6q7YeonnevxT8k/LmD2tJi/jOiKkg==} '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} @@ -966,12 +965,6 @@ packages: peerDependencies: react: '>= 16 || ^19.0.0-rc' - '@hono/node-server@1.19.12': - resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1166,8 +1159,8 @@ packages: peerDependencies: '@langchain/core': ^1.2.1 - '@langchain/core@1.2.1': - resolution: {integrity: sha512-NNG/cC5FGuHDOAP56h0ddp8Rfk8p+othWzEK5RV9JIG6RvnF5vGa5r0AEGtKfQieed7s1kC42GuIzVOBvMBL/g==} + '@langchain/core@1.3.5': + resolution: {integrity: sha512-7mk6lrHhD9vGZ+HnUhbHJzWTuJVktV+XSgDFmDlFu6qOHuNao4iuu5wqOgFNyx3Khjv7PydgbWOwcYeRXZ/ygA==} engines: {node: '>=20'} '@langchain/google-genai@2.2.0': @@ -1189,48 +1182,37 @@ packages: peerDependencies: '@langchain/core': ^1.1.48 - '@langchain/langgraph-sdk@1.9.25': - resolution: {integrity: sha512-mRKW8zyQUaHox+HirRFMRrPqOvNbQI3xeXDt6kkk4PbBg77V92bsO1WzUVNrmJ81zCkvxyOrWSK8D6ioCj0a8A==} + '@langchain/langgraph-checkpoint@1.2.2': + resolution: {integrity: sha512-KL3nzi7I61ll5nANCQe5RoJ5SqTFrE+si9IK6k7IIj9OrPkIKT/Dz0w8CyJF+K7OEuJfA33HvPrZqfzzW1DXDA==} + engines: {node: '>=18'} peerDependencies: '@langchain/core': ^1.1.48 + + '@langchain/langgraph-sdk@1.15.0': + resolution: {integrity: sha512-5s57608G4bjQCxyCXuHXU18K/8Vyl6JHBxa3MPpSD6z/LHvWazyShXKoYiNTSXhg8OrMBobPqxBJm14CWK9APw==} + peerDependencies: + '@langchain/core': ^1.3.2 react: ^18 || ^19 react-dom: ^18 || ^19 - svelte: ^4.0.0 || ^5.0.0 - vue: ^3.0.0 peerDependenciesMeta: react: optional: true react-dom: optional: true - svelte: - optional: true - vue: - optional: true - - '@langchain/langgraph@1.4.5': - resolution: {integrity: sha512-V+o29JPBaMoK/e+8R/m81XaC8h5iNuwWymvgLFhXfJbf7E2xt2mQUkcVXTi4cudGRHbRd14kidCpfaQbfPoYCw==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': ^1.1.48 - zod: ^3.25.32 || ^4.2.0 - zod-to-json-schema: ^3.x - peerDependenciesMeta: - zod-to-json-schema: - optional: true - '@langchain/langgraph@1.4.7': - resolution: {integrity: sha512-2tcyf3QGC7v89kqSxMCtRvzg/3L/4yHtOaWC49A8KieCciWJs7LGaxHoPB6QRxXyUgyR+Zg9Q1ss/XJIE+JuSQ==} + '@langchain/langgraph@1.7.2': + resolution: {integrity: sha512-xkyVqLT3NDWXmm7uh15nWIBXfn5BkTX8V7sKkP4fGG0AJSttzzIt/izJ03xKEmVcNOgeSyWhPKrsoQg8+MB3MQ==} engines: {node: '>=18'} peerDependencies: - '@langchain/core': ^1.1.48 + '@langchain/core': ^1.3.4 zod: ^3.25.32 || ^4.2.0 - '@langchain/mcp-adapters@1.1.3': - resolution: {integrity: sha512-OPHIQNkTUJjnRj1pr+cp2nguMBZeF3Q1pVT1hCbgU7BrHgV7lov99wbU8po8Cm4zZzmeRtVO/T9X1SrDD1ogtQ==} + '@langchain/mcp-adapters@2.0.2': + resolution: {integrity: sha512-p0I6cHoY/DDdxqXoja6saJ3tHMTQEKcPZvI1t+uOIXpQXM3hxcGUGv+WOSwsXnry5xZBc5Xgd62u+Annamdxxw==} engines: {node: '>=20.10.0'} peerDependencies: - '@langchain/core': ^1.0.0 - '@langchain/langgraph': ^1.0.0 + '@langchain/core': ^1.2.6 + '@langchain/langgraph': ^1.4.18 '@langchain/openai@1.5.2': resolution: {integrity: sha512-En/QzXO3YFuaaZWQiGx0ZBNJMK3ipL/tz8F/PReG/63oV3wk2nz906QA8drYnd8r2/3NtSkbf3x/8qms5o6qTg==} @@ -1238,18 +1220,16 @@ packages: peerDependencies: '@langchain/core': ^1.2.1 - '@langchain/protocol@0.0.18': - resolution: {integrity: sha512-XW1egQtPfsGI41w2AMZNFZrUIwFSQHTjVMZs0OaTpCAvht/QLoaPN8FQcsysMVypOhupG28J29yOorrc70otBQ==} + '@langchain/protocol@0.0.21': + resolution: {integrity: sha512-HJS8Cd5GJ437MIrCnbrPHx0FJcluyOk5HdZzD081ap4SBzfWpAj/6AtI7fqpNSMo6VZEoEdaN9fIts8R0XR1AQ==} - '@modelcontextprotocol/sdk@1.29.0': - resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true + '@modelcontextprotocol/client@2.4.0': + resolution: {integrity: sha512-LgrD2OphHuG12vfKFKz6SOnq7bKHQmhGJIAYdD+MZBn2npHEyry6uD8zvktodtl5CQM85MO0anUyD9JwmUmxww==} + engines: {node: '>=20'} + + '@modelcontextprotocol/core@2.4.0': + resolution: {integrity: sha512-z8QBKqdfXLPzkLQuG4xp8hrVEMm1PZ1IgxMN2sMbJiFiAKOWXnAzogXn2W4aqIBlP1ea6U5yiqVquvUNM1Cj6Q==} + engines: {node: '>=20'} '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} @@ -2278,10 +2258,6 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2302,20 +2278,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2425,10 +2390,6 @@ packages: bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - body-parser@2.2.1: - resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} - engines: {node: '>=18'} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -2460,10 +2421,6 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - c12@3.1.0: resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} peerDependencies: @@ -2581,29 +2538,9 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} - content-disposition@1.0.0: - resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} - engines: {node: '>= 0.6'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2682,10 +2619,6 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2824,9 +2757,6 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - effect@3.21.0: resolution: {integrity: sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==} @@ -2843,10 +2773,6 @@ packages: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - enhanced-resolve@5.21.6: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} @@ -2909,9 +2835,6 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -3050,10 +2973,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -3064,8 +2983,8 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - eventsource-parser@3.0.6: - resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + eventsource-parser@3.1.1: + resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} engines: {node: '>=18.0.0'} eventsource@3.0.7: @@ -3080,25 +2999,12 @@ packages: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.3.2: - resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extended-eventsource@1.7.0: - resolution: {integrity: sha512-s8rtvZuYcKBpzytHb5g95cHbZ1J99WeMnV18oKc5wKoxkHzlzpPc/bNAm7Da2Db0BDw0CAu1z3LpH+7UsyzIpw==} - fast-check@3.23.2: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} @@ -3119,9 +3025,6 @@ packages: fast-sha256@1.3.0: resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -3146,10 +3049,6 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} - engines: {node: '>= 0.8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3169,10 +3068,6 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} @@ -3190,10 +3085,6 @@ packages: react-dom: optional: true - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - fs-extra@11.3.2: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} @@ -3374,28 +3265,16 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hono@4.12.11: - resolution: {integrity: sha512-r4xbIa3mGGGoH9nN4A14DOg2wx7y2oQyJEb5O57C/xzETG/qx4c7CVDQ5WMeKHZ7ORk2W0hZ/sQKXTav3cmYBA==} - engines: {node: '>=16.9.0'} - html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - human-signals@4.3.1: resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} engines: {node: '>=14.18.0'} - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} - engines: {node: '>=0.10.0'} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -3429,14 +3308,6 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -3532,9 +3403,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3621,12 +3489,6 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -3683,6 +3545,26 @@ packages: ws: optional: true + langsmith@0.11.8: + resolution: {integrity: sha512-rAskvQCNjwGqwjA9fOJGugZ1vLB66yaivxWGSTA9ldtF9+P00gvXH1DEr0kW798cyUyBlZolFO0kPb//zDNc7w==} + peerDependencies: + '@opentelemetry/api': '*' + '@opentelemetry/exporter-trace-otlp-proto': '*' + '@opentelemetry/sdk-trace-base': '*' + openai: '*' + ws: '>=7' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/exporter-trace-otlp-proto': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + openai: + optional: true + ws: + optional: true + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -3926,14 +3808,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4029,14 +3903,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -4099,10 +3965,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - next@16.2.9: resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==} engines: {node: '>=20.9.0'} @@ -4190,13 +4052,6 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -4295,10 +4150,6 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4314,9 +4165,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -4519,10 +4367,6 @@ packages: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4530,21 +4374,9 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@3.0.1: - resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} - engines: {node: '>= 0.10'} - rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -4650,10 +4482,6 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - require-in-the-middle@8.0.1: resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} @@ -4687,10 +4515,6 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -4709,9 +4533,6 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -4724,14 +4545,6 @@ packages: engines: {node: '>=10'} hasBin: true - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} - engines: {node: '>= 18'} - - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} - engines: {node: '>= 18'} - set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -4744,9 +4557,6 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4815,14 +4625,6 @@ packages: standardwebhooks@1.0.0: resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} @@ -4950,10 +4752,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -4991,10 +4789,6 @@ packages: resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} engines: {node: '>=20'} - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5061,10 +4855,6 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -5084,10 +4874,6 @@ packages: resolution: {integrity: sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==} hasBin: true - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -5225,9 +5011,6 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -5268,11 +5051,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} @@ -5654,7 +5432,7 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@cfworker/json-schema@4.1.1': {} + '@cfworker/json-schema@4.1.6': {} '@drizzle-team/brocli@0.10.2': {} @@ -5984,10 +5762,6 @@ snapshots: dependencies: react: 19.2.7 - '@hono/node-server@1.19.12(hono@4.12.11)': - dependencies: - hono: 4.12.11 - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -6117,18 +5891,18 @@ snapshots: '@js-sdsl/ordered-map@4.4.2': {} - '@langchain/anthropic@1.5.1(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': + '@langchain/anthropic@1.5.1(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': dependencies: '@anthropic-ai/sdk': 0.103.0(zod@4.4.3) - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) zod: 4.4.3 - '@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)': + '@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)': dependencies: - '@cfworker/json-schema': 4.1.1 + '@cfworker/json-schema': 4.1.6 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.10.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + langsmith: 0.11.8(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) mustache: 4.2.0 p-queue: 6.6.2 zod: 4.4.3 @@ -6139,27 +5913,31 @@ snapshots: - openai - ws - '@langchain/google-genai@2.2.0(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': + '@langchain/google-genai@2.2.0(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': dependencies: '@google/generative-ai': 0.24.1 - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph-checkpoint-postgres@1.0.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph-checkpoint@1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)))': + '@langchain/langgraph-checkpoint-postgres@1.0.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph-checkpoint@1.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)))': dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph-checkpoint': 1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/langgraph-checkpoint': 1.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) pg: 8.22.0 transitivePeerDependencies: - pg-native - '@langchain/langgraph-checkpoint@1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': + '@langchain/langgraph-checkpoint@1.1.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph-sdk@1.9.25(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@langchain/langgraph-checkpoint@1.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))': dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/protocol': 0.0.18 + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + + '@langchain/langgraph-sdk@1.15.0(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/protocol': 0.0.21 '@types/json-schema': 7.0.15 p-queue: 9.1.1 p-retry: 7.1.1 @@ -6167,83 +5945,50 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@langchain/langgraph@1.4.5(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3)': + '@langchain/langgraph@1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3)': dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph-checkpoint': 1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) - '@langchain/langgraph-sdk': 1.9.25(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@langchain/protocol': 0.0.18 + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/langgraph-checkpoint': 1.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) + '@langchain/langgraph-sdk': 1.15.0(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@langchain/protocol': 0.0.21 '@standard-schema/spec': 1.1.0 zod: 4.4.3 - optionalDependencies: - zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - react - react-dom - - svelte - - vue - '@langchain/langgraph@1.4.7(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3)': + '@langchain/mcp-adapters@2.0.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph@1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3))': dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph-checkpoint': 1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) - '@langchain/langgraph-sdk': 1.9.25(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@langchain/protocol': 0.0.18 - '@standard-schema/spec': 1.1.0 - zod: 4.4.3 - transitivePeerDependencies: - - react - - react-dom - - svelte - - vue - - '@langchain/mcp-adapters@1.1.3(@cfworker/json-schema@4.1.1)(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@langchain/langgraph@1.4.5(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3))': - dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph': 1.4.5(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) - '@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3) - debug: 4.4.3 + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/langgraph': 1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) + '@modelcontextprotocol/client': 2.4.0 + '@modelcontextprotocol/core': 2.4.0 zod: 4.4.3 - optionalDependencies: - extended-eventsource: 1.7.0 - transitivePeerDependencies: - - '@cfworker/json-schema' - - supports-color - '@langchain/openai@1.5.2(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(ws@8.18.3)': + '@langchain/openai@1.5.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(ws@8.18.3)': dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) js-tiktoken: 1.0.21 openai: 6.44.0(ws@8.18.3)(zod@4.4.3) zod: 4.4.3 transitivePeerDependencies: - ws - '@langchain/protocol@0.0.18': {} + '@langchain/protocol@0.0.21': {} - '@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3)': + '@modelcontextprotocol/client@2.4.0': dependencies: - '@hono/node-server': 1.19.12(hono@4.12.11) - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - content-type: 1.0.5 - cors: 2.8.5 + '@modelcontextprotocol/core': 2.4.0 cross-spawn: 7.0.6 eventsource: 3.0.7 - eventsource-parser: 3.0.6 - express: 5.2.1 - express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.11 + eventsource-parser: 3.1.1 jose: 6.1.3 - json-schema-typed: 8.0.2 pkce-challenge: 5.0.0 - raw-body: 3.0.1 zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - optionalDependencies: - '@cfworker/json-schema': 4.1.1 - transitivePeerDependencies: - - supports-color + + '@modelcontextprotocol/core@2.4.0': + dependencies: + zod: 4.4.3 '@napi-rs/wasm-runtime@0.2.12': dependencies: @@ -7276,11 +7021,6 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.1 - accepts@2.0.0: - dependencies: - mime-types: 3.0.1 - negotiator: 1.0.0 - acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -7297,10 +7037,6 @@ snapshots: acorn@8.16.0: {} - ajv-formats@3.0.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -7308,13 +7044,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -7437,20 +7166,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@2.2.1: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.0 - iconv-lite: 0.7.0 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.1 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - boolbase@1.0.0: {} bowser@2.13.1: {} @@ -7488,8 +7203,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bytes@3.1.2: {} - c12@3.1.0: dependencies: chokidar: 4.0.3 @@ -7597,23 +7310,8 @@ snapshots: consola@3.4.2: optional: true - content-disposition@1.0.0: - dependencies: - safe-buffer: 5.2.1 - - content-type@1.0.5: {} - convert-source-map@2.0.0: {} - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -7686,8 +7384,6 @@ snapshots: defu@6.1.4: optional: true - depd@2.0.0: {} - dequal@2.0.3: {} destr@2.0.5: @@ -7741,8 +7437,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - ee-first@1.1.1: {} - effect@3.21.0: dependencies: '@standard-schema/spec': 1.1.0 @@ -7758,8 +7452,6 @@ snapshots: empathic@2.0.0: optional: true - encodeurl@2.0.0: {} - enhanced-resolve@5.21.6: dependencies: graceful-fs: 4.2.11 @@ -7955,8 +7647,6 @@ snapshots: escalade@3.2.0: {} - escape-html@1.0.3: {} - escape-string-regexp@4.0.0: {} escape-string-regexp@5.0.0: {} @@ -8176,19 +7866,17 @@ snapshots: esutils@2.0.3: {} - etag@1.8.1: {} - eventemitter3@4.0.7: {} eventemitter3@5.0.4: {} events@3.3.0: {} - eventsource-parser@3.0.6: {} + eventsource-parser@3.1.1: {} eventsource@3.0.7: dependencies: - eventsource-parser: 3.0.6 + eventsource-parser: 3.1.1 execa@7.2.0: dependencies: @@ -8204,52 +7892,11 @@ snapshots: expect-type@1.4.0: {} - express-rate-limit@8.3.2(express@5.2.1): - dependencies: - express: 5.2.1 - ip-address: 10.1.0 - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.1 - content-disposition: 1.0.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.0 - fresh: 2.0.0 - http-errors: 2.0.0 - merge-descriptors: 2.0.0 - mime-types: 3.0.1 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.14.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - exsolve@1.0.7: optional: true extend@3.0.2: {} - extended-eventsource@1.7.0: - optional: true - fast-check@3.23.2: dependencies: pure-rand: 6.1.0 @@ -8271,8 +7918,6 @@ snapshots: fast-sha256@1.3.0: {} - fast-uri@3.1.0: {} - fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -8298,17 +7943,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@2.1.0: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -8329,8 +7963,6 @@ snapshots: dependencies: fetch-blob: 3.2.0 - forwarded@0.2.0: {} - fraction.js@5.3.4: {} framer-motion@12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): @@ -8342,8 +7974,6 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - fresh@2.0.0: {} - fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 @@ -8633,26 +8263,12 @@ snapshots: dependencies: hermes-estree: 0.25.1 - hono@4.12.11: {} - html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - human-signals@4.3.1: {} - iconv-lite@0.7.0: - dependencies: - safer-buffer: 2.1.2 - ieee754@1.2.1: {} ignore@5.3.2: {} @@ -8683,10 +8299,6 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - ip-address@10.1.0: {} - - ipaddr.js@1.9.1: {} - is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -8778,8 +8390,6 @@ snapshots: is-plain-obj@4.1.0: {} - is-promise@4.0.0: {} - is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -8861,10 +8471,6 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - - json-schema-typed@8.0.2: {} - json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: @@ -8896,11 +8502,11 @@ snapshots: kleur@3.0.3: {} - langchain@1.5.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3): + langchain@1.5.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3): dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/langgraph': 1.4.7(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) - '@langchain/langgraph-checkpoint': 1.1.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/langgraph': 1.7.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) + '@langchain/langgraph-checkpoint': 1.1.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3)) langsmith: 0.10.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) zod: 4.4.3 transitivePeerDependencies: @@ -8910,8 +8516,6 @@ snapshots: - openai - react - react-dom - - svelte - - vue - ws langsmith@0.10.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3): @@ -8924,6 +8528,16 @@ snapshots: openai: 6.44.0(ws@8.18.3)(zod@4.4.3) ws: 8.18.3 + langsmith@0.11.8(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3): + dependencies: + p-queue: 6.6.2 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/exporter-trace-otlp-proto': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + openai: 6.44.0(ws@8.18.3)(zod@4.4.3) + ws: 8.18.3 + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -9221,10 +8835,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - media-typer@1.1.0: {} - - merge-descriptors@2.0.0: {} - merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -9425,12 +9035,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.54.0: {} - - mime-types@3.0.1: - dependencies: - mime-db: 1.54.0 - mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} @@ -9469,8 +9073,6 @@ snapshots: natural-compare@1.4.0: {} - negotiator@1.0.0: {} - next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.55.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: '@next/env': 16.2.9 @@ -9574,14 +9176,6 @@ snapshots: ohash@2.0.11: optional: true - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -9607,11 +9201,11 @@ snapshots: dependencies: yaml: 2.9.0 - openevals@0.2.2(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(ws@8.18.3)(zod@4.4.3): + openevals@0.2.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(ws@8.18.3)(zod@4.4.3): dependencies: - '@langchain/core': 1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) - '@langchain/openai': 1.5.2(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(ws@8.18.3) - langchain: 1.5.3(@langchain/core@1.2.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3) + '@langchain/core': 1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) + '@langchain/openai': 1.5.2(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(ws@8.18.3) + langchain: 1.5.3(@langchain/core@1.3.5(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3))(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.18.3) langsmith: 0.10.1(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(openai@6.44.0(ws@8.18.3)(zod@4.4.3))(ws@8.18.3) uuid: 14.0.2 zod: 4.4.3 @@ -9624,8 +9218,6 @@ snapshots: - openai - react - react-dom - - svelte - - vue - ws optionator@0.9.4: @@ -9707,8 +9299,6 @@ snapshots: dependencies: entities: 6.0.1 - parseurl@1.3.3: {} - path-exists@4.0.0: {} path-key@3.1.1: {} @@ -9717,8 +9307,6 @@ snapshots: path-parse@1.0.7: {} - path-to-regexp@8.3.0: {} - pathe@2.0.3: {} perfect-debounce@1.0.0: @@ -9871,31 +9459,13 @@ snapshots: '@types/node': 25.5.2 long: 5.3.2 - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - punycode@2.3.1: {} pure-rand@6.1.0: optional: true - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - queue-microtask@1.2.3: {} - range-parser@1.2.1: {} - - raw-body@3.0.1: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.7.0 - unpipe: 1.0.0 - rc9@2.1.2: dependencies: defu: 6.1.4 @@ -10093,8 +9663,6 @@ snapshots: require-directory@2.1.1: {} - require-from-string@2.0.2: {} - require-in-the-middle@8.0.1: dependencies: debug: 4.4.3 @@ -10146,16 +9714,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.1 '@rolldown/binding-win32-x64-msvc': 1.2.1 - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.3.0 - transitivePeerDependencies: - - supports-color - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -10181,39 +9739,12 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safer-buffer@2.1.2: {} - scheduler@0.27.0: {} semver@6.3.1: {} semver@7.7.4: {} - send@1.2.0: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.0 - mime-types: 3.0.1 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.0: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.0 - transitivePeerDependencies: - - supports-color - set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -10236,8 +9767,6 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 - setprototypeof@1.2.0: {} - sharp@0.34.5: dependencies: '@img/colour': 1.0.0 @@ -10343,10 +9872,6 @@ snapshots: '@stablelib/base64': 1.0.1 fast-sha256: 1.3.0 - statuses@2.0.1: {} - - statuses@2.0.2: {} - std-env@4.2.0: {} stdin-discarder@0.1.0: @@ -10487,8 +10012,6 @@ snapshots: dependencies: is-number: 7.0.0 - toidentifier@1.0.1: {} - trim-lines@3.0.1: {} trough@2.2.0: {} @@ -10524,12 +10047,6 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.1 - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -10636,8 +10153,6 @@ snapshots: universalify@2.0.1: {} - unpipe@1.0.0: {} - unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.3 @@ -10676,8 +10191,6 @@ snapshots: uuid@14.0.2: {} - vary@1.1.2: {} - vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -10802,8 +10315,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrappy@1.0.2: {} - ws@8.18.3: optional: true @@ -10829,10 +10340,6 @@ snapshots: yocto-queue@0.1.0: {} - zod-to-json-schema@3.25.2(zod@4.4.3): - dependencies: - zod: 4.4.3 - zod-validation-error@4.0.2(zod@4.4.3): dependencies: zod: 4.4.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index da23f87..8268e19 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,7 +4,6 @@ ignoredBuiltDependencies: - prisma overrides: - "@modelcontextprotocol/sdk@<1.24.0": ">=1.24.0" body-parser@>=2.2.0 <2.2.1: ">=2.2.1" js-yaml@>=4.0.0 <4.1.1: ">=4.1.1" jws@<3.2.3: ">=3.2.3" diff --git a/src/app/api/mcp-servers/route.ts b/src/app/api/mcp-servers/route.ts index 6cf6efb..7848282 100644 --- a/src/app/api/mcp-servers/route.ts +++ b/src/app/api/mcp-servers/route.ts @@ -2,6 +2,7 @@ import { after, NextResponse } from "next/server"; import * as mcpServerRepo from "@/lib/repositories/mcpServerRepository"; import { ConflictError } from "@/lib/repositories/mcpServerRepository"; import { refreshCatalogQuietly } from "@/lib/mcp/catalog"; +import { recordOAuthRequirement } from "@/lib/mcp/oauth-detection"; import { mcpPool } from "@/lib/mcp/pool"; import { MCPServerType } from "@/types/mcp"; import { UpdateMCPServerBody } from "./schema"; @@ -44,6 +45,11 @@ export async function POST(request: Request) { headers: type === "http" ? headers : null, }); + // Before responding, so the page shows Connect for a server that needs sign-in. + if (server.type === MCPServerType.http && server.url) { + server.oauthStatus = await recordOAuthRequirement(server.id, server.url); + } + // Cache its tool list for the agent's server catalog, after the response is sent. after(() => refreshCatalogQuietly(server.id)); @@ -85,11 +91,17 @@ export async function PATCH(request: Request) { patch.env = null; } + const before = url !== undefined ? await mcpServerRepo.getById(id) : null; const server = await mcpServerRepo.update(id, patch); if (!server) { return NextResponse.json({ error: "Server not found" }, { status: 404 }); } + // A new URL is a different server: its sign-in need starts unknown again. + if (server.type === MCPServerType.http && server.url && before?.url !== server.url) { + server.oauthStatus = await recordOAuthRequirement(server.id, server.url); + } + // Enabling or editing can change the tool list, and a description change decides whether the // summary is generated; a rename alone changes neither. const reconfigured = [type, command, args, env, url, headers].some((v) => v !== undefined); diff --git a/src/app/api/oauth/callback/[serverId]/route.ts b/src/app/api/oauth/callback/[serverId]/route.ts index eb97d5c..48c2957 100644 --- a/src/app/api/oauth/callback/[serverId]/route.ts +++ b/src/app/api/oauth/callback/[serverId]/route.ts @@ -3,7 +3,7 @@ import * as mcpServerRepo from "@/lib/repositories/mcpServerRepository"; import { ServerOAuthProvider } from "@/lib/mcp/oauth-provider"; import { refreshCatalogQuietly } from "@/lib/mcp/catalog"; import { mcpPool } from "@/lib/mcp/pool"; -import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client"; import { OAuthStatus } from "@/lib/mcp/oauth-detection"; import { getAppUrl } from "@/lib/config/app-url"; @@ -55,8 +55,9 @@ export async function GET( authProvider, }); - // Exchange authorization code for tokens - await transport.finishAuth(code); + // The full params, not just the code: the SDK validates `iss` (RFC 9207) when the + // authorization server sends it. + await transport.finishAuth(searchParams); // Update server status to connected (clears the transient code verifier) await mcpServerRepo.markConnected(serverId); diff --git a/src/app/api/oauth/check/[serverId]/route.ts b/src/app/api/oauth/check/[serverId]/route.ts index d603c14..1e5ae65 100644 --- a/src/app/api/oauth/check/[serverId]/route.ts +++ b/src/app/api/oauth/check/[serverId]/route.ts @@ -8,12 +8,7 @@ import { } from "@/lib/mcp/oauth-detection"; import type { OAuthStatus as OAuthStatusType } from "@/lib/mcp/oauth-detection"; import { ServerOAuthProvider } from "@/lib/mcp/oauth-provider"; -import { - discoverOAuthProtectedResourceMetadata, - discoverAuthorizationServerMetadata, - startAuthorization, - registerClient, -} from "@modelcontextprotocol/sdk/client/auth.js"; +import { auth } from "@modelcontextprotocol/client"; export const dynamic = "force-dynamic"; export const runtime = "nodejs"; @@ -86,79 +81,32 @@ export async function GET( }); } - // Server requires OAuth - generate authorization URL await updateServerOAuthStatus(serverId, OAuthStatus.REQUIRED); const authProvider = new ServerOAuthProvider(serverId, server.name); try { - // Step 1: Discover protected resource metadata to find the authorization server - const serverUrlObj = new URL(serverUrl); - const resourceMetadata = await discoverOAuthProtectedResourceMetadata( - serverUrlObj, - detection.resourceMetadataUrl - ? { resourceMetadataUrl: detection.resourceMetadataUrl } - : undefined, - ); - - if (!resourceMetadata?.authorization_servers?.length) { - return NextResponse.json({ - serverId, - requiresAuth: true, - connected: false, - oauthStatus: OAuthStatus.REQUIRED, - error: "Could not find authorization server in resource metadata", - }); - } - - // Step 2: Get the authorization server URL and discover its metadata - const authServerUrl = resourceMetadata.authorization_servers[0]; - - const metadata = await discoverAuthorizationServerMetadata(authServerUrl); + // The SDK runs discovery, scope selection, registration and PKCE, so the authorization + // request matches what its token exchange in the callback expects. + const result = await auth(authProvider, { + serverUrl, + scope: detection.scope, + resourceMetadataUrl: toUrl(detection.resourceMetadataUrl), + }); - if (!metadata) { + if (result === "AUTHORIZED") { + // A stored refresh token was still good: no sign-in needed. + await mcpServerRepo.markConnected(serverId); return NextResponse.json({ serverId, requiresAuth: true, - connected: false, - oauthStatus: OAuthStatus.REQUIRED, - error: "Could not discover authorization server metadata", + connected: true, + oauthStatus: OAuthStatus.CONNECTED, }); } - // Get existing client info or register a new client - let clientInfo = await authProvider.clientInformation(); - - if (!clientInfo) { - // Register client dynamically if not already registered - if (metadata.registration_endpoint) { - clientInfo = await registerClient(authServerUrl, { - metadata, - clientMetadata: authProvider.clientMetadata, - }); - // Save the client info for future use - await authProvider.saveClientInformation(clientInfo); - } else { - return NextResponse.json({ - serverId, - requiresAuth: true, - connected: false, - oauthStatus: OAuthStatus.REQUIRED, - error: "Server does not support dynamic client registration", - }); - } - } - - // Start authorization and get the URL - const { authorizationUrl, codeVerifier } = await startAuthorization(authServerUrl, { - metadata, - clientInformation: clientInfo, - redirectUrl: authProvider.redirectUrl, - }); - - // Save code verifier for the callback - await authProvider.saveCodeVerifier(codeVerifier); - + const authorizationUrl = authProvider.pendingAuthorizationUrl; + if (!authorizationUrl) throw new Error("The SDK did not produce an authorization URL."); return NextResponse.json({ serverId, requiresAuth: true, @@ -177,3 +125,12 @@ export async function GET( }); } } + +function toUrl(value: string | undefined): URL | undefined { + if (!value) return undefined; + try { + return new URL(value); + } catch { + return undefined; + } +} diff --git a/src/components/connections/ServerForm.tsx b/src/components/connections/ServerForm.tsx index c8b8da1..3d63d9a 100644 --- a/src/components/connections/ServerForm.tsx +++ b/src/components/connections/ServerForm.tsx @@ -299,7 +299,7 @@ export function ServerForm({ ) : (

Cameron sees its tools as{" "} - {draft.name.trim() || "name"}__tool. + {draft.name.trim() || "name"}_tool.

)} diff --git a/src/hooks/useMCPServers.ts b/src/hooks/useMCPServers.ts index 329c87c..e50bd01 100644 --- a/src/hooks/useMCPServers.ts +++ b/src/hooks/useMCPServers.ts @@ -5,6 +5,7 @@ import { deleteServer, fetchServers, fetchServerTools, + ServerToolsError, setServerEnabled, updateServer, type MCPServerView, @@ -71,9 +72,20 @@ export function useMCPServers() { /** Connects to one server only once `enabled` (the list is opened), then keeps the result. */ export function useServerTools(id: string, enabled: boolean) { + const queryClient = useQueryClient(); return useQuery({ queryKey: toolsKey(id), - queryFn: () => fetchServerTools(id), + queryFn: async () => { + try { + return await fetchServerTools(id); + } catch (error) { + // The server recorded that it needs sign-in: reload the list so Connect appears. + if (error instanceof ServerToolsError && error.code === "auth_required") { + void queryClient.invalidateQueries({ queryKey: SERVERS_KEY }); + } + throw error; + } + }, enabled, staleTime: Infinity, retry: false, diff --git a/src/lib/agent/mcp.ts b/src/lib/agent/mcp.ts index f6eab35..f8a6df1 100644 --- a/src/lib/agent/mcp.ts +++ b/src/lib/agent/mcp.ts @@ -1,82 +1,40 @@ -import { MultiServerMCPClient } from "@langchain/mcp-adapters"; -import { ServerOAuthProvider } from "@/lib/mcp/oauth-provider"; -import { OAuthStatus } from "@/lib/mcp/oauth-status"; -import { toServerConfig } from "@/lib/mcp/serverConfig"; -import { CONNECT_TIMEOUT_MS } from "@/lib/mcp/connector"; +import { ConnectError, openSession } from "@/lib/mcp/connector"; import type { MCPServer, MCPTool } from "@/types/mcp"; -const oauthProvider = (server: MCPServer) => new ServerOAuthProvider(server.id, server.name); - export type ServerToolsError = "auth_required" | "invalid_config" | "timeout" | "connect_failed"; export type ServerToolsResult = | { ok: true; tools: MCPTool[] } | { ok: false; error: ServerToolsError; message: string }; -const PROBE_TIMEOUT_MS = CONNECT_TIMEOUT_MS; - -class ProbeTimeout extends Error {} - /** * Connects to ONE server (enabled or not) and lists its tools, then closes the connection. * A failure is reported rather than skipped: with a single server, "no tools" and "could not - * connect" must not look the same. + * connect" must not look the same. Goes through the connector, so it connects as the agent does. */ export async function listServerTools(server: MCPServer): Promise { - if ( - server.type === "http" && - (server.oauthStatus === OAuthStatus.REQUIRED || server.oauthStatus === OAuthStatus.EXPIRED) - ) { - return { - ok: false, - error: "auth_required", - message: "Connect this server with OAuth before listing its tools.", - }; - } - - const config = toServerConfig(server, oauthProvider); - if (!config) { - return { - ok: false, - error: "invalid_config", - message: server.type === "stdio" ? "No command configured." : "No URL configured.", - }; - } - - const client = new MultiServerMCPClient({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mcpServers: { [server.name]: config } as any, - onConnectionError: "throw", - throwOnLoadError: true, - }); - let timer: ReturnType | undefined; - try { - const tools = await Promise.race([ - client.getTools(), - new Promise((_, reject) => { - timer = setTimeout(() => reject(new ProbeTimeout()), PROBE_TIMEOUT_MS); - }), - ]); - return { - ok: true, - tools: tools.map((tool) => ({ name: tool.name, description: tool.description || undefined })), - }; - } catch (error) { - if (error instanceof ProbeTimeout) { + const session = await openSession(server); + try { + const { tools } = await session.manifest(); return { - ok: false, - error: "timeout", - message: `No response within ${PROBE_TIMEOUT_MS / 1000}s.`, + ok: true, + tools: tools.map((tool) => ({ + name: tool.name, + description: tool.description || undefined, + })), }; + } finally { + await session.close().catch(() => {}); + } + } catch (error) { + if (error instanceof ConnectError) { + return { ok: false, error: error.code, message: error.message }; } return { ok: false, error: "connect_failed", message: error instanceof Error ? error.message : String(error), }; - } finally { - clearTimeout(timer); - await client.close().catch(() => {}); } } diff --git a/src/lib/mcp/authFailure.test.ts b/src/lib/mcp/authFailure.test.ts new file mode 100644 index 0000000..5592804 --- /dev/null +++ b/src/lib/mcp/authFailure.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; +import { UnauthorizedError } from "@modelcontextprotocol/client"; +import { isAuthFailure } from "./authFailure"; + +const wrap = (cause: unknown, message = "Authentication failed for HTTP server") => + new Error(message, { cause }); + +describe("isAuthFailure", () => { + it("finds the SDK's UnauthorizedError under the adapter's wrapper", () => { + expect(isAuthFailure(wrap(new UnauthorizedError("no token")))).toBe(true); + }); + + it("finds an HTTP 401 by status or by the SDK's message", () => { + expect(isAuthFailure(wrap(Object.assign(new Error("x"), { status: 401 })))).toBe(true); + expect( + isAuthFailure( + wrap(new Error("Version negotiation failed: requires authorization (HTTP 401)")), + ), + ).toBe(true); + }); + + it("is false for other failures, including other HTTP statuses", () => { + expect(isAuthFailure(new Error("ECONNREFUSED"))).toBe(false); + expect(isAuthFailure(wrap(Object.assign(new Error("x"), { status: 403 })))).toBe(false); + expect(isAuthFailure("not an error")).toBe(false); + }); +}); diff --git a/src/lib/mcp/authFailure.ts b/src/lib/mcp/authFailure.ts new file mode 100644 index 0000000..b830ce8 --- /dev/null +++ b/src/lib/mcp/authFailure.ts @@ -0,0 +1,29 @@ +import { UnauthorizedError } from "@modelcontextprotocol/client"; + +const MAX_CAUSE_DEPTH = 4; + +function httpStatus(value: unknown): number | undefined { + if (typeof value !== "object" || value === null) return undefined; + const { status, code, message } = value as { + status?: unknown; + code?: unknown; + message?: unknown; + }; + if (typeof status === "number") return status; + if (typeof code === "number") return code; + const match = typeof message === "string" ? message.match(/\(HTTP (\d{3})\)/) : null; + return match ? Number(match[1]) : undefined; +} + +/** + * True when the error, or one of its causes, means the server wants credentials: the SDK's + * `UnauthorizedError` or an HTTP 401. The adapter wraps these, so the cause chain is walked. + */ +export function isAuthFailure(error: unknown): boolean { + let current: unknown = error; + for (let depth = 0; depth <= MAX_CAUSE_DEPTH && current !== undefined; depth += 1) { + if (UnauthorizedError.isInstance(current) || httpStatus(current) === 401) return true; + current = current instanceof Error ? current.cause : undefined; + } + return false; +} diff --git a/src/lib/mcp/connector.ts b/src/lib/mcp/connector.ts index 61579d2..4ed1c13 100644 --- a/src/lib/mcp/connector.ts +++ b/src/lib/mcp/connector.ts @@ -1,11 +1,9 @@ -import { MultiServerMCPClient } from "@langchain/mcp-adapters"; +import { MCPAdapter } from "@langchain/mcp-adapters"; import type { StructuredToolInterface } from "@langchain/core/tools"; -import { - TOOL_NAME_SEPARATOR, - type McpSession, - type ServerManifest, -} from "@agentailor/mcp-disclosure"; +import type { McpSession, ServerManifest } from "@agentailor/mcp-disclosure"; +import { isAuthFailure } from "@/lib/mcp/authFailure"; import { ServerOAuthProvider } from "@/lib/mcp/oauth-provider"; +import * as mcpServerRepo from "@/lib/repositories/mcpServerRepository"; import { OAuthStatus } from "@/lib/mcp/oauth-status"; import { toServerConfig } from "@/lib/mcp/serverConfig"; import type { ServerToolsError } from "@/lib/agent/mcp"; @@ -64,23 +62,19 @@ export async function openSession(server: MCPServer): Promise { ); } - const client = new MultiServerMCPClient({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mcpServers: { [server.name]: config } as any, + // The adapter's default `server_tool` names are the ones the model is shown: no renaming. + const client = new MCPAdapter({ + servers: { [server.name]: config }, onConnectionError: "throw", throwOnLoadError: true, - prefixToolNameWithServerName: true, }); try { - const loaded = await withTimeout(client.getTools()); + const loaded = await withTimeout(client.listTools()); const sdk = await client.getClient(server.name); if (!sdk) throw new ConnectError("connect_failed", "Server did not connect."); - const prefix = `${server.name}${TOOL_NAME_SEPARATOR}`; - const tools = new Map( - loaded.map((t) => [t.name.startsWith(prefix) ? t.name.slice(prefix.length) : t.name, t]), - ); + const tools = new Map(loaded.map((t) => [t.name, t])); const closeListeners: (() => void)[] = []; const previousOnClose = sdk.onclose; sdk.onclose = () => { @@ -106,6 +100,14 @@ export async function openSession(server: MCPServer): Promise { } catch (error) { await client.close().catch(() => {}); if (error instanceof ConnectError) throw error; + if (server.type === "http" && isAuthFailure(error)) { + // Record what the server said, so the Connections page offers Connect. + const lost = server.oauthStatus === OAuthStatus.CONNECTED; + await mcpServerRepo + .setOAuthStatus(server.id, lost ? OAuthStatus.EXPIRED : OAuthStatus.REQUIRED) + .catch(() => {}); + throw new ConnectError("auth_required", "This server needs sign-in: use Connect first."); + } throw new ConnectError( "connect_failed", error instanceof Error ? error.message : String(error), diff --git a/src/lib/mcp/oauth-detection.ts b/src/lib/mcp/oauth-detection.ts index 8a92567..0a18cfd 100644 --- a/src/lib/mcp/oauth-detection.ts +++ b/src/lib/mcp/oauth-detection.ts @@ -6,9 +6,13 @@ import { OAuthStatus } from "./oauth-status"; // server-side call sites that import it from oauth-detection. export { OAuthStatus }; +const DETECTION_TIMEOUT_MS = 5_000; + export interface OAuthDetectionResult { requiresAuth: boolean; resourceMetadataUrl?: string; + /** The `scope` the server's challenge asked for, when it named one. */ + scope?: string; error?: string; } @@ -21,6 +25,7 @@ export async function detectOAuthRequirement(url: string): Promise tokens.expires_at - 60; } + +/** + * Probes a just-saved HTTP server and records whether it needs OAuth, so the Connections page can + * offer Connect at once. An unreachable server is recorded as UNKNOWN ("Check auth"), not as + * NOT_REQUIRED. Returns the status written. + */ +export async function recordOAuthRequirement(serverId: string, url: string): Promise { + const detection = await detectOAuthRequirement(url); + const status = detection.requiresAuth + ? OAuthStatus.REQUIRED + : detection.error + ? OAuthStatus.UNKNOWN + : OAuthStatus.NOT_REQUIRED; + await mcpServerRepo.setOAuthStatus(serverId, status); + return status; +} diff --git a/src/lib/mcp/oauth-provider.ts b/src/lib/mcp/oauth-provider.ts index 17d4f2e..db07623 100644 --- a/src/lib/mcp/oauth-provider.ts +++ b/src/lib/mcp/oauth-provider.ts @@ -1,20 +1,22 @@ -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; import type { OAuthClientInformation, OAuthClientMetadata, + OAuthClientProvider, OAuthTokens, -} from "@modelcontextprotocol/sdk/shared/auth.js"; +} from "@modelcontextprotocol/client"; import * as mcpServerRepo from "@/lib/repositories/mcpServerRepository"; import { OAuthStatus } from "./oauth-detection"; import { getAppUrl } from "@/lib/config/app-url"; /** * Server-side OAuth provider that implements the OAuthClientProvider interface - * from @modelcontextprotocol/sdk. Stores OAuth data in the database. + * from @modelcontextprotocol/client. Stores OAuth data in the database. */ export class ServerOAuthProvider implements OAuthClientProvider { private serverId: string; private serverName: string; + /** Set when the SDK asks to send the user to the authorization server. */ + pendingAuthorizationUrl?: URL; constructor(serverId: string, serverName: string) { this.serverId = serverId; @@ -33,7 +35,7 @@ export class ServerOAuthProvider implements OAuthClientProvider { redirect_uris: [this.redirectUrl], grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], - scope: "read write", + // No scope here: the SDK takes it from the server's challenge or resource metadata. token_endpoint_auth_method: "none", }; } @@ -57,10 +59,8 @@ export class ServerOAuthProvider implements OAuthClientProvider { } async redirectToAuthorization(url: URL): Promise { - // This method is called by the SDK when it needs to redirect to the auth server. - // In a server-side context, we can't redirect directly - we throw an error - // and handle the URL generation separately in the API route. - throw new Error(`REDIRECT_REQUIRED:${url.toString()}`); + // Server-side: nothing to redirect. The OAuth check route hands the URL to the browser. + this.pendingAuthorizationUrl = url; } async saveCodeVerifier(verifier: string): Promise { @@ -74,4 +74,12 @@ export class ServerOAuthProvider implements OAuthClientProvider { } return verifier; } + + async invalidateCredentials( + scope: "all" | "client" | "tokens" | "verifier" | "discovery", + ): Promise { + // Discovery results are not cached here, so there is nothing to drop for that scope. + if (scope === "discovery") return; + await mcpServerRepo.clearOAuthCredentials(this.serverId, scope); + } } diff --git a/src/lib/mcp/serverConfig.test.ts b/src/lib/mcp/serverConfig.test.ts index 4347d65..7de501c 100644 --- a/src/lib/mcp/serverConfig.test.ts +++ b/src/lib/mcp/serverConfig.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; import { MCPServerType, type MCPServer } from "@/types/mcp"; import { toServerConfig } from "./serverConfig"; @@ -36,6 +36,7 @@ describe("toServerConfig", () => { command: "npx", args: ["-y", "pkg"], env: { TOKEN: "x" }, + elicitation: false, }); }); @@ -43,7 +44,12 @@ describe("toServerConfig", () => { const config = toServerConfig( makeServer({ type: MCPServerType.http, url: "https://x.dev/mcp", headers: { A: "b" } }), ); - expect(config).toEqual({ transport: "http", url: "https://x.dev/mcp", headers: { A: "b" } }); + expect(config).toEqual({ + transport: "http", + url: "https://x.dev/mcp", + headers: { A: "b" }, + elicitation: false, + }); }); it("returns null when the command or url is missing", () => { diff --git a/src/lib/mcp/serverConfig.ts b/src/lib/mcp/serverConfig.ts index 55d7c86..c24453b 100644 --- a/src/lib/mcp/serverConfig.ts +++ b/src/lib/mcp/serverConfig.ts @@ -1,12 +1,13 @@ -import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { OAuthClientProvider } from "@modelcontextprotocol/client"; import type { MCPServer } from "@/types/mcp"; import { OAuthStatus } from "./oauth-status"; export interface StdioMCPServerConfig { transport: "stdio"; command: string; - args?: string[]; + args: string[]; env?: Record; + elicitation: false; } export interface HttpMCPServerConfig { @@ -14,6 +15,7 @@ export interface HttpMCPServerConfig { url: string; headers?: Record; authProvider?: OAuthClientProvider; + elicitation: false; } export type MCPServerConfig = StdioMCPServerConfig | HttpMCPServerConfig; @@ -26,25 +28,29 @@ export function usesOAuth(server: MCPServer): boolean { } /** - * Turns a stored row into a MultiServerMCPClient connection config, or null when the row is - * missing its command/url. Shared by the agent and the per-server tools probe so both connect - * the same way. `authProvider` is injected to keep this module free of the database. + * Turns a stored row into an MCPAdapter connection config, or null when the row is missing its + * command/url. Shared by the agent and the per-server tools probe so both connect the same way. + * `authProvider` is injected to keep this module free of the database. + * Elicitation is off: its interrupt is one the approval flow cannot answer. */ export function toServerConfig( server: MCPServer, authProvider?: (server: MCPServer) => OAuthClientProvider, ): MCPServerConfig | null { if (server.type === "stdio" && server.command) { - const config: StdioMCPServerConfig = { transport: "stdio", command: server.command }; - if (Array.isArray(server.args)) { - config.args = server.args.filter((arg): arg is string => typeof arg === "string"); - } + const args = Array.isArray(server.args) ? server.args : []; + const config: StdioMCPServerConfig = { + transport: "stdio", + command: server.command, + args: args.filter((arg): arg is string => typeof arg === "string"), + elicitation: false, + }; if (isRecord(server.env)) config.env = server.env as Record; return config; } if (server.type === "http" && server.url) { - const config: HttpMCPServerConfig = { transport: "http", url: server.url }; + const config: HttpMCPServerConfig = { transport: "http", url: server.url, elicitation: false }; if (isRecord(server.headers)) config.headers = server.headers as Record; if (authProvider && usesOAuth(server)) config.authProvider = authProvider(server); return config; diff --git a/src/lib/repositories/mcpServerRepository.ts b/src/lib/repositories/mcpServerRepository.ts index 75ee42f..45e05bb 100644 --- a/src/lib/repositories/mcpServerRepository.ts +++ b/src/lib/repositories/mcpServerRepository.ts @@ -206,3 +206,27 @@ export async function markConnected(id: string): Promise { .set({ oauthStatus: "CONNECTED", requiresAuth: true, codeVerifier: null, updatedAt: now() }) .where(eq(mcpServers.id, id)); } + +export type OAuthCredentialScope = "all" | "client" | "tokens" | "verifier"; + +/** + * Drops stored OAuth credentials the SDK reported as invalid. Losing tokens (or the client they + * were issued to) marks the server as needing re-authorization. + */ +export async function clearOAuthCredentials( + id: string, + scope: OAuthCredentialScope, +): Promise { + const all = scope === "all"; + const client = all || scope === "client"; + const tokens = client || scope === "tokens"; + await db + .update(mcpServers) + .set({ + ...(client ? { clientInfo: null } : {}), + ...(tokens ? { authTokens: null, oauthStatus: client ? "REQUIRED" : "EXPIRED" } : {}), + ...(all || scope === "verifier" ? { codeVerifier: null } : {}), + updatedAt: now(), + }) + .where(eq(mcpServers.id, id)); +} diff --git a/src/lib/tracing/genAiTracer.test.ts b/src/lib/tracing/genAiTracer.test.ts index 8171b20..414d23a 100644 --- a/src/lib/tracing/genAiTracer.test.ts +++ b/src/lib/tracing/genAiTracer.test.ts @@ -66,14 +66,14 @@ const BALANCE_SCHEMA = { }; const BALANCE_DEFINITION = { type: "function", - name: "bank__get_balance", + name: "bank_get_balance", description: "Current balance of one account.", parameters: BALANCE_SCHEMA, }; // The shapes each provider's `invocation_params.tools` really has (captured from the SDKs). const ANTHROPIC_TOOLS = [ { - name: "bank__get_balance", + name: "bank_get_balance", description: "Current balance of one account.", input_schema: BALANCE_SCHEMA, }, @@ -82,7 +82,7 @@ const OPENAI_TOOLS = [ { type: "function", function: { - name: "bank__get_balance", + name: "bank_get_balance", description: "Current balance of one account.", parameters: BALANCE_SCHEMA, }, @@ -92,7 +92,7 @@ const GEMINI_TOOLS = [ { functionDeclarations: [ { - name: "bank__get_balance", + name: "bank_get_balance", description: "Current balance of one account.", parameters: BALANCE_SCHEMA, },