-
Notifications
You must be signed in to change notification settings - Fork 114
Expand file tree
/
Copy pathcursor.py
More file actions
140 lines (109 loc) · 4.86 KB
/
cursor.py
File metadata and controls
140 lines (109 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""Cursor IDE implementation of MCP client adapter.
Cursor uses the standard ``mcpServers`` JSON format at ``.cursor/mcp.json``
(repo-local). The config schema is identical to GitHub Copilot CLI, so this
adapter subclasses :class:`CopilotClientAdapter` and only overrides the
config-path logic and the user-facing labels.
APM only writes to ``.cursor/mcp.json`` when the ``.cursor/`` directory
already exists — Cursor support is opt-in.
"""
import json
import os
from pathlib import Path
from .copilot import CopilotClientAdapter
class CursorClientAdapter(CopilotClientAdapter):
"""Cursor IDE MCP client adapter.
Inherits all config formatting from :class:`CopilotClientAdapter`
(``mcpServers`` JSON with ``command``/``args``/``env``). Only the
config-file location differs: repo-local ``.cursor/mcp.json`` instead
of global ``~/.copilot/mcp-config.json``.
"""
supports_user_scope: bool = False
# ------------------------------------------------------------------ #
# Config path
# ------------------------------------------------------------------ #
def get_config_path(self):
"""Return the path to ``.cursor/mcp.json`` in the repository root.
Unlike the Copilot adapter this is a **repo-local** path. The
``.cursor/`` directory is *not* created automatically — APM only
writes here when the directory already exists.
"""
cursor_dir = Path(os.getcwd()) / ".cursor"
return str(cursor_dir / "mcp.json")
# ------------------------------------------------------------------ #
# Config read / write — override to avoid auto-creating the directory
# ------------------------------------------------------------------ #
def update_config(self, config_updates):
"""Merge *config_updates* into the ``mcpServers`` section.
The ``.cursor/`` directory must already exist; if it does not, this
method returns silently (opt-in behaviour).
"""
config_path = Path(self.get_config_path())
# Opt-in: only write when .cursor/ already exists
if not config_path.parent.exists():
return
current_config = self.get_current_config()
if "mcpServers" not in current_config:
current_config["mcpServers"] = {}
current_config["mcpServers"].update(config_updates)
with open(config_path, "w", encoding="utf-8") as f:
json.dump(current_config, f, indent=2)
def get_current_config(self):
"""Read the current ``.cursor/mcp.json`` contents."""
config_path = self.get_config_path()
if not os.path.exists(config_path):
return {}
try:
with open(config_path, "r", encoding="utf-8") as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return {}
# ------------------------------------------------------------------ #
# configure_mcp_server — thin override for the print label
# ------------------------------------------------------------------ #
def configure_mcp_server(
self,
server_url,
server_name=None,
enabled=True,
env_overrides=None,
server_info_cache=None,
runtime_vars=None,
):
"""Configure an MCP server in Cursor's ``.cursor/mcp.json``.
Delegates entirely to the parent implementation but prints a
Cursor-specific success message.
"""
if not server_url:
print("Error: server_url cannot be empty")
return False
# Opt-in: skip silently when .cursor/ does not exist
cursor_dir = Path(os.getcwd()) / ".cursor"
if not cursor_dir.exists():
return True # nothing to do, not an error
try:
# Use cached server info if available, otherwise fetch from registry
if server_info_cache and server_url in server_info_cache:
server_info = server_info_cache[server_url]
else:
server_info = self.registry_client.find_server_by_reference(server_url)
if not server_info:
print(f"Error: MCP server '{server_url}' not found in registry")
return False
# Determine config key
if server_name:
config_key = server_name
elif "/" in server_url:
config_key = server_url.split("/")[-1]
else:
config_key = server_url
server_config = self._format_server_config(
server_info, env_overrides, runtime_vars
)
self.update_config({config_key: server_config})
print(
f"Successfully configured MCP server '{config_key}' for Cursor"
)
return True
except Exception as e:
print(f"Error configuring MCP server: {e}")
return False