@biomate/mcp-server 0.1.0 → 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/README.md CHANGED
@@ -27,19 +27,19 @@ Get your API key at **[dev-public.biomate.ai/account/api-keys](https://dev-publi
27
27
 
28
28
  | Tool | Purpose |
29
29
  |---|---|
30
- | `biomate_session` | Primary: natural-language goal → full workflow execution |
31
- | `search_workflow` | Search 2,455 indexed workflows by natural language |
30
+ | `biomate_session` | Plan an analysis and return workflow suggestions and clarification requests |
31
+ | `search_workflow` | Search the workflow catalog by natural language |
32
32
  | `get_workflow_spec` | Full parameter spec for a workflow |
33
33
  | `run_workflow` | Execute with explicit parameters |
34
- | `get_run` / `watch_run` | Status, phases, steps, outputs |
34
+ | `get_run` / `watch_run` | Status snapshots, outputs and available findings |
35
35
  | `cancel_run` | Cancel a running job |
36
36
  | `list_runs` | History with filters |
37
37
  | `preview_file` | Server-side preview of output files |
38
- | `export_report` | PDF/markdown publication report |
38
+ | `export_report` | Markdown findings report (may return pending during preparation) |
39
39
  | `analyze_results` | AI interpretation of findings |
40
40
  | `explain_error` | Root-cause diagnosis for failed runs |
41
41
  | `query_database` | UniProt, PDB, NCBI, ChEMBL, AlphaFold |
42
- | `resolve_accession` | GEO/SRA/ENA → auto-run GEO data connector |
42
+ | `resolve_accession` | Resolve a public accession |
43
43
  | `browse_data` / `fetch_public_data` | Browse and stage public datasets |
44
44
  | `upload_file` | Get signed S3 PUT URL for local uploads |
45
45
  | `recall_memory` | Retrieve relevant prior runs and procedures |
@@ -49,7 +49,7 @@ Get your API key at **[dev-public.biomate.ai/account/api-keys](https://dev-publi
49
49
 
50
50
  | Variable | Description |
51
51
  |---|---|
52
- | `BIOMATE_API_KEY` | Personal API key (`bm_live_xxx`) — required |
52
+ | `BIOMATE_API_KEY` | Personal API key (`bm_live_xxx`) — alternative to OAuth |
53
53
  | `BIOMATE_API_BASE` | BioMate server URL (default: `https://dev-public.biomate.ai`) |
54
54
 
55
55
  ## Example
@@ -69,3 +69,9 @@ Get your API key at **[dev-public.biomate.ai/account/api-keys](https://dev-publi
69
69
  ## License
70
70
 
71
71
  MIT — this package only. BioMate platform usage governed by [biomate.ai/terms](https://biomate.ai/terms).
72
+
73
+ ## OAuth sign-in (0.1.1)
74
+
75
+ Run `npx @biomate/connect@0.1.1 claude-code --auth-base https://dev-public.biomate.ai` (or use `claude-desktop`, `cursor`, or `codex`). This explicitly selects the development deployment. The installer stores a protected credential file and sets `BIOMATE_OAUTH_FILE` in the host configuration. The client refreshes and persists rotating tokens and calls the OAuth gateway using the current user identity. Existing OAuth installations must reconnect; a refresh token is never used as a personal API key.
76
+
77
+ This package has 19 tools. Instrument tools, PDF/DOCX exports, automatic session execution and progress notifications are not supported by this stdio adapter. Use `get_run` to poll status and export Markdown findings.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export type Credential = {
2
+ authBase: string;
3
+ clientId: string;
4
+ access_token: string;
5
+ refresh_token: string;
6
+ expires_at: number;
7
+ };
8
+ export declare function oauthCredential(file: string): Promise<Credential>;
package/dist/auth.js ADDED
@@ -0,0 +1,54 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ function origin(value) {
4
+ const url = new URL(value);
5
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && ['127.0.0.1', 'localhost'].includes(url.hostname)))
6
+ throw new Error('OAuth requires HTTPS');
7
+ if (url.username || url.password || url.search || url.hash || url.pathname !== '/')
8
+ throw new Error('OAuth base must be an origin');
9
+ return url.origin;
10
+ }
11
+ export async function oauthCredential(file) {
12
+ const lock = file + '.lock';
13
+ let handle;
14
+ for (let attempt = 0; attempt < 100; attempt++) {
15
+ try {
16
+ handle = await fs.open(lock, 'wx', 0o600);
17
+ break;
18
+ }
19
+ catch (error) {
20
+ if (error.code !== 'EEXIST')
21
+ throw error;
22
+ await new Promise(resolve => setTimeout(resolve, 100));
23
+ }
24
+ }
25
+ if (!handle)
26
+ throw new Error('OAuth credentials locked; retry or reconnect BioMate');
27
+ try {
28
+ const current = JSON.parse(await fs.readFile(file, 'utf8'));
29
+ current.authBase = origin(current.authBase);
30
+ if (!current.clientId || !current.refresh_token)
31
+ throw new Error('Reconnect BioMate: incomplete OAuth credentials');
32
+ if (current.access_token && current.expires_at > Date.now() + 60000)
33
+ return current;
34
+ const response = await fetch(current.authBase + '/oauth/token', {
35
+ method: 'POST', redirect: 'error', signal: AbortSignal.timeout(15000),
36
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
37
+ body: new URLSearchParams({ grant_type: 'refresh_token', client_id: current.clientId, refresh_token: current.refresh_token }),
38
+ });
39
+ if (!response.ok)
40
+ throw new Error(`OAuth refresh failed (HTTP ${response.status}); reconnect BioMate`);
41
+ const updated = await response.json();
42
+ if (!updated.access_token || !updated.refresh_token || !(Number(updated.expires_in) > 0))
43
+ throw new Error('Invalid OAuth token response; reconnect BioMate');
44
+ const next = { ...current, access_token: updated.access_token, refresh_token: updated.refresh_token, expires_at: Date.now() + Number(updated.expires_in) * 1000 };
45
+ const tmp = file + '.' + randomUUID() + '.tmp';
46
+ await fs.writeFile(tmp, JSON.stringify(next), { mode: 0o600 });
47
+ await fs.rename(tmp, file);
48
+ return next;
49
+ }
50
+ finally {
51
+ await handle.close();
52
+ await fs.unlink(lock);
53
+ }
54
+ }
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * Configuration (environment variables):
9
9
  * BIOMATE_API_BASE BioMate server URL (default: https://dev-public.biomate.ai)
10
10
  * BIOMATE_API_KEY Personal API key (bm_live_xxx) from biomate.ai/account/api-keys
11
- * BIOMATE_REFRESH_TOKEN OAuth refresh token (alternative to API key)
11
+ * BIOMATE_OAUTH_FILE Protected credential file written by @biomate/connect
12
12
  *
13
13
  * Each tool call POSTs to ${BIOMATE_API_BASE}/tools/${toolName} with the
14
14
  * Authorization header set. The /tools/* routes are served by BioMate's
package/dist/index.js CHANGED
@@ -8,12 +8,13 @@
8
8
  * Configuration (environment variables):
9
9
  * BIOMATE_API_BASE BioMate server URL (default: https://dev-public.biomate.ai)
10
10
  * BIOMATE_API_KEY Personal API key (bm_live_xxx) from biomate.ai/account/api-keys
11
- * BIOMATE_REFRESH_TOKEN OAuth refresh token (alternative to API key)
11
+ * BIOMATE_OAUTH_FILE Protected credential file written by @biomate/connect
12
12
  *
13
13
  * Each tool call POSTs to ${BIOMATE_API_BASE}/tools/${toolName} with the
14
14
  * Authorization header set. The /tools/* routes are served by BioMate's
15
15
  * Node.js API server.
16
16
  */
17
+ import { oauthCredential } from "./auth.js";
17
18
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
18
19
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
20
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
@@ -21,7 +22,7 @@ import { createRequire } from "module";
21
22
  const require = createRequire(import.meta.url);
22
23
  // ── Config ────────────────────────────────────────────────────────────────────
23
24
  const API_BASE = (process.env.BIOMATE_API_BASE ?? "https://dev-public.biomate.ai").replace(/\/$/, "");
24
- const API_KEY = process.env.BIOMATE_API_KEY ?? process.env.BIOMATE_REFRESH_TOKEN ?? "";
25
+ const API_KEY = process.env.BIOMATE_API_KEY ?? "";
25
26
  // ── Tools manifest ────────────────────────────────────────────────────────────
26
27
  const manifest = require("./tools_manifest.json");
27
28
  const TOOLS = manifest.mcp.map((t) => ({
@@ -43,7 +44,18 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
43
44
  let data;
44
45
  let ok = true;
45
46
  try {
46
- const res = await fetch(`${API_BASE}/tools/${name}`, {
47
+ if (!TOOLS.some(tool => tool.name === name))
48
+ throw new Error("Unknown tool");
49
+ let endpoint = `${API_BASE}/tools/${name}`;
50
+ if (!API_KEY) {
51
+ if (!process.env.BIOMATE_OAUTH_FILE)
52
+ throw new Error("Reconnect BioMate with the updated installer or configure a personal API key");
53
+ const credential = await oauthCredential(process.env.BIOMATE_OAUTH_FILE);
54
+ headers.Authorization = `Bearer ${credential.access_token}`;
55
+ endpoint = `${credential.authBase}/oauth/tools/${name}`;
56
+ }
57
+ const res = await fetch(endpoint, {
58
+ redirect: "error",
47
59
  method: "POST",
48
60
  headers,
49
61
  body: JSON.stringify(args ?? {}),
@@ -69,7 +81,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
69
81
  };
70
82
  });
71
83
  // ── Start ─────────────────────────────────────────────────────────────────────
72
- if (!API_KEY) {
84
+ if (!API_KEY && !process.env.BIOMATE_OAUTH_FILE) {
73
85
  process.stderr.write("[biomate-mcp] WARNING: BIOMATE_API_KEY not set. " +
74
86
  "Get your key at https://dev-public.biomate.ai/account/api-keys\n");
75
87
  }