Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions cli/skills/get-mslearn-docs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
name: get-mslearn-docs
description: >
Use this skill when you need documentation from Microsoft Learn before writing
code — for example, "use Azure Functions", "configure Cosmos DB", "set up App
Service", or any time the user asks you to work with a Microsoft technology
and you need current reference material. Fetch the docs with mslearn before
answering, rather than relying on training knowledge.
---

# Get Microsoft Learn Docs via mslearn

When you need documentation for a Microsoft technology, fetch it with the

`mslearn` CLI rather than guessing from training data. This gives you the

current, correct content straight from Microsoft Learn.

## Step 1 — Search for the right doc

```bash

mslearn search "<microsoft product or topic>"

```

Review the search results to find the most relevant document `contentUrl`. If nothing matches, try a broader term.

Pick the best-matching `id` from the results (e.g. `openai/chat`, `anthropic/sdk`,
`stripe/api`). If nothing matches, try a broader term.

## Step 2 — Fetch the doc

```bash

mslearn fetch <contentUrl>

```

To fetch only a specific section (saves tokens):


```bash

mslearn fetch "<contentUrl>" --section "<section name>"

```



To limit output length:



```bash

mslearn fetch "<contentUrl>" --max-chars 3000

```

## Step 3 — Use the docs

Read the fetched content and use it to write accurate code or answer the question.

Do not rely on memorized API shapes — use what the docs say.

## Step 4 — Search for code samples

If you need working code examples, search the official Microsoft code samples:



```bash

mslearn code-search "<microsoft product or topic>"

```



To filter by programming language:



```bash

mslearn code-search "<microsoft product or topic>" --language <programming language>

```

## Step 5 — Annotate what you learned

**ALWAYS perform this step before finishing.** Review what you learned and ask yourself:

1. Did the docs contain any surprising behavior, version-specific caveats, or
non-obvious prerequisites?
2. Did you find that the docs were misleading, incomplete, or required combining
multiple pages to get a working answer?
3. Is there project-specific context (e.g., "we use .NET 8, not 6") that would
help future sessions?

If ANY of the above apply, save an annotation:

```bash
mslearn annotate "<contentUrl>" "<note text>"
```

Annotations are local, persist across sessions, and are keyed by URL. Keep notes
concise and actionable. Don't repeat what's already in the doc.

If none apply, explicitly state: "No annotation needed — docs were
straightforward."

To view an existing annotation:

```bash
mslearn annotate "<contentUrl>"
```

## Step 6 — Review and manage annotations

List all saved annotations:



```bash

mslearn annotate --list

```



Remove an annotation that is no longer relevant:



```bash

mslearn annotate "<contentUrl>" --clear

```

## Quick reference

| Goal | Command |

|------|---------|

| Search docs | `mslearn search "<microsoft product or topic>"` |

| Fetch a doc | `mslearn fetch "<contentUrl>"` |

| Fetch one section | `mslearn fetch "<contentUrl>" --section "<section name>"` |

| Limit output size | `mslearn fetch "<contentUrl>" --max-chars 3000` |

| Search code samples | `mslearn code-search "<microsoft product or topic>"` |

| Filter by language | `mslearn code-search "<microsoft product or topic>" --language <programming language>` |

| Save a note | `mslearn annotate "<contentUrl>" "<note text>"` |

| View a note | `mslearn annotate "<contentUrl>"` |

| List all notes | `mslearn annotate --list` |

| Remove a note | `mslearn annotate "<contentUrl>" --clear` |

| Check connectivity | `mslearn doctor` |

| Check (JSON output) | `mslearn doctor --format json` |



## Notes

- All docs are fetched from the Microsoft Learn MCP server at `https://learn.microsoft.com/api/mcp`
- The `<contentUrl>` argument must be a valid Microsoft Learn URL
- Use `--section` with `fetch` to reduce token usage when you only need part of a page
- Use `mslearn doctor` to verify that your environment and connectivity are working
- Override the endpoint with `MSLEARN_ENDPOINT` env var or `--endpoint <url>` flag



