ip-search-mcp 0.0.0-stage → 0.1.0

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.
Files changed (73) hide show
  1. package/ARCHITECTURE.md +90 -0
  2. package/CREDENTIALS.md +107 -0
  3. package/LICENSE +21 -0
  4. package/LIMITS.md +65 -0
  5. package/README.md +117 -2
  6. package/dist/src/axi/args.js +83 -0
  7. package/dist/src/axi/args.js.map +1 -0
  8. package/dist/src/axi/boot.js +26 -0
  9. package/dist/src/axi/boot.js.map +1 -0
  10. package/dist/src/axi/client.js +58 -0
  11. package/dist/src/axi/client.js.map +1 -0
  12. package/dist/src/axi/commands.js +507 -0
  13. package/dist/src/axi/commands.js.map +1 -0
  14. package/dist/src/axi/errors.js +18 -0
  15. package/dist/src/axi/errors.js.map +1 -0
  16. package/dist/src/axi/help.js +39 -0
  17. package/dist/src/axi/help.js.map +1 -0
  18. package/dist/src/axi/index.js +64 -0
  19. package/dist/src/axi/index.js.map +1 -0
  20. package/dist/src/axi/toon.js +42 -0
  21. package/dist/src/axi/toon.js.map +1 -0
  22. package/dist/src/cli.js +76 -0
  23. package/dist/src/cli.js.map +1 -0
  24. package/dist/src/config.js +98 -0
  25. package/dist/src/config.js.map +1 -0
  26. package/dist/src/domain/assessment.js +251 -0
  27. package/dist/src/domain/assessment.js.map +1 -0
  28. package/dist/src/domain/claims.js +297 -0
  29. package/dist/src/domain/claims.js.map +1 -0
  30. package/dist/src/domain/disclosure.js +117 -0
  31. package/dist/src/domain/disclosure.js.map +1 -0
  32. package/dist/src/domain/drawings/geometry.js +437 -0
  33. package/dist/src/domain/drawings/geometry.js.map +1 -0
  34. package/dist/src/domain/drawings/render.js +141 -0
  35. package/dist/src/domain/drawings/render.js.map +1 -0
  36. package/dist/src/domain/drawings/rules.js +53 -0
  37. package/dist/src/domain/drawings/rules.js.map +1 -0
  38. package/dist/src/domain/drawings/validate.js +235 -0
  39. package/dist/src/domain/drawings/validate.js.map +1 -0
  40. package/dist/src/domain/spec.js +161 -0
  41. package/dist/src/domain/spec.js.map +1 -0
  42. package/dist/src/errors.js +35 -0
  43. package/dist/src/errors.js.map +1 -0
  44. package/dist/src/http.js +224 -0
  45. package/dist/src/http.js.map +1 -0
  46. package/dist/src/index.js +16 -0
  47. package/dist/src/index.js.map +1 -0
  48. package/dist/src/numbers.js +142 -0
  49. package/dist/src/numbers.js.map +1 -0
  50. package/dist/src/providers/index.js +390 -0
  51. package/dist/src/providers/index.js.map +1 -0
  52. package/dist/src/providers/odp.js +175 -0
  53. package/dist/src/providers/odp.js.map +1 -0
  54. package/dist/src/providers/ops.js +178 -0
  55. package/dist/src/providers/ops.js.map +1 -0
  56. package/dist/src/providers/ppubs.js +190 -0
  57. package/dist/src/providers/ppubs.js.map +1 -0
  58. package/dist/src/providers/tmsearch.js +158 -0
  59. package/dist/src/providers/tmsearch.js.map +1 -0
  60. package/dist/src/providers/tsdr.js +267 -0
  61. package/dist/src/providers/tsdr.js.map +1 -0
  62. package/dist/src/server.js +726 -0
  63. package/dist/src/server.js.map +1 -0
  64. package/dist/src/types.js +2 -0
  65. package/dist/src/types.js.map +1 -0
  66. package/dist/src/util/cache.js +37 -0
  67. package/dist/src/util/cache.js.map +1 -0
  68. package/dist/src/util/delay.js +6 -0
  69. package/dist/src/util/delay.js.map +1 -0
  70. package/dist/src/util/rate.js +49 -0
  71. package/dist/src/util/rate.js.map +1 -0
  72. package/package.json +49 -4
  73. package/skills/ip-axi/SKILL.md +60 -0
