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

Remote Event Receivers are retiring: move to SharePoint webhooks before July 1, 2027

1 Share

On July 1, 2027, Remote Event Receivers (RERs) will stop working in SharePoint Online. After that date, no remote event receiver will fire events, regardless of how it was registered. If your application still relies on remote event receivers to react to changes in SharePoint, now is the time to plan your move to SharePoint webhooks.

Key message

All remote event receivers in SharePoint Online will stop working on July 1, 2027. Migrate any remaining RER-based functionality to SharePoint webhooks (or Microsoft Graph change notifications) before that date.

Background

Remote event receivers are part of the SharePoint Add-in model and depend on Azure Access Control Services (Azure ACS) for authentication. With the retirement of Azure ACS now complete, remote event receivers registered using Azure ACS have stopped functioning correctly as of April 2, 2026. They can still be added programmatically, events might still fire, but inside the receiver service ACS cannot be used anymore for delegated or app-only access back into SharePoint.

To give developers more time to migrate, as part of the ACS retirement, we enabled a different registration model that removes the Azure ACS dependency: remote event receivers registered using a Microsoft Entra application. Receivers registered this way continue to work until July 1, 2027, and, unlike Azure ACS–registered receivers, they also work for new tenants onboarded after November 1, 2024. You can read the details in Use remote event receivers without an Azure ACS dependency and the Remote event receivers retirement update.

This extension is a bridge, not a destination. On July 1, 2027, both registration models reach end of the road and all remote event receivers stop firing.

Timeline

  • April 2, 2026 – Remote event receivers registered using Azure ACS stopped functioning correctly.
  • July 1, 2027 – All remaining remote event receivers, including those registered using a Microsoft Entra application, stop working. No RER will fire events after this date.

The path forward: SharePoint webhooks

The recommended replacement for remote event receivers is SharePoint webhooks. Webhooks are regular, industry standard, HTTP web APIs, which makes them simpler to build and operate than the WCF services that remote event receivers rely on. When a subscribed event occurs, SharePoint sends an HTTP POST to your service endpoint.

A few things to know as you design your migration:

  • Asynchronous only. Webhooks fire after a change has happened (similar to the “-ed” events). Synchronous “-ing” events are not supported, so you can no longer block or cancel an action from a receiver.
  • Blocking scenarios need a rethink. If you used synchronous events to prevent unauthorized updates or deletes, consider securing the protected files and folders so they can’t be changed, or moving the data to a hidden library.
  • Webhooks currently cover SharePoint list items – that is, changes to items in a list or document library.
  • Get the change details yourself. The notification payload tells you something changed, but not what. Call the GetChanges API on the list with a stored change token to retrieve the actual changes.
  • Manage expiration. Subscriptions expire after at most 180 days, so your application is responsible for renewing them.
  • Or use Microsoft Graph. As an alternative, you can use to subscribe to changes on a SharePoint list. If your application needs to stay continuously up to date of changes happening in SharePoint, then please consult the scan guidance for recommended patterns to use.

How to migrate

  1. Inventory your code for remote event receiver registrations and the WCF endpoints that handle them.
  2. Stand up an HTTP endpoint to receive webhook notifications, and implement the validation handshake.
  3. Create a subscription against the target list or library, store the change token, and call GetChanges when a notification arrives.
  4. Re-implement any synchronous blocking logic using the alternative approaches above.
  5. Add subscription renewal so your webhooks don’t lapse at the 180-day limit.

On discovering RER usage

Today we do not yet have a way for developers and admins to discover remote event receiver usage across a tenant. This is something we are actively working on, and we will update this blog post, and the associated Message Center post, as soon as it is available.

Learn more

The post Remote Event Receivers are retiring: move to SharePoint webhooks before July 1, 2027 appeared first on Microsoft 365 Developer Blog.

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

Reading Notes #704

1 Share

This week’s collection highlights practical ways to improve developer workflows, from faster test runs and more manageable pull requests to intuitive new AI integrations. I have gathered a few standout articles on Blazor components, Azure Functions updates, and the nuances of training coding agents for your specific stack.

Suggestion of the week

Programming

Open Source

DevOps

AI


Sharing my Reading Notes is a habit I started a long time ago, where I share a list of all the articles, blog posts, and books that catch my interest during the week. 

 ~frank


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

