Skip to main content
Glama

Configure a service

add
Destructive

Register a named proxy, file, or TCP service on your private Tailscale network, then configure access or publish it publicly via Funnel after confirmation.

Instructions

Setting funnel true on this tool publishes the service to the entire public internet, so ask the user before doing that; otherwise it writes a registry entry for a proxy, file, or TCP service reachable on the user's private Tailscale network. New gateway apps admit the owner/admins and explicit grants; existing installations retain their mode. Human allow recipients use membership verification and necessary device invitations; incomplete setup returns an error with saved progress. A recipient allowed here has no expiry unless for or until limits it (people_add defaults to 24h); a binding that expires gets a 24h default, never a permanent grant. Use this instead of share when the user wants a named, configured service rather than a one-shot share; it installs the background service when absent unless no_daemon_install is true. Installation announcements go to stderr. After setup it returns current URL/enrollment evidence without an additional URL wait; use url to poll pending endpoints.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "name": "<name>",
      "type": "<type>"
    },
    "name": "add"
  }
}

Input Schema

NameRequiredDescriptionDefault
dirNoAbsolute directory path for file. Rejected for proxy and tcp.
forNoLimit human allow recipients to this lifetime (descending unique units w,d,h,m,s,ms,us,ns) or until <RFC3339|YYYY-MM-DD|YYYY-MM-DDTHH:MM>. Local unless offset given; DST gaps/folds require an offset. Minimum 1h. Presets: 1h, 8h, 24h, 3d, 7d. Omitted: a recipient this call newly allows has no expiry and an existing grant keeps its deadline; a binding that expires or cannot create permanent grants gets 24h. Explicit never requires ack_never for members and device-invited guests; finite guest grants retain the configured maximum.
nameYesRegistry service name (DNS label). An existing entry with this name is replaced.
tagsNoACL tags applied to the tailnet node, each prefixed tag:. Defaults to the configured default tag.
typeYesproxy forwards HTTP to target; file serves the directory dir; tcp forwards a raw stream to target.
allowNoHuman recipients use the common access action: membership verification, app grants and necessary invitations. A recipient named here keeps access without an expiry unless for or until limits it (people_add defaults to 24h); an explicit never requires ack_never, and a binding that cannot create permanent grants gets the 24h default instead. tag: entries remain machine ACLs. New installations use the gateway; existing installs preserve their mode. Refused for TCP and public Funnel.
untilNoLimit human allow recipients to this deadline; conflicts with for.
funnelNoPublish to the public internet through Tailscale Funnel. Requires type proxy, public_ack true, no allow entries, and no control_url.
healthNo
remoteNoHTTPS MagicDNS upstream in the gateway tailnet. Replaces target for type proxy and implies portal_only; requires an observed tagged gateway.
targetNohost:port or URL for proxy, host:port for tcp. Rejected for file. The daemon will proxy to any address it can reach on its own network; link-local and cloud-metadata addresses are refused only as literal IP addresses (unspecified addresses too) or the metadata.google.internal hostname: hostnames are not resolved and nothing is checked at connect time, so a name that resolves to one of those addresses is accepted.
ack_neverNo
ephemeralNoRegister an ephemeral tailnet node that disappears on disconnect.
funnel_ttlNoRelative lifetime (descending unique units w,d,h,m,s,ms,us,ns) or until <RFC3339|YYYY-MM-DD|YYYY-MM-DDTHH:MM>. Local unless offset given; DST gaps/folds require an offset. Minimum 1h. Presets: 1h, 8h, 24h, 3d, 7d. Default 24h; guest/public maximum defaults to 7d, configurable in durations.public_max. Never refused. Omission on add preserves an existing deadline.
public_ackNoExplicit acknowledgement that funnel exposes the service publicly. funnel true without it is rejected.
control_urlNoPer-service custom control server URL, for example a Headscale deployment. A custom control server never receives the stored Tailscale credential or an auth key minted with it: while a credential is stored the daemon does not start such a service (credential_control_url_mismatch), and without one the node enrolls interactively on that server. Rejected together with funnel.
portal_onlyNoCreate no app node; assign a route on the enabled gateway. App becomes people-scoped.
portal_portNoStable port for portal_only or remote.
print_linksNo
requestableNoDisclose this private HTTP/file app in the portal request form; default off.
preserve_hostNoProxy only: forward this node's trusted canonical external name in Host and X-Forwarded-Host; fail closed if unavailable. Default false keeps upstream Host rewriting.
request_limitsNo
no_auto_provisionNoDisable automatic Funnel policy provisioning. Only valid with funnel true.
no_daemon_installNoSave configuration without installing the background service.

