Skip to content

Add MCP server support — expose Repl apps as AI agent tools#10

Merged
carldebilly merged 36 commits intomainfrom
dev/cdb/mcp-format
Mar 19, 2026
Merged

Add MCP server support — expose Repl apps as AI agent tools#10
carldebilly merged 36 commits intomainfrom
dev/cdb/mcp-format

Conversation

@carldebilly
Copy link
Member

@carldebilly carldebilly commented Mar 18, 2026

Summary

Enables any Repl app to become an MCP (Model Context Protocol) server with a single app.UseMcpServer() call. AI agents like Claude Desktop, VS Code Copilot, and Cursor can then discover and invoke commands as typed tools with real JSON Schema, behavioral annotations, and progressive interaction degradation.

var app = ReplApp.Create().UseDefaultInteractive();

app.Map("contacts", () => db.GetAll())
    .ReadOnly()
    .AsResource();

app.Map("contact add", (string name, string email) => db.Add(name, email))
    .OpenWorld().Idempotent();

app.Map("contact delete {id:guid}", async (Guid id, IReplInteractionChannel interaction, CancellationToken ct) =>
    {
        if (!await interaction.AskConfirmationAsync("confirm", $"Delete {id}?", options: new(ct)))
            return Results.Cancelled("Aborted.");
        return db.Delete(id);
    })
    .Destructive();

app.UseMcpServer();
return app.Run(args);
myapp mcp serve                    # starts MCP stdio server
myapp                              # still works as a CLI / interactive REPL

One command graph. CLI, REPL, remote sessions, and AI agents — all from the same code.

What agents see

Commands become MCP tools with typed JSON Schema derived from route constraints and handler parameters. Annotations map directly to MCP hints that agents use for safety decisions:

