@agenttax/mcp-server 0.0.0-stage → 1.1.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agentic Tax Solutions LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,191 @@
1
- # Temporary Holding Version
1
+ # AgentTax MCP Server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Tax compliance for MCP tool developers and AI agents, powered by [AgentTax](https://agenttax.io).
4
+
5
+ ## For MCP tool developers
6
+
7
+ If you build MCP tools that charge for usage, you have sales tax obligations in states where your buyers are located. Most payment processors don't handle this correctly for digital services.
8
+
9
+ Add AgentTax to your MCP setup and call `track_payment` after every payment. That's it.
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "agenttax": {
15
+ "command": "npx",
16
+ "args": ["@agenttax/mcp-server"],
17
+ "env": {
18
+ "AGENTTAX_API_KEY": "atx_live_your_key"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ After a payment:
26
+
27
+ ```
28
+ track_payment({
29
+ amount: 49.00,
30
+ buyer_state: "TX",
31
+ buyer_zip: "78701",
32
+ description: "MCP API access — monthly subscription",
33
+ payment_id: "pi_stripe_abc123",
34
+ source: "stripe"
35
+ })
36
+ ```
37
+
38
+ Returns your tax liability, compliance status, and logs it to your account. All transactions are viewable in your [AgentTax dashboard](https://agenttax.io/?view=dashboard).
39
+
40
+ ## Stripe webhook (fully automated)
41
+
42
+ For fully automatic tax tracking without calling any tool manually, point your Stripe webhook to AgentTax:
43
+
44
+ 1. In [Stripe Dashboard → Developers → Webhooks](https://dashboard.stripe.com/webhooks), add an endpoint:
45
+ ```
46
+ https://agenttax.io/api/v1/webhooks/stripe?key=atx_live_YOUR_KEY
47
+ ```
48
+ 2. Select events: `payment_intent.succeeded`, `checkout.session.completed`, `invoice.paid`, `charge.succeeded`
49
+ 3. Optional: set `metadata.work_type` on your Stripe products (`compute` | `research` | `content` | `consulting` | `trading`) for precise tax classification
50
+
51
+ Every payment is automatically classified, taxed, and logged. No code changes required.
52
+
53
+ > Requires a billing address on the Stripe payment. Enable full address collection in your Stripe Checkout settings.
54
+
55
+ ---
56
+
57
+ ## Install
58
+
59
+ ### Claude Code
60
+
61
+ ```bash
62
+ claude mcp add agenttax -- npx @agenttax/mcp-server
63
+ export AGENTTAX_API_KEY=atx_live_your_key
64
+ ```
65
+
66
+ ### Claude Desktop / Cursor / Windsurf
67
+
68
+ Add to your MCP config file:
69
+
70
+ ```json
71
+ {
72
+ "mcpServers": {
73
+ "agenttax": {
74
+ "command": "npx",
75
+ "args": ["@agenttax/mcp-server"],
76
+ "env": {
77
+ "AGENTTAX_API_KEY": "atx_live_your_key"
78
+ }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ Demo mode works without a key (50 calls/day, no account required). Tools marked "Yes" below need `AGENTTAX_API_KEY`.
85
+
86
+ ---
87
+
88
+ ## Tools
89
+
90
+ | Tool | What it does | Key needed |
91
+ |---|---|---|
92
+ | `track_payment` | Log a payment you received and get your sales tax liability. The main tool for paid MCP servers and APIs. | Demo works; key for history |
93
+ | `calculate_tax` | Full sales/use tax calculation with jurisdiction breakdown, audit trail, confidence score and advisories | Demo works; key for full response |
94
+ | `ingest_transactions` | Bulk-log up to 100 transactions (e.g. x402 purchases), idempotent on `external_tx_id` | Yes |
95
+ | `list_transactions` | Your transaction history with running totals | Yes |
96
+ | `get_nexus_thresholds` | Each state's economic nexus thresholds (revenue / transaction count), notes and DOR source | No |
97
+ | `get_nexus` | The states you have configured nexus in | Yes |
98
+ | `configure_nexus` | Set the states you have nexus in (merge semantics) | Yes |
99
+ | `log_trade` | Log a buy/sell; sells return realized gain/loss with cost basis | Yes |
100
+ | `list_trades` | Trades you have logged | Yes |
101
+ | `export_1099_da` | Draft Form 1099-DA payload for realized digital-asset gains | Yes (Pro) |
102
+ | `get_rates` | State sales tax rates and digital-goods taxability, all 51 jurisdictions or one | No |
103
+ | `get_local_rate` | Combined state + local rate for a zip code | No |
104
+ | `get_capital_gains_rates` | State short/long-term capital gains rates | No |
105
+ | `get_pricing` | Machine-readable pricing contract | No |
106
+ | `check_health` | API health and endpoint list | No |
107
+
108
+ Read-only tools carry the MCP `readOnlyHint` annotation so clients can auto-approve them.
109
+
110
+ ### track_payment
111
+
112
+ ```
113
+ track_payment({
114
+ amount: 49.00,
115
+ buyer_state: "TX",
116
+ buyer_zip: "78701",
117
+ description: "MCP tool subscription",
118
+ payment_id: "pi_stripe_abc123",
119
+ source: "stripe"
120
+ })
121
+ ```
122
+
123
+ Returns `tax_owed`, `tax_rate`, `taxable`, `transaction_id` and a `compliance_note`. Classification comes from `description`. Sellers get $0 in states where no nexus is configured; the response says so in `nexus_warning`.
124
+
125
+ ### calculate_tax
126
+
127
+ ```
128
+ calculate_tax({
129
+ role: "buyer",
130
+ amount: 500,
131
+ buyer_state: "TX",
132
+ buyer_zip: "78701",
133
+ transaction_type: "compute",
134
+ work_type: "compute",
135
+ counterparty_id: "seller-agent-123",
136
+ is_b2b: true
137
+ })
138
+ ```
139
+
140
+ Optional fields: `seller_state` / `seller_zip` (origin-sourced intrastate sales in TX, UT, AZ, TN and some OH sales), `use_context` (Maryland B2B), `digital_content_type` (with `transaction_type: "digital_good"`), `seller_remitting`.
141
+
142
+ ### Nexus
143
+
144
+ ```
145
+ get_nexus_thresholds({ state: "NY" }) // what triggers registration
146
+ configure_nexus({ nexus: { TX: { hasNexus: true, reason: "Economic nexus" } } })
147
+ get_nexus() // what you have configured
148
+ ```
149
+
150
+ ### Capital gains and 1099-DA
151
+
152
+ ```
153
+ log_trade({ asset_symbol: "ETH", trade_type: "buy", quantity: 2, price_per_unit: 2500, asset_class: "crypto" })
154
+ log_trade({ asset_symbol: "ETH", trade_type: "sell", quantity: 1, price_per_unit: 3100, asset_class: "crypto" })
155
+ export_1099_da({ year: 2026 }) // draft, not a filed return
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Get an API Key
161
+
162
+ ```bash
163
+ curl -X POST https://agenttax.io/api/v1/auth/signup \
164
+ -H "Content-Type: application/json" \
165
+ -d '{"email": "you@example.com", "password": "securepass", "agent_name": "my-mcp-server", "agent_work_type": "compute"}'
166
+ ```
167
+
168
+ All four fields are required; `agent_work_type` is one of `compute`, `research`, `information_service`, `content`, `consulting`, `trading`. Save the `api_key.key` from the response — it's only shown once.
169
+
170
+ ## Pricing
171
+
172
+ | Tier | Price | Calls/month |
173
+ |------|-------|-------------|
174
+ | Free | $0 | 1,500 |
175
+ | Starter | $25/mo | 10,000 |
176
+ | Growth | $99/mo | 100,000 |
177
+ | Pro | $199/mo | 1,000,000 |
178
+ | x402 | $0.005/call in USDC on Base | Pay per call, no signup |
179
+
180
+ Current machine-readable pricing: `get_pricing` or `GET https://agenttax.io/api/v1/pricing`.
181
+
182
+ ## Links
183
+
184
+ - [AgentTax](https://agenttax.io)
185
+ - [API Docs](https://agenttax.io/?view=api-docs)
186
+ - [Dashboard](https://agenttax.io/?view=dashboard)
187
+ - [GitHub](https://github.com/AgentTax/agenttax-mcp)
188
+
189
+ ## License
190
+
191
+ MIT
package/index.js ADDED
@@ -0,0 +1,526 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { createRequire } from "node:module";
6
+ import { z } from "zod";
7
+
8
+ const { version: PKG_VERSION } = createRequire(import.meta.url)("./package.json");
9
+
10
+ const BASE_URL = process.env.AGENTTAX_BASE_URL || "https://agenttax.io";
11
+ const API_KEY = process.env.AGENTTAX_API_KEY || "";
12
+
13
+ // Classify work type from a plain-English description.
14
+ // Developers can describe what they sold and we auto-classify for tax purposes.
15
+ function classifyWorkType(description) {
16
+ if (!description) return "content";
17
+ const d = description.toLowerCase();
18
+ if (/\b(compute|inference|gpu|process|api[\s-]?call|token)\b/.test(d)) return "compute";
19
+ if (/\b(research|analysis|analytics|data[\s-]?(process|feed))\b/.test(d)) return "research";
20
+ if (/\b(consult|advisory|advice|audit)\b/.test(d)) return "consulting";
21
+ if (/\b(trade|trading|swap|asset)\b/.test(d)) return "trading";
22
+ return "content"; // Default: SaaS / digital service
23
+ }
24
+
25
+ const WORK_TYPE_TO_TX_TYPE = {
26
+ compute: "compute",
27
+ research: "api_access",
28
+ content: "saas",
29
+ consulting: "consulting",
30
+ trading: "digital_good",
31
+ };
32
+
33
+ async function apiCall(method, path, body = null) {
34
+ const headers = { "Content-Type": "application/json" };
35
+ if (API_KEY) headers["X-API-Key"] = API_KEY;
36
+
37
+ const opts = { method, headers };
38
+ if (body) opts.body = JSON.stringify(body);
39
+
40
+ const resp = await fetch(`${BASE_URL}${path}`, opts);
41
+ const text = await resp.text();
42
+ let parsed;
43
+ try {
44
+ parsed = text ? JSON.parse(text) : {};
45
+ } catch {
46
+ throw new Error(`AgentTax ${resp.status}: non-JSON response: ${text.slice(0, 200)}`);
47
+ }
48
+ if (!resp.ok) {
49
+ const msg = parsed?.error || parsed?.message || `HTTP ${resp.status}`;
50
+ const err = new Error(`AgentTax ${resp.status}: ${msg}`);
51
+ err.status = resp.status;
52
+ err.body = parsed;
53
+ throw err;
54
+ }
55
+ return parsed;
56
+ }
57
+
58
+ function toolError(e) {
59
+ const body = e.body ? `\n${JSON.stringify(e.body, null, 2)}` : "";
60
+ return {
61
+ content: [{ type: "text", text: `AgentTax error: ${e.message}${body}` }],
62
+ isError: true,
63
+ };
64
+ }
65
+
66
+ const STATES = [
67
+ "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY",
68
+ "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND",
69
+ "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY", "DC",
70
+ ];
71
+ const stateCode = z.preprocess((v) => (typeof v === "string" ? v.toUpperCase() : v), z.enum(STATES));
72
+ const zip5 = z.string().regex(/^\d{5}$/);
73
+
74
+ const WORK_TYPES = ["compute", "research", "information_service", "content", "consulting", "trading"];
75
+ const TRANSACTION_TYPES = [
76
+ "compute", "api_access", "data_purchase", "saas", "ai_labor", "storage",
77
+ "digital_good", "consulting", "data_processing", "cloud_infrastructure",
78
+ "ai_model_access", "marketplace_fee", "subscription", "license", "service",
79
+ ];
80
+ const DIGITAL_CONTENT_TYPES = [
81
+ "ebook", "audio", "video", "art_image", "photograph", "printable_document", "software", "data", "other",
82
+ ];
83
+
84
+ const jsonResult = (result) => ({ content: [{ type: "text", text: JSON.stringify(result, null, 2) }] });
85
+
86
+ function withQuery(path, params) {
87
+ const query = new URLSearchParams();
88
+ for (const [k, v] of Object.entries(params)) {
89
+ if (v !== undefined && v !== null && v !== "") query.set(k, String(v));
90
+ }
91
+ const qs = query.toString();
92
+ return qs ? `${path}?${qs}` : path;
93
+ }
94
+
95
+ const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
96
+ const WRITES = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
97
+
98
+ const server = new McpServer({
99
+ name: "agenttax",
100
+ version: PKG_VERSION,
101
+ });
102
+
103
+ // ── calculate_tax ──────────────────────────────────────────────────────────
104
+ server.registerTool(
105
+ "calculate_tax",
106
+ {
107
+ title: "Calculate sales or use tax",
108
+ description:
109
+ "Calculate US sales tax (seller) or use tax (buyer) for an AI agent transaction. Returns tax amount, rate, jurisdiction, " +
110
+ "audit trail, confidence score, and advisories. Sellers get $0 sales tax in states without configured nexus " +
111
+ "(see configure_nexus); a $0 total means no collection obligation, not necessarily no tax, so read nexus_warning and advisories.",
112
+ inputSchema: {
113
+ role: z.enum(["buyer", "seller"]).describe("Your role in the transaction"),
114
+ amount: z.number().positive().max(100_000_000).describe("Transaction amount in USD"),
115
+ buyer_state: stateCode.describe("2-letter US state code of the buyer (or DC)"),
116
+ transaction_type: z.enum(TRANSACTION_TYPES).describe("Type of transaction"),
117
+ counterparty_id: z.string().min(1).max(200).regex(/^[A-Za-z0-9_\-.:@]+$/)
118
+ .describe("Identifier for the other party (letters, digits and _-.:@ only)"),
119
+ buyer_zip: zip5.optional().describe("Buyer's 5-digit zip code for local rate lookup"),
120
+ seller_state: stateCode.optional().describe("Seller's 2-letter state code (needed for origin-sourced intrastate sales)"),
121
+ seller_zip: zip5.optional()
122
+ .describe("Seller's 5-digit zip. Required for TX/UT/AZ/TN origin-sourced intrastate sales and intrastate OH license/digital_good sales"),
123
+ work_type: z.enum(WORK_TYPES).optional()
124
+ .describe("What the agent's work is economically; drives per-state taxability (compute, research, information_service, content, consulting, trading)"),
125
+ is_b2b: z.boolean().optional().describe("Business-to-business transaction (triggers state B2B rules, e.g. IA, MD)"),
126
+ use_context: z.enum(["enterprise_system", "individual", "mixed"]).optional()
127
+ .describe("Maryland only, with is_b2b=true: whether the purchase is solely for use in an enterprise computer system"),
128
+ digital_content_type: z.enum(DIGITAL_CONTENT_TYPES).optional()
129
+ .describe("With transaction_type digital_good: what the download is. Several states tax e-books, music and video but not digital art or printables"),
130
+ seller_remitting: z.boolean().optional().describe("Buyer side: whether the seller is already collecting the tax"),
131
+ },
132
+ annotations: WRITES, // with an API key the calculation is logged to your history
133
+ },
134
+ async (params) => {
135
+ try {
136
+ return jsonResult(await apiCall("POST", "/api/v1/calculate", params));
137
+ } catch (e) {
138
+ return toolError(e);
139
+ }
140
+ }
141
+ );
142
+
143
+ // ── track_payment ─────────────────────────────────────────────────────────
144
+ // The primary tool for MCP tool developers. Call this after receiving any
145
+ // payment — Stripe, x402, direct, whatever. AgentTax classifies the
146
+ // transaction, calculates your sales tax liability, and logs it.
147
+ server.registerTool(
148
+ "track_payment",
149
+ {
150
+ title: "Track a payment you received",
151
+ description:
152
+ "Track a payment you received and calculate your sales tax liability. Call this after receiving any payment — Stripe, x402, or direct. " +
153
+ "Classifies the sale from its description, calculates tax owed, and logs it to your AgentTax account (requires API key for history).",
154
+ inputSchema: {
155
+ amount: z.number().positive().max(100_000_000).describe("Payment amount in USD"),
156
+ buyer_state: stateCode.describe("2-letter US state where the buyer is located (e.g. TX, NY, CA)"),
157
+ buyer_zip: zip5.optional().describe("Buyer's 5-digit zip code for local tax rates (more precise)"),
158
+ description: z.string().optional().describe("What you sold — used to classify the transaction (e.g. 'API access', 'MCP tool subscription', 'compute credits', 'AI consulting')"),
159
+ payment_id: z.string().regex(/^[A-Za-z0-9_\-.:@]{1,200}$/).optional()
160
+ .describe("Your payment reference ID (Stripe payment_intent ID, x402 receipt, invoice number); letters, digits and _-.:@ only"),
161
+ source: z.enum(["stripe", "x402", "direct", "other"]).optional().describe("Payment processor used"),
162
+ is_b2b: z.boolean().optional().describe("Buyer is a business (affects rates in MD, IA, NJ)"),
163
+ },
164
+ annotations: WRITES,
165
+ },
166
+ async (params) => {
167
+ try {
168
+ const workType = classifyWorkType(params.description);
169
+ const transactionType = WORK_TYPE_TO_TX_TYPE[workType] || "saas";
170
+ const counterpartyId = params.payment_id || `payment_${Date.now()}`;
171
+
172
+ const result = await apiCall("POST", "/api/v1/calculate", {
173
+ role: "seller",
174
+ amount: params.amount,
175
+ buyer_state: params.buyer_state,
176
+ buyer_zip: params.buyer_zip,
177
+ transaction_type: transactionType,
178
+ work_type: workType,
179
+ counterparty_id: counterpartyId,
180
+ is_b2b: params.is_b2b || false,
181
+ });
182
+
183
+ if (!result.success) {
184
+ return {
185
+ content: [{ type: "text", text: `AgentTax returned success=false:\n${JSON.stringify(result, null, 2)}` }],
186
+ isError: true,
187
+ };
188
+ }
189
+
190
+ const taxOwed = result.total_tax || 0;
191
+ const summary = {
192
+ payment_tracked: true,
193
+ source: params.source || "unspecified",
194
+ amount: params.amount,
195
+ buyer_state: params.buyer_state,
196
+ tax_owed: taxOwed,
197
+ tax_rate: result.sales_tax?.rate || result.combined_rate || 0,
198
+ taxable: taxOwed > 0,
199
+ work_type: result.work_type,
200
+ transaction_id: result.transaction_id,
201
+ nexus_warning: result.nexus_warning,
202
+ compliance_note: taxOwed > 0
203
+ ? `$${taxOwed.toFixed(2)} sales tax owed to ${params.buyer_state}. Remit to the state DOR.`
204
+ : result.nexus_warning
205
+ ? `No sales tax collected: ${params.buyer_state} is not in your configured nexus states. Use configure_nexus if you have nexus there.`
206
+ : `No sales tax owed in ${params.buyer_state} for this transaction type.`,
207
+ };
208
+
209
+ return jsonResult(summary);
210
+ } catch (e) {
211
+ return toolError(e);
212
+ }
213
+ }
214
+ );
215
+
216
+ // ── ingest_transactions ────────────────────────────────────────────────────
217
+ const ingestRecord = z.object({
218
+ role: z.enum(["buyer", "seller"]).optional().describe("Default seller"),
219
+ amount: z.number().positive().max(100_000_000),
220
+ buyer_state: stateCode,
221
+ buyer_zip: zip5.optional(),
222
+ seller_state: stateCode.optional(),
223
+ seller_zip: zip5.optional(),
224
+ transaction_type: z.enum(TRANSACTION_TYPES).optional(),
225
+ work_type: z.enum(WORK_TYPES).optional(),
226
+ counterparty_id: z.string().min(1).max(200).regex(/^[A-Za-z0-9_\-.:@]+$/).optional(),
227
+ is_b2b: z.boolean().optional(),
228
+ external_tx_id: z.string().max(200).optional().describe("Your ID for the payment (e.g. x402 receipt); makes ingest idempotent"),
229
+ }).passthrough();
230
+
231
+ server.registerTool(
232
+ "ingest_transactions",
233
+ {
234
+ title: "Bulk-log x402 purchases",
235
+ description:
236
+ "Bulk-log up to 100 transactions (e.g. purchases from third-party x402 sellers) into your AgentTax history in one call. " +
237
+ "Idempotent on external_tx_id. Requires API key.",
238
+ inputSchema: {
239
+ records: z.array(ingestRecord).min(1).max(100).describe("Transactions to log (max 100)"),
240
+ },
241
+ annotations: WRITES,
242
+ },
243
+ async ({ records }) => {
244
+ try {
245
+ return jsonResult(await apiCall("POST", "/api/v1/transactions/ingest", records));
246
+ } catch (e) {
247
+ return toolError(e);
248
+ }
249
+ }
250
+ );
251
+
252
+ // ── list_transactions ──────────────────────────────────────────────────────
253
+ server.registerTool(
254
+ "list_transactions",
255
+ {
256
+ title: "List your transactions",
257
+ description: "List your logged transactions with a running summary (seller/buyer counts, total taxable, total tax). Requires API key.",
258
+ inputSchema: {
259
+ role: z.enum(["seller", "buyer"]).optional(),
260
+ state: stateCode.optional().describe("Filter to one state"),
261
+ since: z.string().optional().describe("ISO 8601 date or date-time lower bound, e.g. 2026-08-01"),
262
+ limit: z.number().int().min(1).max(1000).optional(),
263
+ },
264
+ annotations: READ_ONLY,
265
+ },
266
+ async (params) => {
267
+ try {
268
+ return jsonResult(await apiCall("GET", withQuery("/api/v1/transactions", params)));
269
+ } catch (e) {
270
+ return toolError(e);
271
+ }
272
+ }
273
+ );
274
+
275
+ // ── log_trade ──────────────────────────────────────────────────────────────
276
+ server.registerTool(
277
+ "log_trade",
278
+ {
279
+ title: "Log a trade for capital gains",
280
+ description:
281
+ "Log a buy or sell trade for capital gains tracking. Sell trades return realized gain/loss with cost basis, holding period, " +
282
+ "and federal/state estimates. Feeds the 1099-DA export. Requires API key.",
283
+ inputSchema: {
284
+ asset_symbol: z.string().regex(/^[A-Za-z0-9._\-]{1,50}$/).describe("Asset ticker: equity (AAPL), crypto (BTC), or token (COMPUTE-TOKEN)"),
285
+ trade_type: z.enum(["buy", "sell"]).describe("Buy or sell"),
286
+ quantity: z.number().positive().describe("Number of units"),
287
+ price_per_unit: z.number().min(0).describe("Price per unit in USD"),
288
+ fee_amount: z.number().min(0).optional().describe("Fees in USD (default 0)"),
289
+ trade_date: z.string().optional().describe("ISO 8601 trade timestamp; defaults to now"),
290
+ accounting_method: z.enum(["fifo", "lifo", "specific_id"]).optional().describe("Cost basis method (default: fifo)"),
291
+ specific_lot_id: z.string().optional().describe("Lot ID, required when accounting_method is specific_id"),
292
+ asset_class: z.enum(["stock", "security", "securities", "equity", "digital_asset", "crypto"]).optional()
293
+ .describe("stock/security/equity enables wash-sale tracking; digital_asset/crypto do not"),
294
+ resident_state: stateCode.optional().describe("Resident state for the state capital gains estimate"),
295
+ tax_entity_type: z.enum(["individual", "c_corp", "pass_through"]).optional(),
296
+ filing_status: z.enum(["single", "mfj", "mfs", "hoh"]).optional(),
297
+ estimated_annual_income: z.number().min(0).optional().describe("Enables bracket-aware federal estimate and NIIT detection"),
298
+ notes: z.string().max(1000).optional().describe("Free-text notes about the trade"),
299
+ },
300
+ annotations: WRITES,
301
+ },
302
+ async (params) => {
303
+ try {
304
+ return jsonResult(await apiCall("POST", "/api/v1/trades", params));
305
+ } catch (e) {
306
+ return toolError(e);
307
+ }
308
+ }
309
+ );
310
+
311
+ // ── list_trades ────────────────────────────────────────────────────────────
312
+ server.registerTool(
313
+ "list_trades",
314
+ {
315
+ title: "List logged trades",
316
+ description: "List trades you have logged, with realized gains on sells. Requires API key.",
317
+ inputSchema: {
318
+ asset_symbol: z.string().optional(),
319
+ trade_type: z.enum(["buy", "sell"]).optional(),
320
+ limit: z.number().int().min(1).max(500).optional().describe("Default 50"),
321
+ offset: z.number().int().min(0).optional(),
322
+ },
323
+ annotations: READ_ONLY,
324
+ },
325
+ async (params) => {
326
+ try {
327
+ return jsonResult(await apiCall("GET", withQuery("/api/v1/trades", params)));
328
+ } catch (e) {
329
+ return toolError(e);
330
+ }
331
+ }
332
+ );
333
+
334
+ // ── export_1099_da ─────────────────────────────────────────────────────────
335
+ server.registerTool(
336
+ "export_1099_da",
337
+ {
338
+ title: "Export draft Form 1099-DA",
339
+ description:
340
+ "Export a DRAFT IRS Form 1099-DA payload for realized digital-asset gains from your logged trades: payer and recipient blocks, " +
341
+ "per-trade lines (boxes 1a–5), and a short/long-term summary. Draft only; not a filed return. Requires API key.",
342
+ inputSchema: {
343
+ year: z.number().int().min(2020).max(2030).optional().describe("Tax year; defaults to the current year"),
344
+ },
345
+ annotations: READ_ONLY,
346
+ },
347
+ async (params) => {
348
+ try {
349
+ return jsonResult(await apiCall("GET", withQuery("/api/v1/export/1099-da", params)));
350
+ } catch (e) {
351
+ return toolError(e);
352
+ }
353
+ }
354
+ );
355
+
356
+ // ── get_rates ──────────────────────────────────────────────────────────────
357
+ server.registerTool(
358
+ "get_rates",
359
+ {
360
+ title: "Get state sales tax rates",
361
+ description: "Get US state sales tax rates with digital-goods taxability, SaaS notes, and verification metadata, for all 51 jurisdictions or one state. No API key needed.",
362
+ inputSchema: {
363
+ state: stateCode.optional().describe("2-letter state code for a single state. Omit for all states."),
364
+ format: z.enum(["default", "compact", "verified"]).optional()
365
+ .describe("default: full; compact: machine-optimized; verified: with verification details"),
366
+ explain: z.boolean().optional().describe("Include human-readable explanations for the rate and taxability"),
367
+ },
368
+ annotations: READ_ONLY,
369
+ },
370
+ async ({ state, format, explain }) => {
371
+ try {
372
+ return jsonResult(await apiCall("GET", withQuery("/api/v1/rates", { state, format, explain: explain ? "true" : undefined })));
373
+ } catch (e) {
374
+ return toolError(e);
375
+ }
376
+ }
377
+ );
378
+
379
+ // ── get_local_rate ─────────────────────────────────────────────────────────
380
+ server.registerTool(
381
+ "get_local_rate",
382
+ {
383
+ title: "Get combined rate for a zip code",
384
+ description: "Get the combined state + local sales tax rate for a US zip code, with the breakdown and self-administered locality flags (e.g. Colorado home-rule cities). No API key needed.",
385
+ inputSchema: {
386
+ zip: zip5.describe("5-digit US zip code"),
387
+ },
388
+ annotations: READ_ONLY,
389
+ },
390
+ async ({ zip }) => {
391
+ try {
392
+ return jsonResult(await apiCall("GET", withQuery("/api/v1/rates/local", { zip })));
393
+ } catch (e) {
394
+ return toolError(e);
395
+ }
396
+ }
397
+ );
398
+
399
+ // ── get_capital_gains_rates ────────────────────────────────────────────────
400
+ server.registerTool(
401
+ "get_capital_gains_rates",
402
+ {
403
+ title: "Get state capital gains rates",
404
+ description: "Get state capital gains rates (short and long term) with federal context and per-state caveats, for all states or one. No API key needed.",
405
+ inputSchema: {
406
+ state: stateCode.optional().describe("2-letter state code. Omit for all states."),
407
+ explain: z.boolean().optional().describe("With state: add an explanation block"),
408
+ },
409
+ annotations: READ_ONLY,
410
+ },
411
+ async ({ state, explain }) => {
412
+ try {
413
+ return jsonResult(await apiCall("GET", withQuery("/api/v1/rates/capital-gains", { state, explain: explain ? "true" : undefined })));
414
+ } catch (e) {
415
+ return toolError(e);
416
+ }
417
+ }
418
+ );
419
+
420
+ // ── get_nexus_thresholds ───────────────────────────────────────────────────
421
+ server.registerTool(
422
+ "get_nexus_thresholds",
423
+ {
424
+ title: "Get economic nexus thresholds",
425
+ description:
426
+ "Get each state's economic nexus threshold (revenue and transaction count), whether it has sales tax, marketplace-facilitator and " +
427
+ "origin-sourcing rules, notes, and the state DOR source link. Use it to see where your sales could require registration. No API key needed.",
428
+ inputSchema: {
429
+ state: stateCode.optional().describe("2-letter state code. Omit for all 51 jurisdictions."),
430
+ },
431
+ annotations: READ_ONLY,
432
+ },
433
+ async ({ state }) => {
434
+ try {
435
+ return jsonResult(await apiCall("GET", withQuery("/api/jurisdictions/supported", { code: state })));
436
+ } catch (e) {
437
+ return toolError(e);
438
+ }
439
+ }
440
+ );
441
+
442
+ // ── get_nexus ──────────────────────────────────────────────────────────────
443
+ server.registerTool(
444
+ "get_nexus",
445
+ {
446
+ title: "Get your configured nexus states",
447
+ description: "List the states where you have told AgentTax you have nexus, with each state's rate and how nexus monitoring measures thresholds. Requires API key.",
448
+ inputSchema: {},
449
+ annotations: READ_ONLY,
450
+ },
451
+ async () => {
452
+ try {
453
+ return jsonResult(await apiCall("GET", "/api/v1/nexus"));
454
+ } catch (e) {
455
+ return toolError(e);
456
+ }
457
+ }
458
+ );
459
+
460
+ // ── configure_nexus ────────────────────────────────────────────────────────
461
+ server.registerTool(
462
+ "configure_nexus",
463
+ {
464
+ title: "Configure your nexus states",
465
+ description:
466
+ "Set which US states you have economic nexus in. Sellers must configure nexus to get non-zero sales tax results. " +
467
+ "Merge semantics: only the states you send change; send hasNexus:false to remove one. Requires API key.",
468
+ inputSchema: {
469
+ nexus: z.record(
470
+ stateCode,
471
+ z.object({
472
+ hasNexus: z.boolean().describe("Whether you have nexus in this state"),
473
+ reason: z.string().optional().describe("Reason for nexus (e.g. 'Economic nexus — over $100K revenue')"),
474
+ })
475
+ ).describe("State codes as keys, e.g. { TX: { hasNexus: true, reason: '...' } }"),
476
+ },
477
+ annotations: { ...WRITES, idempotentHint: true },
478
+ },
479
+ async (params) => {
480
+ try {
481
+ return jsonResult(await apiCall("POST", "/api/v1/nexus", params));
482
+ } catch (e) {
483
+ return toolError(e);
484
+ }
485
+ }
486
+ );
487
+
488
+ // ── get_pricing ────────────────────────────────────────────────────────────
489
+ server.registerTool(
490
+ "get_pricing",
491
+ {
492
+ title: "Get AgentTax pricing",
493
+ description: "Get AgentTax's machine-readable pricing contract: tiers, call limits, and the x402 per-call price. No API key needed.",
494
+ inputSchema: {},
495
+ annotations: READ_ONLY,
496
+ },
497
+ async () => {
498
+ try {
499
+ return jsonResult(await apiCall("GET", "/api/v1/pricing"));
500
+ } catch (e) {
501
+ return toolError(e);
502
+ }
503
+ }
504
+ );
505
+
506
+ // ── check_health ───────────────────────────────────────────────────────────
507
+ server.registerTool(
508
+ "check_health",
509
+ {
510
+ title: "Check API health",
511
+ description: "Check AgentTax API health, available endpoints, pricing tiers, and registry validation status.",
512
+ inputSchema: {},
513
+ annotations: READ_ONLY,
514
+ },
515
+ async () => {
516
+ try {
517
+ return jsonResult(await apiCall("GET", "/api/v1/health"));
518
+ } catch (e) {
519
+ return toolError(e);
520
+ }
521
+ }
522
+ );
523
+
524
+ // ── Start server ───────────────────────────────────────────────────────────
525
+ const transport = new StdioServerTransport();
526
+ await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "@agenttax/mcp-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.1",
4
+ "mcpName": "io.github.AgentTax/agenttax-mcp",
5
+ "description": "MCP server for AgentTax — US sales tax, nexus thresholds, capital gains and 1099-DA tools for AI agents",
6
+ "main": "index.js",
7
+ "type": "module",
8
+ "bin": {
9
+ "agenttax-mcp": "index.js"
10
+ },
11
+ "files": [
12
+ "index.js",
13
+ "README.md",
14
+ "LICENSE",
15
+ "server.json"
16
+ ],
17
+ "scripts": {
18
+ "start": "node index.js"
19
+ },
20
+ "keywords": [
21
+ "1099-da",
22
+ "agenttax",
23
+ "ai-agents",
24
+ "capital-gains",
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "nexus",
28
+ "sales-tax",
29
+ "tax",
30
+ "use-tax",
31
+ "x402"
32
+ ],
33
+ "author": "Agentic Tax Solutions LLC",
34
+ "license": "MIT",
35
+ "homepage": "https://agenttax.io",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/AgentTax/agenttax-mcp.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/AgentTax/agenttax-mcp/issues"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.28.0",
45
+ "zod": "^3.25.0"
46
+ },
47
+ "engines": {
48
+ "node": ">=18"
49
+ }
50
+ }
package/server.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.AgentTax/agenttax-mcp",
4
+ "title": "AgentTax",
5
+ "description": "US sales/use tax, nexus thresholds, capital gains and 1099-DA tools for AI agent transactions.",
6
+ "version": "1.1.1",
7
+ "websiteUrl": "https://agenttax.io",
8
+ "repository": {
9
+ "url": "https://github.com/AgentTax/agenttax-mcp",
10
+ "source": "github"
11
+ },
12
+ "packages": [
13
+ {
14
+ "registryType": "npm",
15
+ "identifier": "@agenttax/mcp-server",
16
+ "version": "1.1.1",
17
+ "transport": {
18
+ "type": "stdio"
19
+ },
20
+ "environmentVariables": [
21
+ {
22
+ "name": "AGENTTAX_API_KEY",
23
+ "description": "AgentTax API key (free tier at agenttax.io). Optional: without it the server runs in demo mode, 50 calls/day.",
24
+ "isRequired": false,
25
+ "isSecret": true
26
+ }
27
+ ]
28
+ }
29
+ ]
30
+ }