mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 10:25:13 +00:00
* Add TypeSpec for Microsoft 365 Copilot collection: prompts, instruction, manifest, docs * Fix prompt frontmatter (mode + quoted descriptions), quote instructions description, and folderId casing in docs * Prompt frontmatter: remove name, add model field per guide --------- Co-authored-by: Troy Taylor <troytaylor@microsoft.com> Co-authored-by: Matt Soucoup <masoucou@microsoft.com>
11 KiB
11 KiB
description, applyTo
| description | applyTo |
|---|---|
| Guidelines and best practices for building TypeSpec-based declarative agents and API plugins for Microsoft 365 Copilot | **/*.tsp |
TypeSpec for Microsoft 365 Copilot Development Guidelines
Core Principles
When working with TypeSpec for Microsoft 365 Copilot:
- Type Safety First: Leverage TypeSpec's strong typing for all models and operations
- Declarative Approach: Use decorators to describe intent, not implementation
- Scoped Capabilities: Always scope capabilities to specific resources when possible
- Clear Instructions: Write explicit, detailed agent instructions
- User-Centric: Design for the end-user experience in Microsoft 365 Copilot
File Organization
Standard Structure
project/
├── appPackage/
│ ├── cards/ # Adaptive Card templates
│ │ └── *.json
│ ├── .generated/ # Generated manifests (auto-generated)
│ └── manifest.json # Teams app manifest
├── src/
│ ├── main.tsp # Agent definition
│ └── actions.tsp # API operations (for plugins)
├── m365agents.yml # Agents Toolkit configuration
└── package.json
Import Statements
Always include required imports at the top of TypeSpec files:
import "@typespec/http";
import "@typespec/openapi3";
import "@microsoft/typespec-m365-copilot";
using TypeSpec.Http;
using TypeSpec.M365.Copilot.Agents; // For agents
using TypeSpec.M365.Copilot.Actions; // For API plugins
Agent Development Best Practices
Agent Declaration
@agent({
name: "Role-Based Name", // e.g., "Customer Support Assistant"
description: "Clear, concise description under 1,000 characters"
})
- Use role-based names that describe what the agent does
- Make descriptions informative but concise
- Avoid generic names like "Helper" or "Bot"
Instructions
@instructions("""
You are a [specific role] specialized in [domain].
Your responsibilities include:
- [Key responsibility 1]
- [Key responsibility 2]
When helping users:
- [Behavioral guideline 1]
- [Behavioral guideline 2]
You should NOT:
- [Constraint 1]
- [Constraint 2]
""")
- Write in second person ("You are...")
- Be specific about the agent's role and expertise
- Define both what to do AND what not to do
- Keep under 8,000 characters
- Use clear, structured formatting
Conversation Starters
@conversationStarter(#{
title: "Action-Oriented Title", // e.g., "Check Status"
text: "Specific example query" // e.g., "What's the status of my ticket?"
})
- Provide 2-4 diverse starters
- Make each showcase a different capability
- Use action-oriented titles
- Write realistic example queries
Capabilities - Knowledge Sources
Web Search - Scope to specific sites when possible:
op webSearch is AgentCapabilities.WebSearch<Sites = [
{ url: "https://learn.microsoft.com" },
{ url: "https://docs.microsoft.com" }
]>;
OneDrive and SharePoint - Use URLs or IDs:
op oneDriveAndSharePoint is AgentCapabilities.OneDriveAndSharePoint<
ItemsByUrl = [
{ url: "https://contoso.sharepoint.com/sites/Engineering" }
]
>;
Teams Messages - Specify channels/chats:
op teamsMessages is AgentCapabilities.TeamsMessages<Urls = [
{ url: "https://teams.microsoft.com/l/channel/..." }
]>;
Email - Scope to specific folders:
op email is AgentCapabilities.Email<
Folders = [
{ folderId: "Inbox" },
{ folderId: "SentItems" }
],
SharedMailbox = "support@contoso.com" // Optional
>;
People - No scoping needed:
op people is AgentCapabilities.People;
Copilot Connectors - Specify connection IDs:
op copilotConnectors is AgentCapabilities.GraphConnectors<
Connections = [
{ connectionId: "your-connector-id" }
]
>;
Dataverse - Scope to specific tables:
op dataverse is AgentCapabilities.Dataverse<
KnowledgeSources = [
{
hostName: "contoso.crm.dynamics.com";
tables: [
{ tableName: "account" },
{ tableName: "contact" }
];
}
]
>;
Capabilities - Productivity Tools
// Python code execution
op codeInterpreter is AgentCapabilities.CodeInterpreter;
// Image generation
op graphicArt is AgentCapabilities.GraphicArt;
// Meeting content access
op meetings is AgentCapabilities.Meetings;
// Specialized AI models
op scenarioModels is AgentCapabilities.ScenarioModels<
ModelsById = [
{ id: "model-id" }
]
>;
API Plugin Development Best Practices
Service Definition
@service
@actions(#{
nameForHuman: "User-Friendly API Name",
descriptionForHuman: "What users will understand",
descriptionForModel: "What the model needs to know",
contactEmail: "support@company.com",
privacyPolicyUrl: "https://company.com/privacy",
legalInfoUrl: "https://company.com/terms"
})
@server("https://api.example.com", "API Name")
@useAuth([AuthType]) // If authentication needed
namespace APINamespace {
// Operations here
}
Operation Definition
@route("/resource/{id}")
@get
@action
@card(#{
dataPath: "$.items",
title: "$.title",
file: "cards/card.json"
})
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Confirm Action",
body: "Confirm with {{ function.parameters.param }}"
}
})
@reasoning("Consider X when Y")
@responding("Present results as Z")
op getResource(
@path id: string,
@query filter?: string
): ResourceResponse;
Models
model Resource {
id: string;
name: string;
description?: string; // Optional fields
status: "active" | "inactive"; // Union types for enums
@format("date-time")
createdAt: utcDateTime;
@format("uri")
url?: string;
}
model ResourceList {
items: Resource[];
totalCount: int32;
nextPage?: string;
}
Authentication
API Key
@useAuth(ApiKeyAuth<ApiKeyLocation.header, "X-API-Key">)
// Or with reference ID
@useAuth(Auth)
@authReferenceId("${{ENV_VAR_REFERENCE_ID}}")
model Auth is ApiKeyAuth<ApiKeyLocation.header, "X-API-Key">;
OAuth2
@useAuth(OAuth2Auth<[{
type: OAuth2FlowType.authorizationCode;
authorizationUrl: "https://auth.example.com/authorize";
tokenUrl: "https://auth.example.com/token";
refreshUrl: "https://auth.example.com/refresh";
scopes: ["read", "write"];
}]>)
// Or with reference ID
@useAuth(Auth)
@authReferenceId("${{OAUTH_REFERENCE_ID}}")
model Auth is OAuth2Auth<[...]>;
Naming Conventions
Files
main.tsp- Agent definitionactions.tsp- API operations[feature].tsp- Additional feature filescards/*.json- Adaptive Card templates
TypeSpec Elements
- Namespaces: PascalCase (e.g.,
CustomerSupportAgent) - Operations: camelCase (e.g.,
listProjects,createTicket) - Models: PascalCase (e.g.,
Project,TicketResponse) - Model Properties: camelCase (e.g.,
projectId,createdDate)
Common Patterns
Multi-Capability Agent
@agent("Knowledge Worker", "Description")
@instructions("...")
namespace KnowledgeWorker {
op webSearch is AgentCapabilities.WebSearch;
op files is AgentCapabilities.OneDriveAndSharePoint;
op people is AgentCapabilities.People;
}
CRUD API Plugin
namespace ProjectAPI {
@route("/projects") @get @action
op list(): Project[];
@route("/projects/{id}") @get @action
op get(@path id: string): Project;
@route("/projects") @post @action
@capabilities(#{confirmation: ...})
op create(@body project: CreateProject): Project;
@route("/projects/{id}") @patch @action
@capabilities(#{confirmation: ...})
op update(@path id: string, @body project: UpdateProject): Project;
@route("/projects/{id}") @delete @action
@capabilities(#{confirmation: ...})
op delete(@path id: string): void;
}
Adaptive Card Data Binding
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"type": "TextBlock",
"text": "Title: ${if(title, title, 'N/A')}",
"wrap": true
}
]
}
]
}
Validation and Testing
Before Provisioning
- Run TypeSpec validation:
npm run buildor use Agents Toolkit - Check all file paths in
@carddecorators exist - Verify authentication references match configuration
- Ensure capability scoping is appropriate
- Review instructions for clarity and length
Testing Strategy
- Provision: Deploy to development environment
- Test: Use Microsoft 365 Copilot at https://m365.cloud.microsoft/chat
- Debug: Enable Copilot developer mode for orchestrator insights
- Iterate: Refine based on actual behavior
- Validate: Test all conversation starters and capabilities
Performance Optimization
- Scope Capabilities: Don't grant access to all data if only subset needed
- Limit Operations: Only expose API operations the agent actually uses
- Efficient Models: Keep response models focused on necessary data
- Card Optimization: Use conditional rendering (
$when) in Adaptive Cards - Caching: Design APIs with appropriate caching headers
Security Best Practices
- Authentication: Always use authentication for non-public APIs
- Scoping: Limit capability access to minimum required resources
- Validation: Validate all inputs in API operations
- Secrets: Use environment variables for sensitive data
- References: Use
@authReferenceIdfor production credentials - Permissions: Request minimum necessary OAuth scopes
Error Handling
model ErrorResponse {
error: {
code: string;
message: string;
details?: ErrorDetail[];
};
}
model ErrorDetail {
field?: string;
message: string;
}
Documentation
Include comments in TypeSpec for complex operations:
/**
* Retrieves project details with associated tasks and team members.
*
* @param id - Unique project identifier
* @param includeArchived - Whether to include archived tasks
* @returns Complete project information
*/
@route("/projects/{id}")
@get
@action
op getProjectDetails(
@path id: string,
@query includeArchived?: boolean
): ProjectDetails;
Common Pitfalls to Avoid
- ❌ Generic agent names ("Helper Bot")
- ❌ Vague instructions ("Help users with things")
- ❌ No capability scoping (accessing all data)
- ❌ Missing confirmations on destructive operations
- ❌ Overly complex Adaptive Cards
- ❌ Hard-coded credentials in TypeSpec files
- ❌ Missing error response models
- ❌ Inconsistent naming conventions
- ❌ Too many capabilities (use only what's needed)
- ❌ Instructions over 8,000 characters