Skip to main content

v0.3.1

MCP servers are managed from the command line and logged in to. iota mcp add|list|get|remove edits the mcp_servers: block of one file; a server behind OAuth 2.1 is one iota mcp login <name> signs in to — the client identified three ways, the consent prompt asked for, auth discovered from the server's own 401 — the shape one Logto tenant taught, a finding at a time.

Added

  • iota mcp manages the mcp_servers: block. iota mcp add <name> -- <command> [args…] or add <name> --url <url> [--header 'K: V']…, list [--scope user|project|all] [--json] [--probe], get <name> and remove <name> [--scope]. Two scopes and no third file: --scope user (~/.iota.yaml, the default) or --scope project (./.iota.yaml, where a header or environment value must be a ${…} reference), or the -c file alone. The block is machine-managed — every other byte of the file stays as you wrote it; a comment inside the block is not kept.
  • MCP OAuth 2.1. A server that asks for a login is one you log in to: iota mcp login <name> discovers the authorization server, registers a client when the server offers it, opens the browser ($BROWSER, else the platform opener; the URL is printed either way, and a pasted redirect URL works without a browser) and stores the tokens in ~/.iota/mcp/auth/<name>.json (mode 0600). A run puts the bearer token on every request and refreshes it when the server rejects it; a server with no usable token is reported as not logged in: run iota mcp login <name> and left out of that run alone, and a stored login the server will not refresh is reported with the server's answer and the same login to run. iota mcp logout <name> revokes and forgets. In the chat, /mcp shows every server's login state, /mcp login <name> runs the same flow and reconnects the server, /mcp logout <name> takes it down.
  • A login identifies iota three ways. The entry's client_id (a client registered out of band — iota mcp add … --client-id <id> --client-secret-env VAR, the secret only ever a ${env:VAR} reference; or --client-id on login), else dynamic registration when the server offers it, else iota's Client ID Metadata Document (https://iota.sh/oauth/client.json) when the server accepts one — the shape of an authorization server like Logto, which registers nobody. A login asks for the scopes the resource names, plus offline_access when the server lists it, and carries the RFC 8707 resource.
  • A pre-registered client listens on a fixed port. Its redirect URI must match what was registered, so login binds 127.0.0.1:17801 (or the entry's redirect_port, --redirect-port on add) for it and reports a taken port instead of moving; iota mcp get/list show the redirect_uri to register, and login prints it as Redirect:. A refusal names the server's error_description and error_uri.
  • The authorization request asks for the consent prompt. Every login sends prompt=consent (OpenID Connect Core §3.1.2.1), as Claude Code does; a server that does not know the parameter ignores it (RFC 6749 §3.1). A Logto tenant answered a request without it with a bare access_denied right after the consent page.
  • auth is discovered. An HTTP server no longer has to be declared auth: oauth to be logged in to. Not written (the default) is auto: a run connects bare, and a 401 at the handshake is the server asking for a login — that server is reported as not logged in: run iota mcp login <name> and left out of the run, not "connect failed"; once a token file exists for its name, every later connect is an OAuth one. auth: oauth forces the login, auth: none forbids it, and an entry that writes its own Authorization header counts as none — that credential is the one to fix, as Claude Code has it. iota mcp add --url writes no auth: unless --auth oauth|none says so; list/get show auto (auto: logged in with a token file), and --probe labels a server that asked needs login: iota mcp login <name>.