> ## Documentation Index
> Fetch the complete documentation index at: https://ancplua.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server Generator

> Source generator producing MCP dispatch, JSON Schema, OTel instrumentation, and SKILL.md at compile time.

The `Qyl.Agents.Generator` package is an incremental source generator that transforms `[McpServer]` classes into fully functional MCP servers at build time.

## How It Works

1. **Extraction** — scans for `[McpServer]` classes and `[Tool]` methods
2. **Modeling** — builds value-equatable models (incremental generator cache-friendly)
3. **Generation** — emits a single `.g.cs` file per server with all dispatch, schema, OTel, and metadata code

## Server Declaration

```csharp theme={null}
using Qyl.Agents;

/// <summary>A document search server</summary>
[McpServer]
public partial class DocTools
{
    /// <summary>Search documents by query</summary>
    [Tool]
    public string Search(
        [Description("Query text")] string query,
        int limit = 10) => query;
}
```

<Note>
  The class must be `partial` — the generator adds the `IMcpServer`
  implementation.
</Note>

### McpServer Attribute

| Property      | Type      | Default                        | Description                 |
| ------------- | --------- | ------------------------------ | --------------------------- |
| `Name`        | `string?` | Class name, kebab-cased        | Server name in MCP protocol |
| `Description` | `string?` | XML doc summary                | Server description          |
| `Version`     | `string?` | Assembly informational version | Semantic version            |

### Tool Attribute

| Property      | Type              | Default                  | Description                                  |
| ------------- | ----------------- | ------------------------ | -------------------------------------------- |
| `Name`        | `string?`         | Method name, kebab-cased | Tool name in MCP protocol                    |
| `Description` | `string?`         | XML doc summary          | Tool description                             |
| `ReadOnly`    | `ToolHint`        | `Unset`                  | Hint: tool does not modify state             |
| `Idempotent`  | `ToolHint`        | `Unset`                  | Hint: repeated calls have same effect        |
| `Destructive` | `ToolHint`        | `Unset`                  | Hint: tool may delete or destroy data        |
| `OpenWorld`   | `ToolHint`        | `Unset`                  | Hint: tool interacts with external systems   |
| `TaskSupport` | `ToolTaskSupport` | `Unset`                  | Declares long-running task execution support |

Safety hints are emitted in `tools/list` only when non-`Unset`. `ToolHint` has three values: `Unset` (omitted), `True`, `False`.

`ToolTaskSupport` controls whether a tool can return an MCP task instead of an immediate result:

| Value       | Wire behavior                                   |
| ----------- | ----------------------------------------------- |
| `Unset`     | Omitted from `tools/list`                       |
| `Forbidden` | Task execution is forbidden for this tool       |
| `Optional`  | Server may return a task or an immediate result |
| `Required`  | Server always returns a task                    |

```csharp theme={null}
[Tool(ReadOnly = ToolHint.True, Idempotent = ToolHint.True)]
public string Search([Description("Query")] string query) => query;

[Tool(TaskSupport = ToolTaskSupport.Optional)]
public Task<string> LongRunningAnalysis([Description("Input")] string data) => /* ... */;
```

## Generated Output

For each `[McpServer]` class, the generator produces a single `.g.cs` file containing:

### Tool Dispatch

A `DispatchToolCallAsync` method that routes by tool name using a switch expression:

```csharp theme={null}
public async Task<string> DispatchToolCallAsync(
    string toolName,
    JsonElement arguments,
    CancellationToken cancellationToken = default)
{
    return toolName switch
    {
        "search" => await ExecuteTool_SearchAsync(arguments, cancellationToken),
        _ => throw new ArgumentException($"Unknown tool: {toolName}")
    };
}
```

### Parameter Deserialization

Primitives use direct `JsonElement` accessors (AOT-safe). Complex types fall back to `JsonSerializer`.

| Type                     | Accessor                          | AOT-safe |
| ------------------------ | --------------------------------- | -------- |
| `string`                 | `GetString()`                     | Yes      |
| `int`                    | `GetInt32()`                      | Yes      |
| `bool`                   | `GetBoolean()`                    | Yes      |
| `DateTimeOffset`         | `GetDateTimeOffset()`             | Yes      |
| `Guid`                   | `GetGuid()`                       | Yes      |
| `Uri`                    | `new Uri(GetString())`            | Yes      |
| Enums                    | `Enum.Parse<T>(GetString())`      | Yes      |
| `string[]`, custom types | `JsonSerializer.Deserialize<T>()` | No       |

