Skip to content

feat(mcp): manage project services from an agent with the *_service tools and lich service - #1009

Merged
omartelo merged 1 commit into
mainfrom
feat/services-cli-mcp
Nov 1, 2026
Merged

omartelo merged 1 commit into
mainfrom
feat/services-cli-mcp

Conversation

@omartelo

@omartelo omartelo commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

An agent, or a script, can now do to a project's services what the window's Services footer does, over MCP and the CLI.

What CLI (lich service …) MCP
List with state list list_services (read-only)
Create add <name> <command> create_service
Edit (only what is given) edit <service> + --name/--command/--start-with-lich[=false] edit_service
Delete (a running one is refused) delete <service> delete_service
Start / stop / force-stop / restart start, stop, force-stop, restart <service> start_service, stop_service, force_stop_service, restart_service
Start / stop every service of the project start-all, stop-all start_all_services, stop_all_services
Console as it is now output <service> read_service_output (read-only)
  • A service is named by its name in the project, case aside, the way services.Save keeps names apart. The project defaults to the caller's own, by name or directory path otherwise.
  • New spawn.Project resolves a project the way every spawn call does and returns the id the services.* methods take; everything else reuses the existing services.* RPC.
  • New services.LiveOutput returns a running service's replay tail (capped at 2 MiB) at the moment of the call. services.Output is unchanged: nil while the service runs, which the window's console relies on. The CLI and MCP pick one by the service's state. It is one read, not a stream.
  • Typing into a service's console is deliberately not exposed.

Test plan

  • internal/cli/services_test.go: every verb and the MCP tools over the real dispatcher, with the services methods faked at the RPC boundary and the project resolved by the real spawn.Project (partial edit keeps unset fields, unknown name lists the project's services, another project's same-named service is not reached).
  • internal/services: TestLiveOutputReadsARunningService on a real PTY.
  • internal/spawn: spawn.Project resolves the caller's own and a named project, refuses an unknown one and an unreadable workspace.
  • api/testdata/contract.golden updated; TestMCPListsEveryTool lists the new tools.
  • Local gate: gofmt, go vet ./..., go test ./...; -race -count=20 on the live-output test; GOOS=darwin|windows go build ./....

…ools and lich service

An agent can do to a project's services what the Services footer does:
list, create, edit, delete, start, stop, force-stop and restart one, start
or stop them all, and read its console as it is at the call. A service is
named by its name in the project, the caller's own unless one is named;
spawn.Project resolves the project to the id the services methods take.

services.LiveOutput reads a running service's replay tail, leaving Output
as the window reads it: nil while the service runs.
@omartelo
omartelo merged commit c105404 into main Nov 1, 2026
7 checks passed
@omartelo
omartelo deleted the feat/services-cli-mcp branch November 1, 2026 21:33
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