Awesome-copilot typespec-api-operations
Add GET, POST, PATCH, and DELETE operations to a TypeSpec API plugin with proper routing, parameters, and adaptive cards
install
source · Clone the upstream repo
git clone https://github.com/github/awesome-copilot
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/github/awesome-copilot "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/typespec-m365-copilot/skills/typespec-api-operations" ~/.claude/skills/github-awesome-copilot-typespec-api-operations && rm -rf "$T"
manifest:
plugins/typespec-m365-copilot/skills/typespec-api-operations/SKILL.mdsource content
Add TypeSpec API Operations
Add RESTful operations to an existing TypeSpec API plugin for Microsoft 365 Copilot.
Adding GET Operations
Simple GET - List All Items
/** * List all items. */ @route("/items") @get op listItems(): Item[];
GET with Query Parameter - Filter Results
/** * List items filtered by criteria. * @param userId Optional user ID to filter items */ @route("/items") @get op listItems(@query userId?: integer): Item[];
GET with Path Parameter - Get Single Item
/** * Get a specific item by ID. * @param id The ID of the item to retrieve */ @route("/items/{id}") @get op getItem(@path id: integer): Item;
GET with Adaptive Card
/** * List items with adaptive card visualization. */ @route("/items") @card(#{ dataPath: "$", title: "$.title", file: "item-card.json" }) @get op listItems(): Item[];
Create the Adaptive Card (
appPackage/item-card.json):
{ "type": "AdaptiveCard", "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.5", "body": [ { "type": "Container", "$data": "${$root}", "items": [ { "type": "TextBlock", "text": "**${if(title, title, 'N/A')}**", "wrap": true }, { "type": "TextBlock", "text": "${if(description, description, 'N/A')}", "wrap": true } ] } ], "actions": [ { "type": "Action.OpenUrl", "title": "View Details", "url": "https://example.com/items/${id}" } ] }
Adding POST Operations
Simple POST - Create Item
/** * Create a new item. * @param item The item to create */ @route("/items") @post op createItem(@body item: CreateItemRequest): Item; model CreateItemRequest { title: string; description?: string; userId: integer; }
POST with Confirmation
/** * Create a new item with confirmation. */ @route("/items") @post @capabilities(#{ confirmation: #{ type: "AdaptiveCard", title: "Create Item", body: """ Are you sure you want to create this item? * **Title**: {{ function.parameters.item.title }} * **User ID**: {{ function.parameters.item.userId }} """ } }) op createItem(@body item: CreateItemRequest): Item;
Adding PATCH Operations
Simple PATCH - Update Item
/** * Update an existing item. * @param id The ID of the item to update * @param item The updated item data */ @route("/items/{id}") @patch op updateItem( @path id: integer, @body item: UpdateItemRequest ): Item; model UpdateItemRequest { title?: string; description?: string; status?: "active" | "completed" | "archived"; }
PATCH with Confirmation
/** * Update an item with confirmation. */ @route("/items/{id}") @patch @capabilities(#{ confirmation: #{ type: "AdaptiveCard", title: "Update Item", body: """ Updating item #{{ function.parameters.id }}: * **Title**: {{ function.parameters.item.title }} * **Status**: {{ function.parameters.item.status }} """ } }) op updateItem( @path id: integer, @body item: UpdateItemRequest ): Item;
Adding DELETE Operations
Simple DELETE
/** * Delete an item. * @param id The ID of the item to delete */ @route("/items/{id}") @delete op deleteItem(@path id: integer): void;
DELETE with Confirmation
/** * Delete an item with confirmation. */ @route("/items/{id}") @delete @capabilities(#{ confirmation: #{ type: "AdaptiveCard", title: "Delete Item", body: """ ⚠️ Are you sure you want to delete item #{{ function.parameters.id }}? This action cannot be undone. """ } }) op deleteItem(@path id: integer): void;
Complete CRUD Example
Define the Service and Models
@service @server("https://api.example.com") @actions(#{ nameForHuman: "Items API", descriptionForHuman: "Manage items", descriptionForModel: "Read, create, update, and delete items" }) namespace ItemsAPI { // Models model Item { @visibility(Lifecycle.Read) id: integer; userId: integer; title: string; description?: string; status: "active" | "completed" | "archived"; @format("date-time") createdAt: utcDateTime; @format("date-time") updatedAt?: utcDateTime; } model CreateItemRequest { userId: integer; title: string; description?: string; } model UpdateItemRequest { title?: string; description?: string; status?: "active" | "completed" | "archived"; } // Operations @route("/items") @card(#{ dataPath: "$", title: "$.title", file: "item-card.json" }) @get op listItems(@query userId?: integer): Item[]; @route("/items/{id}") @card(#{ dataPath: "$", title: "$.title", file: "item-card.json" }) @get op getItem(@path id: integer): Item; @route("/items") @post @capabilities(#{ confirmation: #{ type: "AdaptiveCard", title: "Create Item", body: "Creating: **{{ function.parameters.item.title }}**" } }) op createItem(@body item: CreateItemRequest): Item; @route("/items/{id}") @patch @capabilities(#{ confirmation: #{ type: "AdaptiveCard", title: "Update Item", body: "Updating item #{{ function.parameters.id }}" } }) op updateItem(@path id: integer, @body item: UpdateItemRequest): Item; @route("/items/{id}") @delete @capabilities(#{ confirmation: #{ type: "AdaptiveCard", title: "Delete Item", body: "⚠️ Delete item #{{ function.parameters.id }}?" } }) op deleteItem(@path id: integer): void; }
Advanced Features
Multiple Query Parameters
@route("/items") @get op listItems( @query userId?: integer, @query status?: "active" | "completed" | "archived", @query limit?: integer, @query offset?: integer ): ItemList; model ItemList { items: Item[]; total: integer; hasMore: boolean; }
Header Parameters
@route("/items") @get op listItems( @header("X-API-Version") apiVersion?: string, @query userId?: integer ): Item[];
Custom Response Models
@route("/items/{id}") @delete op deleteItem(@path id: integer): DeleteResponse; model DeleteResponse { success: boolean; message: string; deletedId: integer; }
Error Responses
model ErrorResponse { error: { code: string; message: string; details?: string[]; }; } @route("/items/{id}") @get op getItem(@path id: integer): Item | ErrorResponse;
Testing Prompts
After adding operations, test with these prompts:
GET Operations:
- "List all items and show them in a table"
- "Show me items for user ID 1"
- "Get the details of item 42"
POST Operations:
- "Create a new item with title 'My Task' for user 1"
- "Add an item: title 'New Feature', description 'Add login'"
PATCH Operations:
- "Update item 10 with title 'Updated Title'"
- "Change the status of item 5 to completed"
DELETE Operations:
- "Delete item 99"
- "Remove the item with ID 15"
Best Practices
Parameter Naming
- Use descriptive parameter names:
notuserIduid - Be consistent across operations
- Use optional parameters (
) for filters?
Documentation
- Add JSDoc comments to all operations
- Describe what each parameter does
- Document expected responses
Models
- Use
for read-only fields like@visibility(Lifecycle.Read)id - Use
for date fields@format("date-time") - Use union types for enums:
"active" | "completed" - Make optional fields explicit with
?
Confirmations
- Always add confirmations to destructive operations (DELETE, PATCH)
- Show key details in confirmation body
- Use warning emoji (⚠️) for irreversible actions
Adaptive Cards
- Keep cards simple and focused
- Use conditional rendering with
${if(..., ..., 'N/A')} - Include action buttons for common next steps
- Test data binding with actual API responses
Routing
- Use RESTful conventions:
- ListGET /items
- Get oneGET /items/{id}
- CreatePOST /items
- UpdatePATCH /items/{id}
- DeleteDELETE /items/{id}
- Group related operations in the same namespace
- Use nested routes for hierarchical resources
Common Issues
Issue: Parameter not showing in Copilot
Solution: Check parameter is properly decorated with
@query, @path, or @body
Issue: Adaptive card not rendering
Solution: Verify file path in
@card decorator and check JSON syntax
Issue: Confirmation not appearing
Solution: Ensure
@capabilities decorator is properly formatted with confirmation object
Issue: Model property not appearing in response
Solution: Check if property needs
@visibility(Lifecycle.Read) or remove it if it should be writable