neon 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/connection_string.js +1 -1
- package/dist/commands/inspect.js +32 -7
- package/dist/init/agents.js +73 -119
- package/dist/init/editors.js +3 -30
- package/dist/init/inspect.js +69 -20
- package/dist/init/install_mcp.js +35 -0
- package/dist/init/interactive.js +95 -107
- package/dist/init/neonctl.js +6 -2
- package/dist/init/orchestrate.js +20 -6
- package/dist/init/phases/mcp.js +91 -50
- package/dist/init/phases/setup.js +43 -34
- package/dist/init/phases/skills.js +10 -2
- package/dist/init/phases/status.js +4 -2
- package/dist/init/skills.js +55 -53
- package/dist/utils/inspect_db.js +108 -2
- package/dist/utils/inspect_queries.js +26 -7
- package/package.json +3 -2
|
@@ -106,7 +106,7 @@ const handler = async (props) => {
|
|
|
106
106
|
let host = props.pooled ? endpoint.host.replace(endpoint.id, `${endpoint.id}-pooler`) : endpoint.host;
|
|
107
107
|
if (parsedPIT.tag !== "head") host = endpoint.host.replace(endpoint.id, endpoint.branch_id);
|
|
108
108
|
const connectionString = new URL(`postgresql://${host}`);
|
|
109
|
-
connectionString.pathname = database
|
|
109
|
+
connectionString.pathname = `/${encodeURIComponent(database)}`;
|
|
110
110
|
connectionString.username = role;
|
|
111
111
|
connectionString.password = password;
|
|
112
112
|
if (props.prisma) {
|
package/dist/commands/inspect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { writer } from "../writer.js";
|
|
3
3
|
import { fillSingleProject } from "../utils/enrichers.js";
|
|
4
|
-
import {
|
|
4
|
+
import { formatInspectQueryError, resolveInspectTargets, runInspectQuery } from "../utils/inspect_db.js";
|
|
5
5
|
import { INSPECT_QUERIES } from "../utils/inspect_queries.js";
|
|
6
6
|
//#region src/commands/inspect.ts
|
|
7
7
|
var inspect_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -12,7 +12,7 @@ var inspect_exports = /* @__PURE__ */ __exportAll({
|
|
|
12
12
|
handler: () => handler
|
|
13
13
|
});
|
|
14
14
|
const command = "inspect";
|
|
15
|
-
const describe = "Inspect a
|
|
15
|
+
const describe = "Inspect a branch's Postgres health and configuration";
|
|
16
16
|
const aliases = ["inspection"];
|
|
17
17
|
/**
|
|
18
18
|
* `fillSingleProject` hits the API to auto-resolve a project. When the user
|
|
@@ -24,11 +24,36 @@ const fillSingleProjectUnlessDbUrl = async (props) => {
|
|
|
24
24
|
};
|
|
25
25
|
const runSubcommand = async (name, props) => {
|
|
26
26
|
const query = INSPECT_QUERIES[name];
|
|
27
|
-
const
|
|
28
|
-
const rows =
|
|
27
|
+
const { targets, includeDatabaseColumn, branchDatabaseCount } = await resolveInspectTargets(props, query.scope);
|
|
28
|
+
const rows = [];
|
|
29
|
+
for (const target of targets) {
|
|
30
|
+
let batch;
|
|
31
|
+
try {
|
|
32
|
+
batch = await runInspectQuery(target.connectionUri, query.sql, { requiresExtension: query.requiresExtension });
|
|
33
|
+
} catch (err) {
|
|
34
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
35
|
+
const wrapped = formatInspectQueryError({
|
|
36
|
+
reason,
|
|
37
|
+
database: target.database,
|
|
38
|
+
dbUrl: props.dbUrl,
|
|
39
|
+
databaseName: props.databaseName,
|
|
40
|
+
offerDatabaseNameHint: branchDatabaseCount > 1,
|
|
41
|
+
scope: query.scope,
|
|
42
|
+
requiresExtension: query.requiresExtension
|
|
43
|
+
});
|
|
44
|
+
if (wrapped === void 0) throw err instanceof Error ? err : new Error(reason);
|
|
45
|
+
throw new Error(wrapped);
|
|
46
|
+
}
|
|
47
|
+
if (includeDatabaseColumn) rows.push(...batch.map((row) => ({
|
|
48
|
+
database: target.database,
|
|
49
|
+
...row
|
|
50
|
+
})));
|
|
51
|
+
else rows.push(...batch);
|
|
52
|
+
}
|
|
53
|
+
const fields = includeDatabaseColumn ? ["database", ...query.fields] : query.fields;
|
|
29
54
|
writer(props).end(rows, {
|
|
30
|
-
fields
|
|
31
|
-
emptyMessage: query.emptyMessage
|
|
55
|
+
fields,
|
|
56
|
+
emptyMessage: includeDatabaseColumn ? query.emptyMessageAll ?? query.emptyMessage : query.emptyMessage
|
|
32
57
|
});
|
|
33
58
|
};
|
|
34
59
|
const dbBuilder = (argv) => {
|
|
@@ -42,7 +67,7 @@ const dbBuilder = (argv) => {
|
|
|
42
67
|
type: "string"
|
|
43
68
|
},
|
|
44
69
|
"database-name": {
|
|
45
|
-
describe: "Database
|
|
70
|
+
describe: "Database to inspect. Omit to cover every database the API lists for the branch. Ranking and row limits stay per database. One failing database fails the whole run. Compute-wide checks run once against the first listed database. Ignored with --db-url.",
|
|
46
71
|
type: "string"
|
|
47
72
|
},
|
|
48
73
|
"role-name": {
|
package/dist/init/agents.js
CHANGED
|
@@ -1,93 +1,6 @@
|
|
|
1
|
+
import { agents, detectGlobalAgents, getAgentTypes } from "add-mcp";
|
|
1
2
|
//#region src/init/agents.ts
|
|
2
|
-
|
|
3
|
-
* All agents that can be configured via neon-init.
|
|
4
|
-
* Aligns with add-mcp's supported agents table.
|
|
5
|
-
* https://github.com/neondatabase/add-mcp#supported-agents
|
|
6
|
-
*/
|
|
7
|
-
const ALL_CONFIGURABLE_AGENTS = [
|
|
8
|
-
{
|
|
9
|
-
editor: "Cursor",
|
|
10
|
-
addMcpId: "cursor",
|
|
11
|
-
hint: "Neon Local Connect extension"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
editor: "VS Code",
|
|
15
|
-
addMcpId: "vscode",
|
|
16
|
-
hint: "Neon Local Connect extension"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
editor: "Claude CLI",
|
|
20
|
-
addMcpId: "claude-code",
|
|
21
|
-
hint: "MCP Server"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
editor: "Claude Desktop",
|
|
25
|
-
addMcpId: "claude-desktop",
|
|
26
|
-
hint: "MCP Server"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
editor: "Codex",
|
|
30
|
-
addMcpId: "codex",
|
|
31
|
-
hint: "MCP Server"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
editor: "OpenCode",
|
|
35
|
-
addMcpId: "opencode",
|
|
36
|
-
hint: "MCP Server"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
editor: "Antigravity",
|
|
40
|
-
addMcpId: "antigravity",
|
|
41
|
-
hint: "MCP Server"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
editor: "Cline",
|
|
45
|
-
addMcpId: "cline",
|
|
46
|
-
hint: "MCP Server"
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
editor: "Cline CLI",
|
|
50
|
-
addMcpId: "cline-cli",
|
|
51
|
-
hint: "MCP Server"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
editor: "Gemini CLI",
|
|
55
|
-
addMcpId: "gemini-cli",
|
|
56
|
-
hint: "MCP Server"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
editor: "GitHub Copilot CLI",
|
|
60
|
-
addMcpId: "github-copilot-cli",
|
|
61
|
-
hint: "MCP Server"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
editor: "Goose",
|
|
65
|
-
addMcpId: "goose",
|
|
66
|
-
hint: "MCP Server"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
editor: "MCPorter",
|
|
70
|
-
addMcpId: "mcporter",
|
|
71
|
-
hint: "MCP Server"
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
editor: "Zed",
|
|
75
|
-
addMcpId: "zed",
|
|
76
|
-
hint: "MCP Server"
|
|
77
|
-
}
|
|
78
|
-
];
|
|
79
|
-
function getAddMcpAgentId(editor) {
|
|
80
|
-
const agent = ALL_CONFIGURABLE_AGENTS.find((a) => a.editor === editor);
|
|
81
|
-
if (!agent) throw new Error(`No add-mcp agent ID found for editor: ${editor}`);
|
|
82
|
-
return agent.addMcpId;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Maps a raw agent identifier (as reported by agents or passed via --agent)
|
|
86
|
-
* to the add-mcp compatible agent ID.
|
|
87
|
-
*
|
|
88
|
-
* This handles aliases like "copilot" → "vscode", "claude" → "claude-code", etc.
|
|
89
|
-
*/
|
|
90
|
-
const AGENT_ALIAS_TO_MCP_ID = {
|
|
3
|
+
const AGENT_ALIASES = {
|
|
91
4
|
cursor: "cursor",
|
|
92
5
|
copilot: "vscode",
|
|
93
6
|
"github-copilot": "vscode",
|
|
@@ -101,46 +14,87 @@ const AGENT_ALIAS_TO_MCP_ID = {
|
|
|
101
14
|
antigravity: "antigravity",
|
|
102
15
|
cline: "cline",
|
|
103
16
|
"cline-cli": "cline-cli",
|
|
17
|
+
"cline-vscode": "cline",
|
|
104
18
|
"gemini-cli": "gemini-cli",
|
|
105
19
|
gemini: "gemini-cli",
|
|
106
20
|
goose: "goose",
|
|
107
21
|
windsurf: "windsurf",
|
|
22
|
+
codeium: "windsurf",
|
|
23
|
+
cascade: "windsurf",
|
|
108
24
|
"github-copilot-cli": "github-copilot-cli",
|
|
109
25
|
mcporter: "mcporter",
|
|
110
|
-
zed: "zed"
|
|
26
|
+
zed: "zed",
|
|
27
|
+
grok: "grok-build",
|
|
28
|
+
"grok-build": "grok-build"
|
|
29
|
+
};
|
|
30
|
+
const SKILLS_AGENT_BY_TYPE = {
|
|
31
|
+
cursor: "cursor",
|
|
32
|
+
vscode: "github-copilot",
|
|
33
|
+
"claude-code": "claude-code",
|
|
34
|
+
"claude-desktop": "claude-code",
|
|
35
|
+
codex: "codex",
|
|
36
|
+
opencode: "opencode",
|
|
37
|
+
antigravity: "antigravity",
|
|
38
|
+
cline: "cline",
|
|
39
|
+
"cline-cli": "cline",
|
|
40
|
+
"gemini-cli": "gemini-cli",
|
|
41
|
+
goose: "goose",
|
|
42
|
+
"github-copilot-cli": "github-copilot",
|
|
43
|
+
windsurf: "windsurf",
|
|
44
|
+
zed: "zed",
|
|
45
|
+
"grok-build": "grok"
|
|
111
46
|
};
|
|
47
|
+
function listMcpAgentIds() {
|
|
48
|
+
return getAgentTypes();
|
|
49
|
+
}
|
|
50
|
+
function getAgentDisplayName(id) {
|
|
51
|
+
return agents[id].displayName;
|
|
52
|
+
}
|
|
53
|
+
function agentSupportsProjectMcp(id) {
|
|
54
|
+
return Boolean(agents[id].localConfigPath);
|
|
55
|
+
}
|
|
56
|
+
function agentSupportsHttpMcp(id) {
|
|
57
|
+
return agents[id].supportedTransports.includes("http");
|
|
58
|
+
}
|
|
59
|
+
function tryResolveAddMcpAgentId(rawAgent) {
|
|
60
|
+
return AGENT_ALIASES[rawAgent.toLowerCase()];
|
|
61
|
+
}
|
|
112
62
|
function resolveAddMcpAgentId(rawAgent) {
|
|
113
|
-
const resolved =
|
|
114
|
-
if (!resolved) throw new Error(`Unknown agent: "${rawAgent}". Supported agents: ${
|
|
63
|
+
const resolved = tryResolveAddMcpAgentId(rawAgent);
|
|
64
|
+
if (!resolved) throw new Error(`Unknown agent: "${rawAgent}". Supported agents: ${listMcpAgentIds().join(", ")}`);
|
|
115
65
|
return resolved;
|
|
116
66
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Maps a raw agent identifier to the skills CLI agent name.
|
|
119
|
-
*/
|
|
120
67
|
function getSkillsAgentName(agent) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
68
|
+
if (Object.prototype.hasOwnProperty.call(SKILLS_AGENT_BY_TYPE, agent)) return SKILLS_AGENT_BY_TYPE[agent];
|
|
69
|
+
const id = tryResolveAddMcpAgentId(agent);
|
|
70
|
+
if (!id) return void 0;
|
|
71
|
+
return SKILLS_AGENT_BY_TYPE[id];
|
|
72
|
+
}
|
|
73
|
+
function supportsSkills(agent) {
|
|
74
|
+
return getSkillsAgentName(agent) !== void 0;
|
|
75
|
+
}
|
|
76
|
+
function agentPickerHint(id) {
|
|
77
|
+
if (id === "cursor" || id === "vscode") return "Neon Local Connect extension";
|
|
78
|
+
if (id === "claude-desktop") return supportsSkills(id) ? "Connectors in the app, skills" : agents[id].unsupportedTransportMessage ?? "Add remote servers through Connectors in the app";
|
|
79
|
+
if (supportsSkills(id)) return "MCP server, skills";
|
|
80
|
+
return "MCP server";
|
|
81
|
+
}
|
|
82
|
+
async function detectInstalledAgents() {
|
|
83
|
+
return detectGlobalAgents();
|
|
84
|
+
}
|
|
85
|
+
function mcpPickerOptions() {
|
|
86
|
+
return listMcpAgentIds().map((id) => ({
|
|
87
|
+
value: id,
|
|
88
|
+
label: getAgentDisplayName(id),
|
|
89
|
+
hint: agentPickerHint(id)
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
function skillsPickerOptions() {
|
|
93
|
+
return listMcpAgentIds().filter((id) => supportsSkills(id)).map((id) => ({
|
|
94
|
+
value: id,
|
|
95
|
+
label: getAgentDisplayName(id),
|
|
96
|
+
hint: "agent skills"
|
|
97
|
+
}));
|
|
144
98
|
}
|
|
145
99
|
//#endregion
|
|
146
|
-
export {
|
|
100
|
+
export { agentPickerHint, agentSupportsHttpMcp, agentSupportsProjectMcp, detectInstalledAgents, getAgentDisplayName, getSkillsAgentName, listMcpAgentIds, mcpPickerOptions, resolveAddMcpAgentId, skillsPickerOptions, supportsSkills, tryResolveAddMcpAgentId };
|
package/dist/init/editors.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { detectInstalledAgents } from "./agents.js";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { execa } from "execa";
|
|
4
3
|
//#region src/init/editors.ts
|
|
5
|
-
/**
|
|
6
|
-
* Gets VS Code's global config directory based on the platform
|
|
7
|
-
*/
|
|
8
4
|
function getVSCodeGlobalConfigDir(homeDir) {
|
|
9
5
|
const platform = process.platform;
|
|
10
6
|
if (platform === "darwin") return resolve(homeDir, "Library", "Application Support", "Code", "User");
|
|
@@ -15,31 +11,8 @@ function getVSCodeGlobalConfigDir(homeDir) {
|
|
|
15
11
|
}
|
|
16
12
|
return null;
|
|
17
13
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*/
|
|
21
|
-
async function isClaudeCLIInstalled() {
|
|
22
|
-
try {
|
|
23
|
-
await execa("claude", ["--version"], {
|
|
24
|
-
stdio: "ignore",
|
|
25
|
-
timeout: 5e3
|
|
26
|
-
});
|
|
27
|
-
return true;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Detects which editors are installed on the system
|
|
34
|
-
*/
|
|
35
|
-
async function detectAvailableEditors(homeDir) {
|
|
36
|
-
const editors = [];
|
|
37
|
-
const cursorDir = resolve(homeDir, ".cursor");
|
|
38
|
-
if (existsSync(cursorDir)) editors.push("Cursor");
|
|
39
|
-
const vscodeGlobalDir = getVSCodeGlobalConfigDir(homeDir);
|
|
40
|
-
if (vscodeGlobalDir && existsSync(vscodeGlobalDir)) editors.push("VS Code");
|
|
41
|
-
if (await isClaudeCLIInstalled()) editors.push("Claude CLI");
|
|
42
|
-
return editors;
|
|
14
|
+
async function detectAvailableEditors(_homeDir) {
|
|
15
|
+
return detectInstalledAgents();
|
|
43
16
|
}
|
|
44
17
|
//#endregion
|
|
45
18
|
export { detectAvailableEditors, getVSCodeGlobalConfigDir };
|
package/dist/init/inspect.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
2
|
+
import { resolve, sep } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { agents, getAgentTypes } from "add-mcp";
|
|
3
5
|
//#region src/init/inspect.ts
|
|
4
6
|
/**
|
|
5
7
|
* Filesystem-based project inspection for interactive (agentless) mode.
|
|
@@ -15,9 +17,10 @@ async function inspectProject(checks) {
|
|
|
15
17
|
const cwd = process.cwd();
|
|
16
18
|
for (const check of checks) switch (check.id) {
|
|
17
19
|
case "mcp_server": {
|
|
18
|
-
const
|
|
19
|
-
results.
|
|
20
|
-
results.
|
|
20
|
+
const hits = findNeonMcpAgents(cwd);
|
|
21
|
+
results.mcpAgents = hits;
|
|
22
|
+
results.mcpConfigured = hits.length > 0;
|
|
23
|
+
results.mcpScope = mcpScopeFromHits(hits);
|
|
21
24
|
break;
|
|
22
25
|
}
|
|
23
26
|
case "connection_string":
|
|
@@ -51,24 +54,70 @@ async function inspectProject(checks) {
|
|
|
51
54
|
}
|
|
52
55
|
return results;
|
|
53
56
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
for (const configPath of projectConfigs) if (existsSync(configPath)) try {
|
|
58
|
-
const content = readFileSync(configPath, "utf-8");
|
|
59
|
-
if (content.includes("neon") || content.includes("mcp.neon.tech")) return "project";
|
|
60
|
-
} catch {}
|
|
61
|
-
const globalConfigs = [
|
|
62
|
-
resolve(home, ".cursor", "mcp.json"),
|
|
63
|
-
resolve(home, ".claude", "settings.local.json"),
|
|
64
|
-
resolve(home, ".claude", "settings.json")
|
|
65
|
-
];
|
|
66
|
-
for (const configPath of globalConfigs) if (existsSync(configPath)) try {
|
|
67
|
-
const content = readFileSync(configPath, "utf-8");
|
|
68
|
-
if (content.includes("neon") || content.includes("mcp.neon.tech")) return "global";
|
|
69
|
-
} catch {}
|
|
57
|
+
function mcpScopeFromHits(hits) {
|
|
58
|
+
if (hits.some((hit) => hit.scope === "project")) return "project";
|
|
59
|
+
if (hits.some((hit) => hit.scope === "global")) return "global";
|
|
70
60
|
return false;
|
|
71
61
|
}
|
|
62
|
+
function addMcpBakedHome() {
|
|
63
|
+
const cursorPath = agents.cursor.configPath;
|
|
64
|
+
const suffix = `${sep}.cursor${sep}mcp.json`;
|
|
65
|
+
if (cursorPath.endsWith(suffix)) return cursorPath.slice(0, -suffix.length);
|
|
66
|
+
return homedir();
|
|
67
|
+
}
|
|
68
|
+
function rebaseAddMcpHome(configPath) {
|
|
69
|
+
const currentHome = process.env.HOME || process.env.USERPROFILE || homedir();
|
|
70
|
+
const baked = addMcpBakedHome();
|
|
71
|
+
if (configPath.startsWith(baked)) return currentHome + configPath.slice(baked.length);
|
|
72
|
+
return configPath;
|
|
73
|
+
}
|
|
74
|
+
function agentProjectPath(id, cwd) {
|
|
75
|
+
const agent = agents[id];
|
|
76
|
+
if (agent.resolveConfigPath && agent.localConfigPath) return agent.resolveConfigPath(agent, {
|
|
77
|
+
local: true,
|
|
78
|
+
cwd
|
|
79
|
+
});
|
|
80
|
+
if (agent.localConfigPath) return resolve(cwd, agent.localConfigPath);
|
|
81
|
+
}
|
|
82
|
+
function agentGlobalPath(id, cwd) {
|
|
83
|
+
const agent = agents[id];
|
|
84
|
+
if (agent.resolveConfigPath) return rebaseAddMcpHome(agent.resolveConfigPath(agent, {
|
|
85
|
+
local: false,
|
|
86
|
+
cwd
|
|
87
|
+
}));
|
|
88
|
+
return rebaseAddMcpHome(agent.configPath);
|
|
89
|
+
}
|
|
90
|
+
function fileHasNeonMcp(configPath) {
|
|
91
|
+
if (!existsSync(configPath)) return false;
|
|
92
|
+
try {
|
|
93
|
+
const content = readFileSync(configPath, "utf-8");
|
|
94
|
+
if (content.toLowerCase().includes("mcp.neon.tech")) return true;
|
|
95
|
+
if (/"neon"\s*:/i.test(content)) return true;
|
|
96
|
+
if (/\[mcp_servers\.neon\]/i.test(content)) return true;
|
|
97
|
+
if (/^neon\s*=/im.test(content)) return true;
|
|
98
|
+
return false;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function findNeonMcpAgents(cwd) {
|
|
104
|
+
const hits = [];
|
|
105
|
+
for (const id of getAgentTypes()) {
|
|
106
|
+
const projectPath = agentProjectPath(id, cwd);
|
|
107
|
+
if (projectPath && fileHasNeonMcp(projectPath)) {
|
|
108
|
+
hits.push({
|
|
109
|
+
agent: id,
|
|
110
|
+
scope: "project"
|
|
111
|
+
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (fileHasNeonMcp(agentGlobalPath(id, cwd))) hits.push({
|
|
115
|
+
agent: id,
|
|
116
|
+
scope: "global"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return hits;
|
|
120
|
+
}
|
|
72
121
|
function checkConnectionString(cwd) {
|
|
73
122
|
for (const envFile of [".env", ".env.local"]) {
|
|
74
123
|
const envPath = resolve(cwd, envFile);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { agents, upsertServer } from "add-mcp";
|
|
2
|
+
//#region src/init/install_mcp.ts
|
|
3
|
+
const NEON_MCP_URL = "https://mcp.neon.tech/mcp";
|
|
4
|
+
const NEON_MCP_NAME = "Neon";
|
|
5
|
+
function installNeonMcpServer(options) {
|
|
6
|
+
const agent = agents[options.agent];
|
|
7
|
+
if (!agent.supportedTransports.includes("http")) return {
|
|
8
|
+
ok: false,
|
|
9
|
+
unsupported: true,
|
|
10
|
+
error: agent.unsupportedTransportMessage ?? `${agent.displayName} does not support remote HTTP MCP servers.`
|
|
11
|
+
};
|
|
12
|
+
if (options.scope === "project" && !agent.localConfigPath) return {
|
|
13
|
+
ok: false,
|
|
14
|
+
unsupported: true,
|
|
15
|
+
error: `${agent.displayName} does not support project-level MCP config.`
|
|
16
|
+
};
|
|
17
|
+
const result = upsertServer(options.agent, NEON_MCP_NAME, {
|
|
18
|
+
type: "http",
|
|
19
|
+
url: NEON_MCP_URL
|
|
20
|
+
}, {
|
|
21
|
+
local: options.scope === "project",
|
|
22
|
+
cwd: options.cwd
|
|
23
|
+
});
|
|
24
|
+
if (result.success) return {
|
|
25
|
+
ok: true,
|
|
26
|
+
path: result.path
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
unsupported: false,
|
|
31
|
+
error: result.error ?? "Failed to write Neon MCP server config"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { NEON_MCP_NAME, NEON_MCP_URL, installNeonMcpServer };
|