From e52eeb93c3192c96dd1f9a0d74d393b14a56c4e7 Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:07:47 -0300 Subject: [PATCH 1/8] docs(mcp): design strict duplicate checks before creation --- ...07-29-mcp-strict-duplicate-check-design.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-29-mcp-strict-duplicate-check-design.md diff --git a/docs/superpowers/specs/2026-07-29-mcp-strict-duplicate-check-design.md b/docs/superpowers/specs/2026-07-29-mcp-strict-duplicate-check-design.md new file mode 100644 index 0000000..a3bf77b --- /dev/null +++ b/docs/superpowers/specs/2026-07-29-mcp-strict-duplicate-check-design.md @@ -0,0 +1,84 @@ +# MCP Strict Duplicate Check Design + +## Problem + +The MCP currently requires application registration and mentions duplicate detection, but the wording is not strict enough to ensure that clients search thoroughly before calling `Create-Application`. + +A client can perform one weak or empty search, treat the absence of results as proof that no application exists, and create a duplicate. This is especially risky because `vacancyLink` is optional and `Search-Applications` does not search the link field directly. + +## Goal + +Strengthen MCP instructions so every client must actively search for confirmed and possible duplicates before creating an application. + +This change is instruction-only. It does not add backend duplicate enforcement, database constraints, URL normalization, new tools, or service logic. + +## Mandatory Duplicate-Check Workflow + +Before every `Create-Application` call, the client must complete all applicable checks below. + +1. Extract the available identifying fields: + - vacancy URL; + - vacancy title; + - organization; + - recruiter name. +2. Search active applications separately using the available title, organization, and recruiter terms. +3. Search archived applications as well. Archived records must participate in duplicate detection and must not be ignored merely because they are hidden from the default active list. +4. Inspect returned records and compare all available identifying fields, including `vacancyLink` when present. +5. Do not treat one empty or weak search result as sufficient evidence that no duplicate exists. +6. Classify matching records as: + - **confirmed duplicate** when the URL matches, or when title, organization, and recruiter identify the same vacancy with high confidence; + - **possible duplicate** when title and organization match but the remaining information is missing, different, or inconclusive. +7. For a confirmed duplicate, reuse the existing record and do not call `Create-Application`. +8. For a possible duplicate, do not create immediately. Show the matching records and ask the user to confirm whether the vacancy is distinct. +9. Call `Create-Application` only after explicitly concluding that no confirmed or possible duplicate remains unresolved. + +When the matching record is archived, the client must identify it as archived and prefer `Restore-Application` when the user intends to continue using the same application record. + +## Instruction Locations + +Repeat the safety-critical rules in three places so clients still receive them when server instructions or resources are cached or partially loaded: + +1. `McpApplicationCreationRulesResource` +2. the `intake_vacancy` prompt in `McpPromptsConfig` +3. the `Create-Application` tool description in `McpApplicationTools` + +The wording must explicitly include these concepts: + +- search before creating; +- search active and archived applications; +- search by multiple identifying fields rather than relying only on the URL; +- an empty search result is not sufficient when the search was incomplete or weak; +- possible duplicates require user confirmation; +- `Create-Application` is allowed only after duplicate checks are complete. + +## Testing + +Add focused tests that inspect the resource text, prompt text, and tool annotation description. + +Tests must assert the presence of language equivalent to: + +- `must search before creating`; +- `active and archived`; +- `possible duplicate`; +- `an empty search result is not sufficient`; +- `do not call Create-Application until` the duplicate check is complete. + +Tests should also verify that the instructions do not claim URL equality is the only duplicate criterion. + +## Non-goals + +- No database unique constraint. +- No duplicate-detection service. +- No URL-normalization implementation. +- No new MCP tool. +- No change to `ApplicationService.create`. +- No automatic blocking performed by the backend. +- No changes to application archive semantics. + +## Success Criteria + +The final MCP metadata and prompt must make the required order unambiguous: + +`extract identifiers -> search active applications -> search archived applications -> inspect likely matches -> resolve possible duplicates -> Create-Application only when safe` + +A client following the instructions must not be able to justify creation based on a single empty search when other identifying fields or archived records have not been checked. From fd58ce8ef343ad537021feec0ed8ab38e9585fd8 Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:14:14 -0300 Subject: [PATCH 2/8] docs: add strict MCP duplicate-check implementation plan --- .../2026-07-29-mcp-strict-duplicate-check.md | 352 ++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-29-mcp-strict-duplicate-check.md diff --git a/docs/superpowers/plans/2026-07-29-mcp-strict-duplicate-check.md b/docs/superpowers/plans/2026-07-29-mcp-strict-duplicate-check.md new file mode 100644 index 0000000..fd29975 --- /dev/null +++ b/docs/superpowers/plans/2026-07-29-mcp-strict-duplicate-check.md @@ -0,0 +1,352 @@ +# MCP Strict Duplicate Check Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make MCP clients search active and archived applications for confirmed or possible duplicates before every `Create-Application` call. + +**Architecture:** This is an instruction-only change. Strengthen the same mandatory duplicate-check workflow in the application-creation resource, the `intake_vacancy` prompt, and the `Create-Application` tool description, then lock the wording with focused reflection/text tests. + +**Tech Stack:** Java 21, Spring Boot 3.5, Spring AI Community MCP annotations, JUnit 5, AssertJ, Maven. + +## Global Constraints + +- Do not add backend duplicate enforcement. +- Do not add database constraints or migrations. +- Do not normalize URLs. +- Do not add MCP tools or service methods. +- Do not change `ApplicationService.create`. +- Duplicate checks must include active and archived applications. +- Duplicate checks must use all available identifiers: URL, vacancy title, organization, and recruiter. +- A single empty or weak search is not sufficient evidence that no duplicate exists. +- Confirmed duplicates reuse the existing record. +- Possible duplicates require user confirmation before creation. + +--- + +### Task 1: Define strict duplicate-check expectations in tests + +**Files:** +- Modify: `src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java` +- Modify: `src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java` +- Modify: `src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java` + +**Interfaces:** +- Consumes: `String McpApplicationCreationRulesResource.applicationCreationRules(McpSyncServerExchange exchange)` +- Consumes: `GetPromptResult McpPromptsConfig.intakeVacancyPrompt(String vacancyContent)` +- Consumes: `@McpTool.description()` on `McpApplicationTools.createApplication(...)` +- Produces: executable wording requirements for the three MCP instruction surfaces + +- [ ] **Step 1: Replace the resource assertion with strict multi-field duplicate-check expectations** + +Update `applicationCreationRules_mandatesRegistrationForEveryApplicationRelatedAction` so the assertion is: + +```java +assertThat(text) + .containsIgnoringCase("must search before creating") + .containsIgnoringCase("active and archived") + .containsIgnoringCase("vacancy URL") + .containsIgnoringCase("vacancy title") + .containsIgnoringCase("organization") + .containsIgnoringCase("recruiter") + .containsIgnoringCase("possible duplicate") + .containsIgnoringCase("an empty search result is not sufficient") + .containsIgnoringCase("do not call Create-Application until"); +``` + +Also assert the old URL-only rule is absent: + +```java +assertThat(text) + .doesNotContain("duplicate only when the URL is identical") + .doesNotContain("Reposts or new vacancy URLs must be registered as separate applications"); +``` + +- [ ] **Step 2: Replace the prompt test with strict active-and-archived workflow expectations** + +Rename the test to `intakeVacancyPrompt_enforcesStrictDuplicateCheckBeforeCreation` and assert: + +```java +assertThat(text) + .containsIgnoringCase("must search before creating") + .containsIgnoringCase("active applications") + .containsIgnoringCase("archived applications") + .containsIgnoringCase("vacancy title") + .containsIgnoringCase("organization") + .containsIgnoringCase("recruiter") + .containsIgnoringCase("possible duplicate") + .containsIgnoringCase("an empty search result is not sufficient") + .containsIgnoringCase("do not call Create-Application until"); +``` + +Also assert: + +```java +assertThat(text) + .doesNotContain("Search by exact vacancy URL → Create-Application when absent") + .doesNotContain("Similar vacancy names, recruiters, organizations, salaries, or technology stacks are NOT sufficient"); +``` + +- [ ] **Step 3: Strengthen the `Create-Application` description test** + +Replace the final URL-only assertion in `createApplicationTool_descriptionMandatesAutomaticRegistration` with: + +```java +assertThat(description) + .contains("must be called automatically") + .contains("applying") + .contains("generating a resume") + .contains("contacting a recruiter") + .contains("evaluating job fit") + .contains("preparing application materials") + .containsIgnoringCase("must search before creating") + .containsIgnoringCase("active and archived") + .containsIgnoringCase("possible duplicate") + .containsIgnoringCase("an empty search result is not sufficient") + .containsIgnoringCase("do not call Create-Application until"); +``` + +- [ ] **Step 4: Run focused tests and verify they fail against the current wording** + +Run: + +```bash +mvn -Dtest=McpApplicationCreationRulesResourceTest,McpPromptsConfigTest,McpApplicationToolsTest test +``` + +Expected: the three new duplicate-check assertions fail because current instructions prioritize exact URL matching and do not require archived searches or possible-duplicate confirmation. + +- [ ] **Step 5: Commit the failing tests** + +```bash +git add src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java \ + src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java \ + src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java +git commit -m "test(mcp): require strict duplicate checks before creation" +``` + +--- + +### Task 2: Strengthen the application-creation resource + +**Files:** +- Modify: `src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java` +- Test: `src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java` + +**Interfaces:** +- Produces: authoritative Markdown duplicate-check workflow from `applicationCreationRules(...)` +- Consumed by: MCP clients reading `resource://job-apply-tracker/application-creation-rules` + +- [ ] **Step 1: Replace the URL-only duplicate section** + +Replace the current duplicate paragraphs with: + +```text +MANDATORY DUPLICATE CHECK: you MUST search before creating any application. +Extract every available identifier: vacancy URL, vacancy title, organization, and recruiter. +Search active applications separately using the available title, organization, and recruiter terms. +Then search archived applications as well; archived records MUST participate in duplicate detection. +Inspect all returned records and compare every available identifier, including vacancyLink when present. + +An empty search result is not sufficient when only one weak, incomplete, or unrelated query was executed. +Do not rely only on vacancyLink because it is optional. Run multiple searches using the available identifiers. + +Treat a matching record as a confirmed duplicate when the URL matches or when title, organization, and recruiter identify the same vacancy with high confidence. Reuse the existing record and do not call Create-Application. +Treat a matching record as a possible duplicate when title and organization match but other information is missing, different, or inconclusive. Show the matching records and ask the user to confirm whether the vacancy is distinct. + +Do not call Create-Application until searches of active and archived applications are complete and no confirmed or possible duplicate remains unresolved. When the matching record is archived, identify it as archived and prefer Restore-Application when the user wants to continue the same application record. +``` + +Keep the existing registration-before-resume rule and field defaults unchanged. + +- [ ] **Step 2: Update the resource freshness date** + +Set: + +```java +private static final String LAST_MODIFIED = "2026-07-29"; +``` + +- [ ] **Step 3: Run the resource test** + +```bash +mvn -Dtest=McpApplicationCreationRulesResourceTest test +``` + +Expected: PASS. + +- [ ] **Step 4: Commit the resource change** + +```bash +git add src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java +git commit -m "docs(mcp): require strict duplicate checks before creation" +``` + +--- + +### Task 3: Strengthen the vacancy-intake prompt + +**Files:** +- Modify: `src/main/java/com/jobtracker/mcp/McpPromptsConfig.java` +- Test: `src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java` + +**Interfaces:** +- Produces: mandatory execution order inside `intakeVacancyPrompt(String vacancyContent)` +- Consumed by: MCP clients invoking the `intake_vacancy` prompt + +- [ ] **Step 1: Replace the registration workflow summary** + +Use this summary: + +```text +MANDATORY REGISTRATION WORKFLOW: +Extract identifiers → Search active applications → Search archived applications → Inspect confirmed and possible duplicates → Resolve possible duplicates with the user → Create-Application only when safe → Continue requested workflow +``` + +- [ ] **Step 2: Replace Step 2 with an explicit duplicate-check algorithm** + +Use wording equivalent to: + +```text +STEP 2 - Check for confirmed and possible duplicates (mandatory, do not skip) +Call List-Statuses to obtain valid status values. +You MUST search before creating. Extract and use every available identifier: vacancy URL, vacancy title, organization, and recruiter. +Search active applications separately using available title, organization, and recruiter terms. Then repeat the search for archived applications by setting archived=true in List-Applications where applicable. +Inspect the returned records and compare all available identifiers, including vacancyLink when present. Do not rely only on vacancyLink because it is optional. +An empty search result is not sufficient when the search was weak, incomplete, or used only one identifier. +A confirmed duplicate is an existing record with the same URL, or a record whose title, organization, and recruiter identify the same vacancy with high confidence. Reuse it and do not call Create-Application. +A possible duplicate is a record whose title and organization match while other information is missing, different, or inconclusive. Show the matching records and ask the user to confirm whether the vacancy is distinct. +Do not call Create-Application until active and archived searches are complete and no confirmed or possible duplicate remains unresolved. +If the matching record is archived, report that state and prefer Restore-Application when the user intends to continue the same record. +``` + +Leave subsequent CV/base-information steps unchanged. + +- [ ] **Step 3: Run the prompt test** + +```bash +mvn -Dtest=McpPromptsConfigTest test +``` + +Expected: PASS. + +- [ ] **Step 4: Commit the prompt change** + +```bash +git add src/main/java/com/jobtracker/mcp/McpPromptsConfig.java +git commit -m "docs(mcp): make vacancy duplicate checks mandatory" +``` + +--- + +### Task 4: Strengthen the `Create-Application` tool description + +**Files:** +- Modify: `src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java` +- Test: `src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java` + +**Interfaces:** +- Produces: safety-critical `@McpTool.description()` metadata for `Create-Application` +- Consumed by: MCP clients even when prompt/resource instructions are cached or omitted + +- [ ] **Step 1: Replace the description with compact mandatory guardrails** + +Use: + +```java +description = "Create a new job application record. IMPORTANT: Call List-Statuses first to get valid status values. Never use a status value from memory. " + + "This tool must be called automatically whenever the user provides a vacancy in the context of applying, generating a resume, contacting a recruiter, evaluating job fit, or preparing application materials. " + + "Before calling it, you MUST search before creating: search active and archived applications using every available identifier, including vacancy URL, vacancy title, organization, and recruiter. " + + "Do not rely only on the URL because vacancyLink is optional. An empty search result is not sufficient when the search was incomplete or weak. " + + "Reuse confirmed duplicates. For a possible duplicate, show the matching records and obtain user confirmation that the vacancy is distinct. " + + "Do not call Create-Application until all duplicate checks are complete and no confirmed or possible duplicate remains unresolved." +``` + +Do not change method parameters, annotations, or delegation logic. + +- [ ] **Step 2: Run the tool test** + +```bash +mvn -Dtest=McpApplicationToolsTest test +``` + +Expected: PASS. + +- [ ] **Step 3: Commit the tool metadata change** + +```bash +git add src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java +git commit -m "fix(mcp): guard application creation against duplicates" +``` + +--- + +### Task 5: Verify and open the pull request + +**Files:** +- Review: all files changed on `fix/mcp-strict-duplicate-check` + +**Interfaces:** +- Consumes: completed Tasks 1-4 +- Produces: reviewed pull request targeting `main` + +- [ ] **Step 1: Run the focused MCP tests** + +```bash +mvn -Dtest=McpApplicationCreationRulesResourceTest,McpPromptsConfigTest,McpApplicationToolsTest test +``` + +Expected: BUILD SUCCESS. + +- [ ] **Step 2: Run the complete test suite** + +```bash +mvn test +``` + +Expected: BUILD SUCCESS. + +- [ ] **Step 3: Inspect the final diff** + +Confirm the branch changes only: + +```text +docs/superpowers/specs/2026-07-29-mcp-strict-duplicate-check-design.md +docs/superpowers/plans/2026-07-29-mcp-strict-duplicate-check.md +src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java +src/main/java/com/jobtracker/mcp/McpPromptsConfig.java +src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java +src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java +src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java +src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java +``` + +Reject any database migration, repository/service duplicate logic, URL-normalization utility, or new MCP tool. + +- [ ] **Step 4: Open the pull request** + +Title: + +```text +fix(mcp): require duplicate checks before application creation +``` + +Body: + +```markdown +## Summary + +- requires MCP clients to search active and archived applications before `Create-Application` +- uses URL, vacancy title, organization, and recruiter instead of relying only on an optional link +- distinguishes confirmed duplicates from possible duplicates that require user confirmation +- repeats the guardrails in the creation resource, intake prompt, and tool description +- adds focused tests for the mandatory wording + +## Scope + +Instruction and metadata changes only. No database constraint, URL normalization, new MCP tool, or backend duplicate-enforcement logic. + +## Tests + +- `mvn -Dtest=McpApplicationCreationRulesResourceTest,McpPromptsConfigTest,McpApplicationToolsTest test` +- `mvn test` +``` From 94390d13669a6adfbd71a7e2238779c6242ea5ba Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:14:44 -0300 Subject: [PATCH 3/8] test(mcp): require strict duplicate checks in creation rules --- ...pApplicationCreationRulesResourceTest.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java b/src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java index 70bf0fd..43773fe 100644 --- a/src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java +++ b/src/test/java/com/jobtracker/unit/mcp/McpApplicationCreationRulesResourceTest.java @@ -14,11 +14,18 @@ void applicationCreationRules_mandatesRegistrationForEveryApplicationRelatedActi String text = resource.applicationCreationRules(null); assertThat(text) - .contains("mandatory for all application-related actions") - .contains("exact vacancy URL") - .contains("are not sufficient evidence of a duplicate") - .contains("Reposts or new vacancy URLs must be registered as separate applications") - .contains("before resume or outreach generation") - .contains("confirm"); + .containsIgnoringCase("must search before creating") + .containsIgnoringCase("active and archived") + .containsIgnoringCase("vacancy URL") + .containsIgnoringCase("vacancy title") + .containsIgnoringCase("organization") + .containsIgnoringCase("recruiter") + .containsIgnoringCase("possible duplicate") + .containsIgnoringCase("an empty search result is not sufficient") + .containsIgnoringCase("do not call Create-Application until"); + + assertThat(text) + .doesNotContain("duplicate only when the URL is identical") + .doesNotContain("Reposts or new vacancy URLs must be registered as separate applications"); } } From 9eeb732896761b6e4627888a4b0f790537b5e166 Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:14:59 -0300 Subject: [PATCH 4/8] test(mcp): require strict duplicate checks in intake prompt --- .../unit/mcp/McpPromptsConfigTest.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java b/src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java index d8f38ad..68195c5 100644 --- a/src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java +++ b/src/test/java/com/jobtracker/unit/mcp/McpPromptsConfigTest.java @@ -17,17 +17,25 @@ class McpPromptsConfigTest { private final McpPromptsConfig prompts = new McpPromptsConfig(); @Test - void intakeVacancyPrompt_enforcesMandatoryUrlBasedRegistrationWorkflow() { + void intakeVacancyPrompt_enforcesStrictDuplicateCheckBeforeCreation() { GetPromptResult result = prompts.intakeVacancyPrompt("Senior Java Backend - Julia Argueiro - https://example.com/vaga/123"); String text = ((TextContent) result.messages().get(0).content()).text(); assertThat(text) - .contains("List-Statuses → Search by exact vacancy URL → Create-Application when absent → Continue requested workflow") - .contains("Call List-Statuses") - .contains("exact vacancy URL") - .contains("Similar vacancy names, recruiters, organizations, salaries, or technology stacks are NOT sufficient") - .contains("resume, message, evaluation, or outreach"); + .containsIgnoringCase("must search before creating") + .containsIgnoringCase("active applications") + .containsIgnoringCase("archived applications") + .containsIgnoringCase("vacancy title") + .containsIgnoringCase("organization") + .containsIgnoringCase("recruiter") + .containsIgnoringCase("possible duplicate") + .containsIgnoringCase("an empty search result is not sufficient") + .containsIgnoringCase("do not call Create-Application until"); + + assertThat(text) + .doesNotContain("Search by exact vacancy URL → Create-Application when absent") + .doesNotContain("Similar vacancy names, recruiters, organizations, salaries, or technology stacks are NOT sufficient"); } @Test From dc9ec821385ccf9d49a551a21be850aeb4dfeb7b Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:15:51 -0300 Subject: [PATCH 5/8] test(mcp): require strict duplicate checks in create tool --- .../com/jobtracker/unit/mcp/McpApplicationToolsTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java b/src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java index 25a840a..2208b52 100644 --- a/src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java +++ b/src/test/java/com/jobtracker/unit/mcp/McpApplicationToolsTest.java @@ -122,7 +122,11 @@ void createApplicationTool_descriptionMandatesAutomaticRegistration() { .contains("contacting a recruiter") .contains("evaluating job fit") .contains("preparing application materials") - .contains("unless the exact vacancy URL is already registered"); + .containsIgnoringCase("must search before creating") + .containsIgnoringCase("active and archived") + .containsIgnoringCase("possible duplicate") + .containsIgnoringCase("an empty search result is not sufficient") + .containsIgnoringCase("do not call Create-Application until"); } @Test From 6076e436bb6a033fa0f4d587829155cd785d4d6e Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:19:05 -0300 Subject: [PATCH 6/8] docs(mcp): require strict duplicate checks before creation --- .../McpApplicationCreationRulesResource.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java b/src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java index 1a65d79..72936aa 100644 --- a/src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java +++ b/src/main/java/com/jobtracker/mcp/resources/McpApplicationCreationRulesResource.java @@ -11,7 +11,7 @@ @Service public class McpApplicationCreationRulesResource { - private static final String LAST_MODIFIED = "2026-07-15"; + private static final String LAST_MODIFIED = "2026-07-29"; @McpResource( uri = McpResourcesConfig.URI_APPLICATION_CREATION_RULES, @@ -34,19 +34,32 @@ public String applicationCreationRules(McpSyncServerExchange exchange) { Whenever the user provides a vacancy and shows application intent, register it before performing the requested action. Never silently skip vacancy registration. - Duplicate detection must prioritize the exact vacancy URL: search existing applications by - the exact vacancy URL first. Similar vacancy names, recruiters, organizations, salaries, or - technology stacks are not sufficient evidence of a duplicate — treat the vacancy as a - duplicate only when the URL is identical or the user explicitly confirms it is the same - vacancy. Reposts or new vacancy URLs must be registered as separate applications, even when - title, recruiter, salary, and stack are identical to an existing application. + MANDATORY DUPLICATE CHECK: you MUST search before creating any application. + Extract every available identifier: vacancy URL, vacancy title, organization, and recruiter. + Search active applications separately using the available title, organization, and recruiter terms. + Then search archived applications as well; archived records MUST participate in duplicate detection. + Inspect all returned records and compare every available identifier, including vacancyLink when present. + + An empty search result is not sufficient when only one weak, incomplete, or unrelated query was executed. + Do not rely only on vacancyLink because it is optional. Run multiple searches using the available identifiers. + + Treat a matching record as a confirmed duplicate when the URL matches or when title, organization, + and recruiter identify the same vacancy with high confidence. Reuse the existing record and do not call + Create-Application. + Treat a matching record as a possible duplicate when title and organization match but other information + is missing, different, or inconclusive. Show the matching records and ask the user to confirm whether the + vacancy is distinct. + + Do not call Create-Application until searches of active and archived applications are complete and no + confirmed or possible duplicate remains unresolved. When the matching record is archived, identify it as + archived and prefer Restore-Application when the user wants to continue the same application record. Registration must happen before resume or outreach generation: call Create-Application (after List-Statuses) first, then perform the requested resume, message, evaluation, or outreach action only once the application exists. - Always confirm to the user, explicitly, that the vacancy was registered (or that it was - already registered under the same URL) before or alongside delivering the requested output. + Always confirm to the user, explicitly, that the vacancy was registered (or that an existing record was + reused) before or alongside delivering the requested output. Apply these defaults on every Create-Application or Update-Application call: From 9efc463165bdfdc71b4ca62d6eb7197321508c07 Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:20:10 -0300 Subject: [PATCH 7/8] docs(mcp): make vacancy duplicate checks mandatory --- .../com/jobtracker/mcp/McpPromptsConfig.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/jobtracker/mcp/McpPromptsConfig.java b/src/main/java/com/jobtracker/mcp/McpPromptsConfig.java index 9c6edb9..6f13fd5 100644 --- a/src/main/java/com/jobtracker/mcp/McpPromptsConfig.java +++ b/src/main/java/com/jobtracker/mcp/McpPromptsConfig.java @@ -48,7 +48,9 @@ Stop only for genuinely necessary questions (see Step 7). MANDATORY REGISTRATION WORKFLOW (applies to this and every other application-related request — generating/adapting a resume, drafting an email/WhatsApp/LinkedIn message, contacting a recruiter, evaluating job fit, or preparing application materials): - List-Statuses → Search by exact vacancy URL → Create-Application when absent → Continue requested workflow + Extract identifiers → Search active applications → Search archived applications → Inspect confirmed + and possible duplicates → Resolve possible duplicates with the user → Create-Application only when safe + → Continue requested workflow Follow this order exactly: @@ -56,18 +58,23 @@ MANDATORY REGISTRATION WORKFLOW (applies to this and every other application-rel Extract: vacancyName (title), organization (company), vacancyLink (URL if present), required stack, seniority, vacancy language, recruiter name/email if present. - STEP 2 - Check for existing application (mandatory, do not skip) - Call List-Statuses to get the valid status values. Then call List-Applications/Search-Applications - and search for an existing application using the exact vacancy URL (vacancyLink). - Similar vacancy names, recruiters, organizations, salaries, or technology stacks are NOT sufficient - evidence of a duplicate. Treat the vacancy as a duplicate only when the vacancyLink is identical to - an existing application, or when I explicitly confirm it is the same vacancy. - If an exact-URL duplicate exists, return a message indicating that an application already exists - and provide its UUID. Do not proceed with the workflow if a duplicate application is found. - If the exact URL is not registered (including reposts of an otherwise similar vacancy under a new - URL), you MUST call Create-Application before performing the requested resume, message, - evaluation, or outreach action — never silently skip vacancy registration. This applies whether - the requested action is generating a resume, message, evaluation, or outreach. + STEP 2 - Check for confirmed and possible duplicates (mandatory, do not skip) + Call List-Statuses to obtain valid status values. + You MUST search before creating. Extract and use every available identifier: vacancy URL, vacancy title, + organization, and recruiter. + Search active applications separately using available title, organization, and recruiter terms. Then + repeat the search for archived applications by setting archived=true in List-Applications where applicable. + Inspect the returned records and compare all available identifiers, including vacancyLink when present. + Do not rely only on vacancyLink because it is optional. + An empty search result is not sufficient when the search was weak, incomplete, or used only one identifier. + A confirmed duplicate is an existing record with the same URL, or a record whose title, organization, + and recruiter identify the same vacancy with high confidence. Reuse it and do not call Create-Application. + A possible duplicate is a record whose title and organization match while other information is missing, + different, or inconclusive. Show the matching records and ask the user to confirm whether the vacancy is distinct. + Do not call Create-Application until active and archived searches are complete and no confirmed or possible + duplicate remains unresolved. + If the matching record is archived, report that state and prefer Restore-Application when the user intends + to continue the same record. STEP 3 - Read my BASE INFORMATION (TOP PRIORITY, mandatory before generating any content) - Call List-Base-Information and read EVERY document via Get-Base-Information-Content @@ -101,7 +108,8 @@ and use it to supplement (never override) my base information. ATS-friendly, without inventing anything. Follow resume-workflow-rules for completeness and key formatting. STEP 9 - Create the application - Follow application-creation-rules. Call Create-Application with the extracted data. + Follow application-creation-rules. Call Create-Application with the extracted data only after completing + STEP 2 and resolving every confirmed or possible duplicate. Do NOT fill nextStepDateTime. Note: ATS-focused summary (stack, seniority, fit, gaps). From 2b28639f41adb16b71f41816fc7ff6121a0c1304 Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Wed, 29 Jul 2026 15:21:31 -0300 Subject: [PATCH 8/8] fix(mcp): guard application creation against duplicates --- .../java/com/jobtracker/mcp/tools/McpApplicationTools.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java b/src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java index 52e30b7..8f66cc8 100644 --- a/src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java +++ b/src/main/java/com/jobtracker/mcp/tools/McpApplicationTools.java @@ -136,8 +136,11 @@ public List getOverdueApplications(McpSyncRequestContext ct name = "Create-Application", title = "Create Application", description = "Create a new job application record. IMPORTANT: Call List-Statuses first to get valid status values. Never use a status value from memory. " - + "This tool must be called automatically whenever the user provides a vacancy in the context of applying, generating a resume, " - + "contacting a recruiter, evaluating job fit, or preparing application materials, unless the exact vacancy URL is already registered.", + + "This tool must be called automatically whenever the user provides a vacancy in the context of applying, generating a resume, contacting a recruiter, evaluating job fit, or preparing application materials. " + + "Before calling it, you MUST search before creating: search active and archived applications using every available identifier, including vacancy URL, vacancy title, organization, and recruiter. " + + "Do not rely only on the URL because vacancyLink is optional. An empty search result is not sufficient when the search was incomplete or weak. " + + "Reuse confirmed duplicates. For a possible duplicate, show the matching records and obtain user confirmation that the vacancy is distinct. " + + "Do not call Create-Application until all duplicate checks are complete and no confirmed or possible duplicate remains unresolved.", annotations = @McpAnnotations( title = "Create Application", readOnlyHint = false,