Skip to content

Commit 463bf67

Browse files
alecfclaude
andauthored
feat: per-thread and threadless MCP token management with contextKey support (#1408)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 11938d9 commit 463bf67

30 files changed

+459
-200
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ yarn-error.log*
4949
# typescript
5050
*.tsbuildinfo
5151
next-env.d.ts
52+
react-sdk/dist/**

cli/src/registry/control-bar/control-bar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
MessageInputSubmitButton,
1414
MessageInputError,
1515
MessageInputFileButton,
16+
MessageInputMcpPromptButton,
17+
MessageInputMcpResourceButton,
1618
// MessageInputMcpConfigButton,
1719
} from "@/components/tambo/message-input";
1820
import {
@@ -101,9 +103,11 @@ export const ControlBar = React.forwardRef<HTMLDivElement, ControlBarProps>(
101103
<MessageInput contextKey={contextKey}>
102104
<MessageInputTextarea />
103105
<MessageInputToolbar>
106+
<MessageInputFileButton />
107+
<MessageInputMcpPromptButton />
108+
<MessageInputMcpResourceButton />
104109
{/* Uncomment this to enable client-side MCP config modal button */}
105110
{/* <MessageInputMcpConfigButton /> */}
106-
<MessageInputFileButton />
107111
<MessageInputSubmitButton />
108112
</MessageInputToolbar>
109113
<MessageInputError />

