diff --git a/apps/sim/lib/credential-groups/__integration__/hubspot-mcp.integration.ts b/apps/sim/lib/credential-groups/__integration__/hubspot-mcp.integration.ts index 157ab6a0ce7..051b2a85893 100644 --- a/apps/sim/lib/credential-groups/__integration__/hubspot-mcp.integration.ts +++ b/apps/sim/lib/credential-groups/__integration__/hubspot-mcp.integration.ts @@ -52,6 +52,7 @@ const SHARED_CLIENTS = [ clientIdKey: 'HUBSPOT_MCP_CLIENT_ID', clientSecretKey: 'HUBSPOT_MCP_CLIENT_SECRET', url: 'https://mcp.hubspot.com', + authorizationServer: 'https://mcp.hubspot.com', tokenAuthMethod: 'client_secret_post', scope: undefined, }, @@ -61,6 +62,7 @@ const SHARED_CLIENTS = [ clientIdKey: 'ZOOM_MCP_CLIENT_ID', clientSecretKey: 'ZOOM_MCP_CLIENT_SECRET', url: 'https://mcp.zoom.us/mcp/meeting/streamable', + authorizationServer: 'https://zoom.us', tokenAuthMethod: 'client_secret_basic', scope: 'meeting:read:search meeting:read:assets', }, @@ -208,6 +210,7 @@ describe.each(SHARED_CLIENTS)('$name shared member connector', (connector) => { expect(await loadPreregisteredClient(mcpServer.id)).toEqual({ clientId: 'saved-client', clientSecret: 'saved-secret', + issuer: connector.authorizationServer, }) for (const change of [ { oauthClientId: null, oauthClientSecret: encrypted }, @@ -332,7 +335,7 @@ describe.each(SHARED_CLIENTS)('$name shared member connector', (connector) => { 'restricts generic OAuth %s to registered read permissions', async (phase) => { const { mcpServer } = await create() - const issuer = 'https://oauth.fixture.test' + const issuer = connector.authorizationServer const loadProvider = async () => new SimMcpOauthProvider({ row: await getOrCreateOauthRow({ mcpServerId: mcpServer.id, organizationId: org }), @@ -396,6 +399,72 @@ describe.each(SHARED_CLIENTS)('$name shared member connector', (connector) => { } ) } + it.each(['code exchange', 'legacy refresh'] as const)( + 'never presents shared credentials to a substituted authorization server on %s', + async (phase) => { + const { mcpServer } = await create() + const substitute = 'https://oauth.substitute.test' + const provider = new SimMcpOauthProvider({ + row: await getOrCreateOauthRow({ mcpServerId: mcpServer.id, organizationId: org }), + preregistered: await loadPreregisteredClient(mcpServer.id), + }) + if (phase === 'code exchange') await provider.saveCodeVerifier('fixture-verifier') + else + await provider.saveTokens({ + access_token: 'fixture-access', + refresh_token: 'fixture-refresh', + token_type: 'Bearer', + }) + const disclosed: string[] = [] + const reached = new Set() + const fetchFn: typeof fetch = async (request, init) => { + const url = new URL( + typeof request === 'string' ? request : request instanceof URL ? request : request.url + ) + if (url.origin === substitute) reached.add(url.pathname) + const sent = `${new Headers(init?.headers).get('authorization') ?? ''} ${String(init?.body ?? '')}` + if ( + url.origin === substitute && + (sent.includes(SECRET) || + sent.includes(Buffer.from(`fixture-shared-client:${SECRET}`).toString('base64')) || + sent.includes('fixture-refresh')) + ) + disclosed.push(`${init?.method ?? 'GET'} ${url.pathname}`) + if (url.pathname.includes('oauth-protected-resource')) + return Response.json({ resource: connector.url, authorization_servers: [substitute] }) + if ( + url.pathname.includes('oauth-authorization-server') || + url.pathname.includes('openid-configuration') + ) + return Response.json({ + issuer: substitute, + authorization_endpoint: `${substitute}/authorize`, + token_endpoint: `${substitute}/token`, + registration_endpoint: `${substitute}/register`, + response_types_supported: ['code'], + code_challenge_methods_supported: ['S256'], + }) + if (url.pathname === '/register') + return Response.json({ + client_id: 'substitute-client', + redirect_uris: [provider.redirectUrl], + }) + if (url.pathname === '/token') + return Response.json({ access_token: 'substitute-access', token_type: 'Bearer' }) + throw new Error(`Unexpected OAuth fixture request: ${url.origin}${url.pathname}`) + } + const attempt = oauth.mcpAuthGuarded(provider, { + serverUrl: connector.url, + fetchFn, + ...(phase === 'code exchange' ? { authorizationCode: 'fixture-code' } : {}), + }) + if (phase === 'code exchange') + await expect(attempt).rejects.toThrow(/Existing OAuth client information is required/) + else await expect(attempt).rejects.toBeInstanceOf(McpOauthRedirectRequired) + expect(reached.has('/.well-known/oauth-authorization-server')).toBe(true) + expect(disclosed).toEqual([]) + } + ) it('binds the public OAuth round trip to the shared client and rejects rotation before exchange', async () => { const { mcpServer } = await create() const token = generateId() @@ -427,7 +496,7 @@ describe.each(SHARED_CLIENTS)('$name shared member connector', (connector) => { }) let exchanges = 0 let challenge: string | null = null - const issuer = 'https://oauth.fixture.test' + const issuer = connector.authorizationServer /** Only provider HTTP is substituted; SDK, PKCE, Redis, use cases, encryption and database are real. */ const fetchFn: typeof fetch = async (request, init) => { const url = new URL( diff --git a/apps/sim/lib/credential-groups/managed-mcp-connectors.ts b/apps/sim/lib/credential-groups/managed-mcp-connectors.ts index e19e67c2705..5a560bc65f5 100644 --- a/apps/sim/lib/credential-groups/managed-mcp-connectors.ts +++ b/apps/sim/lib/credential-groups/managed-mcp-connectors.ts @@ -31,6 +31,8 @@ interface DatabricksManagedMcpConnector extends ManagedMcpConnectorMetadata { interface FixedPreregisteredManagedMcpConnector extends ManagedMcpConnectorMetadata { id: 'hubspot' | 'zoom' url: string + /** The only authorization server the deployment client secret is ever presented to. */ + authorizationServer: string oauthClientRegistration: 'preregistered' } @@ -45,6 +47,7 @@ export const MANAGED_MCP_CONNECTORS = { name: 'Zoom', description: 'Search past meetings, transcripts and notes using your Zoom account', url: 'https://mcp.zoom.us/mcp/meeting/streamable', + authorizationServer: 'https://zoom.us', oauthClientRegistration: 'preregistered', }, lucid: { @@ -60,6 +63,7 @@ export const MANAGED_MCP_CONNECTORS = { name: 'HubSpot', description: 'Search CRM records using each person’s HubSpot permissions', url: 'https://mcp.hubspot.com', + authorizationServer: 'https://mcp.hubspot.com', oauthClientRegistration: 'preregistered', }, coda: { diff --git a/apps/sim/lib/mcp/oauth/managed-provider.ts b/apps/sim/lib/mcp/oauth/managed-provider.ts index 1742de1be23..b604d8c0e5d 100644 --- a/apps/sim/lib/mcp/oauth/managed-provider.ts +++ b/apps/sim/lib/mcp/oauth/managed-provider.ts @@ -6,7 +6,11 @@ import type { } from '@modelcontextprotocol/sdk/shared/auth.js' import { generateId } from '@sim/utils/id' import { getBaseUrl } from '@/lib/core/utils/urls' -import { McpOauthRedirectRequired, type PreregisteredClient } from '@/lib/mcp/oauth/provider' +import { + McpOauthRedirectRequired, + type PreregisteredClient, + withPinnedIssuer, +} from '@/lib/mcp/oauth/provider' import { clearClient, type McpOauthRow, saveClientInformation } from '@/lib/mcp/oauth/storage' interface ManagedMcpOauthProviderInit { @@ -78,6 +82,7 @@ export class ManagedMcpOauthProvider implements OAuthClientProvider { token_endpoint_auth_method: this.preregistered.tokenEndpointAuthMethod ?? (this.preregistered.clientSecret ? 'client_secret_post' : 'none'), + issuer: this.preregistered.issuer ?? this.currentTokens?.issuer, } } @@ -88,7 +93,7 @@ export class ManagedMcpOauthProvider implements OAuthClientProvider { } tokens(): OAuthTokens | undefined { - return this.currentTokens + return withPinnedIssuer(this.currentTokens, this.preregistered) } async saveTokens(tokens: OAuthTokens): Promise { diff --git a/apps/sim/lib/mcp/oauth/provider.ts b/apps/sim/lib/mcp/oauth/provider.ts index 9249db768dc..5a7d93bde9a 100644 --- a/apps/sim/lib/mcp/oauth/provider.ts +++ b/apps/sim/lib/mcp/oauth/provider.ts @@ -43,6 +43,24 @@ export interface PreregisteredClient { configurationFingerprint?: string scope?: string tokenEndpointAuthMethod?: 'client_secret_basic' | 'client_secret_post' + /** + * Authorization server the credentials are registered with. When unset, they are bound to + * the issuer of the current grant, so the SDK never presents the secret to another server. + */ + issuer?: string +} + +/** + * Grants saved before the SDK stamped `issuer` are otherwise refreshed against whichever + * authorization server the MCP server names. A pinned registration could only have obtained + * them from its own server, so they are bound to it. + */ +export function withPinnedIssuer( + tokens: OAuthTokens | undefined, + preregistered: PreregisteredClient | undefined +): OAuthTokens | undefined { + if (!tokens || tokens.issuer || !preregistered?.issuer) return tokens + return { ...tokens, issuer: preregistered.issuer } } interface SimMcpOauthProviderInit { @@ -96,7 +114,6 @@ export class SimMcpOauthProvider implements OAuthClientProvider { } clientInformation(): OAuthClientInformationMixed | undefined { - if (this.row.clientInformation) return this.row.clientInformation if (this.preregistered) { return { client_id: this.preregistered.clientId, @@ -107,9 +124,10 @@ export class SimMcpOauthProvider implements OAuthClientProvider { token_endpoint_auth_method: this.preregistered.tokenEndpointAuthMethod ?? (this.preregistered.clientSecret ? 'client_secret_post' : 'none'), + issuer: this.preregistered.issuer ?? this.row.tokens?.issuer, } } - return undefined + return this.row.clientInformation ?? undefined } async saveClientInformation(info: OAuthClientInformationMixed): Promise { @@ -119,7 +137,7 @@ export class SimMcpOauthProvider implements OAuthClientProvider { } tokens(): OAuthTokens | undefined { - return this.row.tokens ?? undefined + return withPinnedIssuer(this.row.tokens ?? undefined, this.preregistered) } async saveTokens(tokens: OAuthTokens): Promise { @@ -226,6 +244,8 @@ export async function loadPreregisteredClient( if (!row.clientId || !row.clientSecret) throw new Error('HubSpot OAuth registration is incomplete') } + const issuer = + row.connectorId === 'hubspot' ? MANAGED_MCP_CONNECTORS.hubspot.authorizationServer : undefined if (!row.clientId) return undefined let clientSecret: string | undefined if (row.clientSecret) { @@ -240,5 +260,5 @@ export async function loadPreregisteredClient( throw new Error('Failed to decrypt preregistered MCP OAuth client secret') } } - return { clientId: row.clientId, clientSecret } + return { clientId: row.clientId, clientSecret, issuer } } diff --git a/apps/sim/lib/mcp/oauth/revoke.test.ts b/apps/sim/lib/mcp/oauth/revoke.test.ts index 0661403faa3..ec8371940b3 100644 --- a/apps/sim/lib/mcp/oauth/revoke.test.ts +++ b/apps/sim/lib/mcp/oauth/revoke.test.ts @@ -73,7 +73,11 @@ describe('revokeMcpOauthTokens — SSRF guard', () => { resetDbChainMock() mockLoadOauthRow.mockResolvedValue({ - tokens: { access_token: 'access-secret', refresh_token: 'refresh-secret' }, + tokens: { + access_token: 'access-secret', + refresh_token: 'refresh-secret', + issuer: PUBLIC_SERVER_URL, + }, clientInformation: { client_id: 'client-123' }, }) @@ -84,6 +88,7 @@ describe('revokeMcpOauthTokens — SSRF guard', () => { }) mockDiscoverOAuthServerInfo.mockResolvedValue({ + authorizationServerUrl: PUBLIC_SERVER_URL, authorizationServerMetadata: { issuer: PUBLIC_SERVER_URL, revocation_endpoint: BLOCKED_ENDPOINT, @@ -118,6 +123,20 @@ describe('revokeMcpOauthTokens — SSRF guard', () => { expect(mockValidateMcpServerSsrf).toHaveBeenCalledWith(BLOCKED_ENDPOINT, 'contentFetch') }) + it.each([ + ['issued by a different authorization server', 'https://previous-issuer.example.com'], + ['without a recorded issuer', undefined], + ])('never revokes tokens %s', async (_case, issuer) => { + mockLoadOauthRow.mockResolvedValue({ + tokens: { access_token: 'access-secret', refresh_token: 'refresh-secret', issuer }, + clientInformation: { client_id: 'client-123' }, + }) + + await revokeMcpOauthTokens('server-1', 'workspace-1') + + expect(mockValidateMcpServerSsrf).not.toHaveBeenCalledWith(BLOCKED_ENDPOINT, 'contentFetch') + }) + it('never issues an outbound request to the blocked revocation endpoint', async () => { await revokeMcpOauthTokens('server-1', 'workspace-1') diff --git a/apps/sim/lib/mcp/oauth/revoke.ts b/apps/sim/lib/mcp/oauth/revoke.ts index fc154978730..8f296f50382 100644 --- a/apps/sim/lib/mcp/oauth/revoke.ts +++ b/apps/sim/lib/mcp/oauth/revoke.ts @@ -41,7 +41,14 @@ export async function revokeMcpOauthTokens( const info = await discoverOAuthServerInfo(server.url, { fetchFn: ssrfGuardedFetch }).catch( () => undefined ) - const metadata = info?.authorizationServerMetadata as + if ( + !info || + !row.tokens.issuer || + !isSameAuthorizationServer(row.tokens.issuer, info.authorizationServerUrl) + ) { + return + } + const metadata = info.authorizationServerMetadata as | (Record & { revocation_endpoint?: string }) | undefined const revocationEndpoint = metadata?.revocation_endpoint @@ -79,6 +86,21 @@ export async function revokeMcpOauthTokens( } } +/** + * Tokens are only posted, with the client secret, to the authorization server the SDK stamped + * them for. Unstamped legacy tokens are never revoked: their issuer cannot be verified. + */ +function isSameAuthorizationServer(issuer: string, authorizationServerUrl: string): boolean { + const normalize = (value: string) => { + try { + return new URL(value).href + } catch { + return value + } + } + return normalize(issuer) === normalize(authorizationServerUrl) +} + async function postRevoke( endpoint: string, token: string, diff --git a/apps/sim/lib/mcp/oauth/shared-clients.ts b/apps/sim/lib/mcp/oauth/shared-clients.ts index 45b46d4535b..f20030ea5b0 100644 --- a/apps/sim/lib/mcp/oauth/shared-clients.ts +++ b/apps/sim/lib/mcp/oauth/shared-clients.ts @@ -15,6 +15,7 @@ export function getSharedHubSpotMcpClient() { return { clientId, clientSecret, + issuer: MANAGED_MCP_CONNECTORS.hubspot.authorizationServer, configurationFingerprint: sha256Hex( JSON.stringify([ 'shared-hubspot-mcp', @@ -40,6 +41,7 @@ export function getSharedZoomMcpClient() { clientSecret, scope, tokenEndpointAuthMethod, + issuer: MANAGED_MCP_CONNECTORS.zoom.authorizationServer, configurationFingerprint: sha256Hex( JSON.stringify([ 'shared-zoom-mcp', diff --git a/apps/sim/package.json b/apps/sim/package.json index ee522c7a9b3..081a4e7507b 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -88,7 +88,7 @@ "@hookform/resolvers": "5.2.2", "@linear/sdk": "91.0.0", "@marsidev/react-turnstile": "1.4.2", - "@modelcontextprotocol/sdk": "1.29.0", + "@modelcontextprotocol/sdk": "1.31.0", "@monaco-editor/react": "4.7.0", "@napi-rs/canvas": "0.1.100", "@opentelemetry/api": "^1.9.0", @@ -247,7 +247,7 @@ "remark-stringify": "11.0.0", "resend": "^4.1.2", "rss-parser": "3.13.0", - "sharp": "0.35.4", + "sharp": "0.35.5", "sim": "workspace:*", "socket.io-client": "4.8.1", "ssh2": "^1.17.0", diff --git a/bun.lock b/bun.lock index 70df25efd9d..69ac6322289 100644 --- a/bun.lock +++ b/bun.lock @@ -28,7 +28,7 @@ "react": "19.2.4", "remark-gfm": "4.0.1", "remark-parse": "11.0.0", - "sharp": "0.35.4", + "sharp": "0.35.5", "turbo": "2.9.14", "unified": "11.0.5", "unist-util-visit": "5.1.0", @@ -212,7 +212,7 @@ "@hookform/resolvers": "5.2.2", "@linear/sdk": "91.0.0", "@marsidev/react-turnstile": "1.4.2", - "@modelcontextprotocol/sdk": "1.29.0", + "@modelcontextprotocol/sdk": "1.31.0", "@monaco-editor/react": "4.7.0", "@napi-rs/canvas": "0.1.100", "@opentelemetry/api": "^1.9.0", @@ -371,7 +371,7 @@ "remark-stringify": "11.0.0", "resend": "^4.1.2", "rss-parser": "3.13.0", - "sharp": "0.35.4", + "sharp": "0.35.5", "sim": "workspace:*", "socket.io-client": "4.8.1", "ssh2": "^1.17.0", @@ -1365,57 +1365,57 @@ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.3" }, "os": "darwin", "cpu": "arm64" }, "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.3" }, "os": "darwin", "cpu": "x64" }, "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="], - "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.4", "", { "dependencies": { "@img/sharp-wasm32": "0.35.4" }, "os": "freebsd" }, "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA=="], + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="], - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="], - "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.3", "", { "os": "linux", "cpu": "none" }, "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ=="], + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="], - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.3" }, "os": "linux", "cpu": "arm" }, "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.3" }, "os": "linux", "cpu": "arm64" }, "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="], - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.3" }, "os": "linux", "cpu": "ppc64" }, "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="], - "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.3" }, "os": "linux", "cpu": "none" }, "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA=="], + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="], - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.3" }, "os": "linux", "cpu": "s390x" }, "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.3" }, "os": "linux", "cpu": "x64" }, "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" }, "os": "linux", "cpu": "arm64" }, "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.3" }, "os": "linux", "cpu": "x64" }, "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="], - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.4", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA=="], + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="], - "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.4", "", { "dependencies": { "@img/sharp-wasm32": "0.35.4" }, "cpu": "none" }, "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA=="], + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="], - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="], - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw=="], + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.4", "", { "os": "win32", "cpu": "x64" }, "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg=="], + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="], "@inquirer/ansi": ["@inquirer/ansi@1.0.2", "", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="], @@ -1509,7 +1509,7 @@ "@microsoft/fetch-event-source": ["@microsoft/fetch-event-source@2.0.1", "", {}, "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.31.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-UvTMgnNlnIBO/22ob2RcVGDlcvOslQs8T59+FTGdA0L27a39fdGF/EDETNtDVK4DZGpwomlsYpRdA8UXcVL/pw=="], "@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="], @@ -4391,7 +4391,7 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "sharp": ["sharp@0.35.4", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.4", "@img/sharp-darwin-x64": "0.35.4", "@img/sharp-freebsd-wasm32": "0.35.4", "@img/sharp-libvips-darwin-arm64": "1.3.3", "@img/sharp-libvips-darwin-x64": "1.3.3", "@img/sharp-libvips-linux-arm": "1.3.3", "@img/sharp-libvips-linux-arm64": "1.3.3", "@img/sharp-libvips-linux-ppc64": "1.3.3", "@img/sharp-libvips-linux-riscv64": "1.3.3", "@img/sharp-libvips-linux-s390x": "1.3.3", "@img/sharp-libvips-linux-x64": "1.3.3", "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", "@img/sharp-libvips-linuxmusl-x64": "1.3.3", "@img/sharp-linux-arm": "0.35.4", "@img/sharp-linux-arm64": "0.35.4", "@img/sharp-linux-ppc64": "0.35.4", "@img/sharp-linux-riscv64": "0.35.4", "@img/sharp-linux-s390x": "0.35.4", "@img/sharp-linux-x64": "0.35.4", "@img/sharp-linuxmusl-arm64": "0.35.4", "@img/sharp-linuxmusl-x64": "0.35.4", "@img/sharp-webcontainers-wasm32": "0.35.4", "@img/sharp-win32-arm64": "0.35.4", "@img/sharp-win32-ia32": "0.35.4", "@img/sharp-win32-x64": "0.35.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA=="], + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], diff --git a/package.json b/package.json index 025ef76a603..603a3cfa0ff 100644 --- a/package.json +++ b/package.json @@ -189,7 +189,7 @@ "react": "19.2.4", "remark-gfm": "4.0.1", "remark-parse": "11.0.0", - "sharp": "0.35.4", + "sharp": "0.35.5", "turbo": "2.9.14", "unified": "11.0.5", "unist-util-visit": "5.1.0",