Agent Harness: Working with your data, safely

1 Share

Part 2 of Build your own claw and agent harness with Microsoft Agent Framework.

In Part 1 we stood up a harness and gave our personal finance assistant its first abilities: a custom tool, web search, and planning. It can talk about the markets – but it can’t yet touch your data, and nothing stops it from taking a sensitive action on a whim.

This part fixes both, using three abilities that are all included in the harness:

  1. File access – read your portfolio from a CSV and write reports back to disk.
  2. Approvals – gate risky actions (like placing a trade) behind a human’s explicit OK.
  3. Durable memory – remember things across sessions, in two complementary ways.

As before, we only supply what makes our agent ours; the harness provides the machinery. Let’s take the three in turn.

Give it your data: file access

Our assistant should work from the user’s actual holdings, not made-up numbers. The harness includes a file-access provider that exposes a set of file_access_* tools – list, read, write, search – over a default folder. We point it at a working/ folder that already contains a portfolio.csv.

C#:

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    // The agent can read/write files under this folder via the file_access_* tools.
    FileAccessStore = new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "working")),
    ChatOptions = new ChatOptions { Instructions = instructions, Tools = [/* ... */] },
});

Python:

from agent_framework import FileSystemAgentFileStore

agent = create_harness_agent(
    client=client,
    agent_instructions=FINANCE_INSTRUCTIONS,
    tools=[get_stock_price, place_trade],
    # The agent can read/write files under this folder via the file_access_* tools.
    file_access_store=FileSystemAgentFileStore("working"),
)

Amongst others, the agent now has six tools – file_access_read_file, file_access_save_file, file_access_list_files, file_access_list_subdirectories, file_access_search_files, and file_access_delete_file – and we include the following instructions to tell it how to use them:

The user’s holdings live in a file called portfolio.csv. Read it before answering questions about their portfolio, and never modify it unless asked. When asked for a report, write it to a Markdown file (e.g. reports/portfolio-review.md) and tell the user where you saved it.

Now “What’s in my portfolio?” reads real rows, and “Write me a portfolio report and save it” produces a file you can open.

Gate risky actions: approvals

Reading data is often safe. Placing a trade is never safe – that’s an action with consequences, and the agent should never take it without a human’s say-so. The harness has a built-in approval mechanism: mark a tool as approval-required, and the agent will pause and ask before the tool ever runs.

We add a place_trade tool (it only simulates the order). In .NET, wrap it in an ApprovalRequiredAIFunction:

public static AIFunction CreatePlaceTradeTool() =>
    new ApprovalRequiredAIFunction(AIFunctionFactory.Create(PlaceTrade, "place_trade"));

In Python, set approval_mode on the @tool decorator:

from agent_framework import tool

@tool(approval_mode="always_require")
def place_trade(symbol: str, action: str, quantity: int) -> str:
    """Place a (simulated) buy or sell order."""
    ...

When the model decides to call place_trade, the harness emits an approval request instead of running the function, and the shared console surfaces it as an Approve / Deny prompt. (The planning observers we wired up in Part 1 already include a tool approval observer, so there’s nothing extra to add.) Approve and the trade runs; deny and the agent adapts.

Don’t ask again: always-approve

Approving the same tool over and over is its own kind of friction. The harness has built-in support for standing approvals – “don’t ask me again” decisions – so once you trust a call, you won’t be pestered for it again. There are two flavours:

  • Always approve this tool (any arguments) – every future place_trade is approved automatically, whatever the symbol or quantity.
  • Always approve this tool with these arguments – only calls whose arguments exactly match the one you approved are auto-approved; a different symbol or quantity still prompts.

These rules are recorded in the session state, so they last for the duration of the session – not baked into the agent, and not leaked into a brand-new session. Because they live in the session, they travel with it: /session-export and /session-import carry your standing approvals across a restart, just like memory.

When using the sample console, its approval prompt already offers all four options out of the box:

🔐 Tool approval: place_trade(symbol: "MSFT", action: "buy", quantity: 10)
  > Approve this call
    Always approve this tool (any arguments)
    Always approve this tool with these arguments
    Deny

Pick one of the Always approve options and the harness remembers it for the rest of the session; subsequent matching calls sail through without a prompt.

