Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cache-swr-default-false.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nuxtjs/mcp-toolkit": patch
---

Default `swr` to `false` for cached tools and resources. Nitro's underlying default (`swr: true`) returned stale entries immediately and refreshed the handler in a background task that ran after the MCP request had been answered, silently dropping any request-scoped writes (e.g. `useLogger(event).set()`). Pass `cache: { maxAge: '1h', swr: true }` to opt back in.
6 changes: 5 additions & 1 deletion apps/docs/content/2.tools/3.errors-caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,14 @@ export default defineMcpTool({
| `maxAge` | `string \| number` | Yes | Cache duration (e.g., `'1h'`, `3600000`) |
| `getKey` | `(args) => string` | No | Custom cache key generator |
| `staleMaxAge` | `number` | No | Duration for stale-while-revalidate |
| `swr` | `boolean` | No | Enable stale-while-revalidate |
| `swr` | `boolean` | No | Enable stale-while-revalidate (defaults to `false`, see warning below) |
| `name` | `string` | No | Cache name (auto-generated from tool name) |
| `group` | `string` | No | Cache group (default: `'mcp'`) |

::callout{icon="i-lucide-info" color="info"}
See the [Nitro Cache documentation](https://nitro.build/guide/cache#options) for all available options.
::

::callout{icon="i-lucide-triangle-alert" color="warning"}
`swr` defaults to `false` (Nitro itself defaults to `true`). With `swr: true`, stale hits return immediately and the handler refreshes in the background after the request is answered, so request-scoped writes (structured logs, traces) may be dropped. Opt in only when you accept that trade-off.
::
2 changes: 2 additions & 0 deletions apps/docs/skills/manage-mcp/references/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export default defineMcpTool({

`cache: '10m'` keys on the input arguments. Pass a full Nitro cache options object for `swr`, custom `getKey`, etc. ([Nitro caching →](https://nitro.build/guide/cache#options))

`swr` defaults to `false` here (Nitro defaults to `true`). With `swr: true`, the handler refreshes after the response is sent, so request-scoped logs/traces may be dropped — opt in only when you accept that.

## Database Mutation (DB + soft auth + observability)

```typescript [server/mcp/tools/create-todo.ts]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ export interface McpCacheOptions<Args = unknown> {
getKey?: (args: Args) => string
/** Cache group (default: 'mcp') */
group?: string
/** Enable stale-while-revalidate behavior */
/**
* Enable stale-while-revalidate (default: `false`).
* When `true`, the handler refreshes in the background after the request
* has been answered, so request-scoped writes (loggers, traces) may be
* dropped.
*/
swr?: boolean
}

Expand All @@ -91,7 +96,8 @@ export function parseCacheDuration(duration: MsCacheDuration | number): number {
}

/**
* Create cache options from McpCache config
* Create cache options from McpCache config.
* Forces `swr: false` by default — see {@link McpCacheOptions.swr}.
*/
export function createCacheOptions<Args>(
cache: McpCache<Args>,
Expand All @@ -101,6 +107,7 @@ export function createCacheOptions<Args>(
if (typeof cache === 'object') {
return {
getKey: defaultGetKey,
swr: false,
...cache,
maxAge: parseCacheDuration(cache.maxAge),
name: cache.name ?? name,
Expand All @@ -113,6 +120,7 @@ export function createCacheOptions<Args>(
name,
group: 'mcp',
getKey: defaultGetKey,
swr: false,
}
}

Expand Down
20 changes: 20 additions & 0 deletions packages/nuxt-mcp-toolkit/test/cache-options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, it, expect, vi } from 'vitest'

vi.mock('nitropack/runtime', () => ({
defineCachedFunction: <T>(fn: T) => fn,
}))

const { createCacheOptions } = await import(
'../src/runtime/server/mcp/definitions/cache'
)

describe('createCacheOptions', () => {
it('defaults swr to false to keep handlers inside the request lifetime', () => {
expect(createCacheOptions('1h', 'mcp-tool:get-page').swr).toBe(false)
expect(createCacheOptions({ maxAge: '1h' }, 'mcp-tool:get-page').swr).toBe(false)
})

it('respects explicit swr opt-in', () => {
expect(createCacheOptions({ maxAge: '1h', swr: true }, 'mcp-tool:get-page').swr).toBe(true)
})
})
Loading