Repl API MCP effect
.ReadOnly() readOnlyHint: true, destructiveHint: false — agent calls autonomously
.Destructive() destructiveHint: true — agent asks for confirmation
.Idempotent() idempotentHint: true — agent retries on failure
.OpenWorld() openWorldHint: true — agent expects latency
.AutomationHidden() excluded from tool list entirely
.AsResource() exposed as MCP resource with URI template (e.g. repl://config/{env})
.AsPrompt() exposed as MCP prompt template
.WithDetails(md) enriches MCP tool description with extended markdown

Route constraints ({id:guid}, {email:email}, {when:date}) produce proper JSON Schema formats. Parameterized resources (e.g. config {env}) are exposed as URI templates — agents read repl://config/production and the parameters flow through the command pipeline.

Runtime interaction in MCP mode

Commands that prompt users at runtime (AskConfirmationAsync, AskChoiceAsync, etc.) degrade gracefully across four tiers:

  1. Prefill — values from tool arguments (answer:confirm=yes)
  2. Elicitation — structured form request to the user through the agent client
  3. Sampling — LLM answers on behalf of the user
  4. Default/Fail — use explicit default value or fail with descriptive error

AskConfirmationAsync accepts bool? defaultValue = null — explicit defaults are returned before mode checks (consistent with AskChoiceAsync and AskTextAsync). AskSecretAsync is always prefill-only (security). WriteProgressAsync maps to real-time MCP progress notifications. WriteStatusAsync maps to MCP log messages.

Custom transports & HTTP

The server supports three integration patterns:

  • Stdio (default) — myapp mcp serve for standard MCP clients
  • Stdio-over-anythingTransportFactory option for WebSocket, named pipes, SSH, etc.
  • MCP-over-HTTPBuildMcpServerOptions() builds the MCP collections for use with ASP.NET Core or any HTTP host

Multi-session is supported in all patterns via AsyncLocal session isolation (same mechanism as hosted sessions). See docs/mcp-advanced.md for recipes.

// Custom transport via mcp serve
app.UseMcpServer(o => o.TransportFactory = (name, io) =>
    new StreamServerTransport(wsInput, wsOutput, name));

// Standalone options for HTTP integration
var mcpOptions = app.Core.BuildMcpServerOptions();
var server = McpServer.Create(httpTransport, mcpOptions);

Architecture

The design splits into two layers:

  • Core enrichment (Repl.Core) — CommandAnnotations, WithDetails, AsResource, AsPrompt, ReplRuntimeChannel.Programmatic, public CreateDocumentationModel. Useful independently of MCP.
  • Repl.Mcp package — opt-in, references ModelContextProtocol SDK. Reads the documentation model, generates JSON Schema, registers MCP tools/resources/prompts, dispatches tool calls through the existing Repl pipeline, and captures output.

Tool calls execute through CoreReplApp.RunWithServicesAsync — the same pipeline as CLI and interactive mode. No parallel code path.

Reliability & performance

  • Debounced routing invalidation — multiple rapid Map() calls coalesce into a single rebuild (100ms debounce via injectable TimeProvider)
  • Optimistic concurrency — route rebuilds use a temporary adapter; existing routes stay live until the atomic swap
  • Timer crash resilience — unhandled exceptions in rebuild callbacks are caught; server continues with stale routes
  • IsProgrammatic AsyncLocal — properly saved/restored across all execution paths
  • Concurrent sessions — tested with parallel MCP sessions sharing the same app instance

Configuration

app.UseMcpServer(o =>
{
    o.ServerName = "MyApp";
    o.ServerVersion = "1.0.0";
    o.ResourceUriScheme = "myapp";                              // myapp://status instead of repl://status
    o.TransportFactory = (name, io) => customTransport;         // custom transport
    o.InteractivityMode = InteractivityMode.PrefillThenFail;
    o.ResourceFallbackToTools = true;                           // resources also appear as tools
    o.PromptFallbackToTools = true;                             // prompts also appear as tools
    o.CommandFilter = cmd => true;
});

Test plan

  • 736 tests pass (dotnet test --solution src/Repl.slnx)
  • End-to-end tests via real MCP client/server over in-process pipes
  • Concurrent session isolation tested
  • Debounce tested with FakeTimeProvider (deterministic, sync)
  • Parameterized resource read tested
  • Interaction error paths tested (ParseBool, prefix matching, min/max multi-choice)
  • Timer crash resilience tested
  • Custom transport factory tested

- Add CommandAnnotations record with behavioral flags (Destructive,
  ReadOnly, Idempotent, OpenWorld, LongRunning, AutomationHidden)
- Add CommandAnnotationsBuilder as fluent escape hatch
- Enrich CommandBuilder with annotation shortcuts using with-expressions,
  WithDetails, WithMetadata, AsResource, AsPrompt
- Add IContextBuilder.WithDetails for rich context descriptions
- Add ReplRuntimeChannel.Programmatic for agent/automation mode
- Make CreateDocumentationModel public on ICoreReplApp with typed return
- Enrich ReplDocCommand with Details, Annotations, Metadata, IsResource,
  IsPrompt fields
- Add ReplDocResource record and Resources collection on
  ReplDocumentationModel
- Fix route argument descriptions to pick up [Description] attributes
  from handler parameters
- Auto-promote ReadOnly commands to Resources collection
- Update README with MCP server feature highlights
Introduces the Repl.Mcp package following the same opt-in pattern as
Repl.Spectre. The package bridges Repl's command graph to the Model
Context Protocol via the ModelContextProtocol SDK 1.1.0.

New package: Repl.Mcp
- McpModule: registers `mcp serve` as a hidden protocol passthrough
  context (ShellCompletionModule pattern)
- McpReplExtensions: AddReplMcpServer() / UseMcpServer() extension
  methods mirroring the Spectre integration pattern
- ReplMcpServerOptions: server name, version, tool naming separator,
  interactivity mode, command filter, resource fallback, prompt
  registration
- McpSchemaGenerator: generates JSON Schema from ReplDocCommand
  metadata with full type mapping (18 Repl types → JSON Schema) and
  annotation mapping (CommandAnnotations → MCP ToolAnnotations)
- McpToolNameFlattener: flattens hierarchical route paths into flat
  MCP tool names, removing dynamic segments
- McpToolAdapter: reconstructs CLI tokens from route templates and
  JSON arguments for pipeline dispatch (Phase 2: full pipeline
  integration pending)
- ReplMcpServerTool: custom McpServerTool subclass with Repl-specific
  schema generation and pipeline dispatch
- McpServerHandler: MCP server lifecycle orchestration via
  McpServer.Create + StdioServerTransport

New test project: Repl.McpTests
- Schema generation tests (type mapping, required/optional, enums,
  descriptions, annotation mapping)
- Tool name flattening tests (separators, dynamic segment removal,
  collision patterns)
- Token reconstruction tests (route args, named options, interaction
  prefills)
Implements the McpInteractionChannel with progressive degradation for
runtime prompts in MCP mode (prefill → default → fail), and wires up
McpToolAdapter to dispatch tool calls through the full Repl pipeline.

McpInteractionChannel:
- AskChoice/Confirmation/Text: prefill from tool arguments, fall back
  to defaults or fail with descriptive McpInteractionException
- AskSecret: prefill-only (never elicitation/sampling for security)
- AskMultiChoice: prefill with comma-separated values
- ClearScreen: no-op, WriteProgress/WriteStatus: no-op (Phase 3: map
  to MCP progress notifications)
- DispatchAsync<T>: throws NotSupportedException

McpToolAdapter pipeline dispatch:
- Creates scoped execution context per tool call with StringWriter
  output capture, StringReader input, AnsiMode.Never
- Injects McpInteractionChannel via McpServiceProviderOverlay
- Executes through CoreReplApp.RunWithServicesAsync with reconstructed
  CLI tokens
- Returns captured output as CallToolResult

Integration tests:
- mcp serve route registration (default and custom command names)
- Hidden context verification
- AutomationHidden filtering
- Enriched metadata propagation

Interaction channel tests:
- All interaction methods with prefill, defaults, and fail modes
- Secret security (always fails without prefill)
- Multi-choice comma parsing
- DispatchAsync NotSupportedException
Extends McpServerHandler to generate MCP resources from AsResource()
and ReadOnly commands, and collects MCP prompts from AsPrompt() commands
and explicit options.Prompt() registrations (with override-on-collision
semantics).

McpServerHandler:
- GenerateResources: creates McpServerResource instances with repl://
  URI templates, dispatching reads through the tool adapter pipeline
- CollectPrompts: merges AsPrompt() commands with options.Prompt()
  registrations; options.Prompt() wins on name collision
- Capabilities are now conditional: Resources/Prompts capabilities
  only advertised when primitives exist
- Refactored RunAsync into BuildServerOptions for readability

Sample 08-mcp-server:
- Full-featured sample demonstrating all MCP primitives:
  Resources (contacts list), Tools (CRUD with annotations),
  Prompts (explain-error), AutomationHidden (clear screen)
- README with agent configuration for Claude Desktop, VS Code,
  and MCP Inspector
Connects the Repl interaction channel's WriteProgressAsync to MCP
progress notifications via McpServer.NotifyProgressAsync. When a
command reports progress during execution, the MCP client receives
real-time progress updates with the progress token from the original
tool call.

- ReplMcpServerTool: extracts ProgressToken from RequestContext
- McpToolAdapter: passes server + progressToken through to the
  interaction channel
- McpInteractionChannel: reports progress via NotifyProgressAsync
  when server and progress token are available
Completes the McpInteractionChannel progressive degradation and
connects remaining MCP server features.

Elicitation (Tier 2):
- AskChoiceAsync: uses UntitledSingleSelectEnumSchema
- AskConfirmationAsync: uses BooleanSchema
- AskTextAsync: uses StringSchema
- Only active when InteractivityMode.PrefillThenElicitation and
  client advertises Elicitation capability

Sampling (Tier 3):
- Falls back to LLM sampling when elicitation is unavailable
- Active for PrefillThenElicitation and PrefillThenSampling modes
- Uses CreateMessageRequestParams with structured prompts

WriteStatusAsync → MCP logging:
- Maps to notifications/message with LoggingLevel.Info
- Uses SendNotificationAsync with LoggingMessageNotificationParams

list_changed on routing invalidation:
- CoreReplApp.RoutingInvalidated event fires after InvalidateRouting()
- McpServerHandler subscribes and refreshes the ToolCollection
- Collection mutations auto-emit list_changed via the SDK
- Properly unsubscribes on server shutdown
- Add Repl.Mcp/README.md (fixes NU5039 pack error)
- Remove Condition on README.md pack include (Exists check unreliable
  during CI pack with different working directories)
- Publish NuGet packages individually so a rejected package (e.g. new
  package awaiting validation) does not block the others
- Re-runs are safe: --skip-duplicate already handles existing versions
Documentation:
- Add comprehensive docs/mcp-server.md covering all MCP features:
  annotations, tool/resource/prompt guide, JSON Schema mapping,
  interaction degradation, client compatibility matrix (per-agent
  columns, dated March 2025), agent configuration for 4 clients,
  NuGet MCP packaging, and full working example
- Rewrite src/Repl.Mcp/README.md as a concise teaser with link to
  full docs
- Add MCP doc link to main README documentation table

API fixes:
- Rename CommandName → ContextName in ReplMcpServerOptions (it's a
  context segment, not a command name)
- Fix WithDetails xmldoc: remove premature --help --verbose mentions;
  Details is for agent descriptions and documentation export, not
  terminal help (which doesn't support verbose mode yet)

Sample 08-mcp-server:
- Restructure with Context("contact", ...) pattern
- Replace explain-error prompt with troubleshoot prompt that clearly
  demonstrates how prompts guide agent behavior
- Move UseMcpServer() before Map() calls to show order independence
devin-ai-integration[bot]

This comment was marked as resolved.

- ReplMcpServerTool: LongRunning → Tool.Execution.TaskSupport =
  Optional (experimental SDK API, MCPEXP001 suppressed)
- docs: expand OpenWorld — latency, failure modes, and security scope
- docs: LongRunning shows actual MCP hint instead of "future"
- docs: add "Why annotations matter" with parallelization guidance
- docs: per-agent compatibility matrix with Codex and Overall column
- docs: remove env/API key clutter from agent config examples
- docs: rename CommandName → ContextName throughout
Addresses two bugs found in PR review:

1. Resources and prompts were generated but never added to
   McpServerOptions — only ToolCollection was populated. Now
   ResourceCollection and PromptCollection are populated too.

2. AsPrompt() commands returned the static description instead of
   dispatching through the Repl pipeline. Prompts now invoke the
   command handler with arguments via McpToolAdapter, so the handler
   return value becomes the prompt message text as documented.
Integration tests:
- McpTestFixture: connects a real MCP client to the Repl MCP server
  via in-process pipes (System.IO.Pipelines) — no stdio
- tools/list: verifies tool discovery, hidden/AutomationHidden
  exclusion, context flattening, JSON Schema correctness
- tools/call: verifies pipeline dispatch, output capture, context
  command routing
- Schema: verifies format hints (uuid), required fields, optional
  parameters, description combining

Fix list_changed scope:
- SubscribeToRoutingChanges now refreshes resources and prompts too,
  not just tools
- Extracted RefreshCollection helper for consistent refresh pattern

Fixes two PR review bugs:
- Resources and prompts now registered in McpServerOptions
  (ResourceCollection + PromptCollection)
- AsPrompt() commands now dispatch through the Repl pipeline
  instead of returning static description
The publish step tolerates individual package failures (new packages
may need NuGet validation, re-runs use --skip-duplicate), but now
throws when zero packages succeed — catching broken API keys or
misconfigured sources.
github-code-quality[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Bugs fixed:
- CI: $failed count now filters nupkg-only for the succeeded check
  (snupkg failures no longer cause negative count)
- McpTestFixture: factory no longer leaks pipes and CTS — all
  resources are created as locals and passed into the constructor
- Null dereference in E2E tests: explicit NotBeNull assertions
  before accessing TextContentBlock.Text

Behavior fixes:
- AsPrompt() commands are no longer exposed as tools (IsToolCandidate
  now checks !command.IsPrompt)
- ReplRuntimeChannel.Programmatic is now produced by the runtime:
  McpToolAdapter sets ReplSessionIO.IsProgrammatic before executing,
  and CoreReplApp.ResolveCurrentRuntimeChannel checks it first
- ResourceFallbackToTools: xmldoc now notes it's reserved for
  future implementation

New tests:
- E2E: AsPrompt commands excluded from tools/list
- Integration: Programmatic channel excludes admin modules
Three new options in ReplMcpServerOptions:
- ResourceFallbackToTools (default: false) — resources also appear
  as read-only tools for clients without resource support
- PromptFallbackToTools (default: false) — prompts also appear
  as tools for clients without prompt support
- AutoPromoteReadOnlyToResources (default: true) — ReadOnly commands
  are auto-promoted to resources; set false to require explicit
  AsResource()

Tool generation now has three distinct phases:
1. Core tools (regular commands, ReadOnly+AsResource)
2. Resource fallback tools (resource-only commands, when opt-in)
3. Prompt fallback tools (prompt commands, when opt-in)

Resource-only (.AsResource() without .ReadOnly()) commands are NOT
tools by default — they're resources only. ReadOnly commands are
always tools (the annotation is behavioral, not a primitive marker).

Includes full test matrix (9 tests) covering all marker combinations
and option interactions.

Documentation updated with mapping matrix table and fallback usage
example.
github-code-quality[bot]

This comment was marked as resolved.

- AskConfirmationAsync now throws in PrefillThenFail mode when no
  prefill and defaultValue is false (consistent with other Ask methods)
- Remove dead answer: prefix handling from ReconstructTokens —
  PrepareExecution already separates prefills upstream, so
  ReconstructTokens never receives answer: keys
- Update tests to reflect the corrected separation of concerns
devin-ai-integration[bot]

This comment was marked as resolved.

- McpToolAdapter._toolRoutes: use ConcurrentDictionary for thread-safe
  access during concurrent routing invalidation
- CI: pass NuGet API key via env var instead of inline single-quoted
  string to avoid breakage if key contains special characters
- McpServerHandler.RunAsync: set IsProgrammatic = true before building
  the doc model so module presence predicates see Programmatic channel
  (not Cli which is the caller's context)
- RoutingInvalidated handler: save/restore IsProgrammatic around doc
  model rebuild so the handler's caller context is not affected
- ReplSessionIO.SetSession: save/restore IsProgrammatic in SessionScope
  alongside IsHostedSession, preventing leaks across nested sessions
github-code-quality[bot]

This comment was marked as resolved.

improve collision detection, and document limitations

Filtering fixes:
- GenerateResources now applies IsToolCandidate — Hidden and
  AutomationHidden commands are excluded from resources/list
- CollectPrompts now applies IsToolCandidate — Hidden and
  AutomationHidden commands are excluded from prompts/list

Collision detection:
- AddTool now uses Dictionary<name, path> to distinguish same-command
  re-registration (silently skipped) from different-route collisions
  (throws InvalidOperationException at startup with both paths)

Known limitations documented:
- Collection parameters (List<T>) not correctly bound from MCP
- Parameterized resources cannot be invoked via resources/read

New tests: Hidden/AutomationHidden filtering on resources and prompts,
collision detection (same-phase and cross-phase), no-duplicate for
ReadOnly+AsResource with fallback enabled.
and always-on capabilities

- ReconstructTokens: omit empty tokens for missing optional route
  segments (report {period?} without period no longer emits "")
- McpSchemaGenerator: detect List<T>, IList<T>, T[] types and emit
  { type: array, items: { ... } } instead of { type: string }
- CollectPrompts: detect flattened-name collisions between different
  prompt routes (same behavior as tools)
- BuildCapabilities: always advertise Tools, Resources, and Prompts
  capabilities even when initial collections are empty — routing
  invalidation may add them later and capabilities cannot be added
  after the initialize handshake
- Document known limitation: avoid resources on conditional modules
ReplMcpServerPrompt (new):
- Custom McpServerPrompt subclass that derives prompt arguments from
  the Repl doc model (route args + options) instead of reflecting
  the delegate signature. Agents now see the real parameters.
- Surfaces execution failures as McpException instead of returning
  error text as a successful prompt result.

Context WithDetails:
- Wire ContextDefinition.Details through to ReplDocContext.Details
  so context-level WithDetails() is no longer a no-op.

E2E tests:
- prompts/list: verifies prompt arguments are derived from command
  parameters
- prompts/get dispatch: deferred (requires full pipeline context)

Replaces the old CreatePipelinePrompt/ConvertPromptArguments approach
with a proper McpServerPrompt subclass.
McpTestFixture now uses McpServerHandler.BuildServerOptions (internal)
instead of building the server manually. This exercises the full
tool/resource/prompt generation pipeline with fallback options,
filtering, and collision detection.

New E2E tests (Given_McpFallbackEndToEnd):
- Resource-only NOT a tool when fallback disabled
- Resource-only IS a tool when fallback enabled
- ReadOnly+AsResource always a tool regardless of fallback
- Prompt-only NOT a tool when fallback disabled
- Prompt-only IS a tool when fallback enabled
- Prompt still in prompts/list with fallback enabled
- Both fallbacks enabled: all commands visible as tools
- AutomationHidden excluded even with fallbacks enabled
…merable<T>

- McpToolAdapter.ClearRoutes: called before rebuilding on routing
  invalidation so removed commands are no longer callable
- Resource callbacks: throw McpException when IsError is true instead
  of returning error text as successful content
- McpSchemaGenerator: recognize IEnumerable<T> as array type
- ResolveChoiceIndex: add unambiguous prefix matching (exact first,
  then single prefix match) — same as console MatchChoiceByName
- ParseBool: reject unrecognized values instead of coercing to false
  (accepts yes/no, true/false, y/n, 1/0 only)
- ParseMultiChoice: enforce MinSelections/MaxSelections from
  AskMultiChoiceOptions
Add TransportFactory option to ReplMcpServerOptions for custom transports
(WebSocket, SSE) and wire it through McpServerHandler.RunAsync. Add error
path tests for ParseBool, prefix matching, and multi-choice min/max validation.
devin-ai-integration[bot]

This comment was marked as resolved.

…ation throw, debounce

- Restore IsProgrammatic AsyncLocal in RunAsync finally block
- Emit DestructiveHint=false for ReadOnly tools (MCP spec defaults to true)
- AskConfirmationAsync throws in PrefillThenFail regardless of defaultValue
- Resolve DI-registered options in McpModule handler (AddReplMcpServer fix)
- Replace O(n) FirstOrDefault lookups with dictionary in tool/resource generation
- Debounce RoutingInvalidated to coalesce rapid Map() calls into single rebuild
- Add lock around collection refresh to prevent concurrent interleaving
- Debounce test uses FakeTimeProvider with sync test method for
  deterministic time control (callbacks fire during Advance())
- Concurrent session test validates tool isolation between two
  independent MCP sessions running in parallel
- McpServerHandler resolves TimeProvider from DI for testable timers
Replace the parameterless McpServerResource.Create callback with
ReplMcpServerResource — a custom subclass that extracts URI template
variables from the request URI and forwards them to the Repl pipeline.

Resources like `config {env}` now expose `repl://config/{env}` and
correctly pass `env=production` when read via `repl://config/production`.
New `ResourceUriScheme` option (default "repl") controls the URI scheme
for resources. Apps can set e.g. `o.ResourceUriScheme = "myapp"` to get
`myapp://status` instead of `repl://status`.
devin-ai-integration[bot]

This comment was marked as resolved.

…assthrough

- Wrap RebuildCollections in try/catch so timer callback exceptions don't
  crash the process — server continues with stale routes
- Optimistic concurrency: build new routes in a temp adapter, then swap
  atomically under lock — no window where concurrent tool calls fail
- Pass request.Server to prompt execution (was null, disabling elicitation)
- Add test: server survives rebuild failure with stale routes
- Fix null deref warning and empty catch comment from code quality reviews
@carldebilly carldebilly marked this pull request as ready for review March 19, 2026 02:03
github-code-quality[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Change `bool defaultValue = false` to `bool? defaultValue = null` across
the interface and all implementations. This aligns with AskChoiceAsync
(int?) and AskTextAsync (string?) — explicit defaults are returned before
mode checks, null means "no default" and triggers PrefillThenFail throw.
Expose public API to build McpServerOptions from a Repl app's command
graph without starting a server. Enables two advanced scenarios:

- Stdio-over-anything: use TransportFactory with WebSocket/named pipes
- MCP-over-HTTP: pass options to ASP.NET Core MapMcp or McpServer.Create

Add docs/mcp-advanced.md with recipes for both patterns.
BuildMcpServerOptions enables N concurrent sessions over custom
transports — create one McpServer per connection with shared options.
Each session is isolated via AsyncLocal scopes.
@carldebilly carldebilly merged commit a8b70eb into main Mar 19, 2026
11 checks passed
@carldebilly carldebilly deleted the dev/cdb/mcp-format branch March 19, 2026 03:14
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