Keep the safe path frictionless: auto-approval

By default every file_access_* call (even a read) asks for approval. That’s the safe default, but always prompting to read portfolio.csv gets tedious fast, and it muddies the signal when a genuinely risky call (a write, or a trade) needs your attention.

The harness lets you supply auto-approval rules: heuristics that silently approve low-risk calls so only the risky ones interrupt you. The file-access provider ships a ready-made rule that auto-approves its read-only tools (read, list, search) while still prompting for the ones that modify the store (save and delete).

In .NET, add it via ToolApprovalAgentOptions.AutoApprovalRules:

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    ToolApprovalAgentOptions = new ToolApprovalAgentOptions
    {
        // Auto-approve read-only file tools; saving, deleting, and place_trade still prompt.
        AutoApprovalRules = [FileAccessProvider.ReadOnlyToolsAutoApprovalRule],
    },
    // … file access, tools …
});

In Python, pass auto_approval_rules:

from agent_framework import FileAccessProvider

agent = create_harness_agent(
    client=client,
    agent_instructions=FINANCE_INSTRUCTIONS,
    tools=[get_stock_price, place_trade],
    file_access_store=FileSystemAgentFileStore("working"),
    # Auto-approve read-only file tools; saving, deleting, and place_trade still prompt.
    auto_approval_rules=[FileAccessProvider.read_only_tools_auto_approval_rule],
)

Now reading your portfolio just works, while writing a report, deleting a file, or placing a trade still pauses for your OK.

A rule of your own: auto-approve small trades

A rule is just a callback over the pending function call, so you can encode whatever policy you like. A natural one for our claw: auto-approve trades under $1,000, ask for the rest – keeping only the genuinely consequential orders in front of the user. (place_trade takes a symbol and a quantity, so we estimate the order value from the current price.)

In .NET, a rule is a Func<FunctionCallContent, ValueTask<bool>>:

// Returns true to auto-approve; false falls through to the normal approval prompt.
static async ValueTask<bool> AutoApproveSmallTrades(FunctionCallContent call)
{
    if (call.Name != "place_trade" || call.Arguments is null)
    {
        return false; // Not our tool — let it prompt as usual.
    }

    var symbol = call.Arguments["symbol"]?.ToString();
    var quantity = Convert.ToInt32(call.Arguments["quantity"]);
    var estimate = quantity * await GetPriceAsync(symbol!); // your own pricing logic

    return estimate < 1000m; // under $1,000 → auto-approve
}

// Wire it in alongside the read-only rule:
AutoApprovalRules = [FileAccessProvider.ReadOnlyToolsAutoApprovalRule, AutoApproveSmallTrades],

In Python, a rule is a callable taking the function call (sync or async both work):

async def auto_approve_small_trades(call: FunctionCallContent) -> bool:
    """Return True to auto-approve; False falls through to the normal prompt."""
    if call.name != "place_trade":
        return False  # Not our tool — let it prompt as usual.

    args = call.parse_arguments() or {}
    estimate = int(args.get("quantity", 0)) * await get_price(args["symbol"])  # your pricing logic

    return estimate < 1000  # under $1,000 -> auto-approve


# Wire it in alongside the read-only rule:
auto_approval_rules=[
    FileAccessProvider.read_only_tools_auto_approval_rule,
    auto_approve_small_trades,
],

Rules are evaluated in order; the first to return true wins, and returning false simply lets the next rule (or the manual prompt) take over.

Remember across sessions: durable memory

Our claw should remember the user’s watchlist and the things they tell us about themselves – across sessions, not just within one. In this post we demonstrate two kinds of memory, and they’re deliberately different:

File memory Foundry memory
Granularity Coarse – whole files Fine – individual facts
Written by The agent, explicitly (it decides to save a file) Microsoft Foundry, automatically (facts are extracted)
Good for Documents the agent curates: a watchlist, notes, drafts Ambient facts: “prefers low-risk ETFs”, “saving for a house”
Where it lives A folder you control A managed Foundry memory store

You usually want both: file memory for things the agent deliberately maintains, and Foundry memory for the small facts that should just stick without anyone managing a file.

File memory (coarse, explicit)

