govconapi-mcp 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- govconapi_mcp-0.2.2/Dockerfile +17 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/PKG-INFO +22 -4
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/README.md +21 -3
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/agencies.py +2 -1
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/award_compliance.py +14 -13
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/awards.py +2 -1
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/bid_negotiate.py +10 -9
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/capture_teaming.py +13 -12
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/exclusions.py +2 -1
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/identifiers.py +2 -1
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/market_research.py +13 -12
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/opportunities.py +4 -3
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/pyproject.toml +1 -1
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/server.json +4 -3
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/.gitignore +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/LICENSE +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/TOOLS.md +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/glama.json +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/__init__.py +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/core.py +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/server.py +0 -0
- {govconapi_mcp-0.2.1 → govconapi_mcp-0.2.2}/govconapi_mcp/tools/__init__.py +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Image for running the GovCon API MCP server (stdio transport). Used by Glama's build and
|
|
2
|
+
# security checks, and by anyone who wants to run the client in a container.
|
|
3
|
+
#
|
|
4
|
+
# Introspection works with no configuration: the API key is read at CALL time inside core._get(),
|
|
5
|
+
# not at import, so `tools/list` returns all 53 tool definitions without a key and without network
|
|
6
|
+
# access. Actually invoking a tool does need a key, since every tool is a live query against
|
|
7
|
+
# https://govconapi.com. Free 14-day trial, no credit card: https://govconapi.com
|
|
8
|
+
FROM python:3.12-slim
|
|
9
|
+
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
COPY . /app
|
|
12
|
+
|
|
13
|
+
RUN pip install --no-cache-dir .
|
|
14
|
+
|
|
15
|
+
# GOVCONAPI_KEY is required to call tools, not to list them.
|
|
16
|
+
# GOVCONAPI_BASE is optional and defaults to https://govconapi.com.
|
|
17
|
+
ENTRYPOINT ["govconapi-mcp"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: govconapi-mcp
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: MCP server for GovCon API: 53 tools spanning the federal contracting lifecycle (market research, opportunity discovery, capture and teaming, bid and proposal, award and compliance) for Claude Desktop, Claude Code, Cursor, Zed, or any local MCP client.
|
|
5
5
|
Project-URL: Homepage, https://govconapi.com
|
|
6
6
|
Project-URL: Documentation, https://govconapi.com/api-guide
|
|
@@ -27,11 +27,13 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
# GovCon API MCP
|
|
29
29
|
|
|
30
|
-
     [](https://govconapi.com/pricing)
|
|
31
31
|
|
|
32
32
|
**Most SAM.gov MCP servers wrap one dataset behind six to ten tools. This is 53 tools across five federal sources, already joined on the identifiers they share, including full text extracted from inside solicitation attachments.**
|
|
33
33
|
|
|
34
|
-
US federal procurement data for agents: **
|
|
34
|
+
US federal procurement data for agents: **668K** SAM.gov opportunities, **10.6M** FPDS prime contract transactions, **880K** SAM-registered entities, **226K** FFATA subawards, **164K** exclusions, **14K** decided GAO protests, and **~262K** GSA CALC labor rates. Cross-linked on UEI, CAGE, PIID and NAICS, so one question does not become five integrations.
|
|
35
|
+
|
|
36
|
+
*Counts as of 2026-08-31, rounded. The corpus grows daily, so this file is a snapshot: current per-source totals and freshness dates are always live at [`/api/v1/status`](https://govconapi.com/api/v1/status), no key required.*
|
|
35
37
|
|
|
36
38
|
> **Free 14-day trial, no credit card.** [Get a key](https://govconapi.com), then `pipx install govconapi-mcp`.
|
|
37
39
|
|
|
@@ -41,7 +43,7 @@ US federal procurement data for agents: **668,576** SAM.gov opportunities, **10,
|
|
|
41
43
|
|
|
42
44
|
Most federal-contracting MCP servers are thin proxies over the official SAM.gov API. Four concrete differences.
|
|
43
45
|
|
|
44
|
-
- **It searches inside the documents, not just the notice.** We
|
|
46
|
+
- **It searches inside the documents, not just the notice.** We extract and index the full text of every document attached to a notice. A corpus study on **2026-07-16** measured **506,382 documents** across **209,095 opportunities**, 31.4 billion characters, including **57,527 files recovered from archives nested up to five levels deep** that never appear in SAM's own attachment list, and **41,764 scanned documents recovered with OCR**. Those five figures are one dated measurement and belong together; the index has grown since, and the running total is on [`/api/v1/status`](https://govconapi.com/api/v1/status). A requirement like CMMC, a clearance level, or an option year is usually in the SOW, not the notice. Most tools structurally cannot see it.
|
|
45
47
|
- **Five sources, already joined.** SAM.gov opportunities and entities, FPDS contract transactions, USAspending/FFATA subawards, SAM exclusions, SBA DSBS certifications and GSA CALC rates, resolved onto shared identifiers. A wrapper hands your agent one dataset and leaves the joins to it.
|
|
46
48
|
- **No SAM.gov entity registration and no company UEI required.** GSA documents 10 requests/day for a non-federal user without a SAM role, and 1,000/day with one. Access here is an email and a key.
|
|
47
49
|
- **53 tools, organised by lifecycle stage.** Market research, opportunity discovery, capture and teaming, bid and negotiate, award and compliance. You should not have to know our URL structure to know which tool answers your question.
|
|
@@ -145,11 +147,27 @@ MCP access is included with every plan, including the trial. No separate tier.
|
|
|
145
147
|
|
|
146
148
|
---
|
|
147
149
|
|
|
150
|
+
## Privacy Policy
|
|
151
|
+
|
|
152
|
+
Full policy: **<https://govconapi.com/privacy>**
|
|
153
|
+
|
|
154
|
+
What this server does with your data, in short:
|
|
155
|
+
|
|
156
|
+
- **Your API key** is read from `GOVCONAPI_KEY` in your own client's config and sent to `https://govconapi.com` in the `Authorization` header. This server process does not write it to disk or transmit it anywhere else.
|
|
157
|
+
- **Your tool calls** become ordinary GovCon API requests. As with any request to the API, we log the timestamp, endpoint, query parameters, response status, response time and which key made the call, for billing, rate-limit enforcement and abuse detection. Usage logs are retained for 90 days.
|
|
158
|
+
- **No conversation content leaves your machine.** Only the tool parameters your client fills in are sent, never the surrounding chat, and we log the status a request returned, not the contents of the response.
|
|
159
|
+
- **We do not sell your data.**
|
|
160
|
+
- **The data returned is public US federal procurement data** from SAM.gov, FPDS, USAspending, SBA DSBS and GSA CALC.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
148
164
|
## Links
|
|
149
165
|
|
|
150
166
|
- API reference: <https://govconapi.com/api-guide>
|
|
151
167
|
- Tool reference: <https://govconapi.com/mcp/tools>
|
|
168
|
+
- Live coverage and freshness: <https://govconapi.com/api/v1/status>
|
|
152
169
|
- Machine-readable docs: <https://govconapi.com/llms.txt> and <https://govconapi.com/openapi.json>
|
|
170
|
+
- Privacy: <https://govconapi.com/privacy> · Terms: <https://govconapi.com/terms>
|
|
153
171
|
- Support: <support@govconapi.com>
|
|
154
172
|
|
|
155
173
|
MIT licensed. Data sourced from SAM.gov, FPDS, USAspending, SBA DSBS and GSA CALC, all public federal data.
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
# GovCon API MCP
|
|
4
4
|
|
|
5
|
-
     [](https://govconapi.com/pricing)
|
|
6
6
|
|
|
7
7
|
**Most SAM.gov MCP servers wrap one dataset behind six to ten tools. This is 53 tools across five federal sources, already joined on the identifiers they share, including full text extracted from inside solicitation attachments.**
|
|
8
8
|
|
|
9
|
-
US federal procurement data for agents: **
|
|
9
|
+
US federal procurement data for agents: **668K** SAM.gov opportunities, **10.6M** FPDS prime contract transactions, **880K** SAM-registered entities, **226K** FFATA subawards, **164K** exclusions, **14K** decided GAO protests, and **~262K** GSA CALC labor rates. Cross-linked on UEI, CAGE, PIID and NAICS, so one question does not become five integrations.
|
|
10
|
+
|
|
11
|
+
*Counts as of 2026-08-31, rounded. The corpus grows daily, so this file is a snapshot: current per-source totals and freshness dates are always live at [`/api/v1/status`](https://govconapi.com/api/v1/status), no key required.*
|
|
10
12
|
|
|
11
13
|
> **Free 14-day trial, no credit card.** [Get a key](https://govconapi.com), then `pipx install govconapi-mcp`.
|
|
12
14
|
|
|
@@ -16,7 +18,7 @@ US federal procurement data for agents: **668,576** SAM.gov opportunities, **10,
|
|
|
16
18
|
|
|
17
19
|
Most federal-contracting MCP servers are thin proxies over the official SAM.gov API. Four concrete differences.
|
|
18
20
|
|
|
19
|
-
- **It searches inside the documents, not just the notice.** We
|
|
21
|
+
- **It searches inside the documents, not just the notice.** We extract and index the full text of every document attached to a notice. A corpus study on **2026-07-16** measured **506,382 documents** across **209,095 opportunities**, 31.4 billion characters, including **57,527 files recovered from archives nested up to five levels deep** that never appear in SAM's own attachment list, and **41,764 scanned documents recovered with OCR**. Those five figures are one dated measurement and belong together; the index has grown since, and the running total is on [`/api/v1/status`](https://govconapi.com/api/v1/status). A requirement like CMMC, a clearance level, or an option year is usually in the SOW, not the notice. Most tools structurally cannot see it.
|
|
20
22
|
- **Five sources, already joined.** SAM.gov opportunities and entities, FPDS contract transactions, USAspending/FFATA subawards, SAM exclusions, SBA DSBS certifications and GSA CALC rates, resolved onto shared identifiers. A wrapper hands your agent one dataset and leaves the joins to it.
|
|
21
23
|
- **No SAM.gov entity registration and no company UEI required.** GSA documents 10 requests/day for a non-federal user without a SAM role, and 1,000/day with one. Access here is an email and a key.
|
|
22
24
|
- **53 tools, organised by lifecycle stage.** Market research, opportunity discovery, capture and teaming, bid and negotiate, award and compliance. You should not have to know our URL structure to know which tool answers your question.
|
|
@@ -120,11 +122,27 @@ MCP access is included with every plan, including the trial. No separate tier.
|
|
|
120
122
|
|
|
121
123
|
---
|
|
122
124
|
|
|
125
|
+
## Privacy Policy
|
|
126
|
+
|
|
127
|
+
Full policy: **<https://govconapi.com/privacy>**
|
|
128
|
+
|
|
129
|
+
What this server does with your data, in short:
|
|
130
|
+
|
|
131
|
+
- **Your API key** is read from `GOVCONAPI_KEY` in your own client's config and sent to `https://govconapi.com` in the `Authorization` header. This server process does not write it to disk or transmit it anywhere else.
|
|
132
|
+
- **Your tool calls** become ordinary GovCon API requests. As with any request to the API, we log the timestamp, endpoint, query parameters, response status, response time and which key made the call, for billing, rate-limit enforcement and abuse detection. Usage logs are retained for 90 days.
|
|
133
|
+
- **No conversation content leaves your machine.** Only the tool parameters your client fills in are sent, never the surrounding chat, and we log the status a request returned, not the contents of the response.
|
|
134
|
+
- **We do not sell your data.**
|
|
135
|
+
- **The data returned is public US federal procurement data** from SAM.gov, FPDS, USAspending, SBA DSBS and GSA CALC.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
123
139
|
## Links
|
|
124
140
|
|
|
125
141
|
- API reference: <https://govconapi.com/api-guide>
|
|
126
142
|
- Tool reference: <https://govconapi.com/mcp/tools>
|
|
143
|
+
- Live coverage and freshness: <https://govconapi.com/api/v1/status>
|
|
127
144
|
- Machine-readable docs: <https://govconapi.com/llms.txt> and <https://govconapi.com/openapi.json>
|
|
145
|
+
- Privacy: <https://govconapi.com/privacy> · Terms: <https://govconapi.com/terms>
|
|
128
146
|
- Support: <support@govconapi.com>
|
|
129
147
|
|
|
130
148
|
MIT licensed. Data sourced from SAM.gov, FPDS, USAspending, SBA DSBS and GSA CALC, all public federal data.
|
|
@@ -6,9 +6,10 @@ import json
|
|
|
6
6
|
import re
|
|
7
7
|
|
|
8
8
|
from ..core import mcp, _get
|
|
9
|
+
from mcp.types import ToolAnnotations
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
@mcp.tool()
|
|
12
|
+
@mcp.tool(title="Lookup Agency", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
12
13
|
async def lookup_agency(query: str) -> str:
|
|
13
14
|
"""Resolve an agency acronym or partial name to canonical SAM.gov agency strings.
|
|
14
15
|
|
|
@@ -8,11 +8,12 @@ import json
|
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
10
10
|
from ..core import mcp, _get
|
|
11
|
+
from mcp.types import ToolAnnotations
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
# ─── Contracts (FPDS prime awards) ─────────────────────────────────────────
|
|
14
15
|
|
|
15
|
-
@mcp.tool()
|
|
16
|
+
@mcp.tool(title="Search FPDS Contract Transactions", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
16
17
|
async def search_contracts(
|
|
17
18
|
uei: Optional[str] = None,
|
|
18
19
|
parent_uei: Optional[str] = None,
|
|
@@ -65,7 +66,7 @@ async def search_contracts(
|
|
|
65
66
|
return json.dumps(data, indent=2, default=str)
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
@mcp.tool()
|
|
69
|
+
@mcp.tool(title="Get Contract", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
69
70
|
async def get_contract(piid: str) -> str:
|
|
70
71
|
"""Get one contract's LATEST transaction plus a roll-up of obligation/value totals
|
|
71
72
|
across every modification.
|
|
@@ -88,7 +89,7 @@ async def get_contract(piid: str) -> str:
|
|
|
88
89
|
return json.dumps(data, indent=2, default=str)
|
|
89
90
|
|
|
90
91
|
|
|
91
|
-
@mcp.tool()
|
|
92
|
+
@mcp.tool(title="Get Contract Modifications", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
92
93
|
async def get_contract_modifications(piid: str, limit: int = 100, offset: int = 0) -> str:
|
|
93
94
|
"""Get EVERY transaction row for a contract, oldest action first, the full
|
|
94
95
|
modification trail (amendments, options exercised, partial terminations), not just
|
|
@@ -109,7 +110,7 @@ async def get_contract_modifications(piid: str, limit: int = 100, offset: int =
|
|
|
109
110
|
return json.dumps(data, indent=2, default=str)
|
|
110
111
|
|
|
111
112
|
|
|
112
|
-
@mcp.tool()
|
|
113
|
+
@mcp.tool(title="Get the Vehicle Behind a Contract", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
113
114
|
async def get_contract_vehicle(piid: str) -> str:
|
|
114
115
|
"""Get the contract vehicle (IDIQ, GWAC, FSS schedule, or BPA) this order was placed
|
|
115
116
|
against.
|
|
@@ -130,7 +131,7 @@ async def get_contract_vehicle(piid: str) -> str:
|
|
|
130
131
|
|
|
131
132
|
# ─── Vehicles (IDVs) ────────────────────────────────────────────────────────
|
|
132
133
|
|
|
133
|
-
@mcp.tool()
|
|
134
|
+
@mcp.tool(title="Search Vehicles", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
134
135
|
async def search_vehicles(
|
|
135
136
|
uei: Optional[str] = None,
|
|
136
137
|
parent_uei: Optional[str] = None,
|
|
@@ -175,7 +176,7 @@ async def search_vehicles(
|
|
|
175
176
|
return json.dumps(data, indent=2, default=str)
|
|
176
177
|
|
|
177
178
|
|
|
178
|
-
@mcp.tool()
|
|
179
|
+
@mcp.tool(title="Get Vehicle Detail", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
179
180
|
async def get_vehicle(piid: str) -> str:
|
|
180
181
|
"""Get one contract vehicle's detail: ceiling, period, and what's been ordered
|
|
181
182
|
through it.
|
|
@@ -198,7 +199,7 @@ async def get_vehicle(piid: str) -> str:
|
|
|
198
199
|
return json.dumps(data, indent=2, default=str)
|
|
199
200
|
|
|
200
201
|
|
|
201
|
-
@mcp.tool()
|
|
202
|
+
@mcp.tool(title="Get Vehicle Holders", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
202
203
|
async def get_vehicle_holders(piid: str, limit: int = 50) -> str:
|
|
203
204
|
"""Get who holds a vehicle AND who's actually earning through it, two distinct
|
|
204
205
|
populations, don't conflate them: a firm can hold a vehicle for years and earn
|
|
@@ -223,7 +224,7 @@ async def get_vehicle_holders(piid: str, limit: int = 50) -> str:
|
|
|
223
224
|
|
|
224
225
|
# ─── Subawards (FFATA) ──────────────────────────────────────────────────────
|
|
225
226
|
|
|
226
|
-
@mcp.tool()
|
|
227
|
+
@mcp.tool(title="Search Subawards", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
227
228
|
async def search_subawards(
|
|
228
229
|
prime_uei: Optional[str] = None,
|
|
229
230
|
sub_uei: Optional[str] = None,
|
|
@@ -268,7 +269,7 @@ async def search_subawards(
|
|
|
268
269
|
return json.dumps(data, indent=2, default=str)
|
|
269
270
|
|
|
270
271
|
|
|
271
|
-
@mcp.tool()
|
|
272
|
+
@mcp.tool(title="Get Subaward", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
272
273
|
async def get_subaward(subaward_sam_report_id: str) -> str:
|
|
273
274
|
"""Get one FFATA subaward report by its SAM report ID.
|
|
274
275
|
|
|
@@ -280,7 +281,7 @@ async def get_subaward(subaward_sam_report_id: str) -> str:
|
|
|
280
281
|
return json.dumps(data, indent=2, default=str)
|
|
281
282
|
|
|
282
283
|
|
|
283
|
-
@mcp.tool()
|
|
284
|
+
@mcp.tool(title="Get Prime Subawards", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
284
285
|
async def get_prime_subawards(
|
|
285
286
|
uei: str,
|
|
286
287
|
date_from: Optional[str] = None,
|
|
@@ -307,7 +308,7 @@ async def get_prime_subawards(
|
|
|
307
308
|
return json.dumps(data, indent=2, default=str)
|
|
308
309
|
|
|
309
310
|
|
|
310
|
-
@mcp.tool()
|
|
311
|
+
@mcp.tool(title="Get Prime Relationships", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
311
312
|
async def get_prime_relationships(
|
|
312
313
|
uei: str,
|
|
313
314
|
date_from: Optional[str] = None,
|
|
@@ -333,7 +334,7 @@ async def get_prime_relationships(
|
|
|
333
334
|
|
|
334
335
|
# ─── Bid Protests ────────────────────────────────────────────────────────────
|
|
335
336
|
|
|
336
|
-
@mcp.tool()
|
|
337
|
+
@mcp.tool(title="Search Protests", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
337
338
|
async def search_protests(
|
|
338
339
|
outcome: Optional[str] = None,
|
|
339
340
|
agency: Optional[str] = None,
|
|
@@ -374,7 +375,7 @@ async def search_protests(
|
|
|
374
375
|
return json.dumps(data, indent=2, default=str)
|
|
375
376
|
|
|
376
377
|
|
|
377
|
-
@mcp.tool()
|
|
378
|
+
@mcp.tool(title="Get Protests on a Solicitation", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
378
379
|
async def get_protests_on_solicitation(solicitation_number: str) -> str:
|
|
379
380
|
"""Get every protest filed on ONE solicitation, the contestability read for a
|
|
380
381
|
specific opportunity or award: any protest pending right now, and the statutory date
|
|
@@ -13,9 +13,10 @@ import json
|
|
|
13
13
|
from typing import Optional
|
|
14
14
|
|
|
15
15
|
from ..core import mcp, _get
|
|
16
|
+
from mcp.types import ToolAnnotations
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
@mcp.tool()
|
|
19
|
+
@mcp.tool(title="Search SAM Award Notices", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
19
20
|
async def search_awards(
|
|
20
21
|
awardee: Optional[str] = None,
|
|
21
22
|
uei: Optional[str] = None,
|
|
@@ -8,11 +8,12 @@ import json
|
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
10
10
|
from ..core import mcp, _get
|
|
11
|
+
from mcp.types import ToolAnnotations
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
# ─── Contract Price Benchmarks ─────────────────────────────────────────────
|
|
14
15
|
|
|
15
|
-
@mcp.tool()
|
|
16
|
+
@mcp.tool(title="Get Price Benchmark", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
16
17
|
async def get_price_benchmark(
|
|
17
18
|
naics: str,
|
|
18
19
|
set_aside: Optional[str] = None,
|
|
@@ -54,7 +55,7 @@ async def get_price_benchmark(
|
|
|
54
55
|
return json.dumps(data, indent=2, default=str)
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
@mcp.tool()
|
|
58
|
+
@mcp.tool(title="Get Price Position", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
58
59
|
async def get_price_position(
|
|
59
60
|
naics: str,
|
|
60
61
|
value: str,
|
|
@@ -94,7 +95,7 @@ async def get_price_position(
|
|
|
94
95
|
|
|
95
96
|
# ─── Labor Rate Benchmarks ──────────────────────────────────────────────────
|
|
96
97
|
|
|
97
|
-
@mcp.tool()
|
|
98
|
+
@mcp.tool(title="Get Labor Rate Benchmark", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
98
99
|
async def get_labor_rate_benchmark(
|
|
99
100
|
labor_category: str,
|
|
100
101
|
match: str = "contains",
|
|
@@ -147,7 +148,7 @@ async def get_labor_rate_benchmark(
|
|
|
147
148
|
|
|
148
149
|
# ─── Wage Determinations ────────────────────────────────────────────────────
|
|
149
150
|
|
|
150
|
-
@mcp.tool()
|
|
151
|
+
@mcp.tool(title="Search Wage Determinations", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
151
152
|
async def search_wage_determinations(
|
|
152
153
|
type: Optional[str] = None,
|
|
153
154
|
state: Optional[str] = None,
|
|
@@ -190,7 +191,7 @@ async def search_wage_determinations(
|
|
|
190
191
|
return json.dumps(data, indent=2, default=str)
|
|
191
192
|
|
|
192
193
|
|
|
193
|
-
@mcp.tool()
|
|
194
|
+
@mcp.tool(title="Get Wage Determinations by Location", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
194
195
|
async def get_wds_by_location(
|
|
195
196
|
state: str, county: Optional[str] = None, type: Optional[str] = None,
|
|
196
197
|
limit: int = 25, offset: int = 0,
|
|
@@ -216,7 +217,7 @@ async def get_wds_by_location(
|
|
|
216
217
|
return json.dumps(data, indent=2, default=str)
|
|
217
218
|
|
|
218
219
|
|
|
219
|
-
@mcp.tool()
|
|
220
|
+
@mcp.tool(title="Query Wage Rates Across Determinations", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
220
221
|
async def get_wage_rates(
|
|
221
222
|
classification: Optional[str] = None,
|
|
222
223
|
type: Optional[str] = None,
|
|
@@ -252,7 +253,7 @@ async def get_wage_rates(
|
|
|
252
253
|
return json.dumps(data, indent=2, default=str)
|
|
253
254
|
|
|
254
255
|
|
|
255
|
-
@mcp.tool()
|
|
256
|
+
@mcp.tool(title="Get Wage Rate Summary", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
256
257
|
async def get_wage_rate_summary(
|
|
257
258
|
occupation_code: Optional[str] = None,
|
|
258
259
|
classification: Optional[str] = None,
|
|
@@ -285,7 +286,7 @@ async def get_wage_rate_summary(
|
|
|
285
286
|
return json.dumps(data, indent=2, default=str)
|
|
286
287
|
|
|
287
288
|
|
|
288
|
-
@mcp.tool()
|
|
289
|
+
@mcp.tool(title="Get Wage Determination", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
289
290
|
async def get_wage_determination(wd_id: str) -> str:
|
|
290
291
|
"""Get one wage determination's full record: location array, every classification's
|
|
291
292
|
hourly wage + fringe, and (for CBAs) the contractor/union detail block.
|
|
@@ -301,7 +302,7 @@ async def get_wage_determination(wd_id: str) -> str:
|
|
|
301
302
|
|
|
302
303
|
# ─── Vendor Risk ─────────────────────────────────────────────────────────────
|
|
303
304
|
|
|
304
|
-
@mcp.tool()
|
|
305
|
+
@mcp.tool(title="Get Vendor Risk Report", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
305
306
|
async def get_vendor_risk_report(uei: str) -> str:
|
|
306
307
|
"""Get a 7-signal vendor risk report for one UEI, screening facts for teaming or
|
|
307
308
|
subcontracting due diligence, before you commit to a partner.
|
|
@@ -8,11 +8,12 @@ import json
|
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
10
10
|
from ..core import mcp, _get
|
|
11
|
+
from mcp.types import ToolAnnotations
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
# ─── Companies (won at least one award) ───────────────────────────────────
|
|
14
15
|
|
|
15
|
-
@mcp.tool()
|
|
16
|
+
@mcp.tool(title="Search Companies", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
16
17
|
async def search_companies(
|
|
17
18
|
q: Optional[str] = None,
|
|
18
19
|
naics: Optional[str] = None,
|
|
@@ -51,7 +52,7 @@ async def search_companies(
|
|
|
51
52
|
return json.dumps(data, indent=2, default=str)
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
@mcp.tool()
|
|
55
|
+
@mcp.tool(title="Get Company Profile", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
55
56
|
async def get_company_profile(uei: str) -> str:
|
|
56
57
|
"""Get one company's aggregate profile: SAM registration (legal name, address, NAICS,
|
|
57
58
|
PSC, certifications) combined with award-history totals.
|
|
@@ -72,7 +73,7 @@ async def get_company_profile(uei: str) -> str:
|
|
|
72
73
|
return json.dumps(data, indent=2, default=str)
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
@mcp.tool()
|
|
76
|
+
@mcp.tool(title="Get Company Awards", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
76
77
|
async def get_company_awards(
|
|
77
78
|
uei: str,
|
|
78
79
|
limit: int = 50,
|
|
@@ -100,7 +101,7 @@ async def get_company_awards(
|
|
|
100
101
|
return json.dumps(data, indent=2, default=str)
|
|
101
102
|
|
|
102
103
|
|
|
103
|
-
@mcp.tool()
|
|
104
|
+
@mcp.tool(title="Get Company Peers", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
104
105
|
async def get_company_peers(uei: str, limit: int = 10) -> str:
|
|
105
106
|
"""Find companies similar to this one by NAICS + agency overlap, the competitive
|
|
106
107
|
landscape around a firm, not its own history.
|
|
@@ -121,7 +122,7 @@ async def get_company_peers(uei: str, limit: int = 10) -> str:
|
|
|
121
122
|
|
|
122
123
|
# ─── Entities (the full SAM registry, award or not) ───────────────────────
|
|
123
124
|
|
|
124
|
-
@mcp.tool()
|
|
125
|
+
@mcp.tool(title="Search Entities", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
125
126
|
async def search_entities(
|
|
126
127
|
q: Optional[str] = None,
|
|
127
128
|
naics: Optional[str] = None,
|
|
@@ -159,7 +160,7 @@ async def search_entities(
|
|
|
159
160
|
return json.dumps(data, indent=2, default=str)
|
|
160
161
|
|
|
161
162
|
|
|
162
|
-
@mcp.tool()
|
|
163
|
+
@mcp.tool(title="Get Expiring SAM Registrations", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
163
164
|
async def get_entities_expiring(
|
|
164
165
|
within_days: int = 60,
|
|
165
166
|
state: Optional[str] = None,
|
|
@@ -186,7 +187,7 @@ async def get_entities_expiring(
|
|
|
186
187
|
return json.dumps(data, indent=2, default=str)
|
|
187
188
|
|
|
188
189
|
|
|
189
|
-
@mcp.tool()
|
|
190
|
+
@mcp.tool(title="Get Entity", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
190
191
|
async def get_entity(uei: Optional[str] = None, cage_code: Optional[str] = None) -> str:
|
|
191
192
|
"""Get one SAM entity's full registration record by UEI or CAGE code, the same
|
|
192
193
|
question, two different keys, so this is one tool, not two.
|
|
@@ -208,7 +209,7 @@ async def get_entity(uei: Optional[str] = None, cage_code: Optional[str] = None)
|
|
|
208
209
|
|
|
209
210
|
# ─── Partners (teaming) ────────────────────────────────────────────────────
|
|
210
211
|
|
|
211
|
-
@mcp.tool()
|
|
212
|
+
@mcp.tool(title="Search Partners", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
212
213
|
async def search_partners(
|
|
213
214
|
naics: Optional[str] = None,
|
|
214
215
|
agency: Optional[str] = None,
|
|
@@ -247,7 +248,7 @@ async def search_partners(
|
|
|
247
248
|
|
|
248
249
|
# ─── Company-contact resolver ──────────────────────────────────────────────
|
|
249
250
|
|
|
250
|
-
@mcp.tool()
|
|
251
|
+
@mcp.tool(title="Get Company Decision-Maker Contact", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
251
252
|
async def get_company_contact(uei: str) -> str:
|
|
252
253
|
"""Resolve a vendor's REAL decision-maker contact, SAM registration agents
|
|
253
254
|
(third-party filing services) are filtered out, so this is the actual point of
|
|
@@ -267,7 +268,7 @@ async def get_company_contact(uei: str) -> str:
|
|
|
267
268
|
|
|
268
269
|
# ─── Contacts (buyer intel, contracting officers) ─────────────────────────
|
|
269
270
|
|
|
270
|
-
@mcp.tool()
|
|
271
|
+
@mcp.tool(title="Search Contracting Officer Contacts", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
271
272
|
async def search_contacts(
|
|
272
273
|
name: Optional[str] = None,
|
|
273
274
|
agency: Optional[str] = None,
|
|
@@ -293,7 +294,7 @@ async def search_contacts(
|
|
|
293
294
|
|
|
294
295
|
# ─── Recompetes (also an early Capture signal; see Post-Award for the monitoring use) ──
|
|
295
296
|
|
|
296
|
-
@mcp.tool()
|
|
297
|
+
@mcp.tool(title="Search Recompetes", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
297
298
|
async def search_recompetes(
|
|
298
299
|
naics: Optional[str] = None,
|
|
299
300
|
agency: Optional[str] = None,
|
|
@@ -347,7 +348,7 @@ async def search_recompetes(
|
|
|
347
348
|
return json.dumps(data, indent=2, default=str)
|
|
348
349
|
|
|
349
350
|
|
|
350
|
-
@mcp.tool()
|
|
351
|
+
@mcp.tool(title="Get Recompete", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
351
352
|
async def get_recompete(piid: str) -> str:
|
|
352
353
|
"""Get one recompeting/expiring contract by PIID, plus incumbent-vulnerability
|
|
353
354
|
signals (cert-lapse, lone-holder, single-agency dependence) composed from the
|
|
@@ -6,9 +6,10 @@ import json
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
|
|
8
8
|
from ..core import mcp, _get
|
|
9
|
+
from mcp.types import ToolAnnotations
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
@mcp.tool()
|
|
12
|
+
@mcp.tool(title="Check Exclusions for a Vendor", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
12
13
|
async def check_exclusion(
|
|
13
14
|
name: Optional[str] = None,
|
|
14
15
|
uei: Optional[str] = None,
|
|
@@ -5,9 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
|
|
7
7
|
from ..core import mcp, _get
|
|
8
|
+
from mcp.types import ToolAnnotations
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
@mcp.tool()
|
|
11
|
+
@mcp.tool(title="Resolve DUNS to UEI", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
11
12
|
async def resolve_identifier(identifier: str) -> str:
|
|
12
13
|
"""Resolve between legacy DUNS (9 digits, or 13-digit DUNS+4) and current UEI
|
|
13
14
|
(12 alphanumeric), accepts either side, returns both plus the entity name.
|
|
@@ -7,11 +7,12 @@ import json
|
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
9
|
from ..core import mcp, _get
|
|
10
|
+
from mcp.types import ToolAnnotations
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# ─── Contracting Offices ──────────────────────────────────────────────────
|
|
13
14
|
|
|
14
|
-
@mcp.tool()
|
|
15
|
+
@mcp.tool(title="Discover Offices", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
15
16
|
async def discover_offices(
|
|
16
17
|
naics: str,
|
|
17
18
|
sort: str = "biggest",
|
|
@@ -39,7 +40,7 @@ async def discover_offices(
|
|
|
39
40
|
return json.dumps(data, indent=2, default=str)
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
@mcp.tool()
|
|
43
|
+
@mcp.tool(title="Get Office Profile", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
43
44
|
async def get_office_profile(office_code: str) -> str:
|
|
44
45
|
"""Get one contracting office's full buying profile: obligations, competition,
|
|
45
46
|
set-aside lean, and every NAICS it buys.
|
|
@@ -59,7 +60,7 @@ async def get_office_profile(office_code: str) -> str:
|
|
|
59
60
|
|
|
60
61
|
# ─── Federal Hierarchy ────────────────────────────────────────────────────
|
|
61
62
|
|
|
62
|
-
@mcp.tool()
|
|
63
|
+
@mcp.tool(title="List Organizations", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
63
64
|
async def list_organizations(
|
|
64
65
|
type: Optional[str] = None,
|
|
65
66
|
cgac: Optional[str] = None,
|
|
@@ -98,7 +99,7 @@ async def list_organizations(
|
|
|
98
99
|
return json.dumps(data, indent=2, default=str)
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
@mcp.tool()
|
|
102
|
+
@mcp.tool(title="Get Organization", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
102
103
|
async def get_organization(organization_id: int) -> str:
|
|
103
104
|
"""Get one federal organization's full record, with its parent, immediate children,
|
|
104
105
|
and full ancestor chain (root department down to immediate parent) all included in
|
|
@@ -117,7 +118,7 @@ async def get_organization(organization_id: int) -> str:
|
|
|
117
118
|
return json.dumps(data, indent=2, default=str)
|
|
118
119
|
|
|
119
120
|
|
|
120
|
-
@mcp.tool()
|
|
121
|
+
@mcp.tool(title="Get Organization Relationships", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
121
122
|
async def get_org_relationships(organization_id: int, direction: str = "children") -> str:
|
|
122
123
|
"""Get JUST an organization's immediate children or its ancestor chain, without the
|
|
123
124
|
rest of the record (get_organization already includes both if you need everything).
|
|
@@ -138,7 +139,7 @@ async def get_org_relationships(organization_id: int, direction: str = "children
|
|
|
138
139
|
|
|
139
140
|
# ─── Procurement Forecasts ────────────────────────────────────────────────
|
|
140
141
|
|
|
141
|
-
@mcp.tool()
|
|
142
|
+
@mcp.tool(title="Search Forecasts", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
142
143
|
async def search_forecasts(
|
|
143
144
|
source: Optional[str] = None,
|
|
144
145
|
agency: Optional[str] = None,
|
|
@@ -193,7 +194,7 @@ async def search_forecasts(
|
|
|
193
194
|
|
|
194
195
|
# ─── NAICS / Market Pulse ─────────────────────────────────────────────────
|
|
195
196
|
|
|
196
|
-
@mcp.tool()
|
|
197
|
+
@mcp.tool(title="Find NAICS Codes", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
197
198
|
async def find_naics_codes(
|
|
198
199
|
sector: Optional[str] = None,
|
|
199
200
|
prefix: Optional[str] = None,
|
|
@@ -236,7 +237,7 @@ async def find_naics_codes(
|
|
|
236
237
|
return json.dumps(data, indent=2, default=str)
|
|
237
238
|
|
|
238
239
|
|
|
239
|
-
@mcp.tool()
|
|
240
|
+
@mcp.tool(title="Get NAICS Leaderboard", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
240
241
|
async def get_naics_leaderboard(board: str, limit: int = 30) -> str:
|
|
241
242
|
"""Browse curated, ranked NAICS market leaderboards, a fixed set of named rankings,
|
|
242
243
|
distinct from find_naics_codes' open filtered search.
|
|
@@ -256,7 +257,7 @@ async def get_naics_leaderboard(board: str, limit: int = 30) -> str:
|
|
|
256
257
|
return json.dumps(data, indent=2, default=str)
|
|
257
258
|
|
|
258
259
|
|
|
259
|
-
@mcp.tool()
|
|
260
|
+
@mcp.tool(title="Get NAICS Market", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
260
261
|
async def get_naics_market(code: str) -> str:
|
|
261
262
|
"""Get the federal market profile for one NAICS code: spending, competition,
|
|
262
263
|
set-aside leverage, top buyers, and top incumbents.
|
|
@@ -278,7 +279,7 @@ async def get_naics_market(code: str) -> str:
|
|
|
278
279
|
return json.dumps(data, indent=2, default=str)
|
|
279
280
|
|
|
280
281
|
|
|
281
|
-
@mcp.tool()
|
|
282
|
+
@mcp.tool(title="Get NAICS Positioning", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
282
283
|
async def get_naics_positioning(code: str) -> str:
|
|
283
284
|
"""Get the language and set-aside makeup for a NAICS code's SOLICITATION side: the
|
|
284
285
|
phrase vocabulary contracting officers actually use in notices, the set-aside share
|
|
@@ -297,7 +298,7 @@ async def get_naics_positioning(code: str) -> str:
|
|
|
297
298
|
return json.dumps(data, indent=2, default=str)
|
|
298
299
|
|
|
299
300
|
|
|
300
|
-
@mcp.tool()
|
|
301
|
+
@mcp.tool(title="Get NAICS Simplified-Acquisition Breakdown", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
301
302
|
async def get_naics_simplified_acquisition(code: str) -> str:
|
|
302
303
|
"""Get the award-value breakdown for a NAICS code over the last 12 months of FPDS
|
|
303
304
|
prime awards: counts of micro / simplified-acquisition / above-SAT awards, and which
|
|
@@ -319,7 +320,7 @@ async def get_naics_simplified_acquisition(code: str) -> str:
|
|
|
319
320
|
return json.dumps(data, indent=2, default=str)
|
|
320
321
|
|
|
321
322
|
|
|
322
|
-
@mcp.tool()
|
|
323
|
+
@mcp.tool(title="Get NAICS Competition", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
323
324
|
async def get_naics_competition(code: str) -> str:
|
|
324
325
|
"""Get how contested a NAICS market is, over the whole FPDS prime-award market: offers
|
|
325
326
|
received per award, single-bidder share, top place-of-performance states, award-volume
|
|
@@ -5,9 +5,10 @@ import json
|
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
7
|
from ..core import mcp, _get
|
|
8
|
+
from mcp.types import ToolAnnotations
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
@mcp.tool()
|
|
11
|
+
@mcp.tool(title="Search Opportunities", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
11
12
|
async def search_opportunities(
|
|
12
13
|
naics: Optional[str] = None,
|
|
13
14
|
psc: Optional[str] = None,
|
|
@@ -79,7 +80,7 @@ async def search_opportunities(
|
|
|
79
80
|
return json.dumps(data, indent=2, default=str)
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
@mcp.tool()
|
|
83
|
+
@mcp.tool(title="Get Opportunity", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
83
84
|
async def get_opportunity(notice_id: str) -> str:
|
|
84
85
|
"""Fetch a single contract opportunity by its notice_id.
|
|
85
86
|
|
|
@@ -90,7 +91,7 @@ async def get_opportunity(notice_id: str) -> str:
|
|
|
90
91
|
return json.dumps(data, indent=2, default=str)
|
|
91
92
|
|
|
92
93
|
|
|
93
|
-
@mcp.tool()
|
|
94
|
+
@mcp.tool(title="Recent Opportunity Changes", annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True))
|
|
94
95
|
async def recent_changes(
|
|
95
96
|
since: str,
|
|
96
97
|
limit: int = 100,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "govconapi-mcp"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "MCP server for GovCon API: 53 tools spanning the federal contracting lifecycle (market research, opportunity discovery, capture and teaming, bid and proposal, award and compliance) for Claude Desktop, Claude Code, Cursor, Zed, or any local MCP client."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.govconapi/govconapi",
|
|
4
4
|
"title": "GovCon API",
|
|
5
|
-
"description": "US federal procurement
|
|
5
|
+
"description": "53 tools over US federal procurement: SAM.gov, FPDS awards, entities, subawards, exclusions.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/govconapi/govconapi-mcp",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"
|
|
10
|
+
"websiteUrl": "https://govconapi.com/mcp",
|
|
11
|
+
"version": "0.2.2",
|
|
11
12
|
"packages": [
|
|
12
13
|
{
|
|
13
14
|
"registryType": "pypi",
|
|
14
15
|
"identifier": "govconapi-mcp",
|
|
15
|
-
"version": "0.2.
|
|
16
|
+
"version": "0.2.2",
|
|
16
17
|
"transport": {
|
|
17
18
|
"type": "stdio"
|
|
18
19
|
},
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|