Skip to content
Go back

From One Giant ‘Skill’ to a Real Agent Library: Temporal + .NET Aspire Debugging With Copilot

Published:  at  10:00 AM

Header image: Temporal + Aspire + Copilot agent skills

1) Introduction

I’ve been thinking a lot about how debugging has evolved.

It started (for many of us) with the rubber duck: a physical object you explain your code to, until your brain notices the bug. Then we got search, then StackOverflow, then docs-with-better-search. Now we’ve got something that’s simultaneously exciting and dangerous: a conversational “developer pair” that can see our code, reason about it, and (when you let it) run tools.

Temporal workflows are a perfect stress test for this.

Temporal isn’t “just background jobs.” Your workflow code is durable, replayed, and must remain deterministic forever. That single fact turns a lot of typical .NET habits into foot-guns: DateTime.UtcNow, Guid.NewGuid(), Task.Delay, Task.Run, locks, I/O, static mutable state… all the things you might do in normal code can become a replay-time problem.

So if you point a generic AI assistant at a Temporal codebase, you’ll often get suggestions that look fine in normal C# and are deeply wrong inside a workflow.

This post is about the setup I use during local development:

The goal is not “let the agent run my system.” The goal is: give the agent enough context to help you investigate when your Aspire-based Temporal app is running locally — without accidentally suggesting nondeterministic workflow code or running risky commands against the wrong environment.

Repository: https://github.com/rebeccapowell/dotnet-temporal-ai


2) Prerequisites

This is a development-time setup. You should be able to run your app locally and see it in the Aspire dashboard.

If your project is Aspire-based, you’ll also want the Aspire CLI available (so you can run aspire mcp init).


3) Understanding MCP and Agent Modes

There are two related ideas here:

In plain terms: MCP is how you give an agent a controlled, inspectable toolbox.

Here’s the mental model I use:

ComponentTypePurposeRisk
Temporal .NET Agent (router)AgentAlways-on policy + routes to skillsLow (text-only)
Temporal skills (determinism, versioning, testing…)SkillsTask-shaped guidance loaded on demandLow (text-only)
Temporal Docs MCPMCP ServerAuthoritative backstop for API details and edge cases (all languages)Low (read-only)
Aspire Dashboard MCPMCP ServerLive state: resources, console logs, structured logs, tracesMedium (can run commands via MCP)
(Optional) Read-only Temporal CLI SkillSkillSafe inspection commands (list/describe/show/query/trace/...)Low
(Optional) Dangerous Temporal CLI SkillSkillCleanup actions (terminate/cancel/delete/reset/...) restricted to local devHigh (if misused)

Notice what’s missing: I’m not giving the agent a general “run anything on my machine” permission. Everything is scoped to dev investigation.


4) From one giant “skill” to a real agent library

This is the part I wish I’d realised earlier.

I started by putting everything into a single, opinionated “Temporal .NET skill” markdown file:

It worked… but it didn’t scale. It was always loaded, always in-context, and it kept growing.

Eventually I realised what I’d actually built:

A real “skill” is supposed to be task-shaped and loaded only when relevant. A custom agent should stay small: identity, priorities, routing, refusal rules.

So I changed direction and rebuilt this as an agent library, using GitHub’s recommended structure:

.github/
  copilot-instructions.md
  agents/
  skills/
  prompts/

What this structure gives me

This is dramatically easier to maintain than one giant instruction blob.


Temporal workflows are “normal C#” until they replay. Some analyzers can be actively unhelpful inside workflow code.

Temporal recommends .editorconfig settings to suppress analyzers that are misleading in workflows.

Important nuance: I recommend putting these rules in the Temporal workflows folder (scoped), not at repo root (global), unless you have a compelling reason to apply workflow constraints everywhere.

Example:

Here’s the workflow-specific snippet:

##### Configuration specific for Temporal workflows #####
[*.workflow.cs]

# We use getters for queries, they cannot be properties
dotnet_diagnostic.CA1024.severity = none

