By Loomwise
Language models don't know what SAM.gov posted this morning. Ask one for "open small-business set-asides for janitorial work in Virginia" and you'll get a confident guess or a request to go look yourself. The Model Context Protocol (MCP) fixes this by giving the agent a tool it can call for live data.
This tutorial connects an MCP client to the Apify MCP server, loads a single tool that reads SAM.gov's public daily opportunities extract, and covers prompts, cost limits and scheduling. The setup follows Apify's MCP documentation as of October 2026 (docs.apify.com/mcp).
What you need
- An MCP client that supports remote servers, such as an AI coding assistant, an IDE with MCP support, or a desktop chat app. Apify keeps client-specific instructions in its docs and has a configurator at
https://mcp.apify.com/. - An Apify account, so the agent can run tools. Discovery works without one (see Step 4).
Step 1: Connect with only the tools you need
The hosted server lives at https://mcp.apify.com and uses Streamable HTTP. The recommended sign-in is OAuth: on first connect your browser opens, you sign in to Apify and approve access, and no token is stored in the client config.
Load only what the agent needs instead of the default tool set. Apify's docs recommend listing tools explicitly in production, so behavior doesn't change when defaults do.
{
"mcpServers": {
"apify": {
"type": "http",
"url": "https://mcp.apify.com/?tools=fetch-actor-details,loomwise/federal-opportunities"
}
}
}
The tools query parameter does the work here:
-
loomwise/federal-opportunitiesexposes that one Actor as a callable tool. -
fetch-actor-detailslets the agent read the tool's input schema, output schema, README and pricing before it calls the tool. - When an Actor tool is loaded, the server automatically adds
get-actor-run,get-dataset-items,get-key-value-store-recordandabort-actor-run, so the agent can read results and stop a runaway run.
Other ways to connect (all from Apify's docs):
-
Bearer token instead of OAuth: add
"headers": { "Authorization": "Bearer <APIFY_TOKEN>" }, using a token from Settings → API & Integrations in Apify Console. Treat it like a password. -
Apify CLI:
apify mcp install <client>(e.g.apify mcp install cursor) writes the config for you. -
Desktop chat apps that don't accept a remote URL in their config file: add
https://mcp.apify.comas a custom connector in the app instead. -
Local stdio for clients without remote support:
npx -y @apify/actors-mcp-serverwithAPIFY_TOKENin the environment. It needs Node.js 18+. Note that inferred output schemas are only available on the hosted server.
Restart the client and confirm the tools appear.
Step 2: Ask a real question
Try a prompt that maps cleanly onto the tool's filters:
Find open SAM.gov solicitations posted in the last 7 days for NAICS 238220 (plumbing/HVAC contractors) with a Service-Disabled Veteran-Owned Small Business set-aside, performed in Texas or Oklahoma. Keep it to 50 results. Show title, agency, response deadline (with timezone) and the SAM.gov link, sorted by deadline.
A well-behaved agent will:
- Call
fetch-actor-detailsand read the input schema. - Call the Actor tool with an input like this:
{
"naicsCodes": ["238220"],
"setAsideTypes": ["SDVOSBC"],
"placeOfPerformanceStates": ["TX", "OK"],
"noticeTypes": ["Solicitation", "Combined Synopsis/Solicitation"],
"postedWithinDays": 7,
"excludeExpired": true,
"maxResults": 50
}
- Read the rows with
get-dataset-itemsand give you a table.
Each row has the same fields you'd get in a spreadsheet export: id, source, title, solicitationNumber, agency/subAgency/office, noticeType, naicsCode, classificationCode, setAsideCode/setAside, postedDate, responseDeadline (with timezone offset), archiveDate, popCity/popState/popZip/popCountry, link, and a descriptionSnippet with emails and phone numbers removed. Contact names aren't included. Grant-only fields (fundingInstrument, assistanceListings, awardCeiling, awardFloor) are null for contracts.
Prompts that work well
-
Give codes, not vibes. "NAICS 5415 prefix" beats "IT stuff".
naicsCodesaccepts prefixes, so5415matches every5415xxcode. If you don't know your codes, ask the agent to suggest some, then you confirm them. -
Use the set-aside codes from the schema:
SBA(total small business),8A,HZC(HUBZone),SDVOSBC,WOSB,EDWOSB,VSA, orNONEfor unrestricted. The agent can read the full list viafetch-actor-details. -
Ask for early signals: add
"Sources Sought"and"Presolicitation"to the notice types. -
De-duplicate amendments: ask for
dedupeBySolicitation: trueif you only want the latest notice per solicitation number. -
Include grants when relevant:
includeGrants: trueadds Grants.gov opportunities in the same schema. NAICS, set-aside, state and notice-type filters don't apply to grants, so tell the agent to filter grants by keyword or agency. -
Watch the state filter: with
placeOfPerformanceStatesset, notices without a listed place of performance are excluded. Tell the agent if you want those too.
Step 3: Put guardrails on cost
This Actor is pay-per-event: $2 per 1,000 results plus Apify's small Actor-start fee. A 50-row answer costs about ten cents. To keep an agent from running up a bill:
-
Cap results in the input.
maxResultsstops the run at that many rows, and it's the most direct limit for this tool. -
Cap the run itself. If your agent uses the generic
call-actortool (in theactorscategory), passmaxTotalChargeUsd,maxItems,timeoutormemoryincallOptions, not in the Actor input. Apify's docs note that fields with those names inside the input don't cap the run. - Tell the agent the rule. Add to your system prompt: "Before any paid tool call, state the expected maximum cost and wait for confirmation if it's over $0.50." Apify's own agent quickstart says to state the cost before the first paid run.
Step 4: Discovery without an account (optional)
If you only want an agent to find the right Actor and explain its inputs, without running anything, connect with only the discovery tools:
https://mcp.apify.com?tools=search-actors,fetch-actor-details,search-apify-docs,fetch-apify-docs
According to Apify's docs, this works with no API token. Running Actors and reading storage always need authentication. search-actors ranking follows factors similar to Apify's Actor quality score, so a search like "SAM.gov contract opportunities" may return several options. Have the agent compare them with fetch-actor-details.
Step 5: Make it recurring
A chat answer is a one-off. To get the same answer every morning, either:
- Create a task and schedule in Apify Console (e.g. cron
0 8 * * *in your timezone, withpostedWithinDays: 2and de-duplication oniddownstream). SAM.gov regenerates the extract once a day, overnight US time, so morning runs pick up the previous day's notices. Or: - Let the agent do it. Add the
tasksandschedulestool categories to the URL (?tools=fetch-actor-details,loomwise/federal-opportunities,tasks,schedules) and ask it to "save this search as a task and schedule it daily at 8:00 America/New_York." Those tools can create, change and delete schedules, so review what the agent creates.
Production notes
- Pin your tool list in the URL, as above, so default changes don't surprise you.
-
Rate limit: the hosted server allows up to 30 requests per second per user and returns
429above that. Add retries if you fan out. -
Telemetry: the hosted server collects anonymous tool-usage telemetry by default. Append
telemetry-enabled=falseto the URL to opt out. -
Freshness: each run reads the latest daily extract. The run's
RUN_SUMMARYrecord shows the source file's timestamp, so ask the agent to report it if freshness matters. -
Keep a human in the loop for bid decisions. The agent summarizes a ~300-character snippet. Always open
linkand read the full notice and attachments before deciding to bid.
Disclosure: we built the tool used here, the SAM.gov Contract Opportunities Scraper & Feed on Apify ($2 per 1,000 results). The MCP setup above works the same way for any Actor in Apify Store.
Top comments (0)