Skip to content

[PM-35353] feat: support custom fields, organizationId and collectionIds on items#177

Open
tecnologicachile wants to merge 1 commit into
bitwarden:mainfrom
tecnologicachile:feat/custom-fields-and-collections
Open

[PM-35353] feat: support custom fields, organizationId and collectionIds on items#177
tecnologicachile wants to merge 1 commit into
bitwarden:mainfrom
tecnologicachile:feat/custom-fields-and-collections

Conversation

@tecnologicachile
Copy link
Copy Markdown
Contributor

Summary

The create_item and edit_item tools currently expose only name, type, login/card/identity/secureNote and folderId. This forces MCP consumers to drop down to the bw CLI directly whenever they need to:

  • Create or update a vault item with custom fields
  • Put an item inside an organization + collection
  • Rename or migrate a custom field on an existing item

The underlying bw CLI already understands all three (fields are part of the item JSON; organizationId/collectionIds live in the same object; the CLI also needs --organizationid as a flag at create time), so this PR just surfaces them through the MCP schema and threads them through the handler.

Changes

  • src/utils/types.ts — extend BitwardenItem with fields, organizationId, collectionIds.
  • src/schemas/cli.ts — new customFieldSchema (name + optional value + type 0..3) and extensions to createItemSchema/editItemSchema.
  • src/handlers/cli.ts
    • handleCreateItem copies organizationId/collectionIds/fields into the JSON item and appends --organizationid <id> when present.
    • handleEditItem updates organizationId/collectionIds and replaces the fields array on the existing item when one is supplied (callers that want to merge should read the item first and merge client-side).
  • src/tools/cli.ts — exposes the new parameters to MCP clients for both create_item and edit_item.

Backward compatible: all new parameters are optional; existing calls behave identically when they're omitted.

Motivation

