From c14950a3bdb0731de4dccf6b155e4d323847f3c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:59:10 +0000 Subject: [PATCH] chore: version packages --- .changeset/docs-readme-api-drift.md | 5 -- .changeset/feat-versioned-state-contract.md | 10 ---- .changeset/fix-context-schema-inference.md | 5 -- .changeset/fix-manual-tool-pending-state.md | 11 ---- .changeset/fix-resume-string-input.md | 5 -- .changeset/fix-tool-terminal-empty-final.md | 5 -- .changeset/hooks-lifecycle-system.md | 13 ----- .changeset/mcp-result-discrimination.md | 13 ----- .changeset/openrouter-mcp-initial.md | 10 ---- .changeset/orange-drinks-smile.md | 5 -- .changeset/post-model-call-telemetry.md | 11 ---- packages/agent/CHANGELOG.md | 58 +++++++++++++++++++++ packages/agent/package.json | 2 +- packages/mcp/CHANGELOG.md | 26 +++++++++ packages/mcp/package.json | 2 +- 15 files changed, 86 insertions(+), 95 deletions(-) delete mode 100644 .changeset/docs-readme-api-drift.md delete mode 100644 .changeset/feat-versioned-state-contract.md delete mode 100644 .changeset/fix-context-schema-inference.md delete mode 100644 .changeset/fix-manual-tool-pending-state.md delete mode 100644 .changeset/fix-resume-string-input.md delete mode 100644 .changeset/fix-tool-terminal-empty-final.md delete mode 100644 .changeset/hooks-lifecycle-system.md delete mode 100644 .changeset/mcp-result-discrimination.md delete mode 100644 .changeset/openrouter-mcp-initial.md delete mode 100644 .changeset/orange-drinks-smile.md delete mode 100644 .changeset/post-model-call-telemetry.md create mode 100644 packages/mcp/CHANGELOG.md diff --git a/.changeset/docs-readme-api-drift.md b/.changeset/docs-readme-api-drift.md deleted file mode 100644 index f8d8a78..0000000 --- a/.changeset/docs-readme-api-drift.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@openrouter/agent": patch ---- - -Docs: fix three README/API drifts found while building production agents — tool context is `ctx.local` (not `ctx.context`); the `state` option takes a `StateAccessor` (`{load, save}`) and state is read via `result.getState()` (not `(await getResponse()).state`); `getToolStream()` emits argument deltas + generator preliminary results, while execution results are on `getFullResponsesStream()`. Adds a streams cheat-sheet table. diff --git a/.changeset/feat-versioned-state-contract.md b/.changeset/feat-versioned-state-contract.md deleted file mode 100644 index 0ffa23a..0000000 --- a/.changeset/feat-versioned-state-contract.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@openrouter/agent": minor ---- - -Add a versioned `ConversationState` serialization contract. - -- Optional `version` field on `ConversationState` (absence means v1); `createInitialState` now stamps `version: 1`. -- New helpers: `serializeConversationState` / `deserializeConversationState` (package root + `@openrouter/agent/conversation-state`). -- Typed errors: `UnsupportedStateVersionError` (`{found, supported}`) and `InvalidStateError` for malformed payloads. -- Compat policy: treat JSON as opaque; additive changes within a major; migrations run in `deserializeConversationState` on version bump. StateAccessor load/save is unchanged — helpers are opt-in wrappers over what consumers already do with `JSON.stringify`/`parse`. diff --git a/.changeset/fix-context-schema-inference.md b/.changeset/fix-context-schema-inference.md deleted file mode 100644 index 750aab6..0000000 --- a/.changeset/fix-context-schema-inference.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@openrouter/agent": patch ---- - -Infer tool context types from `contextSchema` end-to-end: `tool()` now preserves the concrete Zod schema through its overloads, so `execute`'s `ctx.local` is typed from the schema and `callModel`'s `context` map slots accept/reject the real per-tool shape — no more `ctx.local as X` or `context: map as any`. Tools without a `contextSchema` still resolve their map slot to `Record`. Types-only; runtime behavior unchanged. diff --git a/.changeset/fix-manual-tool-pending-state.md b/.changeset/fix-manual-tool-pending-state.md deleted file mode 100644 index f5f19d7..0000000 --- a/.changeset/fix-manual-tool-pending-state.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@openrouter/agent": minor ---- - -Persist unresolved manual tool calls (`execute: false` / no execute fn) to `ConversationState.pendingToolCalls` when the loop stops, and set status to the new value `'awaiting_client_tools'`. - -Previously, HITL pauses (`onToolCalled → null`) correctly populated `pendingToolCalls` with status `'awaiting_hitl'`, but bare manual tools only `break`'d the loop — `getPendingToolCalls()` returned `[]` and status was left `in_progress`/`complete`. Cold-start consumers could not recover the unresolved calls from serialized state. - -- New `ConversationStatus` value: `'awaiting_client_tools'` (additive; does not replace `'awaiting_hitl'`). -- Mixed auto+manual rounds still execute/persist regular tool outputs, then pause with only the unresolved manual calls in `pendingToolCalls`. -- A successful resume with new input from `'awaiting_client_tools'` clears the stale pendings and continues as a normal turn. Failed resume requests leave the paused state intact. Manual tools are not approved/rejected via call IDs (unlike HITL/`awaiting_approval`). diff --git a/.changeset/fix-resume-string-input.md b/.changeset/fix-resume-string-input.md deleted file mode 100644 index fe51179..0000000 --- a/.changeset/fix-resume-string-input.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@openrouter/agent": patch ---- - -Fix: bare string `input` is now normalized into a message item when resuming a conversation with loaded history. Previously the raw string was appended to the request input array un-normalized, causing an OpenResponses 400 validation error on the advertised string-input style. diff --git a/.changeset/fix-tool-terminal-empty-final.md b/.changeset/fix-tool-terminal-empty-final.md deleted file mode 100644 index a6fcfd5..0000000 --- a/.changeset/fix-tool-terminal-empty-final.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@openrouter/agent": patch ---- - -Tolerate empty final `output` after completed tool rounds: retry the follow-up request once, then resolve successfully with empty text instead of throwing `Invalid final response: empty or invalid output`. Mini-class models intermittently treat a successful tool call as the terminal answer. Opt into the old throw with `strictFinalResponse: true`. Runs with no completed tool work still throw on empty output. diff --git a/.changeset/hooks-lifecycle-system.md b/.changeset/hooks-lifecycle-system.md deleted file mode 100644 index 5852f2e..0000000 --- a/.changeset/hooks-lifecycle-system.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@openrouter/agent': minor ---- - -Add a typed lifecycle hook system to `callModel`, inspired by the Claude Agent SDK hooks pattern. - -Two usage modes: an inline config object (built-in hooks only) or a `HooksManager` instance (custom hooks, dynamic registration via `on()`/`off()`/`removeAll()`, programmatic `emit()`). - -Eight built-in hooks: `PreToolUse` (block or mutate tool input before every client-tool execution), `PostToolUse` / `PostToolUseFailure` (observe results and errors with timing), `UserPromptSubmit` (mutate or reject the prompt before the initial request), `PermissionRequest` (programmatically allow/deny/ask for tools requiring approval), `Stop` (force-resume a halted loop or inject a follow-up prompt, capped against runaway handlers), and `SessionStart` / `SessionEnd` (paired once per run on every exit path, including approval pauses, interruptions, errors, and no-tools streaming paths). - -Features: tool matchers (string / RegExp / predicate), payload filter predicates, sequential mutation piping, short-circuit on block/reject, async fire-and-forget handlers with `drain()` / `abortInflight()` / per-handler timeouts and cooperative cancellation via `ctx.signal`, configurable error handling (`throwOnHandlerError`), and custom hook definitions via Zod schema pairs with full TypeScript inference (transforms/defaults are honored — handlers receive parsed output values). - -The API is additive: existing `onTurnStart`, `onTurnEnd`, and `requireApproval` are unchanged. Public exports: `HooksManager`, `HookName`, `isAsyncOutput`, and the payload/result/config types; also available via the `@openrouter/agent/hooks-manager` subpath. diff --git a/.changeset/mcp-result-discrimination.md b/.changeset/mcp-result-discrimination.md deleted file mode 100644 index 4d4b01f..0000000 --- a/.changeset/mcp-result-discrimination.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@openrouter/agent": minor -"@openrouter/mcp": minor ---- - -Add a `source` discriminant to tool results so untyped MCP tools no longer collapse the type safety of typed tools. - -Previously, mixing an MCP tool (whose output schema is `unknown`) with fully-typed tools in one `callModel({ tools })` array collapsed the entire result union to `unknown` — one untyped tool poisoned every other tool's result type. - -- `ToolExecutionResult` (and `ToolExecutionResultUnion`) now carry `source: 'client' | 'mcp'`. Narrowing on `source === 'client'` recovers the precise, schema-derived results for your own tools; MCP results stay isolated as `unknown` under `source === 'mcp'`. -- `ToolResultEvent` (streaming: `getFullResponsesStream`, `getToolStream`) gains the same `source` field. **Breaking:** the `tool.result` event payload now includes `source`; consumers that constructed or exhaustively matched these events may need to account for it. -- `@openrouter/agent` exports a `markMcp()` helper, an `isMcpTool()` guard, and the `McpBranded` type. `@openrouter/mcp` brands every wrapped tool (including synthetic `list_resources`/`read_resource`) so the discrimination is automatic — callers just spread `mcp.tools` as before. -- MCP tools continue to execute locally and serialize to the wire as `type: 'function'`; the brand is purely informational and does not change runtime behavior. diff --git a/.changeset/openrouter-mcp-initial.md b/.changeset/openrouter-mcp-initial.md deleted file mode 100644 index cf23a14..0000000 --- a/.changeset/openrouter-mcp-initial.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@openrouter/mcp": minor ---- - -Add `@openrouter/mcp`: expose remote MCP server tools (Streamable HTTP / SSE) as `callModel` tools. - -- `createMCPTools()` connects to a non-stdio MCP server, authenticates once (bearer token, custom headers, or a pluggable `OAuthClientProvider`), and returns a handle whose `.tools` drop straight into `callModel({ tools })`. The same auth is reused for tool discovery and every tool call. -- Faithful runtime JSON-Schema → Zod v4 conversion (`convertMcpInputSchema`) so the model sees real parameters; tool output schemas are mapped too. -- Serializable, rehydratable cache (`serialize()` / `rehydrateMCPTools()` / pluggable `MCPCacheStore` + `InMemoryMCPCacheStore`) that skips re-listing and, opt-in, re-authentication. Credential caching is off by default. -- MCP feature support: progress notifications surfaced as generator-tool events, `tools/list_changed` auto-refresh, cancellation via an abort signal, resources exposed as synthetic `list_resources`/`read_resource` tools, and elicitation with an optional handler (auto-declines when none is provided). diff --git a/.changeset/orange-drinks-smile.md b/.changeset/orange-drinks-smile.md deleted file mode 100644 index 91d90fa..0000000 --- a/.changeset/orange-drinks-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@openrouter/agent': patch ---- - -Stop the tool-execution loop when a round contains unresolved manual (client-executed) tool calls, instead of sending a follow-up request whose input carries a `function_call` with no matching `function_call_output` — a history providers reject with a 400 "No tool output found for function call ...". The response is surfaced so the caller can execute the manual calls and continue, mirroring the existing all-manual behavior. diff --git a/.changeset/post-model-call-telemetry.md b/.changeset/post-model-call-telemetry.md deleted file mode 100644 index 0650204..0000000 --- a/.changeset/post-model-call-telemetry.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@openrouter/agent': minor ---- - -Add a `PostModelCall` lifecycle hook and aggregate usage totals on `SessionEnd` — the telemetry primitives for tracing and benchmark consumers. - -`PostModelCall` fires once per completed model response, on **every** request the agent loop makes: the initial request, each tool-round follow-up, the empty-final retry, the `allowFinalResponse` final turn, and approval-resume requests. The payload carries `responseId` (the OpenRouter generation id, deep-linkable), `model`, `durationMs` (dispatch to fully materialized response, including stream consumption), `turnType` (`'initial' | 'resume' | 'tool_round' | 'final' | 'retry'`), `turnNumber`, and a normalized `usage` block (`inputTokens`, `outputTokens`, `totalTokens`, `cachedTokens`, `reasoningTokens`, `cost?`) when the server reported usage accounting. Purely observational: handlers cannot mutate or block. - -`SessionEnd` now carries an optional `totalUsage` aggregate (`modelCalls` plus the summed usage fields, with `cost` present when any call reported one) whenever at least one model call completed during the run. - -New exported types: `PostModelCallPayload`, `ModelCallUsage`, `SessionUsageTotals`. diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md index 74c89d4..2d5e5e3 100644 --- a/packages/agent/CHANGELOG.md +++ b/packages/agent/CHANGELOG.md @@ -1,5 +1,63 @@ # @openrouter/agent +## 0.8.0 + +### Minor Changes + +- [#66](https://github.com/OpenRouterTeam/typescript-agent/pull/66) [`c83cceb`](https://github.com/OpenRouterTeam/typescript-agent/commit/c83cceb17ec1d66b9a1fd2d46ac8ac9b6e60fa4c) Thanks [@LukasParke](https://github.com/LukasParke)! - Add a versioned `ConversationState` serialization contract. + + - Optional `version` field on `ConversationState` (absence means v1); `createInitialState` now stamps `version: 1`. + - New helpers: `serializeConversationState` / `deserializeConversationState` (package root + `@openrouter/agent/conversation-state`). + - Typed errors: `UnsupportedStateVersionError` (`{found, supported}`) and `InvalidStateError` for malformed payloads. + - Compat policy: treat JSON as opaque; additive changes within a major; migrations run in `deserializeConversationState` on version bump. StateAccessor load/save is unchanged — helpers are opt-in wrappers over what consumers already do with `JSON.stringify`/`parse`. + +- [#64](https://github.com/OpenRouterTeam/typescript-agent/pull/64) [`e4d06e3`](https://github.com/OpenRouterTeam/typescript-agent/commit/e4d06e38215d6eafbd5c198e3485f476e65d26f0) Thanks [@LukasParke](https://github.com/LukasParke)! - Persist unresolved manual tool calls (`execute: false` / no execute fn) to `ConversationState.pendingToolCalls` when the loop stops, and set status to the new value `'awaiting_client_tools'`. + + Previously, HITL pauses (`onToolCalled → null`) correctly populated `pendingToolCalls` with status `'awaiting_hitl'`, but bare manual tools only `break`'d the loop — `getPendingToolCalls()` returned `[]` and status was left `in_progress`/`complete`. Cold-start consumers could not recover the unresolved calls from serialized state. + + - New `ConversationStatus` value: `'awaiting_client_tools'` (additive; does not replace `'awaiting_hitl'`). + - Mixed auto+manual rounds still execute/persist regular tool outputs, then pause with only the unresolved manual calls in `pendingToolCalls`. + - A successful resume with new input from `'awaiting_client_tools'` clears the stale pendings and continues as a normal turn. Failed resume requests leave the paused state intact. Manual tools are not approved/rejected via call IDs (unlike HITL/`awaiting_approval`). + +- [#7](https://github.com/OpenRouterTeam/typescript-agent/pull/7) [`80ff8a7`](https://github.com/OpenRouterTeam/typescript-agent/commit/80ff8a730292aa00a3acfcce6ab1e9f5a6a7f0de) Thanks [@mattapperson](https://github.com/mattapperson)! - Add a typed lifecycle hook system to `callModel`, inspired by the Claude Agent SDK hooks pattern. + + Two usage modes: an inline config object (built-in hooks only) or a `HooksManager` instance (custom hooks, dynamic registration via `on()`/`off()`/`removeAll()`, programmatic `emit()`). + + Eight built-in hooks: `PreToolUse` (block or mutate tool input before every client-tool execution), `PostToolUse` / `PostToolUseFailure` (observe results and errors with timing), `UserPromptSubmit` (mutate or reject the prompt before the initial request), `PermissionRequest` (programmatically allow/deny/ask for tools requiring approval), `Stop` (force-resume a halted loop or inject a follow-up prompt, capped against runaway handlers), and `SessionStart` / `SessionEnd` (paired once per run on every exit path, including approval pauses, interruptions, errors, and no-tools streaming paths). + + Features: tool matchers (string / RegExp / predicate), payload filter predicates, sequential mutation piping, short-circuit on block/reject, async fire-and-forget handlers with `drain()` / `abortInflight()` / per-handler timeouts and cooperative cancellation via `ctx.signal`, configurable error handling (`throwOnHandlerError`), and custom hook definitions via Zod schema pairs with full TypeScript inference (transforms/defaults are honored — handlers receive parsed output values). + + The API is additive: existing `onTurnStart`, `onTurnEnd`, and `requireApproval` are unchanged. Public exports: `HooksManager`, `HookName`, `isAsyncOutput`, and the payload/result/config types; also available via the `@openrouter/agent/hooks-manager` subpath. + +- [#56](https://github.com/OpenRouterTeam/typescript-agent/pull/56) [`209499a`](https://github.com/OpenRouterTeam/typescript-agent/commit/209499abacd6783ee5c98155bb2a676e3932c3f4) Thanks [@mattapperson](https://github.com/mattapperson)! - Add a `source` discriminant to tool results so untyped MCP tools no longer collapse the type safety of typed tools. + + Previously, mixing an MCP tool (whose output schema is `unknown`) with fully-typed tools in one `callModel({ tools })` array collapsed the entire result union to `unknown` — one untyped tool poisoned every other tool's result type. + + - `ToolExecutionResult` (and `ToolExecutionResultUnion`) now carry `source: 'client' | 'mcp'`. Narrowing on `source === 'client'` recovers the precise, schema-derived results for your own tools; MCP results stay isolated as `unknown` under `source === 'mcp'`. + - `ToolResultEvent` (streaming: `getFullResponsesStream`, `getToolStream`) gains the same `source` field. **Breaking:** the `tool.result` event payload now includes `source`; consumers that constructed or exhaustively matched these events may need to account for it. + - `@openrouter/agent` exports a `markMcp()` helper, an `isMcpTool()` guard, and the `McpBranded` type. `@openrouter/mcp` brands every wrapped tool (including synthetic `list_resources`/`read_resource`) so the discrimination is automatic — callers just spread `mcp.tools` as before. + - MCP tools continue to execute locally and serialize to the wire as `type: 'function'`; the brand is purely informational and does not change runtime behavior. + +- [#67](https://github.com/OpenRouterTeam/typescript-agent/pull/67) [`cb83f45`](https://github.com/OpenRouterTeam/typescript-agent/commit/cb83f45209ff66f8c58077f4e0a85d35f884afdb) Thanks [@LukasParke](https://github.com/LukasParke)! - Add a `PostModelCall` lifecycle hook and aggregate usage totals on `SessionEnd` — the telemetry primitives for tracing and benchmark consumers. + + `PostModelCall` fires once per completed model response, on **every** request the agent loop makes: the initial request, each tool-round follow-up, the empty-final retry, the `allowFinalResponse` final turn, and approval-resume requests. The payload carries `responseId` (the OpenRouter generation id, deep-linkable), `model`, `durationMs` (dispatch to fully materialized response, including stream consumption), `turnType` (`'initial' | 'resume' | 'tool_round' | 'final' | 'retry'`), `turnNumber`, and a normalized `usage` block (`inputTokens`, `outputTokens`, `totalTokens`, `cachedTokens`, `reasoningTokens`, `cost?`) when the server reported usage accounting. Purely observational: handlers cannot mutate or block. + + `SessionEnd` now carries an optional `totalUsage` aggregate (`modelCalls` plus the summed usage fields, with `cost` present when any call reported one) whenever at least one model call completed during the run. + + New exported types: `PostModelCallPayload`, `ModelCallUsage`, `SessionUsageTotals`. + +### Patch Changes + +- [#62](https://github.com/OpenRouterTeam/typescript-agent/pull/62) [`1362232`](https://github.com/OpenRouterTeam/typescript-agent/commit/1362232975f0254343f9842f30ec1b35d391f4fe) Thanks [@LukasParke](https://github.com/LukasParke)! - Docs: fix three README/API drifts found while building production agents — tool context is `ctx.local` (not `ctx.context`); the `state` option takes a `StateAccessor` (`{load, save}`) and state is read via `result.getState()` (not `(await getResponse()).state`); `getToolStream()` emits argument deltas + generator preliminary results, while execution results are on `getFullResponsesStream()`. Adds a streams cheat-sheet table. + +- [#65](https://github.com/OpenRouterTeam/typescript-agent/pull/65) [`09a041e`](https://github.com/OpenRouterTeam/typescript-agent/commit/09a041ea717b384c6c85d7c81ef391b170b0dd8f) Thanks [@LukasParke](https://github.com/LukasParke)! - Infer tool context types from `contextSchema` end-to-end: `tool()` now preserves the concrete Zod schema through its overloads, so `execute`'s `ctx.local` is typed from the schema and `callModel`'s `context` map slots accept/reject the real per-tool shape — no more `ctx.local as X` or `context: map as any`. Tools without a `contextSchema` still resolve their map slot to `Record`. Types-only; runtime behavior unchanged. + +- [#61](https://github.com/OpenRouterTeam/typescript-agent/pull/61) [`c020bc7`](https://github.com/OpenRouterTeam/typescript-agent/commit/c020bc7c86d2f743ecf9158ca3c9ff7b315e43b3) Thanks [@LukasParke](https://github.com/LukasParke)! - Fix: bare string `input` is now normalized into a message item when resuming a conversation with loaded history. Previously the raw string was appended to the request input array un-normalized, causing an OpenResponses 400 validation error on the advertised string-input style. + +- [#63](https://github.com/OpenRouterTeam/typescript-agent/pull/63) [`d96cd9f`](https://github.com/OpenRouterTeam/typescript-agent/commit/d96cd9fc589c27978bcdc2fd1921f754be88e3f0) Thanks [@LukasParke](https://github.com/LukasParke)! - Tolerate empty final `output` after completed tool rounds: retry the follow-up request once, then resolve successfully with empty text instead of throwing `Invalid final response: empty or invalid output`. Mini-class models intermittently treat a successful tool call as the terminal answer. Opt into the old throw with `strictFinalResponse: true`. Runs with no completed tool work still throw on empty output. + +- [#59](https://github.com/OpenRouterTeam/typescript-agent/pull/59) [`8edae63`](https://github.com/OpenRouterTeam/typescript-agent/commit/8edae63f4f6fe89e146f3abbf6d24dab7a164681) Thanks [@ayush-or](https://github.com/ayush-or)! - Stop the tool-execution loop when a round contains unresolved manual (client-executed) tool calls, instead of sending a follow-up request whose input carries a `function_call` with no matching `function_call_output` — a history providers reject with a 400 "No tool output found for function call ...". The response is surfaced so the caller can execute the manual calls and continue, mirroring the existing all-manual behavior. + ## 0.7.2 ### Patch Changes diff --git a/packages/agent/package.json b/packages/agent/package.json index 684358b..652d2d0 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/agent", - "version": "0.7.2", + "version": "0.8.0", "author": "OpenRouter", "description": "Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.", "keywords": [ diff --git a/packages/mcp/CHANGELOG.md b/packages/mcp/CHANGELOG.md new file mode 100644 index 0000000..82992d3 --- /dev/null +++ b/packages/mcp/CHANGELOG.md @@ -0,0 +1,26 @@ +# @openrouter/mcp + +## 0.2.0 + +### Minor Changes + +- [#56](https://github.com/OpenRouterTeam/typescript-agent/pull/56) [`209499a`](https://github.com/OpenRouterTeam/typescript-agent/commit/209499abacd6783ee5c98155bb2a676e3932c3f4) Thanks [@mattapperson](https://github.com/mattapperson)! - Add a `source` discriminant to tool results so untyped MCP tools no longer collapse the type safety of typed tools. + + Previously, mixing an MCP tool (whose output schema is `unknown`) with fully-typed tools in one `callModel({ tools })` array collapsed the entire result union to `unknown` — one untyped tool poisoned every other tool's result type. + + - `ToolExecutionResult` (and `ToolExecutionResultUnion`) now carry `source: 'client' | 'mcp'`. Narrowing on `source === 'client'` recovers the precise, schema-derived results for your own tools; MCP results stay isolated as `unknown` under `source === 'mcp'`. + - `ToolResultEvent` (streaming: `getFullResponsesStream`, `getToolStream`) gains the same `source` field. **Breaking:** the `tool.result` event payload now includes `source`; consumers that constructed or exhaustively matched these events may need to account for it. + - `@openrouter/agent` exports a `markMcp()` helper, an `isMcpTool()` guard, and the `McpBranded` type. `@openrouter/mcp` brands every wrapped tool (including synthetic `list_resources`/`read_resource`) so the discrimination is automatic — callers just spread `mcp.tools` as before. + - MCP tools continue to execute locally and serialize to the wire as `type: 'function'`; the brand is purely informational and does not change runtime behavior. + +- [#56](https://github.com/OpenRouterTeam/typescript-agent/pull/56) [`26336b5`](https://github.com/OpenRouterTeam/typescript-agent/commit/26336b5c44e5591b380ca4c41bf93b05f0ccdfe2) Thanks [@mattapperson](https://github.com/mattapperson)! - Add `@openrouter/mcp`: expose remote MCP server tools (Streamable HTTP / SSE) as `callModel` tools. + + - `createMCPTools()` connects to a non-stdio MCP server, authenticates once (bearer token, custom headers, or a pluggable `OAuthClientProvider`), and returns a handle whose `.tools` drop straight into `callModel({ tools })`. The same auth is reused for tool discovery and every tool call. + - Faithful runtime JSON-Schema → Zod v4 conversion (`convertMcpInputSchema`) so the model sees real parameters; tool output schemas are mapped too. + - Serializable, rehydratable cache (`serialize()` / `rehydrateMCPTools()` / pluggable `MCPCacheStore` + `InMemoryMCPCacheStore`) that skips re-listing and, opt-in, re-authentication. Credential caching is off by default. + - MCP feature support: progress notifications surfaced as generator-tool events, `tools/list_changed` auto-refresh, cancellation via an abort signal, resources exposed as synthetic `list_resources`/`read_resource` tools, and elicitation with an optional handler (auto-declines when none is provided). + +### Patch Changes + +- Updated dependencies [[`1362232`](https://github.com/OpenRouterTeam/typescript-agent/commit/1362232975f0254343f9842f30ec1b35d391f4fe), [`c83cceb`](https://github.com/OpenRouterTeam/typescript-agent/commit/c83cceb17ec1d66b9a1fd2d46ac8ac9b6e60fa4c), [`09a041e`](https://github.com/OpenRouterTeam/typescript-agent/commit/09a041ea717b384c6c85d7c81ef391b170b0dd8f), [`e4d06e3`](https://github.com/OpenRouterTeam/typescript-agent/commit/e4d06e38215d6eafbd5c198e3485f476e65d26f0), [`c020bc7`](https://github.com/OpenRouterTeam/typescript-agent/commit/c020bc7c86d2f743ecf9158ca3c9ff7b315e43b3), [`d96cd9f`](https://github.com/OpenRouterTeam/typescript-agent/commit/d96cd9fc589c27978bcdc2fd1921f754be88e3f0), [`80ff8a7`](https://github.com/OpenRouterTeam/typescript-agent/commit/80ff8a730292aa00a3acfcce6ab1e9f5a6a7f0de), [`209499a`](https://github.com/OpenRouterTeam/typescript-agent/commit/209499abacd6783ee5c98155bb2a676e3932c3f4), [`8edae63`](https://github.com/OpenRouterTeam/typescript-agent/commit/8edae63f4f6fe89e146f3abbf6d24dab7a164681), [`cb83f45`](https://github.com/OpenRouterTeam/typescript-agent/commit/cb83f45209ff66f8c58077f4e0a85d35f884afdb)]: + - @openrouter/agent@0.8.0 diff --git a/packages/mcp/package.json b/packages/mcp/package.json index d276fba..217e86d 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/mcp", - "version": "0.1.0", + "version": "0.2.0", "author": "OpenRouter", "description": "Expose remote MCP server tools (Streamable HTTP / SSE) as tools for @openrouter/agent's callModel, with serializable caching and pluggable auth.", "keywords": [