# Don't force workflows to have static methods
dotnet_diagnostic.CA1822.severity = none

# Do not need ConfigureAwait for workflows
dotnet_diagnostic.CA2007.severity = none

# Do not need task scheduler for workflows
dotnet_diagnostic.CA2008.severity = none

# Workflow randomness is intentionally deterministic
dotnet_diagnostic.CA5394.severity = none

# Allow async methods to not have await in them
dotnet_diagnostic.CS1998.severity = none

# Don't avoid, but rather encourage things using TaskScheduler.Current in workflows
dotnet_diagnostic.VSTHRD105.severity = none

Even if you never touch agent skills, this is a small quality-of-life improvement: it reduces noise and avoids unhelpful refactor pressure inside workflow code.


6) Configuring the Temporal Docs MCP as a backstop

I treat documentation MCP servers as backstops.

The agent + skills tell the assistant how to think about Temporal in .NET. The docs MCP tells it exact API details when I ask something like:

Temporal provides a hosted docs MCP endpoint (powered by Kapa) that tools like VS Code, Cursor, and Claude Code can connect to.

Important distinction:

Example .vscode/mcp.json layout (Aspire + Temporal docs)

Aspire can generate a working .vscode/mcp.json for you (more on that next). You can add additional MCP servers alongside it.

Here’s an example .vscode/mcp.json that connects to both Aspire (local stdio) and Temporal docs (hosted http):

{
  "servers": {
    "aspire": {
      "type": "stdio",
      "command": "aspire",
      "args": ["mcp", "start"]
    },
    "temporalDocs": {
      "type": "http",
      "url": "https://temporal.mcp.kapa.ai"
    }
  }
}

Getting the Temporal Docs MCP

You can naivigate the Temporal Docs website and via the search that is powered by Kapa.ai, you can follow the instructions to add them to your AI Agent powered IDE of choice.

Screenshot: Temporal Docs MCP “Connect to AI Tools” showing “Copy MCP URL”

I’ve chosen to add them to Visual Studio Code:

Screenshot: Temporal Docs MCP “Connect to AI Tools” showing “Install in vscode”


7) Integrating Aspire MCP for live observability

This is where the setup becomes truly useful.

When the agent can inspect live resources and live telemetry, you can ask it questions you’d normally answer by clicking around dashboards and grepping logs.

Step 1: initialize Aspire MCP

From your AppHost folder, run:

aspire mcp init

Aspire detects your environment and generates config for you. For VS Code, it creates or updates:

What Aspire MCP gives you

Aspire MCP provides tools like:

The three pillars (and what the agent can do with them)

Aspire’s telemetry fundamentals are the classic three pillars:

PillarWhat it answersWhat I ask the agent
Logs“What happened?”“Show me errors for the worker resource in the last minute.”
Traces“Where did time go?”“Find the trace where this request stalls.”
Metrics“Is it healthy?”“Is latency trending up?” (often via dashboard UI rather than MCP)

Under local dev, Aspire configures OpenTelemetry environment variables automatically (service name, instance id, OTLP endpoint), and the dashboard receives OTLP data.

Example .vscode/mcp.json (Aspire-only)

This is the format Aspire generates for VS Code:

{
  "servers": {
    "aspire": {
      "type": "stdio",
      "command": "aspire",
      "args": ["mcp", "start"]
    }
  }
}

Troubleshooting note

If you restart your AppHost or run multiple AppHosts, the agent may need to re-select the correct one. Aspire MCP supports:

Call those explicitly in the troubleshooting section when readers get “wrong app” results.


8) (Optional) Building the read-only Temporal CLI skill

The Temporal CLI is incredibly useful during development, but I want it behind a safety boundary.

The read-only skill is for investigation:

CommandWhy it matters
temporal workflow listFind running/stuck workflows
temporal workflow describeCurrent status + details
temporal workflow showHistory/events (debugging gold)
temporal workflow queryRead-only state inspection
temporal workflow stackDebug stacks (when supported)
temporal workflow countQuick sanity checks
temporal workflow traceTrace a workflow execution
temporal workflow startEdge case: create new execution (safe-ish)
temporal workflow executeEdge case: run and wait (safe-ish)

