Skip to content

refactor(mcp): migrate cloudflared/ngrok install config to fieldform - #430

Merged
pcfreak30 merged 3 commits into
developfrom
refactor/tunnel-provider-fieldform
Aug 20, 2026
Merged

refactor(mcp): migrate cloudflared/ngrok install config to fieldform#430
pcfreak30 merged 3 commits into
developfrom
refactor/tunnel-provider-fieldform

Conversation

@pcfreak30

@pcfreak30 pcfreak30 commented Aug 20, 2026

Copy link
Copy Markdown
Member

Migrates the cloudflared and ngrok install configuration from the legacy
imperative Configurer onto the shared fieldform Fields + Finalize primitive,
then removes the legacy Configurer path.

  • cloudflared: Domain and TunnelName now derive from the provisioned
    named-tunnel state via Field.Derived, with pinner-mcp as the tunnel name
    default; a headless run fails fast when the domain is unresolved.
  • ngrok: the authtoken and public URL now derive from the config-manager store,
    the ngrok CLI config, and the account API with an SDK dev-domain fallback;
    the manual URL paste is the field's prompt. Finalize persists the token and
    fails fast on a headless run when the URL cannot be resolved.
  • TunnelProviderSpec.Fields now accepts the config manager so a Derived hook can
    read the provider's last-resort credential store.
  • Removes the dead cloudflaredConfigurer, ngrokConfigurer, and resolveNgrokURL,
    plus a no-op ngrok.api_key config-manager persist (no such pair is supported).

This pull request migrates the cloudflared and ngrok tunnel providers from the legacy imperative Configurer approach to the shared declarative Fields + Finalize field-resolution primitive, completing the migration that was previously only applied to the OpenAI provider.

Key Changes:

  • Cloudflared provider migration: Replaced the imperative cloudflaredConfigurer with:

    • cloudflaredFields(): Declarative field definitions for Domain and TunnelName with Derived hooks that automatically populate values from provisioned named-tunnel state stored in the tunnel-state file, falling back to the pinner-mcp default for tunnel name.
    • cloudflaredFinalize(): Post-gather hook that validates the domain is resolved on headless runs, failing fast with a clear error message instead of silently writing an incomplete env file.
  • Ngrok provider migration: Replaced the imperative ngrokConfigurer with:

    • ngrokFields(cfgMgr): Declarative fields for the authtoken/tunnel token and public base URL. The Derived hooks pre-resolve tokens from the config-manager store and existing ngrok config, then derive the public URL from three sources in order: operator-supplied value, account API query (when an API key is available), or a short-lived embedded tunnel's stable dev domain.
    • ngrokFinalize(): Persists the resolved token to the config-manager last-resort store and validates that a public URL was resolved on headless runs, failing fast if not.
  • Provider registry updates: Updated Fields signatures to accept the config manager, enabling field hooks to consult the last-resort credential store. Removed the legacy Configurer references for all three providers.

  • Test coverage: Added new test files (service_install_cloudflared_fields_test.go, service_install_ngrok_fields_test.go, tunnel_providers_registry_test.go) that validate field shapes, derivation logic, fail-fast contracts, and confirm no provider remains on the legacy path. Updated existing tests to align with the new field-based approach.

  • Behavior preservation: All existing functionality is maintained—automatic resolution from provisioned state, API-based URL discovery, token pre-resolution, and interactive prompting fallbacks—now expressed through the declarative field framework with headless fail-fast validation.

Both remaining tunnel providers still collected their install config through the
legacy imperative Configurer (hand-rolled `if s.X == ""` prompts around
provider-derived values). Migrate them onto the shared fieldform
Fields + Finalize primitive like OpenAI, then remove the legacy Configurer path.

- cloudflared -> cloudflaredFields(): Domain + TunnelName derive from the
  provisioned named-tunnel state (LoadCloudflareTunnelState) at precedence 0,
  with pinner-mcp as the TunnelName default. cloudflaredFinalize fails fast on a
  headless run when the required Domain is still unresolved.
- ngrok -> ngrokFields(cfgMgr)/ngrokFinalize(): the authtoken derives from the
  ngrok config file / config-manager store; PublicURL derives from the account
  REST API then a stable SDK dev-domain. The legacy resolveNgrokURL's manual
  paste becomes PublicURL's Prompt. ngrokFinalize persists the token and fails
  fast on a headless run when the public URL is unresolved (the token is not
  independently required: an API/operator-resolved URL needs none).
- TunnelProviderSpec.Fields now takes cfgMgr so a field's Derived hook can
  consult the provider's last-resort credential store (the openai/cloudflared
  registrations updated to the new signature).
- Remove the now-dead cloudflaredConfigurer, ngrokConfigurer, and resolveNgrokURL
  plus their dedicated tests; the behaviors are covered by the new fieldform
  shape tests and the existing flattened/collect integration tests.
- Also drops a dead ngrok.api_key config-manager persist (no such supported pair
  existed; the write was silently swallowed).

Verification: go vet + go test clean on fieldform, tunnel, services, install,
cli/wizard. Kodus: 0 HIGH/ERROR remaining (one low false-positive on a test
fixture's literal "secret" key, which is required to parse CloudflareTunnelState).
@kody-ai

This comment has been minimized.

Comment thread internal/mcp/services/service_install_collect_test.go Outdated
Comment thread internal/mcp/services/service_install_configurers.go Outdated
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Code Coverage Report

Total Coverage: 52.0%

Generated from commit: c5ab0d1
Repository: LumeWeb/pinner-cli

The ngrok public-URL Derived hook used context.Background() for the account-API
query and the SDK tunnel lookup, dropping the install command's
deadline/cancellation signal so an interrupted install hung until those calls'
own timeouts. The legacy configurer threaded the caller ctx through both.

Thread the step's ctx through TunnelProviderSpec.Fields into the ngrokFields
closure so the Derived hook (whose signature carries no ctx) honors
cancellation. Also aligns the flattened-test auth-token fixture values with the
repo's test-auth-token convention to avoid trip...ting the secret scanner.
@kody-ai

kody-ai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@pcfreak30
pcfreak30 merged commit 421a19a into develop Aug 20, 2026
12 checks 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.

1 participant