A side-by-side look at the Watchtower and Salesforce Metadata MCP MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Watchtower for developer tools work; choose Salesforce Metadata MCP for developer tools as a local process. You can also run both side by side in one mcpServers block.
DATABASE_URL.watch_jobs, search_jobs, list_companies.SF_INSTANCE_URL, SF_ACCESS_TOKEN.sf_create_custom_object, sf_create_custom_field, sf_add_picklist_values.Rows where the two servers differ are marked.
| Spec | W Watchtower | |
|---|---|---|
| Category | Developer Tools | Developer Tools |
| Quality signal | 53/100 · Good | 63/100 · Good |
| Install command | Not confirmed yet | Confirmed |
| Transport | — | Local subprocess (stdio) |
| Auth | No auth required | Not declared |
| Pricing | Free | Not declared |
| Required env vars | DATABASE_URL | SF_INSTANCE_URLSF_ACCESS_TOKEN |
| Verified owner | Community listing | Community listing |
| GitHub stars | 0 | 2 |
| Directory activity | 0 views 0 installs 0 upvotes | 2 views 0 installs 0 upvotes |
| Tools | 8 | 20 |
| Full listing | View Watchtower | View Salesforce Metadata MCP |
watch_jobsCreate a persistent watch for tech jobs. Describe what you want in query ("iOS jobs in Austin making at least 150k a year with a maximum of 6 years of experience") and leave url out: the watch then covers every job board Watchtower monitors (tech companies and startups, whatever platform they use) and reports each new matching posting (JOB_ADDED). The response shows how the query was read (interpreted), the jobs open right now that match (current_jobs) and how many boards are covered (coverage); later call get_changes for new ones. Use this INSTEAD OF re-running job searches or re-checking careers pages yourself. To follow one company, or to cover a company the directory is missing, pass url (or urls for several): Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Workday, iCIMS, Oracle Recruiting, Eightfold and SuccessFactors boards, and the Apple, Google, Amazon and Microsoft careers sites, are read through their own endpoints; other careers pages are parsed via schema.org JobPosting JSON-LD. A careers page that only links to a supported board is watched through that board (resolved_from says so); a page with neither is rejected with NO_JOB_DATA. A board you watch stays covered for every search watch. A board watch emits JOB_ADDED / JOB_REMOVED / JOB_UPDATED. Jobs carry title, location, other_locations, department, company, url, posted_at, remote, seniority, and salary / experience_years when the posting states them. Explicit filters override what the query says.search_jobsFind the tech jobs open right now that match a request, across every job board Watchtower monitors (tech companies and startups). Describe what you want in query ("iOS jobs in Austin with a maximum of 6 years of experience"). Read-only: nothing is saved and no watch is created, and no token is needed. The response shows how the query was read (interpreted), the matching jobs newest first, the total, and next_offset for the next page. Use this for "what is open right now"; to be told about new postings later, use watch_jobs instead. Jobs carry title, location, other_locations, department, company, url, posted_at, remote, seniority, and salary / experience_years when the posting states them. Explicit filters override what the query says.list_companiesList the companies whose job boards Watchtower monitors, i.e. what search_jobs and watch_jobs without a url cover. Pass query to check whether a company is covered ("stripe"). Each entry has name, board_url (pass it to watch_jobs as url to follow only that company), platform and open_jobs. Read-only, no token needed. If a company is missing, watch its board URL or careers page to add it.get_changesReturn job changes detected since your last get_changes call, across all your watches or one watch. This is the cheap replacement for re-browsing careers pages: if it returns an empty list, nothing you care about has changed. By default the cursor advances so each change is delivered once; pass peek=true to look without advancing, or since=<cursor> to replay.list_watchesList all active watches for your client with their health, last check time and number of pending (undelivered) changes.get_watchGet a watch with its status and the jobs currently open that match its filters: on its board, or across every monitored board for a watch created without a url. Use this to answer "what is open right now" without searching or fetching careers pages yourself.ack_changesMark changes up to a cursor as processed. Use with get_changes(peek=true) for at-least-once delivery: peek, act on the changes, then ack the cursor get_changes returned. Not needed if you call get_changes without peek (that acknowledges automatically).delete_watchStop monitoring. Frees one of your watch slots. Delete watches you no longer need.sf_create_custom_objectCreates a new Salesforce Custom Object using the Metadata API. The object name must end with '__c'. Use this when a user asks to create a new object, entity, or table in Salesforce.sf_create_custom_fieldCreates a new custom field on an existing Salesforce object. The field API name must end with '__c'. Supports all field types: Text, Number, Picklist, Lookup, etc.sf_add_picklist_valuesAdds new picklist values to an existing Picklist or MultiselectPicklist field without removing existing values. Use when a user wants to add new options to a dropdown.sf_create_approval_processCreates or updates a Salesforce Approval Process via the Metadata API. Define who can submit, approval steps with approvers, entry criteria, and what happens on approval or rejection.sf_create_validation_ruleCreates or updates a Salesforce Validation Rule on any object via the Metadata API. The errorConditionFormula returns TRUE when data is INVALID. Use for data quality enforcement.sf_create_workflow_field_updateCreates a Workflow Field Update action that can be referenced by Approval Processes, Workflow Rules, or Flows. Sets a field to a literal value, formula result, or null.sf_create_formula_fieldCreates a formula field on any Salesforce object. Supports all return types (Text, Number, Currency, Date, DateTime, Checkbox, Percent) and the full Salesforce formula language: IF/AND/OR/NOT, BLANKVALUE, TEXT, VALUE, DATE, DATEVALUE, TODAY, NOW, date functions (MONTH/YEAR/DAY), math (FLOOR/CEILING/MOD), string functions (LEN/LEFT/RIGHT/MID/TRIM/UPPER/LOWER/CONTAINS/BEGINS), record type and picklist functions (ISPICKVAL, ISNULL, ISBLANK), cross-object field references (e.g. Account.Owner.Name), and VLOOKUP. Complex multi-line formulas are fully supported.sf_deploy_metadataDeploys a set of metadata components directly to the org using the Metadata API SOAP deploy operation. Builds a package.xml and deployment zip in memory. Supports validate-only (checkOnly:true) for pre-deployment validation without making changes. Specify runTests to execute test classes during deployment (required for production). Polls until complete or timeout.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
No confirmed install command for Watchtower yet — follow the setup steps linked from its listing page rather than a guessed package name.
{
"mcpServers": {
"salesforce-metadata-mcp": {
"command": "npx",
"args": [
"-y",
"salesforce-metadata-mcp"
],
"env": {
"SF_INSTANCE_URL": "YOUR_SF_INSTANCE_URL_HERE",
"SF_ACCESS_TOKEN": "YOUR_SF_ACCESS_TOKEN_HERE"
}
}
}
}
sf_check_deploy_statusChecks the status of an in-progress or recently completed metadata deployment by async job ID. Returns the status (Pending, InProgress, Succeeded, Failed, Canceled), component successes, failures, and test results. Use with the deploy ID returned from sf_deploy_metadata.sf_retrieve_metadataRetrieves metadata components from the org and returns their actual file contents. Use this to read existing configuration before making changes, to back up metadata, or to check what is really deployed rather than what you think is deployed. Waits for the async retrieve to finish and unpacks the resulting zip, returning each file's path and source. Large files are truncated. Accepts 'components' (array), or 'metadataType'+'componentName' as a single-item shortcut, or a raw 'packageXml' document — provide exactly one form.sf_delete_metadataPermanently deletes one or more metadata components of a given type via the Metadata API's deleteMetadata call — works for CustomObject, CustomField, Flow, GenAiFunction, GenAiPlugin, GenAiPlannerBundle, Bot, ApexClass, and most other metadata types. There was previously no way to remove anything created by this MCP server (sf_deploy_metadata only supports adding/updating components, not destructiveChanges) — diagnostic or abandoned metadata had nowhere to go. Deletes each fullName independently: check the response's deleted/errors lists rather than assuming all all-or-nothing. Some types have dependency order requirements (e.g. delete a Bot's GenAiFunction/GenAiPlugin/GenAiPlannerBundle before the Bot itself, delete CustomField before its parent CustomObject) — Salesforce will reject a delete that still has dependents, naming them in the error.sf_query_recordsExecutes a SOQL query against the org and returns matching records. Provide the full SOQL string in the query param. Use for reading data, checking existing records before creating, or verifying changes.
Supports aggregate queries — GROUP BY with COUNT(), SUM(), AVG(), MAX(), MIN(), e.g.:
'SELECT StageName, COUNT(Id), SUM(Amount) FROM Opportunity GROUP BY StageName'
Aggregate results come back as regular records with the aggregate expressions as field keys (e.g. "expr0").
Row cap: 'limit' defaults to 200 and is a ceiling, not a hint. If the SOQL carries its own LIMIT the smaller of the two applies, so a LIMIT in the query can tighten the cap but never raise it. When rows are cut off the response sets truncated: true and reports appliedLimit and limitSource, so a capped result is never mistaken for a complete one.