Skip to content

[EPIC-D][LOU-D20] mcpServers is a first-class, validated AgentSpec field - #73

Merged
LinuxDevil merged 2 commits into
mainfrom
lou-d20-spec-mcp-servers
Oct 1, 2026
Merged

LinuxDevil merged 2 commits into
mainfrom
lou-d20-spec-mcp-servers

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What

  • Adds AgentSpec.mcpServers?: Record<string, McpServerSpec> where McpServerSpec is stdio (command, optional args, env) or HTTP (url, optional headers), validated by zod with AgentSpec validation failed: ... messages. Issue paths carry the entry name (mcpServers.<name>...), so loadSpec errors name the entry.
  • loushy doctor now reads MCP servers from the validated spec (spec.mcpServers); the raw-file reader (DoctorEnvironment.readRawSpec) is removed.
  • specToAgent() does NOT connect the servers: it returns SpecAgent (= SimpleAgent & { mcpServers }) exposing the parsed servers, with a documented TODO(LOU-D20.2).
  • Docs: docs/configuration.md (YAML block + type-checked ts snippet), CHANGELOG entry, regenerated llms-full.txt.

Why

MCP servers were read by doctor from the raw YAML/JSON because the schema had no field. Making it a validated field gives one source of truth and early, named errors.

Connect vs TODO decision: specToAgent() is synchronous and the SDK has no helper that opens an MCP client transport (stdio spawn / HTTP); only loadMcpTools(client, ...) for an already-connected Client exists. Adding transports, lifecycle/cleanup and an async variant is clearly more than ~40 lines, so I exposed agent.mcpServers for a host to connect and left TODO(LOU-D20.2).

Backward compatibility: specs without the field are unchanged. The pre-existing list form doctor accepted ([{ name, command | url }], unnamed entries keyed #N) is normalized into the map by the schema, so such specs keep validating and doctor ids (spec.mcp.#3) are unchanged. Doctor message text is unchanged.

Test changes in existing files: removed doctorCore's raw-spec tests ("list or map", "survives an unreadable raw spec") and replaced them with tests against the validated field; doctor.test.ts now asserts loadSpec(...).mcpServers instead of readRawSpec. These changed because readRawSpec no longer exists, not because of message text.

Acceptance criteria

  • AgentSpec.mcpServers?: Record<string, McpServerSpec>, stdio/HTTP union, zod-validated with AgentSpec validation failed: messages; existing specs validate unchanged
  • loadSpec surfaces bad entries with the entry name in the message
  • loushy doctor reads from the validated spec; existing doctor tests pass (only tests tied to readRawSpec were changed, see above)
  • specToAgent: documented TODO(LOU-D20.2) + parsed mcpServers exposed on the result (reason above)
  • Tests: schema tests (valid stdio/HTTP, missing command+url, both, bad env type, bad url, stray fields, legacy list), doctor test on validated field, loadSpec YAML + bad-entry fixtures, specToAgent exposure test
  • Docs in docs/configuration.md, llms regenerated

How verified

  • npx tsc --noEmit - pass
  • npm run lint - 0 errors, 433 warnings (same as main's 433)
  • npm run test:types - pass (4 files, 19 tests)
  • npx vitest run - 133 files passed, 1962 passed / 3 skipped (needs npm run build first for CLI/dist-dependent tests)
  • npm run build && npm run build --workspace=packages/create-loushy-agent - pass
  • npm run docs:verify-snippets -- --skip-build - all 85 snippets type-check
  • npm run docs:llms:check - pass
  • npm run test:coverage - 133 files passed; npm run fallow - exit 0 (after splitting a complexity-flagged refine into mcpServerProblem)

🤖 Generated with Claude Code

https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR


Generated by Claude Code

claude added 2 commits October 1, 2026 11:32
Adds AgentSpec.mcpServers (stdio command/args/env or HTTP url/headers)
with zod validation and entry-named errors, makes `loushy doctor` read
the validated field instead of the raw file, and exposes the parsed
servers on specToAgent()'s result (connecting is TODO(LOU-D20.2)).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
@LinuxDevil
LinuxDevil merged commit a37cc61 into main Oct 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants