Skip to content

Commit 0cc6d27

Browse files
committed
fix mcp auth
1 parent 6ab912f commit 0cc6d27

File tree

9 files changed

+56
-46
lines changed

9 files changed

+56
-46
lines changed

docs/mcp/connecting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ No API key needed. Your client handles the OAuth flow automatically.
2424
For clients that don't support OAuth, or if you prefer manual key management:
2525

2626
1. [Sign in to your TanStack account](/login)
27-
2. Go to [API Keys](/account/api-keys)
27+
2. Go to [Integrations](/account/integrations)
2828
3. Click "New Key" and give it a descriptive name
2929
4. Copy the key immediately (you won't see it again)
3030

opencode.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
"enabled": false
1313
},
1414
"tanstack": {
15+
"type": "remote",
16+
"url": "https://tanstack.com/api/mcp",
17+
"enabled": true
18+
},
19+
"tanstack-dev": {
1520
"type": "remote",
1621
"url": "http://localhost:3000/api/mcp",
1722
"enabled": false

src/components/AuthenticatedUserMenu.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@ export function AuthenticatedUserMenu({
6969
</DropdownItem>
7070
{canApiKeys && (
7171
<DropdownItem asChild>
72-
<Link to="/account/api-keys" className="flex items-center gap-2">
72+
<Link
73+
to="/account/integrations"
74+
className="flex items-center gap-2"
75+
>
7376
<Key className="w-4 h-4" />
74-
<span>API Keys</span>
77+
<span>Integrations</span>
7578
</Link>
7679
</DropdownItem>
7780
)}

src/mcp/oauth.server.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -333,28 +333,30 @@ export async function validateOAuthToken(token: string): Promise<AuthResult> {
333333

334334
/**
335335
* List connected OAuth apps for a user
336+
* Uses refresh tokens since they persist longer than access tokens
336337
*/
337338
export async function listConnectedApps(userId: string): Promise<
338339
Array<{
339340
clientId: string
340-
scope: string
341-
createdAt: Date
342-
lastUsedAt: Date | null
341+
createdAt: string
342+
lastUsedAt: string | null
343343
}>
344344
> {
345-
// Get unique clients with their most recent token info
345+
// Get unique clients from refresh tokens (more persistent than access tokens)
346346
const result = await db
347347
.select({
348-
clientId: oauthMcpAccessTokens.clientId,
349-
scope: oauthMcpAccessTokens.scope,
350-
createdAt: sql<Date>`MIN(${oauthMcpAccessTokens.createdAt})`,
351-
lastUsedAt: sql<Date | null>`MAX(${oauthMcpAccessTokens.lastUsedAt})`,
348+
clientId: oauthMcpRefreshTokens.clientId,
349+
createdAt: sql<string>`MIN(${oauthMcpRefreshTokens.createdAt})::text`,
352350
})
353-
.from(oauthMcpAccessTokens)
354-
.where(eq(oauthMcpAccessTokens.userId, userId))
355-
.groupBy(oauthMcpAccessTokens.clientId, oauthMcpAccessTokens.scope)
356-
357-
return result
351+
.from(oauthMcpRefreshTokens)
352+
.where(eq(oauthMcpRefreshTokens.userId, userId))
353+
.groupBy(oauthMcpRefreshTokens.clientId)
354+
355+
return result.map((r) => ({
356+
clientId: r.clientId,
357+
createdAt: r.createdAt,
358+
lastUsedAt: null,
359+
}))
358360
}
359361

360362
/**

src/routeTree.gen.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ import { Route as LibrariesFeedIdRouteImport } from './routes/_libraries/feed.$i
8585
import { Route as LibrariesBlogSplatRouteImport } from './routes/_libraries/blog.$'
8686
import { Route as LibrariesAccountSubmissionsRouteImport } from './routes/_libraries/account/submissions'
8787
import { Route as LibrariesAccountNotesRouteImport } from './routes/_libraries/account/notes'
88+
import { Route as LibrariesAccountIntegrationsRouteImport } from './routes/_libraries/account/integrations'
8889
import { Route as LibrariesAccountFeedbackRouteImport } from './routes/_libraries/account/feedback'
89-
import { Route as LibrariesAccountApiKeysRouteImport } from './routes/_libraries/account/api-keys'
9090
import { Route as LibraryIdVersionDocsRouteImport } from './routes/$libraryId/$version.docs'
9191
import { Route as LibrariesVirtualVersionIndexRouteImport } from './routes/_libraries/virtual.$version.index'
9292
import { Route as LibrariesTableVersionIndexRouteImport } from './routes/_libraries/table.$version.index'
@@ -496,17 +496,18 @@ const LibrariesAccountNotesRoute = LibrariesAccountNotesRouteImport.update({
496496
path: '/notes',
497497
getParentRoute: () => LibrariesAccountRoute,
498498
} as any)
499+
const LibrariesAccountIntegrationsRoute =
500+
LibrariesAccountIntegrationsRouteImport.update({
501+
id: '/integrations',
502+
path: '/integrations',
503+
getParentRoute: () => LibrariesAccountRoute,
504+
} as any)
499505
const LibrariesAccountFeedbackRoute =
500506
LibrariesAccountFeedbackRouteImport.update({
501507
id: '/feedback',
502508
path: '/feedback',
503509
getParentRoute: () => LibrariesAccountRoute,
504510
} as any)
505-
const LibrariesAccountApiKeysRoute = LibrariesAccountApiKeysRouteImport.update({
506-
id: '/api-keys',
507-
path: '/api-keys',
508-
getParentRoute: () => LibrariesAccountRoute,
509-
} as any)
510511
const LibraryIdVersionDocsRoute = LibraryIdVersionDocsRouteImport.update({
511512
id: '/docs',
512513
path: '/docs',
@@ -709,8 +710,8 @@ export interface FileRoutesByFullPath {
709710
'/showcase': typeof ShowcaseIndexRoute
710711
'/stats': typeof StatsIndexRoute
711712
'/$libraryId/$version/docs': typeof LibraryIdVersionDocsRouteWithChildren
712-
'/account/api-keys': typeof LibrariesAccountApiKeysRoute
713713
'/account/feedback': typeof LibrariesAccountFeedbackRoute
714+
'/account/integrations': typeof LibrariesAccountIntegrationsRoute
714715
'/account/notes': typeof LibrariesAccountNotesRoute
715716
'/account/submissions': typeof LibrariesAccountSubmissionsRoute
716717
'/blog/$': typeof LibrariesBlogSplatRoute
@@ -809,8 +810,8 @@ export interface FileRoutesByTo {
809810
'/admin': typeof AdminIndexRoute
810811
'/showcase': typeof ShowcaseIndexRoute
811812
'/stats': typeof StatsIndexRoute
812-
'/account/api-keys': typeof LibrariesAccountApiKeysRoute
813813
'/account/feedback': typeof LibrariesAccountFeedbackRoute
814+
'/account/integrations': typeof LibrariesAccountIntegrationsRoute
814815
'/account/notes': typeof LibrariesAccountNotesRoute
815816
'/account/submissions': typeof LibrariesAccountSubmissionsRoute
816817
'/blog/$': typeof LibrariesBlogSplatRoute
@@ -916,8 +917,8 @@ export interface FileRoutesById {
916917
'/showcase/': typeof ShowcaseIndexRoute
917918
'/stats/': typeof StatsIndexRoute
918919
'/$libraryId/$version/docs': typeof LibraryIdVersionDocsRouteWithChildren
919-
'/_libraries/account/api-keys': typeof LibrariesAccountApiKeysRoute
920920
'/_libraries/account/feedback': typeof LibrariesAccountFeedbackRoute
921+
'/_libraries/account/integrations': typeof LibrariesAccountIntegrationsRoute
921922
'/_libraries/account/notes': typeof LibrariesAccountNotesRoute
922923
'/_libraries/account/submissions': typeof LibrariesAccountSubmissionsRoute
923924
'/_libraries/blog/$': typeof LibrariesBlogSplatRoute
@@ -1023,8 +1024,8 @@ export interface FileRouteTypes {
10231024
| '/showcase'
10241025
| '/stats'
10251026
| '/$libraryId/$version/docs'
1026-
| '/account/api-keys'
10271027
| '/account/feedback'
1028+
| '/account/integrations'
10281029
| '/account/notes'
10291030
| '/account/submissions'
10301031
| '/blog/$'
@@ -1123,8 +1124,8 @@ export interface FileRouteTypes {
11231124
| '/admin'
11241125
| '/showcase'
11251126
| '/stats'
1126-
| '/account/api-keys'
11271127
| '/account/feedback'
1128+
| '/account/integrations'
11281129
| '/account/notes'
11291130
| '/account/submissions'
11301131
| '/blog/$'
@@ -1229,8 +1230,8 @@ export interface FileRouteTypes {
12291230
| '/showcase/'
12301231
| '/stats/'
12311232
| '/$libraryId/$version/docs'
1232-
| '/_libraries/account/api-keys'
12331233
| '/_libraries/account/feedback'
1234+
| '/_libraries/account/integrations'
12341235
| '/_libraries/account/notes'
12351236
| '/_libraries/account/submissions'
12361237
| '/_libraries/blog/$'
@@ -1854,20 +1855,20 @@ declare module '@tanstack/react-router' {
18541855
preLoaderRoute: typeof LibrariesAccountNotesRouteImport
18551856
parentRoute: typeof LibrariesAccountRoute
18561857
}
1858+
'/_libraries/account/integrations': {
1859+
id: '/_libraries/account/integrations'
1860+
path: '/integrations'
1861+
fullPath: '/account/integrations'
1862+
preLoaderRoute: typeof LibrariesAccountIntegrationsRouteImport
1863+
parentRoute: typeof LibrariesAccountRoute
1864+
}
18571865
'/_libraries/account/feedback': {
18581866
id: '/_libraries/account/feedback'
18591867
path: '/feedback'
18601868
fullPath: '/account/feedback'
18611869
preLoaderRoute: typeof LibrariesAccountFeedbackRouteImport
18621870
parentRoute: typeof LibrariesAccountRoute
18631871
}
1864-
'/_libraries/account/api-keys': {
1865-
id: '/_libraries/account/api-keys'
1866-
path: '/api-keys'
1867-
fullPath: '/account/api-keys'
1868-
preLoaderRoute: typeof LibrariesAccountApiKeysRouteImport
1869-
parentRoute: typeof LibrariesAccountRoute
1870-
}
18711872
'/$libraryId/$version/docs': {
18721873
id: '/$libraryId/$version/docs'
18731874
path: '/docs'
@@ -2115,16 +2116,16 @@ const LibraryIdRouteRouteWithChildren = LibraryIdRouteRoute._addFileChildren(
21152116
)
21162117

21172118
interface LibrariesAccountRouteChildren {
2118-
LibrariesAccountApiKeysRoute: typeof LibrariesAccountApiKeysRoute
21192119
LibrariesAccountFeedbackRoute: typeof LibrariesAccountFeedbackRoute
2120+
LibrariesAccountIntegrationsRoute: typeof LibrariesAccountIntegrationsRoute
21202121
LibrariesAccountNotesRoute: typeof LibrariesAccountNotesRoute
21212122
LibrariesAccountSubmissionsRoute: typeof LibrariesAccountSubmissionsRoute
21222123
LibrariesAccountIndexRoute: typeof LibrariesAccountIndexRoute
21232124
}
21242125

21252126
const LibrariesAccountRouteChildren: LibrariesAccountRouteChildren = {
2126-
LibrariesAccountApiKeysRoute: LibrariesAccountApiKeysRoute,
21272127
LibrariesAccountFeedbackRoute: LibrariesAccountFeedbackRoute,
2128+
LibrariesAccountIntegrationsRoute: LibrariesAccountIntegrationsRoute,
21282129
LibrariesAccountNotesRoute: LibrariesAccountNotesRoute,
21292130
LibrariesAccountSubmissionsRoute: LibrariesAccountSubmissionsRoute,
21302131
LibrariesAccountIndexRoute: LibrariesAccountIndexRoute,

src/routes/_libraries/account.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function AccountLayout() {
8080
</Link>
8181
{canApiKeys && (
8282
<Link
83-
to="/account/api-keys"
83+
to="/account/integrations"
8484
className="pb-4 px-1 border-b-2 font-medium text-sm transition-colors"
8585
activeProps={{
8686
className: 'border-blue-600 text-blue-600 dark:text-blue-400',
@@ -90,7 +90,7 @@ function AccountLayout() {
9090
'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300',
9191
}}
9292
>
93-
API Keys
93+
Integrations
9494
</Link>
9595
)}
9696
</nav>

src/routes/_libraries/account/api-keys.tsx renamed to src/routes/_libraries/account/integrations.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import {
2626
Clock,
2727
Link2,
2828
} from 'lucide-react'
29-
export const Route = createFileRoute('/_libraries/account/api-keys')({
30-
component: ApiKeysPage,
29+
export const Route = createFileRoute('/_libraries/account/integrations')({
30+
component: IntegrationsPage,
3131
})
3232

33-
function ApiKeysPage() {
33+
function IntegrationsPage() {
3434
const queryClient = useQueryClient()
3535
const { notify } = useToast()
3636
const [isCreating, setIsCreating] = React.useState(false)

src/routes/_libraries/mcp.$version.index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function McpVersionIndex() {
4343
Get Started
4444
</Button>
4545
{canApiKeys && (
46-
<Button as={Link} to="/account/api-keys">
46+
<Button as={Link} to="/account/integrations">
4747
<Key className="w-3.5 h-3.5" />
4848
Get API Key
4949
</Button>

src/utils/oauthMcp.functions.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ export const listConnectedApps = createServerFn({ method: 'POST' }).handler(
1919

2020
return apps.map((app) => ({
2121
clientId: app.clientId,
22-
scope: app.scope,
23-
createdAt: app.createdAt.toISOString(),
24-
lastUsedAt: app.lastUsedAt?.toISOString() ?? null,
22+
createdAt: app.createdAt,
23+
lastUsedAt: app.lastUsedAt,
2524
}))
2625
},
2726
)

0 commit comments

Comments
 (0)