Skip to content

feat: CardDAV MCP server with contact and address book tools - #1

Merged
dominik1001 merged 5 commits into
mainfrom
claude/beautiful-carson-4lqy46
Jul 7, 2026
Merged

feat: CardDAV MCP server with contact and address book tools#1
dominik1001 merged 5 commits into
mainfrom
claude/beautiful-carson-4lqy46

Conversation

@dominik1001

Copy link
Copy Markdown
Owner

Summary

Initial implementation of carddav-mcp: a focused MCP server exposing CardDAV contacts and address books as tools for AI assistants, mirroring the architecture, tooling, and conventions of the sibling project caldav-mcp.

Tools

  • list-address-books — name + URL of every address book
  • list-contacts — per-contact summary (uid, fn, emails, phones)
  • get-contact — full parsed fields plus the raw vCard
  • create-contact — creates a vCard 3.0 contact, returns its UID
  • update-contact — partial update by UID
  • delete-contact — delete by UID

Design notes

  • CardDAV client: tsdav ^2.3.0 — maintained, MIT, plain npm semver range (no forks, no git deps, no vendoring).
  • vCard handling (src/tools/vcard.ts): line-level parse/serialize with unfolding, escaping, and group/parameter tolerance. Updates are surgical — only the lines of the fields being changed are replaced, so properties this server doesn't model (PHOTO, ADR, BDAY, …) survive a partial update.
  • UID resolution (src/tools/resolve-contact.ts): <uid>.vcf href fast path with a full address-book scan fallback for cards created by other clients under arbitrary filenames.
  • Stack: strict TypeScript + ESM on Node ≥18, one file per tool under src/tools/ with a co-located test, Zod input schemas, Biome, Vitest, lefthook, knip, conventional commits + semantic-release, generated README tool docs.

Testing

  • npm run validate green: Biome check, 37 unit tests, knip, docs:check, build.
  • npm run smoke green against a real Radicale CardDAV server: create → list → get → update → delete round-trip, including asserting a partial update preserves untouched fields. CI runs the same harness (Radicale on the runner).
  • npm run smoke:agent green: an LLM drove all six tools end-to-end via claude -p and confirmed graceful errors on bad input.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WJjmg9LMuvaNqbTAXQahgU


Generated by Claude Code

claude added 5 commits July 7, 2026 15:12
Strict TypeScript + ESM on Node >=18 with Biome (lint + format), Vitest,
lefthook, commitlint, knip, and semantic-release — mirroring the
caldav-mcp sibling project. The CardDAV client is tsdav, a maintained
library from the npm registry pinned to a normal semver range.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJjmg9LMuvaNqbTAXQahgU
Exposes six tools over a StdioServerTransport: list-address-books,
list-contacts, get-contact, create-contact, update-contact, and
delete-contact. Each tool lives in its own file under src/tools/ with a
co-located Vitest test and a Zod input schema.

vCard handling is a small line-level helper (src/tools/vcard.ts):
updates replace only the lines of the fields being changed, so
properties this server does not model (PHOTO, ADR, BDAY, ...) survive a
partial update. Contacts are resolved by UID with a <uid>.vcf href
fast path and a full address book scan fallback for cards created by
other clients under arbitrary filenames.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJjmg9LMuvaNqbTAXQahgU
scripts/smoke.ts spawns the built server over stdio via the MCP client
SDK and asserts the full contact round-trip (create -> list -> get ->
update -> delete), including that a partial update preserves fields it
did not touch. The release workflow runs it in CI against a Radicale
CardDAV server alongside lint, knip, docs:check, unit tests, and build,
then publishes via semantic-release on main.

scripts/smoke-agent.sh is the agent-ergonomics harness: it drives the
server through 'claude -p' with a JSON output schema to verify the tool
surface is usable by an LLM, including graceful errors on bad input.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJjmg9LMuvaNqbTAXQahgU
The review job hard-fails on repos where CLAUDE_CODE_OAUTH_TOKEN is not
configured yet. A guard job now checks for the secret and the review is
skipped until it is added, keeping PR checks green in the meantime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJjmg9LMuvaNqbTAXQahgU
semantic-release defaults an initial release to 1.0.0 when no previous
tag exists. The release job now tags the root commit v0.0.0 before
running semantic-release, so the feat commits in the first release bump
minor to 0.1.0. The step is a no-op once any v* tag exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJjmg9LMuvaNqbTAXQahgU
@dominik1001
dominik1001 merged commit ce613c2 into main Jul 7, 2026
9 checks 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