Skip to content

feat(ai-mcp): forward MCP tool annotations and titles - #1031

Merged
AlemTuzlak merged 3 commits into
TanStack:mainfrom
suryaashish-kubesense:feat/mcp-tool-annotations
Aug 7, 2026
Merged

feat(ai-mcp): forward MCP tool annotations and titles#1031
AlemTuzlak merged 3 commits into
TanStack:mainfrom
suryaashish-kubesense:feat/mcp-tool-annotations

Conversation

@suryaashish-kubesense

@suryaashish-kubesense suryaashish-kubesense commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

While using remote MCP servers via @tanstack/ai-mcp, I found that tool
annotations are stripped at discovery. Since they indicate how risky a tool call
is (readOnlyHint, destructiveHint), dropping them loses information a host
needs to label tools and gate approvals.

Discovery previously stamped only serverToolName / serverId /
uiResourceUri onto metadata.mcp. This PR also forwards:

  • annotations — the server's object verbatim, omitted when absent so the
    explicit tools([...defs]) path can't blank out caller-supplied metadata.
  • title — display name resolved with the spec's precedence
    (titleannotations.titlename).

Both the auto-discovery and explicit paths now go through one shared
toolMcpMetadata() so they can't drift (the explicit path was a second, thinner
drop site). Also exports McpToolMetadataTool.metadata is
Record<string, any>, so an exported shape is what lets consumers read the
block without a cast — and re-exports the SDK's ToolAnnotations.

Display-only and non-breaking: the model-facing tool name, description, and
provider payload are unchanged; a prefix still applies to the name, not the
title. Annotations are treated as spec hints — docs show gating that defaults to
requiring approval and relaxes only on readOnlyHint, never as a security
boundary.

Test plan

  • pnpm test:pr — green (includes kiira over the new docs snippets).
  • pnpm --filter @tanstack/ai-e2e test:e2e -- --grep "mcp" — 8/8 pass. The
    fixture-driven chat specs passing is the check that the provider payload
    didn't change.
  • New unit tests: annotations forwarded verbatim; key absent (not undefined)
    when the server declares none; title precedence across all three tiers;
    prefix-vs-title independence. Both paths also covered against a real
    in-memory server declaring a title and all four hints.
  • New E2E: the mock MCP server's get_guitar_price declares a title + hints;
    api.mcp-status-test returns the forwarded metadata and mcp-status.spec.ts
    asserts it. (Coverage lives in the MCP specs rather than
    tools-test-scenarios.ts since this is MCP discovery metadata, not core
    tool-system behavior.)

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset
    (minor, @tanstack/ai-mcp).
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • New Features

    • MCP tools now expose display titles and behavioral annotations, including read-only and non-destructive hints.
    • Titles resolve consistently for automatically discovered and explicitly configured tools.
    • Added public metadata types for tool titles, server information, UI links, and annotations.
    • Tool metadata can be surfaced through APIs and rendered in tool catalogs.
  • Documentation

    • Added guidance and examples for tool titles, annotations, precedence rules, and UI resource links.
    • Updated the MCP Server Tools documentation date.

Discovery previously stamped only serverToolName/serverId/uiResourceUri onto
`metadata.mcp`, dropping the server's `annotations` hints and `title` from
tools/list — so a host could only ever see a tool's name and description.

Forward both, on the auto-discovery AND explicit `tools([...defs])` paths, via
a shared `toolMcpMetadata()` so the two cannot drift:

- `annotations` — the server's object verbatim (readOnlyHint, destructiveHint,
  idempotentHint, openWorldHint, annotations.title), omitted when absent so the
  explicit path's merge can't blank out caller-supplied metadata.
- `title` — display name resolved with the spec's precedence:
  `title` -> `annotations.title` -> `name`.

Titles are display-only: the model-facing tool name and prefixing are
unchanged, and nothing new is sent to providers.

Also exports `McpToolMetadata` (the full `metadata.mcp` shape — `metadata` is
`Record<string, any>`, so this is what makes reads typed without a cast) and
re-exports the SDK's `ToolAnnotations`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@suryaashish-kubesense suryaashish-kubesense changed the title feat(ai-mcp): forward MCP tool annotations and titles feat(mcp): forward MCP tool annotations and titles Jul 31, 2026
@suryaashish-kubesense
suryaashish-kubesense marked this pull request as ready for review July 31, 2026 06:47
@suryaashish-kubesense suryaashish-kubesense changed the title feat(mcp): forward MCP tool annotations and titles feat(mcp): forward mcp tool annotations and titles Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

