# Finishing What I Started: AutoDoc: AI-Powered OpenAPI Docs from C# 🤖

> Source: <https://dev.to/yoges_mohan_511bda5afbe7d/finishing-what-i-started-autodoc-ai-powered-openapi-docs-from-c-25g2>
> Published: 2026-05-28 07:55:03+00:00

*This is a submission for the GitHub Finish-Up-A-Thon Challenge*

AutoDoc is a tool that automatically generates enriched OpenAPI 3.0.3 documentation from raw C# ASP.NET Core controller code using a local AI model - no manual annotations, no attribute decorators, no maintenance required.

The idea came from a real frustration I noticed during my software engineering internship. Swagger documentation in .NET projects only documents what developers explicitly declare. Miss an attribute, forget an error response, skip a summary and your docs silently drift from reality. Developers spend time writing documentation instead of writing code.

AutoDoc fixes this by reading raw C# controller code directly and inferring everything automatically: routes, HTTP methods, parameters, response codes, error schemas, and operation summaries.

The project was built in 3 phases:

| Phase | What it does |
|---|---|
| Phase 1 - Console App | Paste controller code, get OpenAPI YAML in the terminal |
| Phase 2 - Docker Web API | Containerised REST backend, call via HTTP POST, get YAML back |
| Phase 3 - Playground UI | Browser dashboard: paste code, see Raw YAML + Swagger Preview instantly |

What started as a terminal script became a fully containerised, browser-accessible API documentation tool and this challenge is what pushed me to finish it.

How it works: paste any C# ASP.NET Core controller, click generate, get full OpenAPI docs instantly.

The AutoDoc Playground UI is a split-panel dashboard. On the left, you paste any ASP.NET Core controller code - no modifications needed. The sample `TodoController`

comes pre-filled so you can test immediately. Hit Generate OpenAPI Docs and AutoDoc sends the raw code to the AI backend running locally via Ollama.No attributes. No decorators. No `[ProducesResponseType]`

. Just raw C# code.

**Input: this is the only code AutoDoc received:**

```
[ApiController]
[Route("api/[controller]")]
public class TodoController : ControllerBase
{
    [HttpGet]
    public IActionResult GetAll() => Ok(new[] { "Task 1", "Task 2" });
}
```

No `[ProducesResponseType]`

. No `[SwaggerOperation]`

. No XML comments.

No manual annotations of any kind. Just raw C# code.

**Output: full OpenAPI 3.0.3 spec generated automatically:**

AI inferred everything automatically:

| What was inferred | How |
|---|---|
`operationId` |
Unique names like `GetAllTodos` , `CreateTodo`
|
`tags` |
Grouped by controller name `TodoController`
|
`summary` |
Human-readable descriptions per endpoint |
`requestBody` |
Schema inferred from method parameters |
`responses` |
200, 201, 400, 404, 500 from code logic |
`components/schemas/ErrorResponse` |
Defined once, referenced everywhere |

none of this existed in the original controller code.

Switching to the Swagger Preview tab renders the YAML as a live interactive Swagger UI. The POST `/api/Todo`

endpoint shows:

The GET `/api/Todo`

endpoint shows:

`Ok(new[] { "Task 1", "Task 2" })`

.
Internal Server Error fallback on every endpoint automatically.The AI understood what `Ok(new[] { ... })`

returns and documented it correctly as an array schema.

At the bottom of the Swagger Preview, the **Schemas** section shows the auto-generated `ErrorResponse`

schema with:

`code`

: integer`message`

: stringThis schema was never defined anywhere in the C# controller. AutoDoc inferred it from the pattern of error responses across all endpoints and generated it once, referenced consistently throughout the entire spec.

AutoDoc began as a simple console application - a proof of concept to answer 1 question:

Can an AI model read raw C# controller code and generate valid OpenAPI documentation without any manual annotations?

The answer was yes. But the Phase 1 console app was far from finished:

| What existed | What was missing |
|---|---|
| Console app that called Ollama locally | No HTTP interface - unusable by others |
| Raw YAML printed to terminal | No output validation |
| Basic Llama 3.2 prompt | No error handling |
| Proof the concept worked | No UI, no Docker, no way to share it |

It worked on my machine. That was it.

**Phase 2 - From console app to Docker REST API**

The console app was converted into a proper ASP.NET Core minimal API with 2 endpoints:

`POST /generate-openapi`

- receives controller code as JSON, returns enriched OpenAPI YAML.`GET /health`

- confirms the service is running.A `Dockerfile`

was added so the entire service runs in a container with a single `docker run`

command. The Ollama host was made configurable via environment variable so it works both locally (`localhost:11434`

) and inside Docker (`host.docker.internal:11434`

).

**Phase 3 - Playground UI**

A browser-based dashboard was built and served directly from the Docker container via `wwwroot`

. No separate frontend deployment needed - the UI and API ship together.

The playground features:

**The hardest part - taming Llama 3.2 output**

Llama 3.2 is a small, free, local model. It does a remarkable job generating mostly correct OpenAPI YAML - but it makes small inconsistent mistakes. Each one broke the Swagger Preview with a parsing error.

A full post-processing pipeline was built to fix every category of output error:

| Helper function | Problem it solves |
|---|---|
`MergeDuplicatePaths` |
Llama repeating the same API path twice |
`MergeDuplicateMethods` |
Llama repeating GET/POST under the same path |
`MergeDuplicateComponents` |
Llama emitting `components:` key twice |
`RemoveDuplicateSchemaKeys` |
Llama defining `ErrorResponse` twice |
`RemoveMalformedSecurity` |
Llama adding broken `security:` blocks |
`FixMalformedOperationIds` |
Llama using `{verb}` as a literal placeholder |
`NormaliseOutput` |
Schema name typos like `ErrorRespond`
|

Each helper was born from a real parsing error seen in production output. The pipeline runs on every generation before the YAML is returned to the client.

| Phase 1 - Console App | Phase 3 - Full AutoDoc | |
|---|---|---|
| Interface | Terminal only | Browser dashboard |
| Deployment | Local .NET required | Docker container |
| Output | Raw YAML in terminal | Raw YAML + live Swagger Preview |
| Validation | None | YAML cleaning pipeline + spec validation |
| Usability | Developer only | Anyone with a browser |
| Shareable | No | Yes - 1 `docker run` command |

What started as a terminal experiment is now a containerised, browser-accessible documentation tool that anyone can run and use.

GitHub Copilot was my implementation partner throughout AutoDoc.I defined the problems and architecture,Copilot wrote the code.

I used Copilot not for autocomplete but as a problem-to-code translator. I described what I needed in plain English, Copilot read my existing code, and produced working implementations I could review, test, and keep.

The first key prompt connected the backend to the frontend:

"I am building an ASP.NET Core 10 Minimal API called AutoDoc.I want to serve static files from a wwwroot folder and make sure it serves index.html as the default page. How should I update my Program.cs to allow this?"

Copilot read my existing `Program.cs`

directly, understood the current pipeline, and knew exactly what to add without breaking anything.

It explained what it was doing and why - then applied the change automatically:

```
app.UseDefaultFiles();
app.UseStaticFiles();
```

2 lines. Copilot explained that `UseDefaultFiles()`

rewrites `/`

to `/index.html`

and `UseStaticFiles()`

serves everything in `wwwroot`

. It then validated the file for compile errors before confirming. The change was applied in one shot - I clicked **Keep**.

The result: `index.html`

created in `wwwroot`

, `Program.cs`

updated, Copilot confirming **1 file changed, +4 -0**. The Playground UI was now being served directly from the Docker container.

With the static file serving in place, the next prompt built the entire frontend:

"Build a dark-themed single-page HTML dashboard for AutoDoc.Left panel: controller name input and C# code textarea with a Generate button. Right panel: tabbed output with Raw YAML (monospace) and Swagger Preview rendered from the YAML using SwaggerUIBundle. Show a status pill in the header that updates during generation. No frameworks, vanilla JS only."

Copilot generated the complete `index.html`

:

`fetch`

call to `/generate-openapi`

with full error handling.`jsyaml`

for YAML-to-spec parsing.The entire Playground UI came from a single prompt.

Every YAML cleaner in AutoDoc came from describing a real Llama output bug to Copilot:

"Llama is generating the`components:`

key twice in the output YAML. Write a C# helper that scans line by line and removes duplicate`components:`

keys, keeping only the first."

Copilot wrote `MergeDuplicateComponents`

. I repeated this pattern for all seven helpers - each one targeting a specific category of Llama output error discovered during testing.

Copilot was most valuable not when I said *"write this"* but when I said *"here is the problem I am seeing, fix it."*

It read my existing code before responding. It explained its reasoning. It validated changes before applying them. It felt less like autocomplete and more like a senior developer who already knew my codebase.

Copilot is powerful but not perfect. Here is what it could not do:

| Limitation | What happened |
|---|---|
| Could not predict Llama output bugs | Every YAML cleaner was reactive - I discovered the bug first, then asked Copilot to fix it |
| Did not flag environment differences | Generated `host.docker.internal` hardcoded - broke local `dotnet run` , I found the error myself |
| Could not test browser output | Generated the Playground UI confidently but could not see the broken Swagger Preview in a browser |
| Prompt quality = output quality | Vague prompts gave generic results - writing precise prompts was a skill I had to develop |

The pattern was consistent: Copilot excelled at implementation, but discovery and debugging required a human.It could not run the code, open the browser, or experience the errors - I had to do that and bring the findings back to Copilot to fix.

AutoDoc is functional but it is just the beginning.

The current version uses **Llama 3.2 locally via Ollama** which is free

and private, but limited in output consistency. The natural next step is upgrading to a more capable model for cleaner, more reliable YAML generation.

Here is what I hope to build next:

| Feature | Why |
|---|---|
| Support for larger Ollama models (Llama 3.1, Mistral) | Better output quality, fewer post-processing fixes needed |
| Multi-controller batch generation | Generate docs for an entire project at once |
| GitHub Actions CI/CD integration | Auto-generate docs on every push |
| Diff-based incremental generation | Only regenerate endpoints that changed |
| Export to JSON | Support both YAML and JSON OpenAPI formats |

The biggest hope is this: AI-generated documentation that stays in sync with code automatically - no manual maintenance, no drift, no outdated Swagger specs.

Developers should write code. The AI should write the docs.

Working with GitHub Copilot on AutoDoc changed how I think about

building software. Before this project, I used it for small autocomplete suggestions. After this project, I use it as a genuine collaborator.

What I hope to do differently next time:

The most important thing I learned: Copilot is only as good as the problem you give it.vague problems get vague code. Clear problems get working code.

AutoDoc taught me to think more clearly about problems because I had to describe them precisely enough for an AI to solve them. That skill makes me a better developer with or without Copilot.

*Try it yourself! clone the repo, run Ollama, and paste your 1st C# controller. I'd love to see what AutoDoc generates for your API. Drop a comment with your results.*