cli/src/registry/lib/thread-hooks.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export function usePositioning(
141141

142142
/**
143143
* Converts message content into a safely renderable format.
144-
* Primarily joins text blocks from arrays into a single string.
144+
* Handles text, resource references, and other content types.
145145
* @param content - The message content (string, element, array, etc.)
146146
* @returns A renderable string or React element.
147147
*/
@@ -152,10 +152,20 @@ export function getSafeContent(
152152
if (typeof content === "string") return content;
153153
if (React.isValidElement(content)) return content; // Pass elements through
154154
if (Array.isArray(content)) {
155-
// Filter out non-text items and join text
156-
return content
157-
.map((item) => (item?.type === "text" ? (item.text ?? "") : ""))
158-
.join("");
155+
// Map content parts to strings, including resource references
156+
const parts: string[] = [];
157+
for (const item of content) {
158+
if (item?.type === "text") {
159+
parts.push(item.text ?? "");
160+
} else if (item?.type === "resource") {
161+
// Format resource references as @uri (uri already contains serverKey prefix if applicable)
162+
const uri = item.resource?.uri;
163+
if (uri) {
164+
parts.push(`@${uri}`);
165+
}
166+
}
167+
}
168+
return parts.join(" ");
159169
}
160170
// Handle potential edge cases or unknown types
161171
// console.warn("getSafeContent encountered unknown content type:", content);

cli/src/registry/message-thread-collapsible/message-thread-collapsible.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MessageInputError,
99
MessageInputFileButton,
1010
MessageInputMcpPromptButton,
11+
MessageInputMcpResourceButton,
1112
// MessageInputMcpConfigButton,
1213
} from "@/components/tambo/message-input";
1314
import {
@@ -295,6 +296,7 @@ export const MessageThreadCollapsible = React.forwardRef<
295296
<MessageInputToolbar>
296297
<MessageInputFileButton />
297298
<MessageInputMcpPromptButton />
299+
<MessageInputMcpResourceButton />
298300
{/* Uncomment this to enable client-side MCP config modal button */}
299301
{/* <MessageInputMcpConfigButton /> */}
300302
<MessageInputSubmitButton />

cli/src/registry/message-thread-full/message-thread-full.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
MessageInputError,
77
MessageInputFileButton,
88
MessageInputMcpPromptButton,
9+
MessageInputMcpResourceButton,
910
MessageInputSubmitButton,
1011
MessageInputTextarea,
1112
MessageInputToolbar,
@@ -121,6 +122,7 @@ export const MessageThreadFull = React.forwardRef<
121122
<MessageInputToolbar>
122123
<MessageInputFileButton />
123124
<MessageInputMcpPromptButton />
125+
<MessageInputMcpResourceButton />
124126
{/* Uncomment this to enable client-side MCP config modal button */}
125127
{/* <MessageInputMcpConfigButton /> */}
126128
<MessageInputSubmitButton />

cli/src/registry/message-thread-panel/message-thread-panel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MessageInputError,
99
MessageInputFileButton,
1010
MessageInputMcpPromptButton,
11+
MessageInputMcpResourceButton,
1112
// MessageInputMcpConfigButton,
1213
} from "@/components/tambo/message-input";
1314
import {
@@ -269,6 +270,7 @@ export const MessageThreadPanel = React.forwardRef<
269270
<MessageInputToolbar>
270271
<MessageInputFileButton />
271272
<MessageInputMcpPromptButton />
273+
<MessageInputMcpResourceButton />
272274
{/* Uncomment this to enable client-side MCP config modal button */}
273275
{/* <MessageInputMcpConfigButton /> */}
274276
<MessageInputSubmitButton />

react-sdk/src/hooks/use-suggestions.test.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,29 @@ describe("useTamboSuggestions", () => {
8989
// Helper function to create mock CombinedTamboThreadContextProps
9090
const createMockThreadContext = (
9191
overrides: Partial<CombinedTamboThreadContextProps> = {},
92-
): CombinedTamboThreadContextProps => ({
93-
thread: createMockThread(),
94-
switchCurrentThread: jest.fn(),
95-
startNewThread: jest.fn(),
96-
updateThreadName: jest.fn(),
97-
generateThreadName: jest.fn(),
98-
addThreadMessage: jest.fn(),
99-
updateThreadMessage: jest.fn(),
100-
cancel: jest.fn(),
101-
streaming: false,
102-
sendThreadMessage: jest.fn(),
103-
generationStage: GenerationStage.IDLE,
104-
generationStatusMessage: "",
105-
isIdle: true,
106-
...overrides,
107-
});
92+
): CombinedTamboThreadContextProps => {
93+
const mockThread = createMockThread();
94+
return {
95+
thread: mockThread,
96+
currentThreadId: mockThread.id,
97+
currentThread: mockThread,
98+
threadMap: { [mockThread.id]: mockThread },
99+
setThreadMap: jest.fn(),
100+
switchCurrentThread: jest.fn(),
101+
startNewThread: jest.fn(),
102+
updateThreadName: jest.fn(),
103+
generateThreadName: jest.fn(),
104+
addThreadMessage: jest.fn(),
105+
updateThreadMessage: jest.fn(),
106+
cancel: jest.fn(),
107+
streaming: false,
108+
sendThreadMessage: jest.fn(),
109+
generationStage: GenerationStage.IDLE,
110+
generationStatusMessage: "",
111+
isIdle: true,
112+
...overrides,
113+
};
114+
};
108115

109116
beforeEach(() => {
110117
jest.mocked(useTamboQueryClient).mockReturnValue(new QueryClient());

react-sdk/src/mcp/mcp-hooks.test.tsx

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
100100
client: { baseURL: "https://api.tambo.co" } as any,
101101
queryClient,
102102
isUpdatingToken: false,
103-
mcpAccessToken: null,
104-
setMcpAccessToken: () => {},
105103
}}
106104
>
107105
<TamboRegistryProvider
@@ -209,8 +207,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
209207
client: { baseURL: "https://api.tambo.co" } as any,
210208
queryClient,
211209
isUpdatingToken: false,
212-
mcpAccessToken: null,
213-
setMcpAccessToken: () => {},
214210
}}
215211
>
216212
<TamboRegistryProvider
@@ -252,8 +248,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
252248
client: { baseURL: "https://api.tambo.co" } as any,
253249
queryClient,
254250
isUpdatingToken: false,
255-
mcpAccessToken: null,
256-
setMcpAccessToken: () => {},
257251
}}
258252
>
259253
<TamboRegistryProvider
@@ -346,8 +340,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
346340
client: { baseURL: "https://api.tambo.co" } as any,
347341
queryClient,
348342
isUpdatingToken: false,
349-
mcpAccessToken: null,
350-
setMcpAccessToken: () => {},
351343
}}
352344
>
353345
<TamboRegistryProvider
@@ -450,8 +442,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
450442
client: { baseURL: "https://api.tambo.co" } as any,
451443
queryClient,
452444
isUpdatingToken: false,
453-
mcpAccessToken: null,
454-
setMcpAccessToken: () => {},
455445
}}
456446
>
457447
<TamboRegistryProvider
@@ -541,8 +531,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
541531
client: { baseURL: "https://api.tambo.co" } as any,
542532
queryClient,
543533
isUpdatingToken: false,
544-
mcpAccessToken: null,
545-
setMcpAccessToken: () => {},
546534
}}
547535
>
548536
<TamboRegistryProvider
@@ -576,8 +564,6 @@ describe("useTamboMcpPromptList - individual server caching", () => {
576564
client: { baseURL: "https://api.tambo.co" } as any,
577565
queryClient,
578566
isUpdatingToken: false,
579-
mcpAccessToken: null,
580-
setMcpAccessToken: () => {},
581567
}}
582568
>
583569
<TamboRegistryProvider
@@ -709,8 +695,6 @@ describe("useTamboMcpResourceList - resource management", () => {
709695
client: { baseURL: "https://api.tambo.co" } as any,
710696
queryClient,
711697
isUpdatingToken: false,
712-
mcpAccessToken: null,
713-
setMcpAccessToken: () => {},
714698
}}
715699
>
716700
<TamboRegistryProvider
@@ -801,8 +785,6 @@ describe("useTamboMcpResourceList - resource management", () => {
801785
client: { baseURL: "https://api.tambo.co" } as any,
802786
queryClient,
803787
isUpdatingToken: false,
804-
mcpAccessToken: null,
805-
setMcpAccessToken: () => {},
806788
}}
807789
>
808790
<TamboRegistryProvider
@@ -902,8 +884,6 @@ describe("useTamboMcpResourceList - resource management", () => {
902884
client: { baseURL: "https://api.tambo.co" } as any,
903885
queryClient,
904886
isUpdatingToken: false,
905-
mcpAccessToken: null,
906-
setMcpAccessToken: () => {},
907887
}}
908888
>
909889
<TamboRegistryProvider
@@ -943,8 +923,6 @@ describe("useTamboMcpResourceList - resource management", () => {
943923
client: { baseURL: "https://api.tambo.co" } as any,
944924
queryClient,
945925
isUpdatingToken: false,
946-
mcpAccessToken: null,
947-
setMcpAccessToken: () => {},
948926
}}
949927
>
950928
<TamboRegistryProvider
@@ -1051,8 +1029,6 @@ describe("useTamboMcpResource - read individual resource", () => {
10511029
client: { baseURL: "https://api.tambo.co" } as any,
10521030
queryClient,
10531031
isUpdatingToken: false,
1054-
mcpAccessToken: null,
1055-
setMcpAccessToken: () => {},
10561032
}}
10571033
>
10581034
<TamboRegistryProvider
@@ -1164,8 +1140,6 @@ describe("useTamboMcpResource - read individual resource", () => {
11641140
client: { baseURL: "https://api.tambo.co" } as any,
11651141
queryClient,
11661142
isUpdatingToken: false,
1167-
mcpAccessToken: null,
1168-
setMcpAccessToken: () => {},
11691143
}}
11701144
>
11711145
<TamboRegistryProvider

react-sdk/src/mcp/tambo-mcp-provider.test.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ const TestWrapper: React.FC<{
7878
client,
7979
queryClient: {} as any,
8080
isUpdatingToken: false,
81-
mcpAccessToken: null,
82-
setMcpAccessToken: () => {},
8381
}}
8482
>
8583
<TamboRegistryProvider mcpServers={mcpServers}>
@@ -271,8 +269,6 @@ describe("TamboMcpProvider server list changes", () => {
271269
client: useTamboClient(),
272270
queryClient: {} as any,
273271
isUpdatingToken: false,
274-
mcpAccessToken: null,
275-
setMcpAccessToken: () => {},
276272
}}
277273
>
278274
<TamboRegistryProvider mcpServers={["https://a.example"]}>
@@ -297,8 +293,6 @@ describe("TamboMcpProvider server list changes", () => {
297293
client: useTamboClient(),
298294
queryClient: {} as any,
299295
isUpdatingToken: false,
300-
mcpAccessToken: null,
301-
setMcpAccessToken: () => {},
302296
}}
303297
>
304298
<TamboRegistryProvider
@@ -329,8 +323,6 @@ describe("TamboMcpProvider server list changes", () => {
329323
client: useTamboClient(),
330324
queryClient: {} as any,
331325
isUpdatingToken: false,
332-
mcpAccessToken: null,
333-
setMcpAccessToken: () => {},
334326
}}
335327
>
336328
<TamboRegistryProvider
@@ -356,8 +348,6 @@ describe("TamboMcpProvider server list changes", () => {
356348
client: useTamboClient(),
357349
queryClient: {} as any,
358350
isUpdatingToken: false,
359-
mcpAccessToken: null,
360-
setMcpAccessToken: () => {},
361351
}}
362352
>
363353
<TamboRegistryProvider mcpServers={["https://a.example"]}>
@@ -470,8 +460,6 @@ describe("TamboMcpProvider server list changes", () => {
470460
client: useTamboClient(),
471461
queryClient: {} as any,
472462
isUpdatingToken: false,
473-
mcpAccessToken: null,
474-
setMcpAccessToken: () => {},
475463
}}
476464
>
477465
<TamboRegistryProvider
@@ -499,8 +487,6 @@ describe("TamboMcpProvider server list changes", () => {
499487
client: useTamboClient(),
500488
queryClient: {} as any,
501489
isUpdatingToken: false,
502-
mcpAccessToken: null,
503-
setMcpAccessToken: () => {},
504490
}}
505491
>
506492
<TamboRegistryProvider mcpServers={["https://a.example"]}>
@@ -536,8 +522,6 @@ describe("TamboMcpProvider server list changes", () => {
536522
client: useTamboClient(),
537523
queryClient: {} as any,
538524
isUpdatingToken: false,
539-
mcpAccessToken: null,
540-
setMcpAccessToken: () => {},
541525
}}
542526
>
543527
<TamboRegistryProvider

0 commit comments

Comments
 (0)