MCP tool metadata now includes resolved display titles, annotations, server identity, and UI resource URIs. The metadata supports auto-discovered and explicitly bound tools, package exports, documentation, and unit and E2E validation.

Changes

MCP tool metadata

Layer / File(s) Summary
Metadata contract and construction
packages/ai-mcp/src/types.ts, packages/ai-mcp/src/tools.ts, packages/ai-mcp/src/index.ts, packages/ai-mcp/tests/tools.test.ts
Adds typed MCP metadata, resolves titles from top-level titles, annotation titles, or tool names, and forwards annotations. Tests cover metadata omission, title precedence, and prefixed names.
Client and pool metadata integration
packages/ai-mcp/src/client.ts, packages/ai-mcp/src/pool.ts, packages/ai-mcp/tests/helpers/in-memory-server.ts, packages/ai-mcp/tests/client.test.ts
Client and pool APIs return McpServerTool values. Explicitly bound tools use shared metadata construction without mutating the bound tool.
Public type validation
packages/ai-mcp/src/index.ts, packages/ai-mcp/tests/types.test-d.ts
Exports MCP metadata types and validates typed metadata across direct, descriptor-based, default-client, and pooled tool results.
End-to-end metadata validation
testing/e2e/src/routes/api.mcp-server.ts, testing/e2e/src/routes/api.mcp-status-test.ts, testing/e2e/tests/mcp-status.spec.ts
The E2E MCP tool declares annotations. The status route returns them in toolMeta, and the test validates the forwarded values.
Documentation and release support
.changeset/mcp-tool-annotations.md, docs/config.json, docs/tools/mcp.md
Documents the release, metadata behavior, title precedence, annotation semantics, and tool catalog examples.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPServer
  participant MCPClientImpl
  participant toolMcpMetadata
  participant ToolCatalog
  MCPServer->>MCPClientImpl: expose tool definition
  MCPClientImpl->>toolMcpMetadata: resolve MCP metadata
  toolMcpMetadata-->>MCPClientImpl: return title and annotations
  MCPClientImpl-->>ToolCatalog: expose metadata.mcp
  ToolCatalog->>ToolCatalog: render title and read/write status
Loading

Suggested reviewers: alemtuzlak

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: forwarding MCP tool annotations and titles.
Description check ✅ Passed The description covers the change, motivation, tests, checklist, and release impact, including a generated changeset.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@suryaashish-kubesense suryaashish-kubesense changed the title feat(mcp): forward mcp tool annotations and titles feat(ai-mcp): forward MCP tool annotations and titles Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/ai-mcp/src/types.ts (1)

24-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make guaranteed metadata fields required.

toolMcpMetadata always sets serverToolName and title. The interface marks both fields as optional. Make these fields required so consumers can use the documented contract without redundant fallback handling.

Proposed fix
-  serverToolName?: string
+  serverToolName: string
...
-  title?: string
+  title: string
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-mcp/src/types.ts` around lines 24 - 42, Update the
McpToolMetadata interface to make serverToolName and title required properties,
matching the guarantees provided by toolMcpMetadata. Leave serverId,
uiResourceUri, and annotations optional.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/tools/mcp.md`:
- Around line 296-303: Update the tools mapping around mcp.tools() so
readOnlyHint cannot by itself set needsApproval to false; require an
independently established trusted-server condition before bypassing approval,
while retaining the hint for labels or recommendations. Revise the adjacent
warning to explicitly state that annotations are advisory and must not serve as
the security boundary for untrusted servers.
- Around line 319-321: Validate that process.env.MCP_URL is present before the
createMCPClient call, returning a clear response when it is absent. Store the
validated value and pass it to the HTTP transport instead of using the non-null
assertion, ensuring resolveTransport receives a defined URL.
- Around line 292-294: Update the one-shot MCP client example around
createMCPClient to use scoped async disposal with await using, ensuring the
client transport closes automatically after tool discovery. Preserve the
existing HTTP transport configuration and read flow.

In `@packages/ai-mcp/tests/tools.test.ts`:
- Around line 257-316: Move the toServerTools coverage from
packages/ai-mcp/tests/tools.test.ts lines 257-316 into
packages/ai-mcp/src/tools.test.ts, preserving the existing tests. Also move the
MCPClientImpl.tools coverage from packages/ai-mcp/tests/client.test.ts lines
118-150 into packages/ai-mcp/src/client.test.ts; no other test behavior should
change.

---

Nitpick comments:
In `@packages/ai-mcp/src/types.ts`:
- Around line 24-42: Update the McpToolMetadata interface to make serverToolName
and title required properties, matching the guarantees provided by
toolMcpMetadata. Leave serverId, uiResourceUri, and annotations optional.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 249bc044-c1eb-438e-b0af-a04be2ba0763

