@etsquare/sev-mcp 0.0.0-stage → 0.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 ETSquare AI, 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
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,116 @@
1
- # Temporary Holding Version
1
+ # SEV 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
+ Ask a financial research question. Get relevant public companies and original SEC filing passages to investigate.
4
+
5
+ SEV searches its supported SEC corpus and returns a ranked company shortlist with precise filing references. Your agent handles further analysis. Results are research leads; omissions and empty results are possible.
6
+
7
+ ## Connect
8
+
9
+ 1. Install [Node.js](https://nodejs.org/) 20.19 or later.
10
+ 2. Sign in at [etsquare.ai/account](https://etsquare.ai/account), accept the released Terms, and create an API key. Save its secret when shown.
11
+ 3. Add one of the configurations below, replace YOUR_SEV_API_KEY, and restart your MCP client. Keep your other server entries.
12
+
13
+ The first start downloads this version from npm. No source checkout is required. The [Build page](https://etsquare.ai/build) shows the same setup for your platform.
14
+
15
+ ### Codex ? macOS / Linux
16
+
17
+ Add to ~/.codex/config.toml:
18
+
19
+ ```toml
20
+ [mcp_servers.sev]
21
+ command = "npx"
22
+ args = ["-y","@etsquare/sev-mcp@0.1.1"]
23
+ startup_timeout_sec = 30
24
+ tool_timeout_sec = 25
25
+
26
+ [mcp_servers.sev.env]
27
+ SEV_BASE_URL = "https://etsquare.ai"
28
+ SEV_API_KEY = "YOUR_SEV_API_KEY"
29
+ ```
30
+
31
+ ### Codex ? Windows
32
+
33
+ ```toml
34
+ [mcp_servers.sev]
35
+ command = "cmd"
36
+ args = ["/c","npx","-y","@etsquare/sev-mcp@0.1.1"]
37
+ startup_timeout_sec = 30
38
+ tool_timeout_sec = 25
39
+
40
+ [mcp_servers.sev.env]
41
+ SEV_BASE_URL = "https://etsquare.ai"
42
+ SEV_API_KEY = "YOUR_SEV_API_KEY"
43
+ ```
44
+
45
+ ### Claude Desktop ? macOS / Linux
46
+
47
+ Merge into your Claude Desktop configuration JSON:
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "sev": {
53
+ "command": "npx",
54
+ "args": [
55
+ "-y",
56
+ "@etsquare/sev-mcp@0.1.1"
57
+ ],
58
+ "env": {
59
+ "SEV_BASE_URL": "https://etsquare.ai",
60
+ "SEV_API_KEY": "YOUR_SEV_API_KEY"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Claude Desktop ? Windows
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "sev": {
73
+ "command": "cmd",
74
+ "args": [
75
+ "/c",
76
+ "npx",
77
+ "-y",
78
+ "@etsquare/sev-mcp@0.1.1"
79
+ ],
80
+ "env": {
81
+ "SEV_BASE_URL": "https://etsquare.ai",
82
+ "SEV_API_KEY": "YOUR_SEV_API_KEY"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## Tools
90
+
91
+ - **discover_sec_companies**: required question; optional tickers **or** four-digit sic_codes, inclusive filed_since (YYYY-MM-DD), and max_companies (default 10, maximum 50). Explicit filters are never relaxed. There is no hidden date default.
92
+ - **get_sec_passage**: retrieve a returned passage_id and available neighboring context without restarting discovery. Clients supporting MCP resources can also read sev://passages/{passage_id}.
93
+
94
+ Passage IDs accept uppercase, lowercase or mixed-case hexadecimal. Returned IDs may use their canonical uppercase form.
95
+
96
+ Returned references include company identity, accession, form, filing date, section, source URL and stable passage identifier. Optional previews are verbatim. Rankings describe research relevance, not investment merit or proof of current impact.
97
+
98
+ Try: ?Which retailers describe overseas factories or sourcing agents and exposure to higher merchandise costs??
99
+
100
+ ## Access and timeouts
101
+
102
+ Your account's evaluation allowance is shared by Playground, REST and MCP. A successful search consumes one discovery, including a valid empty result; failed searches do not. Source reads have a separate rate limit. Paid plans are coming soon.
103
+
104
+ This is a local stdio MCP client of the hosted REST service, not a remote /mcp endpoint. SEV_BASE_URL is the service origin; the default is https://etsquare.ai. Do not append /SEV or /mcp. HTTPS is required except loopback HTTP for local development. A key created on a local service does not authorize production.
105
+
106
+ The client has a 20-second HTTP deadline and no automatic discovery retry. Codex's example allows 30 seconds for startup (including the first npm download) and 25 seconds per tool call. Those settings belong to Codex; do not add them to Claude Desktop JSON. These are timeout limits, not response-speed guarantees. A submitted request may finish on the server if the caller stops waiting.
107
+
108
+ Keep your key private, including in MCP configuration. Revoke it from Account when no longer needed. An invalid configured key never falls back to anonymous access. The client does not write credentials or cached research results to disk. Protocol messages alone go to stdout.
109
+
110
+ Anonymous use is optional and requires explicit acceptance of the service's released Terms: read the Terms and Privacy Policy first, then set SEV_ACCEPT_TERMS_VERSION to the exact version you accepted. The client never accepts a new agreement automatically. A missing or outdated agreement fails before discovery. Use an account key for normal integrations.
111
+
112
+ ## Support
113
+
114
+ [Documentation](https://etsquare.ai/documentation) ? [Terms](https://etsquare.ai/terms) ? [Privacy](https://etsquare.ai/privacy) ? [Support](https://etsquare.ai/support)
115
+
116
+ MIT licensed thin client. This package is separate from the historical @etsquare/mcp-server-sec package.
package/client.mjs ADDED
@@ -0,0 +1,58 @@
1
+ /** HTTP-only bridge. No Oracle credentials, models, automatic retries or disk state. */
2
+ export class ApiError extends Error {
3
+ constructor(code, message, status=0) {super(message);this.code=code;this.status=status;}
4
+ }
5
+
6
+ export class SevClient {
7
+ constructor({baseURL=process.env.SEV_BASE_URL||'https://etsquare.ai', apiKey=process.env.SEV_API_KEY,
8
+ termsVersion=process.env.SEV_ACCEPT_TERMS_VERSION, fetchImpl=globalThis.fetch, timeout=20000}={}) {
9
+ const url=new URL(baseURL);
10
+ const local=['localhost','127.0.0.1','[::1]'].includes(url.hostname);
11
+ if ((url.protocol!=='https:'&&!(local&&url.protocol==='http:'))||url.username||url.password||url.search||url.hash||url.pathname!=='/')
12
+ throw new Error('SEV_BASE_URL must be an HTTPS origin, or an HTTP loopback origin for development.');
13
+ if(apiKey&&!/^sev_[A-Za-z0-9_-]{43}$/.test(apiKey))throw new Error('SEV_API_KEY is not a valid SEV key.');
14
+ if(termsVersion&&!/^[A-Za-z0-9._-]{1,60}$/.test(termsVersion))throw new Error('Invalid reviewed Terms version.');
15
+ if(!Number.isInteger(timeout)||timeout<1||timeout>60000)throw new Error('Invalid request timeout.');
16
+ this.base=url.origin;this.apiKey=apiKey;this.termsVersion=termsVersion;this.fetch=fetchImpl;this.timeout=timeout;this.cookie='';
17
+ }
18
+ async request(path, body, signal) {
19
+ const headers={'Accept':'application/json','X-SEV-Client':'mcp'};
20
+ if(this.apiKey)headers.Authorization='Bearer '+this.apiKey;
21
+ else if(this.cookie)headers.Cookie=this.cookie;
22
+ if(body!==undefined)headers['Content-Type']='application/json';
23
+ if(!this.apiKey&&path==='/v1/sec/discover-companies'&&this.termsVersion)headers['X-SEV-Terms-Version']=this.termsVersion;
24
+ let response;
25
+ try {
26
+ response=await this.fetch(this.base+path,{method:body===undefined?'GET':'POST',headers,redirect:'error',
27
+ ...(body===undefined?{}:{body:JSON.stringify(body)}),
28
+ signal:signal?AbortSignal.any([signal,AbortSignal.timeout(this.timeout)]):AbortSignal.timeout(this.timeout)});
29
+ if(!this.apiKey){const cookie=response.headers.get('set-cookie')?.match(/(?:^|,\s*)__session=([A-Za-z0-9_.-]+);/);if(cookie)this.cookie='__session='+cookie[1];}
30
+ const declared=Number(response.headers.get('content-length')||0);
31
+ if(declared>4194304)throw new ApiError('invalid_response','The SEV response exceeded the supported size.');
32
+ const reader=response.body?.getReader();let size=0;const parts=[];
33
+ if(reader){try{for(;;){const {done,value}=await reader.read();if(done)break;size+=value.length;if(size>4194304)throw new ApiError('invalid_response','The SEV response exceeded the supported size.');parts.push(value);}}finally{await reader.cancel();}}
34
+ const bytes=new Uint8Array(size);let offset=0;for(const part of parts){bytes.set(part,offset);offset+=part.length;}
35
+ let result;try{result=JSON.parse(new TextDecoder().decode(bytes));}catch{throw new ApiError('invalid_response','SEV returned an invalid JSON response.');}
36
+ if(!response.ok||result.status==='error')throw new ApiError(result.error?.code||'http_error',result.error?.message||'The SEV request failed.',response.status);
37
+ return result;
38
+ } catch(error) {
39
+ if(error instanceof ApiError)throw error;
40
+ if(signal?.aborted)throw new ApiError('request_cancelled','Stopped waiting. Work already submitted may continue on the server.');
41
+ throw new ApiError('service_unreachable','SEV could not be reached or the response timed out. No automatic retry was made.');
42
+ }
43
+ }
44
+ async discover(input,signal){
45
+ if(input.tickers?.length&&input.sic_codes?.length)throw new ApiError('invalid_request','Use tickers or SIC codes, not both.',422);
46
+ const result=await this.request('/v1/sec/discover-companies',input,signal);
47
+ if(result.status!=='ok'||!Array.isArray(result.companies)||typeof result.question!=='string')
48
+ throw new ApiError('invalid_response','SEV returned an invalid discovery response.');
49
+ return result;
50
+ }
51
+ async passage(id,signal){
52
+ if(!/^[a-f0-9]{32}$/i.test(id))throw new ApiError('invalid_passage_id','Use the passage_id returned by discovery.',400);
53
+ const result=await this.request('/v1/sec/passages/'+encodeURIComponent(id),undefined,signal);
54
+ if(typeof result.passage_id!=='string'||result.passage_id.toUpperCase()!==id.toUpperCase()||typeof result.text!=='string'||!Array.isArray(result.adjacent_passages))
55
+ throw new ApiError('invalid_response','SEV returned an invalid source response.');
56
+ return result;
57
+ }
58
+ }
package/index.mjs ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import {McpServer, ResourceTemplate} from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import {z} from 'zod';
5
+ import {pathToFileURL} from 'node:url';
6
+ import {SevClient, ApiError} from './client.mjs';
7
+
8
+ const date=z.string().regex(/^\d{4}-\d{2}-\d{2}$/).refine(value=>{
9
+ const d=new Date(value+'T00:00:00Z');return !Number.isNaN(d.getTime())&&d.toISOString().slice(0,10)===value&&value.slice(0,4)!=='0000';
10
+ },'Use a real calendar date, YYYY-MM-DD.');
11
+ const discovery=z.object({
12
+ question:z.string().trim().min(1).refine(s=>Array.from(s).length<=4000,'Maximum 4,000 characters.').describe('Natural-language SEC research question. Substantive alternatives and relationships matter.'),
13
+ tickers:z.array(z.string().trim().toUpperCase().regex(/^[A-Z0-9][A-Z0-9.\-^/]{0,19}$/)).max(50).optional().describe('Explicit company universe. Alternative to sic_codes; never relaxed.'),
14
+ sic_codes:z.array(z.string().regex(/^\d{4}$/)).max(50).optional().describe('Explicit four-digit industry codes, retaining leading zeroes. Alternative to tickers.'),
15
+ filed_since:date.nullable().optional().describe('Inclusive SEC filing date. No hidden date default.'),
16
+ max_companies:z.number().int().min(1).max(50).default(10),
17
+ }).strict();
18
+ const source=z.object({passage_id:z.string().regex(/^[A-Fa-f0-9]{32}$/)}).strict();
19
+ const text=value=>({content:[{type:'text',text:JSON.stringify(value)}],structuredContent:value});
20
+
21
+ export function createServer(client=new SevClient()) {
22
+ const server=new McpServer({name:'sev',version:'0.1.1'},{instructions:'SEV finds companies and original SEC passages worth investigating. Rankings indicate research relevance, not investment merit or proof of impact. Follow original sources. Do not automatically repeat a failed or uncertain discovery; it may already have used allowance. Use get_sec_passage for full text and adjacent context.'});
23
+ const call=operation=>async(input,extra)=>{
24
+ try{return text(await operation(input,extra.signal));}
25
+ catch(error){const known=error instanceof ApiError;return {...text({status:'error',error:{code:known?error.code:'client_error',message:known?error.message:'The SEV client could not complete the request.'}}),isError:true};}
26
+ };
27
+ server.registerTool('discover_sec_companies',{
28
+ title:'Discover SEC companies',description:'Find relevant public companies and one to three original SEC passages per company for a research question. Searches the supported indexed corpus; omissions and zero results are possible. Optional explicit tickers OR SICs and an inclusive filing-date cutoff. No synthesis or investment recommendation.',
29
+ inputSchema:discovery,annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:false,openWorldHint:true},
30
+ },call((input,signal)=>client.discover(input,signal)));
31
+ server.registerTool('get_sec_passage',{
32
+ title:'Read discovered SEC passage',description:'Retrieve an original passage and available adjacent same-section context using a passage_id from discovery. Supporting source retrieval; no new search or JEV call.',
33
+ inputSchema:source,annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true},
34
+ },call((input,signal)=>client.passage(input.passage_id,signal)));
35
+ server.registerResource('sec-passage',new ResourceTemplate('sev://passages/{passage_id}',{list:undefined}),{mimeType:'application/json'},
36
+ async(uri,{passage_id},extra)=>({contents:[{uri:uri.href,mimeType:'application/json',text:JSON.stringify(await client.passage(passage_id,extra.signal))}]}));
37
+ return server;
38
+ }
39
+
40
+ if(process.argv[1]&&import.meta.url===pathToFileURL(process.argv[1]).href){
41
+ try{await createServer().connect(new StdioServerTransport());}
42
+ catch{process.stderr.write('SEV MCP could not start. Check Node version and SEV configuration.\n');process.exitCode=1;}
43
+ }
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@etsquare/sev-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "SEV company discovery and original SEC passage access over hosted REST.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=20.19"
9
+ },
10
+ "bin": {
11
+ "sev-mcp": "index.mjs"
12
+ },
13
+ "files": [
14
+ "index.mjs",
15
+ "client.mjs",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "test": "node --test test/*.test.mjs"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "1.32.1",
24
+ "zod": "3.25.76"
25
+ },
26
+ "author": "ETSquare AI, LLC",
27
+ "homepage": "https://etsquare.ai/build",
28
+ "bugs": {
29
+ "url": "https://etsquare.ai/support"
30
+ },
31
+ "keywords": [
32
+ "mcp",
33
+ "sec",
34
+ "edgar",
35
+ "company-discovery",
36
+ "claude",
37
+ "codex"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org/"
42
+ }
43
+ }