VS Code LLM Extension Setup: Custom Endpoints That Work
A provider-agnostic guide to wiring any VS Code AI extension to your own endpoint — where settings live, what the extension cannot infer, and how to debug it.
There are a dozen AI extensions for VS Code and they all want the same three things from you: a base URL, an API key and a model identifier. The menus differ, the field labels differ, the file the values land in differs — but if you know what the three values mean and what the extension cannot work out for itself, you can configure any of them without a screenshot walkthrough.
This guide is deliberately provider-agnostic. Where a specific extension puts a setting, look for it; what the setting has to contain is the same everywhere.
The three values, and the one that always breaks
The base URL is the root of your provider OpenAI-compatible surface. It ends at the version segment and nothing more. Almost every client appends the operation path itself, so writing that path into the base URL produces a duplicated path and a 404 — which people then diagnose as an authentication failure because the message rarely says what was actually requested.
The key is passed as a bearer token. It is a secret, so where it is stored matters, and that is the section below.
The model identifier is forwarded upstream verbatim. There is no aliasing layer and no fallback: a name your provider does not publish returns an error rather than quietly resolving to something similar. Get the exact string from the provider model list rather than from memory. The rest of the compatibility surface, including where it stops being reliable, is in the compatibility explainer.
Where the settings actually live
VS Code has two settings scopes that matter here. User settings apply everywhere you work; workspace settings live in a folder inside the repository and apply only there, overriding the user values.
Model choice belongs in workspace settings and should be committed. A repository that pins its model gets consistent behaviour across the team and makes changing it a reviewable act instead of a private preference. Keys must never go there — a key in a committed settings file is a key you will be rotating, usually after a public repository scan finds it.
Better extensions store secrets in the VS Code secret storage, which is backed by the OS keychain, and only keep non-secret configuration in the settings file. If an extension asks you to paste a key directly into JSON, prefer pointing it at an environment variable if it supports one, and check what you are about to commit before you commit it. Where the extension does read environment variables, remember that a VS Code launched from a desktop icon does not inherit your shell profile, so the variable that works in a terminal may be invisible to the editor.
What the extension cannot infer
For providers an extension ships support for, it knows the context window, the output limit and whether the model supports tools or images. For an endpoint it has never seen, it knows none of that, which is why generic OpenAI-compatible provider forms have a block of model metadata fields under the credentials.
The context window is the one that silently changes behaviour. It decides when the extension trims or summarises history. Declared too small, long sessions get compacted for no reason and the agent loses detail it needed; declared too large, requests start failing partway through a task. Use the real figure from the provider page.
The output limit caps a single response, and setting it low truncates a large file write halfway — which appears as a corrupted edit rather than an error. The tool-calling capability flag is what separates an agent from a chatbot: with it off, a perfectly capable model will describe the change it would make and never make it.
Chat extensions and agent extensions are different animals
Worth being clear about, because it changes what you should test. A chat extension sends messages and renders replies; if the base URL and key are right, it works.
An agent extension additionally reads files, writes edits and often runs terminal commands, all through tool calls. It can pass a chat test and still fail completely at its actual job, because tool negotiation is a separate code path from message completion.
So test both explicitly. Ask a one-sentence question, then ask it to read a named file and quote a line back. If the first works and the second does not, stop looking at your credentials — the problem is the model or the tool capability flag, and the tool calling explainer describes what should be happening on the wire.
Proxies, certificates and corporate networks
If the same request works from a terminal on the same machine but fails inside VS Code, the network path is the suspect rather than the configuration. VS Code has its own proxy handling and some extensions use their own HTTP stack that ignores it.
TLS inspection appliances are the other common cause: they present a re-signed certificate that the system trust store accepts but a bundled runtime does not. The symptom is a certificate verification error buried in an output channel rather than shown in the panel, which is why the next section matters.
Ask your network team for the proxy address and the internal certificate bundle before you spend an hour on it. This class of failure is not something a settings change will fix.
Debugging without guessing
Every serious extension writes to an Output channel. Open the Output panel, pick the extension from the dropdown and reproduce the failure — the real status code and message are almost always there, while the panel shows a generic message. The developer tools window gives you the network view if the channel is not enough.
Before blaming the extension, reproduce the same call with a plain request from a terminal using the same base URL, key and model name. If that fails too, the problem is your provider configuration and nothing to do with VS Code. If it succeeds, the difference is in how the extension is building the request or reaching the network. Debugging API errors covers reading the status codes in order.
Choosing between extensions
Because they all speak the same protocol, the choice is about the agent design rather than the model. Some are chat-first with an inline edit action; some run an autonomous loop with approval gates; some separate planning from execution and let you assign a different model to each.
Try more than one — the switching cost is a base URL and a model name, which is the whole point of the standard. Concrete walkthroughs for three in this family are here for Cline, Roo Code and Continue, and if you end up running several, one key across all of them keeps rotation manageable.
The takeaway
Put the model in committed workspace settings, the key in secret storage, and the real context window in the metadata block. Test chat and a file read separately. When something fails, read the Output channel before changing a setting, and reproduce with a plain request before blaming the extension.
Common questions
Should the API key go in settings.json?
Ideally not. Prefer an extension that uses VS Code secret storage, which is backed by the OS keychain. Workspace settings files are frequently committed, and a key in a committed file has to be rotated. Model choice, by contrast, belongs in committed workspace settings.
Chat works but the extension never edits files. Why?
Tool calling and message completion are different code paths. Either the model behind your identifier does not negotiate tool calls, or the extension has not been told the model is tool-capable, which it cannot infer for a custom endpoint.
It works in my terminal but not in VS Code. What changed?
Usually the network path or the environment. A corporate proxy or TLS inspection certificate that the system trusts may be rejected by the extension HTTP stack, and an editor launched from a desktop icon does not inherit shell environment variables. Check the extension Output channel for the real error.