File memory is a harness built-in – it’s on by default, and you never construct or inject the provider yourself. The agent gets a set of file_memory_* tools and decides, on its own, when to write a file like watchlist.md.

Its files live on disk under the configured working folder, in a sub-folder keyed by session id (by default {cwd}/agent-file-memory/<session-id>/). That means the files persist across runs on the same machine automatically. A brand-new session gets a new id – and so starts with empty memory. You may configure the root folder under which these session-specific sub-folders are created by supplying a file memory store with a specific path.

To pick the memory back up after a console app restart, save and reload the session with the console’s session commands (the same save/resume flow from Part 1):

/session-export my-finance-session.json     # before you quit
/session-import my-finance-session.json     # after you relaunch

The export file holds the session state (including its id and conversation), not the memory files themselves. Re-importing restores the session’s identity, so the relaunched session re-links to its still-on-disk memory files. On a fresh checkout or a different machine you’d also copy the agent-file-memory folder for the files to come along.

You don’t need to set anything up for the default behavior. If you want the memory files to land in a specific folder, point the store there – in .NET with HarnessAgentOptions.FileMemoryStore:

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    FileMemoryStore = new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "agent-memory")),
    // … file access, tools …
});

and in Python with the file_memory_store argument:

from agent_framework import FileSystemAgentFileStore

agent = create_harness_agent(
    ...,
    file_memory_store=FileSystemAgentFileStore("agent-memory"),
)

Foundry memory (fine, automatic)

Foundry memory works differently: you don’t tell the agent, and it does not decide to “save” anything. As the conversation flows, Microsoft Foundry extracts durable facts and recalls the relevant ones on later turns – even in a brand-new session. Because it needs a memory store and an embedding model, we make it opt-in via environment variables, so the sample still runs without that setup.

For more on Foundry memory, see What is memory? and Use memory. Foundry memory is currently only available in certain regions, so check that your Foundry project is in a supported region before relying on it.

In .NET, create a FoundryMemoryProvider scoped to a user and add it to the agent’s context providers:

var foundryMemory = new FoundryMemoryProvider(
    projectClient,
    memoryStoreName,
    stateInitializer: _ => new(new FoundryMemoryProviderScope("claw-sample-user")));

await foundryMemory.EnsureMemoryStoreCreatedAsync(deploymentName, embeddingModel, "…");

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    AIContextProviders = [foundryMemory],
    // … file access, file memory, tools …
});

In Python, it’s another context provider:

from agent_framework.foundry import FoundryMemoryProvider

foundry_memory = FoundryMemoryProvider(
    project_client=project_client,
    memory_store_name=store_name,
    scope="claw-sample-user",
    update_delay=0,
)

agent = create_harness_agent(
    ...,
    context_providers=[foundry_memory],
)

Now tell the assistant “I’m a conservative investor saving for a house in two years” in one session, restart, and ask “What do you know about me?” – it remembers, because Foundry quietly stored the fact and recalled it.

Run it

.NET

cd dotnet
dotnet run --project samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step02_WorkingWithData

Python

uv run python/samples/02-agents/harness/build_your_own_claw/claw_step02_working_with_data.py

Then try these in order (the sample starts in execute mode – quick lookups don’t need a plan):

  1. What's in my portfolio? – the agent reads portfolio.csv with the file_access tools.
  2. Write me a short report on my portfolio and save it. – it drafts the report, then prompts you to approve the save (writes aren’t auto-approved), and on approval writes a Markdown file under working/.
  3. I'm a conservative investor saving for a house in two years. – a durable fact, remembered by Foundry memory (when enabled).
  4. Buy 10 shares of MSFT. – the agent calls place_trade; you’re prompted to approve or deny before anything happens.
  5. Add SPY to my watchlist. – saved to watchlist.md in file memory.

To see memory persist across a relaunch, ask “What’s on my watchlist?” or “What do you know about me?” after restarting:

  • Foundry memory (when enabled) recalls the facts about you in any new session – it’s scoped to the hardcoded “claw-sample-user” scope, not the session.
  • File memory (the watchlist) lives on disk keyed by session id in both languages, so /session-export before you quit and /session-import after relaunching to re-link the relaunched session to its files.

The runnable samples

Use these building blocks in your own agent