### JSON Schema

Each tool gets a static `s_schema_{MethodName}` byte array with the JSON Schema for its input parameters, including types, formats, required fields, descriptions, and enum values.

### OpenTelemetry Instrumentation

Every tool call emits:

* An `ActivitySource("Qyl.Agents")` span with `gen_ai.*` semantic conventions
* A `gen_ai.client.operation.duration` histogram for latency tracking
* Error status and `error.type` tag on exceptions

### SKILL.md

A `SkillMd` static property with the full SKILL.md content including YAML frontmatter, tool descriptions, and parameter documentation.

### LLMS.txt

A `LlmsTxt` static property with a summary of the server's capabilities, including tools, resources, and prompts sections.

## Resources

Expose read-only data via `[Resource]`:

```csharp theme={null}
[McpServer]
public partial class ConfigServer
{
    [Resource("config://app-settings")]
    public Task<string> GetAppSettings(CancellationToken ct) =>
        File.ReadAllTextAsync("appsettings.json", ct);
}
```

### Resource Attribute

| Property      | Type      | Default         | Description                   |
| ------------- | --------- | --------------- | ----------------------------- |
| `Uri`         | `string`  | (required)      | Resource URI for MCP protocol |
| `Name`        | `string?` | `null`          | Display name for the resource |
| `Description` | `string?` | XML doc summary | Resource description          |
| `MimeType`    | `string?` | `null`          | Content MIME type             |

The generator emits `DispatchResourceReadAsync` for URI-based dispatch and `GetResourceInfos` for `resources/list`.

Supported return types for resource methods:

| Return type         | Content encoding |
| ------------------- | ---------------- |
| `string`            | Plain text       |
| `Task<string>`      | Plain text       |
| `ValueTask<string>` | Plain text       |
| `byte[]`            | Base64-encoded   |
| `Task<byte[]>`      | Base64-encoded   |
| `ValueTask<byte[]>` | Base64-encoded   |

## Prompts

Expose reusable prompt templates via `[Prompt("name")]`:

```csharp theme={null}
[McpServer]
public partial class MyServer
{
    /// <summary>Summarize a document</summary>
    [Prompt("summarize")]
    public string Summarize([Description("Text to summarize")] string text) =>
        $"Please summarize the following:\n\n{text}";

    /// <summary>Code review with structured messages</summary>
    [Prompt("review")]
    public PromptResult Review([Description("Code to review")] string code) =>
        new([new(PromptRole.User, $"Review this code:\n\n{code}")]);
}
```

* `string` return → wrapped as a single user-role message
* `PromptResult` return → structured messages passed through directly

`PromptMessage` takes `(string role, string content)` via constructor. Use the `PromptRole` constants (`User`, `Assistant`, `System`) for type-safe role values.

The generator emits `DispatchPromptAsync` and `GetPromptInfos` for `prompts/list` and `prompts/get`. Prompt methods must return `string`, `Task<string>`, `PromptResult`, or `Task<PromptResult>`.

## Supported Parameter Types

| Category   | Types                                                                                                              |
| ---------- | ------------------------------------------------------------------------------------------------------------------ |
| Primitives | `string`, `bool`, `int`, `long`, `double`, `float`, `decimal`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `ulong` |
| Special    | `DateTimeOffset`, `DateTime`, `Guid`, `Uri`                                                                        |
| Enums      | Any enum type (schema includes allowed values)                                                                     |
| Nullable   | `T?` for all above                                                                                                 |
| Complex    | Arrays, custom objects (requires `JsonSerializer`)                                                                 |
| Control    | `CancellationToken` (passed through, not in schema)                                                                |

## Supported Return Types

| Type                       | Serialization                   |
| -------------------------- | ------------------------------- |
| `void`                     | Returns `"null"`                |
| `string`                   | Direct passthrough              |
| `bool`                     | `"true"` / `"false"`            |
| Numeric types              | `ToString(InvariantCulture)`    |
| `Task` / `ValueTask`       | Returns `"null"`                |
| `Task<T>` / `ValueTask<T>` | Same rules as sync `T`          |
| Complex types              | `JsonSerializer.Serialize<T>()` |
