@tanstack/ai 0.61.0 → 0.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/esm/activities/chat/agents/define-agent.d.ts +17 -5
- package/dist/esm/activities/chat/agents/define-agent.js.map +1 -1
- package/dist/esm/activities/chat/agents/spawn.d.ts +2 -0
- package/dist/esm/activities/chat/agents/spawn.js +10 -33
- package/dist/esm/activities/chat/agents/spawn.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +2 -0
- package/dist/esm/activities/chat/index.js +166 -60
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +43 -22
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +1 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js +6 -2
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +11 -9
- package/dist/esm/activities/chat/stream/processor.js +38 -18
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +8 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +6 -5
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +20 -3
- package/dist/esm/activities/chat/tools/tool-calls.js +126 -36
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/evaluate/adapter.d.ts +4 -0
- package/dist/esm/activities/evaluate/adapter.js.map +1 -1
- package/dist/esm/activities/evaluate/index.d.ts +4 -0
- package/dist/esm/activities/evaluate/index.js +3 -1
- package/dist/esm/activities/evaluate/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +1 -1
- package/dist/esm/activities/generateSpeech/index.js +1 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +14 -6
- package/dist/esm/activities/generateVideo/adapter.js +6 -3
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +5 -4
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.d.ts +12 -3
- package/dist/esm/activities/generateVideo/snap.js +47 -8
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/generateVoice/index.d.ts +1 -1
- package/dist/esm/activities/generateVoice/index.js +1 -1
- package/dist/esm/activities/generateVoice/index.js.map +1 -1
- package/dist/esm/activities/index.d.ts +2 -2
- package/dist/esm/activities/index.js +2 -2
- package/dist/esm/adapter-internals.d.ts +1 -0
- package/dist/esm/adapter-internals.js +2 -1
- package/dist/esm/client.d.ts +1 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/interrupt-resume.js +29 -4
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/middlewares/otel.d.ts +5 -2
- package/dist/esm/middlewares/otel.js +114 -0
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +70 -4
- package/dist/esm/utilities/ag-ui-wire.js +8 -4
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/merge-streams.d.ts +6 -0
- package/dist/esm/utilities/merge-streams.js +37 -0
- package/dist/esm/utilities/merge-streams.js.map +1 -0
- package/dist/esm/utilities/reasoning-encrypted-value.d.ts +8 -0
- package/dist/esm/utilities/reasoning-encrypted-value.js +11 -1
- package/dist/esm/utilities/reasoning-encrypted-value.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +2 -1
- package/dist/esm/utilities/tool-result.js +4 -1
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/chat-experience/SKILL.md +120 -0
- package/skills/ai-core/media-generation/SKILL.md +3 -3
- package/skills/ai-core/tool-calling/SKILL.md +103 -0
- package/src/activities/chat/agents/define-agent.ts +20 -3
- package/src/activities/chat/agents/spawn.ts +22 -43
- package/src/activities/chat/index.ts +248 -65
- package/src/activities/chat/messages.ts +54 -7
- package/src/activities/chat/middleware/types.ts +1 -0
- package/src/activities/chat/stream/message-updaters.ts +8 -0
- package/src/activities/chat/stream/processor.ts +57 -27
- package/src/activities/chat/tools/schema-converter.ts +17 -5
- package/src/activities/chat/tools/tool-calls.ts +215 -68
- package/src/activities/chat/tools/tool-definition.ts +8 -0
- package/src/activities/evaluate/adapter.ts +4 -0
- package/src/activities/evaluate/index.ts +6 -0
- package/src/activities/generateSpeech/index.ts +1 -1
- package/src/activities/generateVideo/adapter.ts +21 -7
- package/src/activities/generateVideo/index.ts +5 -4
- package/src/activities/generateVideo/snap.ts +64 -6
- package/src/activities/generateVoice/index.ts +1 -1
- package/src/activities/index.ts +2 -1
- package/src/adapter-internals.ts +1 -0
- package/src/client.ts +1 -0
- package/src/index.ts +1 -0
- package/src/interrupt-resume.ts +55 -4
- package/src/middlewares/otel.ts +161 -3
- package/src/types.ts +66 -5
- package/src/utilities/ag-ui-wire.ts +17 -2
- package/src/utilities/merge-streams.ts +34 -0
- package/src/utilities/reasoning-encrypted-value.ts +12 -0
- package/src/utilities/tool-result.ts +7 -1
|
@@ -513,6 +513,9 @@ The post-discovery payload always returns the full description and schema regard
|
|
|
513
513
|
`@tanstack/ai-mcp` lets a server-side `chat()` call discover and invoke tools
|
|
514
514
|
hosted on any MCP server (Streamable HTTP, SSE, or stdio).
|
|
515
515
|
|
|
516
|
+
`createMCPClient` tries spec `2026-07-28` first.
|
|
517
|
+
If the server does not support that spec, the client uses the 2025 initialize handshake.
|
|
518
|
+
|
|
516
519
|
**MCP tools and UI resources:** When an MCP tool result carries a `ui://`
|
|
517
520
|
resource URI (via `_meta.ui.resourceUri`), TanStack AI surfaces it as a
|
|
518
521
|
`UIResourcePart` on the assistant `UIMessage` in the client message list.
|
|
@@ -723,6 +726,106 @@ export async function POST(request: Request) {
|
|
|
723
726
|
}
|
|
724
727
|
```
|
|
725
728
|
|
|
729
|
+
### Host your own MCP server
|
|
730
|
+
|
|
731
|
+
Import `createMCPServer` from `@tanstack/ai-mcp/server`.
|
|
732
|
+
Pass tools from `toolDefinition().server()`.
|
|
733
|
+
Call `server.fetch(request)` in your HTTP route.
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
import { toolDefinition } from '@tanstack/ai'
|
|
737
|
+
import { createMCPServer } from '@tanstack/ai-mcp/server'
|
|
738
|
+
import { z } from 'zod'
|
|
739
|
+
|
|
740
|
+
const getWeather = toolDefinition({
|
|
741
|
+
name: 'get_weather',
|
|
742
|
+
description: 'Current weather for a city',
|
|
743
|
+
inputSchema: z.object({ city: z.string() }),
|
|
744
|
+
}).server(async ({ city }) => {
|
|
745
|
+
return { city, temperature: 18, conditions: 'clear' }
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
const server = createMCPServer({
|
|
749
|
+
name: 'weather',
|
|
750
|
+
version: '1.0.0',
|
|
751
|
+
tools: [getWeather],
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
export function POST(request: Request) {
|
|
755
|
+
return server.fetch(request)
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
`stdioTransport` from `@tanstack/ai-mcp/stdio` connects your client to a command.
|
|
760
|
+
`serveMCPStdio` from `@tanstack/ai-mcp/server/stdio` serves your server on stdin and stdout.
|
|
761
|
+
Write logs with `console.error`.
|
|
762
|
+
stdout carries only protocol messages.
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
import { toolDefinition } from '@tanstack/ai'
|
|
766
|
+
import { createMCPServer } from '@tanstack/ai-mcp/server'
|
|
767
|
+
import { serveMCPStdio } from '@tanstack/ai-mcp/server/stdio'
|
|
768
|
+
import { z } from 'zod'
|
|
769
|
+
|
|
770
|
+
const getWeather = toolDefinition({
|
|
771
|
+
name: 'get_weather',
|
|
772
|
+
description: 'Current weather for a city',
|
|
773
|
+
inputSchema: z.object({ city: z.string() }),
|
|
774
|
+
}).server(async ({ city }) => {
|
|
775
|
+
return { city, temperature: 18, conditions: 'clear' }
|
|
776
|
+
})
|
|
777
|
+
|
|
778
|
+
const server = createMCPServer({
|
|
779
|
+
name: 'weather',
|
|
780
|
+
version: '1.0.0',
|
|
781
|
+
tools: [getWeather],
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
serveMCPStdio(server)
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
The `@tanstack/ai-mcp` skill shows `ctx.context.requestInput` and `ctx.context.sample`.
|
|
788
|
+
|
|
789
|
+
### Read an MCP input interrupt
|
|
790
|
+
|
|
791
|
+
When `chat()` receives an MCP input request, the run outcome is an interrupt.
|
|
792
|
+
The stream ends with `RUN_FINISHED`.
|
|
793
|
+
The outcome type is `interrupt`.
|
|
794
|
+
Read each interrupt whose `reason` is `mcp_input`.
|
|
795
|
+
The payload key is `tanstack:interruptPayload`.
|
|
796
|
+
|
|
797
|
+
`form` means the server asks the user for input.
|
|
798
|
+
`sampling` means the server asks for a model result.
|
|
799
|
+
|
|
800
|
+
```typescript
|
|
801
|
+
import { chat, INTERRUPT_PAYLOAD_METADATA_KEY } from '@tanstack/ai'
|
|
802
|
+
import { openaiText } from '@tanstack/ai-openai'
|
|
803
|
+
import { createMCPClient } from '@tanstack/ai-mcp'
|
|
804
|
+
|
|
805
|
+
const client = await createMCPClient({
|
|
806
|
+
transport: { type: 'http', url: 'https://mcp.example.com/mcp' },
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
const stream = chat({
|
|
810
|
+
adapter: openaiText('gpt-5.5'),
|
|
811
|
+
messages: [{ role: 'user', content: 'Weather in Paris?' }],
|
|
812
|
+
tools: await client.tools(),
|
|
813
|
+
})
|
|
814
|
+
|
|
815
|
+
for await (const chunk of stream) {
|
|
816
|
+
if (chunk.type !== 'RUN_FINISHED') continue
|
|
817
|
+
if (chunk.outcome?.type !== 'interrupt') continue
|
|
818
|
+
|
|
819
|
+
for (const item of chunk.outcome.interrupts) {
|
|
820
|
+
if (item.reason !== 'mcp_input') continue
|
|
821
|
+
const payload = item.metadata?.[INTERRUPT_PAYLOAD_METADATA_KEY]
|
|
822
|
+
if (typeof payload !== 'object' || payload === null) continue
|
|
823
|
+
if (!('kind' in payload)) continue
|
|
824
|
+
// payload.kind is 'form' or 'sampling'
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
726
829
|
## Provider Skills
|
|
727
830
|
|
|
728
831
|
> **Not to be confused with `@tanstack/ai-code-mode-snippets`**, whose snippets are TypeScript functions your application generates and runs in its own Code Mode sandbox (a local JS isolate). Provider Skills are hosted, provider-managed bundles that the model loads on demand and runs inside the provider's server-side sandbox.
|
|
@@ -2,6 +2,7 @@ import type { SubagentInfo as AGUISubagentInfo } from '@ag-ui/core'
|
|
|
2
2
|
import type { InterruptDefinition } from '../../../interrupt-definition'
|
|
3
3
|
import type {
|
|
4
4
|
AnyTool,
|
|
5
|
+
InferSchemaType,
|
|
5
6
|
ModelMessage,
|
|
6
7
|
RunAgentResumeItem,
|
|
7
8
|
SchemaInput,
|
|
@@ -12,8 +13,16 @@ import type { AnyClientTool } from '../tools/tool-definition'
|
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Context the library passes into {@link defineAgent} `run`.
|
|
16
|
+
* `TInput` is the agent's `inputSchema`.
|
|
15
17
|
*/
|
|
16
|
-
export interface SubagentRunContext
|
|
18
|
+
export interface SubagentRunContext<
|
|
19
|
+
TInput extends SchemaInput | undefined = any,
|
|
20
|
+
> {
|
|
21
|
+
/**
|
|
22
|
+
* The input the parent model wrote for this child, checked against
|
|
23
|
+
* `inputSchema`. `undefined` when the agent has no `inputSchema`.
|
|
24
|
+
*/
|
|
25
|
+
input: TInput extends SchemaInput ? InferSchemaType<TInput> : undefined
|
|
17
26
|
messages: Array<UIMessage | ModelMessage>
|
|
18
27
|
abortSignal?: AbortSignal
|
|
19
28
|
threadId: string
|
|
@@ -53,13 +62,20 @@ export interface DefinedAgent<
|
|
|
53
62
|
TSchema extends SchemaInput | undefined = SchemaInput | undefined,
|
|
54
63
|
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
55
64
|
ReadonlyArray<InterruptDefinition<any, any, any, any>>,
|
|
65
|
+
TInput extends SchemaInput | undefined = any,
|
|
56
66
|
> extends AGUISubagentInfo {
|
|
57
67
|
name: TName
|
|
58
68
|
/** Required here: the router and the synthetic tool both read it. */
|
|
59
69
|
description: string
|
|
60
70
|
run: (
|
|
61
|
-
ctx: SubagentRunContext
|
|
71
|
+
ctx: SubagentRunContext<TInput>,
|
|
62
72
|
) => AsyncIterable<StreamChunk> | Promise<AsyncIterable<StreamChunk>>
|
|
73
|
+
/**
|
|
74
|
+
* The input the parent model writes when it calls this agent's tool, such
|
|
75
|
+
* as a short brief. `run` reads it as `ctx.input`. Tool mode only: a
|
|
76
|
+
* `subagents.router` cannot start an agent that has `inputSchema`.
|
|
77
|
+
*/
|
|
78
|
+
inputSchema?: TInput
|
|
63
79
|
tools?: TTools
|
|
64
80
|
interrupts?: TInterrupts
|
|
65
81
|
outputSchema?: TSchema
|
|
@@ -104,7 +120,8 @@ export function defineAgent<
|
|
|
104
120
|
const TInterrupts extends ReadonlyArray<
|
|
105
121
|
InterruptDefinition<any, any, any, any>
|
|
106
122
|
> = readonly [],
|
|
107
|
-
|
|
123
|
+
TInput extends SchemaInput | undefined = undefined,
|
|
124
|
+
>(agent: DefinedAgent<TName, TTools, TSchema, TInterrupts, TInput>) {
|
|
108
125
|
if (agent.name.trim() === '') {
|
|
109
126
|
throw new Error('defineAgent requires a non-empty name')
|
|
110
127
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
tanstackMetadata,
|
|
10
10
|
withTanstackMetadata,
|
|
11
11
|
} from '../../../utilities/merge-metadata'
|
|
12
|
+
import { mergeStreams } from '../../../utilities/merge-streams'
|
|
12
13
|
import { INTERRUPT_BINDING_METADATA_KEY } from '../../../interrupt-resume'
|
|
13
14
|
import { EMIT_STREAM_CHUNK, SUBAGENT_TOOL } from '../tools/tool-calls'
|
|
14
15
|
import type { SubagentToolOutcome } from '../tools/tool-calls'
|
|
@@ -93,6 +94,8 @@ export function createSubagentSink(): SubagentSink {
|
|
|
93
94
|
/** One child to start, or a suspended child to continue. */
|
|
94
95
|
export interface SpawnEntry {
|
|
95
96
|
name: string
|
|
97
|
+
/** The checked tool input for an agent with `inputSchema`. */
|
|
98
|
+
input?: unknown
|
|
96
99
|
resume?: {
|
|
97
100
|
subagentRunId: string
|
|
98
101
|
/** The child's own messages from the interrupted run. */
|
|
@@ -248,6 +251,7 @@ function openAgentStream(
|
|
|
248
251
|
return spawnAgentStream(
|
|
249
252
|
agent,
|
|
250
253
|
{
|
|
254
|
+
input: entry.input,
|
|
251
255
|
messages: resumed?.messages ?? ctx.messages,
|
|
252
256
|
...(ctx.abortSignal ? { abortSignal: ctx.abortSignal } : {}),
|
|
253
257
|
threadId: childThreadId(bag.sandbox, ctx.threadId, entry.name),
|
|
@@ -522,36 +526,6 @@ export async function* spawnAgentStream(
|
|
|
522
526
|
}
|
|
523
527
|
}
|
|
524
528
|
|
|
525
|
-
async function* mergeAgentStreams(streams: Array<AsyncIterable<StreamChunk>>) {
|
|
526
|
-
const readers = streams.map((stream) => {
|
|
527
|
-
const iterator = stream[Symbol.asyncIterator]()
|
|
528
|
-
return {
|
|
529
|
-
iterator,
|
|
530
|
-
next: iterator.next(),
|
|
531
|
-
}
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
try {
|
|
535
|
-
while (readers.length > 0) {
|
|
536
|
-
const indexed = readers.map((reader, index) =>
|
|
537
|
-
reader.next.then((result) => ({ index, result, reader })),
|
|
538
|
-
)
|
|
539
|
-
const winner = await Promise.race(indexed)
|
|
540
|
-
if (winner.result.done) {
|
|
541
|
-
readers.splice(winner.index, 1)
|
|
542
|
-
continue
|
|
543
|
-
}
|
|
544
|
-
yield winner.result.value
|
|
545
|
-
winner.reader.next = winner.reader.iterator.next()
|
|
546
|
-
}
|
|
547
|
-
} finally {
|
|
548
|
-
// The reader stopped early. Close every child so its finally runs.
|
|
549
|
-
for (const reader of readers) {
|
|
550
|
-
void reader.iterator.return?.().catch(() => {})
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
529
|
/** True when a child in these chunks failed or stopped for outside input. */
|
|
556
530
|
function stopsSequence(chunks: ReadonlyArray<StreamChunk>, id: string) {
|
|
557
531
|
return chunks.some(
|
|
@@ -617,7 +591,7 @@ export async function* spawnNamedAgents(
|
|
|
617
591
|
yield* onlyStream
|
|
618
592
|
return
|
|
619
593
|
}
|
|
620
|
-
yield*
|
|
594
|
+
yield* mergeStreams(streams)
|
|
621
595
|
} finally {
|
|
622
596
|
group.controller.abort()
|
|
623
597
|
group.dispose()
|
|
@@ -721,8 +695,10 @@ export function createSyntheticSubagentTools(
|
|
|
721
695
|
return bag.agents.map((agent) => ({
|
|
722
696
|
name: agent.name,
|
|
723
697
|
description: agent.description,
|
|
698
|
+
...(agent.inputSchema !== undefined && { inputSchema: agent.inputSchema }),
|
|
724
699
|
[SUBAGENT_TOOL]: true,
|
|
725
|
-
|
|
700
|
+
// The tool loop checks `input` against `inputSchema` before this runs.
|
|
701
|
+
execute: async (input: unknown, context?: unknown) => {
|
|
726
702
|
const toolContext = context as
|
|
727
703
|
| {
|
|
728
704
|
toolCallId?: string
|
|
@@ -736,17 +712,20 @@ export function createSyntheticSubagentTools(
|
|
|
736
712
|
child.parentToolCallId !== undefined &&
|
|
737
713
|
child.parentToolCallId === toolCallId,
|
|
738
714
|
)
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
715
|
+
// An agent without `inputSchema` still gets `{}` from the model. Its
|
|
716
|
+
// `ctx.input` stays undefined.
|
|
717
|
+
const entry: SpawnEntry = {
|
|
718
|
+
name: agent.name,
|
|
719
|
+
...(agent.inputSchema !== undefined && { input }),
|
|
720
|
+
...(suspended && {
|
|
721
|
+
resume: {
|
|
722
|
+
subagentRunId: suspended.subagentRunId,
|
|
723
|
+
messages: suspended.messages,
|
|
724
|
+
entries: suspended.resume,
|
|
725
|
+
text: suspended.text,
|
|
726
|
+
},
|
|
727
|
+
}),
|
|
728
|
+
}
|
|
750
729
|
const sink = createSubagentSink()
|
|
751
730
|
const link = linkAbort(parent.abortSignal)
|
|
752
731
|
let subagentRunId = suspended?.subagentRunId ?? ''
|