“Safe-ish” means: it creates something new, but doesn’t modify existing workflows.

Read-only CLI skill (example)

If you want to add this to your own agent library, this is the pattern I use:

---
name: Temporal CLI (Read-Only)
description: Safely inspect workflow state via Temporal CLI during local development.
---

# Rules

- Use these commands only for investigation.
- Prefer `list`, then `describe`, then `show` (history) for deep debugging.
- Never run mutating commands in this skill.
- Default to local development address (for example `localhost:7233`) unless the user explicitly provides a different address.

# Allowed Commands

- `temporal workflow list`
- `temporal workflow describe`
- `temporal workflow show`
- `temporal workflow query`
- `temporal workflow stack`
- `temporal workflow count`
- `temporal workflow trace`
- `temporal workflow start` (edge case)
- `temporal workflow execute` (edge case)

# Output expectations

- Summarize results first.
- If output is large, provide a short summary and show the exact command used.
- Ask a follow-up question if critical parameters are missing (namespace, task queue, workflowId).

9) (Optional) Dangerous Temporal CLI skill (local dev only)

This skill exists for one reason: cleanup during investigation.

If you have a workflow stuck in a replay-fail loop, or you want to wipe out noisy local executions, you need tools like terminate or reset.

But those are too dangerous to run against anything but your local dev environment.

Localhost-only rule

This skill must refuse anything that isn’t clearly local:

Allowed hostnames:

Blocked:

Dangerous CLI skill (example)

---
name: Temporal CLI (Dangerous / Local Only)
description: Mutating Temporal CLI operations for local development cleanup only. Refuses non-local endpoints.
---

# Guardrails

1. Refuse any command where `--address` is not local.
2. If `--address` is not provided, assume `localhost:7233`.
3. Before executing, warn what will change and require confirmation ("Type YES to proceed").
4. Prefer the least destructive action.

# Allowed Commands (Mutating)

- `temporal workflow terminate`
- `temporal workflow cancel`
- `temporal workflow signal`
- `temporal workflow delete`
- `temporal workflow reset`

# Output expectations

- Show the exact command you will run.
- Require explicit confirmation.
- After running, report the observed effect (e.g., workflow status changed).

10) Putting it all together: a debugging session (non-determinism)

Here’s a scenario I’ve personally watched happen:

You add a tiny improvement to a workflow:

“Let’s include the current time in the workflow state so we can show it in the UI.”

And you write:

// ❌ Don’t do this in a workflow
var now = DateTime.UtcNow;

Locally, it “works”… until a replay happens.

Step 1: Ask the agent before you ship the bug

Because the agent library is determinism-first, it should immediately tell you:

Step 2: Verify via Aspire MCP

Ask:

The agent uses list_resources and list_console_logs / list_structured_logs and points you to replay-related failures.

Step 3: Confirm via Temporal CLI (read-only)

Ask:

The agent uses temporal workflow describe/show to confirm repeated failures.

Step 4: Fix the code deterministically

// ✅ Deterministic time inside a workflow
var now = Workflow.Now;

If you want the official signature details, that’s when the Temporal docs MCP is useful.

Step 5: Cleanup (dangerous local skill)

If your local workflow is stuck in a replay loop and you want a clean slate, terminate it locally:

This is the whole layered approach working as intended.

Screenshot: Agent debugging with traces and logs


11) Conclusion

This isn’t about outsourcing judgment to an agent.

It’s about giving yourself better tools during local development:

If you’re building Temporal workflows under Aspire, what’s the most painful class of bug you hit during replay: time, randomness, async, or something else? And what guardrails have you put in place to stop it happening twice?


Suggest Changes
Share this post on:

Next Post
Building and Publishing a .NET Aspire Hosting Extension for Webhook Testing