Skip to content

Commit c40effe

Browse files
Copilotstephentoub
andauthored
✨ Set up Copilot instructions for repository (#858)
* Initial plan * Add Copilot instructions for repository Co-authored-by: stephentoub <[email protected]> * Apply suggestions from code review * Refocus instructions on library implementation and architecture Co-authored-by: stephentoub <[email protected]> * Add key types and architectural layers documentation Co-authored-by: stephentoub <[email protected]> * Fix markdown link formatting for link checker Co-authored-by: stephentoub <[email protected]> * Add explicit build and test requirements to instructions Co-authored-by: stephentoub <[email protected]> * Apply suggestions from code review --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: stephentoub <[email protected]> Co-authored-by: Stephen Toub <[email protected]>
1 parent 31339f3 commit c40effe

File tree

1 file changed

+348
-0
lines changed

1 file changed

+348
-0
lines changed

.github/copilot-instructions.md

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
# Copilot Instructions for MCP C# SDK
2+
3+
This repository contains the official C# SDK for the Model Context Protocol (MCP), enabling .NET applications to implement and interact with MCP clients and servers.
4+
5+
## Critical: Always Build and Test
6+
7+
**ALWAYS build and run tests before declaring any task complete or making a pull request.**
8+
9+
When making code changes:
10+
1. **Build first**: Run `dotnet build` to ensure the code compiles without errors
11+
2. **Run tests**: Execute `dotnet test` to verify all tests pass
12+
3. **Fix issues**: Address any build errors or test failures before proceeding
13+
4. **Verify iteratively**: Build and test frequently during development, not just at the end
14+
5. **Check warnings**: Treat warnings as errors - the build is configured with `TreatWarningsAsErrors=true`
15+
16+
**Never skip these steps.** Even small changes can have unexpected impacts. A passing build and test suite is the minimum bar for any code change.
17+
18+
## Project Overview
19+
20+
The SDK consists of three main packages:
21+
- **ModelContextProtocol.Core** - Client and low-level server APIs with minimal dependencies
22+
- **ModelContextProtocol** - The main package with hosting and dependency injection extensions and which references ModelContextProtocol.Core
23+
- **ModelContextProtocol.AspNetCore** - HTTP-based MCP server implementations for ASP.NET Core, referencing ModelContextProtocol
24+
25+
## C# Coding Standards
26+
27+
### Language Features
28+
- Use **file-scoped namespaces** for all C# files
29+
- Enable **implicit usings** and **nullable reference types**
30+
- Use **preview language features** (LangVersion: preview)
31+
- Treat warnings as errors
32+
33+
### Code Style
34+
- Follow the conventions in `.editorconfig`
35+
- Use clear, descriptive XML documentation comments for public APIs
36+
- Follow async/await patterns consistently
37+
- Use file-scoped namespaces: `namespace ModelContextProtocol.Client;`
38+
39+
### Naming Conventions
40+
- Use `McpClient`, `McpServer`, `McpSession` for MCP-related classes (capitalize MCP)
41+
- Prefix MCP-specific types with `Mcp` (e.g., `McpException`, `McpEndpoint`)
42+
- Use descriptive names for parameters with `[Description("...")]` attributes when exposing to MCP
43+
44+
## Architecture Patterns
45+
46+
### Dependency Injection
47+
- Use Microsoft.Extensions.DependencyInjection patterns
48+
- Register services with `.AddMcpServer()` and `.AddMcpClient()` extension methods
49+
- Support both builder patterns and options configuration
50+
51+
### JSON Serialization
52+
- Use `System.Text.Json` exclusively for all JSON operations
53+
- Use `McpJsonUtilities.DefaultOptions` for consistent serialization settings across the SDK
54+
- Support source generation for Native AOT compatibility via `McpJsonUtilities` source generators
55+
- Set `JsonIgnoreCondition.WhenWritingNull` for optional properties to minimize payload size
56+
- Use `JsonSerializerDefaults.Web` for camelCase property naming
57+
- Protocol types are decorated with `[JsonSerializable]` attributes for AOT support
58+
- Custom converters: `CustomizableJsonStringEnumConverter` for flexible enum serialization
59+
60+
### Async Patterns
61+
- All I/O operations should be async
62+
- Use `ValueTask<T>` for hot paths that may complete synchronously
63+
- Always accept `CancellationToken` parameters for async operations
64+
- Name parameters consistently: `cancellationToken`
65+
66+
### MCP Protocol
67+
- Follow the MCP specification at https://spec.modelcontextprotocol.io/ ([specification docs](https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/docs/specification))
68+
- Use JSON-RPC 2.0 for message transport
69+
- Support all standard MCP capabilities (e.g. tools, prompts, resources, sampling)
70+
- Implement proper error handling with `McpException` and `McpErrorCode`
71+
72+
### Error Handling
73+
- Throw `McpException` for MCP protocol-level errors with appropriate `McpErrorCode`
74+
- Use standard error codes: `InvalidRequest`, `MethodNotFound`, `InvalidParams`, `InternalError`
75+
- Let domain exceptions bubble up and convert to `InternalError` at transport boundary
76+
- Include detailed error messages in exception `Message` property for debugging
77+
- Errors are automatically converted to JSON-RPC error responses by the server infrastructure
78+
79+
## Testing
80+
81+
### Test Organization
82+
- Unit tests in `tests/ModelContextProtocol.Tests` for core functionality
83+
- Integration tests in `tests/ModelContextProtocol.AspNetCore.Tests` for HTTP/SSE transports
84+
- Shared test utilities in `tests/Common/Utils/`
85+
- Test servers in `tests/ModelContextProtocol.Test*Server/` for integration scenarios
86+
- Filter manual tests with `[Trait("Execution", "Manual")]` - these require external dependencies
87+
88+
### Test Infrastructure and Helpers
89+
- **LoggedTest**: Base class for tests that need logging output captured to xUnit test output
90+
- Provides `ILoggerFactory` and `ITestOutputHelper` for test logging
91+
- Use when debugging or when tests need to verify log output
92+
- **TestServerTransport**: In-memory transport for testing client-server interactions without network I/O
93+
- **MockLoggerProvider**: For capturing and asserting on log messages
94+
- **XunitLoggerProvider**: Routes `ILogger` output to xUnit's `ITestOutputHelper`
95+
- **KestrelInMemoryTransport** (AspNetCore.Tests): In-memory Kestrel connection for HTTP transport testing without network stack
96+
97+
### Test Best Practices
98+
- Inherit from `LoggedTest` for tests needing logging infrastructure
99+
- Use `TestServerTransport` for in-memory client-server testing
100+
- Mock external dependencies (filesystem, HTTP clients) rather than calling real services
101+
- Use `CancellationTokenSource` with timeouts to prevent hanging tests
102+
- Dispose resources properly (servers, clients, transports) using `IDisposable` or `await using`
103+
- Run tests with: `dotnet test --filter '(Execution!=Manual)'`
104+
105+
## Build and Development
106+
107+
### Build Commands
108+
- **Restore**: `dotnet restore`
109+
- **Build**: `dotnet build`
110+
- **Test**: `dotnet test`
111+
- **Clean**: `dotnet clean`
112+
113+
### Development Workflow
114+
**Critical**: Always follow this workflow when making changes:
115+
1. Make code changes
116+
2. Build immediately: `dotnet build` - fix any compilation errors
117+
3. Run tests: `dotnet test` - fix any test failures
118+
4. Repeat steps 1-3 iteratively as you develop
119+
5. Only after successful build and tests should you consider the change complete
120+
121+
Do not skip or defer building and testing. These are mandatory steps for every code change, no matter how small.
122+
123+
### SDK Requirements
124+
- The repo currently requires the .NET SDK 10.0 to build and run tests.
125+
- Target frameworks: .NET 10.0, .NET 9.0, .NET 8.0, .NET Standard 2.0
126+
- Support Native AOT compilation
127+
128+
### Project Structure
129+
- Source code: `src/`
130+
- Tests: `tests/`
131+
- Samples: `samples/`
132+
- Documentation: `docs/`
133+
- Build artifacts: `artifacts/` (not committed)
134+
135+
## Key Types and Architectural Layers
136+
137+
The SDK is organized into distinct architectural layers, each with specific responsibilities:
138+
139+
### Protocol Layer (DTO Types)
140+
- Located in `ModelContextProtocol.Core/Protocol/`
141+
- Contains Data Transfer Objects (DTOs) for the MCP specification
142+
- All protocol types follow JSON-RPC 2.0 conventions
143+
- Key types:
144+
- **JsonRpcMessage** (abstract base): Represents any JSON-RPC message (request, response, notification, error)
145+
- **JsonRpcRequest**, **JsonRpcResponse**, **JsonRpcNotification**: Concrete message types
146+
- **Tool**, **Prompt**, **Resource**: MCP primitive definitions
147+
- **CallToolRequestParams**, **GetPromptRequestParams**, **ReadResourceRequestParams**: Request parameter types
148+
- **ClientCapabilities**, **ServerCapabilities**: Capability negotiation types
149+
- **Implementation**: Server/client identification metadata
150+
151+
### JSON-RPC Implementation
152+
- Built-in JSON-RPC 2.0 implementation for MCP communication
153+
- **JsonRpcMessage.Converter**: Polymorphic converter that deserializes messages into correct types based on structure
154+
- **JsonRpcMessageContext**: Transport-specific metadata (transport reference, execution context, authenticated user)
155+
- Message routing handled automatically by session implementations
156+
- Error responses generated via **McpException** with **McpErrorCode** enumeration
157+
158+
### Transport Abstraction
159+
- **ITransport**: Core abstraction for bidirectional communication
160+
- Provides `MessageReader` (ChannelReader) for incoming messages
161+
- `SendMessageAsync()` for outgoing messages
162+
- `SessionId` property for multi-session scenarios
163+
- **IClientTransport**: Client-side abstraction that establishes connections and returns ITransport
164+
- **TransportBase**: Base class for transport implementations with common functionality
165+
166+
### Transport Implementations
167+
Two primary transport implementations with different invariants:
168+
169+
1. **Stdio-based transports** (`StdioServerTransport`, `StdioClientTransport`):
170+
- Single-session, process-bound communication
171+
- Uses standard input/output streams
172+
- No session IDs (returns null)
173+
- Automatic lifecycle tied to process
174+
175+
2. **HTTP-based transports**:
176+
- **SseResponseStreamTransport**: Server-Sent Events for server-to-client streaming
177+
- Unidirectional (server → client) event stream
178+
- Client posts messages to separate endpoint (e.g., `/message`)
179+
- Supports multiple concurrent sessions via SessionId
180+
- **StreamableHttpServerTransport**: Bidirectional HTTP with streaming
181+
- Request/response model with streamed progress updates
182+
- Session management for concurrent connections
183+
184+
### Session Layer
185+
- **McpSession** (abstract base): Core bidirectional communication for clients and servers
186+
- Manages JSON-RPC request/response correlation
187+
- Handles notification routing
188+
- Provides `SendRequestAsync<TResult>()`, `SendNotificationAsync()`, `RegisterNotificationHandler()`
189+
- Properties: `SessionId`, `NegotiatedProtocolVersion`
190+
191+
- **McpClient** (extends McpSession): Client-side MCP implementation
192+
- Connects to servers via `CreateAsync(IClientTransport)`
193+
- Exposes `ServerCapabilities`, `ServerInfo`, `ServerInstructions`
194+
- Methods: `ListToolsAsync()`, `CallToolAsync()`, `ListPromptsAsync()`, `GetPromptAsync()`, etc.
195+
196+
- **McpServer** (extends McpSession): Server-side MCP implementation
197+
- Configured via `McpServerOptions` and `IMcpServerBuilder`
198+
- Primitives registered as services: `McpServerTool`, `McpServerPrompt`, `McpServerResource`
199+
- Handles incoming requests through `McpServer.Methods.cs`
200+
- Supports filters via `McpRequestFilter` for cross-cutting concerns
201+
202+
### Serialization Architecture
203+
- **McpJsonUtilities.DefaultOptions**: Singleton JsonSerializerOptions for all MCP types
204+
- Hardcoded to use source-generated serialization for JSON-RPC messages (Native AOT compatible)
205+
- Source generation defined in `McpJsonUtilities` via `[JsonSerializable]` attributes
206+
- Includes Microsoft.Extensions.AI types via chained TypeInfoResolver
207+
208+
- **User-defined types** (tool parameters, return values):
209+
- Accept custom `JsonSerializerOptions` via `McpServerToolCreateOptions.SerializerOptions`
210+
- Default to `McpJsonUtilities.DefaultOptions` if not specified
211+
- Can use reflection-based serialization or custom source generators
212+
213+
- **Enum handling**: `CustomizableJsonStringEnumConverter` for flexible enum serialization
214+
215+
## Architecture and Design Patterns
216+
217+
### Server Implementation Architecture
218+
- **McpServer** is the core server implementation in `ModelContextProtocol.Core/Server/`
219+
- **IMcpServerBuilder** pattern provides fluent API for configuring servers via DI
220+
- Server primitives (tools, prompts, resources) are discovered via reflection using attributes
221+
- Support both attribute-based registration (`WithTools<T>()`) and instance-based (`WithTools(target)`)
222+
- Use `McpServerFactory` to create server instances with configured options
223+
224+
### Tool/Prompt/Resource Discovery
225+
- Tools, prompts, and resources use attribute-based discovery: `[McpServerTool]`, `[McpServerPrompt]`, `[McpServerResource]`
226+
- Type-level attributes (`[McpServerToolType]`, etc.) mark classes containing server primitives
227+
- Discovery supports both static and instance methods (public and non-public)
228+
- For Native AOT compatibility, use generic `WithTools<T>()` methods instead of reflection-based variants
229+
- `AIFunctionMcpServerTool`, `AIFunctionMcpServerPrompt`, and `AIFunctionMcpServerResource` wrap `AIFunction` for integration with Microsoft.Extensions.AI
230+
231+
### Request Processing Pipeline
232+
- Requests flow through `McpServer.Methods.cs` which handles JSON-RPC message routing
233+
- Use `McpRequestFilter` for cross-cutting concerns (logging, auth, validation)
234+
- `RequestContext` provides access to current request state and services
235+
- `RequestServiceProvider` enables scoped dependency injection per request
236+
- Filters can short-circuit request processing or transform requests/responses
237+
238+
### Transport Layer Abstraction
239+
- Transport implementations handle message serialization and connection management
240+
- Core transports: `StdioServerTransport`, `StreamServerTransport`, `SseResponseStreamTransport`, `StreamableHttpServerTransport`
241+
- Transports must implement bidirectional JSON-RPC message exchange
242+
- SSE (Server-Sent Events) transport for unidirectional server→client streaming
243+
- Streamable HTTP for request/response with streamed progress updates
244+
245+
## Implementation Patterns
246+
247+
### Tool Implementation
248+
Tools are methods marked with `[McpServerTool]`:
249+
```csharp
250+
[McpServerToolType]
251+
public class MyTools
252+
{
253+
[McpServerTool, Description("Tool description")]
254+
public static async Task<string> MyTool(
255+
[Description("Parameter description")] string param,
256+
CancellationToken cancellationToken)
257+
{
258+
// Implementation - use Description attributes for parameter documentation
259+
// Return string, TextContent, ImageContent, EmbeddedResource, or arrays of these
260+
}
261+
}
262+
```
263+
- Tools support dependency injection in constructors for instance methods
264+
- Parameters are automatically deserialized from JSON using `System.Text.Json`
265+
- Use `[Description]` attributes on parameters to generate tool schemas
266+
- Return types: `string`, `TextContent`, `ImageContent`, `EmbeddedResource`, or collections of content types
267+
268+
### Prompt Implementation
269+
Prompts return `ChatMessage` or arrays thereof:
270+
```csharp
271+
[McpServerPromptType]
272+
public static class MyPrompts
273+
{
274+
[McpServerPrompt, Description("Prompt description")]
275+
public static ChatMessage MyPrompt([Description("Parameter description")] string content) =>
276+
new(ChatRole.User, $"Prompt template: {content}");
277+
}
278+
```
279+
- Prompts can accept arguments to customize generated messages
280+
- Return single `ChatMessage` or `ChatMessage[]` for multi-turn prompts
281+
- Use `ChatRole.User`, `ChatRole.Assistant`, or `ChatRole.System` appropriately
282+
283+
### Resource Implementation
284+
Resources provide access to data with URI templates:
285+
```csharp
286+
[McpServerResourceType]
287+
public class MyResources
288+
{
289+
[McpServerResource("file:///{path}"), Description("Reads file content")]
290+
public static async Task<string> ReadFile(string path, CancellationToken cancellationToken)
291+
{
292+
// Resource URI matching uses UriTemplate syntax
293+
// Extract parameters from URI and return content
294+
}
295+
}
296+
```
297+
- Use URI templates to define resource paths with parameters
298+
- Resources support subscription for dynamic content updates
299+
- Return content types similar to tools
300+
301+
### Filters and Middleware
302+
Implement `McpRequestFilter` for request/response interception:
303+
```csharp
304+
public class LoggingFilter : McpRequestFilter
305+
{
306+
public override async ValueTask InvokeAsync(RequestContext context, Func<ValueTask> next)
307+
{
308+
// Pre-processing
309+
await next(); // Call next filter or handler
310+
// Post-processing
311+
}
312+
}
313+
```
314+
- Filters execute in registration order
315+
- Can short-circuit by not calling `next()`
316+
- Access request context, services, and can modify responses
317+
- Use for cross-cutting concerns: logging, auth, validation, caching
318+
319+
## OpenTelemetry Integration
320+
321+
- The SDK includes built-in observability support
322+
- Use ActivitySource name: `"Experimental.ModelContextProtocol"`
323+
- Use Meter name: `"Experimental.ModelContextProtocol"`
324+
- Export traces and metrics using OTLP when appropriate
325+
326+
## Documentation
327+
328+
- API documentation is generated using DocFX
329+
- Conceptual documentation is in `docs/concepts/`
330+
- Keep README files up to date in package directories
331+
- Use `///` XML comments for all public APIs
332+
- Include `<remarks>` sections for detailed explanations
333+
334+
## Security
335+
336+
- Never commit secrets or API keys
337+
- Use environment variables for sensitive configuration
338+
- Support authentication mechanisms (OAuth, API keys)
339+
- Validate all user inputs
340+
- Follow secure coding practices per SECURITY.md
341+
342+
## Additional Notes
343+
344+
- This is a preview SDK; breaking changes may occur
345+
- Follow the Model Context Protocol specification
346+
- Integrate with Microsoft.Extensions.AI patterns where applicable
347+
- Support both stdio and HTTP transports
348+
- Maintain compatibility with the broader MCP ecosystem

0 commit comments

Comments
 (0)