Skip to content

Commit e7fcc86

Browse files
authored
feat (ai): introduce dynamic tools (#7595)
## Background MCP tools that are defined without providing schemas have no type information at development time. This breaks type inference, especially for UI parts when dynamic MCP tools and regular tools are mixed. Similarly, tools that are defined at app runtime (e.g. user supplied functions) are conflicting with typing. ## Summary * introduce `dynamic` tool type * introduce `dynamicTool` helper * mcp client tools without schemas are dynamic tools * change tool call, result, and error unions to support dynamic tools (breaking change) * add dynamic flag to ui message chunks * add `dynamic-tool` ui message part ## Verification - [x] manually test behavior and type inference on generate text example with dynamic tools - [x] manually test behavior and type inference on stream text example with dynamic tools - [x] manually test behavior and type inference on ui examples that mixes static tool and dynamic tools ## Future work Add useChat onToolCall for dynamic tools
1 parent 327d6ea commit e7fcc86

File tree

71 files changed

+1841
-247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1841
-247
lines changed

.changeset/bright-dodos-smash.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@ai-sdk/provider-utils': patch
3+
'ai': patch
4+
---
5+
6+
feat (ai): introduce dynamic tools

examples/ai-core/src/generate-text/amazon-bedrock-cache-point-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ async function main() {
140140

141141
// typed tool calls:
142142
for (const toolCall of result.toolCalls) {
143+
if (toolCall.dynamic) {
144+
continue;
145+
}
146+
143147
switch (toolCall.toolName) {
144148
case 'weather': {
145149
toolCall.input.location; // string
@@ -150,6 +154,10 @@ async function main() {
150154

151155
// typed tool results for tools with execute method:
152156
for (const toolResult of result.toolResults) {
157+
if (toolResult.dynamic) {
158+
continue;
159+
}
160+
153161
switch (toolResult.toolName) {
154162
case 'weather': {
155163
toolResult.input.location; // string

examples/ai-core/src/generate-text/amazon-bedrock-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ async function main() {
1919

2020
// typed tool calls:
2121
for (const toolCall of result.toolCalls) {
22+
if (toolCall.dynamic) {
23+
continue;
24+
}
25+
2226
switch (toolCall.toolName) {
2327
case 'cityAttractions': {
2428
toolCall.input.city; // string
@@ -34,6 +38,10 @@ async function main() {
3438

3539
// typed tool results for tools with execute method:
3640
for (const toolResult of result.toolResults) {
41+
if (toolResult.dynamic) {
42+
continue;
43+
}
44+
3745
switch (toolResult.toolName) {
3846
// NOT AVAILABLE (NO EXECUTE METHOD)
3947
// case 'cityAttractions': {

examples/ai-core/src/generate-text/anthropic-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ async function main() {
2020

2121
// typed tool calls:
2222
for (const toolCall of result.toolCalls) {
23+
if (toolCall.dynamic) {
24+
continue;
25+
}
26+
2327
switch (toolCall.toolName) {
2428
case 'cityAttractions': {
2529
toolCall.input.city; // string
@@ -35,6 +39,10 @@ async function main() {
3539

3640
// typed tool results for tools with execute method:
3741
for (const toolResult of result.toolResults) {
42+
if (toolResult.dynamic) {
43+
continue;
44+
}
45+
3846
switch (toolResult.toolName) {
3947
// NOT AVAILABLE (NO EXECUTE METHOD)
4048
// case 'cityAttractions': {

examples/ai-core/src/generate-text/anthropic-web-search.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ async function main() {
2929

3030
// typed tool results for tools with execute method:
3131
for (const toolResult of result.toolResults) {
32+
if (toolResult.dynamic) {
33+
continue;
34+
}
35+
3236
switch (toolResult.toolName) {
3337
case 'web_search': {
3438
toolResult.input.query; // string

examples/ai-core/src/generate-text/cerebras-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ async function main() {
2020

2121
// typed tool calls:
2222
for (const toolCall of result.toolCalls) {
23+
if (toolCall.dynamic) {
24+
continue;
25+
}
26+
2327
switch (toolCall.toolName) {
2428
case 'cityAttractions': {
2529
toolCall.input.city; // string
@@ -35,6 +39,10 @@ async function main() {
3539

3640
// typed tool results for tools with execute method:
3741
for (const toolResult of result.toolResults) {
42+
if (toolResult.dynamic) {
43+
continue;
44+
}
45+
3846
switch (toolResult.toolName) {
3947
// NOT AVAILABLE (NO EXECUTE METHOD)
4048
// case 'cityAttractions': {

examples/ai-core/src/generate-text/cohere-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ async function main() {
2020

2121
// typed tool calls:
2222
for (const toolCall of result.toolCalls) {
23+
if (toolCall.dynamic) {
24+
continue;
25+
}
26+
2327
switch (toolCall.toolName) {
2428
case 'cityAttractions': {
2529
toolCall.input.city; // string
@@ -35,6 +39,10 @@ async function main() {
3539

3640
// typed tool results for tools with execute method:
3741
for (const toolResult of result.toolResults) {
42+
if (toolResult.dynamic) {
43+
continue;
44+
}
45+
3846
switch (toolResult.toolName) {
3947
// NOT AVAILABLE (NO EXECUTE METHOD)
4048
// case 'cityAttractions': {

examples/ai-core/src/generate-text/deepinfra-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ async function main() {
2222

2323
// typed tool calls:
2424
for (const toolCall of result.toolCalls) {
25+
if (toolCall.dynamic) {
26+
continue;
27+
}
28+
2529
switch (toolCall.toolName) {
2630
case 'cityAttractions': {
2731
toolCall.input.city; // string
@@ -37,6 +41,10 @@ async function main() {
3741

3842
// typed tool results for tools with execute method:
3943
for (const toolResult of result.toolResults) {
44+
if (toolResult.dynamic) {
45+
continue;
46+
}
47+
4048
switch (toolResult.toolName) {
4149
// NOT AVAILABLE (NO EXECUTE METHOD)
4250
// case 'cityAttractions': {

examples/ai-core/src/generate-text/gateway-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ async function main() {
1919

2020
// typed tool calls:
2121
for (const toolCall of result.toolCalls) {
22+
if (toolCall.dynamic) {
23+
continue;
24+
}
25+
2226
switch (toolCall.toolName) {
2327
case 'cityAttractions': {
2428
toolCall.input.city; // string
@@ -34,6 +38,10 @@ async function main() {
3438

3539
// typed tool results for tools with execute method:
3640
for (const toolResult of result.toolResults) {
41+
if (toolResult.dynamic) {
42+
continue;
43+
}
44+
3745
switch (toolResult.toolName) {
3846
// NOT AVAILABLE (NO EXECUTE METHOD)
3947
// case 'cityAttractions': {

examples/ai-core/src/generate-text/google-tool-call.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ async function main() {
2020

2121
// typed tool calls:
2222
for (const toolCall of result.toolCalls) {
23+
if (toolCall.dynamic) {
24+
continue;
25+
}
26+
2327
switch (toolCall.toolName) {
2428
case 'cityAttractions': {
2529
toolCall.input.city; // string
@@ -35,6 +39,10 @@ async function main() {
3539

3640
// typed tool results for tools with execute method:
3741
for (const toolResult of result.toolResults) {
42+
if (toolResult.dynamic) {
43+
continue;
44+
}
45+
3846
switch (toolResult.toolName) {
3947
// NOT AVAILABLE (NO EXECUTE METHOD)
4048
// case 'cityAttractions': {

0 commit comments

Comments
 (0)