Encountered while using the MCP server from an assistant to populate Bitwarden with SSH credentials that need a muxterm_init_path-style custom field (for a tool that reads the field and auto-cd's into that directory after login). Without this PR, the only way to create the items with the custom field is to shell out to bw edit item <id> <base64> directly or to wrap the MCP in a consumer-specific API.

Test plan

  • npm run build compiles cleanly (no TypeScript errors)
  • Manually verified custom field + organization + collection round-trip against a self-hosted Vaultwarden via the modified MCP (login item created with Initial_Path field lands in the correct collection, readable back via get item)
  • Existing test suite runs — tests/cli-commands.spec.ts has pre-existing test timeouts in my environment (unrelated tests, no change in pass/fail counts from my diff). Happy to adjust if CI flags anything new.

@tecnologicachile tecnologicachile requested a review from a team as a code owner April 20, 2026 00:20
@bitwarden-bot
Copy link
Copy Markdown

Thank you for your contribution! We've added this to our internal tracking system for review.
ID: PM-35353
Link: https://bitwarden.atlassian.net/browse/PM-35353

Details on our contribution process can be found here: https://contributing.bitwarden.com/contributing/pull-requests/community-pr-process.

@bitwarden-bot bitwarden-bot changed the title feat: support custom fields, organizationId and collectionIds on items [PM-35353] feat: support custom fields, organizationId and collectionIds on items Apr 20, 2026
tecnologicachile added a commit to tecnologicachile/bitwarden-mcp-server that referenced this pull request Apr 24, 2026
- fix: use fileURLToPath + realpathSync for main module detection (PR bitwarden#168)
- feat: support custom fields, organizationId and collectionIds on create/edit item (PR bitwarden#177)
- docs: remove root CLAUDE.md (context moved to .claude/CLAUDE.md and memory)
tecnologicachile added a commit to tecnologicachile/bitwarden-mcp-server that referenced this pull request Apr 25, 2026
- fix: use fileURLToPath + realpathSync for main module detection (PR bitwarden#168)
- feat: support custom fields, organizationId and collectionIds on create/edit item (PR bitwarden#177)
- docs: remove root CLAUDE.md (context moved to .claude/CLAUDE.md and memory)
The `create_item` and `edit_item` tools currently expose only name, type,
login/card/identity/secureNote and folderId. This forces MCP consumers to
drop down to the `bw` CLI directly whenever they need to:

  - Create or update a vault item with custom fields
  - Put an item inside an organization + collection
  - Rename or migrate a custom field on an existing item

The underlying `bw` CLI already understands all three (fields are part of
the item JSON; organizationId/collectionIds live in the same object; the
CLI also needs --organizationid as a flag at create time), so this PR just
surfaces them through the MCP schema and threads them through the handler.

Changes
-------
- src/utils/types.ts: extend BitwardenItem with fields, organizationId,
  collectionIds.
- src/schemas/cli.ts: new customFieldSchema (name + optional value + type
  0..3) and extensions to createItemSchema/editItemSchema.
- src/handlers/cli.ts:
  * handleCreateItem copies organizationId/collectionIds/fields into the
    JSON item and appends `--organizationid <id>` when present.
  * handleEditItem updates organizationId/collectionIds and replaces the
    fields array on the existing item when one is supplied (callers that
    want to merge should read the item first and merge client-side).
- src/tools/cli.ts: exposes the new parameters to MCP clients for both
  create_item and edit_item.

Backward compatible: all new parameters are optional and existing calls
behave identically when they're omitted.
@tecnologicachile tecnologicachile force-pushed the feat/custom-fields-and-collections branch from b816ee2 to 4672c77 Compare April 25, 2026 03:32
@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

❌ Patch coverage is 17.39130% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 51.02%. Comparing base (86b89d9) to head (4672c77).
⚠️ Report is 4 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/handlers/cli.ts 13.63% 15 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #177      +/-   ##
==========================================
- Coverage   51.92%   51.02%   -0.91%     
==========================================
  Files          12       12              
  Lines         909      929      +20     
  Branches      197      208      +11     
==========================================
+ Hits          472      474       +2     
- Misses        387      401      +14     
- Partials       50       54       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@withinfocus withinfocus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! One consistency suggestion and one correction for how orgs and collections work with edits.

Comment thread src/schemas/cli.ts
// Folder ID to assign the item to
folderId: z.string().optional(),
// Organization ID (required to create items shared with an organization)
organizationId: z.string().optional(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ The new fields use z.string().optional() and z.array(z.string()).optional(), which permit empty strings. The rest of the codebase enforces non-empty strings on the same identifiers via .min(1, '...'):

  • editItemCollectionsSchema (line 469) -- organizationId: z.string().min(1, ...) and collectionIds: z.array(z.string().min(1, 'Collection ID cannot be empty'))
  • moveSchema (line 479) -- same pattern
  • createOrgCollectionSchema (line 443) -- same pattern

With the current PR code, { organizationId: "" } validates successfully and then becomes the literal argument --organizationid "" passed to bw, producing a confusing CLI error rather than a clear validation message. Empty strings in collectionIds produce the same class of issue.

Suggested fix (apply to both createItemSchema and editItemSchema):

organizationId: z.string().min(1, 'Organization ID cannot be empty').optional(),
collectionIds: z
  .array(z.string().min(1, 'Collection ID cannot be empty'))
  .optional(),

Same change at src/schemas/cli.ts:419 for editItemSchema.

Comment thread src/handlers/cli.ts
if (name !== undefined) existingItem.name = name;
if (notes !== undefined) existingItem.notes = notes;
if (folderId !== undefined) existingItem.folderId = folderId;
if (organizationId !== undefined)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ The mechanism for moving an item from a personal vault to an organization (or between organizations) is bw move <itemid> <organizationid> [encodedJson]. handleMove already exposes this. These organization and collection changes won't be applied.

  • Drop organizationId from editItemSchema entirely. If collectionIds is meant to manage org-collection membership for an item, drop it too.
  • If organizationId / collectionIds need to remain on edit_item for some validated use case (please share a CLI invocation that actually works), then at minimum mirror handleCreateItem and pass --organizationid through to bw edit item, and verify with the CLI that the change takes effect end-to-end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants