add-data-source
Skill4 starsChanged 8 days agoMITView on GitHub
Moves this app's data from browser-local storage to a shared backend (SharePoint lists, Dataverse tables, a REST API, or any other store) by adding a storage provider. Use when the user wants app data shared between people or devices, kept outside the browser, or stored in a specific system.
- Reads credentials
4 other projects use a version of this.
Adopted by
Most recently taken up by
- microsoft/power-platform-skillstheir own version, first seen Oct 13, 2026
- nirholas/cryptocurrency.cvtheir own version, first seen Oct 11, 2026
- nirholas/free-crypto-newstheir own version, first seen Oct 8, 2026
- paulieb89/property-sharedtheir own version, first seen May 3, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- microsoft/power-platform-skills★ 1.1ksame name
- nirholas/cryptocurrency.cv★ 333same name
- nirholas/free-crypto-news★ 333same name
- paulieb89/property-shared★ 16same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository38 other files it gives its agents.
- github-actions/AGENTS.mdAGENTS.md
- plugins/copilot-managed-runtime/AGENTS.mdAGENTS.md
- samples/CodeFlow/AGENTS.mdAGENTS.md
- samples/LearningHub/AGENTS.mdAGENTS.md
- samples/TaskManager/AGENTS.mdAGENTS.md
- add-azuredevopsSkill
- add-backendSkill
- add-data-sourceSkill
- add-dataverseSkill
- add-excelSkill
- add-mcscopilotSkill
- add-office365Skill
- add-office365-usersSkill
- add-onedriveSkill
- add-sharepointSkill
- add-teamsSkill
- add-workiqSkill
- create-appSkill
- delete-appSkill
- deploySkill
- devSkill
- list-appsSkill
- list-connectorsSkill
- playSkill
- share-linkSkill
- shareSkill
- stateSkill
- add-data-sourceSkill
- setup-codeflowSkill
- add-data-sourceSkill
- setup-codeflowSkill
- add-data-sourceSkill
- setup-learninghubSkill
- add-data-sourceSkill
- setup-learninghubSkill
- setup-taskmanagerSkill
- add-data-sourceSkill
- setup-taskmanagerSkill
The file
We split this skill into the rules it gives an agent
10 rules. 0 are common, 5 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
Every read and write of app-owned data in this template goes through one storage interface (src/storage/core/types.ts).
- Very specific: line 27
Read src/storage/collections.ts.
- Very specific: line 28
Run npm run storage:describe.
- Distinctive: line 32
Ask the user which collections should move (usually all of them) and who should be able to read and write each one.
- Distinctive: line 43
If the user has no preference, recommend SharePoint lists for small teams and Dataverse for anything with roles, auditing or large data.
- Distinctive: line 48
Use the document-in-a-row layout for every collection.
- Distinctive: line 62
Create the lists/tables yourself only if the user asks and has permission.
- Very specific: line 67
Always use the CLI (pinned in package.json) so codegen and ms.config.json stay consistent.
- Very specific: line 159
Run npm test, npm run lint and npm run typecheck.
- Distinctive: line 170
Keep connector permissions least-privilege.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Add a data source
Every read and write of app-owned data in this template goes through one storage interface
(src/storage/core/types.ts). By default the local provider keeps data in the browser
(localStorage). This skill adds another provider and switches the app to it without
changing UI or domain code.
Ground rules
- Never edit
src/storage/core/. It is shared, tested code. Build on it. - Never change UI, hooks or domain modules to make a backend work. If the interface seems insufficient, stop and explain the gap to the user.
- Generated connector code is imported only from
src/connectors/(an ESLint rule enforces this). The provider insrc/storage/providers/depends on that thin wrapper, not ongenerated/directly, so it can be tested with fakes. - A provider is finished when
runProviderContract()passes against it (step 6). - Authorization belongs to the backend. Configure list/table permissions; never rely on hiding UI (step 7).
Step 1: Understand what is stored
- Read
src/storage/collections.ts. Every collection is a zod schema plus a name. - Run
npm run storage:describe. It prints each collection as JSON Schema; you will create one list/table per collection. - Note which data uses
provider.kv(...)(preferences, drafts, UI state). These usually stay device-local, which is the default described in step 5. - Ask the user which collections should move (usually all of them) and who should be able to read and write each one.
Step 2: Choose the backend
| Backend | Good for | Needs | Reference |
| SharePoint lists | Teams that already use SharePoint; simple setup | A SharePoint site where the user can create lists | references/sharepoint.md |
| Dataverse tables | Relational/business data; role-based security; large volumes | A Power Platform environment with Dataverse, and the matching licenses | references/dataverse.md |
| Your own API | Existing systems or databases | A connector (or custom connector) that exposes CRUD operations | references/custom-api.md |
If the user has no preference, recommend SharePoint lists for small teams and Dataverse for anything with roles, auditing or large data.
Step 3: Provision the backing store
Use the document-in-a-row layout for every collection. It maps any record shape onto any tabular store and keeps the provider generic:
| Column | Type | Purpose |
RecordKey | Single line of text, indexed, unique, required | Record id (BackendRow.key) |
Data | Multiple lines of plain text (no rich text, no length cap below ~1 MB) | The record body as JSON |
| (built-in) ETag / row version | backend-managed | BackendRow.etag |
| (built-in) Created / Modified | backend-managed | createdAt / updatedAt |
Name each list/table after the collection, optionally with an app prefix, e.g. LearningHub_sessions.
You may add extra "promoted" columns for fields you want to search in the backend UI, but the
app reads only RecordKey and Data.
Create the lists/tables yourself only if the user asks and has permission. Otherwise give them the exact list of names and columns.
Step 4: Add the connector
Always use the CLI (pinned in package.json) so codegen and ms.config.json stay consistent. Run
npx ms app add data-source --help to see every flag.
# SharePoint: one command per list
npx ms app add data-source --connector shared_sharepointonline --as table --dataset "<site URL>" --table "<list name>"
# Dataverse: one command per table (no connection id needed)
npx ms app add data-source --connector dataverse --as table --table "<table logical name>"
# Any other connector
npx ms connector list --search "<keyword>"
npx ms app add data-source --connector <api-id> --as actionThen find the generated operations. Do not read whole generated files; they are large:
# list method names of a generated service
rg "async \w+\(" generated/services/<Name>Service.ts
# then read one method and its model
rg "async <MethodName>" -A 25 generated/services/<Name>Service.tsStep 5: Implement the provider
The core already provides createTableBackedProvider(). You implement one TableBackend per
collection, which is five methods. Everything else (validation, queries, paging, conflicts,
export/import) is handled for you.
- Connector wrapper:
src/connectors/<backend>Tables.ts. This is the only file that importsgenerated/. Expose plain functions such aslistRows(table, cursor),getRow,createRow,updateRow(table, key, data, etag)anddeleteRow, and translate connector results intoBackendRow({ key, data, etag, createdAt, updatedAt }). - Provider:
src/storage/providers/<backend>.ts.
// Adjust names (config fields, table prefix) to match this app's src/config.ts.
import { createLocalProvider, createTableBackedProvider, type TableBackend } from '../core';
import * as rows from '../../connectors/sharepointTables';
import { config } from '../../config';
function backendFor(collectionName: string): TableBackend {
const table = `${config.storageTablePrefix}${collectionName}`;
return {
list: (cursor) => rows.listRows(table, cursor),
get: (key) => rows.getRow(table, key),
create: (key, data) => rows.createRow(table, key, data), // ConflictError if key exists
update: (key, data, etag) => rows.updateRow(table, key, data, etag), // ConflictError on etag mismatch, NotFoundError if missing
delete: (key, etag) => rows.deleteRow(table, key, etag), // idempotent; ConflictError on etag mismatch
};
}
export function createSharePointProvider() {
const local = createLocalProvider({ prefix: config.storagePrefix });
return createTableBackedProvider({
id: 'sharepoint',
shared: true,
tableFor: backendFor,
kv: (namespace) => local.kv(namespace), // keep preferences on the device
});
}- Register it in
src/storage/index.tsby addingsharepoint: createSharePointProviderto the provider registry. Then setVITE_STORAGE_PROVIDER=sharepoint(plus any new variables, such as the table prefix) in.env.local. Document the new variables in.env.exampleand insrc/config.ts. - Remote stores do not push changes. Call
provider.refresh()on window focus, and optionally on a timer, so other users' edits appear.
Mapping rules every backend must follow
etagmust change on every write, and the backend must reject stale etags with a "precondition failed" error (HTTP 412). Map that error toConflictErrorfrom../core.createwith an existing key must throwConflictError. Enforce uniqueness onRecordKey, or check first.- A missing record on
updatethrowsNotFoundError.deleteof a missing record succeeds. listmust page with the backend's continuation token. Never assume one page holds everything.- Store
Dataexactly as given. Never reformat the JSON.
Step 6: Prove it with the contract suite
Create src/storage/providers/<backend>.test.ts:
import { runProviderContract } from '../core/contract';
// Fake the connector wrapper (src/connectors/<backend>Tables.ts) with an in-memory map that
// reproduces the backend's real behaviour: etags, 412s, paging. See createInMemoryTableBackend
// in core/providers/tableBacked.ts for the reference behaviour.
runProviderContract('sharepoint', () => createSharePointProviderWithFakeRows());Run npm test, npm run lint and npm run typecheck. All must pass. Then run the app against the
real backend and try each feature, ideally in two browsers with different accounts to see
sharing and conflicts.
Step 7: Security and privacy
- Grant list/table permissions to exactly the people who should read or write each collection. The app's UI is not a security boundary.
- Review what becomes visible once data is shared. Collections that were per-browser (drafts,
personal notes) may now be readable by others; keep those in
kv(device-local), or use a per-user table with item-level permissions. - Keep connector permissions least-privilege. Add only the tables the app uses.
Step 8: Move existing data
Users can move what they already have: in the app's Settings, choose Export data while still
on the local provider, switch to the new provider, then Import data. Import validates every
record and skips anything invalid.
Done checklist
- One list/table per moved collection, with
RecordKey(unique) andDatacolumns - Connector added with the CLI; the updated
generated/,.ms/andms.config.jsonare committed to the app's repository. src/connectors/<backend>Tables.tsplussrc/storage/providers/<backend>.ts, registered insrc/storage/index.ts- The contract suite passes for the new provider, and
npm test,npm run lintandnpm run typecheckare green - Permissions configured in the backend
.env.example,src/config.tsand the README's "Storage" section updated
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

