Skip to content

Ask Logue: one assistant on both surfaces, then MCP and skills #56

Description

@shanforge

Four pieces of work on Ask Logue, in the order they depend on each other. Each is its own
PR; this issue is the thread that ties them together.

The ordering is not arbitrary. Every later item lands in two places — the Ask Logue pane
in the main window and the Command Center island — so the first item exists to make "both
surfaces" a single implementation rather than two that drift. Building MCP or skills before
that means building each of them twice.


1. One Ask Logue, two windows

The island was a bare completion call: no tools, no memory, no attachments, no web search
or Deep Research, and nothing kept — dismissing the panel threw the conversation away. The
same question asked from the app got a full agent. Which window you asked from decided what
Logue was.

  • Both surfaces send through one session type, so neither owns a pipeline
  • Routing (agent loop / Deep Research / ImagePlayground) decided in one pure, tested place
  • The island mounts the same conversation view — tool cards, approval prompts, error banner
  • Composer parts shared: attachment intake, + menu, mode chips, file chips
  • Coordinator state read per conversation, so a run on one surface cannot paint an
    indicator onto the other's thread
  • The island's conversation persists, and Open in Logue carries it into the main window
  • Message actions match both ways (copy, export, read aloud, save as note)
  • Invariant written down in CLAUDE.md so the two cannot drift again

Status: done. Delivered by #66, #69, #71, #72, #73 and #74, all merged.

2. Agent UI/UX refinement, and the prompt bar

With one pipeline underneath, the remaining differences are presentation — which is where
the island is still weakest, since it is read at a glance over someone else's window.

  • Prompt bar: glass over the desktop rather than a slab, legible over any wallpaper in
    both appearances
  • Starters on an empty island; live level meter while the mic is open; Return hint once
    there is something to send
  • Tool cards readable at island width — a long argument list should not push the answer
    off screen
  • Approval prompts unmistakable: what is about to happen, to what, and how to refuse
  • Streaming and thinking states legible on both surfaces, including the gap before the
    first token
  • Attachment and mode chips visible without stealing the bar's single line
  • Full keyboard path on the island; VoiceOver labels on every control
  • Motion honours Reduce Motion

3. MCP server support

Closes #55 — an MCP server so other agents can query Logue, and the other direction: the
user adds their own MCP servers and the agent can call them.

  • Settings: add, edit, remove a server; per-server enable
  • Tools discovered from a server appear in the registry alongside the built-ins, and
    obey the existing per-tool disable list
  • Namespaced so a server cannot shadow a built-in tool name
  • Destructive tools from a server route through the same approval gate — a remote tool
    is not more trusted than a local one
  • Failure is visible and local: an unreachable server disables its tools and says so,
    rather than failing a turn
  • Privacy: an MCP server is a network egress. Off by default, named plainly in Settings,
    and the Privacy tab says exactly what leaves the machine
  • Timeouts and response size bounded; tool output sanitised before it reaches a prompt
  • Works from both surfaces, because of item 1

4. Agent skills

  • A skill is a named, reusable instruction set the user can invoke by name
  • Discoverable from the composer on both surfaces
  • Editable in Settings, with the built-ins readable as examples
  • Scoped tools per skill, so a skill can narrow what the agent may do
  • Skills persist, export and import as plain files
  • The system prompt stays the app's, with the skill layered on top rather than replacing it

Ground rules for all four

  • Anything added to one surface is added to both — the composer, the conversation view
    and the error banner are mounted, not redrawn
  • Prompt content from outside the app stays data: wrapped in delimiters, bounded, and
    unable to close its own delimiter
  • Every LLM call validates the context window before making it
  • Pure decisions extracted so they are testable without a model, and covered
  • SwiftFormat and SwiftLint --strict clean; tests that need no model run in CI

Status — 2026-09-04

Parts 2, 3 and 4 are delivered by a stack of seven pull requests, each reviewed over three
rounds and each green on CI. They are stacked, so they merge bottom-up:

PR Closes On top of
#75 #62 main
#76 part of #63 #75
#88 #83 → finishes #63, which closes #55 #76
#90 #84 #88
#91 #85 #90
#92 #86 #91
#93 #87 → finishes #64 #92

#75 had to be rebased before any of this: it targeted a branch #74 had already merged into
main, so it read as conflicting against something that no longer existed independently.

The boxes for parts 2, 3 and 4 are not ticked here, because nothing above has merged yet.
They are ticked as each lands.

What the reviews found worth naming

Several of these are on main today rather than introduced by the stack:

  • A refused send on the island showed nothing. localError was written by both refusal
    paths and rendered by neither — the banner read a conversation-scoped error, and the guard
    that sets it returns before a conversation exists.
  • MCPEndpoint.validate had no caller in the app. The HTTPS-except-loopback rule was
    going to be enforced by a Settings field that had not been written, so a stored defaults
    entry could enable a plaintext remote server.
  • A failed MCP call returned server-controlled text unwrapped, into a sentence ending in
    an instruction to the model. The success path wrapped; the failure path beside it did not.
  • A server's name went raw into the system prompt, where a quote and a bracket close the
    attribution around it.
  • A document title carrying U+202E could reverse the sentence on an approval card, above
    a Touch ID prompt.

Ground rules

  • Anything added to one surface is added to both — the composer, the conversation view
    and the error banner are mounted, not redrawn
  • Prompt content from outside the app stays data: wrapped in delimiters, bounded, and
    unable to close its own delimiter
  • Pure decisions extracted so they are testable without a model, and covered
  • SwiftFormat and SwiftLint --strict clean; tests that need no model run in CI

Every LLM call validates the context window before making it is left unticked: it holds for
everything this stack adds, but it is a claim about the whole codebase and was not audited
end to end here.

Still needed

None of it can be approved on static review alone. Each PR carries its own click-through
list; #93 carries the one for skills end to end, and #88 the one for MCP against a real
server.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions