Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
159135 stories
·
33 followers

Coffee and Open Source Conversation - Brady Gaster

1 Share
From: Isaac Levin
Duration: 1:17:26
Views: 4

Based near Seattle, Brady Gaster aspires to make it easy for .NET developers to party in the cloud as the product manager behind Visual Studio’s Azure right-click publish and Connected Services features. He also helps with Aspire tooling support in Visual Studio and Visual Studio Code. When not designing new tooling features or building demos with teammates, he’s trail running 5Ks, parenting two teens, mixing new 174 BPM drum’n’bass sets, or producing his own electronic music.

You can follow Brady on Social Media
https://github.com/bradygaster
https://www.linkedin.com/in/bradygaster/
https://bsky.app/profile/bradygaster.bsky.social
https://x.com/t80chip
https://www.youtube.com/bradygaster

PLEASE SUBSCRIBE TO THE PODCAST

- Spotify: http://isaacl.dev/podcast-spotify
- Apple Podcasts: http://isaacl.dev/podcast-apple
- Google Podcasts: http://isaacl.dev/podcast-google
- RSS: http://isaacl.dev/podcast-rss

You can check out more episodes of Coffee and Open Source on https://www.coffeeandopensource.com

Coffee and Open Source is hosted by Isaac Levin (https://twitter.com/isaacrlevin)

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft Entra ID Token Protection

1 Share

For years, most identity security conversations have focused on protecting the authentication process. We have moved from passwords to multifactor authentication, Conditional Access, device compliance, risk-based policies, passwordless authentication, and increasingly phishing-resistant methods such as passkeys and FIDO2 security keys. All of these controls make it significantly harder for an attacker to authenticate as another user.

The problem is that authentication is only the beginning of the session.

Once Microsoft Entra ID has authenticated a user, tokens are issued that allow applications to access resources without forcing the user to authenticate every few minutes. This is fundamental to how Microsoft 365 and modern cloud authentication work, but those tokens also become valuable targets. If an attacker can steal a usable token or authenticated session, they may no longer need the user’s password or need to complete MFA again.

This is where Microsoft Entra Token Protection becomes important. Instead of concentrating solely on how a user authenticates, Token Protection helps protect what happens after authentication by making supported tokens more difficult to replay from another device.

Why Token Theft Is a Problem

When a user signs in to Microsoft 365, Microsoft Entra ID does not continuously ask them for their password and MFA response every time Outlook downloads an email or Teams makes a request. Instead, authentication results in tokens that applications can use to access services on the user’s behalf.

There are several types of tokens and session artifacts involved in Microsoft Entra authentication, including access tokens, refresh tokens, Primary Refresh Tokens (PRTs), session cookies, and device-related credentials. They serve different purposes and have different lifetimes, but from an attacker’s perspective, the objective can be very similar: obtain something that represents an authenticated user.

This changes the attack considerably. Rather than attempting to authenticate as Liam using Liam’s password, the attacker attempts to become the session that has already authenticated as Liam.

That distinction is important because many of our strongest controls sit at the authentication boundary. If an attacker steals the resulting session rather than the credentials used to create it, simply strengthening the password policy does very little to solve the problem.

MFA Does Not Automatically Protect the Session

MFA remains one of the most important identity security controls we can deploy, and organizations should absolutely continue using it. However, MFA primarily helps protect the authentication process. It does not mean that every token or session created after successful authentication is automatically protected against theft.

Adversary-in-the-Middle phishing is a good example. Instead of presenting the victim with a completely fake login page, an attacker can proxy the authentication process between the user and the legitimate identity provider. The user may authenticate against the real Microsoft Entra ID service and even successfully complete MFA, while the attacker attempts to capture information associated with the resulting authenticated session.

The attacker has not necessarily defeated MFA. The legitimate user completed it for them.

Phishing-resistant authentication methods can significantly improve our defenses against this type of attack, but the bigger lesson is that identity security cannot end when MFA succeeds. We also need controls that protect the authenticated session that comes afterward.

The Problem with Bearer Tokens

Many access tokens work on the principle that possession of a valid token is enough to present it to the intended resource. The resource validates the token and, assuming everything checks out, accepts it as proof that the request is authorized. That model works extremely well for usability and scalability, but it creates an obvious problem when tokens can be copied. If an attacker steals a usable bearer token and can successfully replay it from another system, possession of the token can effectively become possession of the user’s authenticated identity for whatever access that token provides.

This is the problem Token Protection is designed to make considerably harder.

Instead of allowing a supported token to exist independently of the device, Token Protection introduces a cryptographic relationship between the token and the device on which the authentication session was established. Microsoft refers to this concept as token binding and sender-constrained tokens.

The important idea is straightforward: stealing the token should not necessarily be enough to use it.

What Is Microsoft Entra Token Protection?

Microsoft Entra Token Protection is a Conditional Access session control designed to reduce the risk of token replay attacks. It requires supported sign-in session tokens to be cryptographically bound to the device on which they were originally issued.

On supported Windows devices, this works with the Primary Refresh Token and the cryptographic material associated with the device. Where available, hardware protection such as the Trusted Platform Module can help protect the secrets involved. The result is a significant change in how we think about a stolen token.

An attacker might successfully obtain the token, but the token is associated with cryptographic material that remains on the legitimate device. Copying the token onto another computer therefore does not automatically provide everything required to successfully replay the authenticated session against a protected resource. Rather than simply asking whether a token is valid, the authentication architecture can now consider whether that token is being presented by the device it belongs to.

How Token Protection Fits Into Authentication

At a high level, a supported Windows device establishes its relationship with Microsoft Entra ID and receives a Primary Refresh Token. That PRT and its associated cryptographic material allow the device to participate in Single Sign-On without continually asking the user to authenticate again.

When Token Protection is required through Conditional Access, supported applications and resources expect the sign-in session to use the appropriate device-bound protection. The token is therefore not intended to behave like a completely portable credential that can simply be copied from one system to another.

This creates an additional security boundary around the authenticated session. An attacker now has to overcome more than simply obtaining a valid token because the legitimate device becomes part of proving that the token can be used.

That is an important evolution in identity security. We are moving from protecting credentials to protecting authentication, and now towards protecting the session and the tokens created by that authentication.

Conditional Access Is Where We Enforce It

Token Protection is configured through Microsoft Entra Conditional Access using the Require token protection for sign-in sessions session control.

This makes Token Protection another component of the Conditional Access architecture rather than a separate security product. Policies can determine which users, applications, resources, and conditions require protected sessions. However, this is not a setting I would simply enable across the entire tenant.

Token Protection has specific requirements around supported platforms, clients, authentication scenarios, and resources. Unsupported combinations can cause authentication failures, so Microsoft recommends beginning with Report-only mode and reviewing the impact before moving to enforcement.

This is especially important when dealing with Microsoft 365. It may be tempting to target the complete Office 365 application group, but support needs to be validated for the applications and resources actually being used. A broad policy can create unintended problems for clients that do not support the required token behavior.

Understand the Device Requirement

One of the most important things to understand about Token Protection is its relationship with the device. The protection relies on having the appropriate device-bound authentication state, including the Primary Refresh Token. An unmanaged or unregistered device does not automatically have the same capabilities as a properly registered Windows device. This is why Token Protection fits particularly well into a broader device-based Conditional Access strategy.

If your organization already requires managed or compliant devices for important applications, Token Protection adds another layer to that trust relationship. The device is no longer simply something Conditional Access checks before allowing access. It can also become part of the cryptographic protection of the authenticated session. That makes device identity increasingly important within the overall identity architecture.

Token Protection Should Be Part of a Layered Strategy

Token Protection does not replace the other controls we already use. It strengthens a particular part of the authentication chain, which means it works best when combined with the other layers surrounding identity and endpoint security. A mature approach would typically include controls such as:

  • Phishing-resistant authentication where appropriate.
  • Conditional Access based on user, device, application, location, and risk.
  • Managed and compliant devices.
  • Microsoft Entra ID Protection for user and sign-in risk.
  • Continuous Access Evaluation.
  • Endpoint protection and monitoring.
  • Token Protection for supported sessions and resources.

The important point is not that every organization must immediately enable every control. It is that each one solves a different part of the problem.

MFA makes unauthorized authentication harder. Device compliance helps establish whether the endpoint should be trusted. Token Protection makes supported tokens harder to replay elsewhere. Continuous Access Evaluation helps services react when circumstances change after the session has already been established. Together, those controls provide a much stronger security boundary than any one of them could provide on its own.

Where Continuous Access Evaluation Fits

Continuous Access Evaluation, or CAE, is worth mentioning because it also deals with what happens after authentication, but it solves a different problem.

Traditionally, once an access token was issued, that token could continue being valid until its configured lifetime expired. If something important changed during that period, there could be a delay before the existing session was forced to respond to that change.

CAE allows supported services and clients to react to critical events during an existing session. If the user account is disabled, credentials change, or another supported security event occurs, access can be reevaluated rather than simply waiting for the existing token to expire naturally.

Token Protection and CAE therefore complement each other. Token Protection helps address whether a protected token can be replayed from somewhere it does not belong, while CAE helps determine whether an existing session should continue to be trusted when the surrounding security state changes. Both demonstrate why modern identity security increasingly needs to look beyond the initial login.

Planning a Token Protection Deployment

I would approach Token Protection in the same way I approach most significant Conditional Access changes: understand the environment first, build a targeted policy, observe what happens, and then progressively enforce it.

Start by identifying the users and applications where Token Protection provides the greatest value. Confirm that the devices and applications meet Microsoft’s current support requirements and make sure supported clients are current before introducing enforcement.

Then create the Conditional Access policy in Report-only mode. Review the sign-in logs, identify unexpected clients or unsupported scenarios, and understand what would have happened if the policy had been enforced.

For Windows deployments, client-app selection also needs attention. Browser and desktop authentication scenarios do not necessarily behave identically, and Microsoft specifically documents considerations around browser-based applications and MSAL.js. This is another reason not to treat Token Protection as a simple tenant-wide checkbox.

Once you understand the impact, start enforcement with a controlled population and expand from there.

Think About the Policies Around It

Token Protection should also make you review the rest of your Conditional Access architecture. For example, if Token Protection is enforced for supported Windows devices, what happens when the same user attempts to access the application from an unknown or unsupported platform? You do not want an attacker to avoid one control simply by presenting themselves through a different access path.

Complementary Conditional Access policies can require device compliance, restrict unsupported platforms, control unmanaged devices, and apply stronger authentication requirements where necessary.

This is why Conditional Access policies should be designed as a collection rather than individually. Each policy should contribute to the overall access model without accidentally creating a weaker alternative path around another control.

Why Token Protection Matters

For me, the biggest reason to pay attention to Token Protection is that it changes one of the assumptions we have traditionally made about authentication. We have spent years making it harder to steal passwords and harder to successfully authenticate with stolen credentials. MFA, passwordless authentication, Conditional Access, risk detection, and phishing-resistant credentials have all dramatically improved that part of the process.

Attackers adapt.

If authentication becomes harder to compromise, an authenticated session becomes increasingly attractive. Rather than stealing the credentials required to create the session, attackers can focus on stealing the session itself.

Token Protection helps change the economics of that attack. A token that only works in combination with cryptographic material associated with the legitimate device is considerably less useful when copied somewhere else. That does not eliminate token theft, but it makes successful replay significantly more difficult in supported scenarios.

Final Thoughts

Microsoft Entra Token Protection represents another important step in the evolution of identity security. We started by protecting passwords, then strengthened authentication with MFA and phishing-resistant credentials, added device trust and risk-based Conditional Access, and are now increasingly protecting what happens after authentication has completed. That last part matters.

Successful authentication should not mean we stop evaluating trust for the rest of the session. The device, the token, the application, the user’s risk, and changes to the surrounding security state all need to remain part of the decision.

Token Protection provides another layer by binding supported tokens to the device they were issued to, making it harder for an attacker to simply steal an authenticated session and replay it elsewhere.

I would not deploy it blindly across an entire tenant, but I would absolutely be evaluating it. Start with the supported scenarios, use Report-only mode, understand how it interacts with your existing Conditional Access policies, and then progressively introduce enforcement.

Protecting authentication is still important. Protecting what authentication creates is becoming just as important.

Read the whole story
alvinashcraft
32 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Azure Developer CLI extension framework is GA: build dev workflows for apps using Azure

1 Share

What parts of building and shipping on Azure still take too many steps?

Maybe you want a command that starts a project with your organization’s approved architecture and infrastructure. Maybe you need to connect deployments to an internal service catalog, run security checks before provisioning, or guide developers through a process that currently lives across scripts and documentation.

When we first introduced the azd extension framework, we invited developers to think about that wish list. The framework was still taking shape in beta, and we wanted to learn which workflows developers and platform teams needed azd to support.

Today, the azd extension framework is generally available.

This milestone gives engineering platform and product teams two ways to make developers’ work easier:

  1. You can build custom CLI workflows that help developers follow your organization’s infrastructure and operational practices as they build and ship.
  2. You can create customer-facing workflows that make it easier for developers to configure, deploy, and operate your product or service.

Microsoft Foundry is an example of the second model. Its growing suite of extensions gives developers workflows for building hosted agents, fine-tuning models, and more.

The idea behind extensions is simple. azd provides a consistent application lifecycle from local development to Azure. Extensions let you add the commands and capabilities your workflow needs while keeping developers in the same flow they use to build and deploy on Azure.

Diagram showing azd core supporting an extension framework with custom commands, lifecycle events, providers, and MCP tools that power Microsoft Foundry and custom extensions.

What changed since beta

The beta release established the core model for adding commands and lifecycle behavior to azd. Since then, the framework has expanded into a complete path for building, distributing, and operating extensions.

For GA, we stabilized the extension interfaces, broadened lifecycle and provider integration, added project-level extension requirements and version constraints, and improved authoring and publishing workflows through the azd x developer extension. Extension authors can now distribute releases through official, private, development, or nightly sources, while capabilities such as validation providers and MCP tools support more advanced developer workflows.

The growing Microsoft Foundry extension suite demonstrates how these pieces work together in production. GA means teams can now build on a stable framework while individual extensions continue to release and version their capabilities independently.

Make your supported path easier to follow

An azd extension is a modular component that adds commands and capabilities to the CLI. An extension can introduce a command namespace, automate a multistep workflow, connect to an Azure or third-party service, or run logic during an azd lifecycle event.

For engineering platform teams, this creates a practical way to turn platform practices into a usable developer experience. They can turn infrastructure and operational requirements into guided steps that developers follow as they build and ship.

Teams building developer products and services can use the same framework for customer-facing workflows. Instead of asking customers to understand the underlying resource model, coordinate several tools, and translate documentation into a working deployment, a product team can package the supported workflow as azd commands.

Microsoft Foundry is a good example. Its extensions help developers create and deploy hosted agents, connect agents to tools and data, inspect their behavior, and manage fine-tuning jobs. The product team can guide customers through these workflows in the terminal while building on the project, environment, provisioning, and deployment experience azd already provides.

Consider how many developer workflows begin with good guidance but become harder to follow across several tools. A developer might need to find the right template, copy a script, follow a documentation page, request access in another system, and remember which checks to run before deployment. Each step makes sense on its own. Together, they create friction and make the supported path harder to follow.

An extension can bring those steps into a guided azd workflow. Your team can create commands that:

  • Initialize projects with approved architecture, infrastructure, policy, and observability defaults
  • Connect applications to internal service catalogs, identity systems, and support processes
  • Run security, compliance, quality, and cost checks at the right lifecycle stage
  • Add deployment support for an internal platform or a specialized Azure service
  • Scaffold the recommended configuration for a product or service
  • Guide customers through setup, deployment, updates, and troubleshooting
  • Coordinate domain-specific workflows for data, IoT, Kubernetes, AI, or other platforms

Developers keep working in the CLI they already use to initialize, provision, deploy, monitor, and remove applications hosted on Azure or built with Azure services. Internal platform teams and product teams can maintain the workflow as a product, version it, and distribute it through an extension source.

The official extension source comes preconfigured with azd, so extensions can be installed, updated, and removed right away:

azd ext list
azd ext install <extension-id>
azd ext update <extension-id>
azd ext uninstall <extension-id>

That source also gives product teams a distribution path for customer-facing experiences. You can add development sources for preview extensions or create a private source for internal tools. The model should feel familiar if you have worked with npm, NuGet, or another package registry.

You can also distribute an extension without a source. When you want to hand someone a one-off build created with the azd x developer extension discussed later, azd x pack --bundle packages it into a portable .zip that installs from a file path or a link:

azd ext install ./contoso-platform_1.0.0.zip
azd ext install https://example.com/builds/contoso-platform_1.0.0.zip

A platform connected to the azd lifecycle

Custom commands are one part of the framework. Extensions can also work with azd project and environment context, prompt developers for input, respond to lifecycle events, and plug in their own framework, service target, provisioning, and validation providers.

This gives extension authors several useful building blocks:

  • New commands and nested command namespaces
  • Event handlers for workflows such as provision, package, and deploy
  • Custom service targets that package, publish, and deploy specialized services
  • Custom language and framework support for build, restore, and package steps
  • Custom provisioning providers that replace Bicep or Terraform for a project
  • Validation checks that run before provisioning and can warn on or stop a risky deployment
  • MCP tools that make extension capabilities available to agents and developer tools

These capabilities connect an extension to the same lifecycle developers already use:

azd init
azd provision
azd deploy
azd monitor
azd down

For example, an internal platform extension could scaffold a compliant service, register it in a service catalog, validate required configuration before provisioning, and report deployment metadata after azd deploy. The developer gets one connected flow. The platform team gets a maintainable place to encode and improve that flow.

Projects can also declare the extensions they need. A requiredVersions.extensions section in azure.yaml lists them with a version constraint:

requiredVersions:
  extensions:
    azure.ai.agents: ">=1.0.0"

azd installs them during azd init and checks them again before commands like azd up run.

Build with Microsoft Foundry through azd

When we first introduced the extension framework, we described AI workflows as one area where extensions could help. Microsoft Foundry has since turned that early example into a suite of customer-facing workflows.

The Foundry extensions coordinate project setup, model deployment, connections, tools, agent deployment, and operations under the azd ai namespace. Together, they give developers a consistent path from a local AI project to a running workload in Azure:

  • azure.ai.projects manages the active Foundry project context
  • azure.ai.agents scaffolds, deploys, runs, and invokes hosted agents
  • azure.ai.connections manages connections between Foundry projects and external systems
  • azure.ai.toolboxes manages versioned collections of tools for agents
  • azure.ai.skills manages reusable behavioral guidance
  • azure.ai.routines manages timers, schedules, and event-driven automation
  • azure.ai.inspector provides a browser-based experience for testing and debugging locally running agents

Foundry also provides the azure.ai.finetune extension for initializing fine-tuning projects, submitting and managing jobs, and deploying fine-tuned models from the terminal.

Each extension owns a focused part of the experience, and they come together under one command structure. You can install the hosted-agent extension bundle through the microsoft.foundry meta-extension:

azd ext install microsoft.foundry

You can also install and update individual extensions when you want a smaller set of capabilities:

azd ext install azure.ai.agents azure.ai.routines azure.ai.finetune
azd ext upgrade azure.ai.connections

For repeatable CI environments, install a specific extension version:

azd ext install azure.ai.agents --version 1.0.0-beta.9

This modular model lets the Foundry experience grow while keeping each capability independently versioned. Developers can install the full suite or choose the tools that match their workflow.

Once installed, the extensions work with the familiar azd lifecycle. You can initialize a hosted agent project, provision its Azure resources, deploy the agent, and remove the resources when you finish:

azd ai agent init
azd up
azd down

The project can define its services, infrastructure, model deployments, and agent configuration in azure.yaml and Bicep. That configuration stays with the application in source control and can move across development, test, and production environments.

The Foundry suite gives developers immediate value from the extension framework. Install the extensions, then use the pieces you need to build and operate your AI workflow through azd.

Some Foundry capabilities remain in preview and will continue to evolve. Check the Microsoft Foundry extension documentation for the current extension list, requirements, and feature status.

What is included in the GA release

The GA milestone applies to the azd extension framework: the parts of azd that let teams build, distribute, install, and run extensions as part of the CLI experience.

Here is what that includes technically:

Area What is included
Extension management Commands to discover, inspect, install, update, and uninstall one or more extensions, including dependency resolution and version constraints
Extension sources The preconfigured official source, plus URL- and file-based sources for private, local, development, and nightly registries
Commands and metadata Top-level and nested command namespaces, integrated help, command metadata, configuration schemas, and IntelliSense support
azd context and services Access to project, environment, account, user configuration, prompts, deployment, containers, AI models, and other azd context
Lifecycle integration Project and service event handlers that run extension logic around steps such as provisioning, packaging, and deployment
Extensible application support Providers for custom language frameworks, service targets, provisioning workflows, and validation
Agent and developer-tool integration MCP server capabilities and services that make extension tools available to agents and supported developer experiences
Authoring and distribution The azd x developer extension, scaffolding workflow, registry schema, versioning model, and publishing guidance

GA does not automatically make every extension and every capability inside an extension generally available. Extension owners can release and version their features independently. Development and nightly sources remain intended for preview and experimental builds, and preview features in the Foundry extensions keep their existing preview status.

This boundary lets the framework stay stable while product teams continue to develop and release their own experiences on the schedule that works for them.

Build an extension for your engineering platform

The framework also gives you an on-ramp for building your own experience.

The source code for azd extensions is available in the Azure Developer CLI repository, and the Awesome azd extension gallery provides a browsable catalog.

Start with the demo extension source if you want to see how an extension works with azd context, prompts, lifecycle events, service targets, and MCP tools:

azd ext install microsoft.azd.demo
azd demo

When you are ready to create an extension, install the developer extension and scaffold a project:

Extension authoring flow from scaffolding with azd x init, through custom commands and lifecycle events, to publishing in an extension registry.

azd ext install microsoft.azd.extensions
azd x init

The scaffolding flow helps you choose a namespace for your commands and creates the starting structure for the extension. From there, you can build the workflow your developers need.

Think about the practices your platform or product team wants to make easy and repeatable. A good extension candidate often appears where developers have to coordinate several tools or remember organization-specific steps. It might be a supported path for a new service, a deployment workflow for an internal platform, or a guided setup for security and observability.

You can distribute internal extensions through a private source and keep them available only to your organization. Extensions intended for broader use can follow the current contribution and publishing guidance for the azd extension registry.

What will you build?

The first version of the extension framework gave us a place to explore what developers might add to azd. The GA platform gives teams a foundation for shipping those ideas.

If you build AI applications and agents, install the Microsoft Foundry extensions and explore the capabilities available through azd ai.

If you build an engineering platform, look at the scripts, documentation, and manual handoffs that make up your supported paths today. Those workflows could become a connected CLI experience that works with the azd application lifecycle.

Try the demo extension, scaffold your own with azd x init, and share your feedback with the Azure Developer CLI project on GitHub.

We are excited to see the Foundry extension ecosystem grow, and we are even more excited to see the extensions you build to make developers’ work easier and more productive.

Related resources

The post Azure Developer CLI extension framework is GA: build dev workflows for apps using Azure appeared first on Azure SDK Blog.

Read the whole story
alvinashcraft
41 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Agentic Skills Demystified

1 Share
&&
AI in .NET Tooling
The Shift

AI Agents need more than raw model power to be useful — they need context. Skills package prescriptive, reusable expertise that turns a talented-but-clueless agent into one that actually ships code your way, and Uno Platform Skills bring that same power to cross-platform .NET development.

It is the age of AI – and if you've spent any time building software in the last couple of years, you've felt the shift. GenAI has fundamentally changed the inner developer loop. Scaffolding a project, writing boilerplate, generating tests, even architecting a first pass at a feature – AI Agents can do much of this in minutes, work that used to take experienced developers hours or days.

That's the productivity story everyone's excited about. But here's an honest developer opinion: raw AI model power, or the best of AI Agents, on their own, doesn't get you there.

Context

AI Needs Guidance

An AI Agent dropped into your codebase with no context is like a brilliant new hire on day one – full of talent, zero idea how your team actually ships software. It doesn't know your conventions. It doesn't know which patterns you've standardized on. It doesn't know that "add a page" in your app means five specific files touched in a specific order, not just one page-does-it-all component.

Lot of power without context is not beneficial. It's just fast guessing. And fast guessing at scale is how you end up debugging AI-generated code instead of shipping it.

So the real question isn't "how powerful is the AI Agent?" It's "how do we give the AI Agent the context it needs to be useful?" That's exactly the gap Skills are built to close.

Skills

Basics of Skills

Let's unpack what a Skill actually is.

A Skill is prescriptive guidance for AI – a modular, reusable capability that tells an agent exactly how to accomplish a specific task, packaged up so it can be invoked whenever the right conditions are met. Think of it as the knowledge steps to get something done right, written once and reused forever.

If you're a developer who's also a foodie, here's a frame that'll click immediately: even the best chefs need recipe cards for meal service. A five-star chef doesn't reinvent the béarnaise from scratch every single ticket – they follow a recipe, refined over time, that guarantees consistent results under pressure. Skills are that recipe card for your AI Agent.

A single Skill is useful. A collection of Skills is where things get interesting – because that's what actually brings context to an AI Agent. Instead of one enormous prompt trying to cover every scenario, you get focused, composable units of expertise that the agent reaches for exactly when needed.

And here's why this matters for you as a developer: Skills let you make AI work your way. Your conventions, your architecture decisions, your team's hard-won lessons about what breaks and what doesn't – all of it becomes something the agent can actually follow, not something you have to re-explain in every prompt.

The result? Agents fall into a pit of success. Not because the model magically got smarter, but because the path of least resistance now happens to be the correct path.

In Action

Skills in Action

Talk is cheap – let's look at what a Skill actually looks like. Here's a "Hello World" Skill: dead simple, but it shows the shape.

--- name: hello-world-ascii description: Print "Hello World" as ASCII art. Use this skill whenever the user asks for a greeting banner, an ASCII art hello, or a fun way to say hello in the terminal. --- # Hello World ASCII Skill When invoked, generate a "Hello World" banner using ASCII block letters, suitable for printing directly to a terminal or console. ## Steps 1. Render the text "HELLO WORLD" using large block-style ASCII characters. 2. Wrap the banner in a simple border of `*` characters, 2 lines above and below. 3. Output the result as a fenced code block so spacing is preserved. ## Example Output ``` **************************************** * _ _ _____ _ _ ___ * * | | | | ____| | | | / _ \ * * | |_| | _| | | | | | | | |* * | _ | |___| |___| |__| |_| |* * |_| |_|_____|_____|_____\___/ * **************************************** ```

Simple, right? But notice what's happening structurally – that's the part worth paying attention to:

  • Impact on context window – The agent doesn't load every Skill it has access to for every prompt. It only pulls this one in when the description matches what's being asked. That keeps the context window lean and the agent focused, even when you've got dozens of Skills installed.
  • Composability – Skills can be chained together. A "scaffold a page" Skill might invoke a "apply theming" Skill, which might invoke a "wire up navigation" Skill. Small, focused units combine into much bigger outcomes.
  • Reusable expertise – Skills capture expertise in a structured format, which makes it reusable for both humans and AI. A new team member can read a Skill file and understand the convention just as easily as an agent can execute it.

This isn't a one-off idea, either. There's a growing collection of Skills built specifically for .NET developers – check out the dotnet/skills repository for a look at what's already out there. And that's exactly where things get relevant for Uno Platform.

Uno Platform

Uno Platform Skills

Quick context for anyone new here: Uno Platform is the open-source platform that lets .NET developers build a single app in C# and XAML and ship it everywhere – Windows, macOS, Linux, iOS, Android, and WebAssembly, from one codebase.

Here's the honest reality: cross-platform app development is complex by nature. Navigation behaves differently across form factors. Theming has to hold up across five target platforms, not one. Testing means validating real UI behavior, not just unit tests in isolation. That's a lot of nuance for an AI Agent to guess at from a bare prompt.

This is exactly what Uno Platform Skills are built to solve. They give agents the prescriptive, Uno-specific guidance needed to actually succeed at these tasks – instead of producing code that compiles but doesn't follow the patterns your app actually needs.

Skills work hand-in-hand with MCP Tools here too. If Skills are the recipe cards, MCP Tools are the ingredients and equipment – things like the Uno documentation MCP server, which Skills use to search and pull in official, up-to-date Uno Platform documentation while they work. Recipe card and pantry, working together.

So what's actually in the box? The full Uno Platform Studio Skills catalog ships as a single plugin – uno-platform-studio – installable straight into Claude Code, GitHub Copilot CLI, GitHub Copilot in VS Code, and OpenAI Codex CLI. If your agent doesn't support plugins yet, the same Skills are published as standalone SKILL.md files you can drop into agents like Cursor, Cline, or Aider.

And the Skills themselves are grouped to cover the scenarios that show up in real Uno Platform app development, again and again:

  • mvux – MVUX reactive patterns: feeds, state, list state, selection, pagination, messaging
  • navigation – Navigation Extensions: routes, regions, XAML/code navigation, dialogs, tab shells, troubleshooting
  • toolkit – Toolkit controls and helpers: AutoLayout, Card, Drawer, NavigationBar, TabBar, SafeArea, responsiveness
  • themes – Theming: shared semantic design tokens, Material theme, Simple theme
  • testing – Automated UI testing and assertions

Notice the grouping isn't arbitrary – it maps directly onto the decisions you actually make building an Uno Platform app: how do I manage state, how do users move through the app, what controls do I reach for, how does it look, how do I know it works. Once installed, you don't have to invoke any of this manually, either – describe the task, and the agent selects and executes the applicable Skill on its own.

Wrap Up

Wrap Up

We can recap all the things – but developers get the point. Skills turn AI from a powerful-but-clueless assistant into something that actually understands how your team ships software. Prescriptive, modular, invoked only when needed, composable into bigger workflows – that's what pulls agents out of guesswork and into a pit of success.

And Uno Platform Skills bring that exact same principle to cross-platform .NET development – packaging up real expertise in navigation, theming, MVUX, and testing so your agent isn't guessing at Uno-specific patterns, it's following them.

Context is everything. Skills are how you give AI the context it's been missing. Cheers developers!

The post Agentic Skills Demystified appeared first on Uno Platform.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Shiny.Net.HttpServer — A Real HTTP Server Where ASP.NET Core Will Not Go

1 Share
Ten new packages: a dependency-light, AOT/trim-clean HTTP/1.1, HTTP/2 and HTTP/3 server that runs anywhere .NET runs — including inside a .NET MAUI app on iOS and Android. Routing, middleware, DI scopes, WebSockets, SSE, TLS with managed self-signed certs, an OpenAPI document built with zero reflection, source-generated typed endpoints, XML/MessagePack/protobuf without reflection, gRPC and gRPC-Web, a WebDAV mount, mediator handlers and DocumentDb documents as endpoints, tunnelling so a phone is reachable from the internet, and an MCP server you can point Claude at.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Shiny.Net.HttpServer — HTTP/1.1, HTTP/2 & HTTP/3 Anywhere .NET Runs

1 Share

ASP.NET Core is heavyweight and does not run on .NET MAUI, or in a number of embedded server scenarios. That is the gap this fills.

Shiny.Net.HttpServer is a dependency-light, AOT- and trim-clean HTTP/1.1, HTTP/2 and HTTP/3 server that runs anywhere .NET runs — plus tunnelling, so a server embedded in a phone app is reachable from the public internet. It is available today in beta.

var server = new HttpServer(new HttpServerOptions { Port = 8080 });
server.OnGet("/ping", ctx => ctx.Response.WriteAsync("pong"));
await server.RunAsync();

Only Microsoft.Extensions.* abstractions are taken as dependencies. Everything else — JSON, crypto, JWT, OpenAPI, HPACK, QPACK — is built on what is in the box. Every shipping project has the trim, AOT and single-file analyzers enabled, so “AOT-clean” is enforced by the build rather than claimed in a readme.

The packages

Package Description
NuGet package Shiny.Net.HttpServer The server: protocols, routing, middleware, DI scopes, static files, WebSockets, SSE, sessions, OpenAPI, CORS, rate limiting, IP filtering, tunnelling
NuGet package Shiny.Net.HttpServer.SourceGenerators Compile-time typed endpoints — route registration, parameter binding, OpenAPI. Zero reflection
NuGet package Shiny.Net.HttpServer.Jwt JWT authentication on in-box crypto — no Microsoft.IdentityModel dependency
NuGet package Shiny.Net.HttpServer.Ssh SSH remote-forwarding tunnel provider, including zero-account quick tunnels
NuGet package Shiny.Net.HttpServer.AzureRelay Azure Relay tunnel provider
NuGet package Shiny.Net.HttpServer.Mcp Model Context Protocol (Streamable HTTP) transport — host an MCP server without ASP.NET Core
NuGet package Shiny.Net.HttpServer.Grpc gRPC and gRPC-Web — unary, client-, server- and bidirectional streaming. Marshalling is supplied, so nothing reflects over your messages
NuGet package Shiny.Net.HttpServer.WebDav A directory as an RFC 4918 class 1 & 2 WebDAV mount
NuGet package Shiny.Net.HttpServer.Mediator Shiny.Mediator requests, commands and streams as endpoints, bound at compile time
NuGet package Shiny.Net.HttpServer.DocumentDb A Shiny.DocumentDb document type as a complete HTTP resource
dotnet add package Shiny.Net.HttpServer
dotnet add package Shiny.Net.HttpServer.SourceGenerators

The generator package is an analyzer — it runs inside the compiler and never lands in your output.

Four tiers, one app

Each tier is built on the one below, and they compose in the same server. Pick the altitude that suits the endpoint rather than the framework’s opinion.

Tier 0 — a single delegate. No routing at all.

server.OnRequest(async ctx => await ctx.Response.WriteAsync("hello"));

Tier 1 — routing. Templates with constraints, mutable at runtime.

server.OnGet("/api/users/{id:int}", async ctx => { /* … */ });

Tier 2 — middleware. ASP.NET-shaped.

server.UseAuthentication();
server.UseEmbeddedFiles(typeof(App).Assembly, "MyApp.wwwroot");

Tier 3 — source-generated typed endpoints. Constructor injection, typed parameter binding, and OpenAPI metadata, all emitted at compile time.

[Route("/api/users")]
public class UserEndpoints(IUserService users, ILogger<UserEndpoints> logger)
{
[Get("/{id:int}")]
public async Task<IActionResult> GetUser(int id, CancellationToken ct)
=> await users.FindAsync(id, ct) is { } u ? new OkObjectResult(u) : new NotFoundResult();
}
app.MapMyAppEndpoints(); // emitted for every [Route] class in the assembly

Results come in both spellings — Results.* and IActionResult.

What is in the box

Protocols HTTP/1.1, HTTP/2 (own HPACK), HTTP/3 (own QPACK), WebSockets, SSE. Never guessed — ALPN over TLS, connection preface over cleartext
Content Static files from disk or embedded resources, a published Blazor WASM app, streaming multipart uploads, byte ranges and conditional GETs, a file browser, and brotli/gzip/deflate
Security Authentication and authorization split ASP.NET-style — Basic, API key, cookie and JWT schemes, policies, roles and claims, plus CORS, rate limiting and IP filtering
TLS Per-endpoint TLS, self-signed certificates generated in managed code (iOS and Android included), client certificates, and SPKI pinning for your own HttpClient
OpenAPI An OpenAPI 3.0.3 document built entirely from compile-time metadata and your JsonSerializerContext — no reflection, no document object model
Formats Content negotiation both ways — responses from Accept, request bodies from Content-Type. JSON built in; XML, MessagePack and protobuf one line each
Tunnelling A pluggable ITunnelProvider, the reference relay, SSH remote forwarding, quick tunnels, and Azure Relay
Lifecycle Start, stop and restart at runtime, serialized and idempotent, with observable state

Route constraints cover byte, short, float, the temporal types and value bounds ({page:range(1,100)}), and a constraint decides whether a route matches rather than converting anything — so a refused segment is a 404 and one that matched but will not parse is a 400. Responses can also send trailing headers on all three protocol versions.

Reaching a device from the internet

A cellular device sits behind carrier-grade NAT with no routable address and no port to forward, and an agent-based tunnel like ngrok or cloudflared cannot run on iOS at all — there is no process to spawn. So tunnelling is built in, and every provider works the same way: the device opens an outbound connection and asks the far end to forward traffic back down it.

The zero-ceremony version needs no account and nothing installed:

builder.Services.AddHttpServer(autoStart: false, configureServer: s => s.MapGet("/", …));
builder.Services.AddQuickTunnel();
// then, from a button:
var url = await tunnel.StartAsync();

QuickTunnel is INotifyPropertyChanged with PublicUrl, State and LastError, so a view binds directly to it. Bind to PublicUrl rather than reading it once — a free tunnel assigns a new address on every reconnect, and a phone reconnects whenever it changes network. See SSH & Quick Tunnels for the host presets and what each one requires.

MCP without ASP.NET Core

The MCP SDK’s own HTTP transport is an ASP.NET Core package, which makes this the piece that genuinely could not be done any other way — an MCP server hosted inside a .NET MAUI app.

builder.Services
.AddMcpServer(o => o.ServerInfo = new() { Name = "thermostat", Version = "1.0.0" })
.WithTools<ThermostatTools>()
.WithHttpTransport();
var app = builder.Build();
app.MapMcp(); // POST/GET/DELETE/OPTIONS on /mcp

One thing the compiler cannot check for you: a tool’s parameter and return types are published to the client as a JSON schema, and building that schema by reflection does not survive trimming. Tools that trade only in primitives need nothing extra — give the rest a source-generated context, and MapMcp() will name any type you missed at startup.

[JsonSerializable(typeof(Query))]
[JsonSerializable(typeof(IReadOnlyList<Reading>))]
public partial class ToolJson : JsonSerializerContext;
.WithTools<ThermostatTools>(ToolJson.Default.Options)

Full detail on MCP.

Formats beyond JSON

JSON is the default and covers most of an HTTP API. For the rest — an integration behind a corporate gateway that speaks XML, a battery-powered client that would rather not spend 40% more radio time on braces, a service already generating protobuf from a .proto — formats plug in on both sides. IOutputFormatter writes responses; IInputFormatter reads request bodies.

builder.Services.AddContentNegotiation(o =>
{
o.NegotiateByDefault = true; // Results.Ok(value) honours Accept
o.AddXml();
o.AddMessagePack();
});

That is the whole setup, and no endpoint changes. Request bodies are dispatched on Content-Type, so every existing [FromBody] parameter, mediator contract and hand-written handler accepts the registered formats immediately; responses are chosen from Accept.

XmlSerializer could not be used for the XML side at all — it builds its mapping by reflecting over the type at runtime, which is precisely what a trimmed or AOT-published app has thrown away. The same goes for MessagePack-CSharp’s default resolver. Instead both read the source-generated JsonTypeInfo your types already have, so:

  • No new dependency, and no [XmlRoot], [DataMember] or [MessagePackObject] on your DTOs.
  • A type gets an XML or MessagePack representation on exactly the same terms as JSON — its metadata was registered.
  • The representations cannot drift apart, because the property names and converters have one source.

Reading XML is type-directed for the same reason it has to be. XML has no types: <postalCode>01234</postalCode> is text, and only the target member knows whether that is a string, a number or an enum ordinal. Guessing from the text is how a postal code arrives as the number 1234.

Protobuf needs a schema, so its codecs are supplied rather than discovered — field numbers live in the .proto, and the only thing that has them is the code protoc already generated:

o.AddProtobuf(p => p.Add<Reading>(m => m.ToByteArray(), Reading.Parser.ParseFrom));

The same BinaryCodecRegistry carries CBOR, Avro, or MessagePack-CSharp’s native codec under whichever media type you register it as. One behaviour change to note: a body whose Content-Type nothing reads now answers 415 rather than 400 — the difference between sending a caller to fix a header and sending them hunting for a syntax error that is not there. Full detail on Serialization & Formats.

gRPC and gRPC-Web

gRPC runs over the same HTTP/2 stack as everything else, callable from Grpc.Net.Client, grpcurl or any gRPC client in any language — and therefore from a .NET MAUI app, where ASP.NET Core cannot run at all.

app.MapGrpcService("greet.Greeter", svc =>
{
svc.AddMarshaller<HelloRequest>(m => m.ToByteArray(), HelloRequest.Parser.ParseFrom);
svc.MapUnary<HelloRequest, HelloReply>("SayHello", (request, ctx) => …);
});

All four method shapes are supported, with streams as IAsyncEnumerable<T> in both directions flushed as each message is yielded, grpc-timeout arriving as the handler’s CancellationToken, per-message compression negotiated from grpc-accept-encoding, and status in trailers. MaxReceiveMessageSize is enforced on the decompressed size as well as the length prefix, since a few compressed kilobytes can otherwise expand into gigabytes.

gRPC-Web is enabled by default in both framings, which is how a browser calls in and how anything on HTTP/1.1 does. It matters more here than on a server: native gRPC needs a tunnel that forwards raw TCP, and most hosted providers terminate HTTP/1.1, so gRPC-Web is generally what reaches a device from the internet.

A directory as a mounted drive

The file browser is a JSON API you drive with curl. WebDAV is the protocol every desktop already has a client for.

app.MapWebDav("/dav", o =>
{
o.RootPath = FileSystem.AppDataDirectory;
o.AllowWrite = true;
})
.RequireAuthorization();

Point Finder or Windows Explorer at that URL and the app’s storage appears as a drive, with no client code at all. RFC 4918 compliance classes 1 and 2 — PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK/UNLOCK and the If header.

Class 2 is on by default and is not really optional: Finder and the Windows redirector both mount a class 1 server read-only whatever AllowWrite says. Writes and deletes are opt-in, and PROPFIND with Depth: infinity is refused by default rather than walking a device’s storage into one response — note that a missing Depth header means infinity per the specification, so it gets the same refusal.

Mediator and DocumentDb as endpoints

If you already write Shiny.Mediator handlers, they can be endpoints. It is the Shiny.Mediator.AspNet shape without ASP.NET Core:

[MediatorHttpGroup("/api/gadgets")]
public class GadgetHandlers : IRequestHandler<GetGadget, Gadget>
{
[MediatorHttpGet("/{id:int}")]
public Task<Gadget> Handle(GetGadget request, IMediatorContext ctx, CancellationToken ct) => …;
}
app.MapGeneratedMediatorEndpoints();

The binding is the part that had to change. The ASP.NET package uses [AsParameters]/[FromBody], which is reflection over a delegate’s parameters and annotated RequiresDynamicCode; here a source generator writes it out member by member at compile time, so a contract that cannot be bound is a build error rather than a 500. An ICommand answers with a status code and no body; an IStreamRequest<T> becomes a Server-Sent Events response.

And if the data lives in Shiny.DocumentDb, a document type is a complete resource in one line:

app.MapDocuments<Order>("/orders", o =>
{
o.TypeInfo = AppJson.Default.Order;
o.AllowFilterOn(x => x.Status, x => x.Total);
o.Scope<ITenantContext>((tenant, _) => x => x.TenantId == tenant.TenantId);
})
.RequireAuthorization("orders");

List, by-id, count, create, replace, RFC 7396 merge-patch, delete and a live SSE tail, with filtering, cursor paging, sparse fieldsets and ETag/If-Match — plus a schema-free lane for JSON collections with no CLR type. It is a port of Shiny.DocumentDb.AspNetCore onto this server running the same engine, so filters, cursors, scopes and merge semantics are identical on both.

Two differences are deliberate. MapDocuments returns a builder that fans RequireAuthorization, CORS, rate limiting and IP filtering across every route it registered, because this server attaches metadata per route rather than per group — so adding an operation later cannot quietly leave one unprotected. And there is no reflection fallback when TypeInfo is unset, because a fallback that works on a desktop and throws on a trimmed phone is worse than a clear error in both places. Server-side scopes answer 404 rather than 403 for a document outside the scope. The package requires Shiny.DocumentDb 13.2.1 or newer.

Trade-offs worth knowing up front

Two packages are deliberately not AOT- or trim-clean, and they are separate packages for exactly that reason. Shiny.Net.HttpServer.Ssh carries SSH.NET, which brings BouncyCastle and its own algorithm registries. Shiny.Net.HttpServer.AzureRelay pulls in Azure.Identity, MSAL and IdentityModel. Reference either and you accept the weight; leave them out and the core server stays clean AOT.

The other eight are clean, the four newest included — which is much of why gRPC and protobuf ask for marshalling rather than discovering it, and why XML and MessagePack are written against your existing JSON metadata instead of a reflective serializer.

This is a beta. The API surface is settled and the test suite is substantial — around 1,260 tests, run against a live socket rather than an in-memory harness, since most of what a server can get wrong only exists at that boundary. But it has not yet been run in anger by a large number of people — issues and feedback are very welcome.

Finally, the security note that a tunnel makes urgent: a quick tunnel hands a public HTTPS address to anyone who learns it, pointed at a server whose defaults were chosen for loopback. Put authentication in front of it before you open one.

Getting started

Full documentation is at shinylib.net/httpserver, and the source — including a .NET MAUI sample that serves a page, exposes a file browser behind a password, hosts an MCP server and publishes the lot through a tunnel — is at github.com/shinyorg/httpserver.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories