2026-05-19 00:32:53 +02:00
import type { InstallTarget } from '@bmm/types' ;
export interface SnippetInput {
name : string ;
slug : string ;
publicUrl : string ;
2026-05-28 17:20:01 +02:00
/ * *
* Optional pre - issued OAuth client credentials . When present , Claude
* Desktop / ChatGPT users can paste them into the " OAuth Client ID /
* Secret " form fields as a fallback if Dynamic Client Registration
* ( DCR ) is blocked by their network or the client refuses it .
* /
oauthClientId? : string ;
oauthClientSecret? : string ;
2026-05-19 00:32:53 +02:00
}
2026-05-28 17:20:01 +02:00
export interface SnippetOutput {
label : string ;
code : string ;
/** Optional short hint shown below the snippet. */
note? : string ;
}
const stripTrailingSlash = ( u : string ) = > u . replace ( /\/$/ , '' ) ;
export function buildSnippet ( target : InstallTarget , input : SnippetInput ) : SnippetOutput {
2026-05-19 00:32:53 +02:00
const key = input . slug . replace ( /[^a-zA-Z0-9_-]/g , '_' ) ;
2026-05-28 17:20:01 +02:00
const baseUrl = stripTrailingSlash ( input . publicUrl ) ;
const mcpUrl = ` ${ baseUrl } /mcp ` ;
const hasCreds = ! ! ( input . oauthClientId && input . oauthClientSecret ) ;
2026-05-19 00:32:53 +02:00
switch ( target ) {
2026-05-28 17:20:01 +02:00
case 'claude-desktop' : {
// Claude Desktop now uses a "Custom Connector" UI with form fields:
// Name / Remote MCP Server URL / OAuth Client ID / OAuth Client Secret.
// We show the exact values to paste so the JSON-config dance is gone.
// OAuth fields are blank by default (DCR registers them automatically)
// and only filled when the org has pre-issued credentials as a fallback.
const lines = [
` Name: ${ input . name } ` ,
` Remote MCP Server URL: ${ mcpUrl } ` ,
] ;
if ( hasCreds ) {
lines . push (
` OAuth Client ID: ${ input . oauthClientId } ` ,
` OAuth Client Secret: ${ input . oauthClientSecret } ` ,
) ;
} else {
lines . push (
` OAuth Client ID: (leave blank, registers automatically) ` ,
` OAuth Client Secret: (leave blank, registers automatically) ` ,
) ;
}
return {
label : 'Claude Desktop · Custom Connector' ,
code : lines.join ( '\n' ) ,
note : hasCreds
? 'Settings → Connectors → Add custom connector. Paste each value into the matching field. OAuth handshake runs on first use.'
: 'Settings → Connectors → Add custom connector. Paste Name + URL, leave the OAuth fields blank. If your network blocks Dynamic Client Registration, generate a fixed Client ID / Secret in the server settings and paste them too.' ,
} ;
}
case 'claude-code' :
return {
label : 'Claude Code (CLI)' ,
code : ` claude mcp add ${ key } ${ mcpUrl } --transport http ` ,
note : ` Run in any project. Use \` claude mcp list \` to confirm and \` claude mcp remove ${ key } \` to undo. ` ,
} ;
case 'cursor' :
2026-05-19 00:32:53 +02:00
return {
2026-05-28 17:20:01 +02:00
label : '.cursor/mcp.json' ,
2026-05-19 00:32:53 +02:00
code : JSON.stringify (
{
mcpServers : {
[ key ] : {
url : mcpUrl ,
auth : 'oauth2' ,
} ,
} ,
} ,
null ,
2 ,
) ,
2026-05-28 17:20:01 +02:00
note : 'Place at the project root or in ~/.cursor/mcp.json for global use. Restart Cursor after saving.' ,
2026-05-19 00:32:53 +02:00
} ;
2026-05-28 17:20:01 +02:00
case 'vscode' :
2026-05-19 00:32:53 +02:00
return {
2026-05-28 17:20:01 +02:00
label : '.vscode/mcp.json' ,
2026-05-19 00:32:53 +02:00
code : JSON.stringify (
{
2026-05-28 17:20:01 +02:00
servers : {
2026-05-19 00:32:53 +02:00
[ key ] : {
2026-05-28 17:20:01 +02:00
type : 'http' ,
2026-05-19 00:32:53 +02:00
url : mcpUrl ,
} ,
} ,
} ,
null ,
2 ,
) ,
2026-05-28 17:20:01 +02:00
note : 'VS Code → Copilot Chat → MCP servers. Save the file at the workspace root, then reload the window. OAuth runs on first call.' ,
} ;
case 'chatgpt' : {
const lines = [
` Name: ${ input . name } ` ,
` URL: ${ mcpUrl } ` ,
` Auth: OAuth 2.1 ` ,
] ;
if ( hasCreds ) {
lines . push (
` Client ID: ${ input . oauthClientId } ` ,
` Client Secret: ${ input . oauthClientSecret } ` ,
) ;
}
return {
label : 'ChatGPT · Custom Connector' ,
code : lines.join ( '\n' ) ,
note : hasCreds
? 'ChatGPT → Settings → Connectors → Add custom connector. Paste the values above.'
: 'ChatGPT → Settings → Connectors → Add custom connector. Paste Name + URL. OAuth handshake registers a client automatically on first use.' ,
} ;
}
case 'codex' :
return {
label : '~/.codex/config.toml' ,
code : ` [mcp_servers. ${ key } ]
type = "http"
url = "${mcpUrl}" ` ,
note : 'Append to your ~/.codex/config.toml. Restart the Codex CLI. OAuth runs on first tool call.' ,
2026-05-19 00:32:53 +02:00
} ;
2026-05-28 17:20:01 +02:00
case 'raw-url' :
2026-05-19 00:32:53 +02:00
return {
2026-05-28 17:20:01 +02:00
label : 'Server URL' ,
code : mcpUrl ,
note : 'Any MCP-compatible client. Add it as a "Remote MCP server" (HTTP transport) and approve OAuth on first use.' ,
2026-05-19 00:32:53 +02:00
} ;
}
}