Importing VS Code extensions
Before you can reference a VS Code extension in a profile or a run, it must be imported into your Scope deployment. Importing registers the extension's marketplace ID and metadata so that Scope can resolve and install it when a run starts.
Extensions are only used by the VS Code Copilot coding CLI and Claude Code CLI agents do not support extensions — see Choosing a coding agent.
Extension IDs
Section titled “Extension IDs”Every VS Code extension has a marketplace ID in the form
{publisher}.{name}:
ms-python.pythonesbenp.prettier-vscodedbaeumer.vscode-eslintThis ID is shown on the extension's Visual Studio Marketplace page and in the VS Code extension panel. Scope uses it as the primary key for imported extensions.
Importing from the Portal
Section titled “Importing from the Portal”- Open Extensions in the navigation.
- Use the search box to find the extension you want. The search queries both your already-imported extensions and the VS Code Marketplace in one go.
- Results from the Marketplace that are not yet imported show an Import button. Click it.
- The extension is registered and immediately available for use in profiles.
The Portal also lets you pick a specific version when importing. By default the latest stable version is shown.
Importing from the REST API
Section titled “Importing from the REST API”Use POST /api/v1/extensions to import an extension
programmatically:
curl --request POST \ --url https://your-scope.example.com/api/v1/extensions \ --header 'Content-Type: application/json' \ --data '{ "_id": "ms-python.python", "publisher": "ms-python", "name": "Python", "description": "Python language support with Pylance, debugging, and more", "origin": "marketplace"}'const url = 'https://your-scope.example.com/api/v1/extensions';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"_id":"ms-python.python","publisher":"ms-python","name":"Python","description":"Python language support with Pylance, debugging, and more","origin":"marketplace"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/extensions"
payload = { "_id": "ms-python.python", "publisher": "ms-python", "name": "Python", "description": "Python language support with Pylance, debugging, and more", "origin": "marketplace"}headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.json())package main
import ( "fmt" "strings" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/extensions"
payload := strings.NewReader("{\n \"_id\": \"ms-python.python\",\n \"publisher\": \"ms-python\",\n \"name\": \"Python\",\n \"description\": \"Python language support with Pylance, debugging, and more\",\n \"origin\": \"marketplace\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");RequestBody body = RequestBody.create(mediaType, "{\n \"_id\": \"ms-python.python\",\n \"publisher\": \"ms-python\",\n \"name\": \"Python\",\n \"description\": \"Python language support with Pylance, debugging, and more\",\n \"origin\": \"marketplace\"\n}");Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/extensions") .post(body) .addHeader("Content-Type", "application/json") .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri("https://your-scope.example.com/api/v1/extensions"), Content = new StringContent("{\n \"_id\": \"ms-python.python\",\n \"publisher\": \"ms-python\",\n \"name\": \"Python\",\n \"description\": \"Python language support with Pylance, debugging, and more\",\n \"origin\": \"marketplace\"\n}") { Headers = { ContentType = new MediaTypeHeaderValue("application/json") } }};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}| Field | Required | Description |
| --- | --- | --- |
| _id | ✅ | Marketplace ID ({publisher}.{name}). |
| publisher | ✅ | Publisher identifier (the part before the dot). |
| name | ✅ | Human-readable display name. |
| description | — | Optional description. |
| origin | ✅ | "marketplace" if discovered via search, "manual" if entered by hand. |
If the extension was previously soft-deleted, importing it again restores it.
Searching for extensions
Section titled “Searching for extensions”Before importing, you can search both your internal list and the VS Code Marketplace:
Type in the search box on the Extensions page in the Portal. Results are split into Imported (already in your deployment) and VS Code Marketplace sections.
You can also search programmatically:
curl --request GET \ --url 'https://your-scope.example.com/api/v1/extensions/search?q=python&limit=10'const url = 'https://your-scope.example.com/api/v1/extensions/search?q=python&limit=10';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/extensions/search"
querystring = {"q":"python","limit":"10"}
response = requests.get(url, params=querystring)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/extensions/search?q=python&limit=10"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/extensions/search?q=python&limit=10") .get() .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://your-scope.example.com/api/v1/extensions/search?q=python&limit=10"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}Each result includes an internal flag (true if already
imported).
Listing imported extensions
Section titled “Listing imported extensions”curl --request GET \ --url https://your-scope.example.com/api/v1/extensionsconst url = 'https://your-scope.example.com/api/v1/extensions';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/extensions"
response = requests.get(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/extensions"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/extensions") .get() .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://your-scope.example.com/api/v1/extensions"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}Browsing available versions
Section titled “Browsing available versions”Once an extension is imported, you can query the Marketplace for available versions:
curl --request GET \ --url https://your-scope.example.com/api/v1/extensions/ms-python.python/versionsconst url = 'https://your-scope.example.com/api/v1/extensions/ms-python.python/versions';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/extensions/ms-python.python/versions"
response = requests.get(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/extensions/ms-python.python/versions"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/extensions/ms-python.python/versions") .get() .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://your-scope.example.com/api/v1/extensions/ms-python.python/versions"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}Add ?preRelease=true to include pre-release versions.
Deleting an extension
Section titled “Deleting an extension”Deleting an extension is a soft-delete — it disappears from list views and can no longer be added to new profiles, but past runs that used it still resolve correctly.
curl --request DELETE \ --url https://your-scope.example.com/api/v1/extensions/ms-python.pythonconst url = 'https://your-scope.example.com/api/v1/extensions/ms-python.python';const options = {method: 'DELETE'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/extensions/ms-python.python"
response = requests.delete(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/extensions/ms-python.python"
req, _ := http.NewRequest("DELETE", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/extensions/ms-python.python") .delete(null) .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Delete, RequestUri = new Uri("https://your-scope.example.com/api/v1/extensions/ms-python.python"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}Using imported extensions in profiles
Section titled “Using imported extensions in profiles”After importing, reference extensions in a profile's
extensions array. You can optionally pin a specific version
with @:
{ "extensions": [ "ms-python.python", "esbenp.prettier-vscode@10.4.0" ]}- Unpinned IDs (e.g.
ms-python.python) resolve to the latest stable version at profile-version creation time. - Pinned IDs (e.g.
esbenp.prettier-vscode@10.4.0) are used as-is.
For more on how extensions fit into profiles, see Defining profiles and Using MCP servers, skills & extensions.
- Import before you profile. Extensions must be in the internal list before they can appear in a profile. Search and import first, then create the profile.
- Pin versions for reproducible benchmarks. Unpinned extensions resolve to "latest" — fine for exploration, risky for long-lived comparisons.
- One extension at a time. There's no bulk-import endpoint.
Script multiple
POST /api/v1/extensionscalls if you need to import several at once.