Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions pkgs/mcpx/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ with no symptom.

A daemon is started once, in whatever environment it happened to have, and
then serves every command for hours. A flag on one of those commands that
governs something the *daemon* does was simply lost: accepted, validated,
governs something the _daemon_ does was simply lost: accepted, validated,
printed back, and then ignored, because the process that would act on it had
read its configuration an hour earlier. Nothing anywhere said so.

Expand Down Expand Up @@ -95,22 +95,22 @@ would be silently inert.

## Reaching them

| surface | read | change |
| --- | --- | --- |
| config file | any `mcpx` config on the search path | edit it |
| environment | `MCPX_*` | export it |
| CLI | `mcpx settings list`, `mcpx settings get <path>` | `mcpx settings set <path> <value> [--persist runtime\|project\|user]`, `mcpx settings unset <path>` |
| `/v1` | `GET /v1/settings`, `GET /v1/settings/{path}` | `PUT /v1/settings/{path}`, `DELETE /v1/settings/{path}` |
| MCP | `mcpx_settings_list`, `mcpx_settings_get` | `mcpx_settings_set`, `mcpx_settings_unset` |
| surface | read | change |
| ----------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| config file | any `mcpx` config on the search path | edit it |
| environment | `MCPX_*` | export it |
| CLI | `mcpx settings list`, `mcpx settings get <path>` | `mcpx settings set <path> <value> [--persist runtime\|project\|user]`, `mcpx settings unset <path>` |
| `/v1` | `GET /v1/settings`, `GET /v1/settings/{path}` | `PUT /v1/settings/{path}`, `DELETE /v1/settings/{path}` |
| MCP | `mcpx_settings_list`, `mcpx_settings_get` | `mcpx_settings_set`, `mcpx_settings_unset` |

`mcpx settings list` resolves locally, which is what the *client* will use.
`mcpx settings list` resolves locally, which is what the _client_ will use.
`--daemon` asks the daemon what it resolved, which is what a daemon-scoped
setting actually is. The two differing is information, not a bug: it is
exactly the case that used to be invisible.

`--persist runtime` changes the running daemon and nothing on disk, which is
what an experiment wants. `project` and `user` write the corresponding
configuration file *and* apply at once where the setting allows it -- a change
configuration file _and_ apply at once where the setting allows it -- a change
written to a file but not applied would leave `mcpx settings get` answering
with the old value, which reads as the write having failed.

Expand All @@ -119,11 +119,11 @@ with the old value, which reads as the write having failed.
Servers are configuration too, and until now adding one meant an editor and a
restart.

| surface | list | add | remove |
| --- | --- | --- | --- |
| CLI | `mcpx servers list` | `mcpx servers add <name> -- <cmd>...`, `mcpx registry add <n> --write` | `mcpx servers remove <name>` |
| `/v1` | `GET /v1/servers` | `POST /v1/servers` | `DELETE /v1/servers/{name}` |
| MCP | `mcpx_servers_list` | `mcpx_servers_add` | `mcpx_servers_remove` |
| surface | list | add | remove |
| ------- | ------------------- | ---------------------------------------------------------------------- | ---------------------------- |
| CLI | `mcpx servers list` | `mcpx servers add <name> -- <cmd>...`, `mcpx registry add <n> --write` | `mcpx servers remove <name>` |
| `/v1` | `GET /v1/servers` | `POST /v1/servers` | `DELETE /v1/servers/{name}` |
| MCP | `mcpx_servers_list` | `mcpx_servers_add` | `mcpx_servers_remove` |

The entry is written to a configuration file and the daemon reloads, so the
server is callable immediately and is still there tomorrow. A server whose
Expand All @@ -137,14 +137,14 @@ edit by hand therefore takes effect within `daemon.reapInterval` without
anything being restarted.

Making that work required changing what identifies a daemon. The socket used
to be keyed to the *contents* of the config files, so that editing one got a
to be keyed to the _contents_ of the config files, so that editing one got a
fresh daemon rather than a stale one. A daemon that reloads changes the
contents of the files it is keyed by, which moves its own key, which makes it
unreachable by the client that just edited it -- and hands the next command a
different daemon that happens to match the new key with old servers loaded.
That was observed: `mcpx servers remove` succeeded, the file was correct, and
the next `mcpx servers list` showed the removed server. The key is now the
file *set*, and staleness is handled where it belongs.
file _set_, and staleness is handled where it belongs.

### Hiding tools