📥 Commits

Reviewing files that changed from the base of the PR and between 1cb04d5 and 1169337.

📒 Files selected for processing (13)
  • .changeset/mcp-tool-annotations.md
  • docs/config.json
  • docs/tools/mcp.md
  • packages/ai-mcp/src/client.ts
  • packages/ai-mcp/src/index.ts
  • packages/ai-mcp/src/tools.ts
  • packages/ai-mcp/src/types.ts
  • packages/ai-mcp/tests/client.test.ts
  • packages/ai-mcp/tests/helpers/in-memory-server.ts
  • packages/ai-mcp/tests/tools.test.ts
  • testing/e2e/src/routes/api.mcp-server.ts
  • testing/e2e/src/routes/api.mcp-status-test.ts
  • testing/e2e/tests/mcp-status.spec.ts

Comment thread docs/tools/mcp.md Outdated
Comment on lines +292 to +294
const mcp = await createMCPClient({
transport: { type: 'http', url: 'https://my-mcp-server.example.com/mcp' },
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect this one-shot example to use `await using` or explicit cleanup.
rg -n -C 4 '\bcreateMCPClient\b|await using|\.close\(' docs packages/ai-mcp

Repository: TanStack/ai

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the documented example and nearby lifecycle wording.
if [ -f docs/tools/mcp.md ]; then
  echo '--- docs/tools/mcp.md around createMCPClient example ---'
  sed -n '274,310p' docs/tools/mcp.md | cat -n
else
  echo 'docs/tools/mcp.md not found'
fi

echo '--- createMCPClients examples without close/await using (grep only) ---'
rg -n -C 3 'createMCPClients\b' docs/tools/mcp.md packages/ai-mcp/skills/ai-mcp/SKILL.md | sed -n '1,120p'

Repository: TanStack/ai

Length of output: 9684


Dispose the MCP client in this one-shot example.

This example creates the client and reads tools, but does not close the transport. Use await using mpc = await createMCPClient(...) for this one-shot read, or document explicit cleanup after tool discovery.

Suggested cleanup
-const mcp = await createMCPClient({
+await using mcp = await createMCPClient({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mcp = await createMCPClient({
transport: { type: 'http', url: 'https://my-mcp-server.example.com/mcp' },
})
await using mcp = await createMCPClient({
transport: { type: 'http', url: 'https://my-mcp-server.example.com/mcp' },
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/mcp.md` around lines 292 - 294, Update the one-shot MCP client
example around createMCPClient to use scoped async disposal with await using,
ensuring the client transport closes automatically after tool discovery.
Preserve the existing HTTP transport configuration and read flow.

Comment thread docs/tools/mcp.md Outdated
Comment thread docs/tools/mcp.md
Comment on lines +319 to +321
await using mcp = await createMCPClient({
transport: { type: 'http', url: process.env.MCP_URL! },
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect project conventions for validating this environment variable.
rg -n -C 4 'process\.env\.MCP_URL|createMCPClient' docs packages/ai-mcp

Repository: TanStack/ai

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Target file context:"
fd '^mcp\.md$' docs -x sh -c 'wc -l "$1"; echo "--- $1"; sed -n "290,335p" "$1"' sh {}

echo
echo "Exact process.env.MCP_URL occurrences:"
rg -n -C 3 'process\.env\.MCP_URL|createMCPClient\(\{' docs packages/ai-mcp/skills/ai-mcp/SKILL.md packages/ai-mcp/README.md

echo
echo "Relevant client.ts signatures/top:"
fd '^client\.ts$' packages/ai-mcp/src -x sh -c 'wc -l "$1"; echo "--- $1"; sed -n "1,220p" "$1"' sh {} 2>/dev/null || true

echo
echo "HTTP transport URL behavior in MCP SDK references in repo:"
git ls-files | rg 'modelcontextprotocol|http-stream|streamable|stream-http|ClientTransport' | head -100

Repository: TanStack/ai

Length of output: 33670


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "transport.ts:"
fd '^transport\.ts$' packages/ai-mcp/src -x sh -c 'wc -l "$1"; echo "--- $1"; cat -n "$1"' sh {}

echo
echo "TransportConfig definitions/usages in ai-mcp src:"
rg -n -C 3 'interface TransportConfig|type TransportConfig|resolveTransport|StreamableHTTPClientTransport|new URL|new StreamableHTTPClientTransport|new (StreamableHTTPClientTransport|SSEClientTransport|StdioClientTransport)' packages/ai-mcp/src

echo
echo "Lockfile SDK versions:"
rg -n '"(`@modelcontextprotocol/sdk`)|streamableHttp|SSEClientTransport|StdioClientTransport' package.json pnpm-lock.yaml package-lock.json npm-shrinkwrap.json yarn.lock 2>/dev/null | head -200 || true

echo
echo "Package deps:"
fd '^package\.json$' packages/ai-mcp -x sh -c 'echo "--- $1"; sed -n "1,120p" "$1"' sh {}

Repository: TanStack/ai

Length of output: 10103


Validate MCP_URL before constructing the MCP client.

process.env.MCP_URL! only bypasses TypeScript checking. If the variable is missing, createMCPClient({ transport: { type: 'http', url: process.env.MCP_URL! } }) passes undefined to resolveTransport, and the HTTP transport constructor receives new URL(undefined), which throws before configuration validation. Return a clear response when MCP_URL is absent, then pass the validated URL.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tools/mcp.md` around lines 319 - 321, Validate that process.env.MCP_URL
is present before the createMCPClient call, returning a clear response when it
is absent. Store the validated value and pass it to the HTTP transport instead
of using the non-null assertion, ensuring resolveTransport receives a defined
URL.

Source: Coding guidelines

Comment on lines +257 to +316
describe('toServerTools — annotations + title', () => {
it('forwards the server annotations verbatim', () => {
const annotations = {
title: 'Weather Lookup',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
}
const tool = toServerTools(
fakeMcpClient(vi.fn()),
[mcpToolDef({ name: 'get_weather', description: 'w', annotations })],
{},
)[0]!
expect(readToolMcpMeta(tool).annotations).toEqual(annotations)
})

it('omits annotations entirely when the server declares none', () => {
const tool = toServerTools(
fakeMcpClient(vi.fn()),
[mcpToolDef({ name: 'get_weather' })],
{},
)[0]!
const mcp = readToolMcpMeta(tool)
expect(mcp.annotations).toBeUndefined()
// Omitted, not present-with-undefined — the explicit tools(defs) path
// merges this block over caller-supplied metadata.
expect('annotations' in mcp).toBe(false)
})

it('resolves title with MCP precedence: title > annotations.title > name', () => {
const [both, annotationsOnly, neither] = toServerTools(
fakeMcpClient(vi.fn()),
[
mcpToolDef({
name: 'a',
title: 'Top Level',
annotations: { title: 'Legacy' },
}),
mcpToolDef({ name: 'b', annotations: { title: 'Legacy' } }),
mcpToolDef({ name: 'c' }),
],
{},
)
expect(readToolMcpMeta(both!).title).toBe('Top Level')
expect(readToolMcpMeta(annotationsOnly!).title).toBe('Legacy')
expect(readToolMcpMeta(neither!).title).toBe('c')
})

it('keeps the prefixed tool name independent of the display title', () => {
const tool = toServerTools(
fakeMcpClient(vi.fn()),
[mcpToolDef({ name: 'get_weather', title: 'Weather Lookup' })],
{ prefix: 'wx' },
)[0]!
// The title is display-only — it must never leak into the model-facing name.
expect(tool.name).toBe('wx_get_weather')
expect(readToolMcpMeta(tool).title).toBe('Weather Lookup')
})
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Place new unit coverage beside its source module.

The added unit tests are not colocated with their covered source modules.

  • packages/ai-mcp/tests/tools.test.ts#L257-L316: move the toServerTools coverage to packages/ai-mcp/src/tools.test.ts.
  • packages/ai-mcp/tests/client.test.ts#L118-L150: move the MCPClientImpl.tools coverage to packages/ai-mcp/src/client.test.ts.
📍 Affects 2 files
  • packages/ai-mcp/tests/tools.test.ts#L257-L316 (this comment)
  • packages/ai-mcp/tests/client.test.ts#L118-L150
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-mcp/tests/tools.test.ts` around lines 257 - 316, Move the
toServerTools coverage from packages/ai-mcp/tests/tools.test.ts lines 257-316
into packages/ai-mcp/src/tools.test.ts, preserving the existing tests. Also move
the MCPClientImpl.tools coverage from packages/ai-mcp/tests/client.test.ts lines
118-150 into packages/ai-mcp/src/client.test.ts; no other test behavior should
change.

Source: Coding guidelines

suryaashish-kubesense and others added 2 commits July 31, 2026 12:35
The approval-gating sample keyed `needsApproval` off `readOnlyHint` alone, so a
single server-supplied boolean could waive the confirmation step — a compromised
server can claim `readOnlyHint: true` on a destructive tool. It also contradicted
the warning directly beneath it.

Gate on `!(serverIsTrusted && advertisedReadOnly)`, where trust comes from a
caller-configured allowlist rather than anything on the wire, and rewrite the
warning to say annotations are advisory and must never be the security boundary
for an untrusted server — hints may relax a confirmation only on independently
trusted servers, and are display labels everywhere else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ServerTool.metadata` is `Record<string, any> | undefined`, so
`tool.metadata.mcp` did not compile and its fields were `any` — reading the
forwarded MCP title/annotations needed a hand-written annotation that TS could
not check.

Every `tools()` path (single client, explicit `tools([...defs])`, and the
`createMCPClients` pool) now returns `McpServerTool`s: structurally still
`ServerTool`s, but with `metadata.mcp` statically present and typed as
`McpToolMetadata`. So `tool.metadata.mcp.annotations?.readOnlyHint` infers on
its own and a misspelled field is a compile error.

- `McpToolMetadata.serverToolName` / `.title` are now required, since both are
  always stamped — no more fallback for a value that is never missing.
- The explicit path rebuilds the bound tool instead of mutating it, because
  assigning `metadata` cannot narrow the declared type.
- Docs, tests, and the e2e route drop their annotations and read straight
  through; new type-level tests cover all four paths plus the typo case.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai-mcp/src/types.ts`:
- Around line 20-22: Remove the readOnlyHint-based approval-bypass example from
the documentation comment in the MCP types definition. Replace it with guidance
that server-provided annotations may inform display only and must never bypass
approval or authorization.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ef97b8d-d8d6-4485-a784-f0a2fd18cc85

📥 Commits

Reviewing files that changed from the base of the PR and between 401e71b and fdc1754.

📒 Files selected for processing (11)
  • .changeset/mcp-tool-annotations.md
  • docs/tools/mcp.md
  • packages/ai-mcp/src/client.ts
  • packages/ai-mcp/src/index.ts
  • packages/ai-mcp/src/pool.ts
  • packages/ai-mcp/src/tools.ts
  • packages/ai-mcp/src/types.ts
  • packages/ai-mcp/tests/client.test.ts
  • packages/ai-mcp/tests/tools.test.ts
  • packages/ai-mcp/tests/types.test-d.ts
  • testing/e2e/src/routes/api.mcp-status-test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ai-mcp/src/index.ts
  • packages/ai-mcp/tests/tools.test.ts
  • .changeset/mcp-tool-annotations.md
  • docs/tools/mcp.md
  • packages/ai-mcp/tests/client.test.ts

Comment on lines +20 to +22
* if (tool.metadata.mcp.annotations?.readOnlyHint) {
* // e.g. skip the approval prompt for a read-only tool
* }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not use readOnlyHint to bypass approval.

Lines 20-22 show a server-provided annotation as a reason to skip an approval prompt. A malicious or incorrect MCP server can set this hint and suppress user confirmation for a mutating tool. Remove this example. State that annotations can inform display only and must not bypass approval or authorization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-mcp/src/types.ts` around lines 20 - 22, Remove the
readOnlyHint-based approval-bypass example from the documentation comment in the
MCP types definition. Replace it with guidance that server-provided annotations
may inform display only and must never bypass approval or authorization.

@nx-cloud

nx-cloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit fdc1754

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 6s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-07 11:30:32 UTC

@pkg-pr-new

pkg-pr-new Bot commented Aug 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@1031

@tanstack/ai-acp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-acp@1031

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@1031

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@1031

@tanstack/ai-bedrock

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-bedrock@1031

@tanstack/ai-byteplus

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-byteplus@1031

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@1031

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@1031

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@1031

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-skills@1031

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@1031

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@1031

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-durable-stream@1031

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@1031

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@1031

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@1031

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@1031

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@1031

@tanstack/ai-grok-build

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok-build@1031

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@1031

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@1031

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@1031

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@1031

@tanstack/ai-isolate-quickjs-bun

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs-bun@1031

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@1031

@tanstack/ai-memory

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-memory@1031

@tanstack/ai-mistral

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mistral@1031

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@1031

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@1031

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@1031

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@1031

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@1031

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@1031

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@1031

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@1031

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@1031

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@1031

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-daytona@1031

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@1031

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@1031

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-sprites@1031

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-vercel@1031

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@1031

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@1031

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@1031

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@1031

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@1031

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@1031

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@1031

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@1031

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@1031

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@1031

commit: fdc1754

@AlemTuzlak
AlemTuzlak merged commit 3ba9c8b into TanStack:main Aug 7, 2026
9 checks passed
@github-actions github-actions Bot mentioned this pull request Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants