mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 22:29:11 +03:00
fix(mcp): use native HTTP for Devin MCP connections (#51646)
## Problem
The Devin Desktop preset wraps Supabase MCP URLs in `npx -y mcp-remote`,
although Devin Local and Devin CLI support Streamable HTTP natively.
Connecting therefore adds a Node/npm bridge that the client does not
need. The preset also lacks Devin-specific setup and authentication
instructions.
## Solution
Use the existing native `url` configuration for Devin:
```json
{
"mcpServers": {
"supabase": {
"url": "https://mcp.supabase.com/mcp"
}
}
}
```
The generated URL retains the selected project, read-only mode, and
features. The internal `windsurf` key is preserved.
Add shared setup instructions for the configuration panel and generated
Markdown: user-scope installation, the Windows config file location next
to the shared macOS/Linux one, a re-authentication command for hosted
connections, and loading tools in a new Devin Local or CLI session.
Local connections omit the re-authentication step.
Include Devin in the shared client URL round-trip test. The Devin-only
test files from the first revision were removed in review so that tests
stay shared across clients.
Native configuration follows the official [Devin MCP
documentation](https://docs.devin.ai/cli/extensibility/mcp/configuration)
and [Devin Local
integration](https://docs.devin.ai/desktop/devin-local).
## Validation
- **273 tests passed** across 28 `ui-patterns` test files on the current
revision; 34 of them in the MCP section.
- Package typechecking, Prettier on the four changed files, and `git
diff --check` passed.
- **Devin CLI 3000.11.3:** native HTTP initialization and tool
discovery; synthetic OAuth with S256 PKCE and authenticated tool
discovery using a saved token.
- **Devin Desktop 3.10.48 / bundled Devin Local 3000.10.48:** the server
card showed `Connected` and the fixture tool. Its `tools/list` request
came from the bundled Devin Local process.
OAuth validation used controlled local fixtures; live Supabase Hosted
authorization was not exercised. The full monorepo application build was
not run.
## Review instructions
1. Run `pnpm --filter ui-patterns exec vitest run src/McpUrlBuilder` and
`pnpm --filter ui-patterns typecheck`.
2. Select **Devin Desktop** in the MCP configuration panel. Confirm that
the JSON uses `url` and preserves the selected project, read-only mode,
and features.
3. Switch to the local connection. Confirm that Hosted login
instructions disappear. Check that generated Markdown explicitly limits
that login step to Hosted connections.
## Checklist
- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide).
N/A: no docs topic edited; the change is in the shared MCP configuration
component and its generated instructions.
This commit is contained in:
1 parent
09a1f431bc
commit
0f618d8efb
4 files changed
+46
-38
No files matched your search
@@ -16,7 +16,6 @@ import type {
|
||||
OmpMcpConfig,
|
||||
OpenCodeMcpConfig,
|
||||
VSCodeMcpConfig,
|
||||
WindsurfMcpConfig,
|
||||
} from './types'
|
||||
import { getMcpUrl } from './types'
|
||||
|
||||
@@ -248,17 +247,7 @@ export const MCP_CLIENT_DATA: McpClientData[] = [
|
||||
icon: 'devin',
|
||||
hasDistinctDarkIcon: true,
|
||||
configFile: '~/.config/devin/mcp_config.json',
|
||||
externalDocsUrl: '',
|
||||
transformConfig: (config): WindsurfMcpConfig => {
|
||||
return {
|
||||
mcpServers: {
|
||||
supabase: {
|
||||
command: 'npx',
|
||||
args: ['-y', 'mcp-remote', config.mcpServers.supabase.url],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
externalDocsUrl: 'https://docs.devin.ai/cli/extensibility/mcp/configuration',
|
||||
},
|
||||
{
|
||||
key: 'warp',
|
||||
@@ -441,6 +430,10 @@ export const MCP_CLI_COMMANDS: Record<string, McpCliCommands> = {
|
||||
install: (url) => `copilot mcp add --transport http supabase "${url}"`,
|
||||
authenticate: 'copilot -i /mcp',
|
||||
},
|
||||
windsurf: {
|
||||
install: (url) => `devin mcp add -s user supabase "${url}"`,
|
||||
authenticate: 'devin mcp login supabase',
|
||||
},
|
||||
goose: {
|
||||
install: (url) => `goose session --with-streamable-http-extension "${url}"`,
|
||||
},
|
||||
|
||||
@@ -210,6 +210,39 @@ export const MCP_CLIENT_INSTRUCTIONS: Record<string, McpClientInstructions> = {
|
||||
</>
|
||||
),
|
||||
},
|
||||
windsurf: {
|
||||
primary: ({ url }) => (
|
||||
<>
|
||||
<paragraph>
|
||||
These instructions use the Devin Local agent in Devin Desktop or Devin CLI. Add the
|
||||
Supabase MCP server to your user configuration:
|
||||
</paragraph>
|
||||
<code lang="bash" value={MCP_CLI_COMMANDS['windsurf'].install!(url)} />
|
||||
</>
|
||||
),
|
||||
alternate: ({ isPlatform }) => (
|
||||
<>
|
||||
<paragraph>
|
||||
On Windows, the configuration file is{' '}
|
||||
<inlineCode value="%APPDATA%\devin\mcp_config.json" /> instead.
|
||||
</paragraph>
|
||||
{isPlatform && (
|
||||
<>
|
||||
<paragraph>
|
||||
If you skipped the browser sign-in or need to re-authenticate, run:
|
||||
</paragraph>
|
||||
<code lang="bash" value={MCP_CLI_COMMANDS['windsurf'].authenticate!} />
|
||||
<paragraph>Complete the authorization flow in your browser.</paragraph>
|
||||
</>
|
||||
)}
|
||||
<paragraph>Check the server configuration:</paragraph>
|
||||
<code lang="bash" value="devin mcp list" />
|
||||
<paragraph>
|
||||
Start a new Devin Local or Devin CLI session to load the server's tools.
|
||||
</paragraph>
|
||||
</>
|
||||
),
|
||||
},
|
||||
goose: {
|
||||
primary: ({ url }) => (
|
||||
<>
|
||||
|
||||
@@ -5,22 +5,14 @@ import { getMcpUrl, type McpClientBaseConfig } from './types'
|
||||
|
||||
const SERVER_URL = 'https://mcp.supabase.com/mcp?project_ref=abc123&read_only=true'
|
||||
|
||||
/**
|
||||
* `windsurf` configures a stdio `mcp-remote` command that carries the URL in `args`, so its
|
||||
* config has no URL field to read back. It renders no instructions and no deep link, so it
|
||||
* never reaches `getMcpUrl`.
|
||||
*/
|
||||
const CLIENTS_WITHOUT_URL_IN_CONFIG = new Set(['windsurf'])
|
||||
|
||||
describe('getMcpUrl', () => {
|
||||
it.each(
|
||||
MCP_CLIENT_DATA.filter((client) => !CLIENTS_WITHOUT_URL_IN_CONFIG.has(client.key)).map(
|
||||
(client) => [client.key, client] as const
|
||||
)
|
||||
)('reads the server URL back out of the %s config', (_key, client) => {
|
||||
const base: McpClientBaseConfig = { mcpServers: { supabase: { url: SERVER_URL } } }
|
||||
const config = client.transformConfig ? client.transformConfig(base) : base
|
||||
it.each(MCP_CLIENT_DATA.map((client) => [client.key, client] as const))(
|
||||
'reads the server URL back out of the %s config',
|
||||
(_key, client) => {
|
||||
const base: McpClientBaseConfig = { mcpServers: { supabase: { url: SERVER_URL } } }
|
||||
const config = client.transformConfig ? client.transformConfig(base) : base
|
||||
|
||||
expect(getMcpUrl(config)).toBe(SERVER_URL)
|
||||
})
|
||||
expect(getMcpUrl(config)).toBe(SERVER_URL)
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -64,15 +64,6 @@ export interface VSCodeMcpConfig {
|
||||
}
|
||||
}
|
||||
|
||||
export interface WindsurfMcpConfig {
|
||||
mcpServers: {
|
||||
supabase: {
|
||||
command: 'npx'
|
||||
args: ['-y', 'mcp-remote', string]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface ClaudeCodeMcpConfig extends McpClientBaseConfig {
|
||||
mcpServers: {
|
||||
supabase: {
|
||||
@@ -247,7 +238,6 @@ export type McpClientConfig =
|
||||
| OpenCodeMcpConfig
|
||||
| OtherMcpConfig
|
||||
| VSCodeMcpConfig
|
||||
| WindsurfMcpConfig
|
||||
|
||||
// Type guards
|
||||
export function isVSCodeMcpConfig(config: McpClientConfig): config is VSCodeMcpConfig {
|
||||
|
||||
Reference in new issue
Block a user