@@ -0,0 +1,90 @@
1
+ # Architecture
2
+
3
+ ## Shape
4
+
5
+ ```
6
+ MCP client
7
+ |
8
+ v
9
+ src/server.ts (tool registration, zod input schemas, output formatting)
10
+ |
11
+ +-- domain/ pure logic, no network
12
+ | assessment concept screen (101 risks, CPC/Nice, closest hits)
13
+ | claims claim parsing, dependency graph, 112 lint
14
+ | disclosure 102(b)(1) clock arithmetic
15
+ | drawings/ rules (1.84), geometry (SVG parse), render, validate
16
+ | spec specification assembly, claim scaffold, packet
17
+ |
18
+ +-- providers/ one module per data source
19
+ | odp tsdr ops ppubs (opt-in) tmsearch (opt-in) index (sqlite)
20
+ |
21
+ +-- http.ts fetch, retry, rate limit, cache, credential host check
22
+ +-- numbers.ts patent and trademark identifier normalization
23
+ +-- config.ts env parsing, provider flags, credential state
24
+ ```
25
+
26
+ Domain modules never import providers. Providers never format user-facing text. The server composes them.
27
+
28
+ `ip-axi` sits beside the server as a presentation layer. It creates the same server in process, connects over an in-memory MCP transport, and calls tools through the MCP client, so command behavior cannot drift from tool behavior. Only formatting, flag parsing, and exit codes live in `src/axi/`.
29
+
30
+ ## Provider boundaries
31
+
32
+ | Provider | Host | Auth | Stability |
33
+ |----------|------|------|-----------|
34
+ | ODP | `api.uspto.gov` | `X-API-KEY` | Documented API, docs and swagger behind a USPTO.gov login |
35
+ | TSDR | `tsdrapi.uspto.gov` | `USPTO-API-KEY` | Documented, identifier lookup only |
36
+ | EPO OPS | `ops.epo.org` | OAuth2 client credentials | Documented, fair-use quota |
37
+ | PPUBS | `ppubs.uspto.gov` | session token | Undocumented, opt-in |
38
+ | tmsearch | `tmsearch.uspto.gov` | WAF cookie (optional) | Undocumented, opt-in, backend base discovered at runtime |
39
+ | index | local file | none | Derived from ODP bulk XML |
40
+
41
+ Each provider normalizes into `PatentHit`, `TrademarkHit`, `PatentDocument`, or a raw record, and every hit carries `Provenance`: provider, route, retrieval time, source id, and an `undocumented` flag when it applies.
42
+
43
+ ## Credential isolation
44
+
45
+ `http.ts` refuses to send a request whose host is not the provider's configured host. Keys are attached inside the provider module, never passed in by a tool argument, never logged, and redacted in error details. ODP and TSDR use separate keys; the server never substitutes one for the other.
46
+
47
+ ## Rate limits
48
+
49
+ | Bucket | Limit | Basis |
50
+ |--------|-------|-------|
51
+ | `tsdr:default` | 60/min | USPTO published limit |
52
+ | TSDR PDF/ZIP | 4/min | Not wired: this server links to bundles rather than downloading them |
53
+ | `odp:default` | 60/min | Conservative; ODP publishes limits on its own page |
54
+ | `ops:default` | 60/min | EPO fair-use charter |
55
+ | `ppubs:default` | 30/min | Undocumented; deliberately conservative |
56
+ | `tmsearch:default` | 30/min | Undocumented; deliberately conservative |
57
+
58
+ The limiter is a sliding window shared per bucket across the process. Retries honor `Retry-After` and use exponential backoff for 429 and 5xx.
59
+
60
+ ## Caching
61
+
62
+ File cache under `~/.cache/ip-search-mcp`, keyed by a SHA-256 of provider, URL, and body, with a per-call TTL (5 minutes for searches, 1 minute for case status, 30 minutes to 1 hour for metadata and bibliographic data). Writes are atomic (temp file then rename). Cache hits are marked `cached: true` in provenance.
63
+
64
+ ## Drawing pipeline
65
+
66
+ 1. `renderSheet` emits an SVG in millimetre coordinates: A4 or letter, fixed margins, no sight frame, scan targets in two catercorner corners, header indicia inside the top margin, sheet numbering, and reference characters at 4 mm.
67
+ 2. `geometry.ts` parses any SVG back into primitives with transforms applied: rect, line, circle, ellipse, polygon, polyline, path (M/L/H/V/C/Q/S/T/A/Z with curve sampling), and text with estimated bounds. Unsupported constructs are reported, not guessed.
68
+ 3. `validate.ts` checks the parsed primitives against the encoded rules and returns findings with rule ids and citations, plus a list of things geometry cannot verify (part identity across views, projection lines, print reduction, shading correctness).
69
+
70
+ The rule set is data in `rules.ts`, sourced from eCFR title 37 section 1.84 on 2026-11-10. `drawing_rules` returns it with the source URL and retrieval date so a reader can check freshness.
71
+
72
+ ## Claims pipeline
73
+
74
+ `parseClaims` splits numbered claims, extracts dependency phrases (including ranges and "or"/"and"), and builds a dependency graph. Lint covers sequential numbering, forward references, multiple dependent claims built on multiple dependent claims, alternative form, antecedent basis using transitive ancestors, means-plus-function triggers, relative terms, specification support, abstract length, and title limits. Antecedent basis uses a head-final noun heuristic; it is a screening aid, not a parser.
75
+
76
+ ## Drafting
77
+
78
+ `draftSpecification` assembles sections in the order examiners expect and reports every section the caller did not supply as a gap. It never invents content. `draftClaims` builds an independent claim from supplied elements and never adds limitations. Every artifact starts with `DRAFT - NOT FOR FILING`.
79
+
80
+ There is no submission code path anywhere in the server: no EFS-Web, Patent Center, TEAS, or EPO Online Filing client, no signature handling, and no credentials for any filing system.
81
+
82
+ ## Local index
83
+
84
+ `node:sqlite` with FTS5 over `patents` and `trademarks` tables. Ingest walks a directory of XML, detects patent grant, published application, and trademark record shapes, extracts text subtrees (abstract, claims, description, goods and services), and writes both the row and its FTS entry. Search returns `bm25` order with the `index` provider label.
85
+
86
+ One file can hold thousands of records: `<case-file>` blocks are split and parsed individually so a daily TDXF file indexes every application and memory stays bounded. Patent grant and application XML files are parsed whole. Limits: ingest is not incremental across releases beyond `INSERT OR REPLACE`; field mapping follows the documented TDXF and ICE names and must be checked against the data dictionary for a given snapshot. Measured: 7,330 trademark records from a 143 MB file in 60 seconds.
87
+
88
+ ## Failure behavior
89
+
90
+ Errors are typed (`CREDENTIAL_MISSING`, `PROVIDER_DISABLED`, `RATE_LIMITED`, `UPSTREAM`, `NOT_FOUND`, `INVALID_INPUT`, `UNSUPPORTED_FORMAT`). Tools return them as `isError` results with an actionable hint. Nothing crashes the process, and no error is converted into an empty success.
package/CREDENTIALS.md ADDED
@@ -0,0 +1,107 @@
1
+ # Credentials
2
+
3
+ This server never asks a tool caller for a key. Keys come from the process environment, and each key is bound to one host.
4
+
5
+ ## Where to put a key
6
+
7
+ Pick one location per use.
8
+
9
+ **Agents and MCP clients.** Fill the `env` block of the `ip-search-mcp` entry in `~/.agents/mcp.json`, then restart the harness so the server is respawned:
10
+
11
+ ```json
12
+ "ip-search-mcp": {
13
+ "command": "node",
14
+ "args": ["C:/Users/brand/Desktop/ip-search-mcp/dist/src/index.js"],
15
+ "env": {
16
+ "USPTO_ODP_API_KEY": "your-odp-key",
17
+ "USPTO_TSDR_API_KEY": "your-tsdr-key"
18
+ }
19
+ }
20
+ ```
21
+
22
+ Empty strings are treated as unset, so leaving a slot blank is safe.
23
+
24
+ **Your shell, for CLI runs and tests.** Set the variables for the session, or persist them to the user profile:
25
+
26
+ ```powershell
27
+ $env:USPTO_ODP_API_KEY = "your-odp-key"
28
+ $env:USPTO_TSDR_API_KEY = "your-tsdr-key"
29
+ [Environment]::SetEnvironmentVariable("USPTO_ODP_API_KEY", "your-odp-key", "User")
30
+ ```
31
+
32
+ **A local `.env` file.** The server does not load `.env` by itself. Pass it explicitly:
33
+
34
+ ```bash
35
+ node --env-file=.env dist/src/index.js
36
+ ```
37
+
38
+ `.env` is gitignored. Never put a key in a tool argument, a prompt, a commit, or a skill file.
39
+
40
+ **Verify.** This prints booleans only, never key values:
41
+
42
+ ```bash
43
+ node dist/src/index.js status
44
+ ```
45
+
46
+ A provider shows `configured: true` only when its full set of variables is non-empty.
47
+
48
+ ## USPTO Open Data Portal (ODP)
49
+
50
+ Used for: patent file wrapper search, metadata, continuity, documents, bulk products.
51
+
52
+ 1. Create a USPTO.gov account and enable multi-factor authentication.
53
+ 2. Sign in at https://data.uspto.gov.
54
+ 3. Open Manage API Key and create a key.
55
+ 4. Keep the four profile fields the portal requires current; keys for incomplete profiles can be deactivated.
56
+ 5. Set `USPTO_ODP_API_KEY`.
57
+
58
+ Sent only to `api.uspto.gov` as `X-API-KEY`. ODP documentation and swagger sit behind the same sign-in.
59
+
60
+ ## TSDR (trademark status and documents)
61
+
62
+ Used for: authoritative case status, documents, mark images.
63
+
64
+ 1. Create or sign in to a USPTO.gov account.
65
+ 2. Open https://account.uspto.gov/api-manager/ and create a TSDR key.
66
+ 3. Set `USPTO_TSDR_API_KEY`.
67
+
68
+ Sent only to `tsdrapi.uspto.gov` as `USPTO-API-KEY`. This is a different credential from the ODP key. TSDR is an identifier lookup, not a search engine: you need a serial, registration, IR, or reference number.
69
+
70
+ ## EPO Open Patent Services (OPS)
71
+
72
+ Used for: worldwide bibliographic data, full text, families, images.
73
+
74
+ 1. Register at https://developers.epo.org.
75
+ 2. Create an application to get a consumer key and secret.
76
+ 3. Set `EPO_OPS_CLIENT_ID` and `EPO_OPS_CLIENT_SECRET`.
77
+
78
+ OAuth2 client-credentials token against `ops.epo.org`; the token is cached until shortly before expiry. Usage is governed by the EPO fair use charter.
79
+
80
+ ## Undocumented surfaces (off by default)
81
+
82
+ | Variable | Effect |
83
+ |----------|--------|
84
+ | `IP_ALLOW_UNOFFICIAL=1` | Enables Patent Public Search and the Trademark Search backend adapters |
85
+ | `TMSEARCH_WAF_TOKEN` | Optional `aws-waf-token` cookie copied from a browser session |
86
+
87
+ Both surfaces have no published contract, can change shape without notice, and sit behind terms of use that permit the USPTO to cut off automated access. Results from them are labeled `undocumented: true` in provenance.
88
+
89
+ ## Operational settings
90
+
91
+ | Variable | Default | Purpose |
92
+ |----------|---------|---------|
93
+ | `IP_CACHE_DIR` | `~/.cache/ip-search-mcp` | File cache |
94
+ | `IP_INDEX_DB` | `~/.cache/ip-search-mcp/ip-index.sqlite` | Local FTS index |
95
+ | `IP_TIMEOUT_MS` | `30000` | Per-request timeout |
96
+ | `IP_MAX_RETRIES` | `2` | Retry count for 429, 5xx, and network errors |
97
+ | `IP_USER_AGENT` | `ip-search-mcp/0.1.0 (stdio MCP server)` | User-Agent header |
98
+ | `PPUBS_BASE_URL`, `TMSEARCH_BASE_URL`, `USPTO_ODP_BASE_URL`, `USPTO_TSDR_BASE_URL`, `EPO_OPS_BASE_URL` | provider defaults | Host overrides for testing only |
99
+
100
+ Overriding a base URL does not let a key travel to the new host: the host check compares against the configured base, and keys are attached after that check.
101
+
102
+ ## Handling rules
103
+
104
+ - Do not pass keys through tool arguments, prompts, or committed files.
105
+ - Do not copy a key from one provider's console to another provider's variable.
106
+ - Rotate a key if it appears in a transcript, log, or error message.
107
+ - Error details redact `authorization`, `x-api-key`, `uspto-api-key`, and `x-access-token`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brandon Phillips
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/LIMITS.md ADDED
@@ -0,0 +1,65 @@
1
+ # Limits
2
+
3
+ ## What no implementation can fix
4
+
5
+ - **No official USPTO trademark search API exists.** TSDR is identifier lookup only. The Trademark Search web app has no documented API; its backend is used by the UI and carries terms-of-use risk. Real discovery requires either that backend, a local index over the trademark daily XML, or a paid third party.
6
+ - **PatentsView is gone.** The legacy API was retired in May 2025 and the Search API shut down in March 2026; its data moved to ODP bulk products. Any guide pointing at `api.patentsview.org` or `search.patentsview.org` is stale.
7
+ - **ODP API documentation requires a USPTO.gov sign-in.** Endpoint paths in this server come from the published OpenAPI surface and a bound client; response shapes for fields not exercised here can differ and surface as raw payloads rather than normalized hits.
8
+ - **EPO OPS is not a US legal record.** It is bibliographic and family data; verify status and documents against USPTO records.
9
+
10
+ ## Verified against live providers, 2026-11-10
11
+
12
+ Observed with real USPTO keys, then encoded in the tests and provider code.
13
+
14
+ - **ODP free-text search works.** A plain-language phrase returned 425,510 matching applications with title, filing date, inventor, assignee, status, and application number normalized correctly.
15
+ - **ODP answers a zero-hit search with HTTP 404** and `"No matching records found"`. The provider converts that to an empty result set, not an error.
16
+ - **ODP date-range filtering is not verified.** Four documented shapes (`rangeFilters` with ISO dates, compact dates, and both `filingDate` and `applicationFilingDate`; `filters` with range strings) all returned 404 or 400. `patents_search` therefore rejects `from`/`to` for the ODP provider and routes date-bounded queries to EPO OPS or the local index. ODP sort and filter field names are likewise unverified.
17
+ - **ODP documents live under `documentBag[].downloadOptionBag[]`.** The provider flattens them to code, description, official date, page count, and PDF URL. A 2026 application returned 53 documents.
18
+ - **TSDR returns namespaced ST.96 XML.** With namespace prefixes removed, `ApplicationNumberText`, `RegistrationNumber`, `MarkVerbalElementText`, `NationalStatusExternalDescriptionText`, `ApplicationDate`, `RegistrationDate`, `ClassNumber`, and `EntityName` map correctly. Serial 87454105 returned DOCKER, ACTIVE, registration 6096739, Docker, Inc., class 9, and 57 documents.
19
+ - **Bulk ingest works at record scale.** A real trademark daily file (7.7 MB compressed, 143 MB XML, 7,330 case-file records) indexed in 60 seconds with zero skips and searchable rows. Each case-file is parsed separately, so daily files no longer collapse to one record.
20
+ - **Not yet exercised live:** EPO OPS (no credentials), Patent Public Search, and the Trademark Search backend (both opt-in).
21
+
22
+ ## Search quality
23
+
24
+ - Text search is not classification search. A keyword result set can miss an entire CPC class.
25
+ - Term overlap between a concept and a reference orders references for human review; it does not show disclosure.
26
+ - Ranking is textual. It is not a likelihood-of-confusion score for trademarks and not an obviousness judgment for patents.
27
+ - A zero-result search is recorded as a zero-result search. It is never evidence of novelty.
28
+
29
+ ## Drawing validation
30
+
31
+ Checked: sheet size, margins and sight, frames, ink color, stroke weight, minimum text height, reference-character form and line crossings, view overlap, figure labels, prohibited scale text, description coverage of reference characters, sheet numbering, hatch spacing.
32
+
33
+ Not checked, because geometry cannot decide it:
34
+
35
+ - Whether the same reference character designates the same part across views.
36
+ - Whether views are connected by projection lines or contain center lines.
37
+ - Whether the scale survives two-thirds reproduction.
38
+ - Whether shading correctly communicates surface form.
39
+ - Whether a photograph is the only practicable medium (1.84(b)).
40
+
41
+ Formats: SVG only. PDF and PNG are not parsed; convert a sheet to SVG or validate the generated SVG. A PDF checklist is a practitioner task.
42
+
43
+ ## Drafting
44
+
45
+ - `application_draft` assembles supplied content and reports gaps. It does not invent background, embodiments, or claim limitations.
46
+ - `claims_draft` writes one independent claim from supplied elements. Dependent claims and scope decisions are human work.
47
+ - Claim lint is heuristic. Antecedent basis uses head-final noun detection, specification support uses content-word overlap, and both can miss or over-flag. Findings are screening aids with citations, not rejections.
48
+ - No ADS, declaration, assignment, or fee calculation is produced.
49
+
50
+ ## Disclosure clock
51
+
52
+ `disclosure_review` does arithmetic on dates you supply. It does not know about the inventor's earlier priority filing, derivation proceedings, experimental use, or national exceptions beyond the flags it states. Confirm every date with counsel.
53
+
54
+ ## Undocumented provider risk
55
+
56
+ `IP_ALLOW_UNOFFICIAL=1` enables adapters for:
57
+
58
+ - `ppubs.uspto.gov` session and search endpoints.
59
+ - `tmsearch.uspto.gov` configuration discovery and the Elasticsearch-style search backend.
60
+
61
+ Both can break on any USPTO deploy. Both are subject to USPTO terms of use, which state that unusually high automated access may be denied and that website interfaces are not a bulk-data source. Use the local index or official APIs when reliability matters. When these adapters run, hits are labeled `undocumented: true` and are candidates only.
62
+
63
+ ## Legal
64
+
65
+ Nothing here is legal advice, an opinion of patentability, a registrability opinion, a clearance search, or a freedom-to-operate analysis. Every artifact is a draft for a licensed practitioner. The server has no filing, signature, or submission capability by design.
package/README.md CHANGED
@@ -1,3 +1,118 @@
1
- # Temporary Holding Version
1
+ # ip-search-mcp
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
+ MCP server for patent and trademark research plus review-only drafting of patent application artifacts.
4
+
5
+ It searches official and bulk-derived sources, keeps provenance on every hit, screens concepts for patentability and registrability signals, and produces drawings, claims, specifications, and an attorney packet. It never files, signs, or submits anything.
6
+
7
+ ## Status
8
+
9
+ Working: ODP file wrapper, TSDR lookup, EPO OPS, local SQLite FTS index, 37 CFR 1.84 drawing generation and validation, claim parsing and linting, disclosure-clock arithmetic, concept assessment, attorney packet assembly.
10
+
11
+ Opt-in only: Patent Public Search and the Trademark Search backend. Both are undocumented USPTO surfaces with no published contract; enable with `IP_ALLOW_UNOFFICIAL=1` only if you accept that risk.
12
+
13
+ ## Quickstart
14
+
15
+ ```bash
16
+ pnpm install
17
+ pnpm build
18
+ node dist/src/index.js --help
19
+ ```
20
+
21
+ Register it with an MCP client over stdio:
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "ip-search-mcp": {
27
+ "command": "node",
28
+ "args": ["C:/Users/brand/Desktop/ip-search-mcp/dist/src/index.js"],
29
+ "env": {
30
+ "USPTO_ODP_API_KEY": "...",
31
+ "USPTO_TSDR_API_KEY": "..."
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ No keys are required to run; without them keyed tools return a `CREDENTIAL_MISSING` error with setup instructions, and unkeyed sources are what remain.
39
+
40
+ Where keys go, and how to verify them without printing their values:
41
+
42
+ ```bash
43
+ node dist/src/index.js status
44
+ ```
45
+
46
+ See `CREDENTIALS.md` for the MCP `env` block, shell variables, and `--env-file`.
47
+
48
+ ## Tools
49
+
50
+ | Tool | Reads | Writes | Notes |
51
+ |------|-------|--------|-------|
52
+ | `credential_status` | yes | no | Provider table and required env vars |
53
+ | `concept_assessment` | yes | no | 101 screen, CPC/Nice guesses, closest hits, distinctiveness band |
54
+ | `patents_search` | yes | no | ODP, OPS, local index, opt-in PPUBS |
55
+ | `patent_get` | yes | no | File wrapper metadata, continuity, bibliographic data |
56
+ | `patent_documents` | yes | no | ODP prosecution document list |
57
+ | `patent_family` | yes | no | EPO OPS family |
58
+ | `trademarks_search` | yes | no | Candidates only; hydrate with `trademark_get` |
59
+ | `trademark_get` | yes | no | Authoritative TSDR status |
60
+ | `trademark_documents` | yes | no | TSDR documents and bundle PDF |
61
+ | `prior_art_report` | yes | no | Multi-query report with provenance and gaps |
62
+ | `drawing_spec` | no | optional file | Compliant SVG sheet |
63
+ | `drawing_validate` | yes | no | 37 CFR 1.84 findings with citations |
64
+ | `drawing_rules` | yes | no | Encoded rules and eCFR source |
65
+ | `application_draft` | no | optional file | Specification in examiner-expected order |
66
+ | `claims_draft` | no | no | Independent claim from supplied elements |
67
+ | `application_lint` | yes | no | Claim, abstract, and title checks |
68
+ | `disclosure_review` | yes | no | 102(b)(1) grace clock |
69
+ | `attorney_packet` | yes | optional file | Review packet |
70
+ | `index_build` | yes | yes | Local FTS5 index over bulk XML |
71
+
72
+ ## Shell interface: `ip-axi`
73
+
74
+ An AXI-compliant CLI over the same tools, for shell use and scripted work.
75
+
76
+ ```bash
77
+ node dist/src/axi/index.js # live provider and index status
78
+ node dist/src/axi/index.js patents search "optical sensor" --limit 5
79
+ node dist/src/axi/index.js patents docs 19131214 --code CTNF
80
+ node dist/src/axi/index.js trademarks get 87454105
81
+ node dist/src/axi/index.js drawings validate ip/drawings/fig-1.svg --description @ip/specification.md
82
+ node dist/src/axi/index.js index build --from <dir-of-bulk-xml>
83
+ ```
84
+
85
+ Run `npm link` once in the repo to put `ip-axi` on PATH; afterwards the bare command works from any directory. `pnpm exec ip-axi` also works inside the repo. `npx -y ip-axi` works only after the package is published.
86
+
87
+ Behavior: TOON output, totals on every list, `--fields` for extra columns, `--full` to stop truncation, exit codes 0 success / 1 error / 2 usage, structured errors on stdout with a fixing command, and a `next[]` block after every result.
88
+
89
+ `axi-axi validate` reports 11 pass, 0 fail, 1 warn on Windows. The warning is the validator's own limitation: it spawns `npm` without the `.cmd` suffix, so its skill freshness probe cannot start. `npm run skill:check` passes directly and is the real check.
90
+
91
+ ## Local index
92
+
93
+ ```bash
94
+ node dist/src/index.js index build --from <dir-of-xml> --db ~/.cache/ip-search-mcp/ip-index.sqlite
95
+ node dist/src/index.js index status
96
+ ```
97
+
98
+ Bulk XML comes from the ODP bulk data directory (patent grants, published applications, trademark daily XML). The ingest is best-effort over the documented field names; verify field mapping against the USPTO data dictionary for the snapshot you use.
99
+
100
+ Verified on a real ODP product, 2026-11-10: the trademark daily applications file `apc261107.zip` (7.7 MB compressed, 143 MB XML, 7,330 case-file records) indexed in 60 seconds with zero skips. Dates are normalized to ISO. Each `case-file` becomes one row, so a daily file is searched record by record instead of collapsing to its first entry.
101
+
102
+ ```bash
103
+ node dist/src/axi/index.js index build --from ./apc261107 --db ./ip-index.sqlite
104
+ ```
105
+
106
+ Trademark records from TDXF carry no status description text, only a status code, so `status` is empty for those rows until a status-code table is joined.
107
+
108
+ ## Documentation
109
+
110
+ - `ARCHITECTURE.md`: data flow, provider boundaries, provenance, caching, rate limits.
111
+ - `CREDENTIALS.md`: how to obtain each key and how keys are isolated.
112
+ - `LIMITS.md`: what no tool can do, and which surfaces carry terms-of-use risk.
113
+
114
+ ## Legal
115
+
116
+ Not legal advice. This server produces research and drafts for review by a licensed patent or trademark practitioner. No output is a filing, an opinion of patentability, a clearance opinion, or a freedom-to-operate analysis.
117
+
118
+ Companion reactive skill: `ip-filing-prep` in `~/Desktop/skills/ip-filing-prep`.
@@ -0,0 +1,83 @@
1
+ import { UsageError } from "./errors.js";
2
+ export function validFlagsHint(spec) {
3
+ const names = spec.flags.map((flag) => `--${flag.name}`);
4
+ names.push("--help");
5
+ return `valid flags for '${spec.name || "ip-axi"}': ${names.join(", ")}`;
6
+ }
7
+ export function parseArgs(argv, spec) {
8
+ const flags = {};
9
+ for (const flag of spec.flags) {
10
+ if (flag.default !== undefined)
11
+ flags[flag.name] = flag.default;
12
+ }
13
+ const positionals = [];
14
+ let help = false;
15
+ for (let i = 0; i < argv.length; i += 1) {
16
+ const token = argv[i];
17
+ if (token === "--help" || token === "-h") {
18
+ help = true;
19
+ continue;
20
+ }
21
+ if (token.startsWith("--")) {
22
+ let name = token.slice(2);
23
+ let inline;
24
+ const equals = name.indexOf("=");
25
+ if (equals >= 0) {
26
+ inline = name.slice(equals + 1);
27
+ name = name.slice(0, equals);
28
+ }
29
+ const flag = spec.flags.find((entry) => entry.name === name);
30
+ if (!flag) {
31
+ throw new UsageError(`unknown flag --${name}`, validFlagsHint(spec));
32
+ }
33
+ if (flag.type === "boolean") {
34
+ if (inline !== undefined) {
35
+ throw new UsageError(`flag --${name} does not take a value`, validFlagsHint(spec));
36
+ }
37
+ flags[name] = true;
38
+ continue;
39
+ }
40
+ let value = inline;
41
+ if (value === undefined) {
42
+ const next = argv[i + 1];
43
+ if (next === undefined || next.startsWith("--")) {
44
+ throw new UsageError(`flag --${name} requires a value`, `usage: --${name} <value>`);
45
+ }
46
+ value = next;
47
+ i += 1;
48
+ }
49
+ if (flag.values && !flag.values.includes(value)) {
50
+ throw new UsageError(`invalid value '${value}' for --${name}`, `valid values: ${flag.values.join(", ")}`);
51
+ }
52
+ flags[name] = value;
53
+ continue;
54
+ }
55
+ positionals.push(token);
56
+ }
57
+ if (!help) {
58
+ for (const arg of spec.args ?? []) {
59
+ if (arg.required && positionals.length === 0) {
60
+ throw new UsageError(`missing argument <${arg.name}>`, `usage: ip-axi ${spec.name} <${arg.name}>`);
61
+ }
62
+ }
63
+ }
64
+ return { positionals, flags, help };
65
+ }
66
+ export function stringFlag(parsed, name) {
67
+ const value = parsed.flags[name];
68
+ return typeof value === "string" ? value : undefined;
69
+ }
70
+ export function booleanFlag(parsed, name) {
71
+ return parsed.flags[name] === true;
72
+ }
73
+ export function numberFlag(parsed, name, fallback) {
74
+ const raw = stringFlag(parsed, name);
75
+ if (raw === undefined)
76
+ return fallback;
77
+ const value = Number(raw);
78
+ if (!Number.isFinite(value) || value < 1) {
79
+ throw new UsageError(`--${name} must be a positive number`, `got '${raw}'`);
80
+ }
81
+ return Math.floor(value);
82
+ }
83
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/axi/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ // Bootstrap runs before the CLI imports config, because provider credentials are
3
+ // read once at module load. Keys already in the environment win; otherwise the
4
+ // same MCP config that agents use supplies them, so the shell interface behaves
5
+ // like an MCP client instead of requiring a second copy of every secret.
6
+ import { readFileSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ function seedFromMcpConfig() {
10
+ try {
11
+ const config = JSON.parse(readFileSync(join(homedir(), ".agents", "mcp.json"), "utf8"));
12
+ const env = config.mcpServers?.["ip-search-mcp"]?.env;
13
+ if (!env)
14
+ return;
15
+ for (const [name, value] of Object.entries(env)) {
16
+ if (value && !process.env[name])
17
+ process.env[name] = value;
18
+ }
19
+ }
20
+ catch {
21
+ // Missing or unreadable config is not fatal; the CLI reports unconfigured providers.
22
+ }
23
+ }
24
+ seedFromMcpConfig();
25
+ await import("./index.js");
26
+ //# sourceMappingURL=boot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.js","sourceRoot":"","sources":["../../../src/axi/boot.ts"],"names":[],"mappings":";AACA,iFAAiF;AACjF,+EAA+E;AAC/E,gFAAgF;AAChF,yEAAyE;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAErF,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC;QACtD,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAChD,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QAC7D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;IACvF,CAAC;AACH,CAAC;AAED,iBAAiB,EAAE,CAAC;AACpB,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
+ import { createServer } from "../server.js";
4
+ import { AxiError } from "./errors.js";
5
+ let session;
6
+ let live;
7
+ async function connect() {
8
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
9
+ const server = createServer();
10
+ await server.connect(serverTransport);
11
+ const client = new Client({ name: "ip-axi", version: "0.1.0" });
12
+ await client.connect(clientTransport);
13
+ live = { client, server };
14
+ return client;
15
+ }
16
+ export function getClient() {
17
+ if (!session)
18
+ session = connect();
19
+ return session;
20
+ }
21
+ function textOf(content) {
22
+ if (!Array.isArray(content) || content.length === 0)
23
+ return "";
24
+ const first = content[0];
25
+ if (first === null || typeof first !== "object" || !("text" in first))
26
+ return "";
27
+ const text = first.text;
28
+ return typeof text === "string" ? text : "";
29
+ }
30
+ /** Call an MCP tool in-process and return its JSON payload. */
31
+ export async function callTool(name, args) {
32
+ const client = await getClient();
33
+ const result = await client.callTool({ name, arguments: args });
34
+ const text = textOf(result.content);
35
+ let payload;
36
+ try {
37
+ payload = JSON.parse(text);
38
+ }
39
+ catch {
40
+ throw new AxiError(`${name} returned a non-JSON payload`, text.slice(0, 200));
41
+ }
42
+ if (result.isError) {
43
+ const record = payload;
44
+ throw new AxiError(record.message ?? `${name} failed`, record.hint);
45
+ }
46
+ return payload;
47
+ }
48
+ /** Close the in-process MCP session before exiting so libuv handles drain. */
49
+ export async function shutdown() {
50
+ if (!live)
51
+ return;
52
+ const current = live;
53
+ live = undefined;
54
+ session = undefined;
55
+ await current.client.close().catch(() => undefined);
56
+ await current.server.close().catch(() => undefined);
57
+ }
58
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/axi/client.ts"],"names":[],"mappings":"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"}