Expand All @@ -157,19 +157,18 @@ the MCP pass-through -- and a call to it by name is refused with 400.
{
"pool": { "excludeTools": ["delete_*"] },
"mcpServers": {
"datadog": { "command": "mcp-remote", "args": ["..."],
"mcpx": { "excludeTools": ["execute_*", "/^(update|upsert)_.*dashboard/"] } }
"datadog": { "command": "mcp-remote", "args": ["..."], "mcpx": { "excludeTools": ["execute_*", "/^(update|upsert)_.*dashboard/"] } }
}
}
```

Each entry is one of:

| entry | means | example |
| --- | --- | --- |
| `/.../` | a Go regular expression, unanchored as regexps are | `/delete/`, `/^delete_/` |
| contains `*`, `?` or `[` | a glob over the whole name: `*` any run, `?` one character, `[a-z]` / `[!a-z]` a class | `delete_*`, `*_monitor` |
| anything else | the exact tool name | `delete_datadog_workflow` |
| entry | means | example |
| ------------------------ | -------------------------------------------------------------------------------------- | ------------------------- |
| `/.../` | a Go regular expression, unanchored as regexps are | `/delete/`, `/^delete_/` |
| contains `*`, `?` or `[` | a glob over the whole name: `*` any run, `?` one character, `[a-z]` / `[!a-z]` a class | `delete_*`, `*_monitor` |
| anything else | the exact tool name | `delete_datadog_workflow` |

A pattern that cannot compile is a configuration error naming the field, not
a rule that silently matches nothing.
Expand Down Expand Up @@ -218,7 +217,7 @@ drifted: two settings were missing an alias their flag really answers to.
`mcpx config --schema --plumbing` prints the same inventory, and
`mcpx settings list` prints it with the values that are actually in force.

Entries marked *(plumbing)* are internals. They work and they are supported,
Entries marked _(plumbing)_ are internals. They work and they are supported,
but there is no ordinary reason to change one, so they are kept out of
ordinary help rather than hidden -- a flag a user can find in the source and
that `--help` denies exists is worse than a long list.
Expand Down Expand Up @@ -308,6 +307,7 @@ and a switch to turn the report off would have kept it quiet.
| `daemon.saveInterval` | duration | `5m` | daemon | no | `--daemon-save-interval` | `MCPX_DAEMON_SAVE_INTERVAL` | *(plumbing)* how often daemon state is written to disk |
| `daemon.socketProbeTimeout` | duration | `500ms` | daemon | no | `--daemon-socket-probe-timeout` | `MCPX_DAEMON_SOCKET_PROBE_TIMEOUT` | *(plumbing)* how long a socket left by a crashed daemon is given to answer |
| `daemon.takeoverTimeout` | duration | `15s` | daemon | no | `--daemon-takeover-timeout` | `MCPX_DAEMON_TAKEOVER_TIMEOUT` | *(plumbing)* how long a handoff may take, and how long the old daemon waits for its successor to hang up |
| `daemon.tsnet.hostname` | string | *(empty)* | daemon | no | `--daemon-tsnet-hostname` | `MCPX_DAEMON_TSNET_HOSTNAME` | also serve the daemon on a tailnet, under this name |
| `daemon.warm` | bool | `true` | daemon | no | `--daemon-warm`, `--warm` | `MCPX_DAEMON_WARM` | read every server's schemas in the background at startup |
| `daemon.warmTimeout` | duration | `3m0s` | daemon | no | `--daemon-warm-timeout` | `MCPX_DAEMON_WARM_TIMEOUT` | *(plumbing)* how long the background schema fetch may take |
| `daemon.watchConfig` | bool | `true` | daemon | yes | `--daemon-watch-config` | `MCPX_DAEMON_WATCH_CONFIG` | re-read the configuration files when they change on disk |
Expand Down
69 changes: 68 additions & 1 deletion pkgs/mcpx/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,28 @@ require (
github.com/tetratelabs/wazero v1.9.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.60.0
tailscale.com v1.86.5
Comment thread
dezren39 marked this conversation as resolved.
Resolved
)

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.58 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.13 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
Expand All @@ -22,23 +40,72 @@ require (
github.com/clipperhouse/displaywidth v0.9.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gaissmai/bart v0.18.0 // indirect
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/illarion/gonotify/v3 v3.0.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/sdnotify v1.0.0 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/miekg/dns v1.1.58 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.57.0 // indirect
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect
golang.org/x/mod v0.41.0 // indirect
golang.org/x/net v0.59.0 // indirect
golang.org/x/sync v0.23.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/term v0.46.0 // indirect
golang.org/x/text v0.42.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.50.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633 // indirect
modernc.org/libc v1.77.1 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.12.1 // indirect
Expand Down
Loading
Loading