Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
제작자가 쓴 설명(영어 원문)
Let AI agents see and use your tailnet: status, SSH config, Serve/Funnel sharing, ping matrix
이 세 가지는 절대 하나의 점수로 합치지 않아요. 의견, 측정, 인기는 서로 다른 것이니까요.
이 서버는 아직 쉬운 말로 된 요약을 쓰지 못했어요. 아래는 레지스트리에서 읽어 온 사실이에요.
쓰고 있는 AI 앱을 고른 다음 설정을 복사하세요.
TAILSCALE_MCP_ALLOW_FUNNEL, TAILSCALE_MCP_READONLY, TAILSCALE_MCP_BIN, TAILSCALE_MCP_TIMEOUT_MS필요한 것
어떤 앱을 쓰세요?
위에서 앱을 고르세요. 기억해 두었다가 다음부터는 버튼에 “…에 설치”라고 표시해요.
Claude Desktop → Settings → Developer → Edit Config를 열고 claude_desktop_config.json에 이 내용을 붙여 넣으세요
{
"mcpServers": {
"tailscale": {
"command": "npx",
"args": [
"-y",
"@tmhs/tailscale-mcp"
],
"env": {
"TAILSCALE_MCP_ALLOW_FUNNEL": "your-value",
"TAILSCALE_MCP_READONLY": "your-value",
"TAILSCALE_MCP_BIN": "your-value",
"TAILSCALE_MCP_TIMEOUT_MS": "your-value"
}
}
}
}터미널에서 이 명령어를 실행하세요
claude mcp add tailscale --env TAILSCALE_MCP_ALLOW_FUNNEL=your-value --env TAILSCALE_MCP_READONLY=your-value --env TAILSCALE_MCP_BIN=your-value --env TAILSCALE_MCP_TIMEOUT_MS=your-value -- npx -y @tmhs/tailscale-mcp실행하기 전에 빈 값을 채워 주세요.
프로젝트의 .cursor/mcp.json 또는 ~/.cursor/mcp.json에 붙여 넣으세요
{
"mcpServers": {
"tailscale": {
"command": "npx",
"args": [
"-y",
"@tmhs/tailscale-mcp"
],
"env": {
"TAILSCALE_MCP_ALLOW_FUNNEL": "your-value",
"TAILSCALE_MCP_READONLY": "your-value",
"TAILSCALE_MCP_BIN": "your-value",
"TAILSCALE_MCP_TIMEOUT_MS": "your-value"
}
}
}
}브라우저가 앱을 열지 물어본 다음, 앱이 설정을 보여 주고 확인을 요청해요. 키 값은 비워 두니 직접 입력하세요.
프로젝트의 .vscode/mcp.json에 붙여 넣으세요
{
"servers": {
"tailscale": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@tmhs/tailscale-mcp"
],
"env": {
"TAILSCALE_MCP_ALLOW_FUNNEL": "your-value",
"TAILSCALE_MCP_READONLY": "your-value",
"TAILSCALE_MCP_BIN": "your-value",
"TAILSCALE_MCP_TIMEOUT_MS": "your-value"
}
}
}
}브라우저가 앱을 열지 물어본 다음, 앱이 설정을 보여 주고 확인을 요청해요. 키 값은 비워 두니 직접 입력하세요.
이 서버는 내 컴퓨터에서 실행되는데, ChatGPT는 인터넷에 있는 서버에만 연결할 수 있어요. 다른 앱을 고르세요.
선택한 앱은 이 기기의 쿠키에만 저장돼요. 계정은 필요 없어요.
TAILSCALE_MCP_ALLOW_FUNNEL envTAILSCALE_MCP_READONLY envTAILSCALE_MCP_BIN envTAILSCALE_MCP_TIMEOUT_MS env이 설정은 제작자의 레지스트리 정보로 만들었어요. README와 다르면 README를 따르세요.
내 컴퓨터에서 실행돼요. 설치하면 AI 앱에 도구 목록이 나타나요.
누구나 볼 수 있는 정보를 점검했고, 코드는 읽지 않았어요. 해당하지 않는 항목은 개수에서 뺐어요. · 2026년 11월 7일 점검
레지스트리 이름이 GitHub 계정 TMHSDigital 기준으로 확인되었고, 이 계정이 저장소를 소유하고 있어요
아직 GitHub에서 이 저장소를 읽어 오지 않았어요
아직 GitHub에서 이 저장소를 읽어 오지 않았어요
내 컴퓨터에서 실행되는 서버라 인터넷 엔드포인트가 없어요
내 컴퓨터에서 실행되며, 대신 로컬 설정을 사용해요
내 컴퓨터에서 실행돼요. 도구 목록은 인터넷 엔드포인트가 있는 서버에서만 읽을 수 있어요
레지스트리에서 이 카테고리의 활성 서버 전체와 비교했어요. 스타는 게시자가 저장소를 소유한 경우에만 세요.
해당하는 기본 점검 1개 중 1개를 통과했어요. 코드는 읽지 않았어요. 키나 토큰을 넣기 전에 서버가 어떤 권한을 요구하는지 확인하고, 꼭 필요한 권한만 있는 키를 쓰세요.
레지스트리에 필수 키가 나와 있지 않아요. 다만 처음 쓸 때 로그인을 요구하는 서버도 있어요.
내 컴퓨터에서 실행돼요. AI 앱이 필요할 때 실행하고, 데이터는 제작자의 서버를 거치지 않아요(서버 자체가 온라인 서비스를 호출하는 경우는 제외).
GitHub 저장소는 마지막으로 – 변경됐고, 최신 버전(0.4.0)은 12일 전 레지스트리에 올라왔어요.
Claude Desktop, Claude Code, Cursor, VS Code처럼 MCP를 지원하는 앱에서 쓸 수 있어요. 내 컴퓨터에 Node.js도 있어야 해요.
같은 카테고리에는: Spec Driven Development, Remote Desktop Commander, Python MCP
같은 카테고리에서 다른 제작자가 만든 서버예요. 스타 순으로 정렬했어요.
카드 보는 법:★ GitHub 스타i세 기둥: 리뷰 · 점검 · 사용 정도i업데이트 = 최신 버전을 레지스트리에 공개한 날i
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Local-only Python MCP server for the Files.com API.
제작자의 저장소에서 쓰인 그대로 가져왔어요. PopNx MCP가 검토하거나 보증한 내용이 아니에요. HTML, 이미지, 스크립트는 제거했어요. 링크를 누르면 이 사이트를 벗어나요.
MCP server for safe programmatic access to the local Tailscale daemon: tailnet discovery, SSH config generation, port sharing via Serve/Funnel, and latency matrices.
Gives agents and developer tools a safe, structured window into the local Tailscale daemon over stdio. It shells out to the tailscale CLI (resolved from PATH, with fallbacks to the default install locations on Windows and macOS) and never handles auth keys or tailnet credentials: the CLI talks to the already-authenticated local daemon.
When the binary is missing, the daemon is stopped, or the device needs login, every tool returns a structured error with a code and the exact remedy command instead of throwing.
claude mcp add tailscale -- npx -y @tmhs/tailscale-mcptailscale-mcp.mcpb from the latest release and open it. The install dialog has switches for read-only mode and Funnel.io.github.TMHSDigital/tailscale-mcp, so registry-aware clients and catalogs can find it.Or add it to any MCP client's configuration by hand:
{
"mcpServers": { "tailscale": { "command": "npx", "args": ["-y", "@tmhs/tailscale-mcp"] } } }
Requires Node 22+ (an active LTS) and an installed, logged-in Tailscale client.
The server looks for tailscale on PATH, then in the platform's default install locations: C:\Program Files\Tailscale\tailscale.exe on Windows, and /Applications/Tailscale.app/Contents/MacOS/Tailscale, /opt/homebrew/bin/tailscale, and /usr/local/bin/tailscale on macOS. Mac App Store installs therefore work without extra setup. To use a different binary, set TAILSCALE_MCP_BIN:
{
"mcpServers": { "tailscale": { "command": "npx", "args": ["-y", "@tmhs/tailscale-mcp"], "env": { "TAILSCALE_MCP_BIN": "/path/to/tailscale" } } } }
Every tailscale invocation is bounded, by default to 10 seconds. A hung daemon returns a structured TIMEOUT error instead of stalling the agent. Set TAILSCALE_MCP_TIMEOUT_MS to raise the limit for slow machines or very large tailnets. share_port waits up to 15 seconds and ping scales with count and timeoutMs; ping_all as a whole is capped at 45 seconds.
All settings are environment variables in the server's env block. Boolean flags accept 1/true/yes/on or 0/false/no/off. Any other value stops the server at startup, so a typo can never silently loosen a safety setting.
| Variable | Default | Effect |
|---|---|---|
TAILSCALE_MCP_ALLOW_FUNNEL | off | Allows share_port {"public": true}, which exposes a port to the open internet via Funnel. While off, the call returns FUNNEL_DISABLED and nothing is shared. |
TAILSCALE_MCP_READONLY | off | Monitoring-only mode: share_port and stop_share are not registered, and generate_ssh_config refuses write: true (READ_ONLY). |
TAILSCALE_MCP_BIN | (search) | Path to the tailscale binary. |
TAILSCALE_MCP_TIMEOUT_MS | 10000 | Per-call CLI timeout. |
On Linux only root or the configured operator can change Serve/Funnel settings. If share_port or stop_share returns PERMISSION_DENIED, allow your user once:
sudo tailscale set --operator=$USER
Every tool declares MCP annotations, so clients can auto-approve the read-only ones and confirm the rest. Results come back as structuredContent, typed by each tool's outputSchema, with the same JSON repeated in a text block for older clients. The full definitions are in mcp-tools.json, which is generated from the server.
| Tool | Read-only | Destructive | Idempotent | Open world |
|---|---|---|---|---|
tailnet_status | yes | no | yes | no |
share_status | yes | no | yes | no |
ping_all | yes | no | yes | no |
generate_ssh_config | no (writes only with write: true) | no | yes | no |
share_port | no | no | yes | only when Funnel is allowed |
stop_share | no | yes | yes | no |
Trimmed view of tailscale status --json: self node plus peers with hostname, MagicDNS name, Tailscale IPv4/IPv6, OS, online flag, last seen, exit-node flags, and tags. Input: optional onlineOnly.
{
"backendState": "Running", "tailnet": "user@example.com", "magicDNSSuffix": "tail1234.ts.net", "self": {"hostname": "DEVPC", "dnsName": "devpc.tail1234.ts.net", "ipv4": "100.101.1.10"}, "peerCount": 3, "peers": [ {"hostname": "raspi", "dnsName": "raspi.tail1234.ts.net", "ipv4": "100.101.1.20", "os": "linux", "online": true, "exitNodeOption": true, "tags": []} ] }
Emits ssh-config Host blocks for tailnet nodes (MagicDNS name preferred, Tailscale IP fallback). Dry-run by default: returns the generated text only. Inputs: write, target, onlineOnly, includeShared, user.
Host alias is the node's first MagicDNS label (raspi.tail1234.ts.net → raspi), falling back to a sanitized OS hostname ([a-z0-9-] only). Colliding aliases get a numeric suffix and are listed under renamed. Mullvad exit nodes, and nodes of users you share with (unless includeShared: true), are left out and listed under skipped.write: true writes the dedicated file ~/.ssh/tailscale_dev_config and returns the Include tailscale_dev_config instruction for ~/.ssh/config (never applied automatically).target: "user_ssh_config" (with write: true) edits ~/.ssh/config directly, idempotently, between # BEGIN tailscale-mcp and # END tailscale-mcp markers. Content outside the markers is never touched. If the markers are malformed (orphaned, reversed, or duplicated) the tool returns SSH_CONFIG_MARKERS_CORRUPT and writes nothing.~/.ssh/config is kept as config.tailscale-mcp.bak, new files are created 0600 (existing modes are kept), and a symlinked config is written through rather than replaced.{
"dryRun": true, "hostCount": 2, "configText": "# BEGIN tailscale-mcp\nHost raspi\n HostName raspi.tail1234.ts.net\n\nHost devbox\n HostName devbox.tail1234.ts.net\n# END tailscale-mcp" }
Shares a local port via Tailscale Serve. Tailnet-only by default. Inputs: port (required), public, path.
{"port": 3000, "public": false, "scope": "tailnet-only (Serve)", "urls": ["https://devpc.tail1234.ts.net/"]}
public: true switches to Funnel and exposes the port to the open internet. It's refused unless TAILSCALE_MCP_ALLOW_FUNNEL=1 is set; see the security notes below.
Reports active Serve/Funnel sessions as structured endpoints, parsed from tailscale serve status --json. Each endpoint has its own public flag, which is true when Funnel exposes it to the internet, and publicExposure is true when anything is public:
{
"active": true, "publicExposure": true, "endpoints": [ {"url": "https://devpc.tail1234.ts.net/", "host": "devpc.tail1234.ts.net", "httpsPort": 443, "scheme": "https", "path": "/", "target": "http://127.0.0.1:3000", "targetType": "proxy", "public": true, "foreground": false} ] }
Raw TCP forwards appear under tcpForwards. On CLIs too old for --json, the text status comes back under status and publicExposure is null.
Stops shares. Name exactly one target:
httpsPort and/or path turns off one mount. httpsPort is the serve port shown in share_status (default 443, not the proxied local port), and path is the mount path given to share_port (default /). For example, {"path": "/api"} undoes share_port {"port": 3000, "path": "/api"}.mine: true stops only the shares share_port created during this server session.all: true clears the entire serve/funnel configuration, including shares you set up outside this server.With no arguments nothing is stopped: the tool returns TARGET_REQUIRED and lists the active endpoints. Every successful call returns removed (the endpoints that went away) and remaining (the share_status view afterwards).
Mount paths must start with / and use only letters, digits, and . _ ~ - /, with no . or .. segments. A trailing slash is dropped.
Concurrent tailscale ping against peers (bounded to 5 in flight, with a per-ping timeout). Inputs: count, timeoutMs, onlineOnly (default true).
{
"pinged": 2, "reachable": 1, "timedOut": false, "matrix": [ {"hostname": "raspi", "target": "100.101.1.20", "reachable": true, "latencyMs": 3, "path": "direct", "via": "192.168.1.20:41641", "attempts": 3, "replies": 3}, {"hostname": "exitbox", "target": "100.101.1.40", "reachable": false, "latencyMs": null, "path": null, "via": null, "attempts": 1, "replies": 0} ] }
path is direct (peer-to-peer), derp (relayed through a Tailscale DERP server), peer-relay (relayed through one of your nodes), or unknown, so an agent can tell "works but relayed" from "fast direct path". tailscale ping keeps pinging until a direct path comes up, so path and latencyMs describe the last reply, i.e. the settled path. The whole call is capped at 45 seconds: peers that haven't answered by then come back with an error and timedOut: true.
An agent asked to "deploy this to my dev VPS and show me the result" can do the whole discovery leg through this server:
tailnet_status {"onlineOnly": true} finds the VPS node and its MagicDNS name (devbox.tail1234.ts.net), with no hardcoded IPs.generate_ssh_config {"write": true, "user": "deploy"} writes ~/.ssh/tailscale_dev_config; the agent shows the returned one-line Include instruction, and after the user adds it, ssh devbox resolves through the tailnet.ping_all confirms the node is reachable and whether the path is direct or relayed before starting a large transfer.share_port {"port": 8080} returns a tailnet-only URL teammates on the tailnet can open. Nothing touches the public internet unless you have enabled Funnel with TAILSCALE_MCP_ALLOW_FUNNEL=1 and the agent is explicitly told to pass public: true.share_port defaults to tailnet-only Serve. Nothing is reachable from outside your tailnet.public: true uses Tailscale Funnel, which makes the URL reachable by anyone on the internet, not just your tailnet. A default install refuses it with FUNNEL_DISABLED, so a prompt-injected agent can't expose a port by itself. To allow it, s앞부분만 보여 드려요. 나머지는 저장소에서 읽어 보세요