The harness assembles all of this for you, but none of it is locked inside the harness. Each feature is a plain context provider, middleware, or agent decorator that you can pick up on its own and add to agent – so even if you don’t adopt the full harness, you can reuse exactly the pieces you need. Here’s where to find them:

Feature .NET (type — namespace) Python (import)
File access FileAccessProviderMicrosoft.Agents.AI from agent_framework import FileAccessProvider
Approvals (don’t-ask-again, auto-approval rules) ToolApprovalAgent / ToolApprovalAgentOptions, via builder.UseToolApproval(...)Microsoft.Agents.AI from agent_framework import ToolApprovalMiddleware, ToolApprovalRuleCallback
File memory FileMemoryProviderMicrosoft.Agents.AI from agent_framework import FileMemoryProvider
Foundry memory FoundryMemoryProviderMicrosoft.Agents.AI.Foundry from agent_framework.foundry import FoundryMemoryProvider

In .NET, file access, approvals, and file memory ship in the Microsoft.Agents.AI package, and Foundry memory in Microsoft.Agents.AI.Foundry. In Python, the first three come from the agent-framework package and Foundry memory from agent-framework-foundry. The providers plug in through an agent’s context providers, the approval pieces through middleware – the same wiring the harness does on your behalf.

What’s next

Our claw now works with your data, asks before it acts, and remembers what matters. Next we make it more capable: skills it can load on demand (including Foundry-managed skills), background agents for concurrent work, shell access to reorganize files, and CodeAct for computation it can write and run itself.

📚 The series

Part of Build your own claw and agent harness with Microsoft Agent Framework:

The post Agent Harness: Working with your data, safely appeared first on Microsoft Agent Framework.

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

Prompt Compression and Cache Tuning: Cut Your LLM API Costs by 60%

1 Share
Prompt Compression and Cache Tuning: Cut Your LLM API Costs by 60%

Cross-model guide to reducing LLM costs using prompt compression, semantic caching, chain-of-thought pruning, and output length constraints across OpenAI, Anthropic, and Google Gemini.

Continue reading Prompt Compression and Cache Tuning: Cut Your LLM API Costs by 60% on SitePoint.

Read the whole story
alvinashcraft
9 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Claude Code on Ollama: How to Run a Local Coding Agent Without Burning API Credits

1 Share
Claude Code on Ollama: How to Run a Local Coding Agent Without Burning API Credits

Comprehensive guide covering this topic with practical implementation details.

Continue reading Claude Code on Ollama: How to Run a Local Coding Agent Without Burning API Credits on SitePoint.

Read the whole story
alvinashcraft
9 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Connecting the Unconnected: Doreen Bogdan-Martin

1 Share

For nearly 160 years, the International Telecommunication Union has helped the world communicate across borders, from the telegraph to the telephone, television, satellite, the internet, and now AI. In this episode of Tools and Weapons, Brad Smith sits down with Doreen Bogdan, Secretary-General of the ITU, to discuss why connectivity remains one of the world’s most important foundations for opportunity.

The conversation explores the 2.2 billion people who are still unconnected, the estimated $2.8 trillion needed to connect the world by 2030, and the partnerships required to reach the hardest-to-connect communities. Doreen shares stories from the field, including a refugee camp in Chad where a small computer center gives people access to learning, health care, financial tools, and family connections.

Brad and Doreen also discuss the rise of AI for Good, the challenge of scaling solutions that address real-world needs, and the role of global cooperation in shaping responsible AI governance. From early warning systems that can help save lives during natural disasters to digital skilling and infrastructure investment, this episode examines how technology can create opportunity when access, trust, and partnership come together.

Listen to the full episode and join the conversation about building a more connected and inclusive digital future.





Download audio: https://mgln.ai/e/1457/cdn.simplecast.com/media/audio/transcoded/973e729e-d3fe-467f-b2ac-1080afa0eb0f/4b885e2a-3d0c-4a1e-b2c2-86d5e058b8cc/episodes/audio/group/59812846-8b4b-4c1a-a1b9-5408d7df1e7b/group-item/478ad2b6-5588-4208-8661-0c37abea13ee/128_default_tc.mp3?aid=rss_feed&feed=FGw2u5qj
Read the whole story
alvinashcraft
9 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories