samgov-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@samgov-mcpFind recent federal contract opportunities for IT services in Virginia"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
samgov-mcp
An MCP server over the SAM.gov Contract Opportunities API, built for agents that have to read United States federal solicitations and then be believed about what they read.
Everything this server returns carries the exact URL it came from. Anything SAM.gov did not send
comes back as null with its name listed, never as a zero, an empty string, or a plausible guess.
npm install
npm run smoke # 20 checks, no API key, no network
npm run dev # starts the server on stdioRun it with no key and it serves recorded fixtures, so you can inspect every tool contract before signing up for anything.
Why this exists
I build AI systems that live inside a company's operation and answer from its own data. The failure that costs you the room is never a model that underperforms. It is a confident number nobody can trace back to anything.
An MCP server is where that gets decided. By the time a figure reaches the model it is just a token, and no prompt reliably repairs a tool that quietly returned the wrong record. So the discipline goes in the tool layer, where it can be tested.
Related MCP server: fpds-mcp
The envelope
Every result has the same shape:
{
"source": {
"url": "https://api.sam.gov/opportunities/v2/search?postedFrom=07%2F01%2F2026&...&api_key=REDACTED",
"retrieved_at": "2026-08-07T18:33:52.792Z",
"mode": "live"
},
"data": { "returned": 2, "total_records_upstream": 412, "notices": [ ... ] },
"missing": ["notices[1].response_deadline", "notices[1].set_aside_code"],
"abstained": { "reason": "..." }
}source is the request that produced the payload, with the API key stripped so a transcript
never leaks a credential.
missing names every field the upstream did not provide. SAM.gov signals "not provided" three
different ways, null, "", and an object whose inner name is "", and a model cannot tell those
apart from real values. They all become null here, and they all get named.
abstained appears when the server declines. "I could not find this" and "this does not exist"
are different answers, and collapsing them into an empty list is how an agent ends up telling
somebody a solicitation was cancelled when it was simply posted outside the window it searched.
data.returned is counted off the array actually being returned. total_records_upstream is
reported beside it, separately, because they are different numbers and mixing them is how a report
claims a pipeline processed 412 documents when it read 10.
Tools
Tool | What it does |
| Search a posted-date window. Filters for NAICS, procurement type, set-aside, state, title |
| One notice by its exact solicitation number |
| The downloadable resource links for one notice |
| Download one attachment |
| Which mode it is in and where the data is coming from |
Two things I got wrong, written down
1 · A 404 from this gateway means the key is bad, not the path.
api.sam.gov answers 404 with an empty body for every path when the API key is missing or
unrecognised, including paths that exist. DEMO_KEY does not work, because SAM.gov runs its own
gateway rather than the shared api.data.gov one. I spent a while probing endpoint variants before
realising the endpoint was never the problem. The client now says so in the error text, because the
obvious reading of a 404 sends you somewhere useless.
2 · The first version of this server did the exact thing it was built to prevent.
get_solicitation originally ended with ?? rows[0], a harmless-looking fallback: if the exact
number is not found, use the first result. But SAM.gov's solnum filter is fuzzy and returns
neighbouring notices. So for any number that did not exist, the server would have confidently handed
back a different solicitation under the number that was asked for. Different deadline, different
set-aside, different scope.
The smoke test did not catch it, because I had asserted that the call returned something. It now asserts abstention, and the abstention message names the neighbours it refused to pass off:
No notice numbered exactly "DOES-NOT-EXIST-0000" was posted between 07/01/2026 and 07/31/2026.
The search returned 2 nearby notice(s) which are NOT this one: FIXTURE-70FA-26-R-0001, ...It is the same lesson as the first run of any eval suite. The first pass grades your harness, not the thing you pointed it at.
Use it
Live. Generate a free key at sam.gov under Account Details → Public API Key, then:
export SAM_API_KEY=your-key
npm run buildClaude Desktop or Claude Code, in claude_desktop_config.json or via claude mcp add:
{
"mcpServers": {
"samgov": {
"command": "node",
"args": ["/absolute/path/to/samgov-mcp/dist/index.js"],
"env": { "SAM_API_KEY": "your-key" }
}
}
}Fixtures. Omit SAM_API_KEY and it runs offline against fixtures/search.json, which is
synthetic and labelled as such inside the file. Every tool contract, the grounding envelope and both
abstention paths are exercised without a credential. SAM_MODE=live|fixtures overrides the default.
Environment
Variable | Default | Meaning |
| none | Free key from SAM.gov. Its presence is what selects live mode |
| auto |
|
|
| Upstream request timeout |
|
| Refuse attachments larger than this rather than filling a context window |
Notes on the upstream
postedFromandpostedToare mandatory,MM/dd/yyyy, at most one year apart. This server refuses other formats at the schema rather than reformatting them, so a wrong window fails loudly instead of silently returning the wrong year.SAM.gov puts a URL in the
descriptionfield, not prose. It is returned asdescription_link, so an agent does not quote a link as if it were the scope of work.Attachments in binary formats are reported with their content type and byte length rather than being decoded into noise. Extracting fields from fillable government forms is a separate step and is deliberately not guessed at here.
Status
Working: the five tools, the grounding envelope, both abstention paths, fixtures mode, 20 smoke checks over the real MCP stdio protocol driven by the reference client.
Next: requirement extraction from fillable PDFs into schema-validated JSON, where every extracted field carries the page and span it came from, plus an eval suite over that extraction using the harness in agent-evals.
MIT.
Available Tools
5 toolsfetch_attachmentDownload one attachmentA
Downloads a single attachment by its resource URL, as returned by list_attachments. Returns UTF-8 text when the document is text, and otherwise returns the content type and byte length without guessing at the contents. Binary formats such as PDF are reported, not silently decoded into noise.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | A resource link from list_attachments |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full burden. It discloses exact return behavior: returns UTF-8 text for text documents, and reports content type and byte length for binary formats instead of guessing or decoding. This is unusually transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two focused sentences, front-loaded with the main action. Every sentence adds value – the first states the operation and source, the second explains output handling. No filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description fully explains return behavior. The single parameter is well-defined in the schema, and the link to list_attachments provides necessary context for when this tool is used. Nothing important is missing for a simple fetch operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (the 'url' parameter is described as 'A resource link from list_attachments'). The description reinforces this but adds no new semantics beyond the schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states 'Downloads a single attachment by its resource URL' – a specific verb, resource, and source. This distinguishes it from siblings like list_attachments, which lists attachments, and get_solicitation, which fetches a different entity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'as returned by list_attachments' explicitly indicates the prerequisite workflow. It doesn't name when not to use alternatives, but the context is clear and there is no competing download tool among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_solicitationGet one SAM.gov solicitationA
Retrieves a single notice by its solicitation number, within a posted-date window. Abstains explicitly when the number is not found in that window, because 'not in this window' and 'does not exist' are different answers.
| Name | Required | Description | Default |
|---|---|---|---|
| postedTo | Yes | End of the window to look in, MM/dd/yyyy | |
| postedFrom | Yes | Start of the window to look in, MM/dd/yyyy | |
| solicitationNumber | Yes | The solicitation number as printed on the notice |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It explicitly discloses the abstention behavior when the number is not found within the window, and explains the rationale (distinguishing 'not in window' from 'does not exist'). This is valuable context beyond the schema, though it does not mention error formats or return details.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. The first sentence states the core action; the second adds an important behavioral caveat. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has three required parameters with full schema coverage and no output schema. The description covers the main purpose and a critical edge case (abstention). It implies the return value ('retrieves a single notice'), which is acceptable. Missing explicit return format is a minor gap, but the description is sufficiently complete for an agent to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameter descriptions already document all three fields. The description repeats the concept of a 'posted-date window' but adds no new syntax, defaults, or relationships beyond what the schema provides. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Retrieves' and the resource 'a single notice' by its solicitation number within a posted-date window. This distinguishes it from siblings like search_solicitations (which presumably does broad searches) and fetch_attachment (which handles attachments).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes the usage context clear: this is for retrieving a single notice when you have the exact solicitation number and a specific date window. It does not explicitly list exclusions or alternatives, but the context is unambiguous enough that an agent can infer when to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_attachmentsList attachments for a solicitationA
Returns the downloadable attachment URLs for one notice. An empty list means the notice carries no attachments, which is a real and common state and is reported as zero rather than as an error.
| Name | Required | Description | Default |
|---|---|---|---|
| postedTo | Yes | End of the window to look in, MM/dd/yyyy | |
| postedFrom | Yes | Start of the window to look in, MM/dd/yyyy | |
| solicitationNumber | Yes | The solicitation number |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It proactively explains a common edge case: an empty list is a real state and returns zero, not an error. This adds genuine transparency beyond a simple 'list attachments' statement, though it does not cover authentication or other behaviors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no wasted words. The first sentence states the core function, and the second clarifies an important behavioral nuance. It is front-loaded with the action and resource.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple listing tool with fully documented schema parameters and no output schema, the description adequately covers what the tool does and the notable empty-list behavior. It could mention usage relative to fetch_attachment, but that gap is more about usage guidelines. Overall it is sufficiently complete for the tool's complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema covers 100% of parameters with descriptions, so the baseline is 3. The description does not add any additional parameter-level meaning; it only references the notion of 'one notice', which maps to solicitationNumber but adds no new detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns 'downloadable attachment URLs for one notice', specifying both the action (returns) and the resource (attachment URLs for a specific notice). This distinguishes it from siblings like fetch_attachment (which likely downloads a single attachment) and search_solicitations (which searches rather than lists for a known notice).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies use for a single known notice ('for one notice') but does not explicitly contrast with alternatives or state when not to use it. Sibling tool names provide context, but the description itself lacks direct usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_solicitationsSearch SAM.gov solicitationsA
Searches federal contract opportunities in a posted-date window. postedFrom and postedTo are required, MM/dd/yyyy, at most one year apart. Returns normalised notices, each with its attachment links. The returned count is the length of the list actually returned, which is not the same as totalRecords across all pages.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Records per page, up to 1000 | |
| ncode | No | NAICS code, up to 6 digits. Lodging and housing: 721110 hotels and motels, 721310 rooming and boarding houses, 531110 lessors of residential buildings | |
| ptype | No | Procurement type, e.g. o for solicitation, p for presolicitation, k for combined synopsis | |
| state | No | Two-letter place-of-performance state | |
| title | No | Free-text match against the notice title | |
| offset | No | Page offset | |
| postedTo | Yes | End of the posted-date window, MM/dd/yyyy | |
| postedFrom | Yes | Start of the posted-date window, MM/dd/yyyy | |
| typeOfSetAside | No | Set-aside code, e.g. SBA, SDVOSBC, 8A, WOSB |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of disclosing behavior. It does so effectively by adding non-obvious details: required date format and span limit, return of normalized notices with attachment links, and a warning that the returned count differs from totalRecords across all pages. This is valuable transparency, though it omits error behavior or pagination semantics beyond the count.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences long, front-loaded with the core purpose, followed by constraints and then return/count behavior. Every sentence earns its place; no filler or redundant repetition of schema info.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 9 parameters and no output schema, the description covers the essential operational context: purpose, mandatory date window restrictions, return format, and a pagination count caveat. It does not detail how filters like limit/offset interact with the count, but those are covered by the schema descriptions, so the overall package is quite complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by stating that postedFrom/postedTo are required and at most one year apart—a rule not defined in the schema. It also hints at output semantics ('normalised notices'), which complements the schema's parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource+scope: 'Searches federal contract opportunities in a posted-date window.' This clearly distinguishes it from sibling tools like get_solicitation (retrieves a specific one) and attachment tools. The purpose is unambiguous and immediately identifiable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context on when to use the tool (search within a posted-date window) and states hard requirements (postedFrom and postedTo required, MM/dd/yyyy, at most one year apart). It does not explicitly compare with alternative sibling tools, but the usage constraints and scenario are clear, so only a slight deduction for lacking explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_statusServer statusA
Reports which mode the server is in and where its data is coming from. Call this first when results look surprising.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that the tool reports mode and data source, implying a read-only operation, but it does not explicitly state that there are no side effects or describe the output format. This is adequate but not rich behavioral detail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core action and usage cue. There is zero waste or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the essential aspects of a simple diagnostic tool: what it reports and when to call it. It could elaborate on what 'mode' means or what constitutes 'surprising results,' but for a tool with no parameters and no output schema, it is substantially complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the description cannot add parameter-level meaning. Per the baseline for zero-parameter tools, a score of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Reports') and clearly defines the resource (server mode and data source). It distinguishes itself from sibling tools by focusing on server state rather than data retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: 'Call this first when results look surprising.' This gives a clear, actionable usage guideline, which is especially valuable for a diagnostic tool with no obvious alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
fetch_attachment - First observed
get_solicitation - First observed
list_attachments - First observed
search_solicitations - First observed
server_status
TDQS
Scored across 5 tools
Each tool has a clearly distinct role: searching solicitations, retrieving a single solicitation, listing attachments, fetching an attachment, and checking server status. No two tools could be confused for the same purpose.
Most tools follow the verb_noun pattern (fetch_attachment, get_solicitation, list_attachments, search_solicitations). server_status deviates as a noun phrase but is still logically named and not confusing.
The 5 tools are well-scoped for a focused read-only SAM.gov API server. Each tool serves a distinct need without redundancy or bloat.
The tool set covers the full read lifecycle: search and retrieve notices, list and download attachments, and diagnose issues with server status. No obvious missing operations given the domain.
Maintenance
Related MCP Connectors
MCP access to the U.S. federal procurement graph: contracts, opportunities, entities, and more.
Federal+SLED govcon MCP: SAM, USASpending, recompete, hearings, policy intel, search. Private AI.
SAM.gov MCP — Federal contract opportunities and entity registration data
SAM.gov contract opportunities and entity lookup (BYOK) plus USASpending federal award data.
Related MCP Servers
- AlicenseAqualityAmaintenanceThe most comprehensive keyless federal-data MCP server. 36 tools for SAM.gov + USAspending + Federal Register + eCFR + Grants.gov. No API key, no registration, no signup. Works in Claude Desktop, Claude Code, Codex CLI, Cursor, Continue, Gemini CLI, and any MCP-aware host.6152497 npm8MIT
- FlicenseNot gradedqualityCmaintenanceMCP server for querying U.S. Federal Procurement Data System (FPDS-NG) to search federal contract actions by keyword, agency, NAICS, or vendor, with USDC micropayments via x402.-
- AlicenseAqualityDmaintenanceRead-only MCP server for exploring US federal spending data via the USAspending.gov API, enabling natural language queries on awards, agencies, recipients, and spending trends.12MIT
- FlicenseAqualityDmaintenanceMCP server for searching government tenders from CanadaBuys and SAM.gov with free stats and paid search, latest, and AI matching tools using x402 micropayments.41-