Skip to content

Release 0.0.4: fix MCP empty tools/list (issue #7) + fsa staleness + missing artisan_command stub#8

Merged
anilcancakir merged 25 commits into
masterfrom
develop
May 21, 2026
Merged

Release 0.0.4: fix MCP empty tools/list (issue #7) + fsa staleness + missing artisan_command stub#8
anilcancakir merged 25 commits into
masterfrom
develop

Conversation

@anilcancakir
Copy link
Copy Markdown
Contributor

Closes #7.

Summary

Substrate-side fix for the empty `tools/list` symptom from GitHub issue #7 (`./bin/fsa mcp:serve` returned 0 tools even when plugin providers were correctly registered), plus two pre-existing bugs surfaced during A-Z end-to-end testing in a fresh consumer.

What landed

Issue #7 root cause (verified end-to-end)

  • Bug A `assets/stubs/dispatcher.dart.stub`: missing `collectMcpTools` arg on the generated dispatcher's `runArtisan(...)` call.
  • Bug B `lib/src/commands/mcp_install_command.dart`: hardcoded `.mcp.json` entry pointed at the substrate standalone (`dart run fluttersdk_artisan:mcp`), which never loads consumer plugin providers.
  • Latent regression `lib/src/console/run_artisan.dart`: `_defaultDelegate` emitted `dart run :artisan`, which only resolves to `bin/artisan.dart`. Post-0.0.2 the canonical wrapper is `bin/dispatcher.dart`.

Issue body correction

The issue's proposed fallback `dart run :artisan mcp:serve` does not resolve (per dart docs, `dart run :name` is a pure `bin/.dart` filename lookup; `executables:` is honored only by `dart pub global activate`). The correct fallback shape is `dart run :dispatcher mcp:serve`.

A-Z testing bonus fixes

  • `./bin/fsa` rebuilt AOT bundle on every invocation: staleness condition 4 compared `pubspec.yaml -nt pubspec.lock`. `dart pub add` updates `pubspec.yaml` after pub get writes the lock, leaving `pubspec.yaml` mtime newer than `pubspec.lock` for every freshly installed consumer. Compare against the build stamp file instead.
  • `make:command` crashed with "Stub file not found: artisan_command.stub": `MakeCommandCommand.getStub()` declared the asset name but the file never shipped. Added the canonical stub.

`doctor` migration aid

`doctor` now advisory-warns when `.mcp.json` still contains the pre-fix `fluttersdk_artisan:mcp` args shape, pointing the user at `./bin/fsa mcp:install` to upgrade.

Migration

  • Substrate-installed consumers: `dart run fluttersdk_artisan install --force` then `./bin/fsa mcp:install`.
  • Magic-installed consumers: paired magic-side stub update needed (tracked separately) before `magic:artisan install --force` propagates the fix.

Verification

  • `dart format --output=none --set-exit-if-changed lib/ test/ bin/` 0 changed
  • `dart analyze lib/ test/ bin/` 0 issues
  • `dart test` 1129/1129 pass (+9 new tests covering 4 fix surfaces)
  • `dart pub publish --dry-run` 0 warnings, 398 KB archive
  • Coverage 83.78% scoped to lib/
  • A-Z e2e in fresh Flutter consumer at /tmp: install -> bin/fsa cached startup ~120ms (was ~5s) -> make:command Greet succeeds -> mcp:install writes branched shape -> mcp:serve tools/list returns 10 substrate `artisan_*` descriptors -> doctor advisory fires on legacy + pre-fix shapes

See CHANGELOG.md [0.0.4] for the full bullet list.

Process

Plan + execute autonomous run: `.ac/plans/mcp-empty-tools-list/` (planning artifacts gitignored). Deep code review + oracle both APPROVED first pass.

…lock

pub add updates pubspec.yaml after pub get writes pubspec.lock, leaving
pubspec.yaml mtime newer than pubspec.lock for every freshly installed
consumer. The pre-fix staleness check tripped on that, forcing a ~5s AOT
rebuild on every invocation (the wrapper's ~50ms cached-bundle target was
never met after initial install). Compare pubspec.yaml against the build
stamp instead: the stamp is written at the end of every successful
compile, so pubspec.yaml newer than the stamp means the user actually
edited it after the last build.

Discovered during A-Z e2e testing in a fresh consumer.
MakeCommandCommand.getStub() declared 'artisan_command' but the asset
never shipped, so every consumer invocation of `make:command` crashed
with FileSystemException: Stub file not found. Added the stub with the
canonical `final class ... extends ArtisanCommand` shape, honoring the
{{ className }} / {{ namespace }} / {{ commandName }} placeholders the
generator base class substitutes.

Discovered during A-Z e2e testing in a fresh consumer.
…leness + missing artisan_command stub

Substrate-side fixes for GitHub issue #7 (empty tools/list when consumers
invoke ./bin/fsa mcp:serve), plus two A-Z testing finds: the bin/fsa
wrapper rebuilt the AOT bundle on every invocation due to a pubspec.lock
mtime quirk, and make:command crashed because the canonical artisan_command
stub never shipped in the publish archive. See CHANGELOG [0.0.4] for the
full bullet list and migration steps.
Copilot AI review requested due to automatic review settings May 21, 2026 17:52
…ll + mcp setup pages

The install command's own doc page jumped straight to `dart run fluttersdk_artisan install`
without telling the reader to add the package first; same with the MCP setup page and the
mcp-server skill reference. Added explicit Prerequisites sections so a first-time reader
of any of those pages sees the pub-add step before the scaffold/mcp-install invocation.
…cher.dart

The 0.0.2 rename moved the consumer wrapper from `bin/artisan.dart` to `bin/dispatcher.dart`
but two current-flow descriptions in the getting-started docs still referenced the legacy
filename. Updated installation.md scaffold table + quickstart.md step descriptions to the
canonical name. Plugin-install legacy-mode references at `doc/commands/plugin-install.md`
intentionally keep `bin/artisan.dart` because that section documents the legacy fallback.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prepares the 0.0.4 release by fixing the “empty tools/list” MCP symptom (issue #7) by routing MCP server startup through the consumer wrapper and ensuring MCP tools are collected, plus additional reliability fixes discovered during end-to-end validation (fast CLI staleness detection and a missing make:command stub asset).

Changes:

  • Fix consumer dispatcher stub and mcp:install so MCP server invocation loads consumer plugin providers and collects MCP tool descriptors.
  • Update auto-delegation to target the post-0.0.2 wrapper name (bin/dispatcher.dart) and add doctor advisory for pre-fix .mcp.json shapes.
  • Improve bin/fsa staleness detection and ship the missing artisan_command.stub, with tests and docs updated accordingly.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
test/stubs/stub_loader_test.dart Adds regression test ensuring dispatcher stub forwards collectMcpTools.
test/stubs/bin_fsa_stub_test.dart Adds regression test for corrected bin/fsa staleness check condition.
test/console/run_artisan_test.dart Updates/extends tests to assert delegation prefixes :dispatcher.
test/commands/mcp_uninstall_command_test.dart Adds tests covering uninstall of new .mcp.json shapes.
test/commands/mcp_install_command_test.dart Updates tests for branched .mcp.json install shape (fsa vs dart fallback).
test/commands/make_command_command_test.dart Adds regression test asserting artisan_command stub asset exists.
test/commands/install_command_test.dart Asserts rendered dispatcher includes collectMcpTools forwarding.
test/commands/doctor_command_test.dart Adds coverage for new advisory warning on pre-fix .mcp.json shape.
skills/fluttersdk-artisan/SKILL.md Updates skill guidance for new MCP invocation and installation flow.
skills/fluttersdk-artisan/references/mcp-server.md Updates canonical .mcp.json shapes and client config guidance.
skills/fluttersdk-artisan/references/commands.md Updates MCP command docs to reflect new invocation shapes.
README.md Updates Quick Start MCP wiring snippet and documents new .mcp.json shape.
pubspec.yaml Bumps version to 0.0.4.
lib/src/console/run_artisan.dart Changes auto-delegation to prepend :dispatcher and simplifies delegate invocation.
lib/src/commands/mcp_install_command.dart Branches .mcp.json entry shape based on fsa availability and platform; injects test seams.
lib/src/commands/doctor_command.dart Adds advisory warning for pre-fix fluttersdk_artisan:mcp entry shape.
doc/mcp/tool-reference.md Updates MCP restart/invocation guidance for new server entry points.
doc/mcp/setup.md Updates per-client setup matrix for new MCP server spawn shapes.
doc/mcp/overview.md Updates overview to describe the post-install MCP invocation path and legacy entrypoint behavior.
doc/getting-started/installation.md Updates command list to describe how to run mcp:serve post-install.
doc/commands/mcp-serve.md Updates examples/synopsis to prefer ./bin/fsa mcp:serve with dart fallback.
doc/commands/install.md Documents collectMcpTools forwarding behavior in the generated dispatcher.
CHANGELOG.md Adds 0.0.4 release notes covering the fixes and migrations.
assets/stubs/dispatcher.dart.stub Updates generated dispatcher to forward collectMcpTools for mcp:serve.
assets/stubs/bin_fsa.sh.stub Fixes staleness check to compare pubspec.yaml against the build stamp file.
assets/stubs/artisan_command.stub Adds the missing command stub used by make:command.
Comments suppressed due to low confidence (1)

lib/src/commands/mcp_install_command.dart:123

  • This code writes .mcp.json via File.writeAsString, which is not atomic and can leave a truncated/partial file if the process is interrupted mid-write. The comment calls this atomic; if the repo convention is .tmp + rename for persistent writes, consider switching to that pattern here too (write to $path.tmp, then rename over the original).
    // 3. Write atomically (single write; no partial-read window on success).
    await file.writeAsString(
      '${const JsonEncoder.withIndent('  ').convert(config)}\n',
    );

Comment thread lib/src/console/run_artisan.dart
Comment thread lib/src/commands/doctor_command.dart Outdated
Comment thread README.md
Comment thread doc/mcp/overview.md Outdated
Comment thread doc/mcp/tool-reference.md
Comment thread skills/fluttersdk-artisan/SKILL.md
Comment thread assets/stubs/artisan_command.stub Outdated
Comment thread test/commands/mcp_uninstall_command_test.dart Outdated
Comment thread doc/mcp/setup.md
… :dispatcher

Auto-delegation always prepended :dispatcher when defaultConsumerWrapperExists
returned true. The exists check accepts EITHER bin/dispatcher.dart (canonical
post-0.0.2) OR bin/artisan.dart (legacy), so a legacy-only consumer was detected
as delegatable but the dispatched dart run :dispatcher would then fail because
bin/dispatcher.dart did not exist.

Added defaultConsumerWrapperName + WrapperNameResolver seam that returns
'dispatcher' or 'artisan' (or null) per the file actually on disk; runArtisan
forwards the matching token. Existing wrapperExists bool seam stays for back-
compat with tests that force the boolean against tempdirs; when both are
injected, wrapperExists wins for the bool gate, name defaults to 'dispatcher'.

Copilot PR #8 review finding.
The stale-MCP advisory text told the user to run `dart run fluttersdk_artisan:artisan mcp:install`, but the substrate's pubspec exposes only `fluttersdk_artisan` and `mcp` executables (no `artisan`). The advisory was unactionable. Replaced with `./bin/fsa mcp:install` (canonical fast path) and `dart run fluttersdk_artisan mcp:install` (substrate-direct fallback). Test-local mirror constant in doctor_command_test.dart updated byte-for-byte.

Copilot PR #8 review finding.
The new stub I added shipped with an em-dash in the {{ className }} doc comment,
violating the repo's no-em-dash rule (CLAUDE.md Must NOT). Replaced with a colon.

Copilot PR #8 review finding.
…s with canonical post-rename forms

Five doc surfaces still pointed at invocations that no longer resolve:
- README quick start said `dart run artisan mcp:install` (no `artisan` executable post-rename).
- doc/mcp/overview.md plugin-registration paragraph cited `bin/artisan.dart` + a non-existent `artisanProviders` factory.
- doc/mcp/tool-reference.md plugin-tools intro + .artisan/mcp.json layout cited `bin/artisan.dart` as the registration site.
- skills/.../SKILL.md Core Law #1 cited `dart run <consumer-package>:artisan <cmd>` as the consumer entry.
- doc/mcp/setup.md V1-transport paragraph cited `dart run artisan start`.

Replaced each with `./bin/fsa` (canonical fast path) and the `dart run :dispatcher` fallback. Legacy-mode references in plugin-install docs intentionally kept (they document the legacy fallback branch).

Copilot PR #8 review findings (5 of 9).
The group label said 'new entry shape (fluttersdk_artisan:mcp)' but the section now exercises three shapes via key-based removal: the two post-fix shapes (`./bin/fsa mcp:serve`, `dart run :dispatcher mcp:serve`) and the pre-fix `fluttersdk_artisan:mcp`. Renamed to make the intent explicit.

Copilot PR #8 review finding.
…cp_uninstall_command_test.dart

CI's `dart format --output=none --set-exit-if-changed` flagged 2 files that
escaped my local format pass between the Copilot-fix edits and the commit
phase. Re-formatted both; `dart format` is now clean and CI's format gate
will pass.
@anilcancakir anilcancakir merged commit 3b34551 into master May 21, 2026
1 check passed
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.

MCP server returns empty tools/list — plugin providers not collected via either entry point

2 participants