@etsquare/sev-mcp 0.1.2 → 0.1.3
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/client.mjs +20 -4
- package/index.mjs +1 -1
- package/package.json +1 -1
package/client.mjs
CHANGED
|
@@ -68,6 +68,7 @@ function publicFailure(value, status, request) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export class SevClient {
|
|
71
|
+
#passageTail=Promise.resolve();
|
|
71
72
|
constructor({baseURL=process.env.SEV_BASE_URL||'https://etsquare.ai', apiKey=process.env.SEV_API_KEY,
|
|
72
73
|
termsVersion=process.env.SEV_ACCEPT_TERMS_VERSION, fetchImpl=globalThis.fetch, timeout=20000}={}) {
|
|
73
74
|
const url=new URL(baseURL);
|
|
@@ -118,9 +119,24 @@ export class SevClient {
|
|
|
118
119
|
}
|
|
119
120
|
async passage(id,signal){
|
|
120
121
|
if(!/^[a-f0-9]{32}$/i.test(id))throw new ApiError('invalid_passage_id','Use the passage_id returned by discovery.',400);
|
|
121
|
-
const
|
|
122
|
-
if(
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const cancelled=()=>new ApiError('request_cancelled','Stopped waiting. Work already submitted may continue on the server.');
|
|
123
|
+
if(signal?.aborted)throw cancelled();
|
|
124
|
+
let onAbort;
|
|
125
|
+
const cancellation=signal?new Promise((_,reject)=>{
|
|
126
|
+
onAbort=()=>reject(cancelled());signal.addEventListener('abort',onAbort,{once:true});
|
|
127
|
+
}):null;
|
|
128
|
+
const run=this.#passageTail.then(async()=>{
|
|
129
|
+
// Once sent, request() owns cancellation and starts its existing timeout.
|
|
130
|
+
if(onAbort)signal.removeEventListener('abort',onAbort);
|
|
131
|
+
if(signal?.aborted)throw cancelled();
|
|
132
|
+
const result=await this.request('/v1/sec/passages/'+encodeURIComponent(id),undefined,signal);
|
|
133
|
+
if(typeof result.passage_id!=='string'||result.passage_id.toUpperCase()!==id.toUpperCase()||typeof result.text!=='string'||!Array.isArray(result.adjacent_passages))
|
|
134
|
+
throw new ApiError('invalid_response','SEV returned an invalid source response.');
|
|
135
|
+
return result;
|
|
136
|
+
});
|
|
137
|
+
// Absorb failure only on the queue tail; each caller still gets its own error.
|
|
138
|
+
this.#passageTail=run.then(()=>undefined,()=>undefined);
|
|
139
|
+
try{return await (cancellation?Promise.race([run,cancellation]):run);}
|
|
140
|
+
finally{if(onAbort)signal.removeEventListener('abort',onAbort);}
|
|
125
141
|
}
|
|
126
142
|
}
|
package/index.mjs
CHANGED
|
@@ -19,7 +19,7 @@ const source=z.object({passage_id:z.string().regex(/^[A-Fa-f0-9]{32}$/)}).strict
|
|
|
19
19
|
const text=value=>({content:[{type:'text',text:JSON.stringify(value)}],structuredContent:value});
|
|
20
20
|
|
|
21
21
|
export function createServer(client=new SevClient()) {
|
|
22
|
-
const server=new McpServer({name:'sev',version:'0.1.
|
|
22
|
+
const server=new McpServer({name:'sev',version:'0.1.3'},{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
23
|
const call=operation=>async(input,extra)=>{
|
|
24
24
|
try{return text(await operation(input,extra.signal));}
|
|
25
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};}
|