sublime-mcp 1.0.0__tar.gz

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.
@@ -0,0 +1,230 @@
1
+ Metadata-Version: 2.4
2
+ Name: sublime-mcp
3
+ Version: 1.0.0
4
+ Summary: MCP server for Sublime Text 4 — lets Claude Code read and control a running ST instance
5
+ Author-email: Donald Chitester <donaldchitester@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dpc00/sublime-mcp
8
+ Project-URL: Repository, https://github.com/dpc00/sublime-mcp
9
+ Keywords: mcp,sublime-text,claude,ai,editor
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Text Editors
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: mcp
17
+ Requires-Dist: httpx
18
+
19
+ # sublime-mcp
20
+
21
+ <!-- mcp-name: io.github.dpc00/sublime-mcp -->
22
+
23
+
24
+ MCP server for Sublime Text 4. Lets Claude Code (or any MCP client) read and
25
+ control a running ST instance via a local HTTP bridge.
26
+
27
+ 58 tools covering reading, navigation, editing, searching, build, Terminus
28
+ integration, settings, layout, and live Python scripting.
29
+
30
+ ## Architecture
31
+
32
+ ```
33
+ Claude Code (MCP client)
34
+ │ stdio / MCP protocol
35
+
36
+ mcp_server.py ← Python process you run outside ST
37
+ │ HTTP 127.0.0.1:9500
38
+
39
+ sublime_mcp.py ← ST plugin, HTTP server on ST's main thread
40
+ │ sublime API
41
+
42
+ Sublime Text 4
43
+ ```
44
+
45
+ | File | Role |
46
+ |------|------|
47
+ | `sublime_mcp.py` | ST plugin — runs an HTTP server inside Sublime Text |
48
+ | `mcp_server.py` | MCP server — wraps the HTTP API for MCP clients |
49
+
50
+ ## Installation
51
+
52
+ ### 1. Install the ST plugin
53
+
54
+ Copy `sublime_mcp.py` to your Sublime Text `Packages/User/` folder:
55
+
56
+ ```
57
+ %APPDATA%\Sublime Text\Packages\User\sublime_mcp.py
58
+ ```
59
+
60
+ ST loads it automatically on start (or via `Tools › Developer › New Plugin…`
61
+ then save over it). You should see:
62
+
63
+ ```
64
+ sublime-mcp: listening on 127.0.0.1:9500
65
+ ```
66
+
67
+ in the ST console (`View › Show Console`).
68
+
69
+ ### 2. Install the MCP server
70
+
71
+ ```
72
+ pip install sublime-mcp
73
+ ```
74
+
75
+ ### 3. Register with Claude Code
76
+
77
+ Add to `~/.claude/settings.json`:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "sublime": {
83
+ "command": "sublime-mcp"
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ Then restart Claude Code. Tools will appear with the `mcp__sublime__` prefix.
90
+
91
+ Then restart Claude Code. Tools will appear with the `mcp__sublime__` prefix.
92
+
93
+ ## Tools
94
+
95
+ ### Read / Introspect
96
+
97
+ | Tool | Description |
98
+ |------|-------------|
99
+ | `get_active_file` | Path, full content, cursor line/col, dirty flag, and syntax name |
100
+ | `get_selection` | Current selection(s): text and begin/end line+col for each |
101
+ | `get_cursor_context` | `lines` lines above and below cursor, with 1-based line numbers prepended |
102
+ | `get_open_files` | All files open in the current window (path, name, is_dirty) |
103
+ | `get_project_folders` | Project root folder paths |
104
+ | `get_file_content` | Full content of any already-open file by path |
105
+ | `get_view_content` | Full content of any open tab by name (partial match). Works for Terminus tabs and nameless views |
106
+ | `get_view_size` | Total character count of any open tab. Use to compute offsets before `get_view_chars` |
107
+ | `get_view_chars` | Text at character offsets begin..end (0-based, end exclusive). Clamps to buffer bounds |
108
+ | `get_view_phantoms` | Phantom HTML and extracted plain text from a named view; filters by phantom key |
109
+ | `get_output_panel` | Text content of a named output panel. Omit name for the active panel; `name='exec'` for build output |
110
+ | `get_active_panel` | Active panel id and, if it is an output panel, its content |
111
+ | `get_symbols` | All symbols (functions, classes, etc.) in the active file with line numbers |
112
+ | `lookup_symbol` | Find where a symbol is defined across all open files |
113
+ | `get_project_data` | Raw `.sublime-project` JSON for the current project |
114
+ | `get_variables` | ST build variables: `$file`, `$project_path`, `$platform`, etc. |
115
+ | `get_command_palette` | Command Palette entries from installed `*.sublime-commands` resources; filterable by package, command, or caption |
116
+ | `get_commands` | Runnable command ids from loaded command classes, optionally merged with palette metadata |
117
+ | `get_menu_items` | Menu items from `*.sublime-menu` resources; filterable by menu filename, caption, or command |
118
+ | `get_syntaxes` | All syntax definitions available in ST (name + path) |
119
+ | `get_scope_at_cursor` | Full syntax scope string at the cursor position |
120
+ | `get_word_at_cursor` | Word under the cursor and its line/col |
121
+ | `get_bookmarks` | All bookmarked positions in the active file |
122
+ | `get_line_count` | Total number of lines in the active file |
123
+ | `get_encoding` | Character encoding of the active file |
124
+ | `get_setting` | A ST setting by key. `scope='view'` (default) or `'window'` |
125
+ | `get_layout` | Current window layout (groups, cells) and which files are in each group |
126
+
127
+ ### Navigate
128
+
129
+ | Tool | Description |
130
+ |------|-------------|
131
+ | `open_file` | Open a file, optionally jumping to a specific line and column |
132
+ | `goto_line` | Move cursor to line (and optional column) in the active file |
133
+ | `show_panel` | Bring an output panel to the front. Default `name='exec'` for the build panel |
134
+ | `focus_group` | Move focus to a pane group by 0-based index |
135
+
136
+ ### Edit
137
+
138
+ | Tool | Description |
139
+ |------|-------------|
140
+ | `replace_selection` | Replace the current selection(s) with text |
141
+ | `replace_lines` | Replace lines begin..end (inclusive, 1-based) in the active file |
142
+ | `insert_snippet` | Insert at the cursor using ST snippet syntax (`$1` for tab stops, etc.) |
143
+ | `duplicate_line` | Duplicate the current line(s) |
144
+ | `toggle_comment` | Toggle line comment, or block comment if `block=True` |
145
+ | `sort_lines` | Sort selected lines, or all lines if nothing is selected |
146
+ | `select_lines` | Select lines begin..end (1-based, inclusive) |
147
+ | `fold_lines` | Fold (collapse) lines begin..end in the active file |
148
+ | `undo` | Undo the last edit |
149
+ | `redo` | Redo the last undone edit |
150
+ | `run_command` | Run any ST command with optional args. `scope='window'` (default) or `'view'` |
151
+
152
+ ### Search
153
+
154
+ | Tool | Description |
155
+ |------|-------------|
156
+ | `find_in_file` | Find all occurrences of a pattern in the active file. Returns `{line, col, text}` list |
157
+ | `find_in_files` | Search across project folders (or a supplied list). Skips `.git`, `__pycache__`, `node_modules`, `.venv`. Returns `{path, line, match}` list, capped at `max_results` (default 200) |
158
+
159
+ ### File / Project
160
+
161
+ | Tool | Description |
162
+ |------|-------------|
163
+ | `save_file` | Save the active file |
164
+ | `save_all` | Save all open files |
165
+ | `close_file` | Close a file by path, or the active file if path is omitted |
166
+ | `revert_file` | Revert the active file to its last saved state |
167
+ | `add_folder` | Add a folder to the current project (no-op if already present) |
168
+ | `remove_folder` | Remove a folder from the current project by path |
169
+
170
+ ### Syntax / Encoding
171
+
172
+ | Tool | Description |
173
+ |------|-------------|
174
+ | `set_syntax` | Set the syntax of the active file by name (case-insensitive partial match) |
175
+ | `set_encoding` | Set the character encoding of the active file (e.g. `'UTF-8'`, `'Western (Windows 1252)'`) |
176
+
177
+ ### Settings / Window
178
+
179
+ | Tool | Description |
180
+ |------|-------------|
181
+ | `set_setting` | Set a ST setting by key. `scope='view'` (default) or `'window'` |
182
+ | `toggle_sidebar` | Show or hide the sidebar |
183
+ | `set_layout` | Set the window pane layout. Accepts a ST layout dict with `cols`, `rows`, `cells` |
184
+ | `set_status` | Write a message to ST's status bar |
185
+
186
+ ### Build
187
+
188
+ | Tool | Description |
189
+ |------|-------------|
190
+ | `run_build` | Trigger the current build system, or pass `cmd`/`shell_cmd` + `working_dir` for a custom command |
191
+
192
+ ### Terminus Integration
193
+
194
+ [Terminus](https://github.com/randy3k/Terminus) is a popular ST terminal package.
195
+ `send_to_view` is Terminus-aware: when targeting a Terminus tab it uses
196
+ `terminus_send_string` to type text into the terminal session rather than
197
+ inserting into a buffer.
198
+
199
+ | Tool | Description |
200
+ |------|-------------|
201
+ | `send_to_view` | Send a string to any open tab by name. For Terminus tabs, types the text as if the user typed it. Include a trailing `\n` to execute a command |
202
+
203
+ ### Scripting
204
+
205
+ | Tool | Description |
206
+ |------|-------------|
207
+ | `eval_python` | Execute arbitrary Python in ST's main thread. Locals available: `sublime`, `window`, `view`, `print`. Returns captured stdout in `output` |
208
+
209
+ ## Configuration
210
+
211
+ ### Port
212
+
213
+ Default is `9500`. To change it, edit `_PORT` in `sublime_mcp.py` and `BASE` in `mcp_server.py`.
214
+
215
+ ### Timeout
216
+
217
+ The MCP server waits up to 10 seconds for each HTTP response. Edit `TIMEOUT` in
218
+ `mcp_server.py` if you need longer (e.g. for slow `eval_python` calls).
219
+
220
+ ## Security note
221
+
222
+ The HTTP server binds to `127.0.0.1` only and accepts any request without
223
+ authentication. Do not expose port 9500 to a network interface.
224
+
225
+ ## Requirements
226
+
227
+ - Sublime Text 4
228
+ - Python 3.10+ (for the MCP server process)
229
+ - `pip install mcp httpx`
230
+ - Terminus package (optional, required only for `send_to_view` on terminal tabs)
@@ -0,0 +1,212 @@
1
+ # sublime-mcp
2
+
3
+ <!-- mcp-name: io.github.dpc00/sublime-mcp -->
4
+
5
+
6
+ MCP server for Sublime Text 4. Lets Claude Code (or any MCP client) read and
7
+ control a running ST instance via a local HTTP bridge.
8
+
9
+ 58 tools covering reading, navigation, editing, searching, build, Terminus
10
+ integration, settings, layout, and live Python scripting.
11
+
12
+ ## Architecture
13
+
14
+ ```
15
+ Claude Code (MCP client)
16
+ │ stdio / MCP protocol
17
+
18
+ mcp_server.py ← Python process you run outside ST
19
+ │ HTTP 127.0.0.1:9500
20
+
21
+ sublime_mcp.py ← ST plugin, HTTP server on ST's main thread
22
+ │ sublime API
23
+
24
+ Sublime Text 4
25
+ ```
26
+
27
+ | File | Role |
28
+ |------|------|
29
+ | `sublime_mcp.py` | ST plugin — runs an HTTP server inside Sublime Text |
30
+ | `mcp_server.py` | MCP server — wraps the HTTP API for MCP clients |
31
+
32
+ ## Installation
33
+
34
+ ### 1. Install the ST plugin
35
+
36
+ Copy `sublime_mcp.py` to your Sublime Text `Packages/User/` folder:
37
+
38
+ ```
39
+ %APPDATA%\Sublime Text\Packages\User\sublime_mcp.py
40
+ ```
41
+
42
+ ST loads it automatically on start (or via `Tools › Developer › New Plugin…`
43
+ then save over it). You should see:
44
+
45
+ ```
46
+ sublime-mcp: listening on 127.0.0.1:9500
47
+ ```
48
+
49
+ in the ST console (`View › Show Console`).
50
+
51
+ ### 2. Install the MCP server
52
+
53
+ ```
54
+ pip install sublime-mcp
55
+ ```
56
+
57
+ ### 3. Register with Claude Code
58
+
59
+ Add to `~/.claude/settings.json`:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "sublime": {
65
+ "command": "sublime-mcp"
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ Then restart Claude Code. Tools will appear with the `mcp__sublime__` prefix.
72
+
73
+ Then restart Claude Code. Tools will appear with the `mcp__sublime__` prefix.
74
+
75
+ ## Tools
76
+
77
+ ### Read / Introspect
78
+
79
+ | Tool | Description |
80
+ |------|-------------|
81
+ | `get_active_file` | Path, full content, cursor line/col, dirty flag, and syntax name |
82
+ | `get_selection` | Current selection(s): text and begin/end line+col for each |
83
+ | `get_cursor_context` | `lines` lines above and below cursor, with 1-based line numbers prepended |
84
+ | `get_open_files` | All files open in the current window (path, name, is_dirty) |
85
+ | `get_project_folders` | Project root folder paths |
86
+ | `get_file_content` | Full content of any already-open file by path |
87
+ | `get_view_content` | Full content of any open tab by name (partial match). Works for Terminus tabs and nameless views |
88
+ | `get_view_size` | Total character count of any open tab. Use to compute offsets before `get_view_chars` |
89
+ | `get_view_chars` | Text at character offsets begin..end (0-based, end exclusive). Clamps to buffer bounds |
90
+ | `get_view_phantoms` | Phantom HTML and extracted plain text from a named view; filters by phantom key |
91
+ | `get_output_panel` | Text content of a named output panel. Omit name for the active panel; `name='exec'` for build output |
92
+ | `get_active_panel` | Active panel id and, if it is an output panel, its content |
93
+ | `get_symbols` | All symbols (functions, classes, etc.) in the active file with line numbers |
94
+ | `lookup_symbol` | Find where a symbol is defined across all open files |
95
+ | `get_project_data` | Raw `.sublime-project` JSON for the current project |
96
+ | `get_variables` | ST build variables: `$file`, `$project_path`, `$platform`, etc. |
97
+ | `get_command_palette` | Command Palette entries from installed `*.sublime-commands` resources; filterable by package, command, or caption |
98
+ | `get_commands` | Runnable command ids from loaded command classes, optionally merged with palette metadata |
99
+ | `get_menu_items` | Menu items from `*.sublime-menu` resources; filterable by menu filename, caption, or command |
100
+ | `get_syntaxes` | All syntax definitions available in ST (name + path) |
101
+ | `get_scope_at_cursor` | Full syntax scope string at the cursor position |
102
+ | `get_word_at_cursor` | Word under the cursor and its line/col |
103
+ | `get_bookmarks` | All bookmarked positions in the active file |
104
+ | `get_line_count` | Total number of lines in the active file |
105
+ | `get_encoding` | Character encoding of the active file |
106
+ | `get_setting` | A ST setting by key. `scope='view'` (default) or `'window'` |
107
+ | `get_layout` | Current window layout (groups, cells) and which files are in each group |
108
+
109
+ ### Navigate
110
+
111
+ | Tool | Description |
112
+ |------|-------------|
113
+ | `open_file` | Open a file, optionally jumping to a specific line and column |
114
+ | `goto_line` | Move cursor to line (and optional column) in the active file |
115
+ | `show_panel` | Bring an output panel to the front. Default `name='exec'` for the build panel |
116
+ | `focus_group` | Move focus to a pane group by 0-based index |
117
+
118
+ ### Edit
119
+
120
+ | Tool | Description |
121
+ |------|-------------|
122
+ | `replace_selection` | Replace the current selection(s) with text |
123
+ | `replace_lines` | Replace lines begin..end (inclusive, 1-based) in the active file |
124
+ | `insert_snippet` | Insert at the cursor using ST snippet syntax (`$1` for tab stops, etc.) |
125
+ | `duplicate_line` | Duplicate the current line(s) |
126
+ | `toggle_comment` | Toggle line comment, or block comment if `block=True` |
127
+ | `sort_lines` | Sort selected lines, or all lines if nothing is selected |
128
+ | `select_lines` | Select lines begin..end (1-based, inclusive) |
129
+ | `fold_lines` | Fold (collapse) lines begin..end in the active file |
130
+ | `undo` | Undo the last edit |
131
+ | `redo` | Redo the last undone edit |
132
+ | `run_command` | Run any ST command with optional args. `scope='window'` (default) or `'view'` |
133
+
134
+ ### Search
135
+
136
+ | Tool | Description |
137
+ |------|-------------|
138
+ | `find_in_file` | Find all occurrences of a pattern in the active file. Returns `{line, col, text}` list |
139
+ | `find_in_files` | Search across project folders (or a supplied list). Skips `.git`, `__pycache__`, `node_modules`, `.venv`. Returns `{path, line, match}` list, capped at `max_results` (default 200) |
140
+
141
+ ### File / Project
142
+
143
+ | Tool | Description |
144
+ |------|-------------|
145
+ | `save_file` | Save the active file |
146
+ | `save_all` | Save all open files |
147
+ | `close_file` | Close a file by path, or the active file if path is omitted |
148
+ | `revert_file` | Revert the active file to its last saved state |
149
+ | `add_folder` | Add a folder to the current project (no-op if already present) |
150
+ | `remove_folder` | Remove a folder from the current project by path |
151
+
152
+ ### Syntax / Encoding
153
+
154
+ | Tool | Description |
155
+ |------|-------------|
156
+ | `set_syntax` | Set the syntax of the active file by name (case-insensitive partial match) |
157
+ | `set_encoding` | Set the character encoding of the active file (e.g. `'UTF-8'`, `'Western (Windows 1252)'`) |
158
+
159
+ ### Settings / Window
160
+
161
+ | Tool | Description |
162
+ |------|-------------|
163
+ | `set_setting` | Set a ST setting by key. `scope='view'` (default) or `'window'` |
164
+ | `toggle_sidebar` | Show or hide the sidebar |
165
+ | `set_layout` | Set the window pane layout. Accepts a ST layout dict with `cols`, `rows`, `cells` |
166
+ | `set_status` | Write a message to ST's status bar |
167
+
168
+ ### Build
169
+
170
+ | Tool | Description |
171
+ |------|-------------|
172
+ | `run_build` | Trigger the current build system, or pass `cmd`/`shell_cmd` + `working_dir` for a custom command |
173
+
174
+ ### Terminus Integration
175
+
176
+ [Terminus](https://github.com/randy3k/Terminus) is a popular ST terminal package.
177
+ `send_to_view` is Terminus-aware: when targeting a Terminus tab it uses
178
+ `terminus_send_string` to type text into the terminal session rather than
179
+ inserting into a buffer.
180
+
181
+ | Tool | Description |
182
+ |------|-------------|
183
+ | `send_to_view` | Send a string to any open tab by name. For Terminus tabs, types the text as if the user typed it. Include a trailing `\n` to execute a command |
184
+
185
+ ### Scripting
186
+
187
+ | Tool | Description |
188
+ |------|-------------|
189
+ | `eval_python` | Execute arbitrary Python in ST's main thread. Locals available: `sublime`, `window`, `view`, `print`. Returns captured stdout in `output` |
190
+
191
+ ## Configuration
192
+
193
+ ### Port
194
+
195
+ Default is `9500`. To change it, edit `_PORT` in `sublime_mcp.py` and `BASE` in `mcp_server.py`.
196
+
197
+ ### Timeout
198
+
199
+ The MCP server waits up to 10 seconds for each HTTP response. Edit `TIMEOUT` in
200
+ `mcp_server.py` if you need longer (e.g. for slow `eval_python` calls).
201
+
202
+ ## Security note
203
+
204
+ The HTTP server binds to `127.0.0.1` only and accepts any request without
205
+ authentication. Do not expose port 9500 to a network interface.
206
+
207
+ ## Requirements
208
+
209
+ - Sublime Text 4
210
+ - Python 3.10+ (for the MCP server process)
211
+ - `pip install mcp httpx`
212
+ - Terminus package (optional, required only for `send_to_view` on terminal tabs)
@@ -0,0 +1,495 @@
1
+ """sublime-mcp — MCP server.
2
+
3
+ Wraps the HTTP API exposed by sublime_mcp.py (the ST plugin) and
4
+ presents it as MCP tools to Claude Code (or any MCP client).
5
+
6
+ Requirements: pip install mcp httpx
7
+ Run: python mcp_server.py
8
+ Register: add to ~/.claude/settings.json mcpServers
9
+ """
10
+ import httpx
11
+ from mcp.server.fastmcp import FastMCP
12
+
13
+ BASE = "http://127.0.0.1:9500"
14
+ TIMEOUT = 10.0
15
+
16
+ mcp = FastMCP("sublime-mcp")
17
+
18
+
19
+ def _get(endpoint: str, **params) -> dict:
20
+ r = httpx.get(f"{BASE}{endpoint}", params=params, timeout=TIMEOUT)
21
+ r.raise_for_status()
22
+ return r.json()
23
+
24
+
25
+ def _post(endpoint: str, **body) -> dict:
26
+ r = httpx.post(f"{BASE}{endpoint}", json=body, timeout=TIMEOUT)
27
+ r.raise_for_status()
28
+ return r.json()
29
+
30
+
31
+ # ── read ──────────────────────────────────────────────────────────────────────
32
+
33
+
34
+ @mcp.tool()
35
+ def get_active_file() -> dict:
36
+ """Return the active file's path, full content, cursor line/col, dirty flag, and syntax name."""
37
+ return _get("/active_file")
38
+
39
+
40
+ @mcp.tool()
41
+ def get_selection() -> dict:
42
+ """Return the current selection(s): text and begin/end line+col for each."""
43
+ return _get("/selection")
44
+
45
+
46
+ @mcp.tool()
47
+ def get_cursor_context(lines: int = 10) -> dict:
48
+ """Return `lines` lines above and below the cursor with 1-based line numbers prepended."""
49
+ return _get("/cursor_context", lines=lines)
50
+
51
+
52
+ @mcp.tool()
53
+ def get_open_files() -> dict:
54
+ """List all files open in the current window (path, name, is_dirty)."""
55
+ return _get("/open_files")
56
+
57
+
58
+ @mcp.tool()
59
+ def get_project_folders() -> dict:
60
+ """Return the project's root folder paths."""
61
+ return _get("/project_folders")
62
+
63
+
64
+ @mcp.tool()
65
+ def get_file_content(path: str) -> dict:
66
+ """Return the full content of an already-open file by its path."""
67
+ return _get("/file_content", path=path)
68
+
69
+
70
+ @mcp.tool()
71
+ def get_view_content(name: str = "") -> dict:
72
+ """Return the full content of any open tab by name (partial match, case-insensitive).
73
+ Works for Terminus tabs and other nameless views that have no file path.
74
+ Omit name to read the active view."""
75
+ return _get("/view_content", name=name)
76
+
77
+
78
+ @mcp.tool()
79
+ def get_view_size(name: str = "") -> dict:
80
+ """Return the total character count of any open tab by name (partial match, case-insensitive).
81
+ Use before get_view_chars to compute offsets — e.g. begin=size-5000, end=size for the tail.
82
+ Omit name for the active view."""
83
+ return _get("/view_size", name=name)
84
+
85
+
86
+ @mcp.tool()
87
+ def get_view_chars(begin: int, end: int, name: str = "") -> dict:
88
+ """Return text at character offsets begin..end (0-based, end exclusive) from any open tab.
89
+ Works for Terminus tabs and any other view. Clamps to buffer bounds automatically.
90
+ Use get_view_size first, then e.g. begin=size-5000, end=size to read the last 5000 chars.
91
+ Omit name for the active view."""
92
+ return _get("/view_chars", name=name, begin=begin, end=end)
93
+
94
+
95
+ @mcp.tool()
96
+ def get_view_phantoms(name: str = "", key: str = "") -> dict:
97
+ """Return phantom HTML and extracted text from a view by name.
98
+ If key is omitted, defaults to the common 'pybackup' phantom key."""
99
+ return _get("/view_phantoms", name=name, key=key)
100
+
101
+
102
+ @mcp.tool()
103
+ def send_to_view(text: str, name: str = "") -> dict:
104
+ """Send a string to any open tab by name (partial match, case-insensitive).
105
+ For Terminus tabs this types the text into the terminal as if the user typed it.
106
+ Include a trailing newline (\\n) to execute a command.
107
+ Omit name to target the active view."""
108
+ return _post("/send_to_view", text=text, name=name)
109
+
110
+
111
+ @mcp.tool()
112
+ def get_output_panel(name: str = "") -> dict:
113
+ """Return the text content of an output panel.
114
+ If name is omitted, read the active output panel. Use name='exec' for build output.
115
+ """
116
+ return _get("/output_panel", name=name)
117
+
118
+
119
+ @mcp.tool()
120
+ def get_symbols() -> dict:
121
+ """Return all symbols (functions, classes, etc.) in the active file with line numbers."""
122
+ return _get("/symbols")
123
+
124
+
125
+ @mcp.tool()
126
+ def lookup_symbol(symbol: str) -> dict:
127
+ """Find where a symbol is defined across all open files."""
128
+ return _get("/lookup_symbol", symbol=symbol)
129
+
130
+
131
+ @mcp.tool()
132
+ def get_project_data() -> dict:
133
+ """Return the raw .sublime-project JSON data for the current project."""
134
+ return _get("/project_data")
135
+
136
+
137
+ @mcp.tool()
138
+ def add_folder(path: str) -> dict:
139
+ """Add a folder to the current project."""
140
+ data = _get("/project_data").get("project_data") or {}
141
+ folders = data.get("folders", [])
142
+ if not any(f.get("path") == path for f in folders):
143
+ folders.append({"path": path})
144
+ data["folders"] = folders
145
+ return _post("/set_project_data", data=data)
146
+ return {"ok": True, "note": "already present"}
147
+
148
+
149
+ @mcp.tool()
150
+ def remove_folder(path: str) -> dict:
151
+ """Remove a folder from the current project by path."""
152
+ data = _get("/project_data").get("project_data") or {}
153
+ folders = data.get("folders", [])
154
+ new_folders = [f for f in folders if f.get("path") != path]
155
+ if len(new_folders) == len(folders):
156
+ return {"ok": False, "note": "folder not found"}
157
+ data["folders"] = new_folders
158
+ return _post("/set_project_data", data=data)
159
+
160
+
161
+ @mcp.tool()
162
+ def get_variables() -> dict:
163
+ """Return Sublime Text's build variables: $file, $project_path, $platform, etc."""
164
+ return _get("/variables")
165
+
166
+
167
+ # ── navigate ──────────────────────────────────────────────────────────────────
168
+
169
+
170
+ @mcp.tool()
171
+ def open_file(path: str, line: int = 0, col: int = 0) -> dict:
172
+ """Open a file in Sublime Text, optionally jumping to a specific line and column."""
173
+ return _post("/open_file", path=path, line=line, col=col)
174
+
175
+
176
+ @mcp.tool()
177
+ def goto_line(line: int, col: int = 1) -> dict:
178
+ """Move the cursor to a line (and optional column) in the active file."""
179
+ return _post("/goto_line", line=line, col=col)
180
+
181
+
182
+ @mcp.tool()
183
+ def show_panel(name: str = "exec") -> dict:
184
+ """Bring an output panel to the front. Use name='exec' for the build panel."""
185
+ return _post("/show_panel", name=name)
186
+
187
+
188
+ # ── edit ──────────────────────────────────────────────────────────────────────
189
+
190
+
191
+ @mcp.tool()
192
+ def replace_selection(text: str) -> dict:
193
+ """Replace the current selection(s) with text."""
194
+ return _post("/replace_selection", text=text)
195
+
196
+
197
+ @mcp.tool()
198
+ def replace_lines(begin: int, end: int, text: str, path: str = "") -> dict:
199
+ """Replace lines begin through end (inclusive, 1-based) in the active file with text.
200
+ Pass path to target a specific open file regardless of which tab is focused."""
201
+ return _post("/replace_lines", begin=begin, end=end, text=text, path=path)
202
+
203
+
204
+ @mcp.tool()
205
+ def run_command(command: str, args: dict = None, scope: str = "window") -> dict:
206
+ """Run any Sublime Text command. scope='window' (default) or 'view'."""
207
+ return _post("/run_command", command=command, args=args or {}, scope=scope)
208
+
209
+
210
+ # ── build ─────────────────────────────────────────────────────────────────────
211
+
212
+
213
+ @mcp.tool()
214
+ def run_build(cmd: list = None, shell_cmd: str = None, working_dir: str = "") -> dict:
215
+ """Trigger the current build system, or pass cmd/shell_cmd to run a specific command."""
216
+ body = {}
217
+ if cmd:
218
+ body["cmd"] = cmd
219
+ if shell_cmd:
220
+ body["shell_cmd"] = shell_cmd
221
+ if working_dir:
222
+ body["working_dir"] = working_dir
223
+ return _post("/run_build", **body)
224
+
225
+
226
+ # ── misc ──────────────────────────────────────────────────────────────────────
227
+
228
+
229
+ @mcp.tool()
230
+ def set_status(value: str, key: str = "sublime_mcp") -> dict:
231
+ """Write a message to Sublime Text's status bar."""
232
+ return _post("/set_status", key=key, value=value)
233
+
234
+
235
+ # ── file ops ──────────────────────────────────────────────────────────────────
236
+
237
+
238
+ @mcp.tool()
239
+ def save_file(path: str = "") -> dict:
240
+ """Save a file. Pass path to save a specific open file; omit path to save the active file."""
241
+ return _post("/save_file", path=path) if path else _post("/save_file")
242
+
243
+
244
+ @mcp.tool()
245
+ def save_all() -> dict:
246
+ """Save all open files."""
247
+ return _post("/save_all")
248
+
249
+
250
+ @mcp.tool()
251
+ def close_file(path: str = "") -> dict:
252
+ """Close a file by path, or close the active file if path is omitted."""
253
+ return _post("/close_file", path=path)
254
+
255
+
256
+ @mcp.tool()
257
+ def revert_file() -> dict:
258
+ """Revert the active file to its last saved state, discarding unsaved changes."""
259
+ return _post("/revert_file")
260
+
261
+
262
+ # ── edit ops ──────────────────────────────────────────────────────────────────
263
+
264
+
265
+ @mcp.tool()
266
+ def undo() -> dict:
267
+ """Undo the last edit in the active file."""
268
+ return _post("/undo")
269
+
270
+
271
+ @mcp.tool()
272
+ def redo() -> dict:
273
+ """Redo the last undone edit in the active file."""
274
+ return _post("/redo")
275
+
276
+
277
+ @mcp.tool()
278
+ def duplicate_line() -> dict:
279
+ """Duplicate the current line(s) in the active file."""
280
+ return _post("/duplicate_line")
281
+
282
+
283
+ @mcp.tool()
284
+ def toggle_comment(block: bool = False) -> dict:
285
+ """Toggle line comment (or block comment if block=True) on the current selection."""
286
+ return _post("/toggle_comment", block=block)
287
+
288
+
289
+ @mcp.tool()
290
+ def sort_lines(case_sensitive: bool = False) -> dict:
291
+ """Sort the selected lines (or all lines if nothing is selected)."""
292
+ return _post("/sort_lines", case_sensitive=case_sensitive)
293
+
294
+
295
+ @mcp.tool()
296
+ def select_lines(begin: int, end: int = 0) -> dict:
297
+ """Select lines begin through end (1-based, inclusive). end defaults to begin."""
298
+ return _post("/select_lines", begin=begin, end=end or begin)
299
+
300
+
301
+ @mcp.tool()
302
+ def fold_lines(begin: int, end: int) -> dict:
303
+ """Fold (collapse) lines begin through end (1-based) in the active file."""
304
+ return _post("/fold_lines", begin=begin, end=end)
305
+
306
+
307
+ @mcp.tool()
308
+ def insert_snippet(contents: str) -> dict:
309
+ """Insert a snippet at the cursor using Sublime Text's snippet syntax (e.g. $1 for tab stops)."""
310
+ return _post("/insert_snippet", contents=contents)
311
+
312
+
313
+ # ── search ────────────────────────────────────────────────────────────────────
314
+
315
+
316
+ @mcp.tool()
317
+ def find_in_file(
318
+ pattern: str, case_sensitive: bool = False, regex: bool = False
319
+ ) -> dict:
320
+ """Find all occurrences of pattern in the active file. Returns list of {line, col, text}."""
321
+ return _post(
322
+ "/find_in_file", pattern=pattern, case_sensitive=case_sensitive, regex=regex
323
+ )
324
+
325
+
326
+ @mcp.tool()
327
+ def find_in_files(
328
+ pattern: str,
329
+ folders: list = None,
330
+ case_sensitive: bool = False,
331
+ regex: bool = False,
332
+ max_results: int = 200,
333
+ ) -> dict:
334
+ """Search for pattern across project folders (or the supplied folder list).
335
+ Skips .git, __pycache__, node_modules, .venv. Returns list of {path, line, match}.
336
+ """
337
+ body = dict(
338
+ pattern=pattern,
339
+ case_sensitive=case_sensitive,
340
+ regex=regex,
341
+ max_results=max_results,
342
+ )
343
+ if folders:
344
+ body["folders"] = folders
345
+ return _post("/find_in_files", **body)
346
+
347
+
348
+ # ── syntax / encoding ─────────────────────────────────────────────────────────
349
+
350
+
351
+ @mcp.tool()
352
+ def get_syntaxes() -> dict:
353
+ """List all syntax definitions available in Sublime Text (name + path)."""
354
+ return _get("/syntaxes")
355
+
356
+
357
+ @mcp.tool()
358
+ def get_command_palette(
359
+ package: str = "", command: str = "", caption: str = ""
360
+ ) -> dict:
361
+ """List Command Palette entries from installed *.sublime-commands resources.
362
+ Optional filters: package, command id, or caption substring."""
363
+ return _get("/command_palette", package=package, command=command, caption=caption)
364
+
365
+
366
+ @mcp.tool()
367
+ def get_commands(
368
+ package: str = "", command: str = "", include_palette: bool = True
369
+ ) -> dict:
370
+ """List runnable Sublime command ids from loaded command classes, optionally enriched
371
+ with matching Command Palette entries from installed packages."""
372
+ return _get(
373
+ "/commands",
374
+ package=package,
375
+ command=command,
376
+ include_palette=str(include_palette).lower(),
377
+ )
378
+
379
+
380
+ @mcp.tool()
381
+ def get_menu_items(menu: str = "", caption: str = "", command: str = "") -> dict:
382
+ """List installed menu items from *.sublime-menu resources.
383
+ Optional filters: menu filename, caption substring, or command id substring."""
384
+ return _get("/menu_items", menu=menu, caption=caption, command=command)
385
+
386
+
387
+ @mcp.tool()
388
+ def get_active_panel() -> dict:
389
+ """Return the active panel id and, if it is an output panel, its content."""
390
+ return _get("/active_panel")
391
+
392
+
393
+ @mcp.tool()
394
+ def set_syntax(name: str) -> dict:
395
+ """Set the syntax of the active file by name (case-insensitive partial match is fine)."""
396
+ return _post("/set_syntax", name=name)
397
+
398
+
399
+ @mcp.tool()
400
+ def get_encoding() -> dict:
401
+ """Return the character encoding of the active file."""
402
+ return _get("/encoding")
403
+
404
+
405
+ @mcp.tool()
406
+ def set_encoding(encoding: str) -> dict:
407
+ """Set the character encoding of the active file (e.g. 'UTF-8', 'Western (Windows 1252)')."""
408
+ return _post("/set_encoding", encoding=encoding)
409
+
410
+
411
+ # ── cursor / scope ────────────────────────────────────────────────────────────
412
+
413
+
414
+ @mcp.tool()
415
+ def get_scope_at_cursor() -> dict:
416
+ """Return the full syntax scope string at the cursor position."""
417
+ return _get("/scope_at_cursor")
418
+
419
+
420
+ @mcp.tool()
421
+ def get_word_at_cursor() -> dict:
422
+ """Return the word under the cursor and its line/col."""
423
+ return _get("/word_at_cursor")
424
+
425
+
426
+ @mcp.tool()
427
+ def get_bookmarks() -> dict:
428
+ """Return all bookmarked positions in the active file."""
429
+ return _get("/bookmarks")
430
+
431
+
432
+ @mcp.tool()
433
+ def get_line_count() -> dict:
434
+ """Return the total number of lines in the active file."""
435
+ return _get("/line_count")
436
+
437
+
438
+ # ── settings ──────────────────────────────────────────────────────────────────
439
+
440
+
441
+ @mcp.tool()
442
+ def get_setting(key: str, scope: str = "view") -> dict:
443
+ """Get a Sublime Text setting by key. scope='view' (default) or 'window'."""
444
+ return _post("/get_setting", key=key, scope=scope)
445
+
446
+
447
+ @mcp.tool()
448
+ def set_setting(key: str, value, scope: str = "view") -> dict:
449
+ """Set a Sublime Text setting by key. scope='view' (default) or 'window'."""
450
+ return _post("/set_setting", key=key, value=value, scope=scope)
451
+
452
+
453
+ # ── window / layout ───────────────────────────────────────────────────────────
454
+
455
+
456
+ @mcp.tool()
457
+ def toggle_sidebar() -> dict:
458
+ """Show or hide the Sublime Text sidebar."""
459
+ return _post("/toggle_sidebar")
460
+
461
+
462
+ @mcp.tool()
463
+ def get_layout() -> dict:
464
+ """Return the current window layout (groups, cells) and which files are in each group."""
465
+ return _get("/layout")
466
+
467
+
468
+ @mcp.tool()
469
+ def focus_group(group: int) -> dict:
470
+ """Move focus to a pane group by 0-based index."""
471
+ return _post("/focus_group", group=group)
472
+
473
+
474
+ @mcp.tool()
475
+ def set_layout(layout: dict) -> dict:
476
+ """Set the window pane layout. layout must be a ST layout dict with cols, rows, cells keys."""
477
+ return _post("/set_layout", layout=layout)
478
+
479
+
480
+ # ── scripting ─────────────────────────────────────────────────────────────────
481
+
482
+
483
+ @mcp.tool()
484
+ def eval_python(code: str) -> dict:
485
+ """Execute arbitrary Python in Sublime Text's main thread.
486
+ Locals: sublime, window, view, print. Returns captured stdout in 'output'."""
487
+ return _post("/eval_python", code=code)
488
+
489
+
490
+ def main():
491
+ mcp.run()
492
+
493
+
494
+ if __name__ == "__main__":
495
+ main()
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sublime-mcp"
7
+ version = "1.0.0"
8
+ description = "MCP server for Sublime Text 4 — lets Claude Code read and control a running ST instance"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ dependencies = ["mcp", "httpx"]
13
+ keywords = ["mcp", "sublime-text", "claude", "ai", "editor"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Text Editors",
19
+ ]
20
+
21
+ [[project.authors]]
22
+ name = "Donald Chitester"
23
+ email = "donaldchitester@gmail.com"
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/dpc00/sublime-mcp"
27
+ Repository = "https://github.com/dpc00/sublime-mcp"
28
+
29
+ [project.scripts]
30
+ sublime-mcp = "mcp_server:main"
31
+
32
+ [tool.setuptools]
33
+ py-modules = ["mcp_server"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,230 @@
1
+ Metadata-Version: 2.4
2
+ Name: sublime-mcp
3
+ Version: 1.0.0
4
+ Summary: MCP server for Sublime Text 4 — lets Claude Code read and control a running ST instance
5
+ Author-email: Donald Chitester <donaldchitester@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dpc00/sublime-mcp
8
+ Project-URL: Repository, https://github.com/dpc00/sublime-mcp
9
+ Keywords: mcp,sublime-text,claude,ai,editor
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Text Editors
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: mcp
17
+ Requires-Dist: httpx
18
+
19
+ # sublime-mcp
20
+
21
+ <!-- mcp-name: io.github.dpc00/sublime-mcp -->
22
+
23
+
24
+ MCP server for Sublime Text 4. Lets Claude Code (or any MCP client) read and
25
+ control a running ST instance via a local HTTP bridge.
26
+
27
+ 58 tools covering reading, navigation, editing, searching, build, Terminus
28
+ integration, settings, layout, and live Python scripting.
29
+
30
+ ## Architecture
31
+
32
+ ```
33
+ Claude Code (MCP client)
34
+ │ stdio / MCP protocol
35
+
36
+ mcp_server.py ← Python process you run outside ST
37
+ │ HTTP 127.0.0.1:9500
38
+
39
+ sublime_mcp.py ← ST plugin, HTTP server on ST's main thread
40
+ │ sublime API
41
+
42
+ Sublime Text 4
43
+ ```
44
+
45
+ | File | Role |
46
+ |------|------|
47
+ | `sublime_mcp.py` | ST plugin — runs an HTTP server inside Sublime Text |
48
+ | `mcp_server.py` | MCP server — wraps the HTTP API for MCP clients |
49
+
50
+ ## Installation
51
+
52
+ ### 1. Install the ST plugin
53
+
54
+ Copy `sublime_mcp.py` to your Sublime Text `Packages/User/` folder:
55
+
56
+ ```
57
+ %APPDATA%\Sublime Text\Packages\User\sublime_mcp.py
58
+ ```
59
+
60
+ ST loads it automatically on start (or via `Tools › Developer › New Plugin…`
61
+ then save over it). You should see:
62
+
63
+ ```
64
+ sublime-mcp: listening on 127.0.0.1:9500
65
+ ```
66
+
67
+ in the ST console (`View › Show Console`).
68
+
69
+ ### 2. Install the MCP server
70
+
71
+ ```
72
+ pip install sublime-mcp
73
+ ```
74
+
75
+ ### 3. Register with Claude Code
76
+
77
+ Add to `~/.claude/settings.json`:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "sublime": {
83
+ "command": "sublime-mcp"
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ Then restart Claude Code. Tools will appear with the `mcp__sublime__` prefix.
90
+
91
+ Then restart Claude Code. Tools will appear with the `mcp__sublime__` prefix.
92
+
93
+ ## Tools
94
+
95
+ ### Read / Introspect
96
+
97
+ | Tool | Description |
98
+ |------|-------------|
99
+ | `get_active_file` | Path, full content, cursor line/col, dirty flag, and syntax name |
100
+ | `get_selection` | Current selection(s): text and begin/end line+col for each |
101
+ | `get_cursor_context` | `lines` lines above and below cursor, with 1-based line numbers prepended |
102
+ | `get_open_files` | All files open in the current window (path, name, is_dirty) |
103
+ | `get_project_folders` | Project root folder paths |
104
+ | `get_file_content` | Full content of any already-open file by path |
105
+ | `get_view_content` | Full content of any open tab by name (partial match). Works for Terminus tabs and nameless views |
106
+ | `get_view_size` | Total character count of any open tab. Use to compute offsets before `get_view_chars` |
107
+ | `get_view_chars` | Text at character offsets begin..end (0-based, end exclusive). Clamps to buffer bounds |
108
+ | `get_view_phantoms` | Phantom HTML and extracted plain text from a named view; filters by phantom key |
109
+ | `get_output_panel` | Text content of a named output panel. Omit name for the active panel; `name='exec'` for build output |
110
+ | `get_active_panel` | Active panel id and, if it is an output panel, its content |
111
+ | `get_symbols` | All symbols (functions, classes, etc.) in the active file with line numbers |
112
+ | `lookup_symbol` | Find where a symbol is defined across all open files |
113
+ | `get_project_data` | Raw `.sublime-project` JSON for the current project |
114
+ | `get_variables` | ST build variables: `$file`, `$project_path`, `$platform`, etc. |
115
+ | `get_command_palette` | Command Palette entries from installed `*.sublime-commands` resources; filterable by package, command, or caption |
116
+ | `get_commands` | Runnable command ids from loaded command classes, optionally merged with palette metadata |
117
+ | `get_menu_items` | Menu items from `*.sublime-menu` resources; filterable by menu filename, caption, or command |
118
+ | `get_syntaxes` | All syntax definitions available in ST (name + path) |
119
+ | `get_scope_at_cursor` | Full syntax scope string at the cursor position |
120
+ | `get_word_at_cursor` | Word under the cursor and its line/col |
121
+ | `get_bookmarks` | All bookmarked positions in the active file |
122
+ | `get_line_count` | Total number of lines in the active file |
123
+ | `get_encoding` | Character encoding of the active file |
124
+ | `get_setting` | A ST setting by key. `scope='view'` (default) or `'window'` |
125
+ | `get_layout` | Current window layout (groups, cells) and which files are in each group |
126
+
127
+ ### Navigate
128
+
129
+ | Tool | Description |
130
+ |------|-------------|
131
+ | `open_file` | Open a file, optionally jumping to a specific line and column |
132
+ | `goto_line` | Move cursor to line (and optional column) in the active file |
133
+ | `show_panel` | Bring an output panel to the front. Default `name='exec'` for the build panel |
134
+ | `focus_group` | Move focus to a pane group by 0-based index |
135
+
136
+ ### Edit
137
+
138
+ | Tool | Description |
139
+ |------|-------------|
140
+ | `replace_selection` | Replace the current selection(s) with text |
141
+ | `replace_lines` | Replace lines begin..end (inclusive, 1-based) in the active file |
142
+ | `insert_snippet` | Insert at the cursor using ST snippet syntax (`$1` for tab stops, etc.) |
143
+ | `duplicate_line` | Duplicate the current line(s) |
144
+ | `toggle_comment` | Toggle line comment, or block comment if `block=True` |
145
+ | `sort_lines` | Sort selected lines, or all lines if nothing is selected |
146
+ | `select_lines` | Select lines begin..end (1-based, inclusive) |
147
+ | `fold_lines` | Fold (collapse) lines begin..end in the active file |
148
+ | `undo` | Undo the last edit |
149
+ | `redo` | Redo the last undone edit |
150
+ | `run_command` | Run any ST command with optional args. `scope='window'` (default) or `'view'` |
151
+
152
+ ### Search
153
+
154
+ | Tool | Description |
155
+ |------|-------------|
156
+ | `find_in_file` | Find all occurrences of a pattern in the active file. Returns `{line, col, text}` list |
157
+ | `find_in_files` | Search across project folders (or a supplied list). Skips `.git`, `__pycache__`, `node_modules`, `.venv`. Returns `{path, line, match}` list, capped at `max_results` (default 200) |
158
+
159
+ ### File / Project
160
+
161
+ | Tool | Description |
162
+ |------|-------------|
163
+ | `save_file` | Save the active file |
164
+ | `save_all` | Save all open files |
165
+ | `close_file` | Close a file by path, or the active file if path is omitted |
166
+ | `revert_file` | Revert the active file to its last saved state |
167
+ | `add_folder` | Add a folder to the current project (no-op if already present) |
168
+ | `remove_folder` | Remove a folder from the current project by path |
169
+
170
+ ### Syntax / Encoding
171
+
172
+ | Tool | Description |
173
+ |------|-------------|
174
+ | `set_syntax` | Set the syntax of the active file by name (case-insensitive partial match) |
175
+ | `set_encoding` | Set the character encoding of the active file (e.g. `'UTF-8'`, `'Western (Windows 1252)'`) |
176
+
177
+ ### Settings / Window
178
+
179
+ | Tool | Description |
180
+ |------|-------------|
181
+ | `set_setting` | Set a ST setting by key. `scope='view'` (default) or `'window'` |
182
+ | `toggle_sidebar` | Show or hide the sidebar |
183
+ | `set_layout` | Set the window pane layout. Accepts a ST layout dict with `cols`, `rows`, `cells` |
184
+ | `set_status` | Write a message to ST's status bar |
185
+
186
+ ### Build
187
+
188
+ | Tool | Description |
189
+ |------|-------------|
190
+ | `run_build` | Trigger the current build system, or pass `cmd`/`shell_cmd` + `working_dir` for a custom command |
191
+
192
+ ### Terminus Integration
193
+
194
+ [Terminus](https://github.com/randy3k/Terminus) is a popular ST terminal package.
195
+ `send_to_view` is Terminus-aware: when targeting a Terminus tab it uses
196
+ `terminus_send_string` to type text into the terminal session rather than
197
+ inserting into a buffer.
198
+
199
+ | Tool | Description |
200
+ |------|-------------|
201
+ | `send_to_view` | Send a string to any open tab by name. For Terminus tabs, types the text as if the user typed it. Include a trailing `\n` to execute a command |
202
+
203
+ ### Scripting
204
+
205
+ | Tool | Description |
206
+ |------|-------------|
207
+ | `eval_python` | Execute arbitrary Python in ST's main thread. Locals available: `sublime`, `window`, `view`, `print`. Returns captured stdout in `output` |
208
+
209
+ ## Configuration
210
+
211
+ ### Port
212
+
213
+ Default is `9500`. To change it, edit `_PORT` in `sublime_mcp.py` and `BASE` in `mcp_server.py`.
214
+
215
+ ### Timeout
216
+
217
+ The MCP server waits up to 10 seconds for each HTTP response. Edit `TIMEOUT` in
218
+ `mcp_server.py` if you need longer (e.g. for slow `eval_python` calls).
219
+
220
+ ## Security note
221
+
222
+ The HTTP server binds to `127.0.0.1` only and accepts any request without
223
+ authentication. Do not expose port 9500 to a network interface.
224
+
225
+ ## Requirements
226
+
227
+ - Sublime Text 4
228
+ - Python 3.10+ (for the MCP server process)
229
+ - `pip install mcp httpx`
230
+ - Terminus package (optional, required only for `send_to_view` on terminal tabs)
@@ -0,0 +1,9 @@
1
+ README.md
2
+ mcp_server.py
3
+ pyproject.toml
4
+ sublime_mcp.egg-info/PKG-INFO
5
+ sublime_mcp.egg-info/SOURCES.txt
6
+ sublime_mcp.egg-info/dependency_links.txt
7
+ sublime_mcp.egg-info/entry_points.txt
8
+ sublime_mcp.egg-info/requires.txt
9
+ sublime_mcp.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sublime-mcp = mcp_server:main
@@ -0,0 +1,2 @@
1
+ mcp
2
+ httpx
@@ -0,0 +1 @@
1
+ mcp_server