71 changes: 71 additions & 0 deletions cli/src/commands/annotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Command } from 'commander';

import type { CliContext } from '../context.js';
import { normalizeUrl } from '../utils/options.js';
import { ensureTrailingNewline } from '../utils/text.js';
import { UsageError } from '../utils/errors.js';

interface AnnotateCommandOptions {
clear?: boolean;
list?: boolean;
}

export function registerAnnotateCommand(program: Command, context: CliContext): void {
program
.command('annotate')
.description('Attach a local note to a Microsoft Learn URL. Notes persist across sessions.')
.argument('[url]', 'Microsoft Learn document URL.')
.argument('[note]', 'Note text to attach to the URL.')
.option('--clear', 'Remove the annotation for this URL.')
.option('--list', 'List all saved annotations.')
.action((url: string | undefined, note: string | undefined, options: AnnotateCommandOptions) => {
const store = context.createAnnotationStore();

if (options.list) {
const annotations = store.list();
if (annotations.length === 0) {
context.writeOut(ensureTrailingNewline('No annotations.'));
return;
}
const lines: string[] = [];
for (const a of annotations) {
lines.push(`${a.url} (${a.updatedAt})`);
lines.push(` ${a.note}`);
lines.push('');
}
context.writeOut(ensureTrailingNewline(lines.join('\n')));
return;
}

if (!url) {
throw new UsageError(
'Missing required argument: <url>. Usage: mslearn annotate <url> <note> | mslearn annotate <url> --clear | mslearn annotate --list',
);
}

const normalizedUrl = normalizeUrl(url);

if (options.clear) {
const removed = store.clear(normalizedUrl);
if (removed) {
context.writeOut(ensureTrailingNewline(`Annotation cleared for ${normalizedUrl}.`));
} else {
context.writeOut(ensureTrailingNewline(`No annotation found for ${normalizedUrl}.`));
}
return;
}

if (!note) {
const existing = store.read(normalizedUrl);
if (existing) {
context.writeOut(ensureTrailingNewline(`${existing.url} (${existing.updatedAt})\n${existing.note}`));
} else {
context.writeOut(ensureTrailingNewline(`No annotation for ${normalizedUrl}.`));
}
return;
}

const annotation = store.write(normalizedUrl, note);
context.writeOut(ensureTrailingNewline(`Annotation saved for ${annotation.url}.`));
});
}
4 changes: 4 additions & 0 deletions cli/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { createLearnCliClient, type LearnCliClientLike, type LearnClientOptions } from './mcp/client.js';
import { createFileAnnotationStore, type AnnotationStore } from './utils/annotations.js';

export interface CliContext {
env: NodeJS.ProcessEnv;
version: string;
writeOut: (value: string) => void;
writeErr: (value: string) => void;
createClient: (options: LearnClientOptions) => LearnCliClientLike;
createAnnotationStore: () => AnnotationStore;
}

export function createDefaultContext(version: string): CliContext {
Expand All @@ -18,10 +20,12 @@ export function createDefaultContext(version: string): CliContext {
writeErr: (value) => {
process.stderr.write(value);
},
fetchImpl: globalThis.fetch.bind(globalThis) as typeof fetch,
createClient: (options) =>
createLearnCliClient({
clientVersion: version,
...options,
}),
createAnnotationStore: () => createFileAnnotationStore(),
};
}
2 changes: 2 additions & 0 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createRequire } from 'node:module';
import { realpathSync } from 'node:fs';
import { pathToFileURL } from 'node:url';

import { registerAnnotateCommand } from './commands/annotate.js';
import { registerCodeSearchCommand } from './commands/code-search.js';
import { registerDoctorCommand } from './commands/doctor.js';
import { registerFetchCommand } from './commands/fetch.js';
Expand Down Expand Up @@ -36,6 +37,7 @@ export function createProgram(context: CliContext): Command {
registerFetchCommand(program, context);
registerCodeSearchCommand(program, context);
registerDoctorCommand(program, context);
registerAnnotateCommand(program, context);

return program;
}
Expand Down
Loading
Loading