Use ChatGPT, Claude, Gemini, DeepSeek and friends inside OpenCode, with no API keys.
Your browser is already logged in. NOAPI? just lets OpenCode talk to it.
Quick start • How it works • Any project folder • Troubleshooting • Limitations
API access costs money, and a lot of us already pay for a chat subscription that we barely squeeze the value out of. NOAPI? connects the two. OpenCode sends a prompt, a small browser extension types it into the real chat page you have open, waits for the answer, and hands it back.
It is not magic and it is not an official integration. It is a careful bit of plumbing that works well enough that you can run a real coding agent on top of it, including file edits and shell commands.
|
🧰 Real tool use |
🧠 Live "Thinking" status |
|
🤖 Fully automatic |
🔌 Two ways to use it |
|
🪶 Zero dependencies |
🔒 Local only |
| Site | Model id in OpenCode | Address | |
|---|---|---|---|
| ChatGPT | noapi/chatgpt | chatgpt.com | |
| Claude | noapi/claude | claude.ai | |
| Gemini | noapi/gemini | gemini.google.com | |
| DeepSeek | noapi/deepseek | chat.deepseek.com | |
| Perplexity | noapi/perplexity | perplexity.ai | |
| Grok | noapi/grok | grok.com | |
| Mistral | noapi/mistral | chat.mistral.ai |
You need to be logged in, and the site has to be open in a tab. If you open several, each one can serve its own model.
You need: Node.js 18 or newer, Chrome (or Edge, Brave, Opera, Arc) or Firefox, and OpenCode.
chrome://extensionsextension/chrome folderabout:debugging#/runtime/this-firefoxextension/firefox/manifest.jsonFirefox forgets temporary add-ons when it closes, so you will repeat this after a restart.
npm start
Leave it running. You can peek at the dashboard any time at http://127.0.0.1:8787/.
Open one of the supported sites and log in. You will see a small round ? button in the bottom right corner.
| Dot color | Meaning |
|---|---|
| 🟢 | Connected to the bridge, ready |
| 🟡 | Busy, a prompt is being handled |
| 🔴 | The bridge is not running |
Run opencode inside this folder, type /models, search for NOAPI? and pick something like noapi/chatgpt. Then just work as usual.
💬 Try: "make a folder named test and put a hello.txt inside it". If everything is wired up, you will see the status lines scroll by and the files show up on disk.
The opencode.json in this repo only applies when you launch OpenCode from this exact folder. To use NOAPI? in all your projects, register it once in your global OpenCode config:
npm run install-global
That adds the noapi provider and the MCP server to ~/.config/opencode/opencode.json, using the absolute path of this folder. Your other settings are kept, and a backup is saved next to the file as opencode.json.bak.
Then, for each project:
# terminal 1, inside the NOAPI? folder
npm start
# terminal 2, inside your project
opencode
A few things worth knowing:
npm run install-global again.npm run install-global -- --remove.flowchart LR
A["OpenCode"] -- "OpenAI-style request<br>POST /v1/chat/completions" --> B["Local bridge<br>127.0.0.1:8787"]
A -. "MCP over stdio" .-> B
B -- "WebSocket" --> C["Browser extension"]
C -- "types, sends, reads" --> D["ChatGPT / Claude / Gemini<br>DeepSeek / ..."]
D -- "reply" --> C
C --> B
B -- "answer or tool call" --> A
In plain words:
Chat websites do not accept tool definitions, so the bridge improvises:
TOOL_CALL, followed by a small JSON object.tool_calls response.Because the website already remembers the conversation, only the new message is sent each turn, not the whole history. Very long tool output is trimmed in the middle to keep prompts sane.
opencode.jsonIf you prefer to edit the config by hand, this is what npm run install-global writes (replace the path with your own):
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"noapi": {
"npm": "@ai-sdk/openai-compatible",
"name": "NOAPI? (browser bridge)",
"options": {
"baseURL": "http://127.0.0.1:8787/v1",
"apiKey": "noapi-local"
},
"models": {
"chatgpt": { "name": "ChatGPT via browser", "limit": { "context": 128000, "output": 8192 } },
"claude": { "name": "Claude via browser", "limit": { "context": 200000, "output": 8192 } },
"gemini": { "name": "Gemini via browser", "limit": { "context": 1000000, "output": 8192 } },
"deepseek": { "name": "DeepSeek via browser", "limit": { "context": 128000, "output": 8192 } },
"perplexity": { "name": "Perplexity via browser", "limit": { "context": 100000, "output": 8192 } },
"grok": { "name": "Grok via browser", "limit": { "context": 128000, "output": 8192 } },
"mistral": { "name": "Mistral via browser", "limit": { "context": 128000, "output": 8192 } }
}
}
},
"mcp": {
"noapi": {
"type": "local",
"command": ["node", "/absolute/path/to/NOAPI/bridge/index.js", "--mcp"],
"enabled": true,
"timeout": 30000
}
}
}
The API key is a dummy value. Nothing checks it.
node bridge/index.js [--mcp] [--port=8787] [--host=127.0.0.1]
| Flag | What it does |
|---|---|
--port=8788 | Use another port. Update baseURL in your config to match. |
--host=... | Bind address. Keep it on 127.0.0.1 unless you know why you are changing it. |
--mcp | Also expose the MCP server on stdio. If the port is already taken by a running bridge, it switches to proxy mode and forwards to it. |
| Endpoint | Purpose |
|---|---|
GET / | Status dashboard |
GET /health | JSON status: clients, pending requests, sites |
GET /v1/models | One model per site |
POST /v1/chat/completions | OpenAI compatible chat, streaming and non streaming |
The MCP server offers three tools: chat (ask a site), status and sites.
| What you see | What to try |
|---|---|
| "No browser tab on ... is connected" | Open that site in a tab, make sure you are logged in, and check that the ? button has a green dot. Refresh the tab if the extension was just reloaded. |
Red dot on the ? button | The bridge is not running. Start it with npm start. |
| Port 8787 already in use | Run node bridge/index.js --port=8788 and change baseURL in your config to :8788/v1. |
NOAPI? is missing from /models | You are probably outside this folder. Run npm run install-global once. |
| Stuck at "Waiting for ... to answer" | The site may have changed its layout. Open the ? panel and read the last status line. The stall message includes a short technical summary you can paste into an issue. |
| The prompt gets typed but never sent | The extension retries a few times on its own. If it keeps failing, the send button selector for that site probably needs an update. |
| It worked yesterday, today it does not | Sites change their pages. See Limitations and Adding or fixing a site. |
| Firefox lost the extension | Temporary add-ons disappear on restart. Load it again from about:debugging. |
🪪 Tip: keep the AI tab visible when you can. Browsers slow down background tabs, which can make long responses laggy.
You can check the whole pipeline with a fake browser tab. No real site needed.
npm test
This starts a bridge, connects a fake extension, and runs plain requests, streaming, MCP in both modes, and a full tool call round trip (model asks for bash, gets the result, answers).
NOAPI/
├── bridge/ Local server (pure Node.js)
│ ├── index.js Entry point, WebSocket + HTTP + MCP wiring
│ ├── http.js OpenAI compatible endpoints and dashboard
│ ├── state.js Request queue, routing, progress, cancel
│ ├── toolproto.js TOOL_CALL protocol: prompt building and parsing
│ ├── mcp.js MCP server over stdio (with proxy mode)
│ ├── ws.js / wsclient.js Minimal WebSocket server and client
│ └── sites.js Supported sites
├── extension/
│ ├── chrome/ Manifest V3 extension
│ └── firefox/ Same code, Firefox manifest
├── tools/
│ ├── install-global.js Register NOAPI? in your global OpenCode config
│ ├── fake-extension.js Fake browser tab for tests
│ ├── test-all.js End to end tests
│ └── test-tools.js Tool call round trip test
└── opencode.json Project level OpenCode config
Sites change their HTML from time to time, so selectors rot. The good news is that each site is only a small block of config.
extension/chrome/content.js, find the SITES object. Each entry lists CSS selectors for the input, the send button, the stop button and the reply messages.extension/firefox/content.js so both browsers match.bridge/sites.js, to opencode.json, and to the matches and host_permissions in both manifests.npm test, reload the extension, and try it on the real page.Pull requests with selector fixes are very welcome. 🙏
I would rather tell you now than have you find out later.
127.0.0.1 only. Nothing is exposed to your network.NOAPI? is released under the MIT License. Use it, change it, share it. Just keep the license notice.
Made for people who like their tools local and their bills small. 💚
If NOAPI? saved you some money, a ⭐ on the repo is a nice way to say thanks.
Use ChatGPT, Claude, Gemini, DeepSeek and friends inside OpenCode, with no API keys.
Your browser is already logged in. NOAPI? just lets OpenCode talk to it.
Quick start • How it works • Any project folder • Troubleshooting • Limitations
API access costs money, and a lot of us already pay for a chat subscription that we barely squeeze the value out of. NOAPI? connects the two. OpenCode sends a prompt, a small browser extension types it into the real chat page you have open, waits for the answer, and hands it back.
It is not magic and it is not an official integration. It is a careful bit of plumbing that works well enough that you can run a real coding agent on top of it, including file edits and shell commands.
|
🧰 Real tool use |
🧠 Live "Thinking" status |
|
🤖 Fully automatic |
🔌 Two ways to use it |
|
🪶 Zero dependencies |
🔒 Local only |
| Site | Model id in OpenCode | Address | |
|---|---|---|---|
| ChatGPT | noapi/chatgpt | chatgpt.com | |
| Claude | noapi/claude | claude.ai | |
| Gemini | noapi/gemini | gemini.google.com | |
| DeepSeek | noapi/deepseek | chat.deepseek.com | |
| Perplexity | noapi/perplexity | perplexity.ai | |
| Grok | noapi/grok | grok.com | |
| Mistral | noapi/mistral | chat.mistral.ai |
You need to be logged in, and the site has to be open in a tab. If you open several, each one can serve its own model.
You need: Node.js 18 or newer, Chrome (or Edge, Brave, Opera, Arc) or Firefox, and OpenCode.
chrome://extensionsextension/chrome folderabout:debugging#/runtime/this-firefoxextension/firefox/manifest.jsonFirefox forgets temporary add-ons when it closes, so you will repeat this after a restart.
npm start
Leave it running. You can peek at the dashboard any time at http://127.0.0.1:8787/.
Open one of the supported sites and log in. You will see a small round ? button in the bottom right corner.
| Dot color | Meaning |
|---|---|
| 🟢 | Connected to the bridge, ready |
| 🟡 | Busy, a prompt is being handled |
| 🔴 | The bridge is not running |
Run opencode inside this folder, type /models, search for NOAPI? and pick something like noapi/chatgpt. Then just work as usual.
💬 Try: "make a folder named test and put a hello.txt inside it". If everything is wired up, you will see the status lines scroll by and the files show up on disk.
The opencode.json in this repo only applies when you launch OpenCode from this exact folder. To use NOAPI? in all your projects, register it once in your global OpenCode config:
npm run install-global
That adds the noapi provider and the MCP server to ~/.config/opencode/opencode.json, using the absolute path of this folder. Your other settings are kept, and a backup is saved next to the file as opencode.json.bak.
Then, for each project:
# terminal 1, inside the NOAPI? folder
npm start
# terminal 2, inside your project
opencode
A few things worth knowing:
npm run install-global again.npm run install-global -- --remove.flowchart LR
A["OpenCode"] -- "OpenAI-style request<br>POST /v1/chat/completions" --> B["Local bridge<br>127.0.0.1:8787"]
A -. "MCP over stdio" .-> B
B -- "WebSocket" --> C["Browser extension"]
C -- "types, sends, reads" --> D["ChatGPT / Claude / Gemini<br>DeepSeek / ..."]
D -- "reply" --> C
C --> B
B -- "answer or tool call" --> A
In plain words:
Chat websites do not accept tool definitions, so the bridge improvises:
TOOL_CALL, followed by a small JSON object.tool_calls response.Because the website already remembers the conversation, only the new message is sent each turn, not the whole history. Very long tool output is trimmed in the middle to keep prompts sane.
opencode.jsonIf you prefer to edit the config by hand, this is what npm run install-global writes (replace the path with your own):
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"noapi": {
"npm": "@ai-sdk/openai-compatible",
"name": "NOAPI? (browser bridge)",
"options": {
"baseURL": "http://127.0.0.1:8787/v1",
"apiKey": "noapi-local"
},
"models": {
"chatgpt": { "name": "ChatGPT via browser", "limit": { "context": 128000, "output": 8192 } },
"claude": { "name": "Claude via browser", "limit": { "context": 200000, "output": 8192 } },
"gemini": { "name": "Gemini via browser", "limit": { "context": 1000000, "output": 8192 } },
"deepseek": { "name": "DeepSeek via browser", "limit": { "context": 128000, "output": 8192 } },
"perplexity": { "name": "Perplexity via browser", "limit": { "context": 100000, "output": 8192 } },
"grok": { "name": "Grok via browser", "limit": { "context": 128000, "output": 8192 } },
"mistral": { "name": "Mistral via browser", "limit": { "context": 128000, "output": 8192 } }
}
}
},
"mcp": {
"noapi": {
"type": "local",
"command": ["node", "/absolute/path/to/NOAPI/bridge/index.js", "--mcp"],
"enabled": true,
"timeout": 30000
}
}
}
The API key is a dummy value. Nothing checks it.
node bridge/index.js [--mcp] [--port=8787] [--host=127.0.0.1]
| Flag | What it does |
|---|---|
--port=8788 | Use another port. Update baseURL in your config to match. |
--host=... | Bind address. Keep it on 127.0.0.1 unless you know why you are changing it. |
--mcp | Also expose the MCP server on stdio. If the port is already taken by a running bridge, it switches to proxy mode and forwards to it. |
| Endpoint | Purpose |
|---|---|
GET / | Status dashboard |
GET /health | JSON status: clients, pending requests, sites |
GET /v1/models | One model per site |
POST /v1/chat/completions | OpenAI compatible chat, streaming and non streaming |
The MCP server offers three tools: chat (ask a site), status and sites.
| What you see | What to try |
|---|---|
| "No browser tab on ... is connected" | Open that site in a tab, make sure you are logged in, and check that the ? button has a green dot. Refresh the tab if the extension was just reloaded. |
Red dot on the ? button | The bridge is not running. Start it with npm start. |
| Port 8787 already in use | Run node bridge/index.js --port=8788 and change baseURL in your config to :8788/v1. |
NOAPI? is missing from /models | You are probably outside this folder. Run npm run install-global once. |
| Stuck at "Waiting for ... to answer" | The site may have changed its layout. Open the ? panel and read the last status line. The stall message includes a short technical summary you can paste into an issue. |
| The prompt gets typed but never sent | The extension retries a few times on its own. If it keeps failing, the send button selector for that site probably needs an update. |
| It worked yesterday, today it does not | Sites change their pages. See Limitations and Adding or fixing a site. |
| Firefox lost the extension | Temporary add-ons disappear on restart. Load it again from about:debugging. |
🪪 Tip: keep the AI tab visible when you can. Browsers slow down background tabs, which can make long responses laggy.
You can check the whole pipeline with a fake browser tab. No real site needed.
npm test
This starts a bridge, connects a fake extension, and runs plain requests, streaming, MCP in both modes, and a full tool call round trip (model asks for bash, gets the result, answers).
NOAPI/
├── bridge/ Local server (pure Node.js)
│ ├── index.js Entry point, WebSocket + HTTP + MCP wiring
│ ├── http.js OpenAI compatible endpoints and dashboard
│ ├── state.js Request queue, routing, progress, cancel
│ ├── toolproto.js TOOL_CALL protocol: prompt building and parsing
│ ├── mcp.js MCP server over stdio (with proxy mode)
│ ├── ws.js / wsclient.js Minimal WebSocket server and client
│ └── sites.js Supported sites
├── extension/
│ ├── chrome/ Manifest V3 extension
│ └── firefox/ Same code, Firefox manifest
├── tools/
│ ├── install-global.js Register NOAPI? in your global OpenCode config
│ ├── fake-extension.js Fake browser tab for tests
│ ├── test-all.js End to end tests
│ └── test-tools.js Tool call round trip test
└── opencode.json Project level OpenCode config
Sites change their HTML from time to time, so selectors rot. The good news is that each site is only a small block of config.
extension/chrome/content.js, find the SITES object. Each entry lists CSS selectors for the input, the send button, the stop button and the reply messages.extension/firefox/content.js so both browsers match.bridge/sites.js, to opencode.json, and to the matches and host_permissions in both manifests.npm test, reload the extension, and try it on the real page.Pull requests with selector fixes are very welcome. 🙏
I would rather tell you now than have you find out later.
127.0.0.1 only. Nothing is exposed to your network.NOAPI? is released under the MIT License. Use it, change it, share it. Just keep the license notice.
Made for people who like their tools local and their bills small. 💚
If NOAPI? saved you some money, a ⭐ on the repo is a nice way to say thanks.