mcp-local
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-localsearch my local files for 'meeting agenda'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
aki-mcp-sv
Give Claude on the web (claude.ai) and ChatGPT read/edit access to files and a whitelisted shell on your local machine, over HTTPS via Tailscale Funnel, gated by OAuth 2.1. No desktop app, no device install.
Version: 1.3.0 (CHANGELOG.md) · License: MIT · Windows, Linux, macOS.
Contents: Why this exists · Architecture · Requirements · Directory layout · Install · Run · Exposing via Tailscale · Connecting from Claude web · Connecting from ChatGPT · Finding files · Security
Why this exists
Claude.ai's web/Pro quota is far cheaper than paying per token via the API for equivalent usage. But most real work is project work: reading, editing, and running commands against files on your machine, not open-ended chat.
The Claude Desktop app already does local file access, but ties usage to a device ID you don't control, and running multiple accounts means repeated login/logout instead of just switching a Chrome profile.
This project routes around both problems: run an MCP server on your machine, expose it over HTTPS through Tailscale Funnel, and connect it to claude.ai as a custom connector. You get local file/shell access from the browser, on the web quota, with no app install and no account lock-in.
How this differs from Desktop Commander
Desktop Commander is the most widely used MCP terminal server. It runs locally for Claude Desktop and guards shell access with a blocklist (blockedCommands, an explicit list of forbidden commands). A blocklist is inherently leaky: you can't enumerate every dangerous command and variant, and the default is allow: anything not on the list gets through.
This project targets a different scenario: exposing local access to Claude on the web, across the open internet via Funnel. It makes the opposite default choice: a whitelist. Nothing runs unless it's explicitly allowed. See Security for what that buys you.
Related MCP server: mcp-home-server
Architecture
Claude web / ChatGPT
│ HTTPS + OAuth 2.1 (Claude: paste client ID/secret; ChatGPT: DCR self-register)
▼
Tailscale Funnel (https://your-machine.your-tailnet.ts.net)
│
▼
gatekeeper.js — public port 9999
│ /.well-known/oauth-* + openid-configuration metadata (openid is an alias for ChatGPT discovery)
│ /authorize, /token minimal authorization server (scripts/oauth.js)
│ /register RFC 7591 dynamic client registration (ChatGPT self-registers here)
│ /mcp requires a valid Bearer access token, else 401
│ POST → real Streamable HTTP (scripts/streamable-bridge.js)
▼
mcp-hub — internal only (loopback), port 19999, legacy HTTP+SSE transport
│
├─► MCP filesystem server (read/write inside the allowed folders)
├─► MCP search server (search-mcp.js — find_path/search_content, whole-tree in one call)
├─► MCP shell server (shell-mcp.js — allowlisted commands, curated to read-only)
├─► MCP agy server (agy-mcp.js — Antigravity CLI, read-only plan mode)
└─► MCP kiro server (kiro-mcp.js — Kiro arm: kiro_read (read-only), needs kiro-cli on PATH)
panel.js — 127.0.0.1:9998, never exposed via Funnel
control UI: allowed folders, shell allowlist, restart hub,
install akidevrule, generate the connector promptmcp-hub ships its own unauthenticated admin REST API (/api/*) on the same port. gatekeeper.js exists specifically so that never reaches the internet (docs/plan/done/init.md).
OAuth (not token-in-URL) is used because claude.ai always attempts Dynamic Client Registration regardless of configuration (docs/research/claude-ai-oauth-connector.md). ChatGPT also expects OAuth; this server advertises /register (RFC 7591 DCR) so ChatGPT can self-register while Claude can keep using the pre-issued Client ID/Secret.
Requirements
Node.js, on Windows, Linux, or macOS
Windows only: Git for Windows (or WSL) on
PATH— the shell/search tools shell out to Unix binaries (ls cat pwd grep head tail wc file stat tree ps df du whoami uname), and akidevrule'sinstall.shneedsbash; Git for Windows'usr/binships the coreutils/findutils/grep/diffutils this needs. Same category of prerequisite as Tailscale below, not a code dependency.Tailscale (one-time setup):
Install Tailscale and sign in (on macOS, the app or
brew install tailscaleboth work as long astailscaleis on PATH)Enable Funnel for your tailnet: free on every plan, a one-time toggle via the
login.tailscale.com/f/funnellinknpm startprints if it isn't on yet
After that, npm start enables Funnel on port 9999 automatically every run.
Directory layout
aki-mcp-sv/
├── package.json
├── mcp-hub.config.json # shipped default, uses ${MCP_DATA_DIR}/${HOME} placeholders
├── scripts/
│ ├── start.js # orchestrates mcp-hub + gatekeeper
│ ├── open-browser.js # cross-platform "open default browser" — the one per-OS seam, no external dep
│ ├── gatekeeper.js # OAuth-gated reverse proxy, public port
│ ├── oauth.js # minimal authorization server (pre-registered client + RFC 7591 DCR)
│ ├── streamable-bridge.js # Streamable HTTP shim <-> mcp-hub's legacy SSE transport
│ ├── http.js # shared HTTP helpers: readBody / json / serveStatic (+ MIME)
│ ├── shell-mcp.js # allowlist-gated shell tool (curated to read-only)
│ ├── agy-mcp.js # dedicated MCP server for the agy CLI
│ ├── kiro-mcp.js # Kiro arm: kiro_read (read-only) tool, sonnet-4.5 locked, needs kiro-cli on PATH
│ ├── mcp-tool.js # shared MCP tool-result envelope: ok / err / fail
│ ├── allowlist.js # default command set + settings reader — shared by server and panel
│ ├── search-mcp.js # find_path / search_content — whole tree in one call
│ ├── roots.js # path containment shared by every filesystem-touching tool
│ ├── tailscale.js # reads Funnel status — shared by start.js and panel
│ ├── log.js # shared timestamped logger
│ ├── panel.js # loopback-only control panel (:9998), token-gated
│ ├── config-page.js # renders the panel page
│ ├── html.js # HTML escaper (esc) — shared by oauth confirm page and panel
│ └── userdata.js # user data location (~/.aki/mcpsv) — single source of truth
└── public/ # favicon + images, served publicly by gatekeeperYour data lives outside the repo, at ~/.aki/mcpsv/ (the same convention CLIs like ~/.aws or ~/.docker use):
~/.aki/mcpsv/
├── mcp-hub.config.json # live config (which folders you granted access to)
├── setting.json # shell allowlist, edited from the panel
├── oauth-client.json # pre-issued client ID + secret, for Claude (0600)
├── oauth-dcr-clients.json # clients that self-registered via /register, one per ChatGPT connector (0600)
├── passphrase.txt # passphrase for the /authorize consent screen (0600)
└── tokens.json # access/refresh tokens (0600)A clone stays exactly as checked out: editing folders/allowlist from the panel never produces a diff in the repo.
Install
git clone <repo-url> aki-mcp-sv
cd aki-mcp-sv
npm installRun
npm startNothing needs preparing beforehand; npm start handles it:
Passphrase and OAuth client ID/secret in
~/.aki/mcpsv/: generated once, reused on every later run.Funnel: checks
tailscale funnel status; if port9999isn't on yet, runstailscale funnel --bg 9999(idempotent: never toggles an already-enabled port).Prints the 4 values you need: Remote MCP server URL, OAuth Client ID, OAuth Client Secret (paste into claude.ai), and Passphrase (enter on the confirmation page when you hit Connect).
Opens the control panel at
http://127.0.0.1:9998/?t=<token>, with 7 sections in the order you need them: Tailscale, connector, allowed folders, shell allowlist, akidevrule, connector prompt, utilities.
The default allowed root is your home directory ($HOME, or %USERPROFILE% on Windows): the one folder guaranteed to exist on any machine and to hold the projects you actually want Claude to reach. Add/remove folders from panel section 3: click "+ Add folder…" and type an absolute path (/Users/you/projects or C:\Users\you\projects); saving restarts the hub automatically. To change the root from the start: MCP_DATA_DIR=/other/path npm start (or set MCP_DATA_DIR=D:\work then npm start on Windows cmd).
Beyond $MCP_DATA_DIR, the filesystem server is also granted ~/.aki (where akidevrule deploys) and ~/.claude, so claude.ai can read your native CLAUDE.md and skill router the same way Claude Code does, with no copying or staging.
~/.claude is granted at the folder level (the filesystem server can't scope to individual files), so .claude.json/auth-cache.json (session tokens) and history.jsonl (chat history) inside it are also reachable through the connector. Remove the ${HOME}/.claude line in panel section 3 if you don't want that; claude.ai then loses access to your CLAUDE.md too.
npm start runs in the foreground: Ctrl+C to stop, restart manually when needed. After editing code, Ctrl+C and npm start again (Node doesn't hot-reload).
Exposing via Tailscale
npm start enables Funnel automatically when needed (see above), no manual step. Funnel is state stored in tailscaled (survives reboots), independent of npm start's own lifecycle; disable it entirely with tailscale funnel 9999 off.
Know before enabling Funnel:
Free on every Tailscale plan, but the tailnet needs a one-time opt-in first (the
login.tailscale.com/f/funnel?node=...linktailscale funnel --bgprints if it's missing).Only 3 ports are fundeable:
443,8443,10000; you can't expose an arbitrary port.Bandwidth is limited; Tailscale doesn't publish an exact number.
Don't toggle Funnel on/off repeatedly: re-issuing the certificate too often can hit Let's Encrypt's rate limit (~34h lockout).
start.jsavoids this by checkingWeb[].Handlers[].Proxyfor port 9999 intailscale funnel status --jsonbefore deciding Funnel is off (not theAllowFunnelkey, which reflects the public port 443, not 9999).
Diagnosing "claude.ai can't connect" while tailscale funnel status says "on": the serve-config can save locally but fail to sync to Tailscale's control plane, so a real client on the open internet is blocked at the TLS layer while the host machine, routed through the internal mesh, sees everything as fine. Don't test with a bare curl https://<host> from the machine running npm start: that machine is in the tailnet and silently takes the mesh shortcut. Test the real path instead:
dig @8.8.8.8 <host> A +short # real public IP
curl --resolve <host>:443:<IP-from-above> https://<host>/.well-known/oauth-authorization-serverIf that returns SSL_ERROR_SYSCALL/timeout despite tailscale funnel status saying "on", re-run tailscale funnel --bg 9999 to force a config re-push (not a code bug). Full writeup: docs/research/claude-ai-oauth-connector.md, section "Debug round 5".
Connecting from Claude web
Go to claude.ai → Settings → Connectors → Add custom connector
Remote MCP server URL: paste
https://your-machine.your-tailnet.ts.net/mcp(printed bynpm start)Advanced settings → OAuth Client ID / OAuth Client Secret: paste the two values
npm startprintedClick Connect: a local confirmation page opens; enter the passphrase (contents of
~/.aki/mcpsv/passphrase.txt) to approve
Why not token-in-URL: docs/ref/claude-connector.md, docs/research/claude-ai-oauth-connector.md.
claude.ai connects and calls 14 tools: filesystem__*, search__find_path, search__search_content, shell__run_cmd.
Connecting from ChatGPT
Needs ChatGPT Plus/Pro (or Business/Enterprise/Edu) with Developer mode for custom connectors.
ChatGPT → Settings → Apps & Connectors (or Security) → enable Developer mode
Create a custom connector / app → paste the same MCP URL (
https://your-machine.your-tailnet.ts.net/mcp)Auth: OAuth → Advanced OAuth settings → set Registration URL to
https://your-machine.your-tailnet.ts.net/register(the panel prints the exact value to copy). This is the step that enables DCR: ChatGPT self-registers its own client from it. Skip it and ChatGPT can't register, so it falls back to a user-defined client — and pasting Claude's Client ID there fails, because that client only allowsclaude.airedirects.Leave registration method on DCR, token endpoint auth method none — do not paste Claude's Client ID/Secret here.
Enter the same passphrase on the confirmation page
Same folder allowlist and shell allowlist as Claude. Restart npm start after upgrading so gatekeeper advertises registration_endpoint and serves /.well-known/openid-configuration (ChatGPT reads that to auto-fill the Registration URL).
Connecting from Gemini and Grok
Both ride the same MCP URL and passphrase flow — no separate transport or auth. They differ in how the client authenticates, and the connector panel (section 2) prints the exact copy fields for each.
Gemini (paid tiers — Pro / Business / Enterprise; the free tier may not expose custom apps): pastes a confidential client, exactly like Claude — set the custom app link to the MCP URL, then under Advanced Settings paste the same Client ID / Client secret. Gemini's redirect goes through Google's OAuth proxy https://oauth-redirect.googleusercontent.com/r/... (observed live 2026-08-09), allowlisted by isAllowedRedirect in scripts/oauth.js. Caveat: the OAuth handshake succeeds and Gemini accepts the instruction, but in repeated testing 2026-08-09 it did not reliably discover or drive the MCP tools — connection healthy, tool use unreliable. Claude and Grok are the dependable clients today.
Grok: self-registers via the /register DCR path like ChatGPT — paste only the MCP URL, no Client ID. Its real redirect_uri https://grok.com/connectors-oauth-exchange-code/ was observed live 2026-08-09 and is allowlisted via GROK_CALLBACK_PREFIX. Verified working end to end (authorize → token 200). If a future Grok change moves that callback, a rejected registration logs register REJECTED (redirect_uri not allowlisted): [...] so the new value can be re-allowlisted.
Connector icon
claude.ai doesn't read the icon from the MCP server. It queries Google's favicon service with the tailnet's apex domain, not your host:
https://t2.gstatic.com/faviconV2?...&url=http://<tailnet>.ts.net&size=32<tailnet>.ts.net has no public DNS record, so Google returns 404 and claude.ai falls back to a default letter icon. This server serves /favicon.ico publicly, but no file placed here can change that result: your subdomain never appears in the query Google receives.
Finding files
Use search__find_path, not filesystem__search_files, to locate a file or directory. The built-in search_files doesn't return directories and tends to time out on large trees, so a remote session can appear to "not see" the very project it has access to. find_path scans the whole tree in one call (measured: ~0.2s across 164k files / 11.7k directories), returns both files and directories, and skips node_modules/.git/build output automatically. query is a case-insensitive substring, or a glob when it contains */?.
Security
Minimal OAuth 2.1: Claude uses a pre-issued confidential Client ID/Secret; ChatGPT uses DCR (POST /register) as a public client (token_endpoint_auth_method: none) with chatgpt.com redirect URIs allowlisted. Full writeup: docs/ref/security-model.md.
$MCP_DATA_DIR(default$HOME) is the filesystem server's main root, plus~/.akiand~/.claude(for native rule files), fixed at process start; changing it via the panel restarts the hub.~/.claudeis granted at the folder level, so session tokens and chat history inside it are also in the connector's reach (a known tradeoff, removable from the panel).The shell MCP is hand-written (
shell-mcp.js), enforcing the allowlist in code (execFile, never through a shell,; & | \`` blocked). The default set is read-only, defined inallowlist.js— flag-rich binaries whose own flags escape read-only (find -delete/-exec,sort -o) are deliberately kept out of it (issue #2), so a default connector cannot write, delete, or exec through the shell tool; thefind_path/search_contenttools cover the read-only lookup they were used for. The panel shows exactly that set as your starting point for edits, saved to~/.aki/mcpsv/setting.json→shell.allowlist. **Any command you add is your own responsibility**: adding an obvious write command (e.g.git commit) widens the surface further. A command can run in any directory under the allowed roots via thecwdparameter, used instead ofcd/-C` to target a specific repo.gatekeeper.jsis the single public entry point; the realmcp-hubnever listens on anything but loopback.panel.jswrites config and runs commands on your machine, so it only binds to127.0.0.1and is never exposed via Funnel. Its token is regenerated everynpm startand required both in the page's query string and in thex-panel-tokenheader on every API call, blocking other browser tabs from POSTing to it.~/.aki/mcpsv/passphrase.txt(the/authorizeconsent passphrase) and~/.aki/mcpsv/oauth-client.json(client ID/secret) are mode 0600, live outside the repo (never reach git), and are only ever shared once, pasted into the connector dialog.Access/refresh tokens live in
~/.aki/mcpsv/tokens.json(mode 0600) and survive restarts: a connector is long-lived file access, not a login session, so losing tokens on everynpm startwould just force pointless re-authentication. Access token TTL is 1 year, refresh tokens don't expire. Revoke by deleting~/.aki/mcpsv/tokens.jsonand restarting.Each ChatGPT connector instance self-registers one client into
~/.aki/mcpsv/oauth-dcr-clients.json(mode 0600). Registration is open but not a way in on its own: onlyclaude.aiandchatgpt.comredirect URIs are accepted, and a registered client still has to pass the passphrase consent screen and PKCE before it gets a token. Revoke those registrations by deleting that file and restarting.Funnel stays enabled in the background for the whole project;
npm startis the only thing you actively start/stop.
Why whitelist, not blocklist
See How this differs from Desktop Commander above for the comparison. For a server that exposes itself to the internet via Funnel, the whitelist choice is a real safety property, not a slogan:
Fail-safe: an unfamiliar or new command is blocked automatically, no guessing required.
Minimal attack surface: only the exact commands you've approved can run, nothing more.
Granular down to the subcommand:
gitis scoped tostatus/log/diff/show, something a blocklist can't express cleanly.Read-only by construction: the built-in set is read-only — flag-rich binaries that could escape it via their own flags (
find,sort) are kept out (issue #2); adding a write command is a deliberate edit to~/.aki/mcpsv/setting.json, not the removal of a ban.
Screenshots
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityDmaintenanceExposes local OpenCode instances as remote MCP servers for Claude and ChatGPT, enabling terminal access, session management, and interactive human-in-the-loop workflows. It simplifies deployment for local machines using Cloudflare Tunnels to provide secure public connectivity and OAuth support.
- Flicense-qualityDmaintenanceSelf-hostable MCP server that gives Claude tools to run shell scripts and read files on a home server, deployed via Multipass VM and Cloudflare Tunnel.
- Flicense-qualityBmaintenanceZero-dependency MCP server that provides AI models with secure read/write/exec access to local files and directories over HTTP and SSE, designed to be tunneled via ngrok for integration with Claude Web.
- Alicense-qualityBmaintenanceUnifies local MCP servers into a single secure Cloudflare Tunnel endpoint, making them accessible to MCP clients like Notion and Claude. Supports HTTP/SSE/stdio servers, built-in file system tools, bearer authentication, and one-click Windows setup.MIT
Related MCP Connectors
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/lacvietanh/aki-mcp-sv'
If you have feedback or need assistance with the MCP directory API, please join our Discord server