Output Schema

NameRequiredDescriptionDefault
urlYes
nameYes
nextNo
typeYes
accessNo
createdYesTrue when this call created the registry entry; false when it replaced an existing entry with the same name.
auth_urlNo
endpointYes
exposureYes
warningsNo
requestableNo
url_pendingYes
preserve_hostNoProxy only: forward this node's trusted canonical external name in Host and X-Forwarded-Host; fail closed if unavailable. Default false keeps upstream Host rewriting.
daemon_runningNo
funnel_rearmedYes
request_limitsNo
replaced_fieldsYesregistry.json fields the replace changed or dropped, sorted; empty when created is true. add sets every field from this call, so a field not given again is dropped.
daemon_installedNoPresent only when this call installed TSLink's background service, a persistent OS autostart entry; absent when the service already ran or no_daemon_install was true.
funnel_expires_atNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed6 schema fields changedv0.3.0
    • addedInput schema / properties / ack_never
      Added value: +{
      +  "type": "boolean"
      +}
    • changedInput schema / properties / allow / description
      Previous value: -"Principals allowed to reach the service over HTTP: email addresses, or tag:<name> ACL tags. Omitting it leaves an HTTP service readable by every member of the user's tailnet. Unsupported for tcp and rejected together with funnel."New value: +"Human recipients use the common access action: membership verification, app grants and necessary invitations. A recipient named here keeps access without an expiry unless for or until limits it (people_add defaults to 24h); an explicit never requires ack_never, and a binding that cannot create permanent grants gets the 24h default instead. tag: entries remain machine ACLs. New installations use the gateway; existing installs preserve their mode. Refused for TCP and public Funnel."
    • addedInput schema / properties / for
      Added value: +{
      +  "description": "Limit human allow recipients to this lifetime (descending unique units w,d,h,m,s,ms,us,ns) or until <RFC3339|YYYY-MM-DD|YYYY-MM-DDTHH:MM>. Local unless offset given; DST gaps/folds require an offset. Minimum 1h. Presets: 1h, 8h, 24h, 3d, 7d. Omitted: a recipient this call newly allows has no expiry and an existing grant keeps its deadline; a binding that expires or cannot create permanent grants gets 24h. Explicit never requires ack_never for members and device-invited guests; finite guest grants retain the configured maximum.",
      +  "examples": [
      +    "1h",
      +    "8h",
      +    "24h",
      +    "3d",
      +    "7d",
      +    "90m",
      +    "1w",
      +    "1d12h",
      +    "until 2030-06-01T18:00:00Z"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / print_links
      Added value: +{
      +  "type": "boolean"
      +}
    • addedInput schema / properties / until
      Added value: +{
      +  "description": "Limit human allow recipients to this deadline; conflicts with for.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / access
      Added value: +{
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "access": {
      +        "items": {
      +          "additionalProperties": false,
      +          "properties": {
      +            "access_state": {
      +              "type": "string"
      +            },
      +            "app": {
      +              "type": "string"
      +            },
      +            "message": {
      +              "type": "string"
      +            },
      +            "url": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "app",
      +            "access_state",
      +            "message"
      +          ],
      +          "type": "object"
      +        },
      +        "type": "array"
      +      },
      +      "complete": {
      +        "type": "boolean"
      +      },
      +      "guide": {
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "guide_zh": {
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "invite_requirement": {
      +        "type": "string"
      +      },
      +      "invites": {
      +        "items": {
      +          "additionalProperties": false,
      +          "properties": {
      +            "app": {
      +              "type": "string"
      +            },
      +            "code": {
      +              "type": "string"
      +            },
      +            "id": {
      +              "type": "string"
      +            },
      +            "invite_url": {
      +              "type": "string"
      +            },
      +            "reconcile_ids": {
      +              "items": {
      +                "type": "string"
      +              },
      +              "type": "array"
      +            },
      +            "remote_side_effect_plan": {
      +              "additionalProperties": false,
      +              "properties": {
      +                "boundaries": {
      +                  "items": {
      +                    "type": "string"
      +                  },
      +                  "type": "array"
      +                },
      +                "default": {
      +                  "type": "string"
      +                },
      +                "disable_flag": {
      +                  "type": "string"
      +                },
      +                "id": {
      +                  "type": "string"
      +                },
      +                "mutates": {
      +                  "type": "boolean"
      +                },
      +                "operation": {
      +                  "type": "string"
      +                },
      +                "opt_in_flag": {
      +                  "type": "string"
      +                },
      +                "remote_system": {
      +                  "type": "string"
      +                },
      +                "resources": {
      +                  "items": {
      +                    "type": "string"
      +                  },
      +                  "type": "array"
      +                },
      +                "schema_version": {
      +                  "type": "integer"
      +                }
      +              },
      +              "required": [
      +                "schema_version",
      +                "id",
      +                "operation",
      +                "remote_system",
      +                "mutates",
      +                "default",
      +                "resources",
      +                "boundaries"
      +              ],
      +              "type": "object"
      +            },
      +            "state": {
      +              "type": "string"
      +            },
      +            "target_kind": {
      +              "enum": [
      +                "app",
      +                "gateway"
      +              ],
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "app"
      +          ],
      +          "type": "object"
      +        },
      +        "type": "array"
      +      },
      +      "membership": {
      +        "enum": [
      +          "member",
      +          "non_member"
      +        ],
      +        "type": "string"
      +      },
      +      "message": {
      +        "type": "string"
      +      },
      +      "person": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "grants": {
      +            "items": {
      +              "additionalProperties": false,
      +              "properties": {
      +                "active": {
      +                  "type": "boolean"
      +                },
      +                "app": {
      +                  "type": "string"
      +                },
      +                "expired": {
      +                  "type": "boolean"
      +                },
      +                "expires_at": {
      +                  "format": "date-time",
      +                  "type": "string"
      +                },
      +                "url": {
      +                  "type": "string"
      +                }
      +              },
      +              "required": [
      +                "app",
      +                "active"
      +              ],
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "invites": {
      +            "items": {
      +              "additionalProperties": false,
      +              "properties": {
      +                "app": {
      +                  "type": "string"
      +                },
      +                "attempt": {
      +                  "minimum": 0,
      +                  "type": "integer"
      +                },
      +                "hostname": {
      +                  "type": "string"
      +                },
      +                "id": {
      +                  "type": "string"
      +                },
      +                "node_id": {
      +                  "type": "string"
      +                },
      +                "state": {
      +                  "type": "string"
      +                },
      +                "target_kind": {
      +                  "enum": [
      +                    "app",
      +                    "gateway"
      +                  ],
      +                  "type": "string"
      +                }
      +              },
      +              "required": [
      +                "app",
      +                "hostname",
      +                "node_id",
      +                "state"
      +              ],
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "login": {
      +            "type": "string"
      +          },
      +          "revoked": {
      +            "type": "boolean"
      +          }
      +        },
      +        "required": [
      +          "login",
      +          "revoked",
      +          "grants"
      +        ],
      +        "type": "object"
      +      },
      +      "portal": {
      +        "description": "Independent home portal configuration and exact runtime address when available.",
      +        "type": "object"
      +      },
      +      "qr_payload": {
      +        "type": "string"
      +      },
      +      "qr_warning": {
      +        "type": "string"
      +      },
      +      "warnings": {
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      }
      +    },
      +    "required": [
      +      "person",
      +      "complete",
      +      "message",
      +      "invite_requirement",
      +      "invites"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  2. First observedv0.1.0

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description discloses key behavior: Funnel publishes to the public internet, the tool writes a registry entry, installs a background service unless no_daemon_install is true, reports installation announcements to stderr, and returns URL/enrollment evidence after setup. It also explains incomplete setup behavior and recipient expiry nuances.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense and somewhat run-on, but it is appropriately sized for a destructive 24-parameter configuration tool. The most important safety warning about funnel and public exposure is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex, destructive, open-world tool, the description covers the essential selection, safety, installation, and post-setup behavior. It does not restate every advanced parameter or the existing-entry replacement consequence, but the rich schema and annotations cover those details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is high at 83%, so the schema already carries most parameter detail. The description still adds useful cross-parameter meaning for funnel safety, allow/for/until expiry behavior, and daemon installation, though it does not cover all 24 parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Although the tool name 'add' is generic, the description states the specific action: writing a registry entry for a proxy, file, or TCP service on the user's Tailscale network. It also distinguishes the tool from the sibling 'share', so an agent can identify its role.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly gives an alternative-selection rule: use this instead of 'share' when the user wants a named configured service rather than a one-shot share. It also adds a clear safety instruction to ask the user before enabling Funnel public exposure.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.