Reading a listing: no tool returns the job advert, and the fence tests use a response shape the API never sends #10
Labels
No labels
bug
documentation
enhancement
security
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-mcp#10
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo-mcp at
0b29e70b9, comparing every tool's address with the API description intests/openapi.jsonand with the test fixtures.Related to #3.
The job advert is the text the README and the server's docstring present as the main thing an agent reads and the main thing fenced, but no tool or resource can fetch it. In the API,
descriptionexists only onGET /listings/{pk}(ListingDetailOut), and nothing inserver.pycalls that address. The tests hide the gap because their fixtures put adescriptionwhere the API has none.src/postulo_mcp/server.py:164-166(get_application),src/postulo_mcp/server.py:172-181(search_postingsreads/listings, which answersListingOutwithoutdescription),src/postulo_mcp/server.py:346-355(prepare_for_interview),README.md:86-106;tests/test_server.py:26-41(APPLICATIONwith apostingkey andposting.description),tests/test_server.py:100-101(/listingsanswering adescription),tests/test_server.py:250-260and:372-379;tests/fake_api.py:104-109(Operation.answer).get_applicationreturnslisting: {id, title, company}(ListingRef);search_postingsreturnsListingOut. Neither carries the advert, and there is noget_listingtool orpostulo://listings/{id}resource. A person who runsprepare_for_interview, which asks the agent to say "what they asked for", gets an agent that has no way to read the advert, and the README's description of that prompt ("gathers the posting") is not true.postingobject;ApplicationDetailOuthaslistingand noposting. The/listingsfixture addsdescriptiontoListingOut, which has none.test_somebody_elses_words_reach_the_model_inside_a_fenceandtest_resources_return_readable_text_not_jsontherefore check the fence on a field in a place the API never puts it.Operation.answerwraps whatever the test wrote in{items, count}without checking it against the response schema, and nothing checks request bodies either.Operation.checkcovers query parameters only, so a misspelt write field (application_idonPOST /reminders) or a response field the API dropped would still pass.tests/fake_api.py:25), and the core'sApplicationDetailOuthas since gainedoffersandListingDetailOutgainedevents(coreapi/schemas.py:200-204and:483-488).README.md:159-162and the stale snapshot together mean a change in the API's shape reaches users before it reaches CI.get_listing(listing_id)read tool and apostulo://listings/{listing_id}resource overGET /listings/{pk}, fenced as usual (descriptionis already inUNTRUSTED_FIELDS), and name it inprepare_for_interviewand the README. Rewrite the fixtures in the real shapes (listing, notposting; nodescriptiononListingOut) and move the advert fence test ontoget_listing. MakeOperation.answervalidate the fixture against the operation's success schema and the client's JSON body against its request schema (a small recursive check overproperties,requiredand$ref, orjsonschema), so a fixture or payload in the wrong shape fails. Refreshtests/openapi.jsonfrom the current core.Done when
tests/test_server.pycallsget_listingand finds the advert inside a fence.tests/test_server.pyshows that a fixture carrying a field the response schema does not have, and a write body carrying a field the request schema does not have, both fail intests/fake_api.py.