Skip to content

Make spacetime list, rename and mcp respect the project's server - #5883

Open
krisajenkins wants to merge 1 commit into
clockworklabs:masterfrom
krisajenkins:list-respects-config-server
Open

Make spacetime list, rename and mcp respect the project's server#5883
krisajenkins wants to merge 1 commit into
clockworklabs:masterfrom
krisajenkins:list-respects-config-server

Conversation

@krisajenkins

@krisajenkins krisajenkins commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Create a project, and configure it to work against your local server, by putting this in spacetime.json:

{
  "server": "local",
  "module-path": "./spacetimedb"
}

If you run spacetime call my-db my_reducer it hits your local server, as
you'd expect. However,spacetime list doesn't - that ignores the config and
goes to maincloud. Same for spacetime rename and spacetime mcp - they
ignore the config and go to maincloud.

Whatever the behaviour should be, it should be consistent across all
commands. This PR assumes we want to consistently respect spacetime.json.

The cause is that server resolution from spacetime.json is entirely a
side effect of database resolution. db_arg_resolution.rs walks the
config looking for a database target that matches the database argument,
and the server comes back attached to that target. Every command with a
database argument — call, logs, sql, describe, delete, lock,
unlock, subscribe — is therefore covered. list has no database
argument, so there was nothing to hang the lookup off, and it read
--server straight from clap:

let server = args.get_one::<String>("server").map(|s| s.as_ref());
let token = get_login_token_or_log_in(&mut config, server, !force).await?;

None then falls through to the CLI's global default server. rename
and mcp have the same shape and the same bug: rename addresses its
database by identity rather than by name, and mcp's database argument
is optional because omitting it serves the whole server.

The consequence is worse than a wrong table. That server argument is
also what scopes the auth token, so list was authenticating against
maincloud with your maincloud credentials and reporting maincloud's
databases as though they were the project's. Nothing errors, the output
is a perfectly plausible list of databases you really do own, and the
same command run with an explicit --server local works fine — so it
reads as the user having misremembered which databases they published
rather than as a bug in the CLI.

Add resolve_config_server, which reads the server from the config
without going via a database target, and use it in the three affected
commands. It answers only when every target in the config agrees on one
server; a multi-database config spanning several servers has no single
right answer, so it leaves the CLI default alone rather than guess. A
root-level server with no database at all — the config above — is
handled, and children inherit it.

mcp deliberately only takes the server from the config, not the
database: omitting its database argument is a documented mode that
serves the whole server, and inferring one would silently remove that
mode inside a project directory.

  • Each affected command gains --no-config, matching the commands that
    already resolve through db_arg_resolution.rs.
  • Explicit --server still wins over the config, as everywhere else.

API and ABI breaking changes

None.

Rollback safety impact

n/a

Expected complexity level and risk

2 — the resolution helper is a small pure function over the already
existing collect_all_targets_with_inheritance. The risk worth naming
is behavioural rather than structural: these three commands now talk to
a different server when run inside a project whose config names one, and
list will consequently use that server's token. That is the intended
fix, but it is a visible change for anyone who was relying on list
always showing the default server from within a project directory;
--no-config restores the old behaviour.

Testing

  • Four unit tests over single_config_server: root-level server with
    no database, children inheriting the root server, a config that
    omits server, and children that disagree.
  • Ran against a live setup with maincloud as the default server and
    a spacetime.json naming local: config-driven list matches
    list --no-config -s local, list --no-config matches the default
    server, and an explicit --server still overrides the config.
  • cargo test -p spacetimedb-cli (180 passed), clippy and fmt clean.
  • Reviewer: check the mcp judgement call above — should
    spacetime mcp in a project directory also infer the database?

@krisajenkins
krisajenkins force-pushed the list-respects-config-server branch from 36c73f7 to 6dd5ef6 Compare September 8, 2026 10:58
@krisajenkins
krisajenkins marked this pull request as ready for review September 8, 2026 11:07
Put yourself in a project directory with a perfectly ordinary
`spacetime.json`:

```json
{
  "server": "local",
  "module-path": "./spacetimedb"
}
```

Run `spacetime call my-db my_reducer` and it hits your local server, as
you'd expect. Now run `spacetime list` to see what's actually running
there. You get maincloud.

The reason is that server resolution from `spacetime.json` is entirely a
side effect of *database* resolution. `db_arg_resolution.rs` walks the
config looking for a database target that matches the database argument,
and the server comes back attached to that target. Every command with a
database argument — `call`, `logs`, `sql`, `describe`, `delete`, `lock`,
`unlock`, `subscribe` — is therefore covered. `list` has no database
argument, so there was nothing to hang the lookup off, and it read
`--server` straight from clap:

```rust
let server = args.get_one::<String>("server").map(|s| s.as_ref());
let token = get_login_token_or_log_in(&mut config, server, !force).await?;
```

`None` then falls through to the CLI's global default server. `rename`
and `mcp` have the same shape and the same bug: `rename` addresses its
database by identity rather than by name, and `mcp`'s database argument
is optional because omitting it serves the whole server.

The consequence is worse than a wrong table. That `server` argument is
also what scopes the *auth token*, so `list` was authenticating against
maincloud with your maincloud credentials and reporting maincloud's
databases as though they were the project's. Nothing errors, the output
is a perfectly plausible list of databases you really do own, and the
same command run with an explicit `--server local` works fine — so it
reads as the user having misremembered which databases they published
rather than as a bug in the CLI.

Add `resolve_config_server`, which reads the server from the config
without going via a database target, and use it in the three affected
commands. It answers only when every target in the config agrees on one
server; a multi-database config spanning several servers has no single
right answer, so it leaves the CLI default alone rather than guess. A
root-level `server` with no `database` at all — the config above — is
handled, and children inherit it.

`mcp` deliberately only takes the server from the config, not the
database: omitting its database argument is a documented mode that
serves the whole server, and inferring one would silently remove that
mode inside a project directory.

- Each affected command gains `--no-config`, matching the commands that
  already resolve through `db_arg_resolution.rs`.
- Explicit `--server` still wins over the config, as everywhere else.

# API and ABI breaking changes

None.

# Rollback safety impact

n/a

# Expected complexity level and risk

2 — the resolution helper is a small pure function over the already
existing `collect_all_targets_with_inheritance`. The risk worth naming
is behavioural rather than structural: these three commands now talk to
a different server when run inside a project whose config names one, and
`list` will consequently use that server's token. That is the intended
fix, but it is a visible change for anyone who was relying on `list`
always showing the default server from within a project directory;
`--no-config` restores the old behaviour.

# Testing

- [x] Four unit tests over `single_config_server`: root-level server with
      no database, children inheriting the root server, a config that
      omits `server`, and children that disagree.
- [x] Ran against a live setup with `maincloud` as the default server and
      a `spacetime.json` naming `local`: config-driven `list` matches
      `list --no-config -s local`, `list --no-config` matches the default
      server, and an explicit `--server` still overrides the config.
- [x] `cargo test -p spacetimedb-cli` (180 passed), clippy and fmt clean.
- [ ] Reviewer: check the `mcp` judgement call above — should
      `spacetime mcp` in a project directory also infer the database?
@krisajenkins
krisajenkins force-pushed the list-respects-config-server branch from 6dd5ef6 to 81e9b32 Compare September 8, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant