AI Quickstart
This walkthrough takes you from an empty folder to a running portal where customers sign in and work with their own support cases — by asking an AI coding agent rather than writing it by hand. Each step gives you a prompt to paste, what the agent will do with it, and what to check before you move on. The example uses the Case table (incident) that comes with Dynamics 365 Customer Service; with a table of your own, every prompt works the same way.
Before you start
- The .NET 10 SDK, and Node.js if you choose the React template.
- A Dataverse environment in which you are a System Administrator. Use a development or sandbox environment for this walkthrough.
- Permission to create app registrations in that environment's Entra tenant.
- An address for the portal's email — registration confirmations and password resets — with a shared mailbox for it in Exchange Online, which needs no licence. Approving the address takes an Office 365 Global Administrator or Exchange Administrator.
- An AI coding agent that supports MCP. The commands below are for Claude Code; the MCP Server page has the configuration for Visual Studio, VS Code and Cursor.
How this works
Two things let an agent get this right rather than merely plausible: the Power Portals Pro MCP server, which reads your actual Dataverse environment and makes the configuration changes a portal needs, and the instructions every generated project ships for agents.
- Every change asks you first. Before an app registration, the application user or a security role is created, privileges are granted or a solution is installed, the agent shows you what is about to happen — the action, the target, the cost, the effect and how to undo it — and you then approve or decline it in a one-line prompt. Read the details: they are your review step.
- The project carries the know-how. A generated project includes an
AGENTS.mdand three skills —set-up-portal,secure-a-tableandadd-table-pages— that tell the agent which questions to ask and in which order. You don't need to name them; the prompts below are enough for the agent to pick the right one. - The prompts are a starting point. Put them in your own words if you like. What matters is what they say: who the portal is for, which data those people should see, and what they may do with it.
1. Install the MCP server
Install it once per machine, at user scope, so it is available in every folder — including the empty one you are about to create the project in:
claude mcp add --scope user powerportalspro -- dnx PowerPortalsPro.Mcp --yes
dnx ships with the .NET 10 SDK. Restart your agent if it was already running, then check the server is there by asking something only it can answer, such as "use get_component_api to list MainGrid's parameters".
2. Make a folder and sign in
Create the folder your portal will live in, and sign in from inside it — once to your Dataverse environment, and once to Azure, which is what the app registrations in step 4 are created through:
mkdir C:\Code\Contoso.Portal
cd C:\Code\Contoso.Portal
dnx PowerPortalsPro.Mcp login --environment https://yourorg.crm.dynamics.com
dnx PowerPortalsPro.Mcp azure login
Both sign-ins belong to this folder, not to the machine. They write .ppp/mcp.json here, naming the environment and the tenant, and keep your tokens in your user profile, so a project in another folder can point at another customer at the same time. Both use a device code, so you can finish them on your phone. If your account sees several Azure subscriptions you don't need to choose one yet — that only matters once you deploy. Then start your agent in this folder.
3. Create the project
Tell the agent what you are building — which stack, React or Blazor, and who will sign in:
Create a new Power Portals Pro portal in this folder called Contoso.Portal, using the React template. It is for our customers, who will register and sign in with their own accounts.
The agent installs the project templates if they are missing, asks about anything it cannot tell from your request, and runs dotnet new. Each customer becomes a contact in Dataverse, and what they may see is decided by the security registrations in the project's Program.cs. That is the template's default security model, and the one the rest of this walkthrough uses. Either stack works; every later prompt is the same.
Note
Restart your agent in the same folder once the project exists. The project brings its own
.mcp.json, which pins the MCP server to the version that matches its packages, along with its skills andAGENTS.md— and most agents only read those when they start. Approve the project's MCP server if your agent asks.
4. Connect it to Dataverse
A new project will not start until it can reach Dataverse. Ask the agent to set that up:
Set this portal up against my Dataverse environment so it runs locally. Don't give the application user System Administrator: use the Power Portals Pro role, plus a role of our own for our tables. Send the portal's email from noreply@contoso.com.
The agent follows the project's set-up-portal skill, starting with check_setup to see what is missing. Expect to approve each of these:
- The managed solution — the Power Portals Pro tables, the licence check and the Power Portals Pro Application User security role.
- An Entra app registration that the portal connects to Dataverse as. Its client secret goes straight into the project's user secrets, never into the conversation.
- The Dataverse application user for that registration, with the Power Portals Pro Application User role and the column security profile the framework needs, plus an empty security role of your own. The Power Portals Pro role covers the framework's own tables only; your tables go in your role, in step 6.
- A second app registration for "Sign in with Microsoft" — only if you want that button. Customers with their own accounts don't need it.
- An email queue that registration and password-reset mail is sent from. The agent approves its address and runs Test & Enable on its mailbox. Two things may be left to you: the address needs a mailbox in Exchange Online — a shared mailbox, which needs no licence — and approving it needs an Office 365 Global Administrator or Exchange Administrator. If either is missing, the agent says which; fix it and ask the agent to run the step again.
The connection settings land in the project's user secrets as each step completes. The agent finishes by running check_setup again; once that is clean, run the project — F5, or dotnet run in the server project — and you will see the template's home page and sample pages.
5. Decide what customers should see
Describe the outcome you want in business terms, and let the agent find the table. Asking before anything is written is worth it: the agent reads your environment, so it suggests what is actually there, your own custom tables and columns included.
Customers who sign in should see the support cases they raised, open one to see its details, and log a new case. Which table and columns should we use? Don't change anything yet.
Expect it to look the table up with list_tables and describe_table, read its views with list_views, and come back with the Case table (incident), the lookup that ties a case to its customer (customerid), and the columns to show and to edit. Adjust that list now: it decides what the portal publishes.
6. Secure the table
A portal shows nothing it has not been told to show: until a table is registered, its pages return 403 or an empty grid. Ask the agent to open the table up — only as far as you mean to:
Secure the case table: signed-in customers can read, create and edit only their own cases, with the columns we agreed. They choose the priority when they log a case but can't change it afterwards. Give our own security role the privileges the application user needs on cases.
The agent works through the project's secure-a-table skill, confirming who the access is for, which rows, which operations and which columns. Don't skip any of them: each answer changes what is published.
Then it writes the registration in the server project's Program.cs. For this example, expect something like:
builder.Services.AddTableAccess("incident", t => t
.ForSignedInUsers()
.RowsOwnedByContact("customerid")
.Allow(TableSecurityPermission.Read | TableSecurityPermission.Create | TableSecurityPermission.Write)
.Reading("title", "ticketnumber", "prioritycode", "statuscode", "createdon", "description")
.Writing("title", "description")
.WritingOnCreate("prioritycode"));
RowsOwnedByContact("customerid") limits every query to the signed-in contact's own cases, and makes them the customer on a case they create. Columns are opt-in: anything not named in Reading is withheld, and only the columns in Writing can be changed. WritingOnCreate covers the priority — set when the case is logged, read-only from then on. Security covers every option.
The application user's own role
The registration decides what each customer may do, but the portal still talks to Dataverse as its application user, and that user needs privileges on the table itself. They go in the security role of your own from step 4, never in the Power Portals Pro role. The agent:
- Finds your role on the application user with
get_application_user— and if step 4 didn't make one, creates it withcreate_security_roleand assigns it withassign_application_user_role. - Grants it what the registration needs with
add_table_privileges_to_role: Read, Create, Write and Append onincident, and Append To oncontact, because every new case is linked to a contact. Then it runscheck_setup, which compares the tables your pages use with the tables your code registers.
Why a role of your own?
The Power Portals Pro role ships in a managed solution. Dataverse would let you add privileges to it, but the next upgrade of that solution can put it back the way it shipped, taking your grants with it — so the MCP server refuses to add privileges to any managed role. A role of your own also shows anyone reviewing the environment later exactly what the portal can reach.
7. Add the pages
With the table secured, ask for both pages in one go — the list in the menu, and the page a case opens in:
Add a Cases page to the main menu with a grid of the signed-in customer's cases, and the page a case opens in: title, case number, priority and status in one section, the description in another. Customers can change the title and description, set the priority only when logging a new case, and nothing else.
The agent follows the project's add-table-pages skill. It adds the routes, a list page with a MainGrid, the menu entry and a RecordContext page that also opens empty for logging a new case. It reads real view ids with list_views rather than writing placeholders, and keeps every column within what the registration lets customers read — the stock Dynamics views include agent-only columns, so expect it to offer views of its own. If you ask for a column the registration leaves out, it should stop and ask rather than quietly widen access. Related records can go on the case page as subgrids; name them, and it will secure that table too.
8. Try it as two customers
The agent cannot sign in as a customer, so this part is yours. It is also the test that matters most: the point of the registration is what one customer cannot see.
- Run the portal and register a customer. The confirmation email arrives once the email queue's mailbox has passed Test & Enable in step 4.
- Open Cases, log a new case, then open it again from the grid. Change its title and save.
- Sign out, register a second customer and open Cases. The grid should be empty: the first customer's case is theirs alone. Pasting the address of that case into this session should not show it either.
If either customer sees something they shouldn't, don't adjust it by hand. Tell the agent what you saw and what you expected, and let it work through secure-a-table again.
If something doesn't look right
- The grid is empty, or the page returns 403. Ask the agent to run
check_setup: a table your pages use with no registration covering it is listed there. Otherwise, the customer may simply have no cases yet. - A column is always blank. It isn't in the registration's
Readinglist. Columns are withheld unless they are named, on purpose. - Every data request returns 402. The licence check failed — usually because the managed solution is not installed yet.
check_setupsays which. - The portal will not start, or shows "Some content is unavailable". It cannot sign in to Dataverse. The log line starting
Could not connect to Dataversenames the setting to fix, andcheck_setupreports it too.
Where to go next
- Add more tables the same way: step 5 to choose, step 6 to secure, step 7 for the pages.
- Give some customers more access than others with portal roles — ask the agent for a role such as Case Manager that sees every case of the customer's company. Security explains how roles add to the portal-wide registrations.
- Deploy it. "Deploy this portal to Azure" starts the Azure deployment, with its own sign-in and a price for every resource before it is created.
- Prefer Power Pages' web roles and table permissions? Create the project with
--EnableEnhancedAuthorization true. Access then comes from Dataverse security roles rather thanProgram.cs— see Dataverse Security.
